mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-09 09:11:00 +08:00
The memory-core linter is a ratchet: raw malloc/calloc/realloc/free/strdup sites may not grow, because allocations belong on one accounted path (src/foundation/mem_core.h) rather than spread across hundreds of call sites. This branch's own work had added 35 raw sites across nine files -- the parallel k8s prep, the configlink key matching, the worker pool's counted path, the Go shared stdlib registry, the spill namespace copy, the route service array and the namespace-name arrays. All of them now use cbm_alloc/cbm_calloc/cbm_realloc/cbm_free/ cbm_mem_strdup. One was a latent bug rather than a style point: pass_k8s freed a buffer that k8s_read_file had allocated with raw malloc, so converting only the free would have mismatched the allocator -- the read function and every free of its result are converted together. The Windows free-space path widens its argument through the memory core instead of cbm_utf8_to_wide(), which allocates with raw malloc. Two files improved enough to tighten their baselines, which the ratchet requires so the gain cannot be quietly given back: sqlite_writer.c 92->74 (the index-cell arena), pass_k8s.c 13->6 and registry.c 19->17. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
137 lines
7.0 KiB
C
137 lines
7.0 KiB
C
/*
|
|
* dyn_array.h — Type-safe growable arrays via macros (header-only).
|
|
*
|
|
* Usage:
|
|
* CBM_DYN_ARRAY(int) nums = {0}; // zero-init
|
|
* cbm_da_push(&nums, 42);
|
|
* cbm_da_push(&nums, 99);
|
|
* for (int i = 0; i < nums.count; i++)
|
|
* printf("%d\n", nums.items[i]);
|
|
* cbm_da_free(&nums);
|
|
*
|
|
* Design:
|
|
* - Items are contiguous in memory (cache-friendly)
|
|
* - Grows by 2x (amortized O(1) push)
|
|
* - Uses realloc — NOT arena-compatible (use CBMDefArray etc. for arena)
|
|
* - Header-only: no .c file needed
|
|
*/
|
|
#ifndef CBM_DYN_ARRAY_H
|
|
#define CBM_DYN_ARRAY_H
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "mem_core.h" /* same directory: units without -Isrc include this header relatively */
|
|
#if defined(CBM_MEMWASTE) && CBM_MEMWASTE
|
|
#include "mem_events.h"
|
|
#define CBM_DA_WASTE_NOTE(da) \
|
|
cbm_memev_da_note((size_t)(da)->cap * sizeof(*(da)->items), \
|
|
(size_t)(da)->count * sizeof(*(da)->items), (size_t)(da)->cap)
|
|
#else
|
|
#define CBM_DA_WASTE_NOTE(da) ((void)0)
|
|
#endif
|
|
|
|
/* Declare a dynamic array type for a given element type. */
|
|
#define CBM_DYN_ARRAY(T) \
|
|
struct { \
|
|
T *items; \
|
|
int count; \
|
|
int cap; \
|
|
}
|
|
|
|
/* Push an element. Grows by 2x when full. */
|
|
#define cbm_da_push(da, item) \
|
|
do { \
|
|
if ((da)->count >= (da)->cap) { \
|
|
int _new_cap = (da)->cap ? (da)->cap * 2 : 8; \
|
|
void *_new = cbm_realloc(CBM_MEM_CLASS_DYN_ARRAY, (da)->items, \
|
|
(size_t)_new_cap * sizeof(*(da)->items)); \
|
|
if (!_new) \
|
|
break; \
|
|
(da)->items = _new; \
|
|
(da)->cap = _new_cap; \
|
|
} \
|
|
(da)->items[(da)->count++] = (item); \
|
|
} while (0)
|
|
|
|
/* Push with a caller-chosen first capacity. cbm_da_push starts at 8 slots,
|
|
* which is 64 bytes of headroom on every array; an index that keeps one
|
|
* small array per key (millions of keys, most holding one or two entries)
|
|
* pays that on every key. Growth stays 2x. */
|
|
#define cbm_da_push_min(da, item, min_cap) \
|
|
do { \
|
|
if ((da)->count >= (da)->cap) { \
|
|
int _new_cap = (da)->cap ? (da)->cap * 2 : (min_cap); \
|
|
void *_new = cbm_realloc(CBM_MEM_CLASS_DYN_ARRAY, (da)->items, \
|
|
(size_t)_new_cap * sizeof(*(da)->items)); \
|
|
if (!_new) \
|
|
break; \
|
|
(da)->items = _new; \
|
|
(da)->cap = _new_cap; \
|
|
} \
|
|
(da)->items[(da)->count++] = (item); \
|
|
} while (0)
|
|
|
|
/* Push an element with a pointer return (for in-place init). */
|
|
#define cbm_da_push_ptr(da) \
|
|
(((da)->count >= (da)->cap \
|
|
? ((void)((da)->cap = (da)->cap ? (da)->cap * 2 : 8), \
|
|
(void)((da)->items = cbm_realloc(CBM_MEM_CLASS_DYN_ARRAY, (da)->items, \
|
|
(size_t)(da)->cap * sizeof(*(da)->items)))) \
|
|
: (void)0), \
|
|
&(da)->items[(da)->count++])
|
|
|
|
/* Pop last element. Returns the element. Undefined if empty. */
|
|
#define cbm_da_pop(da) ((da)->items[--(da)->count])
|
|
|
|
/* Get last element without removing. Undefined if empty. */
|
|
#define cbm_da_last(da) ((da)->items[(da)->count - 1])
|
|
|
|
/* Clear without freeing (reset count to 0). */
|
|
#define cbm_da_clear(da) ((da)->count = 0)
|
|
|
|
/* Free all memory. */
|
|
#define cbm_da_free(da) \
|
|
do { \
|
|
CBM_DA_WASTE_NOTE(da); \
|
|
cbm_free(CBM_MEM_CLASS_DYN_ARRAY, (da)->items); \
|
|
(da)->items = NULL; \
|
|
(da)->count = 0; \
|
|
(da)->cap = 0; \
|
|
} while (0)
|
|
|
|
/* Reserve capacity (grow if needed, never shrink). */
|
|
#define cbm_da_reserve(da, n) \
|
|
do { \
|
|
if ((n) > (da)->cap) { \
|
|
void *_new = cbm_realloc(CBM_MEM_CLASS_DYN_ARRAY, (da)->items, \
|
|
(size_t)(n) * sizeof(*(da)->items)); \
|
|
if (_new) { \
|
|
(da)->items = _new; \
|
|
(da)->cap = (n); \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
/* Insert at index, shifting elements right. */
|
|
#define cbm_da_insert(da, idx, item) \
|
|
do { \
|
|
cbm_da_push(da, item); /* ensure space */ \
|
|
if ((idx) < (da)->count - 1) { \
|
|
memmove(&(da)->items[(idx) + 1], &(da)->items[(idx)], \
|
|
(size_t)((da)->count - 1 - (idx)) * sizeof(*(da)->items)); \
|
|
(da)->items[(idx)] = (item); \
|
|
} \
|
|
} while (0)
|
|
|
|
/* Remove at index, shifting elements left. */
|
|
#define cbm_da_remove(da, idx) \
|
|
do { \
|
|
if ((idx) < (da)->count - 1) { \
|
|
memmove(&(da)->items[(idx)], &(da)->items[(idx) + 1], \
|
|
(size_t)((da)->count - 1 - (idx)) * sizeof(*(da)->items)); \
|
|
} \
|
|
(da)->count--; \
|
|
} while (0)
|
|
|
|
#endif /* CBM_DYN_ARRAY_H */
|