Home | History | Annotate | Download | only in utils
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one
      3  * or more contributor license agreements. See the NOTICE file
      4  * distributed with this work for additional information
      5  * regarding copyright ownership. The ASF licenses this file
      6  * to you under the Apache License, Version 2.0 (the  "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *     http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 /*
     19  * $Id: SuballocatedIntVector.java 468655 2006-10-28 07:12:06Z minchau $
     20  */
     21 package org.apache.xml.utils;
     22 
     23 /**
     24  * A very simple table that stores a list of int. Very similar API to our
     25  * IntVector class (same API); different internal storage.
     26  *
     27  * This version uses an array-of-arrays solution. Read/write access is thus
     28  * a bit slower than the simple IntVector, and basic storage is a trifle
     29  * higher due to the top-level array -- but appending is O(1) fast rather
     30  * than O(N**2) slow, which will swamp those costs in situations where
     31  * long vectors are being built up.
     32  *
     33  * Known issues:
     34  *
     35  * Some methods are private because they haven't yet been tested properly.
     36  *
     37  * Retrieval performance is critical, since this is used at the core
     38  * of the DTM model. (Append performance is almost as important.)
     39  * That's pushing me toward just letting reads from unset indices
     40  * throw exceptions or return stale data; safer behavior would have
     41  * performance costs.
     42  * */
     43 public class SuballocatedIntVector
     44 {
     45   /** Size of blocks to allocate          */
     46   protected int m_blocksize;
     47 
     48   /** Bitwise addressing (much faster than div/remainder */
     49   protected int m_SHIFT, m_MASK;
     50 
     51   /** The default number of blocks to (over)allocate by */
     52   protected static final int NUMBLOCKS_DEFAULT = 32;
     53 
     54   /** The number of blocks to (over)allocate by */
     55   protected int m_numblocks = NUMBLOCKS_DEFAULT;
     56 
     57   /** Array of arrays of ints          */
     58   protected int m_map[][];
     59 
     60   /** Number of ints in array          */
     61   protected int m_firstFree = 0;
     62 
     63   /** "Shortcut" handle to m_map[0]. Surprisingly helpful for short vectors. */
     64   protected int m_map0[];
     65 
     66   /** "Shortcut" handle to most recently added row of m_map.
     67    * Very helpful during construction.
     68    * @xsl.usage internal
     69    */
     70   protected int m_buildCache[];
     71   protected int m_buildCacheStartIndex;
     72 
     73 
     74   /**
     75    * Default constructor.  Note that the default
     76    * block size is currently 2K, which may be overkill for
     77    * small lists and undershootng for large ones.
     78    */
     79   public SuballocatedIntVector()
     80   {
     81     this(2048);
     82   }
     83 
     84   /**
     85    * Construct a IntVector, using the given block size and number
     86    * of blocks. For efficiency, we will round the requested size
     87    * off to a power of two.
     88    *
     89    * @param blocksize Size of block to allocate
     90    * @param numblocks Number of blocks to allocate
     91    * */
     92   public SuballocatedIntVector(int blocksize, int numblocks)
     93   {
     94     //m_blocksize = blocksize;
     95     for(m_SHIFT=0;0!=(blocksize>>>=1);++m_SHIFT)
     96       ;
     97     m_blocksize=1<<m_SHIFT;
     98     m_MASK=m_blocksize-1;
     99     m_numblocks = numblocks;
    100 
    101     m_map0=new int[m_blocksize];
    102     m_map = new int[numblocks][];
    103     m_map[0]=m_map0;
    104     m_buildCache = m_map0;
    105     m_buildCacheStartIndex = 0;
    106   }
    107 
    108   /** Construct a IntVector, using the given block size and
    109    * the default number of blocks (32).
    110    *
    111    * @param blocksize Size of block to allocate
    112    * */
    113   public SuballocatedIntVector(int blocksize)
    114   {
    115     this(blocksize, NUMBLOCKS_DEFAULT);
    116   }
    117 
    118   /**
    119    * Get the length of the list.
    120    *
    121    * @return length of the list
    122    */
    123   public int size()
    124   {
    125     return m_firstFree;
    126   }
    127 
    128   /**
    129    * Set the length of the list. This will only work to truncate the list, and
    130    * even then it has not been heavily tested and may not be trustworthy.
    131    *
    132    * @return length of the list
    133    */
    134   public void setSize(int sz)
    135   {
    136     if(m_firstFree>sz) // Whups; had that backward!
    137       m_firstFree = sz;
    138   }
    139 
    140   /**
    141    * Append a int onto the vector.
    142    *
    143    * @param value Int to add to the list
    144    */
    145   public  void addElement(int value)
    146   {
    147     int indexRelativeToCache = m_firstFree - m_buildCacheStartIndex;
    148 
    149     // Is the new index an index into the cache row of m_map?
    150     if(indexRelativeToCache >= 0 && indexRelativeToCache < m_blocksize) {
    151       m_buildCache[indexRelativeToCache]=value;
    152       ++m_firstFree;
    153     } else {
    154       // Growing the outer array should be rare. We initialize to a
    155       // total of m_blocksize squared elements, which at the default
    156       // size is 4M integers... and we grow by at least that much each
    157       // time.  However, attempts to microoptimize for this (assume
    158       // long enough and catch exceptions) yield no noticable
    159       // improvement.
    160 
    161       int index=m_firstFree>>>m_SHIFT;
    162       int offset=m_firstFree&m_MASK;
    163 
    164       if(index>=m_map.length)
    165       {
    166 	int newsize=index+m_numblocks;
    167 	int[][] newMap=new int[newsize][];
    168 	System.arraycopy(m_map, 0, newMap, 0, m_map.length);
    169 	m_map=newMap;
    170       }
    171       int[] block=m_map[index];
    172       if(null==block)
    173 	block=m_map[index]=new int[m_blocksize];
    174       block[offset]=value;
    175 
    176       // Cache the current row of m_map.  Next m_blocksize-1
    177       // values added will go to this row.
    178       m_buildCache = block;
    179       m_buildCacheStartIndex = m_firstFree-offset;
    180 
    181       ++m_firstFree;
    182     }
    183   }
    184 
    185   /**
    186    * Append several int values onto the vector.
    187    *
    188    * @param value Int to add to the list
    189    */
    190   private  void addElements(int value, int numberOfElements)
    191   {
    192     if(m_firstFree+numberOfElements<m_blocksize)
    193       for (int i = 0; i < numberOfElements; i++)
    194       {
    195         m_map0[m_firstFree++]=value;
    196       }
    197     else
    198     {
    199       int index=m_firstFree>>>m_SHIFT;
    200       int offset=m_firstFree&m_MASK;
    201       m_firstFree+=numberOfElements;
    202       while( numberOfElements>0)
    203       {
    204         if(index>=m_map.length)
    205         {
    206           int newsize=index+m_numblocks;
    207           int[][] newMap=new int[newsize][];
    208           System.arraycopy(m_map, 0, newMap, 0, m_map.length);
    209           m_map=newMap;
    210         }
    211         int[] block=m_map[index];
    212         if(null==block)
    213           block=m_map[index]=new int[m_blocksize];
    214         int copied=(m_blocksize-offset < numberOfElements)
    215           ? m_blocksize-offset : numberOfElements;
    216         numberOfElements-=copied;
    217         while(copied-- > 0)
    218           block[offset++]=value;
    219 
    220         ++index;offset=0;
    221       }
    222     }
    223   }
    224 
    225   /**
    226    * Append several slots onto the vector, but do not set the values.
    227    * Note: "Not Set" means the value is unspecified.
    228    *
    229    * @param numberOfElements Int to add to the list
    230    */
    231   private  void addElements(int numberOfElements)
    232   {
    233     int newlen=m_firstFree+numberOfElements;
    234     if(newlen>m_blocksize)
    235     {
    236       int index=m_firstFree>>>m_SHIFT;
    237       int newindex=(m_firstFree+numberOfElements)>>>m_SHIFT;
    238       for(int i=index+1;i<=newindex;++i)
    239         m_map[i]=new int[m_blocksize];
    240     }
    241     m_firstFree=newlen;
    242   }
    243 
    244   /**
    245    * Inserts the specified node in this vector at the specified index.
    246    * Each component in this vector with an index greater or equal to
    247    * the specified index is shifted upward to have an index one greater
    248    * than the value it had previously.
    249    *
    250    * Insertion may be an EXPENSIVE operation!
    251    *
    252    * @param value Int to insert
    253    * @param at Index of where to insert
    254    */
    255   private  void insertElementAt(int value, int at)
    256   {
    257     if(at==m_firstFree)
    258       addElement(value);
    259     else if (at>m_firstFree)
    260     {
    261       int index=at>>>m_SHIFT;
    262       if(index>=m_map.length)
    263       {
    264         int newsize=index+m_numblocks;
    265         int[][] newMap=new int[newsize][];
    266         System.arraycopy(m_map, 0, newMap, 0, m_map.length);
    267         m_map=newMap;
    268       }
    269       int[] block=m_map[index];
    270       if(null==block)
    271         block=m_map[index]=new int[m_blocksize];
    272       int offset=at&m_MASK;
    273           block[offset]=value;
    274           m_firstFree=offset+1;
    275         }
    276     else
    277     {
    278       int index=at>>>m_SHIFT;
    279       int maxindex=m_firstFree>>>m_SHIFT; // %REVIEW% (m_firstFree+1?)
    280       ++m_firstFree;
    281       int offset=at&m_MASK;
    282       int push;
    283 
    284       // ***** Easier to work down from top?
    285       while(index<=maxindex)
    286       {
    287         int copylen=m_blocksize-offset-1;
    288         int[] block=m_map[index];
    289         if(null==block)
    290         {
    291           push=0;
    292           block=m_map[index]=new int[m_blocksize];
    293         }
    294         else
    295         {
    296           push=block[m_blocksize-1];
    297           System.arraycopy(block, offset , block, offset+1, copylen);
    298         }
    299         block[offset]=value;
    300         value=push;
    301         offset=0;
    302         ++index;
    303       }
    304     }
    305   }
    306 
    307   /**
    308    * Wipe it out. Currently defined as equivalent to setSize(0).
    309    */
    310   public void removeAllElements()
    311   {
    312     m_firstFree = 0;
    313     m_buildCache = m_map0;
    314     m_buildCacheStartIndex = 0;
    315   }
    316 
    317   /**
    318    * Removes the first occurrence of the argument from this vector.
    319    * If the object is found in this vector, each component in the vector
    320    * with an index greater or equal to the object's index is shifted
    321    * downward to have an index one smaller than the value it had
    322    * previously.
    323    *
    324    * @param s Int to remove from array
    325    *
    326    * @return True if the int was removed, false if it was not found
    327    */
    328   private  boolean removeElement(int s)
    329   {
    330     int at=indexOf(s,0);
    331     if(at<0)
    332       return false;
    333     removeElementAt(at);
    334     return true;
    335   }
    336 
    337   /**
    338    * Deletes the component at the specified index. Each component in
    339    * this vector with an index greater or equal to the specified
    340    * index is shifted downward to have an index one smaller than
    341    * the value it had previously.
    342    *
    343    * @param i index of where to remove and int
    344    */
    345   private  void removeElementAt(int at)
    346   {
    347         // No point in removing elements that "don't exist"...
    348     if(at<m_firstFree)
    349     {
    350       int index=at>>>m_SHIFT;
    351       int maxindex=m_firstFree>>>m_SHIFT;
    352       int offset=at&m_MASK;
    353 
    354       while(index<=maxindex)
    355       {
    356         int copylen=m_blocksize-offset-1;
    357         int[] block=m_map[index];
    358         if(null==block)
    359           block=m_map[index]=new int[m_blocksize];
    360         else
    361           System.arraycopy(block, offset+1, block, offset, copylen);
    362         if(index<maxindex)
    363         {
    364           int[] next=m_map[index+1];
    365           if(next!=null)
    366             block[m_blocksize-1]=(next!=null) ? next[0] : 0;
    367         }
    368         else
    369           block[m_blocksize-1]=0;
    370         offset=0;
    371         ++index;
    372       }
    373     }
    374     --m_firstFree;
    375   }
    376 
    377   /**
    378    * Sets the component at the specified index of this vector to be the
    379    * specified object. The previous component at that position is discarded.
    380    *
    381    * The index must be a value greater than or equal to 0 and less
    382    * than the current size of the vector.
    383    *
    384    * @param value object to set
    385    * @param at    Index of where to set the object
    386    */
    387   public void setElementAt(int value, int at)
    388   {
    389     if(at<m_blocksize)
    390       m_map0[at]=value;
    391     else
    392     {
    393       int index=at>>>m_SHIFT;
    394       int offset=at&m_MASK;
    395 
    396       if(index>=m_map.length)
    397       {
    398 	int newsize=index+m_numblocks;
    399 	int[][] newMap=new int[newsize][];
    400 	System.arraycopy(m_map, 0, newMap, 0, m_map.length);
    401 	m_map=newMap;
    402       }
    403 
    404       int[] block=m_map[index];
    405       if(null==block)
    406 	block=m_map[index]=new int[m_blocksize];
    407       block[offset]=value;
    408     }
    409 
    410     if(at>=m_firstFree)
    411       m_firstFree=at+1;
    412   }
    413 
    414 
    415   /**
    416    * Get the nth element. This is often at the innermost loop of an
    417    * application, so performance is critical.
    418    *
    419    * @param i index of value to get
    420    *
    421    * @return value at given index. If that value wasn't previously set,
    422    * the result is undefined for performance reasons. It may throw an
    423    * exception (see below), may return zero, or (if setSize has previously
    424    * been used) may return stale data.
    425    *
    426    * @throws ArrayIndexOutOfBoundsException if the index was _clearly_
    427    * unreasonable (negative, or past the highest block).
    428    *
    429    * @throws NullPointerException if the index points to a block that could
    430    * have existed (based on the highest index used) but has never had anything
    431    * set into it.
    432    * %REVIEW% Could add a catch to create the block in that case, or return 0.
    433    * Try/Catch is _supposed_ to be nearly free when not thrown to. Do we
    434    * believe that? Should we have a separate safeElementAt?
    435    */
    436   public int elementAt(int i)
    437   {
    438     // This is actually a significant optimization!
    439     if(i<m_blocksize)
    440       return m_map0[i];
    441 
    442     return m_map[i>>>m_SHIFT][i&m_MASK];
    443   }
    444 
    445   /**
    446    * Tell if the table contains the given node.
    447    *
    448    * @param s object to look for
    449    *
    450    * @return true if the object is in the list
    451    */
    452   private  boolean contains(int s)
    453   {
    454     return (indexOf(s,0) >= 0);
    455   }
    456 
    457   /**
    458    * Searches for the first occurence of the given argument,
    459    * beginning the search at index, and testing for equality
    460    * using the equals method.
    461    *
    462    * @param elem object to look for
    463    * @param index Index of where to begin search
    464    * @return the index of the first occurrence of the object
    465    * argument in this vector at position index or later in the
    466    * vector; returns -1 if the object is not found.
    467    */
    468   public int indexOf(int elem, int index)
    469   {
    470         if(index>=m_firstFree)
    471                 return -1;
    472 
    473     int bindex=index>>>m_SHIFT;
    474     int boffset=index&m_MASK;
    475     int maxindex=m_firstFree>>>m_SHIFT;
    476     int[] block;
    477 
    478     for(;bindex<maxindex;++bindex)
    479     {
    480       block=m_map[bindex];
    481       if(block!=null)
    482         for(int offset=boffset;offset<m_blocksize;++offset)
    483           if(block[offset]==elem)
    484             return offset+bindex*m_blocksize;
    485       boffset=0; // after first
    486     }
    487     // Last block may need to stop before end
    488     int maxoffset=m_firstFree&m_MASK;
    489     block=m_map[maxindex];
    490     for(int offset=boffset;offset<maxoffset;++offset)
    491       if(block[offset]==elem)
    492         return offset+maxindex*m_blocksize;
    493 
    494     return -1;
    495   }
    496 
    497   /**
    498    * Searches for the first occurence of the given argument,
    499    * beginning the search at index, and testing for equality
    500    * using the equals method.
    501    *
    502    * @param elem object to look for
    503    * @return the index of the first occurrence of the object
    504    * argument in this vector at position index or later in the
    505    * vector; returns -1 if the object is not found.
    506    */
    507   public int indexOf(int elem)
    508   {
    509     return indexOf(elem,0);
    510   }
    511 
    512   /**
    513    * Searches for the first occurence of the given argument,
    514    * beginning the search at index, and testing for equality
    515    * using the equals method.
    516    *
    517    * @param elem Object to look for
    518    * @return the index of the first occurrence of the object
    519    * argument in this vector at position index or later in the
    520    * vector; returns -1 if the object is not found.
    521    */
    522   private  int lastIndexOf(int elem)
    523   {
    524     int boffset=m_firstFree&m_MASK;
    525     for(int index=m_firstFree>>>m_SHIFT;
    526         index>=0;
    527         --index)
    528     {
    529       int[] block=m_map[index];
    530       if(block!=null)
    531         for(int offset=boffset; offset>=0; --offset)
    532           if(block[offset]==elem)
    533             return offset+index*m_blocksize;
    534       boffset=0; // after first
    535     }
    536     return -1;
    537   }
    538 
    539   /**
    540    * Return the internal m_map0 array
    541    * @return the m_map0 array
    542    */
    543   public final int[] getMap0()
    544   {
    545     return m_map0;
    546   }
    547 
    548   /**
    549    * Return the m_map double array
    550    * @return the internal map of array of arrays
    551    */
    552   public final int[][] getMap()
    553   {
    554     return m_map;
    555   }
    556 
    557 }
    558