| using Lucene.Net.Diagnostics; |
| using System; |
| using System.Collections.Generic; |
| using System.Runtime.CompilerServices; |
| |
| namespace Lucene.Net.Util |
| { |
| /* |
| * 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> |
| /// A simple append only random-access <see cref="BytesRef"/> array that stores full |
| /// copies of the appended bytes in a <see cref="ByteBlockPool"/>. |
| /// <para/> |
| /// <b>Note: this class is not Thread-Safe!</b> |
| /// <para/> |
| /// @lucene.internal |
| /// @lucene.experimental |
| /// </summary> |
| public sealed class BytesRefArray |
| { |
| private readonly ByteBlockPool pool; |
| private int[] offsets = new int[1]; |
| private int lastElement = 0; |
| private int currentOffset = 0; |
| private readonly Counter bytesUsed; |
| |
| /// <summary> |
| /// Creates a new <see cref="BytesRefArray"/> with a counter to track allocated bytes |
| /// </summary> |
| public BytesRefArray(Counter bytesUsed) |
| { |
| this.pool = new ByteBlockPool(new ByteBlockPool.DirectTrackingAllocator(bytesUsed)); |
| pool.NextBuffer(); |
| bytesUsed.AddAndGet(RamUsageEstimator.NUM_BYTES_ARRAY_HEADER + RamUsageEstimator.NUM_BYTES_INT32); |
| this.bytesUsed = bytesUsed; |
| } |
| |
| /// <summary> |
| /// Clears this <see cref="BytesRefArray"/> |
| /// </summary> |
| public void Clear() |
| { |
| lastElement = 0; |
| currentOffset = 0; |
| Array.Clear(offsets, 0, offsets.Length); |
| pool.Reset(false, true); // no need to 0 fill the buffers we control the allocator |
| } |
| |
| /// <summary> |
| /// Appends a copy of the given <see cref="BytesRef"/> to this <see cref="BytesRefArray"/>. </summary> |
| /// <param name="bytes"> The bytes to append </param> |
| /// <returns> The index of the appended bytes </returns> |
| public int Append(BytesRef bytes) |
| { |
| if (lastElement >= offsets.Length) |
| { |
| int oldLen = offsets.Length; |
| offsets = ArrayUtil.Grow(offsets, offsets.Length + 1); |
| bytesUsed.AddAndGet((offsets.Length - oldLen) * RamUsageEstimator.NUM_BYTES_INT32); |
| } |
| pool.Append(bytes); |
| offsets[lastElement++] = currentOffset; |
| currentOffset += bytes.Length; |
| return lastElement - 1; |
| } |
| |
| /// <summary> |
| /// Returns the current size of this <see cref="BytesRefArray"/>. |
| /// <para/> |
| /// NOTE: This was size() in Lucene. |
| /// </summary> |
| /// <returns> The current size of this <see cref="BytesRefArray"/> </returns> |
| public int Length => lastElement; |
| |
| /// <summary> |
| /// Returns the <i>n'th</i> element of this <see cref="BytesRefArray"/> </summary> |
| /// <param name="spare"> A spare <see cref="BytesRef"/> instance </param> |
| /// <param name="index"> The elements index to retrieve </param> |
| /// <returns> The <i>n'th</i> element of this <see cref="BytesRefArray"/> </returns> |
| public BytesRef Get(BytesRef spare, int index) |
| { |
| if (lastElement > index) |
| { |
| int offset = offsets[index]; |
| int length = index == lastElement - 1 ? currentOffset - offset : offsets[index + 1] - offset; |
| if (Debugging.AssertsEnabled) Debugging.Assert(spare.Offset == 0); |
| spare.Grow(length); |
| spare.Length = length; |
| pool.ReadBytes(offset, spare.Bytes, spare.Offset, spare.Length); |
| return spare; |
| } |
| throw new IndexOutOfRangeException("index " + index + " must be less than the size: " + lastElement); |
| } |
| |
| private int[] Sort(IComparer<BytesRef> comp) |
| { |
| int[] orderedEntries = new int[Length]; |
| for (int i = 0; i < orderedEntries.Length; i++) |
| { |
| orderedEntries[i] = i; |
| } |
| new IntroSorterAnonymousClass(this, comp, orderedEntries).Sort(0, Length); |
| return orderedEntries; |
| } |
| |
| private class IntroSorterAnonymousClass : IntroSorter |
| { |
| private readonly BytesRefArray outerInstance; |
| |
| private readonly IComparer<BytesRef> comp; |
| private readonly int[] orderedEntries; |
| |
| public IntroSorterAnonymousClass(BytesRefArray outerInstance, IComparer<BytesRef> comp, int[] orderedEntries) |
| { |
| this.outerInstance = outerInstance; |
| this.comp = comp; |
| this.orderedEntries = orderedEntries; |
| pivot = new BytesRef(); |
| scratch1 = new BytesRef(); |
| scratch2 = new BytesRef(); |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| protected override void Swap(int i, int j) |
| { |
| int o = orderedEntries[i]; |
| orderedEntries[i] = orderedEntries[j]; |
| orderedEntries[j] = o; |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| protected override int Compare(int i, int j) |
| { |
| int idx1 = orderedEntries[i], idx2 = orderedEntries[j]; |
| return comp.Compare(outerInstance.Get(scratch1, idx1), outerInstance.Get(scratch2, idx2)); |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| protected override void SetPivot(int i) |
| { |
| int index = orderedEntries[i]; |
| outerInstance.Get(pivot, index); |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| protected override int ComparePivot(int j) |
| { |
| int index = orderedEntries[j]; |
| return comp.Compare(pivot, outerInstance.Get(scratch2, index)); |
| } |
| |
| private readonly BytesRef pivot; |
| private readonly BytesRef scratch1; |
| private readonly BytesRef scratch2; |
| } |
| |
| /// <summary> |
| /// Sugar for <see cref="GetIterator(IComparer{BytesRef})"/> with a <c>null</c> comparer |
| /// </summary> |
| [Obsolete("Use GetEnumerator() instead. This method will be removed in 4.8.0 release candidate."), System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] |
| public IBytesRefIterator GetIterator() |
| { |
| return GetIterator(null); |
| } |
| |
| /// <summary> |
| /// <para> |
| /// Returns a <see cref="IBytesRefIterator"/> with point in time semantics. The |
| /// iterator provides access to all so far appended <see cref="BytesRef"/> instances. |
| /// </para> |
| /// <para> |
| /// If a non <c>null</c> <see cref="T:IComparer{BytesRef}"/> is provided the iterator will |
| /// iterate the byte values in the order specified by the comparer. Otherwise |
| /// the order is the same as the values were appended. |
| /// </para> |
| /// <para> |
| /// This is a non-destructive operation. |
| /// </para> |
| /// </summary> |
| [Obsolete("Use GetEnumerator(IComparer<BytesRef>) instead. This method will be removed in 4.8.0 release candidate"), System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] |
| public IBytesRefIterator GetIterator(IComparer<BytesRef> comp) |
| { |
| BytesRef spare = new BytesRef(); |
| int size = Length; |
| int[] indices = comp == null ? null : Sort(comp); |
| return new BytesRefIteratorAnonymousClass(this, comp, spare, size, indices); |
| } |
| |
| [Obsolete("This class will be removed in 4.8.0 release candidate"), System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] |
| private class BytesRefIteratorAnonymousClass : IBytesRefIterator |
| { |
| private readonly BytesRefArray outerInstance; |
| |
| private readonly IComparer<BytesRef> comp; |
| private readonly BytesRef spare; |
| private readonly int size; |
| private readonly int[] indices; |
| |
| public BytesRefIteratorAnonymousClass(BytesRefArray outerInstance, IComparer<BytesRef> comp, BytesRef spare, int size, int[] indices) |
| { |
| this.outerInstance = outerInstance; |
| this.comp = comp; |
| this.spare = spare; |
| this.size = size; |
| this.indices = indices; |
| pos = 0; |
| } |
| |
| internal int pos; |
| |
| public virtual BytesRef Next() |
| { |
| if (pos < size) |
| { |
| return outerInstance.Get(spare, indices == null ? pos++ : indices[pos++]); |
| } |
| return null; |
| } |
| |
| public virtual IComparer<BytesRef> Comparer => comp; |
| } |
| |
| /// <summary> |
| /// Sugar for <see cref="GetEnumerator(IComparer{BytesRef})"/> with a <c>null</c> comparer. |
| /// </summary> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| public IBytesRefEnumerator GetEnumerator() |
| => GetEnumerator(null); |
| |
| |
| /// <summary> |
| /// <para> |
| /// Returns a <see cref="IBytesRefEnumerator"/> with point in time semantics. The |
| /// enumerator provides access to all so far appended <see cref="BytesRef"/> instances. |
| /// </para> |
| /// <para> |
| /// If a non <c>null</c> <see cref="T:IComparer{BytesRef}"/> is provided the enumerator will |
| /// iterate the byte values in the order specified by the comparer. Otherwise |
| /// the order is the same as the values were appended. |
| /// </para> |
| /// <para> |
| /// This is a non-destructive operation. |
| /// </para> |
| /// </summary> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| public IBytesRefEnumerator GetEnumerator(IComparer<BytesRef> comparer) |
| { |
| int[] indices = comparer == null ? null : Sort(comparer); |
| return new Enumerator(this, comparer, this.Length, indices); |
| } |
| |
| private struct Enumerator : IBytesRefEnumerator |
| { |
| private readonly IComparer<BytesRef> comparer; |
| private readonly BytesRef spare; |
| private readonly int size; |
| private readonly int[] indices; |
| |
| private readonly BytesRefArray bytesRefArray; |
| private int pos; |
| |
| public Enumerator(BytesRefArray bytesRefArray, IComparer<BytesRef> comparer, int size, int[] indices) |
| { |
| this.spare = new BytesRef(); |
| this.pos = 0; |
| this.Current = null; |
| this.bytesRefArray = bytesRefArray; |
| this.comparer = comparer; |
| this.size = size; |
| this.indices = indices; |
| } |
| |
| public BytesRef Current { get; private set; } |
| |
| public bool MoveNext() |
| { |
| if (pos < size) |
| { |
| Current = bytesRefArray.Get(spare, indices == null ? pos++ : indices[pos++]); |
| return true; |
| } |
| Current = null; |
| return false; |
| } |
| |
| public IComparer<BytesRef> Comparer => comparer; |
| } |
| } |
| } |