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

llnl / dftracer-utils / 29787659139

20 Jul 2026 11:34PM UTC coverage: 51.578% (-1.1%) from 52.66%
29787659139

Pull #99

github

web-flow
Merge ee805adeb into 06bc84ec9
Pull Request #99: Support CM time_metric (NS/MS/SEC/US) across reader and viz

34832 of 86278 branches covered (40.37%)

Branch coverage included in aggregate %.

1034 of 1319 new or added lines in 30 files covered. (78.39%)

5193 existing lines in 197 files now uncovered.

35349 of 49791 relevant lines covered (70.99%)

9765.54 hits per line

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

42.84
/src/dftracer/utils/utilities/reader/internal/arrow_row_builder.cpp
1
#include <dftracer/utils/utilities/reader/internal/arrow_row_builder.h>
2

3
#ifdef DFTRACER_UTILS_ENABLE_ARROW
4

5
#include <ankerl/unordered_dense.h>
6
#include <dftracer/utils/core/utils/string.h>
7
#include <dftracer/utils/utilities/composites/dft/internal/utils.h>
8

9
#include <cctype>
10
#include <cstdint>
11
#include <cstring>
12
#include <optional>
13
#include <string>
14
#include <string_view>
15
#include <unordered_map>
16

17
namespace dftracer::utils::utilities::reader::internal {
18

19
using common::arrow::ColumnType;
20
using common::arrow::RecordBatchBuilder;
21
using common::json::JsonParser;
22
using common::json::JsonValueHelper;
23

24
namespace {
25

26
// --- Row type constants (must match Python TYPE_* constants) ---
27
enum RowType : std::int8_t {
28
    ROW_EVENT = 0,
29
    ROW_FILE_HASH = 1,
30
    ROW_HOST_HASH = 2,
31
    ROW_STRING_HASH = 3,
32
    ROW_METADATA = 4,
33
    ROW_PROC_METADATA = 5,
34
    ROW_PROFILE = 6,
35
    ROW_SYSTEM = 7,
36
};
37

38
// --- IO category constants (must match Python IOCategory values) ---
39
enum IOCat : std::int8_t {
40
    IO_READ = 1,
41
    IO_WRITE = 2,
42
    IO_METADATA = 3,
43
    IO_PCTL = 4,
44
    IO_IPC = 5,
45
    IO_OTHER = 6,
46
    IO_SYNC = 7,
47
};
48

49
std::int8_t get_io_cat(std::string_view func) {
45✔
50
    using namespace dftracer::utils::utilities::composites::dft::internal;
51
    // Op sets are disjoint, so a single flat lookup preserves the original
52
    // first-match semantics. Keys view the constexpr op arrays (static
53
    // storage).
54
    static const ankerl::unordered_dense::map<std::string_view, std::int8_t>
46!
55
        op_to_cat = [] {
2!
56
            ankerl::unordered_dense::map<std::string_view, std::int8_t> m;
1✔
57
            for (auto op : posix_ops::READ) m.emplace(op, IO_READ);
11!
58
            for (auto op : posix_ops::WRITE) m.emplace(op, IO_WRITE);
14!
59
            for (auto op : posix_ops::SYNC) m.emplace(op, IO_SYNC);
5!
60
            for (auto op : posix_ops::PCTL) m.emplace(op, IO_PCTL);
7!
61
            for (auto op : posix_ops::IPC) m.emplace(op, IO_IPC);
12!
62
            for (auto op : posix_ops::METADATA) m.emplace(op, IO_METADATA);
43!
63
            return m;
1✔
64
        }();
1!
65
    auto it = op_to_cat.find(func);
45✔
66
    return it != op_to_cat.end() ? it->second
45!
67
                                 : static_cast<std::int8_t>(IO_OTHER);
UNCOV
68
}
×
69

70
bool str_iequal(std::string_view a, const char *b) {
55✔
71
    std::size_t len = std::strlen(b);
55✔
72
    if (a.size() != len) return false;
55!
73
    for (std::size_t i = 0; i < len; ++i) {
280✔
74
        if (std::tolower(static_cast<unsigned char>(a[i])) !=
470✔
75
            static_cast<unsigned char>(b[i]))
235✔
76
            return false;
10✔
77
    }
225✔
78
    return true;
45✔
79
}
55✔
80

81
bool str_contains_lower(std::string_view s, const char *needle) {
×
82
    std::size_t nlen = std::strlen(needle);
×
83
    if (s.size() < nlen) return false;
×
84
    for (std::size_t i = 0; i <= s.size() - nlen; ++i) {
×
85
        bool match = true;
×
86
        for (std::size_t j = 0; j < nlen; ++j) {
×
87
            if (std::tolower(static_cast<unsigned char>(s[i + j])) !=
×
88
                static_cast<unsigned char>(needle[j])) {
×
89
                match = false;
×
90
                break;
×
91
            }
UNCOV
92
        }
×
93
        if (match) return true;
×
UNCOV
94
    }
×
95
    return false;
×
UNCOV
96
}
×
97

98
// Fields extracted from a row's "args" object in a single pass.
99
struct ParsedArgs {
100
    std::optional<std::string_view> name, value, hhash, fhash;
101
    std::optional<std::int64_t> epoch, step, size_sum, ret;
102
    std::optional<std::int64_t> offset, image_idx, image_size;
103
    // Other int/float args, kept for profile/sys columns.
104
    std::unordered_map<std::string, std::int64_t> int_map;
105
    std::unordered_map<std::string, double> float_map;
106
};
107

108
// Parse a row's "args" object (already located during the single top-level
109
// walk) into the extracted fields. Mirrors the previous per-key dispatch.
110
ParsedArgs parse_row_args(simdjson::ondemand::value &args_val) {
42✔
111
    using SVH = JsonValueHelper;
112
    ParsedArgs a;
42✔
113
    auto obj = args_val.get_object();
42✔
114
    if (obj.error()) return a;
42!
115
    for (auto field : obj.value_unsafe()) {
166✔
116
        if (field.error()) continue;
124!
117
        auto key_r = field.unescaped_key();
124✔
118
        if (key_r.error()) continue;
124!
119
        std::string_view key = key_r.value_unsafe();
124✔
120
        auto val_r = field.value();
124✔
121
        if (val_r.error()) continue;
124!
122
        auto val = val_r.value_unsafe();
124✔
123
        if (key == "name") {
124!
124
            if (auto s = SVH::get_string(val)) a.name = s;
2!
125
        } else if (key == "value") {
124!
126
            if (auto s = SVH::get_string(val)) a.value = s;
2!
127
        } else if (key == "hhash") {
122!
128
            if (auto s = SVH::get_string(val)) a.hhash = s;
40!
129
        } else if (key == "fhash") {
120!
130
            if (auto s = SVH::get_string(val)) a.fhash = s;
×
131
        } else if (key == "epoch") {
80!
132
            if (auto i = SVH::get_int64(val)) a.epoch = i;
×
133
        } else if (key == "step") {
80!
134
            if (auto i = SVH::get_int64(val)) a.step = i;
×
135
        } else if (key == "size_sum") {
80!
136
            if (auto i = SVH::get_int64(val)) a.size_sum = i;
×
137
        } else if (key == "ret") {
80!
138
            if (auto i = SVH::get_int64(val)) a.ret = i;
40!
139
        } else if (key == "offset") {
80!
140
            if (auto i = SVH::get_int64(val)) a.offset = i;
×
141
        } else if (key == "image_idx") {
40!
142
            if (auto i = SVH::get_int64(val)) a.image_idx = i;
×
143
        } else if (key == "image_size") {
40!
144
            if (auto i = SVH::get_int64(val)) a.image_size = i;
×
UNCOV
145
        } else {
×
146
            if (auto i = SVH::get_int64(val)) {
40!
147
                a.int_map[std::string(key)] = *i;
×
148
            } else if (auto d = SVH::get_double(val)) {
40!
149
                a.float_map[std::string(key)] = *d;
×
UNCOV
150
            }
×
151
        }
152
    }
153
    return a;
42✔
154
}
42!
155

156
void append_profile_columns(
×
157
    RecordBatchBuilder &builder,
158
    const std::unordered_map<std::string, std::int64_t> &int_map) {
159
    static const char *profile_keys[] = {
160
        "count",      "count_max",  "count_min",  "count_sum",  "dft_cnt",
161
        "dur",        "dur_max",    "dur_min",    "dur_sum",    "epoch",
162
        "flags",      "offset",     "offset_max", "offset_min", "offset_sum",
163
        "ret",        "ret_max",    "ret_min",    "ret_sum",    "whence",
164
        "whence_max", "whence_min", "whence_sum", nullptr};
165
    for (const char **pk = profile_keys; *pk; ++pk) {
×
166
        auto it = int_map.find(*pk);
×
167
        if (it != int_map.end()) {
×
168
            auto idx = builder.add_or_get_column(*pk, ColumnType::INT64);
×
169
            builder.append_int64(idx, it->second);
×
UNCOV
170
        }
×
UNCOV
171
    }
×
172
}
×
173

174
void append_system_columns(
×
175
    RecordBatchBuilder &builder,
176
    const std::unordered_map<std::string, double> &float_map) {
177
    static const char *sys_keys[] = {
178
        "user_pct", "system_pct",  "iowait_pct",   "idle_pct",
179
        "irq_pct",  "softirq_pct", "MemAvailable", "MemFree",
180
        "Cached",   "Dirty",       "Active",       nullptr};
181
    for (const char **sk = sys_keys; *sk; ++sk) {
×
182
        auto it = float_map.find(*sk);
×
183
        if (it != float_map.end()) {
×
184
            auto idx = builder.add_or_get_column(*sk, ColumnType::DOUBLE);
×
185
            builder.append_double(idx, it->second);
×
UNCOV
186
        }
×
UNCOV
187
    }
×
188
}
×
189

190
// Normalize a raw JSON row (parsed with simdjson) into the semantic
191
// output schema.  Appends one row to `builder` with the full set of output
192
// columns.  Returns false if the row should be skipped (no valid name).
193
bool normalize_row(RecordBatchBuilder &builder, StringArena &arena,
47✔
194
                   JsonParser &parser, TimeScaleState &time_scale) {
195
    using SVH = JsonValueHelper;
196
    // --- Single-pass extraction: capture top-level fields and args in one
197
    // member walk (dispatch on key, any field order). ---
198
    std::string_view ph, name_sv, cat_sv;
47✔
199
    std::optional<std::int64_t> pid_opt, tid_opt, ts_opt, dur_opt;
47✔
200
    ParsedArgs args;
47✔
201
    parser.for_each_field(
94!
202
        [&](std::string_view key, simdjson::ondemand::value val) {
421✔
203
            if (key == "ph") {
374✔
204
                if (auto s = SVH::get_string(val)) ph = *s;
47!
205
            } else if (key == "name") {
374✔
206
                if (auto s = SVH::get_string(val)) name_sv = *s;
47!
207
            } else if (key == "cat") {
327✔
208
                if (auto s = SVH::get_string(val)) cat_sv = *s;
47!
209
            } else if (key == "pid") {
280✔
210
                pid_opt = SVH::get_int64(val);
47✔
211
            } else if (key == "tid") {
233✔
212
                tid_opt = SVH::get_int64(val);
47✔
213
            } else if (key == "ts") {
186✔
214
                ts_opt = SVH::get_int64(val);
45✔
215
            } else if (key == "dur") {
139✔
216
                dur_opt = SVH::get_int64(val);
45✔
217
            } else if (key == "args") {
94✔
218
                args = parse_row_args(val);
42✔
219
            }
42✔
220
        });
374✔
221

222
    // --- Type classification ---
223
    bool is_M = (ph == "M");
47!
224
    bool is_C = (ph == "C");
47!
225
    bool is_event = !is_M && !is_C;
47✔
226

227
    if (is_M && name_sv == "CM" && args.name && *args.name == "time_metric" &&
47!
228
        args.value) {
2✔
229
        time_scale.metric = composites::dft::parse_time_metric(*args.value);
2!
230
    }
2✔
231

232
    std::int8_t row_type = ROW_EVENT;
47✔
233
    if (is_M) {
47✔
234
        if (name_sv == "FH")
2!
235
            row_type = ROW_FILE_HASH;
×
236
        else if (name_sv == "HH")
2!
237
            row_type = ROW_HOST_HASH;
×
238
        else if (name_sv == "SH")
2!
239
            row_type = ROW_STRING_HASH;
×
240
        else if (name_sv == "PR")
2!
241
            row_type = ROW_PROC_METADATA;
×
242
        else
243
            row_type = ROW_METADATA;
2✔
244
    } else if (is_C) {
47!
245
        row_type = str_iequal(cat_sv, "sys") ? ROW_SYSTEM : ROW_PROFILE;
×
UNCOV
246
    }
×
247
    bool is_hash = (row_type >= ROW_FILE_HASH && row_type <= ROW_STRING_HASH) ||
47!
248
                   row_type == ROW_PROC_METADATA;
47✔
249
    bool is_profile = (row_type == ROW_PROFILE);
47✔
250
    bool is_sys = (row_type == ROW_SYSTEM);
47✔
251

252
    // Name: metadata rows use args.name if available
253
    std::string_view out_name = name_sv;
47✔
254
    if (is_M && args.name && !args.name->empty()) {
47!
255
        out_name = *args.name;
2✔
256
    }
2✔
257
    if (out_name.empty()) return false;  // skip rows without name
47!
258

259
    // --- Declare all output columns ---
260
    auto ci_type = builder.add_or_get_column("type", ColumnType::INT64);
47!
261
    auto ci_cat = builder.add_or_get_column("cat", ColumnType::STRING);
47!
262
    auto ci_name = builder.add_or_get_column("name", ColumnType::STRING);
47!
263
    auto ci_pid = builder.add_or_get_column("pid", ColumnType::INT64);
47!
264
    auto ci_tid = builder.add_or_get_column("tid", ColumnType::INT64);
47!
265
    auto ci_hash = builder.add_or_get_column("hash", ColumnType::STRING);
47!
266
    auto ci_value = builder.add_or_get_column("value", ColumnType::STRING);
47!
267
    auto ci_host_hash =
47✔
268
        builder.add_or_get_column("host_hash", ColumnType::STRING);
47!
269
    auto ci_file_hash =
47✔
270
        builder.add_or_get_column("file_hash", ColumnType::STRING);
47!
271
    auto ci_epoch = builder.add_or_get_column("epoch", ColumnType::INT64);
47!
272
    auto ci_step = builder.add_or_get_column("step", ColumnType::INT64);
47!
273
    auto ci_ts = builder.add_or_get_column("ts", ColumnType::INT64);
47!
274
    auto ci_dur = builder.add_or_get_column("dur", ColumnType::INT64);
47!
275
    auto ci_te = builder.add_or_get_column("te", ColumnType::INT64);
47!
276
    [[maybe_unused]] auto ci_trange =
47✔
277
        builder.add_or_get_column("trange", ColumnType::INT64);
47!
278
    auto ci_io_cat = builder.add_or_get_column("io_cat", ColumnType::INT64);
47!
279
    auto ci_size = builder.add_or_get_column("size", ColumnType::INT64);
47!
280
    auto ci_offset = builder.add_or_get_column("offset", ColumnType::INT64);
47!
281
    auto ci_image_id = builder.add_or_get_column("image_id", ColumnType::INT64);
47!
282

283
    // --- Populate core columns ---
284
    builder.append_int64(ci_type, row_type);
47!
285

286
    // cat (lowercased) - write into arena
287
    if (!cat_sv.empty()) {
47!
288
        char lbuf[256];
289
        std::size_t clen = std::min(cat_sv.size(), sizeof(lbuf));
47!
290
        for (std::size_t i = 0; i < clen; ++i)
278✔
291
            lbuf[i] = static_cast<char>(
231✔
292
                std::tolower(static_cast<unsigned char>(cat_sv[i])));
231!
293
        builder.append_string(ci_cat, arena.push(lbuf, clen));
47!
294
    } else {
47✔
295
        builder.append_null(ci_cat);
×
296
    }
297

298
    builder.append_string(ci_name, out_name);
47!
299

300
    if (pid_opt) builder.append_int64(ci_pid, *pid_opt);
47!
301
    if (tid_opt) builder.append_int64(ci_tid, *tid_opt);
47!
302

303
    // hash / value
304
    if (is_hash && args.value && !args.value->empty())
47!
305
        builder.append_string(ci_hash, *args.value);
×
306
    if (row_type == ROW_METADATA && args.value && !args.value->empty())
47!
307
        builder.append_string(ci_value, *args.value);
2!
308

309
    // host_hash / file_hash
310
    if (args.hhash && !args.hhash->empty())
47!
311
        builder.append_string(ci_host_hash, *args.hhash);
40!
312
    if (args.fhash && !args.fhash->empty())
47!
313
        builder.append_string(ci_file_hash, *args.fhash);
×
314

315
    // epoch / step
316
    if (args.epoch && *args.epoch >= 0)
47!
317
        builder.append_int64(ci_epoch, *args.epoch);
×
318
    if (args.step && *args.step >= 0) builder.append_int64(ci_step, *args.step);
47!
319

320
    // --- Temporal ---
321
    bool has_ts = (is_event || is_C) && ts_opt.has_value();
47!
322
    bool has_dur = dur_opt.has_value();
47✔
323
    std::int64_t ts_val = 0, dur_val = 0;
47✔
324
    const bool scale_time =
47✔
325
        time_scale.target && *time_scale.target != time_scale.metric;
47✔
326
    auto scaled = [&](std::int64_t v) {
53✔
327
        return static_cast<std::int64_t>(composites::dft::scale_between(
6✔
328
            time_scale.metric, *time_scale.target,
6✔
329
            static_cast<std::uint64_t>(v)));
6✔
330
    };
331
    if (has_ts) {
47✔
332
        ts_val = *ts_opt;
45✔
333
        if (scale_time) ts_val = scaled(ts_val);
45!
334
        builder.append_int64(ci_ts, ts_val);
45!
335
    }
45✔
336
    if (is_event && has_ts && has_dur) {
47!
337
        dur_val = *dur_opt;
45✔
338
        if (scale_time) dur_val = scaled(dur_val);
45!
339
        builder.append_int64(ci_dur, dur_val);
45!
340
        builder.append_int64(ci_te, ts_val + dur_val);
45!
341
    }
45✔
342

343
    // --- IO columns (events only) ---
344
    if (is_event) {
47✔
345
        bool is_posix_stdio =
45✔
346
            str_iequal(cat_sv, "posix") || str_iequal(cat_sv, "stdio");
45!
347
        std::int8_t io_cat = IO_OTHER;
45✔
348

349
        // size priority: size_sum > POSIX ret > image_size
350
        if (args.size_sum) {
45!
351
            builder.append_int64(ci_size, *args.size_sum);
×
352
            if (is_posix_stdio) io_cat = get_io_cat(out_name);
×
353
        } else if (is_posix_stdio) {
45!
354
            io_cat = get_io_cat(out_name);
45!
355
            if (args.ret && *args.ret > 0 &&
70!
356
                (io_cat == IO_READ || io_cat == IO_WRITE))
40✔
357
                builder.append_int64(ci_size, *args.ret);
30!
358
            if (args.offset && *args.offset >= 0)
45!
359
                builder.append_int64(ci_offset, *args.offset);
×
360
        } else {
45✔
361
            if (args.image_idx && *args.image_idx > 0)
×
362
                builder.append_int64(ci_image_id, *args.image_idx);
×
363
            if (args.image_size && *args.image_size > 0 &&
×
364
                !str_contains_lower(out_name, "open"))
×
365
                builder.append_int64(ci_size, *args.image_size);
×
366
        }
367
        builder.append_int64(ci_io_cat, io_cat);
45!
368
    }
45✔
369

370
    // --- Profile columns ---
371
    if (is_profile) {
47!
UNCOV
372
        bool is_posix_stdio =
×
373
            str_iequal(cat_sv, "posix") || str_iequal(cat_sv, "stdio");
×
UNCOV
374
        std::int8_t io_cat = is_posix_stdio
×
375
                                 ? get_io_cat(out_name)
×
376
                                 : static_cast<std::int8_t>(IO_OTHER);
377
        builder.append_int64(ci_io_cat, io_cat);
×
378
        append_profile_columns(builder, args.int_map);
×
UNCOV
379
    }
×
380

381
    // --- System columns ---
382
    if (is_sys) {
47!
383
        append_system_columns(builder, args.float_map);
×
UNCOV
384
    }
×
385

386
    builder.end_row();
47!
387
    return true;
47✔
388
}
47✔
389

390
}  // namespace
391

392
// Flatten a simdjson object into "prefix.key" columns using native types.
393
// On type mismatch (same key, different type across rows), appends null.
394
void flatten_object_into(RecordBatchBuilder &builder, StringArena &arena,
×
395
                         std::string_view prefix,
396
                         simdjson::ondemand::object obj) {
397
    using SVH = JsonValueHelper;
398
    char key_buf[512];
399

400
    for (auto field : obj) {
×
401
        if (field.error()) continue;
×
402

403
        auto key_result = field.unescaped_key();
×
404
        if (key_result.error()) continue;
×
405
        std::string_view sk = key_result.value_unsafe();
×
406

407
        auto val_result = field.value();
×
408
        if (val_result.error()) continue;
×
409
        auto sub_val = val_result.value_unsafe();
×
410

411
        std::size_t needed = prefix.size() + 1 + sk.size();
×
412
        if (needed >= sizeof(key_buf)) continue;
×
413
        std::memcpy(key_buf, prefix.data(), prefix.size());
×
414
        key_buf[prefix.size()] = '.';
×
415
        std::memcpy(key_buf + prefix.size() + 1, sk.data(), sk.size());
×
416
        std::string_view full_key(key_buf, needed);
×
417

418
        auto type_result = sub_val.type();
×
419
        if (type_result.error()) continue;
×
420
        auto json_type = type_result.value_unsafe();
×
421

422
        switch (json_type) {
×
423
            case simdjson::ondemand::json_type::number: {
424
                auto num_result = sub_val.get_number();
×
425
                if (num_result.error()) break;
×
426
                auto num = num_result.value_unsafe();
×
427
                if (num.is_int64()) {
×
UNCOV
428
                    auto idx =
×
429
                        builder.add_or_get_column(full_key, ColumnType::INT64);
×
430
                    if (builder.column_type(idx) == ColumnType::INT64)
×
431
                        builder.append_int64(idx, num.get_int64());
×
432
                    else
433
                        builder.append_null(idx);
×
434
                } else if (num.is_uint64()) {
×
UNCOV
435
                    auto idx =
×
436
                        builder.add_or_get_column(full_key, ColumnType::UINT64);
×
437
                    if (builder.column_type(idx) == ColumnType::UINT64)
×
438
                        builder.append_uint64(idx, num.get_uint64());
×
439
                    else
440
                        builder.append_null(idx);
×
UNCOV
441
                } else {
×
UNCOV
442
                    auto idx =
×
443
                        builder.add_or_get_column(full_key, ColumnType::DOUBLE);
×
444
                    if (builder.column_type(idx) == ColumnType::DOUBLE)
×
445
                        builder.append_double(idx, num.get_double());
×
446
                    else
447
                        builder.append_null(idx);
×
448
                }
449
                break;
×
450
            }
451
            case simdjson::ondemand::json_type::string: {
452
                auto str_result = sub_val.get_string();
×
453
                if (str_result.error()) break;
×
454
                auto str = str_result.value_unsafe();
×
UNCOV
455
                auto idx =
×
456
                    builder.add_or_get_column(full_key, ColumnType::STRING);
×
457
                if (builder.column_type(idx) == ColumnType::STRING)
×
458
                    builder.append_string(idx, str);
×
459
                else
460
                    builder.append_null(idx);
×
461
                break;
×
462
            }
463
            case simdjson::ondemand::json_type::boolean: {
464
                auto bool_result = sub_val.get_bool();
×
465
                if (bool_result.error()) break;
×
466
                auto b = bool_result.value_unsafe();
×
UNCOV
467
                auto idx =
×
468
                    builder.add_or_get_column(full_key, ColumnType::BOOL);
×
469
                if (builder.column_type(idx) == ColumnType::BOOL)
×
470
                    builder.append_bool(idx, b);
×
471
                else
472
                    builder.append_null(idx);
×
473
                break;
×
474
            }
475
            case simdjson::ondemand::json_type::null: {
476
                auto existing = builder.find_column(full_key);
×
477
                if (existing) builder.append_null(*existing);
×
478
                break;
×
479
            }
480
            case simdjson::ondemand::json_type::object:
481
            case simdjson::ondemand::json_type::array: {
482
                // Serialize nested object/array to JSON string
483
                auto json_str = SVH::to_json_string(sub_val);
×
UNCOV
484
                auto idx =
×
485
                    builder.add_or_get_column(full_key, ColumnType::STRING);
×
486
                if (json_str) {
×
487
                    builder.append_string(
×
488
                        idx, arena.push(json_str->data(), json_str->size()));
×
UNCOV
489
                } else {
×
490
                    builder.append_null(idx);
×
491
                }
492
                break;
493
            }
×
494
            default:
495
                break;
×
496
        }
497
    }
498
}
×
499

500
bool build_arrow_row(RecordBatchBuilder &builder, JsonParser &parser,
47✔
501
                     StringArena &arena, bool normalize,
502
                     TimeScaleState &time_scale) {
503
    if (normalize) return normalize_row(builder, arena, parser, time_scale);
47!
504

505
    using SVH = JsonValueHelper;
506
    parser.for_each_field([&](std::string_view key_sv,
×
507
                              simdjson::ondemand::value val) {
508
        auto type_result = val.type();
×
509
        if (type_result.error()) return;
×
510
        auto json_type = type_result.value_unsafe();
×
511
        switch (json_type) {
×
512
            case simdjson::ondemand::json_type::number: {
513
                auto num_result = val.get_number();
×
514
                if (num_result.error()) break;
×
515
                auto num = num_result.value_unsafe();
×
516
                if (num.is_int64()) {
×
UNCOV
517
                    std::size_t idx =
×
518
                        builder.add_or_get_column(key_sv, ColumnType::INT64);
×
519
                    builder.append_int64(idx, num.get_int64());
×
520
                } else if (num.is_uint64()) {
×
UNCOV
521
                    std::size_t idx =
×
522
                        builder.add_or_get_column(key_sv, ColumnType::UINT64);
×
523
                    builder.append_uint64(idx, num.get_uint64());
×
UNCOV
524
                } else {
×
UNCOV
525
                    std::size_t idx =
×
526
                        builder.add_or_get_column(key_sv, ColumnType::DOUBLE);
×
527
                    builder.append_double(idx, num.get_double());
×
528
                }
529
                break;
×
530
            }
531
            case simdjson::ondemand::json_type::string: {
532
                auto str_result = val.get_string();
×
533
                if (str_result.error()) break;
×
534
                auto str = str_result.value_unsafe();
×
UNCOV
535
                std::size_t idx =
×
536
                    builder.add_or_get_column(key_sv, ColumnType::STRING);
×
537
                builder.append_string(idx, str);
×
538
                break;
×
539
            }
540
            case simdjson::ondemand::json_type::boolean: {
541
                auto bool_result = val.get_bool();
×
542
                if (bool_result.error()) break;
×
543
                auto b = bool_result.value_unsafe();
×
UNCOV
544
                std::size_t idx =
×
545
                    builder.add_or_get_column(key_sv, ColumnType::BOOL);
×
546
                builder.append_bool(idx, b);
×
547
                break;
×
548
            }
549
            case simdjson::ondemand::json_type::null: {
550
                auto existing = builder.find_column(key_sv);
×
551
                if (existing) builder.append_null(*existing);
×
552
                break;
×
553
            }
554
            case simdjson::ondemand::json_type::object:
555
            case simdjson::ondemand::json_type::array: {
556
                auto json_str = SVH::to_json_string(val);
×
UNCOV
557
                std::size_t idx =
×
558
                    builder.add_or_get_column(key_sv, ColumnType::STRING);
×
559
                if (json_str) {
×
560
                    builder.append_string(
×
561
                        idx, arena.push(json_str->data(), json_str->size()));
×
UNCOV
562
                } else {
×
563
                    builder.append_null(idx);
×
564
                }
565
                break;
566
            }
×
567
            default:
568
                break;
×
569
        }
UNCOV
570
    });
×
571
    builder.end_row();
×
572
    return true;
×
573
}
47✔
574

575
bool process_json_line(RecordBatchBuilder &builder, JsonParser &parser,
×
576
                       StringArena &arena, std::string_view content,
577
                       bool normalize, TimeScaleState &time_scale) {
578
    const char *trimmed;
579
    std::size_t trimmed_length;
580
    if (!dftracer::utils::json_trim_and_validate_with_comma(
×
UNCOV
581
            content.data(), content.size(), trimmed, trimmed_length))
×
582
        return false;
×
583
    if (!parser.parse(std::string_view(trimmed, trimmed_length))) return false;
×
NEW
584
    return build_arrow_row(builder, parser, arena, normalize, time_scale);
×
UNCOV
585
}
×
586

587
}  // namespace dftracer::utils::utilities::reader::internal
588

589
#endif  // DFTRACER_UTILS_ENABLE_ARROW
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