1 // Licensed to the Apache Software Foundation (ASF) under one or more
2 // contributor license agreements. See the NOTICE file distributed with
3 // this work for additional information regarding copyright ownership.
4 // The ASF licenses this file to You under the Apache License, Version 2.0
5 // (the "License"); you may not use this file except in compliance with
6 // the License. You may obtain a copy of the License at
8 // http://www.apache.org/licenses/LICENSE-2.0
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
16 using Apache.Arrow.Memory;
19 using System.Collections.Generic;
20 using System.Runtime.CompilerServices;
22 namespace Apache.Arrow
24 public partial struct ArrowBuffer
27 /// The <see cref="Builder{T}"/> class is able to append value-type items, with fluent-style methods, to build
28 /// up an <see cref="ArrowBuffer"/> of contiguous items.
31 /// Note that <see cref="bool"/> is not supported as a generic type argument for this class. Please use
32 /// <see cref="BitmapBuilder"/> instead.
34 /// <typeparam name="T">Value-type of item to build into a buffer.</typeparam>
35 public class Builder<T>
38 private const int DefaultCapacity = 8;
40 private readonly int _size;
43 /// Gets the number of items that can be contained in the memory allocated by the current instance.
45 public int Capacity => Memory.Length / _size;
48 /// Gets the number of items currently appended.
50 public int Length { get; private set; }
53 /// Gets the raw byte memory underpinning the builder.
55 public Memory<byte> Memory { get; private set; }
58 /// Gets the span of memory underpinning the builder.
62 [MethodImpl(MethodImplOptions.AggressiveInlining)]
63 get => Memory.Span.CastTo<T>();
67 /// Creates an instance of the <see cref="Builder{T}"/> class.
69 /// <param name="capacity">Number of items of initial capacity to reserve.</param>
70 public Builder(int capacity = DefaultCapacity)
72 // Using `bool` as the template argument, if used in an unrestricted fashion, would result in a buffer
73 // with inappropriate contents being produced. Because C# does not support template specialisation,
74 // and because generic type constraints do not support negation, we will throw a runtime error to
75 // indicate that such a template type is not supported.
76 if (typeof(T) == typeof(bool))
78 throw new NotSupportedException(
79 $"An instance of {nameof(Builder<T>)} cannot be instantiated, as `bool` is not an " +
80 $"appropriate generic type to use with this class - please use {nameof(BitmapBuilder)} " +
84 _size = Unsafe.SizeOf<T>();
86 Memory = new byte[capacity * _size];
91 /// Append a buffer, assumed to contain items of the same type.
93 /// <param name="buffer">Buffer to append.</param>
94 /// <returns>Returns the builder (for fluent-style composition).</returns>
95 public Builder<T> Append(ArrowBuffer buffer)
97 Append(buffer.Span.CastTo<T>());
102 /// Append a single item.
104 /// <param name="value">Item to append.</param>
105 /// <returns>Returns the builder (for fluent-style composition).</returns>
106 public Builder<T> Append(T value)
108 EnsureAdditionalCapacity(1);
109 Span[Length++] = value;
114 /// Append a span of items.
116 /// <param name="source">Source of item span.</param>
117 /// <returns>Returns the builder (for fluent-style composition).</returns>
118 public Builder<T> Append(ReadOnlySpan<T> source)
120 EnsureAdditionalCapacity(source.Length);
121 source.CopyTo(Span.Slice(Length, source.Length));
122 Length += source.Length;
127 /// Append a number of items.
129 /// <param name="values">Items to append.</param>
130 /// <returns>Returns the builder (for fluent-style composition).</returns>
131 public Builder<T> AppendRange(IEnumerable<T> values)
135 foreach (T v in values)
145 /// Reserve a given number of items' additional capacity.
147 /// <param name="additionalCapacity">Number of items of required additional capacity.</param>
148 /// <returns>Returns the builder (for fluent-style composition).</returns>
149 public Builder<T> Reserve(int additionalCapacity)
151 if (additionalCapacity < 0)
153 throw new ArgumentOutOfRangeException(nameof(additionalCapacity));
156 EnsureAdditionalCapacity(additionalCapacity);
161 /// Resize the buffer to a given size.
164 /// Note that if the required capacity is larger than the current length of the populated buffer so far,
165 /// the buffer's contents in the new, expanded region are undefined.
168 /// Note that if the required capacity is smaller than the current length of the populated buffer so far,
169 /// the buffer will be truncated and items at the end of the buffer will be lost.
171 /// <param name="capacity">Number of items of required capacity.</param>
172 /// <returns>Returns the builder (for fluent-style composition).</returns>
173 public Builder<T> Resize(int capacity)
177 throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity must be non-negative");
180 EnsureCapacity(capacity);
187 /// Clear all contents appended so far.
189 /// <returns>Returns the builder (for fluent-style composition).</returns>
190 public Builder<T> Clear()
198 /// Build an Arrow buffer from the appended contents so far.
200 /// <param name="allocator">Optional memory allocator.</param>
201 /// <returns>Returns an <see cref="ArrowBuffer"/> object.</returns>
202 public ArrowBuffer Build(MemoryAllocator allocator = default)
204 return Build(64, allocator);
208 /// Build an Arrow buffer from the appended contents so far of the specified byte size.
210 /// <param name="allocator">Optional memory allocator.</param>
211 /// <returns>Returns an <see cref="ArrowBuffer"/> object.</returns>
212 internal ArrowBuffer Build(int byteSize, MemoryAllocator allocator = default)
214 int currentBytesLength = Length * _size;
215 int bufferLength = checked((int)BitUtility.RoundUpToMultiplePowerOfTwo(currentBytesLength, byteSize));
217 MemoryAllocator memoryAllocator = allocator ?? MemoryAllocator.Default.Value;
218 IMemoryOwner<byte> memoryOwner = memoryAllocator.Allocate(bufferLength);
219 Memory.Slice(0, currentBytesLength).CopyTo(memoryOwner.Memory);
221 return new ArrowBuffer(memoryOwner);
224 private void EnsureAdditionalCapacity(int additionalCapacity)
226 EnsureCapacity(checked(Length + additionalCapacity));
229 private void EnsureCapacity(int requiredCapacity)
231 if (requiredCapacity > Capacity)
233 // TODO: specifiable growth strategy
234 // Double the length of the in-memory array, or use the byte count of the capacity, whichever is
236 int capacity = Math.Max(requiredCapacity * _size, Memory.Length * 2);
237 Reallocate(capacity);
241 private void Reallocate(int numBytes)
245 var memory = new Memory<byte>(new byte[numBytes]);
246 Memory.CopyTo(memory);