static typeNode* SplitBranch(typeNode* parent,unsigned long index)
{
typeNode* working;
char* data;
unsigned long I;
if ((index < 1) || (index >= parent->Length))
return 0;
working = CreateBranch(0,parent->Data + index,parent->Length - index);
if (working == 0)
return 0;
working->Child = parent->Child;
parent->Child = working;
data = (char*)malloc(index);
if (data == 0)
return 0;
for (I = 0;I < index;I++)
data[I] = parent->Data[I];
free(parent->Data);
parent->Length = index;
parent->Data = data;
return working;
}
This is a bit complicated. We first create a new node for the back end of the split point, that part is fine. But then we go and allocate a whole new buffer for the front end, fill it up with the front part of the split buffer, and then replace the node string with the new buffer. The problem with this besides too much code is, what if we want to improve the create string function later.
static typeNode* SplitBranch(typeNode* parent,unsigned long index)
{
typeNode* node1;
typeNode* node2;
char* data;
unsigned long I;
if ((index < 1) || (index >= parent->Length))
return 0;
node1 = CreateBranch(0,parent->Data,index);
if (node1 == 0)
return 0;
node2 = CreateBranch(0,parent->Data + index,parent->Length - index);
if (node2 == 0)
return 0;
free(parent->Data);
parent->Length = node1->Length;
parent->Data = node1->Data;
free(node1)
node2->Child = parent->Child;
parent->Child = node2;
return working;
}
This is the improved version of the code. We have not really changed much, except that it is a lot easier to read. What we have done here is made the code simpler and more robust by pushing all the hard work to earlier functions. Instead of creating the first part of the split buffer int a second function, it is now done in the one function that is dedicated to that one thing. If we want to change something later, we can just change create branch and not have to worry about too many other things.
node1 = CreateBranch(0,parent->Data,index);
if (node1 == 0)
return 0;
node2 = CreateBranch(0,parent->Data + index,parent->Length - index);
if (node2 == 0)
return 0;
In this part of the code we just create two new nodes that contain the two parts of the string we are splitting. Because of the way a trie works, we have to keep the original node and discard the first node.
free(parent->Data);
parent->Length = node1->Length;
parent->Data = node1->Data;
free(node1)
node2->Child = parent->Child;
parent->Child = node2;
In this part, we free the original string buffer of the parent node, then replace it with the buffer of the first node we have just created. Then we free the first node. We only created it a in order to get a new buffer. Doing that by calling create branch is a lot easier and safer than doing it manually inside a new function. As I said before, it is a good habit to keep memory operations isolated in a few places. In fact, even freeing the node and the data here would be a mistake, but it is fine because all the operations are isolated inside the CreateBranch and SplitBranch functions.
The next two line do something a bit complicated and not really obvious. The nodes inside a trie form strings with their children. The parent is the first part of the string, and each child is part of a separate string. When we split a node, we still need to have a single string. This is why all the children that the split node had before must be moved to the new node we have created, because the parent node and the new node are a single prefix for all the nodes that the parent had before. This means that after the split, nothing has changed. The next to last line in the code above gives all the children of the parent to the new node, and the last line sets the new node as the parent's only child. Later, after the split, the parent will receive new children, that is why we need to split the node.
This is not quite clear yet, but I will try to make a visual explanation later.