if (isEmptyNode(node)) {
Object[] newNode = new Object[] { packNibbles(key), value };
return this.putToCache(newNode);
}
Value currentNode = this.getNode(node);
// Check for "special" 2 slice type node
if (currentNode.length() == PAIR_SIZE) {
// Decode the key
byte[] k = unpackToNibbles(currentNode.get(0).asBytes());
Object v = currentNode.get(1).asObj();
// Matching key pair (ie. there's already an object with this key)
if (Arrays.equals(k, key)) {
Object[] newNode = new Object[] {packNibbles(key), value};
return this.putToCache(newNode);
}
Object newHash;
int matchingLength = matchingNibbleLength(key, k);
if (matchingLength == k.length) {
// Insert the hash, creating a new node
byte[] remainingKeypart = copyOfRange(key, matchingLength, key.length);
newHash = this.insert(v, remainingKeypart, value);
} else {
// Expand the 2 length slice to a 17 length slice
// Create two nodes to putToCache into the new 17 length node
Object oldNode = this.insert("", copyOfRange(k, matchingLength+1, k.length), v);
Object newNode = this.insert("", copyOfRange(key, matchingLength+1, key.length), value);
// Create an expanded slice
Object[] scaledSlice = emptyStringSlice(17);
// Set the copied and new node
scaledSlice[k[matchingLength]] = oldNode;
scaledSlice[key[matchingLength]] = newNode;
newHash = this.putToCache(scaledSlice);
}
if (matchingLength == 0) {
// End of the chain, return
return newHash;
} else {
Object[] newNode = new Object[] { packNibbles(copyOfRange(key, 0, matchingLength)), newHash};
return this.putToCache(newNode);
}
} else {
// Copy the current node over to the new node
Object[] newNode = copyNode(currentNode);
// Replace the first nibble in the key
newNode[key[0]] = this.insert(currentNode.get(key[0]).asObj(), copyOfRange(key, 1, key.length), value);
return this.putToCache(newNode);
}
}