Live data from Hacker News

Show HN: A hash array-mapped trie implementation in C

github.com

1–10 of 59 posts

Show HN: A hash array-mapped trie implementation in C

#1
Long-simmering side project that is finally ready to see the light. HAMTs are a cool persistent data structure and implementing one has been a lot of fun. Beyond the code, there is likely some value in the extensive and largely complete implementation docs; basic benchmarks are linked in the README, too.

Kind of aiming to be "the libavl for HAMTs". That is obviously a high and aspirational bar but a distinct possibility if it stirs up a little interest and/or contribution.

Anyways, it's time for this to go out, collect feedback and maybe even some use outside of toy projects. Let me know how it goes.

Show HN: A hash array-mapped trie implementation in C
github.com

Re: Show HN: A hash array-mapped trie implementation in C

#2
I discovered HAMTs first when Erlang added support for first-class maps, and it was sort of a "holy shit!" moment for me. They felt like the "holy grail" of data structures for me; I can treat any updates as "copies" without the cost of a copy.

About a year later, I learned Clojure, and fell even more in love with the data structure; when the language fully embraces a useful data structure, it changes the way you think about the entire program, and now it's sort of hard for me to go back to languages that don't have a good HAMT implementation.

I mean, I still do it, but I do think that having a "go to standard" in C really has the potential to set a great precedent.

Re: Show HN: A hash array-mapped trie implementation in C

#3
Just a note about your 'exported memory allocation' API:

    struct hamt_allocator {
        void *(*malloc)(const size_t size);
        void *(*realloc)(void *chunk, const size_t size);
        void (*free)(void *chunk);
    };
This whole thing could just be:

    struct hamt_allocator {
        void *cookie;
        void*  (*realloc) (struct hamt_allocator* h, void* chk, const size_t size);
    };
With the following constraints:

1. `realloc(H, nullptr, N)` -- allocated N bytes

2. `realloc(H, p, 0)` -- frees the pointer p

3. `realloc(H, p, N)` -- resizes the pointer p

And, the user has access to a 'context' (`cookie`) so they can use a (for instance) pool allocation scheme. Personally, I like a slightly different API:

    struct hamt_allocator {
        void *cookie;
        void*  (*realloc) (struct hamt_allocator* h, void* chk, const size_t oldsize, const size_t newsize);
    };
With the following constraints:

1. `realloc(H, nullptr, 0, N)` -- allocated N bytes

2. `realloc(H, p, N, 0)` -- frees the pointer p

3. `realloc(H, p, N, M)` -- resizes the pointer p

But I know a lot of people get confused and/or don't like having to pass (& thus keep) so much information to the allocator.

Re: Show HN: A hash array-mapped trie implementation in C

#4
post #3

Just a note about your 'exported memory allocation' API: struct hamt_allocator { void *(*malloc)(const size_t size); void *(*realloc)(void *chunk, const size_t size); void (*free)(void *chunk); }; This whole thing could just be: struct hamt_allocator { void *cookie; void* (*realloc) (struct hamt_allocator* h, void* chk, const size_t size); }; With the following constraints: 1. `realloc(H, nullptr, N)` -- allocated N…

I generally like this pattern, but why pass the allocator instead of the cookie?

Re: Show HN: A hash array-mapped trie implementation in C

#5
post #3

Just a note about your 'exported memory allocation' API: struct hamt_allocator { void *(*malloc)(const size_t size); void *(*realloc)(void *chunk, const size_t size); void (*free)(void *chunk); }; This whole thing could just be: struct hamt_allocator { void *cookie; void* (*realloc) (struct hamt_allocator* h, void* chk, const size_t size); }; With the following constraints: 1. `realloc(H, nullptr, N)` -- allocated N…

I generally like this pattern, but why pass the allocator instead of the cookie?

If you want to "shim" the API, then it's easier to have the whole previous object, rather than just the cookie.

Re: Show HN: A hash array-mapped trie implementation in C

#6
post #3

Just a note about your 'exported memory allocation' API: struct hamt_allocator { void *(*malloc)(const size_t size); void *(*realloc)(void *chunk, const size_t size); void (*free)(void *chunk); }; This whole thing could just be: struct hamt_allocator { void *cookie; void* (*realloc) (struct hamt_allocator* h, void* chk, const size_t size); }; With the following constraints: 1. `realloc(H, nullptr, N)` -- allocated N…

Sadly, this pattern doesn't work with standard realloc() anymore. C23 makes this undefined behavior due to existing non-conforming implementations.

https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf

Re: Show HN: A hash array-mapped trie implementation in C

#7
post #3

Just a note about your 'exported memory allocation' API: struct hamt_allocator { void *(*malloc)(const size_t size); void *(*realloc)(void *chunk, const size_t size); void (*free)(void *chunk); }; This whole thing could just be: struct hamt_allocator { void *cookie; void* (*realloc) (struct hamt_allocator* h, void* chk, const size_t size); }; With the following constraints: 1. `realloc(H, nullptr, N)` -- allocated N…

The biggest advantages here are not having a single function, but instead:

1. Having a context.

2. Having free and realloc getting the old size. Not having the size passed is more or less consensually regarded as a mistake in the C stdlib.

Re: Show HN: A hash array-mapped trie implementation in C

#8
post #5

Earlier quoted context omitted.

I generally like this pattern, but why pass the allocator instead of the cookie?

If you want to "shim" the API, then it's easier to have the whole previous object, rather than just the cookie.

You don't need the cookie then. You can just allocate a larger struct (sort of subclassing it). You save a pointer indirection.
Post reply on HN