Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
hashmap.h File Reference

This is an implementation of Compressed Hash-Array Mapped Prefix-tree, which is a bit-partitioned, persistent, immutable Hashed array mapped trie. More...

#include <stddef.h>
#include <stdint.h>
#include "serene/rt/errors.h"
Include dependency graph for hashmap.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  hmap_key_t
 Note: For key equality we use the memcpy function. More...
struct  hmap_kv_entry_t
struct  hmap_node_t
struct  hmap_t
struct  hmap_control_t
 If we ever want to modify some of these behaviours for a new instance of hashmap, we should use this control type. More...

Macros

#define HMAP_BR   32u
 branching factor (power of two)
#define HMAP_SHIFT   5u
#define HMAP_MASK   (HMAP_BR - 1u)
#define HMAP_HASH_SIZE   32u
 By default we use 32bit hashes.
#define HMAP_HASH_TYPE   uint32_t
#define HMAP_BITMAP_TYPE   uint32_t

Typedefs

typedef HMAP_HASH_TYPE hmap_hash_t
typedef HMAP_BITMAP_TYPE hmap_bitmap_t
typedef void * hmap_data_t
typedef struct hmap_key_t hmap_key_t
 Note: For key equality we use the memcpy function.
typedef struct hmap_kv_entry_t hmap_kv_entry_t
typedef struct hmap_node_t hmap_node_t
typedef struct hmap_t hmap_t
typedef struct hmap_control_t hmap_control_t
 If we ever want to modify some of these behaviours for a new instance of hashmap, we should use this control type.

Functions

hmap_t hmap_empty (const srn_context_t *ctx)
 Create, initialize and return a new hashmap pinned to ctx.
hmap_t hmap_insert (const hmap_t *hmap, hmap_key_t *k, void *v)
 Insert the given key k with the value v in the given hash hmap and return the new map.
hmap_t hmap_insert_ctl (const hmap_control_t *ctl, const hmap_t *hmap, hmap_key_t *k, void *v)
 Just like the hmap_insert function but, it receives a hmap_control_t to customize the comparison and hashing function.
void * hmap_lookup (const hmap_t *hmap, const hmap_key_t *k, void *default_value)
 Lookup the given k in the given hmap and return the value if it's been found.
void * hmap_lookup_ctl (const hmap_control_t *ctl, const hmap_t *hmap, const hmap_key_t *k, void *default_value)
 Just like the hmap_lookup function but, it receives a hmap_control_t to customize the comparison and hashing function.
hmap_hash_t hmap_hash (const srn_context_t *ctx, const void *data, size_t len)
 This is a simple hack to mock the hash function during tests to force a high collision hashing function.
hmap_key_thmap_make_key (srn_context_t *ctx, void *data, size_t len)
 Create a new key out of the given data, with the given len.
hmap_key_thmap_copy_key (const hmap_t *hmap, const hmap_key_t *k)
 The default copy_key hook.

Variables

hmap_control_t hmap_default_control

Detailed Description

This is an implementation of Compressed Hash-Array Mapped Prefix-tree, which is a bit-partitioned, persistent, immutable Hashed array mapped trie.

For more information, have a look at the Optimizing Hash-Array Mapped Tries for Fast and Lean Immutable JVM Collections paper.

TL;DR:

  • Every struct is immutable
  • Nodes are persistent and immutable.
  • Nodes are heap allocated
  • Heads can be stack allocated
  • We create a new head for any change and probably will create inner nodes as well depending on the change
  • New heads will share structure with the older version
  • This is not a ordered hashmap
  • The hashing function HAS TO BE DETERMINISTIC
  • Don't use pointers value as the data to hash, use the data that they point to instead.

Definition in file hashmap.h.

Macro Definition Documentation

◆ HMAP_BITMAP_TYPE

#define HMAP_BITMAP_TYPE   uint32_t

Definition at line 59 of file hashmap.h.

◆ HMAP_BR

#define HMAP_BR   32u

