larksuite-oapi/src/main/java/com/larksuite/oapi/okio1_17/Options.java
private static void buildTrieRecursive(
      long nodeOffset,
      Buffer node,
      int byteStringOffset,
      List<ByteString> byteStrings,
      int fromIndex,
      int toIndex,
      List<Integer> indexes) {
    if (fromIndex >= toIndex) throw new AssertionError();
    for (int i = fromIndex; i < toIndex; i++) {
      if (byteStrings.get(i).size() < byteStringOffset) throw new AssertionError();
    }

    ByteString from = byteStrings.get(fromIndex);
    ByteString to = byteStrings.get(toIndex - 1);
    int prefixIndex = -1;

    // If the first element is already matched, that's our prefix.
    if (byteStringOffset == from.size()) {
      prefixIndex = indexes.get(fromIndex);
      fromIndex++;
      from = byteStrings.get(fromIndex);
    }

    if (from.getByte(byteStringOffset) != to.getByte(byteStringOffset)) {
      // If we have multiple bytes to choose from, encode a SELECT node.
      int selectChoiceCount = 1;
      for (int i = fromIndex + 1; i < toIndex; i++) {
        if (byteStrings.get(i - 1).getByte(byteStringOffset)
            != byteStrings.get(i).getByte(byteStringOffset)) {
          selectChoiceCount++;
        }
      }

      // Compute the offset that childNodes will get when we append it to node.
      long childNodesOffset = nodeOffset + intCount(node) + 2 + (selectChoiceCount * 2);

      node.writeInt(selectChoiceCount);
      node.writeInt(prefixIndex);

      for (int i = fromIndex; i < toIndex; i++) {
        byte rangeByte = byteStrings.get(i).getByte(byteStringOffset);
        if (i == fromIndex || rangeByte != byteStrings.get(i - 1).getByte(byteStringOffset)) {
          node.writeInt(rangeByte & 0xff);
        }
      }

      Buffer childNodes = new Buffer();
      int rangeStart = fromIndex;
      while (rangeStart < toIndex) {
        byte rangeByte = byteStrings.get(rangeStart).getByte(byteStringOffset);
        int rangeEnd = toIndex;
        for (int i = rangeStart + 1; i < toIndex; i++) {
          if (rangeByte != byteStrings.get(i).getByte(byteStringOffset)) {
            rangeEnd = i;
            break;
          }
        }

        if (rangeStart + 1 == rangeEnd
            && byteStringOffset + 1 == byteStrings.get(rangeStart).size()) {
          // The result is a single index.
          node.writeInt(indexes.get(rangeStart));
        } else {
          // The result is another node.
          node.writeInt((int) (-1 * (childNodesOffset + intCount(childNodes))));
          buildTrieRecursive(
              childNodesOffset,
              childNodes,
              byteStringOffset + 1,
              byteStrings,
              rangeStart,
              rangeEnd,
              indexes);
        }

        rangeStart = rangeEnd;
      }

      node.write(childNodes, childNodes.size());

    } else {
      // If all of the bytes are the same, encode a SCAN node.
      int scanByteCount = 0;
      for (int i = byteStringOffset, max = Math.min(from.size(), to.size()); i < max; i++) {
        if (from.getByte(i) == to.getByte(i)) {
          scanByteCount++;
        } else {
          break;
        }
      }

      // Compute the offset that childNodes will get when we append it to node.
      long childNodesOffset = nodeOffset + intCount(node) + 2 + scanByteCount + 1;

      node.writeInt(-scanByteCount);
      node.writeInt(prefixIndex);

      for (int i = byteStringOffset; i < byteStringOffset + scanByteCount; i++) {
        node.writeInt(from.getByte(i) & 0xff);
      }

      if (fromIndex + 1 == toIndex) {
        // The result is a single index.
        if (byteStringOffset + scanByteCount != byteStrings.get(fromIndex).size()) {
          throw new AssertionError();
        }
        node.writeInt(indexes.get(fromIndex));
      } else {
        // The result is another node.
        Buffer childNodes = new Buffer();
        node.writeInt((int) (-1 * (childNodesOffset + intCount(childNodes))));
        buildTrieRecursive(
            childNodesOffset,
            childNodes,
            byteStringOffset + scanByteCount,
            byteStrings,
            fromIndex,
            toIndex,
            indexes);
        node.write(childNodes, childNodes.size());
      }
    }
  }
/**
   * Builds a trie encoded as an int array. Nodes in the trie are of two types: SELECT and SCAN.
   *
   * SELECT nodes are encoded as:
   *  - selectChoiceCount: the number of bytes to choose between (a positive int)
   *  - prefixIndex: the result index at the current position or -1 if the current position is not
   *    a result on its own
   *  - a sorted list of selectChoiceCount bytes to match against the input string
   *  - a heterogeneous list of selectChoiceCount result indexes (>= 0) or offsets (< 0) of the
   *    next node to follow. Elements in this list correspond to elements in the preceding list.
   *    Offsets are negative and must be multiplied by -1 before being used.
   *
   * SCAN nodes are encoded as:
   *  - scanByteCount: the number of bytes to match in sequence. This count is negative and must
   *    be multiplied by -1 before being used.
   *  - prefixIndex: the result index at the current position or -1 if the current position is not
   *    a result on its own
   *  - a list of scanByteCount bytes to match
   *  - nextStep: the result index (>= 0) or offset (< 0) of the next node to follow. Offsets are
   *    negative and must be multiplied by -1 before being used.
   *
   * This structure is used to improve locality and performance when selecting from a list of
   * options.
   */
