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