| using J2N.Numerics; |
| using Lucene.Net.Codecs.Sep; |
| using Lucene.Net.Diagnostics; |
| using Lucene.Net.Store; |
| using Lucene.Net.Support; |
| using System.Diagnostics; |
| using System.Diagnostics.CodeAnalysis; |
| |
| namespace Lucene.Net.Codecs.IntBlock |
| { |
| /* |
| * 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. |
| */ |
| |
| // TODO: much of this can be shared code w/ the fixed case |
| |
| /// <summary> |
| /// Abstract base class that reads variable-size blocks of ints |
| /// from an <see cref="IndexInput"/>. While this is a simple approach, a |
| /// more performant approach would directly create an impl |
| /// of <see cref="Int32IndexInput"/> inside <see cref="Directory"/>. Wrapping a generic |
| /// <see cref="IndexInput"/> will likely cost performance. |
| /// <para/> |
| /// NOTE: This was VariableIntBlockIndexInput in Lucene |
| /// <para/> |
| /// @lucene.experimental |
| /// </summary> |
| /// <remarks> |
| /// Naive int block API that writes vInts. This is |
| /// expected to give poor performance; it's really only for |
| /// testing the pluggability. One should typically use pfor instead. |
| /// </remarks> |
| public abstract class VariableInt32BlockIndexInput : Int32IndexInput |
| { |
| private readonly IndexInput input; |
| protected readonly int m_maxBlockSize; |
| |
| protected internal VariableInt32BlockIndexInput(IndexInput input) |
| { |
| this.input = input; |
| m_maxBlockSize = input.ReadInt32(); |
| } |
| |
| public override Int32IndexInput.Reader GetReader() |
| { |
| var buffer = new int[m_maxBlockSize]; |
| var clone = (IndexInput)input.Clone(); |
| // TODO: can this be simplified? |
| return new Reader(clone, buffer, GetBlockReader(clone, buffer)); |
| } |
| |
| protected override void Dispose(bool disposing) |
| { |
| if (disposing) |
| { |
| input.Dispose(); |
| } |
| } |
| |
| public override Int32IndexInput.Index GetIndex() |
| { |
| return new Index(this); |
| } |
| |
| protected abstract IBlockReader GetBlockReader(IndexInput @in, int[] buffer); |
| |
| /// <summary> |
| /// Interface for variable-size block decoders. |
| /// <para> |
| /// Implementations should decode into the buffer in <see cref="ReadBlock()"/>. |
| /// </para> |
| /// </summary> |
| public interface IBlockReader |
| { |
| int ReadBlock(); |
| void Seek(long pos); |
| } |
| |
| new private class Reader : Int32IndexInput.Reader |
| { |
| private readonly IndexInput input; |
| |
| [WritableArray] |
| [SuppressMessage("Microsoft.Performance", "CA1819", Justification = "Lucene's design requires some writable array properties")] |
| public int[] Pending => pending; |
| |
| private readonly int[] pending; |
| private int upto; |
| |
| private bool seekPending; |
| private long pendingFP; |
| private int pendingUpto; |
| private long lastBlockFP; |
| private int blockSize; |
| private readonly IBlockReader blockReader; |
| |
| public Reader(IndexInput input, int[] pending, IBlockReader blockReader) |
| { |
| this.input = input; |
| this.pending = pending; |
| this.blockReader = blockReader; |
| } |
| |
| internal virtual void Seek(long fp, int upto) |
| { |
| // TODO: should we do this in real-time, not lazy? |
| pendingFP = fp; |
| pendingUpto = upto; |
| if (Debugging.AssertsEnabled) Debugging.Assert(pendingUpto >= 0, "pendingUpto={0}", pendingUpto); |
| seekPending = true; |
| } |
| |
| private void MaybeSeek() |
| { |
| if (seekPending) |
| { |
| if (pendingFP != lastBlockFP) |
| { |
| // need new block |
| input.Seek(pendingFP); |
| blockReader.Seek(pendingFP); |
| lastBlockFP = pendingFP; |
| blockSize = blockReader.ReadBlock(); |
| } |
| upto = pendingUpto; |
| |
| // TODO: if we were more clever when writing the |
| // index, such that a seek point wouldn't be written |
| // until the int encoder "committed", we could avoid |
| // this (likely minor) inefficiency: |
| |
| // This is necessary for int encoders that are |
| // non-causal, ie must see future int values to |
| // encode the current ones. |
| while (upto >= blockSize) |
| { |
| upto -= blockSize; |
| lastBlockFP = input.GetFilePointer(); |
| blockSize = blockReader.ReadBlock(); |
| } |
| seekPending = false; |
| } |
| } |
| |
| public override int Next() |
| { |
| this.MaybeSeek(); |
| if (upto == blockSize) |
| { |
| lastBlockFP = input.GetFilePointer(); |
| blockSize = blockReader.ReadBlock(); |
| upto = 0; |
| } |
| |
| return pending[upto++]; |
| } |
| } |
| |
| new private class Index : Int32IndexInput.Index |
| { |
| private readonly VariableInt32BlockIndexInput outerInstance; |
| |
| public Index(VariableInt32BlockIndexInput outerInstance) |
| { |
| this.outerInstance = outerInstance; |
| } |
| |
| private long fp; |
| private int upto; |
| |
| public override void Read(DataInput indexIn, bool absolute) |
| { |
| if (absolute) |
| { |
| upto = indexIn.ReadVInt32(); |
| fp = indexIn.ReadVInt64(); |
| } |
| else |
| { |
| int uptoDelta = indexIn.ReadVInt32(); |
| if ((uptoDelta & 1) == 1) |
| { |
| // same block |
| upto += uptoDelta.TripleShift(1); |
| } |
| else |
| { |
| // new block |
| upto = uptoDelta.TripleShift(1); |
| fp += indexIn.ReadVInt64(); |
| } |
| } |
| // TODO: we can't do this assert because non-causal |
| // int encoders can have upto over the buffer size |
| //assert upto < maxBlockSize: "upto=" + upto + " max=" + maxBlockSize; |
| } |
| |
| public override string ToString() |
| { |
| return "VarIntBlock.Index fp=" + fp + " upto=" + upto + " maxBlock=" + outerInstance.m_maxBlockSize; |
| } |
| |
| public override void Seek(Int32IndexInput.Reader other) |
| { |
| ((Reader)other).Seek(fp, upto); |
| } |
| |
| public override void CopyFrom(Int32IndexInput.Index other) |
| { |
| Index idx = (Index)other; |
| fp = idx.fp; |
| upto = idx.upto; |
| } |
| |
| public override object Clone() |
| { |
| Index other = new Index(outerInstance); |
| other.fp = fp; |
| other.upto = upto; |
| return other; |
| } |
| } |
| } |
| } |