public V remove(DirectBytes keyBytes, V value, int hash2) {
      if (hash2 == hashLookup.unsetKey()) hash2 = ~hash2;
      lock();
      try {
        hashLookup.startSearch(hash2);
        while (true) {
          int pos = hashLookup.nextInt();
          if (pos == hashLookup.unsetValue()) {
            return null;

          } else {
            long offset = entriesOffset + pos * builder.entrySize();
            tmpBytes.storePositionAndSize(bytes, offset, builder.entrySize());
            if (!keyEquals(keyBytes, tmpBytes)) continue;
            long keyLength = align(keyBytes.remaining());
            tmpBytes.skip(keyLength);
            V v =
                value == null && builder.removeReturnsNull()
                    ? null
                    : readObjectUsing(value, offset + keyLength);
            hashLookup.remove(hash2, pos);
            freeList.clear(pos);
            if (pos < nextSet) nextSet = pos;
            return v;
          }
        }
      } finally {
        unlock();
      }
    }
    public V acquire(DirectBytes keyBytes, V value, int hash2, boolean create) {
      if (hash2 == hashLookup.unsetKey()) hash2 = ~hash2;
      lock();
      try {
        hashLookup.startSearch(hash2);
        while (true) {
          int pos = hashLookup.nextInt();
          if (pos == hashLookup.unsetValue()) {
            return create ? acquireEntry(keyBytes, value, hash2) : null;

          } else {
            long offset = entriesOffset + pos * builder.entrySize();
            tmpBytes.storePositionAndSize(bytes, offset, builder.entrySize());
            long start0 = System.nanoTime();
            boolean miss = !keyEquals(keyBytes, tmpBytes);
            long time0 = System.nanoTime() - start0;
            if (time0 > 1e6)
              System.out.println("startsWith took " + time0 / 100000 / 10.0 + " ms.");
            if (miss) continue;
            long keyLength =
                align(keyBytes.remaining() + tmpBytes.position()); // includes the stop bit length.
            tmpBytes.position(keyLength);
            return readObjectUsing(value, offset + keyLength);
          }
        }
      } finally {
        unlock();
      }
    }
    public V put(DirectBytes keyBytes, V value, int hash2, boolean replaceIfPresent) {
      if (hash2 == hashLookup.unsetKey()) hash2 = ~hash2;
      lock();
      try {
        hashLookup.startSearch(hash2);
        while (true) {
          int pos = hashLookup.nextInt();
          if (pos == hashLookup.unsetValue()) {
            putEntry(keyBytes, value, hash2);
            return null;

          } else {
            long offset = entriesOffset + pos * builder.entrySize();
            tmpBytes.storePositionAndSize(bytes, offset, builder.entrySize());
            if (!keyEquals(keyBytes, tmpBytes)) continue;
            long keyLength = keyBytes.remaining();
            tmpBytes.skip(keyLength);
            long alignPosition = align(tmpBytes.position());
            tmpBytes.position(alignPosition);
            if (replaceIfPresent) {
              if (builder.putReturnsNull()) {
                appendInstance(keyBytes, value);
                return null;
              }
              V v = readObjectUsing(null, offset + alignPosition);
              tmpBytes.position(alignPosition);
              appendInstance(keyBytes, value);
              return v;

            } else {
              if (builder.putReturnsNull()) {
                return null;
              }
              return readObjectUsing(null, offset + keyLength);
            }
          }
        }
      } finally {
        unlock();
      }
    }