• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Giorgi / DuckDB.NET / 22241305397

20 Feb 2026 09:09PM UTC coverage: 89.754% (-0.1%) from 89.875%
22241305397

push

github

Giorgi
Simplify VarInt to BigInteger conversion

Replace manual base-256 digit-by-digit decimal conversion
with BigInteger constructor that accepts big-endian byte
spans. Use ArrayPool for the complement buffer on negative
values.

1226 of 1423 branches covered (86.16%)

Branch coverage included in aggregate %.

12 of 13 new or added lines in 1 file covered. (92.31%)

2497 of 2725 relevant lines covered (91.63%)

481057.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.53
/DuckDB.NET.Data/DataChunk/Reader/NumericVectorDataReader.cs
1
using System.Buffers;
2
using System.Runtime.CompilerServices;
3

4
namespace DuckDB.NET.Data.DataChunk.Reader;
5

6
internal sealed class NumericVectorDataReader : VectorDataReaderBase
7
{
8
    private const int VarIntHeaderSize = 3;
9

10
    internal unsafe NumericVectorDataReader(void* dataPointer, ulong* validityMaskPointer, DuckDBType columnType, string columnName) : base(dataPointer, validityMaskPointer, columnType, columnName)
45,143✔
11
    {
12
    }
45,143✔
13

14
    protected override T GetValidValue<T>(ulong offset, Type targetType)
15
    {
16
        var isFloatingNumericType = TypeExtensions.IsFloatingNumericType<T>();
2,791,241✔
17
        var isIntegralNumericType = TypeExtensions.IsIntegralNumericType<T>();
2,791,241✔
18

19
        if (!(isIntegralNumericType || isFloatingNumericType))
2,791,241✔
20
        {
21
            return base.GetValidValue<T>(offset, targetType);
18,856✔
22
        }
23

24
        //If T is integral type and column is also integral read the data and use Unsafe.As<> or Convert.ChangeType to change type
25
        //If T is floating and column is floating too, read data and cast to T
26
        //Otherwise use the non-generic path
27
        if (isIntegralNumericType)
2,772,385✔
28
        {
29
            return DuckDBType switch
1,277,409!
30
            {
1,277,409✔
31
                DuckDBType.TinyInt => GetUnmanagedTypeValue<sbyte, T>(offset),
96✔
32
                DuckDBType.SmallInt => GetUnmanagedTypeValue<short, T>(offset),
96✔
33
                DuckDBType.Integer => GetUnmanagedTypeValue<int, T>(offset),
957,360✔
34
                DuckDBType.BigInt => GetUnmanagedTypeValue<long, T>(offset),
150,480✔
35
                DuckDBType.UnsignedTinyInt => GetUnmanagedTypeValue<byte, T>(offset),
96✔
36
                DuckDBType.UnsignedSmallInt => GetUnmanagedTypeValue<ushort, T>(offset),
96✔
37
                DuckDBType.UnsignedInteger => GetUnmanagedTypeValue<uint, T>(offset),
96✔
38
                DuckDBType.UnsignedBigInt => GetUnmanagedTypeValue<ulong, T>(offset),
168✔
39
                DuckDBType.HugeInt => GetBigInteger<T>(offset, false),
66✔
40
                DuckDBType.UnsignedHugeInt => GetBigInteger<T>(offset, true),
36✔
41
                DuckDBType.VarInt => GetBigInteger<T>(offset),
168,819✔
42
                _ => base.GetValidValue<T>(offset, targetType)
×
43
            };
1,277,409✔
44
        }
45

46
        return DuckDBType switch
1,494,976!
47
        {
1,494,976✔
48
            DuckDBType.Float => (T)(object)GetFieldData<float>(offset),
744,664✔
49
            DuckDBType.Double => (T)(object)GetFieldData<double>(offset),
750,312✔
50
            _ => base.GetValidValue<T>(offset, targetType)
×
51
        };
1,494,976✔
52
    }
53

54
    internal override object GetValue(ulong offset, Type targetType)
55
    {
56
        var value = DuckDBType switch
10,627,902!
57
        {
10,627,902✔
58
            DuckDBType.TinyInt => GetFieldData<sbyte>(offset),
1,120,854✔
59
            DuckDBType.SmallInt => GetFieldData<short>(offset),
1,115,238✔
60
            DuckDBType.Integer => GetFieldData<int>(offset),
407,231✔
61
            DuckDBType.BigInt => GetFieldData<long>(offset),
1,211,467✔
62
            DuckDBType.UnsignedTinyInt => GetFieldData<byte>(offset),
1,122,722✔
63
            DuckDBType.UnsignedSmallInt => GetFieldData<ushort>(offset),
1,114,396✔
64
            DuckDBType.UnsignedInteger => GetFieldData<uint>(offset),
1,129,314✔
65
            DuckDBType.UnsignedBigInt => GetFieldData<ulong>(offset),
1,120,520✔
66
            DuckDBType.Float => GetFieldData<float>(offset),
377,547✔
67
            DuckDBType.Double => GetFieldData<double>(offset),
371,954✔
68
            DuckDBType.HugeInt => GetBigInteger(offset, false),
746,533✔
69
            DuckDBType.UnsignedHugeInt => GetBigInteger(offset, true),
752,603✔
70
            DuckDBType.VarInt => GetBigInteger<BigInteger>(offset),
37,523✔
71
            _ => base.GetValue(offset, targetType)
×
72
        };
10,627,902✔
73

74
        if (targetType.IsNumeric())
10,627,902✔
75
        {
76
            try
77
            {
78
                return Convert.ChangeType(value, targetType);
10,609,046✔
79
            }
80
            catch (OverflowException)
15✔
81
            {
82
                throw new InvalidCastException($"Cannot cast from {value.GetType().Name} to {targetType.Name} in column {ColumnName}");
15✔
83
            }
84
        }
85

86
        throw new InvalidCastException($"Cannot cast from {value.GetType().Name} to {targetType.Name} in column {ColumnName}");
18,856✔
87
    }
10,609,031✔
88

89
    internal unsafe BigInteger GetBigInteger(ulong offset, bool unsigned)
90
    {
91
        if (unsigned)
2,620,055✔
92
        {
93
            var unsignedHugeInt = (DuckDBUHugeInt*)DataPointer + offset;
752,639✔
94
            return unsignedHugeInt->ToBigInteger();
752,639✔
95
        }
96
        else
97
        {
98
            var hugeInt = (DuckDBHugeInt*)DataPointer + offset;
1,867,416✔
99
            return hugeInt->ToBigInteger();
1,867,416✔
100
        }
101
    }
102

103
    private unsafe T GetBigInteger<T>(ulong offset)
104
    {
105
        var data = (DuckDBString*)DataPointer + offset;
206,342✔
106

107
        if (data->Length < VarIntHeaderSize + 1)
206,342!
108
        {
109
            throw new DuckDBException("Invalid blob size for Varint.");
×
110
        }
111

112
        var buffer = new ReadOnlySpan<byte>(data->Data, data->Length);
206,342✔
113
        var isPositive = (buffer[0] & 0x80) != 0;
206,342✔
114
        var source = buffer.Slice(VarIntHeaderSize);
206,342✔
115

116
        byte[]? rented = null;
206,342✔
117

118
        try
119
        {
120
            if (isPositive) return CastTo<T>(new BigInteger(source, isUnsigned: true, isBigEndian: true));
310,342✔
121

122
            // Negative values need byte complementing — use stack for small payloads, pool for large.
123
            Span<byte> payload = source.Length <= 128
102,342!
124
                ? stackalloc byte[source.Length]
102,342✔
125
                : (rented = ArrayPool<byte>.Shared.Rent(source.Length)).AsSpan(0, source.Length);
102,342✔
126

127
            for (var i = 0; i < source.Length; i++)
1,144,686✔
128
            {
129
                payload[i] = (byte)~source[i];
470,001✔
130
            }
131

132
            return CastTo<T>(-new BigInteger(payload, isUnsigned: true, isBigEndian: true));
102,342✔
133
        }
134
        catch (OverflowException)
92,693✔
135
        {
136
            throw new InvalidCastException($"Cannot cast from {nameof(BigInteger)} to {typeof(T).Name} in column {ColumnName}");
92,693✔
137
        }
138
        finally
139
        {
140
            if (rented != null)
206,342!
141
            {
NEW
142
                ArrayPool<byte>.Shared.Return(rented);
×
143
            }
144
        }
206,342✔
145
    }
113,649✔
146

147
    private T GetBigInteger<T>(ulong offset, bool unsigned)
148
    {
149
        var bigInteger = GetBigInteger(offset, unsigned);
102✔
150

151
        try
152
        {
153
            return CastTo<T>(bigInteger);
102✔
154
        }
155
        catch (OverflowException)
×
156
        {
157
            throw new InvalidCastException($"Cannot cast from {nameof(BigInteger)} to {typeof(T).Name} in column {ColumnName}");
×
158
        }
159
    }
102✔
160

161
    private static T CastTo<T>(BigInteger bigInteger)
162
    {
163
        if (typeof(T) == typeof(byte))
206,444✔
164
        {
165
            return (T)(object)(byte)bigInteger;
18,757✔
166
        }
167

168
        if (typeof(T) == typeof(sbyte))
187,687✔
169
        {
170
            return (T)(object)(sbyte)bigInteger;
18,760✔
171
        }
172

173
        if (typeof(T) == typeof(short))
168,927✔
174
        {
175
            return (T)(object)(short)bigInteger;
18,760✔
176
        }
177

178
        if (typeof(T) == typeof(ushort))
150,167✔
179
        {
180
            return (T)(object)(ushort)bigInteger;
18,757✔
181
        }
182

183
        if (typeof(T) == typeof(int))
131,410✔
184
        {
185
            return (T)(object)(int)bigInteger;
18,760✔
186
        }
187

188
        if (typeof(T) == typeof(uint))
112,650✔
189
        {
190
            return (T)(object)(uint)bigInteger;
18,760✔
191
        }
192

193
        if (typeof(T) == typeof(long))
93,890✔
194
        {
195
            return (T)(object)(long)bigInteger;
18,760✔
196
        }
197

198
        if (typeof(T) == typeof(ulong))
75,130✔
199
        {
200
            return (T)(object)(ulong)bigInteger;
18,760✔
201
        }
202

203
        return (T)(object)bigInteger;
56,370✔
204
    }
205

206
    private TResult GetUnmanagedTypeValue<TQuery, TResult>(ulong offset) where TQuery : unmanaged
207
        , INumberBase<TQuery>
208
    {
209
        var resultType = typeof(TResult);
1,108,488✔
210
        var value = GetFieldData<TQuery>(offset);
1,108,488✔
211

212
        if (typeof(TQuery) == resultType)
1,108,488✔
213
        {
214
            return Unsafe.As<TQuery, TResult>(ref value);
1,107,789✔
215
        }
216

217
        try
218
        {
219
            if (resultType == typeof(byte))
699✔
220
            {
221
                return (TResult)(object)byte.CreateChecked(value);
90✔
222
            }
223
            if (resultType == typeof(sbyte))
609✔
224
            {
225
                return (TResult)(object)sbyte.CreateChecked(value);
90✔
226
            }
227
            if (resultType == typeof(short))
519✔
228
            {
229
                return (TResult)(object)short.CreateChecked(value);
90✔
230
            }
231
            if (resultType == typeof(ushort))
429✔
232
            {
233
                return (TResult)(object)ushort.CreateChecked(value);
90✔
234
            }
235
            if (resultType == typeof(int))
339✔
236
            {
237
                return (TResult)(object)int.CreateChecked(value);
87✔
238
            }
239
            if (resultType == typeof(uint))
252✔
240
            {
241
                return (TResult)(object)uint.CreateChecked(value);
90✔
242
            }
243
            if (resultType == typeof(long))
162✔
244
            {
245
                return (TResult)(object)long.CreateChecked(value);
81✔
246
            }
247
            if (resultType == typeof(ulong))
81!
248
            {
249
                return (TResult)(object)ulong.CreateChecked(value);
81✔
250
            }
251

252
            return (TResult)Convert.ChangeType(value, resultType);
×
253
        }
254
        catch (OverflowException)
243✔
255
        {
256
            throw new InvalidCastException($"Cannot cast from {value.GetType().Name} to {resultType.Name} in column {ColumnName}");
243✔
257
        }
258
    }
456✔
259
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc