/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.react.flat; import javax.annotation.Nullable; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.uimanager.ReactShadowNode; /** * Helper class that sorts moveFrom/moveTo arrays in lockstep. */ /* package */ final class MoveProxy { private @Nullable ReadableArray mMoveTo; private int mSize; private int[] mMapping = new int[8]; private ReactShadowNode[] mChildren = new ReactShadowNode[4]; /** * Retuns size of underlying moveTo/moveFrom arrays */ public int size() { return mSize; } /** * Assigns ith child that we want to move if moveFrom was sorted. */ public void setChildMoveFrom(int moveFromIndex, ReactShadowNode node) { mChildren[moveFromToIndex(moveFromIndex)] = node; } /** * Returns ith child that we want to move if moveTo was sorted. */ public ReactShadowNode getChildMoveTo(int moveToIndex) { return mChildren[moveToToIndex(moveToIndex)]; } /** * Returns index of the ith child that we want to move if moveFrom was sorted */ public int getMoveFrom(int moveFromIndex) { return moveFromToValue(moveFromIndex); } /** * Returns index of the ith child that we want to move to if moveTo was sorted */ public int getMoveTo(int moveToIndex) { return moveToToValue(moveToIndex); } /** * Initialize MoveProxy with given moveFrom and moveTo arrays. */ public void setup(ReadableArray moveFrom, ReadableArray moveTo) { mMoveTo = moveTo; if (moveFrom == null) { setSize(0); return; } int size = moveFrom.size(); int requiredSpace = size + size; if (mMapping.length < requiredSpace) { mMapping = new int[requiredSpace]; mChildren = new FlatShadowNode[size]; } setSize(size); // Array contains data in the following way: // [ k0, v0, k1, v1, k2, v2, ... ] // // where vi = moveFrom.getInt(ki) // We don't technically *need* to store vi, but they are accessed so often that it makes sense // to cache it instead of calling ReadableArray.getInt() all the time. // Sorting algorithm will reorder ki/vi pairs in such a way that vi < v(i+1) // Code below is an insertion sort, adapted from DualPivotQuicksort.doSort() // At each step i, we got the following data: // [k0, v0, k1, v2, .. k(i-1), v(i-1), unused...] // where v0 < v1 < v2 ... < v(i-1) // // This holds true for step i = 0 (array of size one is sorted) // Again, k0 = 0, v0 = moveFrom.getInt(k0) setKeyValue(0, 0, moveFrom.getInt(0)); // At each of the next steps, we grab a new key and walk back until we find first key that is // less than current, shifting key/value pairs if they are larger than current key. for (int i = 1; i < size; i++) { // this is our next key int current = moveFrom.getInt(i); // this loop will find correct position for it int j; // At this point, array is like this: [ k0, v0, k1, v1, k2, v2, ..., k(i-1), v(i-1), ... ] for (j = i - 1; j >= 0; j--) { if (moveFromToValue(j) < current) { break; } // value at index j is < current value, shift that value and its key setKeyValue(j + 1, moveFromToIndex(j), moveFromToValue(j)); } setKeyValue(j + 1, i, current); } } /** * Returns index of ith key in array. */ private static int k(int i) { return i * 2; } /** * Returns index of ith value in array. */ private static int v(int i) { return i * 2 + 1; } private void setKeyValue(int index, int key, int value) { mMapping[k(index)] = key; mMapping[v(index)] = value; } private int moveFromToIndex(int index) { return mMapping[k(index)]; } private int moveFromToValue(int index) { return mMapping[v(index)]; } private static int moveToToIndex(int index) { return index; } private int moveToToValue(int index) { return Assertions.assumeNotNull(mMoveTo).getInt(index); } private void setSize(int newSize) { // reset references to null when shrinking to avoid memory leaks for (int i = newSize; i < mSize; ++i) { mChildren[i] = null; } mSize = newSize; } }