Serene Runtime 1.0.0
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 (srn_context_t *ctx)
 Create, initialize and return a new hashmap.
 
hmap_t hmap_insert (srn_context_t *ctx, 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, srn_context_t *ctx, 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 (srn_context_t *ctx, 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, srn_context_t *ctx, 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 (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.
 

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_empty()

hmap_t hmap_empty ( srn_context_t * ctx)
nodiscard

Create, initialize and return a new hashmap.

Definition at line 612 of file hashmap.c.

612 {
613 UNUSED(ctx);
614 hmap_t map = {.len = 0, .root = nullptr};
615 return map;
616}
#define UNUSED(x)
Definition utils.h:43
Here is the caller graph for this function:

◆ hmap_hash()

hmap_hash_t hmap_hash ( 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 78 of file hashmap.c.

78 {
79 return srn_hash(ctx->engine, data, len);
80}
srn_hash_t srn_hash(const srn_engine_t *engine, const void *data, size_t len)
Definition engine.c:131
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 ( srn_context_t * ctx,
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.

Definition at line 618 of file hashmap.c.

618 {
619 return hmap_default_control.insert(&hmap_default_control, ctx, hmap, k, v);
620}
hmap_control_t hmap_default_control
Definition hashmap.c:636
Here is the caller graph for this function:

◆ hmap_insert_ctl()

hmap_t hmap_insert_ctl ( const hmap_control_t * ctl,
srn_context_t * ctx,
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 559 of file hashmap.c.

560 {
561 PANIC_IF_NULL(ctl);
562 PANIC_IF_NULL(ctx);
563 PANIC_IF_NULL(hmap);
564 PANIC_IF_NULL(k);
565
566 hmap_hash_t hash = ctl->hash(ctx, k);
567 hmap_t new_map = *hmap;
568
569 HMAP_LOG("Trying to insert a key with the hash: %x", hash);
570
571 if (hmap->root == nullptr) {
572 // First insertion: just create a singleton node.
573 HMAP_LOG("First insertion on an empty map");
574 hmap_kv_entry_t *kv = hmap_new_kv_entry(ctx, hash, k, v);
575 hmap_node_t *root = hmap_singleton_node(ctx, hash, 0, kv);
576
577 new_map.root = root;
578 new_map.len = hmap->len + 1;
579 return new_map;
580 }
581 HMAP_LOG("Find the correct node to insert the key");
582 // Create the intermediate nodes recursively
583 bool added = false;
584 hmap_node_t *new_root = hmap_insert_node(ctl, ctx, hmap->root, hash, k, v, 0, &added);
585
586 new_map.root = new_root;
587 if (added) {
588 HMAP_LOG("The key was added successfully");
589 new_map.len = hmap->len + 1;
590 } else {
591 HMAP_LOG("The key existed, just updated the value");
592 new_map.len = hmap->len;
593 }
594 return new_map;
595}
static hmap_kv_entry_t * hmap_new_kv_entry(srn_context_t *ctx, hmap_hash_t hash, hmap_key_t *k, void *v)
Abstract away the kv entry allocation.
Definition hashmap.c:185
#define HMAP_LOG(...)
Definition hashmap.c:49
static hmap_node_t * hmap_insert_node(const hmap_control_t *ctl, srn_context_t *ctx, 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:298
static hmap_node_t * hmap_singleton_node(srn_context_t *ctx, hmap_hash_t hash, uint32_t shift, hmap_kv_entry_t *kv)
Definition hashmap.c:204
HMAP_HASH_TYPE hmap_hash_t
Definition hashmap.h:61
hmap_hash_t(* hash)(srn_context_t *ctx, const hmap_key_t *k)
Definition hashmap.h:105
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
#define PANIC_IF_NULL(ptr)
Definition utils.h:64
Here is the call graph for this function:

◆ hmap_lookup()

void * hmap_lookup ( srn_context_t * ctx,
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 622 of file hashmap.c.

623 {
624 return hmap_default_control.lookup(&hmap_default_control, ctx, hmap, k, default_value);
625}
Here is the caller graph for this function:

◆ hmap_lookup_ctl()

void * hmap_lookup_ctl ( const hmap_control_t * ctl,
srn_context_t * ctx,
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 597 of file hashmap.c.

598 {
599 PANIC_IF_NULL(ctl);
600 PANIC_IF_NULL(ctx);
601 PANIC_IF_NULL(hmap);
602 PANIC_IF_NULL(k);
603
604 if (hmap->root == nullptr) {
605 return default_value;
606 }
607
608 hmap_hash_t hash = ctl->hash(ctx, k);
609 return hmap_lookup_in_node(ctl, ctx, hmap->root, hash, k, 0, default_value);
610}
static void * hmap_lookup_in_node(const hmap_control_t *ctl, srn_context_t *ctx, const hmap_node_t *node, hmap_hash_t hash, const hmap_key_t *k, uint32_t shift, void *default_value)
Definition hashmap.c:504
Here is the call 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 627 of file hashmap.c.

627 {
628 PANIC_IF_NULL(ctx);
629 PANIC_IF_NULL(data);
630 hmap_key_t *key = ALLOC(ctx, hmap_key_t);
631 key->data = data;
632 key->len = len;
633 return key;
634}
#define ALLOC(ctx, T)
Definition context.h:82
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
Here is the caller graph for this function:

Variable Documentation

◆ hmap_default_control

hmap_control_t hmap_default_control
extern

Definition at line 636 of file hashmap.c.

636 {
637 .cmp = &hmap_internal_cmp,
638 .hash = &hmap_hash_internal,
639 .insert = &hmap_insert_ctl,
640 .lookup = &hmap_lookup_ctl,
641};
static hmap_hash_t hmap_hash_internal(srn_context_t *ctx, const hmap_key_t *k)
Definition hashmap.c:554
void * hmap_lookup_ctl(const hmap_control_t *ctl, srn_context_t *ctx, 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:597
static bool hmap_internal_cmp(srn_context_t *ctx, const hmap_key_t *a, const hmap_key_t *b)
Definition hashmap.c:548
hmap_t hmap_insert_ctl(const hmap_control_t *ctl, srn_context_t *ctx, 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:559