示例#1
0
  @Override
  public boolean containsKey(Object key) {
    for (HashNode<V> node : array) {
      if (node.getCode() == key.hashCode()) return true;
    }

    return false;
  }
示例#2
0
 private void updateModel(int index) {
   byte c = buf[index];
   if (index > 0) {
     HashNode hNode = new HashNode();
     byte prevC = buf[index - 1];
     int pos = ((prevC & 0xff) << 8) | (c & 0xff);
     hNode.index = index - 1;
     hNode.next = hashTable[pos];
     hashTable[pos] = hNode;
   }
 }
示例#3
0
  @Override
  public V get(Object key) {
    int code = key.hashCode();
    int index = calculateHash(code);

    HashNode<V> arrayNode = array[index];

    while (arrayNode != null && arrayNode.getCode() != code) {
      arrayNode = arrayNode.getNext();
    }

    if (arrayNode == null) return null;
    else return arrayNode.getObject();
  }
示例#4
0
 public void serialize(SerializerOutput out, HashNode obj) throws IOException {
   if (obj.getClass() == HashBucket.class) {
     out.write(Serialization.HTREE_BUCKET);
     HashBucket b = (HashBucket) obj;
     b.writeExternal(out);
   } else {
     out.write(Serialization.HTREE_DIRECTORY);
     HashDirectory n = (HashDirectory) obj;
     n.writeExternal(out);
   }
 }
示例#5
0
 @Specialization(guards = {"isNullHash(hash)", "!isRubyString(key)"})
 public Object setNull(
     VirtualFrame frame, DynamicObject hash, Object key, Object value, boolean byIdentity) {
   HashNodes.setStore(
       hash,
       PackedArrayStrategy.createStore(hashNode.hash(frame, key), key, value),
       1,
       null,
       null);
   assert HashNodes.verifyStore(hash);
   return value;
 }
示例#6
0
  @ExplodeLoop
  @Specialization(guards = {"isPackedHash(hash)", "!isRubyString(key)"})
  public Object setPackedArray(
      VirtualFrame frame, DynamicObject hash, Object key, Object value, boolean byIdentity) {
    assert HashNodes.verifyStore(hash);

    final int hashed = hashNode.hash(frame, key);

    final Object[] store = (Object[]) Layouts.HASH.getStore(hash);
    final int size = Layouts.HASH.getSize(hash);

    for (int n = 0; n < PackedArrayStrategy.MAX_ENTRIES; n++) {
      if (n < size) {
        if (hashed == PackedArrayStrategy.getHashed(store, n)) {
          final boolean equal;

          if (byIdentityProfile.profile(byIdentity)) {
            equal =
                equalNode.executeReferenceEqual(frame, key, PackedArrayStrategy.getKey(store, n));
          } else {
            equal =
                eqlNode.callBoolean(frame, key, "eql?", null, PackedArrayStrategy.getKey(store, n));
          }

          if (equal) {
            PackedArrayStrategy.setValue(store, n, value);
            assert HashNodes.verifyStore(hash);
            return value;
          }
        }
      }
    }

    extendProfile.enter();

    if (strategyProfile.profile(size + 1 <= PackedArrayStrategy.MAX_ENTRIES)) {
      PackedArrayStrategy.setHashedKeyValue(store, size, hashed, key, value);
      Layouts.HASH.setSize(hash, size + 1);
      return value;
    } else {
      PackedArrayStrategy.promoteToBuckets(hash, store, size);
      BucketsStrategy.addNewEntry(hash, hashed, key, value);
    }

    assert HashNodes.verifyStore(hash);

    return value;
  }
