private void insertOrUpdate(long recid, DataIO.DataOutputByteArray out, boolean isInsert) { if (CC.ASSERT) assertWriteLocked(lockPos(recid)); // TODO assert indexTable state, record should already exist/not exist final int realSize = out == null ? 0 : out.pos; final int shiftedSize = out == null ? 0 : realSize + 1; // one additional state to indicate null final int headSize = 1 + // instruction DataIO.packLongSize(longParitySet(recid)) + // recid DataIO.packLongSize(longParitySet(shiftedSize)); // length long offset = alloc(headSize, headSize + realSize); final long origOffset = offset; // ensure available worst case scenario vol.ensureAvailable(offset + headSize + realSize); // instruction vol.putUnsignedByte(offset, isInsert ? I_INSERT : I_UPDATE); offset++; // recid offset += vol.putPackedLong(offset, longParitySet(recid)); // size offset += vol.putPackedLong(offset, longParitySet(shiftedSize)); if (realSize != 0) vol.putDataOverlap(offset, out.buf, 0, out.pos); // -3 is null record // -2 is zero size record indexTablePut(recid, out == null ? -3 : (realSize == 0) ? -2 : origOffset); }
@Test public void testPackLongSize() { assertEquals( "packLongSize should have returned 1 since number 1 can be represented using 1 byte when packed", 1, DataIO.packLongSize(1)); assertEquals( "packLongSize should have returned 2 since 1 << 7 can be represented using 2 bytes when packed", 2, DataIO.packLongSize(1 << 7)); assertEquals( "packLongSize should have returned 10 since 1 << 63 can be represented using 10 bytes when packed", 10, DataIO.packLongSize(1 << 63)); }
@Override protected <A> A get2(long recid, Serializer<A> serializer) { if (CC.ASSERT) assertReadLocked(recid); long offset = modified[lockPos(recid)].get(recid); if (offset == 0) { try { offset = indexTable.getLong(recid * 8); } catch (ArrayIndexOutOfBoundsException e) { // TODO this code should be aware if indexTable internals? throw new DBException.EngineGetVoid(); } } if (offset == -3 || offset == -1) // null, preallocated or deleted return null; if (offset == 0) { // non existent throw new DBException.EngineGetVoid(); } if (offset == -2) { // zero size record return deserialize(serializer, 0, new DataIO.DataInputByteArray(new byte[0])); } final long packedRecidSize = DataIO.packLongSize(longParitySet(recid)); if (CC.ASSERT) { int instruction = vol.getUnsignedByte(offset); if (instruction != I_UPDATE && instruction != I_INSERT) throw new DBException.DataCorruption("wrong instruction " + instruction); long recid2 = vol.getPackedLong(offset + 1); if (packedRecidSize != recid2 >>> 60) throw new DBException.DataCorruption("inconsistent recid len"); recid2 = longParityGet(recid2 & DataIO.PACK_LONG_RESULT_MASK); if (recid != recid2) throw new DBException.DataCorruption("recid does not match"); } offset += 1 + // instruction size packedRecidSize; // recid size // read size long size = vol.getPackedLong(offset); offset += size >>> 60; size = longParityGet(size & DataIO.PACK_LONG_RESULT_MASK); size -= 1; // normalize size if (CC.ASSERT && size <= 0) throw new DBException.DataCorruption("wrong size"); DataInput input = vol.getDataInputOverlap(offset, (int) size); return deserialize(serializer, (int) size, input); }
@Override protected <A> void delete2(long recid, Serializer<A> serializer) { if (CC.ASSERT) assertWriteLocked(lockPos(recid)); final int headSize = 1 + DataIO.packLongSize(longParitySet(recid)); long offset = alloc(headSize, headSize); vol.ensureAvailable(offset + headSize); vol.putUnsignedByte(offset, I_DELETE); // delete instruction offset++; vol.putPackedLong(offset, longParitySet(recid)); indexTablePut(recid, -1); // -1 is deleted record }
@Override public long preallocate() { long recid = highestRecid.incrementAndGet(); Lock lock = locks[lockPos(recid)].writeLock(); lock.lock(); try { final int headSize = 1 + DataIO.packLongSize(longParitySet(recid)); long offset = alloc(headSize, headSize); vol.ensureAvailable(offset + headSize); vol.putUnsignedByte(offset, I_PREALLOC); offset++; vol.putPackedLong(offset, longParitySet(recid)); indexTablePut(recid, -3); } finally { lock.unlock(); } return recid; }