Coverage Report

Created: 2026-07-14 20:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/util/variant/variant_scalar_encoding.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "util/variant/variant_scalar_encoding.h"
19
20
#include <bit>
21
#include <cstring>
22
#include <limits>
23
24
#include "common/exception.h"
25
#include "util/utf8_check.h"
26
#include "util/variant/variant_encoding.h"
27
28
namespace doris {
29
namespace {
30
31
constexpr unsigned __int128 DECIMAL4_MAX = 999'999'999;
32
constexpr unsigned __int128 DECIMAL8_MAX = 999'999'999'999'999'999;
33
34
0
constexpr unsigned __int128 max_decimal38() {
35
0
    unsigned __int128 value = 1;
36
0
    for (uint8_t digit = 0; digit < 38; ++digit) {
37
0
        value *= 10;
38
0
    }
39
0
    return value - 1;
40
0
}
41
42
constexpr unsigned __int128 MAX_DECIMAL38 = max_decimal38();
43
44
111
unsigned __int128 magnitude(__int128 value) {
45
111
    const auto unsigned_value = static_cast<unsigned __int128>(value);
46
111
    return value < 0 ? ~unsigned_value + 1 : unsigned_value;
47
111
}
48
49
1.08M
constexpr uint8_t primitive_header(VariantPrimitiveId id) noexcept {
50
1.08M
    return static_cast<uint8_t>(static_cast<uint8_t>(id) << VARIANT_VALUE_HEADER_SHIFT);
51
1.08M
}
52
53
61
constexpr VariantPrimitiveId boolean_primitive_id(bool value) noexcept {
54
61
    return value ? VariantPrimitiveId::TRUE_VALUE : VariantPrimitiveId::FALSE_VALUE;
55
61
}
56
57
struct IntegerEncoding {
58
    VariantPrimitiveId id;
59
    uint8_t width;
60
};
61
62
13.9k
IntegerEncoding resolve_integer_encoding(int64_t value, uint8_t requested_width) {
63
13.9k
    uint8_t width = requested_width;
64
13.9k
    if (width == 0) {
65
13.9k
        if (value >= std::numeric_limits<int8_t>::min() &&
66
13.9k
            value <= std::numeric_limits<int8_t>::max()) {
67
701
            width = 1;
68
13.2k
        } else if (value >= std::numeric_limits<int16_t>::min() &&
69
13.2k
                   value <= std::numeric_limits<int16_t>::max()) {
70
920
            width = 2;
71
12.3k
        } else if (value >= std::numeric_limits<int32_t>::min() &&
72
12.3k
                   value <= std::numeric_limits<int32_t>::max()) {
73
12.3k
            width = 4;
74
12.3k
        } else {
75
16
            width = 8;
76
16
        }
77
13.9k
    }
78
79
13.9k
    VariantPrimitiveId id = VariantPrimitiveId::INT64;
80
13.9k
    bool fits = true;
81
13.9k
    if (width == 1) {
82
704
        id = VariantPrimitiveId::INT8;
83
704
        fits = value >= std::numeric_limits<int8_t>::min() &&
84
704
               value <= std::numeric_limits<int8_t>::max();
85
13.2k
    } else if (width == 2) {
86
922
        id = VariantPrimitiveId::INT16;
87
922
        fits = value >= std::numeric_limits<int16_t>::min() &&
88
922
               value <= std::numeric_limits<int16_t>::max();
89
12.3k
    } else if (width == 4) {
90
12.3k
        id = VariantPrimitiveId::INT32;
91
12.3k
        fits = value >= std::numeric_limits<int32_t>::min() &&
92
12.3k
               value <= std::numeric_limits<int32_t>::max();
93
12.3k
    } else if (width != 8) {
94
1
        throw Exception(ErrorCode::INVALID_ARGUMENT,
95
1
                        "Variant integer width {} must be one of 1, 2, 4, or 8", width);
96
1
    }
97
13.9k
    if (!fits) {
98
1
        throw Exception(ErrorCode::INVALID_ARGUMENT,
99
1
                        "Variant integer value does not fit requested width {}", width);
100
1
    }
101
13.9k
    return {.id = id, .width = width};
102
13.9k
}
103
104
16.1k
void write_unsigned(char*& output, uint64_t value, uint8_t width) noexcept {
105
77.7k
    for (uint8_t byte = 0; byte < width; ++byte) {
106
61.6k
        *output++ = static_cast<char>(value >> (byte * 8));
107
61.6k
    }
108
16.1k
}
109
110
73
void write_signed128(char*& output, __int128 value, uint8_t width) noexcept {
111
73
    const auto unsigned_value = static_cast<unsigned __int128>(value);
112
801
    for (uint8_t byte = 0; byte < width; ++byte) {
113
728
        *output++ = static_cast<char>(unsigned_value >> (byte * 8));
114
728
    }
115
73
}
116
117
2.24k
void validate_string_ref(StringRef value, const char* description) {
118
2.24k
    if (value.data == nullptr && value.size != 0) {
119
2
        throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant {} has a null data pointer",
120
2
                        description);
121
2
    }
122
2.24k
}
123
124
2.23k
void validate_utf8_string_ref(StringRef value, const char* description) {
125
2.23k
    validate_string_ref(value, description);
126
2.23k
    if (value.size != 0 && !validate_utf8(value.data, value.size)) {
127
8
        throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant {} is not valid UTF-8", description);
128
8
    }
129
2.23k
}
130
131
} // namespace
132
133
1.06M
VariantScalarEncodingPlan VariantScalarEncodingPlan::null_value() noexcept {
134
1.06M
    VariantScalarEncodingPlan plan;
135
1.06M
    plan._header = primitive_header(VariantPrimitiveId::NULL_VALUE);
136
1.06M
    plan._encoded_size = 1;
137
1.06M
    return plan;
138
1.06M
}
139
140
61
VariantScalarEncodingPlan VariantScalarEncodingPlan::boolean(bool value) noexcept {
141
61
    VariantScalarEncodingPlan plan;
142
61
    plan._header = primitive_header(boolean_primitive_id(value));
143
61
    plan._encoded_size = 1;
144
61
    return plan;
145
61
}
146
147
13.9k
VariantScalarEncodingPlan VariantScalarEncodingPlan::integer(int64_t value, uint8_t width) {
148
13.9k
    const IntegerEncoding encoding = resolve_integer_encoding(value, width);
149
150
13.9k
    VariantScalarEncodingPlan plan;
151
13.9k
    plan._header = primitive_header(encoding.id);
152
13.9k
    plan._unsigned_value = std::bit_cast<uint64_t>(value);
153
13.9k
    plan._payload_width = encoding.width;
154
13.9k
    plan._encoded_size = static_cast<size_t>(encoding.width) + 1;
155
13.9k
    plan._kind = PayloadKind::UNSIGNED;
156
13.9k
    return plan;
157
13.9k
}
158
159
14
VariantScalarEncodingPlan VariantScalarEncodingPlan::float32(float value) noexcept {
160
14
    VariantScalarEncodingPlan plan;
161
14
    plan._header = static_cast<uint8_t>(VariantPrimitiveId::FLOAT) << VARIANT_VALUE_HEADER_SHIFT;
162
14
    plan._unsigned_value = std::bit_cast<uint32_t>(value);
163
14
    plan._payload_width = sizeof(float);
164
14
    plan._encoded_size = sizeof(float) + 1;
165
14
    plan._kind = PayloadKind::UNSIGNED;
166
14
    return plan;
167
14
}
168
169
95
VariantScalarEncodingPlan VariantScalarEncodingPlan::float64(double value) noexcept {
170
95
    VariantScalarEncodingPlan plan;
171
95
    plan._header = static_cast<uint8_t>(VariantPrimitiveId::DOUBLE) << VARIANT_VALUE_HEADER_SHIFT;
172
95
    plan._unsigned_value = std::bit_cast<uint64_t>(value);
173
95
    plan._payload_width = sizeof(double);
174
95
    plan._encoded_size = sizeof(double) + 1;
175
95
    plan._kind = PayloadKind::UNSIGNED;
176
95
    return plan;
177
95
}
178
179
VariantScalarEncodingPlan VariantScalarEncodingPlan::decimal(__int128 unscaled, uint8_t scale,
180
78
                                                             uint8_t width) {
181
78
    if (scale > 38) {
182
3
        throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant decimal scale {} is outside [0, 38]",
183
3
                        scale);
184
3
    }
