|  | using Lucene.Net.Diagnostics; | 
|  | using Lucene.Net.Support; | 
|  | 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; | 
|  | Arrays.Fill(offsets, 0); | 
|  | 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 ArgumentOutOfRangeException(nameof(index), $"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 sealed 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 is 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 sealed 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; | 
|  | } | 
|  |  | 
|  | private int pos; | 
|  |  | 
|  | public BytesRef Next() | 
|  | { | 
|  | if (pos < size) | 
|  | { | 
|  | return outerInstance.Get(spare, indices is null ? pos++ : indices[pos++]); | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public 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 is 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 is null ? pos++ : indices[pos++]); | 
|  | return true; | 
|  | } | 
|  | Current = null; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public IComparer<BytesRef> Comparer => comparer; | 
|  | } | 
|  | } | 
|  | } |