示例#7
0
    @ExplodeLoop
    @Override
    public Object execute(VirtualFrame frame) {
      final Object[] store = PackedArrayStrategy.createStore();

      int size = 0;

      initializers:
      for (int n = 0; n < keyValues.length / 2; n++) {
        Object key = keyValues[n * 2].execute(frame);

        if (stringKeyProfile.profile(RubyGuards.isRubyString(key))) {
          if (isFrozenNode == null) {
            CompilerDirectives.transferToInterpreter();
            isFrozenNode = insert(IsFrozenNodeGen.create(getContext(), getSourceSection(), null));
          }

          if (!isFrozenNode.executeIsFrozen(key)) {
            key = freezeNode.call(frame, dupNode.call(frame, key, "dup", null), "freeze", null);
          }
        }

        final int hashed = hashNode.hash(frame, key);

        final Object value = keyValues[n * 2 + 1].execute(frame);

        for (int i = 0; i < n; i++) {
          if (i < size
              && hashed == PackedArrayStrategy.getHashed(store, i)
              && equalNode.callBoolean(
                  frame, key, "eql?", null, PackedArrayStrategy.getKey(store, i))) {
            PackedArrayStrategy.setKey(store, i, key);
            PackedArrayStrategy.setValue(store, i, value);
            continue initializers;
          }
        }

        PackedArrayStrategy.setHashedKeyValue(store, size, hashed, key, value);
        size++;
      }

      return HashNodes.createHash(getContext().getCoreLibrary().getHashClass(), store, size);
    }
示例#8
0
  @Override
  public V put(K key, V value) {
    HashNode<V> hashNode = new HashNode<V>(key.hashCode(), value);
    int index = calculateHash(hashNode.getCode());

    if (array[index] == null) array[index] = hashNode;
    else {
      HashNode<V> arrayNode = array[index];

      while (arrayNode.getNext() != null) {
        arrayNode = arrayNode.getNext();
      }

      arrayNode.setNext(hashNode);
    }

    return value;
  }