branching factor (power of two)

Definition at line 54 of file hashmap.h.

◆ HMAP_HASH_SIZE

#define HMAP_HASH_SIZE   32u

By default we use 32bit hashes.

Definition at line 57 of file hashmap.h.

◆ HMAP_HASH_TYPE

#define HMAP_HASH_TYPE   uint32_t

Definition at line 58 of file hashmap.h.

◆ HMAP_MASK

#define HMAP_MASK   (HMAP_BR - 1u)

Definition at line 56 of file hashmap.h.

◆ HMAP_SHIFT

#define HMAP_SHIFT   5u

Definition at line 55 of file hashmap.h.

Typedef Documentation

◆ hmap_bitmap_t

Definition at line 62 of file hashmap.h.

◆ hmap_control_t

typedef struct hmap_control_t hmap_control_t

If we ever want to modify some of these behaviours for a new instance of hashmap, we should use this control type.

◆ hmap_data_t

typedef void* hmap_data_t

Definition at line 63 of file hashmap.h.

◆ hmap_hash_t

Definition at line 61 of file hashmap.h.

◆ hmap_key_t

typedef struct hmap_key_t hmap_key_t

Note: For key equality we use the memcpy function.

◆ hmap_kv_entry_t

typedef struct hmap_kv_entry_t hmap_kv_entry_t

◆ hmap_node_t

typedef struct hmap_node_t hmap_node_t

◆ hmap_t

typedef struct hmap_t hmap_t

Function Documentation

◆ hmap_copy_key()

hmap_key_t * hmap_copy_key ( const hmap_t * hmap,
const hmap_key_t * k )

The default copy_key hook.

Clone the key struct and its bytes into the map's map_ctx so the map never retains pointers into a caller's block.

Definition at line 678 of file hashmap.c.

678 {
679 PANIC_IF_NULL(hmap);
680 PANIC_IF_NULL(k);
681 PANIC_IF_NULL(hmap->map_ctx);
682
683 hmap_key_t *copy = ALLOC(hmap->map_ctx, hmap_key_t);
684 copy->len = k->len;
685 copy->data =
686 (k->len == 0) ? nullptr : srn_copy_bytes(hmap->map_ctx, k->data, k->len, alignof(char));
687 return copy;
688}
#define ALLOC(ctx, T)
Definition context.h:84
Note: For key equality we use the memcpy function.
Definition hashmap.h:66
void * data
len 0 -> data == nullptr
Definition hashmap.h:68
size_t len
Definition hashmap.h:69
const srn_context_t * map_ctx
The context that owns every allocation the map retains.
Definition hashmap.h:104
#define PANIC_IF_NULL(ptr)
Definition utils.h:66
char * srn_copy_bytes(const srn_context_t *ctx, const char *src, size_t len, size_t alignment)
Definition utils.c:56
Here is the call graph for this function:

◆ hmap_empty()

hmap_t hmap_empty ( const srn_context_t * ctx)
nodiscard

Create, initialize and return a new hashmap pinned to ctx.

The pinned context owns every allocation the map (and all of its future versions) retains, so pick a context that lives at least as long as the map.

Definition at line 655 of file hashmap.c.

655 {
656 PANIC_IF_NULL(ctx);
657 hmap_t map = {.len = 0, .root = nullptr, .map_ctx = ctx};
658 return map;
659}
Here is the caller graph for this function:

◆ hmap_hash()

hmap_hash_t hmap_hash ( const srn_context_t * ctx,
const void * data,
size_t len )

This is a simple hack to mock the hash function during tests to force a high collision hashing function.

Outside of tests, this will be a wrapper around srn_hash.

Definition at line 77 of file hashmap.c.

77 {
78 return srn_hash(ctx->engine, data, len);
79}
srn_hash_t srn_hash(const srn_engine_t *engine, const void *data, size_t len)
Definition engine.c:166
srn_engine_t * engine
Long term state of the compiler.
Definition context.h:49
Here is the call graph for this function:
Here is the caller graph for this function:

