// ObjectIndexCache.java // (C) 2008 by Michael Peter Christen; mc@yacy.net, Frankfurt a. M., Germany // first published 07.01.2008 on http://yacy.net // // $LastChangedDate: 2006-04-02 22:40:07 +0200 (So, 02 Apr 2006) $ // $LastChangedRevision: 1986 $ // $LastChangedBy: orbiter $ // // LICENSE // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package net.yacy.kelondro.index; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import net.yacy.kelondro.index.Row.Entry; import net.yacy.kelondro.order.CloneableIterator; import net.yacy.kelondro.order.MergeIterator; import net.yacy.kelondro.order.StackIterator; public final class ObjectIndexCache implements ObjectIndex, Iterable { private final Row rowdef; private RowSet index0; private RowSetArray index1; private final Row.EntryComparator entryComparator; private final int spread; public ObjectIndexCache(final Row rowdef, final int initialspace, final int expectedspace) { this.rowdef = rowdef; this.entryComparator = new Row.EntryComparator(rowdef.objectOrder); this.spread = Math.max(10, expectedspace / 3000); reset(initialspace); } public void clear() { reset(0); } public final synchronized void reset(final int initialspace) { this.index0 = null; // first flush RAM to make room this.index0 = new RowSet(rowdef, initialspace); this.index1 = null; // to show that this is the initialization phase } public final Row row() { return index0.row(); } protected final void finishInitialization() { if (index1 == null) { // finish initialization phase index0.sort(); index0.uniq(); index0.trim(false); index1 = new RowSetArray(rowdef, 0, spread); } } public final synchronized byte[] smallestKey() { byte[] b0 = index0.smallestKey(); if (b0 == null) return null; if (index1 == null) return b0; byte[] b1 = index0.smallestKey(); if (b1 == null || rowdef.objectOrder.compare(b1, b0) > 0) return b0; return b1; } public final synchronized byte[] largestKey() { byte[] b0 = index0.largestKey(); if (b0 == null) return null; if (index1 == null) return b0; byte[] b1 = index0.largestKey(); if (b1 == null || rowdef.objectOrder.compare(b0, b1) > 0) return b0; return b1; } public final synchronized Row.Entry get(final byte[] key) { assert (key != null); finishInitialization(); assert index0.isSorted(); final Row.Entry indexentry = index0.get(key); if (indexentry != null) return indexentry; return index1.get(key); } public final synchronized boolean has(final byte[] key) { assert (key != null); finishInitialization(); assert index0.isSorted(); if (index0.has(key)) return true; return index1.has(key); } public final synchronized Row.Entry replace(final Row.Entry entry) throws RowSpaceExceededException { assert (entry != null); finishInitialization(); // if the new entry is within the initialization part, just overwrite it assert index0.isSorted(); byte[] key = entry.getPrimaryKeyBytes(); if (index0.has(key)) { // replace the entry return index0.replace(entry); } // else place it in the index1 return index1.replace(entry); } public final synchronized void put(final Row.Entry entry) throws RowSpaceExceededException { assert (entry != null); if (entry == null) return; finishInitialization(); // if the new entry is within the initialization part, just overwrite it assert index0.isSorted(); byte[] key = entry.getPrimaryKeyBytes(); if (index0.has(key)) { // replace the entry index0.put(entry); } // else place it in the index1 index1.put(entry); } public final synchronized void addUnique(final Row.Entry entry) throws RowSpaceExceededException { assert (entry != null); if (entry == null) return; if (index1 == null) { // we are in the initialization phase index0.addUnique(entry); return; } // initialization is over, add to secondary index index1.addUnique(entry); } public final void addUnique(final List rows) throws RowSpaceExceededException { final Iterator i = rows.iterator(); while (i.hasNext()) addUnique(i.next()); } public final synchronized long inc(final byte[] key, int col, long add, Row.Entry initrow) throws RowSpaceExceededException { assert (key != null); finishInitialization(); assert index0.isSorted(); long l = index0.inc(key, col, add, null); if (l != Long.MIN_VALUE) return l; return index1.inc(key, col, add, initrow); } public final synchronized ArrayList removeDoubles() throws RowSpaceExceededException { // finish initialization phase explicitely index0.sort(); if (index1 == null) { return index0.removeDoubles(); } ArrayList d0 = index0.removeDoubles(); ArrayList d1 = index1.removeDoubles(); d0.addAll(d1); return d0; } public final synchronized Row.Entry remove(final byte[] key) { finishInitialization(); // if the new entry is within the initialization part, just delete it final Row.Entry indexentry = index0.remove(key); if (indexentry != null) { assert index0.get(key) == null; // check if remove worked return indexentry; } // else remove it from the index1 final Row.Entry removed = index1.remove(key); assert index1.get(key) == null : "removed " + ((removed == null) ? " is null" : " is not null") + ", and index entry still exists"; // check if remove worked return removed; } public final synchronized Row.Entry removeOne() { if (index1 != null && !index1.isEmpty()) { return index1.removeOne(); } if (index0 != null && !index0.isEmpty()) { return index0.removeOne(); } return null; } public final synchronized int size() { if (index0 != null && index1 == null) { return index0.size(); } if (index0 == null && index1 != null) { return index1.size(); } assert (index0 != null && index1 != null); return index0.size() + index1.size(); } public final synchronized boolean isEmpty() { if (index0 != null && index1 == null) { return index0.isEmpty(); } if (index0 == null && index1 != null) { return index1.isEmpty(); } assert (index0 != null && index1 != null); if (!index0.isEmpty()) return false; if (!index1.isEmpty()) return false; return true; } public final synchronized CloneableIterator keys(final boolean up, final byte[] firstKey) { // returns the key-iterator of the underlying kelondroIndex if (index1 == null) { // finish initialization phase index0.sort(); index0.uniq(); index1 = new RowSetArray(rowdef, 0, spread); return index0.keys(up, firstKey); } assert (index1 != null); if (index0 == null) { //assert consistencyAnalysis0() : "consistency problem: " + consistencyAnalysis(); return index1.keys(up, firstKey); } // index0 should be sorted // sort index1 to enable working of the merge iterator //assert consistencyAnalysis0() : "consistency problem: " + consistencyAnalysis(); CloneableIterator k0 = index0.keys(up, firstKey); CloneableIterator k1 = index1.keys(up, firstKey); if (k0 == null) return k1; if (k1 == null) return k0; return new MergeIterator( k0, k1, rowdef.objectOrder, MergeIterator.simpleMerge, true); } public final synchronized CloneableIterator rows(final boolean up, final byte[] firstKey) { // returns the row-iterator of the underlying kelondroIndex if (index1 == null) { // finish initialization phase index0.sort(); index0.uniq(); index1 = new RowSetArray(rowdef, 0, spread); return index0.rows(up, firstKey); } assert (index1 != null); if (index0 == null) { //assert consistencyAnalysis0() : "consistency problem: " + consistencyAnalysis(); return index1.rows(up, firstKey); } // index0 should be sorted // sort index1 to enable working of the merge iterator //index1.sort(); //assert consistencyAnalysis0() : "consistency problem: " + consistencyAnalysis(); CloneableIterator k0 = index0.rows(up, firstKey); CloneableIterator k1 = index1.rows(up, firstKey); if (k0 == null) return k1; if (k1 == null) return k0; return new MergeIterator( k0, k1, entryComparator, MergeIterator.simpleMerge, true); } public final Iterator iterator() { return rows(); } public final synchronized CloneableIterator rows() { // returns the row-iterator of the underlying kelondroIndex if (index1 == null) { // finish initialization phase index0.sort(); index0.uniq(); index1 = new RowSetArray(rowdef, 0, spread); return index0.rows(); } assert (index1 != null); if (index0 == null) { //assert consistencyAnalysis0() : "consistency problem: " + consistencyAnalysis(); return index1.rows(); } // index0 should be sorted // sort index1 to enable working of the merge iterator //index1.sort(); //assert consistencyAnalysis0() : "consistency problem: " + consistencyAnalysis(); return new StackIterator(index0.rows(), index1.rows()); } public final synchronized void close() { if (index0 != null) index0.close(); if (index1 != null) index1.close(); } public final String filename() { return null; // this does not have a file name } public final void deleteOnExit() { // do nothing, there is no file } }