Builds a trie encoded as an int array. Nodes in the trie are of two types: SELECT and SCAN.

SCAN nodes are encoded as:
- scanByteCount: the number of bytes to match in sequence. This count is negative and must
be multiplied by -1 before being used.
- prefixIndex: the result index at the current position or -1 if the current position is not
a result on its own
- a list of scanByteCount bytes to match
- nextStep: the result index (>= 0) or offset (< 0) of the next node to follow. Offsets are
negative and must be multiplied by -1 before being used.

This structure is used to improve locality and performance when selecting from a list of
options.
Builds a trie encoded as an int array . Nodes in the trie are of two types : SELECT and SCAN . SCAN nodes are encoded as : - scanByteCount : the number of bytes to match in sequence . This count is negative and must be multiplied by - 1 before being used . - prefixIndex : the result index at the current position or - 1 if the current position is not a result on its own - a list of scanByteCount bytes to match - nextStep : the result index ( > = 0 ) or offset ( < 0 ) of the next node to follow . Offsets are negative and must be multiplied by - 1 before being used . This structure is used to improve locality and performance when selecting from a list of options .
private static void buildTrieRecursive(
      long nodeOffset,
      Buffer node,
      int byteStringOffset,
      List<ByteString> byteStrings,
      int fromIndex,
      int toIndex,
      List<Integer> indexes) {
    if (fromIndex >= toIndex) throw new AssertionError();
    for (int i = fromIndex; i < toIndex; i++) {
      if (byteStrings.get(i).size() < byteStringOffset) throw new AssertionError();
    }
    ByteString from = byteStrings.get(fromIndex);
    ByteString to = byteStrings.get(toIndex - 1);
    int prefixIndex = -1;
    if (byteStringOffset == from.size()) {
      prefixIndex = indexes.get(fromIndex);
      fromIndex++;
      from = byteStrings.get(fromIndex);
    }
    if (from.getByte(byteStringOffset) != to.getByte(byteStringOffset)) {
      int selectChoiceCount = 1;
      for (int i = fromIndex + 1; i < toIndex; i++) {
        if (byteStrings.get(i - 1).getByte(byteStringOffset)
            != byteStrings.get(i).getByte(byteStringOffset)) {
          selectChoiceCount++;
        }
      }
      long childNodesOffset = nodeOffset + intCount(node) + 2 + (selectChoiceCount * 2);
      node.writeInt(selectChoiceCount);
      node.writeInt(prefixIndex);
      for (int i = fromIndex; i < toIndex; i++) {
        byte rangeByte = byteStrings.get(i).getByte(byteStringOffset);
        if (i == fromIndex || rangeByte != byteStrings.get(i - 1).getByte(byteStringOffset)) {
          node.writeInt(rangeByte & 0xff);
        }
      }
      Buffer childNodes = new Buffer();
      int rangeStart = fromIndex;
      while (rangeStart < toIndex) {
        byte rangeByte = byteStrings.get(rangeStart).getByte(byteStringOffset);
        int rangeEnd = toIndex;
        for (int i = rangeStart + 1; i < toIndex; i++) {
          if (rangeByte != byteStrings.get(i).getByte(byteStringOffset)) {
            rangeEnd = i;
            break;
          }
        }
        if (rangeStart + 1 == rangeEnd
            && byteStringOffset + 1 == byteStrings.get(rangeStart).size()) {
          node.writeInt(indexes.get(rangeStart));
        } else {
          node.writeInt((int) (-1 * (childNodesOffset + intCount(childNodes))));
          buildTrieRecursive(
              childNodesOffset,
              childNodes,
              byteStringOffset + 1,
              byteStrings,
              rangeStart,
              rangeEnd,
              indexes);
        }
        rangeStart = rangeEnd;
      }
      node.write(childNodes, childNodes.size());
    } else {
      int scanByteCount = 0;
      for (int i = byteStringOffset, max = Math.min(from.size(), to.size()); i < max; i++) {
        if (from.getByte(i) == to.getByte(i)) {
          scanByteCount++;
        } else {
          break;
        }
      }
      long childNodesOffset = nodeOffset + intCount(node) + 2 + scanByteCount + 1;
      node.writeInt(-scanByteCount);
      node.writeInt(prefixIndex);
      for (int i = byteStringOffset; i < byteStringOffset + scanByteCount; i++) {
        node.writeInt(from.getByte(i) & 0xff);
      }
      if (fromIndex + 1 == toIndex) {
        if (byteStringOffset + scanByteCount != byteStrings.get(fromIndex).size()) {
          throw new AssertionError();
        }
        node.writeInt(indexes.get(fromIndex));
      } else {
        Buffer childNodes = new Buffer();
        node.writeInt((int) (-1 * (childNodesOffset + intCount(childNodes))));
        buildTrieRecursive(
            childNodesOffset,
            childNodes,
            byteStringOffset + scanByteCount,
            byteStrings,
            fromIndex,
            toIndex,
            indexes);
        node.write(childNodes, childNodes.size());
      }
    }
  }
