blob: b35825d4ec1da83aed6a6aae5751b7fed0383b5e [file] [log] [blame]
using J2N.Numerics;
using Lucene.Net.Diagnostics;
using Lucene.Net.Support;
using System;
using System.IO;
using System.Runtime.CompilerServices;
namespace Lucene.Net.Util.Packed
{
/*
* 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.
*/
using DataInput = Lucene.Net.Store.DataInput;
using IndexInput = Lucene.Net.Store.IndexInput;
/// <summary>
/// Reader for sequences of <see cref="long"/>s written with <see cref="BlockPackedWriter"/>.
/// <para/>
/// @lucene.internal
/// </summary>
/// <seealso cref="BlockPackedWriter"/>
public sealed class BlockPackedReaderIterator
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static long ZigZagDecode(long n)
{
return ((n.TripleShift(1)) ^ -(n & 1));
}
// same as DataInput.ReadVInt64 but supports negative values
/// <summary>
/// NOTE: This was readVLong() in Lucene.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static long ReadVInt64(DataInput @in)
{
byte b = @in.ReadByte();
if (b <= sbyte.MaxValue) // LUCENENET: Optimized equivalent of "if ((sbyte)b >= 0)"
{
return b;
}
long i = b & 0x7FL;
b = @in.ReadByte();
i |= (b & 0x7FL) << 7;
if (b <= sbyte.MaxValue) // LUCENENET: Optimized equivalent of "if ((sbyte)b >= 0)"
{
return i;
}
b = @in.ReadByte();
i |= (b & 0x7FL) << 14;
if (b <= sbyte.MaxValue) // LUCENENET: Optimized equivalent of "if ((sbyte)b >= 0)"
{
return i;
}
b = @in.ReadByte();
i |= (b & 0x7FL) << 21;
if (b <= sbyte.MaxValue) // LUCENENET: Optimized equivalent of "if ((sbyte)b >= 0)"
{
return i;
}
b = @in.ReadByte();
i |= (b & 0x7FL) << 28;
if (b <= sbyte.MaxValue) // LUCENENET: Optimized equivalent of "if ((sbyte)b >= 0)"
{
return i;
}
b = @in.ReadByte();
i |= (b & 0x7FL) << 35;
if (b <= sbyte.MaxValue) // LUCENENET: Optimized equivalent of "if ((sbyte)b >= 0)"
{
return i;
}
b = @in.ReadByte();
i |= (b & 0x7FL) << 42;
if (b <= sbyte.MaxValue) // LUCENENET: Optimized equivalent of "if ((sbyte)b >= 0)"
{
return i;
}
b = @in.ReadByte();
i |= (b & 0x7FL) << 49;
if (b <= sbyte.MaxValue) // LUCENENET: Optimized equivalent of "if ((sbyte)b >= 0)"
{
return i;
}
b = @in.ReadByte();
i |= (b & 0xFFL) << 56;
return i;
}
internal DataInput @in;
internal readonly int packedIntsVersion;
internal long valueCount;
internal readonly int blockSize;
internal readonly long[] values;
internal readonly Int64sRef valuesRef;
internal byte[] blocks;
internal int off;
internal long ord;
/// <summary>
/// Sole constructor. </summary>
/// <param name="blockSize"> The number of values of a block, must be equal to the
/// block size of the <see cref="BlockPackedWriter"/> which has
/// been used to write the stream. </param>
public BlockPackedReaderIterator(DataInput @in, int packedIntsVersion, int blockSize, long valueCount)
{
PackedInt32s.CheckBlockSize(blockSize, AbstractBlockPackedWriter.MIN_BLOCK_SIZE, AbstractBlockPackedWriter.MAX_BLOCK_SIZE);
this.packedIntsVersion = packedIntsVersion;
this.blockSize = blockSize;
this.values = new long[blockSize];
this.valuesRef = new Int64sRef(this.values, 0, 0);
Reset(@in, valueCount);
}
/// <summary>
/// Reset the current reader to wrap a stream of <paramref name="valueCount"/>
/// values contained in <paramref name="in"/>. The block size remains unchanged.
/// </summary>
public void Reset(DataInput @in, long valueCount)
{
this.@in = @in;
if (Debugging.AssertsEnabled) Debugging.Assert(valueCount >= 0);
this.valueCount = valueCount;
off = blockSize;
ord = 0;
}
/// <summary>
/// Skip exactly <paramref name="count"/> values. </summary>
public void Skip(long count)
{
if (Debugging.AssertsEnabled) Debugging.Assert(count >= 0);
if (ord + count > valueCount || ord + count < 0)
{
throw new EndOfStreamException();
}
// 1. skip buffered values
int skipBuffer = (int)Math.Min(count, blockSize - off);
off += skipBuffer;
ord += skipBuffer;
count -= skipBuffer;
if (count == 0L)
{
return;
}
// 2. skip as many blocks as necessary
if (Debugging.AssertsEnabled) Debugging.Assert(off == blockSize);
while (count >= blockSize)
{
int token = @in.ReadByte() & 0xFF;
int bitsPerValue = token.TripleShift(AbstractBlockPackedWriter.BPV_SHIFT);
if (bitsPerValue > 64)
{
throw new IOException("Corrupted");
}
if ((token & AbstractBlockPackedWriter.MIN_VALUE_EQUALS_0) == 0)
{
ReadVInt64(@in);
}
long blockBytes = PackedInt32s.Format.PACKED.ByteCount(packedIntsVersion, blockSize, bitsPerValue);
SkipBytes(blockBytes);
ord += blockSize;
count -= blockSize;
}
if (count == 0L)
{
return;
}
// 3. skip last values
if (Debugging.AssertsEnabled) Debugging.Assert(count < blockSize);
Refill();
ord += count;
off += (int)count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SkipBytes(long count)
{
if (@in is IndexInput input)
{
input.Seek(input.GetFilePointer() + count);
}
else
{
if (blocks == null)
{
blocks = new byte[blockSize];
}
long skipped = 0;
while (skipped < count)
{
int toSkip = (int)Math.Min(blocks.Length, count - skipped);
@in.ReadBytes(blocks, 0, toSkip);
skipped += toSkip;
}
}
}
/// <summary>
/// Read the next value. </summary>
public long Next()
{
if (ord == valueCount)
{
throw new EndOfStreamException();
}
if (off == blockSize)
{
Refill();
}
long value = values[off++];
++ord;
return value;
}
/// <summary>
/// Read between <c>1</c> and <paramref name="count"/> values. </summary>
public Int64sRef Next(int count)
{
if (Debugging.AssertsEnabled) Debugging.Assert(count > 0);
if (ord == valueCount)
{
throw new EndOfStreamException();
}
if (off == blockSize)
{
Refill();
}
count = Math.Min(count, blockSize - off);
count = (int)Math.Min(count, valueCount - ord);
valuesRef.Offset = off;
valuesRef.Length = count;
off += count;
ord += count;
return valuesRef;
}
private void Refill()
{
int token = @in.ReadByte() & 0xFF;
bool minEquals0 = (token & AbstractBlockPackedWriter.MIN_VALUE_EQUALS_0) != 0;
int bitsPerValue = token.TripleShift(AbstractBlockPackedWriter.BPV_SHIFT);
if (bitsPerValue > 64)
{
throw new IOException("Corrupted");
}
long minValue = minEquals0 ? 0L : ZigZagDecode(1L + ReadVInt64(@in));
if (Debugging.AssertsEnabled) Debugging.Assert(minEquals0 || minValue != 0);
if (bitsPerValue == 0)
{
Arrays.Fill(values, minValue);
}
else
{
PackedInt32s.IDecoder decoder = PackedInt32s.GetDecoder(PackedInt32s.Format.PACKED, packedIntsVersion, bitsPerValue);
int iterations = blockSize / decoder.ByteValueCount;
int blocksSize = iterations * decoder.ByteBlockCount;
if (blocks == null || blocks.Length < blocksSize)
{
blocks = new byte[blocksSize];
}
int valueCount = (int)Math.Min(this.valueCount - ord, blockSize);
int blocksCount = (int)PackedInt32s.Format.PACKED.ByteCount(packedIntsVersion, valueCount, bitsPerValue);
@in.ReadBytes(blocks, 0, blocksCount);
decoder.Decode(blocks, 0, values, 0, iterations);
if (minValue != 0)
{
for (int i = 0; i < valueCount; ++i)
{
values[i] += minValue;
}
}
}
off = 0;
}
/// <summary>
/// Return the offset of the next value to read. </summary>
public long Ord => ord;
}
}