File Coverage

src/simd/mds_simd_scalar.c
Criterion Covered Total %
statement 39 64 60.9
branch 33 46 71.7
condition n/a
subroutine n/a
pod n/a
total 72 110 65.4


line stmt bran cond sub pod time code
1             /* src/simd/mds_simd_scalar.c — reference implementation.
2             *
3             * Defines the semantics; every other backend must produce identical
4             * output for identical input. AVX2/SSE2/NEON backends may delegate
5             * here for operations they have not specialised.
6             */
7             #include "mds_simd.h"
8             #include "mds_classifier_lut.h"
9             #include
10              
11             /* Bytes that "start something" in CommonMark/GFM. Kept in sync with
12             * src/mds_block.c (block recognisers) and src/mds_inline.c (inline).
13             * The set of structural bytes is defined by mds_classifier_truth() in
14             * mds_classifier_lut.h — the scalar path uses that exact same lookup
15             * so the SIMD backends can be fuzz-checked against it. */
16              
17 601           static void classify_structural_scalar(const char* in, size_t len,
18             uint64_t* out_bitmap)
19             {
20 601           const uint8_t* p = (const uint8_t*)in;
21             size_t i;
22 515395 100         for (i = 0; i < len; i++) {
23 514794           uint8_t b = p[i];
24 514794           uint8_t m = (uint8_t)(MDS_CLASSIFIER_LO[b & 0xF] &
25 514794           MDS_CLASSIFIER_HI[b >> 4]);
26 514794 100         if (m) out_bitmap[i >> 6] |= (uint64_t)1 << (i & 63);
27             }
28 601           }
29              
30             /* Minimal-correctness UTF-8 validator. Spec: RFC 3629. Rejects
31             * overlong sequences and surrogate halves. */
32 575           static int validate_utf8_scalar(const char* in, size_t len)
33             {
34 575           const uint8_t* p = (const uint8_t*)in;
35 575           const uint8_t* e = p + len;
36             unsigned need;
37             uint32_t cp;
38             uint32_t lo, hi;
39             unsigned i;
40 216424 100         while (p < e) {
41 216283           uint8_t c = *p;
42 216283 100         if (c < 0x80) { p++; continue; }
43 691 100         if ((c & 0xE0) == 0xC0) { need = 1; cp = c & 0x1F; lo = 0x80; hi = 0x7FF; }
44 552 100         else if ((c & 0xF0) == 0xE0) { need = 2; cp = c & 0x0F; lo = 0x800; hi = 0xFFFF; }
45 482 100         else if ((c & 0xF8) == 0xF0) { need = 3; cp = c & 0x07; lo = 0x10000; hi = 0x10FFFF; }
46 250           else return 0;
47 441 100         if (p + 1 + need > e) return 0;
48 1178 100         for (i = 0; i < need; i++) {
49 901           uint8_t cc = p[1 + i];
50 901 100         if ((cc & 0xC0) != 0x80) return 0;
51 745           cp = (cp << 6) | (cc & 0x3F);
52             }
53 277 100         if (cp < lo || cp > hi) return 0;
    100          
54 261 100         if (cp >= 0xD800 && cp <= 0xDFFF) return 0;
    100          
55 257           p += 1 + need;
56             }
57 141           return 1;
58             }
59              
60 905           static size_t find_newlines_scalar(const char* in, size_t len,
61             uint32_t* out_offsets, size_t cap)
62             {
63 905           const char* p = in;
64 905           const char* end = in + len;
65 905           size_t k = 0;
66             const char* nl;
67 43424 100         while (p < end) {
68 42700           nl = (const char*)memchr(p, '\n', (size_t)(end - p));
69 42700 100         if (!nl) break;
70 42519 50         if (k >= cap) return (size_t)-1; /* overflow sentinel */
71 42519           out_offsets[k++] = (uint32_t)(nl - in);
72 42519           p = nl + 1;
73             }
74 905           return k;
75             }
76              
77 0           static const char* next_structural_scalar(const char* p, const char* end)
78             {
79 0           const uint8_t* q = (const uint8_t*)p;
80 0           const uint8_t* e = (const uint8_t*)end;
81 0 0         while (q < e) {
82 0           uint8_t b = *q;
83 0 0         if (MDS_CLASSIFIER_LO[b & 0xF] & MDS_CLASSIFIER_HI[b >> 4]) break;
84 0           q++;
85             }
86 0           return (const char*)q;
87             }
88              
89             /* Bitmap-aware variant: ctzll walk over uint64_t words. O(set-bits). */
90 0           static const char* next_structural_bm_scalar(const char* base, size_t bm_len,
91             const uint64_t* bm, size_t p_off)
92             {
93             size_t word;
94             size_t off;
95             uint64_t w;
96             size_t nwords;
97             size_t i;
98 0 0         if (p_off >= bm_len) return base + bm_len;
99 0           word = p_off >> 6;
100 0           off = p_off & 63u;
101             /* First (possibly partial) word: mask out bits before p_off. */
102 0           w = bm[word] & (~(uint64_t)0 << off);
103 0 0         if (w) {
104 0           size_t bit = (size_t)__builtin_ctzll(w);
105 0           size_t cand = (word << 6) + bit;
106 0           return base + (cand < bm_len ? cand : bm_len);
107             }
108             /* Full words after. */
109 0           nwords = (bm_len + 63) >> 6;
110 0 0         for (i = word + 1; i < nwords; i++) {
111 0           uint64_t v = bm[i];
112 0 0         if (v) {
113 0           size_t bit = (size_t)__builtin_ctzll(v);
114 0           size_t cand = (i << 6) + bit;
115 0           return base + (cand < bm_len ? cand : bm_len);
116             }
117             }
118 0           return base + bm_len;
119             }
120              
121             static const mds_simd_ops k_ops_scalar = {
122             classify_structural_scalar,
123             validate_utf8_scalar,
124             find_newlines_scalar,
125             next_structural_bm_scalar,
126             next_structural_scalar,
127             };
128              
129 2083           const mds_simd_ops* mds_simd_ops_scalar(void) { return &k_ops_scalar; }