dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 92.4% 170 / 0 / 184
Functions: 100.0% 22 / 0 / 22
Branches: 76.6% 49 / 34 / 98

src/block-iter.c
Line Branch Exec Source
1 #include <string.h>
2 #include "block-iter.h"
3 #include "util/debug.h"
4 #include "util/str-util.h"
5 #include "util/utf8.h"
6 #include "util/xmemrchr.h"
7
8 // Move to end of previous line (if any) and return number of bytes moved
9 101 static size_t block_iter_prev_line_eol(BlockIter *bi)
10 {
11 101 BlockIter tmp = *bi;
12 101 size_t n = block_iter_bol(&tmp);
13
2/2
✓ Branch 3 → 4 taken 91 times.
✓ Branch 3 → 6 taken 10 times.
101 if (block_iter_is_bof(&tmp)) {
14 return 0; // Already on first line; leave `bi` unchanged
15 }
16
17 91 CodePoint u;
18 91 *bi = tmp;
19 91 return n + block_iter_prev_char(bi, &u);
20 }
21
22 // Move to beginning of previous line (if any) and return number of bytes moved
23 101 size_t block_iter_prev_line(BlockIter *bi)
24 {
25 101 size_t n = block_iter_prev_line_eol(bi);
26
2/2
✓ Branch 3 → 4 taken 91 times.
✓ Branch 3 → 6 taken 10 times.
101 return n ? n + block_iter_bol(bi) : 0;
27 }
28
29 126 size_t block_iter_get_char(const BlockIter *bi, CodePoint *up)
30 {
31 126 BlockIter tmp = *bi;
32 126 return block_iter_next_char(&tmp, up);
33 }
34
35 522 size_t block_iter_next_char(BlockIter *bi, CodePoint *up)
36 {
37
3/4
✓ Branch 2 → 3 taken 20 times.
✓ Branch 2 → 5 taken 502 times.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 11 taken 20 times.
522 if (unlikely(bi->offset == bi->blk->size && !block_iter_next_block(bi))) {
38 return 0; // Already at EOF
39 }
40
41 502 BUG_ON(bi->blk->size == 0); // This block can't be empty
42
43 502 unsigned char byte = bi->blk->data[bi->offset];
44
2/2
✓ Branch 7 → 8 taken 500 times.
✓ Branch 7 → 9 taken 2 times.
502 if (likely(byte < 0x80)) {
45 500 *up = byte;
46 500 bi->offset++;
47 500 return 1;
48 }
49
50 2 size_t prev_offset = bi->offset;
51 2 *up = u_get_nonascii(bi->blk->data, bi->blk->size, &bi->offset);
52 2 return bi->offset - prev_offset;
53 }
54
55 185 size_t block_iter_prev_char(BlockIter *bi, CodePoint *up)
56 {
57
3/4
✓ Branch 2 → 3 taken 6 times.
✓ Branch 2 → 5 taken 179 times.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 13 taken 6 times.
185 if (unlikely(bi->offset == 0 && !block_iter_end_of_prev_block(bi))) {
58 return 0; // Already at BOF
59 }
60
61 179 BUG_ON(bi->blk->size == 0); // This block can't be empty
62 179 BUG_ON(bi->offset == 0);
63
64 179 unsigned char byte = bi->blk->data[bi->offset - 1];
65
1/2
✓ Branch 9 → 10 taken 179 times.
✗ Branch 9 → 11 not taken.
179 if (likely(byte < 0x80)) {
66 179 *up = byte;
67 179 bi->offset--;
68 179 return 1;
69 }
70
71 size_t prev_offset = bi->offset;
72 *up = u_prev_char(bi->blk->data, &bi->offset);
73 return prev_offset - bi->offset;
74 }
75
76 45 size_t block_iter_next_column(BlockIter *bi)
77 {
78 45 CodePoint u;
79 45 size_t size = block_iter_next_char(bi, &u);
80
3/4
✓ Branch 7 → 8 taken 38 times.
✓ Branch 7 → 10 taken 7 times.
✗ Branch 9 → 4 not taken.
✓ Branch 9 → 10 taken 38 times.
45 while (block_iter_get_char(bi, &u) && u_is_zero_width(u)) {
81 size += block_iter_next_char(bi, &u);
82 }
83 45 return size;
84 }
85
86 16 size_t block_iter_prev_column(BlockIter *bi)
87 {
88 16 CodePoint u;
89 16 size_t skip, total = 0;
90 16 do {
91 16 skip = block_iter_prev_char(bi, &u);
92 16 total += skip;
93
3/4
✓ Branch 4 → 5 taken 13 times.
✓ Branch 4 → 8 taken 3 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 13 times.
16 } while (skip && u_is_zero_width(u));
94 16 return total;
95 }
96
97 437 size_t block_iter_bol(BlockIter *bi)
98 {
99 437 block_iter_normalize(bi);
100
2/2
✓ Branch 3 → 4 taken 206 times.
✓ Branch 3 → 13 taken 231 times.
437 if (block_iter_is_bol(bi)) {
101 return 0;
102 }
103
104 // These cases are handled by the condition above
105 206 const Block *blk = bi->blk;
106 206 size_t offset = bi->offset;
107 206 BUG_ON(offset == 0);
108 206 BUG_ON(offset >= blk->size);
109
110
2/2
✓ Branch 8 → 9 taken 31 times.
✓ Branch 8 → 10 taken 175 times.
206 if (blk->nl == 1) {
111 31 bi->offset = 0; // Only 1 line in Block; bol is at offset 0
112 31 return offset;
113 }
114
115 175 const char *nl = xmemrchr(blk->data, '\n', offset - 1);
116
2/2
✓ Branch 10 → 11 taken 45 times.
✓ Branch 10 → 12 taken 130 times.
175 if (!nl) {
117 45 bi->offset = 0; // No newline before offset; bol is at offset 0
118 45 return offset;
119 }
120
121 130 offset = (size_t)(nl - blk->data) + 1;
122 130 size_t count = bi->offset - offset;
123 130 bi->offset = offset;
124 130 return count;
125 }
126
127 247 size_t block_iter_eol(BlockIter *bi)
128 {
129 247 block_iter_normalize(bi);
130 247 const Block *blk = bi->blk;
131 247 size_t offset = bi->offset;
132
2/2
✓ Branch 3 → 4 taken 241 times.
✓ Branch 3 → 12 taken 6 times.
247 if (unlikely(offset == blk->size)) {
133 return 0; // Already at EOF
134 }
135
136 241 BUG_ON(blk->size == 0); // This block can't be empty
137 241 BUG_ON(blk->nl == 0);
138
139
2/2
✓ Branch 8 → 9 taken 11 times.
✓ Branch 8 → 10 taken 230 times.
241 if (blk->nl == 1) {
140 11 bi->offset = blk->size - 1;
141 11 return bi->offset - offset;
142 }
143
144 230 StringView line = buf_slice_next_line(blk->data, &offset, blk->size);
145 230 bi->offset += line.length;
146 230 return line.length;
147 }
148
149 // Move after next newline (beginning of next line or end of file) and
150 // return number of bytes moved
151 228 size_t block_iter_eat_line(BlockIter *bi)
152 {
153 228 CodePoint u;
154 228 size_t n = block_iter_eol(bi);
155 228 size_t m = block_iter_next_char(bi, &u);
156 228 BUG_ON(m && (m != 1 || u != '\n'));
157 228 return n + m;
158 }
159
160 // Move to beginning of next line (if any) and return number of bytes moved
161 79 size_t block_iter_next_line(BlockIter *bi)
162 {
163 79 BlockIter tmp = *bi;
164 79 size_t move = block_iter_eat_line(&tmp);
165
2/2
✓ Branch 3 → 4 taken 64 times.
✓ Branch 3 → 5 taken 15 times.
79 if (unlikely(block_iter_is_eof(&tmp))) {
166 return 0;
167 }
168
169 64 *bi = tmp;
170 64 return move;
171 }
172
173 // Count spaces and tabs at or after iterator (and move beyond them)
174 30 size_t block_iter_skip_blanks_fwd(BlockIter *bi)
175 {
176 30 block_iter_normalize(bi);
177 30 StringView sv = strview_from_slice(bi->blk->data, bi->offset, bi->blk->size);
178
179 // We're only operating on one line and checking for ASCII characters,
180 // so Block traversal and Unicode-aware decoding are both unnecessary
181 30 size_t count = strview_blank_prefix_length(sv);
182
183 30 bi->offset += count + 1;
184 30 return count;
185 }
186
187 // Count spaces and tabs before iterator (and move to beginning of them)
188 30 size_t block_iter_skip_blanks_bwd(BlockIter *bi)
189 {
190 30 block_iter_normalize(bi);
191 30 StringView sv = string_view(bi->blk->data, bi->offset);
192 30 size_t count = strview_blank_suffix_length(sv);
193 30 bi->offset -= count;
194 30 return count;
195 }
196
197 // Non-empty line can be used to determine size of indentation for the next line
198 14 bool block_iter_find_non_empty_line_bwd(BlockIter *bi)
199 {
200 14 block_iter_bol(bi);
201 18 do {
202 18 StringView line = block_iter_get_line(bi);
203
2/2
✓ Branch 5 → 6 taken 14 times.
✓ Branch 5 → 7 taken 4 times.
18 if (!strview_isblank(line)) {
204 14 return true;
205 }
206
1/2
✓ Branch 8 → 3 taken 4 times.
✗ Branch 8 → 9 not taken.
4 } while (block_iter_prev_line(bi));
207 return false;
208 }
209
210 1 void block_iter_back_bytes(BlockIter *bi, size_t count)
211 {
212 1 while (count > bi->offset) {
213 count -= bi->offset;
214 bool have_prev_block = block_iter_end_of_prev_block(bi);
215 1 BUG_ON(!have_prev_block);
216 }
217 1 bi->offset -= count;
218 1 }
219
220 334 void block_iter_skip_bytes(BlockIter *bi, size_t count)
221 {
222 334 size_t avail = bi->blk->size - bi->offset;
223
1/2
✗ Branch 7 → 3 not taken.
✓ Branch 7 → 8 taken 334 times.
334 while (count > avail) {
224 count -= avail;
225 bool have_next_block = block_iter_next_block(bi);
226 BUG_ON(!have_next_block);
227 avail = bi->blk->size;
228 }
229 334 bi->offset += count;
230 334 }
231
232 63 void block_iter_goto_offset(BlockIter *bi, size_t offset)
233 {
234 63 Block *blk;
235
1/2
✓ Branch 6 → 3 taken 63 times.
✗ Branch 6 → 7 not taken.
63 block_for_each(blk, bi->head) {
236
1/2
✓ Branch 3 → 4 taken 63 times.
✗ Branch 3 → 5 not taken.
63 if (offset <= blk->size) {
237 63 bi->blk = blk;
238 63 bi->offset = offset;
239 63 return;
240 }
241 offset -= blk->size;
242 }
243 }
244
245 4 void block_iter_goto_line(BlockIter *bi, size_t line)
246 {
247 4 Block *blk = block_iter_get_first_block(bi);
248 4 size_t nl = 0;
249
1/4
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 4 times.
✗ Branch 5 → 3 not taken.
✗ Branch 5 → 6 not taken.
4 while (block_has_next(blk, bi->head) && nl + blk->nl < line) {
250 nl += blk->nl;
251 blk = block_next(blk);
252 }
253
254 4 bi->blk = blk;
255 4 bi->offset = 0;
256
2/2
✓ Branch 10 → 7 taken 8 times.
✓ Branch 10 → 11 taken 3 times.
11 while (nl < line) {
257
2/2
✓ Branch 8 → 9 taken 7 times.
✓ Branch 8 → 11 taken 1 time.
8 if (!block_iter_eat_line(bi)) {
258 break;
259 }
260 7 nl++;
261 }
262 4 }
263
264 338 size_t block_iter_get_offset(const BlockIter *bi)
265 {
266 338 const Block *blk;
267 338 size_t offset = 0;
268
1/2
✓ Branch 5 → 3 taken 338 times.
✗ Branch 5 → 6 not taken.
338 block_for_each(blk, bi->head) {
269
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 338 times.
338 if (blk == bi->blk) {
270 break;
271 }
272 offset += blk->size;
273 }
274 338 return offset + bi->offset;
275 }
276
277 13 static void string_append_from_block_iter(String *buf, BlockIter bi, size_t len)
278 {
279
1/2
✓ Branch 2 → 3 taken 13 times.
✗ Branch 2 → 12 not taken.
13 if (len == 0) {
280 return;
281 }
282
283 // Reserve +1 byte, for convenient string_steal_cstring() in callers
284 13 char *reserved = string_reserve_space(buf, len + 1);
285 13 size_t pos = 0;
286
287 13 while (pos < len) {
288 13 const size_t avail = bi.blk->size - bi.offset;
289 13 size_t count = MIN(len - pos, avail);
290 13 memcpy(reserved + pos, bi.blk->data + bi.offset, count);
291 13 pos += count;
292 13 bool have_next_block = block_iter_next_block(&bi);
293 26 BUG_ON(pos < len && !have_next_block);
294 }
295
296 13 BUG_ON(pos != len);
297 13 buf->len += pos;
298 }
299
300 13 String block_iter_get_bytes(BlockIter bi, size_t len)
301 {
302 13 String buf = string_new(len + 1);
303 13 string_append_from_block_iter(&buf, bi, len);
304 13 return buf;
305 }
306
307 // Return the contents of the line that extends from `bi`. Callers
308 // should ensure `bi` is already at BOL, if whole lines are needed.
309 393 StringView block_iter_get_line_with_nl(BlockIter *bi)
310 {
311 393 block_iter_normalize(bi);
312
2/2
✓ Branch 3 → 4 taken 10 times.
✓ Branch 3 → 5 taken 383 times.
393 if (unlikely(bi->offset == bi->blk->size)) {
313 // Cursor at end of last block
314 10 return strview("");
315 }
316
317 383 StringView line;
318
2/2
✓ Branch 5 → 6 taken 48 times.
✓ Branch 5 → 7 taken 335 times.
383 if (bi->blk->nl == 1) {
319 // Block contains only 1 line; end-of-line is end-of-block
320 48 line = strview_from_slice(bi->blk->data, bi->offset, bi->blk->size);
321 } else {
322 335 size_t pos = bi->offset;
323 335 line = buf_slice_next_line(bi->blk->data, &pos, bi->blk->size);
324 335 line.length += 1; // Include the newline
325 }
326
327 383 BUG_ON(!strview_has_suffix(line, "\n"));
328 383 return line;
329 }
330