📄 recordstore.java
字号:
r = (r << 8) | ((int)(data[offset++]) & 0xff); r = (r << 8) | ((int)(data[offset++]) & 0xff); return r; } /** * A convenience method for converting a byte array into * a long (assumes big-endian byte ordering). * * @param data the byte array returned from the database. * @param offset the offset into the array of the first byte to start from. * @return a long corresponding to the first eight bytes * of the array passed in. */ static long getLong(byte[] data, int offset) { long r = data[offset++]; r = (r << 8) | ((long)(data[offset++]) & 0xff); r = (r << 8) | ((long)(data[offset++]) & 0xff); r = (r << 8) | ((long)(data[offset++]) & 0xff); r = (r << 8) | ((long)(data[offset++]) & 0xff); r = (r << 8) | ((long)(data[offset++]) & 0xff); r = (r << 8) | ((long)(data[offset++]) & 0xff); r = (r << 8) | ((long)(data[offset++]) & 0xff); return r; } /** * A convenience method for converting an integer into * a byte array. * * @param i the integer to turn into a byte array. * @param data a place to store the bytes of <code>i</code>. * @param offset starting point within <code>data<code> to * store <code>i</code>. * * @return the number of bytes written to the array. */ static int putInt(int i, byte[] data, int offset) { data[offset++] = (byte)((i >> 24) & 0xff); data[offset++] = (byte)((i >> 16) & 0xff); data[offset++] = (byte)((i >> 8) & 0xff); data[offset] = (byte)(i & 0xff); return 4; } /** * A convenience method for converting a long into * a byte array. * * @param l the <code>long<code> to turn into a byte array. * @param data a place to store the bytes of <code>l</code>. * @param offset Starting point within <code>data</code> to * store <code>l</code>. * * @return the number of bytes written to the array. */ static int putLong(long l, byte[] data, int offset) { data[offset++] = (byte)((l >> 56) & 0xff); data[offset++] = (byte)((l >> 48) & 0xff); data[offset++] = (byte)((l >> 40) & 0xff); data[offset++] = (byte)((l >> 32) & 0xff); data[offset++] = (byte)((l >> 24) & 0xff); data[offset++] = (byte)((l >> 16) & 0xff); data[offset++] = (byte)((l >> 8) & 0xff); data[offset] = (byte)(l & 0xff); return 8; } /** * Returns all of the recordId's currently in the record store. * * MUST be called after obtaining rsLock, e.g in a * <code>synchronized (rsLock) {</code> block. * * @return an array of the recordId's currently in the record store * or null if the record store is closed. */ int[] getRecordIDs() { if (dbraf == null) { // lower overhead than checkOpen() return null; } int index = 0; int[] tmp = new int[dbNumLiveRecords]; int offset = dbFirstRecordOffset; // start at beginning of file RecordHeader rh = new RecordHeader(); try { while (offset != 0) { rh.load(offset); if (rh.id > 0) { tmp[index++] = rh.id; } offset = rh.nextOffset; } } catch (java.io.IOException ioe) { return null; } return tmp; } /** * Remove free blocks from the record store and compact records * with data into as small a space in <code>rsFile</code> as * possible. Operates from smallest to greatest offset in * <code>rsFile</code>, copying data in chunks towards the * beginning of the file, and updating record store meta-data * as it progresses. * * Warning: This is a slow operation that scales linearly * with rsFile size. * * @exception RecordStoreNotOpenException if this record store * is closed * @exception RecordStoreException if an error occurs during record * store compaction */ private void compactRecords() throws RecordStoreNotOpenException, RecordStoreException { int offset = dbDataStart; // after record store header structure int target = 0; int bytesLeft; int numToMove; byte[] chunkBuffer = new byte[DB_COMPACTBUFFER_SIZE]; RecordHeader rh = new RecordHeader(); int prevRec = 0; while (offset < dbDataEnd) { try { rh.load(offset); } catch (java.io.IOException ioe) { // NOTE - should throw some exception here System.out.println("Unexpected IOException in CompactRS!"); } if (rh.id == -1) { // a free block if (target == 0) { target = offset; } // else skip free block offset += rh.blockSize; } else { // a record block if (target == 0) { // No move needed so far. prevRec = offset; offset += rh.blockSize; } else { int old_offset = target; // Move a record back in the file rh.offset = target; rh.nextOffset = prevRec; try { rh.store(); offset += DB_RECORD_HEADER_LENGTH; target += DB_RECORD_HEADER_LENGTH; bytesLeft = (rh.blockSize - DB_RECORD_HEADER_LENGTH); while (bytesLeft > 0) { if (bytesLeft < DB_COMPACTBUFFER_SIZE) { numToMove = bytesLeft; } else { numToMove = DB_COMPACTBUFFER_SIZE; } dbraf.seek(offset); dbraf.read(chunkBuffer, 0, numToMove); dbraf.seek(target); dbraf.write(chunkBuffer, 0, numToMove); offset += numToMove; target += numToMove; bytesLeft -= numToMove; } } catch (java.io.IOException ioe) { // NOTE - should throw some exception here System.out.println("Unexpected IOException " + "in CompactRS!"); } prevRec = old_offset; } } } if (rh.offset != 0) { dbDataEnd = rh.offset + rh.blockSize; } dbFirstRecordOffset = rh.offset; dbFirstFreeBlockOffset = 0; storeDBState(); } /* * Internal Classes */ /** * A class representing a RecordHeader, which may be the start of a * record block or a free block. (In a free block, only the * <code> NextOffset </code> and <code> BlockSize </code> fields * are valid.) Currently it is a conveinience structure, with * fields visible and directly modifyable by the enclosing * RecordStore class. */ private class RecordHeader { /* * Each record is laid out in the file system as follows: * Bytes - Usage * 00-03 - Record ID * 04-07 - Next record offset * 08-11 - Record size...total (in bytes, big endian). * 12-15 - Length of Data. This is stored separately for the case * where a record has gotten smaller. * 16-xx - Data * * Each free block is laid out in the file system as follows: * Bytes - Usage * 00-03 - Free block ID (set to -1) * 04-07 - Next record offset (not used by free block) * 08-11 - Free block size in bytes (in bytes, big endian) * 12-15 - Next free block offset (zero if this is the last free block) * 16-xx - Data (not used by free block) */ /** REC_ID offset */ private static final int REC_ID = 0; /** NEXT_OFFSET offset */ private static final int NEXT_OFFSET = 4; /** BLOCK_SIZE offset */ private static final int BLOCK_SIZE = 8; /** * data length offset of record block or next free block * offset of free block */ private static final int DATALEN_OR_NEXTFREE = 12; /** DATA_OFFSET offset (offset to record data) */ private static final int DATA_OFFSET = 16; /** offset of the record block or free block in RSFile */ int offset; /** record id -or- -1 if free block */ int id; /** next record offset */ int nextOffset; /** record size -or- free block size */ int blockSize; /** record length -or- next free block offset */ int dataLenOrNextFree; /** * default RecordHeader constructor - creates an empty header */ RecordHeader() { } /** * Creates a new RecordHeader and initializes it with data * read from a RecordStoreFile at offset <code> offset </code>. * * @param _offset seek offset in RecordStoreFile of desired * RecordHeader. * * @exception IOException if there is an error reading the * underlying RecordStoreFile. */ RecordHeader(int _offset) throws java.io.IOException { load(_offset); } /** * Creates a new RecordHeader and initializes it with data * provided in the parameters. This RecordHeader will not * be stored (on disk/in persistant storage) until the * <code> store </code> method is called. * * @param _offset offset in RecordStoreFile where this * RecordHeader should be stored. * @param _id record id of this new RecordHeader * * @param next_offset next RecordHeader in linked list of * records. * @param size total size of the storage block allocated for * this record or free block. * @param len_or_free length of data in this record (may be shorter * than <code>size</code> -or- next free block if this * header is a free block header */ RecordHeader(int _offset, int _id, int next_offset, int size, int len_or_free) { offset = _offset; id = _id; nextOffset = next_offset; blockSize = size; dataLenOrNextFree = len_or_free; } /** * Re-uses a RecordHeader and initializes it with data * read from a RecordStoreFile at offset <code> offset </code>. * * @param _offset seek offset in RecordStoreFile of desired * RecordHeader. * * @exception IOException if there is an error reading the * underlying RecordStoreFile */ void load(int _offset) throws java.io.IOException { offset = _offset; // read rec header from file. dbraf.seek(offset); dbraf.read(recHeadBuf, 0, DB_RECORD_HEADER_LENGTH); id = RecordStore.getInt(recHeadBuf, REC_ID); nextOffset = RecordStore.getInt(recHeadBuf, NEXT_OFFSET); blockSize = RecordStore.getInt(recHeadBuf, BLOCK_SIZE); dataLenOrNextFree = RecordStore.getInt(recHeadBuf, DATALEN_OR_NEXTFREE); } /** * Flushes an in memory RecordHeader instance to storage in a * a RecordStoreFile at <code>offset</code>. * @exception IOException if there is an error writing the * underlying RecordStoreFile */ void store() throws java.io.IOException { RecordStore.putInt(id, recHeadBuf, REC_ID); RecordStore.putInt(nextOffset, recHeadBuf, NEXT_OFFSET); RecordStore.putInt(blockSize, recHeadBuf, BLOCK_SIZE); RecordStore.putInt(dataLenOrNextFree, recHeadBuf, DATALEN_OR_NEXTFREE); // write record header; dbraf.seek(offset); dbraf.write(recHeadBuf, 0, DB_RECORD_HEADER_LENGTH); } /** * Reads data associated with this record from storage (a * RecordStoreFile) at <code>offset</code>. * * Assumes CALLER has ensured <code>dataLenOrNextFree</code> is set * correctly, and that <code>dataLenOrNextFree</code> bytes will * fit into the array. * * @param buf data is read into this buffer. * @param _offset position in <code>buf</code> to start reading * data into. * * @return number of bytes read. * * @exception IOException if there is an error reading the * underlying RecordStoreFile */ int read(byte[] buf, int _offset) throws java.io.IOException { dbraf.seek(offset + DATA_OFFSET); return dbraf.read(buf, _offset, dataLenOrNextFree); } /** * Writes data associated with this record to storage (a * RecordStoreFile) at <code>offset</code>. * * Assumes CALLER has ensured <code>dataLenOrNextFree</code> is * set correctly. * * @param buf data to store in this record. * @param _offset point in <code>buf</code> to begin write from. * * @exception IOException if there is an error writing the * underlying RecordStoreFile. */ void write(byte[] buf, int _offset) throws java.io.IOException { dbraf.seek(offset + DATA_OFFSET); dbraf.write(buf, _offset, dataLenOrNextFree); } } /** * RecordHeaderCache providing a per RecordStore, in memory cac
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -