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

com.squarespace.less.exec.CssSet Maven / Gradle / Ivy

The newest version!
package com.squarespace.less.exec;

import java.util.Iterator;

/**
 * Implementation of a set that maintains insertion order without removal.
 */
public class CssSet implements Iterable {

  /**
   * Initial capacity of the set by default. MUST be a power of 2.
   */
  private static final int DEFAULT_CAPACITY = 32;

  /**
   * Percent of free slots we maintain.
   */
  private static final float DEFAULT_LOAD_FACTOR = 0.75f;

  /**
   * Head of the ordered linked list of nodes.
   */
  private Node head;

  /**
   * Tail of the ordered linked list of nodes.
   */
  private Node tail;

  /**
   * Hash table of nodes.
   */
  private Node[] table;

  /**
   * Bit mask for mapping an element's hash code to its position in the table.
   */
  private int mask;

  /**
   * Number of elements in the table.
   */
  private int size;

  /**
   * Construct a CssSet with the default initial capacity.
   */
  public CssSet() {
    this(DEFAULT_CAPACITY);
  }

  /**
   * Construct a CssSet with the given initial capacity.
   */
  @SuppressWarnings("unchecked")
  public CssSet(int capacity) {
    Node root = new Node<>(null);
    root.next = root;
    root.prev = root;
    this.head = root;
    this.tail = root;
    this.table = new Node[capacity];
    this.mask = table.length - 1;
    this.size = 0;
  }

  /**
   * Number of elements in the set.
   */
  public int size() {
    return size;
  }

  /**
   * Add a new element to the set. If the element already exists in the
   * set, move it to the end of the ordered list of nodes.
   */
  public void add(E elem) {
    grow();
    int ix = locate(elem);
    Node e = null;
    if (table[ix] == null) {
      // Insert the element and increase set size
      e = new Node<>(elem);
      size++;
      table[ix] = e;

    } else {
      // Element exists, so remove it
      e = table[ix];
      this.remove(e);
    }

    // Insert / move the element to the end of the ordered list
    this.insertBefore(e, this.tail);
  }

  @Override
  public Iterator iterator() {
    return new CssSetIterator(this.head.next);
  }

  @Override
  public String toString() {
    StringBuilder buf = new StringBuilder();
    Node n = this.head.next;
    while (n != this.tail) {
      if (buf.length() > 0) {
        buf.append(" ");
      }
      buf.append(n.elem);
      n = n.next;
    }
    return buf.toString();
  }

  /**
   * Locate an element's index in the table.
   */
  private int locate(E elem) {
    int ix = hash(elem.hashCode()) & mask;
    while (table[ix] != null) {
      if (elem.equals(table[ix].elem)) {
        return ix;
      }
      ix = (ix + 1) & mask;
    }
    return ix;
  }

  /**
   * Insert 'e' node before the 'at' node in the list.
   */
  private Node insertBefore(Node e, Node at) {
    Node n = at.prev;
    at.prev = e;
    e.prev = n;
    e.next = at;
    n.next = e;
    return e;
  }

  /**
   * Remove 'n' node from the list.
   */
  private Node remove(Node n) {
    n.prev.next = n.next;
    n.next.prev = n.prev;
    return n;
  }

  /**
   * Grow the table capacity by a factor of 2 to ensure its
   * size is always below load factor threshold.
   */
  @SuppressWarnings("unchecked")
  private void grow() {
    // Check if storage is big enough
    if (size < Math.floor(table.length * DEFAULT_LOAD_FACTOR)) {
      return;
    }

    // Double the table size
    Node[] old = table;
    table = new Node[table.length * 2];


    // Rehash the contents to new locations
    int newmask = table.length - 1;
    for (int i = old.length - 1; i >= 0; i--) {
      Node node = old[i];
      if (node != null) {
        int ix = hash(node.elem.hashCode()) & newmask;
        while (table[ix] != null) {
          ix = (ix + 1) & newmask;
        }
        table[ix] = node;
      }
    }
    this.mask = newmask;
  }

  /**
   * Murmur3 32-bit hash.
   */
  static int hash(int key) {
    key ^= key >>> 16;
    key *= 0x85ebca6b;
    key ^= key >>> 13;
    key *= 0xc2b2ae35;
    key ^= key >>> 16;
    return key;
  }

  /**
   * Iterate over the linked list of nodes until we hit the root/tail.
   */
  public static class CssSetIterator implements Iterator {

    private Node curr;

    private CssSetIterator(Node node) {
      this.curr = node;
    }

    @Override
    public boolean hasNext() {
      return this.curr.elem != null;
    }

    @Override
    public E next() {
      E elem = this.curr.elem;
      this.curr = this.curr.next;
      return elem;
    }
  }

  /**
   * Internal node in our table / ordered list.
   */
  private static class Node {
    final E elem;
    Node prev;
    Node next;

    Node(E elem) {
      this.elem = elem;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy