mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-03 08:03:38 +08:00
perf(coverage): find a line by table lookup, not by rescanning the file (#963)
cbm_refine_regions_with_pp_lines asks cbm_line_is_toplevel_macro_call about every preprocessor-parsed line in a region. That check walked the source from byte 0 to find the line, so the refinement cost bytes times lines — on exactly the whole-file-ERROR C shape the refinement exists to narrow. Build one line-offset table per file, and hand the byte span to the macro check. The line-based form stays for the two callers that ask about a whole region, where a single walk is cheaper than a table. A file with no table (the allocation failed) still gets the old walk, so the answer never changes. Measured on one C file whose file-scope brace is split by an #ifndef, padded to 5k / 10k / 20k / 40k lines. Release build, one machine, seconds: lines main branch this commit 5000 0.98 1.14 0.98 10000 3.30 4.02 3.41 20000 11.64 14.77 12.56 40000 44.04 58.58 46.90 The branch ran 33% over main at 40k lines and now runs 6.5% over. The reported ranges do not change: 8-8, where main reports the whole file as 5-40000. A profile of the 40k run no longer reaches the macro check. What is left is cbm_line_contains and cbm_span_contains_callable_def, which main carries too and this branch does not touch. The fixture holds 40k definitions, which makes those two cost more here than on a smaller file. parse_coverage, extraction, pipeline, mcp and index_resilience: 964 passed, 4 skipped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Joshua Richter <jrichter5781@gmail.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
f871178b8c
commit
b04f54506a
+77
-23
@@ -1208,27 +1208,12 @@ static void cbm_subtract_recovered_regions(cbm_error_regions_t *regs, const CBMD
|
||||
* missing from the graph; it's a benign call the grammar can't parse without the
|
||||
* preprocessor. True if the [start_line, end_line] span contains a call `NAME(` to
|
||||
* a file-defined function-like macro (Macro label + a parameter signature). */
|
||||
static bool cbm_span_is_macro_invocation(const char *src, int src_len, uint32_t start_line,
|
||||
uint32_t end_line, const CBMDefArray *defs) {
|
||||
if (!src || src_len <= 0 || !defs || start_line == 0 || end_line < start_line) {
|
||||
static bool cbm_byte_span_is_macro_invocation(const char *src, int src_len, int span_start,
|
||||
int span_end, const CBMDefArray *defs) {
|
||||
if (!src || src_len <= 0 || !defs || span_start < 0 || span_end > src_len ||
|
||||
span_start >= span_end) {
|
||||
return false;
|
||||
}
|
||||
int span_start = 0;
|
||||
uint32_t line = 1;
|
||||
while (span_start < src_len && line < start_line) {
|
||||
if (src[span_start++] == '\n') {
|
||||
line++;
|
||||
}
|
||||
}
|
||||
if (line != start_line) {
|
||||
return false;
|
||||
}
|
||||
int span_end = span_start;
|
||||
while (span_end < src_len && line <= end_line) {
|
||||
if (src[span_end++] == '\n') {
|
||||
line++;
|
||||
}
|
||||
}
|
||||
for (int di = 0; di < defs->count; di++) {
|
||||
const CBMDefinition *d = &defs->items[di];
|
||||
/* Function-like macros only: an object-like macro (#define PI 3.14) has no
|
||||
@@ -1256,6 +1241,66 @@ static bool cbm_span_is_macro_invocation(const char *src, int src_len, uint32_t
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Byte offset where every 1-based line starts, so finding a line's span costs
|
||||
* one table read instead of a walk from the start of the file.
|
||||
*
|
||||
* The table holds line_count + 2 entries. Entry [L] is where line L starts, and
|
||||
* the last entry is the end of the source, which gives the final line somewhere
|
||||
* to stop. A line the file never reaches starts at the end of the source, so its
|
||||
* span is empty and nothing can match inside it — the same answer the walk gives.
|
||||
*
|
||||
* Returns NULL when the allocation fails; a caller then falls back to the walk. */
|
||||
static int *cbm_build_line_offsets(const char *src, int src_len, uint32_t line_count) {
|
||||
int *offsets = (int *)malloc(((size_t)line_count + 2) * sizeof(int));
|
||||
if (!offsets) {
|
||||
return NULL;
|
||||
}
|
||||
for (uint32_t l = 0; l <= line_count + 1; l++) {
|
||||
offsets[l] = src_len;
|
||||
}
|
||||
offsets[0] = 0;
|
||||
offsets[1] = 0;
|
||||
uint32_t line = 1;
|
||||
for (int i = 0; i < src_len; i++) {
|
||||
if (src[i] != '\n') {
|
||||
continue;
|
||||
}
|
||||
line++;
|
||||
if (line > line_count + 1) {
|
||||
break;
|
||||
}
|
||||
offsets[line] = i + 1;
|
||||
}
|
||||
return offsets;
|
||||
}
|
||||
|
||||
/* Same question by line number, for the few callers that ask about one region
|
||||
* rather than every line of a file. This form walks the source to find the span,
|
||||
* which is why the per-line caller below uses a table instead. */
|
||||
static bool cbm_span_is_macro_invocation(const char *src, int src_len, uint32_t start_line,
|
||||
uint32_t end_line, const CBMDefArray *defs) {
|
||||
if (!src || src_len <= 0 || !defs || start_line == 0 || end_line < start_line) {
|
||||
return false;
|
||||
}
|
||||
int span_start = 0;
|
||||
uint32_t line = 1;
|
||||
while (span_start < src_len && line < start_line) {
|
||||
if (src[span_start++] == '\n') {
|
||||
line++;
|
||||
}
|
||||
}
|
||||
if (line != start_line) {
|
||||
return false;
|
||||
}
|
||||
int span_end = span_start;
|
||||
while (span_end < src_len && line <= end_line) {
|
||||
if (src[span_end++] == '\n') {
|
||||
line++;
|
||||
}
|
||||
}
|
||||
return cbm_byte_span_is_macro_invocation(src, src_len, span_start, span_end, defs);
|
||||
}
|
||||
|
||||
/* True if [rs, re] is fully enclosed by an extracted callable definition (a
|
||||
* Function/Method body). A macro invocation INSIDE a real function body is an
|
||||
* expression-level use where nothing is missing (#1071). A TOP-LEVEL invocation
|
||||
@@ -1327,9 +1372,12 @@ static void cbm_push_trimmed_run(cbm_error_regions_t *out, uint32_t start, uint3
|
||||
* An invocation INSIDE a function body is the benign #1071 case and is left
|
||||
* alone here — cbm_subtract_macro_invocation_regions handles it later. */
|
||||
static bool cbm_line_is_toplevel_macro_call(const char *src, int src_len, uint32_t line,
|
||||
const CBMDefArray *defs) {
|
||||
return cbm_span_is_macro_invocation(src, src_len, line, line, defs) &&
|
||||
!cbm_region_inside_callable(line, line, defs);
|
||||
const int *line_offsets, const CBMDefArray *defs) {
|
||||
bool is_call = line_offsets
|
||||
? cbm_byte_span_is_macro_invocation(src, src_len, line_offsets[line],
|
||||
line_offsets[line + 1], defs)
|
||||
: cbm_span_is_macro_invocation(src, src_len, line, line, defs);
|
||||
return is_call && !cbm_region_inside_callable(line, line, defs);
|
||||
}
|
||||
|
||||
/* Cut every raw region down to the lines the preprocessed parse could not
|
||||
@@ -1344,13 +1392,18 @@ static void cbm_refine_regions_with_pp_lines(cbm_error_regions_t *regs, const ui
|
||||
uint32_t line_count, const char *src, int src_len,
|
||||
const CBMDefArray *defs) {
|
||||
cbm_error_regions_t out = {{0}, {0}, 0, regs->dropped};
|
||||
/* One offset table for the whole file. The macro check below runs once per
|
||||
* line, and without the table each of those calls walks the source from byte
|
||||
* 0 to find its line — bytes times lines, on exactly the whole-file-error
|
||||
* shape this refinement exists to narrow. */
|
||||
int *line_offsets = cbm_build_line_offsets(src, src_len, line_count);
|
||||
for (int i = 0; i < regs->count; i++) {
|
||||
uint32_t run_start = 0;
|
||||
uint32_t run_end = 0;
|
||||
uint32_t end = regs->ends[i] < line_count ? regs->ends[i] : line_count;
|
||||
for (uint32_t line = regs->starts[i]; line <= end; line++) {
|
||||
if ((map[line] & CBM_LINE_PP_PARSED) &&
|
||||
!cbm_line_is_toplevel_macro_call(src, src_len, line, defs)) {
|
||||
!cbm_line_is_toplevel_macro_call(src, src_len, line, line_offsets, defs)) {
|
||||
if (run_start != 0) {
|
||||
cbm_push_trimmed_run(&out, run_start, run_end, map, line_count);
|
||||
run_start = 0;
|
||||
@@ -1366,6 +1419,7 @@ static void cbm_refine_regions_with_pp_lines(cbm_error_regions_t *regs, const ui
|
||||
cbm_push_trimmed_run(&out, run_start, run_end, map, line_count);
|
||||
}
|
||||
}
|
||||
free(line_offsets);
|
||||
*regs = out;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user