Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   package org.mapdb;
   
   import java.io.IOError;
   import java.io.IOException;
   import java.nio.ByteBuffer;
   import java.util.Arrays;
   import java.util.zip.CRC32;

Write-Ahead-Log
  
  public class StoreWAL extends StoreDirect {
  
      protected static final long LOG_MASK_OFFSET = 0x0000FFFFFFFFFFFFL;
  
      protected static final byte WAL_INDEX_LONG = 101;
      protected static final byte WAL_LONGSTACK_PAGE = 102;
      protected static final byte WAL_PHYS_ARRAY_ONE_LONG = 103;
  
      protected static final byte WAL_PHYS_ARRAY = 104;
      protected static final byte WAL_SKIP_REST_OF_BLOCK = 105;


    
last instruction in log file
  
      protected static final byte WAL_SEAL = 111;
    
added to offset 8 into log file, indicates that log was synced and close
  
      protected static final long LOG_SEAL = 4566556446554645L;
  
      public static final String TRANS_LOG_FILE_EXT = ".t";
  
      protected static final long[] TOMBSTONE = new long[0];
      protected static final long[] PREALLOC = new long[0];
  
      protected final Volume.Factory volFac;
      protected Volume log;
  
      protected volatile long logSize;
  
      protected final LongConcurrentHashMap<long[]> modified = new LongConcurrentHashMap<long[]>();
      protected final LongMap<byte[]> longStackPages = new LongHashMap<byte[]>();
      protected final long[] indexVals = new long[/8];
      protected final boolean[] indexValsModified = new boolean[.];
  
      protected boolean replayPending = true;
  
  
      protected final AtomicInteger logChecksum = new AtomicInteger();
  
      public StoreWAL(Volume.Factory volFac) {
          this(volFacfalsefalse, 5, false, 0L, falsefalsenullfalse);
      }
      public StoreWAL(Volume.Factory volFacboolean readOnlyboolean deleteFilesAfterClose,
                      int spaceReclaimModeboolean syncOnCommitDisabledlong sizeLimit,
                      boolean checksumboolean compressbyte[] passwordboolean fullChunkAllocation) {
          super(volFacreadOnlydeleteFilesAfterClosespaceReclaimModesyncOnCommitDisabledsizeLimit,
                  checksumcompresspasswordfullChunkAllocation);
          this. = volFac;
          this. = volFac.createTransLogVolume();
  
          boolean allGood = false;
          .lock();
          try{
              reloadIndexFile();
              if(verifyLogFile()){
                  replayLogFile();
              }
               = false;
              checkHeaders();
               = null;
              allGood = true;
          }finally{
              if(!allGood) {
                  //exception was thrown, try to unlock files
                  if (!=null) {
                      .close();
                       = null;
                  }
                  if (!=null) {
                      .close();
                       = null;
                  }
                  if (!=null) {
                      .close();
                       = null;
                  }
              }
  
              .unlock();
          }
      }
  
      @Override
      protected void checkHeaders() {
          if(return;
          super.checkHeaders();
      }
  
     protected void reloadIndexFile() {
         assert(.isHeldByCurrentThread());
          = 0;
         .clear();
         .clear();
          = .getLong();
          = .getLong();
          = .getLong();
         for(int i = 0;i<;i+=8){
             [i/8] = .getLong(i);
         }
         Arrays.fill(false);
 
         .set(0);
 
         =-8;
         while([((int) ( / 8))]!=0 && >)
             -=8;
     }
 
     protected void openLogIfNeeded(){
         assert(.isHeldByCurrentThread());
         if( !=nullreturn;
          = .createTransLogVolume();
         .ensureAvailable(16);
         .putInt(0, );
         .putUnsignedShort(4, );
         .putUnsignedShort(6, expectedMasks());
         .putLong(8, 0L);
          = 16;
     }
 
 
     @Override
     public  long preallocate() {
         final long ioRecid;
         final long logPos;
 
         .readLock().lock();
         try{
             .lock();
             try{
                 openLogIfNeeded();
                 ioRecid = freeIoRecidTake(false);
                 //now get space in log
                 openLogIfNeeded();
                 logPos = ;
                 +=1+8+8; //space used for index val
                 .ensureAvailable();
 
             }finally{
                 .unlock();
             }
             final Lock lock  = [Store.lockPos(ioRecid)].writeLock();
             lock.lock();
             try{
 
                 //write data into log
                 walIndexVal(logPosioRecid);
                 .put(ioRecid);
             }finally{
                 lock.unlock();
             }
         }finally{
             .readLock().unlock();
         }
 
         long recid =  (ioRecid-)/8;
         assert(recid>0);
         return recid;
     }
 
 
     @Override
     public void preallocate(final long[] recids) {
         long logPos;
 
         .readLock().lock();
         try{
 
             .lock();
             try{
                 openLogIfNeeded();
                 logPos = ;
                 for(int i=0;i<recids.length;i++)
                     recids[i] = freeIoRecidTake(false) ;
 
                 //now get space in log
                 openLogIfNeeded();
                 +=recids.length*(1+8+8); //space used for index vals
                 .ensureAvailable();
 
             }finally{
                 .unlock();
             }
             //write data into log
             for(int i=0;i<recids.length;i++){
                 final long ioRecid = recids[i];
                 final Lock lock2 = [Store.lockPos(ioRecid)].writeLock();
                 lock2.lock();
                 try{
                     walIndexVal(logPosioRecid);
                     logPos+=1+8+8;
                     .put(ioRecid);
                 }finally{
                     lock2.unlock();
                 }
                 recids[i] =  (ioRecid-)/8;
                 assert(recids[i]>0);
             }
         }finally{
             .readLock().unlock();
         }
     }
 
 
     @Override
     public <A> long put(A valueSerializer<A> serializer) {
         assert(value!=null);
         DataOutput2 out = serialize(valueserializer);
 
         final long ioRecid;
         final long[] physPos;
         final long[] logPos;
 
         .readLock().lock();
         try{
 
             .lock();
         try{
             openLogIfNeeded();
             ioRecid = freeIoRecidTake(false);
             //first get space in phys
             physPos = physAllocate(out.pos,false,false);
             //now get space in log
             logPos = logAllocate(physPos);
 
         }finally{
             .unlock();
         }
 
         final Lock lock  = [Store.lockPos(ioRecid)].writeLock();
         lock.lock();
         try{
             //write data into log
             walIndexVal((logPos[0]&) - 1-8-8-1-8, ioRecidphysPos[0]|);
             walPhysArray(outphysPoslogPos);
 
             .put(ioRecid,logPos);
             .offer(out);
         }finally{
             lock.unlock();
         }
         }finally{
             .readLock().unlock();
         }
 
         long recid =  (ioRecid-)/8;
         assert(recid>0);
         return recid;
     }
 
     protected void walPhysArray(DataOutput2 outlong[] physPoslong[] logPos) {
         //write byte[] data
         int outPos = 0;
         int logC = 0;
         CRC32 crc32  = new CRC32();
 
         for(int i=0;i<logPos.length;i++){
             int c =  i==logPos.length-1 ? 0: 8;
             final long pos = logPos[i]&;
             int size = (int) (logPos[i]>>>48);
 
             byte header = c==0 ?  : ;
             .putByte(pos -  8 - 1, header);
             .putLong(pos -  8, physPos[i]);
 
             if(c>0){
                 .putLong(posphysPos[i + 1]);
             }
             .putData(pos+cout.bufoutPossize - c);
 
             crc32.reset();
             crc32.update(out.buf,outPossize-c);
             logC |= LongHashMap.longHashpos | header | physPos[i] | (c>0?physPos[i+1]:0) | crc32.getValue());
 
             outPos +=size-c;
             assert(>=outPos);
         }
         logChecksumAdd(logC);
         assert(outPos==out.pos);
     }
 
 
     protected void walIndexVal(long logPoslong ioRecidlong indexVal) {
         assert([Store.lockPos(ioRecid)].writeLock().isHeldByCurrentThread());
         assert(>=logPos+1+8+8);
         .putByte(logPos);
         .putLong(logPos + 1, ioRecid);
         .putLong(logPos + 9, indexVal);
 
         logChecksumAdd(LongHashMap.longHash(logPos |  | ioRecid | indexVal));
     }
 
 
     protected long[] logAllocate(long[] physPos) {
         assert(.isHeldByCurrentThread());
         openLogIfNeeded();
         +=1+8+8; //space used for index val
 
         long[] ret = new long[physPos.length];
         for(int i=0;i<physPos.length;i++){
             long size = physPos[i]>>>48;
             //would overlaps Volume Block?
             +=1+8; //space used for WAL_PHYS_ARRAY
             ret[i] = (size<<48) | ;
 
             +=size;
             checkLogRounding();
         }
         .ensureAvailable();
         return ret;
     }
 
     protected void checkLogRounding() {
         assert(.isHeldByCurrentThread());
             .ensureAvailable(+1);
             .putByte();
              += . - (&.);
         }
     }
 
 
     @Override
     public <A> A get(long recidSerializer<A> serializer) {
         assert(recid>0);
         final long ioRecid =  + recid*8;
         final Lock lock  = [Store.lockPos(ioRecid)].readLock();
         lock.lock();
         try{
             return get2(ioRecidserializer);
         }catch(IOException e){
             throw new IOError(e);
         }finally{
             lock.unlock();
         }
     }
 
     @Override
     protected <A> A get2(long ioRecidSerializer<A> serializerthrows IOException {
         assert([Store.lockPos(ioRecid)].getWriteHoldCount()==0||
                 [Store.lockPos(ioRecid)].writeLock().isHeldByCurrentThread());
 
         //check if record was modified in current transaction
         long[] r = .get(ioRecid);
         //no, read main version
         if(r==nullreturn super.get2(ioRecidserializer);
         //check for tombstone (was deleted in current trans)
         if(r== || r== || r.length==0) return null;
 
         //was modified in current transaction, so read it from trans log
         if(r.length==1){
             //single record
             final int size = (int) (r[0]>>>48);
             DataInput2 in = .getDataInput(r[0]&size);
             return deserialize(serializer,size,in);
         }else{
             //linked record
             int totalSize = 0;
             for(int i=0;i<r.length;i++){
                 int c =  i==r.length-1 ? 0: 8;
                 totalSize+=  (int) (r[i]>>>48)-c;
             }
             byte[] b = new byte[totalSize];
             int pos = 0;
             for(int i=0;i<r.length;i++){
                 int c =  i==r.length-1 ? 0: 8;
                 int size = (int) (r[i]>>>48) -c;
                 .getDataInput((r[i] & ) + csize).readFully(b,pos,size);
                 pos+=size;
             }
             if(pos!=totalSize)throw new AssertionError();
 
             return deserialize(serializer,totalSizenew DataInput2(b));
         }
     }
 
     @Override
     public <A> void update(long recid, A valueSerializer<A> serializer) {
         assert(recid>0);
         assert(value!=null);
         DataOutput2 out = serialize(valueserializer);
         final long ioRecid =  + recid*8;
         final Lock lock  = [Store.lockPos(ioRecid)].writeLock();
         lock.lock();
         try{
             final long[] physPos;
             final long[] logPos;
 
             long indexVal = 0;
             long[] linkedRecords = getLinkedRecordsFromLog(ioRecid);
             if(linkedRecords==null){
                 indexVal = .getLong(ioRecid);
                 linkedRecords = getLinkedRecordsIndexVals(indexVal);
             }else if(linkedRecords == ){
                 linkedRecords = null;
             }
 
             .lock();
             try{
                 openLogIfNeeded();
 
                 //free first record pointed from indexVal
                 if((indexVal>>>48)>0)
                     freePhysPut(indexVal,false);
 
                 //if there are more linked records, free those as well
                 if(linkedRecords!=null){
                     for(int i=0; i<linkedRecords.length &&linkedRecords[i]!=0;i++){
                         freePhysPut(linkedRecords[i],false);
                     }
                 }
 
 
                 //first get space in phys
                 physPos = physAllocate(out.pos,false,false);
                 //now get space in log
                 logPos = logAllocate(physPos);
 
             }finally{
                 .unlock();
             }
 
             //write data into log
             walIndexVal((logPos[0]&) - 1-8-8-1-8, ioRecidphysPos[0]|);
             walPhysArray(outphysPoslogPos);
 
             .put(ioRecid,logPos);
         }finally{
             lock.unlock();
         }
         .offer(out);
     }
 
     @Override
     public <A> boolean compareAndSwap(long recid, A expectedOldValue, A newValueSerializer<A> serializer) {
         assert(recid>0);
         assert(expectedOldValue!=null && newValue!=null);
         final long ioRecid =  + recid*8;
         final Lock lock  = [Store.lockPos(ioRecid)].writeLock();
         lock.lock();
         DataOutput2 out;
         try{
 
             A oldVal = get2(ioRecid,serializer);
             if((oldVal == null && expectedOldValue!=null) || (oldVal!=null && !oldVal.equals(expectedOldValue)))
                 return false;
 
             out = serialize(newValueserializer);
 
             final long[] physPos;
             final long[] logPos;
 
             long indexVal = 0;
             long[] linkedRecords = getLinkedRecordsFromLog(ioRecid);
             if(linkedRecords==null){
                 indexVal = .getLong(ioRecid);
                 linkedRecords = getLinkedRecordsIndexVals(indexVal);
             }
 
             .lock();
             try{
                 openLogIfNeeded();
 
                 //free first record pointed from indexVal
                 if((indexVal>>>48)>0)
                     freePhysPut(indexVal,false);
 
                 //if there are more linked records, free those as well
                 if(linkedRecords!=null){
                     for(int i=0; i<linkedRecords.length &&linkedRecords[i]!=0;i++){
                         freePhysPut(linkedRecords[i],false);
                     }
                 }
 
 
                 //first get space in phys
                 physPos = physAllocate(out.pos,false,false);
                 //now get space in log
                 logPos = logAllocate(physPos);
 
             }finally{
                 .unlock();
             }
 
             //write data into log
             walIndexVal((logPos[0]&) - 1-8-8-1-8, ioRecidphysPos[0]|);
             walPhysArray(outphysPoslogPos);
 
             .put(ioRecid,logPos);
 
         }catch(IOException e){
             throw new IOError(e);
         }finally{
             lock.unlock();
         }
         .offer(out);
         return true;
     }
 
     @Override
     public <A> void delete(long recidSerializer<A> serializer) {
         assert(recid>0);
         final long ioRecid =  + recid*8;
         final Lock lock  = [Store.lockPos(ioRecid)].writeLock();
         lock.lock();
         try{
             final long logPos;
 
             long indexVal = 0;
             long[] linkedRecords = getLinkedRecordsFromLog(ioRecid);
             if(linkedRecords==null){
                 indexVal = .getLong(ioRecid);
                 if(indexVal==return;
                 linkedRecords = getLinkedRecordsIndexVals(indexVal);
             }
             .lock();
             try{
                 openLogIfNeeded();
                 logPos = ;
                 checkLogRounding();
                 +=1+8+8; //space used for index val
                 .ensureAvailable();
                 longStackPut(ioRecid,false);
 
                 //free first record pointed from indexVal
                 if((indexVal>>>48)>0)
                     freePhysPut(indexVal,false);
 
                 //if there are more linked records, free those as well
                 if(linkedRecords!=null){
                     for(int i=0; i<linkedRecords.length &&linkedRecords[i]!=0;i++){
                         freePhysPut(linkedRecords[i],false);
                     }
                 }
 
             }finally {
                 .unlock();
             }
             walIndexVal(logPos,ioRecid,0|);
             .put(ioRecid);
         }finally {
             lock.unlock();
         }
     }
 
     @Override
     public void commit() {
         lockAllWrite();
         try{
             if(!=null && .hasUnsavedChanges()){
                 .save(this);
             }
 
             if(!.isEmpty() && ==nullopenLogIfNeeded();
 
             if(==null){
                 return//no modifications
             }
             //dump long stack pages
             int crc = 0;
             LongMap.LongMapIterator<byte[]> iter = .longMapIterator();
             while(iter.moveToNext()){
                 assert(iter.key()>>>48==0);
                 final byte[] array = iter.value();
                 final long pageSize = ((array[0]&0xFF)<<8)|(array[1]&0xFF) ;
                 assert(array.length==pageSize);
                 final long firstVal = (pageSize<<48)|iter.key();
                 .ensureAvailable(+1+8+pageSize);
 
                 crc |= LongHashMap.longHash(||firstVal);
 
                 .putByte();
                 +=1;
                 .putLong(firstVal);
                 +=8;
 
                 //put array
                 CRC32 crc32  = new CRC32();
                 crc32.update(array);
                 crc |= crc32.getValue();
                 .putData(,array,0,array.length);
                 +=array.length;
 
                 checkLogRounding();
             }
 
 
             for(int i=;i<;i+=8){
                 if(![i/8]) continue;
                 .ensureAvailable( + 17);
                 +=17;
                 walIndexVal(-17, i,[i/8]);
                 //no need to update crc, since IndexVal already does it
             }
 
             //seal log file
             .ensureAvailable( + 1 + 3*6 + 8+4);
             long indexChecksum = indexHeaderChecksumUncommited();
             crc|=LongHashMap.longHash(|||||indexChecksum);
             .putByte();
             +=1;
             .putSixLong();
             +=6;
             .putSixLong(,);
             +=6;
             .putSixLong(,);
             +=6;
             .putLong(indexChecksum);
             +=8;
             .putInt(crc|.get());
             +=4;
 
             //write mark it was sealed
             .putLong(8, );
 
             //and flush log file
             if(!.sync();
 
             replayLogFile();
             reloadIndexFile();
         }finally {
              unlockAllWrite();
         }
 
     }
 
     protected long indexHeaderChecksumUncommited() {
         long ret = 0;
 
         for(int offset = 0;offset<;offset+=8){
             if(offset == continue;
             long indexVal;
 
             if(offset==){
                 indexVal = ;
             }else if(offset==){
                 indexVal = ;
             }else if(offset==){
                 indexVal = ;
             }else
                 indexVal = [offset / 8];
 
             ret |=  indexVal | LongHashMap.longHash(indexVal|offset) ;
         }
 
         return ret;
     }
 
     protected boolean verifyLogFile() {
         assert(.isHeldByCurrentThread());
 
         if( && ==null)
             return false;
 
          = 0;
 
 
 
         //read headers
         if(.isEmpty() || .getInt(0)!=  || .getLong(8) !=){
             //wrong headers, discard log
             .close();
             .deleteFile();
              = null;
             return false;
         }
 
         if(.getUnsignedShort(4)>){
             throw new IOError(new IOException("New store format version, please use newer MapDB version"));
         }
 
         if(.getUnsignedShort(6)!=expectedMasks())
             throw new IllegalArgumentException("Log file created with different features. Please check compression, checksum or encryption");
 
 
 
         final CRC32 crc32 = new CRC32();
 
         //all good, calculate checksum
         =16;
         byte ins = .getByte();
         +=1;
         int crc = 0;
 
         while(ins!=try{
             if(ins == ){
                 long ioRecid = .getLong();
                 +=8;
                 long indexVal = .getLong();
                 +=8;
                 crc |= LongHashMap.longHash((-1-8-8) |  | ioRecid | indexVal);
             }else if(ins == ){
                 final long offset2 = .getLong();
                 +=8;
                 final int size = (int) (offset2>>>48);
 
                 byte[] b = new byte[size];
                 try{
                     .getDataInput(size).readFully(b);
                 } catch (IOException e) {
                     throw new IOError(e);
                 }
                 crc32.reset();
                 crc32.update(b);
 
                 crc |= LongHashMap.longHash( |  | offset2 | crc32.getValue());
 
                 +=size;
             }else if(ins == ){
                 final long offset2 = .getLong();
                 +=8;
                 final int size = (int) (offset2>>>48)-8;
 
                 final long nextPageLink = .getLong();
                 +=8;
 
                 byte[] b = new byte[size];
                 .getDataInput(size).readFully(b);
                 crc32.reset();
                 crc32.update(b);
 
                 crc |= LongHashMap.longHash(() |  | offset2 | nextPageLink | crc32.getValue());
 
                 +=size;
             }else if(ins == ){
                 final long offset = .getLong();
                 +=8;
                 final long origLogSize = ;
                 final int size = (int) (offset>>>48);
 
                 crc |= LongHashMap.longHash(origLogSize |  | offset );
 
                 byte[] b = new byte[size];
                 .getDataInput(size).readFully(b);
                 crc32.reset();
                 crc32.update(b);
                 crc|=crc32.getValue();
 
                 .getDataInput(size).readFully(b);
             }else if(ins == ){
                  += . -(&.);
             }else{
                 throw new AssertionError("unknown trans log instruction '"+ins +"' at log offset: "+(-1));
             }
 
             ins = .getByte();
             +=1;
         } catch (IOException e) {
             throw new IOError(e);
         }
 
         long indexSize = .getSixLong();
         +=6;
         long physSize = .getSixLong();
         +=6;
         long freeSize = .getSixLong();
         +=6;
         long indexSum = .getLong();
         +=8;
         crc |= LongHashMap.longHash((-1-3*6-8)|indexSize|physSize|freeSize|indexSum);
 
         final int realCrc = .getInt();
         +=4;
 
         =0;
         assert(.isHeldByCurrentThread());
         if(realCrc == .)
             return true//in future WAL CRC might be switched off, in that case this value will be used
 
         return realCrc == crc ;
     }
 
 
 
     protected void replayLogFile(){
         assert(.isHeldByCurrentThread());
 
         if( && ==null)
             return;
 
          = 0;
 
 
         //read headers
         if(.isEmpty() || .getInt(0)!= ||
                 .getUnsignedShort(4)> || .getLong(8) != ||
                 .getUnsignedShort(6)!=expectedMasks()){
             //wrong headers, discard log
             .close();
             .deleteFile();
              = null;
             return;
         }
 
 
         //all good, start replay
         =16;
         byte ins = .getByte();
         +=1;
 
         while(ins!=){
             if(ins == ){
                 long ioRecid = .getLong();
                 +=8;
                 long indexVal = .getLong();
                 +=8;
                 .ensureAvailable(ioRecid+8);
                 .putLong(ioRecidindexVal);
             }else if(ins == ||ins ==  || ins == ){
                 long offset = .getLong();
                 +=8;
                 final int size = (int) (offset>>>48);
                 offset = offset&;
 
                 //transfer byte[] directly from log file without copying into memory
                 DataInput2 input = .getDataInput(size);
                 ByteBuffer buf = input.buf.duplicate();
 
                 buf.position(input.pos);
                 buf.limit(input.pos+size);
                 .ensureAvailable(offset+size);
                 .putData(offsetbuf);
 
                 +=size;
             }else if(ins == ){
                  += . -(&.);
             }else{
                 throw new AssertionError("unknown trans log instruction '"+ins +"' at log offset: "+(-1));
             }
 
             ins = .getByte();
             +=1;
         }
         +=6;
         +=6;
         +=6;
         +=8;
 
 
         =0;
 
         //flush dbs
         if(!){
             .sync();
             .sync();
         }
         //and discard log
         .putLong(0, 0);
         .putLong(8, 0); //destroy seal to prevent log file from being replayed
         .close();
         .deleteFile();
          = null;
         assert(.isHeldByCurrentThread());
     }
 
 
 
     @Override
     public void rollback() throws UnsupportedOperationException {
         lockAllWrite();
         try{
             //discard trans log
             if( !=null){
                 .close();
                 .deleteFile();
                  = null;
             }
 
             reloadIndexFile();
         }finally {
             unlockAllWrite();
         }
     }
 
     protected long[] getLinkedRecordsFromLog(long ioRecid){
         assert([Store.lockPos(ioRecid)].writeLock().isHeldByCurrentThread());
         long[] ret0 = .get(ioRecid);
         if(ret0==return ret0;
 
         if(ret0!=null && ret0!=){
             long[] ret = new long[ret0.length];
             for(int i=0;i<ret0.length;i++){
                 long offset = ret0[i] & ;
                 //offset now points to log file, read phys offset from log file
                 ret[i] =  .getLong(offset-8);
             }
             return ret;
         }
         return null;
     }
 
     @Override
     protected long longStackTake(long ioListboolean recursive) {
         assert(.isHeldByCurrentThread());
         assert(ioList>= && ioList<) :"wrong ioList: "+ioList;
 
 
         long dataOffset = [((intioList/8)];
         if(dataOffset == 0)
             return 0; //there is no such list, so just return 0
 
         long pos = dataOffset>>>48;
         dataOffset &= ;
         byte[] page = longStackGetPage(dataOffset);
 
         if(pos<8) throw new AssertionError();
 
         final long ret = longStackGetSixLong(page, (intpos);
 
         //was it only record at that page?
         if(pos == 8){
             //yes, delete this page
             long next = longStackGetSixLong(page,2);
             long size = ((page[0]&0xFF)<<8) | (page[1]&0xFF);
             assert(size == page.length);
             if(next !=0){
                 //update index so it points to previous page
                 byte[] nextPage = longStackGetPage(next); //TODO this page is not modifed, but is added to LOG
                 long nextSize = ((nextPage[0]&0xFF)<<8) | (nextPage[1]&0xFF);
                 assert((nextSize-8)%6==0);
                 [((intioList/8)]=((nextSize-6)<<48)|next;
                 [((intioList/8)]=true;
             }else{
                 //zero out index
                 [((intioList/8)]=0L;
                 [((intioList/8)]=true;
                 if(==ioList){
                     //max value was just deleted, so find new maxima
                     while([((int/8)]==0 && >){
                         -=8;
                     }
                 }
             }
             //put space used by this page into free list
             freePhysPut((size<<48) | dataOffsettrue);
             assert(dataOffset>>>48==0);
             .remove(dataOffset);
         }else{
             //no, it was not last record at this page, so just decrement the counter
             pos-=6;
             [((intioList/8)] = (pos<<48)| dataOffset;
             [((intioList/8)] = true;
         }
 
         //System.out.println("longStackTake: "+ioList+" - "+ret);
 
         return ret;
 
     }
 
     @Override
     protected void longStackPut(long ioListlong offsetboolean recursive) {
         assert(.isHeldByCurrentThread());
         assert(offset>>>48==0);
         assert(ioList>= && ioList<=): "wrong ioList: "+ioList;
 
         long dataOffset = [((intioList/8)];
         long pos = dataOffset>>>48;
         dataOffset &= ;
 
         if(dataOffset == 0){ //empty list?
             //yes empty, create new page and fill it with values
             final long listPhysid = freePhysTake((int,true,true) &;
             if(listPhysid == 0) throw new AssertionError();
             assert(listPhysid>>>48==0);
             //set previous Free Index List page to zero as this is first page
             //also set size of this record
             byte[] page = new byte[(int];
             page[0] = (byte) (0xFF & (page.length>>>8));
             page[1] = (byte) (0xFF & (page.length));
             longStackPutSixLong(page,2,0L);
             //set  record
             longStackPutSixLong(page, 8, offset);
             //and update index file with new page location
             [((intioList/8)] = ( 8L << 48) | listPhysid;
             [((intioList/8)] = true;
             if(<=ioList=ioList;
             .put(listPhysid,page);
         }else{
             byte[] page = longStackGetPage(dataOffset);
             long size = ((page[0]&0xFF)<<8)|(page[1]&0xFF);
 
             assert(pos+6<=size);
             if(pos+6==size){ //is current page full?
                long newPageSize = ;
                if(ioList == size2ListIoRecid()){
                    //TODO double allocation fix needs more investigation
                    newPageSize = ;
                }
                //yes it is full, so we need to allocate new page and write our number there
                final long listPhysid = freePhysTake((intnewPageSize,true,true) &;
                if(listPhysid == 0) throw new AssertionError();
                byte[] newPage = new byte[(intnewPageSize];
                //set current page size
                newPage[0] = (byte) (0xFF & (newPageSize>>>8));
                newPage[1] = (byte) (0xFF & (newPageSize));
                //set location to previous page and
                longStackPutSixLong(newPage,2,dataOffset&);
                //set the value itself
                longStackPutSixLong(newPage, 8, offset);
                assert(listPhysid>>>48==0);
                .put(listPhysid,newPage);
                //and update index file with new page location and number of records
                [((intioList/8)] = (8L<<48) | listPhysid;
                [((intioList/8)] = true;
            }else{
                //there is space on page, so just write offset and increase the counter
                pos+=6;
                longStackPutSixLong(page, (intpos,offset);
                [((intioList/8)] = (pos<<48)| dataOffset;
                [((intioList/8)] = true;
            }
        }
    }
    protected static long