package org.apache.lucene.index;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.util.Accountable;
import org.apache.lucene.util.BitSet;
import org.apache.lucene.util.BitSetIterator;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.IntroSorter;
import org.apache.lucene.util.PriorityQueue;
import org.apache.lucene.util.RamUsageEstimator;
import org.apache.lucene.util.SparseFixedBitSet;
import org.apache.lucene.util.packed.PackedInts;
import org.apache.lucene.util.packed.PagedMutable;
import static org.apache.lucene.search.DocIdSetIterator.NO_MORE_DOCS;
abstract class DocValuesFieldUpdates implements Accountable {
protected static final int PAGE_SIZE = 1024;
private static final long HAS_VALUE_MASK = 1;
private static final long HAS_NO_VALUE_MASK = 0;
private static final int SHIFT = 1;
static abstract class Iterator extends DocValuesIterator {
@Override
public final boolean advanceExact(int target) {
throw new UnsupportedOperationException();
}
@Override
public final int advance(int target) {
throw new UnsupportedOperationException();
}
@Override
public final long cost() {
throw new UnsupportedOperationException();
}
@Override
public abstract int nextDoc();
abstract long longValue();
abstract BytesRef binaryValue();
abstract long delGen();
abstract boolean hasValue();
static BinaryDocValues asBinaryDocValues(Iterator iterator) {
return new BinaryDocValues() {
@Override
public int docID() {
return iterator.docID();
}
@Override
public BytesRef binaryValue() {
return iterator.binaryValue();
}
@Override
public boolean advanceExact(int target) {
return iterator.advanceExact(target);
}
@Override
public int nextDoc() {
return iterator.nextDoc();
}
@Override
public int advance(int target) {
return iterator.advance(target);
}
@Override
public long cost() {
return iterator.cost();
}
};
}
static NumericDocValues asNumericDocValues(Iterator iterator) {
return new NumericDocValues() {
@Override
public long longValue() {
return iterator.longValue();
}
@Override
public boolean advanceExact(int target) {
throw new UnsupportedOperationException();
}
@Override
public int docID() {
return iterator.docID();
}
@Override
public int nextDoc() {
return iterator.nextDoc();
}
@Override
public int advance(int target) {
return iterator.advance(target);
}
@Override
public long cost() {
return iterator.cost();
}
};
}
}
public static Iterator mergedIterator(Iterator[] subs) {
if (subs.length == 1) {
return subs[0];
}
PriorityQueue<Iterator> queue = new PriorityQueue<Iterator>(subs.length) {
@Override
protected boolean lessThan(Iterator a, Iterator b) {
int cmp = Integer.compare(a.docID(), b.docID());
if (cmp == 0) {
cmp = Long.compare(b.delGen(), a.delGen());
assert cmp != 0;
}
return cmp < 0;
}
};
for (Iterator sub : subs) {
if (sub.nextDoc() != NO_MORE_DOCS) {
queue.add(sub);
}
}
if (queue.size() == 0) {
return null;
}
return new Iterator() {
private int doc = -1;
@Override
public int nextDoc() {
while (true) {
if (queue.size() == 0) {
doc = NO_MORE_DOCS;
break;
}
int newDoc = queue.top().docID();
if (newDoc != doc) {
assert newDoc > doc: "doc=" + doc + " newDoc=" + newDoc;
doc = newDoc;
break;
}
if (queue.top().nextDoc() == NO_MORE_DOCS) {
queue.pop();
} else {
queue.updateTop();
}
}
return doc;
}
@Override
public int docID() {
return doc;
}
@Override
long longValue() {
return queue.top().longValue();
}
@Override
BytesRef binaryValue() {
return queue.top().binaryValue();
}
@Override
public long delGen() {
throw new UnsupportedOperationException();
}
@Override
boolean hasValue() {
return queue.top().hasValue();
}
};
}
final String field;
final DocValuesType type;
final long delGen;
private final int bitsPerValue;
private boolean finished;
protected final int maxDoc;
protected PagedMutable docs;
protected int size;
protected DocValuesFieldUpdates(int maxDoc, long delGen, String field, DocValuesType type) {
this.maxDoc = maxDoc;
this.delGen = delGen;
this.field = field;
if (type == null) {
throw new NullPointerException("DocValuesType must not be null");
}
this.type = type;
bitsPerValue = PackedInts.bitsRequired(maxDoc - 1) + SHIFT;
docs = new PagedMutable(1, PAGE_SIZE, bitsPerValue, PackedInts.DEFAULT);
}
final boolean getFinished() {
return finished;
}
abstract void add(int doc, long value);
abstract void add(int doc, BytesRef value);
abstract void add(int docId, Iterator iterator);
abstract Iterator iterator();
final synchronized void finish() {
if (finished) {
throw new IllegalStateException("already finished");
}
finished = true;
if (size < docs.size()) {
resize(size);
}
if (size > 0) {
final PackedInts.Mutable ords = PackedInts.getMutable(size, PackedInts.bitsRequired(size - 1), PackedInts.DEFAULT);
for (int i = 0; i < size; ++i) {
ords.set(i, i);
}
new IntroSorter() {
@Override
protected void swap(int i, int j) {
final long tmpOrd = ords.get(i);
ords.set(i, ords.get(j));
ords.set(j, tmpOrd);
DocValuesFieldUpdates.this.swap(i, j);
}
@Override
protected int compare(int i, int j) {
int cmp = Long.compare(docs.get(i)>>>1, docs.get(j)>>>1);
if (cmp == 0) {
cmp = (int) (ords.get(i) - ords.get(j));
}
return cmp;
}
long pivotDoc;
int pivotOrd;
@Override
protected void setPivot(int i) {
pivotDoc = docs.get(i) >>> 1;
pivotOrd = (int) ords.get(i);
}
@Override
protected int comparePivot(int j) {
int cmp = Long.compare(pivotDoc, docs.get(j) >>> 1);
if (cmp == 0) {
cmp = pivotOrd - (int) ords.get(j);
}
return cmp;
}
}.sort(0, size);
}
}
synchronized boolean any() {
return size > 0;
}
synchronized final int size() {
return size;
}
synchronized void reset(int doc) {
addInternal(doc, HAS_NO_VALUE_MASK);
}
final synchronized int add(int doc) {
return addInternal(doc, HAS_VALUE_MASK);
}
private synchronized int addInternal(int doc, long hasValueMask) {
if (finished) {
throw new IllegalStateException("already finished");
}
assert doc < maxDoc;
if (size == Integer.MAX_VALUE) {
throw new IllegalStateException("cannot support more than Integer.MAX_VALUE doc/value entries");
}
if (docs.size() == size) {
grow(size+1);
}
docs.set(size, (((long)doc) << SHIFT) | hasValueMask);
++size;
return size-1;
}
protected void swap(int i, int j) {
long tmpDoc = docs.get(j);
docs.set(j, docs.get(i));
docs.set(i, tmpDoc);
}
protected void grow(int size) {
docs = docs.grow(size);
}
protected void resize(int size) {
docs = docs.resize(size);
}
protected final void ensureFinished() {
if (finished == false) {
throw new IllegalStateException("call finish first");
}
}
@Override
public long ramBytesUsed() {
return docs.ramBytesUsed()
+ RamUsageEstimator.NUM_BYTES_OBJECT_HEADER
+ 2 * Integer.BYTES
+ 2 + Long.BYTES
+ RamUsageEstimator.NUM_BYTES_OBJECT_REF;
}
protected abstract static class AbstractIterator extends DocValuesFieldUpdates.Iterator {
private final int size;
private final PagedMutable docs;
private long idx = 0;
private int doc = -1;
private final long delGen;
private boolean hasValue;
AbstractIterator(int size, PagedMutable docs, long delGen) {
this.size = size;
this.docs = docs;
this.delGen = delGen;
}
@Override
public final int nextDoc() {
if (idx >= size) {
return doc = DocIdSetIterator.NO_MORE_DOCS;
}
long longDoc = docs.get(idx);
++idx;
for (; idx < size; idx++) {
final long nextLongDoc = docs.get(idx);
if ((longDoc >>> 1) != (nextLongDoc >>> 1)) {
break;
}
longDoc = nextLongDoc;
}
hasValue = (longDoc & HAS_VALUE_MASK) > 0;
if (hasValue) {
set(idx - 1);
}
doc = (int)(longDoc >> SHIFT);
return doc;
}
protected abstract void set(long idx);
@Override
public final int docID() {
return doc;
}
@Override
final long delGen() {
return delGen;
}
@Override
final boolean hasValue() {
return hasValue;
}
}
static abstract class SingleValueDocValuesFieldUpdates extends DocValuesFieldUpdates {
private final BitSet bitSet;
private BitSet hasNoValue;
private boolean hasAtLeastOneValue;
protected SingleValueDocValuesFieldUpdates(int maxDoc, long delGen, String field, DocValuesType type) {
super(maxDoc, delGen, field, type);
this.bitSet = new SparseFixedBitSet(maxDoc);
}
@Override
void add(int doc, long value) {
assert longValue() == value;
bitSet.set(doc);
this.hasAtLeastOneValue = true;
if (hasNoValue != null) {
hasNoValue.clear(doc);
}
}
@Override
void add(int doc, BytesRef value) {
assert binaryValue().equals(value);
bitSet.set(doc);
this.hasAtLeastOneValue = true;
if (hasNoValue != null) {
hasNoValue.clear(doc);
}
}
@Override
synchronized void reset(int doc) {
bitSet.set(doc);
this.hasAtLeastOneValue = true;
if (hasNoValue == null) {
hasNoValue = new SparseFixedBitSet(maxDoc);
}
hasNoValue.set(doc);
}
@Override
void add(int docId, Iterator iterator) {
throw new UnsupportedOperationException();
}
protected abstract BytesRef binaryValue();
protected abstract long longValue();
@Override
synchronized boolean any() {
return super.any() || hasAtLeastOneValue;
}
@Override
public long ramBytesUsed() {
return super.ramBytesUsed() + bitSet.ramBytesUsed() + (hasNoValue == null ? 0 : hasNoValue.ramBytesUsed());
}
@Override
Iterator iterator() {
BitSetIterator iterator = new BitSetIterator(bitSet, maxDoc);
return new DocValuesFieldUpdates.Iterator() {
@Override
public int docID() {
return iterator.docID();
}
@Override
public int nextDoc() {
return iterator.nextDoc();
}
@Override
long longValue() {
return SingleValueDocValuesFieldUpdates.this.longValue();
}
@Override
BytesRef binaryValue() {
return SingleValueDocValuesFieldUpdates.this.binaryValue();
}
@Override
long delGen() {
return delGen;
}
@Override
boolean hasValue() {
if (hasNoValue != null) {
return hasNoValue.get(docID()) == false;
}
return true;
}
};
}
}
}