| using Lucene.Net.Diagnostics; |
| using System; |
| |
| 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; |
| } |
| |
| 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; |
| } |
| } |
| |
| 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; |
| } |
| } |
| } |