swim.db.BTreeNode Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of swim-db Show documentation
Show all versions of swim-db Show documentation
Lock-free document store—optimized for high rate atomic state changes—that concurrently commits and compacts on-disk log-structured storage files without blocking parallel in-memory updates to associative B-tree maps, spatial Q-tree maps, sequential S-tree lists, and singleton U-tree values
The newest version!
// Copyright 2015-2024 Nstream, 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.db;
import swim.codec.Output;
import swim.codec.Unicode;
import swim.concurrent.Cont;
import swim.recon.Recon;
import swim.structure.Num;
import swim.structure.Record;
import swim.structure.Slot;
import swim.structure.Value;
import swim.util.Builder;
import swim.util.CombinerFunction;
import swim.util.OrderedMapCursor;
public final class BTreeNode extends BTreePage {
final BTreePageRef pageRef;
final long version;
final BTreePageRef[] childRefs;
final Value[] knotKeys;
protected BTreeNode(BTreePageRef pageRef, long version,
BTreePageRef[] childRefs, Value[] knotKeys) {
this.pageRef = pageRef;
this.version = version;
this.childRefs = childRefs;
this.knotKeys = knotKeys;
for (int i = 0; i < childRefs.length; i += 1) {
if (childRefs[i] == null) {
throw new AssertionError();
}
}
}
@Override
public boolean isNode() {
return true;
}
@Override
public BTreePageRef pageRef() {
return this.pageRef;
}
@Override
public PageType pageType() {
return PageType.NODE;
}
@Override
public long version() {
return this.version;
}
@Override
public boolean isEmpty() {
return this.pageRef.span == 0;
}
@Override
public int arity() {
return this.knotKeys.length;
}
@Override
public int childCount() {
return this.childRefs.length;
}
@Override
public BTreePageRef getChildRef(int index) {
return this.childRefs[index];
}
@Override
public BTreePage getChild(int index) {
try {
return this.childRefs[index].page();
} catch (Throwable cause) {
if (Cont.isNonFatal(cause)) {
throw new StoreException(this.toDebugString(), cause);
} else {
throw cause;
}
}
}
@Override
public Slot getSlot(int x) {
throw new UnsupportedOperationException();
}
@Override
public Value getKey(int x) {
return this.knotKeys[x];
}
@Override
public Value minKey() {
return this.getChild(0).minKey();
}
@Override
public Value maxKey() {
return this.getChild(this.childRefs.length - 1).maxKey();
}
int lookup(Value key) {
final Value[] knotKeys = this.knotKeys;
int low = 0;
int high = knotKeys.length - 1;
while (low <= high) {
final int x = (low + high) >>> 1;
final int order = key != null ? key.compareTo(knotKeys[x]) : -1;
if (order > 0) {
low = x + 1;
} else if (order < 0) {
high = x - 1;
} else {
return x;
}
}
return -(low + 1);
}
@Override
public boolean containsKey(Value key) {
int x = this.lookup(key);
if (x > 0) {
x += 1;
} else if (x < 0) {
x = -(x + 1);
} else {
return true;
}
return this.getChild(x).containsKey(key);
}
@Override
public boolean containsValue(Value value) {
try {
final BTreePageRef[] childRefs = this.childRefs;
for (int i = 0, n = childRefs.length; i < n; i += 1) {
if (childRefs[i].page().containsValue(value)) {
return true;
}
}
return false;
} catch (Throwable cause) {
if (Cont.isNonFatal(cause)) {
throw new StoreException(this.toDebugString(), cause);
} else {
throw cause;
}
}
}
@Override
public long indexOf(Value key) {
int x = this.lookup(key);
if (x >= 0) {
x += 1;
} else {
x = -(x + 1);
}
long count = 0L;
for (int i = 0; i < x; i += 1) {
count += this.childRefs[x].span();
}
try {
final long index = this.childRefs[x].page().indexOf(key);
if (index >= 0) {
return count + index;
} else {
return index - count;
}
} catch (Throwable cause) {
if (Cont.isNonFatal(cause)) {
throw new StoreException(this.toDebugString(), cause);
} else {
throw cause;
}
}
}
@Override
public Value get(Value key) {
int x = this.lookup(key);
if (x >= 0) {
x += 1;
} else {
x = -(x + 1);
}
return this.getChild(x).get(key);
}
@Override
public Slot getEntry(Value key) {
int x = this.lookup(key);
if (x >= 0) {
x += 1;
} else {
x = -(x + 1);
}
return this.getChild(x).getEntry(key);
}
@Override
public Slot getIndex(long index) {
final BTreePageRef[] childRefs = this.childRefs;
for (int i = 0, n = childRefs.length; i < n; i += 1) {
final BTreePageRef childRef = childRefs[i];
final long k = childRef.span();
if (index < k) {
try {
return childRef.page().getIndex(index);
} catch (Throwable cause) {
if (Cont.isNonFatal(cause)) {
throw new StoreException(this.toDebugString(), cause);
} else {
throw cause;
}
}
} else {
index -= k;
}
}
return null;
}
@Override
public Slot firstEntry(Value key) {
int x = this.lookup(key);
if (x >= 0) {
x += 1;
} else {
x = -(x + 1);
}
try {
return this.childRefs[x].page().firstEntry(key);
} catch (Throwable cause) {
if (Cont.isNonFatal(cause)) {
throw new StoreException(this.toDebugString(), cause);
} else {
throw cause;
}
}
}
@Override
public Slot firstEntry() {
final BTreePageRef[] childRefs = this.childRefs;
if (childRefs.length != 0) {
try {
return childRefs[0].page().firstEntry();
} catch (Throwable cause) {
if (Cont.isNonFatal(cause)) {
throw new StoreException(this.toDebugString(), cause);
} else {
throw cause;
}
}
} else {
return null;
}
}
@Override
public Slot lastEntry() {
final BTreePageRef[] childRefs = this.childRefs;
if (childRefs.length != 0) {
try {
return childRefs[childRefs.length - 1].page().lastEntry();
} catch (Throwable cause) {
if (Cont.isNonFatal(cause)) {
throw new StoreException(this.toDebugString(), cause);
} else {
throw cause;
}
}
} else {
return null;
}
}
@Override
public Slot nextEntry(Value key) {
int x = this.lookup(key);
if (x >= 0) {
x += 1;
} else {
x = -(x + 1);
}
final BTreePageRef[] childRefs = this.childRefs;
try {
Slot entry = childRefs[x].page().nextEntry(key);
if (entry == null && x + 1 < childRefs.length) {
entry = childRefs[x + 1].page().nextEntry(key);
}
return entry;
} catch (Throwable cause) {
if (Cont.isNonFatal(cause)) {
throw new StoreException(this.toDebugString(), cause);
} else {
throw cause;
}
}
}
@Override
public Slot previousEntry(Value key) {
int x = this.lookup(key);
if (x >= 0) {
x += 1;
} else {
x = -(x + 1);
}
final BTreePageRef[] childRefs = this.childRefs;
try {
Slot entry = childRefs[x].page().previousEntry(key);
if (entry == null && x > 0) {
entry = childRefs[x - 1].page().previousEntry(key);
}
return entry;
} catch (Throwable cause) {
if (Cont.isNonFatal(cause)) {
throw new StoreException(this.toDebugString(), cause);
} else {
throw cause;
}
}
}
@Override
public BTreePage updated(Value key, Value newValue, long newVersion) {
int x = this.lookup(key);
if (x >= 0) {
x += 1;
} else {
x = -(x + 1);
}
final BTreePage oldPage = this.getChild(x);
final BTreePage newPage = oldPage.updated(key, newValue, newVersion);
if (oldPage != newPage) {
if (oldPage.span() != newPage.span() && this.pageRef.context.pageShouldSplit(newPage)) {
return this.updatedPageSplit(x, newPage, oldPage, newVersion);
} else {
return this.updatedPage(x, newPage, oldPage, newVersion);
}
} else {
return this;
}
}
BTreeNode updatedPage(int x, BTreePage newPage, BTreePage oldPage, long newVersion) {
final BTreePageRef[] oldChildRefs = this.childRefs;
final int n = oldChildRefs.length;
final BTreePageRef[] newChildRefs = new BTreePageRef[n];
System.arraycopy(oldChildRefs, 0, newChildRefs, 0, n);
newChildRefs[x] = newPage.pageRef();
final Value[] oldKnotKeys = this.knotKeys;
final Value[] newKnotKeys;
if (n - 1 > 0) {
newKnotKeys = new Value[n - 1];
System.arraycopy(oldKnotKeys, 0, newKnotKeys, 0, n - 1);
if (x > 0) {
newKnotKeys[x - 1] = newPage.minKey();
}
} else {
newKnotKeys = BTreeNode.EMPTY_KNOT_KEYS;
}
final long newSpan = this.pageRef.span - oldPage.span() + newPage.span();
return BTreeNode.create(this.pageRef.context, this.pageRef.stem, newVersion,
newSpan, Value.absent(), newChildRefs, newKnotKeys);
}
BTreeNode updatedPageSplit(int x, BTreePage newPage, BTreePage oldPage, long newVersion) {
final BTreePageRef[] oldChildRefs = this.childRefs;
final int n = oldChildRefs.length + 1;
final BTreePageRef[] newChildRefs = new BTreePageRef[n];
System.arraycopy(oldChildRefs, 0, newChildRefs, 0, x);
final int y = newPage.arity() >>> 1;
final BTreePage newLeftPage = newPage.splitLeft(y, newVersion);
final BTreePage newRightPage = newPage.splitRight(y, newVersion);
newChildRefs[x] = newLeftPage.pageRef();
newChildRefs[x + 1] = newRightPage.pageRef();
System.arraycopy(oldChildRefs, x + 1, newChildRefs, x + 2, n - (x + 2));
final Value[] oldKnotKeys = this.knotKeys;
final Value[] newKnotKeys = new Value[n - 1];
if (x > 0) {
System.arraycopy(oldKnotKeys, 0, newKnotKeys, 0, x - 1);
newKnotKeys[x - 1] = newLeftPage.minKey();
newKnotKeys[x] = newRightPage.minKey();
System.arraycopy(oldKnotKeys, x, newKnotKeys, x + 1, n - (x + 2));
} else {
newKnotKeys[0] = newRightPage.minKey();
System.arraycopy(oldKnotKeys, 0, newKnotKeys, 1, n - 2);
}
final long newSpan = this.pageRef.span - oldPage.span() + newPage.span();
return BTreeNode.create(this.pageRef.context, this.pageRef.stem, newVersion,
newSpan, Value.absent(), newChildRefs, newKnotKeys);
}
BTreeNode updatedPageMerge(int x, BTreeNode newPage, BTreePage oldPage, long newVersion) {
try {
final BTreePageRef[] oldChildRefs = this.childRefs;
final BTreePageRef[] mergePages = newPage.childRefs;
final int k = mergePages.length;
final int n = oldChildRefs.length + (k - 1);
final BTreePageRef[] newChildRefs = new BTreePageRef[n];
System.arraycopy(oldChildRefs, 0, newChildRefs, 0, x);
System.arraycopy(mergePages, 0, newChildRefs, x, k);
System.arraycopy(oldChildRefs, x + 1, newChildRefs, x + k, n - (x + k));
final Value[] oldKnotKeys = this.knotKeys;
final Value[] mergeKeys = newPage.knotKeys;
final Value[] newKnotKeys = new Value[n - 1];
if (x > 0) {
System.arraycopy(oldKnotKeys, 0, newKnotKeys, 0, x - 1);
newKnotKeys[x - 1] = mergePages[0].page().minKey();
System.arraycopy(mergeKeys, 0, newKnotKeys, x, k - 1);
System.arraycopy(oldKnotKeys, x, newKnotKeys, x + (k - 1), n - (x + k));
} else {
System.arraycopy(mergeKeys, 0, newKnotKeys, 0, k - 1);
newKnotKeys[k - 1] = oldChildRefs[1].page().minKey();
System.arraycopy(oldKnotKeys, 1, newKnotKeys, k, n - k - 1);
}
final long newSpan = this.pageRef.span - oldPage.span() + newPage.span();
return BTreeNode.create(this.pageRef.context, this.pageRef.stem, newVersion,
newSpan, Value.absent(), newChildRefs, newKnotKeys);
} catch (Throwable cause) {
if (Cont.isNonFatal(cause)) {
throw new StoreException(this.toDebugString(), cause);
} else {
throw cause;
}
}
}
@Override
public BTreePage removed(Value key, long newVersion) {
int x = this.lookup(key);
if (x >= 0) {
x += 1;
} else {
x = -(x + 1);
}
final BTreePage oldPage = this.getChild(x);
final BTreePage newPage = oldPage.removed(key, newVersion);
if (oldPage != newPage) {
return this.replacedPage(x, newPage, oldPage, newVersion);
} else {
return this;
}
}
BTreePage replacedPage(int x, BTreePage newPage, BTreePage oldPage, long newVersion) {
if (!newPage.isEmpty()) {
if (newPage.isNode() && this.pageRef.context.pageShouldMerge(newPage)) {
return this.updatedPageMerge(x, (BTreeNode) newPage, oldPage, newVersion);
} else {
return this.updatedPage(x, newPage, oldPage, newVersion);
}
} else if (this.childRefs.length > 2) {
return this.removedPage(x, newPage, oldPage, newVersion);
} else if (this.childRefs.length > 1) {
if (x == 0) {
return this.getChild(1);
} else {
return this.getChild(0);
}
} else {
return BTreeLeaf.empty(this.pageRef.context, this.pageRef.stem, newVersion);
}
}
BTreeNode removedPage(int x, BTreePage newPage, BTreePage oldPage, long newVersion) {
final BTreePageRef[] oldChildRefs = this.childRefs;
final int n = oldChildRefs.length - 1;
final BTreePageRef[] newChildRefs = new BTreePageRef[n];
System.arraycopy(oldChildRefs, 0, newChildRefs, 0, x);
System.arraycopy(oldChildRefs, x + 1, newChildRefs, x, n - x);
final Value[] oldKnotKeys = this.knotKeys;
final Value[] newKnotKeys = new Value[n - 1];
if (x > 0) {
System.arraycopy(oldKnotKeys, 0, newKnotKeys, 0, x - 1);
System.arraycopy(oldKnotKeys, x, newKnotKeys, x - 1, n - x);
} else {
System.arraycopy(oldKnotKeys, 1, newKnotKeys, 0, n - 1);
}
final long newSpan = this.pageRef.span - oldPage.span();
return BTreeNode.create(this.pageRef.context, this.pageRef.stem, newVersion,
newSpan, Value.absent(), newChildRefs, newKnotKeys);
}
@Override
public BTreePage drop(long lower, long newVersion) {
try {
if (lower > 0L) {
long newSpan = this.span();
if (lower < newSpan) {
final BTreePageRef[] oldChildRefs = this.childRefs;
final int k = oldChildRefs.length;
int x = 0;
while (x < k) {
final long childSpan = oldChildRefs[x].span();
if (childSpan <= lower) {
newSpan -= childSpan;
lower -= childSpan;
x += 1;
} else {
break;
}
}
final int n = k - x;
if (n > 1) {
final BTreeNode newNode;
if (x > 0) {
final BTreePageRef[] newChildRefs = new BTreePageRef[n];
System.arraycopy(oldChildRefs, x, newChildRefs, 0, n);
final Value[] newKnotKeys = new Value[n - 1];
System.arraycopy(this.knotKeys, x, newKnotKeys, 0, n - 1);
newNode = BTreeNode.create(this.pageRef.context, this.pageRef.stem, newVersion,
newSpan, Value.absent(), newChildRefs, newKnotKeys);
} else {
newNode = this;
}
if (lower > 0L) {
final BTreePage oldPage = oldChildRefs[x].page();
final BTreePage newPage = oldPage.drop(lower, newVersion);
return newNode.replacedPage(0, newPage, oldPage, newVersion);
} else {
return newNode;
}
} else {
return oldChildRefs[x].page().drop(lower, newVersion);
}
} else {
return BTreeLeaf.empty(this.pageRef.context, this.pageRef.stem, newVersion);
}
} else {
return this;
}
} catch (Throwable cause) {
if (Cont.isNonFatal(cause)) {
throw new StoreException(this.toDebugString(), cause);
} else {
throw cause;
}
}
}
@Override
public BTreePage take(long upper, long newVersion) {
try {
if (upper < this.span()) {
if (upper > 0L) {
final BTreePageRef[] oldChildRefs = this.childRefs;
final int k = oldChildRefs.length;
int x = 0;
long newSpan = 0L;
while (x < k && upper > 0L) {
final long childSpan = oldChildRefs[x].span();
newSpan += childSpan;
x += 1;
if (childSpan <= upper) {
upper -= childSpan;
} else {
break;
}
}
final int n = upper == 0 ? x : x + 1;
if (n > 1) {
final BTreeNode newNode;
if (x < k) {
final BTreePageRef[] newChildRefs = new BTreePageRef[n];
System.arraycopy(oldChildRefs, 0, newChildRefs, 0, n);
final Value[] newKnotKeys = new Value[n - 1];
System.arraycopy(this.knotKeys, 0, newKnotKeys, 0, n - 1);
newNode = BTreeNode.create(this.pageRef.context, this.pageRef.stem, newVersion,
newSpan, Value.absent(), newChildRefs, newKnotKeys);
} else {
newNode = this;
}
if (upper > 0L) {
final BTreePage oldPage = oldChildRefs[x - 1].page();
final BTreePage newPage = oldPage.take(upper, newVersion);
return newNode.replacedPage(x - 1, newPage, oldPage, newVersion);
} else {
return newNode;
}
} else if (upper > 0L) {
return oldChildRefs[0].page().take(upper, newVersion);
} else {
return oldChildRefs[0].page();
}
} else {
return BTreeLeaf.empty(this.pageRef.context, this.pageRef.stem, newVersion);
}
} else {
return this;
}
} catch (Throwable cause) {
if (Cont.isNonFatal(cause)) {
throw new StoreException(this.toDebugString(), cause);
} else {
throw cause;
}
}
}
@Override
public BTreeNode balanced(long newVersion) {
if (this.childRefs.length > 1 && this.pageRef.context.pageShouldSplit(this)) {
final int x = this.knotKeys.length >>> 1;
return this.split(x, newVersion);
} else {
return this;
}
}
@Override
public BTreeNode split(int x, long newVersion) {
final BTreePageRef[] newChildRefs = new BTreePageRef[2];
final BTreeNode newLeftPage = this.splitLeft(x, newVersion);
final BTreeNode newRightPage = this.splitRight(x, newVersion);
newChildRefs[0] = newLeftPage.pageRef();
newChildRefs[1] = newRightPage.pageRef();
final Value[] newKnotKeys = new Value[1];
newKnotKeys[0] = newRightPage.minKey();
return BTreeNode.create(this.pageRef.context, this.pageRef.stem, newVersion,
this.pageRef.span, Value.absent(), newChildRefs, newKnotKeys);
}
@Override
public BTreeNode splitLeft(int x, long newVersion) {
final BTreePageRef[] oldChildRefs = this.childRefs;
final BTreePageRef[] newChildRefs = new BTreePageRef[x + 1];
System.arraycopy(oldChildRefs, 0, newChildRefs, 0, x + 1);
final Value[] oldKnotKeys = this.knotKeys;
final Value[] newKnotKeys = new Value[x];
System.arraycopy(oldKnotKeys, 0, newKnotKeys, 0, x);
long newSpan = 0L;
for (int i = 0; i <= x; i += 1) {
newSpan += newChildRefs[i].span();
}
return BTreeNode.create(this.pageRef.context, this.pageRef.stem, newVersion,
newSpan, Value.absent(), newChildRefs, newKnotKeys);
}
@Override
public BTreeNode splitRight(int x, long newVersion) {
final BTreePageRef[] oldChildRefs = this.childRefs;
final int y = oldChildRefs.length - (x + 1);
final BTreePageRef[] newChildRefs = new BTreePageRef[y];
System.arraycopy(oldChildRefs, x + 1, newChildRefs, 0, y);
final Value[] oldKnotKeys = this.knotKeys;
final Value[] newKnotKeys = new Value[y - 1];
System.arraycopy(oldKnotKeys, x + 1, newKnotKeys, 0, y - 1);
long newSpan = 0L;
for (int i = 0; i < y; i += 1) {
newSpan += newChildRefs[i].span();
}
return BTreeNode.create(this.pageRef.context, this.pageRef.stem, newVersion,
newSpan, Value.absent(), newChildRefs, newKnotKeys);
}
@Override
public int pageSize() {
return this.pageRef.pageSize();
}
@Override
public int diffSize() {
return this.pageRef.diffSize();
}
@Override
public long treeSize() {
return this.pageRef.treeSize();
}
@Override
void memoizeSize(BTreePageRef pageRef) {
int pageSize = 12; // "@bnode(stem:"
pageSize += Recon.sizeOf(Num.from(this.pageRef.stem));
pageSize += 3; // ",v:"
pageSize += Recon.sizeOf(Num.from(this.version));
pageSize += 1; // ')'
final BTreePageRef[] childRefs = this.childRefs;
final int n = childRefs.length;
final Value[] knotKeys = this.knotKeys;
int diffSize = 0;
long treeSize = 0L;
if (n > 0) {
pageSize += 1; // '{'
for (int i = 0; i < n; i += 1) {
if (i > 0) {
final Value key = knotKeys[i - 1];
pageSize += 11; // ",@knot(key:"
pageSize += Recon.sizeOf(key);
pageSize += 2; // "),"
}
final BTreePageRef childRef = childRefs[i];
pageSize += childRef.pageRefSize();
if (this.version == childRef.softVersion()) {
diffSize += childRef.diffSize();
}
treeSize += childRef.treeSize();
}
pageSize += 1; // '}'
pageSize += 1; // '\n'
}
diffSize += pageSize;
treeSize += pageSize;
pageRef.pageSize = pageSize; // Must match bytes written by writePage
pageRef.diffSize = diffSize; // Must match bytes written by writeDiff
pageRef.treeSize = treeSize;
}
@Override
public Value toHeader() {
final Record header = Record.create(2).slot("stem", this.pageRef.stem)
.slot("v", this.version);
return Record.create(1).attr("bnode", header);
}
@Override
public Value toValue() {
final Record record = (Record) this.toHeader();
final BTreePageRef[] childRefs = this.childRefs;
final Value[] knotKeys = this.knotKeys;
for (int i = 0, n = childRefs.length; i < n; i += 1) {
if (i > 0) {
record.add(Record.create(1).attr("knot", Record.create(1).slot("key", knotKeys[i - 1])));
}
record.add(childRefs[i].toValue());
}
return record;
}
@Override
public BTreeNode reduced(Value identity, CombinerFunction super Value, Value> accumulator,
CombinerFunction combiner, long newVersion) {
final BTreePageRef[] oldChildRefs = this.childRefs;
final int n = oldChildRefs.length;
final BTreePageRef[] newChildRefs = new BTreePageRef[n];
for (int i = 0; i < n; i += 1) {
newChildRefs[i] = oldChildRefs[i].reduced(identity, accumulator, combiner, newVersion);
}
// assert n > 0;
Value fold = newChildRefs[0].fold();
for (int i = 1; i < n; i += 1) {
fold = combiner.combine(fold, newChildRefs[i].fold());
}
return BTreeNode.create(this.pageRef.context, this.pageRef.stem, newVersion,
this.pageRef.span, fold, newChildRefs, this.knotKeys);
}
@Override
public BTreeNode evacuated(int post, long version) {
final int oldPost = this.pageRef.post;
if (oldPost != 0 && oldPost < post) {
final BTreePageRef[] oldChildRefs = this.childRefs;
final int n = oldChildRefs.length;
final BTreePageRef[] newChildRefs = new BTreePageRef[n];
for (int i = 0; i < n; i += 1) {
final BTreePageRef oldChildRef = oldChildRefs[i];
final BTreePageRef newChildRef = oldChildRef.evacuated(post, version);
newChildRefs[i] = newChildRef;
if (oldChildRef != newChildRef) {
i += 1;
if (i < n) {
System.arraycopy(oldChildRefs, i, newChildRefs, i, n - i);
}
return BTreeNode.create(this.pageRef.context, this.pageRef.stem, version,
this.pageRef.span, this.pageRef.fold, newChildRefs, this.knotKeys);
}
}
}
return this;
}
@Override
public BTreeNode committed(int zone, long base, long version) {
final BTreePageRef[] oldChildRefs = this.childRefs;
final int n = oldChildRefs.length;
final BTreePageRef[] newChildRefs = new BTreePageRef[n];
long step = base;
for (int i = 0; i < n; i += 1) {
final BTreePageRef oldChildRef = oldChildRefs[i];
if (!oldChildRef.isCommitted()) {
final BTreePageRef newChildRef = oldChildRef.committed(zone, step, version);
newChildRefs[i] = newChildRef;
step += newChildRef.diffSize();
} else {
newChildRefs[i] = oldChildRef;
}
}
return BTreeNode.create(this.pageRef.context, this.pageRef.stem, version, zone, step,
this.pageRef.span, this.pageRef.fold, newChildRefs, this.knotKeys);
}
@Override
public BTreeNode uncommitted(long version) {
final BTreePageRef[] oldChildRefs = this.childRefs;
final int n = oldChildRefs.length;
final BTreePageRef[] newChildRefs = new BTreePageRef[n];
for (int i = 0; i < n; i += 1) {
newChildRefs[i] = oldChildRefs[i].uncommitted(version);
}
return BTreeNode.create(this.pageRef.context, this.pageRef.stem, version,
this.pageRef.span, this.pageRef.fold, newChildRefs, this.knotKeys);
}
@Override
public void writePage(Output> output) {
Recon.write(output, this.toHeader());
this.writePageContent(output);
output.write('\n');
}
void writePageContent(Output> output) {
final BTreePageRef[] childRefs = this.childRefs;
final int n = childRefs.length;
final Value[] knotKeys = this.knotKeys;
if (n > 0) {
output.write('{');
for (int i = 0; i < n; i += 1) {
if (i > 0) {
output.write(',').write('@').write('k').write('n').write('o').write('t')
.write('(').write('k').write('e').write('y').write(':');
Recon.write(output, knotKeys[i - 1]);
output.write(')').write(',');
}
childRefs[i].writePageRef(output);
}
output.write('}');
}
}
@Override
public void writeDiff(Output> output) {
final BTreePageRef[] childRefs = this.childRefs;
for (int i = 0, n = childRefs.length; i < n; i += 1) {
final BTreePageRef childRef = childRefs[i];
if (this.version == childRef.softVersion()) {
childRef.writeDiff(output);
}
}
this.writePage(output);
}
@Override
public void buildDiff(Builder builder) {
final BTreePageRef[] childRefs = this.childRefs;
for (int i = 0, n = childRefs.length; i < n; i += 1) {
final BTreePageRef childRef = childRefs[i];
if (this.version == childRef.softVersion()) {
childRef.buildDiff(builder);
}
}
builder.add(this);
}
@Override
public BTreePage loadTree(PageLoader pageLoader) {
final BTreePageRef[] childRefs = this.childRefs;
for (int i = 0, n = childRefs.length; i < n; i += 1) {
childRefs[i].loadTree(pageLoader);
}
return this;
}
@Override
public void soften(long version) {
final BTreePageRef[] childRefs = this.childRefs;
for (int i = 0, n = childRefs.length; i < n; i += 1) {
childRefs[i].soften(version);
}
}
@Override
public OrderedMapCursor cursor() {
return new BTreeNodeDepthCursor(this, Integer.MAX_VALUE);
}
@Override
public OrderedMapCursor depthCursor(int maxDepth) {
return new BTreeNodeDepthCursor(this, maxDepth);
}
@Override
public OrderedMapCursor deltaCursor(long sinceVersion) {
return new BTreeNodeDeltaCursor(this, sinceVersion);
}
@Override
public String toString() {
final Output output = Unicode.stringOutput(this.pageSize() - 1); // ignore trailing '\n'
Recon.write(output, this.toHeader());
this.writePageContent(output);
return output.bind();
}
static final Value[] EMPTY_KNOT_KEYS = new Value[0];
public static BTreeNode create(PageContext context, int stem, long version,
int post, int zone, long base, long span, Value fold,
BTreePageRef[] childRefs, Value[] knotKeys) {
final BTreePageRef pageRef = new BTreePageRef(context, PageType.NODE, stem,
post, zone, base, span, fold);
final BTreeNode page = new BTreeNode(pageRef, version, childRefs, knotKeys);
pageRef.page = page;
return page;
}
public static BTreeNode create(PageContext context, int stem, long version,
int zone, long base, long span, Value fold,
BTreePageRef[] childRefs, Value[] knotKeys) {
int post = zone;
for (int i = 0, n = childRefs.length; i < n; i += 1) {
final int childPost = childRefs[i].post;
if (childPost != 0) {
post = post == 0 ? childPost : Math.min(post, childPost);
}
}
return BTreeNode.create(context, stem, version, post, zone, base, span, fold, childRefs, knotKeys);
}
public static BTreeNode create(PageContext context, int stem, long version, long span,
Value fold, BTreePageRef[] childRefs, Value[] knotKeys) {
return BTreeNode.create(context, stem, version, 0, 0L, span, fold, childRefs, knotKeys);
}
public static BTreeNode fromValue(BTreePageRef pageRef, Value value) {
Throwable cause = null;
try {
final Value header = value.header("bnode");
final long version = header.get("v").longValue();
final Record tail = value.tail();
final int n = tail.size() >>> 1;
final BTreePageRef[] childRefs = new BTreePageRef[n + 1];
final Value[] knotKeys = new Value[n];
childRefs[0] = BTreePageRef.fromValue(pageRef.context, pageRef.stem,
tail.get(0).toValue());
for (int i = 1; i <= n; i += 1) {
knotKeys[i - 1] = tail.get(2 * i - 1).header("knot").get("key");
childRefs[i] = BTreePageRef.fromValue(pageRef.context, pageRef.stem,
tail.get(2 * i).toValue());
}
return new BTreeNode(pageRef, version, childRefs, knotKeys);
} catch (Throwable error) {
if (Cont.isNonFatal(error)) {
cause = error;
} else {
throw error;
}
}
final Output message = Unicode.stringOutput("Malformed bnode: ");
Recon.write(message, value);
throw new StoreException(message.bind(), cause);
}
}