Skip to content

Commit 164b2dc

Browse files
authored
Merge pull request #1996 from Jumaga2015/fix/1762-paired-free-functions
fix(store,ui): export paired free functions for FFI callers (#1762)
2 parents d01ba80 + 5842b84 commit 164b2dc

8 files changed

Lines changed: 237 additions & 30 deletions

File tree

‎src/mcp/mcp.c‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8280,12 +8280,10 @@ static int arch_compute_cycles(cbm_store_t *store, const char *project, int64_t
82808280
*scanned_edges = ecount;
82818281
scc_graph_t g;
82828282
if (!scc_build(src, tgt, ecount, &g)) {
8283-
free(src);
8284-
free(tgt);
8283+
cbm_store_free_call_edges(src, tgt);
82858284
return CBM_STORE_OK; /* no edges = no cycles, not an error */
82868285
}
8287-
free(src);
8288-
free(tgt);
8286+
cbm_store_free_call_edges(src, tgt);
82898287

82908288
int *comp = malloc((size_t)g.nverts * sizeof(int));
82918289
if (!comp) {

‎src/store/store.c‎

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4165,10 +4165,18 @@ int cbm_store_fetch_call_edges(cbm_store_t *s, const char *project, int max_edge
41654165
bind_text(stmt, SKIP_ONE, project);
41664166
sqlite3_bind_int(stmt, 2, max_edges > 0 ? max_edges + SKIP_ONE : CBM_NOT_FOUND);
41674167

4168+
/* Both arrays are STORE-class memory-core blocks: the caller releases them
4169+
* with cbm_store_free_call_edges, the only free that matches. */
41684170
int cap = ST_INIT_CAP_16;
41694171
int n = 0;
4170-
int64_t *src = malloc((size_t)cap * sizeof(int64_t));
4171-
int64_t *tgt = malloc((size_t)cap * sizeof(int64_t));
4172+
int64_t *src = cbm_alloc(CBM_MEM_CLASS_STORE, (size_t)cap * sizeof(int64_t));
4173+
int64_t *tgt = cbm_alloc(CBM_MEM_CLASS_STORE, (size_t)cap * sizeof(int64_t));
4174+
if (!src || !tgt) {
4175+
sqlite3_finalize(stmt);
4176+
cbm_store_free_call_edges(src, tgt);
4177+
store_set_error(s, "fetch_call_edges: out of memory");
4178+
return CBM_STORE_ERR;
4179+
}
41724180
int scan_rc;
41734181
while ((scan_rc = sqlite3_step(stmt)) == SQLITE_ROW) {
41744182
if (max_edges > 0 && n >= max_edges) {
@@ -4178,18 +4186,31 @@ int cbm_store_fetch_call_edges(cbm_store_t *s, const char *project, int max_edge
41784186
break;
41794187
}
41804188
if (n >= cap) {
4189+
size_t grown_bytes = (size_t)cap * ST_GROWTH * sizeof(int64_t);
4190+
int64_t *grown_src = cbm_realloc(CBM_MEM_CLASS_STORE, src, grown_bytes);
4191+
if (grown_src) {
4192+
src = grown_src;
4193+
}
4194+
int64_t *grown_tgt =
4195+
grown_src ? cbm_realloc(CBM_MEM_CLASS_STORE, tgt, grown_bytes) : NULL;
4196+
if (grown_tgt) {
4197+
tgt = grown_tgt;
4198+
}
4199+
if (!grown_src || !grown_tgt) {
4200+
sqlite3_finalize(stmt);
4201+
cbm_store_free_call_edges(src, tgt);
4202+
store_set_error(s, "fetch_call_edges: out of memory");
4203+
return CBM_STORE_ERR;
4204+
}
41814205
cap *= ST_GROWTH;
4182-
src = safe_realloc(src, (size_t)cap * sizeof(int64_t));
4183-
tgt = safe_realloc(tgt, (size_t)cap * sizeof(int64_t));
41844206
}
41854207
src[n] = sqlite3_column_int64(stmt, 0);
41864208
tgt[n] = sqlite3_column_int64(stmt, 1);
41874209
n++;
41884210
}
41894211
sqlite3_finalize(stmt);
41904212
if (scan_rc != SQLITE_DONE && scan_rc != SQLITE_ROW) {
4191-
free(src);
4192-
free(tgt);
4213+
cbm_store_free_call_edges(src, tgt);
41934214
store_set_error_sqlite(s, "fetch_call_edges scan");
41944215
return CBM_STORE_ERR;
41954216
}
@@ -4199,6 +4220,11 @@ int cbm_store_fetch_call_edges(cbm_store_t *s, const char *project, int max_edge
41994220
return CBM_STORE_OK;
42004221
}
42014222

4223+
void cbm_store_free_call_edges(int64_t *src, int64_t *tgt) {
4224+
cbm_free(CBM_MEM_CLASS_STORE, src);
4225+
cbm_free(CBM_MEM_CLASS_STORE, tgt);
4226+
}
4227+
42024228
int cbm_store_find_edges_by_source(cbm_store_t *s, int64_t source_id, cbm_edge_t **out,
42034229
int *count) {
42044230
bind_id_t b = {source_id};
@@ -10647,6 +10673,10 @@ int cbm_louvain(const int64_t *nodes, int node_count, const cbm_louvain_edge_t *
1064710673
return cbm_leiden(nodes, node_count, edges, edge_count, 1.0, out, out_count);
1064810674
}
1064910675

10676+
void cbm_leiden_free(cbm_louvain_result_t *result) {
10677+
cbm_free(CBM_MEM_CLASS_OTHER, result); /* cbm_leiden allocates OTHER */
10678+
}
10679+
1065010680
/* ── Architecture: community clusters via Leiden ───────────────── */
1065110681

1065210682
enum {

‎src/store/store.h‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -708,12 +708,16 @@ int64_t cbm_store_insert_edge(cbm_store_t *s, const cbm_edge_t *e);
708708
int cbm_store_insert_edge_batch(cbm_store_t *s, const cbm_edge_t *edges, int count);
709709

710710
/* Fetch all CALLS edges among Function/Method nodes for a project as parallel
711-
* (source_id, target_id) arrays (caller frees both). For SCC / cycle analysis.
711+
* (source_id, target_id) arrays, released together with
712+
* cbm_store_free_call_edges. For SCC / cycle analysis.
712713
* Stops at max_edges and sets *truncated — never a silent cap. Returns
713714
* CBM_STORE_OK (or _ERR); *count is the number returned. */
714715
int cbm_store_fetch_call_edges(cbm_store_t *s, const char *project, int max_edges,
715716
int64_t **out_src, int64_t **out_tgt, int *count, bool *truncated);
716717

718+
/* Free the (source_id, target_id) arrays returned by cbm_store_fetch_call_edges. */
719+
void cbm_store_free_call_edges(int64_t *src, int64_t *tgt);
720+
717721
/* Find edges by source node. */
718722
int cbm_store_find_edges_by_source(cbm_store_t *s, int64_t source_id, cbm_edge_t **out, int *count);
719723

@@ -1306,14 +1310,17 @@ typedef struct {
13061310
* reported community is internally connected. The resolution parameter
13071311
* controls granularity (higher -> more, smaller communities); 1.0 is standard.
13081312
* Allocates *out (length *out_count == node_count) in CBM_MEM_CLASS_OTHER;
1309-
* the caller releases it with cbm_free(CBM_MEM_CLASS_OTHER, *out). */
1313+
* the caller releases it with cbm_leiden_free(*out). */
13101314
int cbm_leiden(const int64_t *nodes, int node_count, const cbm_louvain_edge_t *edges,
13111315
int edge_count, double resolution, cbm_louvain_result_t **out, int *out_count);
13121316

13131317
/* Convenience wrapper: cbm_leiden with resolution 1.0. */
13141318
int cbm_louvain(const int64_t *nodes, int node_count, const cbm_louvain_edge_t *edges,
13151319
int edge_count, cbm_louvain_result_t **out, int *out_count);
13161320

1321+
/* Free the result array returned by cbm_leiden / cbm_louvain. */
1322+
void cbm_leiden_free(cbm_louvain_result_t *result);
1323+
13171324
/* ── Memory management helpers ──────────────────────────────────── */
13181325

13191326
/* Free heap-allocated strings in a stack-allocated node (does NOT free the node itself). */

‎src/ui/http_server.c‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2042,7 +2042,7 @@ static bool attach_missed_graph(yyjson_mut_doc *mdoc, yyjson_mut_val *mroot, cbm
20422042
return false;
20432043
}
20442044
yyjson_doc *mldoc = yyjson_read(mjson, strlen(mjson), 0);
2045-
free(mjson);
2045+
cbm_layout_free_json(mjson);
20462046
if (!mldoc) {
20472047
return false;
20482048
}
@@ -2164,7 +2164,7 @@ static void handle_layout(cbm_http_conn_t *c, const cbm_http_req_t *req) {
21642164
return;
21652165
}
21662166
cbm_http_replyf(c, 200, g_cors_json, "%s", scoped_json);
2167-
free(scoped_json);
2167+
cbm_layout_free_json(scoped_json);
21682168
return;
21692169
}
21702170

@@ -2200,13 +2200,13 @@ static void handle_layout(cbm_http_conn_t *c, const cbm_http_req_t *req) {
22002200
if (linked_count == 0 && missed_graph) {
22012201
cbm_store_close(store);
22022202
cbm_http_replyf(c, 200, g_cors_json, "%s", primary_json);
2203-
free(primary_json);
2203+
cbm_layout_free_json(primary_json);
22042204
return;
22052205
}
22062206

22072207
/* Parse primary JSON and append missed_graph + linked_projects */
22082208
yyjson_doc *pdoc = yyjson_read(primary_json, strlen(primary_json), 0);
2209-
free(primary_json);
2209+
cbm_layout_free_json(primary_json);
22102210
if (!pdoc) {
22112211
cbm_store_close(store);
22122212
cbm_http_replyf(c, 500, g_cors_json, "{\"error\":\"JSON parse failed\"}");
@@ -2258,7 +2258,7 @@ static void handle_layout(cbm_http_conn_t *c, const cbm_http_req_t *req) {
22582258

22592259
/* Parse linked project layout */
22602260
yyjson_doc *lpdoc = yyjson_read(lp_json, strlen(lp_json), 0);
2261-
free(lp_json);
2261+
cbm_layout_free_json(lp_json);
22622262
if (!lpdoc) {
22632263
cbm_store_close(lp_store);
22642264
free(linked[li]);

‎src/ui/layout3d.c‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "ui/layout3d.h"
1515
#include "ui/layout_internal.h"
1616
#include "foundation/log.h"
17+
#include "foundation/json_writer.h"
1718
#include "foundation/mem_core.h"
1819
#include "foundation/platform.h"
1920

@@ -899,10 +900,12 @@ char *cbm_layout_to_json(const cbm_layout_result_t *r) {
899900
yyjson_mut_obj_add_val(doc, root, "edges", ea);
900901
yyjson_mut_obj_add_int(doc, root, "total_nodes", r->total_nodes);
901902

903+
/* The output string is an OTHER-class memory-core block, released by
904+
* cbm_layout_free_json; the document keeps its own allocator. */
902905
size_t len = 0;
903906
yyjson_write_err write_err = {0};
904-
char *json =
905-
yyjson_mut_write_opts(doc, YYJSON_WRITE_ALLOW_INVALID_UNICODE, NULL, &len, &write_err);
907+
char *json = yyjson_mut_write_opts(doc, YYJSON_WRITE_ALLOW_INVALID_UNICODE,
908+
cbm_json_writer_allocator(), &len, &write_err);
906909
yyjson_mut_doc_free(doc);
907910
if (!json) {
908911
char code[CBM_SZ_32];
@@ -912,3 +915,7 @@ char *cbm_layout_to_json(const cbm_layout_result_t *r) {
912915
}
913916
return json;
914917
}
918+
919+
void cbm_layout_free_json(char *json) {
920+
cbm_free(CBM_MEM_CLASS_OTHER, json);
921+
}

‎src/ui/layout3d.h‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,12 @@ cbm_layout_result_t *cbm_layout_compute(cbm_store_t *store, const char *project,
6969
/* Release a computed layout result and its fields through their matching allocators. */
7070
void cbm_layout_free(cbm_layout_result_t *result);
7171

72-
/* Serialize layout result to JSON string. Caller must free(). */
72+
/* Serialize layout result to JSON string. Release with cbm_layout_free_json(). */
7373
char *cbm_layout_to_json(const cbm_layout_result_t *result);
7474

75+
/* Free the JSON string returned by cbm_layout_to_json. */
76+
void cbm_layout_free_json(char *json);
77+
7578
/* ── CBM Atlas region level ───────────────────────────────────── */
7679

7780
/* level=regions payload: one body per region (Leiden communities voted per

0 commit comments

Comments
 (0)