All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javaemul.internal.StringHashCache Maven / Gradle / Ivy

There is a newer version: 2.10.0
Show newest version
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package javaemul.internal;

import static javaemul.internal.Coercions.ensureInt;

/**
 * Hashcode caching for strings.
 */
class StringHashCache {
  /**
   * The "old" cache; it will be dumped when front is full.
   */
  private static Object back = createNativeObject();
  /**
   * Tracks the number of entries in front.
   */
  private static int count = 0;
  /**
   * The "new" cache; it will become back when it becomes full.
   */
  private static Object front = createNativeObject();
  /**
   * Pulled this number out of thin air.
   */
  private static final int MAX_CACHE = 256;

  public static int getHashCode(String str) {
    // Accesses must to be prefixed with ':' to prevent conflict with built-in
    // JavaScript properties.
    String key = ":" + str;

    // Check the front store.
    Object result = getProperty(front, key);
    if (!JsUtils.isUndefined(result)) {
      return unsafeCastToInt(result);
    }
    // Check the back store.
    result = getProperty(back, key);
    int hashCode = JsUtils.isUndefined(result) ? compute(str) : unsafeCastToInt(result);
    // Increment can trigger the swap/flush; call after checking back but
    // before writing to front.
    increment();
    JsUtils.setIntProperty(front, key, hashCode);

    return hashCode;
  }

  private static int compute(String str) {
    int hashCode = 0;
    int n = str.length();
    int nBatch = n - 4;
    int i = 0;

    // Process batches of 4 characters at a time and add them to the hash coercing to 32 bits
    while (i < nBatch) {
      hashCode = str.charAt(i + 3)
          + 31 * (str.charAt(i + 2)
          + 31 * (str.charAt(i + 1)
          + 31 * (str.charAt(i)
          + 31 * hashCode)));

      hashCode = ensureInt(hashCode); // make sure we don't overflow
      i += 4;
    }

    // Now process the leftovers
    while (i < n) {
      hashCode = hashCode * 31 + str.charAt(i++);
    }
    hashCode = ensureInt(hashCode); // make sure we don't overflow

    return hashCode;
  }

  private static void increment() {
    if (count == MAX_CACHE) {
      back = front;
      front = createNativeObject();
      count = 0;
    }
    ++count;
  }

  private static native Object getProperty(Object map, String key) /*-{
    return map[key];
  }-*/;

  private static native Object createNativeObject() /*-{
    return {};
  }-*/;

  private static native int unsafeCastToInt(Object o) /*-{
    return o;
  }-*/;
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy