static void test_ght_hash_leaf_parts(void) { /* GhtErr ght_hash_leaf_parts(const GhtHash *a, const GhtHash *b, int maxlen, GhtHashMatch *matchtype, GhtHash **a_leaf, GhtHash **b_leaf) */ GhtHash *a, *b; GhtHash *a_leaf, *b_leaf; GhtHashMatch matchtype; GhtErr e; int maxlen = 5; a = "abcdefgh"; b = "abcdefgh"; maxlen = 8; e = ght_hash_leaf_parts(a, b, maxlen, &matchtype, &a_leaf, &b_leaf); CU_ASSERT_EQUAL(e, GHT_OK); CU_ASSERT_EQUAL(matchtype, GHT_SAME); maxlen = 5; e = ght_hash_leaf_parts(a, b, maxlen, &matchtype, &a_leaf, &b_leaf); CU_ASSERT_EQUAL(e, GHT_OK); CU_ASSERT_EQUAL(matchtype, GHT_SPLIT); CU_ASSERT_STRING_EQUAL(a_leaf, "fgh"); CU_ASSERT_STRING_EQUAL(b_leaf, "fgh"); a = "abcde"; b = "abcdefgh"; maxlen = 8; e = ght_hash_leaf_parts(a, b, maxlen, &matchtype, &a_leaf, &b_leaf); CU_ASSERT_EQUAL(e, GHT_OK); CU_ASSERT_EQUAL(matchtype, GHT_CHILD); CU_ASSERT_STRING_EQUAL(b_leaf, "fgh"); a = "abcde"; b = "1abcdefgh"; maxlen = 8; e = ght_hash_leaf_parts(a, b, maxlen, &matchtype, &a_leaf, &b_leaf); CU_ASSERT_EQUAL(e, GHT_ERROR); CU_ASSERT_EQUAL(matchtype, GHT_NONE); a = ""; b = "1abcdefgh"; maxlen = 8; e = ght_hash_leaf_parts(a, b, maxlen, &matchtype, &a_leaf, &b_leaf); CU_ASSERT_EQUAL(e, GHT_OK); CU_ASSERT_EQUAL(matchtype, GHT_GLOBAL); a = "abcdafda"; b = "1abcdh"; maxlen = 8; e = ght_hash_leaf_parts(a, b, maxlen, &matchtype, &a_leaf, &b_leaf); CU_ASSERT_EQUAL(e, GHT_ERROR); CU_ASSERT_EQUAL(matchtype, GHT_NONE); }
/** * Recursive function, walk down from parent node, looking for * appropriate insertion point for node_to_insert. If duplicates, * and duplicate leaf, insert as hash-less "attribute only" node. * ["abcdefg", "abcdeff", "abcdddd", "abbbeee"] becomes * "ab"->["c"->["d"->["ddd","ef"->["g","f"]]],"b"] */ GhtErr ght_node_insert_node(GhtNode *node, GhtNode *node_to_insert, GhtDuplicates duplicates) { GhtHash *node_leaf, *node_to_insert_leaf; GhtErr err; GhtHashMatch matchtype; /* NULL hash implies this node is a faux node for duplicate points */ if ( ! node->hash ) return GHT_INCOMPLETE; /* matchtype in (GHT_NONE, GHT_GLOBAL, GHT_SAME, GHT_CHILD, GHT_SPLIT) */ /* NONE and GLOBAL come back with GHT_ERROR, so we don't handle them yet */ GHT_TRY(ght_hash_leaf_parts(node->hash, node_to_insert->hash, GHT_MAX_HASH_LENGTH, &matchtype, &node_leaf, &node_to_insert_leaf)); /* Insert node is child of node, either explicitly, or implicitly for */ /* the "" hash which serves as a master parent */ /* "abcdef" is a GHT_CHILD or "abc", and gets added as "def" */ if ( matchtype == GHT_CHILD || matchtype == GHT_GLOBAL ) { int i; ght_node_set_hash(node_to_insert, ght_strdup(node_to_insert_leaf)); for ( i = 0; i < ght_node_num_children(node); i++ ) { err = ght_node_insert_node(node->children->nodes[i], node_to_insert, duplicates); /* Node added to one of the children */ if ( err == GHT_OK ) return GHT_OK; } /* Node didn't fit any of the children, so add it at this level */ return ght_node_add_child(node, node_to_insert); } if ( matchtype == GHT_SAME ) { /* New node is duplicate of this node. We insert an */ /* empty node (no hash) underneath, to hang attributes off of */ /* and use this node as the parent */ if ( duplicates ) { /* If this is the first duplicate, add a copy of the parent */ /* To serve as a proxy leaf for this value */ if ( ( ! node->children ) || ( node->children->num_nodes == 0 ) ) { GhtNode *parent_leaf; GHT_TRY(ght_node_new(&parent_leaf)); GHT_TRY(ght_node_transfer_attributes(node, parent_leaf)); GHT_TRY(ght_node_add_child(node, parent_leaf)); } /* Add the new node under the parent, stripping the hash */ ght_free(node_to_insert->hash); node_to_insert->hash = NULL; GHT_TRY(ght_node_add_child(node, node_to_insert)); return GHT_OK; } else { /* For now, we just skip duplicates. */ /* In future, average / median the duplicates onto parent here? */ return GHT_OK; } } /* "abcdef" and "abcghi" need to GHT_SPLIT, into "abc"->["def", "ghi"] */ if ( matchtype == GHT_SPLIT ) { /* We need a new node to hold that part of the parent that is not shared */ GhtNode *another_node_to_insert; GHT_TRY(ght_node_new_from_hash(node_leaf, &another_node_to_insert)); /* Move attributes to the new child */ GHT_TRY(ght_node_transfer_attributes(node, another_node_to_insert)); /* Any children of the parent need to move down the tree with the unique part of the hash */ if ( node->children ) { another_node_to_insert->children = node->children; node->children = NULL; } /* Null-terminate parent hash at end of shared part */ *node_leaf = '\0'; /* Pull the non-shared part of insert node hash to the front */ memmove(node_to_insert->hash, node_to_insert_leaf, strlen(node_to_insert_leaf)+1); /* Add the unique portion of the parent to the parent */ GHT_TRY(ght_node_add_child(node, another_node_to_insert)); /* Add the unique portion of the insert node to the parent */ GHT_TRY(ght_node_add_child(node, node_to_insert)); /* Done! */ return GHT_OK; } /* Don't get here */ return GHT_ERROR; }