| /* |
| * 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.util.fst; |
| |
| import java.io.IOException; |
| |
| import org.apache.lucene.store.DataInput; |
| import org.apache.lucene.store.DataOutput; |
| import org.apache.lucene.util.RamUsageEstimator; |
| import org.apache.lucene.util.SuppressForbidden; |
| |
| /** |
| * An FST {@link Outputs} implementation where each output |
| * is one or two non-negative long values. If it's a |
| * single output, Long is returned; else, TwoLongs. Order |
| * is preserved in the TwoLongs case, ie .first is the first |
| * input/output added to Builder, and .second is the |
| * second. You cannot store 0 output with this (that's |
| * reserved to mean "no output")! |
| * |
| * <p>NOTE: the only way to create a TwoLongs output is to |
| * add the same input to the FST twice in a row. This is |
| * how the FST maps a single input to two outputs (e.g. you |
| * cannot pass a TwoLongs to {@link Builder#add}. If you |
| * need more than two then use {@link ListOfOutputs}, but if |
| * you only have at most 2 then this implementation will |
| * require fewer bytes as it steals one bit from each long |
| * value. |
| * |
| * <p>NOTE: the resulting FST is not guaranteed to be minimal! |
| * See {@link Builder}. |
| * |
| * @lucene.experimental |
| */ |
| |
| @SuppressForbidden(reason = "Uses a Long instance as a marker") |
| public final class UpToTwoPositiveIntOutputs extends Outputs<Object> { |
| |
| /** Holds two long outputs. */ |
| public final static class TwoLongs { |
| public final long first; |
| public final long second; |
| |
| public TwoLongs(long first, long second) { |
| this.first = first; |
| this.second = second; |
| assert first >= 0; |
| assert second >= 0; |
| } |
| |
| @Override |
| public String toString() { |
| return "TwoLongs:" + first + "," + second; |
| } |
| |
| @Override |
| public boolean equals(Object _other) { |
| if (_other instanceof TwoLongs) { |
| final TwoLongs other = (TwoLongs) _other; |
| return first == other.first && second == other.second; |
| } else { |
| return false; |
| } |
| } |
| |
| @Override |
| public int hashCode() { |
| return (int) ((first^(first>>>32)) ^ (second^(second>>32))); |
| } |
| } |
| |
| private final static Long NO_OUTPUT = new Long(0); |
| |
| private final boolean doShare; |
| |
| private final static UpToTwoPositiveIntOutputs singletonShare = new UpToTwoPositiveIntOutputs(true); |
| private final static UpToTwoPositiveIntOutputs singletonNoShare = new UpToTwoPositiveIntOutputs(false); |
| |
| private UpToTwoPositiveIntOutputs(boolean doShare) { |
| this.doShare = doShare; |
| } |
| |
| public static UpToTwoPositiveIntOutputs getSingleton(boolean doShare) { |
| return doShare ? singletonShare : singletonNoShare; |
| } |
| |
| public Long get(long v) { |
| if (v == 0) { |
| return NO_OUTPUT; |
| } else { |
| return Long.valueOf(v); |
| } |
| } |
| |
| public TwoLongs get(long first, long second) { |
| return new TwoLongs(first, second); |
| } |
| |
| @Override |
| public Long common(Object _output1, Object _output2) { |
| assert valid(_output1, false); |
| assert valid(_output2, false); |
| final Long output1 = (Long) _output1; |
| final Long output2 = (Long) _output2; |
| if (output1 == NO_OUTPUT || output2 == NO_OUTPUT) { |
| return NO_OUTPUT; |
| } else if (doShare) { |
| assert output1 > 0; |
| assert output2 > 0; |
| return Math.min(output1, output2); |
| } else if (output1.equals(output2)) { |
| return output1; |
| } else { |
| return NO_OUTPUT; |
| } |
| } |
| |
| @Override |
| public Long subtract(Object _output, Object _inc) { |
| assert valid(_output, false); |
| assert valid(_inc, false); |
| final Long output = (Long) _output; |
| final Long inc = (Long) _inc; |
| assert output >= inc; |
| |
| if (inc == NO_OUTPUT) { |
| return output; |
| } else if (output.equals(inc)) { |
| return NO_OUTPUT; |
| } else { |
| return output - inc; |
| } |
| } |
| |
| @Override |
| public Object add(Object _prefix, Object _output) { |
| assert valid(_prefix, false); |
| assert valid(_output, true); |
| final Long prefix = (Long) _prefix; |
| if (_output instanceof Long) { |
| final Long output = (Long) _output; |
| if (prefix == NO_OUTPUT) { |
| return output; |
| } else if (output == NO_OUTPUT) { |
| return prefix; |
| } else { |
| return prefix + output; |
| } |
| } else { |
| final TwoLongs output = (TwoLongs) _output; |
| final long v = prefix; |
| return new TwoLongs(output.first + v, output.second + v); |
| } |
| } |
| |
| @Override |
| public void write(Object _output, DataOutput out) throws IOException { |
| assert valid(_output, true); |
| if (_output instanceof Long) { |
| final Long output = (Long) _output; |
| out.writeVLong(output<<1); |
| } else { |
| final TwoLongs output = (TwoLongs) _output; |
| out.writeVLong((output.first<<1) | 1); |
| out.writeVLong(output.second); |
| } |
| } |
| |
| @Override |
| public Object read(DataInput in) throws IOException { |
| final long code = in.readVLong(); |
| if ((code & 1) == 0) { |
| // single long |
| final long v = code >>> 1; |
| if (v == 0) { |
| return NO_OUTPUT; |
| } else { |
| return Long.valueOf(v); |
| } |
| } else { |
| // two longs |
| final long first = code >>> 1; |
| final long second = in.readVLong(); |
| return new TwoLongs(first, second); |
| } |
| } |
| |
| private boolean valid(Long o) { |
| assert o != null; |
| assert o instanceof Long; |
| assert o == NO_OUTPUT || o > 0; |
| return true; |
| } |
| |
| // Used only by assert |
| private boolean valid(Object _o, boolean allowDouble) { |
| if (!allowDouble) { |
| assert _o instanceof Long; |
| return valid((Long) _o); |
| } else if (_o instanceof TwoLongs) { |
| return true; |
| } else { |
| return valid((Long) _o); |
| } |
| } |
| |
| @Override |
| public Object getNoOutput() { |
| return NO_OUTPUT; |
| } |
| |
| @Override |
| public String outputToString(Object output) { |
| return output.toString(); |
| } |
| |
| @Override |
| public Object merge(Object first, Object second) { |
| assert valid(first, false); |
| assert valid(second, false); |
| return new TwoLongs((Long) first, (Long) second); |
| } |
| |
| private static final long TWO_LONGS_NUM_BYTES = RamUsageEstimator.shallowSizeOf(new TwoLongs(0, 0)); |
| |
| @Override |
| public long ramBytesUsed(Object o) { |
| if (o instanceof Long) { |
| return RamUsageEstimator.sizeOf((Long) o); |
| } else { |
| assert o instanceof TwoLongs; |
| return TWO_LONGS_NUM_BYTES; |
| } |
| } |
| } |