Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   *  Copyright (c) 2012 Jan Kotek
   *
   *  Licensed under the Apache License, Version 2.0 (the "License");
   *  you may not use this file except in compliance with the License.
   *  You may obtain a copy of the License at
   *
   *    http://www.apache.org/licenses/LICENSE-2.0
   *
  *  Unless required by applicable law or agreed to in writing, software
  *  distributed under the License is distributed on an "AS IS" BASIS,
  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
 package org.mapdb;
 
 import java.io.File;
Storage Engine which saves record directly into file. It has zero protection from data corruption and must be closed properly after modifications. It is used when Write-Ahead-Log transactions are disabled. Storage format ---------------- `StoreDirect` is composed of two files: Index file is sequence of 8-byte longs, it translates `recid` (offset in index file) to record size and offset in physical file. Records position may change, but it requires stable ID, so the index file is used for translation. This store uses data structure called `Long Stack` to manage (and reuse) free space, it is is linked LIFO queue of 8-byte longs. Index file -------------- Index file is translation table between permanent record ID (recid) and mutable location in physical file. Index file is sequence of 8-byte longs, one for each record. It also has some extra longs to manage free space and other metainfo. Index table and physical data could be stored in single file, but keeping index table separate simplifies compaction. Basic **structure of index file** is bellow. Each slot is 8-bytes long so `offset=slot*8` slot | in code | description --- | --- | --- 0 | HEADER | File header 1 | IO_INDEX_SIZE | Allocated file size of index file in bytes. 2 | IO_PHYS_SIZE | Allocated file size of physical file in bytes. 3 | IO_FREE_SIZE | Space occupied by free records in physical file in bytes. 4..9 | | Reserved for future use 10..14 | | For usage by user 15 | IO_FREE_RECID |Long Stack of deleted recids, those will be reused and returned by Engine.put(java.lang.Object,org.mapdb.Serializer) 16..4111 | |Long Stack of free physical records. This contains free space released by record update or delete. Each slots corresponds to free record size. TODO check 4111 is right 4112 | IO_USER_START |Record size and offset in physical file for recid=1 4113 | |Record size and offset in physical file for recid=2 ... | ... |... snip ... N+4111 | |Record size and offset in physical file for recid=N Long Stack ------------ Long Stack is data structure used to store free records. It is LIFO queue which uses linked records to store 8-byte longs. Long Stack is identified by slot in Index File, which stores pointer to Long Stack head. The structure of of index pointer is following: byte | description --- |--- 0..1 | relative offset in head Long Stack Record to take value from. This value decreases by 8 each take 2..7 | physical file offset of head Long Stack Record, zero if Long Stack is empty Each Long Stack Record is sequence of 8-byte longs, first slot is header. Long Stack Record structure is following: byte | description --- |--- 0..1 | length of current Long Stack Record in bytes 2..7 | physical file offset of next Long Stack Record, zero of this record is last 8-15 | Long Stack value 16-23 | Long Stack value ... | and so on until end of Long Stack Record Physical pointer ---------------- Index slot value typically contains physical pointer (information about record location and size in physical file). First 2 bytes are record size (max 65536). Then there is 6 byte offset in physical file (max store size is 281 TB). Physical file offset must always be multiple of 16, so last 4 bites are used to flag extra record information. Structure of **physical pointer**: bite | in code | description --- | --- | --- 0-15 |`val>>>48` | record size 16-59 |`val&MASK_OFFSET` | physical offset 60 |`val&MASK_LINKED!=0` | linked record flag 61 |`val&MASK_DISCARD!=0` | to be discarded while storage is offline flag 62 |`val&MASK_ARCHIVE!=0` | record modified since last backup flag 63 | | not used yet Records in Physical File --------------------------- Records are stored in physical file. Maximal record size size is 64KB, so larger records must be stored in form of the linked list. Each record starts by Physical Pointer from Index File. There is flag in Physical Pointer indicating if record is linked. If record is not linked you may just read ByteBuffer from given size and offset. If record is linked, each record starts with Physical Pointer to next record. So actual data payload is record size-8. The last linked record does not have the Physical Pointer header to next record, there is MASK_LINKED flag which indicates if next record is the last one.

