]> source.dussan.org Git - jackcess.git/commitdiff
switch from Integer list to BitSet in UsageMap
authorJames Ahlborn <jtahlborn@yahoo.com>
Mon, 9 Jul 2007 03:16:52 +0000 (03:16 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Mon, 9 Jul 2007 03:16:52 +0000 (03:16 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@150 f203690c-595d-4dc9-a70b-905162fa7fd2

src/java/com/healthmarketscience/jackcess/InlineUsageMap.java
src/java/com/healthmarketscience/jackcess/Table.java
src/java/com/healthmarketscience/jackcess/UsageMap.java

index daf6ae440ef3ff60e387dd18964d12e4ec005c34..da2459a1d03b619cc38c29ccf9d88afd29b4db2f 100644 (file)
@@ -42,9 +42,6 @@ public class InlineUsageMap extends UsageMap {
   /** Size in bytes of the map */
   private static final int MAP_SIZE = 64;
   
-  /** First page that this usage map applies to */
-  private int _startPage = 0;
-  
   /**
    * @param pageChannel Used to read in pages
    * @param dataBuffer Buffer that contains this map's declaration
@@ -57,38 +54,38 @@ public class InlineUsageMap extends UsageMap {
   throws IOException
   {
     super(pageChannel, dataBuffer, pageNum, format, rowStart);
-    _startPage = dataBuffer.getInt(rowStart + 1);
-    processMap(dataBuffer, 0, _startPage);
+    int startPage = dataBuffer.getInt(rowStart + 1);
+    processMap(dataBuffer, 0, startPage);
   }
   
   //Javadoc copied from UsageMap
   protected void addOrRemovePageNumber(final int pageNumber, boolean add)
   throws IOException
   {
-    if (add && pageNumber < _startPage) {
+    int startPage = getStartPage();
+    if (add && pageNumber < startPage) {
       throw new IOException("Can't add page number " + pageNumber +
-          " because it is less than start page " + _startPage);
+          " because it is less than start page " + startPage);
     }
-    int relativePageNumber = pageNumber - _startPage;
+    int relativePageNumber = pageNumber - startPage;
     ByteBuffer buffer = getDataBuffer();
-    if ((!add && !getPageNumbers().remove(new Integer(pageNumber))) || (add &&
-        (relativePageNumber > MAP_SIZE * 8 - 1)))
+    if ((!add && !getPageNumbers().get(relativePageNumber)) ||
+        (add && (relativePageNumber > MAP_SIZE * 8 - 1)))
     {
       //Increase the start page to the current page and clear out the map.
-      _startPage = pageNumber;
+      startPage = pageNumber;
+      setStartPage(startPage);
       buffer.position(getRowStart() + 1);
-      buffer.putInt(_startPage);
+      buffer.putInt(startPage);
       getPageNumbers().clear();
       if (!add) {
         for (int j = 0; j < MAP_SIZE; j++) {
           buffer.put((byte) 0xff); //Fill bitmap with 1s
         }
-        for (int j = _startPage; j < _startPage + MAP_SIZE * 8; j++) {
-          getPageNumbers().add(new Integer(j)); //Fill our list with page numbers
-        }
+        getPageNumbers().set(0, (MAP_SIZE * 8)); //Fill our list with page numbers
       }
       getPageChannel().writePage(buffer, getDataPageNumber());
-      relativePageNumber = pageNumber - _startPage;
+      relativePageNumber = pageNumber - startPage;
     }
     updateMap(pageNumber, relativePageNumber, 1 << (relativePageNumber % 8), buffer, add);
     //Write the updated map back to disk
index 4a2489773185200067e064993c559463e04f6288..05c32575f1f2129bb094556cb108f503b5c1bf87 100644 (file)
@@ -31,14 +31,12 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.ListIterator;
 import java.util.Map;
 import java.util.NoSuchElementException;
 
@@ -113,6 +111,8 @@ public class Table
   private String _name;
   /** Usage map of pages that this table owns */
   private UsageMap _ownedPages;
+  /** Iterator over the pages that this table owns */
+  private UsageMap.PageIterator _ownedPagesIterator;
   /** Usage map of pages that this table owns with free space on them */
   private UsageMap _freeSpacePages;
   
@@ -219,7 +219,7 @@ public class Table
   public void reset() {
     _rowsLeftOnPage = 0;
     _currentRowInPage = INVALID_ROW_NUMBER;
-    _ownedPages.reset();
+    _ownedPagesIterator.reset();
     _rowState.reset();
   }
   
@@ -421,8 +421,8 @@ public class Table
         // reset row number
         _currentRowInPage = INVALID_ROW_NUMBER;
         
-        Integer nextPageNumber = _ownedPages.getNextPage();
-        if (nextPageNumber == null) {
+        int nextPageNumber = _ownedPagesIterator.getNextPage();
+        if (nextPageNumber == PageChannel.INVALID_PAGE_NUMBER) {
           //No more owned pages.  No more rows.
           return null;
         }
@@ -573,6 +573,7 @@ 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);
+    _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);
@@ -734,10 +735,10 @@ public class Table
 
     // find last data page (Not bothering to check other pages for free
     // space.)
-    List<Integer> pageNumbers = _ownedPages.getPageNumbers();
-    for(ListIterator<Integer> pageIter = pageNumbers.listIterator(
-            pageNumbers.size()); pageIter.hasPrevious(); ) {
-      Integer tmpPageNumber = pageIter.previous();
+    for(UsageMap.PageIterator revPageIter = _ownedPages.reverseIterator();
+        revPageIter.hasNextPage(); )
+    {
+      int tmpPageNumber = revPageIter.getNextPage();
       _pageChannel.readPage(dataPage, tmpPageNumber);
       if(dataPage.get() == PageTypes.DATA) {
         // found last data page
index a0f1f59afb94a9f5118b80a9d089ffbe752396ce..1d2fcd4be1dbb399fffef41f34bb1fa801133af4 100644 (file)
@@ -30,7 +30,9 @@ package com.healthmarketscience.jackcess;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
+import java.util.BitSet;
 import java.util.List;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
@@ -47,8 +49,6 @@ public abstract class UsageMap {
   /** Reference map type, for maps that are too large to fit inline */
   public static final byte MAP_TYPE_REFERENCE = 0x1;
   
-  /** Index of the current page, incremented after calling getNextPage */
-  private int _currentPageIndex = 0;
   /** Page number of the map declaration */
   private int _dataPageNum;
   /** Offset of the data page at which the usage map data starts */
@@ -57,12 +57,17 @@ public abstract class UsageMap {
   private short _rowStart;
   /** Format of the database that contains this usage map */
   private JetFormat _format;
-  /** List of page numbers used */
-  private List<Integer> _pageNumbers = new ArrayList<Integer>();
+  /** First page that this usage map applies to */
+  private int _startPage;
+  /** bits representing page numbers used, offset from _startPage */
+  private BitSet _pageNumbers = new BitSet();
   /** Buffer that contains the usage map declaration page */
   private ByteBuffer _dataBuffer;
   /** Used to read in pages */
   private PageChannel _pageChannel;
+  /** modification count on the usage map, used to keep the iterators in
+      sync */
+  private int _modCount = 0;
   
   /**
    * @param pageChannel Used to read in pages
@@ -115,19 +120,19 @@ public abstract class UsageMap {
           dataBuffer.limit() - _rowStart));
     }
   }
+
+  public PageIterator iterator() {
+    return new ForwardPageIterator();
+  }
+
+  public PageIterator reverseIterator() {
+    return new ReversePageIterator();
+  }
   
   protected short getRowStart() {
     return _rowStart;
   }
   
-  public List<Integer> getPageNumbers() {
-    return _pageNumbers;
-  }
-
-  public Integer getCurrentPageNumber() {
-    return _pageNumbers.get(_currentPageIndex - 1);
-  }
-
   protected void setStartOffset(int startOffset) {
     _startOffset = startOffset;
   }
@@ -152,24 +157,16 @@ public abstract class UsageMap {
     return _format;
   }
 
-  /**
-   * After calling this method, getNextPage will return the first page in the
-   * map
-   */
-  public void reset() {
-    _currentPageIndex = 0;
+  protected int getStartPage() {
+    return _startPage;
   }
-  
-  /**
-   * @return non-<code>null</code> if there was another page to read,
-   *         <code>null</code> otherwise
-   */
-  public Integer getNextPage() {
-    if (_pageNumbers.size() > _currentPageIndex) {
-      return _pageNumbers.get(_currentPageIndex++);
-    } else {
-      return null;
-    }
+
+  protected void setStartPage(int newStartPage) {
+    _startPage = newStartPage;
+  }
+
+  protected BitSet getPageNumbers() {
+    return _pageNumbers;
   }
   
   /**
@@ -177,13 +174,14 @@ public abstract class UsageMap {
    */
   protected void processMap(ByteBuffer buffer, int pageIndex, int startPage) {
     int byteCount = 0;
+    _startPage = startPage;
     while (buffer.hasRemaining()) {
       byte b = buffer.get();
       for (int i = 0; i < 8; i++) {
         if ((b & (1 << i)) != 0) {
-          Integer pageNumber = new Integer((startPage + byteCount * 8 + i) +
-              (pageIndex * _format.PAGES_PER_USAGE_MAP_PAGE));
-          _pageNumbers.add(pageNumber);
+          int pageNumberOffset = (byteCount * 8 + i) +
+            (pageIndex * _format.PAGES_PER_USAGE_MAP_PAGE);
+          _pageNumbers.set(pageNumberOffset);
         }
       }
       byteCount++;
@@ -195,9 +193,15 @@ public abstract class UsageMap {
    */
   public void addPageNumber(int pageNumber) throws IOException {
     //Sanity check, only on in debug mode for performance considerations
-    if (LOG.isDebugEnabled() && _pageNumbers.contains(new Integer(pageNumber))) {
-      throw new IOException("Page number " + pageNumber + " already in usage map");
+    if (LOG.isDebugEnabled()) {
+      int pageNumberOffset = pageNumber - _startPage;
+      if((pageNumberOffset < 0) ||
+         _pageNumbers.get(pageNumberOffset)) {
+        throw new IOException("Page number " + pageNumber +
+                              " already in usage map");
+      }
     }
+    ++_modCount;
     addOrRemovePageNumber(pageNumber, true);
   }
   
@@ -205,6 +209,7 @@ public abstract class UsageMap {
    * Remove a page number from this usage map
    */
   public void removePageNumber(int pageNumber) throws IOException {
+    ++_modCount;
     addOrRemovePageNumber(pageNumber, false);
   }
   
@@ -215,17 +220,27 @@ public abstract class UsageMap {
     int offset = relativePageNumber / 8;
     byte b = buffer.get(_startOffset + offset);
     //Apply the bitmask
+    int pageNumberOffset = absolutePageNumber - _startPage;
     if (add) {
       b |= bitmask;
-      _pageNumbers.add(new Integer(absolutePageNumber));
+      _pageNumbers.set(pageNumberOffset);
     } else {
       b &= ~bitmask;
+      _pageNumbers.clear(pageNumberOffset);
     }
     buffer.put(_startOffset + offset, b);
   }
   
   public String toString() {
-    return "page numbers: " + _pageNumbers;
+    StringBuilder builder = new StringBuilder("page numbers: [");
+    for(PageIterator iter = iterator(); iter.hasNextPage(); ) {
+      builder.append(iter.getNextPage());
+      if(iter.hasNextPage()) {
+        builder.append(", ");
+      }
+    }
+    builder.append("]");
+    return builder.toString();
   }
   
   /**
@@ -233,5 +248,116 @@ public abstract class UsageMap {
    * @param add True to add it, false to remove it
    */
   protected abstract void addOrRemovePageNumber(int pageNumber, boolean add) throws IOException;
+
+  /**
+   * Utility class to iterate over the pages in the UsageMap.
+   */
+  public abstract class PageIterator
+  {
+    /** the next set page number bit */
+    protected int _nextSetBit;
+    /** the previous set page number bit */
+    protected int _prevSetBit;
+    /** the last read modification count on the UsageMap.  we track this so
+        that the iterator can detect updates to the usage map while iterating
+        and act accordingly */
+    protected int _lastModCount;
+
+    protected PageIterator() {
+    }
+
+    /**
+     * @return {@code true} if there is another valid page, {@code false}
+     *         otherwise.
+     */
+    public boolean hasNextPage() {
+      if((_nextSetBit < 0) &&
+         (_lastModCount != _modCount)) {
+        // recheck the last page, in case more showed up
+        if(_prevSetBit < 0) {
+          // we were at the beginning
+          reset();
+        } else {
+          _nextSetBit = _prevSetBit;
+          getNextPage();
+        }
+      }
+      return(_nextSetBit >= 0);
+    }      
+    
+    /**
+     * @return valid page number if there was another page to read,
+     *         {@link PageChannel#INVALID_PAGE_NUMBER} otherwise
+     */
+    public abstract int getNextPage();
+
+    /**
+     * After calling this method, getNextPage will return the first page in the
+     * map
+     */
+    public abstract void reset();
+  }
+  
+  /**
+   * Utility class to iterate forward over the pages in the UsageMap.
+   */
+  public class ForwardPageIterator extends PageIterator
+  {
+    private ForwardPageIterator() {
+      reset();
+    }
+    
+    @Override
+    public int getNextPage() {
+      if (hasNextPage()) {
+        _lastModCount = _modCount;
+        _prevSetBit = _nextSetBit;
+        _nextSetBit = _pageNumbers.nextSetBit(_nextSetBit + 1);
+        return _prevSetBit + _startPage;
+      } else {
+        return PageChannel.INVALID_PAGE_NUMBER;
+      }
+    }
+
+    @Override
+    public final void reset() {
+      _lastModCount = _modCount;
+      _prevSetBit = -1;
+      _nextSetBit = _pageNumbers.nextSetBit(0);
+    }
+  }
+  
+  /**
+   * Utility class to iterate backward over the pages in the UsageMap.
+   */
+  public class ReversePageIterator extends PageIterator
+  {
+    private ReversePageIterator() {
+      reset();
+    }
+    
+    @Override
+    public int getNextPage() {
+      if(hasNextPage()) {
+        _lastModCount = _modCount;
+        _prevSetBit = _nextSetBit;
+        --_nextSetBit;
+        while(hasNextPage() && !_pageNumbers.get(_nextSetBit)) {
+          --_nextSetBit;
+        }
+        return _prevSetBit + _startPage;
+      } else {
+        return PageChannel.INVALID_PAGE_NUMBER;
+      }
+    }
+
+    @Override
+    public final void reset() {
+      _lastModCount = _modCount;
+      _prevSetBit = -1;
+      _nextSetBit = _pageNumbers.length() - 1;
+    }
+  }
+
   
 }