From: James Ahlborn Date: Tue, 10 Jul 2007 16:41:25 +0000 (+0000) Subject: rework inline usage map handling to handle out-of-range cases better and with more... X-Git-Tag: rel_1_1_9~6 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=a97ce25f570f8867e93ff2e244b9dc5a8fe4b2bc;p=jackcess.git rework inline usage map handling to handle out-of-range cases better and with more clarity git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@156 f203690c-595d-4dc9-a70b-905162fa7fd2 --- diff --git a/src/java/com/healthmarketscience/jackcess/ByteUtil.java b/src/java/com/healthmarketscience/jackcess/ByteUtil.java index fdfa9db..29ddecd 100644 --- a/src/java/com/healthmarketscience/jackcess/ByteUtil.java +++ b/src/java/com/healthmarketscience/jackcess/ByteUtil.java @@ -181,6 +181,35 @@ public final class ByteUtil { buffer.order(origOrder); } } + + /** + * Sets all bits in the given byte range to 0. + */ + public static void clearRange(ByteBuffer buffer, int start, + int end) + { + putRange(buffer, start, end, (byte)0x00); + } + + /** + * Sets all bits in the given byte range to 1. + */ + public static void fillRange(ByteBuffer buffer, int start, + int end) + { + putRange(buffer, start, end, (byte)0xff); + } + + /** + * Sets all bytes in the given byte range to the given byte value. + */ + public static void putRange(ByteBuffer buffer, int start, + int end, byte b) + { + for(int i = start; i < end; ++i) { + buffer.put(i, b); + } + } /** * Convert a byte buffer to a hexadecimal string for display diff --git a/src/java/com/healthmarketscience/jackcess/JetFormat.java b/src/java/com/healthmarketscience/jackcess/JetFormat.java index 501d19b..b88c5f6 100644 --- a/src/java/com/healthmarketscience/jackcess/JetFormat.java +++ b/src/java/com/healthmarketscience/jackcess/JetFormat.java @@ -118,7 +118,6 @@ public abstract class JetFormat { public final int SIZE_COLUMN_DEF_BLOCK; public final int SIZE_INDEX_ENTRY_MASK; - public final int PAGES_PER_USAGE_MAP_PAGE; public final int USAGE_MAP_TABLE_BYTE_LENGTH; public final Charset CHARSET; @@ -203,7 +202,6 @@ public abstract class JetFormat { SIZE_COLUMN_DEF_BLOCK = defineSizeColumnDefBlock(); SIZE_INDEX_ENTRY_MASK = defineSizeIndexEntryMask(); - PAGES_PER_USAGE_MAP_PAGE = definePagesPerUsageMapPage(); USAGE_MAP_TABLE_BYTE_LENGTH = defineUsageMapTableByteLength(); CHARSET = defineCharset(); @@ -266,7 +264,6 @@ public abstract class JetFormat { protected abstract int defineSizeColumnDefBlock(); protected abstract int defineSizeIndexEntryMask(); - protected abstract int definePagesPerUsageMapPage(); protected abstract int defineUsageMapTableByteLength(); protected abstract Charset defineCharset(); @@ -339,7 +336,6 @@ public abstract class JetFormat { protected int defineSizeColumnDefBlock() { return 25; } protected int defineSizeIndexEntryMask() { return 453; } - protected int definePagesPerUsageMapPage() { return 4092 * 8; } protected int defineUsageMapTableByteLength() { return 64; } protected Charset defineCharset() { return Charset.forName("UTF-16LE"); } diff --git a/src/java/com/healthmarketscience/jackcess/PageChannel.java b/src/java/com/healthmarketscience/jackcess/PageChannel.java index b81987b..ca824db 100644 --- a/src/java/com/healthmarketscience/jackcess/PageChannel.java +++ b/src/java/com/healthmarketscience/jackcess/PageChannel.java @@ -72,7 +72,10 @@ public class PageChannel implements Channel { _autoSync = autoSync; //Null check only exists for unit tests. Channel should never normally be null. if (channel != null) { - _globalUsageMap = UsageMap.read(this, PAGE_GLOBAL_USAGE_MAP, (byte) 0, format); + // note the global usage map is a special map where any page outside of + // the current range is assumed to be "on" + _globalUsageMap = UsageMap.read(this, PAGE_GLOBAL_USAGE_MAP, (byte) 0, + format, true); } } @@ -131,10 +134,15 @@ public class PageChannel implements Channel { * @param page Page to write * @return Page number at which the page was written */ - public int writeNewPage(ByteBuffer page) throws IOException { + public int writeNewPage(ByteBuffer page) throws IOException + { long size = _channel.size(); page.rewind(); - _channel.write(page, size); + // 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) + long offset = size + (_format.PAGE_SIZE - page.remaining()); + _channel.write(page, offset); int pageNumber = (int) (size / _format.PAGE_SIZE); _globalUsageMap.removePageNumber(pageNumber); //force is done here return pageNumber; @@ -145,14 +153,8 @@ public class PageChannel implements Channel { * until it is written in a call to {@link #writePage}. */ public int allocateNewPage() throws IOException { - long size = _channel.size(); - FORCE_BYTES.rewind(); - long offset = size + _format.PAGE_SIZE - FORCE_BYTES.remaining(); // this will force the file to be extended with mostly undefined bytes - _channel.write(FORCE_BYTES, offset); - int pageNumber = (int) (size / _format.PAGE_SIZE); - _globalUsageMap.removePageNumber(pageNumber); //force is done here - return pageNumber; + return writeNewPage(FORCE_BYTES); } /** diff --git a/src/java/com/healthmarketscience/jackcess/Table.java b/src/java/com/healthmarketscience/jackcess/Table.java index 05c3257..bc04545 100644 --- a/src/java/com/healthmarketscience/jackcess/Table.java +++ b/src/java/com/healthmarketscience/jackcess/Table.java @@ -572,11 +572,13 @@ public class Table byte rowNum = tableBuffer.get(_format.OFFSET_OWNED_PAGES); int pageNum = ByteUtil.get3ByteInt(tableBuffer, _format.OFFSET_OWNED_PAGES + 1); - _ownedPages = UsageMap.read(_pageChannel, pageNum, rowNum, _format); + _ownedPages = UsageMap.read(_pageChannel, pageNum, rowNum, _format, + false); _ownedPagesIterator = _ownedPages.iterator(); rowNum = tableBuffer.get(_format.OFFSET_FREE_SPACE_PAGES); pageNum = ByteUtil.get3ByteInt(tableBuffer, _format.OFFSET_FREE_SPACE_PAGES + 1); - _freeSpacePages = UsageMap.read(_pageChannel, pageNum, rowNum, _format); + _freeSpacePages = UsageMap.read(_pageChannel, pageNum, rowNum, _format, + false); for (int i = 0; i < _indexCount; i++) { Index index = new Index(_tableDefPageNumber, _pageChannel, _format); diff --git a/src/java/com/healthmarketscience/jackcess/TempPageHolder.java b/src/java/com/healthmarketscience/jackcess/TempPageHolder.java index d2a59a6..23b1b2d 100644 --- a/src/java/com/healthmarketscience/jackcess/TempPageHolder.java +++ b/src/java/com/healthmarketscience/jackcess/TempPageHolder.java @@ -78,31 +78,20 @@ public abstract class TempPageHolder { } /** - * Sets the current page number to INVALID_PAGE_NUMBER and returns a new - * empty buffer. Once the new page has been written to the page channel, - * {@link #finishNewPage} must be called with the new page number. + * Allocates a new buffer in the database (with undefined data) and returns + * a new empty buffer. */ - public ByteBuffer startNewPage(PageChannel pageChannel) + public ByteBuffer setNewPage(PageChannel pageChannel) throws IOException { + // ditch any current data clear(); + // allocate a new page in the database + _pageNumber = pageChannel.allocateNewPage(); + // return a new buffer return getBuffer(pageChannel); } - /** - * Sets the new page number for the current buffer of data. Must be called - * after the buffer returned from {@link #startNewPage} has been written to - * the PageChannel as a new page and assigned a number. - */ - public void finishNewPage(int pageNumber) - throws IOException - { - if(_pageNumber != PageChannel.INVALID_PAGE_NUMBER) { - throw new IOException("page number should not be set"); - } - _pageNumber = pageNumber; - } - /** * Forces any current page data to be disregarded (any * getPage/setPage call must reload page data). diff --git a/src/java/com/healthmarketscience/jackcess/UsageMap.java b/src/java/com/healthmarketscience/jackcess/UsageMap.java index 7034db6..55c2015 100644 --- a/src/java/com/healthmarketscience/jackcess/UsageMap.java +++ b/src/java/com/healthmarketscience/jackcess/UsageMap.java @@ -109,7 +109,8 @@ public class UsageMap * which type of map is found */ public static UsageMap read(PageChannel pageChannel, int pageNum, - byte rowNum, JetFormat format) + byte rowNum, JetFormat format, + boolean assumeOutOfRangeBitsOn) throws IOException { ByteBuffer tableBuffer = pageChannel.createPageBuffer(); @@ -120,15 +121,15 @@ public class UsageMap byte mapType = tableBuffer.get(rowStart); UsageMap rtn = new UsageMap(pageChannel, tableBuffer, pageNum, format, rowStart); - rtn.initHandler(mapType); + rtn.initHandler(mapType, assumeOutOfRangeBitsOn); return rtn; } - private void initHandler(byte mapType) + private void initHandler(byte mapType, boolean assumeOutOfRangeBitsOn) throws IOException { if (mapType == MAP_TYPE_INLINE) { - _handler = new InlineHandler(); + _handler = new InlineHandler(assumeOutOfRangeBitsOn); } else if (mapType == MAP_TYPE_REFERENCE) { _handler = new ReferenceHandler(); } else { @@ -176,6 +177,10 @@ public class UsageMap return _startPage; } + protected int getEndPage() { + return _endPage; + } + protected BitSet getPageNumbers() { return _pageNumbers; } @@ -230,6 +235,20 @@ public class UsageMap (pageNumber - _startPage) : -1); } + protected void clearTableAndPages() + { + // reset some values + _pageNumbers.clear(); + _startPage = 0; + _endPage = 0; + ++_modCount; + + // clear out the table data + int tableStart = getRowStart() + getFormat().OFFSET_MAP_START - 4; + int tableEnd = tableStart + getFormat().USAGE_MAP_TABLE_BYTE_LENGTH + 4; + ByteUtil.clearRange(_tableBuffer, tableStart, tableEnd); + } + protected void writeTable() throws IOException { @@ -240,7 +259,7 @@ public class UsageMap /** * Read in the page numbers in this inline map */ - protected void processMap(ByteBuffer buffer, int pageIndex) + protected void processMap(ByteBuffer buffer, int bufferStartPage) { int byteCount = 0; while (buffer.hasRemaining()) { @@ -248,8 +267,7 @@ public class UsageMap if(b != (byte)0) { for (int i = 0; i < 8; i++) { if ((b & (1 << i)) != 0) { - int pageNumberOffset = (byteCount * 8 + i) + - (pageIndex * _format.PAGES_PER_USAGE_MAP_PAGE); + int pageNumberOffset = (byteCount * 8 + i) + bufferStartPage; _pageNumbers.set(pageNumberOffset); } } @@ -262,15 +280,6 @@ public class UsageMap * Add a page number to this usage map */ public void addPageNumber(int pageNumber) throws IOException { - //Sanity check, only on in debug mode for performance considerations - if (LOG.isDebugEnabled()) { - int pageNumberOffset = pageNumber - _startPage; - if((pageNumberOffset < 0) || - _pageNumbers.get(pageNumberOffset)) { - throw new IOException("Page number " + pageNumber + - " already in usage map"); - } - } ++_modCount; _handler.addOrRemovePageNumber(pageNumber, true); } @@ -286,13 +295,23 @@ public class UsageMap protected void updateMap(int absolutePageNumber, int bufferRelativePageNumber, ByteBuffer buffer, boolean add) + throws IOException { - //Find the byte to apply the bitmask to + //Find the byte to which to apply the bitmask and create the bitmask int offset = bufferRelativePageNumber / 8; int bitmask = 1 << (bufferRelativePageNumber % 8); byte b = buffer.get(_startOffset + offset); + + // check current value for this page number + int pageNumberOffset = pageNumberToBitIndex(absolutePageNumber); + boolean isOn = _pageNumbers.get(pageNumberOffset); + if(isOn == add) { + throw new IOException("Page number " + absolutePageNumber + " already " + + ((add) ? "added to" : "removed from") + + " usage map"); + } + //Apply the bitmask - int pageNumberOffset = absolutePageNumber - _startPage; if (add) { b |= bitmask; _pageNumbers.set(pageNumberOffset); @@ -302,27 +321,22 @@ public class UsageMap } buffer.put(_startOffset + offset, b); } - + + /** + * Promotes and inline usage map to a reference usage map. + */ private void promoteInlineHandlerToReferenceHandler(int newPageNumber) throws IOException { - System.out.println("FOO promoting!"); - // copy current page number info to new references and then clear old int oldStartPage = _startPage; BitSet oldPageNumbers = (BitSet)_pageNumbers.clone(); - _pageNumbers.clear(); - _startPage = 0; // clear out the main table (inline usage map data and start page) - int tableStart = _startOffset - 4; - int tableEnd = tableStart + getFormat().USAGE_MAP_TABLE_BYTE_LENGTH + 4; - for(int i = tableStart; i < tableEnd; ++i) { - _tableBuffer.put(i, (byte)0); - } - + clearTableAndPages(); + // set the new map type - _tableBuffer.put(tableStart - 1, MAP_TYPE_REFERENCE); + _tableBuffer.put(getRowStart(), MAP_TYPE_REFERENCE); // write the new table data writeTable(); @@ -330,14 +344,24 @@ public class UsageMap // set new handler _handler = new ReferenceHandler(); - // now add all the old pages back in + // update new handler with old data + reAddPages(oldStartPage, oldPageNumbers, newPageNumber); + } + + private void reAddPages(int oldStartPage, BitSet oldPageNumbers, + int newPageNumber) + throws IOException + { + // add all the old pages back in for(int i = oldPageNumbers.nextSetBit(0); i >= 0; i = oldPageNumbers.nextSetBit(i + 1)) { addPageNumber(oldStartPage + i); } - // and then add the new page - addPageNumber(newPageNumber); + if(newPageNumber != PageChannel.INVALID_PAGE_NUMBER) { + // and then add the new page + addPageNumber(newPageNumber); + } } public String toString() { @@ -374,54 +398,186 @@ public class UsageMap */ private class InlineHandler extends Handler { - private InlineHandler() + private final boolean _assumeOutOfRangeBitsOn; + + private InlineHandler(boolean assumeOutOfRangeBitsOn) throws IOException { + _assumeOutOfRangeBitsOn = assumeOutOfRangeBitsOn; int startPage = getTableBuffer().getInt(getRowStart() + 1); - setPageRange(startPage, startPage + - (getFormat().USAGE_MAP_TABLE_BYTE_LENGTH * 8)); + setInlinePageRange(startPage); processMap(getTableBuffer(), 0); } - + + private int getMaxInlinePages() { + return(getFormat().USAGE_MAP_TABLE_BYTE_LENGTH * 8); + } + + /** + * Sets the page range for an inline usage map starting from the given + * page. + */ + private void setInlinePageRange(int startPage) { + setPageRange(startPage, startPage + getMaxInlinePages()); + } + @Override public void addOrRemovePageNumber(int pageNumber, boolean add) throws IOException { - int startPage = getStartPage(); - if (add && pageNumber < startPage) { - throw new IOException("Can't add page number " + pageNumber + - " because it is less than start page " + startPage); - } - int relativePageNumber = pageNumber - startPage; - ByteBuffer buffer = getTableBuffer(); - if ((!add && !getPageNumbers().get(relativePageNumber)) || - (add && (relativePageNumber > - (getFormat().USAGE_MAP_TABLE_BYTE_LENGTH * 8 - 1)))) - { + if(isPageWithinRange(pageNumber)) { + + // easy enough, just update the inline data + int bufferRelativePageNumber = pageNumberToBitIndex(pageNumber); + updateMap(pageNumber, bufferRelativePageNumber, getTableBuffer(), add); + // Write the updated map back to disk + writeTable(); + + } else { + + // uh-oh, we've split our britches. what now? determine what our + // status is + int firstPage = getFirstPageNumber(); + int lastPage = getLastPageNumber(); + if(add) { - // we need to expand to a reference handler - promoteInlineHandlerToReferenceHandler(pageNumber); - return; - } - //Increase the start page to the current page and clear out the map. - startPage = pageNumber; - setPageRange(startPage, startPage + - (getFormat().USAGE_MAP_TABLE_BYTE_LENGTH * 8)); - buffer.position(getRowStart() + 1); - buffer.putInt(startPage); - getPageNumbers().clear(); - if (!add) { - for (int j = 0; j < getFormat().USAGE_MAP_TABLE_BYTE_LENGTH; j++) { - buffer.put((byte) 0xff); //Fill bitmap with 1s + + // we can ignore out-of-range page addition if we are already + // assuming out-of-range bits are "on" + if(!_assumeOutOfRangeBitsOn) { + + // we are adding, can we shift the bits and stay inline? + if(firstPage == PageChannel.INVALID_PAGE_NUMBER) { + // no pages currently + firstPage = pageNumber; + lastPage = pageNumber; + } else if(pageNumber > lastPage) { + lastPage = pageNumber; + } else { + firstPage = pageNumber; + } + if((lastPage - firstPage + 1) < getMaxInlinePages()) { + + // we can still fit within an inline map + moveToNewStartPage(firstPage, pageNumber); + + } else { + // not going to happen, need to promote the usage map to a + // reference map + promoteInlineHandlerToReferenceHandler(pageNumber); + } + } + } else { + + // we are removing, what does that mean? + if(_assumeOutOfRangeBitsOn) { + + // we are using an inline map and assuming that anything not + // within the current range is "on" + if((firstPage == PageChannel.INVALID_PAGE_NUMBER) || + (pageNumber > lastPage)) { + + // move to new start page, filling in as we move + moveToNewStartPageForRemove(firstPage, lastPage, pageNumber); + + } else { + + // umm, we are removing an already passed bit. whatever, we are + // not going back for it + } + + } else { + + // this should not happen, we are removing a page which is not in + // the map + throw new IOException("Page number " + pageNumber + + " already removed from usage map"); } - getPageNumbers().set(0, (getFormat().USAGE_MAP_TABLE_BYTE_LENGTH * 8)); //Fill our list with page numbers } - writeTable(); - relativePageNumber = pageNumber - startPage; + } - updateMap(pageNumber, relativePageNumber, buffer, add); - //Write the updated map back to disk + } + + /** + * Shifts the inline usage map so that it now starts with the given page. + * @param newStartPage new page at which to start + * @param newPageNumber optional page number to add once the map has been + * shifted to the new start page + */ + private void moveToNewStartPage(int newStartPage, int newPageNumber) + throws IOException + { + int oldStartPage = getStartPage(); + BitSet oldPageNumbers = (BitSet)getPageNumbers().clone(); + + // clear out the main table (inline usage map data and start page) + clearTableAndPages(); + + // write new start page + ByteBuffer tableBuffer = getTableBuffer(); + tableBuffer.position(getRowStart() + 1); + tableBuffer.putInt(newStartPage); + + // write the new table data writeTable(); + + // set new page range + setInlinePageRange(newStartPage); + + // put the pages back in + reAddPages(oldStartPage, oldPageNumbers, newPageNumber); + } + + /** + * Shifts the inline usage map so that it now starts with the given + * firstPage (if valid), otherwise the newPageNumber. Any page numbers + * added to the end of the usage map are set to "on". + * @param firstPage current first used page + * @param lastPage current last used page + * @param newPageNumber page number to remove once the map has been + * shifted to the new start page + */ + private void moveToNewStartPageForRemove(int firstPage, + int lastPage, + int newPageNumber) + throws IOException + { + int newStartPage = firstPage; + if(newStartPage == PageChannel.INVALID_PAGE_NUMBER) { + newStartPage = newPageNumber; + } else if((newPageNumber - newStartPage + 1) >= + getMaxInlinePages()) { + // this will not move us far enough to hold the new page. just + // discard any initial unused pages + newStartPage += (newPageNumber - getMaxInlinePages() + 1); + } + + // move the current data + moveToNewStartPage(newStartPage, PageChannel.INVALID_PAGE_NUMBER); + + if(firstPage == PageChannel.INVALID_PAGE_NUMBER) { + + // this is the common case where we left everything behind + int tableStart = getRowStart() + getFormat().OFFSET_MAP_START; + int tableEnd = tableStart + getFormat().USAGE_MAP_TABLE_BYTE_LENGTH; + ByteUtil.fillRange(_tableBuffer, tableStart, tableEnd); + + // write out the updated table + writeTable(); + + // "add" all the page numbers + getPageNumbers().set(0, getMaxInlinePages()); + + } else { + + // add every new page manually + for(int i = (lastPage + 1); i < getEndPage(); ++i) { + addPageNumber(i); + } + } + + // lastly, remove the new page + removePageNumber(newPageNumber); } } @@ -441,18 +597,16 @@ public class UsageMap private ReferenceHandler() throws IOException { - int numPages = (getFormat().USAGE_MAP_TABLE_BYTE_LENGTH / 4) + 1; - int numBitsPerPage = ((getFormat().PAGE_SIZE - - getFormat().OFFSET_USAGE_MAP_PAGE_DATA) * 8); + int numUsagePages = (getFormat().USAGE_MAP_TABLE_BYTE_LENGTH / 4) + 1; setStartOffset(getFormat().OFFSET_USAGE_MAP_PAGE_DATA); - setPageRange(0, (numPages * numBitsPerPage)); + setPageRange(0, (numUsagePages * getMaxPagesPerUsagePage())); + // there is no "start page" for a reference usage map, so we get an // extra page reference on top of the number of page references that fit // in the table - for (int i = 0; i < numPages; i++) { + for (int i = 0; i < numUsagePages; i++) { int mapPageNum = getTableBuffer().getInt( - getRowStart() + getFormat().OFFSET_REFERENCE_MAP_PAGE_NUMBERS + - (4 * i)); + calculateMapPagePointerOffset(i)); if (mapPageNum > 0) { ByteBuffer mapPageBuffer = _mapPageHolder.setPage(getPageChannel(), mapPageNum); @@ -463,24 +617,35 @@ public class UsageMap pageType); } mapPageBuffer.position(getFormat().OFFSET_USAGE_MAP_PAGE_DATA); - processMap(mapPageBuffer, i); + processMap(mapPageBuffer, (getMaxPagesPerUsagePage() * i)); } } } - + + private int getMaxPagesPerUsagePage() { + return((getFormat().PAGE_SIZE - getFormat().OFFSET_USAGE_MAP_PAGE_DATA) + * 8); + } + @Override public void addOrRemovePageNumber(int pageNumber, boolean add) throws IOException { - int pageIndex = (int) Math.floor(pageNumber / getFormat().PAGES_PER_USAGE_MAP_PAGE); - int mapPageNum = getTableBuffer().getInt(calculateMapPagePointerOffset(pageIndex)); + if(!isPageWithinRange(pageNumber)) { + throw new IOException("Page number " + pageNumber + + " is out of supported range"); + } + int pageIndex = (int)(pageNumber / getMaxPagesPerUsagePage()); + int mapPageNum = getTableBuffer().getInt( + calculateMapPagePointerOffset(pageIndex)); if(mapPageNum <= 0) { - //Need to create a new usage map page + // Need to create a new usage map page mapPageNum = createNewUsageMapPage(pageIndex); } ByteBuffer mapPageBuffer = _mapPageHolder.setPage(getPageChannel(), mapPageNum); - updateMap(pageNumber, pageNumber - (getFormat().PAGES_PER_USAGE_MAP_PAGE * pageIndex), + updateMap(pageNumber, + (pageNumber - (getMaxPagesPerUsagePage() * pageIndex)), mapPageBuffer, add); getPageChannel().writePage(mapPageBuffer, mapPageNum); } @@ -491,15 +656,14 @@ public class UsageMap * @param pageIndex Index of the page reference within the map declaration */ private int createNewUsageMapPage(int pageIndex) throws IOException { - ByteBuffer mapPageBuffer = _mapPageHolder.startNewPage(getPageChannel()); + ByteBuffer mapPageBuffer = _mapPageHolder.setNewPage(getPageChannel()); mapPageBuffer.put(PageTypes.USAGE_MAP); mapPageBuffer.put((byte) 0x01); //Unknown mapPageBuffer.putShort((short) 0); //Unknown for(int i = 0; i < mapPageBuffer.limit(); ++i) { byte b = mapPageBuffer.get(i); } - int mapPageNum = getPageChannel().writeNewPage(mapPageBuffer); - _mapPageHolder.finishNewPage(mapPageNum); + int mapPageNum = _mapPageHolder.getPageNumber(); getTableBuffer().putInt(calculateMapPagePointerOffset(pageIndex), mapPageNum); writeTable();