swim.collections.BTreeNode Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of swim-collections Show documentation
Show all versions of swim-collections Show documentation
Immutable, structure sharing collections, including hash array mapped tries, finger tries, B-trees, and S-trees (sequence trees)
// Copyright 2015-2019 SWIM.AI inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package swim.collections;
import java.util.Map;
import swim.util.CombinerFunction;
import swim.util.OrderedMapCursor;
class BTreeNode extends BTreePage {
final BTreePage[] pages;
final K[] knots;
final U fold;
final int size;
BTreeNode(BTreePage[] pages, K[] knots, U fold, int size) {
this.pages = pages;
this.knots = knots;
this.fold = fold;
this.size = size;
}
@Override
public final boolean isEmpty() {
return this.size == 0;
}
@Override
public final int size() {
return this.size;
}
@Override
public final int arity() {
return this.pages.length;
}
@Override
public final U fold() {
return this.fold;
}
@Override
public final K minKey() {
return this.pages[0].minKey();
}
@Override
public final K maxKey() {
return this.pages[this.pages.length - 1].maxKey();
}
@Override
public final boolean containsKey(Object key, BTreeContext tree) {
int x = lookup(key, tree);
if (x > 0) {
x += 1;
} else if (x < 0) {
x = -(x + 1);
} else {
return true;
}
return this.pages[x].containsKey(key, tree);
}
@Override
public final boolean containsValue(Object value) {
final BTreePage[] pages = this.pages;
for (int i = 0, n = pages.length; i < n; i += 1) {
if (pages[i].containsValue(value)) {
return true;
}
}
return false;
}
@Override
public final int indexOf(Object key, BTreeContext tree) {
int x = lookup(key, tree);
if (x >= 0) {
x += 1;
} else {
x = -(x + 1);
}
int count = 0;
for (int i = 0; i < x; i += 1) {
count += this.pages[x].size();
}
final int index = this.pages[x].indexOf(key, tree);
if (index >= 0) {
return count + index;
} else {
return index - count;
}
}
@Override
public final V get(Object key, BTreeContext tree) {
int x = lookup(key, tree);
if (x >= 0) {
x += 1;
} else {
x = -(x + 1);
}
return this.pages[x].get(key, tree);
}
@Override
public final Map.Entry getEntry(Object key, BTreeContext tree) {
int x = lookup(key, tree);
if (x >= 0) {
x += 1;
} else {
x = -(x + 1);
}
return this.pages[x].getEntry(key, tree);
}
@Override
public final Map.Entry getIndex(int index) {
final BTreePage[] pages = this.pages;
for (int i = 0, n = pages.length; i < n; i += 1) {
final BTreePage page = pages[i];
if (index < page.size()) {
return page.getIndex(index);
} else {
index -= page.size();
}
}
return null;
}
@Override
public final Map.Entry firstEntry() {
final BTreePage[] pages = this.pages;
if (pages.length != 0) {
return pages[0].firstEntry();
} else {
return null;
}
}
@Override
public final Map.Entry lastEntry() {
final BTreePage[] pages = this.pages;
if (pages.length != 0) {
return pages[pages.length - 1].lastEntry();
} else {
return null;
}
}
@Override
public final Map.Entry nextEntry(K key, BTreeContext tree) {
int x = lookup(key, tree);
if (x >= 0) {
x += 1;
} else {
x = -(x + 1);
}
final BTreePage[] pages = this.pages;
Map.Entry entry = pages[x].nextEntry(key, tree);
if (entry == null && x + 1 < pages.length) {
entry = pages[x + 1].nextEntry(key, tree);
}
return entry;
}
@Override
public final Map.Entry previousEntry(K key, BTreeContext tree) {
int x = lookup(key, tree);
if (x >= 0) {
x += 1;
} else {
x = -(x + 1);
}
final BTreePage[] pages = this.pages;
Map.Entry entry = pages[x].previousEntry(key, tree);
if (entry == null && x > 0) {
entry = pages[x - 1].previousEntry(key, tree);
}
return entry;
}
@Override
public final BTreeNode updated(K key, V newValue, BTreeContext tree) {
int x = lookup(key, tree);
if (x >= 0) {
x += 1;
} else {
x = -(x + 1);
}
final BTreePage oldPage = this.pages[x];
final BTreePage newPage = oldPage.updated(key, newValue, tree);
if (oldPage != newPage) {
if (oldPage.size() != newPage.size() && tree.pageShouldSplit(newPage)) {
return updatedPageSplit(x, newPage, oldPage);
} else {
return updatedPage(x, newPage, oldPage);
}
} else {
return this;
}
}
@SuppressWarnings("unchecked")
private BTreeNode updatedPage(int x, BTreePage newPage, BTreePage oldPage) {
final BTreePage[] oldPages = this.pages;
final int n = oldPages.length;
final BTreePage[] newPages = (BTreePage[]) new BTreePage, ?, ?>[n];
System.arraycopy(oldPages, 0, newPages, 0, n);
newPages[x] = newPage;
final K[] oldKnots = this.knots;
final K[] newKnots;
if (n - 1 > 0) {
newKnots = (K[]) new Object[n - 1];
System.arraycopy(oldKnots, 0, newKnots, 0, n - 1);
if (x > 0) {
newKnots[x - 1] = newPage.minKey();
}
} else {
newKnots = (K[]) new Object[0];
}
final int newSize = this.size - oldPage.size() + newPage.size();
return newNode(newPages, newKnots, null, newSize);
}
@SuppressWarnings("unchecked")
private BTreeNode updatedPageSplit(int x, BTreePage newPage, BTreePage oldPage) {
final BTreePage[] oldPages = this.pages;
final int n = oldPages.length + 1;
final BTreePage[] newPages = (BTreePage[]) new BTreePage, ?, ?>[n];
System.arraycopy(oldPages, 0, newPages, 0, x);
final int y = newPage.arity() >>> 1;
final BTreePage newLeftPage = newPage.splitLeft(y);
final BTreePage newRightPage = newPage.splitRight(y);
newPages[x] = newLeftPage;
newPages[x + 1] = newRightPage;
System.arraycopy(oldPages, x + 1, newPages, x + 2, n - (x + 2));
final K[] oldKnots = this.knots;
final K[] newKnots = (K[]) new Object[n - 1];
if (x > 0) {
System.arraycopy(oldKnots, 0, newKnots, 0, x - 1);
newKnots[x - 1] = newLeftPage.minKey();
newKnots[x] = newRightPage.minKey();
System.arraycopy(oldKnots, x, newKnots, x + 1, n - (x + 2));
} else {
newKnots[0] = newRightPage.minKey();
System.arraycopy(oldKnots, 0, newKnots, 1, n - 2);
}
final int newSize = this.size - oldPage.size() + newPage.size();
return newNode(newPages, newKnots, null, newSize);
}
@SuppressWarnings("unchecked")
private BTreeNode updatedPageMerge(int x, BTreeNode newPage, BTreePage oldPage) {
final BTreePage[] oldPages = this.pages;
final BTreePage[] midPages = newPage.pages;
final int k = midPages.length;
final int n = oldPages.length + (k - 1);
final BTreePage[] newPages = (BTreePage[]) new BTreePage, ?, ?>[n];
System.arraycopy(oldPages, 0, newPages, 0, x);
System.arraycopy(midPages, 0, newPages, x, k);
System.arraycopy(oldPages, x + 1, newPages, x + k, n - (x + k));
final K[] oldKnots = this.knots;
final K[] midKnots = newPage.knots;
final K[] newKnots = (K[]) new Object[n - 1];
if (x > 0) {
System.arraycopy(oldKnots, 0, newKnots, 0, x - 1);
newKnots[x - 1] = midPages[0].minKey();
System.arraycopy(midKnots, 0, newKnots, x, k - 1);
System.arraycopy(oldKnots, x, newKnots, x + (k - 1), n - (x + k));
} else {
System.arraycopy(midKnots, 0, newKnots, 0, k - 1);
newKnots[midKnots.length] = oldPages[1].minKey();
System.arraycopy(oldKnots, 1, newKnots, k, n - k - 1);
}
final int newSize = this.size - oldPage.size() + newPage.size();
return newNode(newPages, newKnots, null, newSize);
}
@Override
public final BTreePage removed(Object key, BTreeContext tree) {
int x = lookup(key, tree);
if (x >= 0) {
x += 1;
} else {
x = -(x + 1);
}
final BTreePage oldPage = this.pages[x];
final BTreePage newPage = oldPage.removed(key, tree);
if (oldPage != newPage) {
return replacedPage(x, newPage, oldPage, tree);
} else {
return this;
}
}
private BTreePage replacedPage(int x, BTreePage newPage,
BTreePage oldPage, BTreeContext tree) {
if (!newPage.isEmpty()) {
if (newPage instanceof BTreeNode, ?, ?> && tree.pageShouldMerge(newPage)) {
return updatedPageMerge(x, (BTreeNode) newPage, oldPage);
} else {
return updatedPage(x, newPage, oldPage);
}
} else if (this.pages.length > 2) {
return removedPage(x, newPage, oldPage);
} else if (this.pages.length > 1) {
if (x == 0) {
return this.pages[1];
} else {
return this.pages[0];
}
} else {
return BTreeLeaf.empty();
}
}
@SuppressWarnings("unchecked")
private BTreeNode removedPage(int x, BTreePage newPage, BTreePage oldPage) {
final BTreePage[] oldPages = this.pages;
final int n = oldPages.length - 1;
final BTreePage[] newPages = (BTreePage[]) new BTreePage, ?, ?>[n];
System.arraycopy(oldPages, 0, newPages, 0, x);
System.arraycopy(oldPages, x + 1, newPages, x, n - x);
final K[] oldKnots = this.knots;
final K[] newKnots = (K[]) new Object[n - 1];
if (x > 0) {
System.arraycopy(oldKnots, 0, newKnots, 0, x - 1);
System.arraycopy(oldKnots, x, newKnots, x - 1, n - x);
} else {
System.arraycopy(oldKnots, 1, newKnots, 0, n - 1);
}
final int newSize = this.size - oldPage.size();
return newNode(newPages, newKnots, null, newSize);
}
@SuppressWarnings("unchecked")
@Override
public final BTreePage drop(int lower, BTreeContext tree) {
if (lower > 0) {
int newSize = this.size;
if (lower < newSize) {
final BTreePage[] oldPages = this.pages;
final int k = oldPages.length;
int x = 0;
while (x < k) {
final int childSize = oldPages[x].size();
if (childSize <= lower) {
newSize -= childSize;
lower -= childSize;
x += 1;
} else {
break;
}
}
final int n = k - x;
if (n > 1) {
final BTreeNode newNode;
if (x > 0) {
final BTreePage[] newPages = (BTreePage[]) new BTreePage, ?, ?>[n];
System.arraycopy(oldPages, x, newPages, 0, n);
final K[] newKnots = (K[]) new Object[n - 1];
System.arraycopy(this.knots, x, newKnots, 0, n - 1);
for (int i = 0; i < newKnots.length; i += 1) {
newKnots[i] = this.knots[i + x];
}
newNode = newNode(newPages, newKnots, null, newSize);
} else {
newNode = this;
}
if (lower > 0) {
final BTreePage oldPage = oldPages[x];
final BTreePage newPage = oldPage.drop(lower, tree);
return newNode.replacedPage(0, newPage, oldPage, tree);
} else {
return newNode;
}
} else {
return oldPages[x].drop(lower, tree);
}
} else {
return BTreeLeaf.empty();
}
} else {
return this;
}
}
@SuppressWarnings("unchecked")
@Override
public final BTreePage take(int upper, BTreeContext tree) {
if (upper < this.size) {
if (upper > 0) {
final BTreePage[] oldPages = this.pages;
final int k = oldPages.length;
int x = 0;
int newSize = 0;
while (x < k && upper > 0) {
final int childSize = oldPages[x].size();
newSize += childSize;
x += 1;
if (childSize <= upper) {
upper -= childSize;
} else {
break;
}
}
final int n = upper == 0 ? x : x + 1;
if (n > 1) {
final BTreeNode newNode;
if (x < k) {
final BTreePage[] newPages = (BTreePage[]) new BTreePage, ?, ?>[n];
System.arraycopy(oldPages, 0, newPages, 0, n);
final K[] newKnots = (K[]) new Object[n - 1];
System.arraycopy(this.knots, 0, newKnots, 0, n - 1);
newNode = newNode(newPages, newKnots, null, newSize);
} else {
newNode = this;
}
if (upper > 0) {
final BTreePage oldPage = oldPages[x - 1];
final BTreePage newPage = oldPage.take(upper, tree);
return newNode.replacedPage(x - 1, newPage, oldPage, tree);
} else {
return newNode;
}
} else if (upper > 0) {
return oldPages[0].take(upper, tree);
} else {
return oldPages[0];
}
} else {
return BTreeLeaf.empty();
}
} else {
return this;
}
}
@Override
public final BTreeNode balanced(BTreeContext tree) {
if (this.pages.length > 1 && tree.pageShouldSplit(this)) {
final int x = this.knots.length >>> 1;
return split(x);
} else {
return this;
}
}
@SuppressWarnings("unchecked")
@Override
public final BTreeNode split(int x) {
final BTreePage[] newPages = (BTreePage[]) new BTreePage, ?, ?>[2];
final BTreeNode newLeftPage = splitLeft(x);
final BTreeNode newRightPage = splitRight(x);
newPages[0] = newLeftPage;
newPages[1] = newRightPage;
final K[] newKnots = (K[]) new Object[1];
newKnots[0] = newRightPage.minKey();
return newNode(newPages, newKnots, null, this.size);
}
@SuppressWarnings("unchecked")
@Override
public final BTreeNode splitLeft(int x) {
final BTreePage[] oldPages = this.pages;
final BTreePage[] newPages = (BTreePage[]) new BTreePage, ?, ?>[x + 1];
System.arraycopy(oldPages, 0, newPages, 0, x + 1);
final K[] oldKnots = this.knots;
final K[] newKnots = (K[]) new Object[x];
System.arraycopy(oldKnots, 0, newKnots, 0, x);
int newSize = 0;
for (int i = 0; i <= x; i += 1) {
newSize += newPages[i].size();
}
return newNode(newPages, newKnots, null, newSize);
}
@SuppressWarnings("unchecked")
@Override
public final BTreeNode splitRight(int x) {
final BTreePage[] oldPages = this.pages;
final int y = oldPages.length - (x + 1);
final BTreePage[] newPages = (BTreePage[]) new BTreePage, ?, ?>[y];
System.arraycopy(oldPages, x + 1, newPages, 0, y);
final K[] oldKnots = this.knots;
final K[] newKnots = (K[]) new Object[y - 1];
System.arraycopy(oldKnots, x + 1, newKnots, 0, y - 1);
int newSize = 0;
for (int i = 0; i < y; i += 1) {
newSize += newPages[i].size();
}
return newNode(newPages, newKnots, null, newSize);
}
@SuppressWarnings("unchecked")
@Override
public final BTreeNode reduced(U identity, CombinerFunction super V, U> accumulator,
CombinerFunction combiner) {
if (this.fold == null) {
final BTreePage[] oldPages = this.pages;
final int n = oldPages.length;
final BTreePage[] newPages = (BTreePage[]) new BTreePage, ?, ?>[n];
for (int i = 0; i < n; i += 1) {
newPages[i] = oldPages[i].reduced(identity, accumulator, combiner);
}
// assert n > 0;
U fold = newPages[0].fold();
for (int i = 1; i < n; i += 1) {
fold = combiner.combine(fold, newPages[i].fold());
}
return newNode(newPages, this.knots, fold, this.size);
} else {
return this;
}
}
@Override
public final OrderedMapCursor iterator() {
return new BTreeNodeCursor(this.pages);
}
@Override
public final OrderedMapCursor lastIterator() {
return new BTreeNodeCursor(this.pages, this.size, this.pages.length);
}
protected final int lookup(Object key, BTreeContext tree) {
int lo = 0;
int hi = this.knots.length - 1;
while (lo <= hi) {
final int mid = (lo + hi) >>> 1;
final int order = tree.compareKey(key, this.knots[mid]);
if (order > 0) {
lo = mid + 1;
} else if (order < 0) {
hi = mid - 1;
} else {
return mid;
}
}
return -(lo + 1);
}
protected BTreeNode newNode(BTreePage[] pages, K[] knots, U fold, int size) {
return new BTreeNode(pages, knots, fold, size);
}
}