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

llnl / dftracer-utils / 30002033718

23 Jul 2026 11:08AM UTC coverage: 52.742% (+0.08%) from 52.66%
30002033718

Pull #99

github

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

41941 of 102829 branches covered (40.79%)

Branch coverage included in aggregate %.

2221 of 2839 new or added lines in 58 files covered. (78.23%)

137 existing lines in 13 files now uncovered.

37042 of 46924 relevant lines covered (78.94%)

59523.07 hits per line

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

44.43
/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) {
90✔
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 = [] {
3!
56
            ankerl::unordered_dense::map<std::string_view, std::int8_t> m;
2✔
57
            for (auto op : posix_ops::READ) m.emplace(op, IO_READ);
22✔
58
            for (auto op : posix_ops::WRITE) m.emplace(op, IO_WRITE);
28✔
59
            for (auto op : posix_ops::SYNC) m.emplace(op, IO_SYNC);
10✔
60
            for (auto op : posix_ops::PCTL) m.emplace(op, IO_PCTL);
14✔
61
            for (auto op : posix_ops::IPC) m.emplace(op, IO_IPC);
24✔
62
            for (auto op : posix_ops::METADATA) m.emplace(op, IO_METADATA);
86✔
63
            return m;
2✔
64
        }();
46!
65
    auto it = op_to_cat.find(func);
90!
66
    return it != op_to_cat.end() ? it->second
90!
67
                                 : static_cast<std::int8_t>(IO_OTHER);
45✔
68
}
69

70
bool str_iequal(std::string_view a, const char *b) {
110✔
71
    std::size_t len = std::strlen(b);
110✔
72
    if (a.size() != len) return false;
110!
73
    for (std::size_t i = 0; i < len; ++i) {
560✔
74
        if (std::tolower(static_cast<unsigned char>(a[i])) !=
705✔
75
            static_cast<unsigned char>(b[i]))
470✔
76
            return false;
20✔
77
    }
225✔
78
    return true;
90✔
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
            }
92
        }
93
        if (match) return true;
×
94
    }
95
    return false;
×
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) {
84✔
111
    using SVH = JsonValueHelper;
112
    ParsedArgs a;
84✔
113
    auto obj = args_val.get_object();
84✔
114
    if (obj.error()) return a;
84!
115
    for (auto field : obj.value_unsafe()) {
332✔
116
        if (field.error()) continue;
248!
117
        auto key_r = field.unescaped_key();
248✔
118
        if (key_r.error()) continue;
248!
119
        std::string_view key = key_r.value_unsafe();
248✔
120
        auto val_r = field.value();
248✔
121
        if (val_r.error()) continue;
248!
122
        auto val = val_r.value_unsafe();
248✔
123
        if (key == "name") {
248✔
124
            if (auto s = SVH::get_string(val)) a.name = s;
4!
125
        } else if (key == "value") {
246✔
126
            if (auto s = SVH::get_string(val)) a.value = s;
4!
127
        } else if (key == "hhash") {
242✔
128
            if (auto s = SVH::get_string(val)) a.hhash = s;
80!
129
        } else if (key == "fhash") {
200!
130
            if (auto s = SVH::get_string(val)) a.fhash = s;
×
131
        } else if (key == "epoch") {
160!
132
            if (auto i = SVH::get_int64(val)) a.epoch = i;
×
133
        } else if (key == "step") {
160!
134
            if (auto i = SVH::get_int64(val)) a.step = i;
×
135
        } else if (key == "size_sum") {
160!
136
            if (auto i = SVH::get_int64(val)) a.size_sum = i;
×
137
        } else if (key == "ret") {
160✔
138
            if (auto i = SVH::get_int64(val)) a.ret = i;
80!
139
        } else if (key == "offset") {
120!
140
            if (auto i = SVH::get_int64(val)) a.offset = i;
×
141
        } else if (key == "image_idx") {
80!
142
            if (auto i = SVH::get_int64(val)) a.image_idx = i;
×
143
        } else if (key == "image_size") {
80!
144
            if (auto i = SVH::get_int64(val)) a.image_size = i;
×
145
        } else {
146
            if (auto i = SVH::get_int64(val)) {
80!
147
                a.int_map[std::string(key)] = *i;
×
148
            } else if (auto d = SVH::get_double(val)) {
80!
149
                a.float_map[std::string(key)] = *d;
×
150
            }
151
        }
152
    }
153
    return a;
84✔
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);
×
170
        }
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);
×
186
        }
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,
94✔
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;
94✔
199
    std::optional<std::int64_t> pid_opt, tid_opt, ts_opt, dur_opt;
94✔
200
    ParsedArgs args;
94✔
201
    parser.for_each_field(
141!
202
        [&](std::string_view key, simdjson::ondemand::value val) {
795✔
203
            if (key == "ph") {
748✔
204
                if (auto s = SVH::get_string(val)) ph = *s;
94!
205
            } else if (key == "name") {
701✔
206
                if (auto s = SVH::get_string(val)) name_sv = *s;
94!
207
            } else if (key == "cat") {
607✔
208
                if (auto s = SVH::get_string(val)) cat_sv = *s;
94!
209
            } else if (key == "pid") {
513✔
210
                pid_opt = SVH::get_int64(val);
94✔
211
            } else if (key == "tid") {
419✔
212
                tid_opt = SVH::get_int64(val);
94✔
213
            } else if (key == "ts") {
325✔
214
                ts_opt = SVH::get_int64(val);
90✔
215
            } else if (key == "dur") {
233✔
216
                dur_opt = SVH::get_int64(val);
90✔
217
            } else if (key == "args") {
143✔
218
                args = parse_row_args(val);
84✔
219
            }
42✔
220
        });
748✔
221

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

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

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

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

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

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

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

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

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

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

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

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

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

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

349
        // size priority: size_sum > POSIX ret > image_size
350
        if (args.size_sum) {
90!
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) {
90!
354
            io_cat = get_io_cat(out_name);
90!
355
            if (args.ret && *args.ret > 0 &&
140!
356
                (io_cat == IO_READ || io_cat == IO_WRITE))
65✔
357
                builder.append_int64(ci_size, *args.ret);
60!
358
            if (args.offset && *args.offset >= 0)
90!
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);
90!
368
    }
45✔
369

370
    // --- Profile columns ---
371
    if (is_profile) {
94!
372
        bool is_posix_stdio =
373
            str_iequal(cat_sv, "posix") || str_iequal(cat_sv, "stdio");
×
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);
×
379
    }
380

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

386
    builder.end_row();
94!
387
    return true;
94✔
388
}
94✔
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()) {
×
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()) {
×
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);
×
441
                } else {
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();
×
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();
×
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);
×
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()));
×
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,
94✔
501
                     StringArena &arena, bool normalize,
502
                     TimeScaleState &time_scale) {
503
    if (normalize) return normalize_row(builder, arena, parser, time_scale);
94!
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()) {
×
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()) {
×
521
                    std::size_t idx =
522
                        builder.add_or_get_column(key_sv, ColumnType::UINT64);
×
523
                    builder.append_uint64(idx, num.get_uint64());
×
524
                } else {
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();
×
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();
×
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);
×
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()));
×
562
                } else {
563
                    builder.append_null(idx);
×
564
                }
565
                break;
566
            }
×
567
            default:
568
                break;
×
569
        }
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(
×
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);
×
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