📄 recordstore.java
字号:
rh.load(cur_offset); if (rh.id == recordId) { break; } else { cur_offset = rh.nextOffset; } } if (cur_offset == 0) { // hit the end of the linked list w/o finding record. throw new InvalidRecordIDException(); } if (addToCache) { recHeadCache.insert(rh); } return rh; } /** * Return the block allocation size for a record with <code>numBytes * </code> data bytes. This includes space for the record header * and is a multiple of <code>DB_BLOCK_SIZE</code>. * * @param numBytes number of data bytes that will be stored in record * * @return the amount of space to allocate for this record. */ private int getAllocSize(int numBytes) { int rv; int pad; rv = DB_RECORD_HEADER_LENGTH + numBytes; pad = DB_BLOCK_SIZE - (rv % DB_BLOCK_SIZE); if (pad != DB_BLOCK_SIZE) { rv += pad; } return rv; } /** * Returns a new record header for record <code>id</code> large * enough to hold <code>dataSize</code> bytes of record data. * * Picks a free block using a first fit strategy that is * large enough for a record header and the associated * record data. The block will be a multiple of DB_BLOCK_SIZE. * * @param id the record id to assign to the returned record header. * @param dataSize length of record data that will be * stored with this record. * * @return a new record header * the record store's backing file. */ private RecordHeader allocateNewRecordStorage(int id, int dataSize) throws RecordStoreException, RecordStoreFullException { int allocSize = getAllocSize(dataSize); boolean foundBlock = false; /* * Traverse the free block linked list in the file, looking * for the first fit */ RecordHeader block = new RecordHeader(); try { int offset = dbFirstFreeBlockOffset; while (offset != 0) { block.load(offset); // If block is big enough, use it. if (block.blockSize >= allocSize) { foundBlock = true; break; // use this free block } offset = block.dataLenOrNextFree; // next free block } } catch (java.io.IOException ioe) { throw new RecordStoreException("error finding first fit block"); } if (foundBlock == false) { /* * No free block was found that would hold this record, so * find the last (biggest offset) record in the file and * append this record after it. */ // Is there room to grow the file? if (RecordStoreFile.spaceAvailable() < allocSize) { throw new RecordStoreFullException(); } block = new RecordHeader(dbDataEnd, id, dbFirstRecordOffset, allocSize, dataSize); try { block.store(); } catch (java.io.IOException ioe) { throw new RecordStoreException("error writing "+ "new record data"); } dbFirstRecordOffset = dbDataEnd; dbDataEnd += allocSize; } else { // block is where the new record should be stored if (block.id != -1) { throw new RecordStoreException("ALLOC ERR " + block.id + " is not a free block!"); } removeFreeBlock(block); // remove from free block list block.id = id; if (block.blockSize - allocSize >= DB_BLOCK_SIZE + DB_RECORD_HEADER_LENGTH) { splitRecord(block, allocSize); // sets block.blockSize } block.dataLenOrNextFree = dataSize; try { block.store(); } catch (java.io.IOException ioe) { throw new RecordStoreException("error writing free block "+ "after alloc"); } } // add new record to cache recHeadCache.insert(block); return block; } /** * Splits a free block off the tail end of a large * record block which contains extra free space. * After calling this method, the caller must call * <code>storeDBState</code>. * * On return, <code>recHead.blockSize</code> will contain * allocSize. * * @param recHead the current record header * @param allocSize the size that <code>recHEad</code> * will have as its <code>blockSize</code> variable * when the call returns. * * @exception RecordStoreException if there is an error * splitting the record */ private void splitRecord(RecordHeader recHead, int allocSize) throws RecordStoreException { RecordHeader newfb; int extraSpace = recHead.blockSize - allocSize; int oldBlockSize = recHead.blockSize; recHead.blockSize = allocSize; // only split records inside the linked list if (recHead.offset != dbFirstRecordOffset) { int fboffset = recHead.offset + allocSize; newfb = new RecordHeader(fboffset, -1, recHead.offset, extraSpace, 0); try { freeRecord(newfb); // write new free block to disk RecordHeader prh = new RecordHeader(recHead.offset + oldBlockSize); prh.nextOffset = fboffset; prh.store(); recHeadCache.invalidate(prh.id); storeDBState(); } catch (java.io.IOException ioe) { throw new RecordStoreException("splitRecord error"); } } else { // drop free space at the end of the file dbDataEnd = recHead.offset + recHead.blockSize; } } /** * Free a record into the Free list. * Turns the RecordHeader block <code>rh</code> into a free * block, then adds it to the free block linked list. * * After calling this method the caller must call * <code>storeDBState</code>. * * @param rh RecordHeader of record to make into a free block * * @exception RecordStoreException if there is an IO error updating the * free list */ private void freeRecord(RecordHeader rh) throws RecordStoreException { if (rh.offset == dbFirstRecordOffset) { // don't put free blocks at the end of the record file dbFirstRecordOffset = rh.nextOffset; dbDataEnd = rh.offset; } else { rh.id = -1; // indicate this is a free block rh.dataLenOrNextFree = dbFirstFreeBlockOffset; // insert this new free block at front of free list dbFirstFreeBlockOffset = rh.offset; try { rh.store(); } catch (java.io.IOException ioe) { throw new RecordStoreException("free record failed"); } } } /** * Remove a free block from the free block linked list * * @param blockToFree record header for the free block to remove * * @exception recordStoreException if error occurs during the * update. */ private void removeFreeBlock(RecordHeader blockToFree) throws RecordStoreException { RecordHeader block = new RecordHeader(); RecordHeader prev = new RecordHeader(); RecordHeader tmp = null; try { int offset = dbFirstFreeBlockOffset; while (offset != 0) { block.load(offset); if (block.offset == blockToFree.offset) { if (block.id != -1) { throw new RecordStoreException("removeFreeBlock id" + " is not -1"); } if (prev.offset == 0) { // Set next free block as new freelist head dbFirstFreeBlockOffset = block.dataLenOrNextFree; } else { /* * Update previous block's pointer to the * block this block was pointing to */ prev.dataLenOrNextFree = block.dataLenOrNextFree; prev.store(); } } offset = block.dataLenOrNextFree; // avoid creating lots of garbage! tmp = prev; prev = block; block = tmp; } } catch (java.io.IOException ioe) { throw new RecordStoreException("removeFreeBlock block not found"); } } /** * Helper method that stores the internal state variables * into the record store file. * * checkopen should have been called. will not work * if dbraf is not open * * Updates dbLastModified time to current system time */ private void storeDBState() throws RecordStoreException { try { // set modification time dbLastModified = System.currentTimeMillis(); // Capture the db state into the byte array RecordStore.putInt(dbNumLiveRecords, dbState, RS_NUM_LIVE); RecordStore.putInt(dbAuthMode, dbState, RS_AUTHMODE); RecordStore.putInt(dbVersion, dbState, RS_VERSION); RecordStore.putInt(dbNextRecordID, dbState, RS_NEXT_ID); RecordStore.putInt(dbFirstRecordOffset, dbState, RS_REC_START); RecordStore.putInt(dbFirstFreeBlockOffset, dbState, RS_FREE_START); RecordStore.putLong(dbLastModified, dbState, RS_LAST_MODIFIED); RecordStore.putInt(dbDataStart, dbState, RS_DATA_START); RecordStore.putInt(dbDataEnd, dbState, RS_DATA_END); // Write the state to the db file dbraf.seek(SIGNATURE_LENGTH); // skip RS header 8 bytes int numbytes = DB_INIT.length - SIGNATURE_LENGTH; dbraf.write(dbState, SIGNATURE_LENGTH, numbytes); } catch (java.io.IOException ioe) { throw new RecordStoreException("error writing record store " + "attributes"); } } /* * Package Private Methods */ /** * Get the open status of this record store. (Package accessable * for use by record enumeration objects.) * * @return true if record store is open, false otherwise. */ boolean isOpen() { if (dbraf == null) { return false; } return true; } /* * Private Utility Methods */ /** * Throws a RecordStoreNotOpenException if the RecordStore * is closed. (A RecordStore is closed if the RecordStoreFile * instance variable <code>dbraf</code> is null. * * @exception RecordStoreNotOpenException if RecordStore is closed */ private void checkOpen() throws RecordStoreNotOpenException { if (dbraf == null) { throw new RecordStoreNotOpenException(); } } /** * Notifies all registered listeners that a record changed. * * @param recordId the record id of the changed record. */ private void notifyRecordChangedListeners(int recordId) { for (int i = 0; i < recordListener.size(); i++) { RecordListener rl = (RecordListener)recordListener.elementAt(i); rl.recordChanged(this, recordId); } } /** * Notifies all registered listeners that a record was added. * * @param recordId the record id of the added record. */ private void notifyRecordAddedListeners(int recordId) { for (int i = 0; i < recordListener.size(); i++) { RecordListener rl = (RecordListener)recordListener.elementAt(i); rl.recordAdded(this, recordId); } } /** * Notifies all registered listeners that a record was deleted. * * @param recordId the record id of the changed record. */ private void notifyRecordDeletedListeners(int recordId) { for (int i = 0; i < recordListener.size(); i++) { RecordListener rl = (RecordListener)recordListener.elementAt(i); rl.recordDeleted(this, recordId); } } /** * A convenience method for converting a byte array into * an int (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 an int corresponding to the first four bytes * of the array passed in. */ static int getInt(byte[] data, int offset) { int r = data[offset++]; r = (r << 8) | ((int)(data[offset++]) & 0xff);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -