private boolean insertLesser(Edge into, char[] key, int index, int end, E value) { int matchesTo = 1; // only called when we've already matched the first char final int keyLen = end - index; final char[] lesserKey = into.lesser.key; for (; matchesTo < keyLen; matchesTo++) { if (matchesTo == lesserKey.length) { return false; } int delta = key[index + matchesTo] - lesserKey[matchesTo]; if (delta < 0) { // new node is less than lesser into.lesser = newEdgeLesser(into.lesser, keyLen, lesserKey, matchesTo, key, index, end, value); return true; } if (delta > 0) { // new node is greater than lesser into.lesser = newEdgeGreater(into.lesser, keyLen, lesserKey, matchesTo, key, index, end, value); return true; } } if (matchesTo == lesserKey.length) { return false; } // If we haven't returned, than the existing key is longer than the one // we are inserting. Thus, we must slip the new node behind the old one. Edge newNode = new Edge(key, index, end); newNode.value = value; char[] newLesser = new char[lesserKey.length - keyLen]; System.arraycopy(lesserKey, keyLen, newLesser, 0, newLesser.length); newNode.lesser = into.lesser; into.lesser = newNode; newNode.lesser.key = newLesser; return true; }
protected Edge newEdgeGreater( Edge previous, int keyMax, char[] existing, int matchesTo, char[] key, int keyIndex, int keyEnd, E value) { // found our break point char[] newRootKey = new char[matchesTo]; char[] newExistingKey = new char[existing.length - newRootKey.length]; char[] newInsertedKey = new char[keyMax - newRootKey.length]; // copy the common root into our new parent edge System.arraycopy(existing, 0, newRootKey, 0, newRootKey.length); Edge newRoot = new Edge(newRootKey, 0, newRootKey.length); // trim the existing key to it's unique suffix value System.arraycopy(existing, newRootKey.length, newExistingKey, 0, newExistingKey.length); previous.key = newExistingKey; // create a new node for our value System.arraycopy(key, keyIndex + newRootKey.length, newInsertedKey, 0, newInsertedKey.length); Edge newEdge = new Edge(newInsertedKey, 0, newInsertedKey.length); newEdge.value = value; assert newRoot.key.length > 0; assert previous.key.length > 0; assert newEdge.key.length > 0; newRoot.lesser = previous; newRoot.greater = newEdge; assert newEdge.toString().compareTo(previous.toString()) > 0; return newRoot; }
protected void doPut(final Edge into, char[] key, final int index, int end, E value) { assert index < end; // To stay threadsafe, we synchronize on Edges when we modify them. // To stay fast, we don't recurse until we are out of the synchro block. // We optimize for our worst-case scenario off the hop; // which is a deep node transversal (when one node points to many). final Edge nextInto; int nextIndex; final char k = key[index]; // handle peeking into deeper nodes that will result in recursion. final Edge greater = into.greater; if (greater != null) { assert into.lesser != null; final char[] greaterKey = greater.key; // deep nodes are stored in greater slot if (greaterKey.length == 0) { // this is a deep node! // bounds check on its lesser if (k - greater.lesser.key[0] >= 0) { // if inserted key is not less than the lesser of the deep node, // then recurse into the greater, without locking. doPut(greater, key, index, end, value); return; } // we are in a deep node, and are less than the greater. // check if we need to insert a new deep node. synchronized (into) { // wait for any operations to finish if (greater == into.greater) { // The only comod we need to worry about here is the greater node; // if the lesser is changed while we were waiting, we're still okay. final Edge lesser = into.lesser; final int delta = k - lesser.key[0]; if (delta != 0) { Edge newParent = new Edge(); newParent.greater = into.greater; Edge newNode = new Edge(key, index, end); newNode.value = value; if (delta > 0) { // new node is greater than current lesser; replace into.greater newParent.lesser = newNode; } else { // new node is less than our lesser; take lesser spot // and make the old lesser a new deep node newParent.lesser = lesser; into.lesser = newNode; } into.greater = newParent; return; // done! } // we start with the same char as into.lesser; // find out how far we match, and possibly recurse. if (insertLesser(into, key, index, end, value)) return; // if we didn't return, we must recurse into this lesser nextInto = into.lesser; nextIndex = index + lesser.key.length; } else { // the trie was modified while we were waiting, // recurse, as we need to run the deep checks again. nextInto = into; nextIndex = index; } } // end synchro // if we didn't return, we need to recurse. if (nextIndex == end) { nextInto.value = value; } else { doPut(nextInto, key, nextIndex, end, value); } return; } // end deep node } // end into.greater != null // because we are only locking on the parent node, // but potentially modifying the structure of child nodes, // and we don't want to invite deadlock, we only ever iterate downward; // we acquire the locks on children before modifying them // or reading their lesser / greater nodes. synchro: synchronized (into) { // into.lesser will only ever be null on the very first put. if (into.lesser == null) { assert into.greater == null; // both null, just take lesser and exit into.lesser = new Edge(key, index, end); into.lesser.value = value; return; } // start our compare on lesser... final char[] lesserKey = into.lesser.key; final int deltaLesser = k - lesserKey[0]; if (deltaLesser == 0) { // we match the first char of the lesser. if (insertLesser(into, key, index, end, value)) { return; } else { // if we didn't return, we must recurse nextInto = into.lesser; nextIndex = index + lesserKey.length; break synchro; } } // if we are less than the lesser, we need to usurp its position if (into.greater == null) { // with no greater node, our job is easy. Just fill this node up. Edge newNode = new Edge(key, index, end); newNode.value = value; if (deltaLesser < 0) { into.greater = into.lesser; into.lesser = newNode; } else { into.greater = newNode; } return; } // we have to check the greater, // which may have changed since we last deep-checked it... final char[] greaterKey = into.greater.key; if (greaterKey.length == 0) { // the greater is now deep and it wasn't before. // recurse back into the same node; we can't get back here once deep nextInto = into; nextIndex = index; break synchro; } if (deltaLesser < 0) { // A greater exists, but we still need to usurp lesser Edge newParent = new Edge(); Edge newNode = new Edge(key, index, end); newNode.value = value; newParent.lesser = into.lesser; newParent.greater = into.greater; into.greater = newParent; into.lesser = newNode; return; } // The only thing left to do is run a compare on greater final int deltaGreater = k - greaterKey[0]; if (deltaGreater == 0) { // we must insert into the greater, or else recurse if (insertGreater(into, key, index, end, value)) return; nextInto = into.greater; nextIndex = index + into.greater.key.length; break synchro; } // we don't start with greater or lesser, and must create a deep node Edge newParent = new Edge(); Edge newNode = new Edge(key, index, end); newNode.value = value; if (deltaGreater > 0) { // new node is the greatest newParent.greater = newNode; newParent.lesser = into.greater; } else { newParent.greater = into.greater; newParent.lesser = newNode; } into.greater = newParent; return; } // end synchro. If we haven't returned, we need to recurse. if (nextIndex == end) { nextInto.value = value; } else { doPut(nextInto, key, nextIndex, end, value); } }