◆ hmap_insert()

hmap_t hmap_insert ( const hmap_t * hmap,
hmap_key_t * k,
void * v )
nodiscard

Insert the given key k with the value v in the given hash hmap and return the new map.

The new map will share parts of its structure with the old map and never mutate the old map. All retained allocations go through the map's map_ctx. The control's copy_key clones the key into it, so k may point at stack or transient memory. The value v is stored as is and must outlive the map.

Definition at line 661 of file hashmap.c.

661 {
662 return hmap_default_control.insert(&hmap_default_control, hmap, k, v);
663}
hmap_control_t hmap_default_control
Definition hashmap.c:690
Here is the caller graph for this function:

◆ hmap_insert_ctl()

hmap_t hmap_insert_ctl ( const hmap_control_t * ctl,
const hmap_t * hmap,
hmap_key_t * k,
void * v )
nodiscard

Just like the hmap_insert function but, it receives a hmap_control_t to customize the comparison and hashing function.

Definition at line 594 of file hashmap.c.

594 {
595 PANIC_IF_NULL(ctl);
596 PANIC_IF_NULL(hmap);
597 PANIC_IF_NULL(k);
599
600 // Everything the map retains has to live in the map's context, so the map
601 // survives the release of the inserting caller's context. A null map_ctx
602 // means the map was zero-initialized instead of created with hmap_empty.
603 const srn_context_t *map_ctx = hmap->map_ctx;
604 PANIC_IF_NULL(map_ctx);
605
606 hmap_hash_t hash = ctl->hash(hmap, k);
607 hmap_key_t *key = ctl->copy_key(hmap, k);
608 hmap_t new_map = *hmap;
609
610 HMAP_LOG("Trying to insert a key with the hash: %x", hash);
611
612 if (hmap->root == nullptr) {
613 // First insertion, just create a singleton node.
614 HMAP_LOG("First insertion on an empty map");
615 hmap_kv_entry_t *kv = hmap_new_kv_entry(map_ctx, hash, key, v);
616 hmap_node_t *root = hmap_singleton_node(map_ctx, hash, 0, kv);
617
618 new_map.root = root;
619 new_map.len = hmap->len + 1;
620 return new_map;
621 }
622 HMAP_LOG("Find the correct node to insert the key");
623 // Create the intermediate nodes recursively
624 bool added = false;
625 hmap_node_t *new_root = hmap_insert_node(ctl, hmap, hmap->root, hash, key, v, 0, &added);
626
627 new_map.root = new_root;
628 if (added) {
629 HMAP_LOG("The key was added successfully");
630 new_map.len = hmap->len + 1;
631 } else {
632 HMAP_LOG("The key existed, just updated the value");
633 new_map.len = hmap->len;
634 }
635 return new_map;
636}
static hmap_kv_entry_t * hmap_new_kv_entry(const srn_context_t *ctx, hmap_hash_t hash, hmap_key_t *k, void *v)
Abstract away the kv entry allocation.
Definition hashmap.c:187
static hmap_node_t * hmap_singleton_node(const srn_context_t *ctx, hmap_hash_t hash, uint32_t shift, hmap_kv_entry_t *kv)
Definition hashmap.c:205
#define HMAP_LOG(...)
Definition hashmap.c:48
static hmap_node_t * hmap_insert_node(const hmap_control_t *ctl, const hmap_t *hmap, const hmap_node_t *node, hmap_hash_t hash, hmap_key_t *k, void *v, uint32_t shift, bool *out_added)
Recursively insert a new node containing the k and v.
Definition hashmap.c:304
HMAP_HASH_TYPE hmap_hash_t
Definition hashmap.h:61
hmap_key_t *(* copy_key)(const hmap_t *hmap, const hmap_key_t *k)
Clone k into the map's map_ctx before the map retains it, so the map never holds a pointer into the i...
Definition hashmap.h:119
hmap_hash_t(* hash)(const hmap_t *hmap, const hmap_key_t *k)
Definition hashmap.h:111
Definition hashmap.h:72
hmap_node_t * root
Definition hashmap.h:98
size_t len
Number of key/value pairs in the map.
Definition hashmap.h:97
Here is the call graph for this function:
Here is the caller graph for this function:

