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

com.shapesecurity.functional.data.HashTable Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
/*
 * Copyright 2014 Shape Security, 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 com.shapesecurity.functional.data;

import com.shapesecurity.functional.Effect;
import com.shapesecurity.functional.F;
import com.shapesecurity.functional.F2;
import com.shapesecurity.functional.Pair;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * An immutable hash trie tree implementation.
 *
 * @param  Key type
 * @param  Value type
 */
public abstract class HashTable {
  private final static Hasher DEFAULT_HASHER = new Hasher() {
    @Override
    public int hash(@NotNull Object data) {
      return data.hashCode();
    }

    @Override
    public boolean eq(@NotNull Object o, @NotNull Object b) {
      return o.equals(b);
    }
  };
  @NotNull
  public final Hasher hasher;
  public final int length;

  protected HashTable(@NotNull Hasher hasher, int length) {
    super();
    this.hasher = hasher;
    this.length = length;
  }

  @SuppressWarnings("unchecked")
  @NotNull
  public static  Hasher defaultHasher() {
    return (Hasher) DEFAULT_HASHER;
  }

  @NotNull
  public static  HashTable empty(@NotNull Hasher hasher) {
    return new Empty<>(hasher);
  }

  @NotNull
  public static  HashTable empty() {
    return empty(HashTable.defaultHasher());
  }

  @NotNull
  public final HashTable put(@NotNull K key, @NotNull V value) {
    return this.put(key, value, this.hasher.hash(key));
  }

  @NotNull
  public final HashTable remove(@NotNull K key) {
    return this.remove(key, this.hasher.hash(key)).orJust(this);
  }

  @NotNull
  protected abstract HashTable put(@NotNull K key, @NotNull V value, int hash);

  @NotNull
  protected abstract Maybe> remove(@NotNull K key, int hash);

  @NotNull
  public final Maybe get(@NotNull K key) {
    return this.get(key, this.hasher.hash(key));
  }

  @NotNull
  protected abstract Maybe get(@NotNull K key, int hash);

  @SuppressWarnings("unchecked")
  @NotNull
  public final HashTable merge(@NotNull HashTable tree) {
    return this.merge(tree, (a, b) -> b);
  }

  @NotNull
  public abstract HashTable merge(@NotNull HashTable tree, @NotNull F2 merger);

  @NotNull
  public abstract  A foldLeft(@NotNull F2, A> f, @NotNull A init);

  @NotNull
  public abstract  A foldRight(@NotNull F2, A, A> f, @NotNull A init);

  @NotNull
  public ImmutableList> entries() {
    return this.foldRight((kvPair, pairs) -> pairs.cons(kvPair), ImmutableList.nil());
  }

  public abstract void foreach(@NotNull Effect> e);

  @NotNull
  public abstract Maybe> find(@NotNull F, Boolean> f);

  @NotNull
  public abstract  Maybe findMap(@NotNull F, Maybe> f);

  public abstract  HashTable map(@NotNull F f);

  /**
   * An empty hash table.
   *
   * @param  Key type
   * @param  Value type
   */
  private final static class Empty extends HashTable {
    protected Empty(@NotNull Hasher hasher) {
      super(hasher, 0);
    }

    @NotNull
    @Override
    protected HashTable put(@NotNull K key, @NotNull V value, int hash) {
      return new Leaf<>(this.hasher, ImmutableList.list(new Pair<>(key, value)), hash, 1);
    }

    @NotNull
    @Override
    protected Maybe> remove(@NotNull K key, int hash) {
      return Maybe.nothing();
    }

    @NotNull
    @Override
    protected Maybe get(@NotNull K key, int hash) {
      return Maybe.nothing();
    }

    @NotNull
    @Override
    public HashTable merge(@NotNull HashTable tree, @NotNull F2 merger) {
      return tree;
    }

    @NotNull
    @Override
    public  A foldLeft(@NotNull F2, A> f, @NotNull A init) {
      return init;
    }

    @NotNull
    @Override
    public  A foldRight(@NotNull F2, A, A> f, @NotNull A init) {
      return init;
    }

    @Override
    public void foreach(@NotNull Effect> e) {

    }

    @NotNull
    @Override
    public Maybe> find(@NotNull F, Boolean> f) {
      return Maybe.nothing();
    }

    @NotNull
    @Override
    public  Maybe findMap(@NotNull F, Maybe> f) {
      return Maybe.nothing();
    }

    @Override
    public  HashTable map(@NotNull F f) {
      return empty();
    }
  }

