1 /*
  2  * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  *
 23  */
 24 
 25 #ifndef SHARE_CLASSFILE_COMPACTHASHTABLE_HPP
 26 #define SHARE_CLASSFILE_COMPACTHASHTABLE_HPP
 27 
 28 #include "cds/cds_globals.hpp"
 29 #include "oops/array.hpp"
 30 #include "oops/symbol.hpp"
 31 #include "runtime/globals.hpp"
 32 #include "utilities/growableArray.hpp"
 33 
 34 
 35 template <
 36   typename K,
 37   typename V,
 38   V (*DECODE)(address base_address, u4 offset),
 39   bool (*EQUALS)(V value, K key, int len)
 40   >
 41 class CompactHashtable;
 42 class NumberSeq;
 43 class SimpleCompactHashtable;
 44 class SerializeClosure;
 45 
 46 // Stats for symbol tables in the CDS archive
 47 class CompactHashtableStats {
 48 public:
 49   int hashentry_count;
 50   int hashentry_bytes;
 51   int bucket_count;
 52   int bucket_bytes;
 53 
 54   CompactHashtableStats() :
 55     hashentry_count(0), hashentry_bytes(0),
 56     bucket_count(0), bucket_bytes(0) {}
 57 };
 58 
 59 #if INCLUDE_CDS
 60 /////////////////////////////////////////////////////////////////////////
 61 //
 62 // The compact hash table writer. Used at dump time for writing out
 63 // the compact table to the shared archive.
 64 //
 65 // At dump time, the CompactHashtableWriter obtains all entries from the
 66 // symbol/string table and adds them to a new temporary hash table. The hash
 67 // table size (number of buckets) is calculated using
 68 // '(num_entries + bucket_size - 1) / bucket_size'. The default bucket
 69 // size is 4 and can be changed by -XX:SharedSymbolTableBucketSize option.
 70 // 4 is chosen because it produces smaller sized bucket on average for
 71 // faster lookup. It also has relatively small number of empty buckets and
 72 // good distribution of the entries.
 73 //
 74 // We use a simple hash function (hash % num_bucket) for the table.
 75 // The new table is compacted when written out. Please see comments
 76 // above the CompactHashtable class for the table layout detail. The bucket
 77 // offsets are written to the archive as part of the compact table. The
 78 // bucket offset is encoded in the low 30-bit (0-29) and the bucket type
 79 // (regular or compact) are encoded in bit[31, 30]. For buckets with more
 80 // than one entry, both hash and entry offset are written to the
 81 // table. For buckets with only one entry, only the entry offset is written
 82 // to the table and the buckets are tagged as compact in their type bits.
 83 // Buckets without entry are skipped from the table. Their offsets are
 84 // still written out for faster lookup.
 85 //
 86 class CompactHashtableWriter: public StackObj {
 87 public:
 88   class Entry {
 89     unsigned int _hash;
 90     u4 _value;
 91 
 92   public:
 93     Entry() {}
 94     Entry(unsigned int hash, u4 val) : _hash(hash), _value(val) {}
 95 
 96     u4 value() {
 97       return _value;
 98     }
 99     unsigned int hash() {
100       return _hash;
101     }
102 
103     bool operator==(const CompactHashtableWriter::Entry& other) {
104       return (_value == other._value && _hash == other._hash);
105     }
106   }; // class CompactHashtableWriter::Entry
107 
108 private:
109   int _num_entries_written;
110   int _num_buckets;
111   int _num_empty_buckets;
112   int _num_value_only_buckets;
113   int _num_other_buckets;
114   GrowableArray<Entry>** _buckets;
115   CompactHashtableStats* _stats;
116   Array<u4>* _compact_buckets;
117   Array<u4>* _compact_entries;
118 
119 public:
120   // This is called at dump-time only
121   CompactHashtableWriter(int num_entries, CompactHashtableStats* stats);
122   ~CompactHashtableWriter();
123 
124   void add(unsigned int hash, u4 value);
125 
126 private:
127   void allocate_table();
128   void dump_table(NumberSeq* summary);
129   static int calculate_num_buckets(int num_entries) {
130     int num_buckets = num_entries / SharedSymbolTableBucketSize;
131     // calculation of num_buckets can result in zero buckets, we need at least one
132     return (num_buckets < 1) ? 1 : num_buckets;
133   }
134 
135 public:
136   void dump(SimpleCompactHashtable *cht, const char* table_name);
137 };
138 #endif // INCLUDE_CDS
139 
140 #define REGULAR_BUCKET_TYPE       0
141 #define VALUE_ONLY_BUCKET_TYPE    1
142 #define TABLEEND_BUCKET_TYPE      3
143 #define BUCKET_OFFSET_MASK        0x3FFFFFFF
144 #define BUCKET_OFFSET(info)       ((info) & BUCKET_OFFSET_MASK)
145 #define BUCKET_TYPE_SHIFT         30
146 #define BUCKET_TYPE(info)         (((info) & ~BUCKET_OFFSET_MASK) >> BUCKET_TYPE_SHIFT)
147 #define BUCKET_INFO(offset, type) (((type) << BUCKET_TYPE_SHIFT) | ((offset) & BUCKET_OFFSET_MASK))
148 
149 /////////////////////////////////////////////////////////////////////////////
150 //
151 // CompactHashtable is used to store the CDS archive's symbol/string tables.
152 //
153 // Because these tables are read-only (no entries can be added/deleted) at run-time
154 // and tend to have large number of entries, we try to minimize the footprint
155 // cost per entry.
156 //
157 // The CompactHashtable is split into two arrays
158 //
159 //   u4 buckets[num_buckets+1]; // bit[31,30]: type; bit[29-0]: offset
160 //   u4 entries[<variable size>]
161 //
162 // The size of buckets[] is 'num_buckets + 1'. Each entry of
163 // buckets[] is a 32-bit encoding of the bucket type and bucket offset,
164 // with the type in the left-most 2-bit and offset in the remaining 30-bit.
165 // The last entry is a special type. It contains the end of the last
166 // bucket.
167 //
168 // There are two types of buckets, regular buckets and value_only buckets. The
169 // value_only buckets have '01' in their highest 2-bit, and regular buckets have
170 // '00' in their highest 2-bit.
171 //
172 // For normal buckets, each entry is 8 bytes in the entries[]:
173 //   u4 hash;    /* symbol/string hash */
174 //   union {
175 //     u4 offset;  /* Symbol* sym = (Symbol*)(base_address + offset) */
176 //     narrowOop str; /* String narrowOop encoding */
177 //   }
178 //
179 //
180 // For value_only buckets, each entry has only the 4-byte 'offset' in the entries[].
181 //
182 // Example -- note that the second bucket is a VALUE_ONLY_BUCKET_TYPE so the hash code
183 //            is skipped.
184 // buckets[0, 4, 5, ....]
185 //         |  |  |
186 //         |  |  +---+
187 //         |  |      |
188 //         |  +----+ |
189 //         v       v v
190 // entries[H,O,H,O,O,H,O,H,O.....]
191 //
192 // See CompactHashtable::lookup() for how the table is searched at runtime.
193 // See CompactHashtableWriter::dump() for how the table is written at CDS
194 // dump time.
195 //
196 class SimpleCompactHashtable {
197 protected:
198   address  _base_address;
199   u4  _bucket_count;
200   u4  _entry_count;
201   u4* _buckets;
202   u4* _entries;
203 
204 public:
205   SimpleCompactHashtable() :
206     _base_address(nullptr),
207     _bucket_count(0),
208     _entry_count(0),
209     _buckets(nullptr),
210     _entries(nullptr)
211   {}
212 
213   void reset() {
214     _base_address = nullptr;
215     _bucket_count = 0;
216     _entry_count = 0;
217     _buckets = nullptr;
218     _entries = nullptr;
219   }
220 
221   void init(address base_address, u4 entry_count, u4 bucket_count, u4* buckets, u4* entries);
222 
223   // Read/Write the table's header from/to the CDS archive
224   void serialize_header(SerializeClosure* soc) NOT_CDS_RETURN;
225 
226   inline bool empty() const {
227     return (_entry_count == 0);
228   }
229 
230   inline size_t entry_count() const {
231     return _entry_count;
232   }
233 
234   static size_t calculate_header_size();
235 };
236 
237 template <
238   typename K,
239   typename V,
240   V (*DECODE)(address base_address, u4 offset),
241   bool (*EQUALS)(V value, K key, int len)
242   >
243 class CompactHashtable : public SimpleCompactHashtable {
244 
245   V decode(u4 offset) const {
246     return DECODE(_base_address, offset);
247   }
248 
249 public:
250   // Lookup a value V from the compact table using key K
251   inline V lookup(K key, unsigned int hash, int len) const {
252     if (_entry_count > 0) {
253       int index = hash % _bucket_count;
254       u4 bucket_info = _buckets[index];
255       u4 bucket_offset = BUCKET_OFFSET(bucket_info);
256       int bucket_type = BUCKET_TYPE(bucket_info);
257       u4* entry = _entries + bucket_offset;
258 
259       if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
260         V value = decode(entry[0]);
261         if (EQUALS(value, key, len)) {
262           return value;
263         }
264       } else {
265         // This is a regular bucket, which has more than one
266         // entries. Each entry is a pair of entry (hash, offset).
267         // Seek until the end of the bucket.
268         u4* entry_max = _entries + BUCKET_OFFSET(_buckets[index + 1]);
269         while (entry < entry_max) {
270           unsigned int h = (unsigned int)(entry[0]);
271           if (h == hash) {
272             V value = decode(entry[1]);
273             if (EQUALS(value, key, len)) {
274               return value;
275             }
276           }
277           entry += 2;
278         }
279       }
280     }
281     return nullptr;
282   }
283 
284   template <class ITER>
285   inline void iterate(ITER* iter) const {
286     for (u4 i = 0; i < _bucket_count; i++) {
287       u4 bucket_info = _buckets[i];
288       u4 bucket_offset = BUCKET_OFFSET(bucket_info);
289       int bucket_type = BUCKET_TYPE(bucket_info);
290       u4* entry = _entries + bucket_offset;
291 
292       if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
293         iter->do_value(decode(entry[0]));
294       } else {
295         u4*entry_max = _entries + BUCKET_OFFSET(_buckets[i + 1]);
296         while (entry < entry_max) {
297           iter->do_value(decode(entry[1]));
298           entry += 2;
299         }
300       }
301     }
302   }
303 
304   void print_table_statistics(outputStream* st, const char* name) {
305     st->print_cr("%s statistics:", name);
306     int total_entries = 0;
307     int max_bucket = 0;
308     for (u4 i = 0; i < _bucket_count; i++) {
309       u4 bucket_info = _buckets[i];
310       int bucket_type = BUCKET_TYPE(bucket_info);
311       int bucket_size;
312 
313       if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
314         bucket_size = 1;
315       } else {
316         bucket_size = (BUCKET_OFFSET(_buckets[i + 1]) - BUCKET_OFFSET(bucket_info)) / 2;
317       }
318       total_entries += bucket_size;
319       if (max_bucket < bucket_size) {
320         max_bucket = bucket_size;
321       }
322     }
323     st->print_cr("Number of buckets       : %9d", _bucket_count);
324     st->print_cr("Number of entries       : %9d", total_entries);
325     st->print_cr("Maximum bucket size     : %9d", max_bucket);
326   }
327 };
328 
329 ////////////////////////////////////////////////////////////////////////
330 //
331 // OffsetCompactHashtable -- This is used to store many types of objects
332 // in the CDS archive. On 64-bit platforms, we save space by using a 32-bit
333 // offset from the CDS base address.
334 
335 template <typename V>
336 inline V read_value_from_compact_hashtable(address base_address, u4 offset) {
337   return (V)(base_address + offset);
338 }
339 
340 template <
341   typename K,
342   typename V,
343   bool (*EQUALS)(V value, K key, int len)
344   >
345 class OffsetCompactHashtable : public CompactHashtable<
346     K, V, read_value_from_compact_hashtable<V>, EQUALS> {
347 };
348 
349 
350 ////////////////////////////////////////////////////////////////////////
351 //
352 // Read/Write the contents of a hashtable textual dump (created by
353 // SymbolTable::dump and StringTable::dump).
354 // Because the dump file may be big (hundred of MB in extreme cases),
355 // we use mmap for fast access when reading it.
356 //
357 class HashtableTextDump {
358   int _fd;
359   const char* _base;
360   const char* _p;
361   const char* _end;
362   const char* _filename;
363   size_t      _size;
364   int         _prefix_type;
365   int         _line_no;
366 public:
367   HashtableTextDump(const char* filename);
368   ~HashtableTextDump();
369 
370   enum {
371     SymbolPrefix = 1 << 0,
372     StringPrefix = 1 << 1,
373     Unknown = 1 << 2
374   };
375 
376   void quit(const char* err, const char* msg);
377 
378   inline int remain() {
379     return (int)(_end - _p);
380   }
381   int last_line_no() {
382     return _line_no - 1;
383   }
384 
385   void corrupted(const char *p, const char *msg);
386 
387   inline void corrupted_if(bool cond, const char *msg) {
388     if (cond) {
389       corrupted(_p, msg);
390     }
391   }
392 
393   bool skip_newline();
394   int skip(char must_be_char);
395   void skip_past(char c);
396   void check_version(const char* ver);
397 
398   inline void get_num(char delim, int *num) {
399     const char* p   = _p;
400     const char* end = _end;
401     u8 n = 0;
402 
403     while (p < end) {
404       char c = *p++;
405       if ('0' <= c && c <= '9') {
406         n = n * 10 + (c - '0');
407         if (n > (u8)INT_MAX) {
408           corrupted(_p, "Num overflow");
409         }
410       } else if (c == delim) {
411         _p = p;
412         *num = (int)n;
413         return;
414       } else {
415         // Not [0-9], not 'delim'
416         corrupted(_p, "Unrecognized format");;
417       }
418     }
419 
420     corrupted(_end, "Incorrect format");
421     ShouldNotReachHere();
422   }
423 
424   void scan_prefix_type();
425   int scan_prefix(int* utf8_length);
426   int scan_string_prefix();
427   int scan_symbol_prefix();
428 
429   int unescape(const char* from, const char* end, int count);
430   void get_utf8(char* utf8_buffer, int utf8_length);
431   static void put_utf8(outputStream* st, const char* utf8_string, size_t utf8_length);
432 };
433 
434 #endif // SHARE_CLASSFILE_COMPACTHASHTABLE_HPP