/*
|
* CDDL HEADER START
|
*
|
* The contents of this file are subject to the terms of the
|
* Common Development and Distribution License, Version 1.0 only
|
* (the "License"). You may not use this file except in compliance
|
* with the License.
|
*
|
* You can obtain a copy of the license at
|
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
|
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
|
* See the License for the specific language governing permissions
|
* and limitations under the License.
|
*
|
* When distributing Covered Code, include this CDDL HEADER in each
|
* file and include the License file at
|
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
|
* add the following below this CDDL HEADER, with the fields enclosed
|
* by brackets "[]" replaced with your own identifying information:
|
* Portions Copyright [yyyy] [name of copyright owner]
|
*
|
* CDDL HEADER END
|
*
|
*
|
* Copyright 2006-2008 Sun Microsystems, Inc.
|
*/
|
package org.opends.server.backends.jeb.importLDIF;
|
|
import com.sleepycat.je.dbi.MemoryBudget;
|
import org.opends.server.util.RuntimeInformation;
|
import org.opends.server.backends.jeb.EntryID;
|
import org.opends.server.backends.jeb.JebFormat;
|
|
|
/**
|
* A import ID set backed by an array of longs.
|
*/
|
public class LongImportIDSet implements ImportIDSet {
|
|
//Indicate if a undefined import set has been written to the index DB.
|
private boolean dirty = true;
|
|
//Overhead values gleamed from JHAT.
|
private final static int LONGS_OVERHEAD;
|
private final static int LONGS_OVERHEAD_32 = 25;
|
private final static int LONGS_OVERHEAD_64 = 25;
|
|
/**
|
* The internal array where elements are stored.
|
*/
|
private long[] array = null;
|
|
|
/**
|
* The number of valid elements in the array.
|
*/
|
private int count = 0;
|
|
|
//Boolean to keep track if the instance is defined or not.
|
boolean isDefined=true;
|
|
|
//Size of the undefines.
|
private long undefinedSize = 0;
|
|
|
static {
|
if(RuntimeInformation.is64Bit()) {
|
LONGS_OVERHEAD = LONGS_OVERHEAD_64;
|
} else {
|
LONGS_OVERHEAD = LONGS_OVERHEAD_32;
|
}
|
}
|
|
/**
|
* Create an empty instance.
|
*/
|
public LongImportIDSet() {
|
}
|
|
/**
|
* Create instance and add specified entry ID to the set.
|
*
|
* @param id The entry ID.
|
*/
|
public LongImportIDSet(EntryID id) {
|
this.array = new long[1];
|
this.array[0] = id.longValue();
|
count=1;
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public void setEntryID(EntryID id) {
|
if(array == null) {
|
this.array = new long[1];
|
}
|
reset();
|
this.array[0] = id.longValue();
|
count=1;
|
}
|
|
|
/**
|
* {@inheritDoc}
|
*/
|
public void reset() {
|
count = 0;
|
isDefined = true;
|
undefinedSize = 0;
|
dirty = true;
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public void setDirty(boolean dirty) {
|
this.dirty = dirty;
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public boolean isDirty() {
|
return dirty;
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public boolean isDefined() {
|
return isDefined;
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public void setUndefined() {
|
array = null;
|
isDefined = false;
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public long getUndefinedSize() {
|
return undefinedSize;
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public int getMemorySize() {
|
if(array != null) {
|
return LONGS_OVERHEAD + MemoryBudget.byteArraySize(array.length * 8);
|
} else {
|
return LONGS_OVERHEAD;
|
}
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public void
|
merge(ImportIDSet importIDSet, int limit, boolean maintainCount) {
|
if(!isDefined()) {
|
if(maintainCount) {
|
if(importIDSet.isDefined()) {
|
undefinedSize += importIDSet.size();
|
} else {
|
undefinedSize += importIDSet.getUndefinedSize();
|
}
|
}
|
return;
|
}
|
if(isDefined() && ((count + importIDSet.size()) > limit)) {
|
isDefined = false;
|
if(maintainCount) {
|
undefinedSize = size() + importIDSet.size();
|
} else {
|
undefinedSize = Long.MAX_VALUE;
|
}
|
array = null;
|
count = 0;
|
} else {
|
addAll((LongImportIDSet) importIDSet);
|
}
|
}
|
|
|
/**
|
* {@inheritDoc}
|
*/
|
public boolean merge(byte[] DBbytes, ImportIDSet importIdSet,
|
int limit, boolean maintainCount) {
|
boolean incrLimitCount=false;
|
boolean dbUndefined = ((DBbytes[0] & 0x80) == 0x80);
|
|
if(dbUndefined) {
|
isDefined=false;
|
undefinedSize = Long.MAX_VALUE;
|
} else if(!importIdSet.isDefined()) {
|
isDefined=false;
|
incrLimitCount=true;
|
undefinedSize = Long.MAX_VALUE;
|
} else {
|
array = JebFormat.entryIDListFromDatabase(DBbytes);
|
if(array.length + importIdSet.size() > limit) {
|
isDefined=false;
|
incrLimitCount=true;
|
importIdSet.setUndefined();
|
undefinedSize = Long.MAX_VALUE;
|
} else {
|
count = array.length;
|
addAll((LongImportIDSet) importIdSet);
|
}
|
}
|
return incrLimitCount;
|
}
|
|
|
/**
|
* {@inheritDoc}
|
*/
|
public void addEntryID(EntryID entryID, int limit, boolean maintainCount) {
|
if(!isDefined()) {
|
if(maintainCount) {
|
undefinedSize++;
|
}
|
return;
|
}
|
if(isDefined() && ((count + 1) > limit)) {
|
isDefined = false;
|
array = null;
|
if(maintainCount) {
|
undefinedSize = count + 1;
|
} else {
|
undefinedSize = Long.MAX_VALUE;
|
}
|
count = 0;
|
} else {
|
add(entryID.longValue());
|
}
|
}
|
|
|
/**
|
* {@inheritDoc}
|
*/
|
public byte[] toDatabase() {
|
if (isDefined()) {
|
return encode(null);
|
} else {
|
return JebFormat.entryIDUndefinedSizeToDatabase(undefinedSize);
|
}
|
}
|
|
/**
|
* Decodes a set from a byte array.
|
* @param bytes The encoded value.
|
*/
|
void decode(byte[] bytes)
|
{
|
if (bytes == null)
|
{
|
count = 0;
|
return;
|
}
|
|
int count = bytes.length / 8;
|
resize(count);
|
|
for (int pos = 0, i = 0; i < count; i++)
|
{
|
long v = 0;
|
v |= (bytes[pos++] & 0xFFL) << 56;
|
v |= (bytes[pos++] & 0xFFL) << 48;
|
v |= (bytes[pos++] & 0xFFL) << 40;
|
v |= (bytes[pos++] & 0xFFL) << 32;
|
v |= (bytes[pos++] & 0xFFL) << 24;
|
v |= (bytes[pos++] & 0xFFL) << 16;
|
v |= (bytes[pos++] & 0xFFL) << 8;
|
v |= (bytes[pos++] & 0xFFL);
|
array[i] = v;
|
}
|
this.count = count;
|
}
|
|
|
/**
|
* Encode this value into a byte array.
|
* @param bytes The array into which the value will be encoded. If the
|
* provided array is null, or is not big enough, a new array will be
|
* allocated.
|
* @return The encoded array. If the provided array was bigger than needed
|
* to encode the value then the provided array is returned and the number
|
* of bytes of useful data is given by the encodedSize method.
|
*/
|
byte[] encode(byte[] bytes) {
|
int encodedSize = count * 8;
|
if (bytes == null || bytes.length < encodedSize)
|
{
|
bytes = new byte[encodedSize];
|
}
|
|
for (int pos = 0, i = 0; i < count; i++)
|
{
|
long v = array[i];
|
bytes[pos++] = (byte) ((v >>> 56) & 0xFF);
|
bytes[pos++] = (byte) ((v >>> 48) & 0xFF);
|
bytes[pos++] = (byte) ((v >>> 40) & 0xFF);
|
bytes[pos++] = (byte) ((v >>> 32) & 0xFF);
|
bytes[pos++] = (byte) ((v >>> 24) & 0xFF);
|
bytes[pos++] = (byte) ((v >>> 16) & 0xFF);
|
bytes[pos++] = (byte) ((v >>> 8) & 0xFF);
|
bytes[pos++] = (byte) (v & 0xFF);
|
}
|
|
return bytes;
|
}
|
|
|
|
/**
|
* This is very much like Arrays.binarySearch except that it searches only
|
* an initial portion of the provided array.
|
* @param a The array to be searched.
|
* @param count The number of initial elements in the array to be searched.
|
* @param key The element to search for.
|
* @return See Arrays.binarySearch.
|
*/
|
private static int binarySearch(long[] a, int count, long key) {
|
int low = 0;
|
int high = count-1;
|
|
while (low <= high)
|
{
|
int mid = (low + high) >> 1;
|
long midVal = a[mid];
|
|
if (midVal < key)
|
low = mid + 1;
|
else if (midVal > key)
|
high = mid - 1;
|
else
|
return mid; // key found
|
}
|
return -(low + 1); // key not found.
|
}
|
|
|
|
/**
|
* Add a new value to the set.
|
* @param v The value to be added.
|
* @return true if the value was added, false if it was already present
|
* in the set.
|
*/
|
private boolean add(long v) {
|
resize(count+1);
|
|
if (count == 0 || v > array[count-1])
|
{
|
array[count++] = v;
|
return true;
|
}
|
|
int pos = binarySearch(array, count, v);
|
if (pos >=0)
|
{
|
return false;
|
}
|
|
// For a negative return value r, the index -(r+1) gives the array
|
// index at which the specified value can be inserted to maintain
|
// the sorted order of the array.
|
pos = -(pos+1);
|
|
System.arraycopy(array, pos, array, pos+1, count-pos);
|
array[pos] = v;
|
count++;
|
return true;
|
}
|
|
/**
|
* Adds all the elements of a provided set to this set if they are not
|
* already present.
|
* @param that The set of elements to be added.
|
*/
|
private void addAll(LongImportIDSet that) {
|
resize(this.count+that.count);
|
|
if (that.count == 0)
|
{
|
return;
|
}
|
|
// Optimize for the case where the two sets are sure to have no overlap.
|
if (this.count == 0 || that.array[0] > this.array[this.count-1])
|
{
|
System.arraycopy(that.array, 0, this.array, this.count, that.count);
|
count += that.count;
|
return;
|
}
|
|
if (this.array[0] > that.array[that.count-1])
|
{
|
System.arraycopy(this.array, 0, this.array, that.count, this.count);
|
System.arraycopy(that.array, 0, this.array, 0, that.count);
|
count += that.count;
|
return;
|
}
|
|
int destPos = binarySearch(this.array, this.count, that.array[0]);
|
if (destPos < 0)
|
{
|
destPos = -(destPos+1);
|
}
|
|
// Make space for the copy.
|
int aCount = this.count - destPos;
|
int aPos = destPos + that.count;
|
int aEnd = aPos + aCount;
|
System.arraycopy(this.array, destPos, this.array, aPos, aCount);
|
|
// Optimize for the case where there is no overlap.
|
if (this.array[aPos] > that.array[that.count-1])
|
{
|
System.arraycopy(that.array, 0, this.array, destPos, that.count);
|
count += that.count;
|
return;
|
}
|
|
int bPos;
|
for ( bPos = 0; aPos < aEnd && bPos < that.count; )
|
{
|
if ( this.array[aPos] < that.array[bPos] )
|
{
|
this.array[destPos++] = this.array[aPos++];
|
}
|
else if ( this.array[aPos] > that.array[bPos] )
|
{
|
this.array[destPos++] = that.array[bPos++];
|
}
|
else
|
{
|
this.array[destPos++] = this.array[aPos++];
|
bPos++;
|
}
|
}
|
|
// Copy any remainder.
|
int aRemain = aEnd - aPos;
|
if (aRemain > 0)
|
{
|
System.arraycopy(this.array, aPos, this.array, destPos, aRemain);
|
destPos += aRemain;
|
}
|
|
int bRemain = that.count - bPos;
|
if (bRemain > 0)
|
{
|
System.arraycopy(that.array, bPos, this.array, destPos, bRemain);
|
destPos += bRemain;
|
}
|
|
count = destPos;
|
}
|
|
|
|
/**
|
* {@inheritDoc}
|
*/
|
public int size() {
|
return count;
|
}
|
|
|
/**
|
* Ensures capacity of the internal array for a given number of elements.
|
* @param size The internal array will be guaranteed to be at least this
|
* size.
|
*/
|
private void resize(int size) {
|
if (array == null)
|
{
|
array = new long[size];
|
}
|
else if (array.length < size)
|
{
|
// Expand the size of the array in powers of two.
|
int newSize = array.length == 0 ? 1 : array.length;
|
do
|
{
|
newSize *= 2;
|
} while (newSize < size);
|
|
long[] newBytes = new long[newSize];
|
System.arraycopy(array, 0, newBytes, 0, count);
|
array = newBytes;
|
}
|
}
|
|
}
|