Skip to content

Commit

Permalink
Atom table
Browse files Browse the repository at this point in the history
Signed-off-by: Davide Bettio <davide@uninstall.it>
  • Loading branch information
bettio committed Oct 25, 2023
1 parent e01a7cb commit f897075
Show file tree
Hide file tree
Showing 10 changed files with 396 additions and 85 deletions.
1 change: 1 addition & 0 deletions src/libAtomVM/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ set(HEADER_FILES
set(SOURCE_FILES
atom.c
atomshashtable.c
atom_table.c
avmpack.c
bif.c
bitstring.c
Expand Down
299 changes: 299 additions & 0 deletions src/libAtomVM/atom_table.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
/*
* This file is part of AtomVM.
*
* Copyright 2023 Davide Bettio <davide@uninstall.it>
*
* 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 <atom_table.h>

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#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;
}
}
39 changes: 39 additions & 0 deletions src/libAtomVM/atom_table.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* This file is part of AtomVM.
*
* Copyright 2023 Davide Bettio <davide@uninstall.it>
*
* 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
Loading

0 comments on commit f897075

Please sign in to comment.