pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / contrib / facet / src / java / org / apache / lucene / facet / taxonomy / writercache / cl2o / CollisionMap.java
diff --git a/lucene-java-3.5.0/lucene/contrib/facet/src/java/org/apache/lucene/facet/taxonomy/writercache/cl2o/CollisionMap.java b/lucene-java-3.5.0/lucene/contrib/facet/src/java/org/apache/lucene/facet/taxonomy/writercache/cl2o/CollisionMap.java
new file mode 100644 (file)
index 0000000..247de72
--- /dev/null
@@ -0,0 +1,267 @@
+package org.apache.lucene.facet.taxonomy.writercache.cl2o;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.apache.lucene.facet.taxonomy.CategoryPath;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+/**
+ * HashMap to store colliding labels. See {@link CompactLabelToOrdinal} for
+ * details.
+ * 
+ * @lucene.experimental
+ */
+public class CollisionMap {
+
+  private int capacity;
+  private float loadFactor;
+  private int size;
+  private int threshold;
+
+  static class Entry {
+    int offset;
+    int cid;
+    Entry next;
+    int hash;
+
+    Entry(int offset, int cid, int h, Entry e) {
+      this.offset = offset;
+      this.cid = cid;
+      this.next = e;
+      this.hash = h;
+    }
+  }
+
+  private CharBlockArray labelRepository;
+
+  private Entry[] entries;
+
+  public CollisionMap(CharBlockArray labelRepository) {
+    this(16 * 1024, 0.75f, labelRepository);
+  }
+
+  public CollisionMap(int initialCapacity, CharBlockArray labelRepository) {
+    this(initialCapacity, 0.75f, labelRepository);
+  }
+
+  private CollisionMap(int initialCapacity, float loadFactor, CharBlockArray labelRepository) {
+    this.labelRepository = labelRepository;
+    this.loadFactor = loadFactor;
+    this.capacity = CompactLabelToOrdinal.determineCapacity(2, initialCapacity);
+
+    this.entries = new Entry[this.capacity];
+    this.threshold = (int) (this.capacity * this.loadFactor);
+  }
+
+  public int size() {
+    return this.size;
+  }
+
+  public int capacity() {
+    return this.capacity;
+  }
+
+  private void grow() {
+    int newCapacity = this.capacity * 2;
+    Entry[] newEntries = new Entry[newCapacity];
+    Entry[] src = this.entries;
+
+    for (int j = 0; j < src.length; j++) {
+      Entry e = src[j];
+      if (e != null) {
+        src[j] = null;
+        do {
+          Entry next = e.next;
+          int hash = e.hash;
+          int i = indexFor(hash, newCapacity);  
+          e.next = newEntries[i];
+          newEntries[i] = e;
+          e = next;
+        } while (e != null);
+      }
+    }
+
+    this.capacity = newCapacity;
+    this.entries = newEntries;
+    this.threshold = (int) (this.capacity * this.loadFactor);
+  }
+
+  public int get(CategoryPath label, int hash) {
+    int bucketIndex = indexFor(hash, this.capacity);
+    Entry e = this.entries[bucketIndex];
+
+    while (e != null && !(hash == e.hash && label.equalsToSerialized(this.labelRepository, e.offset))) { 
+      e = e.next;
+    }
+    if (e == null) {
+      return LabelToOrdinal.InvalidOrdinal;
+    }
+
+    return e.cid;
+  }
+
+  public int get(CategoryPath label, int prefixLen, int hash) {
+    int bucketIndex = indexFor(hash, this.capacity);
+    Entry e = this.entries[bucketIndex];
+
+    while (e != null && !(hash == e.hash && label.equalsToSerialized(prefixLen, this.labelRepository, e.offset))) { 
+      e = e.next;
+    }
+    if (e == null) {
+      return LabelToOrdinal.InvalidOrdinal;
+    }
+
+    return e.cid;
+  }
+
+  public int addLabel(CategoryPath label, int hash, int cid) {
+    int bucketIndex = indexFor(hash, this.capacity);
+    for (Entry e = this.entries[bucketIndex]; e != null; e = e.next) {
+      if (e.hash == hash && label.equalsToSerialized(this.labelRepository, e.offset)) {
+        return e.cid;
+      }
+    }
+
+    // new string; add to label repository
+    int offset = this.labelRepository.length();
+    try {
+      label.serializeAppendTo(labelRepository);
+    } catch (IOException e) {
+      // can't happen, because labelRepository.append() doesn't throw an exception
+    }
+
+    addEntry(offset, cid, hash, bucketIndex);
+    return cid;
+  }
+
+  public int addLabel(CategoryPath label, int prefixLen, int hash, int cid) {
+    int bucketIndex = indexFor(hash, this.capacity);
+    for (Entry e = this.entries[bucketIndex]; e != null; e = e.next) {
+      if (e.hash == hash && label.equalsToSerialized(prefixLen, this.labelRepository, e.offset)) {
+        return e.cid;
+      }
+    }
+
+    // new string; add to label repository
+    int offset = this.labelRepository.length();
+    try {
+      label.serializeAppendTo(prefixLen, labelRepository);
+    } catch (IOException e) {
+      // can't happen, because labelRepository.append() doesn't throw an exception
+    }
+
+    addEntry(offset, cid, hash, bucketIndex);
+    return cid;
+  }
+
+  /**
+   * This method does not check if the same value is already
+   * in the map because we pass in an char-array offset, so
+   * so we now that we're in resize-mode here. 
+   */
+  public void addLabelOffset(int hash, int offset, int cid) {
+    int bucketIndex = indexFor(hash, this.capacity);
+    addEntry(offset, cid, hash, bucketIndex);
+  }
+
+  private void addEntry(int offset, int cid, int hash, int bucketIndex) {
+    Entry e = this.entries[bucketIndex];
+    this.entries[bucketIndex] = new Entry(offset, cid, hash, e);
+    if (this.size++ >= this.threshold) {
+      grow();
+    }
+  }
+
+  Iterator<CollisionMap.Entry> entryIterator() {
+    return new EntryIterator(entries, size);
+  }
+
+  /**
+   * Returns index for hash code h. 
+   */
+  static int indexFor(int h, int length) {
+    return h & (length - 1);
+  }
+
+  /**
+   * Returns an estimate of the memory usage of this CollisionMap.
+   * @return The approximate number of bytes used by this structure.
+   */
+  int getMemoryUsage() {
+    int memoryUsage = 0;
+    if (this.entries != null) {
+      for (Entry e : this.entries) {
+        if (e != null) {
+          memoryUsage += (4 * 4);
+          for (Entry ee = e.next; ee != null; ee = ee.next) {
+            memoryUsage += (4 * 4);
+          }
+        }
+      }
+    }
+    return memoryUsage;
+  }
+
+  private class EntryIterator implements Iterator<Entry> {
+    Entry next;    // next entry to return
+    int index;        // current slot 
+    Entry[] ents;
+    
+    EntryIterator(Entry[] entries, int size) {
+      this.ents = entries;
+      Entry[] t = entries;
+      int i = t.length;
+      Entry n = null;
+      if (size != 0) { // advance to first entry
+        while (i > 0 && (n = t[--i]) == null) {
+          // advance
+        }
+      }
+      this.next = n;
+      this.index = i;
+    }
+
+    public boolean hasNext() {
+      return this.next != null;
+    }
+
+    public Entry next() { 
+      Entry e = this.next;
+      if (e == null) throw new NoSuchElementException();
+
+      Entry n = e.next;
+      Entry[] t = ents;
+      int i = this.index;
+      while (n == null && i > 0) {
+        n = t[--i];
+      }
+      this.index = i;
+      this.next = n;
+      return  e;
+    }
+
+    public void remove() {
+      throw new UnsupportedOperationException();
+    }
+
+  }
+
+}