]> git.proxmox.com Git - ceph.git/blob - ceph/src/arrow/csharp/src/Apache.Arrow/ArrowBuffer.Builder.cs
import quincy 17.2.0
[ceph.git] / ceph / src / arrow / csharp / src / Apache.Arrow / ArrowBuffer.Builder.cs
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
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
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.
15
16 using Apache.Arrow.Memory;
17 using System;
18 using System.Buffers;
19 using System.Collections.Generic;
20 using System.Runtime.CompilerServices;
21
22 namespace Apache.Arrow
23 {
24 public partial struct ArrowBuffer
25 {
26 /// <summary>
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.
29 /// </summary>
30 /// <remarks>
31 /// Note that <see cref="bool"/> is not supported as a generic type argument for this class. Please use
32 /// <see cref="BitmapBuilder"/> instead.
33 /// </remarks>
34 /// <typeparam name="T">Value-type of item to build into a buffer.</typeparam>
35 public class Builder<T>
36 where T : struct
37 {
38 private const int DefaultCapacity = 8;
39
40 private readonly int _size;
41
42 /// <summary>
43 /// Gets the number of items that can be contained in the memory allocated by the current instance.
44 /// </summary>
45 public int Capacity => Memory.Length / _size;
46
47 /// <summary>
48 /// Gets the number of items currently appended.
49 /// </summary>
50 public int Length { get; private set; }
51
52 /// <summary>
53 /// Gets the raw byte memory underpinning the builder.
54 /// </summary>
55 public Memory<byte> Memory { get; private set; }
56
57 /// <summary>
58 /// Gets the span of memory underpinning the builder.
59 /// </summary>
60 public Span<T> Span
61 {
62 [MethodImpl(MethodImplOptions.AggressiveInlining)]
63 get => Memory.Span.CastTo<T>();
64 }
65
66 /// <summary>
67 /// Creates an instance of the <see cref="Builder{T}"/> class.
68 /// </summary>
69 /// <param name="capacity">Number of items of initial capacity to reserve.</param>
70 public Builder(int capacity = DefaultCapacity)
71 {
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))
77 {
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)} " +
81 $"instead");
82 }
83
84 _size = Unsafe.SizeOf<T>();
85
86 Memory = new byte[capacity * _size];
87 Length = 0;
88 }
89
90 /// <summary>
91 /// Append a buffer, assumed to contain items of the same type.
92 /// </summary>
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)
96 {
97 Append(buffer.Span.CastTo<T>());
98 return this;
99 }
100
101 /// <summary>
102 /// Append a single item.
103 /// </summary>
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)
107 {
108 EnsureAdditionalCapacity(1);
109 Span[Length++] = value;
110 return this;
111 }
112
113 /// <summary>
114 /// Append a span of items.
115 /// </summary>
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)
119 {
120 EnsureAdditionalCapacity(source.Length);
121 source.CopyTo(Span.Slice(Length, source.Length));
122 Length += source.Length;
123 return this;
124 }
125
126 /// <summary>
127 /// Append a number of items.
128 /// </summary>
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)
132 {
133 if (values != null)
134 {
135 foreach (T v in values)
136 {
137 Append(v);
138 }
139 }
140
141 return this;
142 }
143
144 /// <summary>
145 /// Reserve a given number of items' additional capacity.
146 /// </summary>
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)
150 {
151 if (additionalCapacity < 0)
152 {
153 throw new ArgumentOutOfRangeException(nameof(additionalCapacity));
154 }
155
156 EnsureAdditionalCapacity(additionalCapacity);
157 return this;
158 }
159
160 /// <summary>
161 /// Resize the buffer to a given size.
162 /// </summary>
163 /// <remarks>
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.
166 /// </remarks>
167 /// <remarks>
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.
170 /// </remarks>
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)
174 {
175 if (capacity < 0)
176 {
177 throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity must be non-negative");
178 }
179
180 EnsureCapacity(capacity);
181 Length = capacity;
182
183 return this;
184 }
185
186 /// <summary>
187 /// Clear all contents appended so far.
188 /// </summary>
189 /// <returns>Returns the builder (for fluent-style composition).</returns>
190 public Builder<T> Clear()
191 {
192 Span.Fill(default);
193 Length = 0;
194 return this;
195 }
196
197 /// <summary>
198 /// Build an Arrow buffer from the appended contents so far.
199 /// </summary>
200 /// <param name="allocator">Optional memory allocator.</param>
201 /// <returns>Returns an <see cref="ArrowBuffer"/> object.</returns>
202 public ArrowBuffer Build(MemoryAllocator allocator = default)
203 {
204 return Build(64, allocator);
205 }
206
207 /// <summary>
208 /// Build an Arrow buffer from the appended contents so far of the specified byte size.
209 /// </summary>
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)
213 {
214 int currentBytesLength = Length * _size;
215 int bufferLength = checked((int)BitUtility.RoundUpToMultiplePowerOfTwo(currentBytesLength, byteSize));
216
217 MemoryAllocator memoryAllocator = allocator ?? MemoryAllocator.Default.Value;
218 IMemoryOwner<byte> memoryOwner = memoryAllocator.Allocate(bufferLength);
219 Memory.Slice(0, currentBytesLength).CopyTo(memoryOwner.Memory);
220
221 return new ArrowBuffer(memoryOwner);
222 }
223
224 private void EnsureAdditionalCapacity(int additionalCapacity)
225 {
226 EnsureCapacity(checked(Length + additionalCapacity));
227 }
228
229 private void EnsureCapacity(int requiredCapacity)
230 {
231 if (requiredCapacity > Capacity)
232 {
233 // TODO: specifiable growth strategy
234 // Double the length of the in-memory array, or use the byte count of the capacity, whichever is
235 // greater.
236 int capacity = Math.Max(requiredCapacity * _size, Memory.Length * 2);
237 Reallocate(capacity);
238 }
239 }
240
241 private void Reallocate(int numBytes)
242 {
243 if (numBytes != 0)
244 {
245 var memory = new Memory<byte>(new byte[numBytes]);
246 Memory.CopyTo(memory);
247
248 Memory = memory;
249 }
250 }
251
252 }
253
254 }
255 }