📄 recordfile.java
字号:
/** * JDBM LICENSE v1.00 * * Redistribution and use of this software and associated documentation * ("Software"), with or without modification, are permitted provided * that the following conditions are met: * * 1. Redistributions of source code must retain copyright * statements and notices. Redistributions must also contain a * copy of this document. * * 2. Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. The name "JDBM" must not be used to endorse or promote * products derived from this Software without prior written * permission of Cees de Groot. For written permission, * please contact cg@cdegroot.com. * * 4. Products derived from this Software may not be called "JDBM" * nor may "JDBM" appear in their names without prior written * permission of Cees de Groot. * * 5. Due credit should be given to the JDBM Project * (http://jdbm.sourceforge.net/). * * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * CEES DE GROOT OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Copyright 2000 (C) Cees de Groot. All Rights Reserved. * Contributions are Copyright (C) 2000 by their associated contributors. * * $Id: RecordFile.java,v 1.6 2005/06/25 23:12:32 doomdark Exp $ */package jdbm.recman;import java.io.*;import java.util.*;/** * This class represents a random access file as a set of fixed size * records. Each record has a physical record number, and records are * cached in order to improve access. *<p> * The set of dirty records on the in-use list constitutes a transaction. * Later on, we will send these records to some recovery thingy. */public final class RecordFile { final TransactionManager txnMgr; // Todo: reorganize in hashes and fifos as necessary. // free -> inUse -> dirty -> inTxn -> free // free is a cache, thus a FIFO. The rest are hashes. private final LinkedList free = new LinkedList(); private final HashMap inUse = new HashMap(); private final HashMap dirty = new HashMap(); private final HashMap inTxn = new HashMap(); // transactions disabled? private boolean transactionsDisabled = false; /** The length of a single block. */ public final static int BLOCK_SIZE = 8192;//4096; /** The extension of a record file */ final static String extension = ".db"; /** A block of clean data to wipe clean pages. */ final static byte[] cleanData = new byte[BLOCK_SIZE]; private RandomAccessFile file; private final String fileName; /** * Creates a new object on the indicated filename. The file is * opened in read/write mode. * * @param fileName the name of the file to open or create, without * an extension. * @throws IOException whenever the creation of the underlying * RandomAccessFile throws it. */ RecordFile(String fileName) throws IOException { this.fileName = fileName; file = new RandomAccessFile(fileName + extension, "rw"); txnMgr = new TransactionManager(this); } /** * Returns the file name. */ String getFileName() { return fileName; } /** * Disables transactions: doesn't sync and doesn't use the * transaction manager. */ void disableTransactions() { transactionsDisabled = true; } /** * Gets a block from the file. The returned byte array is * the in-memory copy of the record, and thus can be written * (and subsequently released with a dirty flag in order to * write the block back). * * @param blockid The record number to retrieve. */ BlockIo get(long blockid) throws IOException { Long key = new Long(blockid); // try in transaction list, dirty list, free list BlockIo node = (BlockIo) inTxn.get(key); if (node != null) { inTxn.remove(key); inUse.put(key, node); return node; } node = (BlockIo) dirty.get(key); if (node != null) { dirty.remove(key); inUse.put(key, node); return node; } for (Iterator i = free.iterator(); i.hasNext(); ) { BlockIo cur = (BlockIo) i.next(); if (cur.getBlockId() == blockid) { node = cur; i.remove(); inUse.put(key, node); return node; } } // sanity check: can't be on in use list if (inUse.get(key) != null) { throw new Error("double get for block " + blockid); } // get a new node and read it from the file node = getNewNode(blockid); long offset = blockid * BLOCK_SIZE; if (file.length() > 0 && offset <= file.length()) { read(file, offset, node.getData(), BLOCK_SIZE); } else { System.arraycopy(cleanData, 0, node.getData(), 0, BLOCK_SIZE); } inUse.put(key, node); node.setClean(); return node; } /** * Releases a block. * * @param blockid The record number to release. * @param isDirty If true, the block was modified since the get(). */ void release(long blockid, boolean isDirty) throws IOException { BlockIo node = (BlockIo) inUse.get(new Long(blockid)); if (node == null) throw new IOException("bad blockid " + blockid + " on release"); if (!node.isDirty() && isDirty) node.setDirty(); release(node); } /** * Releases a block. * * @param block The block to release. */ void release(BlockIo block) { Long key = new Long(block.getBlockId()); inUse.remove(key); if (block.isDirty()) { // System.out.println( "Dirty: " + key + block ); dirty.put(key, block); } else { if (!transactionsDisabled && block.isInTransaction()) { inTxn.put(key, block); } else { free.add(block); } } } /** * Discards a block (will not write the block even if it's dirty) * * @param block The block to discard. */ void discard(BlockIo block) { Long key = new Long(block.getBlockId()); inUse.remove(key); // note: block not added to free list on purpose, because // it's considered invalid } /** * Commits the current transaction by flushing all dirty buffers * to disk. */ void commit() throws IOException { // debugging... if (!inUse.isEmpty() && inUse.size() > 1) { showList(inUse.values().iterator()); throw new Error("in use list not empty at commit time (" + inUse.size() + ")"); } // System.out.println("committing..."); if ( dirty.size() == 0 ) { // if no dirty blocks, skip commit process return; } if (!transactionsDisabled) { txnMgr.start(); } for (Iterator i = dirty.values().iterator(); i.hasNext(); ) { BlockIo node = (BlockIo) i.next(); i.remove(); // System.out.println("node " + node + " map size now " + dirty.size()); if (transactionsDisabled) { long offset = node.getBlockId() * BLOCK_SIZE; file.seek(offset); file.write(node.getData()); node.setClean(); free.add(node); } else { txnMgr.add(node); inTxn.put(new Long(node.getBlockId()), node); } } if (!transactionsDisabled) { txnMgr.commit(); } } /** * Rollback the current transaction by discarding all dirty buffers */ void rollback() throws IOException { // debugging... if (!inUse.isEmpty()) { showList(inUse.values().iterator()); throw new Error("in use list not empty at rollback time (" + inUse.size() + ")"); } // System.out.println("rollback..."); dirty.clear(); txnMgr.synchronizeLogFromDisk(); if (!inTxn.isEmpty()) { showList(inTxn.values().iterator()); throw new Error("in txn list not empty at rollback time (" + inTxn.size() + ")"); }; } /** * Commits and closes file. */ void close() throws IOException { if (!dirty.isEmpty()) { commit(); } txnMgr.shutdown(); if (!inTxn.isEmpty()) { showList(inTxn.values().iterator()); throw new Error("In transaction not empty"); } // these actually ain't that bad in a production release if (!dirty.isEmpty()) { System.out.println("ERROR: dirty blocks at close time"); showList(dirty.values().iterator()); throw new Error("Dirty blocks at close time"); } if (!inUse.isEmpty()) { System.out.println("ERROR: inUse blocks at close time"); showList(inUse.values().iterator()); throw new Error("inUse blocks at close time"); } // debugging stuff to keep an eye on the free list // System.out.println("Free list size:" + free.size()); file.close(); file = null; } /** * Force closing the file and underlying transaction manager. * Used for testing purposed only. */ void forceClose() throws IOException { txnMgr.forceClose(); file.close(); } /** * Prints contents of a list */ private void showList(Iterator i) { int cnt = 0; while (i.hasNext()) { System.out.println("elem " + cnt + ": " + i.next()); cnt++; } } /** * Returns a new node. The node is retrieved (and removed) * from the released list or created new. */ private BlockIo getNewNode(long blockid) throws IOException { BlockIo retval = null; if (!free.isEmpty()) { retval = (BlockIo) free.removeFirst(); } if (retval == null) retval = new BlockIo(0, new byte[BLOCK_SIZE]); retval.setBlockId(blockid); retval.setView(null); return retval; } /** * Synchs a node to disk. This is called by the transaction manager's * synchronization code. */ void synch(BlockIo node) throws IOException { byte[] data = node.getData(); if (data != null) { long offset = node.getBlockId() * BLOCK_SIZE; file.seek(offset); file.write(data); } } /** * Releases a node from the transaction list, if it was sitting * there. * * @param recycle true if block data can be reused */ void releaseFromTransaction(BlockIo node, boolean recycle) throws IOException { Long key = new Long(node.getBlockId()); if ((inTxn.remove(key) != null) && recycle) { free.add(node); } } /** * Synchronizes the file. */ void sync() throws IOException { file.getFD().sync(); } /** * Utility method: Read a block from a RandomAccessFile */ private static void read(RandomAccessFile file, long offset, byte[] buffer, int nBytes) throws IOException { file.seek(offset); int remaining = nBytes; int pos = 0; while (remaining > 0) { int read = file.read(buffer, pos, remaining); if (read == -1) { System.arraycopy(cleanData, 0, buffer, pos, remaining); break; } remaining -= read; pos += read; } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -