git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@488 f203690c-595d-4dc9-a70b-905162fa7fd2tags/jackcess-1.2.2
/* | |||||
Copyright (c) 2005 Health Market Science, Inc. | |||||
This library is free software; you can redistribute it and/or | |||||
modify it under the terms of the GNU Lesser General Public | |||||
License as published by the Free Software Foundation; either | |||||
version 2.1 of the License, or (at your option) any later version. | |||||
This library is distributed in the hope that it will be useful, | |||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
Lesser General Public License for more details. | |||||
You should have received a copy of the GNU Lesser General Public | |||||
License along with this library; if not, write to the Free Software | |||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |||||
USA | |||||
You can contact Health Market Science at info@healthmarketscience.com | |||||
or at the following address: | |||||
Health Market Science | |||||
2700 Horizon Drive | |||||
Suite 200 | |||||
King of Prussia, PA 19406 | |||||
*/ | |||||
package com.healthmarketscience.jackcess; | |||||
import java.io.IOException; | |||||
import java.nio.ByteBuffer; | |||||
/** | |||||
* Interface for a handler which can encode/decode a specific access page | |||||
* encoding. | |||||
* | |||||
* @author James Ahlborn | |||||
*/ | |||||
public interface CodecHandler | |||||
{ | |||||
/** | |||||
* Decodes the given page buffer inline. | |||||
* | |||||
* @param page the page to be decoded | |||||
* @param pageNumber the page number of the given page | |||||
* | |||||
* @throws IOException if an exception occurs during decoding | |||||
*/ | |||||
public void decodePage(ByteBuffer page, int pageNumber) throws IOException; | |||||
/** | |||||
* Encodes the given page buffer into a new page buffer and returns it. The | |||||
* returned page buffer will be used immediately and discarded so that it | |||||
* may be re-used for subsequent page encodings. | |||||
* | |||||
* @param page the page to be encoded, should not be modified | |||||
* @param pageNumber the page number of the given page | |||||
* @param pageOffset offset within the page at which to start writing the | |||||
* page data | |||||
* | |||||
* @throws IOException if an exception occurs during decoding | |||||
* | |||||
* @return the properly encoded page buffer for the given page buffer | |||||
*/ | |||||
public ByteBuffer encodePage(ByteBuffer page, int pageNumber, | |||||
int pageOffset) | |||||
throws IOException; | |||||
} |
/* | |||||
Copyright (c) 2005 Health Market Science, Inc. | |||||
This library is free software; you can redistribute it and/or | |||||
modify it under the terms of the GNU Lesser General Public | |||||
License as published by the Free Software Foundation; either | |||||
version 2.1 of the License, or (at your option) any later version. | |||||
This library is distributed in the hope that it will be useful, | |||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
Lesser General Public License for more details. | |||||
You should have received a copy of the GNU Lesser General Public | |||||
License along with this library; if not, write to the Free Software | |||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |||||
USA | |||||
You can contact Health Market Science at info@healthmarketscience.com | |||||
or at the following address: | |||||
Health Market Science | |||||
2700 Horizon Drive | |||||
Suite 200 | |||||
King of Prussia, PA 19406 | |||||
*/ | |||||
package com.healthmarketscience.jackcess; | |||||
import java.io.IOException; | |||||
/** | |||||
* Interface for a provider which can generate CodecHandlers for various types | |||||
* of database encodings. The {@link DefaultCodecProvider} is the default | |||||
* implementation of this inferface, but it does not have any actual | |||||
* encoding/decoding support (due to possible export issues with calling | |||||
* encryption APIs). See the separate | |||||
* <a href="https://sourceforge.net/projects/jackcessencrypt/">Jackcess | |||||
* Encrypt</a> project for an implementation of this interface which supports | |||||
* various access database encryption types. | |||||
* | |||||
* @author James Ahlborn | |||||
*/ | |||||
public interface CodecProvider | |||||
{ | |||||
/** | |||||
* Returns a new CodecHandler for the database associated with the given | |||||
* PageChannel. | |||||
* | |||||
* @param channel the PageChannel for a Database | |||||
* | |||||
* @return a new CodecHandler, may not be {@code null} | |||||
*/ | |||||
public CodecHandler createHandler(PageChannel channel) | |||||
throws IOException; | |||||
} |
Charset charset, TimeZone timeZone) | Charset charset, TimeZone timeZone) | ||||
throws IOException | throws IOException | ||||
{ | { | ||||
return open(mdbFile, readOnly, autoSync, charset, timeZone, null); | |||||
} | |||||
/** | |||||
* Open an existing Database. If the existing file is not writeable or the | |||||
* readOnly flag is <code>true</code>, the file will be opened read-only. | |||||
* @param mdbFile File containing the database | |||||
* @param readOnly iff <code>true</code>, force opening file in read-only | |||||
* mode | |||||
* @param autoSync whether or not to enable auto-syncing on write. if | |||||
* {@code true}, writes will be immediately flushed to disk. | |||||
* This leaves the database in a (fairly) consistent state | |||||
* on each write, but can be very inefficient for many | |||||
* updates. if {@code false}, flushing to disk happens at | |||||
* the jvm's leisure, which can be much faster, but may | |||||
* leave the database in an inconsistent state if failures | |||||
* are encountered during writing. | |||||
* @param charset Charset to use, if {@code null}, uses default | |||||
* @param timeZone TimeZone to use, if {@code null}, uses default | |||||
* @param provider CodecProvider for handling page encoding/decoding, may be | |||||
* {@code null} if no special encoding is necessary | |||||
*/ | |||||
public static Database open(File mdbFile, boolean readOnly, boolean autoSync, | |||||
Charset charset, TimeZone timeZone, | |||||
CodecProvider provider) | |||||
throws IOException | |||||
{ | |||||
if(!mdbFile.exists() || !mdbFile.canRead()) { | if(!mdbFile.exists() || !mdbFile.canRead()) { | ||||
throw new FileNotFoundException("given file does not exist: " + mdbFile); | throw new FileNotFoundException("given file does not exist: " + mdbFile); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return new Database(channel, autoSync, null, charset, timeZone); | |||||
return new Database(channel, autoSync, null, charset, timeZone, provider); | |||||
} | } | ||||
/** | /** | ||||
channel.transferFrom(Channels.newChannel( | channel.transferFrom(Channels.newChannel( | ||||
Thread.currentThread().getContextClassLoader().getResourceAsStream( | Thread.currentThread().getContextClassLoader().getResourceAsStream( | ||||
fileFormat._emptyFile)), 0, Integer.MAX_VALUE); | fileFormat._emptyFile)), 0, Integer.MAX_VALUE); | ||||
return new Database(channel, autoSync, fileFormat, charset, timeZone); | |||||
return new Database(channel, autoSync, fileFormat, charset, timeZone, | |||||
null); | |||||
} | } | ||||
/** | /** | ||||
* @param timeZone TimeZone to use, if {@code null}, uses default | * @param timeZone TimeZone to use, if {@code null}, uses default | ||||
*/ | */ | ||||
protected Database(FileChannel channel, boolean autoSync, | protected Database(FileChannel channel, boolean autoSync, | ||||
FileFormat fileFormat, Charset charset, TimeZone timeZone) | |||||
FileFormat fileFormat, Charset charset, TimeZone timeZone, | |||||
CodecProvider provider) | |||||
throws IOException | throws IOException | ||||
{ | { | ||||
boolean success = false; | boolean success = false; | ||||
_fileFormat = fileFormat; | _fileFormat = fileFormat; | ||||
_pageChannel = new PageChannel(channel, _format, autoSync); | _pageChannel = new PageChannel(channel, _format, autoSync); | ||||
_timeZone = ((timeZone == null) ? getDefaultTimeZone() : timeZone); | _timeZone = ((timeZone == null) ? getDefaultTimeZone() : timeZone); | ||||
if(provider == null) { | |||||
provider = DefaultCodecProvider.INSTANCE; | |||||
} | |||||
// note, it's slighly sketchy to pass ourselves along partially | // note, it's slighly sketchy to pass ourselves along partially | ||||
// constructed, but only our _format and _pageChannel refs should be | // constructed, but only our _format and _pageChannel refs should be | ||||
// needed | // needed | ||||
_pageChannel.initialize(this); | |||||
_pageChannel.initialize(this, provider); | |||||
_buffer = _pageChannel.createPageBuffer(); | _buffer = _pageChannel.createPageBuffer(); | ||||
readSystemCatalog(); | readSystemCatalog(); | ||||
success = true; | success = true; |
/* | |||||
Copyright (c) 2005 Health Market Science, Inc. | |||||
This library is free software; you can redistribute it and/or | |||||
modify it under the terms of the GNU Lesser General Public | |||||
License as published by the Free Software Foundation; either | |||||
version 2.1 of the License, or (at your option) any later version. | |||||
This library is distributed in the hope that it will be useful, | |||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
Lesser General Public License for more details. | |||||
You should have received a copy of the GNU Lesser General Public | |||||
License along with this library; if not, write to the Free Software | |||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |||||
USA | |||||
You can contact Health Market Science at info@healthmarketscience.com | |||||
or at the following address: | |||||
Health Market Science | |||||
2700 Horizon Drive | |||||
Suite 200 | |||||
King of Prussia, PA 19406 | |||||
*/ | |||||
package com.healthmarketscience.jackcess; | |||||
import java.io.IOException; | |||||
import java.nio.ByteBuffer; | |||||
/** | |||||
* Default implementation of CodecProvider which does not have any actual | |||||
* encoding/decoding support. See {@link CodecProvider} for details on a more | |||||
* useful implementation. | |||||
* | |||||
* @author James Ahlborn | |||||
*/ | |||||
public class DefaultCodecProvider implements CodecProvider | |||||
{ | |||||
/** common instance of DefaultCodecProvider */ | |||||
public static final CodecProvider INSTANCE = | |||||
new DefaultCodecProvider(); | |||||
/** common instance of {@link DummyHandler} */ | |||||
public static final CodecHandler DUMMY_HANDLER = | |||||
new DummyHandler(); | |||||
/** common instance of {@link UnsupportedHandler} */ | |||||
public static final CodecHandler UNSUPPORTED_HANDLER = | |||||
new UnsupportedHandler(); | |||||
/** | |||||
* {@inheritDoc} | |||||
* <p> | |||||
* This implementation returns DUMMY_HANDLER for databases with no encoding | |||||
* and UNSUPPORTED_HANDLER for databases with any encoding. | |||||
*/ | |||||
public CodecHandler createHandler(PageChannel channel) | |||||
throws IOException | |||||
{ | |||||
JetFormat format = channel.getFormat(); | |||||
switch(format.CODEC_TYPE) { | |||||
case NONE: | |||||
// no encoding, all good | |||||
return DUMMY_HANDLER; | |||||
case JET: | |||||
// check for an encode key. if 0, not encoded | |||||
ByteBuffer bb = channel.createPageBuffer(); | |||||
channel.readPage(bb, 0); | |||||
int codecKey = bb.getInt(format.OFFSET_ENCODING_KEY); | |||||
return((codecKey == 0) ? DUMMY_HANDLER : UNSUPPORTED_HANDLER); | |||||
case MSISAM: | |||||
// always encoded, we don't handle it | |||||
return UNSUPPORTED_HANDLER; | |||||
default: | |||||
throw new RuntimeException("Unknown codec type " + format.CODEC_TYPE); | |||||
} | |||||
} | |||||
/** | |||||
* CodecHandler implementation which does nothing, useful for databases with | |||||
* no extra encoding. | |||||
*/ | |||||
public static class DummyHandler implements CodecHandler | |||||
{ | |||||
public void decodePage(ByteBuffer page, int pageNumber) throws IOException | |||||
{ | |||||
// does nothing | |||||
} | |||||
public ByteBuffer encodePage(ByteBuffer page, int pageNumber, | |||||
int pageOffset) | |||||
throws IOException | |||||
{ | |||||
// does nothing | |||||
return page; | |||||
} | |||||
} | |||||
/** | |||||
* CodecHandler implementation which always throws | |||||
* UnsupportedOperationException, useful for databases with unsupported | |||||
* encodings. | |||||
*/ | |||||
public static class UnsupportedHandler implements CodecHandler | |||||
{ | |||||
public void decodePage(ByteBuffer page, int pageNumber) throws IOException | |||||
{ | |||||
throw new UnsupportedOperationException("Decoding not supported"); | |||||
} | |||||
public ByteBuffer encodePage(ByteBuffer page, int pageNumber, | |||||
int pageOffset) | |||||
throws IOException | |||||
{ | |||||
throw new UnsupportedOperationException("Encoding not supported"); | |||||
} | |||||
} | |||||
} |
public static final short TEXT_FIELD_UNIT_SIZE = 2; | public static final short TEXT_FIELD_UNIT_SIZE = 2; | ||||
/** Maximum size of a text field */ | /** Maximum size of a text field */ | ||||
public static final short TEXT_FIELD_MAX_LENGTH = 255 * TEXT_FIELD_UNIT_SIZE; | public static final short TEXT_FIELD_MAX_LENGTH = 255 * TEXT_FIELD_UNIT_SIZE; | ||||
public enum CodecType { | |||||
NONE, JET, MSISAM; | |||||
} | |||||
/** Offset in the file that holds the byte describing the Jet format | /** Offset in the file that holds the byte describing the Jet format | ||||
version */ | version */ | ||||
private static final long OFFSET_VERSION = 20L; | |||||
private static final int OFFSET_VERSION = 20; | |||||
/** Version code for Jet version 3 */ | /** Version code for Jet version 3 */ | ||||
private static final byte CODE_VERSION_3 = 0x0; | private static final byte CODE_VERSION_3 = 0x0; | ||||
/** Version code for Jet version 4 */ | /** Version code for Jet version 4 */ | ||||
/** Version code for Jet version 5 */ | /** Version code for Jet version 5 */ | ||||
private static final byte CODE_VERSION_5 = 0x2; | private static final byte CODE_VERSION_5 = 0x2; | ||||
/** location of the engine name in the header */ | |||||
private static final int OFFSET_ENGINE_NAME = 0x4; | |||||
/** amount of initial data to be read to determine database type */ | |||||
private static final int HEADER_LENGTH = 21; | |||||
private final static byte[] MSISAM_ENGINE = new byte[] { | |||||
'M', 'S', 'I', 'S', 'A', 'M', ' ', 'D', 'a', 't', 'a', 'b', 'a', 's', 'e' | |||||
}; | |||||
/** mask used to obfuscate the db header */ | /** mask used to obfuscate the db header */ | ||||
private static final byte[] BASE_HEADER_MASK = new byte[]{ | private static final byte[] BASE_HEADER_MASK = new byte[]{ | ||||
(byte)0xB5, (byte)0x6F, (byte)0x03, (byte)0x62, (byte)0x61, (byte)0x08, | (byte)0xB5, (byte)0x6F, (byte)0x03, (byte)0x62, (byte)0x61, (byte)0x08, | ||||
/** whether or not we can use indexes in this format */ | /** whether or not we can use indexes in this format */ | ||||
public final boolean INDEXES_SUPPORTED; | public final boolean INDEXES_SUPPORTED; | ||||
/** type of page encoding supported */ | |||||
public final CodecType CODEC_TYPE; | |||||
/** Database page size in bytes */ | /** Database page size in bytes */ | ||||
public final int PAGE_SIZE; | public final int PAGE_SIZE; | ||||
public final int OFFSET_HEADER_DATE; | public final int OFFSET_HEADER_DATE; | ||||
public final int OFFSET_PASSWORD; | public final int OFFSET_PASSWORD; | ||||
public final int SIZE_PASSWORD; | public final int SIZE_PASSWORD; | ||||
public final int OFFSET_ENCODING_KEY; | |||||
public final int OFFSET_NEXT_TABLE_DEF_PAGE; | public final int OFFSET_NEXT_TABLE_DEF_PAGE; | ||||
public final int OFFSET_NUM_ROWS; | public final int OFFSET_NUM_ROWS; | ||||
public final int OFFSET_NEXT_AUTO_NUMBER; | public final int OFFSET_NEXT_AUTO_NUMBER; | ||||
public static final JetFormat VERSION_3 = new Jet3Format(); | public static final JetFormat VERSION_3 = new Jet3Format(); | ||||
public static final JetFormat VERSION_4 = new Jet4Format(); | public static final JetFormat VERSION_4 = new Jet4Format(); | ||||
public static final JetFormat VERSION_MSISAM = new MSISAMFormat(); | |||||
public static final JetFormat VERSION_5 = new Jet5Format(); | public static final JetFormat VERSION_5 = new Jet5Format(); | ||||
/** | /** | ||||
* @throws IOException if the database file format is unsupported. | * @throws IOException if the database file format is unsupported. | ||||
*/ | */ | ||||
public static JetFormat getFormat(FileChannel channel) throws IOException { | public static JetFormat getFormat(FileChannel channel) throws IOException { | ||||
ByteBuffer buffer = ByteBuffer.allocate(1); | |||||
int bytesRead = channel.read(buffer, OFFSET_VERSION); | |||||
if(bytesRead < 1) { | |||||
ByteBuffer buffer = ByteBuffer.allocate(HEADER_LENGTH); | |||||
int bytesRead = channel.read(buffer, 0L); | |||||
if(bytesRead < HEADER_LENGTH) { | |||||
throw new IOException("Empty database file"); | throw new IOException("Empty database file"); | ||||
} | } | ||||
buffer.flip(); | buffer.flip(); | ||||
byte version = buffer.get(); | |||||
byte version = buffer.get(OFFSET_VERSION); | |||||
if (version == CODE_VERSION_3) { | if (version == CODE_VERSION_3) { | ||||
return VERSION_3; | return VERSION_3; | ||||
} else if (version == CODE_VERSION_4) { | } else if (version == CODE_VERSION_4) { | ||||
if(ByteUtil.matchesRange(buffer, OFFSET_ENGINE_NAME, MSISAM_ENGINE)) { | |||||
return VERSION_MSISAM; | |||||
} | |||||
return VERSION_4; | return VERSION_4; | ||||
} else if (version == CODE_VERSION_5) { | } else if (version == CODE_VERSION_5) { | ||||
return VERSION_5; | return VERSION_5; | ||||
READ_ONLY = defineReadOnly(); | READ_ONLY = defineReadOnly(); | ||||
INDEXES_SUPPORTED = defineIndexesSupported(); | INDEXES_SUPPORTED = defineIndexesSupported(); | ||||
CODEC_TYPE = defineCodecType(); | |||||
PAGE_SIZE = definePageSize(); | PAGE_SIZE = definePageSize(); | ||||
MAX_DATABASE_SIZE = defineMaxDatabaseSize(); | MAX_DATABASE_SIZE = defineMaxDatabaseSize(); | ||||
OFFSET_HEADER_DATE = defineOffsetHeaderDate(); | OFFSET_HEADER_DATE = defineOffsetHeaderDate(); | ||||
OFFSET_PASSWORD = defineOffsetPassword(); | OFFSET_PASSWORD = defineOffsetPassword(); | ||||
SIZE_PASSWORD = defineSizePassword(); | SIZE_PASSWORD = defineSizePassword(); | ||||
OFFSET_ENCODING_KEY = defineOffsetEncodingKey(); | |||||
OFFSET_NEXT_TABLE_DEF_PAGE = defineOffsetNextTableDefPage(); | OFFSET_NEXT_TABLE_DEF_PAGE = defineOffsetNextTableDefPage(); | ||||
OFFSET_NUM_ROWS = defineOffsetNumRows(); | OFFSET_NUM_ROWS = defineOffsetNumRows(); | ||||
OFFSET_NEXT_AUTO_NUMBER = defineOffsetNextAutoNumber(); | OFFSET_NEXT_AUTO_NUMBER = defineOffsetNextAutoNumber(); | ||||
protected abstract boolean defineReadOnly(); | protected abstract boolean defineReadOnly(); | ||||
protected abstract boolean defineIndexesSupported(); | protected abstract boolean defineIndexesSupported(); | ||||
protected abstract CodecType defineCodecType(); | |||||
protected abstract int definePageSize(); | protected abstract int definePageSize(); | ||||
protected abstract long defineMaxDatabaseSize(); | protected abstract long defineMaxDatabaseSize(); | ||||
protected abstract int defineOffsetHeaderDate(); | protected abstract int defineOffsetHeaderDate(); | ||||
protected abstract int defineOffsetPassword(); | protected abstract int defineOffsetPassword(); | ||||
protected abstract int defineSizePassword(); | protected abstract int defineSizePassword(); | ||||
protected abstract int defineOffsetEncodingKey(); | |||||
protected abstract int defineOffsetNextTableDefPage(); | protected abstract int defineOffsetNextTableDefPage(); | ||||
protected abstract int defineOffsetNumRows(); | protected abstract int defineOffsetNumRows(); | ||||
protected abstract int defineOffsetNextAutoNumber(); | protected abstract int defineOffsetNextAutoNumber(); | ||||
@Override | @Override | ||||
protected boolean defineIndexesSupported() { return false; } | protected boolean defineIndexesSupported() { return false; } | ||||
@Override | |||||
protected CodecType defineCodecType() { | |||||
return CodecType.JET; | |||||
} | |||||
@Override | @Override | ||||
protected int definePageSize() { return 2048; } | protected int definePageSize() { return 2048; } | ||||
@Override | @Override | ||||
protected int defineSizePassword() { return 20; } | protected int defineSizePassword() { return 20; } | ||||
@Override | @Override | ||||
protected int defineOffsetEncodingKey() { return 62; } | |||||
@Override | |||||
protected int defineOffsetNextTableDefPage() { return 4; } | protected int defineOffsetNextTableDefPage() { return 4; } | ||||
@Override | @Override | ||||
protected int defineOffsetNumRows() { return 12; } | protected int defineOffsetNumRows() { return 12; } | ||||
@Override | @Override | ||||
protected boolean defineIndexesSupported() { return true; } | protected boolean defineIndexesSupported() { return true; } | ||||
@Override | |||||
protected CodecType defineCodecType() { | |||||
return CodecType.JET; | |||||
} | |||||
@Override | @Override | ||||
protected int definePageSize() { return 4096; } | protected int definePageSize() { return 4096; } | ||||
@Override | @Override | ||||
protected int defineSizePassword() { return 40; } | protected int defineSizePassword() { return 40; } | ||||
@Override | @Override | ||||
protected int defineOffsetEncodingKey() { return 62; } | |||||
@Override | |||||
protected int defineOffsetNextTableDefPage() { return 4; } | protected int defineOffsetNextTableDefPage() { return 4; } | ||||
@Override | @Override | ||||
protected int defineOffsetNumRows() { return 16; } | protected int defineOffsetNumRows() { return 16; } | ||||
} | } | ||||
private static final class MSISAMFormat extends Jet4Format { | |||||
private MSISAMFormat() { | |||||
super("MSISAM"); | |||||
} | |||||
@Override | |||||
protected boolean defineReadOnly() { | |||||
return true; | |||||
} | |||||
@Override | |||||
protected CodecType defineCodecType() { | |||||
return CodecType.MSISAM; | |||||
} | |||||
} | |||||
private static final class Jet5Format extends Jet4Format { | private static final class Jet5Format extends Jet4Format { | ||||
private Jet5Format() { | private Jet5Format() { | ||||
super("VERSION_5"); | super("VERSION_5"); |
private final ByteBuffer _forceBytes = ByteBuffer.allocate(1); | private final ByteBuffer _forceBytes = ByteBuffer.allocate(1); | ||||
/** Tracks free pages in the database. */ | /** Tracks free pages in the database. */ | ||||
private UsageMap _globalUsageMap; | private UsageMap _globalUsageMap; | ||||
/** handler for the current database encoding type */ | |||||
private CodecHandler _codecHandler = DefaultCodecProvider.DUMMY_HANDLER; | |||||
/** | /** | ||||
* @param channel Channel containing the database | * @param channel Channel containing the database | ||||
/** | /** | ||||
* Does second-stage initialization, must be called after construction. | * Does second-stage initialization, must be called after construction. | ||||
*/ | */ | ||||
public void initialize(Database database) | |||||
public void initialize(Database database, CodecProvider codecProvider) | |||||
throws IOException | throws IOException | ||||
{ | { | ||||
// note the global usage map is a special map where any page outside of | // note the global usage map is a special map where any page outside of | ||||
// the current range is assumed to be "on" | // the current range is assumed to be "on" | ||||
_globalUsageMap = UsageMap.read(database, PAGE_GLOBAL_USAGE_MAP, | _globalUsageMap = UsageMap.read(database, PAGE_GLOBAL_USAGE_MAP, | ||||
ROW_GLOBAL_USAGE_MAP, true); | ROW_GLOBAL_USAGE_MAP, true); | ||||
// initialize page en/decoding support | |||||
_codecHandler = codecProvider.createHandler(this); | |||||
} | } | ||||
/** | /** | ||||
} | } | ||||
if(pageNumber == 0) { | if(pageNumber == 0) { | ||||
// de-mask header | |||||
// de-mask header (note, page 0 never has additional encoding) | |||||
applyHeaderMask(buffer); | applyHeaderMask(buffer); | ||||
} else { | |||||
_codecHandler.decodePage(buffer, pageNumber); | |||||
} | } | ||||
} | } | ||||
* @param pageOffset offset within the page at which to start writing the | * @param pageOffset offset within the page at which to start writing the | ||||
* page data | * page data | ||||
*/ | */ | ||||
public void writePage(ByteBuffer page, int pageNumber, | |||||
int pageOffset) | |||||
public void writePage(ByteBuffer page, int pageNumber, int pageOffset) | |||||
throws IOException | throws IOException | ||||
{ | { | ||||
validatePageNumber(pageNumber); | validatePageNumber(pageNumber); | ||||
"Page buffer is too large, size " + (page.remaining() - pageOffset)); | "Page buffer is too large, size " + (page.remaining() - pageOffset)); | ||||
} | } | ||||
ByteBuffer encodedPage = page; | |||||
if(pageNumber == 0) { | if(pageNumber == 0) { | ||||
// re-mask header | // re-mask header | ||||
applyHeaderMask(page); | applyHeaderMask(page); | ||||
} else { | |||||
// re-encode page | |||||
encodedPage = _codecHandler.encodePage(page, pageNumber, pageOffset); | |||||
} | } | ||||
try { | try { | ||||
page.position(pageOffset); | |||||
_channel.write(page, (getPageOffset(pageNumber) + pageOffset)); | |||||
encodedPage.position(pageOffset); | |||||
_channel.write(encodedPage, (getPageOffset(pageNumber) + pageOffset)); | |||||
if(_autoSync) { | if(_autoSync) { | ||||
flush(); | flush(); | ||||
} | } | ||||
// push the buffer to the end of the page, so that a full page's worth of | // push the buffer to the end of the page, so that a full page's worth of | ||||
// data is written regardless of the incoming buffer size (we use a tiny | // data is written regardless of the incoming buffer size (we use a tiny | ||||
// buffer in allocateNewPage) | // buffer in allocateNewPage) | ||||
long offset = size + (getFormat().PAGE_SIZE - page.remaining()); | |||||
_channel.write(page, offset); | |||||
int pageOffset = (getFormat().PAGE_SIZE - page.remaining()); | |||||
long offset = size + pageOffset; | |||||
int pageNumber = getNextPageNumber(size); | int pageNumber = getNextPageNumber(size); | ||||
_channel.write(_codecHandler.encodePage(page, pageNumber, pageOffset), | |||||
offset); | |||||
_globalUsageMap.removePageNumber(pageNumber); //force is done here | _globalUsageMap.removePageNumber(pageNumber); //force is done here | ||||
return pageNumber; | return pageNumber; | ||||
} | } |