File Coverage

src/mds_buf.c
Criterion Covered Total %
statement 56 58 96.5
branch 24 36 66.6
condition n/a
subroutine n/a
pod n/a
total 80 94 85.1


line stmt bran cond sub pod time code
1             #include "mds_buf.h"
2             #include
3              
4             /* Measured output/input ratios per bench corpus:
5             * progit (prose) 1.04
6             * commonmark-spec 1.12
7             * synth-prose 1.21
8             * synth-tables 2.08 \u2190 dominates the high end
9             * Old heuristic was a flat 1.25\u00d7 + 64, which under-allocated by 2 grows
10             * on table-heavy inputs (a 1MB synth-tables corpus triggered two
11             * SvGROW realloc-copies of >1 MB each). New heuristic samples up to
12             * 4 KB of the head of the input to estimate pipe density; high pipe
13             * density (table corpora) gets 2.25\u00d7, everything else gets 1.50\u00d7.
14             * The 64-byte slack remains so tiny inputs never start at zero. */
15 2444           static size_t mds_buf_predict_cap(const char* s, size_t hint) {
16             size_t scan;
17 2444           size_t pipes = 0, newlines = 0;
18             int table_heavy;
19             size_t cap;
20             size_t i;
21              
22 2444 100         if (hint == 0) return 64;
23 2442           scan = hint < 4096 ? hint : 4096;
24 2442 50         if (s) {
25 73940 100         for (i = 0; i < scan; i++) {
26 71498           unsigned char c = (unsigned char)s[i];
27 71498           pipes += (c == '|');
28 71498           newlines += (c == '\n');
29             }
30             }
31             /* Table heuristic: average >= 1 pipe per line in the sampled head. */
32 2442 100         table_heavy = (newlines && pipes >= newlines);
    100          
33 2442           cap = table_heavy
34 71           ? hint + (hint >> 1) + (hint >> 2) + 64 /* ~2.25\u00d7 */
35 2442 100         : hint + (hint >> 1) + 64; /* ~1.50\u00d7 */
36 2442           return cap;
37             }
38              
39 1           void mds_buf_init(pTHX_ mds_buf* b, SV* sv, size_t hint) {
40             size_t cap;
41 1           b->sv = sv;
42 1           cap = mds_buf_predict_cap(SvPVX(sv), hint);
43             /* Note: at this point sv has no PV yet; the predictor falls into the
44             * `s == NULL` branch and returns the prose-default 1.5\u00d7. Callers
45             * that want table-density sizing should call mds_buf_init_for_input
46             * below with the input pointer. */
47 1 50         SvUPGRADE(sv, SVt_PV);
48 1 50         SvGROW(sv, cap);
    50          
49 1           SvCUR_set(sv, 0);
50 1           SvPOK_on(sv);
51 1           b->base = SvPVX(sv);
52 1           b->cur = b->base;
53 1           b->end = b->base + SvLEN(sv) - 1; /* keep room for NUL */
54 1           *b->cur = '\0';
55 1           }
56              
57 2443           void mds_buf_init_for_input(pTHX_ mds_buf* b, SV* sv,
58             const char* input, size_t hint) {
59             size_t cap;
60 2443           b->sv = sv;
61 2443           cap = mds_buf_predict_cap(input, hint);
62 2443 50         SvUPGRADE(sv, SVt_PV);
63 2443 50         SvGROW(sv, cap);
    50          
64 2443           SvCUR_set(sv, 0);
65 2443           SvPOK_on(sv);
66 2443           b->base = SvPVX(sv);
67 2443           b->cur = b->base;
68 2443           b->end = b->base + SvLEN(sv) - 1;
69 2443           *b->cur = '\0';
70 2443           }
71              
72 82           MDS_COLD void mds_buf_reserve(pTHX_ mds_buf* b, size_t need) {
73 82           size_t used = (size_t)(b->cur - b->base);
74 82           size_t cap = SvLEN(b->sv);
75 82           size_t want = used + need + 1;
76             size_t newcap;
77 82 50         if (want <= cap) {
78             /* SvGROW may have been a no-op; just refresh end */
79 0           b->end = b->base + cap - 1;
80 0           return;
81             }
82 82 50         newcap = cap ? cap : 64;
83 164 100         while (newcap < want) newcap = newcap + (newcap >> 1) + 64; /* 1.5x */
84             /* materialise current cursor into SvCUR so SvGROW preserves it */
85 82           SvCUR_set(b->sv, used);
86 82 50         SvGROW(b->sv, newcap);
    50          
87 82           b->base = SvPVX(b->sv);
88 82           b->cur = b->base + used;
89 82           b->end = b->base + SvLEN(b->sv) - 1;
90             }
91              
92 2444           void mds_buf_finalize(pTHX_ mds_buf* b) {
93 2444           size_t used = (size_t)(b->cur - b->base);
94 2444           SvCUR_set(b->sv, used);
95 2444 50         if (b->base) b->base[used] = '\0';
96 2444           SvPOK_on(b->sv);
97 2444           }