Mistivia 1 year ago
commit
00bd7a2c51
17 changed files with 1044 additions and 0 deletions
  1. 5 0
      .gitignore
  2. 38 0
      Makefile
  3. 76 0
      README.md
  4. 9 0
      scripts/runall.sh
  5. 11 0
      src/dymc.h
  6. 10 0
      src/gc_prelude.h
  7. 91 0
      src/map.c
  8. 33 0
      src/map.h
  9. 375 0
      src/rb_tree.c
  10. 61 0
      src/rb_tree.h
  11. 141 0
      src/str.c
  12. 23 0
      src/str.h
  13. 74 0
      src/vec.c
  14. 15 0
      src/vec.h
  15. 33 0
      tests/test_map.c
  16. 23 0
      tests/test_str.c
  17. 26 0
      tests/test_vec.c

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+build/
+*.a
+*.o
+*.d
+*.bin

+ 38 - 0
Makefile

@@ -0,0 +1,38 @@
+cc = gcc
+src = $(shell ls src/*.c)
+obj = $(src:.c=.o)
+ldflags = -lgc
+
+tests=$(shell ls tests/*.c)
+tests_bin=$(tests:.c=.bin)
+
+all: libdymc.a
+	-rm -rf build/
+	-@mkdir -p build/include/dymc/
+	-@mkdir -p build/lib
+	mv libdymc.a build/lib/
+	cp src/*.h build/include/dymc
+
+libdymc.a: $(obj)
+	ar cr $@ $^
+
+test: $(tests_bin)
+	@echo
+	@echo "Run tests:"
+	@scripts/runall.sh $^
+
+$(obj):%.o:%.c
+	$(cc) -c -g $< -MD -MF $@.d -o $@
+
+$(tests_bin):%.bin:%.c libdymc.a
+	$(cc) -g -Isrc/ $(ldflags) $< libdymc.a -MD -MF $@.d -o $@
+
+clean:
+	-rm $(shell find tests/ -name '*.bin')
+	-rm $(shell find . -name '*.o' -or -name '*.a' -or -name '*.d')
+	-rm -rf build
+
+DEPS := $(shell find . -name *.d)
+ifneq ($(DEPS),)
+include $(DEPS)
+endif

+ 76 - 0
README.md

@@ -0,0 +1,76 @@
+# DymC
+
+Utility library for very dynamic typed programming in C with [bdwgc](https://github.com/ivmai/bdwgc).
+
+## Dependencies
+
+Arch Linux:
+
+    sudo pacman -S libgc
+
+Ubuntu/Debian:
+
+    sudo apt install libgc libgc-dev
+
+## Build
+
+    make
+
+Run tests:
+
+    make test
+
+## Examples
+
+### Vector
+
+    #include <dymc.h>
+    #include <stdio.h>
+
+    int main() {
+        void *v = new_vec();
+
+        int *p;
+
+        p= malloc(sizeof(int));
+        *p = 100;
+        vec_push_back(v, p);
+
+        p = malloc(sizeof(int));
+        *p = 101;
+        vec_push_back(v, p);
+
+        for (int i = 0; i < vec_size(v); i++) {
+            printf("%d\n", *(int*)vec_get(v, i));
+        }
+
+        return 0;
+    }
+
+### Map/Dict
+
+    #include <dymc.h>
+    #include <stdio.h>
+
+    int main() {
+        void *d = new_dict();
+        int *p;
+
+        p = malloc(sizeof(int));
+        *p = 1;
+        dict_set(d, "a", p);
+
+        p = malloc(sizeof(int));
+        *p = 2;
+        dict_set(d, "b", p);
+
+        printf("%d\n", *(int*)dict_get(d, "a"));
+
+        void *iter;
+        for (iter = dict_begin(d); iter != NULL; iter = dict_next(d, iter)) {
+            printf("key: %s, value: %d\n", dict_iter_key(iter), *(int*)dict_iter_value(iter));
+        }
+
+        return 0;
+    }
+

+ 9 - 0
scripts/runall.sh

@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+for var in "$@"; do
+    ./$var
+    if [ $? -ne 0 ]; then
+        exit 255
+    fi
+done
+

+ 11 - 0
src/dymc.h

@@ -0,0 +1,11 @@
+#ifndef DYMC_H_
+#define DYMC_H_
+
+#include <stdlib.h>
+#include <gc_prelude.h>
+#include <vec.h>
+#include <map.h>
+#include <str.h>
+
+#endif
+

+ 10 - 0
src/gc_prelude.h

@@ -0,0 +1,10 @@
+#ifndef DYMC_GC_PRELUDE_H_
+#define DYMC_GC_PRELUDE_H_
+
+#include <gc.h>
+
+#define malloc(x) GC_malloc((x))
+#define free(x) GC_free((x))
+#define realloc(x, y) GC_realloc((x), (y))
+
+#endif

+ 91 - 0
src/map.c

@@ -0,0 +1,91 @@
+#include "map.h"
+#include "rb_tree.h"
+
+#include <string.h>
+
+#include "gc_prelude.h"
+
+typedef int (*rb_cmp_t)(void *, void *);
+
+void* new_map(cmp_t compare) {
+    rb_tree_t *tree = malloc(sizeof(rb_tree_t));
+    *tree = (rb_tree_t){NULL, (rb_cmp_t)compare, NULL};
+    return tree;
+}
+
+void map_set(void* self, void* key, void* value) {
+    node_entry_t *iter = rb_tree_find(self, &key);
+    if (iter == NULL) {
+        iter = malloc(sizeof(*iter));
+        iter->key = key;
+        iter->value = value;
+        rb_tree_insert(self, iter);
+    } else {
+        iter->value = value;
+    }
+}
+
+void* map_get(void* self, void* key) {
+    node_entry_t *iter = rb_tree_find(self, &key);
+    if (iter == NULL) return NULL;
+    return iter->value;
+}
+
+void map_erase(void* self, void* key) {
+    rb_tree_remove(self, key);
+}
+
+void* map_begin(void *self) {
+    return rb_tree_min(self);
+}
+
+void* map_next(void *self, void *iter) {
+    return rb_tree_next(self, iter);
+}
+
+void* map_iter_key(void* iter_) {
+    node_entry_t *iter = iter_;
+    return iter->key;
+}
+
+void* map_iter_value(void* iter_) {
+    node_entry_t *iter = iter_;
+    return iter->value;
+}
+
+static int dict_cmp(void **a, void** b) {
+    return strcmp(*a, *b);
+}
+void* new_dict() {
+    return new_map(dict_cmp);
+}
+
+void dict_set(void* self, const char *key, void* value) {
+    map_set(self, (void*)key, value);
+}
+
+void* dict_get(void* self, const char* key) {
+    return map_get(self, (void*)key);
+}
+
+void dict_erase(void* self, const char* key) {
+    map_erase(self, (void*)key);
+}
+
+void* dict_begin(void *self) {
+    return map_begin(self);
+}
+
+void* dict_next(void *self, void *iter) {
+    return map_next(self, iter);
+}
+
+const char* dict_iter_key(void* iter) {
+    return map_iter_key(iter);
+}
+
+void* dict_iter_value(void* iter) {
+    return map_iter_value(iter);
+}
+
+

+ 33 - 0
src/map.h

@@ -0,0 +1,33 @@
+#ifndef DYMC_MAP_H_
+#define DYMC_MAP_H_
+
+#include "rb_tree.h"
+
+typedef struct {
+    rb_node_t node;
+    void *key;
+    void *value;
+} node_entry_t;
+typedef int (*cmp_t)(void** a, void** b);
+
+void* new_map(cmp_t compare);
+void map_set(void* self, void* key, void* value);
+void* map_get(void* self, void* key);
+void map_erase(void* self, void* key);
+
+void* map_begin(void *self);
+void* map_next(void *self, void *iter);
+void* map_iter_key(void* iter);
+void* map_iter_value(void* iter);
+
+void* new_dict();
+void dict_set(void* self, const char *key, void* value);
+void* dict_get(void* self, const char* key);
+void dict_erase(void* self, const char* key);
+
+void* dict_begin(void *self);
+void* dict_next(void *self, void *iter);
+const char* dict_iter_key(void* iter);
+void* dict_iter_value(void* iter);
+
+#endif

+ 375 - 0
src/rb_tree.c

@@ -0,0 +1,375 @@
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "rb_tree.h"
+
+#define RED 1
+#define BLACK 0
+
+static rb_node_t *rb_tree_minmax(rb_tree_t *, int);
+void *rb_tree_min(rb_tree_t *head) { return rb_tree_minmax(head, -1); }
+void *rb_tree_max(rb_tree_t *head) { return rb_tree_minmax(head, 1); }
+
+void *rb_tree_left(void *node) {
+    rb_node_t *elm = node;
+    if (node == NULL) return NULL;
+    return elm->entry.rbe_left;
+}
+void *rb_tree_right(void *node) {
+    rb_node_t *elm = node;
+    if (node == NULL) return NULL;
+    return elm->entry.rbe_right;
+}
+void *rb_tree_parent(void *node) {
+    rb_node_t *elm = node;
+    if (node == NULL) return NULL;
+    return elm->entry.rbe_parent;
+}
+
+static void augment(rb_tree_t *head, rb_node_t *elm) {
+    if (head->augment != NULL) head->augment(elm);
+}
+
+static void rb_tree_insert_color(rb_tree_t *head, rb_node_t *elm);
+static void rb_tree_remove_color(rb_tree_t *head, rb_node_t *parent,
+                                 rb_node_t *elm);
+
+static void rotate_left(rb_tree_t *head, rb_node_t *elm) {
+    rb_node_t *tmp = elm->entry.rbe_right;
+    if ((elm->entry.rbe_right = tmp->entry.rbe_left)) {
+        tmp->entry.rbe_left->entry.rbe_parent = elm;
+    }
+    augment(head, elm);
+    if ((tmp->entry.rbe_parent = elm->entry.rbe_parent)) {
+        if (elm == elm->entry.rbe_parent->entry.rbe_left)
+            elm->entry.rbe_parent->entry.rbe_left = tmp;
+        else
+            elm->entry.rbe_parent->entry.rbe_right = tmp;
+    } else {
+        head->rbh_root = tmp;
+    }
+    tmp->entry.rbe_left = elm;
+    elm->entry.rbe_parent = tmp;
+    augment(head, tmp);
+    if (tmp->entry.rbe_parent) {
+        augment(head, tmp->entry.rbe_parent);
+    }
+}
+
+static void rotate_right(rb_tree_t *head, rb_node_t *elm) {
+    rb_node_t *tmp = elm->entry.rbe_left;
+    if ((elm->entry.rbe_left = tmp->entry.rbe_right)) {
+        tmp->entry.rbe_right->entry.rbe_parent = elm;
+    }
+    augment(head, elm);
+    if ((tmp->entry.rbe_parent = elm->entry.rbe_parent)) {
+        if (elm == elm->entry.rbe_parent->entry.rbe_left)
+            elm->entry.rbe_parent->entry.rbe_left = tmp;
+        else
+            elm->entry.rbe_parent->entry.rbe_right = tmp;
+    } else {
+        head->rbh_root = tmp;
+    }
+    tmp->entry.rbe_right = elm;
+    elm->entry.rbe_parent = tmp;
+    augment(head, tmp);
+    if (tmp->entry.rbe_parent) {
+        augment(head, tmp->entry.rbe_parent);
+    }
+}
+
+static void rb_tree_insert_color(rb_tree_t *head, rb_node_t *elm) {
+    rb_node_t *parent, *gparent, *tmp;
+    while ((parent = elm->entry.rbe_parent) && parent->entry.rbe_color == 1) {
+        gparent = parent->entry.rbe_parent;
+        if (parent == gparent->entry.rbe_left) {
+            tmp = gparent->entry.rbe_right;
+            if (tmp && tmp->entry.rbe_color == 1) {
+                tmp->entry.rbe_color = BLACK;
+                parent->entry.rbe_color = BLACK;
+                gparent->entry.rbe_color = RED;
+                elm = gparent;
+                continue;
+            }
+            if (parent->entry.rbe_right == elm) {
+                rotate_left(head, parent);
+                tmp = parent;
+                parent = elm;
+                elm = tmp;
+            }
+            parent->entry.rbe_color = BLACK;
+            gparent->entry.rbe_color = RED;
+            rotate_right(head, gparent);
+        } else {
+            tmp = gparent->entry.rbe_left;
+            if (tmp && tmp->entry.rbe_color == 1) {
+                tmp->entry.rbe_color = BLACK;
+                parent->entry.rbe_color = BLACK;
+                gparent->entry.rbe_color = RED;
+                ;
+                elm = gparent;
+                continue;
+            }
+            if (parent->entry.rbe_left == elm) {
+                rotate_right(head, parent);
+                tmp = parent;
+                parent = elm;
+                elm = tmp;
+            }
+            parent->entry.rbe_color = BLACK;
+            gparent->entry.rbe_color = RED;
+            rotate_left(head, gparent);
+        }
+    }
+    head->rbh_root->entry.rbe_color = BLACK;
+}
+
+static void rb_tree_remove_color(rb_tree_t *head, rb_node_t *parent,
+                                 rb_node_t *elm) {
+    rb_node_t *tmp;
+    while ((elm == NULL || elm->entry.rbe_color == 0) &&
+           elm != head->rbh_root) {
+        if (parent->entry.rbe_left == elm) {
+            tmp = parent->entry.rbe_right;
+            if (tmp->entry.rbe_color == 1) {
+                tmp->entry.rbe_color = BLACK;
+                parent->entry.rbe_color = RED;
+                rotate_left(head, parent);
+                tmp = parent->entry.rbe_right;
+            }
+            if ((tmp->entry.rbe_left == NULL ||
+                 tmp->entry.rbe_left->entry.rbe_color == 0) &&
+                (tmp->entry.rbe_right == NULL ||
+                 tmp->entry.rbe_right->entry.rbe_color == 0)) {
+                tmp->entry.rbe_color = RED;
+                elm = parent;
+                parent = elm->entry.rbe_parent;
+            } else {
+                if (tmp->entry.rbe_right == NULL ||
+                    tmp->entry.rbe_right->entry.rbe_color == 0) {
+                    rb_node_t *oleft;
+                    if ((oleft = tmp->entry.rbe_left))
+                        oleft->entry.rbe_color = BLACK;
+                    tmp->entry.rbe_color = RED;
+                    rotate_right(head, tmp);
+                    tmp = parent->entry.rbe_right;
+                }
+                tmp->entry.rbe_color = parent->entry.rbe_color;
+                parent->entry.rbe_color = BLACK;
+                if (tmp->entry.rbe_right)
+                    tmp->entry.rbe_right->entry.rbe_color = BLACK;
+                rotate_left(head, parent);
+                elm = head->rbh_root;
+                break;
+            }
+        } else {
+            tmp = parent->entry.rbe_left;
+            if (tmp->entry.rbe_color == 1) {
+                tmp->entry.rbe_color = BLACK;
+                parent->entry.rbe_color = RED;
+                rotate_right(head, parent);
+                tmp = parent->entry.rbe_left;
+            }
+            if ((tmp->entry.rbe_left == NULL ||
+                 tmp->entry.rbe_left->entry.rbe_color == 0) &&
+                (tmp->entry.rbe_right == NULL ||
+                 tmp->entry.rbe_right->entry.rbe_color == 0)) {
+                tmp->entry.rbe_color = RED;
+                elm = parent;
+                parent = elm->entry.rbe_parent;
+            } else {
+                if (tmp->entry.rbe_left == NULL ||
+                    tmp->entry.rbe_left->entry.rbe_color == 0) {
+                    rb_node_t *oright;
+                    if ((oright = tmp->entry.rbe_right))
+                        oright->entry.rbe_color = BLACK;
+                    tmp->entry.rbe_color = RED;
+                    rotate_left(head, tmp);
+                    tmp = parent->entry.rbe_left;
+                }
+                tmp->entry.rbe_color = parent->entry.rbe_color;
+                parent->entry.rbe_color = BLACK;
+                if (tmp->entry.rbe_left)
+                    tmp->entry.rbe_left->entry.rbe_color = BLACK;
+                rotate_right(head, parent);
+                elm = head->rbh_root;
+                break;
+            }
+        }
+    }
+    if (elm) elm->entry.rbe_color = BLACK;
+}
+
+void rb_tree_remove(rb_tree_t *head, void *elmv) {
+    rb_node_t *elm = elmv;
+    rb_node_t *child, *parent;
+    int color;
+    if (elm->entry.rbe_left == NULL)
+        child = elm->entry.rbe_right;
+    else if (elm->entry.rbe_right == NULL)
+        child = elm->entry.rbe_left;
+    else {
+        rb_node_t *old = elm, *left;
+        elm = elm->entry.rbe_right;
+        while ((left = elm->entry.rbe_left))
+            elm = left;
+        child = elm->entry.rbe_right;
+        parent = elm->entry.rbe_parent;
+        color = elm->entry.rbe_color;
+        if (child) child->entry.rbe_parent = parent;
+        if (parent) {
+            if (parent->entry.rbe_left == elm)
+                parent->entry.rbe_left = child;
+            else
+                parent->entry.rbe_right = child;
+            augment(head, parent);
+        } else
+            head->rbh_root = child;
+        if (elm->entry.rbe_parent == old) parent = elm;
+        elm->entry = old->entry;
+        if (old->entry.rbe_parent) {
+            if ((old->entry.rbe_parent)->entry.rbe_left == old)
+                (old->entry.rbe_parent)->entry.rbe_left = elm;
+            else
+                (old->entry.rbe_parent)->entry.rbe_right = elm;
+            augment(head, old->entry.rbe_parent);
+        } else
+            head->rbh_root = elm;
+        old->entry.rbe_left->entry.rbe_parent = elm;
+        if (old->entry.rbe_right) old->entry.rbe_right->entry.rbe_parent = elm;
+        if (parent) {
+            left = parent;
+            if (head->augment != NULL) {
+                do {
+                    augment(head, left);
+                } while ((left = left->entry.rbe_parent));
+            }
+        }
+        goto color;
+    }
+    parent = elm->entry.rbe_parent;
+    color = elm->entry.rbe_color;
+    if (child) child->entry.rbe_parent = parent;
+    if (parent) {
+        if (parent->entry.rbe_left == elm)
+            parent->entry.rbe_left = child;
+        else
+            parent->entry.rbe_right = child;
+        rb_node_t *goback = parent;
+        if (head->augment != NULL) {
+            do {
+                augment(head, goback);
+            } while ((goback = goback->entry.rbe_parent));
+        }
+    } else
+        head->rbh_root = child;
+color:
+    if (color == 0) rb_tree_remove_color(head, parent, child);
+}
+
+void *rb_tree_insert(rb_tree_t *head, void *elmv) {
+    rb_node_t *elm = elmv;
+    rb_node_t *tmp;
+    rb_node_t *parent = NULL;
+    int comp = 0;
+    tmp = head->rbh_root;
+    while (tmp) {
+        parent = tmp;
+        comp = head->cmp((void *)elm->content, (void *)parent->content);
+        if (comp < 0)
+            tmp = tmp->entry.rbe_left;
+        else if (comp > 0)
+            tmp = tmp->entry.rbe_right;
+        else
+            return tmp;
+    }
+    elm->entry.rbe_parent = parent;
+    elm->entry.rbe_left = elm->entry.rbe_right = NULL;
+    elm->entry.rbe_color = RED;
+    if (parent != NULL) {
+        if (comp < 0)
+            parent->entry.rbe_left = elm;
+        else
+            parent->entry.rbe_right = elm;
+        rb_node_t *goback = parent;
+        if (head->augment != NULL) {
+            do {
+                augment(head, goback);
+            } while ((goback = goback->entry.rbe_parent));
+        }
+    } else
+        head->rbh_root = elm;
+    rb_tree_insert_color(head, elm);
+    return (NULL);
+}
+
+void *rb_tree_find(rb_tree_t *head, void *key) {
+    rb_node_t *tmp = head->rbh_root;
+    int comp;
+    while (tmp) {
+        comp = head->cmp(key, (void *)tmp->content);
+        if (comp < 0)
+            tmp = tmp->entry.rbe_left;
+        else if (comp > 0)
+            tmp = tmp->entry.rbe_right;
+        else
+            return tmp;
+    }
+    return (NULL);
+}
+
+void *rb_tree_next(rb_tree_t *head, void *elmv) {
+    rb_node_t *elm = elmv;
+    if (elm->entry.rbe_right) {
+        elm = elm->entry.rbe_right;
+        while (elm->entry.rbe_left)
+            elm = elm->entry.rbe_left;
+    } else {
+        if (elm->entry.rbe_parent &&
+            (elm == (elm->entry.rbe_parent)->entry.rbe_left))
+            elm = elm->entry.rbe_parent;
+        else {
+            while (elm->entry.rbe_parent &&
+                   (elm == (elm->entry.rbe_parent)->entry.rbe_right))
+                elm = elm->entry.rbe_parent;
+            elm = elm->entry.rbe_parent;
+        }
+    }
+    return elm;
+}
+
+static rb_node_t *rb_tree_minmax(rb_tree_t *head, int val) {
+    rb_node_t *tmp = head->rbh_root;
+    rb_node_t *parent = NULL;
+    while (tmp) {
+        parent = tmp;
+        if (val < 0)
+            tmp = tmp->entry.rbe_left;
+        else
+            tmp = tmp->entry.rbe_right;
+    }
+    return parent;
+};
+

+ 61 - 0
src/rb_tree.h

@@ -0,0 +1,61 @@
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RB_TREE_H_
+#define RB_TREE_H_
+
+#include <stdlib.h>
+
+struct rb_node {
+    struct {
+        struct rb_node *rbe_left;
+        struct rb_node *rbe_right;
+        struct rb_node *rbe_parent;
+        int rbe_color;
+    } entry;
+    char content[0];
+};
+typedef struct rb_node rb_node_t;
+
+struct rb_tree {
+    rb_node_t *rbh_root;
+    int (*cmp)(void *k1, void *k2);
+    void (*augment)(void *elm);
+};
+typedef struct rb_tree rb_tree_t;
+
+void rb_tree_remove(rb_tree_t *, void *iter);
+
+// return a iterator
+void *rb_tree_insert(rb_tree_t *, void *treenode);
+void *rb_tree_find(rb_tree_t *, void *val);
+void *rb_tree_next(rb_tree_t *, void *iter);
+void *rb_tree_min(rb_tree_t *);
+void *rb_tree_max(rb_tree_t *);
+void *rb_tree_left(void *node);
+void *rb_tree_right(void *node);
+void *rb_tree_parent(void *node);
+
+#endif

+ 141 - 0
src/str.c

@@ -0,0 +1,141 @@
+#include "str.h"
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gc_prelude.h"
+#include "vec.h"
+
+void *str_split(const char *str, char delim) {
+    void* ret = new_vec();
+
+    if (str == NULL) return NULL;
+    if (*str == '\n') {
+        return ret;
+    }
+    int count = 0;
+    const char *begin = str;
+    for (const char *p = str; *p != '\0'; p++) {
+        if (*p != delim && !(delim == '\0' && isspace(*p))) {
+            continue;
+        }
+        int size = p - begin;
+        if (size > 0) count++;
+    }
+    count++;
+
+    begin = str;
+    int i = 0;
+    bool finished = false;
+    for (const char *p = str; !finished; p++) {
+        if (*p == '\0') finished = true;
+        if (*p != delim && *p != '\0' && !(delim == '\0' && isspace(*p))) {
+            continue;
+        }
+        int size = p - begin;
+        if (size == 0) {
+            begin = p + 1;
+            continue;
+        }
+        char *buf = malloc(sizeof(char) * (size + 1));
+        buf[size] = '\0';
+        memcpy(buf, begin, size * sizeof(char));
+        begin = p + 1;
+        vec_push_back(ret, buf);
+    }
+    return ret;
+}
+
+char *str_strip(const char *str) {
+    if (str == NULL) return NULL;
+    int len = strlen(str);
+    const char *begin = str;
+    const char *end = str + len - 1;
+    while (isspace(*begin) && begin < end) {
+        begin++;
+    }
+    while (isspace(*end) && end >= begin) {
+        end--;
+    }
+    len = end - begin + 1;
+    char *buf = malloc(sizeof(char) * (len) + 1);
+    buf[len] = '\0';
+    memcpy(buf, begin, len);
+    return buf;
+}
+
+typedef struct {
+    size_t size;
+    size_t cap;
+    char *buf;
+} str_builder_t;
+
+// string stream
+void* new_ss() {
+    str_builder_t *self = malloc(sizeof(str_builder_t));
+    *self = (str_builder_t){.size = 0, .cap = 16};
+    self->buf = malloc(sizeof(char) * 17);
+    return self;
+}
+
+static void ss_reserve(str_builder_t *self, int extra) {
+    if (self->size + extra <= self->cap) {
+        return;
+    }
+    int new_cap = (self->size + extra) * 2;
+    self->buf = realloc(self->buf, new_cap + 1);
+    memset(self->buf + self->cap, 0, new_cap - self->cap + 1);
+    self->cap = new_cap;
+}
+
+void ss_add(void *self_, char *format, ...) {
+    str_builder_t *self = self_;
+    va_list va1;
+    va_list va2;
+    va_start(va1, format);
+    va_copy(va2, va1);
+    int size = vsnprintf(NULL, 0, format, va1);
+    ss_reserve(self, size);
+    vsnprintf(self->buf + self->size, self->cap - self->size + 1, format, va2);
+    self->size += size;
+}
+
+void ss_addc(void *self_, char c) {
+    str_builder_t *self = self_;
+    ss_reserve(self, 1);
+    self->buf[self->size] = c;
+    self->size++;
+}
+
+char *ss_cstr(void *self_) {
+    str_builder_t *self = self_;
+    return self->buf;    
+}
+
+size_t ss_size(void *self_) {
+    str_builder_t *self = self_;
+    return self->size;
+}
+
+char *fgetline(FILE *fp) {
+    void *ss = new_ss();
+    while (true) {
+        int c = fgetc(fp);
+        if (c == EOF && ss_size(ss) == 0) return NULL;
+        if (c != EOF) ss_addc(ss, c);
+        if (c == EOF || c == '\n') return ss_cstr(ss);
+    }
+    return NULL;
+}
+
+int fpeek(FILE *fp) {
+    int c = fgetc(fp);
+    if (c == EOF) return c;
+    ungetc(c, fp);
+    return c;
+}

+ 23 - 0
src/str.h

@@ -0,0 +1,23 @@
+#ifndef DYMC_STR_H_
+#define DYMC_STR_H_
+
+#include <stdio.h>
+#include <stddef.h>
+
+char *str_strip(const char *str);
+
+// return: vec of char*
+void *str_split(const char *str, char delim);
+
+
+// string stream
+void* new_ss();
+void ss_add(void *self, char *format, ...);
+void ss_addc(void *self, char c);
+char *ss_cstr(void *self);
+size_t ss_size(void* self);
+
+char *fgetline(FILE *fp);
+int fpeek(FILE *fp);
+
+#endif

+ 74 - 0
src/vec.c

@@ -0,0 +1,74 @@
+#include "vec.h"
+
+#include <string.h>
+
+#include "gc_prelude.h"
+
+
+struct vec {
+    size_t length;
+    size_t capacity;
+    void** buf;
+};
+
+static void vec_enlarge(struct vec* self) {
+    self->buf = realloc(self->buf, self->capacity * sizeof(void*) * 2);
+    self->capacity = self->capacity * 2;
+}
+
+void *new_vec() {
+    struct vec* vec = malloc(sizeof(struct vec));
+    vec->length = 0;
+    vec->capacity = 16;
+    vec->buf = malloc(16 * sizeof(void*));
+    return vec;
+}
+
+void vec_push_back(void *self_, void* obj) {
+    struct vec *self = self_;
+    if (self->length == self->capacity) vec_enlarge(self);
+    self->buf[self->length] = obj;
+    self->length++;
+}
+
+void* vec_get(void *self_, size_t n) {
+    struct vec *self = self_;
+    if (n < 0 || n >= self->length) return NULL;
+    return self->buf[n];
+}
+
+
+void vec_erase(void *self_, size_t n) {
+    struct vec *self = self_;
+    if (self->length <= n) {
+        return;
+    }
+    memmove(self->buf + n, self->buf + n + 1, (self->length - n - 1) * sizeof(void*));
+    self->length--;
+}
+
+size_t vec_size(void *self_) {
+    struct vec *self = self_;
+    return self->length;
+}
+
+void vec_reserve(void *self_, size_t n) {
+    struct vec *self = self_;
+    if (n <= self->capacity) {
+        return;
+    }
+    self->buf = malloc(sizeof(void*) * n);
+    self->capacity = n;
+}
+
+void vec_insert(void *self_, size_t pos, void *obj) {
+    struct vec *self = self_;
+    if (self->length == self->capacity) {
+        vec_enlarge(self);
+    }
+    if (pos > self->length || pos < 0) return;
+    memmove(self->buf + pos + 1, self->buf + pos, sizeof(void*) * (self->length - pos));
+    self->buf[pos] = obj;
+    self->length++;
+}
+

+ 15 - 0
src/vec.h

@@ -0,0 +1,15 @@
+#ifndef DYMC_VEC_H_ 
+#define DYMC_VEC_H_ 
+
+#include <stddef.h>
+
+void *new_vec();
+void vec_push_back(void *self, void* obj);
+void* vec_get(void *self, size_t n);
+size_t vec_length(void *self);
+void vec_erase(void *self, size_t n);
+size_t vec_size(void* self);
+void vec_reserve(void* self, size_t n);
+void vec_insert(void* self, size_t pos, void* obj);
+
+#endif

+ 33 - 0
tests/test_map.c

@@ -0,0 +1,33 @@
+#include "map.h"
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "gc_prelude.h"
+
+int main() {
+    void *dict = new_dict();
+    int *i = malloc(sizeof(int));
+    *i = 1;
+    dict_set(dict, "1", i);
+    i = malloc(sizeof(int));
+    *i = 2;
+    dict_set(dict, "2", i);
+    i = malloc(sizeof(int));
+    *i = 3;
+    dict_set(dict, "3", i);
+    i = malloc(sizeof(int));
+    *i = 4;
+    dict_set(dict, "4", i);
+
+    assert(*(int*)dict_get(dict, "3") == 3);
+
+    void *iter = dict_begin(dict);
+    for (int i = 1; i <= 4; i++) {
+        assert(*(int*)dict_iter_value(iter) == i);
+        iter = dict_next(dict, iter);
+    }
+
+    printf("[PASSED] map\n");
+    return 0;
+}

+ 23 - 0
tests/test_str.c

@@ -0,0 +1,23 @@
+#include "dymc.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+int main() {
+    void *ss = new_ss();
+    ss_add(ss, "test %d", 1);
+    ss_add(ss, ", %d", 2);
+    assert(strcmp("test 1, 2", ss_cstr(ss)) == 0);
+    assert(ss_size(ss) == 9);
+
+    const char *s = "a,bc,def";
+    void *str_list = str_split(s, ',');
+    assert(vec_size(str_list) == 3);
+    assert(strcmp("a", vec_get(str_list, 0)) == 0);
+    assert(strcmp("bc", vec_get(str_list, 1)) == 0);
+    assert(strcmp("def", vec_get(str_list, 2)) == 0);
+
+    printf("[PASSED] str\n");
+    return 0;
+}

+ 26 - 0
tests/test_vec.c

@@ -0,0 +1,26 @@
+#include <assert.h>
+#include <stdio.h>
+
+#include "vec.h"
+#include "gc_prelude.h"
+
+int main() {
+    void *v = new_vec();
+    double *n = malloc(sizeof(double));
+    *n = 42.42;
+    vec_push_back(v, n);
+    assert(*(double*)vec_get(v, 0) == 42.42);
+
+    v = new_vec();
+    for (int i = 0; i < 10000; i++) {
+        int *x = malloc(sizeof(int));
+        *x = i;
+        vec_push_back(v, x);
+        assert(vec_size(v) == i + 1);
+    }
+    for (int i = 0; i < 10000; i++) {
+        assert(*(int*)vec_get(v, i) == i);
+    }
+
+    printf("[PASSED] vec\n");
+}