public inbox for ~johnnyrichard/olang-devel@lists.sr.ht
 help / color / mirror / code / Atom feed
* [olang/patches/.build.yml] build success
  2024-02-27 19:59 ` [PATCH olang v2 2/2] utils: create hash map data structure Johnny Richard
@ 2024-02-27 19:01   ` builds.sr.ht
  2024-02-27 23:11   ` [PATCH olang v2 2/2] utils: create hash map data structure Carlos Maniero
  1 sibling, 0 replies; 5+ messages in thread
From: builds.sr.ht @ 2024-02-27 19:01 UTC (permalink / raw)
  To: Johnny Richard; +Cc: ~johnnyrichard/olang-devel

olang/patches/.build.yml: SUCCESS in 37s

[introduce hash map data structure][0] v2 from [Johnny Richard][1]

[0]: https://lists.sr.ht/~johnnyrichard/olang-devel/patches/49853
[1]: mailto:johnny@johnnyrichard.com

✓ #1158194 SUCCESS olang/patches/.build.yml https://builds.sr.ht/~johnnyrichard/job/1158194

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH olang v2 0/2] introduce hash map data structure
@ 2024-02-27 19:59 Johnny Richard
  2024-02-27 19:59 ` [PATCH olang v2 1/2] build: add clean target on root Makefile Johnny Richard
  2024-02-27 19:59 ` [PATCH olang v2 2/2] utils: create hash map data structure Johnny Richard
  0 siblings, 2 replies; 5+ messages in thread
From: Johnny Richard @ 2024-02-27 19:59 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

This patchset creates a simple hash map implementation. We need this
data structure before start implementing the parser. After this patchset
I believe we should be able to start the parser.

Johnny Richard (2):
  build: add clean target on root Makefile
  utils: create hash map data structure

 Makefile              |   6 ++
 src/map.c             | 140 ++++++++++++++++++++++++++++++++++++++++++
 src/map.h             |  59 ++++++++++++++++++
 tests/unit/map_test.c |  77 +++++++++++++++++++++++
 4 files changed, 282 insertions(+)
 create mode 100644 src/map.c
 create mode 100644 src/map.h
 create mode 100644 tests/unit/map_test.c

-- 
2.43.2


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH olang v2 1/2] build: add clean target on root Makefile
  2024-02-27 19:59 [PATCH olang v2 0/2] introduce hash map data structure Johnny Richard
@ 2024-02-27 19:59 ` Johnny Richard
  2024-02-27 19:59 ` [PATCH olang v2 2/2] utils: create hash map data structure Johnny Richard
  1 sibling, 0 replies; 5+ messages in thread
From: Johnny Richard @ 2024-02-27 19:59 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

In order to make easier recompile the whole project and start from a
fresh environemnt, this patch introduce a **clean** target on project
root Makefile which uses the tests/unit and tests/integration **clean**.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 Makefile | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Makefile b/Makefile
index ea6ba53..662d039 100644
--- a/Makefile
+++ b/Makefile
@@ -38,6 +38,12 @@ unit-test:
 	$(MAKE)
 	$(MAKE) -C tests/unit/
 
+.PHONY: clean
+clean:
+	$(MAKE) -C tests/integration/ clean
+	$(MAKE) -C tests/unit/ clean
+	@rm -rf build/ 0c
+
 .PHONY: check
 check:
 	$(MAKE)
-- 
2.43.2


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH olang v2 2/2] utils: create hash map data structure
  2024-02-27 19:59 [PATCH olang v2 0/2] introduce hash map data structure Johnny Richard
  2024-02-27 19:59 ` [PATCH olang v2 1/2] build: add clean target on root Makefile Johnny Richard
