1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xml.dtm.ref;
  58. import org.w3c.dom.*;
  59. import org.apache.xml.dtm.*;
  60. import org.apache.xalan.res.XSLTErrorResources;
  61. import org.apache.xalan.res.XSLMessages;
  62. /**
  63. * <code>ChunkedIntArray</code> is an extensible array of blocks of integers.
  64. * (I'd consider Vector, but it's unable to handle integers except by
  65. * turning them into Objects.)
  66. * <p>Making this a separate class means some call-and-return overhead. But
  67. * doing it all inline tends to be fragile and expensive in coder time,
  68. * not to mention driving up code size. If you want to inline it, feel free.
  69. * The Java text suggest that private and Final methods may be inlined,
  70. * and one can argue that this beast need not be made subclassable...</p>
  71. *
  72. * <p>%REVIEW% This has strong conceptual overlap with the IntVector class.
  73. * It would probably be a good thing to merge the two, when time permits.<p>
  74. */
  75. final class ChunkedIntArray
  76. {
  77. final int slotsize=4; // Locked, MUST be power of two in current code
  78. // Debugging tip: Cranking lowbits down to 4 or so is a good
  79. // way to pound on the array addressing code.
  80. static final int lowbits=10; // How many bits address within chunks
  81. static final int chunkalloc=1<<lowbits;
  82. static final int lowmask=chunkalloc-1;
  83. ChunksVector chunks=new ChunksVector();
  84. final int fastArray[] = new int[chunkalloc];
  85. int lastUsed=0;
  86. /**
  87. * Create a new CIA with specified record size. Currently record size MUST
  88. * be a power of two... and in fact is hardcoded to 4.
  89. */
  90. ChunkedIntArray(int slotsize)
  91. {
  92. if(this.slotsize<slotsize)
  93. throw new ArrayIndexOutOfBoundsException(XSLMessages.createMessage(XSLTErrorResources.ER_CHUNKEDINTARRAY_NOT_SUPPORTED, new Object[]{Integer.toString(slotsize)})); //"ChunkedIntArray("+slotsize+") not currently supported");
  94. else if (this.slotsize>slotsize)
  95. System.out.println("*****WARNING: ChunkedIntArray("+slotsize+") wasting "+(this.slotsize-slotsize)+" words per slot");
  96. chunks.addElement(fastArray);
  97. }
  98. /**
  99. * Append a 4-integer record to the CIA, starting with record 1. (Since
  100. * arrays are initialized to all-0, 0 has been reserved as the "unknown"
  101. * value in DTM.)
  102. * @return the index at which this record was inserted.
  103. */
  104. int appendSlot(int w0, int w1, int w2, int w3)
  105. {
  106. /*
  107. try
  108. {
  109. int newoffset = (lastUsed+1)*slotsize;
  110. fastArray[newoffset] = w0;
  111. fastArray[newoffset+1] = w1;
  112. fastArray[newoffset+2] = w2;
  113. fastArray[newoffset+3] = w3;
  114. return ++lastUsed;
  115. }
  116. catch(ArrayIndexOutOfBoundsException aioobe)
  117. */
  118. {
  119. final int slotsize=4;
  120. int newoffset = (lastUsed+1)*slotsize;
  121. int chunkpos = newoffset >> lowbits;
  122. int slotpos = (newoffset & lowmask);
  123. // Grow if needed
  124. if (chunkpos > chunks.size() - 1)
  125. chunks.addElement(new int[chunkalloc]);
  126. int[] chunk = chunks.elementAt(chunkpos);
  127. chunk[slotpos] = w0;
  128. chunk[slotpos+1] = w1;
  129. chunk[slotpos+2] = w2;
  130. chunk[slotpos+3] = w3;
  131. return ++lastUsed;
  132. }
  133. }
  134. /**
  135. * Retrieve an integer from the CIA by record number and column within
  136. * the record, both 0-based (though position 0 is reserved for special
  137. * purposes).
  138. * @param position int Record number
  139. * @param slotpos int Column number
  140. */
  141. int readEntry(int position, int offset) throws ArrayIndexOutOfBoundsException
  142. {
  143. /*
  144. try
  145. {
  146. return fastArray[(position*slotsize)+offset];
  147. }
  148. catch(ArrayIndexOutOfBoundsException aioobe)
  149. */
  150. {
  151. // System.out.println("Using slow read (1)");
  152. if (offset>=slotsize)
  153. throw new ArrayIndexOutOfBoundsException(XSLMessages.createMessage(XSLTErrorResources.ER_OFFSET_BIGGER_THAN_SLOT, null)); //"Offset bigger than slot");
  154. position*=slotsize;
  155. int chunkpos = position >> lowbits;
  156. int slotpos = position & lowmask;
  157. int[] chunk = chunks.elementAt(chunkpos);
  158. return chunk[slotpos + offset];
  159. }
  160. }
  161. // Check that the node at index "position" is not an ancestor
  162. // of the node at index "startPos". IF IT IS, DO NOT ACCEPT IT AND
  163. // RETURN -1. If position is NOT an ancestor, return position.
  164. // Special case: The Document node (position==0) is acceptable.
  165. //
  166. // This test supports DTM.getNextPreceding.
  167. int specialFind(int startPos, int position)
  168. {
  169. // We have to look all the way up the ancestor chain
  170. // to make sure we don't have an ancestor.
  171. int ancestor = startPos;
  172. while(ancestor > 0)
  173. {
  174. // Get the node whose index == ancestor
  175. ancestor*=slotsize;
  176. int chunkpos = ancestor >> lowbits;
  177. int slotpos = ancestor & lowmask;
  178. int[] chunk = chunks.elementAt(chunkpos);
  179. // Get that node's parent (Note that this assumes w[1]
  180. // is the parent node index. That's really a DTM feature
  181. // rather than a ChunkedIntArray feature.)
  182. ancestor = chunk[slotpos + 1];
  183. if(ancestor == position)
  184. break;
  185. }
  186. if (ancestor <= 0)
  187. {
  188. return position;
  189. }
  190. return -1;
  191. }
  192. /**
  193. * @return int index of highest-numbered record currently in use
  194. */
  195. int slotsUsed()
  196. {
  197. return lastUsed;
  198. }
  199. /** Disard the highest-numbered record. This is used in the string-buffer
  200. CIA; when only a single characters() chunk has been recieved, its index
  201. is moved into the Text node rather than being referenced by indirection
  202. into the text accumulator.
  203. */
  204. void discardLast()
  205. {
  206. --lastUsed;
  207. }
  208. /**
  209. * Overwrite the integer found at a specific record and column.
  210. * Used to back-patch existing records, most often changing their
  211. * "next sibling" reference from 0 (unknown) to something meaningful
  212. * @param position int Record number
  213. * @param offset int Column number
  214. * @param value int New contents
  215. */
  216. void writeEntry(int position, int offset, int value) throws ArrayIndexOutOfBoundsException
  217. {
  218. /*
  219. try
  220. {
  221. fastArray[( position*slotsize)+offset] = value;
  222. }
  223. catch(ArrayIndexOutOfBoundsException aioobe)
  224. */
  225. {
  226. if (offset >= slotsize)
  227. throw new ArrayIndexOutOfBoundsException(XSLMessages.createMessage(XSLTErrorResources.ER_OFFSET_BIGGER_THAN_SLOT, null)); //"Offset bigger than slot");
  228. position*=slotsize;
  229. int chunkpos = position >> lowbits;
  230. int slotpos = position & lowmask;
  231. int[] chunk = chunks.elementAt(chunkpos);
  232. chunk[slotpos + offset] = value; // ATOMIC!
  233. }
  234. }
  235. /**
  236. * Overwrite an entire (4-integer) record at the specified index.
  237. * Mostly used to create record 0, the Document node.
  238. * @param position integer Record number
  239. * @param w0 int
  240. * @param w1 int
  241. * @param w2 int
  242. * @param w3 int
  243. */
  244. void writeSlot(int position, int w0, int w1, int w2, int w3)
  245. {
  246. position *= slotsize;
  247. int chunkpos = position >> lowbits;
  248. int slotpos = (position & lowmask);
  249. // Grow if needed
  250. if (chunkpos > chunks.size() - 1)
  251. chunks.addElement(new int[chunkalloc]);
  252. int[] chunk = chunks.elementAt(chunkpos);
  253. chunk[slotpos] = w0;
  254. chunk[slotpos + 1] = w1;
  255. chunk[slotpos + 2] = w2;
  256. chunk[slotpos + 3] = w3;
  257. }
  258. /**
  259. * Retrieve the contents of a record into a user-supplied buffer array.
  260. * Used to reduce addressing overhead when code will access several
  261. * columns of the record.
  262. * @param position int Record number
  263. * @param buffer int[] Integer array provided by user, must be large enough
  264. * to hold a complete record.
  265. */
  266. void readSlot(int position, int[] buffer)
  267. {
  268. /*
  269. try
  270. {
  271. System.arraycopy(fastArray, position*slotsize, buffer, 0, slotsize);
  272. }
  273. catch(ArrayIndexOutOfBoundsException aioobe)
  274. */
  275. {
  276. // System.out.println("Using slow read (2): "+position);
  277. position *= slotsize;
  278. int chunkpos = position >> lowbits;
  279. int slotpos = (position & lowmask);
  280. // Grow if needed
  281. if (chunkpos > chunks.size() - 1)
  282. chunks.addElement(new int[chunkalloc]);
  283. int[] chunk = chunks.elementAt(chunkpos);
  284. System.arraycopy(chunk,slotpos,buffer,0,slotsize);
  285. }
  286. }
  287. class ChunksVector
  288. {
  289. final int BLOCKSIZE = 64;
  290. int[] m_map[] = new int[BLOCKSIZE][];
  291. int m_mapSize = BLOCKSIZE;
  292. int pos = 0;
  293. ChunksVector()
  294. {
  295. }
  296. final int size()
  297. {
  298. return pos;
  299. }
  300. void addElement(int[] value)
  301. {
  302. if(pos >= m_mapSize)
  303. {
  304. int orgMapSize = m_mapSize;
  305. while(pos >= m_mapSize)
  306. m_mapSize+=BLOCKSIZE;
  307. int[] newMap[] = new int[m_mapSize][];
  308. System.arraycopy(m_map, 0, newMap, 0, orgMapSize);
  309. m_map = newMap;
  310. }
  311. // For now, just do a simple append. A sorted insert only
  312. // makes sense if we're doing an binary search or some such.
  313. m_map[pos] = value;
  314. pos++;
  315. }
  316. final int[] elementAt(int pos)
  317. {
  318. return m_map[pos];
  319. }
  320. }
  321. }