blob: fc305197d8646bb4b56b7509f538425b12648dc0 [file] [log] [blame]
/*-
* Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.je.tree;
import java.util.Arrays;
import com.sleepycat.je.dbi.MemoryBudget;
import com.sleepycat.je.evictor.Evictor;
import com.sleepycat.je.utilint.SizeofMarker;
/**
* The abstract class that defines the various representations used to
* represent an array of target pointers to children of an IN node. These
* arrays can be sparse, so the non-default representations are designed to
* make efficient representations for the sparse cases. Each specialized
* representation is a subclass of INTargetReps.
*
* A new IN node starts out with the None representation and grows through a
* sparse into the full default representation. Subsequently, the default
* representation can be <i>compacted</i> into a Sparse or None representation
* whenever an IN is stripped. Note that representations do not currently move
* to more compact forms when entries are nulled to minimize the possibility of
* tansitionary representation changes, since each representation change has
* a cpu cost and a gc cost associated with it.
*/
public abstract class INTargetRep
extends INArrayRep<INTargetRep, INTargetRep.Type, Node> {
/* Single instance used for None rep. */
public static final None NONE = new None();
/* Enumeration for the different types of supported representations. */
public enum Type { DEFAULT, SPARSE, NONE }
public INTargetRep() {
}
/* The default non-sparse representation. It simply wraps an array. */
public static class Default extends INTargetRep {
/* The target nodes */
private final Node[] targets;
public Default(int capacity) {
this.targets = new Node[capacity];
}
/* Only for use by the Sizeof utility. */
public Default(@SuppressWarnings("unused") SizeofMarker marker) {
targets = null;
}
private Default(Node[] targets) {
this.targets = targets;
}
@Override
public Default resize(int capacity) {
return new Default(Arrays.copyOfRange(targets, 0, capacity));
}
@Override
public Type getType() {
return Type.DEFAULT;
}
@Override
public Node get(int idx) {
return targets[idx];
}
@Override
public INTargetRep set(int idx, Node node, IN parent) {
targets[idx] = node;
return this;
}
@Override
public INTargetRep copy(int from, int to, int n, IN parent) {
System.arraycopy(targets, from, targets, to, n);
return this;
}
@Override
public INTargetRep compact(IN parent) {
int count = 0;
for (Node target : targets) {
if (target != null) {
count++;
}
}
if ((count > Sparse.MAX_ENTRIES) ||
(targets.length > Sparse.MAX_INDEX)) {
return this;
}
INTargetRep newRep = null;
if (count == 0) {
newRep = NONE;
} else {
newRep = new Sparse(targets.length);
for (int i=0; i < targets.length; i++) {
if (targets[i] != null) {
newRep.set(i, targets[i], parent);
}
}
}
noteRepChange(newRep, parent);
return newRep;
}
@Override
public long calculateMemorySize() {
return MemoryBudget.DEFAULT_TARGET_ENTRY_OVERHEAD +
MemoryBudget.objectArraySize(targets.length);
}
@Override
public void updateCacheStats(@SuppressWarnings("unused")
boolean increment,
@SuppressWarnings("unused")
Evictor evictor) {
/* No stats for this default rep. */
}
}
/**
* Representation used when 1-4 children are cached. Note that the IN
* itself may have more children, but they are not currently cached.
* The INArrayRep is represented by two parallel arrays: an array of
* indices (idxs) and an array of values (targets). All elements that are
* not explicitly represented are null.
*/
public static class Sparse extends INTargetRep {
/* The maximum number of entries that can be represented. */
public static final int MAX_ENTRIES = 4;
/* The maximum index that can be represented. */
public static final int MAX_INDEX = Short.MAX_VALUE;
/*
* The parallel arrays implementing the INArrayRep.
*/
final short idxs[] = new short[MAX_ENTRIES];
final Node targets[] = new Node[MAX_ENTRIES];
public Sparse(int capacity) {
/* Unroll initialization. */
idxs[0] = idxs[1] = idxs[2] = idxs[3] = -1;
}
/* Only for use by the Sizeof utility. */
public Sparse(@SuppressWarnings("unused") SizeofMarker marker) {
}
@Override
public Sparse resize(int capacity) {
return this;
}
@Override
public Type getType() {
return Type.SPARSE;
}
@Override
public Node get(int j) {
assert (j >= 0) && (j <= MAX_INDEX);
/* Unrolled for loop */
if (idxs[0] == j) {
return targets[0];
}
if (idxs[1] == j) {
return targets[1];
}
if (idxs[2] == j) {
return targets[2];
}
if (idxs[3] == j) {
return targets[3];
}
return null;
}
@Override
public INTargetRep set(int j, Node node, IN parent) {
assert (j >= 0) && (j <= MAX_INDEX);
int slot = -1;
for (int i=0; i < targets.length; i++) {
if (idxs[i] == j) {
targets[i] = node;
return this;
}
if ((slot < 0) && (targets[i] == null)) {
slot = i;
}
}
if (node == null) {
return this;
}
/* Have a free slot, use it. */
if (slot >= 0) {
targets[slot] = node;
idxs[slot] = (short)j;
return this;
}
/* It's full, mutate it. */
Default fe = new Default(parent.getMaxEntries());
noteRepChange(fe, parent);
for (int i=0; i < targets.length; i++) {
if (targets[i] != null) {
fe.set(idxs[i], targets[i], parent);
}
}
return fe.set(j, node, parent);
}
@Override
public INTargetRep copy(int from, int to, int n, IN parent) {
INTargetRep target = this;
if ((to == from) || (n == 0)) {
/* Nothing to do */
} else if (to < from) {
/* Copy ascending */
for (int i = 0; i < n; i++) {
target = target.set(to++, get(from++), parent);
}
} else {
/* to > from. Copy descending */
from += n;
to += n;
for (int i = 0; i < n; i++) {
target = target.set(--to, get(--from), parent);
}
}
return target;
}
@Override
public INTargetRep compact(IN parent) {
int count = 0;
for (Node target : targets) {
if (target != null) {
count++;
}
}
if (count == 0) {
None newRep = NONE;
noteRepChange(newRep, parent);
return newRep;
}
return this;
}
@Override
public long calculateMemorySize() {
/*
* Note that fixed array sizes are already accounted for in the
* SPARSE_TARGET_ENTRY_OVERHEAD computed vis Sizeof.
*/
return MemoryBudget.SPARSE_TARGET_ENTRY_OVERHEAD;
}
@Override
public void updateCacheStats(boolean increment, Evictor evictor) {
if (increment) {
evictor.getNINSparseTarget().incrementAndGet();
} else {
evictor.getNINSparseTarget().decrementAndGet();
}
}
}
/**
* Representation used when an IN has no children cached.
*/
public static class None extends INTargetRep {
private None() {
}
/* Only for use by the Sizeof utility. */
public None(@SuppressWarnings("unused") SizeofMarker marker) {
}
@Override
public None resize(int capacity) {
return this;
}
@Override
public Type getType() {
return Type.NONE;
}
@Override
public Node get(@SuppressWarnings("unused") int idx) {
return null;
}
@Override
public INTargetRep set(int idx, Node node, IN parent) {
if (node == null) {
return this;
}
INTargetRep targets = new Sparse(parent.getMaxEntries());
noteRepChange(targets, parent);
return targets.set(idx, node, parent);
}
@Override
public INTargetRep copy(@SuppressWarnings("unused") int from,
@SuppressWarnings("unused") int to,
@SuppressWarnings("unused") int n,
@SuppressWarnings("unused") IN parent) {
/* Nothing to copy. */
return this;
}
@Override
public INTargetRep compact(IN parent) {
return this;
}
@Override
public long calculateMemorySize() {
/* A single static instance is used. */
return 0;
}
@Override
public void updateCacheStats(boolean increment, Evictor evictor) {
if (increment) {
evictor.getNINNoTarget().incrementAndGet();
} else {
evictor.getNINNoTarget().decrementAndGet();
}
}
}
}