  /**
   * A leaf node that contains a list of pairs where all the keys have exactly the same hash code.
   *
   * @param  Key type
   * @param  Value type
   */
  private final static class Leaf extends HashTable {
    @NotNull
    private final ImmutableList> dataList;
    public int baseHash;

    protected Leaf(@NotNull Hasher hasher, @NotNull ImmutableList> dataList, int baseHash, int length) {
      super(hasher, length);
      this.dataList = dataList;
      this.baseHash = baseHash;
    }

    @NotNull
    @Override
    protected HashTable put(@NotNull final K key, @NotNull final V value, final int hash) {
      if (hash == this.baseHash) {
        Pair>> result = this.dataList.mapAccumL((found, kvPair) -> {
          if (found) {
            return new Pair<>(true, kvPair);
          }
          if (Leaf.this.hasher.eq(kvPair.a, key)) {
            return new Pair<>(true, new Pair<>(key, value));
          }
          return new Pair<>(false, kvPair);
        }, false);
        if (result.a) {
          return new Leaf<>(this.hasher, result.b, hash, this.length);
        }
        return new Leaf<>(this.hasher, this.dataList.cons(new Pair<>(key, value)), hash, this.length + 1);
      }
      return toFork().put(key, value, hash);
    }

    @NotNull
    @Override
    protected Maybe> remove(@NotNull final K key, int hash) {
      if (this.baseHash != hash) {
        return Maybe.nothing();
      }
      Pair>> result = this.dataList.foldRight((i, p) -> {
        if (p.a) {
          return new Pair<>(true, p.b.cons(i));
        }
        if (Leaf.this.hasher.eq(i.a, key)) {
          return new Pair<>(true, p.b);
        }
        return new Pair<>(false, p.b.cons(i));
      }, new Pair<>(false, ImmutableList.nil()));
      if (result.a) {
        if (this.length == 1) {
          return Maybe.just(HashTable.empty());
        }
        return Maybe.just(new Leaf<>(this.hasher, result.b, this.baseHash, this.length - 1));
      }
      return Maybe.nothing();
    }

    @SuppressWarnings("unchecked")
    private Fork toFork() {
      int subHash = this.baseHash & 31;
      HashTable[] children = new HashTable[32];
      children[subHash] = new Leaf<>(this.hasher, this.dataList, this.baseHash >>> 5, this.length);
      return new Fork<>(this.hasher, children, this.length);
    }

    @NotNull
    @Override
    protected Maybe get(@NotNull final K key, final int hash) {
      if (this.baseHash != hash) {
        return Maybe.nothing();
      }
      Maybe> pairMaybe = this.dataList.find(kvPair -> Leaf.this.hasher.eq(kvPair.a, key));
      return pairMaybe.map(p -> p.b);
    }

    @SuppressWarnings("unchecked")
    @NotNull
    @Override
    public HashTable merge(@NotNull HashTable tree, @NotNull final F2 merger) {
      if (tree instanceof Empty) {
        return this;
      } else if (tree instanceof Leaf) {
        final Leaf leaf = (Leaf) tree;
        if (leaf.baseHash == this.baseHash) {
          final Pair[] pairs = this.dataList.toArray(new Pair[this.dataList.length]);
          ImmutableList> right = leaf.dataList.foldLeft(
              (@NotNull ImmutableList> result, @NotNull Pair kvPair) -> {
                for (int i = 0; i < pairs.length; i++) {
                  if (Leaf.this.hasher.eq(pairs[i].a, kvPair.a)) {
                    pairs[i] = new Pair<>(pairs[i].a, merger.apply(pairs[i].b, kvPair.b));
                    return result;
                  }
                }
                return result.cons(kvPair);
              }, ImmutableList.nil());
          ImmutableList> newList = ImmutableList.from(pairs).append(right);
          return new Leaf<>(this.hasher, newList, this.baseHash, newList.length);
        }
      }
      return toFork().merge(tree, merger);
    }

    @NotNull
    public  A foldLeft(@NotNull F2, A> f, @NotNull A init) {
      return this.dataList.foldLeft(f, init);
    }

    @NotNull
    @Override
    public  A foldRight(@NotNull F2, A, A> f, @NotNull A init) {
      return this.dataList.foldRight(f, init);
    }

    @Override
    public void foreach(@NotNull Effect> e) {
      this.dataList.foreach(e);
    }

    @NotNull
    @Override
    public Maybe> find(@NotNull F, Boolean> f) {
      return this.dataList.find(f);
    }

    @NotNull
    @Override
    public  Maybe findMap(@NotNull F, Maybe> f) {
      return this.dataList.findMap(f);
    }

