/*
* 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;
import java.io.DataInputStream;
import java.io.IOException;
/**
* This class represents a sorted set of longs. Internally it uses an array
* that can grow when necessary. A goal of this class is to avoid memory
* allocations where possible.
*/
public class Longs
{
/**
* The internal array where elements are stored.
*/
private long[] array = null;
/**
* The number of valid elements in the array.
*/
private int count = 0;
/**
* Construct a new empty set.
*/
public Longs()
{
}
/**
* 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;
}
/**
* Get the number of bytes needed to encode this value into a byte array.
* @return The number of bytes needed to encode this value into a byte array.
*/
public int encodedSize()
{
return count*8;
}
/**
* 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 = encodedSize();
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.
*/
public 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.
*/
public void addAll(Longs 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;
}
/**
* Deletes all the elements of a provided set from this set if they are
* present.
* @param that The set of elements to be deleted.
*/
public void deleteAll(Longs that)
{
int thisPos, thatPos, destPos;
for ( destPos = 0, thisPos = 0, thatPos = 0;
thisPos < count && thatPos < that.count; )
{
if ( array[thisPos] < that.array[thatPos] )
{
array[destPos++] = array[thisPos++];
}
else if ( array[thisPos] > that.array[thatPos] )
{
thatPos++;
}
else
{
thisPos++;
thatPos++;
}
}
System.arraycopy(array, thisPos, array, destPos, count - thisPos);
destPos += count - thisPos;
count = destPos;
}
/**
* Return the number of elements in the set.
* @return The number of elements in the set.
*/
public int size()
{
return count;
}
/**
* Decode a value from a data input stream.
* @param dataInputStream The data input stream to read the value from.
* @throws IOException If an I/O error occurs while reading the value.
*/
public void decode(DataInputStream dataInputStream)
throws IOException
{
int len = dataInputStream.readInt();
int count = len/8;
resize(count);
for (int i = 0; i < count; i++)
{
array[i] = dataInputStream.readLong();
}
this.count = 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;
}
}
/**
* Clears the set leaving it empty.
*/
public void clear()
{
count = 0;
}
/**
* Convert the set to a new array of longs.
* @return An array of longs.
*/
public long[] toArray()
{
long[] dst = new long[count];
System.arraycopy(array, 0, dst, 0, count);
return dst;
}
/**
* {@inheritDoc}
*/
@Override
public String toString()
{
StringBuilder b = new StringBuilder();
b.append(count);
if (count > 0)
{
b.append('[');
b.append(array[0]);
if (count > 1)
{
b.append(':');
b.append(array[count-1]);
}
b.append(']');
}
return b.toString();
}
}