/** * Creates a tree slice from a digit. * * @param <N> node type * @param <E> element type * @param nodes the digit * @param from element offset * @param len number of elements * @param buffer buffer to insert the node slice into * @param inBuffer initial number of nodes in the buffer * @return the slice */ private static <N, E> int splitDigit( final Node<N, E>[] nodes, final long from, final long len, final NodeLike<N, E>[] buffer, final int inBuffer) { if (len <= 0) return inBuffer; // find the first sub-node containing used elements int firstPos = 0; long firstOff = from; Node<N, E> first = nodes[0]; long firstSize = first.size(); while (firstOff >= firstSize) { firstOff -= firstSize; first = nodes[++firstPos]; firstSize = first.size(); } // firstOff < firstSize final long inFirst = firstSize - firstOff; if (inFirst >= len) { // everything in first sub-node final NodeLike<N, E> part = len == firstSize ? first : first.slice(firstOff, len); return part.append(buffer, inBuffer); } final NodeLike<N, E> firstSlice = firstOff == 0 ? first : first.slice(firstOff, inFirst); int numMerged = firstSlice.append(buffer, inBuffer); int pos = firstPos; long remaining = len - inFirst; while (remaining > 0) { final Node<N, E> curr = nodes[++pos]; final long currSize = curr.size(); final NodeLike<N, E> slice = remaining >= currSize ? curr : curr.slice(0, remaining); numMerged = slice.append(buffer, numMerged); remaining -= currSize; } return numMerged; }
@Override public TreeSlice<N, E> slice(final long from, final long len) { if (from == 0 && len == size) return new TreeSlice<>(this); final long midSize = middle.size(), rightOff = leftSize + midSize; final long inLeft = from + len <= leftSize ? len : from < leftSize ? leftSize - from : 0; final long inRight = from >= rightOff ? len : from + len > rightOff ? from + len - rightOff : 0; @SuppressWarnings("unchecked") final NodeLike<N, E>[] buffer = new NodeLike[2 * MAX_DIGIT + 1]; int inBuffer = splitDigit(left, from, inLeft, buffer, 0); if (inLeft == len) { if (inBuffer == 1) return new TreeSlice<>(buffer[0]); final int mid1 = inBuffer / 2; final Node<N, E>[] ls = slice(buffer, 0, mid1), rs = slice(buffer, mid1, inBuffer); return new TreeSlice<>(get(ls, rs, len)); } final long inMiddle = len - inLeft - inRight; final FingerTree<Node<N, E>, E> mid; final TreeSlice<Node<N, E>, E> slice; if (inMiddle == 0) { mid = EmptyTree.getInstance(); slice = new TreeSlice<>(mid); } else { final long midOff = from <= leftSize ? 0 : from - leftSize; slice = middle.slice(midOff, inMiddle); if (slice.isTree()) { mid = slice.getTree(); } else { final NodeLike<N, E> sub = ((PartialInnerNode<N, E>) slice.getPartial()).sub; inBuffer = sub.append(buffer, inBuffer); mid = EmptyTree.getInstance(); } } final long rightFrom = from < rightOff ? 0 : from - rightOff; if (mid.isEmpty()) { inBuffer = splitDigit(right, rightFrom, inRight, buffer, inBuffer); return slice.setNodes(buffer, inBuffer, len); } final FingerTree<Node<N, E>, E> mid2; if (inBuffer > 1 || buffer[0] instanceof Node) { mid2 = mid; } else { final InnerNode<N, E> head = (InnerNode<N, E>) mid.head(); final int k = head.arity(); inBuffer = head.getSub(0).append(buffer, inBuffer); for (int i = 1; i < k; i++) buffer[inBuffer++] = head.getSub(i); mid2 = mid.tail(); } if (mid2.isEmpty()) { inBuffer = splitDigit(right, rightFrom, inRight, buffer, inBuffer); return slice.setNodes(buffer, inBuffer, len); } final Node<N, E>[] newLeft = slice(buffer, 0, inBuffer); inBuffer = splitDigit(right, rightFrom, inRight, buffer, 0); final FingerTree<Node<N, E>, E> mid3; final Node<N, E>[] newRight; if (inBuffer == 0) { mid3 = mid2.init(); newRight = ((InnerNode<N, E>) mid2.last()).children; } else if (inBuffer > 1 || buffer[0] instanceof Node) { mid3 = mid2; newRight = slice(buffer, 0, inBuffer); } else { final NodeLike<N, E> partial = buffer[0]; final InnerNode<N, E> last = (InnerNode<N, E>) mid2.last(); final int k = last.arity(); for (int i = 0; i < k; i++) buffer[i] = last.getSub(i); inBuffer = partial.append(buffer, k); mid3 = mid2.init(); newRight = slice(buffer, 0, inBuffer); } return slice.setTree(get(newLeft, mid3, newRight, len)); }