185
75
    const unsigned __int128 absolute = magnitude(unscaled);
186
75
    if (absolute > MAX_DECIMAL38) {
187
2
        throw Exception(ErrorCode::INVALID_ARGUMENT,
188
2
                        "Variant decimal unscaled value exceeds precision 38");
189
2
    }
190
73
    if (width == 0) {
191
24
        if (absolute <= DECIMAL4_MAX) {
192
10
            width = 4;
193
14
        } else if (absolute <= DECIMAL8_MAX) {
194
8
            width = 8;
195
8
        } else {
196
6
            width = 16;
197
6
        }
198
24
    }
199
200
73
    VariantPrimitiveId id = VariantPrimitiveId::DECIMAL16;
201
73
    unsigned __int128 width_max = MAX_DECIMAL38;
202
73
    if (width == 4) {
203
25
        id = VariantPrimitiveId::DECIMAL4;
204
25
        width_max = DECIMAL4_MAX;
205
48
    } else if (width == 8) {
206
25
        id = VariantPrimitiveId::DECIMAL8;
207
25
        width_max = DECIMAL8_MAX;
208
25
    } else if (width != 16) {
209
1
        throw Exception(ErrorCode::INVALID_ARGUMENT,
210
1
                        "Variant decimal width {} must be one of 4, 8, or 16", width);
211
1
    }
