• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 com.android.server.utils;
18 
19 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.Size;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 import com.android.internal.util.ArrayUtils;
27 import com.android.internal.util.GrowingArrayUtils;
28 
29 import java.util.Arrays;
30 
31 /**
32  * A {@link WatchedSparseBooleanMatrix} is an compact NxN array of booleans.  The rows and
33  * columns of the array are indexed by integers, which need not be contiguous.  The matrix
34  * is square and the row and column indices are identical.  This matrix is intended to be
35  * very memory efficient.
36  *
37  * The matrix contains a map from indices to columns: this map requires 2*N integers.  The
38  * boolean array is bit-packed and requires N*N/8 bytes.  The memory required for an
39  * order-N matrix is therefore 2*N*4 + N*N bytes.
40  *
41  * See {@link SparseBooleanArray} for a discussion of sparse arrays.
42  */
43 public class WatchedSparseBooleanMatrix extends WatchableImpl implements Snappable {
44 
45     /**
46      * The matrix is implemented through four arrays.  First, the matrix of booleans is
47      * stored in a two-dimensional {@code mValues} array of bit-packed booleans.
48      * {@code mValues} is always of size {@code mOrder * mOrder / 8}.  The factor of 8 is
49      * present because there are 8 bits in a byte.  Elements of {@code mValues} are
50      * addressed with arithmetic: the element {@code {row, col}} is bit {@code col % 8} in
51      * byte * {@code (row * mOrder + col) / 8}.  The term "storage index" applies to
52      * {@code mValues}.  A storage index designates a row (column) in the underlying
53      * storage.  This is not the same as the row seen by client code.
54      *
55      * Client code addresses the matrix through indices.  These are integers that need not
56      * be contiguous.  Client indices are mapped to storage indices through two linear
57      * integer arrays.  {@code mKeys} is a sorted list of client indices.
58      * {@code mIndices} is a parallel array that contains storage indices.  The storage
59      * index of a client index {@code k} is {@code mIndices[i]}, where
60      * {@code mKeys[i] == k}.
61      *
62      * A final array, {@code mInUse} records if storage indices are free or in use.  This
63      * array is of size {@code mOrder}.  A client index is deleted by removing it from
64      * {@code mKeys} and {@code mIndices} and then setting the original storage index
65      * false in {@code mInUse}.
66      *
67      * Some notes:
68      * <ul>
69      * <li> The matrix does not automatically shrink but there is a compress() method that
70      *      will recover unused space.
71      * <li> Equality is a very, very expensive operation because it must walk the matrices
72      *      beimg compared element by element.
73      * </ul>
74      */
75 
76     /**
77      * mOrder is always a multiple of this value.  A  minimal matrix therefore holds 2^12
78      * values and requires 1024 bytes.  The value is visible for testing.
79      */
80     @VisibleForTesting(visibility = PRIVATE)
81     static final int STEP = 64;
82 
83     /**
84      * The number of bits in the mValues array element.
85      */
86     private static final int PACKING = 32;
87 
88     /**
89      * Constants that index into the string array returned by matrixToString.  The primary
90      * consumer is test code.
91      */
92     static final int STRING_KEY_INDEX = 0;
93     static final int STRING_MAP_INDEX = 1;
94     static final int STRING_INUSE_INDEX = 2;
95 
96     /**
97      * The order of the matrix storage, including any padding.  The matrix is always
98      * square.  mOrder is always greater than or equal to mSize.
99      */
100     private int mOrder;
101 
102     /**
103      * The number of client keys.  This is always less than or equal to mOrder.  It is the
104      * order of the matrix as seen by the client.
105      */
106     private int mSize;
107 
108     /**
109      * The in-use list.
110      */
111     private boolean[] mInUse;
112 
113     /**
114      * The array of client keys (indices), in sorted order.
115      */
116     private int[] mKeys;
117 
118     /**
119      * The mapping from a client key to an storage index.  If client key K is at index N
120      * in mKeys, then the storage index for K is at mMap[N].
121      */
122     private int[] mMap;
123 
124     /**
125      * The boolean array.  This array is always {@code mOrder x mOrder} in size.
126      */
127     private int[] mValues;
128 
129     /**
130      * A convenience function called when the elements are added to or removed from the storage.
131      * The watchable is always {@link this}.
132      */
onChanged()133     private void onChanged() {
134         dispatchChange(this);
135     }
136 
137     /**
138      * Creates a new WatchedSparseBooleanMatrix containing no mappings.
139      */
WatchedSparseBooleanMatrix()140     public WatchedSparseBooleanMatrix() {
141         this(STEP);
142     }
143 
144     /**
145      * Creates a new SparseBooleanMatrix containing no mappings that will not require any
146      * additional memory allocation to store the specified number of mappings.  The
147      * capacity is always rounded up to a non-zero multiple of STEP.
148      */
WatchedSparseBooleanMatrix(int initialCapacity)149     public WatchedSparseBooleanMatrix(int initialCapacity) {
150         mOrder = initialCapacity;
151         if (mOrder < STEP) {
152             mOrder = STEP;
153         }
154         if (mOrder % STEP != 0) {
155             mOrder = ((initialCapacity / STEP) + 1) * STEP;
156         }
157         if (mOrder < STEP || (mOrder % STEP != 0)) {
158             throw new RuntimeException("mOrder is " + mOrder + " initCap is " + initialCapacity);
159         }
160 
161         mInUse = ArrayUtils.newUnpaddedBooleanArray(mOrder);
162         mKeys = ArrayUtils.newUnpaddedIntArray(mOrder);
163         mMap = ArrayUtils.newUnpaddedIntArray(mOrder);
164         mValues = ArrayUtils.newUnpaddedIntArray(mOrder * mOrder / PACKING);
165         mSize = 0;
166     }
167 
168     /**
169      * A copy constructor that can be used for snapshotting.
170      */
WatchedSparseBooleanMatrix(WatchedSparseBooleanMatrix r)171     private WatchedSparseBooleanMatrix(WatchedSparseBooleanMatrix r) {
172         copyFrom(r);
173     }
174 
175     /**
176      * Copy from src to this.
177      */
copyFrom(@onNull WatchedSparseBooleanMatrix src)178     public void copyFrom(@NonNull WatchedSparseBooleanMatrix src) {
179         mOrder = src.mOrder;
180         mSize = src.mSize;
181         mKeys = src.mKeys.clone();
182         mMap = src.mMap.clone();
183         mInUse = src.mInUse.clone();
184         mValues = src.mValues.clone();
185     }
186 
187     /**
188      * Return a copy of this object.
189      */
snapshot()190     public WatchedSparseBooleanMatrix snapshot() {
191         return new WatchedSparseBooleanMatrix(this);
192     }
193 
194     /**
195      * Gets the boolean mapped from the specified key, or <code>false</code>
196      * if no such mapping has been made.
197      */
get(int row, int col)198     public boolean get(int row, int col) {
199         return get(row, col, false);
200     }
201 
202     /**
203      * Gets the boolean mapped from the specified key, or the specified value
204      * if no such mapping has been made.
205      */
get(int row, int col, boolean valueIfKeyNotFound)206     public boolean get(int row, int col, boolean valueIfKeyNotFound) {
207         int r = indexOfKey(row, false);
208         int c = indexOfKey(col, false);
209         if (r >= 0 && c >= 0) {
210             return valueAt(r, c);
211         } else {
212             return valueIfKeyNotFound;
213         }
214     }
215 
216     /**
217      * Adds a mapping from the specified keys to the specified value, replacing the
218      * previous mapping from the specified keys if there was one.
219      */
put(int row, int col, boolean value)220     public void put(int row, int col, boolean value) {
221         int r = indexOfKey(row);
222         int c = indexOfKey(col);
223         if (r < 0 || c < 0) {
224             // One or both of the keys has not be installed yet.  Install them now.
225             // Installing either key may shift the other key.  The safest course is to
226             // install the keys that are not present and then recompute both indices.
227             if (r < 0) {
228                 r = indexOfKey(row, true);
229             }
230             if (c < 0) {
231                 c = indexOfKey(col, true);
232             }
233             r = indexOfKey(row);
234             c = indexOfKey(col);
235         }
236         if (r >= 0 && c >= 0) {
237             setValueAt(r, c, value);
238             // setValueAt() will call onChanged().
239         } else {
240             throw new RuntimeException("matrix overflow");
241         }
242     }
243 
244     /**
245      * Removes the mapping from the specified key, if there was any.  Note that deletion
246      * applies to a single index, not to an element.  The matrix never shrinks but the
247      * space will be reused the next time an index is added.
248      */
deleteKey(int key)249     public void deleteKey(int key) {
250         int i = indexOfKey(key, false);
251         if (i >= 0) {
252             removeAt(i);
253         }
254     }
255 
256     /**
257      * Removes the mapping at the specified index.  The matrix does not shrink.  This
258      * throws ArrayIndexOutOfBounds if the index out outside the range {@code 0..size()-1}.
259      */
removeAt(int index)260     public void removeAt(int index) {
261         validateIndex(index);
262         mInUse[mMap[index]] = false;
263         // Remove the specified index and ensure that unused words in mKeys and mMap are
264         // always zero, to simplify the equality function.
265         System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
266         mKeys[mSize - 1] = 0;
267         System.arraycopy(mMap, index + 1, mMap, index, mSize - (index + 1));
268         mMap[mSize - 1] = 0;
269         mSize--;
270         onChanged();
271     }
272 
273     /**
274      * Removes all of the mappings whose index is between {@code fromIndex}, inclusive, and
275      * {@code toIndex}, exclusive. The matrix does not shrink.
276      */
removeRange(int fromIndex, int toIndex)277     public void removeRange(int fromIndex, int toIndex) {
278         if (toIndex < fromIndex) {
279             throw new ArrayIndexOutOfBoundsException("toIndex < fromIndex");
280         }
281         final int num = toIndex - fromIndex;
282         if (num == 0) {
283             return;
284         }
285         validateIndex(fromIndex);
286         validateIndex(toIndex - 1);
287         for (int i = fromIndex; i < toIndex; i++) {
288             mInUse[mMap[i]] = false;
289         }
290         System.arraycopy(mKeys, toIndex, mKeys, fromIndex, mSize - toIndex);
291         System.arraycopy(mMap, toIndex, mMap, fromIndex, mSize - toIndex);
292         for (int i = mSize - num; i < mSize; i++) {
293             mKeys[i] = 0;
294             mMap[i] = 0;
295         }
296         mSize -= num;
297         onChanged();
298     }
299 
300     /**
301      * Returns the number of key-value mappings that this WatchedSparseBooleanMatrix
302      * currently stores.
303      */
size()304     public int size() {
305         return mSize;
306     }
307 
308     /**
309      * Removes all key-value mappings from this WatchedSparseBooleanMatrix.
310      */
clear()311     public void clear() {
312         mSize = 0;
313         Arrays.fill(mInUse, false);
314         onChanged();
315     }
316 
317     /**
318      * Given an index in the range <code>0...size()-1</code>, returns the key from the
319      * <code>index</code>th key-value mapping that this WatchedSparseBooleanMatrix stores.
320      *
321      * <p>The keys corresponding to indices in ascending order are guaranteed to be in
322      * ascending order, e.g., <code>keyAt(0)</code> will return the smallest key and
323      * <code>keyAt(size()-1)</code> will return the largest key.</p>
324      *
325      * <p>{@link ArrayIndexOutOfBoundsException} is thrown for indices outside of the
326      * range <code>0...size()-1</code></p>
327      */
keyAt(int index)328     public int keyAt(int index) {
329         validateIndex(index);
330         return mKeys[index];
331     }
332 
333     /**
334      * An internal method to fetch the boolean value given the mValues row and column
335      * indices.  These are not the indices used by the *At() methods.
336      */
valueAtInternal(int row, int col)337     private boolean valueAtInternal(int row, int col) {
338         int element = row * mOrder + col;
339         int offset = element / PACKING;
340         int mask = 1 << (element % PACKING);
341         return (mValues[offset] & mask) != 0;
342     }
343 
344     /**
345      * Given a row and column, each in the range <code>0...size()-1</code>, returns the
346      * value from the <code>index</code>th key-value mapping that this WatchedSparseBooleanMatrix
347      * stores.
348      */
valueAt(int rowIndex, int colIndex)349     public boolean valueAt(int rowIndex, int colIndex) {
350         validateIndex(rowIndex, colIndex);
351         int r = mMap[rowIndex];
352         int c = mMap[colIndex];
353         return valueAtInternal(r, c);
354     }
355 
356     /**
357      * An internal method to set the boolean value given the mValues row and column
358      * indices.  These are not the indices used by the *At() methods.
359      */
setValueAtInternal(int row, int col, boolean value)360     private void setValueAtInternal(int row, int col, boolean value) {
361         int element = row * mOrder + col;
362         int offset = element / PACKING;
363         int mask = 1 << (element % PACKING);
364         if (value) {
365             mValues[offset] |= mask;
366         } else {
367             mValues[offset] &= ~mask;
368         }
369     }
370 
371     /**
372      * Directly set the value at a particular index.
373      */
setValueAt(int rowIndex, int colIndex, boolean value)374     public void setValueAt(int rowIndex, int colIndex, boolean value) {
375         validateIndex(rowIndex, colIndex);
376         int r = mMap[rowIndex];
377         int c = mMap[colIndex];
378         setValueAtInternal(r, c, value);
379         onChanged();
380     }
381 
382     /**
383      * Returns the index for which {@link #keyAt} would return the specified key, or a
384      * negative number if the specified key is not mapped.
385      */
indexOfKey(int key)386     public int indexOfKey(int key) {
387         return binarySearch(mKeys, mSize, key);
388     }
389 
390     /**
391      * Return true if the matrix knows the user index.
392      */
contains(int key)393     public boolean contains(int key) {
394         return indexOfKey(key) >= 0;
395     }
396 
397     /**
398      * Fetch the index of a key.  If the key does not exist and grow is true, then add the
399      * key.  If the does not exist and grow is false, return -1.
400      */
indexOfKey(int key, boolean grow)401     private int indexOfKey(int key, boolean grow) {
402         int i = binarySearch(mKeys, mSize, key);
403         if (i < 0 && grow) {
404             i = ~i;
405             if (mSize >= mOrder) {
406                 // Preemptively grow the matrix, which also grows the free list.
407                 growMatrix();
408             }
409             int newIndex = nextFree(true /* acquire */);
410             mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
411             mMap = GrowingArrayUtils.insert(mMap, mSize, i, newIndex);
412             mSize++;
413 
414             // Initialize the row and column corresponding to the new index.
415             int valueRow = mOrder / PACKING;
416             int offset = newIndex / PACKING;
417             int mask = ~(1 << (newIndex % PACKING));
418             Arrays.fill(mValues, newIndex * valueRow, (newIndex + 1) * valueRow, 0);
419             for (int n = 0; n < mSize; n++) {
420                 mValues[n * valueRow + offset] &= mask;
421             }
422             // Do not report onChanged() from this private method.  onChanged() is the
423             // responsibility of public methods that call this one.
424         }
425         return i;
426     }
427 
428     /**
429      * Validate the index.  This can throw.
430      */
validateIndex(int index)431     private void validateIndex(int index) {
432         if (index >= mSize) {
433             // The array might be slightly bigger than mSize, in which case, indexing won't fail.
434             throw new ArrayIndexOutOfBoundsException(index);
435         }
436     }
437 
438     /**
439      * Validate two indices.
440      */
validateIndex(int row, int col)441     private void validateIndex(int row, int col) {
442         validateIndex(row);
443         validateIndex(col);
444     }
445 
446     /**
447      * Expand the 2D array.  This also extends the free list.
448      */
growMatrix()449     private void growMatrix() {
450         resizeMatrix(mOrder + STEP);
451     }
452 
453     /**
454      * Resize the values array to the new dimension.
455      */
resizeMatrix(int newOrder)456     private void resizeMatrix(int newOrder) {
457         if (newOrder % STEP != 0) {
458             throw new IllegalArgumentException("matrix order " + newOrder
459                                                + " is not a multiple of " + STEP);
460         }
461         int minOrder = Math.min(mOrder, newOrder);
462 
463         boolean[] newInUse = ArrayUtils.newUnpaddedBooleanArray(newOrder);
464         System.arraycopy(mInUse, 0, newInUse, 0, minOrder);
465         int[] newMap = ArrayUtils.newUnpaddedIntArray(newOrder);
466         System.arraycopy(mMap, 0, newMap, 0, minOrder);
467         int[] newKeys = ArrayUtils.newUnpaddedIntArray(newOrder);
468         System.arraycopy(mKeys, 0, newKeys, 0, minOrder);
469 
470         int[] newValues = ArrayUtils.newUnpaddedIntArray(newOrder * newOrder / PACKING);
471         for (int i = 0; i < minOrder; i++) {
472             int row = mOrder * i / PACKING;
473             int newRow = newOrder * i / PACKING;
474             System.arraycopy(mValues, row, newValues, newRow, minOrder / PACKING);
475         }
476 
477         mInUse = newInUse;
478         mMap = newMap;
479         mKeys = newKeys;
480         mValues = newValues;
481         mOrder = newOrder;
482     }
483 
484     /**
485      * Find an unused storage index, and return it. Mark it in-use if the {@code acquire} is true.
486      */
nextFree(boolean acquire)487     private int nextFree(boolean acquire) {
488         for (int i = 0; i < mInUse.length; i++) {
489             if (!mInUse[i]) {
490                 mInUse[i] = acquire;
491                 return i;
492             }
493         }
494         throw new RuntimeException();
495     }
496 
497     /**
498      * Return the index of the key that uses the highest row index in use.  This returns
499      * -1 if the matrix is empty.  Note that the return is an index suitable for the *At()
500      * methods.  It is not the index in the mInUse array.
501      */
lastInuse()502     private int lastInuse() {
503         for (int i = mOrder - 1; i >= 0; i--) {
504             if (mInUse[i]) {
505                 for (int j = 0; j < mSize; j++) {
506                     if (mMap[j] == i) {
507                         return j;
508                     }
509                 }
510                 throw new IndexOutOfBoundsException();
511             }
512         }
513         return -1;
514     }
515 
516     /**
517      * Compress the matrix by packing keys into consecutive indices.  If the compression
518      * is sufficient, the mValues array can be shrunk.
519      */
pack()520     private void pack() {
521         if (mSize == 0 || mSize == mOrder) {
522             return;
523         }
524         // dst and src are identify raw (row, col) in mValues.  srcIndex is the index (as
525         // in the result of keyAt()) of the key being relocated.
526         for (int dst = nextFree(false); dst < mSize; dst = nextFree(false)) {
527             mInUse[dst] = true;
528             int srcIndex = lastInuse();
529             int src = mMap[srcIndex];
530             mInUse[src] = false;
531             mMap[srcIndex] = dst;
532             System.arraycopy(mValues, src * mOrder / PACKING,
533                              mValues, dst * mOrder / PACKING,
534                              mOrder / PACKING);
535             int srcOffset = (src / PACKING);
536             int srcMask = 1 << (src % PACKING);
537             int dstOffset = (dst / PACKING);
538             int dstMask = 1 << (dst % PACKING);
539             for (int i = 0; i < mOrder; i++) {
540                 if ((mValues[srcOffset] & srcMask) == 0) {
541                     mValues[dstOffset] &= ~dstMask;
542                 } else {
543                     mValues[dstOffset] |= dstMask;
544                 }
545                 srcOffset += mOrder / PACKING;
546                 dstOffset += mOrder / PACKING;
547             }
548         }
549     }
550 
551     /**
552      * Shrink the matrix, if possible.
553      */
compact()554     public void compact() {
555         pack();
556         int unused = (mOrder - mSize) / STEP;
557         if (unused > 0) {
558             resizeMatrix(mOrder - (unused * STEP));
559         }
560     }
561 
562     /**
563      * Return a copy of the keys that are in use by the matrix.
564      */
keys()565     public int[] keys() {
566         return Arrays.copyOf(mKeys, mSize);
567     }
568 
569     /**
570      * Return the size of the 2D matrix.  This is always greater than or equal to size().
571      * This does not reflect the sizes of the meta-information arrays (such as mKeys).
572      */
capacity()573     public int capacity() {
574         return mOrder;
575     }
576 
577     /**
578      * Set capacity to enlarge the size of the 2D matrix. Capacity less than the {@link #capacity()}
579      * is not supported.
580      */
setCapacity(int capacity)581     public void setCapacity(int capacity) {
582         if (capacity <= mOrder) {
583             return;
584         }
585         if (capacity % STEP != 0) {
586             capacity = ((capacity / STEP) + 1) * STEP;
587         }
588         resizeMatrix(capacity);
589     }
590 
591     /**
592      * {@inheritDoc}
593      */
594     @Override
hashCode()595     public int hashCode() {
596         int hashCode = mSize;
597         hashCode = 31 * hashCode + Arrays.hashCode(mKeys);
598         hashCode = 31 * hashCode + Arrays.hashCode(mMap);
599         for (int i = 0; i < mSize; i++) {
600             int row = mMap[i];
601             for (int j = 0; j < mSize; j++) {
602                 hashCode = 31 * hashCode + (valueAtInternal(row, mMap[j]) ? 1 : 0);
603             }
604         }
605         return hashCode;
606     }
607 
608     /**
609      * {@inheritDoc}
610      */
611     @Override
equals(@ullable Object that)612     public boolean equals(@Nullable Object that) {
613         if (this == that) {
614             return true;
615         }
616 
617         if (!(that instanceof WatchedSparseBooleanMatrix)) {
618             return false;
619         }
620 
621         WatchedSparseBooleanMatrix other = (WatchedSparseBooleanMatrix) that;
622         if (mSize != other.mSize) {
623             return false;
624         }
625         if (!Arrays.equals(mKeys, other.mKeys)) {
626             // mKeys is zero padded at the end and is sorted, so the arrays can always be
627             // directly compared.
628             return false;
629         }
630         for (int i = 0; i < mSize; i++) {
631             int row = mMap[i];
632             for (int j = 0; j < mSize; j++) {
633                 int col = mMap[j];
634                 if (valueAtInternal(row, col) != other.valueAtInternal(row, col)) {
635                     return false;
636                 }
637             }
638         }
639         return true;
640     }
641 
642     /**
643      * Return the matrix meta information.  This is always three strings long.  The
644      * strings are indexed by the constants STRING_KEY_INDEX, STRING_MAP_INDEX, and
645      * STRING_INUSE_INDEX.
646      */
647     @VisibleForTesting(visibility = PRIVATE)
matrixToStringMeta()648     @Size(3) String[] matrixToStringMeta() {
649         String[] result = new String[3];
650 
651         StringBuilder k = new StringBuilder();
652         for (int i = 0; i < mSize; i++) {
653             k.append(mKeys[i]);
654             if (i < mSize - 1) {
655                 k.append(" ");
656             }
657         }
658         result[STRING_KEY_INDEX] = k.substring(0);
659 
660         StringBuilder m = new StringBuilder();
661         for (int i = 0; i < mSize; i++) {
662             m.append(mMap[i]);
663             if (i < mSize - 1) {
664                 m.append(" ");
665             }
666         }
667         result[STRING_MAP_INDEX] = m.substring(0);
668 
669         StringBuilder u = new StringBuilder();
670         for (int i = 0; i < mOrder; i++) {
671             u.append(mInUse[i] ? "1" : "0");
672         }
673         result[STRING_INUSE_INDEX] = u.substring(0);
674         return result;
675     }
676 
677     /**
678      * Return the matrix as an array of strings.  There is one string per row.  Each
679      * string has a '1' or a '0' in the proper column.  This is the raw data indexed by
680      * row/column disregarding the key map.
681      */
682     @VisibleForTesting(visibility = PRIVATE)
matrixToStringRaw()683     String[] matrixToStringRaw() {
684         String[] result = new String[mOrder];
685         for (int i = 0; i < mOrder; i++) {
686             StringBuilder line = new StringBuilder(mOrder);
687             for (int j = 0; j < mOrder; j++) {
688                 line.append(valueAtInternal(i, j) ? "1" : "0");
689             }
690             result[i] = line.substring(0);
691         }
692         return result;
693     }
694 
695     /**
696      * Return the matrix as an array of strings.  There is one string per row.  Each
697      * string has a '1' or a '0' in the proper column.  This is the cooked data indexed by
698      * keys, in key order.
699      */
700     @VisibleForTesting(visibility = PRIVATE)
matrixToStringCooked()701     String[] matrixToStringCooked() {
702         String[] result = new String[mSize];
703         for (int i = 0; i < mSize; i++) {
704             int row = mMap[i];
705             StringBuilder line = new StringBuilder(mSize);
706             for (int j = 0; j < mSize; j++) {
707                 line.append(valueAtInternal(row, mMap[j]) ? "1" : "0");
708             }
709             result[i] = line.substring(0);
710         }
711         return result;
712     }
713 
matrixToString(boolean raw)714     public String[] matrixToString(boolean raw) {
715         String[] meta = matrixToStringMeta();
716         String[] data;
717         if (raw) {
718             data = matrixToStringRaw();
719         } else {
720             data = matrixToStringCooked();
721         }
722         String[] result = new String[meta.length + data.length];
723         System.arraycopy(meta, 0, result, 0, meta.length);
724         System.arraycopy(data, 0, result, meta.length, data.length);
725         return result;
726     }
727 
728     /**
729      * {@inheritDoc}
730      *
731      * <p>This implementation creates a string that describes the size of the array.  A
732      * string with all the values could easily exceed 1Mb.
733      */
734     @Override
toString()735     public String toString() {
736         return "{" + mSize + "x" + mSize + "}";
737     }
738 
739     // Copied from android.util.ContainerHelpers, which is not visible outside the
740     // android.util package.
binarySearch(int[] array, int size, int value)741     private static int binarySearch(int[] array, int size, int value) {
742         int lo = 0;
743         int hi = size - 1;
744 
745         while (lo <= hi) {
746             final int mid = (lo + hi) >>> 1;
747             final int midVal = array[mid];
748 
749             if (midVal < value) {
750                 lo = mid + 1;
751             } else if (midVal > value) {
752                 hi = mid - 1;
753             } else {
754                 return mid;  // value found
755             }
756         }
757         return ~lo;  // value not present
758     }
759 }
760