?? recordstore.java
字號:
} /** * 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 cache of * recent RecordHeader lookups in order to absorb in many cases the * need to search for records on disk. Since the on disk data model * for record storage is a linked list, this can improve performance * when a record is accessed frequently. * * (Currently implemented in a simple, direct mapped way. Could be * modified to be random replacement/linear lookup, etc. These design * considerations should be determined by measuring cache performance * on representative RMS use cases.) */ private class RecordHeaderCache { /** a cache of RecordHeader objects */ private RecordHeader[] mCache; /** * Returns a new RecordHeaderCache able to hold up to <code> * size</code> record headers. * * @param size max number of RecordHeader objects allowed * in <code>mCache</code>. */ RecordHeaderCache(int size) { mCache = new RecordHeader[size]; } /** * Returns a RecordHeader for record <code>rec_id</code> or * null if the desired record header is not in the cache * * @param rec_id record id of the desired record header. * * @return a RecordHeader object for record * <code>rec_id</code>. */ RecordHeader get(int rec_id) { int idx = rec_id % mCache.length; RecordHeader rh = (RecordHeader)mCache[idx]; if ((mCache[idx] != null) && (mCache[idx].id != rec_id)) return null; return rh; } /** * Inserts a new RecordHeader into the cache. * * @param rh a RecordHeader to add to the cache. */ void insert(RecordHeader rh) { int idx = rh.id % mCache.length; mCache[idx] = rh; } /** * Removes a RecordHeader from the cache if it exists, * otherwise does nothing. Used in the case that the * cached RecordHeader is no longer valid. * * @param rec_id the record ID of the RecordHeader to * invalidate */ void invalidate(int rec_id) { if (rec_id > 0) { int idx = rec_id % mCache.length; if ((mCache[idx] != null) && (mCache[idx].id == rec_id)) { mCache[idx] = null; } } } }}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -