001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: ChunkedIntArray.java 1225427 2011-12-29 04:33:32Z mrglavas $ 020 */ 021 package org.apache.xml.dtm.ref; 022 023 import org.apache.xml.res.XMLErrorResources; 024 import org.apache.xml.res.XMLMessages; 025 026 /** 027 * <code>ChunkedIntArray</code> is an extensible array of blocks of integers. 028 * (I'd consider Vector, but it's unable to handle integers except by 029 * turning them into Objects.) 030 031 * <p>Making this a separate class means some call-and-return overhead. But 032 * doing it all inline tends to be fragile and expensive in coder time, 033 * not to mention driving up code size. If you want to inline it, feel free. 034 * The Java text suggest that private and Final methods may be inlined, 035 * and one can argue that this beast need not be made subclassable...</p> 036 * 037 * <p>%REVIEW% This has strong conceptual overlap with the IntVector class. 038 * It would probably be a good thing to merge the two, when time permits.<p> 039 */ 040 final class ChunkedIntArray 041 { 042 static final int slotsize=4; // Locked, MUST be power of two in current code 043 // Debugging tip: Cranking lowbits down to 4 or so is a good 044 // way to pound on the array addressing code. 045 static final int lowbits=10; // How many bits address within chunks 046 static final int chunkalloc=1<<lowbits; 047 static final int lowmask=chunkalloc-1; 048 049 ChunksVector chunks=new ChunksVector(); 050 final int fastArray[] = new int[chunkalloc]; 051 int lastUsed=0; 052 053 /** 054 * Create a new CIA with specified record size. Currently record size MUST 055 * be a power of two... and in fact is hardcoded to 4. 056 */ 057 ChunkedIntArray(int slotsize) 058 { 059 if(this.slotsize<slotsize) 060 throw new ArrayIndexOutOfBoundsException(XMLMessages.createXMLMessage(XMLErrorResources.ER_CHUNKEDINTARRAY_NOT_SUPPORTED, new Object[]{Integer.toString(slotsize)})); //"ChunkedIntArray("+slotsize+") not currently supported"); 061 else if (this.slotsize>slotsize) 062 System.out.println("*****WARNING: ChunkedIntArray("+slotsize+") wasting "+(this.slotsize-slotsize)+" words per slot"); 063 chunks.addElement(fastArray); 064 } 065 /** 066 * Append a 4-integer record to the CIA, starting with record 1. (Since 067 * arrays are initialized to all-0, 0 has been reserved as the "unknown" 068 * value in DTM.) 069 * @return the index at which this record was inserted. 070 */ 071 int appendSlot(int w0, int w1, int w2, int w3) 072 { 073 /* 074 try 075 { 076 int newoffset = (lastUsed+1)*slotsize; 077 fastArray[newoffset] = w0; 078 fastArray[newoffset+1] = w1; 079 fastArray[newoffset+2] = w2; 080 fastArray[newoffset+3] = w3; 081 return ++lastUsed; 082 } 083 catch(ArrayIndexOutOfBoundsException aioobe) 084 */ 085 { 086 final int slotsize=4; 087 int newoffset = (lastUsed+1)*slotsize; 088 int chunkpos = newoffset >> lowbits; 089 int slotpos = (newoffset & lowmask); 090 091 // Grow if needed 092 if (chunkpos > chunks.size() - 1) 093 chunks.addElement(new int[chunkalloc]); 094 int[] chunk = chunks.elementAt(chunkpos); 095 chunk[slotpos] = w0; 096 chunk[slotpos+1] = w1; 097 chunk[slotpos+2] = w2; 098 chunk[slotpos+3] = w3; 099 100 return ++lastUsed; 101 } 102 } 103 /** 104 * Retrieve an integer from the CIA by record number and column within 105 * the record, both 0-based (though position 0 is reserved for special 106 * purposes). 107 * @param position int Record number 108 * @param slotpos int Column number 109 */ 110 int readEntry(int position, int offset) throws ArrayIndexOutOfBoundsException 111 { 112 /* 113 try 114 { 115 return fastArray[(position*slotsize)+offset]; 116 } 117 catch(ArrayIndexOutOfBoundsException aioobe) 118 */ 119 { 120 // System.out.println("Using slow read (1)"); 121 if (offset>=slotsize) 122 throw new ArrayIndexOutOfBoundsException(XMLMessages.createXMLMessage(XMLErrorResources.ER_OFFSET_BIGGER_THAN_SLOT, null)); //"Offset bigger than slot"); 123 position*=slotsize; 124 int chunkpos = position >> lowbits; 125 int slotpos = position & lowmask; 126 int[] chunk = chunks.elementAt(chunkpos); 127 return chunk[slotpos + offset]; 128 } 129 } 130 131 // Check that the node at index "position" is not an ancestor 132 // of the node at index "startPos". IF IT IS, DO NOT ACCEPT IT AND 133 // RETURN -1. If position is NOT an ancestor, return position. 134 // Special case: The Document node (position==0) is acceptable. 135 // 136 // This test supports DTM.getNextPreceding. 137 int specialFind(int startPos, int position) 138 { 139 // We have to look all the way up the ancestor chain 140 // to make sure we don't have an ancestor. 141 int ancestor = startPos; 142 while(ancestor > 0) 143 { 144 // Get the node whose index == ancestor 145 ancestor*=slotsize; 146 int chunkpos = ancestor >> lowbits; 147 int slotpos = ancestor & lowmask; 148 int[] chunk = chunks.elementAt(chunkpos); 149 150 // Get that node's parent (Note that this assumes w[1] 151 // is the parent node index. That's really a DTM feature 152 // rather than a ChunkedIntArray feature.) 153 ancestor = chunk[slotpos + 1]; 154 155 if(ancestor == position) 156 break; 157 } 158 159 if (ancestor <= 0) 160 { 161 return position; 162 } 163 return -1; 164 } 165 166 /** 167 * @return int index of highest-numbered record currently in use 168 */ 169 int slotsUsed() 170 { 171 return lastUsed; 172 } 173 174 /** Disard the highest-numbered record. This is used in the string-buffer 175 CIA; when only a single characters() chunk has been recieved, its index 176 is moved into the Text node rather than being referenced by indirection 177 into the text accumulator. 178 */ 179 void discardLast() 180 { 181 --lastUsed; 182 } 183 184 /** 185 * Overwrite the integer found at a specific record and column. 186 * Used to back-patch existing records, most often changing their 187 * "next sibling" reference from 0 (unknown) to something meaningful 188 * @param position int Record number 189 * @param offset int Column number 190 * @param value int New contents 191 */ 192 void writeEntry(int position, int offset, int value) throws ArrayIndexOutOfBoundsException 193 { 194 /* 195 try 196 { 197 fastArray[( position*slotsize)+offset] = value; 198 } 199 catch(ArrayIndexOutOfBoundsException aioobe) 200 */ 201 { 202 if (offset >= slotsize) 203 throw new ArrayIndexOutOfBoundsException(XMLMessages.createXMLMessage(XMLErrorResources.ER_OFFSET_BIGGER_THAN_SLOT, null)); //"Offset bigger than slot"); 204 position*=slotsize; 205 int chunkpos = position >> lowbits; 206 int slotpos = position & lowmask; 207 int[] chunk = chunks.elementAt(chunkpos); 208 chunk[slotpos + offset] = value; // ATOMIC! 209 } 210 } 211 212 /** 213 * Overwrite an entire (4-integer) record at the specified index. 214 * Mostly used to create record 0, the Document node. 215 * @param position integer Record number 216 * @param w0 int 217 * @param w1 int 218 * @param w2 int 219 * @param w3 int 220 */ 221 void writeSlot(int position, int w0, int w1, int w2, int w3) 222 { 223 position *= slotsize; 224 int chunkpos = position >> lowbits; 225 int slotpos = (position & lowmask); 226 227 // Grow if needed 228 if (chunkpos > chunks.size() - 1) 229 chunks.addElement(new int[chunkalloc]); 230 int[] chunk = chunks.elementAt(chunkpos); 231 chunk[slotpos] = w0; 232 chunk[slotpos + 1] = w1; 233 chunk[slotpos + 2] = w2; 234 chunk[slotpos + 3] = w3; 235 } 236 237 /** 238 * Retrieve the contents of a record into a user-supplied buffer array. 239 * Used to reduce addressing overhead when code will access several 240 * columns of the record. 241 * @param position int Record number 242 * @param buffer int[] Integer array provided by user, must be large enough 243 * to hold a complete record. 244 */ 245 void readSlot(int position, int[] buffer) 246 { 247 /* 248 try 249 { 250 System.arraycopy(fastArray, position*slotsize, buffer, 0, slotsize); 251 } 252 catch(ArrayIndexOutOfBoundsException aioobe) 253 */ 254 { 255 // System.out.println("Using slow read (2): "+position); 256 position *= slotsize; 257 int chunkpos = position >> lowbits; 258 int slotpos = (position & lowmask); 259 260 // Grow if needed 261 if (chunkpos > chunks.size() - 1) 262 chunks.addElement(new int[chunkalloc]); 263 int[] chunk = chunks.elementAt(chunkpos); 264 System.arraycopy(chunk,slotpos,buffer,0,slotsize); 265 } 266 } 267 268 static class ChunksVector 269 { 270 static final int BLOCKSIZE = 64; 271 int[] m_map[] = new int[BLOCKSIZE][]; 272 int m_mapSize = BLOCKSIZE; 273 int pos = 0; 274 275 ChunksVector() 276 { 277 } 278 279 final int size() 280 { 281 return pos; 282 } 283 284 void addElement(int[] value) 285 { 286 if(pos >= m_mapSize) 287 { 288 int orgMapSize = m_mapSize; 289 while(pos >= m_mapSize) 290 m_mapSize+=BLOCKSIZE; 291 int[] newMap[] = new int[m_mapSize][]; 292 System.arraycopy(m_map, 0, newMap, 0, orgMapSize); 293 m_map = newMap; 294 } 295 // For now, just do a simple append. A sorted insert only 296 // makes sense if we're doing an binary search or some such. 297 m_map[pos] = value; 298 pos++; 299 } 300 301 final int[] elementAt(int pos) 302 { 303 return m_map[pos]; 304 } 305 } 306 }