示例#9
0
  public void inspect(Node node) {
    if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
      disable();
      // we still inspect since some nodes change state as a result (JRUBY-6836)
    }

    if (node == null) return;

    switch (node.getNodeType()) {
      case ALIASNODE:
        setFlag(node, METHOD);
        break;
      case ANDNODE:
        AndNode andNode = (AndNode) node;
        inspect(andNode.getFirstNode());
        inspect(andNode.getSecondNode());
        break;
      case ARGSCATNODE:
        ArgsCatNode argsCatNode = (ArgsCatNode) node;
        inspect(argsCatNode.getFirstNode());
        inspect(argsCatNode.getSecondNode());
        break;
      case ARGSPUSHNODE:
        ArgsPushNode argsPushNode = (ArgsPushNode) node;
        inspect(argsPushNode.getFirstNode());
        inspect(argsPushNode.getSecondNode());
        break;
      case ARGUMENTNODE:
        break;
      case ARRAYNODE:
      case BLOCKNODE:
      case DREGEXPNODE:
      case DSTRNODE:
      case DSYMBOLNODE:
      case DXSTRNODE:
      case LISTNODE:
        ListNode listNode = (ListNode) node;
        for (int i = 0; i < listNode.size(); i++) {
          inspect(listNode.get(i));
        }
        break;
      case ARGSNODE:
        ArgsNode argsNode = (ArgsNode) node;
        if (argsNode.getBlock() != null) setFlag(node, BLOCK_ARG);
        if (argsNode.getOptArgs() != null) {
          setFlag(node, OPT_ARGS);
          inspect(argsNode.getOptArgs());
        }
        if (argsNode.getRestArg() == -2 || argsNode.getRestArg() >= 0) setFlag(node, REST_ARG);
        break;
      case ATTRASSIGNNODE:
        AttrAssignNode attrAssignNode = (AttrAssignNode) node;
        inspect(attrAssignNode.getArgsNode());
        inspect(attrAssignNode.getReceiverNode());
        break;
      case BACKREFNODE:
        setFlag(node, BACKREF);
        break;
      case BEGINNODE:
        inspect(((BeginNode) node).getBodyNode());
        break;
      case BIGNUMNODE:
        break;
      case BINARYOPERATORNODE:
        BinaryOperatorNode binaryOperatorNode = (BinaryOperatorNode) node;
        inspect(binaryOperatorNode.getFirstNode());
        inspect(binaryOperatorNode.getSecondNode());
        break;
      case BLOCKARGNODE:
        break;
      case BLOCKPASSNODE:
        BlockPassNode blockPassNode = (BlockPassNode) node;
        inspect(blockPassNode.getArgsNode());
        inspect(blockPassNode.getBodyNode());
        break;
      case BREAKNODE:
        inspect(((BreakNode) node).getValueNode());
        break;
      case CALLNODE:
        CallNode callNode = (CallNode) node;
        inspect(callNode.getReceiverNode());
        // check for Proc.new, an especially magic method
        if (callNode.getName() == "new"
            && callNode.getReceiverNode() instanceof ConstNode
            && ((ConstNode) callNode.getReceiverNode()).getName() == "Proc") {
          // Proc.new needs the caller's block to instantiate a proc
          setFlag(node, FRAME_BLOCK);
        }
        if (callNode.getArgsNode() == null && callNode.getIterNode() == null) {
          switch (callNode.getReceiverNode().getNodeType()) {
              // no unary methods on literal numbers, symbols, or strings have frame/scope effects
            case FIXNUMNODE:
            case FLOATNODE:
            case BIGNUMNODE:
            case STRNODE:
            case SYMBOLNODE:
              return;
          }
        }
      case FCALLNODE:
        inspect(((IArgumentNode) node).getArgsNode());
        inspect(((BlockAcceptingNode) node).getIterNode());
      case VCALLNODE:
        INameNode nameNode = (INameNode) node;
        if (FRAME_AWARE_METHODS.contains(nameNode.getName())) {
          setFlag(node, FRAME_AWARE);
          if (nameNode.getName().indexOf("eval") != -1) {
            setFlag(node, EVAL);
          }
        }
        if (SCOPE_AWARE_METHODS.contains(nameNode.getName())) {
          setFlag(node, SCOPE_AWARE);
        }
        break;
      case CASENODE:
        CaseNode caseNode = (CaseNode) node;
        inspect(caseNode.getCaseNode());
        if (caseNode.getCases().size() > Options.COMPILE_OUTLINE_CASECOUNT.load()) {
          // if more than N cases, disable; we'll compile them as separate bodies
          // see BaseBodyCompiler#compiledSequencedConditional and ASTCompiler#compileCase
          disable();
          return;
        } else {
          for (Node when : caseNode.getCases().childNodes()) {
            inspect(when);
          }
          inspect(caseNode.getElseNode());
        }
        break;
      case CLASSNODE:
        setFlag(node, CLASS);
        ClassNode classNode = (ClassNode) node;
        inspect(classNode.getCPath());
        inspect(classNode.getSuperNode());
        break;
      case CLASSVARNODE:
        setFlag(node, CLASS_VAR);
        break;
      case CONSTDECLNODE:
        inspect(((AssignableNode) node).getValueNode());
        setFlag(node, CONSTANT);
        break;
      case CLASSVARASGNNODE:
        inspect(((AssignableNode) node).getValueNode());
        setFlag(node, CLASS_VAR);
        break;
      case CLASSVARDECLNODE:
        inspect(((AssignableNode) node).getValueNode());
        setFlag(node, CLASS_VAR);
        break;
      case COLON2NODE:
        inspect(((Colon2Node) node).getLeftNode());
        break;
      case COLON3NODE:
        break;
      case CONSTNODE:
        setFlag(node, CONSTANT);
        break;
      case DEFNNODE:
      case DEFSNODE:
        setFlag(node, METHOD);
        setFlag(node, FRAME_VISIBILITY);
        setFlag(node, SCOPE_AWARE);
        break;
      case DEFINEDNODE:
        switch (((DefinedNode) node).getExpressionNode().getNodeType()) {
          case CLASSVARASGNNODE:
          case CLASSVARDECLNODE:
          case CONSTDECLNODE:
          case DASGNNODE:
          case GLOBALASGNNODE:
          case LOCALASGNNODE:
          case MULTIPLEASGNNODE:
          case OPASGNNODE:
          case OPELEMENTASGNNODE:
          case DVARNODE:
          case FALSENODE:
          case TRUENODE:
          case LOCALVARNODE:
          case INSTVARNODE:
          case BACKREFNODE:
          case SELFNODE:
          case VCALLNODE:
          case YIELDNODE:
          case GLOBALVARNODE:
          case CONSTNODE:
          case FCALLNODE:
          case CLASSVARNODE:
            // ok, we have fast paths
            inspect(((DefinedNode) node).getExpressionNode());
            break;
          default:
            // long, slow way causes disabling
            // we still inspect because some nodes may change state (JRUBY-6836)
            inspect(((DefinedNode) node).getExpressionNode());
            disable();
        }
        break;
      case DOTNODE:
        DotNode dotNode = (DotNode) node;
        inspect(dotNode.getBeginNode());
        inspect(dotNode.getEndNode());
        break;
      case DASGNNODE:
        inspect(((AssignableNode) node).getValueNode());
        break;
      case DVARNODE:
        break;
      case ENSURENODE:
        inspect(((EnsureNode) node).getBodyNode());
        inspect(((EnsureNode) node).getEnsureNode());
        disable();
        break;
      case ENCODINGNODE:
        break;
      case EVSTRNODE:
        inspect(((EvStrNode) node).getBody());
        break;
      case FALSENODE:
        break;
      case FIXNUMNODE:
        break;
      case FLIPNODE:
        inspect(((FlipNode) node).getBeginNode());
        inspect(((FlipNode) node).getEndNode());
        break;
      case FLOATNODE:
        break;
      case FORNODE:
        setFlag(node, CLOSURE);
        setFlag(node, SCOPE_AWARE);
        inspect(((ForNode) node).getIterNode());
        inspect(((ForNode) node).getBodyNode());
        inspect(((ForNode) node).getVarNode());
        break;
      case GLOBALASGNNODE:
        GlobalAsgnNode globalAsgnNode = (GlobalAsgnNode) node;
        if (globalAsgnNode.getName().equals("$_")) {
          setFlag(node, LASTLINE);
        } else if (globalAsgnNode.getName().equals("$~")) {
          setFlag(node, BACKREF);
        }
        inspect(globalAsgnNode.getValueNode());
        break;
      case GLOBALVARNODE:
        {
          String name = ((GlobalVarNode) node).getName();
          if (name.equals("$_") || name.equals("$LAST_READ_LINE")) {
            setFlag(node, LASTLINE);
          } else if (name.equals("$~")
              || name.equals("$`")
              || name.equals("$'")
              || name.equals("$+")
              || name.equals("$LAST_MATCH_INFO")
              || name.equals("$PREMATCH")
              || name.equals("$POSTMATCH")
              || name.equals("$LAST_PAREN_MATCH")) {
            setFlag(node, BACKREF);
          }
          break;
        }
      case HASHNODE:
        HashNode hashNode = (HashNode) node;
        inspect(hashNode.getListNode());
        break;
      case IFNODE:
        IfNode ifNode = (IfNode) node;
        inspect(ifNode.getCondition());
        inspect(ifNode.getThenBody());
        inspect(ifNode.getElseBody());
        break;
      case INSTASGNNODE:
        inspect(((AssignableNode) node).getValueNode());
        break;
      case INSTVARNODE:
        break;
      case ISCOPINGNODE:
        IScopingNode iscopingNode = (IScopingNode) node;
        inspect(iscopingNode.getCPath());
        break;
      case ITERNODE:
        setFlag(node, CLOSURE);
        break;
      case LAMBDANODE:
        setFlag(node, CLOSURE);
        break;
      case LOCALASGNNODE:
        LocalAsgnNode localAsgnNode = (LocalAsgnNode) node;
        if (PRAGMAS.contains(localAsgnNode.getName())) {
          if (localAsgnNode.getName().equals("__NOFRAME__")) {
            noFrame = localAsgnNode.getValueNode() instanceof TrueNode;
          }
          break;
        }
        inspect(localAsgnNode.getValueNode());
        break;
      case LOCALVARNODE:
        break;
      case MATCHNODE:
        inspect(((MatchNode) node).getRegexpNode());
        setFlag(node, BACKREF);
        break;
      case MATCH2NODE:
        Match2Node match2Node = (Match2Node) node;
        inspect(match2Node.getReceiverNode());
        inspect(match2Node.getValueNode());
        setFlag(node, BACKREF);
        if (match2Node instanceof Match2CaptureNode) {
          // additionally need scope, to set local vars
          // FIXME: this can be done without heap scope
          setFlag(node, SCOPE_AWARE);
        }
        break;
      case MATCH3NODE:
        Match3Node match3Node = (Match3Node) node;
        inspect(match3Node.getReceiverNode());
        inspect(match3Node.getValueNode());
        setFlag(node, BACKREF);
        break;
      case MODULENODE:
        setFlag(node, CLASS);
        inspect(((ModuleNode) node).getCPath());
        break;
      case MULTIPLEASGN19NODE:
        MultipleAsgn19Node multipleAsgn19Node = (MultipleAsgn19Node) node;
        inspect(multipleAsgn19Node.getPre());
        inspect(multipleAsgn19Node.getPost());
        inspect(multipleAsgn19Node.getRest());
        inspect(multipleAsgn19Node.getValueNode());
        break;
      case MULTIPLEASGNNODE:
        MultipleAsgnNode multipleAsgnNode = (MultipleAsgnNode) node;
        inspect(multipleAsgnNode.getArgsNode());
        inspect(multipleAsgnNode.getHeadNode());
        inspect(multipleAsgnNode.getValueNode());
        break;
      case NEWLINENODE:
        inspect(((NewlineNode) node).getNextNode());
        break;
      case NEXTNODE:
        inspect(((NextNode) node).getValueNode());
        break;
      case NILNODE:
        break;
      case NOTNODE:
        inspect(((NotNode) node).getConditionNode());
        break;
      case NTHREFNODE:
        break;
      case OPASGNANDNODE:
        OpAsgnAndNode opAsgnAndNode = (OpAsgnAndNode) node;
        inspect(opAsgnAndNode.getFirstNode());
        inspect(opAsgnAndNode.getSecondNode());
        break;
      case OPASGNNODE:
        OpAsgnNode opAsgnNode = (OpAsgnNode) node;
        inspect(opAsgnNode.getReceiverNode());
        inspect(opAsgnNode.getValueNode());
        break;
      case OPASGNORNODE:
        switch (((OpAsgnOrNode) node).getFirstNode().getNodeType()) {
          case CLASSVARASGNNODE:
          case CLASSVARDECLNODE:
          case CONSTDECLNODE:
          case DASGNNODE:
          case GLOBALASGNNODE:
          case LOCALASGNNODE:
          case MULTIPLEASGNNODE:
          case OPASGNNODE:
          case OPELEMENTASGNNODE:
          case DVARNODE:
          case FALSENODE:
          case TRUENODE:
          case LOCALVARNODE:
          case INSTVARNODE:
          case BACKREFNODE:
          case SELFNODE:
          case VCALLNODE:
          case YIELDNODE:
          case GLOBALVARNODE:
          case CONSTNODE:
          case FCALLNODE:
          case CLASSVARNODE:
            // ok, we have fast paths
            inspect(((OpAsgnOrNode) node).getSecondNode());
            break;
          default:
            // long, slow way causes disabling for defined
            inspect(((OpAsgnOrNode) node).getFirstNode());
            inspect(((OpAsgnOrNode) node).getSecondNode());
            disable();
        }
        break;
      case OPELEMENTASGNNODE:
        OpElementAsgnNode opElementAsgnNode = (OpElementAsgnNode) node;
        inspect(opElementAsgnNode.getArgsNode());
        inspect(opElementAsgnNode.getReceiverNode());
        inspect(opElementAsgnNode.getValueNode());
        break;
      case OPTARGNODE:
        inspect(((OptArgNode) node).getValue());
        break;
      case ORNODE:
        OrNode orNode = (OrNode) node;
        inspect(orNode.getFirstNode());
        inspect(orNode.getSecondNode());
        break;
      case POSTEXENODE:
        PostExeNode postExeNode = (PostExeNode) node;
        setFlag(node, CLOSURE);
        setFlag(node, SCOPE_AWARE);
        inspect(postExeNode.getBodyNode());
        inspect(postExeNode.getVarNode());
        break;
      case PREEXENODE:
        PreExeNode preExeNode = (PreExeNode) node;
        setFlag(node, CLOSURE);
        setFlag(node, SCOPE_AWARE);
        inspect(preExeNode.getBodyNode());
        inspect(preExeNode.getVarNode());
        break;
      case REDONODE:
        break;
      case REGEXPNODE:
        break;
      case ROOTNODE:
        inspect(((RootNode) node).getBodyNode());
        if (((RootNode) node).getBodyNode() instanceof BlockNode) {
          BlockNode blockNode = (BlockNode) ((RootNode) node).getBodyNode();
          if (blockNode.size() > 500) {
            // method has more than 500 lines; we'll need to split it
            // and therefore need to use a heap-based scope
            setFlag(node, SCOPE_AWARE);
          }
        }
        break;
      case RESCUEBODYNODE:
        RescueBodyNode rescueBody = (RescueBodyNode) node;
        inspect(rescueBody.getExceptionNodes());
        inspect(rescueBody.getBodyNode());
        inspect(rescueBody.getOptRescueNode());
        break;
      case RESCUENODE:
        RescueNode rescueNode = (RescueNode) node;
        inspect(rescueNode.getBodyNode());
        inspect(rescueNode.getElseNode());
        inspect(rescueNode.getRescueNode());
        disable();
        break;
      case RETRYNODE:
        setFlag(node, RETRY);
        break;
      case RETURNNODE:
        inspect(((ReturnNode) node).getValueNode());
        break;
      case SCLASSNODE:
        setFlag(node, CLASS);
        setFlag(node, FRAME_AWARE);
        SClassNode sclassNode = (SClassNode) node;
        inspect(sclassNode.getReceiverNode());
        break;
      case SCOPENODE:
        break;
      case SELFNODE:
        break;
      case SPLATNODE:
        inspect(((SplatNode) node).getValue());
        break;
      case STARNODE:
        break;
      case STRNODE:
        break;
      case SUPERNODE:
        SuperNode superNode = (SuperNode) node;
        inspect(superNode.getArgsNode());
        inspect(superNode.getIterNode());
        setFlag(node, SUPER);
        break;
      case SVALUENODE:
        inspect(((SValueNode) node).getValue());
        break;
      case SYMBOLNODE:
        break;
      case TOARYNODE:
        inspect(((ToAryNode) node).getValue());
        break;
      case TRUENODE:
        break;
      case UNDEFNODE:
        setFlag(node, METHOD);
        break;
      case UNTILNODE:
        UntilNode untilNode = (UntilNode) node;
        ASTInspector untilInspector =
            subInspect(untilNode.getConditionNode(), untilNode.getBodyNode());
        // a while node could receive non-local flow control from any of these:
        // * a closure within the loop
        // * an eval within the loop
        // * a block-arg-based proc called within the loop
        if (untilInspector.getFlag(CLOSURE) || untilInspector.getFlag(EVAL)) {
          untilNode.containsNonlocalFlow = true;

          // we set scope-aware to true to force heap-based locals
          setFlag(node, SCOPE_AWARE);
        }
        integrate(untilInspector);
        break;
      case VALIASNODE:
        break;
      case WHENNODE:
        {
          inspect(((WhenNode) node).getBodyNode());
          inspect(((WhenNode) node).getExpressionNodes());
          inspect(((WhenNode) node).getNextCase());
          // if any elements are not literals or are regexp, set backref
          Node expr = ((WhenNode) node).getExpressionNodes();
          if (!(expr instanceof ILiteralNode) || expr.getNodeType() == NodeType.REGEXPNODE) {
            setFlag(node, BACKREF);
          }
          break;
        }
      case WHILENODE:
        WhileNode whileNode = (WhileNode) node;
        ASTInspector whileInspector =
            subInspect(whileNode.getConditionNode(), whileNode.getBodyNode());
        // a while node could receive non-local flow control from any of these:
        // * a closure within the loop
        // * an eval within the loop
        // * a block-arg-based proc called within the loop
        // * any case that disables optimization, like rescues and ensures
        if (whileInspector.getFlag(CLOSURE) || whileInspector.getFlag(EVAL) || getFlag(BLOCK_ARG)) {
          whileNode.containsNonlocalFlow = true;

          // we set scope-aware to true to force heap-based locals
          setFlag(node, SCOPE_AWARE);
        }
        integrate(whileInspector);
        break;
      case XSTRNODE:
        break;
      case YIELDNODE:
        inspect(((YieldNode) node).getArgsNode());
        break;
      case ZARRAYNODE:
        break;
      case ZEROARGNODE:
        break;
      case ZSUPERNODE:
        setFlag(node, SCOPE_AWARE);
        setFlag(node, ZSUPER);
        inspect(((ZSuperNode) node).getIterNode());
        break;
      default:
        // encountered a node we don't recognize, set everything to true to disable optz
        assert false : "All nodes should be accounted for in AST inspector: " + node;
        disable();
    }
  }
