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

Giorgi / DuckDB.NET / 22737074339

05 Mar 2026 09:14PM UTC coverage: 89.329% (-0.2%) from 89.491%
22737074339

push

github

Giorgi
Refactor NumericVectorDataReader

1271 of 1485 branches covered (85.59%)

Branch coverage included in aggregate %.

15 of 17 new or added lines in 1 file covered. (88.24%)

2605 of 2854 relevant lines covered (91.28%)

450950.22 hits per line

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

84.44
/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)
44,454✔
11
    {
12
    }
44,454✔
13

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

19
        if (!(isIntegralNumericType || isFloatingNumericType))
4,057,368✔
20
        {
21
            return base.GetValidValue<T>(offset, targetType);
18,858✔
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)
4,038,510✔
28
        {
29
            return DuckDBType switch
1,793,514!
30
            {
1,793,514✔
31
                DuckDBType.TinyInt => GetUnmanagedTypeValue<sbyte, T>(offset),
96✔
32
                DuckDBType.SmallInt => GetUnmanagedTypeValue<short, T>(offset),
96✔
33
                DuckDBType.Integer => GetUnmanagedTypeValue<int, T>(offset),
1,353,402✔
34
                DuckDBType.BigInt => GetUnmanagedTypeValue<long, T>(offset),
270,516✔
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),
177✔
39
                DuckDBType.HugeInt => GetBigInteger<T>(offset, false),
66✔
40
                DuckDBType.UnsignedHugeInt => GetBigInteger<T>(offset, true),
36✔
41
                DuckDBType.VarInt => GetBigInteger<T>(offset),
168,837✔
42
                _ => base.GetValidValue<T>(offset, targetType)
×
43
            };
1,793,514✔
44
        }
45

46
        return DuckDBType switch
2,244,996!
47
        {
2,244,996✔
48
            DuckDBType.Float => (T)(object)GetFieldData<float>(offset),
1,114,946✔
49
            DuckDBType.Double => (T)(object)GetFieldData<double>(offset),
1,130,050✔
50
            _ => base.GetValidValue<T>(offset, targetType)
×
51
        };
2,244,996✔
52
    }
53

54
    internal override object GetValue(ulong offset, Type targetType)
55
    {
56
        var value = DuckDBType switch
9,605,031!
57
        {
9,605,031✔
58
            DuckDBType.TinyInt => GetFieldData<sbyte>(offset),
1,124,741✔
59
            DuckDBType.SmallInt => GetFieldData<short>(offset),
1,114,308✔
60
            DuckDBType.Integer => GetFieldData<int>(offset),
30,954✔
61
            DuckDBType.BigInt => GetFieldData<long>(offset),
1,320,751✔
62
            DuckDBType.UnsignedTinyInt => GetFieldData<byte>(offset),
1,120,848✔
63
            DuckDBType.UnsignedSmallInt => GetFieldData<ushort>(offset),
1,117,340✔
64
            DuckDBType.UnsignedInteger => GetFieldData<uint>(offset),
1,121,882✔
65
            DuckDBType.UnsignedBigInt => GetFieldData<ulong>(offset),
1,118,983✔
66
            DuckDBType.Float => GetFieldData<float>(offset),
78✔
67
            DuckDBType.Double => GetFieldData<double>(offset),
246✔
68
            DuckDBType.HugeInt => GetBigInteger(offset, false),
749,646✔
69
            DuckDBType.UnsignedHugeInt => GetBigInteger(offset, true),
747,727✔
70
            DuckDBType.VarInt => GetBigInteger<BigInteger>(offset),
37,527✔
71
            _ => base.GetValue(offset, targetType)
×
72
        };
9,605,031✔
73

74
        if (targetType.IsNumeric())
9,605,031✔
75
        {
76
            try
77
            {
78
                return Convert.ChangeType(value, targetType);
9,586,173✔
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,858✔
87
    }
9,586,158✔
88

89
    internal unsafe BigInteger GetBigInteger(ulong offset, bool unsigned)
90
    {
91
        if (unsigned)
2,616,679✔
92
        {
93
            var unsignedHugeInt = (DuckDBUHugeInt*)DataPointer + offset;
747,763✔
94
            return unsignedHugeInt->ToBigInteger();
747,763✔
95
        }
96
        else
97
        {
98
            var hugeInt = (DuckDBHugeInt*)DataPointer + offset;
1,868,916✔
99
            return hugeInt->ToBigInteger();
1,868,916✔
100
        }
101
    }
102

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

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

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

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

118
        try
119
        {
120
            if (isPositive) return ConvertNumeric<BigInteger, T>(new BigInteger(source, isUnsigned: true, isBigEndian: true));
310,386✔
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 ConvertNumeric<BigInteger, T>(-new BigInteger(payload, isUnsigned: true, isBigEndian: true));
102,342✔
133
        }
134
        catch (OverflowException)
92,709✔
135
        {
136
            throw new InvalidCastException($"Cannot cast from {nameof(BigInteger)} to {typeof(T).Name} in column {ColumnName}");
92,709✔
137
        }
138
        finally
139
        {
140
            if (rented != null)
206,364!
141
            {
142
                ArrayPool<byte>.Shared.Return(rented);
×
143
            }
144
        }
206,364✔
145
    }
113,655✔
146

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

151
        try
152
        {
153
            return ConvertNumeric<BigInteger, 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 TResult GetUnmanagedTypeValue<TQuery, TResult>(ulong offset) where TQuery : unmanaged, INumberBase<TQuery>
162
    {
163
        var value = GetFieldData<TQuery>(offset);
1,624,575✔
164

165
        try
166
        {
167
            return ConvertNumeric<TQuery, TResult>(value);
1,624,575✔
168
        }
169
        catch (OverflowException)
243✔
170
        {
171
            throw new InvalidCastException($"Cannot cast from {typeof(TQuery).Name} to {typeof(TResult).Name} in column {ColumnName}");
243✔
172
        }
173
    }
1,624,332✔
174

175
    private static TResult ConvertNumeric<TSource, TResult>(TSource value) where TSource : INumberBase<TSource>
176
    {
177
        if (typeof(TSource) == typeof(TResult))
1,831,041✔
178
            return Unsafe.As<TSource, TResult>(ref value);
1,680,252✔
179

180
        if (typeof(TResult) == typeof(byte))       return (TResult)(object)byte.CreateChecked(value);
169,638✔
181
        if (typeof(TResult) == typeof(sbyte))      return (TResult)(object)sbyte.CreateChecked(value);
150,792✔
182
        if (typeof(TResult) == typeof(short))      return (TResult)(object)short.CreateChecked(value);
131,940✔
183
        if (typeof(TResult) == typeof(ushort))     return (TResult)(object)ushort.CreateChecked(value);
113,085✔
184
        if (typeof(TResult) == typeof(int))        return (TResult)(object)int.CreateChecked(value);
94,236✔
185
        if (typeof(TResult) == typeof(uint))       return (TResult)(object)uint.CreateChecked(value);
75,390✔
186
        if (typeof(TResult) == typeof(long))       return (TResult)(object)long.CreateChecked(value);
56,529✔
187
        if (typeof(TResult) == typeof(ulong))      return (TResult)(object)ulong.CreateChecked(value);
37,686!
NEW
188
        if (typeof(TResult) == typeof(BigInteger)) return (TResult)(object)BigInteger.CreateChecked(value);
×
189

NEW
190
        throw new InvalidCastException($"Cannot convert {typeof(TSource).Name} to {typeof(TResult).Name}");
×
191
    }
192
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc