| 1413 | } |
| 1414 | |
| 1415 | static CBMDumpNode *build_dump_nodes(cbm_gbuf_t *gb, int live_count, int64_t *temp_to_final, |
| 1416 | int64_t max_temp_id, int *out_count, |
| 1417 | cbm_gbuf_node_t ***src_out) { |
| 1418 | size_t cap = (size_t)(live_count > 0 ? live_count : SKIP_ONE); |
| 1419 | CBMDumpNode *dump_nodes = malloc(cap * sizeof(CBMDumpNode)); |
| 1420 | /* Parallel gbuf-node pointers so a streamed partition can free its heavy |
| 1421 | * properties_json after the rows are persisted. NULL on OOM disables the |
| 1422 | * per-partition free (the dump still succeeds). */ |
| 1423 | cbm_gbuf_node_t **src = malloc(cap * sizeof(cbm_gbuf_node_t *)); |
| 1424 | int idx = 0; |
| 1425 | |
| 1426 | for (int i = 0; i < gb->nodes.count; i++) { |
| 1427 | cbm_gbuf_node_t *n = gb->nodes.items[i]; |
| 1428 | if (!n->qualified_name || !cbm_ht_get(gb->node_by_qn, n->qualified_name)) { |
| 1429 | continue; |
| 1430 | } |
| 1431 | |
| 1432 | int64_t final_id = idx + SKIP_ONE; /* 1-based sequential */ |
| 1433 | if (n->id < max_temp_id) { |
| 1434 | temp_to_final[n->id] = final_id; |
| 1435 | } |
| 1436 | |
| 1437 | const char *fp = n->file_path ? n->file_path : ""; |
| 1438 | const char *props = n->properties_json ? n->properties_json : "{}"; |
| 1439 | dump_nodes[idx] = (CBMDumpNode){ |
| 1440 | .id = final_id, |
| 1441 | .project = gb->project, |
| 1442 | .label = n->label, |
| 1443 | .name = n->name, |
| 1444 | .qualified_name = n->qualified_name, |
| 1445 | .file_path = fp, |
| 1446 | .start_line = n->start_line, |
| 1447 | .end_line = n->end_line, |
| 1448 | .properties = props, |
| 1449 | }; |
| 1450 | if (src) { |
| 1451 | src[idx] = n; |
| 1452 | } |
| 1453 | idx++; |
| 1454 | } |
| 1455 | |
| 1456 | *out_count = idx; |
| 1457 | *src_out = src; |
| 1458 | return dump_nodes; |
| 1459 | } |
| 1460 | |
| 1461 | /* Build dump-ready edge array with remapped IDs. Returns url_paths and |
| 1462 | * local_names (heap string arrays owned by the caller) via out params. */ |
no test coverage detected