Author(s):
Jan Kotek
public class StoreDirect extends Store{
    protected static final long MASK_OFFSET = 0x0000FFFFFFFFFFF0L;
    protected static final long MASK_LINKED = 0x8L;
    protected static final long MASK_DISCARD = 0x4L;
    protected static final long MASK_ARCHIVE = 0x2L;
    protected static final long HEADER = 9032094932889042394L;

    
maximal non linked record size
    protected static final int MAX_REC_SIZE = 65536-1;

    
number of free physical slots
    protected static final int PHYS_FREE_SLOTS_COUNT = 2048*2;

    
index file offset where current size of index file is store
    protected static final int IO_INDEX_SIZE = 1*8;
    
index file offset where current size of phys file is stored
    protected static final int IO_PHYS_SIZE = 2*8;

    
index file offset where space occupied by free phys records is stored
    protected static final int IO_FREE_SIZE = 3*8;

    
index file offset where reference to longstack of free recid is store
    protected static final int IO_FREE_RECID = 15*8;

    
index file offset where first recid available to user is stored
    protected static final int IO_USER_START = +*8+8;
    public static final String DATA_FILE_EXT = ".p";
    protected final static int LONG_STACK_PREF_COUNT = 204;
    protected final static long LONG_STACK_PREF_SIZE = 8+*6;
    protected final static int LONG_STACK_PREF_COUNT_ALTER = 212;
    protected final static long LONG_STACK_PREF_SIZE_ALTER = 8+*6;
    protected final ReentrantReadWriteLock[] locks = Utils.newReadWriteLocks();
    protected final ReentrantLock structuralLock = new ReentrantLock();
    protected Volume index;
    protected Volume phys;
    protected long physSize;
    protected long indexSize;
    protected long freeSize;
    protected final boolean deleteFilesAfterClose;
    protected final boolean readOnly;
    protected final boolean syncOnCommitDisabled;
    protected final boolean spaceReclaimReuse;
    protected final boolean spaceReclaimSplit;
    protected final boolean spaceReclaimTrack;
    protected final long sizeLimit;
    public StoreDirect(Volume.Factory volFacboolean readOnlyboolean deleteFilesAfterClose,
                       int spaceReclaimModeboolean syncOnCommitDisabledlong sizeLimit,
                       boolean checksumboolean compressbyte[] password) {
        super(checksumcompresspassword);
        this. = readOnly;
        this. = deleteFilesAfterClose;
        this. = syncOnCommitDisabled;
        this. = sizeLimit;
        this. = spaceReclaimMode>4;
        this. = spaceReclaimMode>2;
        this. = spaceReclaimMode>0;
         = volFac.createIndexVolume();
         = volFac.createPhysVolume();
        if(.isEmpty()){
            createStructure();
        }else{
            checkHeaders();
             = .getLong();
             = .getLong();
             = .getLong();
        }
    }
    public StoreDirect(Volume.Factory volFac) {
        this(volFacfalse,false,5,false,0L, false,false,null);
    }
    protected void checkHeaders() {
        if(.getLong(0)!=||.getLong(0)!=)throw new IOError(new IOException("storage has invalid header"));
    }
    protected void createStructure() {
        for(int i=0;i<;i+=8) .putLong(i,0L);
        .putLong(0, );
         =16;
        .ensureAvailable();
        .putLong(0, );
         = 0;
    }
    @Override
    public <A> long put(A valueSerializer<A> serializer) {
        assert(value!=null);
        DataOutput2 out = serialize(valueserializer);
        .lock();
        final long ioRecid;
        final long[] indexVals;
        try{
            ioRecid = freeIoRecidTake(true) ;
            indexVals = physAllocate(out.pos,true,false);
        }finally {
            .unlock();
        }
        put2(outioRecidindexVals);
        .offer(out);
        return (ioRecid-)/8;
    }
    protected void put2(DataOutput2 outlong ioRecidlong[] indexVals) {
        .putLong(ioRecidindexVals[0]|);
        //write stuff
        if(indexVals.length==1||indexVals[1]==0){ //is more then one? ie linked
            //write single
            .putData(indexVals[0]&out.buf, 0, out.pos);
        }else{
            int outPos = 0;
            //write linked
            for(int i=0;i<indexVals.length;i++){
                final int c =   i==indexVals.length-1 ? 0: 8;
                final long indexVal = indexVals[i];
                final boolean isLast = (indexVal & ) ==0;
                if(isLast!=(i==indexVals.length-1)) throw new InternalError();
                final int size = (int) (indexVal>>>48);
                final long offset = indexVal&;
                //write data
                .putData(offset+c,out.buf,outPossize-c);
                outPos+=size-c;
                if(c>0){
                    //write position of next linked record
                    .putLong(offsetindexVals[i + 1]);
                }
            }
              if(outPos!=out.posthrow new InternalError();
        }
    }
    @Override
    public <A> A get(long recidSerializer<A> serializer) {
        assert(recid>0);
        final long ioRecid =  + recid*8;
        final Lock lock  = [Utils.longHash(recid)&.].readLock();
        lock.lock();
        try{
            return get2(ioRecid,serializer);
        }catch(IOException e){
            throw new IOError(e);
        }finally{
            lock.unlock();
        }
    }
    protected <A> A get2(long ioRecid,Serializer<A> serializerthrows IOException {
        long indexVal = .getLong(ioRecid);
        int size = (int) (indexVal>>>48);
        DataInput2 di;
        long offset = indexVal&;
        if((indexVal)==0){
            //read single record
            di = .getDataInput(offsetsize);
        }else{
            //is linked, first construct buffer we will read data to
            int pos = 0;
            int c = 8;
            //TODO use mapped bb and direct copying?
            byte[] buf = new byte[64];
            //read parts into segment
            for(;;){
                DataInput2 in = .getDataInput(offset + csize-c);
                if(buf.length<pos+size-c)
                    buf = Arrays.copyOf(buf,Math.max(pos+size-c,buf.length*2)); //buf to small, grow
                in.readFully(buf,pos,size-c);
                pos+=size-c;
                if(c==0) break;
                //read next part
                long next = .getLong(offset);
                offset = next&;
                size = (int) (next>>>48);
                //is the next part last?
                c =  ((next)==0)? 0 : 8;
            }
            di = new DataInput2(buf);
            size = pos;
        }
        return deserialize(serializersizedi);
    }
    @Override
    public <A> void update(long recid, A valueSerializer<A> serializer) {
        assert(value!=null);
        assert(recid>0);
        DataOutput2 out = serialize(valueserializer);
        final long ioRecid =  + recid*8;
        final Lock lock  = [Utils.longHash(recid)&.].writeLock();
        lock.lock();
        try{
            update2(outioRecid);
        }finally{
            lock.unlock();
        }
        .offer(out);
    }
    protected void update2(DataOutput2 outlong ioRecid) {
        final long indexVal = .getLong(ioRecid);
        final long size = indexVal>>>48;
        final boolean linked = (indexVal&)!=0;
        if(!linked && out.pos>0 && size>0 && size2ListIoRecid(size) == size2ListIoRecid(out.pos)){
            //size did change, but still fits into this location
            final long offset = indexVal & ;
            //note: if size would not change, we still have to write MASK_ARCHIVE bit
            .putLong(ioRecid, (((long)out.pos)<<48)|offset|);
            .putData(offsetout.buf, 0, out.pos);
        }else{
            long[] indexVals =  ? getLinkedRecordsIndexVals(indexVal) : null;
            .lock();
            try{
                if(){
                    //free first record pointed from indexVal
                    freePhysPut(indexVal,false);
                    //if there are more linked records, free those as well
                    if(indexVals!=null){
                        for(int i=0;i<indexVals.length && indexVals[i]!=0;i++){
                            freePhysPut(indexVals[i],false);
                        }
                    }
                }
                indexVals = physAllocate(out.pos,true,false);
            }finally {
                .unlock();
            }
            put2(outioRecidindexVals);
        }
    }
    @Override
    public <A> boolean compareAndSwap(long recid, A expectedOldValue, A newValueSerializer<A> serializer) {
        assert(expectedOldValue!=null && newValue!=null);
        assert(recid>0);
        final long ioRecid =  + recid*8;
        final Lock lock  = [Utils.longHash(recid)&.].writeLock();
        lock.lock();
        DataOutput2 out;
        try{
            /*
             * deserialize old value
             */
            A oldVal = get2(ioRecid,serializer);
            /*
             * compare oldValue and expected
             */
            if((oldVal == null && expectedOldValue!=null) || (oldVal!=null && !oldVal.equals(expectedOldValue)))
                return false;
            /*
             * write new value
             */
             out = serialize(newValueserializer);
            update2(outioRecid);
        }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  = [Utils.longHash(recid)&.].writeLock();
        lock.lock();
        try{
            //get index val and zero it out
            final long indexVal = .getLong(ioRecid);
            .putLong(ioRecid,0L|);
            if(!return//free space is not tracked, so do not mark stuff as free
            long[] linkedRecords = getLinkedRecordsIndexVals(indexVal);
            //now lock everything and mark free space
            .lock();
            try{
                //free recid
                freeIoRecidPut(ioRecid);
                //free first record pointed from indexVal
                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();
            }
        }finally{
            lock.unlock();
        }
    }
    protected long[] getLinkedRecordsIndexVals(long indexVal) {
        long[] linkedRecords = null;
        int linkedPos = 0;
        if((indexVal)!=0){
            //record is composed of multiple linked records, so collect all of them
            linkedRecords = new long[2];
            //traverse linked records
            long linkedVal = .getLong(indexVal&);
            for(;;){
                if(linkedPos==linkedRecords.length//grow if necessary
                    linkedRecords = Arrays.copyOf(linkedRecordslinkedRecords.length * 2);
                //store last linkedVal
                linkedRecords[linkedPos] = linkedVal;
                if((linkedVal)==0){
                    break//this is last linked record, so break
                }
                //move and read to next
                linkedPos++;
                linkedVal = .getLong(linkedVal&);
            }
        }
        return linkedRecords;
    }
    protected long[] physAllocate(int sizeboolean ensureAvail,boolean recursive) {
        if(size==0L) return new long[]{0L};
        //append to end of file
        if(size<){
            long indexVal = freePhysTake(size,ensureAvail,recursive);
            indexVal |= ((long)size)<<48;
            return new long[]{indexVal};
        }else{
            long[] ret = new long[2];
            int retPos = 0;
            int c = 8;
            while(size>0){
                if(retPos == ret.lengthret = Arrays.copyOf(retret.length*2);
                int allocSize = Math.min(size);
                size -= allocSize - c;
                //append to end of file
                long indexVal = freePhysTake(allocSizeensureAvail,recursive);
                indexVal |= (((long)allocSize)<<48);
                if(c!=0) indexVal|= ;
                ret[retPos++] = indexVal;
                c = size<= ? 0 : 8;
            }
            if(size!=0) throw new InternalError();
            return Arrays.copyOf(retretPos);
        }
    }
    protected static long roundTo16(long offset){
        long rem = offset&15;  // modulo 16
        if(rem!=0) offset +=16-rem;
        return offset;
    }
    @Override
    public void close() {
        .lock();
        for(ReentrantReadWriteLock lock:lock.writeLock().lock();
        if(!){
            .putLong(,);
            .putLong(,);
            .putLong(,);
        }
        .sync();
        .sync();
        .close();
        .close();
        if(){
            .deleteFile();
            .deleteFile();
        }
         = null;
         = null;
        for(ReentrantReadWriteLock lock:lock.writeLock().unlock();
        .unlock();
    }
    @Override
    public boolean isClosed() {
        return ==null;
    }
    @Override
    public void commit() {
        if(!){
            .putLong(,);
            .putLong(,);
            .putLong(,);
        }
        if(!){
            .sync();
            .sync();
        }
    }
    @Override
    public void rollback() throws UnsupportedOperationException {
        throw new UnsupportedOperationException("rollback not supported with journal disabled");
    }
    @Override
    public boolean isReadOnly() {
        return ;
    }
    @Override
    public boolean canRollback(){
        return false;
    }
    @Override
    public void clearCache() {
    }
    @Override
    public void compact() {
        if(throw new IllegalAccessError();
        if(.getFile()==nullthrow new UnsupportedOperationException("compact not supported for memory storage yet");
        .lock();
        for(ReentrantReadWriteLock l:l.writeLock().lock();
        try{
            //create secondary files for compaction
            //TODO RAF
            //TODO memory based stores
            final File indexFile = .getFile();
            final File physFile = .getFile();
            int rafMode = 0;
            if( instanceof  Volume.FileChannelVolrafMode=2;
            if( instanceof  Volume.MappedFileVol &&  instanceof Volume.FileChannelVolrafMode = 1;
            final boolean isRaf =  instanceof Volume.FileChannelVol;
            Volume.Factory fab = Volume.fileFactory(falserafModenew File(indexFile+".compact"),);
            StoreDirect store2 = new StoreDirect(fab,false,false,5,false,0L, ,,);
            store2.structuralLock.lock();
            //transfer stack of free recids
            for(long recid =longStackTake(,false);
                recid!=0; recid=longStackTake(,false)){
                store2.longStackPut(recid,false);
            }
            //iterate over recids and transfer physical records
            store2.index.putLong();
            for(long ioRecid = ioRecid<;ioRecid+=8){
                byte[] bb = get2(ioRecid,.);
                store2.index.ensureAvailable(ioRecid+8);
                if(bb==null||bb.length==0){
                    store2.index.putLong(ioRecid,0);
                }else{
                    DataOutput2 out = serialize(bb,.);
                    long[] indexVals = store2.physAllocate(out.pos,true,false);
                    store2.put2(outioRecid,indexVals);
                }
            }
            File indexFile2 = store2.index.getFile();
            File physFile2 = store2.phys.getFile();
            store2.structuralLock.unlock();
            store2.close();
            long time = System.currentTimeMillis();
            File indexFile_ = new File(indexFile.getPath()+"_"+time+"_orig");
            File physFile_ = new File(physFile.getPath()+"_"+time+"_orig");
            .close();
            .close();
            if(!indexFile.renameTo(indexFile_))throw new InternalError("could not rename file");
            if(!physFile.renameTo(physFile_))throw new InternalError("could not rename file");
            if(!indexFile2.renameTo(indexFile))throw new InternalError("could not rename file");
            //TODO process may fail in middle of rename, analyze sequence and add recovery
            if(!physFile2.renameTo(physFile))throw new InternalError("could not rename file");
            indexFile_.delete();
            physFile_.delete();
            Volume.Factory fac2 = Volume.fileFactory(falserafModeindexFile,);
             = fac2.createIndexVolume();
             = fac2.createPhysVolume();
             = store2.physSize;
            .putLong();
            .putLong();
            .putLong();
        }catch(IOException e){
            throw new IOError(e);
        }finally {
            .unlock();
            for(ReentrantReadWriteLock l:l.writeLock().unlock();
        }
    }
    protected long longStackTake(final long ioListboolean recursive) {
//        if(recursive) throw new InternalError();
        if(!.isLocked())throw new InternalError();
        if(ioList< || ioList>=throw new IllegalArgumentException("wrong ioList: "+ioList);
        long dataOffset = .getLong(ioList);
        if(dataOffset == 0) return 0; //there is no such list, so just return 0
        long pos = dataOffset>>>48;
        dataOffset &= ;
        if(pos<8) throw new InternalError();
        final long ret = .getSixLong(dataOffset + pos);
        //was it only record at that page?
        if(pos == 8){
            //yes, delete this page
            long next =.getLong(dataOffset);
            long size = next>>>48;
            next &=;
            if(next !=0){
                //update index so it points to previous page
                long nextSize = .getUnsignedShort(next);
                assert((nextSize-8)%6==0);
                .putLong(ioList , ((nextSize-6)<<48)|next);
            }else{
                //zero out index
                .putLong(ioList , 0L);
            }
            //put space used by this page into free list
            freePhysPut((size<<48) | dataOffsettrue);
        }else{
            //no, it was not last record at this page, so just decrement the counter
            pos-=6;
            .putLong(ioList, (pos<<48)| dataOffset); //TODO update just 2 bytes
        }
        //System.out.println("longStackTake: "+ioList+" - "+ret);
        return ret;
    }
    protected void longStackPut(final long ioListlong offsetboolean recursive){
//        if(recursive) throw new InternalError();
        if(offset>>>48!=0) throw new IllegalArgumentException();
        if(!.isLocked())throw new InternalError();
        if(ioList< || ioList>=throw new InternalError("wrong ioList: "+ioList);
        long dataOffset = .getLong(ioList);
        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 InternalError();
            //set previous Free Index List page to zero as this is first page
            //also set size of this record
            .putLong(listPhysid ,  << 48);
            //set  record
            .putSixLong(listPhysid + 8, offset);
            //and update index file with new page location
            .putLong(ioList , ( 8L << 48) | listPhysid);
        }else{
            long next = .getLong(dataOffset);
            long size = next>>>48;
            next &=;
            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 InternalError();
                //set location to previous page and set current page size
                .putLong(listPhysid, (newPageSize<<48)|(dataOffset&));
                //set the value itself
                .putSixLong(listPhysid+8, offset);
                //and update index file with new page location and number of records
                .putLong(ioList , (8L<<48) | listPhysid);
            }else{
                //there is space on page, so just write offset and increase the counter
                pos+=6;
                .putSixLong(dataOffset + posoffset);
                .putLong(ioList, (pos<<48)| dataOffset); //TODO update just 2 bytes
            }
        }
    }
    protected void freeIoRecidPut(long ioRecid) {
        if()
            longStackPut(ioRecid,false);
    }
    protected long freeIoRecidTake(boolean ensureAvail){
        if(){
            long ioRecid = longStackTake(,false);
            if(ioRecid!=0) return ioRecid;
        }
        +=8;
        if(ensureAvail)
            .ensureAvailable();
        return -8;
    }
    protected static long size2ListIoRecid(long size){
        return  + 8 + ((size-1)/16)*8;
    }
    protected void freePhysPut(long indexValboolean recursive) {
        long size = indexVal >>>48;
        +=roundTo16(size);
        longStackPut(size2ListIoRecid(size), indexVal & ,recursive);
    }
    protected long freePhysTake(int sizeboolean ensureAvailboolean recursive) {
        if(size==0)throw new IllegalArgumentException();
        //check free space
        if(){
            long ret =  longStackTake(size2ListIoRecid(size),recursive);
            if(ret!=0){
                -=roundTo16(size);
                return ret;
            }
        }
        //try to take large record and split it into two
        if(!recursive &&  ){
            for(long s=  roundTo16(size)+16;s<;s+=16){
                long ret = longStackTake(size2ListIoRecid(s),recursive);
                if(ret!=0){
                    //found larger record, split in two chunks, take first, mark second free
                    final long offset = ret & ;
                    long remaining = s - roundTo16(size);
                    long markFree = (remaining<<48) | (offset+s-remaining);
                    freePhysPut(markFree,recursive);
                    -=roundTo16(s);
                    return (((long)size)<<48) |offset;
                }
            }
        }
        //not available, increase file size
             += . - (&.);
        long physSize2 = ;
         = roundTo16(+size);
        if(ensureAvail)
            .ensureAvailable();
        return physSize2;
    }
    @Override
    public long getMaxRecid() {
        return (-)/8;
    }
    @Override
    public ByteBuffer getRaw(long recid) {
        //TODO use direct BB
        byte[] bb = get(recid.);
        if(bb==nullreturn null;
        return ByteBuffer.wrap(bb);
    }
    @Override
    public Iterator<LonggetFreeRecids() {
        return .//TODO iterate over stack of free recids, without modifying it
    }
    @Override
    public void updateRaw(long recidByteBuffer data) {
        long ioRecid = recid*8 + ;
        if(ioRecid>=){
             = ioRecid+8;
            .ensureAvailable();
        }
        byte[] b = null;
        if(data!=null){
            data = data.duplicate();
            b = new byte[data.remaining()];
            data.get(b);
        }
        //TODO use BB without copying
        update(recidb.);
    }
    @Override
    public long getSizeLimit() {
        return ;
    }
    @Override
    public long getCurrSize() {
        return ;
    }
    @Override
    public long getFreeSize() {
        return ;
    }
    @Override
    public String calculateStatistics() {
        String s = "";
        s+=getClass().getName()+"\n";
        s+="volume: "+"\n";
        s+="  "++"\n";
        s+="indexSize="++"\n";
        s+="physSize="++"\n";
        s+="freeSize="++"\n";
        s+="num of freeRecids: "+countLongStackItems()+"\n";
        for(int size = 16;size<+10;size*=2){
            long sum = 0;
            for(int ss=size/2;ss<size;s+=16){
                sum+=countLongStackItems(size2ListIoRecid(ss))*ss;
            }
            s+="Size occupied by free records (size="+size+") = "+sum;
        }
        return s;
    }
    protected long countLongStackItems(long ioList){
        long ret=0;
        long v = .getLong(ioList);
        while(true){
            long next = v&;
            if(next==0) return ret;
            ret+=v>>>48;
            v = .getLong(next);
        }
    }
New to GrepCode? Check out our FAQ X