blob: c43a40fff727fdeaff2383e0dc0fd0439d8c4dcc [file] [log] [blame]
/**
* 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: PageManager.java,v 1.3 2005/06/25 23:12:32 doomdark Exp $
*/
package jdbm.recman;
import java.io.*;
/**
* This class manages the linked lists of pages that make up a file.
*/
final class PageManager {
// our record file
private RecordFile file;
// header data
private FileHeader header;
private BlockIo headerBuf;
/**
* Creates a new page manager using the indicated record file.
*/
PageManager(RecordFile file) throws IOException {
this.file = file;
// check the file header. If the magic is 0, we assume a new
// file. Note that we hold on to the file header node.
headerBuf = file.get(0);
if (headerBuf.readShort(0) == 0)
header = new FileHeader(headerBuf, true);
else
header = new FileHeader(headerBuf, false);
}
/**
* Allocates a page of the indicated type. Returns recid of the
* page.
*/
long allocate(short type) throws IOException {
if (type == Magic.FREE_PAGE)
throw new Error("allocate of free page?");
// do we have something on the free list?
long retval = header.getFirstOf(Magic.FREE_PAGE);
boolean isNew = false;
if (retval != 0) {
// yes. Point to it and make the next of that page the
// new first free page.
header.setFirstOf(Magic.FREE_PAGE, getNext(retval));
}
else {
// nope. make a new record
retval = header.getLastOf(Magic.FREE_PAGE);
if (retval == 0)
// very new file - allocate record #1
retval = 1;
header.setLastOf(Magic.FREE_PAGE, retval + 1);
isNew = true;
}
// Cool. We have a record, add it to the correct list
BlockIo buf = file.get(retval);
PageHeader pageHdr = isNew ? new PageHeader(buf, type)
: PageHeader.getView(buf);
long oldLast = header.getLastOf(type);
// Clean data.
System.arraycopy(RecordFile.cleanData, 0,
buf.getData(), 0,
RecordFile.BLOCK_SIZE);
pageHdr.setType(type);
pageHdr.setPrev(oldLast);
pageHdr.setNext(0);
if (oldLast == 0)
// This was the first one of this type
header.setFirstOf(type, retval);
header.setLastOf(type, retval);
file.release(retval, true);
// If there's a previous, fix up its pointer
if (oldLast != 0) {
buf = file.get(oldLast);
pageHdr = PageHeader.getView(buf);
pageHdr.setNext(retval);
file.release(oldLast, true);
}
// remove the view, we have modified the type.
buf.setView(null);
return retval;
}
/**
* Frees a page of the indicated type.
*/
void free(short type, long recid) throws IOException {
if (type == Magic.FREE_PAGE)
throw new Error("free free page?");
if (recid == 0)
throw new Error("free header page?");
// get the page and read next and previous pointers
BlockIo buf = file.get(recid);
PageHeader pageHdr = PageHeader.getView(buf);
long prev = pageHdr.getPrev();
long next = pageHdr.getNext();
// put the page at the front of the free list.
pageHdr.setType(Magic.FREE_PAGE);
pageHdr.setNext(header.getFirstOf(Magic.FREE_PAGE));
pageHdr.setPrev(0);
header.setFirstOf(Magic.FREE_PAGE, recid);
file.release(recid, true);
// remove the page from its old list
if (prev != 0) {
buf = file.get(prev);
pageHdr = PageHeader.getView(buf);
pageHdr.setNext(next);
file.release(prev, true);
}
else {
header.setFirstOf(type, next);
}
if (next != 0) {
buf = file.get(next);
pageHdr = PageHeader.getView(buf);
pageHdr.setPrev(prev);
file.release(next, true);
}
else {
header.setLastOf(type, prev);
}
}
/**
* Returns the page following the indicated block
*/
long getNext(long block) throws IOException {
try {
return PageHeader.getView(file.get(block)).getNext();
} finally {
file.release(block, false);
}
}
/**
* Returns the page before the indicated block
*/
long getPrev(long block) throws IOException {
try {
return PageHeader.getView(file.get(block)).getPrev();
} finally {
file.release(block, false);
}
}
/**
* Returns the first page on the indicated list.
*/
long getFirst(short type) throws IOException {
return header.getFirstOf(type);
}
/**
* Returns the last page on the indicated list.
*/
long getLast(short type) throws IOException {
return header.getLastOf(type);
}
/**
* Commit all pending (in-memory) data by flushing the page manager.
* This forces a flush of all outstanding blocks (this it's an implicit
* {@link RecordFile#commit} as well).
*/
void commit() throws IOException {
// write the header out
file.release(headerBuf);
file.commit();
// and obtain it again
headerBuf = file.get(0);
header = new FileHeader(headerBuf, false);
}
/**
* Flushes the page manager. This forces a flush of all outstanding
* blocks (this it's an implicit {@link RecordFile#commit} as well).
*/
void rollback() throws IOException {
// release header
file.discard(headerBuf);
file.rollback();
// and obtain it again
headerBuf = file.get(0);
if (headerBuf.readShort(0) == 0)
header = new FileHeader(headerBuf, true);
else
header = new FileHeader(headerBuf, false);
}
/**
* Closes the page manager. This flushes the page manager and releases
* the lock on the header.
*/
void close() throws IOException {
file.release(headerBuf);
file.commit();
headerBuf = null;
header = null;
file = null;
}
/**
* Returns the file header.
*/
FileHeader getFileHeader() {
return header;
}
}