| | |
| | | this.doCount = doCount; |
| | | } |
| | | |
| | | /** Create an empty import instance. */ |
| | | public ImportIDSet() |
| | | { |
| | | this.limit = -1; |
| | | this.doCount = false; |
| | | } |
| | | |
| | | /** |
| | | * Clear the set so it can be reused again. The boolean indexParam specifies |
| | | * if the index parameters should be cleared also. |
| | |
| | | /** |
| | | * Add the specified long value to an import ID set. |
| | | * |
| | | * @param l The long value to add to an import ID set. |
| | | * @param entryID The long value to add to an import ID set. |
| | | */ |
| | | void addEntryID(long l) { |
| | | void addEntryID(long entryID) { |
| | | if(!isDefined()) { |
| | | if(doCount) { |
| | | undefinedSize++; |
| | | } |
| | | return; |
| | | } |
| | | if (l < 0 || (isDefined() && count + 1 > limit)) |
| | | if (entryID < 0 || (isDefined() && count + 1 > limit)) |
| | | { |
| | | setUndefined(); |
| | | if(doCount) { |
| | |
| | | } |
| | | count = 0; |
| | | } else { |
| | | add(l); |
| | | add(entryID); |
| | | } |
| | | } |
| | | |
| | |
| | | return count; |
| | | } |
| | | |
| | | private boolean add(long v) |
| | | private boolean add(long entryID) |
| | | { |
| | | resize(count+1); |
| | | |
| | | if (count == 0 || v > array[count-1]) |
| | | if (count == 0 || entryID > array[count-1]) |
| | | { |
| | | array[count++] = v; |
| | | array[count++] = entryID; |
| | | return true; |
| | | } |
| | | |
| | | int pos = binarySearch(array, count, v); |
| | | int pos = binarySearch(array, count, entryID); |
| | | if (pos >=0) |
| | | { |
| | | return false; |
| | |
| | | pos = -(pos+1); |
| | | |
| | | System.arraycopy(array, pos, array, pos+1, count-pos); |
| | | array[pos] = v; |
| | | array[pos] = entryID; |
| | | count++; |
| | | return true; |
| | | } |
| | |
| | | private final DatabaseEntry dbKey, dbValue; |
| | | private final int cacheSize; |
| | | private final Map<Integer, DNState> dnStateMap = new HashMap<Integer, DNState>(); |
| | | private final Map<Integer, Index> indexMap = new HashMap<Integer, Index>(); |
| | | private final Semaphore permits; |
| | | private final int maxPermits; |
| | | private final AtomicLong bytesRead = new AtomicLong(); |
| | |
| | | } |
| | | else if (b.compare(key, indexID) != 0) |
| | | { |
| | | addToDB(insertIDSet, deleteIDSet, indexID); |
| | | addToDB(indexID, insertIDSet, deleteIDSet); |
| | | keyCount.incrementAndGet(); |
| | | |
| | | indexID = b.getIndexID(); |
| | |
| | | |
| | | if (key != null) |
| | | { |
| | | addToDB(insertIDSet, deleteIDSet, indexID); |
| | | addToDB(indexID, insertIDSet, deleteIDSet); |
| | | } |
| | | } |
| | | return null; |
| | |
| | | } |
| | | } |
| | | |
| | | private void addToDB(ImportIDSet insertSet, ImportIDSet deleteSet, |
| | | int indexID) throws DirectoryException |
| | | private void addToDB(int indexID, ImportIDSet insertSet, ImportIDSet deleteSet) throws DirectoryException |
| | | { |
| | | if (!indexMgr.isDN2ID()) |
| | | if (indexMgr.isDN2ID()) |
| | | { |
| | | addDN2ID(indexID, insertSet); |
| | | } |
| | | else |
| | | { |
| | | if (deleteSet.size() > 0 || !deleteSet.isDefined()) |
| | | { |
| | | dbKey.setData(deleteSet.getKey().array(), 0, deleteSet.getKey().limit()); |
| | | final Index index = idContainerMap.get(indexID); |
| | | index.delete(dbKey, deleteSet, dbValue); |
| | | if (!indexMap.containsKey(indexID)) |
| | | { |
| | | indexMap.put(indexID, index); |
| | | } |
| | | } |
| | | if (insertSet.size() > 0 || !insertSet.isDefined()) |
| | | { |
| | | dbKey.setData(insertSet.getKey().array(), 0, insertSet.getKey().limit()); |
| | | final Index index = idContainerMap.get(indexID); |
| | | index.insert(dbKey, insertSet, dbValue); |
| | | if (!indexMap.containsKey(indexID)) |
| | | { |
| | | indexMap.put(indexID, index); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | addDN2ID(insertSet, indexID); |
| | | } |
| | | } |
| | | |
| | | private void addDN2ID(ImportIDSet record, Integer indexID) |
| | | throws DirectoryException |
| | | private void addDN2ID(int indexID, ImportIDSet record) throws DirectoryException |
| | | { |
| | | DNState dnState; |
| | | if (!dnStateMap.containsKey(indexID)) |
| | |
| | | deleteKeyCount = 0; |
| | | } |
| | | |
| | | private void insertOrDeleteKey(IndexOutputBuffer indexBuffer, int i) |
| | | private void insertOrDeleteKey(IndexOutputBuffer indexBuffer, int position) |
| | | { |
| | | if (indexBuffer.isInsertRecord(i)) |
| | | if (indexBuffer.isInsertRecord(position)) |
| | | { |
| | | indexBuffer.writeID(insertByteStream, i); |
| | | indexBuffer.writeEntryID(insertByteStream, position); |
| | | insertKeyCount++; |
| | | } |
| | | else |
| | | { |
| | | indexBuffer.writeID(deleteByteStream, i); |
| | | indexBuffer.writeEntryID(deleteByteStream, position); |
| | | deleteKeyCount++; |
| | | } |
| | | } |
| | | |
| | | private void insertOrDeleteKeyCheckEntryLimit(IndexOutputBuffer indexBuffer, int i) |
| | | private void insertOrDeleteKeyCheckEntryLimit(IndexOutputBuffer indexBuffer, int position) |
| | | { |
| | | if (indexBuffer.isInsertRecord(i)) |
| | | if (indexBuffer.isInsertRecord(position)) |
| | | { |
| | | if (insertKeyCount++ <= indexMgr.getLimit()) |
| | | { |
| | | indexBuffer.writeID(insertByteStream, i); |
| | | indexBuffer.writeEntryID(insertByteStream, position); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | indexBuffer.writeID(deleteByteStream, i); |
| | | indexBuffer.writeEntryID(deleteByteStream, position); |
| | | deleteKeyCount++; |
| | | } |
| | | } |
| | |
| | | * @param stream The stream to write the record at the index to. |
| | | * @param position The position of the record to write. |
| | | */ |
| | | public void writeID(ByteArrayOutputStream stream, int position) |
| | | public void writeEntryID(ByteArrayOutputStream stream, int position) |
| | | { |
| | | int offSet = getOffset(position); |
| | | int len = PackedInteger.getReadLongLength(buffer, offSet + REC_OVERHEAD); |
| | |
| | | public int getKeySize() |
| | | { |
| | | int offSet = getOffset(position) + REC_OVERHEAD; |
| | | offSet += PackedInteger.getReadIntLength(buffer, offSet); |
| | | offSet += PackedInteger.getReadLongLength(buffer, offSet); |
| | | return PackedInteger.readInt(buffer, offSet); |
| | | } |
| | | |
| | |
| | | { |
| | | keyBuffer.clear(); |
| | | int offSet = getOffset(position) + REC_OVERHEAD; |
| | | offSet += PackedInteger.getReadIntLength(buffer, offSet); |
| | | offSet += PackedInteger.getReadLongLength(buffer, offSet); |
| | | int keyLen = PackedInteger.readInt(buffer, offSet); |
| | | offSet += PackedInteger.getReadIntLength(buffer, offSet); |
| | | //Re-allocate if the key is bigger than the capacity. |
| | |
| | | private byte[] getKey(int position) |
| | | { |
| | | int offSet = getOffset(position) + REC_OVERHEAD; |
| | | offSet += PackedInteger.getReadIntLength(buffer, offSet); |
| | | offSet += PackedInteger.getReadLongLength(buffer, offSet); |
| | | int keyLen = PackedInteger.readInt(buffer, offSet); |
| | | offSet += PackedInteger.getReadIntLength(buffer, offSet); |
| | | byte[] key = new byte[keyLen]; |
| | |
| | | int xoffSet = getOffset(xPosition); |
| | | int xIndexID = getIndexIDFromOffset(xoffSet); |
| | | xoffSet += REC_OVERHEAD; |
| | | xoffSet += PackedInteger.getReadIntLength(buffer, xoffSet); |
| | | xoffSet += PackedInteger.getReadLongLength(buffer, xoffSet); |
| | | int xKeyLen = PackedInteger.readInt(buffer, xoffSet); |
| | | int xKey = PackedInteger.getReadIntLength(buffer, xoffSet) + xoffSet; |
| | | |
| | | int yoffSet = getOffset(yPosition); |
| | | int yIndexID = getIndexIDFromOffset(yoffSet); |
| | | yoffSet += REC_OVERHEAD; |
| | | yoffSet += PackedInteger.getReadIntLength(buffer, yoffSet); |
| | | yoffSet += PackedInteger.getReadLongLength(buffer, yoffSet); |
| | | int yKeyLen = PackedInteger.readInt(buffer, yoffSet); |
| | | int yKey = PackedInteger.getReadIntLength(buffer, yoffSet) + yoffSet; |
| | | |
| | |
| | | int xoffSet = getOffset(xPosition); |
| | | int xIndexID = getIndexIDFromOffset(xoffSet); |
| | | xoffSet += REC_OVERHEAD; |
| | | xoffSet += PackedInteger.getReadIntLength(buffer, xoffSet); |
| | | xoffSet += PackedInteger.getReadLongLength(buffer, xoffSet); |
| | | int xKeyLen = PackedInteger.readInt(buffer, xoffSet); |
| | | int xKey = PackedInteger.getReadIntLength(buffer, xoffSet) + xoffSet; |
| | | |
| | |
| | | int offset = getOffset(position); |
| | | int indexID = getIndexIDFromOffset(offset); |
| | | offset += REC_OVERHEAD; |
| | | offset += PackedInteger.getReadIntLength(buffer, offset); |
| | | offset += PackedInteger.getReadLongLength(buffer, offset); |
| | | int keyLen = PackedInteger.readInt(buffer, offset); |
| | | int key = PackedInteger.getReadIntLength(buffer, offset) + offset; |
| | | return comparator.compare(buffer, key, keyLen, b, b.length) == 0 |
| | |
| | | int offset = getOffset(position); |
| | | int indexID = getIndexIDFromOffset(offset); |
| | | offset += REC_OVERHEAD; |
| | | offset += PackedInteger.getReadIntLength(buffer, offset); |
| | | offset += PackedInteger.getReadLongLength(buffer, offset); |
| | | int keyLen = PackedInteger.readInt(buffer, offset); |
| | | int key = PackedInteger.getReadIntLength(buffer, offset) + offset; |
| | | |
| | |
| | | public void writeKey(DataOutputStream dataStream) throws IOException |
| | | { |
| | | int offSet = getOffset(position) + REC_OVERHEAD; |
| | | offSet += PackedInteger.getReadIntLength(buffer, offSet); |
| | | offSet += PackedInteger.getReadLongLength(buffer, offSet); |
| | | int keyLen = PackedInteger.readInt(buffer, offSet); |
| | | offSet += PackedInteger.getReadIntLength(buffer, offSet); |
| | | dataStream.write(buffer, offSet, keyLen); |
| | |
| | | /** |
| | | * Convert to a short string to aid with debugging. |
| | | * |
| | | * @param buffer The string is appended to this string builder. |
| | | * @param sb The string is appended to this string builder. |
| | | */ |
| | | void toString(StringBuilder buffer) |
| | | void toString(StringBuilder sb) |
| | | { |
| | | if (!isDefined()) |
| | | if (isDefined()) |
| | | { |
| | | if (key != null) |
| | | sb.append("[COUNT:").append(size()).append("]"); |
| | | } |
| | | else if (key != null) |
| | | { |
| | | // The index entry limit was exceeded |
| | | sb.append("[LIMIT-EXCEEDED"); |
| | | if (undefinedSize == Long.MAX_VALUE) |
| | | { |
| | | // The index entry limit was exceeded |
| | | if(undefinedSize == Long.MAX_VALUE) |
| | | { |
| | | buffer.append("[LIMIT-EXCEEDED]"); |
| | | } |
| | | else |
| | | { |
| | | buffer.append("[LIMIT-EXCEEDED:"); |
| | | buffer.append(undefinedSize); |
| | | buffer.append("]"); |
| | | } |
| | | sb.append(":").append(undefinedSize); |
| | | } |
| | | else |
| | | { |
| | | // Not indexed |
| | | buffer.append("[NOT-INDEXED]"); |
| | | } |
| | | sb.append("]"); |
| | | } |
| | | else |
| | | { |
| | | buffer.append("[COUNT:"); |
| | | buffer.append(size()); |
| | | buffer.append("]"); |
| | | sb.append("[NOT-INDEXED]"); |
| | | } |
| | | } |
| | | |
| | |
| | | private int count; |
| | | /** Boolean to keep track if the instance is defined or not. */ |
| | | private boolean isDefined = true; |
| | | /** Size of the undefined if count is kept. */ |
| | | private long undefinedSize; |
| | | /** Key related to an ID set. */ |
| | | private ByteBuffer key; |
| | | /** The entry limit size. */ |
| | | private final int limit; |
| | | /** Set to true if a count of ids above the entry limit should be kept. */ |
| | | private final boolean doCount; |
| | | /** The index entry limit size. */ |
| | | private final int indexEntryLimit; |
| | | /** |
| | | * Set to true if a count of ids above the index entry limit should be kept, a.k.a |
| | | * {@link #undefinedSize}. |
| | | * |
| | | * @see #undefinedSize |
| | | */ |
| | | private final boolean maintainCount; |
| | | /** |
| | | * Size of the undefined id set, if count is maintained. |
| | | * |
| | | * @see #maintainCount |
| | | */ |
| | | private long undefinedSize; |
| | | |
| | | /** |
| | | * Create an import ID set of the specified size, index limit and index |
| | |
| | | * |
| | | * @param size The size of the the underlying array, plus some extra space. |
| | | * @param limit The index entry limit. |
| | | * @param doCount The index maintain count. |
| | | * @param maintainCount whether to maintain the count when size is undefined. |
| | | */ |
| | | public ImportIDSet(int size, int limit, boolean doCount) |
| | | public ImportIDSet(int size, int limit, boolean maintainCount) |
| | | { |
| | | this.array = new long[size + 128]; |
| | | // A limit of 0 means unlimited. |
| | | this.limit = limit == 0 ? Integer.MAX_VALUE : limit; |
| | | this.doCount = doCount; |
| | | } |
| | | |
| | | /** Create an empty import instance. */ |
| | | public ImportIDSet() |
| | | { |
| | | this.limit = -1; |
| | | this.doCount = false; |
| | | this.indexEntryLimit = limit == 0 ? Integer.MAX_VALUE : limit; |
| | | this.maintainCount = maintainCount; |
| | | } |
| | | |
| | | /** |
| | |
| | | /** |
| | | * Add the specified long value to an import ID set. |
| | | * |
| | | * @param l The long value to add to an import ID set. |
| | | * @param entryID The {@link EntryID} to add to an import ID set. |
| | | */ |
| | | void addEntryID(long l) { |
| | | void addEntryID(long entryID) { |
| | | if(!isDefined()) { |
| | | if(doCount) { |
| | | if (maintainCount) { |
| | | undefinedSize++; |
| | | } |
| | | return; |
| | | } |
| | | if (l < 0 || (isDefined() && count + 1 > limit)) |
| | | if (entryID < 0 || (isDefined() && count + 1 > indexEntryLimit)) |
| | | { |
| | | setUndefined(); |
| | | if(doCount) { |
| | | if (maintainCount) { |
| | | undefinedSize = count + 1; |
| | | } else { |
| | | undefinedSize = Long.MAX_VALUE; |
| | | } |
| | | count = 0; |
| | | } else { |
| | | add(l); |
| | | add(entryID); |
| | | } |
| | | } |
| | | |
| | |
| | | incrementLimitCount = true; |
| | | } else { |
| | | array = EntryIDSet.decodeEntryIDList(dBbytes); |
| | | if(array.length + importIdSet.size() > limit) { |
| | | if (array.length + importIdSet.size() > indexEntryLimit) { |
| | | undefinedSize = array.length + importIdSet.size(); |
| | | isDefined=false; |
| | | incrementLimitCount=true; |
| | |
| | | undefinedSize = Long.MAX_VALUE; |
| | | } else { |
| | | array = EntryIDSet.decodeEntryIDList(bytes); |
| | | if(array.length - importIdSet.size() > limit) { |
| | | if (array.length - importIdSet.size() > indexEntryLimit) { |
| | | isDefined=false; |
| | | count = 0; |
| | | importIdSet.setUndefined(); |
| | |
| | | public boolean merge(ByteString bytes, ImportIDSet importIdSet) |
| | | { |
| | | boolean incrementLimitCount=false; |
| | | if(doCount) { |
| | | if (maintainCount) { |
| | | incrementLimitCount = mergeCount(bytes, importIdSet); |
| | | } else if (isDBUndefined(bytes)) { |
| | | isDefined = false; |
| | |
| | | count = 0; |
| | | } else { |
| | | array = EntryIDSet.decodeEntryIDList(bytes); |
| | | if (array.length + importIdSet.size() > limit) { |
| | | if (array.length + importIdSet.size() > indexEntryLimit) { |
| | | isDefined = false; |
| | | incrementLimitCount = true; |
| | | count = 0; |
| | |
| | | return count; |
| | | } |
| | | |
| | | private boolean add(long v) |
| | | private boolean add(long entryID) |
| | | { |
| | | resize(count+1); |
| | | |
| | | if (count == 0 || v > array[count-1]) |
| | | if (count == 0 || entryID > array[count-1]) |
| | | { |
| | | array[count++] = v; |
| | | array[count++] = entryID; |
| | | return true; |
| | | } |
| | | |
| | | int pos = binarySearch(array, count, v); |
| | | int pos = binarySearch(array, count, entryID); |
| | | if (pos >=0) |
| | | { |
| | | return false; |
| | |
| | | pos = -(pos+1); |
| | | |
| | | System.arraycopy(array, pos, array, pos+1, count-pos); |
| | | array[pos] = v; |
| | | array[pos] = entryID; |
| | | count++; |
| | | return true; |
| | | } |
| | |
| | | */ |
| | | ByteString keyToByteString() |
| | | { |
| | | return ByteString.wrap(getKey().array(), 0, getKey().limit()); |
| | | return ByteString.wrap(key.array(), 0, key.limit()); |
| | | } |
| | | |
| | | /** |
| | |
| | | { |
| | | return key; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public String toString() |
| | | { |
| | | final StringBuilder sb = new StringBuilder(); |
| | | if (isDefined()) |
| | | { |
| | | sb.append("[COUNT:").append(size()).append("]"); |
| | | } |
| | | else |
| | | { |
| | | sb.append("[LIMIT-EXCEEDED"); |
| | | if (undefinedSize < Long.MAX_VALUE) |
| | | { |
| | | sb.append(":").append(undefinedSize); |
| | | } |
| | | sb.append("]"); |
| | | } |
| | | return sb.toString(); |
| | | } |
| | | } |
| | |
| | | // Ensure that there are minimum two threads available for parallel |
| | | // processing of smaller indexes. |
| | | dbThreads = Math.max(2, dbThreads); |
| | | dbThreads = 1; // FIXME JNR |
| | | |
| | | logger.info(NOTE_JEB_IMPORT_LDIF_PHASE_TWO_MEM_REPORT, availableMemory, readAheadSize, buffers); |
| | | |
| | |
| | | private final IndexManager indexMgr; |
| | | private final int cacheSize; |
| | | private final Map<Integer, DNState> dnStateMap = new HashMap<Integer, DNState>(); |
| | | private final Map<Integer, Index> indexMap = new HashMap<Integer, Index>(); |
| | | private final Semaphore permits; |
| | | private final int maxPermits; |
| | | private final AtomicLong bytesRead = new AtomicLong(); |
| | |
| | | { |
| | | final Index index = idContainerMap.get(indexID); |
| | | int limit = index.getIndexEntryLimit(); |
| | | boolean doCount = index.getMaintainCount(); |
| | | insertIDSet = new ImportIDSet(1, limit, doCount); |
| | | deleteIDSet = new ImportIDSet(1, limit, doCount); |
| | | boolean maintainCount = index.getMaintainCount(); |
| | | insertIDSet = new ImportIDSet(1, limit, maintainCount); |
| | | deleteIDSet = new ImportIDSet(1, limit, maintainCount); |
| | | } |
| | | |
| | | key = ByteBuffer.allocate(b.getKeyLen()); |
| | |
| | | } |
| | | else if (b.compare(key, indexID) != 0) |
| | | { |
| | | addToDB(insertIDSet, deleteIDSet, indexID); |
| | | addToDB(indexID, insertIDSet, deleteIDSet); |
| | | keyCount.incrementAndGet(); |
| | | |
| | | indexID = b.getIndexID(); |
| | |
| | | { |
| | | final Index index = idContainerMap.get(indexID); |
| | | int limit = index.getIndexEntryLimit(); |
| | | boolean doCount = index.getMaintainCount(); |
| | | insertIDSet = new ImportIDSet(1, limit, doCount); |
| | | deleteIDSet = new ImportIDSet(1, limit, doCount); |
| | | boolean maintainCount = index.getMaintainCount(); |
| | | insertIDSet = new ImportIDSet(1, limit, maintainCount); |
| | | deleteIDSet = new ImportIDSet(1, limit, maintainCount); |
| | | } |
| | | |
| | | key.clear(); |
| | |
| | | |
| | | if (key != null) |
| | | { |
| | | addToDB(insertIDSet, deleteIDSet, indexID); |
| | | addToDB(indexID, insertIDSet, deleteIDSet); |
| | | } |
| | | } |
| | | return null; |
| | |
| | | } |
| | | } |
| | | |
| | | private void addToDB(ImportIDSet insertSet, ImportIDSet deleteSet, int indexID) throws DirectoryException |
| | | private void addToDB(int indexID, ImportIDSet insertSet, ImportIDSet deleteSet) throws DirectoryException |
| | | { |
| | | if (indexMgr.isDN2ID()) |
| | | { |
| | | addDN2ID(insertSet, indexID); |
| | | addDN2ID(indexID, insertSet); |
| | | } |
| | | else |
| | | { |
| | |
| | | ByteString key = deleteSet.keyToByteString(); |
| | | final Index index = idContainerMap.get(indexID); |
| | | index.delete(txn, key, deleteSet); |
| | | if (!indexMap.containsKey(indexID)) |
| | | { |
| | | indexMap.put(indexID, index); |
| | | } |
| | | } |
| | | if (insertSet.size() > 0 || !insertSet.isDefined()) |
| | | { |
| | | ByteString key = insertSet.keyToByteString(); |
| | | final Index index = idContainerMap.get(indexID); |
| | | index.insert(txn, key, insertSet); |
| | | if (!indexMap.containsKey(indexID)) |
| | | { |
| | | indexMap.put(indexID, index); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void addDN2ID(ImportIDSet record, Integer indexID) throws DirectoryException |
| | | private void addDN2ID(int indexID, ImportIDSet record) throws DirectoryException |
| | | { |
| | | DNState dnState; |
| | | if (!dnStateMap.containsKey(indexID)) |
| | |
| | | private final int DRAIN_TO = 3; |
| | | private final IndexManager indexMgr; |
| | | private final BlockingQueue<IndexOutputBuffer> queue; |
| | | /** Stream where to output insert ImportIDSet data. */ |
| | | private final ByteArrayOutputStream insertByteStream = new ByteArrayOutputStream(2 * bufferSize); |
| | | /** Stream where to output delete ImportIDSet data. */ |
| | | private final ByteArrayOutputStream deleteByteStream = new ByteArrayOutputStream(2 * bufferSize); |
| | | private final DataOutputStream bufferStream; |
| | | private final DataOutputStream bufferIndexStream; |
| | |
| | | insertOrDeleteKey(indexBuffer, i); |
| | | continue; |
| | | } |
| | | if (!indexBuffer.compare(i)) |
| | | if (!indexBuffer.byteArraysEqual(i)) |
| | | { |
| | | bufferLen += writeRecord(indexBuffer); |
| | | indexBuffer.setPosition(i); |
| | |
| | | saveIndexID = b.getIndexID(); |
| | | insertOrDeleteKey(b, b.getPosition()); |
| | | } |
| | | else if (!b.compare(saveKey, saveIndexID)) |
| | | else if (!b.recordsEqual(saveKey, saveIndexID)) |
| | | { |
| | | bufferLen += writeRecord(saveKey, saveIndexID); |
| | | resetStreams(); |
| | |
| | | deleteKeyCount = 0; |
| | | } |
| | | |
| | | private void insertOrDeleteKey(IndexOutputBuffer indexBuffer, int i) |
| | | private void insertOrDeleteKey(IndexOutputBuffer indexBuffer, int position) |
| | | { |
| | | if (indexBuffer.isInsertRecord(i)) |
| | | if (indexBuffer.isInsertRecord(position)) |
| | | { |
| | | indexBuffer.writeID(insertByteStream, i); |
| | | indexBuffer.writeEntryID(insertByteStream, position); |
| | | insertKeyCount++; |
| | | } |
| | | else |
| | | { |
| | | indexBuffer.writeID(deleteByteStream, i); |
| | | indexBuffer.writeEntryID(deleteByteStream, position); |
| | | deleteKeyCount++; |
| | | } |
| | | } |
| | | |
| | | private void insertOrDeleteKeyCheckEntryLimit(IndexOutputBuffer indexBuffer, int i) |
| | | private void insertOrDeleteKeyCheckEntryLimit(IndexOutputBuffer indexBuffer, int position) |
| | | { |
| | | if (indexBuffer.isInsertRecord(i)) |
| | | if (indexBuffer.isInsertRecord(position)) |
| | | { |
| | | if (insertKeyCount++ <= indexMgr.getLimit()) |
| | | { |
| | | indexBuffer.writeID(insertByteStream, i); |
| | | indexBuffer.writeEntryID(insertByteStream, position); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | indexBuffer.writeID(deleteByteStream, i); |
| | | indexBuffer.writeEntryID(deleteByteStream, position); |
| | | deleteKeyCount++; |
| | | } |
| | | } |
| | |
| | | insertByteStream.write(-1); |
| | | } |
| | | |
| | | int insertSize = 4; |
| | | int insertSize = INT_SIZE; |
| | | bufferStream.writeInt(insertKeyCount); |
| | | if (insertByteStream.size() > 0) |
| | | { |
| | | insertByteStream.writeTo(bufferStream); |
| | | } |
| | | |
| | | int deleteSize = 4; |
| | | bufferStream.write(deleteKeyCount); |
| | | int deleteSize = INT_SIZE; |
| | | bufferStream.writeInt(deleteKeyCount); |
| | | if (deleteByteStream.size() > 0) |
| | | { |
| | | deleteByteStream.writeTo(bufferStream); |
| | |
| | | int headerSize = writeHeader(b.getIndexID(), keySize); |
| | | b.writeKey(bufferStream); |
| | | int bodySize = writeByteStreams(); |
| | | return headerSize + bodySize + keySize; |
| | | return headerSize + keySize + bodySize; |
| | | } |
| | | |
| | | private int writeRecord(byte[] k, int indexID) throws IOException |
| | |
| | | int headerSize = writeHeader(indexID, keySize); |
| | | bufferStream.write(k); |
| | | int bodySize = writeByteStreams(); |
| | | return headerSize + bodySize + keySize; |
| | | return headerSize + keySize + bodySize; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | |
| | | package org.opends.server.backends.pluggable; |
| | | |
| | | import static org.opends.messages.JebMessages.*; |
| | | import static org.opends.server.backends.pluggable.IndexOutputBuffer.*; |
| | | |
| | | import java.io.IOException; |
| | | import java.nio.ByteBuffer; |
| | |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.opends.server.backends.pluggable.Importer.IndexManager; |
| | | |
| | | import com.sleepycat.util.PackedInteger; |
| | | |
| | | /** |
| | | * The buffer class is used to process a buffer from the temporary index files |
| | | * during phase 2 processing. |
| | |
| | | indexID = getInt(); |
| | | |
| | | ensureData(20); |
| | | byte[] ba = cache.array(); |
| | | int p = cache.position(); |
| | | int len = PackedInteger.getReadIntLength(ba, p); |
| | | int keyLen = PackedInteger.readInt(ba, p); |
| | | cache.position(p + len); |
| | | int keyLen = getInt(); |
| | | if (keyLen > keyBuf.capacity()) |
| | | { |
| | | keyBuf = ByteBuffer.allocate(keyLen); |
| | |
| | | |
| | | private int getInt() throws IOException |
| | | { |
| | | ensureData(4); |
| | | ensureData(INT_SIZE); |
| | | return cache.getInt(); |
| | | } |
| | | |
| | | private long getLong() throws IOException |
| | | { |
| | | ensureData(LONG_SIZE); |
| | | return cache.getLong(); |
| | | } |
| | | |
| | | /** |
| | | * Reads the next ID set from the record and merges it with the provided ID |
| | | * set. |
| | |
| | | } |
| | | |
| | | ensureData(20); |
| | | int p = cache.position(); |
| | | byte[] ba = cache.array(); |
| | | int len = PackedInteger.getReadIntLength(ba, p); |
| | | int keyCount = PackedInteger.readInt(ba, p); |
| | | p += len; |
| | | cache.position(p); |
| | | int keyCount = getInt(); |
| | | for (int k = 0; k < keyCount; k++) |
| | | { |
| | | if (ensureData(9)) |
| | | { |
| | | p = cache.position(); |
| | | } |
| | | len = PackedInteger.getReadLongLength(ba, p); |
| | | long l = PackedInteger.readLong(ba, p); |
| | | p += len; |
| | | cache.position(p); |
| | | long entryID = getLong(); |
| | | |
| | | // idSet will be null if skipping. |
| | | if (idSet != null) |
| | | { |
| | | idSet.addEntryID(l); |
| | | idSet.addEntryID(entryID); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | import org.forgerock.opendj.ldap.ByteSequence; |
| | | |
| | | import com.sleepycat.util.PackedInteger; |
| | | |
| | | /** |
| | | * This class represents a index buffer used to store the keys and entry IDs |
| | | * processed from the LDIF file during phase one of an import, or rebuild index |
| | |
| | | * The record size is used for fast seeks to quickly "jump" over records. |
| | | * </p> |
| | | * <p> |
| | | * The records are packed as much as possible, to optimize the buffer space.<br> |
| | | * The records are packed as much as possible, to optimize the buffer space. |
| | | * </p> |
| | | * <p> |
| | | * This class is not thread safe. |
| | | * </p> |
| | | */ |
| | |
| | | LT, GT, LE, GE, EQ |
| | | } |
| | | |
| | | /** The size of a Java int. A Java int is 32 bits, i.e. 4 bytes. */ |
| | | /** The number of bytes of a Java int. */ |
| | | static final int INT_SIZE = 4; |
| | | /** The number of bytes of a Java long. */ |
| | | static final int LONG_SIZE = 8; |
| | | |
| | | /** |
| | | * The record overhead. In addition to entryID, key length and key bytes, the |
| | | * record overhead includes the indexID + INS/DEL bit |
| | | * record overhead includes the INS/DEL bit + indexID |
| | | */ |
| | | private static final int REC_OVERHEAD = INT_SIZE + 1; |
| | | private static final int REC_OVERHEAD = 1 + INT_SIZE; |
| | | |
| | | /** Buffer records are either insert records or delete records. */ |
| | | private static final byte DEL = 0, INS = 1; |
| | |
| | | } |
| | | |
| | | /** |
| | | * Determines if buffer should be re-cycled by calling {@link #reset()}. |
| | | * Determines if buffer should be re-cycled by calling {@link IndexOutputBuffer#reset()}. |
| | | * |
| | | * @return {@code true} if buffer should be recycled, or {@code false} if it |
| | | * should not. |
| | | * @return {@code true} if buffer should be recycled, or {@code false} if it should not. |
| | | */ |
| | | public boolean isDiscarded() |
| | | boolean isDiscarded() |
| | | { |
| | | return discarded; |
| | | } |
| | |
| | | // before it |
| | | recordOffset = addRecord(keyBytes, entryID.longValue(), indexID, insert); |
| | | // then write the returned record size |
| | | keyOffset += writeIntBytes(buffer, keyOffset, recordOffset); |
| | | keyOffset = writeInt(buffer, keyOffset, recordOffset); |
| | | bytesLeft = recordOffset - keyOffset; |
| | | keys++; |
| | | } |
| | |
| | | */ |
| | | private int addRecord(ByteSequence key, long entryID, int indexID, boolean insert) |
| | | { |
| | | int retOffset = recordOffset - getRecordSize(key.length(), entryID); |
| | | int retOffset = recordOffset - getRecordSize(key.length()); |
| | | int offSet = retOffset; |
| | | |
| | | // write the INS/DEL bit |
| | | buffer[offSet++] = insert ? INS : DEL; |
| | | // write the indexID |
| | | offSet += writeIntBytes(buffer, offSet, indexID); |
| | | offSet = writeInt(buffer, offSet, indexID); |
| | | // write the entryID |
| | | offSet = PackedInteger.writeLong(buffer, offSet, entryID); |
| | | offSet = writeLong(buffer, offSet, entryID); |
| | | // write the key length |
| | | offSet = PackedInteger.writeInt(buffer, offSet, key.length()); |
| | | offSet = writeInt(buffer, offSet, key.length()); |
| | | // write the key bytes |
| | | key.copyTo(buffer, offSet); |
| | | return retOffset; |
| | |
| | | */ |
| | | public static int getRequiredSize(int keyLen, long entryID) |
| | | { |
| | | // Adds up the key length + key bytes + entryID + indexID + the INS/DEL bit |
| | | // and finally the space needed to store the record size |
| | | return getRecordSize(keyLen, entryID) + INT_SIZE; |
| | | // also add up the space needed to store the record size |
| | | return getRecordSize(keyLen) + INT_SIZE; |
| | | } |
| | | |
| | | private static int getRecordSize(int keyLen, long entryID) |
| | | private static int getRecordSize(int keyLen) |
| | | { |
| | | // Adds up the key length + key bytes + ... |
| | | return PackedInteger.getWriteIntLength(keyLen) + keyLen + |
| | | // ... entryID + (indexID + INS/DEL bit). |
| | | PackedInteger.getWriteLongLength(entryID) + REC_OVERHEAD; |
| | | // Adds up (INS/DEL bit + indexID) + entryID + key length + key bytes |
| | | return REC_OVERHEAD + LONG_SIZE + INT_SIZE + keyLen; |
| | | } |
| | | |
| | | /** |
| | | * Write record at specified position to the specified output stream. |
| | | * Used when when writing the index scratch files. |
| | | * Write the entryID at the specified position to the specified output stream. |
| | | * Used when writing the index scratch files. |
| | | * |
| | | * @param stream The stream to write the record at the index to. |
| | | * @param position The position of the record to write. |
| | | */ |
| | | public void writeID(ByteArrayOutputStream stream, int position) |
| | | public void writeEntryID(ByteArrayOutputStream stream, int position) |
| | | { |
| | | int offSet = getOffset(position); |
| | | int len = PackedInteger.getReadLongLength(buffer, offSet + REC_OVERHEAD); |
| | | stream.write(buffer, offSet + REC_OVERHEAD, len); |
| | | stream.write(buffer, offSet + REC_OVERHEAD, LONG_SIZE); |
| | | } |
| | | |
| | | |
| | |
| | | */ |
| | | public int getKeySize() |
| | | { |
| | | int offSet = getOffset(position) + REC_OVERHEAD; |
| | | offSet += PackedInteger.getReadIntLength(buffer, offSet); |
| | | return PackedInteger.readInt(buffer, offSet); |
| | | int offSet = getOffset(position) + REC_OVERHEAD + LONG_SIZE; |
| | | return readInt(buffer, offSet); |
| | | } |
| | | |
| | | /** |
| | |
| | | private ByteBuffer getKeyBuf(int position) |
| | | { |
| | | keyBuffer.clear(); |
| | | int offSet = getOffset(position) + REC_OVERHEAD; |
| | | offSet += PackedInteger.getReadIntLength(buffer, offSet); |
| | | int keyLen = PackedInteger.readInt(buffer, offSet); |
| | | offSet += PackedInteger.getReadIntLength(buffer, offSet); |
| | | int offSet = getOffset(position) + REC_OVERHEAD + LONG_SIZE; |
| | | int keyLen = readInt(buffer, offSet); |
| | | offSet += INT_SIZE; |
| | | //Re-allocate if the key is bigger than the capacity. |
| | | if(keyLen > keyBuffer.capacity()) |
| | | { |
| | |
| | | */ |
| | | private byte[] getKey(int position) |
| | | { |
| | | int offSet = getOffset(position) + REC_OVERHEAD; |
| | | offSet += PackedInteger.getReadIntLength(buffer, offSet); |
| | | int keyLen = PackedInteger.readInt(buffer, offSet); |
| | | offSet += PackedInteger.getReadIntLength(buffer, offSet); |
| | | int offSet = getOffset(position) + REC_OVERHEAD + LONG_SIZE; |
| | | int keyLen = readInt(buffer, offSet); |
| | | offSet += INT_SIZE; |
| | | byte[] key = new byte[keyLen]; |
| | | System.arraycopy(buffer, offSet, key, 0, keyLen); |
| | | return key; |
| | |
| | | |
| | | private int getOffset(int position) |
| | | { |
| | | return getIntegerValue(position * INT_SIZE); |
| | | return readInt(position * INT_SIZE); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | private int getIndexIDFromOffset(int offset) |
| | | { |
| | | return getIntegerValue(offset + 1); |
| | | return readInt(offset + 1); |
| | | } |
| | | |
| | | private boolean is(CompareOp op, int xPosition, int yPosition) |
| | | { |
| | | int xoffSet = getOffset(xPosition); |
| | | int xIndexID = getIndexIDFromOffset(xoffSet); |
| | | xoffSet += REC_OVERHEAD; |
| | | xoffSet += PackedInteger.getReadIntLength(buffer, xoffSet); |
| | | int xKeyLen = PackedInteger.readInt(buffer, xoffSet); |
| | | int xKey = PackedInteger.getReadIntLength(buffer, xoffSet) + xoffSet; |
| | | xoffSet += REC_OVERHEAD + LONG_SIZE; |
| | | int xKeyLen = readInt(buffer, xoffSet); |
| | | int xKey = INT_SIZE + xoffSet; |
| | | |
| | | int yoffSet = getOffset(yPosition); |
| | | int yIndexID = getIndexIDFromOffset(yoffSet); |
| | | yoffSet += REC_OVERHEAD; |
| | | yoffSet += PackedInteger.getReadIntLength(buffer, yoffSet); |
| | | int yKeyLen = PackedInteger.readInt(buffer, yoffSet); |
| | | int yKey = PackedInteger.getReadIntLength(buffer, yoffSet) + yoffSet; |
| | | yoffSet += REC_OVERHEAD + LONG_SIZE; |
| | | int yKeyLen = readInt(buffer, yoffSet); |
| | | int yKey = INT_SIZE + yoffSet; |
| | | |
| | | int cmp = comparator.compare(buffer, xKey, xKeyLen, xIndexID, yKey, yKeyLen, yIndexID); |
| | | return evaluateReturnCode(cmp, op); |
| | |
| | | { |
| | | int xoffSet = getOffset(xPosition); |
| | | int xIndexID = getIndexIDFromOffset(xoffSet); |
| | | xoffSet += REC_OVERHEAD; |
| | | xoffSet += PackedInteger.getReadIntLength(buffer, xoffSet); |
| | | int xKeyLen = PackedInteger.readInt(buffer, xoffSet); |
| | | int xKey = PackedInteger.getReadIntLength(buffer, xoffSet) + xoffSet; |
| | | xoffSet += REC_OVERHEAD + LONG_SIZE; |
| | | int xKeyLen = readInt(buffer, xoffSet); |
| | | int xKey = INT_SIZE + xoffSet; |
| | | |
| | | int cmp = comparator.compare(buffer, xKey, xKeyLen, xIndexID, yKey, yKey.length, yIndexID); |
| | | return evaluateReturnCode(cmp, op); |
| | | } |
| | | |
| | | /** |
| | | * Compare the byte array at the current position with the specified one and |
| | | * using the specified index id. It will return {@code true} if the byte |
| | | * array at the current position is equal to the specified byte array as |
| | | * determined by the comparator and the index ID is is equal. It will |
| | | * return {@code false} otherwise. |
| | | * Verifies whether the provided byte array and indexID are equal to |
| | | * the byte array and indexIDs currently pointed to by this index output buffer. |
| | | * |
| | | * @param b The byte array to compare. |
| | | * @param bIndexID The index key. |
| | | * @param bIndexID The index key to compare. |
| | | * @return <CODE>True</CODE> if the byte arrays are equal. |
| | | */ |
| | | public boolean compare(byte[]b, int bIndexID) |
| | | public boolean recordsEqual(byte[] b, int bIndexID) |
| | | { |
| | | int offset = getOffset(position); |
| | | int indexID = getIndexIDFromOffset(offset); |
| | | offset += REC_OVERHEAD; |
| | | offset += PackedInteger.getReadIntLength(buffer, offset); |
| | | int keyLen = PackedInteger.readInt(buffer, offset); |
| | | int key = PackedInteger.getReadIntLength(buffer, offset) + offset; |
| | | offset += REC_OVERHEAD + LONG_SIZE; |
| | | int keyLen = readInt(buffer, offset); |
| | | int key = INT_SIZE + offset; |
| | | return comparator.compare(buffer, key, keyLen, b, b.length) == 0 |
| | | && indexID == bIndexID; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Compare current IndexBuffer to the specified index buffer using both the |
| | | * comparator and index ID of both buffers. |
| | |
| | | final ByteBuffer keyBuf = b.getKeyBuf(b.position); |
| | | int offset = getOffset(position); |
| | | int indexID = getIndexIDFromOffset(offset); |
| | | offset += REC_OVERHEAD; |
| | | offset += PackedInteger.getReadIntLength(buffer, offset); |
| | | int keyLen = PackedInteger.readInt(buffer, offset); |
| | | int key = PackedInteger.getReadIntLength(buffer, offset) + offset; |
| | | offset += REC_OVERHEAD + LONG_SIZE; |
| | | int keyLen = readInt(buffer, offset); |
| | | int key = INT_SIZE + offset; |
| | | |
| | | final int cmp = comparator.compare(buffer, key, keyLen, keyBuf.array(), keyBuf.limit()); |
| | | if (cmp != 0) |
| | |
| | | */ |
| | | public void writeKey(DataOutputStream dataStream) throws IOException |
| | | { |
| | | int offSet = getOffset(position) + REC_OVERHEAD; |
| | | offSet += PackedInteger.getReadIntLength(buffer, offSet); |
| | | int keyLen = PackedInteger.readInt(buffer, offSet); |
| | | offSet += PackedInteger.getReadIntLength(buffer, offSet); |
| | | int offSet = getOffset(position) + REC_OVERHEAD + LONG_SIZE; |
| | | int keyLen = readInt(buffer, offSet); |
| | | offSet += INT_SIZE; |
| | | dataStream.write(buffer, offSet, keyLen); |
| | | } |
| | | |
| | | /** |
| | | * Compare the byte array at the current position with the byte array at the |
| | | * specified index. |
| | | * provided position. |
| | | * |
| | | * @param i The index pointing to the byte array to compare. |
| | | * @param position The index pointing to the byte array to compare. |
| | | * @return {@code true} if the byte arrays are equal, or {@code false} otherwise |
| | | */ |
| | | public boolean compare(int i) |
| | | public boolean byteArraysEqual(int position) |
| | | { |
| | | return is(CompareOp.EQ, i, position); |
| | | return is(CompareOp.EQ, position, this.position); |
| | | } |
| | | |
| | | /** |
| | |
| | | position++; |
| | | } |
| | | |
| | | private int writeIntBytes(byte[] buffer, int offset, int val) |
| | | private int writeInt(byte[] buffer, int offset, int val) |
| | | { |
| | | for (int i = offset + INT_SIZE - 1; i >= offset; i--) { |
| | | final int endOffset = offset + INT_SIZE; |
| | | for (int i = endOffset - 1; i >= offset; i--) { |
| | | buffer[i] = (byte) (val & 0xff); |
| | | val >>>= 8; |
| | | } |
| | | return INT_SIZE; |
| | | return endOffset; |
| | | } |
| | | |
| | | private int getIntegerValue(int index) |
| | | private int writeLong(byte[] buffer, int offset, long val) |
| | | { |
| | | final int endOffset = offset + LONG_SIZE; |
| | | for (int i = endOffset - 1; i >= offset; i--) { |
| | | buffer[i] = (byte) (val & 0xff); |
| | | val >>>= 8; |
| | | } |
| | | return endOffset; |
| | | } |
| | | |
| | | private int readInt(int index) |
| | | { |
| | | return readInt(buffer, index); |
| | | } |
| | | |
| | | private int readInt(byte[] buffer, int index) |
| | | { |
| | | int answer = 0; |
| | | for (int i = 0; i < INT_SIZE; i++) { |
| | |
| | | |
| | | private void swap(int a, int b) |
| | | { |
| | | int aOffset = a * INT_SIZE; |
| | | int bOffset = b * INT_SIZE; |
| | | int bVal = getIntegerValue(bOffset); |
| | | System.arraycopy(buffer, aOffset, buffer, bOffset, INT_SIZE); |
| | | writeIntBytes(buffer, aOffset, bVal); |
| | | int aOffset = a * INT_SIZE; |
| | | int bOffset = b * INT_SIZE; |
| | | int bVal = readInt(bOffset); |
| | | System.arraycopy(buffer, aOffset, buffer, bOffset, INT_SIZE); |
| | | writeInt(buffer, aOffset, bVal); |
| | | } |
| | | |
| | | private void vectorSwap(int a, int b, int n) |
| | |
| | | { |
| | | for(int i = 0; i < length && i < otherLength; i++) |
| | | { |
| | | if(b[offset + i] > b[otherOffset + i]) |
| | | byte b1 = b[offset + i]; |
| | | byte b2 = b[otherOffset + i]; |
| | | if (b1 > b2) |
| | | { |
| | | return 1; |
| | | } |
| | | else if (b[offset + i] < b[otherOffset + i]) |
| | | else if (b1 < b2) |
| | | { |
| | | return -1; |
| | | } |