5195N/A/*
5195N/A * CDDL HEADER START
5195N/A *
5195N/A * The contents of this file are subject to the terms of the
5195N/A * Common Development and Distribution License, Version 1.0 only
5195N/A * (the "License"). You may not use this file except in compliance
5195N/A * with the License.
5195N/A *
5195N/A * You can obtain a copy of the license at
5195N/A * trunk/opends/resource/legal-notices/OpenDS.LICENSE
5195N/A * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
5195N/A * See the License for the specific language governing permissions
5195N/A * and limitations under the License.
5195N/A *
5195N/A * When distributing Covered Code, include this CDDL HEADER in each
5195N/A * file and include the License file at
5195N/A * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
5195N/A * add the following below this CDDL HEADER, with the fields enclosed
5195N/A * by brackets "[]" replaced with your own identifying information:
5195N/A * Portions Copyright [yyyy] [name of copyright owner]
5195N/A *
5195N/A * CDDL HEADER END
5195N/A *
5195N/A *
5195N/A * Copyright 2009-2010 Sun Microsystems, Inc.
6353N/A * Portions Copyright 2013 ForgeRock AS.
5195N/A */
5195N/A
5195N/A
5195N/Apackage org.opends.server.backends.jeb.importLDIF;
5195N/A
5195N/Aimport java.io.DataOutputStream;
5195N/Aimport java.io.IOException;
5195N/Aimport java.io.ByteArrayOutputStream;
5195N/Aimport java.nio.ByteBuffer;
5195N/A
5195N/Aimport org.opends.server.backends.jeb.*;
5195N/Aimport com.sleepycat.util.PackedInteger;
5195N/A
5195N/A
5195N/A/**
5195N/A * This class represents a index buffer used to store the keys and entry IDs
5195N/A * processed from the LDIF file during phase one of an import, or rebuild index
5195N/A * process. Each key and ID is stored in a record in the buffer.
5195N/A *
5195N/A * The records in the buffer are eventually sorted, based on the key, when the
5195N/A * maximum size of the buffer is reached and no more records will fit into the
5195N/A * buffer. The buffer is the scheduled to be flushed to an indexes scratch file
5195N/A * and then re-cycled by the import, or rebuild-index process.
5195N/A *
5195N/A * The records are packed as much as possible, to optimize the buffer space.
5195N/A * This class is not thread safe.
5195N/A *
5195N/A */
5195N/Apublic final class IndexOutputBuffer implements Comparable<IndexOutputBuffer> {
5195N/A
5195N/A /**
5195N/A * Enumeration used when sorting a buffer.
5195N/A */
5195N/A private enum CompareOp {
5195N/A LT, GT, LE, GE, EQ
5195N/A }
5195N/A
5195N/A //The record over head.
5195N/A private static final int REC_OVERHEAD = 5;
5195N/A
6353N/A //The size of int.
6353N/A private static final int INT_SIZE = 4;
6353N/A
5195N/A //Buffer records are either insert records or delete records.
5195N/A private static final byte DEL = 0, INS = 1;
5195N/A
5195N/A //The size of a buffer.
5195N/A private final int size;
5195N/A
5195N/A //Byte array holding the actual buffer data.
5195N/A private final byte buffer[];
5195N/A
5195N/A //id is used to break a tie (keys equal) when the buffers are being merged
5195N/A //for writing to the index scratch file.
5195N/A private long id;
5195N/A
5195N/A //Temporary buffer used to store integer values.
6353N/A private final byte[] intBytes = new byte[INT_SIZE];
5195N/A
5195N/A /*
5195N/A keyOffset - offSet where next key is written
5195N/A recordOffset- offSet where next value record is written
5195N/A bytesLeft - amount of bytes left in the buffer
5195N/A */
5195N/A private int keyOffset =0, recordOffset=0, bytesLeft = 0;
5195N/A
5195N/A //keys - number of keys in the buffer
5195N/A //position - used to iterate over the buffer when writing to a scratch file.
5195N/A private int keys = 0, position = 0;
5195N/A
5195N/A //The comparator to use sort the keys.
5195N/A private ComparatorBuffer<byte[]> comparator;
5195N/A
5195N/A //This is used to make sure that an instance of this class is put on the
5195N/A //correct scratch file writer work queue for processing.
5195N/A private Importer.IndexKey indexKey;
5195N/A
5195N/A //Initial capacity of re-usable buffer used in key compares.
5195N/A private static final int CAP = 32;
5195N/A
5195N/A //This buffer is reused during key compares. It's main purpose is to keep
5195N/A //memory footprint as small as possible.
5195N/A private ByteBuffer keyBuffer = ByteBuffer.allocate(CAP);
5195N/A
5195N/A //Set to {@code true} if the buffer should not be recycled. Used when the
5195N/A //importer/rebuild index process is doing phase one cleanup and flushing
5195N/A //buffers not completed.
5195N/A private boolean discard = false;
5195N/A
5195N/A
5195N/A /**
5195N/A * Create an instance of a IndexBuffer using the specified size.
5195N/A *
5195N/A * @param size The size of the underlying byte array.
5195N/A */
5195N/A public IndexOutputBuffer(int size) {
5195N/A this.size = size;
5195N/A this.buffer = new byte[size];
5195N/A this.bytesLeft = size;
5195N/A this.recordOffset = size - 1;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Reset an IndexBuffer so it can be re-cycled.
5195N/A */
5195N/A public void reset() {
5195N/A bytesLeft = size;
5195N/A keyOffset = 0;
5195N/A recordOffset = size - 1;
5195N/A keys = 0;
5195N/A position = 0;
5195N/A comparator = null;
5195N/A indexKey = null;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Set the ID of a buffer to the specified value.
5195N/A *
5195N/A * @param id The value to set the ID to.
5195N/A */
5195N/A public void setID(long id)
5195N/A {
5195N/A this.id = id;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Return the ID of a buffer.
5195N/A *
5195N/A * @return The value of a buffer's ID.
5195N/A */
5195N/A private long getBufferID()
5195N/A {
5195N/A return this.id;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Determines if a buffer is a poison buffer. A poison buffer is used to
5195N/A * shutdown work queues when import/rebuild index phase one is completed.
5195N/A * A poison buffer has a 0 size.
5195N/A *
5195N/A * @return {@code true} if a buffer is a poison buffer, or {@code false}
5195N/A * otherwise.
5195N/A */
5195N/A public boolean isPoison()
5195N/A {
5195N/A return (size == 0);
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Determines of a buffer should be re-cycled.
5195N/A *
5195N/A * @return {@code true} if buffer should be recycled, or {@code false} if it
5195N/A * should not.
5195N/A */
5195N/A public boolean isDiscard()
5195N/A {
5195N/A return discard;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Set the discard flag to {@code true}.
5195N/A */
5195N/A public void setDiscard()
5195N/A {
5195N/A discard = true;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Returns {@code true} if there is enough space available to write the
5195N/A * specified byte array in the buffer. It returns {@code false} otherwise.
5195N/A *
5195N/A * @param kBytes The byte array to check space against.
5195N/A * @param id The id value to check space against.
5195N/A * @return {@code true} if there is space to write the byte array in a
5195N/A * buffer, or {@code false} otherwise.
5195N/A */
5195N/A public boolean isSpaceAvailable(byte[] kBytes, long id) {
6353N/A return (getRecordSize(kBytes.length, id) + INT_SIZE) < bytesLeft;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Set the comparator to be used in the buffer processing to the specified
5195N/A * comparator.
5195N/A *
5195N/A * @param comparator The comparator to set the buffer's comparator to.
5195N/A */
5195N/A public void setComparator(ComparatorBuffer<byte[]> comparator)
5195N/A {
5195N/A this.comparator = comparator;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Return a buffer's current position value.
5195N/A *
5195N/A * @return The buffer's current position value.
5195N/A */
5195N/A public int getPosition()
5195N/A {
5195N/A return position;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Set a buffer's position value to the specified position.
5195N/A *
5195N/A * @param position The value to set the position to.
5195N/A */
5195N/A public void setPosition(int position)
5195N/A {
5195N/A this.position = position;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Sort the buffer.
5195N/A */
5195N/A public void sort() {
5195N/A sort(0, keys);
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Add the specified key byte array and EntryID to the buffer.
5195N/A *
5195N/A * @param keyBytes The key byte array.
5195N/A * @param entryID The EntryID.
5195N/A * @param indexID The index ID the record belongs.
5195N/A * @param insert <CODE>True</CODE> if key is an insert, false otherwise.
5195N/A */
5195N/A
5195N/A public void add(byte[] keyBytes, EntryID entryID, int indexID,
5195N/A boolean insert) {
5195N/A recordOffset = addRecord(keyBytes, entryID.longValue(), indexID, insert);
6353N/A System.arraycopy(getIntBytes(recordOffset), 0, buffer, keyOffset, INT_SIZE);
6353N/A keyOffset += INT_SIZE;
5195N/A bytesLeft = recordOffset - keyOffset;
5195N/A keys++;
5195N/A }
5195N/A
5195N/A
5195N/A private int addRecord(byte[]key, long id, int indexID, boolean insert)
5195N/A {
5195N/A int retOffset = recordOffset - getRecordSize(key.length, id);
5195N/A int offSet = retOffset;
6353N/A
6353N/A buffer[offSet++] = insert ? INS : DEL;
6353N/A System.arraycopy(getIntBytes(indexID), 0, buffer, offSet, INT_SIZE);
6353N/A offSet += INT_SIZE;
5195N/A offSet = PackedInteger.writeLong(buffer, offSet, id);
5195N/A offSet = PackedInteger.writeInt(buffer, offSet, key.length);
5195N/A System.arraycopy(key, 0, buffer, offSet, key.length);
5195N/A return retOffset;
5195N/A }
5195N/A
5195N/A
6353N/A /**
6353N/A * Computes the full size of the record.
6353N/A *
6353N/A * @param keyLen The length of the key of index
6353N/A * @param id The entry id
6353N/A * @return The size that such record would take in the IndexOutputBuffer
6353N/A */
6353N/A public static int getRequiredSize(int keyLen, long id)
6353N/A {
6353N/A return PackedInteger.getWriteIntLength(keyLen) + keyLen +
6353N/A PackedInteger.getWriteLongLength(id) + REC_OVERHEAD + INT_SIZE;
6353N/A }
6353N/A
5195N/A private int getRecordSize(int keyLen, long id)
5195N/A {
5195N/A return PackedInteger.getWriteIntLength(keyLen) + keyLen +
5195N/A PackedInteger.getWriteLongLength(id) + REC_OVERHEAD;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Write record at specified index to the specified output stream. Used when
5195N/A * when writing the index scratch files.
5195N/A
5195N/A * @param stream The stream to write the record at the index to.
5195N/A * @param index The index of the record to write.
5195N/A */
5195N/A public void writeID(ByteArrayOutputStream stream, int index)
5195N/A {
6353N/A int offSet = getIntegerValue(index * INT_SIZE);
5195N/A int len = PackedInteger.getReadLongLength(buffer, offSet + REC_OVERHEAD);
5195N/A stream.write(buffer, offSet + REC_OVERHEAD, len);
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Return {@code true} if the record specified by the index is an insert
5195N/A * record, or {@code false} if it a delete record.
5195N/A *
5195N/A * @param index The index of the record.
5195N/A *
5195N/A * @return {@code true} if the record is an insert record, or {@code false}
5195N/A * if it is a delete record.
5195N/A */
5195N/A public boolean isInsert(int index)
5195N/A {
6353N/A int recOffset = getIntegerValue(index * INT_SIZE);
6353N/A return buffer[recOffset] != DEL;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Return the size of the key part of the record.
5195N/A *
5195N/A * @return The size of the key part of the record.
5195N/A */
5195N/A public int getKeySize()
5195N/A {
6353N/A int offSet = getIntegerValue(position * INT_SIZE) + REC_OVERHEAD;
5195N/A offSet += PackedInteger.getReadIntLength(buffer, offSet);
5195N/A return PackedInteger.readInt(buffer, offSet);
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Return the key value part of a record indicated by the current buffer
5195N/A * position.
5195N/A *
5195N/A * @return byte array containing the key value.
5195N/A */
5195N/A public byte[] getKey()
5195N/A {
5195N/A return getKey(position);
5195N/A }
5195N/A
5195N/A //Used to minimized memory usage when comparing keys.
5195N/A private ByteBuffer getKeyBuf(int x)
5195N/A {
5195N/A keyBuffer.clear();
6353N/A int offSet = getIntegerValue(x * INT_SIZE) + REC_OVERHEAD;
5195N/A offSet += PackedInteger.getReadIntLength(buffer, offSet);
5195N/A int keyLen = PackedInteger.readInt(buffer, offSet);
5195N/A offSet += PackedInteger.getReadIntLength(buffer, offSet);
5195N/A //Re-allocate if the key is bigger than the capacity.
5195N/A if(keyLen > keyBuffer.capacity())
5195N/A {
5195N/A keyBuffer = ByteBuffer.allocate(keyLen);
5195N/A }
5195N/A keyBuffer.put(buffer, offSet, keyLen);
5195N/A keyBuffer.flip();
5195N/A return keyBuffer;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Return the key value part of a record specified by the index.
5195N/A *
5195N/A * @param x index to return.
5195N/A * @return byte array containing the key value.
5195N/A */
5195N/A private byte[] getKey(int x)
5195N/A {
6353N/A int offSet = getIntegerValue(x * INT_SIZE) + REC_OVERHEAD;
5195N/A offSet += PackedInteger.getReadIntLength(buffer, offSet);
5195N/A int keyLen = PackedInteger.readInt(buffer, offSet);
5195N/A offSet += PackedInteger.getReadIntLength(buffer, offSet);
5195N/A byte[] key = new byte[keyLen];
5195N/A System.arraycopy(buffer, offSet, key, 0, keyLen);
5195N/A return key;
5195N/A }
5195N/A
5195N/A
5195N/A private int getIndexID(int x)
5195N/A {
6353N/A return getIntegerValue(getIntegerValue(x * INT_SIZE) + 1);
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Return index id associated with the current position's record.
5195N/A *
5195N/A * @return The index id.
5195N/A */
5195N/A public int getIndexID()
5195N/A {
6353N/A return getIntegerValue(getIntegerValue(position * INT_SIZE) + 1);
5195N/A }
5195N/A
5195N/A
5195N/A private boolean is(int x, int y, CompareOp op)
5195N/A {
6353N/A int xoffSet = getIntegerValue(x * INT_SIZE);
5195N/A int xIndexID = getIntegerValue(xoffSet + 1);
5195N/A xoffSet += REC_OVERHEAD;
5195N/A xoffSet += PackedInteger.getReadIntLength(buffer, xoffSet);
5195N/A int xKeyLen = PackedInteger.readInt(buffer, xoffSet);
5195N/A int xKey = PackedInteger.getReadIntLength(buffer, xoffSet) + xoffSet;
6353N/A int yoffSet = getIntegerValue(y * INT_SIZE);
5195N/A int yIndexID = getIntegerValue(yoffSet + 1);
5195N/A yoffSet += REC_OVERHEAD;
5195N/A yoffSet += PackedInteger.getReadIntLength(buffer, yoffSet);
5195N/A int yKeyLen = PackedInteger.readInt(buffer, yoffSet);
5195N/A int yKey = PackedInteger.getReadIntLength(buffer, yoffSet) + yoffSet;
5195N/A return evaluateReturnCode(comparator.compare(buffer, xKey, xKeyLen,
5195N/A xIndexID, yKey, yKeyLen, yIndexID), op);
5195N/A }
5195N/A
5195N/A
5195N/A private boolean is(int x, byte[] yKey, CompareOp op, int yIndexID)
5195N/A {
6353N/A int xoffSet = getIntegerValue(x * INT_SIZE);
5195N/A int xIndexID = getIntegerValue(xoffSet + 1);
5195N/A xoffSet += REC_OVERHEAD;
5195N/A xoffSet += PackedInteger.getReadIntLength(buffer, xoffSet);
5195N/A int xKeyLen = PackedInteger.readInt(buffer, xoffSet);
5195N/A int xKey = PackedInteger.getReadIntLength(buffer, xoffSet) + xoffSet;
5195N/A return evaluateReturnCode(comparator.compare(buffer, xKey, xKeyLen,
5195N/A xIndexID, yKey, yKey.length, yIndexID), op);
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Compare the byte array at the current position with the specified one and
5195N/A * using the specified index id. It will return {@code true} if the byte
6353N/A * array at the current position is equal to the specified byte array as
5195N/A * determined by the comparator and the index ID is is equal. It will
5195N/A * return {@code false} otherwise.
5195N/A *
5195N/A * @param b The byte array to compare.
5195N/A * @param bIndexID The index key.
5195N/A * @return <CODE>True</CODE> if the byte arrays are equal.
5195N/A */
5195N/A public boolean compare(byte[]b, int bIndexID)
5195N/A {
6353N/A int offset = getIntegerValue(position * INT_SIZE);
5195N/A int indexID = getIntegerValue(offset + 1);
5195N/A offset += REC_OVERHEAD;
5195N/A offset += PackedInteger.getReadIntLength(buffer, offset);
5195N/A int keyLen = PackedInteger.readInt(buffer, offset);
5195N/A int key = PackedInteger.getReadIntLength(buffer, offset) + offset;
5195N/A if( comparator.compare(buffer, key, keyLen, b, b.length) == 0)
5195N/A {
5195N/A if(indexID == bIndexID)
5195N/A {
5195N/A return true;
5195N/A }
5195N/A }
5195N/A return false;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Compare current IndexBuffer to the specified index buffer using both the
5195N/A * comparator and index ID of both buffers.
5195N/A *
5195N/A * The key at the value of position in both buffers are used in the compare.
5195N/A *
5195N/A * @param b The IndexBuffer to compare to.
5195N/A * @return 0 if the buffers are equal, -1 if the current buffer is less
5195N/A * than the specified buffer, or 1 if it is greater.
5195N/A */
5195N/A public int compareTo(IndexOutputBuffer b)
5195N/A {
5195N/A ByteBuffer keyBuf = b.getKeyBuf(b.position);
6353N/A int offset = getIntegerValue(position * INT_SIZE);
5195N/A int indexID = getIntegerValue(offset + 1);
5195N/A offset += REC_OVERHEAD;
5195N/A offset += PackedInteger.getReadIntLength(buffer, offset);
5195N/A int keyLen = PackedInteger.readInt(buffer, offset);
5195N/A int key = PackedInteger.getReadIntLength(buffer, offset) + offset;
5195N/A int returnCode = comparator.compare(buffer, key, keyLen, keyBuf.array(),
5195N/A keyBuf.limit());
5195N/A if(returnCode == 0)
5195N/A {
5195N/A int bIndexID = b.getIndexID();
5195N/A if(indexID == bIndexID)
5195N/A {
5195N/A long otherBufferID = b.getBufferID();
5195N/A //This is tested in a tree set remove when a buffer is removed from
5195N/A //the tree set.
5195N/A if(this.id == otherBufferID)
5195N/A {
5195N/A returnCode = 0;
5195N/A }
5195N/A else if(this.id < otherBufferID)
5195N/A {
5195N/A returnCode = -1;
5195N/A }
5195N/A else
5195N/A {
5195N/A returnCode = 1;
5195N/A }
5195N/A }
5195N/A else if(indexID < bIndexID)
5195N/A {
5195N/A returnCode = -1;
5195N/A }
5195N/A else
5195N/A {
5195N/A returnCode = 1;
5195N/A }
5195N/A }
5195N/A return returnCode;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Write a record to specified output stream using the record pointed to by
5195N/A * the current position and the specified byte stream of ids.
5195N/A *
5195N/A * @param dataStream The data output stream to write to.
5195N/A *
5195N/A * @throws IOException If an I/O error occurs writing the record.
5195N/A */
5195N/A public void writeKey(DataOutputStream dataStream) throws IOException
5195N/A {
6353N/A int offSet = getIntegerValue(position * INT_SIZE) + REC_OVERHEAD;
5195N/A offSet += PackedInteger.getReadIntLength(buffer, offSet);
5195N/A int keyLen = PackedInteger.readInt(buffer, offSet);
5195N/A offSet += PackedInteger.getReadIntLength(buffer, offSet);
5195N/A dataStream.write(buffer, offSet, keyLen);
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Compare the byte array at the current position with the byte array at the
5195N/A * specified index.
5195N/A *
5195N/A * @param i The index pointing to the byte array to compare.
5195N/A * @return {@code true} if the byte arrays are equal, or {@code false}
5195N/A * otherwise.
5195N/A */
5195N/A public boolean compare(int i)
5195N/A {
5195N/A return is(i, position, CompareOp.EQ);
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Return the current number of keys.
5195N/A *
5195N/A * @return The number of keys currently in an index buffer.
5195N/A */
5195N/A public int getNumberKeys()
5195N/A {
5195N/A return keys;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Return {@code true} if the buffer has more data to process, or
5195N/A * {@code false} otherwise. Used when iterating over the buffer writing the
5195N/A * scratch index file.
5195N/A *
5195N/A * @return {@code true} if a buffer has more data to process, or
5195N/A * {@code false} otherwise.
5195N/A */
5195N/A public boolean hasMoreData()
5195N/A {
5195N/A return (position + 1) < keys;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Advance the position pointer to the next record in the buffer. Used when
5195N/A * iterating over the buffer examining keys.
5195N/A */
5195N/A public void getNextRecord()
5195N/A {
5195N/A position++;
5195N/A }
5195N/A
5195N/A
5195N/A private byte[] getIntBytes(int val)
5195N/A {
5195N/A for (int i = 3; i >= 0; i--) {
5195N/A intBytes[i] = (byte) (val & 0xff);
5195N/A val >>>= 8;
5195N/A }
5195N/A return intBytes;
5195N/A }
5195N/A
5195N/A
5195N/A private int getIntegerValue(int index)
5195N/A {
5195N/A int answer = 0;
6353N/A for (int i = 0; i < INT_SIZE; i++) {
5195N/A byte b = buffer[index + i];
5195N/A answer <<= 8;
5195N/A answer |= (b & 0xff);
5195N/A }
5195N/A return answer;
5195N/A }
5195N/A
5195N/A
5195N/A private int med3(int a, int b, int c)
5195N/A {
5195N/A return (is(a,b, CompareOp.LT) ?
5195N/A (is(b,c,CompareOp.LT) ? b : is(a,c,CompareOp.LT) ? c : a) :
5195N/A (is(b,c,CompareOp.GT) ? b :is(a,c,CompareOp.GT) ? c : a));
5195N/A }
5195N/A
5195N/A
5195N/A private void sort(int off, int len)
5195N/A {
5195N/A if (len < 7) {
5195N/A for (int i=off; i<len+off; i++)
5195N/A for (int j=i; j>off && is(j-1, j, CompareOp.GT); j--)
5195N/A swap(j, j-1);
5195N/A return;
5195N/A }
5195N/A
5195N/A int m = off + (len >> 1);
5195N/A if (len > 7) {
5195N/A int l = off;
5195N/A int n = off + len - 1;
5195N/A if (len > 40) {
5195N/A int s = len/8;
5195N/A l = med3(l, l+s, l+2*s);
5195N/A m = med3(m-s, m, m+s);
5195N/A n = med3(n-2*s, n-s, n);
5195N/A }
5195N/A m = med3(l, m, n);
5195N/A }
5195N/A
5195N/A byte[] mKey = getKey(m);
5195N/A int mIndexID = getIndexID(m);
5195N/A
5195N/A int a = off, b = a, c = off + len - 1, d = c;
5195N/A while(true)
5195N/A {
5195N/A while ((b <= c) && is(b, mKey, CompareOp.LE, mIndexID))
5195N/A {
5195N/A if (is(b, mKey, CompareOp.EQ, mIndexID))
5195N/A swap(a++, b);
5195N/A b++;
5195N/A }
5195N/A while (c >= b && is(c, mKey, CompareOp.GE, mIndexID))
5195N/A {
5195N/A if (is(c, mKey, CompareOp.EQ, mIndexID))
5195N/A swap(c, d--);
5195N/A c--;
5195N/A }
5195N/A if (b > c)
5195N/A break;
5195N/A swap(b++, c--);
5195N/A }
5195N/A
5195N/A // Swap partition elements back to middle
5195N/A int s, n = off + len;
5195N/A s = Math.min(a-off, b-a );
5195N/A vectorSwap(off, b-s, s);
5195N/A s = Math.min(d-c, n-d-1);
5195N/A vectorSwap(b, n-s, s);
5195N/A
5195N/A // Recursively sort non-partition-elements
5195N/A if ((s = b-a) > 1)
5195N/A sort(off, s);
5195N/A if ((s = d-c) > 1)
5195N/A sort(n-s, s);
5195N/A }
5195N/A
5195N/A
5195N/A private void swap(int a, int b)
5195N/A {
6353N/A int aOffset = a * INT_SIZE;
6353N/A int bOffset = b * INT_SIZE;
5195N/A int bVal = getIntegerValue(bOffset);
6353N/A System.arraycopy(buffer, aOffset, buffer, bOffset, INT_SIZE);
6353N/A System.arraycopy(getIntBytes(bVal), 0, buffer, aOffset, INT_SIZE);
5195N/A }
5195N/A
5195N/A
5195N/A private void vectorSwap(int a, int b, int n)
5195N/A {
5195N/A for (int i=0; i<n; i++, a++, b++)
5195N/A swap(a, b);
5195N/A }
5195N/A
5195N/A
5195N/A private boolean evaluateReturnCode(int rc, CompareOp op)
5195N/A {
5195N/A boolean returnCode = false;
5195N/A switch(op) {
5195N/A case LT:
5195N/A returnCode = rc < 0;
5195N/A break;
5195N/A case GT:
5195N/A returnCode = rc > 0;
5195N/A break;
5195N/A case LE:
5195N/A returnCode = rc <= 0;
5195N/A break;
5195N/A case GE:
5195N/A returnCode = rc >= 0;
5195N/A break;
5195N/A case EQ:
5195N/A returnCode = rc == 0;
5195N/A break;
5195N/A }
5195N/A return returnCode;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Interface that defines two methods used to compare keys used in this
5195N/A * class. The Comparator interface cannot be used in this class, so this
5195N/A * special one is used that knows about the special properties of this class.
5195N/A *
5195N/A * @param <T> object to use in the compare
5195N/A */
5195N/A public static interface ComparatorBuffer<T> {
5195N/A
5195N/A
5195N/A /**
5195N/A * Compare two offsets in an object, usually a byte array.
5195N/A *
5195N/A * @param o The object.
5195N/A * @param offset The first offset.
5195N/A * @param length The first length.
5195N/A * @param indexID The first index id.
5195N/A * @param otherOffset The second offset.
5195N/A * @param otherLength The second length.
5195N/A * @param otherIndexID The second index id.
5195N/A * @return a negative integer, zero, or a positive integer as the first
5195N/A * offset value is less than, equal to, or greater than the second.
5195N/A */
5195N/A int compare(T o, int offset, int length, int indexID, int otherOffset,
5195N/A int otherLength, int otherIndexID);
5195N/A
5195N/A
5195N/A /**
5195N/A * Compare an offset in an object with the specified object.
5195N/A *
5195N/A * @param o The first object.
5195N/A * @param offset The first offset.
5195N/A * @param length The first length.
5195N/A * @param indexID The first index id.
5195N/A * @param other The second object.
5195N/A * @param otherLength The length of the second object.
5195N/A * @param otherIndexID The second index id.
5195N/A * @return a negative integer, zero, or a positive integer as the first
5195N/A * offset value is less than, equal to, or greater than the second
5195N/A * object.
5195N/A */
5195N/A int compare(T o, int offset, int length, int indexID, T other,
5195N/A int otherLength, int otherIndexID);
5195N/A
5195N/A
5195N/A /**
5195N/A * Compare an offset in an object with the specified object.
5195N/A *
5195N/A * @param o The first object.
5195N/A * @param offset The first offset.
5195N/A * @param length The first length.
5195N/A * @param other The second object.
5195N/A * @param otherLen The length of the second object.
5195N/A * @return a negative integer, zero, or a positive integer as the first
5195N/A * offset value is less than, equal to, or greater than the second
5195N/A * object.
5195N/A */
5195N/A int compare(T o, int offset, int length, T other,
5195N/A int otherLen);
5195N/A
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Implementation of ComparatorBuffer interface. Used to compare keys when
5195N/A * they are non-DN indexes.
5195N/A */
5195N/A public static
5195N/A class IndexComparator implements IndexOutputBuffer.ComparatorBuffer<byte[]>
5195N/A {
5195N/A
5195N/A /**
5195N/A * Compare two offsets in an byte array using the index compare
6353N/A * algorithm. The specified index ID is used in the comparison if the
5195N/A * byte arrays are equal.
5195N/A *
5195N/A * @param b The byte array.
5195N/A * @param offset The first offset.
5195N/A * @param length The first length.
5195N/A * @param indexID The first index id.
5195N/A * @param otherOffset The second offset.
5195N/A * @param otherLength The second length.
5195N/A * @param otherIndexID The second index id.
5195N/A * @return a negative integer, zero, or a positive integer as the first
5195N/A * offset value is less than, equal to, or greater than the second.
5195N/A */
5195N/A public int compare(byte[] b, int offset, int length, int indexID,
5195N/A int otherOffset, int otherLength, int otherIndexID)
5195N/A {
5195N/A for(int i = 0; i < length && i < otherLength; i++)
5195N/A {
5195N/A if(b[offset + i] > b[otherOffset + i])
5195N/A {
5195N/A return 1;
5195N/A }
5195N/A else if (b[offset + i] < b[otherOffset + i])
5195N/A {
5195N/A return -1;
5195N/A }
5195N/A }
5195N/A //The arrays are equal, make sure they are in the same index since
5195N/A //multiple suffixes might have the same key.
5195N/A if(length == otherLength)
5195N/A {
5195N/A if(indexID == otherIndexID)
5195N/A {
5195N/A return 0;
5195N/A }
5195N/A else if(indexID > otherIndexID)
5195N/A {
5195N/A return 1;
5195N/A }
5195N/A else
5195N/A {
5195N/A return -1;
5195N/A }
5195N/A }
5195N/A if (length > otherLength)
5195N/A {
5195N/A return 1;
5195N/A }
5195N/A else
5195N/A {
5195N/A return -1;
5195N/A }
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Compare an offset in an byte array with the specified byte array,
5195N/A * using the DN compare algorithm. The specified index ID is used in the
6353N/A * comparison if the byte arrays are equal.
5195N/A *
5195N/A * @param b The byte array.
5195N/A * @param offset The first offset.
5195N/A * @param length The first length.
5195N/A * @param indexID The first index id.
5195N/A * @param other The second byte array to compare to.
5195N/A * @param otherLength The second byte array's length.
5195N/A * @param otherIndexID The second index id.
5195N/A * @return a negative integer, zero, or a positive integer as the first
5195N/A * offset value is less than, equal to, or greater than the second
5195N/A * byte array.
5195N/A */
5195N/A public int compare(byte[] b, int offset, int length, int indexID,
5195N/A byte[] other, int otherLength, int otherIndexID)
5195N/A {
5195N/A for(int i = 0; i < length && i < otherLength; i++)
5195N/A {
5195N/A if(b[offset + i] > other[i])
5195N/A {
5195N/A return 1;
5195N/A }
5195N/A else if (b[offset + i] < other[i])
5195N/A {
5195N/A return -1;
5195N/A }
5195N/A }
5195N/A //The arrays are equal, make sure they are in the same index since
5195N/A //multiple suffixes might have the same key.
5195N/A if(length == otherLength)
5195N/A {
5195N/A if(indexID == otherIndexID)
5195N/A {
5195N/A return 0;
5195N/A }
5195N/A else if(indexID > otherIndexID)
5195N/A {
5195N/A return 1;
5195N/A }
5195N/A else
5195N/A {
5195N/A return -1;
5195N/A }
5195N/A }
5195N/A if (length > otherLength)
5195N/A {
5195N/A return 1;
5195N/A }
5195N/A else
5195N/A {
5195N/A return -1;
5195N/A }
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Compare an offset in an byte array with the specified byte array,
5195N/A * using the DN compare algorithm.
5195N/A *
5195N/A * @param b The byte array.
5195N/A * @param offset The first offset.
5195N/A * @param length The first length.
5195N/A * @param other The second byte array to compare to.
5195N/A * @param otherLength The second byte array's length.
5195N/A * @return a negative integer, zero, or a positive integer as the first
5195N/A * offset value is less than, equal to, or greater than the second
5195N/A * byte array.
5195N/A */
5195N/A public int compare(byte[] b, int offset, int length, byte[] other,
5195N/A int otherLength)
5195N/A {
5195N/A for(int i = 0; i < length && i < otherLength; i++)
5195N/A {
5195N/A if(b[offset + i] > other[i])
5195N/A {
5195N/A return 1;
5195N/A }
5195N/A else if (b[offset + i] < other[i])
5195N/A {
5195N/A return -1;
5195N/A }
5195N/A }
5195N/A if(length == otherLength)
5195N/A {
5195N/A return 0;
5195N/A }
5195N/A if (length > otherLength)
5195N/A {
5195N/A return 1;
5195N/A }
5195N/A else
5195N/A {
5195N/A return -1;
5195N/A }
5195N/A }
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Set the index key associated with an index buffer.
5195N/A *
5195N/A * @param indexKey The index key.
5195N/A */
5195N/A public void setIndexKey(Importer.IndexKey indexKey)
5195N/A {
5195N/A this.indexKey = indexKey;
5195N/A }
5195N/A
5195N/A
5195N/A /**
5195N/A * Return the index key of an index buffer.
5195N/A * @return The index buffer's index key.
5195N/A */
5195N/A public Importer.IndexKey getIndexKey()
5195N/A {
5195N/A return indexKey;
5195N/A }
5195N/A}