@@ -350,6 +350,36 @@ static void free_edge_array(cbm_edge_t *edges, int count) {
350350 free (edges );
351351}
352352
353+ /* ── Node ID → index map (for O(log n) edge filtering) ───────── */
354+
355+ typedef struct {
356+ int64_t id ;
357+ int idx ;
358+ } node_id_entry_t ;
359+
360+ static int cmp_node_id_entry (const void * a , const void * b ) {
361+ int64_t da = ((const node_id_entry_t * )a )-> id ;
362+ int64_t db = ((const node_id_entry_t * )b )-> id ;
363+ return (da > db ) - (da < db );
364+ }
365+
366+ static int find_node_index (const node_id_entry_t * map , int count , int64_t id ) {
367+ int lo = 0 ;
368+ int hi = count - SKIP_ONE ;
369+ while (lo <= hi ) {
370+ int mid = lo + (hi - lo ) / PAIR_LEN ;
371+ if (map [mid ].id == id ) {
372+ return map [mid ].idx ;
373+ }
374+ if (map [mid ].id < id ) {
375+ lo = mid + SKIP_ONE ;
376+ } else {
377+ hi = mid - SKIP_ONE ;
378+ }
379+ }
380+ return CBM_NOT_FOUND ;
381+ }
382+
353383/* ── Public API ───────────────────────────────────────────────── */
354384
355385cbm_layout_result_t * cbm_layout_compute (cbm_store_t * store , const char * project ,
@@ -385,61 +415,82 @@ cbm_layout_result_t *cbm_layout_compute(cbm_store_t *store, const char *project,
385415 return r ;
386416 }
387417
388- /* 2. Query edges */
389- int total_edges = 0 , edge_cap = CBM_SZ_256 ;
418+ /* 2. Build sorted node-ID → index map for O(log n) edge filtering */
419+ node_id_entry_t * id_map = malloc ((size_t )n * sizeof (node_id_entry_t ));
420+ if (!id_map ) {
421+ cbm_store_search_free (& search_out );
422+ cbm_layout_result_t * r = calloc (CBM_ALLOC_ONE , sizeof (* r ));
423+ if (r ) {
424+ r -> total_nodes = total_count ;
425+ }
426+ return r ;
427+ }
428+ for (int i = 0 ; i < n ; i ++ ) {
429+ id_map [i ].id = search_out .results [i ].node .id ;
430+ id_map [i ].idx = i ;
431+ }
432+ qsort (id_map , (size_t )n , sizeof (node_id_entry_t ), cmp_node_id_entry );
433+
434+ /* 3. Query edges — filter during fetch via binary search (O(e log n)) */
435+ int * deg = calloc ((size_t )n , sizeof (int ));
436+ int mapped = 0 ;
437+ int edge_cap = CBM_SZ_256 ;
390438 cbm_edge_t * all_edges = malloc ((size_t )edge_cap * sizeof (cbm_edge_t ));
439+ int * es = malloc ((size_t )edge_cap * sizeof (int ));
440+ int * ed = malloc ((size_t )edge_cap * sizeof (int ));
391441 cbm_schema_info_t schema ;
392442 memset (& schema , 0 , sizeof (schema ));
393- if (cbm_store_get_schema (store , project , & schema ) == CBM_STORE_OK ) {
443+ if (deg && all_edges && es && ed &&
444+ cbm_store_get_schema (store , project , & schema ) == CBM_STORE_OK ) {
394445 for (int t = 0 ; t < schema .edge_type_count ; t ++ ) {
395446 cbm_edge_t * te = NULL ;
396447 int tc = 0 ;
397448 if (cbm_store_find_edges_by_type (store , project , schema .edge_types [t ].type , & te , & tc ) ==
398449 CBM_STORE_OK ) {
399450 for (int e = 0 ; e < tc ; e ++ ) {
400- if (total_edges >= edge_cap ) {
401- edge_cap *= 2 ;
402- cbm_edge_t * tmp = realloc (all_edges , (size_t )edge_cap * sizeof (cbm_edge_t ));
403- if (!tmp ) {
404- free_edge_array (te + e , tc - e );
405- break ;
451+ int si = find_node_index (id_map , n , te [e ].source_id );
452+ int di = find_node_index (id_map , n , te [e ].target_id );
453+ if (si >= 0 && di >= 0 ) {
454+ if (mapped >= edge_cap ) {
455+ int nc = edge_cap * PAIR_LEN ;
456+ cbm_edge_t * te2 = realloc (all_edges , (size_t )nc * sizeof (cbm_edge_t ));
457+ int * ts = realloc (es , (size_t )nc * sizeof (int ));
458+ int * td = realloc (ed , (size_t )nc * sizeof (int ));
459+ if (!te2 || !ts || !td ) {
460+ if (te2 )
461+ all_edges = te2 ;
462+ if (ts )
463+ es = ts ;
464+ if (td )
465+ ed = td ;
466+ free_edge_array (te + e , tc - e );
467+ goto edges_done ;
468+ }
469+ all_edges = te2 ;
470+ es = ts ;
471+ ed = td ;
472+ edge_cap = nc ;
406473 }
407- all_edges = tmp ;
474+ all_edges [mapped ] = te [e ];
475+ memset (& te [e ], 0 , sizeof (cbm_edge_t ));
476+ es [mapped ] = si ;
477+ ed [mapped ] = di ;
478+ deg [si ]++ ;
479+ deg [di ]++ ;
480+ mapped ++ ;
481+ } else {
482+ free ((void * )te [e ].project );
483+ free ((void * )te [e ].type );
484+ free ((void * )te [e ].properties_json );
408485 }
409- all_edges [total_edges ++ ] = te [e ];
410- memset (& te [e ], 0 , sizeof (cbm_edge_t ));
411486 }
412487 free (te );
413488 }
414489 }
490+ edges_done :
415491 cbm_store_schema_free (& schema );
416492 }
417-
418- /* 3. Map edges + degree */
419- int * deg = calloc ((size_t )n , sizeof (int ));
420- int * es = calloc ((size_t )(total_edges > 0 ? total_edges : 1 ), sizeof (int ));
421- int * ed = calloc ((size_t )(total_edges > 0 ? total_edges : 1 ), sizeof (int ));
422- int mapped = 0 ;
423- if (es && ed && deg ) {
424- for (int e = 0 ; e < total_edges ; e ++ ) {
425- int si = -1 , di = -1 ;
426- for (int i = 0 ; i < n ; i ++ ) {
427- if (search_out .results [i ].node .id == all_edges [e ].source_id )
428- si = i ;
429- if (search_out .results [i ].node .id == all_edges [e ].target_id )
430- di = i ;
431- if (si >= 0 && di >= 0 )
432- break ;
433- }
434- if (si >= 0 && di >= 0 ) {
435- es [mapped ] = si ;
436- ed [mapped ] = di ;
437- deg [si ]++ ;
438- deg [di ]++ ;
439- mapped ++ ;
440- }
441- }
442- }
493+ free (id_map );
443494
444495 /* 4. Call depth for z-axis */
445496 int * cdepth = calloc ((size_t )n , sizeof (int ));
@@ -462,7 +513,7 @@ cbm_layout_result_t *cbm_layout_compute(cbm_store_t *store, const char *project,
462513 free (ed );
463514 free (cdepth );
464515 cbm_layout_free (result );
465- free_edge_array (all_edges , total_edges );
516+ free_edge_array (all_edges , mapped );
466517 cbm_store_search_free (& search_out );
467518 return NULL ;
468519 }
@@ -545,7 +596,7 @@ cbm_layout_result_t *cbm_layout_compute(cbm_store_t *store, const char *project,
545596 free (es );
546597 free (ed );
547598 free (cdepth );
548- free_edge_array (all_edges , total_edges );
599+ free_edge_array (all_edges , mapped );
549600 cbm_store_search_free (& search_out );
550601 return result ;
551602}
0 commit comments