1 /*
  2  * Copyright (c) 1997, 2023, 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   static size_t estimate_size(int num_entries);
139 };
140 #endif // INCLUDE_CDS
141 
142 #define REGULAR_BUCKET_TYPE       0
143 #define VALUE_ONLY_BUCKET_TYPE    1
144 #define TABLEEND_BUCKET_TYPE      3
145 #define BUCKET_OFFSET_MASK        0x3FFFFFFF
146 #define BUCKET_OFFSET(info)       ((info) & BUCKET_OFFSET_MASK)
147 #define BUCKET_TYPE_SHIFT         30
148 #define BUCKET_TYPE(info)         (((info) & ~BUCKET_OFFSET_MASK) >> BUCKET_TYPE_SHIFT)
149 #define BUCKET_INFO(offset, type) (((type) << BUCKET_TYPE_SHIFT) | ((offset) & BUCKET_OFFSET_MASK))
150 
151 /////////////////////////////////////////////////////////////////////////////
152 //
153 // CompactHashtable is used to store the CDS archive's symbol/string tables.
154 //
155 // Because these tables are read-only (no entries can be added/deleted) at run-time
156 // and tend to have large number of entries, we try to minimize the footprint
157 // cost per entry.
158 //
159 // The CompactHashtable is split into two arrays
160 //
161 //   u4 buckets[num_buckets+1]; // bit[31,30]: type; bit[29-0]: offset
162 //   u4 entries[<variable size>]
163 //
164 // The size of buckets[] is 'num_buckets + 1'. Each entry of
165 // buckets[] is a 32-bit encoding of the bucket type and bucket offset,
166 // with the type in the left-most 2-bit and offset in the remaining 30-bit.
167 // The last entry is a special type. It contains the end of the last
168 // bucket.
169 //
170 // There are two types of buckets, regular buckets and value_only buckets. The
171 // value_only buckets have '01' in their highest 2-bit, and regular buckets have
172 // '00' in their highest 2-bit.
173 //
174 // For normal buckets, each entry is 8 bytes in the entries[]:
175 //   u4 hash;    /* symbol/string hash */
176 //   union {
177 //     u4 offset;  /* Symbol* sym = (Symbol*)(base_address + offset) */
178 //     narrowOop str; /* String narrowOop encoding */
179 //   }
180 //
181 //
182 // For value_only buckets, each entry has only the 4-byte 'offset' in the entries[].
183 //
184 // Example -- note that the second bucket is a VALUE_ONLY_BUCKET_TYPE so the hash code
185 //            is skipped.
186 // buckets[0, 4, 5, ....]
187 //         |  |  |
188 //         |  |  +---+
189 //         |  |      |
190 //         |  +----+ |
191 //         v       v v
192 // entries[H,O,H,O,O,H,O,H,O.....]
193 //
194 // See CompactHashtable::lookup() for how the table is searched at runtime.
195 // See CompactHashtableWriter::dump() for how the table is written at CDS
196 // dump time.
197 //
198 class SimpleCompactHashtable {
199 protected:
200   address  _base_address;
201   u4  _bucket_count;
202   u4  _entry_count;
203   u4* _buckets;
204   u4* _entries;
205 
206 public:
207   SimpleCompactHashtable() {
208     _entry_count = 0;
209     _bucket_count = 0;
210     _buckets = 0;
211     _entries = 0;
212   }
213 
214   void reset() {
215     _bucket_count = 0;
216     _entry_count = 0;
217     _buckets = 0;
218     _entries = 0;
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   friend class VMStructs;
245 
246   V decode(u4 offset) const {
247     return DECODE(_base_address, offset);
248   }
249 
250 public:
251   // Lookup a value V from the compact table using key K
252   inline V lookup(K key, unsigned int hash, int len) const {
253     if (_entry_count > 0) {
254       int index = hash % _bucket_count;
255       u4 bucket_info = _buckets[index];
256       u4 bucket_offset = BUCKET_OFFSET(bucket_info);
257       int bucket_type = BUCKET_TYPE(bucket_info);
258       u4* entry = _entries + bucket_offset;
259 
260       if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
261         V value = decode(entry[0]);
262         if (EQUALS(value, key, len)) {
263           return value;
264         }
265       } else {
266         // This is a regular bucket, which has more than one
267         // entries. Each entry is a pair of entry (hash, offset).
268         // Seek until the end of the bucket.
269         u4* entry_max = _entries + BUCKET_OFFSET(_buckets[index + 1]);
270         while (entry < entry_max) {
271           unsigned int h = (unsigned int)(entry[0]);
272           if (h == hash) {
273             V value = decode(entry[1]);
274             if (EQUALS(value, key, len)) {
275               return value;
276             }
277           }
278           entry += 2;
279         }
280       }
281     }
282     return nullptr;
283   }
284 
285   template <class ITER>
286   inline void iterate(ITER* iter) const {
287     for (u4 i = 0; i < _bucket_count; i++) {
288       u4 bucket_info = _buckets[i];
289       u4 bucket_offset = BUCKET_OFFSET(bucket_info);
290       int bucket_type = BUCKET_TYPE(bucket_info);
291       u4* entry = _entries + bucket_offset;
292 
293       if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
294         iter->do_value(decode(entry[0]));
295       } else {
296         u4*entry_max = _entries + BUCKET_OFFSET(_buckets[i + 1]);
297         while (entry < entry_max) {
298           iter->do_value(decode(entry[1]));
299           entry += 2;
300         }
301       }
302     }
303   }
304 
305   void print_table_statistics(outputStream* st, const char* name) {
306     st->print_cr("%s statistics:", name);
307     int total_entries = 0;
308     int max_bucket = 0;
309     for (u4 i = 0; i < _bucket_count; i++) {
310       u4 bucket_info = _buckets[i];
311       int bucket_type = BUCKET_TYPE(bucket_info);
312       int bucket_size;
313 
314       if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
315         bucket_size = 1;
316       } else {
317         bucket_size = (BUCKET_OFFSET(_buckets[i + 1]) - BUCKET_OFFSET(bucket_info)) / 2;
318       }
319       total_entries += bucket_size;
320       if (max_bucket < bucket_size) {
321         max_bucket = bucket_size;
322       }
323     }
324     st->print_cr("Number of buckets       : %9d", _bucket_count);
325     st->print_cr("Number of entries       : %9d", total_entries);
326     st->print_cr("Maximum bucket size     : %9d", max_bucket);
327   }
328 };
329 
330 ////////////////////////////////////////////////////////////////////////
331 //
332 // OffsetCompactHashtable -- This is used to store many types of objects
333 // in the CDS archive. On 64-bit platforms, we save space by using a 32-bit
334 // offset from the CDS base address.
335 
336 template <typename V>
337 inline V read_value_from_compact_hashtable(address base_address, u4 offset) {
338   return (V)(base_address + offset);
339 }
340 
341 template <
342   typename K,
343   typename V,
344   bool (*EQUALS)(V value, K key, int len)
345   >
346 class OffsetCompactHashtable : public CompactHashtable<
347     K, V, read_value_from_compact_hashtable<V>, EQUALS> {
348 };
349 
350 
351 ////////////////////////////////////////////////////////////////////////
352 //
353 // Read/Write the contents of a hashtable textual dump (created by
354 // SymbolTable::dump and StringTable::dump).
355 // Because the dump file may be big (hundred of MB in extreme cases),
356 // we use mmap for fast access when reading it.
357 //
358 class HashtableTextDump {
359   int _fd;
360   const char* _base;
361   const char* _p;
362   const char* _end;
363   const char* _filename;
364   size_t      _size;
365   int         _prefix_type;
366   int         _line_no;
367 public:
368   HashtableTextDump(const char* filename);
369   ~HashtableTextDump();
370 
371   enum {
372     SymbolPrefix = 1 << 0,
373     StringPrefix = 1 << 1,
374     Unknown = 1 << 2
375   };
376 
377   void quit(const char* err, const char* msg);
378 
379   inline int remain() {
380     return (int)(_end - _p);
381   }
382   int last_line_no() {
383     return _line_no - 1;
384   }
385 
386   void corrupted(const char *p, const char *msg);
387 
388   inline void corrupted_if(bool cond, const char *msg) {
389     if (cond) {
390       corrupted(_p, msg);
391     }
392   }
393 
394   bool skip_newline();
395   int skip(char must_be_char);
396   void skip_past(char c);
397   void check_version(const char* ver);
398 
399   inline void get_num(char delim, int *num) {
400     const char* p   = _p;
401     const char* end = _end;
402     u8 n = 0;
403 
404     while (p < end) {
405       char c = *p++;
406       if ('0' <= c && c <= '9') {
407         n = n * 10 + (c - '0');
408         if (n > (u8)INT_MAX) {
409           corrupted(_p, "Num overflow");
410         }
411       } else if (c == delim) {
412         _p = p;
413         *num = (int)n;
414         return;
415       } else {
416         // Not [0-9], not 'delim'
417         corrupted(_p, "Unrecognized format");;
418       }
419     }
420 
421     corrupted(_end, "Incorrect format");
422     ShouldNotReachHere();
423   }
424 
425   void scan_prefix_type();
426   int scan_prefix(int* utf8_length);
427   int scan_string_prefix();
428   int scan_symbol_prefix();
429 
430   int unescape(const char* from, const char* end, int count);
431   void get_utf8(char* utf8_buffer, int utf8_length);
432   static void put_utf8(outputStream* st, const char* utf8_string, int utf8_length);
433 };
434 
435 #endif // SHARE_CLASSFILE_COMPACTHASHTABLE_HPP