@ 2024-02-27 19:59 ` Johnny Richard
  2024-02-27 19:01   ` [olang/patches/.build.yml] build success builds.sr.ht
  2024-02-27 23:11   ` [PATCH olang v2 2/2] utils: create hash map data structure Carlos Maniero
  1 sibling, 2 replies; 5+ messages in thread
From: Johnny Richard @ 2024-02-27 19:59 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

This is a simple hash map implementation using FNV1A 32 bits hash
function.  The map starts with capacity of 32 but is expandable.

Initially there is no function to destroy the map.  Hence, the memory
ownership will be on the callee.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/map.c             | 140 ++++++++++++++++++++++++++++++++++++++++++
 src/map.h             |  59 ++++++++++++++++++
 tests/unit/map_test.c |  77 +++++++++++++++++++++++
 3 files changed, 276 insertions(+)
 create mode 100644 src/map.c
 create mode 100644 src/map.h
 create mode 100644 tests/unit/map_test.c

diff --git a/src/map.c b/src/map.c
new file mode 100644
index 0000000..6665e18
--- /dev/null
+++ b/src/map.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "map.h"
+#include "arena.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static uint32_t
+u32_fnv1a_hash(const char *s);
+
+static void
+map_init(map_t *map);
+
+static char *
+_strdup(const char *s, arena_t *arena);
+
+static uint32_t
+map_get_index(map_t *map, uint32_t hash);
+
+map_t *
+map_new(arena_t *arena)
+{
+    map_t *map = (map_t *)arena_alloc(arena, sizeof(map_t));
+    if (map == NULL) {
+        fprintf(stderr, "[FATAL] Out of memory: map_new: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    map->arena = arena;
+    map_init(map);
+    return map;
+}
+
+static void
+map_init(map_t *map)
+{
+    assert(map);
+    map->entries = (map_entry_t *)arena_alloc(map->arena, MAP_INITIAL_CAPACITY * sizeof(map_entry_t));
+    assert(map->entries != NULL);
+    memset(map->entries, 0, MAP_INITIAL_CAPACITY * sizeof(map_entry_t));
+    if (map->entries == NULL) {
+        fprintf(stderr, "[FATAL] Out of memory: map_init: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    map->capacity = MAP_INITIAL_CAPACITY;
+}
+
+static uint32_t
+u32_fnv1a_hash(const char *s)
+{
+    uint32_t hash = U32_FNV1A_OFFSET_BASIS;
+    size_t len = strlen(s);
+    for (size_t i = 0; i < len; ++i) {
+        hash ^= s[i];
+        hash *= U32_FNV1A_PRIME;
+    }
+    return hash;
+}
+
+bool
+map_put(map_t *map, char *key, void *value)
+{
+    assert(map && key);
+    uint32_t hash = u32_fnv1a_hash(key);
+    map_entry_t *entry = map->entries + map_get_index(map, hash);
+
+    if (entry->key == NULL) {
+        *entry = (map_entry_t){ .key = _strdup(key, map->arena), .hash = hash, .value = value, .next = NULL };
+        return true;
+    }
+
+    do {
+        if (entry->hash == hash && strcmp(entry->key, key) == 0) {
+            entry->value = value;
+            break;
+        }
+        if (entry->next == NULL) {
+            entry->next = (map_entry_t *)arena_alloc(map->arena, sizeof(map_entry_t));
+            *entry->next = (map_entry_t){ .key = _strdup(key, map->arena), .hash = hash, .value = value, .next = NULL };
+
+            break;
+        }
+        entry = entry->next;
+    } while (entry != NULL);
+
+    return true;
+}
+
+void *
+map_get(map_t *map, char *key)
+{
+    uint32_t hash = u32_fnv1a_hash(key);
+    map_entry_t *entry = map->entries + map_get_index(map, hash);
+    while (entry != NULL) {
+        if (entry->hash == hash && strcmp(entry->key, key) == 0) {
+            return entry->value;
+        }
+        entry = entry->next;
+    }
+    return NULL;
+}
+
+static uint32_t
+map_get_index(map_t *map, uint32_t hash)
+{
+    uint32_t capacity_mask = map->capacity - 1;
+    return hash & capacity_mask;
+}
+
+static char *
+_strdup(const char *s, arena_t *arena)
+{
+    size_t slen = strlen(s);
+    char *result = arena_alloc(arena, slen + 1);
+    if (result == NULL) {
+        return NULL;
+    }
+
+    memcpy(result, s, slen + 1);
+    return result;
+}
diff --git a/src/map.h b/src/map.h
new file mode 100644
index 0000000..123ad4d
--- /dev/null
+++ b/src/map.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef MAP_H
+#define MAP_H
+
+#include "arena.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#define MAP_INITIAL_CAPACITY 32
+
+#define U32_FNV1A_PRIME 0x01000193
+#define U32_FNV1A_OFFSET_BASIS 0x811c9dc5
+
+typedef struct map map_t;
+typedef struct map_bucket map_bucket_t;
+typedef struct map_entry map_entry_t;
+
+typedef struct map
+{
+    arena_t *arena;
+    map_entry_t *entries;
+    size_t capacity;
+} map_t;
+
+typedef struct map_entry
+{
+    char *key;
+    void *value;
+    uint32_t hash;
+    map_entry_t *next;
+} map_entry_t;
+
+map_t *
+map_new(arena_t *arena);
+
+bool
+map_put(map_t *map, char *key, void *value);
+
+void *
+map_get(map_t *map, char *key);
+
+#endif /* MAP_H */
diff --git a/tests/unit/map_test.c b/tests/unit/map_test.c
new file mode 100644
index 0000000..449bca6
--- /dev/null
+++ b/tests/unit/map_test.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#define MUNIT_ENABLE_ASSERT_ALIASES
+
+#include "arena.h"
+#include "map.h"
+#include "munit.h"
+#include <stdio.h>
+
+#define MAP_TEST_ARENA_CAPACITY (1024 * 16)
+
+static MunitResult
+test_create_new(const MunitParameter params[], void *user_data_or_fixture)
+{
+    arena_t arena = arena_new(MAP_TEST_ARENA_CAPACITY);
+
+    map_t *map = map_new(&arena);
+
+    assert_not_null(map);
+
+    arena_free(&arena);
+
+    return MUNIT_OK;
+}
+
+static MunitResult
+test_map_put_and_get(const MunitParameter params[], void *user_data_or_fixture)
+{
+    arena_t arena = arena_new(MAP_TEST_ARENA_CAPACITY);
+    map_t *map = map_new(&arena);
+
+    int n1 = 1;
+    int n2 = 2;
+
+    map_put(map, "n1", (void *)&n1);
+    map_put(map, "n2", (void *)&n2);
+
+    assert_int(*((int *)map_get(map, "n1")), ==, n1);
+    assert_int(*((int *)map_get(map, "n2")), ==, n2);
+
+    map_put(map, "n1", (void *)&n2);
+
+    assert_int(*((int *)map_get(map, "n1")), ==, n2);
+
+    arena_free(&arena);
+
+    return MUNIT_OK;
+}
+
+static MunitTest tests[] = {
+    { "/test_create_new", test_create_new, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+    { "/test_map_put_and_get", test_map_put_and_get, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+    { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }
+};
+
+static const MunitSuite suite = { "/map", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
+
+int
+main(int argc, char *argv[])
+{
+    return munit_suite_main(&suite, NULL, argc, argv);
+    return EXIT_SUCCESS;
+}
-- 
2.43.2


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH olang v2 2/2] utils: create hash map data structure
  2024-02-27 19:59 ` [PATCH olang v2 2/2] utils: create hash map data structure Johnny Richard
  2024-02-27 19:01   ` [olang/patches/.build.yml] build success builds.sr.ht
@ 2024-02-27 23:11   ` Carlos Maniero
  1 sibling, 0 replies; 5+ messages in thread
From: Carlos Maniero @ 2024-02-27 23:11 UTC (permalink / raw)
  To: Johnny Richard, ~johnnyrichard/olang-devel

Applied! Thank you for the discussion!

To git.sr.ht:~johnnyrichard/olang
   e78b280..badace9  main -> main

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2024-02-27 23:11 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-02-27 19:59 [PATCH olang v2 0/2] introduce hash map data structure Johnny Richard
2024-02-27 19:59 ` [PATCH olang v2 1/2] build: add clean target on root Makefile Johnny Richard
2024-02-27 19:59 ` [PATCH olang v2 2/2] utils: create hash map data structure Johnny Richard
2024-02-27 19:01   ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-27 23:11   ` [PATCH olang v2 2/2] utils: create hash map data structure Carlos Maniero

Code repositories for project(s) associated with this public inbox

	https://git.johnnyrichard.com/olang.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox