You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UsageMap.java 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*
  2. Copyright (c) 2005 Health Market Science, Inc.
  3. This library is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public
  5. License as published by the Free Software Foundation; either
  6. version 2.1 of the License, or (at your option) any later version.
  7. This library is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public
  12. License along with this library; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  14. USA
  15. You can contact Health Market Science at info@healthmarketscience.com
  16. or at the following address:
  17. Health Market Science
  18. 2700 Horizon Drive
  19. Suite 200
  20. King of Prussia, PA 19406
  21. */
  22. package com.healthmarketscience.jackcess;
  23. import java.io.IOException;
  24. import java.nio.ByteBuffer;
  25. import java.util.ArrayList;
  26. import java.util.List;
  27. import org.apache.commons.logging.Log;
  28. import org.apache.commons.logging.LogFactory;
  29. /**
  30. * Describes which database pages a particular table uses
  31. * @author Tim McCune
  32. */
  33. public abstract class UsageMap {
  34. private static final Log LOG = LogFactory.getLog(UsageMap.class);
  35. /** Inline map type */
  36. public static final byte MAP_TYPE_INLINE = 0x0;
  37. /** Reference map type, for maps that are too large to fit inline */
  38. public static final byte MAP_TYPE_REFERENCE = 0x1;
  39. /** Index of the current page, incremented after calling getNextPage */
  40. private int _currentPageIndex = 0;
  41. /** Page number of the map declaration */
  42. private int _dataPageNum;
  43. /** Offset of the data page at which the usage map data starts */
  44. private int _startOffset;
  45. /** Offset of the data page at which the usage map declaration starts */
  46. private short _rowStart;
  47. /** Format of the database that contains this usage map */
  48. private JetFormat _format;
  49. /** List of page numbers used */
  50. private List<Integer> _pageNumbers = new ArrayList<Integer>();
  51. /** Buffer that contains the usage map declaration page */
  52. private ByteBuffer _dataBuffer;
  53. /** Used to read in pages */
  54. private PageChannel _pageChannel;
  55. /**
  56. * @param pageChannel Used to read in pages
  57. * @param pageNum Page number that this usage map is contained in
  58. * @param rowNum Number of the row on the page that contains this usage map
  59. * @param format Format of the database that contains this usage map
  60. * @return Either an InlineUsageMap or a ReferenceUsageMap, depending on which
  61. * type of map is found
  62. */
  63. public static UsageMap read(PageChannel pageChannel, int pageNum, byte rowNum, JetFormat format)
  64. throws IOException
  65. {
  66. ByteBuffer dataBuffer = pageChannel.createPageBuffer();
  67. pageChannel.readPage(dataBuffer, pageNum);
  68. short rowStart = dataBuffer.getShort(format.OFFSET_ROW_START + 2 * rowNum);
  69. int rowEnd;
  70. if (rowNum == 0) {
  71. rowEnd = format.PAGE_SIZE - 1;
  72. } else {
  73. rowEnd = (dataBuffer.getShort(format.OFFSET_ROW_START + (rowNum - 1) * 2) & 0x0FFF) - 1;
  74. }
  75. dataBuffer.limit(rowEnd + 1);
  76. byte mapType = dataBuffer.get(rowStart);
  77. UsageMap rtn;
  78. if (mapType == MAP_TYPE_INLINE) {
  79. rtn = new InlineUsageMap(pageChannel, dataBuffer, pageNum, format, rowStart);
  80. } else if (mapType == MAP_TYPE_REFERENCE) {
  81. rtn = new ReferenceUsageMap(pageChannel, dataBuffer, pageNum, format, rowStart);
  82. } else {
  83. throw new IOException("Unrecognized map type: " + mapType);
  84. }
  85. return rtn;
  86. }
  87. /**
  88. * @param pageChannel Used to read in pages
  89. * @param dataBuffer Buffer that contains this map's declaration
  90. * @param pageNum Page number that this usage map is contained in
  91. * @param format Format of the database that contains this usage map
  92. * @param rowStart Offset at which the declaration starts in the buffer
  93. */
  94. public UsageMap(PageChannel pageChannel, ByteBuffer dataBuffer, int pageNum,
  95. JetFormat format, short rowStart)
  96. throws IOException
  97. {
  98. _pageChannel = pageChannel;
  99. _dataBuffer = dataBuffer;
  100. _dataPageNum = pageNum;
  101. _format = format;
  102. _rowStart = rowStart;
  103. _dataBuffer.position((int) _rowStart + format.OFFSET_MAP_START);
  104. _startOffset = _dataBuffer.position();
  105. if (LOG.isDebugEnabled()) {
  106. LOG.debug("Usage map block:\n" + ByteUtil.toHexString(_dataBuffer, _rowStart,
  107. dataBuffer.limit() - _rowStart));
  108. }
  109. }
  110. protected short getRowStart() {
  111. return _rowStart;
  112. }
  113. public List<Integer> getPageNumbers() {
  114. return _pageNumbers;
  115. }
  116. public Integer getCurrentPageNumber() {
  117. return _pageNumbers.get(_currentPageIndex - 1);
  118. }
  119. protected void setStartOffset(int startOffset) {
  120. _startOffset = startOffset;
  121. }
  122. protected int getStartOffset() {
  123. return _startOffset;
  124. }
  125. protected ByteBuffer getDataBuffer() {
  126. return _dataBuffer;
  127. }
  128. protected int getDataPageNumber() {
  129. return _dataPageNum;
  130. }
  131. protected PageChannel getPageChannel() {
  132. return _pageChannel;
  133. }
  134. protected JetFormat getFormat() {
  135. return _format;
  136. }
  137. /**
  138. * After calling this method, getNextPage will return the first page in the map
  139. */
  140. public void reset() {
  141. _currentPageIndex = 0;
  142. }
  143. /**
  144. * @param buffer Buffer to read the next page into
  145. * @return Whether or not there was another page to read
  146. */
  147. public boolean getNextPage(ByteBuffer buffer) throws IOException {
  148. if (_pageNumbers.size() > _currentPageIndex) {
  149. Integer pageNumber = _pageNumbers.get(_currentPageIndex++);
  150. _pageChannel.readPage(buffer, pageNumber);
  151. return true;
  152. } else {
  153. return false;
  154. }
  155. }
  156. /**
  157. * Read in the page numbers in this inline map
  158. */
  159. protected void processMap(ByteBuffer buffer, int pageIndex, int startPage) {
  160. int byteCount = 0;
  161. while (buffer.hasRemaining()) {
  162. byte b = buffer.get();
  163. for (int i = 0; i < 8; i++) {
  164. if ((b & (1 << i)) != 0) {
  165. Integer pageNumber = new Integer((startPage + byteCount * 8 + i) +
  166. (pageIndex * _format.PAGES_PER_USAGE_MAP_PAGE));
  167. _pageNumbers.add(pageNumber);
  168. }
  169. }
  170. byteCount++;
  171. }
  172. }
  173. /**
  174. * Add a page number to this usage map
  175. */
  176. public void addPageNumber(int pageNumber) throws IOException {
  177. //Sanity check, only on in debug mode for performance considerations
  178. if (LOG.isDebugEnabled() && _pageNumbers.contains(new Integer(pageNumber))) {
  179. throw new IOException("Page number " + pageNumber + " already in usage map");
  180. }
  181. addOrRemovePageNumber(pageNumber, true);
  182. }
  183. /**
  184. * Remove a page number from this usage map
  185. */
  186. public void removePageNumber(int pageNumber) throws IOException {
  187. addOrRemovePageNumber(pageNumber, false);
  188. }
  189. protected void updateMap(int absolutePageNumber, int relativePageNumber,
  190. int bitmask, ByteBuffer buffer, boolean add)
  191. {
  192. //Find the byte to apply the bitmask to
  193. int offset = relativePageNumber / 8;
  194. byte b = buffer.get(_startOffset + offset);
  195. //Apply the bitmask
  196. if (add) {
  197. b |= bitmask;
  198. _pageNumbers.add(new Integer(absolutePageNumber));
  199. } else {
  200. b &= ~bitmask;
  201. }
  202. buffer.put(_startOffset + offset, b);
  203. }
  204. public String toString() {
  205. return "page numbers: " + _pageNumbers;
  206. }
  207. /**
  208. * @param pageNumber Page number to add or remove from this map
  209. * @param add True to add it, false to remove it
  210. */
  211. protected abstract void addOrRemovePageNumber(int pageNumber, boolean add) throws IOException;
  212. }