blob: 3385cc6fc16c245c19aaa02a749c674f191bd917 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xml.dtm;
import org.w3c.dom.*;
/**
* <code>ChunkedIntArray</code> 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.)
* <p>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...</p>
*
* <p>%REVIEW% This has strong conceptual overlap with the IntVector class.
* It would probably be a good thing to merge the two, when time permits.<p>
*/
final class ChunkedIntArray
{
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<<lowbits;
static final int lowmask=chunkalloc-1;
ChunksVector chunks=new ChunksVector();
final int fastArray[] = new int[chunkalloc];
int lastUsed=0;
/**
* Create a new CIA with specified record size. Currently record size MUST
* be a power of two... and in fact is hardcoded to 4.
*/
ChunkedIntArray(int slotsize)
{
if(this.slotsize<slotsize)
throw new ArrayIndexOutOfBoundsException("ChunkedIntArray("+slotsize+") not currently supported");
else if (this.slotsize>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("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("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);
}
}
class ChunksVector
{
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];
}
}
}