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