summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Ahlborn <jtahlborn@yahoo.com>2012-06-13 02:58:03 +0000
committerJames Ahlborn <jtahlborn@yahoo.com>2012-06-13 02:58:03 +0000
commitfb3533f16221e78f9f9af42cef5153e72d7285bd (patch)
tree88e02f4255722fece5ebfd1a58f2f1d35a56a59e
parent4fd4eb6ea2a312689de4b4dfaadc1e49ef090a05 (diff)
downloadjackcess-fb3533f16221e78f9f9af42cef5153e72d7285bd.tar.gz
jackcess-fb3533f16221e78f9f9af42cef5153e72d7285bd.zip
rework partial page writing and interacting with CodecHandlers (issue #3532250)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@628 f203690c-595d-4dc9-a70b-905162fa7fd2
-rw-r--r--src/changes/changes.xml6
-rw-r--r--src/java/com/healthmarketscience/jackcess/CodecHandler.java7
-rw-r--r--src/java/com/healthmarketscience/jackcess/DefaultCodecProvider.java14
-rw-r--r--src/java/com/healthmarketscience/jackcess/PageChannel.java74
4 files changed, 68 insertions, 33 deletions
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 389314b..7cf0ebc 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -15,6 +15,12 @@
<action dev="jahlborn" type="fix" issue="3529534">
Fix NPE when running unit tests with db format MSISAM.
</action>
+ <action dev="jahlborn" type="fix" issue="3532250">
+ Fix writing partial pages when CodecHandler is in use. Note, this fix
+ involves a backwards incompatible change to the CodecHandler interface
+ (Jackcess Encrypt 1.0.3 or later is compatible with this version of
+ Jackcess).
+ </action>
</release>
<release version="1.2.7" date="2012-04-02">
<action dev="jahlborn" type="update" issue="3479560">
diff --git a/src/java/com/healthmarketscience/jackcess/CodecHandler.java b/src/java/com/healthmarketscience/jackcess/CodecHandler.java
index c0f6170..c448668 100644
--- a/src/java/com/healthmarketscience/jackcess/CodecHandler.java
+++ b/src/java/com/healthmarketscience/jackcess/CodecHandler.java
@@ -30,6 +30,13 @@ import java.nio.ByteBuffer;
*/
public interface CodecHandler
{
+ /**
+ * Returns {@code true} if this handler can encode partial pages,
+ * {@code false} otherwise. If this method returns {@code false}, the
+ * {@link #encodePage} method will never be called with a non-zero
+ * pageOffset.
+ */
+ public boolean canEncodePartialPage();
/**
* Decodes the given page buffer inline.
diff --git a/src/java/com/healthmarketscience/jackcess/DefaultCodecProvider.java b/src/java/com/healthmarketscience/jackcess/DefaultCodecProvider.java
index e24634a..7694617 100644
--- a/src/java/com/healthmarketscience/jackcess/DefaultCodecProvider.java
+++ b/src/java/com/healthmarketscience/jackcess/DefaultCodecProvider.java
@@ -83,8 +83,11 @@ public class DefaultCodecProvider implements CodecProvider
*/
public static class DummyHandler implements CodecHandler
{
- public void decodePage(ByteBuffer page, int pageNumber) throws IOException
- {
+ public boolean canEncodePartialPage() {
+ return true;
+ }
+
+ public void decodePage(ByteBuffer page, int pageNumber) throws IOException {
// does nothing
}
@@ -104,8 +107,11 @@ public class DefaultCodecProvider implements CodecProvider
*/
public static class UnsupportedHandler implements CodecHandler
{
- public void decodePage(ByteBuffer page, int pageNumber) throws IOException
- {
+ public boolean canEncodePartialPage() {
+ return true;
+ }
+
+ public void decodePage(ByteBuffer page, int pageNumber) throws IOException {
throw new UnsupportedCodecException("Decoding not supported. Please choose a CodecProvider which supports reading the current database encoding.");
}
diff --git a/src/java/com/healthmarketscience/jackcess/PageChannel.java b/src/java/com/healthmarketscience/jackcess/PageChannel.java
index 68ba5cd..8759fcf 100644
--- a/src/java/com/healthmarketscience/jackcess/PageChannel.java
+++ b/src/java/com/healthmarketscience/jackcess/PageChannel.java
@@ -76,6 +76,9 @@ public class PageChannel implements Channel, Flushable {
private UsageMap _globalUsageMap;
/** handler for the current database encoding type */
private CodecHandler _codecHandler = DefaultCodecProvider.DUMMY_HANDLER;
+ /** temp page buffer used when pages cannot be partially encoded */
+ private final TempPageHolder _fullPageEncodeBufferH =
+ TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
/**
* @param channel Channel containing the database
@@ -200,11 +203,12 @@ public class PageChannel implements Channel, Flushable {
{
validatePageNumber(pageNumber);
- page.rewind();
+ page.rewind().position(pageOffset);
- if((page.remaining() - pageOffset) > getFormat().PAGE_SIZE) {
+ int writeLen = page.remaining();
+ if((writeLen + pageOffset) > getFormat().PAGE_SIZE) {
throw new IllegalArgumentException(
- "Page buffer is too large, size " + (page.remaining() - pageOffset));
+ "Page buffer is too large, size " + (writeLen + pageOffset));
}
ByteBuffer encodedPage = page;
@@ -212,9 +216,35 @@ public class PageChannel implements Channel, Flushable {
// re-mask header
applyHeaderMask(page);
} else {
+
+ if(!_codecHandler.canEncodePartialPage()) {
+ if((pageOffset > 0) && (writeLen < getFormat().PAGE_SIZE)) {
+
+ // current codec handler cannot encode part of a page, so need to
+ // copy the modified part into the current page contents in a temp
+ // buffer so that we can encode the entire page
+ ByteBuffer fullPage = _fullPageEncodeBufferH.setPage(
+ this, pageNumber);
+
+ // copy the modified part to the full page
+ fullPage.position(pageOffset);
+ fullPage.put(page);
+ fullPage.rewind();
+
+ // reset so we can write the whole page
+ page = fullPage;
+ pageOffset = 0;
+
+ } else {
+
+ _fullPageEncodeBufferH.possiblyInvalidate(pageNumber, null);
+ }
+ }
+
// re-encode page
encodedPage = _codecHandler.encodePage(page, pageNumber, pageOffset);
}
+
try {
encodedPage.position(pageOffset);
_channel.write(encodedPage, (getPageOffset(pageNumber) + pageOffset));
@@ -230,12 +260,11 @@ public class PageChannel implements Channel, Flushable {
}
/**
- * Write a page to disk as a new page, appending it to the database
- * @param page Page to write
- * @return Page number at which the page was written
+ * Allocates a new page in the database. Data in the page is undefined
+ * until it is written in a call to {@link #writePage(ByteBuffer,int)}.
*/
- public int writeNewPage(ByteBuffer page) throws IOException
- {
+ public int allocateNewPage() throws IOException {
+ // this will force the file to be extended with mostly undefined bytes
long size = _channel.size();
if(size >= getFormat().MAX_DATABASE_SIZE) {
throw new IOException("Database is at maximum size " +
@@ -247,21 +276,18 @@ public class PageChannel implements Channel, Flushable {
getFormat().PAGE_SIZE);
}
- page.rewind();
-
- if(page.remaining() > getFormat().PAGE_SIZE) {
- throw new IllegalArgumentException("Page buffer is too large, size " +
- page.remaining());
- }
+ _forceBytes.rewind();
// 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
- // buffer in allocateNewPage)
- int pageOffset = (getFormat().PAGE_SIZE - page.remaining());
+ // data is written
+ int pageOffset = (getFormat().PAGE_SIZE - _forceBytes.remaining());
long offset = size + pageOffset;
int pageNumber = getNextPageNumber(size);
- _channel.write(_codecHandler.encodePage(page, pageNumber, pageOffset),
- offset);
+
+ // since we are just allocating page space at this point and not writing
+ // meaningful data, we do _not_ encode the page.
+ _channel.write(_forceBytes, offset);
+
// note, we "force" page removal because we know that this is an unused
// page (since we just added it to the file)
_globalUsageMap.removePageNumber(pageNumber, true);
@@ -269,15 +295,6 @@ public class PageChannel implements Channel, Flushable {
}
/**
- * Allocates a new page in the database. Data in the page is undefined
- * until it is written in a call to {@link #writePage(ByteBuffer,int)}.
- */
- public int allocateNewPage() throws IOException {
- // this will force the file to be extended with mostly undefined bytes
- return writeNewPage(_forceBytes);
- }
-
- /**
* Deallocate a previously used page in the database.
*/
public void deallocatePage(int pageNumber) throws IOException {
@@ -353,5 +370,4 @@ public class PageChannel implements Channel, Flushable {
.position(position)
.mark();
}
-
}