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