示例#10
0
 // consider refactoring signature to return PotentialMatch object with fields set...
 int findMatch(int index, int[] distOut, int[] gainOut, int[] costPerByteOut) {
   final int maxCostCacheLength = 32;
   int[] literalCostCache = new int[maxCostCacheLength + 1];
   int maxIndexMinusIndex = buf.length - index;
   int bestLength = 0;
   int bestDist = 0;
   int bestGain = 0;
   int bestCopyCost = 0;
   int maxComputedLength = 0;
   if (maxIndexMinusIndex > 1) {
     int pos = ((buf[index] & 0xff) << 8) | (buf[index + 1] & 0xff);
     HashNode prevNode = null;
     int hNodeCount = 0;
     for (HashNode hNode = hashTable[pos]; hNode != null; prevNode = hNode, hNode = hNode.next) {
       int i = hNode.index;
       int dist = index - i;
       hNodeCount++;
       if (hNodeCount > 256 || dist > maxCopyDist) {
         if (hashTable[pos] == hNode) {
           hashTable[pos] = null;
         } else {
           prevNode.next = null;
         }
         break;
       }
       int maxLen = index - i;
       if (maxIndexMinusIndex < maxLen) {
         maxLen = maxIndexMinusIndex;
       }
       if (maxLen < LEN_MIN) {
         continue;
       }
       i += 2;
       int length = 2;
       for (length = 2; length < maxLen && buf[i] == buf[index + length]; length++) {
         i++;
       }
       if (length < LEN_MIN) {
         continue;
       }
       dist = dist - length + 1;
       if (dist > distMax || (length == 2 && dist >= MAX_2BYTE_DIST)) {
         continue;
       }
       if (length <= bestLength && dist > bestDist) {
         if (length <= bestLength - 2) {
           continue;
         }
         if (dist > (bestDist << DIST_WIDTH)) {
           if (length < bestLength || dist > (bestDist << (DIST_WIDTH + 1))) {
             continue;
           }
         }
       }
       int literalCost = 0;
       if (length > maxComputedLength) {
         int limit = length;
         if (limit > maxCostCacheLength) limit = maxCostCacheLength;
         for (i = maxComputedLength; i < limit; i++) {
           byte c = buf[index + i];
           literalCostCache[i + 1] = literalCostCache[i] + symEncoder.writeSymbolCost(c & 0xff);
         }
         maxComputedLength = limit;
         if (length > maxCostCacheLength) {
           literalCost = literalCostCache[maxCostCacheLength];
           literalCost += literalCost / maxCostCacheLength * (length - maxCostCacheLength);
         } else {
           literalCost = literalCostCache[length];
         }
       } else {
         literalCost = literalCostCache[length];
       }
       if (literalCost > bestGain) {
         int distRanges = getNumDistRanges(dist);
         int copyCost = encodeLengthCost(length, dist, distRanges);
         if (literalCost - copyCost - (distRanges << 16) > bestGain) {
           copyCost += encodeDistance2Cost(dist, distRanges);
           int gain = literalCost - copyCost;
           if (gain > bestGain) {
             bestGain = gain;
             bestLength = length;
             bestDist = dist;
             bestCopyCost = copyCost;
           }
         }
       }
     }
   }
   costPerByteOut[0] = bestLength > 0 ? bestCopyCost / bestLength : 0;
   distOut[0] = bestDist;
   gainOut[0] = bestGain;
   return bestLength;
 }