|
What this is
Other links
The source code
/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.mdr.persistence.btreeimpl.btreeindex;
import org.netbeans.mdr.persistence.*;
import java.io.*;
import java.util.Arrays;
/**
* Implementation of a BtreePage with fixed key length and fixed data length that
* are stored in compressed form if all the stored keys or data prefixes are
* strings of zeros (these prefixes are excluded then).
*/
public class ShrinkablePage extends BtreePage {
private static boolean debug = false;
private static final byte KEYS_SHRINKED_MASK = 1;
private static final byte DATA_SHRINKED_MASK = 2;
// length of the key prefix relavant to the page compression
static final int KEY_ZERO_PREFIX_LENGTH = MOFID.LENGTH / 2;
// length of the data prefix relavant to the page compression
static final int DATA_ZERO_PREFIX_LENGTH = MOFID.LENGTH / 2;
// length of the key - determined by key info
int keyLengthX;
// length of data - determined by data info
int dataLengthX;
// flag indicating if the keys are stored in the compressed form
boolean keysShrinked;
// flag indicating if the data are stored in the compressed form
boolean dataShrinked;
// true if keys are shrinkable
boolean keysShrinkable;
// true if data are shrinkable
boolean dataShrinkable;
// number of bytes currently used to store a key
int currentKeyLength;
// number of bytes currently used to store data
int currentDataLength;
/*
// number of keys with non-zero prefix stored in the page
int longKeysCount;
// number of data with non-zero prefix stored in the page
int longDataCount;
*/
// flag indicating if an operation requires key parts of entries to expanded (from shrinked form)
boolean expandKeys;
// flag indicating if an operation requires data parts of entries to expanded (from shrinked form)
boolean expandData;
/**
* Initialize a newly-instantiated or recycled ShrinkablePage. Note that the
* isNew parameter pertains to whether a new page is being added to the
* btree, not to whether this BtreePage object is new or recycled.
*
* @param btree btree to which this page belongs
* @param pageId page ID in byte array
* @param pageBuffer page buffer
* @param isNew is this page new to the btree
*/
public void init(Btree btree, byte[] pageId, byte[] pageBuffer,
boolean isNew) throws StorageException {
if (initialized) {
/* This page was found in cache and doesn't need initialization */
return;
}
super.init(btree, pageId, pageBuffer, isNew);
headerLength++;
keyLengthX = btree.keyInfo.getLength();
dataLengthX = btree.dataInfo.getLength();
keysShrinkable = btree.keyInfo instanceof MOFIDInfo;
dataShrinkable = btree.dataInfo instanceof MOFIDInfo;
if (isNew) {
keysShrinked = keysShrinkable;
dataShrinked = dataShrinkable;
freeStart++;
} else {
keysShrinked = (pageBuffer [headerLength - 1] & KEYS_SHRINKED_MASK) > 0;
dataShrinked = (pageBuffer [headerLength - 1] & DATA_SHRINKED_MASK) > 0;
}
currentKeyLength = keysShrinked ? MOFID.LENGTH / 2 : keyLengthX;
currentDataLength = dataShrinked ? MOFID.LENGTH / 2 : dataLengthX;
/*
longKeysCount = 0;
longDataCount = 0;
*/
}
/**
* Returns the number of entries currently on this page.
*
* @return number of entries on this page
*/
int numEntries() {
return (freeStart - headerLength)/(currentKeyLength + currentDataLength);
}
/**
* Returns the offset in the page where a specified entry's key is located.
*
* @param entryNum entry number
*
* @return offset of entryNum's key
*/
int keyOffset(int entryNum) {
return headerLength + (entryNum * (currentKeyLength + currentDataLength));
}
/**
* Returns the length of a specified entry's key.
*
* @param entryNum entry number
*
* @return length of entryNum's key
*/
int keyLength(int entryNum) {
return keyLengthX;
}
/**
* Returns the data value for the specified entry.
*
* @param entryNum entry number
*
* @return new byte array containing the data at entryNum
*/
byte[] getData(int entryNum) {
byte[] data = new byte[dataLengthX];
if (dataShrinked) {
System.arraycopy(pageBuffer, keyOffset(entryNum) + currentKeyLength,
data, DATA_ZERO_PREFIX_LENGTH, currentDataLength);
Arrays.fill(data, 0, DATA_ZERO_PREFIX_LENGTH, (byte) 0);
} else {
System.arraycopy(pageBuffer, keyOffset(entryNum) + currentKeyLength,
data, 0, currentDataLength);
}
if (debug) {
System.out.println("getData called: " + entryNum);
}
return data;
}
/**
* Returns the key for the specified entry.
*
* @param entryNum entry number
*
* @return new byte array containing the key at entryNum
*/
byte[] getKey(int entryNum) {
byte[] key = new byte[keyLengthX];
if (keysShrinked) {
System.arraycopy(pageBuffer, keyOffset(entryNum), key,
KEY_ZERO_PREFIX_LENGTH, currentKeyLength);
Arrays.fill(key, 0, KEY_ZERO_PREFIX_LENGTH, (byte) 0);
} else {
System.arraycopy(pageBuffer, keyOffset(entryNum), key, 0, currentKeyLength);
}
if (debug) {
System.out.print("getKey: ");
for (int x = 0; x < key.length; x++)
System.out.print(key[x] + ":");
System.out.println("");
}
return key;
}
protected byte compare(byte[] key, int entryNum) {
if (!isLeaf() && (entryNum == 0)
&& pageSource.isNoPage(previousPage)) {
return EntryTypeInfo.GREATER;
}
int index = 0;
if (keysShrinkable && keysShrinked) {
for (; index < KEY_ZERO_PREFIX_LENGTH; index++) {
if (key [index] < 0) {
return EntryTypeInfo.LESS;
} else if (key [index] > 0) {
return EntryTypeInfo.GREATER;
}
} // for
} // if
int offset = keyOffset (entryNum);
for (; index < key.length; index++, offset++) {
if (key[index] < pageBuffer[offset]) {
return EntryTypeInfo.LESS;
} else if (key[index] > pageBuffer[offset]) {
return EntryTypeInfo.GREATER;
}
}
return EntryTypeInfo.EQUAL;
}
protected byte compareData(byte[] data, int entryNum) {
int index = 0;
if (dataShrinkable && dataShrinked) {
for (; index < DATA_ZERO_PREFIX_LENGTH; index++) {
if (data [index] < 0) {
return EntryTypeInfo.LESS;
} else if (data [index] > 0) {
return EntryTypeInfo.GREATER;
}
} // for
} // if
int offset = keyOffset (entryNum) + currentKeyLength;
for (; index < data.length; index++, offset++) {
if (data[index] < pageBuffer[offset]) {
return EntryTypeInfo.LESS;
} else if (data[index] > pageBuffer[offset]) {
return EntryTypeInfo.GREATER;
}
}
return EntryTypeInfo.EQUAL;
}
/*
* Inserts an entry at the specified location.
*
* @param entry BtreeEntry to be inserted
* @param entryNum location where entry is to be inserted
*
* @return If a split occurred, an entry to be inserted in this page's
* parent is returned; otherwise null is returned.
*/
BtreeEntry insert(BtreeEntry entry, int entryNum, SearchResult resultPosition)
throws StorageException {
if (debug) {
System.out.print("insert called: ");
for (int x = 0; x < entry.key.length; x++) {
System.out.print(entry.key [x]);
}
System.out.println("");
}
if (debug) {
for (int x = 0; x < entry.key.length; x++)
System.out.print(entry.key[x] + ":");
System.out.print(" ");
for (int x = 0; x < entry.data.length; x++)
System.out.print(entry.data[x] + ":");
System.out.println();
}
int offset;
int space = 0;
expandKeys = false;
expandData = false;
pageSource.dirtyPage(this);
if (keysShrinked && hasLongKey (entry)) {
expandKeys = true;
space += numEntries () * KEY_ZERO_PREFIX_LENGTH + entry.key.length;
}
if (dataShrinked && hasLongData (entry)) {
expandData = true;
space += numEntries () * DATA_ZERO_PREFIX_LENGTH + entry.data.length;
}
if (!expandKeys)
space += currentKeyLength;
if (!expandData)
space += currentDataLength;
if (btree.pageSize - freeStart - keyLengthX - dataLengthX >= space) {
insertHere(entry, entryNum, resultPosition);
return null;
} else {
BtreeEntry result = split(entry, entryNum, resultPosition);
return result;
}
}
/*
* Replaces the entry at the specified location with the passed-in entry.
*
* @param entry BtreeEntry to be inserted
* @param entryNum location where entry is to be inserted
*
* @return
*/
BtreeEntry replace(BtreeEntry entry, int entryNum, SearchResult position) throws StorageException {
if (debug) {
System.out.println("replace called");
}
pageSource.dirtyPage(this);
if (dataShrinked && hasLongData (entry)) {
int space = numEntries () * (currentKeyLength + dataLengthX);
if (btree.pageSize - freeStart - keyLengthX - dataLengthX < space) {
delete (entryNum, entryNum);
return insert (entry, entryNum, position);
} else {
expandData ();
}
}
int offset = keyOffset(entryNum) + currentKeyLength;
int dataStart = dataShrinked ? DATA_ZERO_PREFIX_LENGTH : 0;
System.arraycopy(entry.data, dataStart, pageBuffer, offset, currentDataLength);
return null;
}
private void insertHere(BtreeEntry entry, int entryNum, SearchResult resultPosition) {
if (debug) {
//if (expandKeys || expandData) {
dump ();
dumpBuffer ();
//}
}
int offset;
if (expandKeys)
expandKeys ();
if (expandData)
expandData ();
offset = keyOffset(entryNum);
if (offset != freeStart) {
/* Shift entries down to make room for this one */
System.arraycopy(pageBuffer, offset,
pageBuffer, offset + (currentKeyLength + currentDataLength),
freeStart - offset);
}
copyEntry (offset, entry);
freeStart += currentKeyLength + currentDataLength;
if (resultPosition != null) {
resultPosition.entryNum = entryNum;
resultPosition.matched = true;
resultPosition.page = this;
resultPosition.skipCount = 0;
}
if (debug) {
//if ((expandKeys || expandData)) {
//System.out.print("insert called: ");
for (int x = 0; x < entry.key.length; x++) {
System.out.print(entry.key [x]);
}
System.out.println("");
dump ();
dumpBuffer ();
//}
}
}
/**
* Deletes an entry or range of entries from this page.
*
* @param first entry number of first entry to be deleted
* @param last entry number of last entry to be deleted
*/
void delete(int firstEntry, int lastEntry) throws StorageException {
int startOffset, endOffset;
if (debug) {
System.out.println("delete called: " + firstEntry + " " + lastEntry);
}
pageSource.dirtyPage(this);
startOffset = keyOffset(firstEntry);
endOffset = keyOffset(lastEntry+1);
if (freeStart != endOffset) {
System.arraycopy(pageBuffer, endOffset, pageBuffer, startOffset,
freeStart - endOffset);
}
freeStart -= endOffset - startOffset;
}
/**
* Splits the entries on the current page between two pages, and inserts
* an entry into its correct location on one of those pages. The left
* page may be this page or it may be a different page. The right page is
* always a different page from this.
*
* @param left left page to move entries to (may be this page)
* @param right right page to move entries to
* @param entry BtreeEntry to be inserted
* @param newEntryNum insert location relative to the current page
*
* @return new byte array containing first key on right page
*/
byte[] splitEntries(BtreePage leftPage, BtreePage rightPage, BtreeEntry entry,
int entryNum, SearchResult resultPosition) {
int entryLength;
int offset;
int midpoint;
int half;
int copyLength;
boolean insertLeft;
byte[] rightKey;
if (debug) {
System.out.println("Split entries called.");
}
ShrinkablePage left = (ShrinkablePage) leftPage;
ShrinkablePage right = (ShrinkablePage) rightPage;
copyState (right);
if (left != this)
copyState (left);
offset = keyOffset(entryNum);
entryLength = currentKeyLength + currentDataLength;
half = numEntries()/2;
midpoint = headerLength + half*entryLength;
insertLeft = offset < midpoint;
// Move second half of entries to right page.
if (insertLeft) {
copyLength = this.freeStart - midpoint;
} else {
// First just copy entries up to the insert point
copyLength = offset - midpoint;
}
// record position of inserted entry
if (resultPosition != null) {
resultPosition.matched = true;
resultPosition.skipCount = 0;
if (insertLeft) {
resultPosition.page = left;
resultPosition.entryNum = entryNum;
} else {
resultPosition.page = right;
resultPosition.entryNum = entryNum - half;
}
}
if (copyLength > 0) {
System.arraycopy(this.pageBuffer, midpoint, right.pageBuffer,
right.freeStart, copyLength);
right.freeStart += copyLength;
}
if (!insertLeft) {
// System.arraycopy(entry.key, 0, right.pageBuffer, right.freeStart, currentKeyLength);
right.freeStart += currentKeyLength;
// System.arraycopy(entry.data, 0, right.pageBuffer, right.freeStart, currentDataLength);
right.freeStart += currentDataLength;
if (this.freeStart != offset) {
System.arraycopy(this.pageBuffer, offset, right.pageBuffer,
right.freeStart, this.freeStart - offset);
right.freeStart += this.freeStart - offset;
}
if (expandKeys)
right.expandKeys ();
if (expandData)
right.expandData ();
right.copyEntry (right.keyOffset (entryNum - half), entry);
}
// Move first half of entries to left page, if left page is new.
// Either way, insert the new entry if it belongs here.
if (left != this) {
if (insertLeft) {
copyLength = offset - headerLength;
} else {
copyLength = midpoint - headerLength;
}
if (copyLength > 0) {
System.arraycopy(this.pageBuffer, headerLength,
left.pageBuffer, left.freeStart, copyLength);
}
}
if (insertLeft) {
if (midpoint != offset) {
System.arraycopy(this.pageBuffer, offset, left.pageBuffer,
offset+entryLength, midpoint - offset);
}
left.freeStart = midpoint + entryLength;
if (expandKeys)
left.expandKeys ();
if (expandData)
left.expandData ();
left.copyEntry (keyOffset (entryNum), entry);
// System.arraycopy(entry.key, 0, left.pageBuffer, offset, keyLength);
// System.arraycopy(entry.data, 0, left.pageBuffer, offset + keyLength, dataLength);
} else {
left.freeStart = midpoint;
}
if (left != this) {
// All entries were moved off of this page
this.freeStart = headerLength;
}
/*
rightKey = new byte[keyLength];
System.arraycopy(right.pageBuffer, headerLength, rightKey, 0, keyLength);
return rightKey;
*/
int entNum = insertLeft ? entryNum : entryNum - half;
BtreePage pg = insertLeft ? left : right;
/*
try {
System.out.println("============");
for (int x = 0; x < entry.key.length; x++)
System.out.print(entry.key[x]);
System.out.print(" ");
for (int x = 0; x < entry.data.length; x++)
System.out.print(entry.data[x]);
System.out.println();
pg.getKey (entNum);
pg.getData (entNum);
System.out.println("============");
} catch (Exception e) {
}
*/
// System.out.println("split done: " + left.numEntries () + " " + right.numEntries ());
return right.getKey (0);
}
public void store() {
super.store();
byte val = 0;
if (keysShrinked) {
val += KEYS_SHRINKED_MASK;
}
if (dataShrinked) {
val += DATA_SHRINKED_MASK;
}
pageBuffer [headerLength - 1] = val;
}
// helper methods ...........................................................
/**
* @return true if the entry's key prefix does not consist of 0's
*/
private boolean hasLongKey (BtreeEntry entry) {
for (int x = 0; x < KEY_ZERO_PREFIX_LENGTH; x++) {
if (entry.key [x] != 0)
return true;
} // for
return false;
}
/**
* @return true if the entry's data prefix does not consist of 0's
*/
private boolean hasLongData (BtreeEntry entry) {
for (int x = 0; x < KEY_ZERO_PREFIX_LENGTH; x++) {
if (entry.data [x] != 0)
return true;
} // for
return false;
}
void expandKeys () {
if (debug) {
System.out.println("expand keys called");
dump ();
}
int numEntries = numEntries ();
int length = numEntries * (currentKeyLength + currentDataLength);
byte [] tempBuffer = new byte [length];
System.arraycopy (pageBuffer, headerLength, tempBuffer, 0, length);
for (int i = 0; i < numEntries; i++) {
int index = headerLength + i*(keyLengthX + currentDataLength);
Arrays.fill (pageBuffer, index,
index + KEY_ZERO_PREFIX_LENGTH, (byte) 0);
System.arraycopy (tempBuffer, i*(currentKeyLength + currentDataLength), pageBuffer,
KEY_ZERO_PREFIX_LENGTH + index,
currentKeyLength + currentDataLength);
}
freeStart += numEntries * KEY_ZERO_PREFIX_LENGTH;
currentKeyLength = keyLengthX;
keysShrinked = false;
if (debug) {
dump ();
}
}
void expandData () {
if (debug) {
System.out.println("expand data called");
}
int numEntries = numEntries ();
int length = numEntries * (currentKeyLength + currentDataLength);
byte [] tempBuffer = new byte [length];
System.arraycopy (pageBuffer, headerLength, tempBuffer, 0, length);
for (int i = 0; i < numEntries; i++) {
int index = headerLength + currentKeyLength + i*(currentKeyLength + dataLengthX);
System.arraycopy (tempBuffer,
i*(currentKeyLength + currentDataLength),
pageBuffer,
headerLength + i*(keyLengthX + currentDataLength), currentKeyLength);
Arrays.fill (pageBuffer,
index,
index + DATA_ZERO_PREFIX_LENGTH, (byte) 0);
System.arraycopy (tempBuffer,
currentKeyLength + i*(currentKeyLength + currentDataLength),
pageBuffer,
headerLength + currentKeyLength + DATA_ZERO_PREFIX_LENGTH + i*(keyLengthX + currentDataLength),
currentDataLength);
}
freeStart += numEntries * DATA_ZERO_PREFIX_LENGTH;
currentDataLength = dataLengthX;
dataShrinked = false;
}
void copyEntry (int offset, BtreeEntry entry) {
int keyStart = keysShrinked ? KEY_ZERO_PREFIX_LENGTH : 0;
int dataStart = dataShrinked ? DATA_ZERO_PREFIX_LENGTH : 0;
System.arraycopy(entry.key, keyStart, pageBuffer, offset, currentKeyLength);
System.arraycopy(entry.data, dataStart, pageBuffer,
offset + currentKeyLength, currentDataLength);
}
private void copyState (ShrinkablePage page) {
page.keysShrinked = keysShrinked;
page.dataShrinked = dataShrinked;
page.currentKeyLength = currentKeyLength;
page.currentDataLength = currentDataLength;
}
public void dump () {
System.out.println("# entries: " + numEntries () + " ------------------");
for (int i = 0; i < numEntries (); i++) {
boolean temp = debug;
debug = true;
getKey (i);
System.out.print(" ");
getData (i);
debug = temp;
}
System.out.println("----------------------------------");
}
public void dumpBuffer () {
System.out.print("buff: ");
for (int x = headerLength; x < freeStart; x++) {
System.out.print(pageBuffer[x] + ".");
}
System.out.println("");
}
}
|
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.