◆ hmap_lookup()

void * hmap_lookup ( const hmap_t * hmap,
const hmap_key_t * k,
void * default_value )
nodiscard

Lookup the given k in the given hmap and return the value if it's been found.

Otherwise return the default_value value. Since a nullptr is a valid value for any keys, returning a nullptr is not a good option to indicate that the key does not exist, So it's safer to use explicit values to differenciate between a missing key and a legit nullptr as a value.

Definition at line 665 of file hashmap.c.

665 {
666 return hmap_default_control.lookup(&hmap_default_control, hmap, k, default_value);
667}
Here is the caller graph for this function:

◆ hmap_lookup_ctl()

void * hmap_lookup_ctl ( const hmap_control_t * ctl,
const hmap_t * hmap,
const hmap_key_t * k,
void * default_value )

Just like the hmap_lookup function but, it receives a hmap_control_t to customize the comparison and hashing function.

Definition at line 638 of file hashmap.c.

640 {
641 PANIC_IF_NULL(ctl);
642 PANIC_IF_NULL(hmap);
643 PANIC_IF_NULL(k);
644
645 PANIC_IF_NULL(hmap->map_ctx);
646
647 if (hmap->root == nullptr) {
648 return default_value;
649 }
650
651 hmap_hash_t hash = ctl->hash(hmap, k);
652 return hmap_lookup_in_node(ctl, hmap, hmap->root, hash, k, 0, default_value);
653}
static void * hmap_lookup_in_node(const hmap_control_t *ctl, const hmap_t *hmap, const hmap_node_t *node, hmap_hash_t hash, const hmap_key_t *k, uint32_t shift, void *default_value)
Definition hashmap.c:535
Here is the call graph for this function:
Here is the caller graph for this function:

◆ hmap_make_key()

hmap_key_t * hmap_make_key ( srn_context_t * ctx,
void * data,
size_t len )

Create a new key out of the given data, with the given len.

Definition at line 669 of file hashmap.c.

669 {
670 PANIC_IF_NULL(ctx);
671 PANIC_IF_NULL(data);
672 hmap_key_t *key = ALLOC(ctx, hmap_key_t);
673 key->data = data;
674 key->len = len;
675 return key;
676}

Variable Documentation

◆ hmap_default_control

hmap_control_t hmap_default_control
extern

Definition at line 690 of file hashmap.c.

690 {
691 .cmp = &hmap_internal_cmp,
692 .hash = &hmap_hash_internal,
693 .insert = &hmap_insert_ctl,
694 .lookup = &hmap_lookup_ctl,
695 .copy_key = &hmap_copy_key,
696};
static bool hmap_internal_cmp(const hmap_t *hmap, const hmap_key_t *a, const hmap_key_t *b)
Definition hashmap.c:583
void * hmap_lookup_ctl(const hmap_control_t *ctl, const hmap_t *hmap, const hmap_key_t *k, void *default_value)
Just like the hmap_lookup function but, it receives a hmap_control_t to customize the comparison and ...
Definition hashmap.c:638
hmap_t hmap_insert_ctl(const hmap_control_t *ctl, const hmap_t *hmap, hmap_key_t *k, void *v)
Just like the hmap_insert function but, it receives a hmap_control_t to customize the comparison and ...
Definition hashmap.c:594
static hmap_hash_t hmap_hash_internal(const hmap_t *hmap, const hmap_key_t *k)
Definition hashmap.c:589
hmap_key_t * hmap_copy_key(const hmap_t *hmap, const hmap_key_t *k)
The default copy_key hook.
Definition hashmap.c:678