/* * 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. */ package org.apache.lucene.codecs.blocktreeords; import java.io.IOException; import org.apache.lucene.store.DataInput; import org.apache.lucene.store.DataOutput; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.RamUsageEstimator; import org.apache.lucene.util.StringHelper; import org.apache.lucene.util.fst.Outputs; /** A custom FST outputs implementation that stores block data * (BytesRef), long ordStart, long numTerms. */ final class FSTOrdsOutputs extends Outputs<FSTOrdsOutputs.Output> { public static final Output NO_OUTPUT = new Output(new BytesRef(), 0, 0); private static final BytesRef NO_BYTES = new BytesRef(); public static final class Output { public final BytesRef bytes; // Inclusive: public final long startOrd; // Inclusive: public final long endOrd; public Output(BytesRef bytes, long startOrd, long endOrd) { assert startOrd >= 0: "startOrd=" + startOrd; assert endOrd >= 0: "endOrd=" + endOrd; this.bytes = bytes; this.startOrd = startOrd; this.endOrd = endOrd; } @Override public String toString() { long x; if (endOrd > Long.MAX_VALUE/2) { x = Long.MAX_VALUE-endOrd; } else { assert endOrd >= 0; x = -endOrd; } return startOrd + " to " + x; } @Override public int hashCode() { int hash = bytes.hashCode(); hash = (int) (hash ^ startOrd); hash = (int) (hash ^ endOrd); return hash; } @Override public boolean equals(Object _other) { if (_other instanceof Output) { Output other = (Output) _other; return bytes.equals(other.bytes) && startOrd == other.startOrd && endOrd == other.endOrd; } else { return false; } } } @Override public Output common(Output output1, Output output2) { BytesRef bytes1 = output1.bytes; BytesRef bytes2 = output2.bytes; assert bytes1 != null; assert bytes2 != null; int pos1 = bytes1.offset; int pos2 = bytes2.offset; int stopAt1 = pos1 + Math.min(bytes1.length, bytes2.length); while(pos1 < stopAt1) { if (bytes1.bytes[pos1] != bytes2.bytes[pos2]) { break; } pos1++; pos2++; } BytesRef prefixBytes; if (pos1 == bytes1.offset) { // no common prefix prefixBytes = NO_BYTES; } else if (pos1 == bytes1.offset + bytes1.length) { // bytes1 is a prefix of bytes2 prefixBytes = bytes1; } else if (pos2 == bytes2.offset + bytes2.length) { // bytes2 is a prefix of bytes1 prefixBytes = bytes2; } else { prefixBytes = new BytesRef(bytes1.bytes, bytes1.offset, pos1-bytes1.offset); } return newOutput(prefixBytes, Math.min(output1.startOrd, output2.startOrd), Math.min(output1.endOrd, output2.endOrd)); } @Override public Output subtract(Output output, Output inc) { assert output != null; assert inc != null; if (inc == NO_OUTPUT) { // no prefix removed return output; } else { assert StringHelper.startsWith(output.bytes, inc.bytes); BytesRef suffix; if (inc.bytes.length == output.bytes.length) { // entire output removed suffix = NO_BYTES; } else if (inc.bytes.length == 0) { suffix = output.bytes; } else { assert inc.bytes.length < output.bytes.length: "inc.length=" + inc.bytes.length + " vs output.length=" + output.bytes.length; assert inc.bytes.length > 0; suffix = new BytesRef(output.bytes.bytes, output.bytes.offset + inc.bytes.length, output.bytes.length-inc.bytes.length); } assert output.startOrd >= inc.startOrd; assert output.endOrd >= inc.endOrd; return newOutput(suffix, output.startOrd-inc.startOrd, output.endOrd - inc.endOrd); } } @Override public Output add(Output prefix, Output output) { assert prefix != null; assert output != null; if (prefix == NO_OUTPUT) { return output; } else if (output == NO_OUTPUT) { return prefix; } else { BytesRef bytes = new BytesRef(prefix.bytes.length + output.bytes.length); System.arraycopy(prefix.bytes.bytes, prefix.bytes.offset, bytes.bytes, 0, prefix.bytes.length); System.arraycopy(output.bytes.bytes, output.bytes.offset, bytes.bytes, prefix.bytes.length, output.bytes.length); bytes.length = prefix.bytes.length + output.bytes.length; return newOutput(bytes, prefix.startOrd + output.startOrd, prefix.endOrd + output.endOrd); } } @Override public void write(Output prefix, DataOutput out) throws IOException { out.writeVInt(prefix.bytes.length); out.writeBytes(prefix.bytes.bytes, prefix.bytes.offset, prefix.bytes.length); out.writeVLong(prefix.startOrd); out.writeVLong(prefix.endOrd); } @Override public Output read(DataInput in) throws IOException { int len = in.readVInt(); BytesRef bytes; if (len == 0) { bytes = NO_BYTES; } else { bytes = new BytesRef(len); in.readBytes(bytes.bytes, 0, len); bytes.length = len; } long startOrd = in.readVLong(); long endOrd = in.readVLong(); Output result = newOutput(bytes, startOrd, endOrd); return result; } @Override public void skipOutput(DataInput in) throws IOException { int len = in.readVInt(); in.skipBytes(len); in.readVLong(); in.readVLong(); } @Override public void skipFinalOutput(DataInput in) throws IOException { skipOutput(in); } @Override public Output getNoOutput() { return NO_OUTPUT; } @Override public String outputToString(Output output) { if ((output.endOrd == 0 || output.endOrd == Long.MAX_VALUE) && output.startOrd == 0) { return ""; } else { return output.toString(); } } public Output newOutput(BytesRef bytes, long startOrd, long endOrd) { if (bytes.length == 0 && startOrd == 0 && endOrd == 0) { return NO_OUTPUT; } else { return new Output(bytes, startOrd, endOrd); } } @Override public long ramBytesUsed(Output output) { return 2 * RamUsageEstimator.NUM_BYTES_OBJECT_HEADER + 2 * Long.BYTES + 2 * RamUsageEstimator.NUM_BYTES_OBJECT_REF + RamUsageEstimator.NUM_BYTES_ARRAY_HEADER + 2 * Integer.BYTES + output.bytes.length; } }