package divconq.db.rocks.keyquery;
import divconq.db.Constants;
import divconq.db.util.ByteUtil;
import divconq.lang.Memory;
import divconq.util.ArrayUtil;
// any key this level
public class WildcardKeyLevel extends KeyLevel {
protected byte[] last = Constants.DB_ALPHA_MARKER_ARRAY;
protected boolean skipchild = false;
protected boolean isFinalWild = false;
public void setFinalWild(boolean v) {
this.isFinalWild = v;
}
@Override
public int compare(byte[] key, int offset, boolean browseMode, Memory browseKey) {
byte[] old = this.last;
this.last = Constants.DB_ALPHA_MARKER_ARRAY;
this.skipchild = false;
int mlen = 0;
// the only type allowed to contain zeros is Number, it is fixed
if (key[offset] == Constants.DB_TYPE_NUMBER) {
mlen = 18;
}
else {
for (int i = offset; i < key.length; i++) {
if (key[i] == Constants.DB_TYPE_MARKER_ALPHA)
break;
mlen++;
}
}
if (offset + mlen > key.length) {
this.resetLast();
return -1;
}
// can be nothing past me if there is no `next`
if ((this.next == null) && (key.length > offset + mlen)) {
if (!browseMode)
return 1;
if (ByteUtil.keyContains(key, offset, old, mlen)) {
this.last = new byte[mlen + 1]; // one longer and set last is 0x00
ArrayUtil.blockCopy(key, offset, this.last, 0, mlen);
this.last[this.last.length - 1] = 0x01;
return 1;
}
// treat as match - need special handling
this.last = new byte[mlen]; // exact match
ArrayUtil.blockCopy(key, offset, this.last, 0, mlen);
browseKey.setLength(offset + mlen);
return 0;
}
if (this.next == null) {
this.last = new byte[mlen + 1]; // one longer and set last is 0x00
ArrayUtil.blockCopy(key, offset, this.last, 0, mlen);
this.last[this.last.length - 1] = 0x01;
return 0;
}
// look for field separator - check length
if (offset + mlen + 1 > key.length) {
this.last = new byte[mlen]; // exact match
ArrayUtil.blockCopy(key, offset, this.last, 0, mlen);
this.next.resetLast();
return -1;
}
// look for field separator - check byte
if (Constants.DB_TYPE_MARKER_ALPHA != key[offset + mlen]) {
this.next.resetLast();
return 1;
}
int ret = this.next.compare(key, offset + mlen + 1, browseMode, browseKey);
// when there is a child in the chain, only the final wild can peek forward
if ((ret <= 0) || !this.isFinalWild) {
this.last = new byte[mlen]; // exact match
ArrayUtil.blockCopy(key, offset, this.last, 0, mlen);
}
else {
this.last = new byte[mlen + 1]; // one longer and set last is 0x00
ArrayUtil.blockCopy(key, offset, this.last, 0, mlen);
this.last[this.last.length - 1] = 0x01;
this.skipchild = true;
}
return ret;
}
@Override
public void resetLast() {
this.last = Constants.DB_ALPHA_MARKER_ARRAY;
this.skipchild = false;
if (this.next != null)
this.next.resetLast();
}
@Override
public void buildSeek(Memory mem) {
mem.write(this.last); // last byte has a trailing 1 (in addition to ALPHA) forcing the next key to be sought
if (!this.skipchild && (this.next != null)) {
mem.writeByte(Constants.DB_TYPE_MARKER_ALPHA);
this.next.buildSeek(mem);
}
}
}