Home | History | Annotate | Download | only in text
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.text;
     18 
     19 import com.android.internal.util.ArrayUtils;
     20 
     21 
     22 /**
     23  * PackedIntVector stores a two-dimensional array of integers,
     24  * optimized for inserting and deleting rows and for
     25  * offsetting the values in segments of a given column.
     26  */
     27 class PackedIntVector {
     28     private final int mColumns;
     29     private int mRows;
     30 
     31     private int mRowGapStart;
     32     private int mRowGapLength;
     33 
     34     private int[] mValues;
     35     private int[] mValueGap; // starts, followed by lengths
     36 
     37     /**
     38      * Creates a new PackedIntVector with the specified width and
     39      * a height of 0.
     40      *
     41      * @param columns the width of the PackedIntVector.
     42      */
     43     public PackedIntVector(int columns) {
     44         mColumns = columns;
     45         mRows = 0;
     46 
     47         mRowGapStart = 0;
     48         mRowGapLength = mRows;
     49 
     50         mValues = null;
     51         mValueGap = new int[2 * columns];
     52     }
     53 
     54     /**
     55      * Returns the value at the specified row and column.
     56      *
     57      * @param row the index of the row to return.
     58      * @param column the index of the column to return.
     59      *
     60      * @return the value stored at the specified position.
     61      *
     62      * @throws IndexOutOfBoundsException if the row is out of range
     63      *         (row < 0 || row >= size()) or the column is out of range
     64      *         (column < 0 || column >= width()).
     65      */
     66     public int getValue(int row, int column) {
     67         final int columns = mColumns;
     68 
     69         if (((row | column) < 0) || (row >= size()) || (column >= columns)) {
     70             throw new IndexOutOfBoundsException(row + ", " + column);
     71         }
     72 
     73         if (row >= mRowGapStart) {
     74             row += mRowGapLength;
     75         }
     76 
     77         int value = mValues[row * columns + column];
     78 
     79         int[] valuegap = mValueGap;
     80         if (row >= valuegap[column]) {
     81             value += valuegap[column + columns];
     82         }
     83 
     84         return value;
     85     }
     86 
     87     /**
     88      * Sets the value at the specified row and column.
     89      *
     90      * @param row the index of the row to set.
     91      * @param column the index of the column to set.
     92      *
     93      * @throws IndexOutOfBoundsException if the row is out of range
     94      *         (row &lt; 0 || row >= size()) or the column is out of range
     95      *         (column &lt; 0 || column >= width()).
     96      */
     97     public void setValue(int row, int column, int value) {
     98         if (((row | column) < 0) || (row >= size()) || (column >= mColumns)) {
     99             throw new IndexOutOfBoundsException(row + ", " + column);
    100         }
    101 
    102         if (row >= mRowGapStart) {
    103             row += mRowGapLength;
    104         }
    105 
    106         int[] valuegap = mValueGap;
    107         if (row >= valuegap[column]) {
    108             value -= valuegap[column + mColumns];
    109         }
    110 
    111         mValues[row * mColumns + column] = value;
    112     }
    113 
    114     /**
    115      * Sets the value at the specified row and column.
    116      * Private internal version: does not check args.
    117      *
    118      * @param row the index of the row to set.
    119      * @param column the index of the column to set.
    120      *
    121      */
    122     private void setValueInternal(int row, int column, int value) {
    123         if (row >= mRowGapStart) {
    124             row += mRowGapLength;
    125         }
    126 
    127         int[] valuegap = mValueGap;
    128         if (row >= valuegap[column]) {
    129             value -= valuegap[column + mColumns];
    130         }
    131 
    132         mValues[row * mColumns + column] = value;
    133     }
    134 
    135 
    136     /**
    137      * Increments all values in the specified column whose row >= the
    138      * specified row by the specified delta.
    139      *
    140      * @param startRow the row at which to begin incrementing.
    141      *        This may be == size(), which case there is no effect.
    142      * @param column the index of the column to set.
    143      *
    144      * @throws IndexOutOfBoundsException if the row is out of range
    145      *         (startRow &lt; 0 || startRow > size()) or the column
    146      *         is out of range (column &lt; 0 || column >= width()).
    147      */
    148     public void adjustValuesBelow(int startRow, int column, int delta) {
    149         if (((startRow | column) < 0) || (startRow > size()) ||
    150                 (column >= width())) {
    151             throw new IndexOutOfBoundsException(startRow + ", " + column);
    152         }
    153 
    154         if (startRow >= mRowGapStart) {
    155             startRow += mRowGapLength;
    156         }
    157 
    158         moveValueGapTo(column, startRow);
    159         mValueGap[column + mColumns] += delta;
    160     }
    161 
    162     /**
    163      * Inserts a new row of values at the specified row offset.
    164      *
    165      * @param row the row above which to insert the new row.
    166      *        This may be == size(), which case the new row is added
    167      *        at the end.
    168      * @param values the new values to be added.  If this is null,
    169      *        a row of zeroes is added.
    170      *
    171      * @throws IndexOutOfBoundsException if the row is out of range
    172      *         (row &lt; 0 || row > size()) or if the length of the
    173      *         values array is too small (values.length < width()).
    174      */
    175     public void insertAt(int row, int[] values) {
    176         if ((row < 0) || (row > size())) {
    177             throw new IndexOutOfBoundsException("row " + row);
    178         }
    179 
    180         if ((values != null) && (values.length < width())) {
    181             throw new IndexOutOfBoundsException("value count " + values.length);
    182         }
    183 
    184         moveRowGapTo(row);
    185 
    186         if (mRowGapLength == 0) {
    187             growBuffer();
    188         }
    189 
    190         mRowGapStart++;
    191         mRowGapLength--;
    192 
    193         if (values == null) {
    194             for (int i = mColumns - 1; i >= 0; i--) {
    195                 setValueInternal(row, i, 0);
    196             }
    197         } else {
    198             for (int i = mColumns - 1; i >= 0; i--) {
    199                 setValueInternal(row, i, values[i]);
    200             }
    201         }
    202     }
    203 
    204     /**
    205      * Deletes the specified number of rows starting with the specified
    206      * row.
    207      *
    208      * @param row the index of the first row to be deleted.
    209      * @param count the number of rows to delete.
    210      *
    211      * @throws IndexOutOfBoundsException if any of the rows to be deleted
    212      *         are out of range (row &lt; 0 || count &lt; 0 ||
    213      *         row + count > size()).
    214      */
    215     public void deleteAt(int row, int count) {
    216         if (((row | count) < 0) || (row + count > size())) {
    217             throw new IndexOutOfBoundsException(row + ", " + count);
    218         }
    219 
    220         moveRowGapTo(row + count);
    221 
    222         mRowGapStart -= count;
    223         mRowGapLength += count;
    224 
    225         // TODO: Reclaim memory when the new height is much smaller
    226         // than the allocated size.
    227     }
    228 
    229     /**
    230      * Returns the number of rows in the PackedIntVector.  This number
    231      * will change as rows are inserted and deleted.
    232      *
    233      * @return the number of rows.
    234      */
    235     public int size() {
    236         return mRows - mRowGapLength;
    237     }
    238 
    239     /**
    240      * Returns the width of the PackedIntVector.  This number is set
    241      * at construction and will not change.
    242      *
    243      * @return the number of columns.
    244      */
    245     public int width() {
    246         return mColumns;
    247     }
    248 
    249     /**
    250      * Grows the value and gap arrays to be large enough to store at least
    251      * one more than the current number of rows.
    252      */
    253     private final void growBuffer() {
    254         final int columns = mColumns;
    255         int newsize = size() + 1;
    256         newsize = ArrayUtils.idealIntArraySize(newsize * columns) / columns;
    257         int[] newvalues = new int[newsize * columns];
    258 
    259         final int[] valuegap = mValueGap;
    260         final int rowgapstart = mRowGapStart;
    261 
    262         int after = mRows - (rowgapstart + mRowGapLength);
    263 
    264         if (mValues != null) {
    265             System.arraycopy(mValues, 0, newvalues, 0, columns * rowgapstart);
    266             System.arraycopy(mValues, (mRows - after) * columns,
    267                              newvalues, (newsize - after) * columns,
    268                              after * columns);
    269         }
    270 
    271         for (int i = 0; i < columns; i++) {
    272             if (valuegap[i] >= rowgapstart) {
    273                 valuegap[i] += newsize - mRows;
    274 
    275                 if (valuegap[i] < rowgapstart) {
    276                     valuegap[i] = rowgapstart;
    277                 }
    278             }
    279         }
    280 
    281         mRowGapLength += newsize - mRows;
    282         mRows = newsize;
    283         mValues = newvalues;
    284     }
    285 
    286     /**
    287      * Moves the gap in the values of the specified column to begin at
    288      * the specified row.
    289      */
    290     private final void moveValueGapTo(int column, int where) {
    291         final int[] valuegap = mValueGap;
    292         final int[] values = mValues;
    293         final int columns = mColumns;
    294 
    295         if (where == valuegap[column]) {
    296             return;
    297         } else if (where > valuegap[column]) {
    298             for (int i = valuegap[column]; i < where; i++) {
    299                 values[i * columns + column] += valuegap[column + columns];
    300             }
    301         } else /* where < valuegap[column] */ {
    302             for (int i = where; i < valuegap[column]; i++) {
    303                 values[i * columns + column] -= valuegap[column + columns];
    304             }
    305         }
    306 
    307         valuegap[column] = where;
    308     }
    309 
    310     /**
    311      * Moves the gap in the row indices to begin at the specified row.
    312      */
    313     private final void moveRowGapTo(int where) {
    314         if (where == mRowGapStart) {
    315             return;
    316         } else if (where > mRowGapStart) {
    317             int moving = where + mRowGapLength - (mRowGapStart + mRowGapLength);
    318             final int columns = mColumns;
    319             final int[] valuegap = mValueGap;
    320             final int[] values = mValues;
    321             final int gapend = mRowGapStart + mRowGapLength;
    322 
    323             for (int i = gapend; i < gapend + moving; i++) {
    324                 int destrow = i - gapend + mRowGapStart;
    325 
    326                 for (int j = 0; j < columns; j++) {
    327                     int val = values[i * columns+ j];
    328 
    329                     if (i >= valuegap[j]) {
    330                         val += valuegap[j + columns];
    331                     }
    332 
    333                     if (destrow >= valuegap[j]) {
    334                         val -= valuegap[j + columns];
    335                     }
    336 
    337                     values[destrow * columns + j] = val;
    338                 }
    339             }
    340         } else /* where < mRowGapStart */ {
    341             int moving = mRowGapStart - where;
    342             final int columns = mColumns;
    343             final int[] valuegap = mValueGap;
    344             final int[] values = mValues;
    345             final int gapend = mRowGapStart + mRowGapLength;
    346 
    347             for (int i = where + moving - 1; i >= where; i--) {
    348                 int destrow = i - where + gapend - moving;
    349 
    350                 for (int j = 0; j < columns; j++) {
    351                     int val = values[i * columns+ j];
    352 
    353                     if (i >= valuegap[j]) {
    354                         val += valuegap[j + columns];
    355                     }
    356 
    357                     if (destrow >= valuegap[j]) {
    358                         val -= valuegap[j + columns];
    359                     }
    360 
    361                     values[destrow * columns + j] = val;
    362                 }
    363             }
    364         }
    365 
    366         mRowGapStart = where;
    367     }
    368 }
    369