org.apache.xml.dtm.ref.ChunkedIntArray Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id: ChunkedIntArray.java 1225427 2011-12-29 04:33:32Z mrglavas $
*/
package org.apache.xml.dtm.ref;
import org.apache.xml.res.XMLErrorResources;
import org.apache.xml.res.XMLMessages;
/**
* ChunkedIntArray
is an extensible array of blocks of integers.
* (I'd consider Vector, but it's unable to handle integers except by
* turning them into Objects.)
* Making this a separate class means some call-and-return overhead. But
* doing it all inline tends to be fragile and expensive in coder time,
* not to mention driving up code size. If you want to inline it, feel free.
* The Java text suggest that private and Final methods may be inlined,
* and one can argue that this beast need not be made subclassable...
*
* %REVIEW% This has strong conceptual overlap with the IntVector class.
* It would probably be a good thing to merge the two, when time permits.
*/
final class ChunkedIntArray
{
static final int slotsize=4; // Locked, MUST be power of two in current code
// Debugging tip: Cranking lowbits down to 4 or so is a good
// way to pound on the array addressing code.
static final int lowbits=10; // How many bits address within chunks
static final int chunkalloc=1<slotsize)
System.out.println("*****WARNING: ChunkedIntArray("+slotsize+") wasting "+(this.slotsize-slotsize)+" words per slot");
chunks.addElement(fastArray);
}
/**
* Append a 4-integer record to the CIA, starting with record 1. (Since
* arrays are initialized to all-0, 0 has been reserved as the "unknown"
* value in DTM.)
* @return the index at which this record was inserted.
*/
int appendSlot(int w0, int w1, int w2, int w3)
{
/*
try
{
int newoffset = (lastUsed+1)*slotsize;
fastArray[newoffset] = w0;
fastArray[newoffset+1] = w1;
fastArray[newoffset+2] = w2;
fastArray[newoffset+3] = w3;
return ++lastUsed;
}
catch(ArrayIndexOutOfBoundsException aioobe)
*/
{
final int slotsize=4;
int newoffset = (lastUsed+1)*slotsize;
int chunkpos = newoffset >> lowbits;
int slotpos = (newoffset & lowmask);
// Grow if needed
if (chunkpos > chunks.size() - 1)
chunks.addElement(new int[chunkalloc]);
int[] chunk = chunks.elementAt(chunkpos);
chunk[slotpos] = w0;
chunk[slotpos+1] = w1;
chunk[slotpos+2] = w2;
chunk[slotpos+3] = w3;
return ++lastUsed;
}
}
/**
* Retrieve an integer from the CIA by record number and column within
* the record, both 0-based (though position 0 is reserved for special
* purposes).
* @param position int Record number
* @param slotpos int Column number
*/
int readEntry(int position, int offset) throws ArrayIndexOutOfBoundsException
{
/*
try
{
return fastArray[(position*slotsize)+offset];
}
catch(ArrayIndexOutOfBoundsException aioobe)
*/
{
// System.out.println("Using slow read (1)");
if (offset>=slotsize)
throw new ArrayIndexOutOfBoundsException(XMLMessages.createXMLMessage(XMLErrorResources.ER_OFFSET_BIGGER_THAN_SLOT, null)); //"Offset bigger than slot");
position*=slotsize;
int chunkpos = position >> lowbits;
int slotpos = position & lowmask;
int[] chunk = chunks.elementAt(chunkpos);
return chunk[slotpos + offset];
}
}
// Check that the node at index "position" is not an ancestor
// of the node at index "startPos". IF IT IS, DO NOT ACCEPT IT AND
// RETURN -1. If position is NOT an ancestor, return position.
// Special case: The Document node (position==0) is acceptable.
//
// This test supports DTM.getNextPreceding.
int specialFind(int startPos, int position)
{
// We have to look all the way up the ancestor chain
// to make sure we don't have an ancestor.
int ancestor = startPos;
while(ancestor > 0)
{
// Get the node whose index == ancestor
ancestor*=slotsize;
int chunkpos = ancestor >> lowbits;
int slotpos = ancestor & lowmask;
int[] chunk = chunks.elementAt(chunkpos);
// Get that node's parent (Note that this assumes w[1]
// is the parent node index. That's really a DTM feature
// rather than a ChunkedIntArray feature.)
ancestor = chunk[slotpos + 1];
if(ancestor == position)
break;
}
if (ancestor <= 0)
{
return position;
}
return -1;
}
/**
* @return int index of highest-numbered record currently in use
*/
int slotsUsed()
{
return lastUsed;
}
/** Disard the highest-numbered record. This is used in the string-buffer
CIA; when only a single characters() chunk has been recieved, its index
is moved into the Text node rather than being referenced by indirection
into the text accumulator.
*/
void discardLast()
{
--lastUsed;
}
/**
* Overwrite the integer found at a specific record and column.
* Used to back-patch existing records, most often changing their
* "next sibling" reference from 0 (unknown) to something meaningful
* @param position int Record number
* @param offset int Column number
* @param value int New contents
*/
void writeEntry(int position, int offset, int value) throws ArrayIndexOutOfBoundsException
{
/*
try
{
fastArray[( position*slotsize)+offset] = value;
}
catch(ArrayIndexOutOfBoundsException aioobe)
*/
{
if (offset >= slotsize)
throw new ArrayIndexOutOfBoundsException(XMLMessages.createXMLMessage(XMLErrorResources.ER_OFFSET_BIGGER_THAN_SLOT, null)); //"Offset bigger than slot");
position*=slotsize;
int chunkpos = position >> lowbits;
int slotpos = position & lowmask;
int[] chunk = chunks.elementAt(chunkpos);
chunk[slotpos + offset] = value; // ATOMIC!
}
}
/**
* Overwrite an entire (4-integer) record at the specified index.
* Mostly used to create record 0, the Document node.
* @param position integer Record number
* @param w0 int
* @param w1 int
* @param w2 int
* @param w3 int
*/
void writeSlot(int position, int w0, int w1, int w2, int w3)
{
position *= slotsize;
int chunkpos = position >> lowbits;
int slotpos = (position & lowmask);
// Grow if needed
if (chunkpos > chunks.size() - 1)
chunks.addElement(new int[chunkalloc]);
int[] chunk = chunks.elementAt(chunkpos);
chunk[slotpos] = w0;
chunk[slotpos + 1] = w1;
chunk[slotpos + 2] = w2;
chunk[slotpos + 3] = w3;
}
/**
* Retrieve the contents of a record into a user-supplied buffer array.
* Used to reduce addressing overhead when code will access several
* columns of the record.
* @param position int Record number
* @param buffer int[] Integer array provided by user, must be large enough
* to hold a complete record.
*/
void readSlot(int position, int[] buffer)
{
/*
try
{
System.arraycopy(fastArray, position*slotsize, buffer, 0, slotsize);
}
catch(ArrayIndexOutOfBoundsException aioobe)
*/
{
// System.out.println("Using slow read (2): "+position);
position *= slotsize;
int chunkpos = position >> lowbits;
int slotpos = (position & lowmask);
// Grow if needed
if (chunkpos > chunks.size() - 1)
chunks.addElement(new int[chunkalloc]);
int[] chunk = chunks.elementAt(chunkpos);
System.arraycopy(chunk,slotpos,buffer,0,slotsize);
}
}
static class ChunksVector
{
static final int BLOCKSIZE = 64;
int[] m_map[] = new int[BLOCKSIZE][];
int m_mapSize = BLOCKSIZE;
int pos = 0;
ChunksVector()
{
}
final int size()
{
return pos;
}
void addElement(int[] value)
{
if(pos >= m_mapSize)
{
int orgMapSize = m_mapSize;
while(pos >= m_mapSize)
m_mapSize+=BLOCKSIZE;
int[] newMap[] = new int[m_mapSize][];
System.arraycopy(m_map, 0, newMap, 0, orgMapSize);
m_map = newMap;
}
// For now, just do a simple append. A sorted insert only
// makes sense if we're doing an binary search or some such.
m_map[pos] = value;
pos++;
}
final int[] elementAt(int pos)
{
return m_map[pos];
}
}
}