private static void buildTrieRecursive ( long nodeOffset , Buffer node , int byteStringOffset , List < ByteString > byteStrings , int fromIndex , int toIndex , List < Integer > indexes ) { if ( fromIndex >= toIndex ) throw new AssertionError ( ) ; for ( int i = fromIndex ; i < toIndex ; i ++ ) { if ( byteStrings . get ( i ) . size ( ) < byteStringOffset ) throw new AssertionError ( ) ; } ByteString from = byteStrings . get ( fromIndex ) ; ByteString to = byteStrings . get ( toIndex - 1 ) ; int prefixIndex = - 1 ; if ( byteStringOffset == from . size ( ) ) { prefixIndex = indexes . get ( fromIndex ) ; fromIndex ++ ; from = byteStrings . get ( fromIndex ) ; } if ( from . getByte ( byteStringOffset ) != to . getByte ( byteStringOffset ) ) { int selectChoiceCount = 1 ; for ( int i = fromIndex + 1 ; i < toIndex ; i ++ ) { if ( byteStrings . get ( i - 1 ) . getByte ( byteStringOffset ) != byteStrings . get ( i ) . getByte ( byteStringOffset ) ) { selectChoiceCount ++ ; } } long childNodesOffset = nodeOffset + intCount ( node ) + 2 + ( selectChoiceCount * 2 ) ; node . writeInt ( selectChoiceCount ) ; node . writeInt ( prefixIndex ) ; for ( int i = fromIndex ; i < toIndex ; i ++ ) { byte rangeByte = byteStrings . get ( i ) . getByte ( byteStringOffset ) ; if ( i == fromIndex || rangeByte != byteStrings . get ( i - 1 ) . getByte ( byteStringOffset ) ) { node . writeInt ( rangeByte & 0xff ) ; } } Buffer childNodes = new Buffer ( ) ; int rangeStart = fromIndex ; while ( rangeStart < toIndex ) { byte rangeByte = byteStrings . get ( rangeStart ) . getByte ( byteStringOffset ) ; int rangeEnd = toIndex ; for ( int i = rangeStart + 1 ; i < toIndex ; i ++ ) { if ( rangeByte != byteStrings . get ( i ) . getByte ( byteStringOffset ) ) { rangeEnd = i ; break ; } } if ( rangeStart + 1 == rangeEnd && byteStringOffset + 1 == byteStrings . get ( rangeStart ) . size ( ) ) { node . writeInt ( indexes . get ( rangeStart ) ) ; } else { node . writeInt ( ( int ) ( - 1 * ( childNodesOffset + intCount ( childNodes ) ) ) ) ; buildTrieRecursive ( childNodesOffset , childNodes , byteStringOffset + 1 , byteStrings , rangeStart , rangeEnd , indexes ) ; } rangeStart = rangeEnd ; } node . write ( childNodes , childNodes . size ( ) ) ; } else { int scanByteCount = 0 ; for ( int i = byteStringOffset , max = Math . min ( from . size ( ) , to . size ( ) ) ; i < max ; i ++ ) { if ( from . getByte ( i ) == to . getByte ( i ) ) { scanByteCount ++ ; } else { break ; } } long childNodesOffset = nodeOffset + intCount ( node ) + 2 + scanByteCount + 1 ; node . writeInt ( - scanByteCount ) ; node . writeInt ( prefixIndex ) ; for ( int i = byteStringOffset ; i < byteStringOffset + scanByteCount ; i ++ ) { node . writeInt ( from . getByte ( i ) & 0xff ) ; } if ( fromIndex + 1 == toIndex ) { if ( byteStringOffset + scanByteCount != byteStrings . get ( fromIndex ) . size ( ) ) { throw new AssertionError ( ) ; } node . writeInt ( indexes . get ( fromIndex ) ) ; } else { Buffer childNodes = new Buffer ( ) ; node . writeInt ( ( int ) ( - 1 * ( childNodesOffset + intCount ( childNodes ) ) ) ) ; buildTrieRecursive ( childNodesOffset , childNodes , byteStringOffset + scanByteCount , byteStrings , fromIndex , toIndex , indexes ) ; node . write ( childNodes , childNodes . size ( ) ) ; } } }
Builds a trie encoded as an int array.
Builds a trie encoded as an int array .
// If the first element is already matched, that's our prefix. // If we have multiple bytes to choose from, encode a SELECT node. // Compute the offset that childNodes will get when we append it to node. // The result is a single index. // The result is another node. // If all of the bytes are the same, encode a SCAN node. // Compute the offset that childNodes will get when we append it to node. // The result is a single index. // The result is another node.