From fb8c54059a356f59735fac15828831238aa8080a Mon Sep 17 00:00:00 2001 From: ronanj Date: Wed, 20 Mar 2024 16:03:47 +0800 Subject: [PATCH 1/2] literal storage using hashmap JerryScript-DCO-1.0-Signed-off-by: Ronan Jezequel ronan.jezequel@gmail.com --- jerry-core/CMakeLists.txt | 2 + jerry-core/config.h | 7 + jerry-core/ecma/base/ecma-literal-storage.c | 133 +++++-- jerry-core/jcontext/jcontext.h | 5 + jerry-core/jmem/jmem-heap.c | 10 + jerry-core/lit/lit-hashmap-internal.h | 80 ++++ jerry-core/lit/lit-hashmap.c | 388 ++++++++++++++++++++ jerry-core/lit/lit-hashmap.h | 89 +++++ tools/build.py | 3 + tools/run-tests.py | 5 + 10 files changed, 695 insertions(+), 27 deletions(-) create mode 100644 jerry-core/lit/lit-hashmap-internal.h create mode 100644 jerry-core/lit/lit-hashmap.c create mode 100644 jerry-core/lit/lit-hashmap.h diff --git a/jerry-core/CMakeLists.txt b/jerry-core/CMakeLists.txt index aa58a3d0fa..105ea84cb5 100644 --- a/jerry-core/CMakeLists.txt +++ b/jerry-core/CMakeLists.txt @@ -40,6 +40,7 @@ set(JERRY_SYSTEM_ALLOCATOR OFF CACHE BOOL "Enable system all set(JERRY_VALGRIND OFF CACHE BOOL "Enable Valgrind support?") set(JERRY_VM_HALT OFF CACHE BOOL "Enable VM execution stop callback?") set(JERRY_VM_THROW OFF CACHE BOOL "Enable VM throw callback?") +set(JERRY_LIT_HASHMAP OFF CACHE BOOL "Enable literal hashmap storage?") set(JERRY_GLOBAL_HEAP_SIZE "(512)" CACHE STRING "Size of memory heap, in kilobytes") set(JERRY_GC_LIMIT "(0)" CACHE STRING "Heap usage limit to trigger garbage collection") set(JERRY_STACK_LIMIT "(0)" CACHE STRING "Maximum stack usage size, in kilobytes") @@ -304,6 +305,7 @@ set(SOURCE_CORE_FILES lit/lit-char-helpers.c lit/lit-magic-strings.c lit/lit-strings.c + lit/lit-hashmap.c parser/js/byte-code.c parser/js/common.c parser/js/js-lexer.c diff --git a/jerry-core/config.h b/jerry-core/config.h index 002f49e3ca..55eb144baf 100644 --- a/jerry-core/config.h +++ b/jerry-core/config.h @@ -466,6 +466,13 @@ * Advanced section configurations. */ +/** + * The JERRY_LIT_HASHMAP uses an hashmap for faster literal storage. + */ +#ifndef JERRY_LIT_HASHMAP +#define JERRY_LIT_HASHMAP 0 +#endif /* !defined (JERRY_LIT_HASHMAP) */ + /** * Allow configuring attributes on a few constant data inside the engine. * diff --git a/jerry-core/ecma/base/ecma-literal-storage.c b/jerry-core/ecma/base/ecma-literal-storage.c index db086a51be..edae62d9ca 100644 --- a/jerry-core/ecma/base/ecma-literal-storage.c +++ b/jerry-core/ecma/base/ecma-literal-storage.c @@ -153,23 +153,68 @@ ecma_finalize_lit_storage (void) } /* ecma_finalize_lit_storage */ /** - * Find or create a literal string. + * Create a new literal string slot "pool". * - * @return ecma_string_t compressed pointer + * @return jmem_cpointer_t slot pointer */ -ecma_value_t -ecma_find_or_create_literal_string (const lit_utf8_byte_t *chars_p, /**< string to be searched */ - lit_utf8_size_t size, /**< size of the string */ - bool is_ascii) /**< encode of the string */ + +static jmem_cpointer_t * +ecma_allocate_new_string_slot (void) { - ecma_string_t *string_p = - (is_ascii ? ecma_new_ecma_string_from_ascii (chars_p, size) : ecma_new_ecma_string_from_utf8 (chars_p, size)); + ecma_lit_storage_item_t *new_item_p; + new_item_p = (ecma_lit_storage_item_t *) jmem_pools_alloc (sizeof (ecma_lit_storage_item_t)); - if (ECMA_IS_DIRECT_STRING (string_p)) + for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { - return ecma_make_string_value (string_p); + new_item_p->values[i] = JMEM_CP_NULL; + } + + new_item_p->next_cp = JERRY_CONTEXT (string_list_first_cp); + JMEM_CP_SET_NON_NULL_POINTER (JERRY_CONTEXT (string_list_first_cp), new_item_p); + + return new_item_p->values + 0; +} /* ecma_allocate_new_string_slot */ + +#if JERRY_LIT_HASHMAP +/** + * Find an empty a literal string slot. + * + * @return jmem_cpointer_t slot pointer + */ + +static jmem_cpointer_t * +ecma_find_empty_literal_string_slot (void) +{ + jmem_cpointer_t string_list_cp = JERRY_CONTEXT (string_list_first_cp); + + while (string_list_cp != JMEM_CP_NULL) + { + ecma_lit_storage_item_t *string_list_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_lit_storage_item_t, string_list_cp); + + for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) + { + if (string_list_p->values[i] == JMEM_CP_NULL) + { + return string_list_p->values + i; + } + } + string_list_cp = string_list_p->next_cp; } + return ecma_allocate_new_string_slot (); +} /* ecma_find_empty_literal_string_slot */ +#endif /* JERRY_LIT_HASHMAP */ + +/** + * Find an empty or similar a literal string slot. + * + * @return jmem_cpointer_t slot pointer + */ + +#if !JERRY_LIT_HASHMAP +static jmem_cpointer_t * +ecma_find_empty_or_same_literal_string_slot (ecma_string_t *string_p /**< string to be searched */) +{ jmem_cpointer_t string_list_cp = JERRY_CONTEXT (string_list_first_cp); jmem_cpointer_t *empty_cpointer_p = NULL; @@ -189,12 +234,9 @@ ecma_find_or_create_literal_string (const lit_utf8_byte_t *chars_p, /**< string else { ecma_string_t *value_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, string_list_p->values[i]); - if (ecma_compare_ecma_strings (string_p, value_p)) { - /* Return with string if found in the list. */ - ecma_deref_ecma_string (string_p); - return ecma_make_string_value (value_p); + return string_list_p->values + i; } } } @@ -202,29 +244,66 @@ ecma_find_or_create_literal_string (const lit_utf8_byte_t *chars_p, /**< string string_list_cp = string_list_p->next_cp; } - ECMA_SET_STRING_AS_STATIC (string_p); - jmem_cpointer_t result; - JMEM_CP_SET_NON_NULL_POINTER (result, string_p); - if (empty_cpointer_p != NULL) { - *empty_cpointer_p = result; - return ecma_make_string_value (string_p); + return empty_cpointer_p; } - ecma_lit_storage_item_t *new_item_p; - new_item_p = (ecma_lit_storage_item_t *) jmem_pools_alloc (sizeof (ecma_lit_storage_item_t)); + return ecma_allocate_new_string_slot (); +} /* ecma_find_empty_or_same_literal_string_slot */ +#endif /* !JERRY_LIT_HASHMAP */ - new_item_p->values[0] = result; - for (int i = 1; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) +/** + * Find or create a literal string. + * + * @return ecma_string_t compressed pointer + */ + +ecma_value_t +ecma_find_or_create_literal_string (const lit_utf8_byte_t *chars_p, /**< string to be searched */ + lit_utf8_size_t size, /**< size of the string */ + bool is_ascii) /**< encode of the string */ +{ + ecma_string_t *string_p = + (is_ascii ? ecma_new_ecma_string_from_ascii (chars_p, size) : ecma_new_ecma_string_from_utf8 (chars_p, size)); + + if (ECMA_IS_DIRECT_STRING (string_p)) { - new_item_p->values[i] = JMEM_CP_NULL; + return ecma_make_string_value (string_p); } - new_item_p->next_cp = JERRY_CONTEXT (string_list_first_cp); - JMEM_CP_SET_NON_NULL_POINTER (JERRY_CONTEXT (string_list_first_cp), new_item_p); +#if JERRY_LIT_HASHMAP + const ecma_string_t *hashmap_entry = hashmap_get (&JERRY_CONTEXT (string_hashmap), string_p); + if (hashmap_entry != NULL) + { + ecma_deref_ecma_string (string_p); + return ecma_make_string_value (hashmap_entry); + } + // Since the string is not found, just find an empty slot + jmem_cpointer_t *slot = ecma_find_empty_literal_string_slot (); +#else /* JERRY_LIT_HASHMAP */ + jmem_cpointer_t *slot = ecma_find_empty_or_same_literal_string_slot (string_p); + if (*slot != JMEM_CP_NULL) + { + // The string has been found + ecma_string_t *value_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, *slot); + ecma_deref_ecma_string (string_p); + return ecma_make_string_value (value_p); + } +#endif /* JERRY_LIT_HASHMAP */ + + // String has not been found... + ECMA_SET_STRING_AS_STATIC (string_p); + jmem_cpointer_t result; + JMEM_CP_SET_NON_NULL_POINTER (result, string_p); + *slot = result; + +#if JERRY_LIT_HASHMAP + hashmap_put (&JERRY_CONTEXT (string_hashmap), string_p); +#endif /* JERRY_LIT_HASHMAP */ return ecma_make_string_value (string_p); + } /* ecma_find_or_create_literal_string */ /** diff --git a/jerry-core/jcontext/jcontext.h b/jerry-core/jcontext/jcontext.h index 397863a72f..6ab3af5d60 100644 --- a/jerry-core/jcontext/jcontext.h +++ b/jerry-core/jcontext/jcontext.h @@ -28,6 +28,7 @@ #include "debugger.h" #include "jmem.h" #include "js-parser-internal.h" +#include "lit-hashmap.h" #include "re-bytecode.h" #include "vm-defines.h" @@ -138,6 +139,10 @@ struct jerry_context_t #endif /* JERRY_BUILTIN_BIGINT */ jmem_cpointer_t global_symbols_cp[ECMA_BUILTIN_GLOBAL_SYMBOL_COUNT]; /**< global symbols */ +#if JERRY_LIT_HASHMAP + struct hashmap_s string_hashmap; +#endif /* JERRY_LIT_HASHMAP */ + #if JERRY_MODULE_SYSTEM ecma_module_t *module_current_p; /**< current module context */ jerry_module_state_changed_cb_t module_state_changed_callback_p; /**< callback which is called after the diff --git a/jerry-core/jmem/jmem-heap.c b/jerry-core/jmem/jmem-heap.c index fa83605cff..4b9bc3d6f7 100644 --- a/jerry-core/jmem/jmem-heap.c +++ b/jerry-core/jmem/jmem-heap.c @@ -23,6 +23,7 @@ #include "jmem.h" #include "jrt-bit-fields.h" #include "jrt-libc-includes.h" +#include "lit-hashmap.h" #define JMEM_ALLOCATOR_INTERNAL #include "jmem-allocator-internal.h" @@ -103,6 +104,11 @@ jmem_heap_init (void) JMEM_VALGRIND_NOACCESS_SPACE (JERRY_HEAP_CONTEXT (area), JMEM_HEAP_AREA_SIZE); #endif /* !JERRY_SYSTEM_ALLOCATOR */ + +#if JERRY_LIT_HASHMAP + hashmap_init (&JERRY_CONTEXT (string_hashmap)); +#endif /* JERRY_LIT_HASHMAP */ + JMEM_HEAP_STAT_INIT (); } /* jmem_heap_init */ @@ -112,6 +118,10 @@ jmem_heap_init (void) void jmem_heap_finalize (void) { +#if JERRY_LIT_HASHMAP + hashmap_destroy (&JERRY_CONTEXT (string_hashmap)); +#endif /* JERRY_LIT_HASHMAP */ + JERRY_ASSERT (JERRY_CONTEXT (jmem_heap_allocated_size) == 0); #if !JERRY_SYSTEM_ALLOCATOR JMEM_VALGRIND_NOACCESS_SPACE (&JERRY_HEAP_CONTEXT (first), JMEM_HEAP_SIZE); diff --git a/jerry-core/lit/lit-hashmap-internal.h b/jerry-core/lit/lit-hashmap-internal.h new file mode 100644 index 0000000000..aaf4d14b08 --- /dev/null +++ b/jerry-core/lit/lit-hashmap-internal.h @@ -0,0 +1,80 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + The lit-hashmap library is based on the public domain software + available from https://github.com/sheredom/hashmap.h +*/ + +#ifndef LIT_HASHMAP_INTERNAL_H +#define LIT_HASHMAP_INTERNAL_H + +#include "lit-hashmap.h" + +#define HASHMAP_ALWAYS_INLINE __attribute__ ((always_inline)) inline +#define HASHMAP_LINEAR_PROBE_LENGTH (8) + +/** + * hashmap creation options. + */ + +typedef struct hashmap_create_options_s +{ + hashmap_uint32_t initial_capacity; /**< initial hashmap capacity */ +} hashmap_create_options_t; + +/// @brief Create a hashmap. +/// @param options The options to create the hashmap with. +/// @param out_hashmap The storage for the created hashmap. +/// @return On success 0 is returned. +/// +/// The options members work as follows: +/// - initial_capacity The initial capacity of the hashmap. +/// - hasher Which hashing function to use with the hashmap (by default the +// crc32 with Robert Jenkins' mix is used). +int hashmap_create_ex (struct hashmap_create_options_s options, struct hashmap_s *const out_hashmap); + +/// @brief Iterate over all the elements in a hashmap. +/// @param hashmap The hashmap to iterate over. +/// @param iterator The function pointer to call on each element. +/// @param context The context to pass as the first argument to f. +/// @return If the entire hashmap was iterated then 0 is returned. +/// Otherwise if the callback function f returned positive then the positive +/// value is returned. If the callback function returns -1, the current item +/// is removed and iteration continues. +int hashmap_iterate_pairs (struct hashmap_s *const hashmap, + int (*iterator) (void *const, struct hashmap_element_s *const), + void *const context); + +/// @brief Get the size of the hashmap. +/// @param hashmap The hashmap to get the size of. +/// @return The size of the hashmap. +HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_num_entries (const struct hashmap_s *const hashmap); + +/// @brief Get the capacity of the hashmap. +/// @param hashmap The hashmap to get the size of. +/// @return The capacity of the hashmap. +HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_capacity (const struct hashmap_s *const hashmap); + +HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_hash_helper_int_helper (const struct hashmap_s *const m, + const ecma_string_t *const key); +HASHMAP_ALWAYS_INLINE int hashmap_hash_helper (const struct hashmap_s *const m, + const ecma_string_t *const key, + hashmap_uint32_t *const out_index); +int hashmap_rehash_iterator (void *const new_hash, struct hashmap_element_s *const e); +HASHMAP_ALWAYS_INLINE int hashmap_rehash_helper (struct hashmap_s *const m); +HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_clz (const hashmap_uint32_t x); + +#endif // LIT_HASHMAP_INTERNAL_H diff --git a/jerry-core/lit/lit-hashmap.c b/jerry-core/lit/lit-hashmap.c new file mode 100644 index 0000000000..1befc99dad --- /dev/null +++ b/jerry-core/lit/lit-hashmap.c @@ -0,0 +1,388 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * The lit-hashmap library is based on the public domain software + * available from https://github.com/sheredom/hashmap.h + */ + +#include +#include + +#include "ecma-globals.h" + +#if JERRY_LIT_HASHMAP + +#include "ecma-helpers.h" + +#include "lit-hashmap-internal.h" + +#define HASHMAP_CAST(type, x) ((type) (x)) +#define HASHMAP_PTR_CAST(type, x) ((type) (x)) +#define HASHMAP_NULL 0 + +int +hashmap_create_ex (struct hashmap_create_options_s options, struct hashmap_s *const out_hashmap) +{ + if (2 > options.initial_capacity) + { + options.initial_capacity = 2; + } + else if (0 != (options.initial_capacity & (options.initial_capacity - 1))) + { + options.initial_capacity = 1u << (32 - hashmap_clz (options.initial_capacity)); + } + + out_hashmap->alloc_size = + (options.initial_capacity + HASHMAP_LINEAR_PROBE_LENGTH) * sizeof (struct hashmap_element_s); + out_hashmap->data = HASHMAP_CAST (struct hashmap_element_s *, jmem_heap_alloc_block (out_hashmap->alloc_size)); + memset (out_hashmap->data, 0, out_hashmap->alloc_size); + + out_hashmap->log2_capacity = 31 - hashmap_clz (options.initial_capacity); + out_hashmap->size = 0; + out_hashmap->initialized = 1; + + return 0; +} /* hashmap_create_ex */ + +/** + * hashmap_put + * + */ +int +hashmap_put (struct hashmap_s *const m, const ecma_string_t *const key) +{ + JERRY_ASSERT (m->initialized == 0 || m->initialized == 1); + hashmap_uint32_t index; + + if ((HASHMAP_NULL == key)) + { + return 1; + } + + /* Find a place to put our value. */ + while (!hashmap_hash_helper (m, key, &index)) + { + if (hashmap_rehash_helper (m)) + { + return 1; + } + } + + if (0 == m->data[index].data) + m->size++; + + /* Set the data. */ + m->data[index].data = key; + + return 0; +} /* hashmap_put */ + +/** + * hashmap_init + * + */ +void +hashmap_init (struct hashmap_s *m) +{ + if (m->initialized == 0) + { + m->initialized = 1; + hashmap_create_options_t opts = { .initial_capacity = 32 }; + hashmap_create_ex (opts, m); + } + else if (m->initialized != 1) + { + JERRY_ASSERT (0); + } +} /* hashmap_init */ + +/** + * hashmap_get + * + */ +const ecma_string_t * +hashmap_get (const struct hashmap_s *const m, const ecma_string_t *const key) +{ + JERRY_ASSERT (m->initialized == 0 || m->initialized == 1); + hashmap_uint32_t i, curr; + + if ((HASHMAP_NULL == key)) + { + JERRY_ASSERT (0); + return HASHMAP_NULL; + } + + curr = hashmap_hash_helper_int_helper (m, key); + + /* Linear probing, if necessary */ + for (i = 0; i < HASHMAP_LINEAR_PROBE_LENGTH; i++) + { + const hashmap_uint32_t index = curr + i; + + if (m->data[index].data) + { + if (ecma_compare_ecma_strings (m->data[index].data, key)) + { + JERRY_ASSERT (m->initialized == 0 || m->initialized == 1); + return m->data[index].data; + } + } + } + + JERRY_ASSERT (m->initialized == 0 || m->initialized == 1); + /* Not found */ + return HASHMAP_NULL; +} /* hashmap_get */ + +/** + * hashmap_remove + * + */ +int +hashmap_remove (struct hashmap_s *const m, const ecma_string_t *const key) +{ + hashmap_uint32_t i, curr; + + if ((HASHMAP_NULL == key)) + { + return 1; + } + + curr = hashmap_hash_helper_int_helper (m, key); + + /* Linear probing, if necessary */ + for (i = 0; i < HASHMAP_LINEAR_PROBE_LENGTH; i++) + { + const hashmap_uint32_t index = curr + i; + + if (m->data[index].data) + { + if (ecma_compare_ecma_strings (m->data[index].data, key)) + { + /* Blank out the fields including in_use */ + memset (&m->data[index], 0, sizeof (struct hashmap_element_s)); + + /* Reduce the size */ + m->size--; + + return 0; + } + } + } + + return 1; +} /* hashmap_remove */ + +/** + * hashmap_iterate_pairs + * + */ +int +hashmap_iterate_pairs (struct hashmap_s *const m, + int (*f) (void *const, struct hashmap_element_s *const), + void *const context) +{ + hashmap_uint32_t i; + int r; + + for (i = 0; i < (hashmap_capacity (m) + HASHMAP_LINEAR_PROBE_LENGTH); i++) + { + struct hashmap_element_s *p; + p = &m->data[i]; + if (p->data) + { + r = f (context, p); + switch (r) + { + case -1: /* remove item */ + memset (p, 0, sizeof (struct hashmap_element_s)); + m->size--; + break; + case 0: /* continue iterating */ + break; + default: /* early exit */ + return 1; + } + } + } + return 0; +} /* hashmap_iterate_pairs */ + +/** + * hashmap_destroy + * + */ +void +hashmap_destroy (struct hashmap_s *const m) +{ + if (m->data) + jmem_heap_free_block (m->data, m->alloc_size); + memset (m, 0, sizeof (struct hashmap_s)); +} /* hashmap_destroy */ + +/** + * hashmap_num_entries + * + */ +HASHMAP_ALWAYS_INLINE hashmap_uint32_t +hashmap_num_entries (const struct hashmap_s *const m) +{ + return m->size; +} /* hashmap_num_entries */ + +/** + * hashmap_capacity + * + */ +HASHMAP_ALWAYS_INLINE hashmap_uint32_t +hashmap_capacity (const struct hashmap_s *const m) +{ + return 1u << m->log2_capacity; +} /* hashmap_capacity */ + +/** + * hashmap_hash_helper_int_helper + * + */ +HASHMAP_ALWAYS_INLINE hashmap_uint32_t +hashmap_hash_helper_int_helper (const struct hashmap_s *const m, const ecma_string_t *const k) +{ + return (k->u.hash * 2654435769u) >> (32u - m->log2_capacity); +} /* hashmap_hash_helper_int_helper */ + +/** + * hashmap_hash_helper + * + */ +HASHMAP_ALWAYS_INLINE int +hashmap_hash_helper (const struct hashmap_s *const m, const ecma_string_t *const key, hashmap_uint32_t *const out_index) +{ + hashmap_uint32_t curr; + hashmap_uint32_t i; + hashmap_uint32_t first_free; + + /* If full, return immediately */ + if (hashmap_num_entries (m) == hashmap_capacity (m)) + { + return 0; + } + + /* Find the best index */ + curr = hashmap_hash_helper_int_helper (m, key); + first_free = ~0u; + + for (i = 0; i < HASHMAP_LINEAR_PROBE_LENGTH; i++) + { + const hashmap_uint32_t index = curr + i; + + if (!m->data[index].data) + { + first_free = (first_free < index) ? first_free : index; + } + else if (ecma_compare_ecma_strings (m->data[index].data, key)) + { + *out_index = index; + return 1; + } + } + + // Couldn't find a free element in the linear probe. + if (~0u == first_free) + { + return 0; + } + + *out_index = first_free; + return 1; +} /* hashmap_hash_helper */ + +/** + * hashmap_rehash_iterator + * + */ +int +hashmap_rehash_iterator (void *const new_hash, struct hashmap_element_s *const e) +{ + int temp = hashmap_put (HASHMAP_PTR_CAST (struct hashmap_s *, new_hash), e->data); + + if (0 < temp) + { + return 1; + } + + /* clear old value to avoid stale pointers */ + return -1; +} /* hashmap_rehash_iterator */ + +/** + * hashmap_rehash_helper + * + * Doubles the size of the hashmap, and rehashes all the elements + */ +HASHMAP_ALWAYS_INLINE int +hashmap_rehash_helper (struct hashmap_s *const m) +{ + struct hashmap_create_options_s options; + struct hashmap_s new_m; + int flag; + + memset (&options, 0, sizeof (options)); + options.initial_capacity = hashmap_capacity (m) * 2; + + if (0 == options.initial_capacity) + { + return 1; + } + + flag = hashmap_create_ex (options, &new_m); + + if (0 != flag) + { + JERRY_ASSERT (0); + return flag; + } + + /* copy the old elements to the new table */ + flag = hashmap_iterate_pairs (m, hashmap_rehash_iterator, HASHMAP_PTR_CAST (void *, &new_m)); + + if (0 != flag) + { + return flag; + } + + hashmap_destroy (m); + + /* put new hash into old hash structure by copying */ + memcpy (m, &new_m, sizeof (struct hashmap_s)); + + return 0; +} /* hashmap_rehash_helper */ + +/** + * hashmap_clz + * + */ +HASHMAP_ALWAYS_INLINE hashmap_uint32_t +hashmap_clz (const hashmap_uint32_t x) +{ +#if defined(_MSC_VER) + unsigned long result; + _BitScanReverse (&result, x); + return 31 - HASHMAP_CAST (hashmap_uint32_t, result); +#else /* _MSC_VER */ + return HASHMAP_CAST (hashmap_uint32_t, __builtin_clz (x)); +#endif /* _MSC_VER */ +} /* hashmap_clz */ + +#endif /* JERRY_LIT_HASHMAP */ diff --git a/jerry-core/lit/lit-hashmap.h b/jerry-core/lit/lit-hashmap.h new file mode 100644 index 0000000000..c03370be78 --- /dev/null +++ b/jerry-core/lit/lit-hashmap.h @@ -0,0 +1,89 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + The lit-hashmap library is based on the public domain software + available from https://github.com/sheredom/hashmap.h +*/ + +#ifndef LIT_HASHMAP_H +#define LIT_HASHMAP_H + +#include +#include +#include + +#include "ecma-globals.h" +typedef uint8_t hashmap_uint8_t; +typedef uint32_t hashmap_uint32_t; +typedef uint64_t hashmap_uint64_t; + +/** + * hashmap element + */ + +typedef struct hashmap_element_s +{ + const ecma_string_t *data; /**< point to a literal */ +} hashmap_element_t; + +/** + * hashmap structure + */ + +typedef struct hashmap_s +{ + hashmap_uint32_t log2_capacity; /**< hashmap capacity */ + hashmap_uint32_t size; /**< hashmap size*/ + struct hashmap_element_s *data; /**< element array */ + size_t alloc_size; /**< allocated size */ + uint8_t initialized; /**< 0 if not initialized */ +} hashmap_t; + +/// @brief Initialize the hashmap. +/// @param hashmap The hashmap to initialize. +void hashmap_init (struct hashmap_s *m); + +/// @brief Put an element into the hashmap. +/// @param hashmap The hashmap to insert into. +/// @param key The string key to use. +/// @param len The length of the string key. +/// @param value The value to insert. +/// @return On success 0 is returned. +/// +/// The key string slice is not copied when creating the hashmap entry, and thus +/// must remain a valid pointer until the hashmap entry is removed or the +/// hashmap is destroyed. +int hashmap_put (struct hashmap_s *const hashmap, const ecma_string_t *const key); + +/// @brief Get an element from the hashmap. +/// @param hashmap The hashmap to get from. +/// @param key The string key to use. +/// @param len The length of the string key. +/// @return The previously set element, or NULL if none exists. +const ecma_string_t *hashmap_get (const struct hashmap_s *const hashmap, const ecma_string_t *const key); + +/// @brief Remove an element from the hashmap. +/// @param hashmap The hashmap to remove from. +/// @param key The string key to use. +/// @param len The length of the string key. +/// @return On success 0 is returned. +int hashmap_remove (struct hashmap_s *const hashmap, const ecma_string_t *const key); + +/// @brief Destroy the hashmap. +/// @param hashmap The hashmap to destroy. +void hashmap_destroy (struct hashmap_s *const hashmap); + +#endif // LIT_HASHMAP_H diff --git a/tools/build.py b/tools/build.py index 137c32b48f..7245ac8e98 100755 --- a/tools/build.py +++ b/tools/build.py @@ -158,6 +158,8 @@ def devhelp(helpstring): help='enable VM execution stop callback (%(choices)s)') coregrp.add_argument('--vm-throw', metavar='X', choices=['ON', 'OFF'], type=str.upper, help='enable VM throw callback (%(choices)s)') + coregrp.add_argument('--lit-hashmap', metavar='X', choices=['ON', 'OFF'], type=str.upper, + help='Enable literal hashmap storage (%(choices)s)') maingrp = parser.add_argument_group('jerry-main options') maingrp.add_argument('--link-map', metavar='X', choices=['ON', 'OFF'], type=str.upper, @@ -227,6 +229,7 @@ def build_options_append(cmakeopt, cliarg): build_options_append('JERRY_VALGRIND', arguments.valgrind) build_options_append('JERRY_VM_HALT', arguments.vm_exec_stop) build_options_append('JERRY_VM_THROW', arguments.vm_throw) + build_options_append('JERRY_LIT_HASHMAP', arguments.lit_hashmap) if arguments.gc_mark_limit is not None: build_options.append(f'-DJERRY_GC_MARK_LIMIT={arguments.gc_mark_limit}') diff --git a/tools/run-tests.py b/tools/run-tests.py index 64bc1ed827..f1a76fae47 100755 --- a/tools/run-tests.py +++ b/tools/run-tests.py @@ -77,6 +77,9 @@ def skip_if(condition, desc): Options('jerry_tests-external_context', OPTIONS_COMMON + OPTIONS_STACK_LIMIT + OPTIONS_GC_MARK_LIMIT + ['--external-context=on']), + Options('jerry_tests-lit-hashmap', + OPTIONS_COMMON + OPTIONS_STACK_LIMIT + OPTIONS_GC_MARK_LIMIT + + ['--lit-hashmap=on']), ] # Test options for test262 @@ -139,6 +142,8 @@ def skip_if(condition, desc): ['--compile-flag=-DJERRY_MODULE_SYSTEM=0', '--lto=off']), Options('buildoption_test-builtin-proxy-off', ['--compile-flag=-DJERRY_BUILTIN_PROXY=0']), + Options('buildoption_test-literal-hashmap-on', + ['--compile-flag=-DJERRY_LIT_HASHMAP=1']), ] def get_arguments(): From a5390506715dbf746924e971b4bb3d3180bdbaa4 Mon Sep 17 00:00:00 2001 From: ronan Date: Mon, 29 Apr 2024 16:44:38 +0800 Subject: [PATCH 2/2] literal storage using hashmap: new hashmap implementation JerryScript-DCO-1.0-Signed-off-by: Ronan Jezequel ronan.jezequel@gmail.com --- jerry-core/CMakeLists.txt | 2 + jerry-core/ecma/base/ecma-literal-storage.c | 4 +- jerry-core/jcontext/jcontext.h | 2 +- jerry-core/lit/lit-hashmap-impl.h | 202 +++++++++++ jerry-core/lit/lit-hashmap-internal.h | 80 ----- jerry-core/lit/lit-hashmap.c | 368 ++------------------ jerry-core/lit/lit-hashmap.h | 63 +--- 7 files changed, 241 insertions(+), 480 deletions(-) create mode 100644 jerry-core/lit/lit-hashmap-impl.h delete mode 100644 jerry-core/lit/lit-hashmap-internal.h diff --git a/jerry-core/CMakeLists.txt b/jerry-core/CMakeLists.txt index 105ea84cb5..d1ecac54da 100644 --- a/jerry-core/CMakeLists.txt +++ b/jerry-core/CMakeLists.txt @@ -500,6 +500,8 @@ if(ENABLE_AMALGAM) lit/lit-unicode-folding.inc.h lit/lit-unicode-ranges-sup.inc.h lit/lit-unicode-ranges.inc.h + lit/lit-hashmap-impl.h + lit/lit-hashmap.h vm/opcodes.h vm/vm-defines.h vm/vm-stack.h diff --git a/jerry-core/ecma/base/ecma-literal-storage.c b/jerry-core/ecma/base/ecma-literal-storage.c index edae62d9ca..6f845b2219 100644 --- a/jerry-core/ecma/base/ecma-literal-storage.c +++ b/jerry-core/ecma/base/ecma-literal-storage.c @@ -273,7 +273,7 @@ ecma_find_or_create_literal_string (const lit_utf8_byte_t *chars_p, /**< string } #if JERRY_LIT_HASHMAP - const ecma_string_t *hashmap_entry = hashmap_get (&JERRY_CONTEXT (string_hashmap), string_p); + const ecma_string_t *hashmap_entry = hashmap_get (JERRY_CONTEXT (string_hashmap), string_p); if (hashmap_entry != NULL) { ecma_deref_ecma_string (string_p); @@ -299,7 +299,7 @@ ecma_find_or_create_literal_string (const lit_utf8_byte_t *chars_p, /**< string *slot = result; #if JERRY_LIT_HASHMAP - hashmap_put (&JERRY_CONTEXT (string_hashmap), string_p); + hashmap_put (JERRY_CONTEXT (string_hashmap), string_p); #endif /* JERRY_LIT_HASHMAP */ return ecma_make_string_value (string_p); diff --git a/jerry-core/jcontext/jcontext.h b/jerry-core/jcontext/jcontext.h index 6ab3af5d60..063a331bc1 100644 --- a/jerry-core/jcontext/jcontext.h +++ b/jerry-core/jcontext/jcontext.h @@ -140,7 +140,7 @@ struct jerry_context_t jmem_cpointer_t global_symbols_cp[ECMA_BUILTIN_GLOBAL_SYMBOL_COUNT]; /**< global symbols */ #if JERRY_LIT_HASHMAP - struct hashmap_s string_hashmap; + hashmap_t string_hashmap; #endif /* JERRY_LIT_HASHMAP */ #if JERRY_MODULE_SYSTEM diff --git a/jerry-core/lit/lit-hashmap-impl.h b/jerry-core/lit/lit-hashmap-impl.h new file mode 100644 index 0000000000..ddfc7cce94 --- /dev/null +++ b/jerry-core/lit/lit-hashmap-impl.h @@ -0,0 +1,202 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * The lit-hashmap library is based on the public domain software + * available from https://github.com/sheredom/hashmap.h + */ + +#include +#include + +#include "ecma-globals.h" +#include "ecma-helpers.h" + +/* ------------------------------------------------------------------------------------------------ */ +// Configuration + +#define HASHMAP_LINEAR_PROBE_LENGTH ((size_t) 8) + +/* ------------------------------------------------------------------------------------------------ */ + +#define HASHMAP_CAST(type, x) ((type) (x)) +#define HASHMAP_PTR_CAST(type, x) ((type) (x)) +#define HASHMAP_NULL 0 + +#define HASHMAP_IMPL_INLINE __attribute__ ((always_inline)) inline + +/* ------------------------------------------------------------------------------------------------ */ + +/** + * HashMap object, internal representation + */ +typedef struct +{ + size_t log2_capacity; /**< hashmap capacity */ + uint32_t used_entries; /**< hashmap size*/ + size_t array_size; /**< allocated size */ + const ecma_string_t **literals; /**< element array */ +} hashmap_impl; + +/** + * HashMap iterator object, internal representation + */ +typedef struct +{ + hashmap_impl *hmap; /**< pointer to the hashmap */ + uint32_t index; /**< iterator start index */ + uint32_t slot; /**< iterator slot, from 0 to HASHMAP_LINEAR_PROBE_LENGTH */ + lit_string_hash_t key; /**< key beeing looked for */ +} hashmap_impl_iter; + +/* ------------------------------------------------------------------------------------------------ */ + +void hashmap_impl_rehash (hashmap_impl *const hashmap); + +/* ------------------------------------------------------------------------------------------------ */ + +HASHMAP_IMPL_INLINE size_t +hashmap_impl_log2 (size_t v) +{ + int n = 31 - __builtin_clz ((uint32_t) v); + return (size_t) n; +} /* hashmap_round_log2 */ + +HASHMAP_IMPL_INLINE uint32_t +hashmap_impl_lithash_to_index (const hashmap_impl *const m, lit_string_hash_t hash) +{ + uint32_t v = (hash * 2654435769u) >> (32u - m->log2_capacity); + return v; +} /* hashmap_hash_to_index */ + +/* ------------------------------------------------------------------------------------------------ */ + +HASHMAP_IMPL_INLINE void +hashmap_impl_init (size_t initial_capacity, hashmap_impl *hashmap) +{ + initial_capacity = ((size_t) 1) << hashmap_impl_log2 (initial_capacity); + size_t array_size = (initial_capacity + HASHMAP_LINEAR_PROBE_LENGTH); + + hashmap->literals = jmem_heap_alloc_block (array_size * sizeof (const ecma_string_t *)); + memset (hashmap->literals, 0, array_size * sizeof (const ecma_string_t *)); + hashmap->log2_capacity = hashmap_impl_log2 (initial_capacity); + hashmap->used_entries = 0; +} /* hashmap_new */ + +/* ------------------------------------------------------------------------------------------------ */ + +HASHMAP_IMPL_INLINE int +hashmap_impl_find_empty_slot (const hashmap_impl *const hashmap, const lit_string_hash_t key, uint32_t *const out_index) +{ + uint32_t start_index = hashmap_impl_lithash_to_index (hashmap, key); + + for (uint32_t offset = 0; offset < HASHMAP_LINEAR_PROBE_LENGTH; offset++) + { + const uint32_t index = start_index + offset; + + if (hashmap->literals[index] == NULL) + { + *out_index = index; + return 1; + } + } + + return 0; +} /* hashmap_impl_find_empty_slot */ + +/* ------------------------------------------------------------------------------------------------ */ + +HASHMAP_IMPL_INLINE void +hashmap_impl_insert (hashmap_impl *hashmap, const ecma_string_t *literal) +{ + /* Find a place to put our value. */ + uint32_t index; + uint32_t lithash = literal->u.hash; + while (!hashmap_impl_find_empty_slot (hashmap, lithash, &index)) + { + hashmap_impl_rehash (hashmap); + } + + hashmap->literals[index] = literal; + hashmap->used_entries++; +} /* hashmap_impl_insert */ + +/* ------------------------------------------------------------------------------------------------ */ + +HASHMAP_IMPL_INLINE hashmap_impl_iter +hashmap_impl_find (hashmap_impl *hashmap, lit_string_hash_t key) +{ + uint32_t start_index = hashmap_impl_lithash_to_index (hashmap, key); + return (hashmap_impl_iter){ + .hmap = hashmap, + .index = start_index, + .slot = 0, + .key = key, + }; +} /* hashmap_impl_find */ + +HASHMAP_IMPL_INLINE const ecma_string_t * +hashmap_impl_iter_get (hashmap_impl_iter *iter) +{ + const ecma_string_t **literals = iter->hmap->literals; + while (iter->slot < HASHMAP_LINEAR_PROBE_LENGTH) + { + uint32_t index = iter->index + iter->slot; + const ecma_string_t *literal = literals[index]; + if (literal && literal->u.hash == iter->key) + { + return literals[index]; + } + + iter->slot++; + } + return NULL; +} /* hashmap_impl_iter_get */ + +HASHMAP_IMPL_INLINE const ecma_string_t * +hashmap_impl_iter_next (hashmap_impl_iter *iter) +{ + iter->slot++; + return hashmap_impl_iter_get (iter); +} /* hashmap_impl_iter_next */ + +HASHMAP_IMPL_INLINE void +hashmap_impl_destroy (hashmap_impl *hashmap) +{ + size_t l = ((size_t) (1 << hashmap->log2_capacity)) + HASHMAP_LINEAR_PROBE_LENGTH; + jmem_heap_free_block (hashmap->literals, l * sizeof (const ecma_string_t *)); +} /* hashmap_impl_destroy */ + +/* ------------------------------------------------------------------------------------------------ */ + +void +hashmap_impl_rehash (hashmap_impl *const hashmap) +{ + size_t clen = ((size_t) (1 << hashmap->log2_capacity)) + HASHMAP_LINEAR_PROBE_LENGTH; + + hashmap_impl new_hashmap; + hashmap_impl_init (clen * 2, &new_hashmap); + + size_t l = clen + HASHMAP_LINEAR_PROBE_LENGTH; + for (size_t i = 0; i < l; i++) + { + const ecma_string_t *p = hashmap->literals[i]; + if (!p) + continue; + hashmap_impl_insert (&new_hashmap, p); + } + + jmem_heap_free_block (hashmap->literals, l * sizeof (const ecma_string_t *)); + + memcpy (hashmap, &new_hashmap, sizeof (hashmap_impl)); +} /* hashmap_impl_rehash */ diff --git a/jerry-core/lit/lit-hashmap-internal.h b/jerry-core/lit/lit-hashmap-internal.h deleted file mode 100644 index aaf4d14b08..0000000000 --- a/jerry-core/lit/lit-hashmap-internal.h +++ /dev/null @@ -1,80 +0,0 @@ -/* Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - The lit-hashmap library is based on the public domain software - available from https://github.com/sheredom/hashmap.h -*/ - -#ifndef LIT_HASHMAP_INTERNAL_H -#define LIT_HASHMAP_INTERNAL_H - -#include "lit-hashmap.h" - -#define HASHMAP_ALWAYS_INLINE __attribute__ ((always_inline)) inline -#define HASHMAP_LINEAR_PROBE_LENGTH (8) - -/** - * hashmap creation options. - */ - -typedef struct hashmap_create_options_s -{ - hashmap_uint32_t initial_capacity; /**< initial hashmap capacity */ -} hashmap_create_options_t; - -/// @brief Create a hashmap. -/// @param options The options to create the hashmap with. -/// @param out_hashmap The storage for the created hashmap. -/// @return On success 0 is returned. -/// -/// The options members work as follows: -/// - initial_capacity The initial capacity of the hashmap. -/// - hasher Which hashing function to use with the hashmap (by default the -// crc32 with Robert Jenkins' mix is used). -int hashmap_create_ex (struct hashmap_create_options_s options, struct hashmap_s *const out_hashmap); - -/// @brief Iterate over all the elements in a hashmap. -/// @param hashmap The hashmap to iterate over. -/// @param iterator The function pointer to call on each element. -/// @param context The context to pass as the first argument to f. -/// @return If the entire hashmap was iterated then 0 is returned. -/// Otherwise if the callback function f returned positive then the positive -/// value is returned. If the callback function returns -1, the current item -/// is removed and iteration continues. -int hashmap_iterate_pairs (struct hashmap_s *const hashmap, - int (*iterator) (void *const, struct hashmap_element_s *const), - void *const context); - -/// @brief Get the size of the hashmap. -/// @param hashmap The hashmap to get the size of. -/// @return The size of the hashmap. -HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_num_entries (const struct hashmap_s *const hashmap); - -/// @brief Get the capacity of the hashmap. -/// @param hashmap The hashmap to get the size of. -/// @return The capacity of the hashmap. -HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_capacity (const struct hashmap_s *const hashmap); - -HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_hash_helper_int_helper (const struct hashmap_s *const m, - const ecma_string_t *const key); -HASHMAP_ALWAYS_INLINE int hashmap_hash_helper (const struct hashmap_s *const m, - const ecma_string_t *const key, - hashmap_uint32_t *const out_index); -int hashmap_rehash_iterator (void *const new_hash, struct hashmap_element_s *const e); -HASHMAP_ALWAYS_INLINE int hashmap_rehash_helper (struct hashmap_s *const m); -HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_clz (const hashmap_uint32_t x); - -#endif // LIT_HASHMAP_INTERNAL_H diff --git a/jerry-core/lit/lit-hashmap.c b/jerry-core/lit/lit-hashmap.c index 1befc99dad..1729a4d539 100644 --- a/jerry-core/lit/lit-hashmap.c +++ b/jerry-core/lit/lit-hashmap.c @@ -16,6 +16,8 @@ * available from https://github.com/sheredom/hashmap.h */ +#include "lit-hashmap.h" + #include #include @@ -23,366 +25,52 @@ #if JERRY_LIT_HASHMAP -#include "ecma-helpers.h" - -#include "lit-hashmap-internal.h" - -#define HASHMAP_CAST(type, x) ((type) (x)) -#define HASHMAP_PTR_CAST(type, x) ((type) (x)) -#define HASHMAP_NULL 0 +#include "lit-hashmap-impl.h" +#include "lit-hashmap.h" -int -hashmap_create_ex (struct hashmap_create_options_s options, struct hashmap_s *const out_hashmap) +struct hashmap_s { - if (2 > options.initial_capacity) - { - options.initial_capacity = 2; - } - else if (0 != (options.initial_capacity & (options.initial_capacity - 1))) - { - options.initial_capacity = 1u << (32 - hashmap_clz (options.initial_capacity)); - } - - out_hashmap->alloc_size = - (options.initial_capacity + HASHMAP_LINEAR_PROBE_LENGTH) * sizeof (struct hashmap_element_s); - out_hashmap->data = HASHMAP_CAST (struct hashmap_element_s *, jmem_heap_alloc_block (out_hashmap->alloc_size)); - memset (out_hashmap->data, 0, out_hashmap->alloc_size); - - out_hashmap->log2_capacity = 31 - hashmap_clz (options.initial_capacity); - out_hashmap->size = 0; - out_hashmap->initialized = 1; + hashmap_impl map; +}; - return 0; -} /* hashmap_create_ex */ - -/** - * hashmap_put - * - */ -int -hashmap_put (struct hashmap_s *const m, const ecma_string_t *const key) +void +hashmap_init (hashmap_t *map) { - JERRY_ASSERT (m->initialized == 0 || m->initialized == 1); - hashmap_uint32_t index; - - if ((HASHMAP_NULL == key)) - { - return 1; - } - - /* Find a place to put our value. */ - while (!hashmap_hash_helper (m, key, &index)) - { - if (hashmap_rehash_helper (m)) - { - return 1; - } - } - - if (0 == m->data[index].data) - m->size++; - - /* Set the data. */ - m->data[index].data = key; - - return 0; -} /* hashmap_put */ + struct hashmap_s *hashmap = jmem_heap_alloc_block (sizeof (struct hashmap_s)); + hashmap_impl_init (8, &(hashmap->map)); + (*map) = hashmap; +} /* hashmap_init */ -/** - * hashmap_init - * - */ void -hashmap_init (struct hashmap_s *m) +hashmap_put (hashmap_t hashmap, const ecma_string_t *const literal) { - if (m->initialized == 0) - { - m->initialized = 1; - hashmap_create_options_t opts = { .initial_capacity = 32 }; - hashmap_create_ex (opts, m); - } - else if (m->initialized != 1) - { - JERRY_ASSERT (0); - } -} /* hashmap_init */ + hashmap_impl_insert (&hashmap->map, literal); +} /* hashmap_put */ -/** - * hashmap_get - * - */ const ecma_string_t * -hashmap_get (const struct hashmap_s *const m, const ecma_string_t *const key) +hashmap_get (hashmap_t hashmap, const ecma_string_t *const literal) { - JERRY_ASSERT (m->initialized == 0 || m->initialized == 1); - hashmap_uint32_t i, curr; + hashmap_impl_iter it = hashmap_impl_find (&hashmap->map, literal->u.hash); - if ((HASHMAP_NULL == key)) + for (const ecma_string_t *p = hashmap_impl_iter_get (&it); p != NULL; p = hashmap_impl_iter_next (&it)) { - JERRY_ASSERT (0); - return HASHMAP_NULL; - } - - curr = hashmap_hash_helper_int_helper (m, key); - - /* Linear probing, if necessary */ - for (i = 0; i < HASHMAP_LINEAR_PROBE_LENGTH; i++) - { - const hashmap_uint32_t index = curr + i; - - if (m->data[index].data) + if (ecma_compare_ecma_strings (p, literal)) { - if (ecma_compare_ecma_strings (m->data[index].data, key)) - { - JERRY_ASSERT (m->initialized == 0 || m->initialized == 1); - return m->data[index].data; - } + return p; } } - - JERRY_ASSERT (m->initialized == 0 || m->initialized == 1); - /* Not found */ - return HASHMAP_NULL; + return NULL; } /* hashmap_get */ -/** - * hashmap_remove - * - */ -int -hashmap_remove (struct hashmap_s *const m, const ecma_string_t *const key) -{ - hashmap_uint32_t i, curr; - - if ((HASHMAP_NULL == key)) - { - return 1; - } - - curr = hashmap_hash_helper_int_helper (m, key); - - /* Linear probing, if necessary */ - for (i = 0; i < HASHMAP_LINEAR_PROBE_LENGTH; i++) - { - const hashmap_uint32_t index = curr + i; - - if (m->data[index].data) - { - if (ecma_compare_ecma_strings (m->data[index].data, key)) - { - /* Blank out the fields including in_use */ - memset (&m->data[index], 0, sizeof (struct hashmap_element_s)); - - /* Reduce the size */ - m->size--; - - return 0; - } - } - } - - return 1; -} /* hashmap_remove */ - -/** - * hashmap_iterate_pairs - * - */ -int -hashmap_iterate_pairs (struct hashmap_s *const m, - int (*f) (void *const, struct hashmap_element_s *const), - void *const context) -{ - hashmap_uint32_t i; - int r; - - for (i = 0; i < (hashmap_capacity (m) + HASHMAP_LINEAR_PROBE_LENGTH); i++) - { - struct hashmap_element_s *p; - p = &m->data[i]; - if (p->data) - { - r = f (context, p); - switch (r) - { - case -1: /* remove item */ - memset (p, 0, sizeof (struct hashmap_element_s)); - m->size--; - break; - case 0: /* continue iterating */ - break; - default: /* early exit */ - return 1; - } - } - } - return 0; -} /* hashmap_iterate_pairs */ - -/** - * hashmap_destroy - * - */ void -hashmap_destroy (struct hashmap_s *const m) -{ - if (m->data) - jmem_heap_free_block (m->data, m->alloc_size); - memset (m, 0, sizeof (struct hashmap_s)); -} /* hashmap_destroy */ - -/** - * hashmap_num_entries - * - */ -HASHMAP_ALWAYS_INLINE hashmap_uint32_t -hashmap_num_entries (const struct hashmap_s *const m) -{ - return m->size; -} /* hashmap_num_entries */ - -/** - * hashmap_capacity - * - */ -HASHMAP_ALWAYS_INLINE hashmap_uint32_t -hashmap_capacity (const struct hashmap_s *const m) -{ - return 1u << m->log2_capacity; -} /* hashmap_capacity */ - -/** - * hashmap_hash_helper_int_helper - * - */ -HASHMAP_ALWAYS_INLINE hashmap_uint32_t -hashmap_hash_helper_int_helper (const struct hashmap_s *const m, const ecma_string_t *const k) -{ - return (k->u.hash * 2654435769u) >> (32u - m->log2_capacity); -} /* hashmap_hash_helper_int_helper */ - -/** - * hashmap_hash_helper - * - */ -HASHMAP_ALWAYS_INLINE int -hashmap_hash_helper (const struct hashmap_s *const m, const ecma_string_t *const key, hashmap_uint32_t *const out_index) -{ - hashmap_uint32_t curr; - hashmap_uint32_t i; - hashmap_uint32_t first_free; - - /* If full, return immediately */ - if (hashmap_num_entries (m) == hashmap_capacity (m)) - { - return 0; - } - - /* Find the best index */ - curr = hashmap_hash_helper_int_helper (m, key); - first_free = ~0u; - - for (i = 0; i < HASHMAP_LINEAR_PROBE_LENGTH; i++) - { - const hashmap_uint32_t index = curr + i; - - if (!m->data[index].data) - { - first_free = (first_free < index) ? first_free : index; - } - else if (ecma_compare_ecma_strings (m->data[index].data, key)) - { - *out_index = index; - return 1; - } - } - - // Couldn't find a free element in the linear probe. - if (~0u == first_free) - { - return 0; - } - - *out_index = first_free; - return 1; -} /* hashmap_hash_helper */ - -/** - * hashmap_rehash_iterator - * - */ -int -hashmap_rehash_iterator (void *const new_hash, struct hashmap_element_s *const e) +hashmap_destroy (hashmap_t *hashmap) { - int temp = hashmap_put (HASHMAP_PTR_CAST (struct hashmap_s *, new_hash), e->data); - - if (0 < temp) - { - return 1; - } - - /* clear old value to avoid stale pointers */ - return -1; -} /* hashmap_rehash_iterator */ - -/** - * hashmap_rehash_helper - * - * Doubles the size of the hashmap, and rehashes all the elements - */ -HASHMAP_ALWAYS_INLINE int -hashmap_rehash_helper (struct hashmap_s *const m) -{ - struct hashmap_create_options_s options; - struct hashmap_s new_m; - int flag; - - memset (&options, 0, sizeof (options)); - options.initial_capacity = hashmap_capacity (m) * 2; - - if (0 == options.initial_capacity) + if (*hashmap) { - return 1; + hashmap_impl_destroy (&((*hashmap)->map)); + jmem_heap_free_block (&((*hashmap)->map), sizeof (struct hashmap_s)); + *hashmap = (hashmap_t) 0; } - - flag = hashmap_create_ex (options, &new_m); - - if (0 != flag) - { - JERRY_ASSERT (0); - return flag; - } - - /* copy the old elements to the new table */ - flag = hashmap_iterate_pairs (m, hashmap_rehash_iterator, HASHMAP_PTR_CAST (void *, &new_m)); - - if (0 != flag) - { - return flag; - } - - hashmap_destroy (m); - - /* put new hash into old hash structure by copying */ - memcpy (m, &new_m, sizeof (struct hashmap_s)); - - return 0; -} /* hashmap_rehash_helper */ - -/** - * hashmap_clz - * - */ -HASHMAP_ALWAYS_INLINE hashmap_uint32_t -hashmap_clz (const hashmap_uint32_t x) -{ -#if defined(_MSC_VER) - unsigned long result; - _BitScanReverse (&result, x); - return 31 - HASHMAP_CAST (hashmap_uint32_t, result); -#else /* _MSC_VER */ - return HASHMAP_CAST (hashmap_uint32_t, __builtin_clz (x)); -#endif /* _MSC_VER */ -} /* hashmap_clz */ +} /* hashmap_destroy */ #endif /* JERRY_LIT_HASHMAP */ diff --git a/jerry-core/lit/lit-hashmap.h b/jerry-core/lit/lit-hashmap.h index c03370be78..3a008e20ea 100644 --- a/jerry-core/lit/lit-hashmap.h +++ b/jerry-core/lit/lit-hashmap.h @@ -26,64 +26,13 @@ #include #include "ecma-globals.h" -typedef uint8_t hashmap_uint8_t; -typedef uint32_t hashmap_uint32_t; -typedef uint64_t hashmap_uint64_t; -/** - * hashmap element - */ - -typedef struct hashmap_element_s -{ - const ecma_string_t *data; /**< point to a literal */ -} hashmap_element_t; - -/** - * hashmap structure - */ - -typedef struct hashmap_s -{ - hashmap_uint32_t log2_capacity; /**< hashmap capacity */ - hashmap_uint32_t size; /**< hashmap size*/ - struct hashmap_element_s *data; /**< element array */ - size_t alloc_size; /**< allocated size */ - uint8_t initialized; /**< 0 if not initialized */ -} hashmap_t; - -/// @brief Initialize the hashmap. -/// @param hashmap The hashmap to initialize. -void hashmap_init (struct hashmap_s *m); - -/// @brief Put an element into the hashmap. -/// @param hashmap The hashmap to insert into. -/// @param key The string key to use. -/// @param len The length of the string key. -/// @param value The value to insert. -/// @return On success 0 is returned. -/// -/// The key string slice is not copied when creating the hashmap entry, and thus -/// must remain a valid pointer until the hashmap entry is removed or the -/// hashmap is destroyed. -int hashmap_put (struct hashmap_s *const hashmap, const ecma_string_t *const key); - -/// @brief Get an element from the hashmap. -/// @param hashmap The hashmap to get from. -/// @param key The string key to use. -/// @param len The length of the string key. -/// @return The previously set element, or NULL if none exists. -const ecma_string_t *hashmap_get (const struct hashmap_s *const hashmap, const ecma_string_t *const key); - -/// @brief Remove an element from the hashmap. -/// @param hashmap The hashmap to remove from. -/// @param key The string key to use. -/// @param len The length of the string key. -/// @return On success 0 is returned. -int hashmap_remove (struct hashmap_s *const hashmap, const ecma_string_t *const key); +typedef struct hashmap_s *hashmap_t; -/// @brief Destroy the hashmap. -/// @param hashmap The hashmap to destroy. -void hashmap_destroy (struct hashmap_s *const hashmap); +void hashmap_init (hashmap_t *); +void hashmap_put (hashmap_t hashmap, const ecma_string_t *const key); +const ecma_string_t *hashmap_get (hashmap_t hashmap, const ecma_string_t *const key); +void hashmap_remove (hashmap_t hashmap, const ecma_string_t *const key); +void hashmap_destroy (hashmap_t *hashmap); #endif // LIT_HASHMAP_H