diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fd5c65b3..365bffbd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,11 +4,11 @@ on: push: branches: [ "dev" ] paths-ignore: - - '**.md' + - '*.md' pull_request: branches: [ "dev" ] paths-ignore: - - '**.md' + - '*.md' jobs: unit-tests-linux: diff --git a/bflibc/makefile b/bflibc/makefile index 5d6e2310..908501b3 100644 --- a/bflibc/makefile +++ b/bflibc/makefile @@ -27,7 +27,7 @@ filesystem coreutils stringutils \ bftime thread bfmath \ lock filewriter hash \ rand map tree internal/tree \ -hashmap +hashmap set hashset ### Release settings ifeq ($(CONFIG),release) # release diff --git a/bflibc/src/hashset.c b/bflibc/src/hashset.c new file mode 100644 index 00000000..ebd389d6 --- /dev/null +++ b/bflibc/src/hashset.c @@ -0,0 +1,103 @@ +/** + * author: brando + * date: 5/14/25 + */ + +#include "hashset.h" +#include "hashmap.h" +#include "free.h" + +typedef struct _BFHashSet { + /** + * we will use a hash map with this schema: + * : -> : + */ + BFHashMap map; + + void (*release)(BFHashSetValue value); +} _BFHashSet; + +/** + * value: hashset object + * key: hashset value + */ +void _BFHashSetReleaseHashMap(BFHashSetValue value, BFHashSet _set) { + _BFHashSet * set = (_BFHashSet *) _set; + if (!set && !set->release) return; + + set->release(value); +} + +BFHashSet BFHashSetCreate() { + _BFHashSet * res = (_BFHashSet *) malloc(sizeof(_BFHashSet)); + if (!res) return NULL; + + res->release = NULL; + + res->map = BFHashMapCreate(); + if (!res->map) { + return NULL; + } + + BFHashMapSetRelease(res->map, _BFHashSetReleaseHashMap); + + return (BFHashSet) res; +} + +void BFHashSetSetCompare(BFHashSet _set, int (*compare)(BFHashSetValue a, BFHashSetValue b)) { + _BFHashSet * set = (_BFHashSet *) _set; + if (!set) return; + + BFHashMapSetCompare(set->map, compare); +} + +void BFHashSetSetHashFunction(BFHashSet _set, unsigned long (*hash)(BFHashSetValue value)) { + _BFHashSet * set = (_BFHashSet *) _set; + if (!set) return; + + BFHashMapSetHashFunction(set->map, hash); +} + +void BFHashSetSetRelease(BFHashSet _set, void (*release)(BFHashSetValue value)) { + _BFHashSet * set = (_BFHashSet *) _set; + if (!set) return; + + set->release = release; +} + +void BFHashSetRelease(BFHashSet _set) { + _BFHashSet * set = (_BFHashSet *) _set; + if (!set) return; + + BFHashMapRelease(set->map); + BFFree(set); +} + +int BFHashSetInsert(BFHashSet _set, BFHashSetValue value) { + _BFHashSet * set = (_BFHashSet *) _set; + if (!set) return -1; + + return BFHashMapInsert(set->map, value, set); +} + +int BFHashSetRemove(BFHashSet _set, BFHashSetValue value) { + _BFHashSet * set = (_BFHashSet *) _set; + if (!set) return -1; + + return BFHashMapRemove(set->map, value); +} + +bool BFHashSetContains(BFHashSet _set, BFHashSetValue value) { + _BFHashSet * set = (_BFHashSet *) _set; + if (!set) return false; + + return BFHashMapContains(set->map, value); +} + +size_t BFHashSetGetSize(BFHashSet _set) { + _BFHashSet * set = (_BFHashSet *) _set; + if (!set) return 0; + + return BFHashMapGetSize(set->map); +} + diff --git a/bflibc/src/hashset.h b/bflibc/src/hashset.h new file mode 100644 index 00000000..b535218d --- /dev/null +++ b/bflibc/src/hashset.h @@ -0,0 +1,63 @@ +/** + * author: brando + * date: 5/14/25 + */ + +#ifndef HASHSET_H +#define HASHSET_H + +#include +#include + +typedef void * BFHashSet; +typedef void * BFHashSetValue; + +/** + * creates set + * + * caller owns memory + */ +BFHashSet BFHashSetCreate(); + +/** + * sets how the values are compared to one another + */ +void BFHashSetSetCompare(BFHashSet set, int (*compare)(BFHashSetValue a, BFHashSetValue b)); + +/** + * how each value memory is handled once set is released + */ +void BFHashSetSetRelease(BFHashSet set, void (*release)(BFHashSetValue value)); + +/** + * sets the hash function + */ +void BFHashSetSetHashFunction(BFHashSet set, unsigned long (*hash)(BFHashSetValue key)); + +/** + * releases set from memory + */ +void BFHashSetRelease(BFHashSet set); + +/** + * inserts value into set + */ +int BFHashSetInsert(BFHashSet set, BFHashSetValue value); + +/** + * removes value from set + */ +int BFHashSetRemove(BFHashSet set, BFHashSetValue value); + +/** + * checks if value is in set + */ +bool BFHashSetContains(BFHashSet set, BFHashSetValue value); + +/** + * returns the current size of set + */ +size_t BFHashSetGetSize(BFHashSet set); + +#endif // HASHSET_H + diff --git a/bflibc/src/internal/tree.c b/bflibc/src/internal/tree.c index 85f796eb..381ba8b5 100644 --- a/bflibc/src/internal/tree.c +++ b/bflibc/src/internal/tree.c @@ -16,6 +16,7 @@ _BFTreeNode * _BFTreeNodeCreate() { res->right = NULL; res->object = NULL; res->height = 1; + res->count = 0; return res; } @@ -87,6 +88,8 @@ _BFTreeNode * _BFTreeNodeInsert( _BFTreeNode * node, BFTreeObject object, int (*compare)(BFTreeObject a, BFTreeObject b), + void (*release)(BFTreeObject object), + unsigned char flags, int * error ) { // 1. Perform the normal BST insertion @@ -96,16 +99,24 @@ _BFTreeNode * _BFTreeNodeInsert( return node; } - if (compare(object, node->object) < 0) { + int cmpval = compare(object, node->object); + if (cmpval < 0) { node->left = _BFTreeNodeInsert( - node->left, object, compare, error + node->left, object, compare, release, flags, error ); - } else if (compare(object, node->object) > 0) { + } else if (cmpval > 0) { node->right = _BFTreeNodeInsert( - node->right, object, compare, error + node->right, object, compare, release, flags, error ); } else { // Equal keys are not allowed in BST - if (error) *error = -1; + if ((flags & (0x01 << _BFTREE_FLAG_ALLOW_DUPLICATES)) != 0) { + node->count++; + if (release) { + release(object); + } + } else { + if (error) *error = -11; + } return node; } @@ -170,7 +181,8 @@ _BFTreeNode * _BFTreeNodeRemove( _BFTreeNode * root, BFTreeObject object, int (*compare)(BFTreeObject a, BFTreeObject b), - void (*release)(BFTreeObject object) + void (*release)(BFTreeObject object), + unsigned char flags ) { // STEP 1: PERFORM STANDARD BST DELETE @@ -180,19 +192,29 @@ _BFTreeNode * _BFTreeNodeRemove( // If the key to be deleted is smaller than the // root's key, then it lies in left subtree - if (compare(object, root->object) < 0) { + int cmpval = compare(object, root->object); + if (cmpval < 0) { root->left = _BFTreeNodeRemove( - root->left, object, compare, release + root->left, object, compare, release, flags ); // If the key to be deleted is greater than the // root's key, then it lies in right subtree - } else if (compare(object, root->object) > 0) { + } else if (cmpval > 0) { root->right = _BFTreeNodeRemove( - root->right, object, compare, release + root->right, object, compare, release, flags ); // if key is same as root's key, then This is // the node to be deleted + // + // unless duplicates are allowed } else { + if (flags & (0x01 << _BFTREE_FLAG_ALLOW_DUPLICATES)) { + if (root->count > 0) { + root->count--; + return root; + } + } + // node with only one child or no child if (root->left == NULL || root->right == NULL) { _BFTreeNode * temp = root->left ? root->left : root->right; @@ -223,7 +245,7 @@ _BFTreeNode * _BFTreeNodeRemove( // Delete the inorder successor root->right = _BFTreeNodeRemove( - root->right, temp->object, compare, NULL + root->right, temp->object, compare, NULL, flags ); } } @@ -275,8 +297,8 @@ bool _BFTreeNodeSearch( ) { if (!node) { return false; - } else if (!obj) { - return false; + //} else if (!obj) { + // return false; } int comp = compare(obj, node->object); diff --git a/bflibc/src/internal/tree.h b/bflibc/src/internal/tree.h index a78a3867..feb30071 100644 --- a/bflibc/src/internal/tree.h +++ b/bflibc/src/internal/tree.h @@ -13,6 +13,12 @@ typedef struct _BFTreeNode { struct _BFTreeNode * right; size_t height; BFTreeObject object; + + /** + * IIF duplicates are allowed this will hold the count of + * instances of object in the tree + */ + size_t count; } _BFTreeNode; _BFTreeNode * _BFTreeNodeCreate(); @@ -35,29 +41,49 @@ typedef struct _BFTree { * BFTreeRelease() is called */ void (*release)(BFTreeObject object); + + /** + * |x|x|x|x|x|x|x|| + * + * : default value == 0. 0: not allow. 1: allow + */ + unsigned char flags; } _BFTree; +#define _BFTREE_FLAG_ALLOW_DUPLICATES 0 + /** * error: will nonzero if object couldn't be inserted. * one reason is there may be a duplicate * + * object: pointer or integer value. value=0 is allowed. If this tree + * is allowed to have duplicates, any duplicates found will be counted + * and released using its release callback + + * flags: _BFTree::flags + * * returns node */ _BFTreeNode * _BFTreeNodeInsert( _BFTreeNode * node, BFTreeObject object, int (*compare)(BFTreeObject a, BFTreeObject b), + void (*release)(BFTreeObject object), + unsigned char flags, int * error ); /** + * flags: _BFTree::flags + * * returns node */ _BFTreeNode * _BFTreeNodeRemove( _BFTreeNode * node, BFTreeObject object, int (*compare)(BFTreeObject a, BFTreeObject b), - void (*release)(BFTreeObject object) + void (*release)(BFTreeObject object), + unsigned char flags ); /** diff --git a/bflibc/src/set.c b/bflibc/src/set.c new file mode 100644 index 00000000..892c9292 --- /dev/null +++ b/bflibc/src/set.c @@ -0,0 +1,71 @@ +/** + * author: brando + * date: 5/14/25 + */ + +#include "set.h" +#include "tree.h" +#include "free.h" + +typedef struct _BFSet { + BFTree tree; +} _BFSet; + +BFSet BFSetCreate() { + _BFSet * res = (_BFSet *) malloc(sizeof(_BFSet)); + if (!res) return NULL; + + res->tree = BFTreeCreate(); + return (BFSet) res; +} + +void BFSetSetCompare(BFSet _set, int (*compare)(BFSetValue a, BFSetValue b)) { + _BFSet * set = (_BFSet *) _set; + if (!set) return; + + BFTreeSetCompare(set->tree, compare); +} + +void BFSetSetRelease(BFSet _set, void (*release)(BFSetValue value)) { + _BFSet * set = (_BFSet *) _set; + if (!set) return; + + BFTreeSetRelease(set->tree, release); +} + +void BFSetRelease(BFSet _set) { + _BFSet * set = (_BFSet *) _set; + if (!set) return; + + BFTreeRelease(set->tree); + BFFree(set); +} + +int BFSetInsert(BFSet _set, BFSetValue value) { + _BFSet * set = (_BFSet *) _set; + if (!set) return -1; + + return BFTreeInsert(set->tree, value); +} + +int BFSetRemove(BFSet _set, BFSetValue value) { + _BFSet * set = (_BFSet *) _set; + if (!set) return -1; + + return BFTreeRemove(set->tree, value); +} + +bool BFSetContains(BFSet _set, BFSetValue value) { + _BFSet * set = (_BFSet *) _set; + if (!set) return false; + + return BFTreeContains(set->tree, value); +} + +size_t BFSetGetSize(BFSet _set) { + _BFSet * set = (_BFSet *) _set; + if (!set) return 0; + + return BFTreeSize(set->tree); +} + diff --git a/bflibc/src/set.h b/bflibc/src/set.h new file mode 100644 index 00000000..73ba3076 --- /dev/null +++ b/bflibc/src/set.h @@ -0,0 +1,58 @@ +/** + * author: brando + * date: 5/14/25 + */ + +#ifndef SET_H +#define SET_H + +#include +#include + +typedef void * BFSet; +typedef void * BFSetValue; + +/** + * creates set + * + * caller owns memory + */ +BFSet BFSetCreate(); + +/** + * sets how the values are compared to one another + */ +void BFSetSetCompare(BFSet set, int (*compare)(BFSetValue a, BFSetValue b)); + +/** + * how each value memory is handled once set is released + */ +void BFSetSetRelease(BFSet set, void (*release)(BFSetValue value)); + +/** + * releases set from memory + */ +void BFSetRelease(BFSet set); + +/** + * inserts value into set + */ +int BFSetInsert(BFSet set, BFSetValue value); + +/** + * removes value from set + */ +int BFSetRemove(BFSet set, BFSetValue value); + +/** + * checks if value is in set + */ +bool BFSetContains(BFSet set, BFSetValue value); + +/** + * returns the current size of set + */ +size_t BFSetGetSize(BFSet set); + +#endif // SET_H + diff --git a/bflibc/src/tree.c b/bflibc/src/tree.c index fe8ab2c0..81631191 100644 --- a/bflibc/src/tree.c +++ b/bflibc/src/tree.c @@ -14,6 +14,7 @@ BFTree BFTreeCreate() { res->compare = NULL; res->release = NULL; res->size = 0; + res->flags = 0x00; return (BFTree) res; } @@ -27,18 +28,28 @@ void BFTreeSetRelease(BFTree tree, void (*release)(BFTreeObject object)) { ((_BFTree *) tree)->release = release; } +bool BFTreeGetAllowDuplicates(BFTree _tree) { + _BFTree * tree = (_BFTree *) _tree; + if (!tree) return false; + + return (tree->flags & (0x01 << _BFTREE_FLAG_ALLOW_DUPLICATES)) != 0; +} + +void BFTreeSetAllowDuplicates(BFTree _tree, bool allow) { + _BFTree * tree = (_BFTree *) _tree; + if (!tree) return; + + unsigned char bit = allow ? 0x01 : 0x00; + + tree->flags = tree->flags ^ (bit << _BFTREE_FLAG_ALLOW_DUPLICATES); +} + // left->right->node void BFTreeReleaseNode(_BFTree * tree, _BFTreeNode * node) { if (!node) return; BFTreeReleaseNode(tree, node->left); BFTreeReleaseNode(tree, node->right); - /* - if (tree->release) { - tree->release(node->object); - } - */ - _BFTreeNodeRelease(node, tree->release); tree->size--; } @@ -64,6 +75,8 @@ int BFTreeInsert(BFTree _tree, BFTreeObject object) { tree->root, object, tree->compare, + tree->release, + tree->flags, &err ); @@ -80,7 +93,7 @@ int BFTreeRemove(BFTree _tree, BFTreeObject object) { } _BFTree * tree = (_BFTree *) _tree; tree->root = _BFTreeNodeRemove( - tree->root, object, tree->compare, tree->release + tree->root, object, tree->compare, tree->release, tree->flags ); tree->size--; diff --git a/bflibc/src/tree.h b/bflibc/src/tree.h index 9f6484a7..88f9b799 100644 --- a/bflibc/src/tree.h +++ b/bflibc/src/tree.h @@ -1,6 +1,8 @@ /** * author: brando * date: 11/22/24 + * + * binary search tree (avl implementation) */ #ifndef TREE_H @@ -32,6 +34,18 @@ void BFTreeSetCompare(BFTree tree, int (*compare)(BFTreeObject a, BFTreeObject b */ void BFTreeSetRelease(BFTree tree, void (*release)(BFTreeObject object)); +/** + * allow == true to permit the use of duplicates in this binary tree + * + * by default, this binary tree does NOT allow duplicates. + */ +void BFTreeSetAllowDuplicates(BFTree tree, bool allow); + +/** + * returns true if duplicates are allowed + */ +bool BFTreeGetAllowDuplicates(BFTree tree); + /** * frees tree * @@ -49,7 +63,9 @@ size_t BFTreeSize(BFTree tree); /** * no duplicates allowed * - * object: pointer or integer value. value=0 is allowed + * object: pointer or integer value. value=0 is allowed. If this tree + * is allowed to have duplicates, any duplicates found will be counted + * and released using its release callback * * returns: -1 if object couldn't be inserted. Object may be a duplicate */ diff --git a/bflibc/testbench/hashset_tests.h b/bflibc/testbench/hashset_tests.h new file mode 100644 index 00000000..a6f80287 --- /dev/null +++ b/bflibc/testbench/hashset_tests.h @@ -0,0 +1,75 @@ +/** + * author: Brando + * date: 5/16/25 + */ + +#ifndef HASHSET_TESTS_H +#define HASHSET_TESTS_H + +#include "clib_tests.h" +#include "hashset.h" +#include "hash.h" +#include + +unsigned long BFTestHashSetHashString(BFHashSetValue val) { + return BFHashDjb2((unsigned char *) val); +} + +int BFTestHashSetKeyCompare(BFHashSetValue aval, BFHashSetValue bval) { + const char * a = (const char *) aval; + const char * b = (const char *) bval; + return strcmp(a, b); +} + +char * _CreateRandomWord() { + const char charset[] = "abcdefghijklmnopqrstuvwxyz"; + size_t charset_size = strlen(charset); + + srand(time(0)); + + size_t reslen = 2 << 3; + char * res = (char *) malloc(sizeof(char) * (reslen + 1)); + for (int i = 0; i < reslen; i++) { + res[i] = charset[rand() % charset_size]; + } + + res[reslen] = '\0'; + + return res; +} + +BFTEST_UNIT_FUNC(test_hashsetinit, 2<<10, { + BFHashSet set = BFHashSetCreate(); + BF_ASSERT(set, "null hash set"); + BFHashSetSetHashFunction(set, BFTestHashSetHashString); + BFHashSetSetCompare(set, BFTestHashSetKeyCompare); + BFHashSetSetRelease(set, free); + BFHashSetRelease(set); +}) + +BFTEST_UNIT_FUNC(test_hashsetsize, 2<<10, { + BFHashSet set = BFHashSetCreate(); + BF_ASSERT(set, "null set"); + + BFHashSetSetHashFunction(set, BFTestHashSetHashString); + BFHashSetSetCompare(set, BFTestHashSetKeyCompare); + BFHashSetSetRelease(set, free); + + srand(time(0)); + int max = rand() % 2<<9; + for (int i = 0; i < max; i++) { + char * randword = _CreateRandomWord(); + BF_ASSERT(BFHashSetInsert(set, randword) == 0, "could not insert '%s'", randword); + } + + BF_ASSERT(BFHashSetGetSize(set) == max, "size(%ld) != %ld", BFHashSetGetSize(set), max); + BFHashSetRelease(set); +}) + +BFTEST_COVERAGE_FUNC(hashset_tests, { + BFTEST_LAUNCH(test_hashsetinit); + BFTEST_LAUNCH(test_hashsetsize); +}) + +#endif // HASHSET_TESTS_H + diff --git a/bflibc/testbench/set_tests.h b/bflibc/testbench/set_tests.h new file mode 100644 index 00000000..33f9ac2b --- /dev/null +++ b/bflibc/testbench/set_tests.h @@ -0,0 +1,67 @@ +/** + * author: Brando + * date: 5/14/25 + */ + +#ifndef SET_TESTS_H +#define SET_TESTS_H + +#include "clib_tests.h" +#include "set.h" + +BFTEST_UNIT_FUNC(test_setinit, 2<<10, { + BFSet set = BFSetCreate(); + BF_ASSERT(set, "null set"); + BFSetRelease(set); +}) + +int BFTestSetCompareInteger(BFSetValue aobj, BFSetValue bobj) { + int a = (intptr_t) aobj; + int b = (intptr_t) bobj; + return a - b; +} + +BFTEST_UNIT_FUNC(test_setcontains, 2<<10, { + BFSet set = BFSetCreate(); + BF_ASSERT(set, "null set"); + + BFSetSetCompare(set, BFTestSetCompareInteger); + + int max = 2 << 9; + for (int i = 0; i < max; i++) { + if (i % 2 == 0) { + BF_ASSERT(BFSetInsert(set, (BFSetValue) (intptr_t) i) == 0, "could not insert %d", i); + } + } + + for (int i = 0; i < max; i++) { + BF_ASSERT(BFSetContains(set, (BFSetValue) (intptr_t) i) == (i % 2 == 0 ? true : false), "contains(%d) != %s", i, (i % 2 == 0 ? "true" : "false")); + } + + BFSetRelease(set); +}) + +BFTEST_UNIT_FUNC(test_setsize, 2<<10, { + BFSet set = BFSetCreate(); + BF_ASSERT(set, "null set"); + + BFSetSetCompare(set, BFTestSetCompareInteger); + + srand(time(0)); + int max = rand() % 2<<9; + for (int i = 0; i < max; i++) { + BF_ASSERT(BFSetInsert(set, (BFSetValue) (intptr_t) i) == 0, "could not insert %d", i); + } + + BF_ASSERT(BFSetGetSize(set) == max, "size(%ld) != %ld", BFSetGetSize(set), max); + BFSetRelease(set); +}) + +BFTEST_COVERAGE_FUNC(set_tests, { + BFTEST_LAUNCH(test_setinit); + BFTEST_LAUNCH(test_setcontains); + BFTEST_LAUNCH(test_setsize); +}) + +#endif // SET_TESTS_H + diff --git a/bflibc/testbench/tests.c b/bflibc/testbench/tests.c index b2d3908a..e7027daa 100644 --- a/bflibc/testbench/tests.c +++ b/bflibc/testbench/tests.c @@ -18,6 +18,8 @@ #include "map_tests.h" #include "tree_tests.h" #include "hashmap_tests.h" +#include "set_tests.h" +#include "hashset_tests.h" #include BFTEST_SUITE_FUNC({ @@ -37,6 +39,7 @@ BFTEST_SUITE_FUNC({ BFTEST_SUITE_LAUNCH(hashmap_tests); BFTEST_SUITE_LAUNCH(map_tests); BFTEST_SUITE_LAUNCH(tree_tests); - + BFTEST_SUITE_LAUNCH(set_tests); + BFTEST_SUITE_LAUNCH(hashset_tests); }) diff --git a/bflibc/testbench/tree_tests.h b/bflibc/testbench/tree_tests.h index 41a55f55..5b38bafa 100644 --- a/bflibc/testbench/tree_tests.h +++ b/bflibc/testbench/tree_tests.h @@ -132,7 +132,6 @@ BFTEST_UNIT_FUNC(test_InsertNodesAndSearch, 2<<10, { }) void BFTestTreeFree(BFTreeObject object) { - //printf("freeing: %p\n", object); free(object); } @@ -156,7 +155,6 @@ BFTEST_UNIT_FUNC(test_InsertAndRemovingNodes, 2<<10, { *objects[i] = i; // insert into tree - //printf("inserting: %p\n", objects[i]); int err = BFTreeInsert(tree, objects[i]); BF_ASSERT(err == 0, "node insertion failed, node(obj=%d)", i); } @@ -166,15 +164,10 @@ BFTEST_UNIT_FUNC(test_InsertAndRemovingNodes, 2<<10, { // remove nodes int randNumSearch = 20; while (randNumSearch--) { - //for (int i = 0; i < randNumSearch; i++) { - //for (int i = treesize - 1; i >= treesize - randNumSearch; i--) { int index = abs(BFRand()) % treesize; - //int index = i; int * object = objects[index]; - //printf("can we remove %p\n", object); if (object && BFTreeContains(tree, object)) { - //printf("removing %p\n", object); int err = BFTreeRemove(tree, object); BF_ASSERT(err == 0, "couldn't remove node for object=%d", *object); @@ -187,11 +180,73 @@ BFTEST_UNIT_FUNC(test_InsertAndRemovingNodes, 2<<10, { //BFTestTreePrint(tree->root); } - //printf("freeing tree\n"); BFTreeRelease(tree); }) -BFTEST_UNIT_FUNC(test_InsertDuplicates, 2<<10, { +BFTEST_UNIT_FUNC(test_InsertDuplicatesAllowed, 2<<10, { + if (BFTEST_UNIT_FUNC_ITR == 0) { + BFRandInit(time(0)); + } + + // create trees + BFTree tree = BFTreeCreate(); + BF_ASSERT(tree, "a null tree was returned"); + BFTreeSetCompare(tree, BFTestTreeCompare); + BFTreeSetRelease(tree, free); + BFTreeSetAllowDuplicates(tree, true); + + BF_ASSERT(BFTreeGetAllowDuplicates(tree)); + + int val = BFRand(); + + // object 1 + int * object = (int *) malloc(sizeof(int)); + *object = val; + int err = BFTreeInsert(tree, object); + BF_ASSERT(err == 0, "error inserting %d", err); + + // object 2 using the same value + object = (int *) malloc(sizeof(int)); + *object = val; + err = BFTreeInsert(tree, object); + BF_ASSERT(err == 0, "should not be an error inserting %d because we allow duplicates", err); + + // now free since the tree doesn't hold onto it + //BFFree(object); + + if (BFTEST_UNIT_FUNC_ITR == 0) { + //BFTestTreePrint(tree->root); + } + + BFTreeRelease(tree); +}) + +int BFTestTreeCompareInteger(BFTreeObject aobj, BFTreeObject bobj) { + int a = (intptr_t) aobj; + int b = (intptr_t) bobj; + return a - b; +} + +BFTEST_UNIT_FUNC(test_InsertDuplicatesAllowedEvenMore, 2<<10, { + // create trees + BFTree tree = BFTreeCreate(); + BF_ASSERT(tree, "a null tree was returned"); + BFTreeSetCompare(tree, BFTestTreeCompareInteger); + BFTreeSetAllowDuplicates(tree, true); + + BF_ASSERT(BFTreeGetAllowDuplicates(tree)); + + int max = 2 << 9; + for (int i = 0; i < max; i++) { + BFTreeInsert(tree, (BFTreeObject) (intptr_t) 0xFF); + } + + BF_ASSERT(BFTreeSize(tree) == max); + + BFTreeRelease(tree); +}) + +BFTEST_UNIT_FUNC(test_InsertDuplicatesNotAllowed, 2<<10, { if (BFTEST_UNIT_FUNC_ITR == 0) { BFRandInit(time(0)); } @@ -373,7 +428,9 @@ BFTEST_COVERAGE_FUNC(tree_tests, { BFTEST_LAUNCH(test_InsertNodes); BFTEST_LAUNCH(test_InsertNodesAndSearch); BFTEST_LAUNCH(test_InsertAndRemovingNodes); - BFTEST_LAUNCH(test_InsertDuplicates); + BFTEST_LAUNCH(test_InsertDuplicatesAllowed); + BFTEST_LAUNCH(test_InsertDuplicatesAllowedEvenMore); + BFTEST_LAUNCH(test_InsertDuplicatesNotAllowed); BFTEST_LAUNCH(test_InsertAndRemovingNoPointers); BFTEST_LAUNCH(test_treeGettingNonexistentValues); BFTEST_LAUNCH(test_treeTraversal); diff --git a/bflibc/todo.md b/bflibc/todo.md index 5bc6fa43..c601227b 100644 --- a/bflibc/todo.md +++ b/bflibc/todo.md @@ -1,29 +1,34 @@ -``` -[] queue object - [] add to the traversal test case for trees "Level Order Traversal" -[x] add documentation to hash, map, tree, hashmap -[x] remove the pair from map header -[x] add node accessor to tree class -[] sprintf is deprecated -[] improve sqrt, it sometimes stalls - [x] use binary search - [] store all known square root in a map -[x] BFMapContains -[x] map - [x] make a tree object so data store is close to O(logn) - [x] make a hash function good enough to use for O(1) operations -[] improve prime num getter using hash map -[] remove checksum -[x] add tests to nonexistent entries in tree, map, and hashmap -[x] link math lib for linux only -[x] put all these flags into the libs file and couple it -[x] make hashing function for bflibcpp library hash maps -[x] make a max and min function like python3 using macros -[x] random generator -[x] abs() in bfmath -[x] tree - [x] insertion - [x] removal - [x] getter -[x] consider allowing duplicate values into tree - no -``` +- [ ] queue object + - [ ] add to the traversal test case for trees "Level Order Traversal" +- [x] add documentation to hash, map, tree, hashmap +- [x] remove the pair from map header +- [x] add node accessor to tree class +- [ ] sprintf is deprecated +- [ ] improve sqrt, it sometimes stalls + - [x] use binary search + - [ ] store all known square root in a map +- [x] BFMapContains +- [x] map + - [x] make a tree object so data store is close to O(logn) + - [x] make a hash function good enough to use for O(1) operations +- [ ] improve prime num getter using hash map +- [ ] remove checksum +- [x] add tests to nonexistent entries in tree, map, and hashmap +- [x] link math lib for linux only +- [x] put all these flags into the libs file and couple it +- [x] make hashing function for bflibcpp library hash maps +- [x] make a max and min function like python3 using macros +- [x] random generator +- [x] abs() in bfmath +- [x] tree + - [x] insertion + - [x] removal + - [x] getter +- [x] consider allowing duplicate values into tree - no +- [x] reconsider duplicates + - [x] handle how memory is freed if duplicates are found +- [x] Set object + - [x] allow tree to permit duplicates + - [x] tree set + - [x] hash set + diff --git a/bflibcpp/src/allocator.hpp b/bflibcpp/src/allocator.hpp new file mode 100644 index 00000000..b4766008 --- /dev/null +++ b/bflibcpp/src/allocator.hpp @@ -0,0 +1,23 @@ +/** + * author: brando + * date: 5/19/25 + */ + +#ifndef ALLOCATOR_HPP +#define ALLOCATOR_HPP + +namespace BF { + +template +struct Allocator { + T create() const { + return 0; + } + + void release(T obj) const { } +}; + +} + +#endif // ALLOCATOR_HPP + diff --git a/bflibcpp/src/basicmap.hpp b/bflibcpp/src/basicmap.hpp index c38a6322..61643d33 100644 --- a/bflibcpp/src/basicmap.hpp +++ b/bflibcpp/src/basicmap.hpp @@ -10,6 +10,8 @@ #include "release.hpp" #include "retain.hpp" #include "exception.hpp" +#include "compare.hpp" +#include "allocator.hpp" #include @@ -25,7 +27,12 @@ namespace BF { * This is formatted to fit the bflibc implementation of * HashMap and Map */ -template +template < + typename K, + typename V, + typename S = size_t, + class C = Compare +> class BasicMap : public Collection { public: virtual const char * className() const { @@ -41,55 +48,21 @@ class BasicMap : public Collection { * this object will get injected into the * map implementations */ - template + template> class Container : public Object { public: T _obj; const BasicMap * _mapRef; Container(T obj, const BasicMap * mapRef) - : _obj(obj), _mapRef(mapRef), Object() { - BFRetain(this->_mapRef); - } + : _obj(obj), _mapRef(mapRef), Object() { } virtual ~Container() { - BFRelease(this->_mapRef); - } - - bool isBFObject() const { - return std::is_base_of_v; - } - }; - - template - class Key : public Container { - public: - Key(T obj, const BasicMap * mapRef) - : Container(obj, mapRef) { } - virtual ~Key() { - if (this->_mapRef->_releaseKey) { - this->_mapRef->_releaseKey(this->_obj); - } - } - }; - - template - class Value : public Container { - public: - Value(T obj, const BasicMap * mapRef) - : Container(obj, mapRef) { } - virtual ~Value() { - if (this->_mapRef->_releaseValue) { - this->_mapRef->_releaseValue(this->_obj); - } + A allocator; + allocator.release(this->_obj); } }; - friend class Key; - friend class Value; - public: - BasicMap() - : _compare(NULL), _releaseKey(NULL), - _releaseValue(NULL), Collection() { } + BasicMap() : Collection() { } virtual ~BasicMap() { } @@ -98,24 +71,21 @@ class BasicMap : public Collection { * * similar behavior to strcmp and memcmp */ - void setCompare(int (*compare)(K & a, K & b)) { - this->_compare = compare; - } + [[deprecated("Please use BF::Compare functor")]] + void setCompare(int (*compare)(K & a, K & b)) { } /** * defines how Key and values are released */ - void setRelease(void (*releaseKey)(K obj), void (*releaseValue)(V obj)) { - this->_releaseKey = releaseKey; - this->_releaseValue = releaseValue; - } + [[deprecated("Please use BF::Allocatorfunctor")]] + void setRelease(void (*releaseKey)(K obj), void (*releaseValue)(V obj)) { } /** * adds key and value into map */ int insert(K k, V v) { - Key * key = new Key(k, this); - Value * value = new Value(v, this); + Container * key = new Container(k, this); + Container * value = new Container(v, this); return this->_insert(key, value); } @@ -125,9 +95,9 @@ class BasicMap : public Collection { * throws an exception if no value could be found for key */ V & getValueForKey(K k) const { - Key key(k, this); + Container key(k, this); - Value * value = this->_getValueForKey(&key); + Container * value = this->_getValueForKey(&key); if (value) { return value->_obj; } else { @@ -152,7 +122,7 @@ class BasicMap : public Collection { * removes key/value pair with key */ int remove(K k) { - Key key(k, this); + Container key(k, this); return this->_remove(&key); } @@ -160,7 +130,7 @@ class BasicMap : public Collection { * true if there is an entry with key=k */ bool contains(K k) const { - Key key(k, this); + Container key(k, this); return this->_contains(&key); } @@ -172,11 +142,7 @@ class BasicMap : public Collection { /** * returns NULL if there is no value for key */ - virtual Value * _getValueForKey(void * key) const = 0; - - int (*_compare)(K & a, K & b); - void (*_releaseKey)(K obj); - void (*_releaseValue)(V obj); + virtual Container * _getValueForKey(void * key) const = 0; protected: /** @@ -187,28 +153,22 @@ class BasicMap : public Collection { * default return(-1) */ static int _BFMapCompare(void * a, void * b) { - Key * akey = (Key *) a; - Key * bkey = (Key *) b; + Container * akey = (Container *) a; + Container * bkey = (Container *) b; if (!akey || !bkey) { - return -1; - } - - if (!akey->_mapRef->_compare && akey->isBFObject() && bkey->isBFObject()) { - BF::Object * obja = (BF::Object *) &akey->_obj; - BF::Object * objb = (BF::Object *) &bkey->_obj; - if (!obja || !objb) return -1; - return obja->compare(*objb); + return 0; } - return akey->_mapRef->_compare(akey->_obj, bkey->_obj); + C cmp; + return cmp(akey->_obj, bkey->_obj); } /** * Releases the Key & Value objects */ static void _BFMapRelease(void * k, void * v) { - Key * key = (Key *) k; - Value * value = (Value *) v; + Container * key = (Container *) k; + Container * value = (Container *) v; BFRelease(key); BFRelease(value); } diff --git a/bflibcpp/src/basicset.hpp b/bflibcpp/src/basicset.hpp new file mode 100644 index 00000000..8fcc06b8 --- /dev/null +++ b/bflibcpp/src/basicset.hpp @@ -0,0 +1,123 @@ +/** + * author: brando + * date: 5/27/25 + */ + +#ifndef BASIC_SET_HPP +#define BASIC_SET_HPP + +#include "collection.hpp" +#include "release.hpp" +#include "retain.hpp" +#include "exception.hpp" +#include "compare.hpp" +#include "allocator.hpp" + +#include + +namespace BF { + +/** + * General implementation of a Map + * + * This is formatted to fit the bflibc implementation of + * HashMap and Map + */ +template < + typename V, + typename S = size_t, + class C = Compare +> +class BasicSet : public Collection { +public: + virtual const char * className() const { + return "BF::BasicSet"; + } + +protected: + + /** + * template on how Key and Value objects are + * held + * + * this object will get injected into the + * map implementations + */ + template> + class Container : public Object { + public: + T _obj; + const BasicSet * _setRef; + Container(T obj, const BasicSet * mapRef) + : _obj(obj), _setRef(mapRef), Object() { } + virtual ~Container() { + A allocator; + allocator.release(this->_obj); + } + }; + +public: + BasicSet() : Collection() { } + + virtual ~BasicSet() { } + + /** + * adds key and value into map + */ + int insert(V value) { + Container * v = new Container(value, this); + return this->_insert(v); + } + + /** + * removes key/value pair with key + */ + int remove(V value) { + Container v(value, this); + return this->_remove(&v); + } + + /** + * true if there is an entry with key=k + */ + bool contains(V value) const { + Container v(value, this); + return this->_contains(&v); + } + +private: + virtual int _insert(void * value) = 0; + virtual int _remove(void * value) = 0; + virtual bool _contains(void * value) const = 0; + +protected: + /** + * compares a with b + * + * we assume similar behavior to strcmp or memcmp + * + * default return(-1) + */ + static int _BFSetCompare(void * a, void * b) { + Container * aval = (Container *) a; + Container * bval = (Container *) b; + if (!aval || !bval) { + return 0; + } + + C cmp; + return cmp(aval->_obj, bval->_obj); + } + + /** + * Releases the Key & Value objects + */ + static void _BFSetRelease(void * v) { + Container * value = (Container *) v; + BFRelease(value); + } +}; +} + +#endif // BASIC_SET_HPP + diff --git a/bflibcpp/src/bflibcpp.hpp b/bflibcpp/src/bflibcpp.hpp index d3a15998..fbe48a68 100644 --- a/bflibcpp/src/bflibcpp.hpp +++ b/bflibcpp/src/bflibcpp.hpp @@ -32,6 +32,9 @@ #include "hash.hpp" #include "defer.hpp" #include "sort.hpp" +#include "set.hpp" +#include "hashset.hpp" +#include "removepointer.hpp" #endif // BFLIBCPP_HPP diff --git a/bflibcpp/src/compare.hpp b/bflibcpp/src/compare.hpp new file mode 100644 index 00000000..55265fd8 --- /dev/null +++ b/bflibcpp/src/compare.hpp @@ -0,0 +1,28 @@ +/** + * author: brando + * date: 5/16/25 + */ + +#ifndef COMPARE_HPP +#define COMPARE_HPP + +namespace BF { + +template +struct Compare { + int operator()(const T & a, const T & b) { + return a - b; + } +}; + +template <> +struct Compare { + int operator()(const char * const & a, const char * const & b) { + return strcmp(a, b); + } +}; + +} + +#endif // COMPARE_HPP + diff --git a/bflibcpp/src/hashmap.hpp b/bflibcpp/src/hashmap.hpp index 214f5c5a..31fad58c 100644 --- a/bflibcpp/src/hashmap.hpp +++ b/bflibcpp/src/hashmap.hpp @@ -48,13 +48,13 @@ class HashMap : public BasicMap { return BFHashMapInsert(this->_map, key, value); } - virtual typename BasicMap::template Value * _getValueForKey(void * key) const { + virtual typename BasicMap::template Container * _getValueForKey(void * key) const { if (!this->_map) { return NULL; } - typename BasicMap::template Value * value = - (typename BasicMap::template Value *) BFHashMapGetValue(this->_map, key, NULL); + typename BasicMap::template Container * value = + (typename BasicMap::template Container *) BFHashMapGetValue(this->_map, key, NULL); return value; } @@ -70,7 +70,7 @@ class HashMap : public BasicMap { } static unsigned long _BFHashMapHashFunction(void * k) { - typename BasicMap::template Key * key = (typename BasicMap::template Key *) k; + typename BasicMap::template Container * key = (typename BasicMap::template Container *) k; if (!key) { return 0; } diff --git a/bflibcpp/src/hashset.hpp b/bflibcpp/src/hashset.hpp new file mode 100644 index 00000000..a6cec87c --- /dev/null +++ b/bflibcpp/src/hashset.hpp @@ -0,0 +1,79 @@ +/** + * author: brando + * date: 5/27/25 + */ + +#ifndef HASH_SET_HPP +#define HASH_SET_HPP + +#include "basicset.hpp" +#include "release.hpp" +#include "retain.hpp" +#include "hash.hpp" + +extern "C" { +#include +} + +namespace BF { + +/** + * Map implemented using self-balancing tree. See bflibc/map.h + */ +template > +class HashSet : public BasicSet { +public: + HashSet() : _set(NULL), BasicSet() { + this->_set = BFHashSetCreate(); + if (!this->_set) return; + BFHashSetSetCompare(this->_set, this->_BFSetCompare); + BFHashSetSetRelease(this->_set, this->_BFSetRelease); + BFHashSetSetHashFunction(this->_set, this->_BFHashSetHashFunction); + } + + virtual ~HashSet() { + BFHashSetRelease(this->_set); + } + +private: + size_t size() const { + return BFHashSetGetSize(this->_set); + } + + virtual int _insert(void * value) { + if (!this->_set) return -1; + return BFHashSetInsert(this->_set, value); + } + + virtual int _remove(void * value) { + if (!this->_set) return -1; + return BFHashSetRemove(this->_set, value); + } + + virtual bool _contains(void * value) const { + if (!this->_set) return false; + return BFHashSetContains(this->_set, value); + } + + static unsigned long _BFHashSetHashFunction(void * value) { + typename BasicSet::template Container * v = (typename BasicSet::template Container *) value; + if (!v) { + return 0; + } + + HashSet * set = (HashSet *) v->_setRef; + if (!set) { + return 0; + } + + V obj = v->_obj; + return set->_hash(obj); + } + + H _hash; + BFHashSet _set; +}; +} + +#endif // MAP_HPP + diff --git a/bflibcpp/src/map.hpp b/bflibcpp/src/map.hpp index fa40c026..9615e42e 100644 --- a/bflibcpp/src/map.hpp +++ b/bflibcpp/src/map.hpp @@ -42,13 +42,13 @@ class Map : public BasicMap { return BFMapInsert(this->_map, key, value); } - virtual typename BasicMap::template Value * _getValueForKey(void * key) const { + virtual typename BasicMap::template Container * _getValueForKey(void * key) const { if (!this->_map) { return NULL; } - typename BasicMap::template Value * value = - (typename BasicMap::template Value *) BFMapGetValue(this->_map, key, NULL); + typename BasicMap::template Container * value = + (typename BasicMap::template Container *) BFMapGetValue(this->_map, key, NULL); return value; } diff --git a/bflibcpp/src/removepointer.hpp b/bflibcpp/src/removepointer.hpp new file mode 100644 index 00000000..b59a9f0a --- /dev/null +++ b/bflibcpp/src/removepointer.hpp @@ -0,0 +1,20 @@ +/** + * author: brando + * date: 5/27/25 + */ + +#ifndef BF_REMOVE_POINTER_HPP +#define BF_REMOVE_POINTER_HPP + +template +struct RemovePointer { + using type = T; +}; + +template +struct RemovePointer { + using type = T; +}; + +#endif // BF_REMOVE_POINTER_HPP + diff --git a/bflibcpp/src/set.hpp b/bflibcpp/src/set.hpp new file mode 100644 index 00000000..1362c34f --- /dev/null +++ b/bflibcpp/src/set.hpp @@ -0,0 +1,60 @@ +/** + * author: brando + * date: 5/27/25 + */ + +#ifndef SET_HPP +#define SET_HPP + +#include "basicset.hpp" +#include "release.hpp" + +extern "C" { +#include +} + +namespace BF { + +/** + * Set implemented using self-balancing tree. See bflibc/map.h + */ +template +class Set : public BasicSet { +public: + Set() : _set(NULL), BasicSet() { + this->_set = BFSetCreate(); + if (!this->_set) return; + BFSetSetCompare(this->_set, this->_BFSetCompare); + BFSetSetRelease(this->_set, this->_BFSetRelease); + } + + virtual ~Set() { + BFSetRelease(this->_set); + } + +private: + size_t size() const { + return BFSetGetSize(this->_set); + } + + virtual int _insert(void * value) { + if (!this->_set) return -1; + return BFSetInsert(this->_set, value); + } + + virtual int _remove(void * value) { + if (!this->_set) return -1; + return BFSetRemove(this->_set, value); + } + + virtual bool _contains(void * value) const { + if (!this->_set) return false; + return BFSetContains(this->_set, value); + } + + BFSet _set; +}; +} + +#endif // SET_HPP + diff --git a/bflibcpp/src/string.cpp b/bflibcpp/src/string.cpp index d83cc770..93c1f969 100644 --- a/bflibcpp/src/string.cpp +++ b/bflibcpp/src/string.cpp @@ -220,6 +220,10 @@ String & String::operator=(const String & str) { return *this; } +int String::operator-(const String & str) const { + return strcmp(this->cString(), str.cString()); +} + int String::toi(const String & s) { return atoi(s.cString()); } diff --git a/bflibcpp/src/string.hpp b/bflibcpp/src/string.hpp index 3b1f7d17..961dad79 100644 --- a/bflibcpp/src/string.hpp +++ b/bflibcpp/src/string.hpp @@ -141,6 +141,7 @@ class String : public Array { virtual bool operator!=(const String & s) const; virtual bool operator!=(const char * s) const; virtual String & operator=(const String & str); + virtual int operator-(const String & str) const; // Conversions public: diff --git a/bflibcpp/src/tree.hpp b/bflibcpp/src/tree.hpp index 51eeae5c..4854736f 100644 --- a/bflibcpp/src/tree.hpp +++ b/bflibcpp/src/tree.hpp @@ -9,6 +9,8 @@ #include "collection.hpp" #include "release.hpp" #include "exception.hpp" +#include "compare.hpp" +#include "allocator.hpp" extern "C" { #include @@ -19,7 +21,7 @@ namespace BF { /** * AVL Tree */ -template +template, class A = Allocator> class Tree : public Collection { /** * holds container of type T @@ -28,14 +30,15 @@ class Tree : public Collection { public: T _obj; const Tree * _treeRef; - void (*_release)(T obj); - Container(T obj, const Tree * treeRef, void (*release)(T obj)) + bool _release; + Container(T obj, const Tree * treeRef, bool release) : _obj(obj), _treeRef(treeRef), _release(release), Object() { BFRetain(this->_treeRef); } virtual ~Container() { + A allocator; if (this->_release) { - this->_release(this->_obj); + allocator.release(this->_obj); } BFRelease(this->_treeRef); } @@ -66,11 +69,15 @@ class Tree : public Collection { bool isNull() const { return this->_node == NULL; } }; - Tree() : Collection() { + Tree() : _tree(NULL), Collection() { this->_tree = BFTreeCreate(); if (!this->_tree) return; - BFTreeSetCompare(this->_tree, this->_BFTreeCompare); - BFTreeSetRelease(this->_tree, this->_BFTreeRelease); + BFTreeSetCompare(this->_tree, this->_compare); + BFTreeSetRelease(this->_tree, this->_release); + } + + Tree(bool allowDuplicates) : Tree() { + BFTreeSetAllowDuplicates(this->_tree, allowDuplicates); } virtual ~Tree() { @@ -83,15 +90,20 @@ class Tree : public Collection { * * see _compare */ - void setCompare(int (*compare)(const T & a, const T & b)) { - this->_compare = compare; - } + [[deprecated("Please use BF::Compare functor")]] + void setCompare(int (*compare)(const T & a, const T & b)) { } /** * defines how objects are released */ - void setRelease(void (*release)(T obj)) { - this->_release = release; + [[deprecated("Please use BF::Allocator functor")]] + void setRelease(void (*release)(T obj)) { } + + /** + * true if tree can allow duplicates + */ + bool allowDuplicates() const { + return BFTreeGetAllowDuplicates(this->_tree); } /** @@ -106,7 +118,7 @@ class Tree : public Collection { */ int insert(T object) { if (!this->_tree) return -1; - Container * c = new Container(object, this, this->_release); + Container * c = new Container(object, this, true); return BFTreeInsert(this->_tree, c); } @@ -115,7 +127,7 @@ class Tree : public Collection { */ int remove(T object) { if (!this->_tree) return -1; - Container c(object, this, NULL); + Container c(object, this, false); return BFTreeRemove(this->_tree, &c); } @@ -124,7 +136,7 @@ class Tree : public Collection { */ bool contains(T object) const { if (!this->_tree) return -1; - Container c(object, this, NULL); + Container c(object, this, false); return BFTreeContains(this->_tree, &c); } @@ -143,15 +155,16 @@ class Tree : public Collection { * a > b -> result > 0 * a == b -> result == 0 */ - int (*_compare)(const T & a, const T & b); - static int _BFTreeCompare(BFTreeObject a, BFTreeObject b) { + static int _compare(BFTreeObject a, BFTreeObject b) { + if (!a || !b) return 0; Container * acont = (Container *) a; Container * bcont = (Container *) b; - return acont->_treeRef->_compare(acont->_obj, bcont->_obj); + + C cmp; + return cmp(acont->_obj, bcont->_obj); } - void (*_release)(T obj); - static void _BFTreeRelease(BFTreeObject object) { + static void _release(BFTreeObject object) { Container * cont = (Container *) object; BFRelease(cont); } diff --git a/bflibcpp/testbench/cpplib_tests.hpp b/bflibcpp/testbench/cpplib_tests.hpp index f71498bd..496c43bd 100644 --- a/bflibcpp/testbench/cpplib_tests.hpp +++ b/bflibcpp/testbench/cpplib_tests.hpp @@ -6,9 +6,32 @@ #ifndef CPPLIB_TESTS_HPP #define CPPLIB_TESTS_HPP +#include "allocator.hpp" + extern "C" { #include +#include } +template<> struct BF::Allocator { + char * create() const { + return 0; + } + + void release(char * obj) const { + BFFree(obj); + } +}; + +template<> struct BF::Allocator { + int * create() const { + return 0; + } + + void release(int * obj) const { + BFFree(obj); + } +}; + #endif // CPPLIB_TESTS_HPP diff --git a/bflibcpp/testbench/hashmap_tests.hpp b/bflibcpp/testbench/hashmap_tests.hpp index 142548ba..48e9ee99 100644 --- a/bflibcpp/testbench/hashmap_tests.hpp +++ b/bflibcpp/testbench/hashmap_tests.hpp @@ -27,7 +27,6 @@ int BFTestHashMapCompareString(String & a, String & b) { BFTEST_UNIT_FUNC(test_hashMapInsert, 2<<10, { HashMap map; - map.setCompare(BFTestHashMapCompareString); int mapsize = 2<<7; for (int i = 0; i < mapsize; i++) { @@ -39,7 +38,6 @@ BFTEST_UNIT_FUNC(test_hashMapInsert, 2<<10, { BFTEST_UNIT_FUNC(test_hashMapGet, 2<<10, { HashMap map; - map.setCompare(BFTestHashMapCompareString); // insert int mapsize = 2<<7; @@ -66,7 +64,6 @@ BFTEST_UNIT_FUNC(test_hashMapRemove, 2<<10, { BFRandInit(time(0)); } HashMap map; - map.setCompare(BFTestHashMapCompareString); // insert int mapsize = 2<<7; @@ -105,8 +102,6 @@ BFTEST_UNIT_FUNC(test_hashMapWithAllocMem, 2<<10, { BFRandInit(time(0)); } HashMap map; - map.setCompare(BFTestHashMapCompareCString); - map.setRelease(BFTestHashMapReleaseKeyCString, BFTestHashMapReleaseValueInteger); // insert int mapsize = 2<<7; @@ -122,7 +117,6 @@ BFTEST_UNIT_FUNC(test_hashMapWithAllocMem, 2<<10, { BFTEST_UNIT_FUNC(test_hashMapGetterWithSubscript, 2<<10, { HashMap map; - map.setCompare(BFTestHashMapCompareString); // insert int mapsize = 2<<7; diff --git a/bflibcpp/testbench/hashset_tests.hpp b/bflibcpp/testbench/hashset_tests.hpp new file mode 100644 index 00000000..a767e3df --- /dev/null +++ b/bflibcpp/testbench/hashset_tests.hpp @@ -0,0 +1,85 @@ +/** + * author: Brando + * date: 5/27/25 + */ + +#ifndef HASH_SET_TESTS_HPP +#define HASH_SET_TESTS_HPP + +#define ASSERT_PUBLIC_MEMBER_ACCESS + +#include "hashset.hpp" +#include "string.hpp" + +extern "C" { +#include +} + +using namespace BF; + +BFTEST_UNIT_FUNC(test_hashSetInit, 2<<10, { + HashSet set; +}) + +BFTEST_UNIT_FUNC(test_hashSetInsert, 2<<10, { + HashSet set; + + int setsize = 2<<7; + for (int i = 0; i < setsize; i++) { + String value("%d", i); + int err = set.insert(value); + BF_ASSERT(err == 0, "error inserting %d", err); + } +}) + +BFTEST_UNIT_FUNC(test_hashSetRemove, 2<<10, { + if (BFTEST_UNIT_FUNC_ITR == 0) { + BFRandInit(time(0)); + } + HashSet set; + + // insert + int setsize = 2<<7; + for (int i = 0; i < setsize; i++) { + String value("%d", i); + int err = set.insert(value); + BF_ASSERT(err == 0, "error inserting %d", err); + } + + // remove + int removeCount = 10; + while (removeCount) { + String value("%d", BFMathAbs(BFRand()) % setsize); + if (set.contains(value)) { + int err = set.remove(value); + BF_ASSERT(err == 0, "error removing for key=%s: %d", value.c_str(), err); + removeCount--; + } + } +}) + +BFTEST_UNIT_FUNC(test_hashSetWithAllocMem, 2<<10, { + if (BFTEST_UNIT_FUNC_ITR == 0) { + BFRandInit(time(0)); + } + HashSet set; + + // insert + int setsize = 2<<7; + for (int i = 0; i < setsize; i++) { + char * value = (char *) malloc(sizeof(char) * 32); + snprintf(value, 32, "%d", i); + int err = set.insert(value); + BF_ASSERT(err == 0, "error inserting %d", err); + } +}) + +BFTEST_COVERAGE_FUNC(hashset_tests, { + BFTEST_LAUNCH(test_hashSetInit); + BFTEST_LAUNCH(test_hashSetInsert); + BFTEST_LAUNCH(test_hashSetRemove); + BFTEST_LAUNCH(test_hashSetWithAllocMem); +}) + +#endif // HASH_SET_TESTS_HPP + diff --git a/bflibcpp/testbench/map_tests.hpp b/bflibcpp/testbench/map_tests.hpp index 2b6a07e8..6ea317e2 100644 --- a/bflibcpp/testbench/map_tests.hpp +++ b/bflibcpp/testbench/map_tests.hpp @@ -26,7 +26,6 @@ int BFTestMapCompareString(String & a, String & b) { BFTEST_UNIT_FUNC(test_mapInsert, 2<<10, { Map map; - map.setCompare(BFTestMapCompareString); int mapsize = 2<<7; for (int i = 0; i < mapsize; i++) { @@ -38,7 +37,6 @@ BFTEST_UNIT_FUNC(test_mapInsert, 2<<10, { BFTEST_UNIT_FUNC(test_mapGet, 2<<10, { Map map; - map.setCompare(BFTestMapCompareString); // insert int mapsize = 2<<7; @@ -65,7 +63,6 @@ BFTEST_UNIT_FUNC(test_mapRemove, 2<<10, { BFRandInit(time(0)); } Map map; - map.setCompare(BFTestMapCompareString); // insert int mapsize = 2<<7; @@ -104,8 +101,6 @@ BFTEST_UNIT_FUNC(test_mapWithAllocMem, 2<<10, { BFRandInit(time(0)); } Map map; - map.setCompare(BFTestMapCompareCString); - map.setRelease(BFTestMapReleaseKeyCString, BFTestMapReleaseValueInteger); // insert int mapsize = 2<<7; @@ -121,7 +116,6 @@ BFTEST_UNIT_FUNC(test_mapWithAllocMem, 2<<10, { BFTEST_UNIT_FUNC(test_mapGetWithSubscript, 2<<10, { Map map; - map.setCompare(BFTestMapCompareString); // insert int mapsize = 2<<7; diff --git a/bflibcpp/testbench/removepointer_tests.hpp b/bflibcpp/testbench/removepointer_tests.hpp new file mode 100644 index 00000000..7ab91f95 --- /dev/null +++ b/bflibcpp/testbench/removepointer_tests.hpp @@ -0,0 +1,31 @@ +/** + * author: Brando + * date: 5/27/25 + */ + +#ifndef REMOVE_POINTER_TESTS_HPP +#define REMOVE_POINTER_TESTS_HPP + +#define ASSERT_PUBLIC_MEMBER_ACCESS + +#include "removepointer.hpp" + +extern "C" { +#include +} + +using namespace BF; + +BFTEST_UNIT_FUNC(test_removePointerInit, 2<<10, { + srand(time(0)); + int expected = rand(); + RemovePointer::type actual = expected; + BF_ASSERT(actual == expected); +}) + +BFTEST_COVERAGE_FUNC(removepointer_tests, { + BFTEST_LAUNCH(test_removePointerInit); +}) + +#endif // REMOVE_POINTER_TESTS_HPP + diff --git a/bflibcpp/testbench/set_tests.hpp b/bflibcpp/testbench/set_tests.hpp new file mode 100644 index 00000000..870b91f7 --- /dev/null +++ b/bflibcpp/testbench/set_tests.hpp @@ -0,0 +1,107 @@ +/** + * author: Brando + * date: 5/27/25 + */ + +#ifndef SET_TESTS_HPP +#define SET_TESTS_HPP + +#define ASSERT_PUBLIC_MEMBER_ACCESS + +#include "set.hpp" + +extern "C" { +#include +} + +using namespace BF; + +BFTEST_UNIT_FUNC(test_setInit, 2<<10, { + Set set; +}) + +BFTEST_UNIT_FUNC(test_setInsert, 2<<10, { + Set set; + + int setsize = 2<<7; + for (int i = 0; i < setsize; i++) { + String value("%d", i); + int err = set.insert(value); + BF_ASSERT(err == 0, "error inserting %d", err); + } +}) + +BFTEST_UNIT_FUNC(test_setContains, 2<<10, { + Set set; + + // insert + int setsize = 2<<7; + for (int i = 0; i < setsize; i++) { + String value("%d", i); + int err = set.insert(value); + BF_ASSERT(err == 0, "error inserting %d", err); + } + + // get + for (int i = 0; i < setsize; i++) { + String value("%d", i); + try { + BF_ASSERT(set.contains(value), "!contains(%s)", value.cString()); + } catch (Exception & e) { + BF_ASSERT(false, "%s", e.what()); + } + } +}) + +BFTEST_UNIT_FUNC(test_setRemove, 2<<10, { + if (BFTEST_UNIT_FUNC_ITR == 0) { + BFRandInit(time(0)); + } + Set set; + + // insert + int setsize = 2<<7; + for (int i = 0; i < setsize; i++) { + String value("%d", i); + int err = set.insert(value); + BF_ASSERT(err == 0, "error inserting %d", err); + } + + // remove + int removeCount = 10; + while (removeCount) { + String value("%d", BFMathAbs(BFRand()) % setsize); + if (set.contains(value)) { + int err = set.remove(value); + BF_ASSERT(err == 0, "error removing for key=%s: %d", value.c_str(), err); + removeCount--; + } + } +}) + +BFTEST_UNIT_FUNC(test_setWithAllocMem, 2<<10, { + if (BFTEST_UNIT_FUNC_ITR == 0) { + BFRandInit(time(0)); + } + Set set; + + // insert + int setsize = 2<<7; + for (int i = 0; i < setsize; i++) { + char * value = (char *) malloc(sizeof(char) * 32); + snprintf(value, 32, "%d", i); + int err = set.insert(value); + BF_ASSERT(err == 0, "error inserting %d", err); + } +}) + +BFTEST_COVERAGE_FUNC(set_tests, { + BFTEST_LAUNCH(test_setInit); + BFTEST_LAUNCH(test_setInsert); + BFTEST_LAUNCH(test_setContains); + BFTEST_LAUNCH(test_setRemove); + BFTEST_LAUNCH(test_setWithAllocMem); +}) + +#endif // SET_TESTS_HPP + diff --git a/bflibcpp/testbench/tests.cpp b/bflibcpp/testbench/tests.cpp index 77ef3eac..ad3375a3 100644 --- a/bflibcpp/testbench/tests.cpp +++ b/bflibcpp/testbench/tests.cpp @@ -20,6 +20,9 @@ #include "tree_tests.hpp" #include "url_tests.hpp" #include "defer_tests.hpp" +#include "set_tests.hpp" +#include "hashset_tests.hpp" +#include "removepointer_tests.hpp" BFTEST_SUITE_FUNC({ BFTEST_SUITE_LAUNCH(atomic_tests); @@ -37,6 +40,8 @@ BFTEST_SUITE_FUNC({ BFTEST_SUITE_LAUNCH(exception_tests); BFTEST_SUITE_LAUNCH(tree_tests); BFTEST_SUITE_LAUNCH(url_tests); - BFTEST_SUITE_LAUNCH(defer_tests); + BFTEST_SUITE_LAUNCH(set_tests); + BFTEST_SUITE_LAUNCH(hashset_tests); + BFTEST_SUITE_LAUNCH(removepointer_tests); }) diff --git a/bflibcpp/testbench/tree_tests.hpp b/bflibcpp/testbench/tree_tests.hpp index 49fdbb47..5cafcc28 100644 --- a/bflibcpp/testbench/tree_tests.hpp +++ b/bflibcpp/testbench/tree_tests.hpp @@ -21,22 +21,17 @@ int BFTestTreeCompare(const int & a, const int & b) { return a - b; } -void BFTestTreeRelease(int object) { } - BFTEST_UNIT_FUNC(test_treeInit, 2<<10, { Tree tree; - tree.setCompare(BFTestTreeCompare); - tree.setRelease(BFTestTreeRelease); }) BFTEST_UNIT_FUNC(test_treeInsert, 2<<10, { Tree tree; - tree.setCompare(BFTestTreeCompare); - tree.setRelease(BFTestTreeRelease); int treesize = 2<<8; for (int i = 0; i < treesize; i++) { - BF_ASSERT(!tree.insert(i)); + int err = tree.insert(i); + BF_ASSERT(err == 0, "itr=%d, couldn't insert %d, err=%d", BFTEST_UNIT_FUNC_ITR, i, err); } BF_ASSERT(tree.size() == treesize, "%ld != %ld", tree.size(), treesize); }) @@ -46,12 +41,10 @@ BFTEST_UNIT_FUNC(test_treeRemove, 2<<10, { BFRandInit(time(0)); } Tree tree; - tree.setCompare(BFTestTreeCompare); - tree.setRelease(BFTestTreeRelease); int treesize = 2<<8; for (int i = 0; i < treesize; i++) { - BF_ASSERT(!tree.insert(i)); + BF_ASSERT(!tree.insert(i), "couldn't insert %d", i); } BF_ASSERT(tree.size() == treesize, "%ld != %ld", tree.size(), treesize); @@ -66,12 +59,10 @@ BFTEST_UNIT_FUNC(test_treeRemove, 2<<10, { BFTEST_UNIT_FUNC(test_treeContains, 2<<10, { Tree tree; - tree.setCompare(BFTestTreeCompare); - tree.setRelease(BFTestTreeRelease); int treesize = 2<<8; for (int i = 0; i < treesize; i += 2) { - BF_ASSERT(!tree.insert(i)); + BF_ASSERT(!tree.insert(i), "couldn't insert %d", i); } BF_ASSERT(tree.size() == treesize/2, "%ld != %ld", tree.size(), treesize); @@ -117,12 +108,10 @@ void _BFTestTreeTraversePostorder(const typename Tree::Node node) { BFTEST_UNIT_FUNC(test_treeTraversing, 2<<10, { Tree tree; - tree.setCompare(BFTestTreeCompare); - tree.setRelease(BFTestTreeRelease); int treesize = 2<<8; for (int i = 0; i < treesize; i += 2) { - BF_ASSERT(!tree.insert(i)); + BF_ASSERT(!tree.insert(i), "couldn't insert %d", i); } BF_ASSERT(tree.size() == treesize/2, "%ld != %ld", tree.size(), treesize); @@ -139,17 +128,11 @@ int BFTestTreeComparePointers(int * const &ap, int * const &bp) { return a - b; } -void BFTestTreeReleasePointer(int * object) { - BFFree(object); -} - BFTEST_UNIT_FUNC(test_treeWithMallocObjects, 2<<10, { if (BFTEST_UNIT_FUNC_ITR == 0) { BFRandInit(time(0)); } Tree tree; - tree.setCompare(BFTestTreeComparePointers); - tree.setRelease(BFTestTreeReleasePointer); int treesize = 2<<8; for (int i = 0; i < treesize; i++) { @@ -168,6 +151,55 @@ BFTEST_UNIT_FUNC(test_treeWithMallocObjects, 2<<10, { } }) +String _BFTreeTestsCreateRandomWord() { + const char charset[] = "abcdefghijklmnopqrstuvwxyz"; + size_t charset_size = strlen(charset); + + srand(time(0)); + + size_t reslen = 2 << 3; + char word[reslen + 1]; + for (int i = 0; i < reslen; i++) { + word[i] = charset[rand() % charset_size]; + } + + word[reslen] = '\0'; + + return word; +} + +BFTEST_UNIT_FUNC(test_treeWithStrings, 2<<8, { + Tree tree(true); + + BF_ASSERT(tree.allowDuplicates()); + + int treesize = 2<<8; + for (int i = 0; i < treesize; i++) { + String word = _BFTreeTestsCreateRandomWord(); + int err = tree.insert(word); + BF_ASSERT(err == 0, "itr=%d, couldn't insert '%s', err=%d", BFTEST_UNIT_FUNC_ITR, word.cString(), err); + } + BF_ASSERT(tree.size() == treesize, "%ld != %ld", tree.size(), treesize); +}) + +void _BFTestTreeRelease(char * str) { + free(str); +} + +BFTEST_UNIT_FUNC(test_treeWithCustomCompare, 2<<10, { + Tree tree(true); + + BF_ASSERT(tree.allowDuplicates()); + + int treesize = 2<<8; + for (int i = 0; i < treesize; i++) { + char * word = _BFTreeTestsCreateRandomWord().cStringCopy(); + int err = tree.insert(word); + BF_ASSERT(err == 0, "itr=%d, couldn't insert '%s', err=%d", BFTEST_UNIT_FUNC_ITR, word, err); + } + BF_ASSERT(tree.size() == treesize, "%ld != %ld", tree.size(), treesize); +}) + BFTEST_COVERAGE_FUNC(tree_tests, { BFTEST_LAUNCH(test_treeInit); BFTEST_LAUNCH(test_treeInsert); @@ -175,7 +207,8 @@ BFTEST_COVERAGE_FUNC(tree_tests, { BFTEST_LAUNCH(test_treeContains); BFTEST_LAUNCH(test_treeTraversing); BFTEST_LAUNCH(test_treeWithMallocObjects); - + BFTEST_LAUNCH(test_treeWithStrings); + BFTEST_LAUNCH(test_treeWithCustomCompare); }) #endif // TREE_TESTS_HPP diff --git a/bflibcpp/todo.md b/bflibcpp/todo.md index 13691c3f..aca6d676 100644 --- a/bflibcpp/todo.md +++ b/bflibcpp/todo.md @@ -31,3 +31,12 @@ - [x] Ability to key a HashMap without defining a compare callback, at least for our standard objects - [ ] URL url = "this"; url = "this"; crashes - [x] Atomically figure out hash function without manually defining one for HashMap +- [ ] Set + - [x] implementation + - [x] tests + - [x] release & compare functor work + - [x] tree + - [x] basic map +- [ ] allocator + - [ ] add onto the create() method. Might be useful in the Array and List structures + diff --git a/bftest/src/bftest.h b/bftest/src/bftest.h index eba2f627..3e969a99 100644 --- a/bftest/src/bftest.h +++ b/bftest/src/bftest.h @@ -95,6 +95,8 @@ void __BFTestFormatElapsedTime__(long long elapsedTimeNS, char * buf, size_t buf int result = 0;\ time_t startTime = __BFTestGetCurrentTimeNS__(); +// the total time executed might be innaccurate considering +// i am running usleep(50) in the loop #define BFTEST_UNIT_END \ time_t endTime = __BFTestGetCurrentTimeNS__();\ if (result == 0) { printf("PASS"); }\