    @Override
    public  Leaf map(@NotNull F f) {
      return new Leaf<>(this.hasher, this.dataList.map(pair -> pair.mapB(f)), baseHash, length);
    }
  }

  private final static class Fork extends HashTable {
    @NotNull
    private final HashTable[] children;

    private Fork(@NotNull Hasher hasher, @NotNull HashTable[] children, int length) {
      super(hasher, length);
      this.children = children;
    }

    @NotNull
    @Override
    protected HashTable put(@NotNull K key, @NotNull V value, int hash) {
      int subHash = hash & 31;
      HashTable[] cloned = Fork.this.children.clone();
      if (cloned[subHash] == null) {
        cloned[subHash] = new Leaf<>(Fork.this.hasher, ImmutableList.nil(), hash >>> 5, 0);
      }
      int length1 = cloned[subHash].length;
      cloned[subHash] = cloned[subHash].put(key, value, hash >>> 5);
      return new Fork<>(this.hasher, cloned, this.length - length1 + cloned[subHash].length);
    }

    @NotNull
    @Override
    protected Maybe> remove(@NotNull K key, int hash) {
      final int subHash = hash & 31;
      if (this.children[subHash] == null) {
        return Maybe.nothing();
      }
      Maybe> removed = this.children[subHash].remove(key, hash >>> 5);
      return removed.map(newChild -> {
        HashTable[] cloned = Fork.this.children.clone();
        cloned[subHash] = newChild;
        return new Fork<>(Fork.this.hasher, cloned, Fork.this.length - 1);
      });
    }

    @NotNull
    @Override
    protected Maybe get(@NotNull K key, int hash) {
      int subHash = hash & 31;
      if (this.children[subHash] == null) {
        return Maybe.nothing();
      }
      return this.children[subHash].get(key, hash >>> 5);
    }

    @NotNull
    @Override
    public Fork merge(@NotNull HashTable tree, @NotNull F2 merger) {
      if (tree instanceof Empty) {
        return this;
      } else if (tree instanceof Leaf) {
        Leaf leaf = (Leaf) tree;
        return this.mergeFork(leaf.toFork(), merger);
      }
      return this.mergeFork(((Fork) tree), merger);
    }

    @NotNull
    private Fork mergeFork(@NotNull Fork tree, @NotNull F2 merger) {
      // Mutable array.
      HashTable[] cloned = Fork.this.children.clone();
      int count = 0;
      for (int i = 0; i < cloned.length; i++) {
        if (cloned[i] == null) {
          cloned[i] = tree.children[i];
        } else if (tree.children[i] != null) {
          cloned[i] = cloned[i].merge(tree.children[i], merger);
        }
        if (cloned[i] != null) {
          count += cloned[i].length;
        }
      }
      return new Fork<>(this.hasher, cloned, count);
    }

    @NotNull
    @Override
    public  A foldLeft(@NotNull F2, A> f, @NotNull A init) {
      for (@Nullable HashTable child : this.children) {
        if (child != null) {
          init = child.foldLeft(f, init);
        }
      }
      return init;
    }

    @NotNull
    @Override
    public  A foldRight(@NotNull F2, A, A> f, @NotNull A init) {
      for (int i = this.children.length - 1; i >= 0; i--) {
        if (this.children[i] == null) {
          continue;
        }
        init = this.children[i].foldRight(f, init);
      }
      return init;
    }

    @Override
    public void foreach(@NotNull Effect> e) {
      for (@Nullable HashTable child : this.children) {
        if (child != null) {
          child.foreach(e);
        }
      }
    }

    @NotNull
    @Override
    public Maybe> find(@NotNull F, Boolean> f) {
      HashTable[] children = this.children;
      for (int i = 0, ln = children.length; i < ln; i++) {
        HashTable child = children[i];
        if (child != null) {
          Maybe> p = child.find(f);
          if (p.isJust()) {
            return p;
          }
        }
      }
      return Maybe.nothing();
    }

    @NotNull
    @Override
    public  Maybe findMap(@NotNull F, Maybe> f) {
      HashTable[] children = this.children;
      for (int i = 0, ln = children.length; i < ln; i++) {
        HashTable child = children[i];
        if (child != null) {
          Maybe p = child.findMap(f);
          if (p.isJust()) {
            return p;
          }
        }
      }
      return Maybe.nothing();
    }

    @SuppressWarnings("unchecked")
    @Override
    public  Fork map(@NotNull F f) {
      HashTable[] clone = new HashTable[this.children.length];
      for (int i = 0; i < clone.length; i++) {
        if (this.children[i] != null) {
          clone[i] = this.children[i].map(f);
        }
      }
      return new Fork<>(hasher, clone, length);
    }
  }
}