use of jcog.data.byt.AbstractBytes in project narchy by automenta.
the class MyConcurrentRadixTree method compute.
/**
* Atomically adds the given value to the tree, creating a node for the value as necessary. If the value is already
* stored for the same key, either overwrites the existing value, or simply returns the existing value, depending
* on the given value of the <code>overwrite</code> flag.
*
* @param key The key against which the value should be stored
* @param newValue The value to store against the key
* @param overwrite If true, should replace any existing value, if false should not replace any existing value
* @return The existing value for this key, if there was one, otherwise null
*/
<V> X compute(@NotNull AbstractBytes key, V value, QuadFunction<AbstractBytes, SearchResult, X, V, X> computeFunc) {
// if (key.length() == 0) {
// throw new IllegalArgumentException("The key argument was zero-length");
// }
int version;
X newValue, foundX;
SearchResult result;
int matched;
Object foundValue;
Node found;
version = writes.intValue();
acquireReadLockIfNecessary();
try {
// Note we search the tree here after we have acquired the write lock...
result = searchTree(key);
found = result.found;
matched = result.charsMatched;
foundValue = found != null ? found.getValue() : null;
foundX = ((matched == key.length()) && (foundValue != VoidValue.SINGLETON)) ? ((X) foundValue) : null;
} finally {
releaseReadLockIfNecessary();
}
newValue = computeFunc.apply(key, result, foundX, value);
if (newValue != foundX) {
int version2 = acquireWriteLock();
try {
if (version + 1 != version2) {
// search again because the tree has changed since the initial lookup
result = searchTree(key);
found = result.found;
matched = result.charsMatched;
foundValue = found != null ? found.getValue() : null;
foundX = ((matched == key.length()) && (foundValue != VoidValue.SINGLETON)) ? ((X) foundValue) : null;
if (foundX == newValue)
// no change; the requested value has already been supplied since the last access
return newValue;
}
SearchResult.Classification classification = result.classification;
if (foundX == null)
estSize.incrementAndGet();
FasterList<Node> oedges = found.getOutgoingEdges();
switch(classification) {
case EXACT_MATCH:
if (newValue != foundValue) {
// clone and reattach
cloneAndReattach(result, found, foundValue, oedges);
}
break;
case KEY_ENDS_MID_EDGE:
{
// Search ran out of characters from the key while in the middle of an edge in the node.
// -> Split the node in two: Create a new parent node storing the new value,
// and a new child node holding the original value and edges from the existing node...
AbstractBytes keyCharsFromStartOfNodeFound = key.subSequence(matched - result.charsMatchedInNodeFound, key.length());
AbstractBytes commonPrefix = getCommonPrefix(keyCharsFromStartOfNodeFound, found.getIncomingEdge());
AbstractBytes suffixFromExistingEdge = subtractPrefix(found.getIncomingEdge(), commonPrefix);
// Create new nodes...
Node newChild = createNode(suffixFromExistingEdge, foundValue, oedges, false);
Node newParent = createNode(commonPrefix, newValue, new FasterList(new Node[] { newChild }), false);
// Add the new parent to the parent of the node being replaced (replacing the existing node)...
result.parentNode.updateOutgoingEdge(newParent);
break;
}
case INCOMPLETE_MATCH_TO_END_OF_EDGE:
// Search found a difference in characters between the key and the start of all child edges leaving the
// node, the key still has trailing unmatched characters.
// -> Add a new child to the node, containing the trailing characters from the key.
// NOTE: this is the only branch which allows an edge to be added to the root.
// (Root node's own edge is "" empty string, so is considered a prefixing edge of every key)
// Create a new child node containing the trailing characters...
AbstractBytes keySuffix = key.subSequence(matched, key.length());
Node newChild = createNode(keySuffix, newValue, emptyList, false);
// Clone the current node adding the new child...
int numEdges = oedges.size();
Node[] edgesArray;
if (numEdges > 0) {
edgesArray = new Node[numEdges + 1];
arraycopy(oedges.array(), 0, edgesArray, 0, numEdges);
edgesArray[numEdges] = newChild;
} else {
edgesArray = new Node[] { newChild };
}
cloneAndReattach(result, found, foundValue, new FasterList(edgesArray.length, edgesArray));
break;
case INCOMPLETE_MATCH_TO_MIDDLE_OF_EDGE:
// Search found a difference in characters between the key and the characters in the middle of the
// edge in the current node, and the key still has trailing unmatched characters.
// -> Split the node in three:
// Let's call node found: NF
// (1) Create a new node N1 containing the unmatched characters from the rest of the key, and the
// value supplied to this method
// (2) Create a new node N2 containing the unmatched characters from the rest of the edge in NF, and
// copy the original edges and the value from NF unmodified into N2
// (3) Create a new node N3, which will be the split node, containing the matched characters from
// the key and the edge, and add N1 and N2 as child nodes of N3
// (4) Re-add N3 to the parent node of NF, effectively replacing NF in the tree
AbstractBytes suffixFromKey = key.subSequence(matched, key.length());
// Create new nodes...
Node n1 = createNode(suffixFromKey, newValue, emptyList, false);
AbstractBytes keyCharsFromStartOfNodeFound = key.subSequence(matched - result.charsMatchedInNodeFound, key.length());
AbstractBytes commonPrefix = getCommonPrefix(keyCharsFromStartOfNodeFound, found.getIncomingEdge());
AbstractBytes suffixFromExistingEdge = subtractPrefix(found.getIncomingEdge(), commonPrefix);
Node n2 = createNode(suffixFromExistingEdge, foundValue, oedges, false);
@SuppressWarnings("NullableProblems") Node n3 = createNode(commonPrefix, null, new FasterList(2, new Node[] { n1, n2 }), false);
result.parentNode.updateOutgoingEdge(n3);
// Return null for the existing value...
break;
default:
// This is a safeguard against a new enum constant being added in future.
throw new IllegalStateException("Unexpected classification for search result: " + result);
}
} finally {
releaseWriteLock();
}
}
return newValue;
}
use of jcog.data.byt.AbstractBytes in project narchy by automenta.
the class MyConcurrentRadixTree method getKeyValuePairsForClosestKeys.
/**
* {@inheritDoc}
*/
public Iterable<Pair<AbstractBytes, Object>> getKeyValuePairsForClosestKeys(AbstractBytes candidate) {
acquireReadLockIfNecessary();
try {
SearchResult searchResult = searchTree(candidate);
SearchResult.Classification classification = searchResult.classification;
switch(classification) {
case EXACT_MATCH:
return getDescendantKeyValuePairs(candidate, searchResult.found);
case KEY_ENDS_MID_EDGE:
// Append the remaining characters of the edge to the key.
// For example if we searched for CO, but first matching node was COFFEE,
// the key associated with the first node should be COFFEE...
AbstractBytes edgeSuffix = getSuffix(searchResult.found.getIncomingEdge(), searchResult.charsMatchedInNodeFound);
candidate = concatenate(candidate, edgeSuffix);
return getDescendantKeyValuePairs(candidate, searchResult.found);
case INCOMPLETE_MATCH_TO_MIDDLE_OF_EDGE:
{
// Example: if we searched for CX, but deepest matching node was CO,
// the results should include node CO and its descendants...
AbstractBytes keyOfParentNode = getPrefix(candidate, searchResult.charsMatched - searchResult.charsMatchedInNodeFound);
AbstractBytes keyOfNodeFound = concatenate(keyOfParentNode, searchResult.found.getIncomingEdge());
return getDescendantKeyValuePairs(keyOfNodeFound, searchResult.found);
}
case INCOMPLETE_MATCH_TO_END_OF_EDGE:
if (searchResult.charsMatched == 0) {
// Closest match is the root node, we don't consider this a match for anything...
break;
}
// Example: if we searched for COFFEE, but deepest matching node was CO,
// the results should include node CO and its descendants...
AbstractBytes keyOfNodeFound = getPrefix(candidate, searchResult.charsMatched);
return getDescendantKeyValuePairs(keyOfNodeFound, searchResult.found);
}
return Collections.emptySet();
} finally {
releaseReadLockIfNecessary();
}
}
use of jcog.data.byt.AbstractBytes in project narchy by automenta.
the class MyConcurrentRadixTree method cloneAndReattach.
private void cloneAndReattach(SearchResult searchResult, Node found, Object foundValue, FasterList<Node> edges) {
AbstractBytes ie = found.getIncomingEdge();
boolean root = ie.length() == 0;
Node clonedNode = createNode(ie, foundValue, edges, root);
// Re-add the cloned node to its parent node...
if (root) {
this.root = clonedNode;
} else {
searchResult.parentNode.updateOutgoingEdge(clonedNode);
}
}
use of jcog.data.byt.AbstractBytes in project narchy by automenta.
the class MyConcurrentRadixTree method getClosestKeys.
/**
* {@inheritDoc}
*/
public Iterable<AbstractBytes> getClosestKeys(AbstractBytes candidate) {
acquireReadLockIfNecessary();
try {
SearchResult searchResult = searchTree(candidate);
SearchResult.Classification classification = searchResult.classification;
switch(classification) {
case EXACT_MATCH:
return getDescendantKeys(candidate, searchResult.found);
case KEY_ENDS_MID_EDGE:
// Append the remaining characters of the edge to the key.
// For example if we searched for CO, but first matching node was COFFEE,
// the key associated with the first node should be COFFEE...
AbstractBytes edgeSuffix = getSuffix(searchResult.found.getIncomingEdge(), searchResult.charsMatchedInNodeFound);
candidate = concatenate(candidate, edgeSuffix);
return getDescendantKeys(candidate, searchResult.found);
case INCOMPLETE_MATCH_TO_MIDDLE_OF_EDGE:
{
// Example: if we searched for CX, but deepest matching node was CO,
// the results should include node CO and its descendants...
AbstractBytes keyOfParentNode = getPrefix(candidate, searchResult.charsMatched - searchResult.charsMatchedInNodeFound);
AbstractBytes keyOfNodeFound = concatenate(keyOfParentNode, searchResult.found.getIncomingEdge());
return getDescendantKeys(keyOfNodeFound, searchResult.found);
}
case INCOMPLETE_MATCH_TO_END_OF_EDGE:
if (searchResult.charsMatched == 0) {
// Closest match is the root node, we don't consider this a match for anything...
break;
}
// Example: if we searched for COFFEE, but deepest matching node was CO,
// the results should include node CO and its descendants...
AbstractBytes keyOfNodeFound = getPrefix(candidate, searchResult.charsMatched);
return getDescendantKeys(keyOfNodeFound, searchResult.found);
}
return Collections.emptySet();
} finally {
releaseReadLockIfNecessary();
}
}
use of jcog.data.byt.AbstractBytes in project narchy by automenta.
the class MyConcurrentRadixTree method getKeysStartingWith.
/**
* {@inheritDoc}
*/
public Iterable<AbstractBytes> getKeysStartingWith(AbstractBytes prefix) {
acquireReadLockIfNecessary();
try {
SearchResult searchResult = searchTree(prefix);
Node nodeFound = searchResult.found;
switch(searchResult.classification) {
case EXACT_MATCH:
return getDescendantKeys(prefix, nodeFound);
case KEY_ENDS_MID_EDGE:
// Append the remaining characters of the edge to the key.
// For example if we searched for CO, but first matching node was COFFEE,
// the key associated with the first node should be COFFEE...
AbstractBytes edgeSuffix = getSuffix(nodeFound.getIncomingEdge(), searchResult.charsMatchedInNodeFound);
prefix = concatenate(prefix, edgeSuffix);
return getDescendantKeys(prefix, nodeFound);
default:
// Incomplete match means key is not a prefix of any node...
return Collections.emptySet();
}
} finally {
releaseReadLockIfNecessary();
}
}
Aggregations