blob: d96859a50a53986cfc3b4e2fb2c0ac9d787ab3c9 [file] [log] [blame]
using Lucene.Net.Diagnostics;
using System;
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.
*/
/// <summary>
/// Common functionality shared by <see cref="AppendingDeltaPackedInt64Buffer"/> and <see cref="MonotonicAppendingInt64Buffer"/>.
/// <para/>
/// NOTE: This was AbstractAppendingLongBuffer in Lucene
/// </summary>
public abstract class AbstractAppendingInt64Buffer : Int64Values // LUCENENET NOTE: made public rather than internal because has public subclasses
{
internal const int MIN_PAGE_SIZE = 64;
// More than 1M doesn't really makes sense with these appending buffers
// since their goal is to try to have small numbers of bits per value
internal const int MAX_PAGE_SIZE = 1 << 20;
internal readonly int pageShift, pageMask;
internal PackedInt32s.Reader[] values;
private long valuesBytes;
internal int valuesOff;
internal long[] pending;
internal int pendingOff;
internal float acceptableOverheadRatio;
internal AbstractAppendingInt64Buffer(int initialBlockCount, int pageSize, float acceptableOverheadRatio)
{
values = new PackedInt32s.Reader[initialBlockCount];
pending = new long[pageSize];
pageShift = PackedInt32s.CheckBlockSize(pageSize, MIN_PAGE_SIZE, MAX_PAGE_SIZE);
pageMask = pageSize - 1;
valuesOff = 0;
pendingOff = 0;
this.acceptableOverheadRatio = acceptableOverheadRatio;
}
public int PageSize => pageMask + 1;
/// <summary>
/// Get the number of values that have been added to the buffer.
/// <para/>
/// NOTE: This was size() in Lucene.
/// </summary>
public long Count
{
get
{
long size = pendingOff;
if (valuesOff > 0)
{
size += values[valuesOff - 1].Count;
}
if (valuesOff > 1)
{
size += (long)(valuesOff - 1) * PageSize;
}
return size;
}
}
/// <summary>
/// Append a value to this buffer. </summary>
public void Add(long l)
{
if (pending == null)
{
throw new Exception("this buffer is frozen");
}
if (pendingOff == pending.Length)
{
// check size
if (values.Length == valuesOff)
{
int newLength = ArrayUtil.Oversize(valuesOff + 1, 8);
Grow(newLength);
}
PackPendingValues();
valuesBytes += values[valuesOff].RamBytesUsed();
++valuesOff;
// reset pending buffer
pendingOff = 0;
}
pending[pendingOff++] = l;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual void Grow(int newBlockCount)
{
Array.Resize<PackedInt32s.Reader>(ref values, newBlockCount);
}
internal abstract void PackPendingValues();
public override sealed long Get(long index)
{
if (Debugging.AssertsEnabled) Debugging.Assert(index >= 0 && index < Count);
int block = (int)(index >> pageShift);
int element = (int)(index & pageMask);
return Get(block, element);
}
/// <summary>
/// Bulk get: read at least one and at most <paramref name="len"/> <see cref="long"/>s starting
/// from <paramref name="index"/> into <c>arr[off:off+len]</c> and return
/// the actual number of values that have been read.
/// </summary>
public int Get(long index, long[] arr, int off, int len)
{
if (Debugging.AssertsEnabled)
{
Debugging.Assert(len > 0, "len must be > 0 (got {0})", len);
Debugging.Assert(index >= 0 && index < Count);
Debugging.Assert(off + len <= arr.Length);
}
int block = (int)(index >> pageShift);
int element = (int)(index & pageMask);
return Get(block, element, arr, off, len);
}
internal abstract long Get(int block, int element);
internal abstract int Get(int block, int element, long[] arr, int off, int len);
/// <summary>
/// Return an iterator over the values of this buffer.
/// </summary>
public virtual Iterator GetIterator()
{
return new Iterator(this);
}
public sealed class Iterator
{
private readonly AbstractAppendingInt64Buffer outerInstance;
internal long[] currentValues;
internal int vOff, pOff;
internal int currentCount; // number of entries of the current page
internal Iterator(AbstractAppendingInt64Buffer outerInstance)
{
this.outerInstance = outerInstance;
vOff = pOff = 0;
if (outerInstance.valuesOff == 0)
{
currentValues = outerInstance.pending;
currentCount = outerInstance.pendingOff;
}
else
{
currentValues = new long[outerInstance.values[0].Count];
FillValues();
}
}
internal void FillValues()
{
if (vOff == outerInstance.valuesOff)
{
currentValues = outerInstance.pending;
currentCount = outerInstance.pendingOff;
}
else
{
currentCount = outerInstance.values[vOff].Count;
for (int k = 0; k < currentCount; )
{
k += outerInstance.Get(vOff, k, currentValues, k, currentCount - k);
}
}
}
/// <summary>
/// Whether or not there are remaining values. </summary>
public bool HasNext => pOff < currentCount;
/// <summary>
/// Return the next long in the buffer. </summary>
public long Next()
{
if (Debugging.AssertsEnabled) Debugging.Assert(HasNext);
long result = currentValues[pOff++];
if (pOff == currentCount)
{
vOff += 1;
pOff = 0;
if (vOff <= outerInstance.valuesOff)
{
FillValues();
}
else
{
currentCount = 0;
}
}
return result;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual long BaseRamBytesUsed()
{
return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER
+ 2 * RamUsageEstimator.NUM_BYTES_OBJECT_REF
+ 2 * RamUsageEstimator.NUM_BYTES_INT32
+ 2 * RamUsageEstimator.NUM_BYTES_INT32
+ RamUsageEstimator.NUM_BYTES_SINGLE
+ RamUsageEstimator.NUM_BYTES_INT64; // valuesBytes - acceptable overhead - pageShift, pageMask - the 2 offsets - the 2 arrays
}
/// <summary>
/// Return the number of bytes used by this instance. </summary>
public virtual long RamBytesUsed()
{
// TODO: this is called per-doc-per-norms/dv-field, can we optimize this?
long bytesUsed = RamUsageEstimator.AlignObjectSize(BaseRamBytesUsed()) + (pending != null ? RamUsageEstimator.SizeOf(pending) : 0L) + RamUsageEstimator.AlignObjectSize(RamUsageEstimator.NUM_BYTES_ARRAY_HEADER + (long)RamUsageEstimator.NUM_BYTES_OBJECT_REF * values.Length); // values
return bytesUsed + valuesBytes;
}
/// <summary>
/// Pack all pending values in this buffer. Subsequent calls to <see cref="Add(long)"/> will fail. </summary>
public virtual void Freeze()
{
if (pendingOff > 0)
{
if (values.Length == valuesOff)
{
Grow(valuesOff + 1); // don't oversize!
}
PackPendingValues();
valuesBytes += values[valuesOff].RamBytesUsed();
++valuesOff;
pendingOff = 0;
}
pending = null;
}
}
}