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 < 0 || row >= size()) or the column is out of range 101 * (column < 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 < 0 || startRow > size()) or the column 152 * is out of range (column < 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 < 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 < 0 || count < 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