Search in sources :

Example 1 with AbstractBytes

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;
}
Also used : AbstractBytes(jcog.data.byt.AbstractBytes) FasterList(jcog.list.FasterList)

Example 2 with AbstractBytes

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();
    }
}
Also used : AbstractBytes(jcog.data.byt.AbstractBytes)

Example 3 with AbstractBytes

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);
    }
}
Also used : AbstractBytes(jcog.data.byt.AbstractBytes)

Example 4 with AbstractBytes

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();
    }
}
Also used : AbstractBytes(jcog.data.byt.AbstractBytes)

Example 5 with AbstractBytes

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();
    }
}
Also used : AbstractBytes(jcog.data.byt.AbstractBytes)

Aggregations

AbstractBytes (jcog.data.byt.AbstractBytes)8 FasterList (jcog.list.FasterList)2