Skip to content

Commit

Permalink
improvements to Rope and List
Browse files Browse the repository at this point in the history
  • Loading branch information
ztellman committed Nov 23, 2017
1 parent ba0b4b0 commit faa2853
Show file tree
Hide file tree
Showing 9 changed files with 483 additions and 291 deletions.
10 changes: 5 additions & 5 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
:bench {:jvm-opts ^:replace ["-server" "-Xmx10g" "-XX:+UseParallelGC"]}
:dev {:dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/test.check "0.9.0"]
[criterium "0.4.3"]
[potemkin "0.4.3"]
[criterium "0.4.4"]
[potemkin "0.4.4"]
[proteus "0.1.6"]
[byte-streams "0.2.2"]
[eftest "0.1.4"]
[virgil "0.1.7-alpha1"]]}}
[byte-streams "0.2.3"]
[eftest "0.4.1"]
[virgil "0.1.7"]]}}
:aliases {"partest" ["run" "-m" "bifurcan.run-tests"]
"benchmark" ["with-profile" "bench,dev" "run" "-m" "bifurcan.benchmark-test" "benchmark"]}
:jvm-opts ^:replace ["-server" "-XX:+UseG1GC" "-Xmx10g" "-XX:-OmitStackTraceInFastThrow"]
Expand Down
2 changes: 1 addition & 1 deletion src/io/lacuna/bifurcan/IntMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
/**
* A map which has integer keys, which is an combination of Okasaki and Gill's
* <a href="http://ittc.ku.edu/~andygill/papers/IntMap98.pdf">Fast Mergeable Integer Maps</a> with the memory layout
* suggested by Steindorfer and Vinju used in {@code Map}, with which it shares the same broad performance
* suggested by Steindorfer and Vinju used in {@link io.lacuna.bifurcan.Map}, with which it shares the same broad performance
* characteristics.
* <p>
* This collection keeps the keys in sorted order, and can thought of as either a map of integers or a sparse vector.
Expand Down
28 changes: 13 additions & 15 deletions src/io/lacuna/bifurcan/List.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package io.lacuna.bifurcan;

import io.lacuna.bifurcan.nodes.ListNodes.Node;
import io.lacuna.bifurcan.utils.Bits;

import java.util.Collection;
import java.util.Iterator;

import static io.lacuna.bifurcan.utils.Bits.log2Ceil;
Expand Down Expand Up @@ -86,7 +84,7 @@ public V nth(long idx) {

// look in the tree
} else if (i - prefixLen < rootSize) {
return (V) root.nth(i - prefixLen);
return (V) root.nth(i - prefixLen, false);

// look in the suffix
} else {
Expand Down Expand Up @@ -151,7 +149,7 @@ public Iterator<V> iterator() {
initOffset = pIdx(0);
initLimit = prefix.length;
} else if (rootSize > 0) {
initChunk = root.arrayFor(0);
initChunk = (Object[]) root.nth(0, true);
initOffset = 0;
initLimit = initChunk.length;
} else {
Expand Down Expand Up @@ -185,7 +183,7 @@ public V next() {
chunk = suffix;
limit = suffixLen;
} else {
chunk = root.arrayFor(idx - prefixLen);
chunk = (Object[]) root.nth(idx - prefixLen, true);
limit = chunk.length;
}
offset = 0;
Expand Down Expand Up @@ -226,7 +224,7 @@ public List<V> slice(long start, long end) {
}

return new List<V>(linear,
root.slice(editor, Math.max(0, min(root.size(), s - prefixLen)), Math.max(0, min(root.size(), e - prefixLen))),
root.slice(Math.max(0, min(root.size(), s - prefixLen)), Math.max(0, min(root.size(), e - prefixLen)), editor),
pLen, pre, sLen, suf);
}

Expand All @@ -238,17 +236,17 @@ public IList<V> concat(IList<V> l) {
Object editor = new Object();

// append our own suffix
if (suffix != null && suffixLen > 0) {
r = r.addLast(editor, suffixArray());
if (suffixLen > 0) {
r = r.pushLast(suffixArray(), editor);
}

// append their prefix
if (b.prefix != null && b.prefixLen > 0) {
r = r.addLast(editor, b.prefixArray());
if (b.prefixLen > 0) {
r = r.pushLast(b.prefixArray(), editor);
}

if (b.root.size() > 0) {
r = r.concat(editor, b.root);
r = r.concat(b.root, editor);
}

return new List<V>(linear, r,
Expand Down Expand Up @@ -341,7 +339,7 @@ List<V> pushFirst(V value) {
prefixLen++;

if (prefixLen == 32) {
root = root.addFirst(editor, prefix);
root = root.pushFirst(prefix, editor);
prefix = null;
prefixLen = 0;
}
Expand All @@ -362,7 +360,7 @@ List<V> pushLast(V value) {
suffix[suffixLen++] = value;

if (suffixLen == 32) {
root = root.addLast(editor, suffix);
root = root.pushLast(suffix, editor);
suffix = null;
suffixLen = 0;
}
Expand All @@ -378,7 +376,7 @@ List<V> popFirst() {
if (chunk != null) {
prefix = chunk.clone();
prefixLen = (byte) prefix.length;
root = root.removeFirst(editor);
root = root.popFirst(editor);
}
} else if (suffixLen > 0) {
arraycopy(suffix, 1, suffix, 0, --suffixLen);
Expand All @@ -402,7 +400,7 @@ List<V> popLast() {
if (chunk != null) {
suffix = chunk.clone();
suffixLen = (byte) suffix.length;
root = root.removeLast(editor);
root = root.popLast(editor);
}
} else if (prefixLen > 0) {
prefixLen--;
Expand Down
110 changes: 90 additions & 20 deletions src/io/lacuna/bifurcan/Rope.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@
*
* @author ztellman
*/
public class Rope implements Comparable<Rope> {
public class Rope implements Comparable<Rope>, ILinearizable<Rope>, IForkable<Rope> {

private final boolean linear;
private final Object editor;
public Node root;

Rope(Object editor, Node node) {
Rope(Object editor, Node node, boolean linear) {
this.linear = linear;
this.editor = editor;
this.root = node;
}
Expand All @@ -51,26 +53,44 @@ public static Rope from(CharSequence cs) {
}
}

return new Rope(editor, root);
return new Rope(editor, root, false);

}

/**
* @param rope another {@code Rope}
* @return a new Rope which is the concatenation of these two values
*/
public Rope concat(Rope rope) {
Object editor = new Object();
return new Rope(editor, root.concat(rope.root, editor));
return new Rope(editor, root.concat(rope.root, editor), linear);
}

/**
* @return the nth code point within the rope
* @throws IndexOutOfBoundsException if {@code idx} is not within {@code [0, size())}
*/
public int nth(int idx) {
if (idx < 0 || idx >= size()) {
throw new IndexOutOfBoundsException();
}
return root.nthPoint(idx);
}

/**
* @return the number of code points in the rope
*/
public int size() {
return root.numCodePoints();
}

/**
* @return a rope without the code points within {@code [start, end)}
* @throws IllegalArgumentException if {@code start} or {@code end} are not within {@code [0, size()) }
*/
public Rope remove(int start, int end) {

Object editor = new Object();
Object editor = linear ? this.editor : new Object();

if (end < start || start < 0 || end > size()) {
throw new IllegalArgumentException("[" + start + ", " + end + ") is not a valid range");
Expand All @@ -81,6 +101,7 @@ public Rope remove(int start, int end) {
// try to update a single leaf
Node newRoot = root.update(0, start, editor, (offset, chunk) -> {
int len = UnicodeChunk.numCodePoints(chunk);

if (end < offset + len) {
return UnicodeChunk.concat(
UnicodeChunk.slice(chunk, 0, start - offset),
Expand All @@ -93,7 +114,13 @@ public Rope remove(int start, int end) {
if (newRoot == null) {
newRoot = root.slice(0, start, editor).concat(root.slice(end, size(), editor), editor);
}
return new Rope(editor, newRoot);

if (linear) {
root = newRoot;
return this;
} else {
return new Rope(editor, newRoot, false);
}
}

private Rope insert(final int index, Iterator<byte[]> chunks, int numCodeUnits) {
Expand All @@ -102,11 +129,12 @@ private Rope insert(final int index, Iterator<byte[]> chunks, int numCodeUnits)
throw new IndexOutOfBoundsException();
}

Object editor = new Object();
Object editor = linear ? this.editor : new Object();
Node newRoot = null;

// can we just update a single leaf node?
if (numCodeUnits < MAX_CHUNK_CODE_UNITS) {
Node newRoot = root.update(0, index, editor, (offset, chunk) -> {
newRoot = root.update(0, index, editor, (offset, chunk) -> {
if (numCodeUnits + UnicodeChunk.numCodeUnits(chunk) <= MAX_CHUNK_CODE_UNITS) {
byte[] newChunk = UnicodeChunk.slice(chunk, 0, index - offset);
while (chunks.hasNext()) {
Expand All @@ -117,22 +145,27 @@ private Rope insert(final int index, Iterator<byte[]> chunks, int numCodeUnits)
return null;
}
});
}

// success!
if (newRoot != null) {
return new Rope(editor, newRoot);
if (newRoot == null) {
newRoot = root.slice(0, index, editor);
while (chunks.hasNext()) {
newRoot = newRoot.pushLast(chunks.next(), editor);
}
newRoot = newRoot.concat(root.slice(index, size(), editor), editor);
}

Node newRoot = root.slice(0, index, editor);
while (chunks.hasNext()) {
newRoot = newRoot.pushLast(chunks.next(), editor);
if (linear) {
root = newRoot;
return this;
} else {
return new Rope(editor, newRoot, false);
}
newRoot = newRoot.concat(root.slice(index, size(), editor), editor);

return new Rope(editor, newRoot);
}

/**
* @return a new rope with {@code rope} inserted after the first {@code index} code points
*/
public Rope insert(int index, Rope rope) {
if (rope.size() == 0) {
return this;
Expand All @@ -142,43 +175,77 @@ public Rope insert(int index, Rope rope) {

}

/**
* @return a new rope with {@code cs} inserted after the first {@code index} code points
*/
public Rope insert(int index, CharSequence cs) {
if (cs.length() == 0) {
return this;
}
return insert(index, chunks(cs), cs.length());
}

/**
* @return a new rope representing the code points within {@code [start, end)}
* @throws IllegalArgumentException if {@code end} < {@code start}, or {@code start} and {@code end} are not within {@code [0, size())}
*/
public Rope slice(int start, int end) {
if (end < start || start < 0 || end > size()) {
throw new IllegalArgumentException("[" + start + ", " + end + ") is not a valid range");
}

Object editor = new Object();
return new Rope(editor, root.slice(start, end, editor), linear);
}

@Override
public Rope forked() {
return linear ? new Rope(new Object(), root, false) : this;
}

return new Rope(editor, root.slice(start, end, editor));
@Override
public Rope linear() {
return linear ? this : new Rope(new Object(), root, true);
}

/**
* @return a sequence of bytes representing the UTF-8 encoding of the rope
*/
public Iterator<ByteBuffer> bytes() {
return Iterators.map(chunks(), ary -> ByteBuffer.wrap(ary, 2, ary.length - 2).slice());
}

/**
* @return a sequence of integers representing the UTF-16 code units from back to front
*/
public PrimitiveIterator.OfInt reverseChars() {
return IntIterators.flatMap(reverseChunks(), UnicodeChunk::reverseCodeUnitIterator);
}

/**
* @return a sequence of integers representing the UTF-16 code units from front to back
*/
public PrimitiveIterator.OfInt chars() {
return IntIterators.flatMap(chunks(), UnicodeChunk::codeUnitIterator);
}

/**
* @return a sequence of integers representing the code points from back to front
*/
public PrimitiveIterator.OfInt reverseCodePoints() {
return IntIterators.flatMap(reverseChunks(), UnicodeChunk::reverseCodePointIterator);
}

/**
* @return a sequence of integers representing the code points from front to back
*/
public PrimitiveIterator.OfInt codePoints() {
return IntIterators.flatMap(chunks(), UnicodeChunk::codePointIterator);
}

/**
* @return a corresponding Java-style {@code String} in {@code O(N)} time
*/
@Override
public String toString() {
char[] cs = new char[root.numCodeUnits()];
Expand All @@ -192,7 +259,7 @@ public String toString() {
}

/**
* @return a corresponding Java-style {@code CharSequence}
* @return a corresponding Java-style {@code CharSequence} in {@code O(1)} time
*/
public CharSequence toCharSequence() {
return new CharSequence() {
Expand Down Expand Up @@ -239,6 +306,9 @@ public boolean equals(Object obj) {
}
}

/**
* @return a value representing the lexicographic comparison of the code points
*/
@Override
public int compareTo(Rope o) {
if (this == o) {
Expand Down Expand Up @@ -292,7 +362,7 @@ public byte[] next() {
};
}

public static Iterator<byte[]> chunks(CharSequence cs) {
private static Iterator<byte[]> chunks(CharSequence cs) {
return new Iterator<byte[]>() {
int offset = 0;

Expand Down
Loading

0 comments on commit faa2853

Please sign in to comment.