• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
PackedIntVector(int columns)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      */
getValue(int row, int column)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      */
setValue(int row, int column, int value)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      */
setValueInternal(int row, int column, int value)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      */
adjustValuesBelow(int startRow, int column, int delta)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      */
insertAt(int row, int[] values)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      */
deleteAt(int row, int count)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      */
size()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      */
width()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      */
growBuffer()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      */
moveValueGapTo(int column, int where)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      */
moveRowGapTo(int where)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