diff --git a/src/libAtomVM/CMakeLists.txt b/src/libAtomVM/CMakeLists.txt index c26f939804..5ac8a72d8d 100644 --- a/src/libAtomVM/CMakeLists.txt +++ b/src/libAtomVM/CMakeLists.txt @@ -69,6 +69,7 @@ set(HEADER_FILES set(SOURCE_FILES atom.c atomshashtable.c + atom_table.c avmpack.c bif.c bitstring.c diff --git a/src/libAtomVM/atom_table.c b/src/libAtomVM/atom_table.c new file mode 100644 index 0000000000..89d2642296 --- /dev/null +++ b/src/libAtomVM/atom_table.c @@ -0,0 +1,299 @@ +/* + * This file is part of AtomVM. + * + * Copyright 2023 Davide Bettio + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later + */ + +#include + +#include +#include +#include +#include +#include + +#include "atom.h" +#include "smp.h" +#include "utils.h" + +#ifndef AVM_NO_SMP +#define SMP_RDLOCK(htable) smp_rwlock_rdlock(htable->lock) +#define SMP_WRLOCK(htable) smp_rwlock_wrlock(htable->lock) +#define SMP_UNLOCK(htable) smp_rwlock_unlock(htable->lock) +#else +#define SMP_RDLOCK(htable) +#define SMP_WRLOCK(htable) +#define SMP_UNLOCK(htable) +#endif + +#define DEFAULT_SIZE 8 + +struct HNode +{ + struct HNode *next; + AtomString key; + long index; +}; + +struct HNodeGroup; + +struct AtomTable +{ + int capacity; + int count; +#ifndef AVM_NO_SMP + RWLock *lock; +#endif + struct HNode **buckets; + + struct HNodeGroup *first_node_group; + struct HNodeGroup *last_node_group; +}; + +struct HNodeGroup +{ + struct HNodeGroup *next; + long first_index; + uint16_t len; + uint16_t avail; + + struct HNode nodes[]; +}; + +static struct HNodeGroup *new_node_group(struct AtomTable *table, int len); +static unsigned long sdbm_hash(const unsigned char *str, int len); + +struct AtomTable *atom_table_new() +{ + struct AtomTable *htable = malloc(sizeof(struct AtomTable)); + if (IS_NULL_PTR(htable)) { + return NULL; + } + htable->buckets = calloc(DEFAULT_SIZE, sizeof(struct HNode *)); + if (IS_NULL_PTR(htable->buckets)) { + free(htable); + return NULL; + } + + htable->count = 0; + htable->capacity = DEFAULT_SIZE; + + htable->last_node_group = NULL; + htable->first_node_group = new_node_group(htable, DEFAULT_SIZE); + +#ifndef AVM_NO_SMP + htable->lock = smp_rwlock_create(); +#endif + + return htable; +} + +void atom_table_destroy(struct AtomTable *table) +{ + struct HNodeGroup *node_group = table->first_node_group; + while (node_group) { + struct HNodeGroup *next_group = node_group->next; + free(node_group); + node_group = next_group; + } +#ifndef AVM_NO_SMP + smp_rwlock_destroy(table->lock); +#endif + free(table); +} + +int atom_table_count(struct AtomTable *table) +{ + SMP_RDLOCK(table); + int count = table->count; + SMP_UNLOCK(table); + + return count; +} + +static struct HNodeGroup *new_node_group(struct AtomTable *table, int len) +{ + struct HNodeGroup *new_group = malloc(sizeof(struct HNodeGroup) + sizeof(struct HNode) * len); + new_group->next = NULL; + new_group->first_index = table->count; + new_group->len = len; + new_group->avail = len; + + if (LIKELY(table->last_node_group != NULL)) { + table->last_node_group->next = new_group; + } + table->last_node_group = new_group; + + return new_group; +} + +static unsigned long sdbm_hash(const unsigned char *str, int len) +{ + unsigned long hash = 0; + int c; + + for (int i = 0; i < len; i++) { + c = *str++; + hash = c + (hash << 6) + (hash << 16) - hash; + } + + return hash; +} + +#define ATOM_TABLE_NOT_FOUND -1 + +static inline struct HNode *get_node_from_bucket( + const struct AtomTable *hash_table, unsigned long bucket_index, const AtomString string) +{ + struct HNode *node = hash_table->buckets[bucket_index]; + while (node) { + if (atom_are_equals(string, node->key)) { + return node; + } + + node = node->next; + } + + return NULL; +} + +static inline struct HNode *lock_and_get_node( + const struct AtomTable *hash_table, const AtomString string) +{ + unsigned long hash = sdbm_hash(string, atom_string_len(string)); + + SMP_RDLOCK(hash_table); + unsigned long bucket_index = hash % hash_table->capacity; + return get_node_from_bucket(hash_table, bucket_index, string); +} + +long atom_table_get_index(struct AtomTable *table, const AtomString string) +{ + struct HNode *node = lock_and_get_node(table, string); + long result = (node != NULL) ? node->index : ATOM_TABLE_NOT_FOUND; + + SMP_UNLOCK(table); + return result; +} + +bool atom_table_has_atom_string(const struct AtomTable *table, const AtomString string) +{ + struct HNode *node = lock_and_get_node(table, string); + bool result = (node != NULL); + + SMP_UNLOCK(table); + return result; +} + +// TODO: this function needs to be optimized +const AtomString *atom_table_get_atom_string(const struct AtomTable *table, long index) +{ + SMP_RDLOCK(table); + + struct HNodeGroup *node_group = table->first_node_group; + while (node_group) { + long first_index = node_group->first_index; + if (first_index + node_group->len > index) { + if (index - first_index > (node_group->len - node_group->avail)) { + SMP_UNLOCK(table); + return NULL; + } + + SMP_UNLOCK(table); + return node_group->nodes[index - first_index].key; + } + + node_group = node_group->next; + } + + SMP_UNLOCK(table); + return NULL; +} + +static inline void init_node(struct HNode *node, const AtomString *atom, long index) +{ + node->key = atom; + node->index = index; +} + +static inline void insert_node_into_bucket( + struct AtomTable *table, int bucket_index, struct HNode *node) +{ + struct HNode *maybe_existing_node = table->buckets[bucket_index]; + table->buckets[bucket_index] = node; + node->next = maybe_existing_node; +} + +long atom_table_ensure_atom(struct AtomTable *table, const AtomString string) +{ + unsigned long hash = sdbm_hash(string, atom_string_len(string)); + SMP_WRLOCK(table); + unsigned long bucket_index = hash % table->capacity; + + struct HNode *node = get_node_from_bucket(table, bucket_index, string); + if (node) { + SMP_UNLOCK(table); + return node->index; + } + + struct HNodeGroup *node_group = table->last_node_group; + if (!node_group->avail) { + node_group = new_node_group(table, DEFAULT_SIZE); + } + + long new_index = table->count; + table->count++; + + node = &node_group->nodes[new_index - node_group->first_index]; + node_group->avail--; + init_node(node, string, new_index); + insert_node_into_bucket(table, bucket_index, node); + + SMP_UNLOCK(table); + return new_index; +} + +void maybe_rehash(struct AtomTable *table, int new_entries) +{ + if (table->capacity - table->count + new_entries > 0) { + return; + } + + int new_capacity = table->capacity; + + struct HNode **new_bucket = malloc(sizeof(struct HNode *) * new_capacity); + if (IS_NULL_PTR(new_bucket)) { + return; + } + + struct HNodeGroup *group = table->first_node_group; + + while (group) { + int group_count = group->len - group->avail; + for (int i = 0; i < group_count; i++) { + struct HNode *node = &group->nodes[i]; + AtomString key = node->key; + + unsigned long hash = sdbm_hash(key, atom_string_len(key)); + unsigned long bucket_index = hash % table->capacity; + + insert_node_into_bucket(table, bucket_index, node); + } + + group = group->next; + } +} diff --git a/src/libAtomVM/atom_table.h b/src/libAtomVM/atom_table.h new file mode 100644 index 0000000000..73b7ff89ca --- /dev/null +++ b/src/libAtomVM/atom_table.h @@ -0,0 +1,39 @@ +/* + * This file is part of AtomVM. + * + * Copyright 2023 Davide Bettio + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later + */ + +#ifndef _ATOM_TABLE_ +#define _ATOM_TABLE_ + +#include "atom.h" + +struct AtomTable; + +struct AtomTable *atom_table_new(); +void atom_table_destroy(struct AtomTable *table); + +int atom_table_count(struct AtomTable *table); + +long atom_table_ensure_atom(struct AtomTable *table, const AtomString string); +const AtomString *atom_table_get_atom_string(const struct AtomTable *table, long index); +long atom_table_get_index(struct AtomTable *table, const AtomString string); + +int do_test(struct AtomTable *table); + +#endif diff --git a/src/libAtomVM/globalcontext.c b/src/libAtomVM/globalcontext.c index 4727ecb874..a58653fa76 100644 --- a/src/libAtomVM/globalcontext.c +++ b/src/libAtomVM/globalcontext.c @@ -24,6 +24,7 @@ #include "globalcontext.h" #include "atomshashtable.h" +#include "atom_table.h" #include "avmpack.h" #include "context.h" #include "defaultatoms.h" @@ -88,17 +89,12 @@ GlobalContext *globalcontext_new() #ifndef AVM_NO_SMP smp_spinlock_init(&glb->atom_insert_lock); #endif - glb->atoms_table = atomshashtable_new(); - if (IS_NULL_PTR(glb->atoms_table)) { - free(glb); - return NULL; - } - glb->atoms_ids_table = valueshashtable_new(); - if (IS_NULL_PTR(glb->atoms_ids_table)) { - free(glb->atoms_table); + glb->atom_table = atom_table_new(); + if (IS_NULL_PTR(glb->atom_table)) { free(glb); return NULL; } + //do_test(glb->atom_table); defaultatoms_init(glb); @@ -106,8 +102,7 @@ GlobalContext *globalcontext_new() glb->loaded_modules_count = 0; glb->modules_table = atomshashtable_new(); if (IS_NULL_PTR(glb->modules_table)) { - free(glb->atoms_ids_table); - free(glb->atoms_table); + atom_table_destroy(glb->atom_table); free(glb); return NULL; } @@ -115,8 +110,7 @@ GlobalContext *globalcontext_new() glb->modules_lock = smp_rwlock_create(); if (IS_NULL_PTR(glb->modules_lock)) { free(glb->modules_table); - free(glb->atoms_ids_table); - free(glb->atoms_table); + atom_table_destroy(glb->atom_table); free(glb); return NULL; } @@ -141,8 +135,7 @@ GlobalContext *globalcontext_new() smp_rwlock_destroy(glb->modules_lock); #endif free(glb->modules_table); - free(glb->atoms_ids_table); - free(glb->atoms_table); + atom_table_destroy(glb->atom_table); free(glb); return NULL; } @@ -158,8 +151,7 @@ GlobalContext *globalcontext_new() #endif smp_rwlock_destroy(glb->modules_lock); free(glb->modules_table); - free(glb->atoms_ids_table); - free(glb->atoms_table); + atom_table_destroy(glb->atom_table); free(glb); return NULL; } @@ -171,8 +163,7 @@ GlobalContext *globalcontext_new() #endif smp_rwlock_destroy(glb->modules_lock); free(glb->modules_table); - free(glb->atoms_ids_table); - free(glb->atoms_table); + atom_table_destroy(glb->atom_table); free(glb); return NULL; } @@ -399,57 +390,35 @@ int globalcontext_get_registered_process(GlobalContext *glb, int atom_index) int globalcontext_insert_atom(GlobalContext *glb, AtomString atom_string) { - return globalcontext_insert_atom_maybe_copy(glb, atom_string, 0); + long index = atom_table_ensure_atom(glb->atom_table, atom_string); + if (UNLIKELY(index < 0)) { + abort(); + } + return index; } int globalcontext_insert_atom_maybe_copy(GlobalContext *glb, AtomString atom_string, int copy) { - struct AtomsHashTable *htable = glb->atoms_table; - - unsigned long atom_index = atomshashtable_get_value(htable, atom_string, ULONG_MAX); - if (atom_index == ULONG_MAX) { - if (copy) { - uint8_t len = *((uint8_t *) atom_string); - uint8_t *buf = malloc(1 + len); - if (UNLIKELY(IS_NULL_PTR(buf))) { - fprintf(stderr, "Unable to allocate memory for atom string\n"); - AVM_ABORT(); - } - memcpy(buf, atom_string, 1 + len); - atom_string = buf; - } - // TODO: This lock can be avoided by returning from a new atomshashtable_get_or_insert - // function the new atom index, and using it when calling valueshashtable_insert. - // See also https://github.com/atomvm/AtomVM/pull/812 discussion - SMP_SPINLOCK_LOCK(&glb->atom_insert_lock); - - // things might have changed in the meantime, so let's check this again now that there is - // a lock. - atom_index = atomshashtable_get_value(htable, atom_string, ULONG_MAX); - if (atom_index != ULONG_MAX) { - SMP_SPINLOCK_UNLOCK(&glb->atom_insert_lock); - return atom_index; - } - - atom_index = htable->count; - if (!atomshashtable_insert(htable, atom_string, atom_index)) { - SMP_SPINLOCK_UNLOCK(&glb->atom_insert_lock); - return -1; - } - if (!valueshashtable_insert(glb->atoms_ids_table, atom_index, (unsigned long) atom_string)) { - SMP_SPINLOCK_UNLOCK(&glb->atom_insert_lock); - return -1; + //TODO: this leaks, fix it + if (copy) { + uint8_t len = *((uint8_t *) atom_string); + uint8_t *buf = malloc(1 + len); + if (UNLIKELY(IS_NULL_PTR(buf))) { + fprintf(stderr, "Unable to allocate memory for atom string\n"); + AVM_ABORT(); } - SMP_SPINLOCK_UNLOCK(&glb->atom_insert_lock); + memcpy(buf, atom_string, 1 + len); + atom_string = buf; } - return (int) atom_index; + long index = atom_table_ensure_atom(glb->atom_table, atom_string); + return index; } bool globalcontext_is_atom_index_equal_to_atom_string(GlobalContext *glb, int atom_index_a, AtomString atom_string_b) { AtomString atom_string_a; - atom_string_a = (AtomString) valueshashtable_get_value(glb->atoms_ids_table, atom_index_a, 0); + atom_string_a = atom_table_get_atom_string(glb->atom_table, atom_index_a); return atom_are_equals(atom_string_a, atom_string_b); } @@ -459,18 +428,17 @@ AtomString globalcontext_atomstring_from_term(GlobalContext *glb, term t) AVM_ABORT(); } unsigned long atom_index = term_to_atom_index(t); - unsigned long ret; - ret = valueshashtable_get_value(glb->atoms_ids_table, atom_index, ULONG_MAX); - if (ret == ULONG_MAX) { + AtomString *str = atom_table_get_atom_string(glb->atom_table, atom_index); + if (IS_NULL_PTR(str)) { return NULL; } - return (AtomString) ret; + return str; } term globalcontext_existing_term_from_atom_string(GlobalContext *glb, AtomString atom_string) { - unsigned long atom_index = atomshashtable_get_value(glb->atoms_table, atom_string, ULONG_MAX); - if (atom_index == ULONG_MAX) { + long atom_index = atom_table_get_index(glb->atom_table, atom_string); + if (UNLIKELY(atom_index < 0)) { return term_invalid_term(); } return term_from_atom_index(atom_index); diff --git a/src/libAtomVM/globalcontext.h b/src/libAtomVM/globalcontext.h index ee02b53dfc..bc704aa097 100644 --- a/src/libAtomVM/globalcontext.h +++ b/src/libAtomVM/globalcontext.h @@ -35,6 +35,7 @@ extern "C" { #include #include "atom.h" +#include "atom_table.h" #include "erl_nif.h" #include "list.h" #include "smp.h" @@ -86,8 +87,7 @@ struct GlobalContext #ifndef AVM_NO_SMP SpinLock atom_insert_lock; #endif - struct AtomsHashTable *atoms_table; - struct ValuesHashTable *atoms_ids_table; + struct AtomTable *atom_table; struct AtomsHashTable *modules_table; #ifndef AVM_NO_SMP diff --git a/src/libAtomVM/interop.c b/src/libAtomVM/interop.c index b6b6479399..703410da2b 100644 --- a/src/libAtomVM/interop.c +++ b/src/libAtomVM/interop.c @@ -20,6 +20,7 @@ #include "interop.h" +#include "atom_table.h" #include "bitstring.h" #include "defaultatoms.h" #include "tempstack.h" @@ -140,7 +141,7 @@ char *interop_list_to_string(term list, int *ok) char *interop_atom_to_string(Context *ctx, term atom) { int atom_index = term_to_atom_index(atom); - AtomString atom_string = (AtomString) valueshashtable_get_value(ctx->global->atoms_ids_table, atom_index, (unsigned long) NULL); + AtomString atom_string = atom_table_get_atom_string(ctx->global->atom_table, atom_index); int len = atom_string_len(atom_string); char *str = malloc(len + 1); diff --git a/src/libAtomVM/module.c b/src/libAtomVM/module.c index 1fb2d52e32..8b28ee26e6 100644 --- a/src/libAtomVM/module.c +++ b/src/libAtomVM/module.c @@ -412,8 +412,8 @@ term module_load_literal(Module *mod, int index, Context *ctx) const struct ExportedFunction *module_resolve_function0(Module *mod, int import_table_index, struct UnresolvedFunctionCall *unresolved, GlobalContext *glb) { - AtomString module_name_atom = (AtomString) valueshashtable_get_value(glb->atoms_ids_table, unresolved->module_atom_index, (unsigned long) NULL); - AtomString function_name_atom = (AtomString) valueshashtable_get_value(glb->atoms_ids_table, unresolved->function_atom_index, (unsigned long) NULL); + AtomString module_name_atom = atom_table_get_atom_string(glb->atom_table, unresolved->module_atom_index); + AtomString function_name_atom = atom_table_get_atom_string(glb->atom_table, unresolved->function_atom_index); int arity = unresolved->arity; Module *found_module = globalcontext_get_module(glb, module_name_atom); diff --git a/src/libAtomVM/module.h b/src/libAtomVM/module.h index d4b838a67d..f6741e5cad 100644 --- a/src/libAtomVM/module.h +++ b/src/libAtomVM/module.h @@ -37,6 +37,7 @@ extern "C" { #include "atom.h" #include "atomshashtable.h" +#include "atom_table.h" #include "context.h" #include "exportedfunction.h" #include "globalcontext.h" @@ -244,7 +245,7 @@ term module_load_literal(Module *mod, int index, Context *ctx); static inline AtomString module_get_atom_string_by_id(const Module *mod, int local_atom_id, GlobalContext *glb) { int global_id = mod->local_atoms_to_global_table[local_atom_id]; - return (AtomString) valueshashtable_get_value(glb->atoms_ids_table, global_id, (unsigned long) NULL); + return atom_table_get_atom_string(glb->atom_table, global_id); } /** diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index b9b76689cc..1f2f73cc0d 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -32,6 +32,7 @@ #include #include "atomshashtable.h" +#include "atom_table.h" #include "avmpack.h" #include "bif.h" #include "context.h" @@ -1956,8 +1957,9 @@ static term binary_to_atom(Context *ctx, int argc, term argv[], int create_new) ((uint8_t *) atom)[0] = atom_string_len; memcpy(((char *) atom) + 1, atom_string, atom_string_len); - unsigned long global_atom_index = atomshashtable_get_value(ctx->global->atoms_table, atom, ULONG_MAX); - int has_atom = (global_atom_index != ULONG_MAX); + // race condition here too + long global_atom_index = atom_table_get_index(ctx->global->atom_table, atom); + bool has_atom = (global_atom_index >= 0); if (create_new || has_atom) { if (!has_atom) { @@ -2006,8 +2008,10 @@ term list_to_atom(Context *ctx, int argc, term argv[], int create_new) ((uint8_t *) atom)[0] = atom_string_len; memcpy(((char *) atom) + 1, atom_string, atom_string_len); - unsigned long global_atom_index = atomshashtable_get_value(ctx->global->atoms_table, atom, ULONG_MAX); - int has_atom = (global_atom_index != ULONG_MAX); + //FIXME use globalcontext_insert_atom_maybe_copy + //this might leak in case of race condition + long global_atom_index = atom_table_get_index(ctx->global->atom_table, atom); + bool has_atom = (global_atom_index >= 0); if (create_new || has_atom) { if (!has_atom) { @@ -2035,7 +2039,7 @@ static term nif_erlang_atom_to_binary_2(Context *ctx, int argc, term argv[]) } int atom_index = term_to_atom_index(atom_term); - AtomString atom_string = (AtomString) valueshashtable_get_value(ctx->global->atoms_ids_table, atom_index, (unsigned long) NULL); + AtomString atom_string = atom_table_get_atom_string(ctx->global->atom_table, atom_index); int atom_len = atom_string_len(atom_string); @@ -2055,7 +2059,7 @@ static term nif_erlang_atom_to_list_1(Context *ctx, int argc, term argv[]) VALIDATE_VALUE(atom_term, term_is_atom); int atom_index = term_to_atom_index(atom_term); - AtomString atom_string = (AtomString) valueshashtable_get_value(ctx->global->atoms_ids_table, atom_index, (unsigned long) NULL); + AtomString atom_string = atom_table_get_atom_string(ctx->global->atom_table, atom_index); int atom_len = atom_string_len(atom_string); @@ -2667,7 +2671,7 @@ static term nif_erlang_system_info(Context *ctx, int argc, term argv[]) return term_from_int32(nif_num_ports(ctx->global)); } if (key == ATOM_COUNT_ATOM) { - return term_from_int32(ctx->global->atoms_table->count); + return term_from_int32(atom_table_count(ctx->global->atom_table)); } if (key == WORDSIZE_ATOM) { return term_from_int32(TERM_BYTES); @@ -3756,8 +3760,7 @@ static term nif_atomvm_read_priv(Context *ctx, int argc, term argv[]) } int atom_index = term_to_atom_index(app_term); - AtomString atom_string = (AtomString) valueshashtable_get_value(glb->atoms_ids_table, - atom_index, (unsigned long) NULL); + AtomString atom_string = atom_table_get_atom_string(glb->atom_table, atom_index); int app_len = atom_string_len(atom_string); char *app = malloc(app_len + 1); diff --git a/src/libAtomVM/term.c b/src/libAtomVM/term.c index 792f381177..f117673498 100644 --- a/src/libAtomVM/term.c +++ b/src/libAtomVM/term.c @@ -21,10 +21,10 @@ #include "term.h" #include "atom.h" +#include "atom_table.h" #include "context.h" #include "interop.h" #include "tempstack.h" -#include "valueshashtable.h" #include #include @@ -113,8 +113,7 @@ int term_funprint(PrinterFun *fun, term t, const GlobalContext *global) { if (term_is_atom(t)) { int atom_index = term_to_atom_index(t); - AtomString atom_string = (AtomString) valueshashtable_get_value( - global->atoms_ids_table, atom_index, (unsigned long) NULL); + AtomString atom_string = atom_table_get_atom_string(global->atom_table, atom_index); return fun->print(fun, "%.*s", (int) atom_string_len(atom_string), (char *) atom_string_data(atom_string)); @@ -607,15 +606,15 @@ TermCompareResult term_compare(term t, term other, TermCompareOpts opts, GlobalC } else if (term_is_atom(t) && term_is_atom(other)) { int t_atom_index = term_to_atom_index(t); - AtomString t_atom_string = (AtomString) valueshashtable_get_value(global->atoms_ids_table, - t_atom_index, (unsigned long) NULL); + AtomString t_atom_string = atom_table_get_atom_string(global->atom_table, + t_atom_index); int t_atom_len = atom_string_len(t_atom_string); const char *t_atom_data = (const char *) atom_string_data(t_atom_string); int other_atom_index = term_to_atom_index(other); - AtomString other_atom_string = (AtomString) valueshashtable_get_value(global->atoms_ids_table, - other_atom_index, (unsigned long) NULL); + AtomString other_atom_string = atom_table_get_atom_string(global->atom_table, + other_atom_index); int other_atom_len = atom_string_len(other_atom_string); const char *other_atom_data = (const char *) atom_string_data(other_atom_string);