212
72
    if (absolute > width_max) {
213
4
        throw Exception(ErrorCode::INVALID_ARGUMENT,
214
4
                        "Variant decimal unscaled value exceeds precision for width {}", width);
215
4
    }
216
217
68
    VariantScalarEncodingPlan plan;
218
68
    plan._header = static_cast<uint8_t>(static_cast<uint8_t>(id) << VARIANT_VALUE_HEADER_SHIFT);
219
68
    plan._signed_value = unscaled;
220
68
    plan._payload_width = width;
221
68
    plan._scale = scale;
222
68
    plan._encoded_size = static_cast<size_t>(width) + 2;
223
68
    plan._kind = PayloadKind::SIGNED;
224
68
    plan._has_scale = true;
225
68
    return plan;
226
72
}
227
228
34
VariantScalarEncodingPlan VariantScalarEncodingPlan::date(int32_t days_since_epoch) noexcept {
229
34
    VariantScalarEncodingPlan plan;
230
34
    plan._header = static_cast<uint8_t>(VariantPrimitiveId::DATE) << VARIANT_VALUE_HEADER_SHIFT;
231
34
    plan._unsigned_value = std::bit_cast<uint32_t>(days_since_epoch);
232
34
    plan._payload_width = sizeof(int32_t);
233
34
    plan._encoded_size = sizeof(int32_t) + 1;
234
34
    plan._kind = PayloadKind::UNSIGNED;
235
34
    return plan;
236
34
}
237
238
VariantScalarEncodingPlan VariantScalarEncodingPlan::timestamp_micros(int64_t value,
239
43
                                                                      bool utc_adjusted) noexcept {
240
43
    VariantScalarEncodingPlan plan;
241
43
    plan._header = static_cast<uint8_t>(
242
43
            static_cast<uint8_t>(utc_adjusted ? VariantPrimitiveId::TIMESTAMP_MICROS
243
43
                                              : VariantPrimitiveId::TIMESTAMP_NTZ_MICROS)
244
43
            << VARIANT_VALUE_HEADER_SHIFT);
245
43
    plan._unsigned_value = std::bit_cast<uint64_t>(value);
246
43
    plan._payload_width = sizeof(int64_t);
247
43
    plan._encoded_size = sizeof(int64_t) + 1;
248
43
    plan._kind = PayloadKind::UNSIGNED;
249
43
    return plan;
250
43
}
251
252
VariantScalarEncodingPlan VariantScalarEncodingPlan::timestamp_nanos(int64_t value,
253
28
                                                                     bool utc_adjusted) noexcept {
254
28
    VariantScalarEncodingPlan plan;
255
28
    plan._header = static_cast<uint8_t>(
256
28
            static_cast<uint8_t>(utc_adjusted ? VariantPrimitiveId::TIMESTAMP_NANOS
257
28
                                              : VariantPrimitiveId::TIMESTAMP_NTZ_NANOS)
258
28
            << VARIANT_VALUE_HEADER_SHIFT);
259
28
    plan._unsigned_value = std::bit_cast<uint64_t>(value);
260
28
    plan._payload_width = sizeof(int64_t);
261
28
    plan._encoded_size = sizeof(int64_t) + 1;
262
28
    plan._kind = PayloadKind::UNSIGNED;
263
28
    return plan;
264
28
}
265
266
10
VariantScalarEncodingPlan VariantScalarEncodingPlan::time_ntz_micros(int64_t value) noexcept {
267
10
    VariantScalarEncodingPlan plan;
268
10
    plan._header = static_cast<uint8_t>(VariantPrimitiveId::TIME_NTZ_MICROS)
269
10
                   << VARIANT_VALUE_HEADER_SHIFT;
270
10
    plan._unsigned_value = std::bit_cast<uint64_t>(value);
271
10
    plan._payload_width = sizeof(int64_t);
272
10
    plan._encoded_size = sizeof(int64_t) + 1;
273
10
    plan._kind = PayloadKind::UNSIGNED;
274
10
    return plan;
275
10
}
276
277
14
VariantScalarEncodingPlan VariantScalarEncodingPlan::binary(StringRef value) {
278
14
    validate_string_ref(value, "binary");
279
14
    if (value.size > std::numeric_limits<uint32_t>::max()) {
280
0
        throw Exception(ErrorCode::INVALID_ARGUMENT,
281
0
                        "Variant binary exceeds the uint32 byte limit");
282
0
    }
283
14
    VariantScalarEncodingPlan plan;
284
14
    plan._header = static_cast<uint8_t>(VariantPrimitiveId::BINARY) << VARIANT_VALUE_HEADER_SHIFT;
285
14
    plan._borrowed = value;
286
14
    plan._encoded_size = value.size + sizeof(uint32_t) + 1;
287
14
    plan._kind = PayloadKind::BORROWED_BYTES;
288
14
    plan._has_length = true;
289
14
    return plan;
290
14
}
291
292
2.23k
VariantScalarEncodingPlan VariantScalarEncodingPlan::string(StringRef value) {
293
2.23k
    validate_utf8_string_ref(value, "string");
294
2.23k
    if (value.size > std::numeric_limits<uint32_t>::max()) {
295
0
        throw Exception(ErrorCode::INVALID_ARGUMENT,
296
0
                        "Variant string exceeds the uint32 byte limit");
297
0
    }
298
2.23k
    VariantScalarEncodingPlan plan;
299
2.23k
    plan._borrowed = value;
300
2.23k
    plan._kind = PayloadKind::BORROWED_BYTES;
301
2.23k
    if (value.size <= VARIANT_MAX_SHORT_STRING_SIZE) {
302
151
        plan._header = static_cast<uint8_t>((value.size << VARIANT_VALUE_HEADER_SHIFT) |
303
151
                                            static_cast<uint8_t>(VariantBasicType::SHORT_STRING));
304
151
        plan._encoded_size = value.size + 1;
305
2.08k
    } else {
306
2.08k
        plan._header = static_cast<uint8_t>(VariantPrimitiveId::STRING)
307
2.08k
                       << VARIANT_VALUE_HEADER_SHIFT;
308
2.08k
        plan._encoded_size = value.size + sizeof(uint32_t) + 1;
309
2.08k
        plan._has_length = true;
310
2.08k
    }
311
2.23k
    return plan;
312
2.23k
}
313
314
VariantScalarEncodingPlan VariantScalarEncodingPlan::uuid(
315
9
        const std::array<uint8_t, 16>& value) noexcept {
316
9
    VariantScalarEncodingPlan plan;
317
9
    plan._header = static_cast<uint8_t>(VariantPrimitiveId::UUID) << VARIANT_VALUE_HEADER_SHIFT;
318
9
    plan._uuid = value;
319
9
    plan._encoded_size = value.size() + 1;
320
9
    plan._kind = PayloadKind::UUID;
321
9
    return plan;
322
9
}
323
324
23
VariantScalarEncodingPlan VariantScalarEncodingPlan::largeint(__int128 value) noexcept {
325
23
    if (magnitude(value) <= MAX_DECIMAL38) {
326
10
        VariantScalarEncodingPlan plan;
327
10
        plan._header = static_cast<uint8_t>(VariantPrimitiveId::DECIMAL16)
328
10
                       << VARIANT_VALUE_HEADER_SHIFT;
329
10
        plan._signed_value = value;
330
10
        plan._payload_width = 16;
331
10
        plan._encoded_size = 18;
332
10
        plan._kind = PayloadKind::SIGNED;
333
10
        plan._has_scale = true;
334
10
        return plan;
335
10
    }
336
337
13
    VariantScalarEncodingPlan plan;
338
13
    char reversed[39];
339
13
    size_t digit_count = 0;
340
13
    unsigned __int128 remaining = magnitude(value);
341
507
    do {
342
507
        reversed[digit_count++] = static_cast<char>('0' + remaining % 10);
343
507
        remaining /= 10;
344
507
    } while (remaining != 0);
345
13
    if (value < 0) {
346
7
        plan._inline_string[plan._inline_size++] = '-';
347
7
    }
348
520
    while (digit_count != 0) {
349
507
        plan._inline_string[plan._inline_size++] = reversed[--digit_count];
350
507
    }
351
13
    plan._header = static_cast<uint8_t>(
352
13
            (static_cast<size_t>(plan._inline_size) << VARIANT_VALUE_HEADER_SHIFT) |
353
13
            static_cast<uint8_t>(VariantBasicType::SHORT_STRING));
354
13
    plan._encoded_size = static_cast<size_t>(plan._inline_size) + 1;
355
13
    plan._kind = PayloadKind::INLINE_STRING;
356
13
    plan._used_string_fallback = true;
357
13
    return plan;
358
23
}
359
360
1.08M
void VariantScalarEncodingPlan::write(char* destination, size_t capacity) const {
361
1.08M
    if (destination == nullptr) {
362
1
        throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant scalar destination must not be null");
363
1
    }
364
1.08M
    if (capacity < _encoded_size) {
365
1
        throw Exception(ErrorCode::INVALID_ARGUMENT,
366
1
                        "Variant scalar destination capacity {} is smaller than required {}",
367
1
                        capacity, _encoded_size);
368
1
    }
369
1.08M
    char* output = destination;
370
1.08M
    *output++ = static_cast<char>(_header);
371
1.08M
    if (_has_scale) {
372
73
        *output++ = static_cast<char>(_scale);
373
73
    }
374
1.08M
    if (_has_length) {
375
2.08k
        write_unsigned(output, _borrowed.size, sizeof(uint32_t));
376
2.08k
    }
377
1.08M
    switch (_kind) {
378
1.06M
    case PayloadKind::HEADER_ONLY:
379
1.06M
        break;
380
14.0k
    case PayloadKind::UNSIGNED:
381
14.0k
        write_unsigned(output, _unsigned_value, _payload_width);
382
14.0k
        break;
383
73
    case PayloadKind::SIGNED:
384
73
        write_signed128(output, _signed_value, _payload_width);
385
73
        break;
386
2.21k
    case PayloadKind::BORROWED_BYTES:
387
2.21k
        if (_borrowed.size != 0) {
388
2.21k
            std::memcpy(output, _borrowed.data, _borrowed.size);
389
2.21k
            output += _borrowed.size;
390
2.21k
        }
391
2.21k
        break;
392
9
    case PayloadKind::UUID:
393
9
        std::memcpy(output, _uuid.data(), _uuid.size());
394
9
        output += _uuid.size();
395
9
        break;
396
13
    case PayloadKind::INLINE_STRING:
397
13
        std::memcpy(output, _inline_string.data(), _inline_size);
398
13
        output += _inline_size;
399
13
        break;
400
1.08M
    }
401
1.08M
    DCHECK_EQ(output, destination + _encoded_size);
402
1.08M
}
403
404
} // namespace doris