SCViewer/com.minres.scviewer.databas.../src/org/apache/jdbm/HTreeBucket.java

353 lines
11 KiB
Java

/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* 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 org.apache.jdbm;
import java.io.*;
import java.util.ArrayList;
/**
* A bucket is a placeholder for multiple (key, value) pairs. Buckets
* are used to store collisions (same hash value) at all levels of an
* H*tree.
* <p/>
* There are two types of buckets: leaf and non-leaf.
* <p/>
* Non-leaf buckets are buckets which hold collisions which happen
* when the H*tree is not fully expanded. Keys in a non-leaf buckets
* can have different hash codes. Non-leaf buckets are limited to an
* arbitrary size. When this limit is reached, the H*tree should create
* a new HTreeDirectory node and distribute keys of the non-leaf buckets into
* the newly created HTreeDirectory.
* <p/>
* A leaf bucket is a bucket which contains keys which all have
* the same <code>hashCode()</code>. Leaf buckets stand at the
* bottom of an H*tree because the hashing algorithm cannot further
* discriminate between different keys based on their hash code.
*
* @author Alex Boisvert
*/
final class HTreeBucket<K, V> {
/**
* The maximum number of elements (key, value) a non-leaf bucket
* can contain.
*/
public static final int OVERFLOW_SIZE = 16;
/**
* Depth of this bucket.
*/
private byte _depth;
/**
* Keys and values in this bucket. Keys are followed by values at KEYPOS+OVERFLOW_SIZE
*/
private Object[] _keysAndValues;
private byte size = 0;
private final HTree<K, V> tree;
/**
* Public constructor for serialization.
*/
public HTreeBucket(HTree<K, V> tree) {
this.tree = tree;
}
/**
* Construct a bucket with a given depth level. Depth level is the
* number of <code>HashDirectory</code> above this bucket.
*/
public HTreeBucket(HTree<K, V> tree, byte level) {
this.tree = tree;
if (level > HTreeDirectory.MAX_DEPTH + 1) {
throw new IllegalArgumentException(
"Cannot create bucket with depth > MAX_DEPTH+1. "
+ "Depth=" + level);
}
_depth = level;
_keysAndValues = new Object[OVERFLOW_SIZE * 2];
}
/**
* Returns the number of elements contained in this bucket.
*/
public int getElementCount() {
return size;
}
/**
* Returns whether or not this bucket is a "leaf bucket".
*/
public boolean isLeaf() {
return (_depth > HTreeDirectory.MAX_DEPTH);
}
/**
* Returns true if bucket can accept at least one more element.
*/
public boolean hasRoom() {
if (isLeaf()) {
return true; // leaf buckets are never full
} else {
// non-leaf bucket
return (size < OVERFLOW_SIZE);
}
}
/**
* Add an element (key, value) to this bucket. If an existing element
* has the same key, it is replaced silently.
*
* @return Object which was previously associated with the given key
* or <code>null</code> if no association existed.
*/
public V addElement(K key, V value) {
//find entry
byte existing = -1;
for (byte i = 0; i < size; i++) {
if (key.equals(_keysAndValues[i])) {
existing = i;
break;
}
}
if (existing != -1) {
// replace existing element
Object before = _keysAndValues[existing + OVERFLOW_SIZE];
if (before instanceof BTreeLazyRecord) {
BTreeLazyRecord<V> rec = (BTreeLazyRecord<V>) before;
before = rec.get();
rec.delete();
}
_keysAndValues[existing + OVERFLOW_SIZE] = value;
return (V) before;
} else {
// add new (key, value) pair
_keysAndValues[size] = key;
_keysAndValues[size + OVERFLOW_SIZE] = value;
size++;
return null;
}
}
/**
* Remove an element, given a specific key.
*
* @param key Key of the element to remove
* @return Removed element value, or <code>null</code> if not found
*/
public V removeElement(K key) {
//find entry
byte existing = -1;
for (byte i = 0; i < size; i++) {
if (key.equals(_keysAndValues[i])) {
existing = i;
break;
}
}
if (existing != -1) {
Object o = _keysAndValues[existing + OVERFLOW_SIZE];
if (o instanceof BTreeLazyRecord) {
BTreeLazyRecord<V> rec = (BTreeLazyRecord<V>) o;
o = rec.get();
rec.delete();
}
//move last element to existing
size--;
_keysAndValues[existing] = _keysAndValues[size];
_keysAndValues[existing + OVERFLOW_SIZE] = _keysAndValues[size + OVERFLOW_SIZE];
//and unset last element
_keysAndValues[size] = null;
_keysAndValues[size + OVERFLOW_SIZE] = null;
return (V) o;
} else {
// not found
return null;
}
}
/**
* Returns the value associated with a given key. If the given key
* is not found in this bucket, returns <code>null</code>.
*/
public V getValue(K key) {
//find entry
byte existing = -1;
for (byte i = 0; i < size; i++) {
if (key.equals(_keysAndValues[i])) {
existing = i;
break;
}
}
if (existing != -1) {
Object o = _keysAndValues[existing + OVERFLOW_SIZE];
if (o instanceof BTreeLazyRecord)
return ((BTreeLazyRecord<V>) o).get();
else
return (V) o;
} else {
// key not found
return null;
}
}
/**
* Obtain keys contained in this buckets. Keys are ordered to match
* their values, which be be obtained by calling <code>getValues()</code>.
* <p/>
* As an optimization, the Vector returned is the instance member
* of this class. Please don't modify outside the scope of this class.
*/
ArrayList<K> getKeys() {
ArrayList<K> ret = new ArrayList<K>();
for (byte i = 0; i < size; i++) {
ret.add((K) _keysAndValues[i]);
}
return ret;
}
/**
* Obtain values contained in this buckets. Values are ordered to match
* their keys, which be be obtained by calling <code>getKeys()</code>.
* <p/>
* As an optimization, the Vector returned is the instance member
* of this class. Please don't modify outside the scope of this class.
*/
ArrayList<V> getValues() {
ArrayList<V> ret = new ArrayList<V>();
for (byte i = 0; i < size; i++) {
ret.add((V) _keysAndValues[i + OVERFLOW_SIZE]);
}
return ret;
}
public void writeExternal(DataOutput out)
throws IOException {
out.write(_depth);
out.write(size);
DataInputOutput out3 = tree.writeBufferCache.getAndSet(null);
if (out3 == null)
out3 = new DataInputOutput();
else
out3.reset();
Serializer keySerializer = tree.keySerializer != null ? tree.keySerializer : tree.getRecordManager().defaultSerializer();
for (byte i = 0; i < size; i++) {
out3.reset();
keySerializer.serialize(out3, _keysAndValues[i]);
LongPacker.packInt(out, out3.getPos());
out.write(out3.getBuf(), 0, out3.getPos());
}
//write values
if(tree.hasValues()){
Serializer valSerializer = tree.valueSerializer != null ? tree.valueSerializer : tree.getRecordManager().defaultSerializer();
for (byte i = 0; i < size; i++) {
Object value = _keysAndValues[i + OVERFLOW_SIZE];
if (value == null) {
out.write(BTreeLazyRecord.NULL);
} else if (value instanceof BTreeLazyRecord) {
out.write(BTreeLazyRecord.LAZY_RECORD);
LongPacker.packLong(out, ((BTreeLazyRecord) value).recid);
} else {
//transform to byte array
out3.reset();
valSerializer.serialize(out3, value);
if (out3.getPos() > BTreeLazyRecord.MAX_INTREE_RECORD_SIZE) {
//store as separate record
long recid = tree.getRecordManager().insert(out3.toByteArray(), BTreeLazyRecord.FAKE_SERIALIZER,true);
out.write(BTreeLazyRecord.LAZY_RECORD);
LongPacker.packLong(out, recid);
} else {
out.write(out3.getPos());
out.write(out3.getBuf(), 0, out3.getPos());
}
}
}
}
tree.writeBufferCache.set(out3);
}
public void readExternal(DataInputOutput in) throws IOException, ClassNotFoundException {
_depth = in.readByte();
size = in.readByte();
//read keys
Serializer keySerializer = tree.keySerializer != null ? tree.keySerializer : tree.getRecordManager().defaultSerializer();
_keysAndValues = (K[]) new Object[OVERFLOW_SIZE * 2];
for (byte i = 0; i < size; i++) {
int expectedSize = LongPacker.unpackInt(in);
K key = (K) BTreeLazyRecord.fastDeser(in, keySerializer, expectedSize);
_keysAndValues[i] = key;
}
//read values
if(tree.hasValues()){
Serializer<V> valSerializer = tree.valueSerializer != null ? tree.valueSerializer : (Serializer<V>) tree.getRecordManager().defaultSerializer();
for (byte i = 0; i < size; i++) {
int header = in.readUnsignedByte();
if (header == BTreeLazyRecord.NULL) {
_keysAndValues[i + OVERFLOW_SIZE] = null;
} else if (header == BTreeLazyRecord.LAZY_RECORD) {
long recid = LongPacker.unpackLong(in);
_keysAndValues[i + OVERFLOW_SIZE] = (new BTreeLazyRecord(tree.getRecordManager(), recid, valSerializer));
} else {
_keysAndValues[i + OVERFLOW_SIZE] = BTreeLazyRecord.fastDeser(in, valSerializer, header);
}
}
}else{
for (byte i = 0; i < size; i++) {
if(_keysAndValues[i]!=null)
_keysAndValues[i+OVERFLOW_SIZE] = Utils.EMPTY_STRING;
}
}
}
}