MMgr v1.0.0
Zero-heap memory manager in C11. One borrowed buffer, carved by asserted offset.
Loading...
Searching...
No Matches
verbum_scrutor.h
Go to the documentation of this file.
1/* MMgr - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2 * SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-Commercial OR LicenseRef-Educational
3 *
4 * Every use falls under AGPL-3.0-or-later unless you hold explicit permission, which is either a
5 * negotiated commercial licensing contract or an educator's license issued to you personally.
6 */
7/**
8 * @file verbum_scrutor.h
9 * @brief SWAR byte tests, the lane constants, the arguments, and the lane, mask and word dispatch
10 * tables.
11 * @author dstroy0 (Douglas Quigg) <dquigg123@gmail.com>
12 * @date 2026-08-29
13 *
14 * @note One embed_word is treated as MMGR_SWAR_BYTES independent lanes. A whole word is tested at once.
15 * @note A lane mask carries one set bit per matching lane, in that lane's high bit. mask.spread widens it.
16 * @note The entries whose meaning depends on byte order are bound through MMGR_HW_BIG_ENDIAN in the tables below.
17 */
18#ifndef MMGR_VERBUM_SCRUTOR_H
19#define MMGR_VERBUM_SCRUTOR_H
20
22
23#include "mmgr.h"
24
25EMBED_BEGIN_DECLS
26
27/**
28 * @brief Expands to sizeof(embed_word), and that is how many lanes one word carries.
29 *
30 * @note Eight at EMBED_WORD_BITS 64, four at 32 and two at 16.
31 * @note Also the value both lane index calls report when the mask they are given is empty.
32 */
33#define MMGR_SWAR_BYTES (sizeof(embed_word))
34
35/**
36 * @brief Expands to EMBED_WORD_BITS, the width of a whole word in bits.
37 *
38 * @note This is the word's width and not one lane's, despite the name. A lane is eight bits.
39 * @note Bounds the shift doubling in scrut_smear and supplies the final shift in scrut_lane_count, both of
40 * which want the whole width.
41 */
42#define MMGR_SWAR_LANE_BITS (EMBED_WORD_BITS)
43
44/**
45 * @brief Expands to a word holding 1 in every lane, which is 0x0101...01.
46 *
47 * @note Multiplying it by a byte broadcasts that byte into every lane, and that is how the comparisons start.
48 * @note Multiplying a lane mask by it sums the lanes, and that is how scrut_lane_count counts them.
49 */
50#define MMGR_SWAR_ONES (((embed_word) ~(embed_word)0) / 0xFFu)
51
52/**
53 * @brief Expands to a word holding 0x80 in every lane, which is 0x8080...80.
54 *
55 * @note The shape every lane mask takes. A caller can and two masks together lane by lane.
56 * @note Or-ing it into a word before a subtraction gives each lane a borrow to spend, keeping lanes separate.
57 */
58#define MMGR_VERBUM_SCRUTOR_HIGH (MMGR_SWAR_ONES * 0x80u)
59
60/**
61 * @brief Expands to a word holding 0x7F in every lane, which is 0x7F7F...7F.
62 *
63 * @note The complement of MMGR_VERBUM_SCRUTOR_HIGH, and so the two split each lane into its value and its mask bit.
64 * @note scrut_has_zero adds it to make every non-zero lane carry into its high bit.
65 */
66#define MMGR_SWAR_LOW7 (MMGR_SWAR_ONES * 0x7Fu)
67
68/**
69 * @brief Expands to 0, the first of three verdicts a scan step can report.
70 *
71 * @note MMGR_SWAR_GO, MMGR_SWAR_YES and MMGR_SWAR_NO are 0, 1 and 2, and so the three fit in one byte.
72 * @note The three carry no sizing suffix, and so each takes int in an expression. A scanner keeping a
73 * verdict in a uint8_t converts it on the way in.
74 * @note verbum_scrutor.c reads none of the three. They are declared here for the scanners that
75 * include it.
76 */
77#define MMGR_SWAR_GO 0
78
79/** @brief Expands to 1, the second of the three verdicts described at MMGR_SWAR_GO. */
80#define MMGR_SWAR_YES 1
81
82/** @brief Expands to 2, the third of the three verdicts described at MMGR_SWAR_GO. */
83#define MMGR_SWAR_NO 2
84
85/**
86 * @brief Expands to 0x60u, the two bits that separate the ASCII blocks a letter can fall in.
87 *
88 * @note Passed to lane.fam_eq as the family, with MMGR_FAM_CI as the byte, to pick out the capital block.
89 */
90#define MMGR_FAM_CS 0x60u
91
92/**
93 * @brief Expands to 0x40u, the value the MMGR_FAM_CS bits take across the 0x40 to 0x5F block.
94 *
95 * @note That block holds A through Z along with six other bytes: the at sign, and the five that follow Z.
96 */
97#define MMGR_FAM_CI 0x40u
98
99/**
100 * @brief Arguments for the lane calls.
101 *
102 * @note ge, le and sub7 read word and byte. eq adds ci. xor_ reads word, val and ci. fam_eq adds fam.
103 * @note has_zero, alpha, any_upper and any_digit read word alone.
104 * @note count, first and last read mask alone.
105 */
106typedef struct
107{
108 const embed_word word; /**< The MMGR_SWAR_BYTES bytes under test, one per lane. */
109 const embed_word val; /**< Whole word xor_ compares against, which the caller broadcasts itself. */
110 const embed_word mask; /**< Lane mask the three index calls read. */
111 const uint8_t byte; /**< Byte the call broadcasts into every lane before comparing. */
112 const uint8_t fam; /**< Bits fam_eq keeps on both sides before comparing. */
113 const embed_bool ci; /**< Non-zero to let a letter match either case. */
115
116/**
117 * @brief Arguments for the mask calls.
118 *
119 * @note spread, drop_first, drop_last and before read mask.
120 * @note bytes_below, lanes_below and run_edge read bytes.
121 * @note run reads both mask and bytes. tail reads bytes and wi, and is the only call that reads wi.
122 */
123typedef struct
124{
125 const embed_word mask; /**< Lane mask to reshape or examine. */
126 const size_t bytes; /**< Byte count the mask is built over, or the run length wanted. */
127 const size_t wi; /**< Whole words a scan has already covered. */
129
130/**
131 * @brief Arguments for the word calls.
132 *
133 * @note load and load_al read at. fold_lower reads word. count reads bytes.
134 */
135typedef struct
136{
137 const embed_word word; /**< Word fold_lower folds. */
138 const void *const at; /**< Address the two loads read a word from [BORROWS]. */
139 const size_t bytes; /**< Byte count count converts into a word count. */
141
142/**
143 * @brief Type of the lane dispatch table.
144 *
145 * @note EMBED_TABLE_LAYOUT asserts the thirteen members sit at consecutive EMBED_FUNCTION_POINTER_BYTES offsets, with
146 * nothing else.
147 * @note Eight of the first ten return lane masks. sub7 returns seven bit values and xor_ a whole word.
148 * @note count, first and last return positions instead of words.
149 * @note first and last mean first and last in memory order, as the endian binding below delivers.
150 * @note xor_ carries a trailing underscore because xor is an alternative form of an operator in C++.
151 */
152typedef struct
153{
154 embed_word (*ge)(const ScrutLaneCfg *args); /**< Lanes at or above a byte. */
155 embed_word (*le)(const ScrutLaneCfg *args); /**< Lanes at or below a byte. */
156 embed_word (*sub7)(const ScrutLaneCfg *args); /**< Seven low bits of each lane's difference from a byte. */
157 embed_word (*has_zero)(const ScrutLaneCfg *args); /**< Lanes holding zero. */
158 embed_word (*eq)(const ScrutLaneCfg *args); /**< Lanes equal to a byte. */
159 embed_word (*xor_)(const ScrutLaneCfg *args); /**< Lane by lane difference of two words. */
160 embed_word (*fam_eq)(const ScrutLaneCfg *args); /**< Lanes matching a byte within a set of bits. */
161 embed_word (*any_upper)(const ScrutLaneCfg *args); /**< Lanes whose MMGR_FAM_CS bits equal MMGR_FAM_CI. */
162 embed_word (*any_digit)(const ScrutLaneCfg *args); /**< Lanes in the 0x30 to 0x3F block. */
163 embed_word (*alpha)(const ScrutLaneCfg *args); /**< Lanes holding an ASCII letter. */
164 size_t (*count)(const ScrutLaneCfg *args); /**< How many lanes a mask has set. */
165 size_t (*first)(const ScrutLaneCfg *args); /**< Index of the first set lane in memory order. */
166 size_t (*last)(const ScrutLaneCfg *args); /**< Index of the last set lane in memory order. */
168EMBED_TABLE_LAYOUT(ScrutLaneNs, ge, le, sub7, has_zero, eq, xor_, fam_eq, any_upper, any_digit, alpha, count, first,
169 last);
170
171/**
172 * @brief Type of the mask dispatch table.
173 *
174 * @note EMBED_TABLE_LAYOUT asserts the nine members sit at consecutive EMBED_FUNCTION_POINTER_BYTES offsets, with
175 * nothing else.
176 * @note drop_first, drop_last and before all mean memory order, which the endian binding below delivers.
177 */
178typedef struct
179{
180 embed_word (*spread)(const ScrutMaskCfg *args); /**< Widens each set lane to a full byte of ones. */
181 embed_word (*drop_first)(const ScrutMaskCfg *args); /**< Clears the first set lane in memory order. */
182 embed_word (*drop_last)(const ScrutMaskCfg *args); /**< Clears the last set lane in memory order. */
183 embed_word (*bytes_below)(const ScrutMaskCfg *args); /**< Full byte mask over the first bytes of a word. */
184 embed_word (*lanes_below)(const ScrutMaskCfg *args); /**< Lane mask over the first lanes of a word. */
185 embed_word (*before)(const ScrutMaskCfg *args); /**< Lanes ahead of the first set one in memory order. */
186 embed_word (*tail)(const ScrutMaskCfg *args); /**< Lane mask for a scan's last partial word. */
187 embed_word (*run)(const ScrutMaskCfg *args); /**< Lanes that begin a run of set lanes. */
188 embed_word (*run_edge)(const ScrutMaskCfg *args); /**< Lanes too near the end for a run to fit. */
190EMBED_TABLE_LAYOUT(ScrutMaskNs, spread, drop_first, drop_last, bytes_below, lanes_below, before, tail, run, run_edge);
191
192/**
193 * @brief Type of the word dispatch table.
194 *
195 * @note EMBED_TABLE_LAYOUT asserts the four members sit at consecutive EMBED_FUNCTION_POINTER_BYTES offsets, with
196 * nothing else.
197 * @note Its count member converts a byte count into a word count, where the lane table's count counts lanes.
198 */
199typedef struct
200{
201 embed_word (*load)(const ScrutWordCfg *args); /**< Reads a word from any address. */
202 embed_word (*load_al)(const ScrutWordCfg *args); /**< Reads a word from an aligned address. */
203 embed_word (*fold_lower)(const ScrutWordCfg *args); /**< Turns every letter lane to lower case. */
204 size_t (*count)(const ScrutWordCfg *args); /**< Words a scan of a byte count must read. */
206EMBED_TABLE_LAYOUT(ScrutWordNs, load, load_al, fold_lower, count);
207
208/**
209 * @brief Marks the lanes of args->word that are at or above args->byte.
210 *
211 * @param[in] args The word and the byte to compare against [BORROWS].
212 * @return A lane mask holding those lanes.
213 * @note Compares bytes as unsigned, and 0x80 and above are the largest values.
214 * @warning args->byte must be under 0x80. At or above it the lanes under 0x80 come out wrong and nothing
215 * diagnoses it.
216 */
217embed_word mmgr_scrut_ge(const ScrutLaneCfg *args);
218
219/**
220 * @brief Marks the lanes of args->word that are at or below args->byte.
221 *
222 * @param[in] args The word and the byte to compare against [BORROWS].
223 * @return A lane mask holding those lanes.
224 * @note Compares bytes as unsigned, and 0x80 and above are the largest values.
225 * @warning args->byte must be under 0x80, for the reason given on mmgr_scrut_ge.
226 * @note And this with mmgr_scrut_ge to get a range test, as mmgr_scrut_alpha does.
227 */
228embed_word mmgr_scrut_le(const ScrutLaneCfg *args);
229
230/**
231 * @brief Subtracts args->byte from every lane of args->word and keeps the low seven bits of each result.
232 *
233 * @param[in] args The word and the byte to subtract [BORROWS].
234 * @return The seven low bits of each lane's difference, every lane's high bit clear.
235 * @note Yields values and not a lane mask, unlike every other lane entry but count, first and last.
236 * @warning A lane below args->byte wraps, and so its seven bits are the difference taken modulo 128.
237 * @warning args->byte must be under 0x80, for the reason given on mmgr_scrut_ge: at or above it a borrow reaches
238 * the next lane.
239 */
240embed_word mmgr_scrut_sub7(const ScrutLaneCfg *args);
241
242/**
243 * @brief Marks the lanes of args->word that hold zero.
244 *
245 * @param[in] args The word to test [BORROWS].
246 * @return A lane mask holding the zero lanes.
247 * @note Reads args->word alone, and byte, val, fam and ci take no part.
248 * @note Apply this to the result of mmgr_scrut_xor to find where two words agreed.
249 */
250embed_word mmgr_scrut_has_zero(const ScrutLaneCfg *args);
251
252/**
253 * @brief Marks the lanes of args->word that equal args->byte.
254 *
255 * @param[in] args The word, the byte to find, and the case flag [BORROWS].
256 * @return A lane mask holding the matching lanes.
257 * @note Broadcasts args->byte itself, and so the caller passes a single byte and not a whole word.
258 * @note With args->ci set, a letter matches either case. Every other byte still compares exactly.
259 */
260embed_word mmgr_scrut_eq(const ScrutLaneCfg *args);
261
262/**
263 * @brief Returns the lane by lane difference of args->word and args->val.
264 *
265 * @param[in] args The two words and the case flag [BORROWS].
266 * @return A word whose lanes are zero exactly where the two agreed.
267 * @note Takes a whole word in args->val, and so the caller broadcasts a byte itself if it wants that.
268 * @note With args->ci set, the case bit is cleared on letter lanes, and so the two cases of a letter agree.
269 * @note Pass the result to mmgr_scrut_has_zero to turn it into a lane mask.
270 */
271embed_word mmgr_scrut_xor(const ScrutLaneCfg *args);
272
273/**
274 * @brief Marks the lanes of args->word that match args->byte once both are reduced to the args->fam bits.
275 *
276 * @param[in] args The word, the byte to match, and the bits that count [BORROWS].
277 * @return A lane mask holding the matching lanes.
278 * @note args->byte is reduced by args->fam first, and so bits outside the family take no part on either side.
279 * @note A args->fam of 0xFF makes this an exact match, and an args->fam of 0 marks every lane.
280 */
281embed_word mmgr_scrut_fam_eq(const ScrutLaneCfg *args);
282
283/**
284 * @brief Marks the lanes of args->word whose MMGR_FAM_CS bits equal MMGR_FAM_CI, the 0x40 to 0x5F block.
285 *
286 * @param[in] args The word to test [BORROWS].
287 * @return A lane mask holding those lanes.
288 * @note Reads args->word alone, since the family and the byte are both fixed.
289 * @warning That block holds six bytes that are not capitals, the at sign and the five after Z, and
290 * bit seven takes no part, and so 0xC0 to 0xDF is marked as well. And it with mmgr_scrut_alpha
291 * for letters alone.
292 */
293embed_word mmgr_scrut_any_upper(const ScrutLaneCfg *args);
294
295/**
296 * @brief Marks the lanes of args->word that fall in the 0x30 to 0x3F block.
297 *
298 * @param[in] args The word to test [BORROWS].
299 * @return A lane mask holding those lanes.
300 * @note Reads args->word alone, since the family and the byte are both fixed.
301 * @warning That block holds six symbols after the nine, and so this marks more than 0 through 9. And it
302 * with mmgr_scrut_le at the character nine when only the ten digits should count.
303 */
304embed_word mmgr_scrut_any_digit(const ScrutLaneCfg *args);
305
306/**
307 * @brief Marks the lanes of args->word holding an ASCII letter, of either case.
308 *
309 * @param[in] args The word to test [BORROWS].
310 * @return A lane mask holding the letter lanes.
311 * @note Reads args->word alone, and byte, val, fam and ci take no part.
312 * @note A byte at 0x80 or above is never marked, and so this is exact where mmgr_scrut_any_upper is not.
313 * Beyond the ten digits mmgr_scrut_any_digit marks only the six symbols after the nine
314 * and never a high byte.
315 */
316embed_word mmgr_scrut_alpha(const ScrutLaneCfg *args);
317
318/**
319 * @brief Counts the set lanes of args->mask.
320 *
321 * @param[in] args The lane mask to count [BORROWS].
322 * @return How many lanes are set, 0 through MMGR_SWAR_BYTES.
323 * @note Reads args->mask alone, and so word, byte, val, fam and ci take no part.
324 * @warning Expects a lane mask, one bit per lane in that lane's high bit. A spread mask counts wrong.
325 */
327
328/**
329 * @brief Returns the index of the lowest set lane of args->mask.
330 *
331 * @param[in] args The lane mask to examine [BORROWS].
332 * @return The index, or MMGR_SWAR_BYTES when no lane is set.
333 * @note The lane table reaches this as first on a little endian target and as last on a big endian one, and so
334 * prefer lane.first or lane.last when memory order is what matters.
335 * @note A return of MMGR_SWAR_BYTES is one past the last valid index, and that is how an empty mask reports.
336 */
338
339/**
340 * @brief Returns the index of the highest set lane of args->mask.
341 *
342 * @param[in] args The lane mask to examine [BORROWS].
343 * @return The index, or MMGR_SWAR_BYTES when no lane is set.
344 * @note The lane table reaches this as last on a little endian target and as first on a big endian one, and so
345 * prefer lane.first or lane.last when memory order is what matters.
346 * @note A return of MMGR_SWAR_BYTES is one past the last valid index, and that is how an empty mask reports.
347 */
349
350/**
351 * @brief Widens every set lane of args->mask from its high bit to a full byte of ones.
352 *
353 * @param[in] args The lane mask to widen [BORROWS].
354 * @return A word holding 0xFF in each set lane and 0x00 in the rest.
355 * @note Use the result to select between two words lane by lane, which a lane mask alone cannot do.
356 * @warning The result is no longer a lane mask, and so do not hand it to the counting or index entries.
357 */
358embed_word mmgr_scrut_spread(const ScrutMaskCfg *args);
359
360/**
361 * @brief Clears the lowest set lane of args->mask.
362 *
363 * @param[in] args The lane mask to reduce [BORROWS].
364 * @return The mask with that lane cleared, or 0 when it contained only one.
365 * @note An empty mask stays empty, and stepping a mask down repeatedly ends instead of wrapping.
366 * @note The mask table reaches this as drop_first on a little endian target and drop_last on a big endian one.
367 */
368embed_word mmgr_scrut_drop_lo(const ScrutMaskCfg *args);
369
370/**
371 * @brief Clears the highest set lane of args->mask.
372 *
373 * @param[in] args The lane mask to reduce [BORROWS].
374 * @return The mask with that lane cleared, or 0 when it contained only one.
375 * @note An empty mask stays empty, and stepping a mask down repeatedly ends instead of wrapping.
376 * @note The mask table reaches this as drop_last on a little endian target and drop_first on a big endian one.
377 */
378embed_word mmgr_scrut_drop_hi(const ScrutMaskCfg *args);
379
380/**
381 * @brief Builds a mask covering the first args->bytes bytes of a word, in memory order.
382 *
383 * @param[in] args The byte count to cover [BORROWS].
384 * @return A word of ones over those bytes and zeros over the rest.
385 * @note Covers the bytes that come first in memory on either byte order.
386 * @note A count of 0 gives an empty mask and a count of MMGR_SWAR_BYTES or more gives a full one.
387 */
388embed_word mmgr_scrut_bytes_below(const ScrutMaskCfg *args);
389
390/**
391 * @brief Builds a lane mask covering the first args->bytes lanes of a word, in memory order.
392 *
393 * @param[in] args The lane count to cover [BORROWS].
394 * @return A lane mask holding those lanes.
395 * @note mmgr_scrut_bytes_below reduced to lane bits, and so this ands cleanly against another lane mask.
396 * @note And a match mask with this to keep a scan from acting on bytes past its count.
397 */
398embed_word mmgr_scrut_lanes_below(const ScrutMaskCfg *args);
399
400/**
401 * @brief Returns the lanes that come before the first set lane of args->mask, in memory order.
402 *
403 * @param[in] args The lane mask to examine [BORROWS].
404 * @return A lane mask holding those lanes.
405 * @note An empty args->mask reports every lane, since nothing comes first.
406 * @note Count the result to get the same index mmgr_scrut_lane_lo reports on a little endian target.
407 */
408embed_word mmgr_scrut_lanes_before(const ScrutMaskCfg *args);
409
410/**
411 * @brief Builds the lane mask for the last partial word of a scan of args->bytes bytes.
412 *
413 * @param[in] args The total byte count and the whole words already covered [BORROWS].
414 * @return A lane mask over the bytes still wanted, or 0 once the count is reached.
415 * @note A word wanted in full gets a full mask, and so this can be applied on every pass of a scan loop.
416 * @note The only mask entry that reads args->wi.
417 */
418embed_word mmgr_scrut_tail_mask(const ScrutMaskCfg *args);
419
420/**
421 * @brief Marks the lanes of args->mask that begin a run of args->bytes set lanes.
422 *
423 * @param[in] args The lane mask and the run length wanted [BORROWS].
424 * @return A lane mask holding the lanes each run starts at.
425 * @note A surviving lane is where a run begins, in memory order, on either byte order.
426 * @note A args->bytes of 0 or 1 returns args->mask as it stands, since every set lane starts such a run.
427 * @warning Returns 0 for an args->bytes above MMGR_SWAR_BYTES, since no run that long fits in one word.
428 */
429embed_word mmgr_scrut_run(const ScrutMaskCfg *args);
430
431/**
432 * @brief Marks the lanes too near the end of a word for a run of args->bytes to fit inside it.
433 *
434 * @param[in] args The run length wanted [BORROWS].
435 * @return A lane mask holding those lanes, or 0 when no lane is too near.
436 * @note A run of args->bytes can begin at any of the first MMGR_SWAR_BYTES minus args->bytes plus one lanes.
437 * @note A caller matching across a word boundary uses this to tell which starts must be tried again.
438 * @note Returns 0 for an args->bytes of 0 or 1, since any lane can start such a run, and for any length
439 * above MMGR_SWAR_BYTES, since no lane can.
440 */
441embed_word mmgr_scrut_run_edge(const ScrutMaskCfg *args);
442
443/**
444 * @brief Loads one embed_word from args->at.
445 *
446 * @param[in] args Address to load from [BORROWS].
447 * @return The bytes at args->at, one per lane, in the target's own order.
448 * @warning Reads MMGR_SWAR_BYTES bytes even when fewer are wanted. args->at must be readable for all of them.
449 * @note Any alignment will do. Use mmgr_scrut_load_al when the address is known to be aligned.
450 */
451embed_word mmgr_scrut_load(const ScrutWordCfg *args);
452
453/**
454 * @brief Loads one embed_word from an aligned args->at.
455 *
456 * @param[in] args Address to load from [BORROWS].
457 * @return The bytes at args->at, one per lane, in the target's own order.
458 * @warning Reads MMGR_SWAR_BYTES bytes even when fewer are wanted, and args->at must be aligned for a word.
459 * @note Use mmgr_scrut_load when the address may sit anywhere.
460 */
461embed_word mmgr_scrut_load_al(const ScrutWordCfg *args);
462
463/**
464 * @brief Returns args->word with every letter lane turned to lower case.
465 *
466 * @param[in] args The word to fold [BORROWS].
467 * @return The word with its letter lanes lowered and every other lane untouched.
468 * @note Only letter lanes are changed. A digit or a symbol keeps bit five exactly as it was.
469 * @note Fold two words and compare them for a case insensitive test over whole words at once.
470 */
471embed_word mmgr_scrut_fold_lower(const ScrutWordCfg *args);
472
473/**
474 * @brief Returns how many whole words a scan of args->bytes bytes must read.
475 *
476 * @param[in] args The byte count to convert [BORROWS].
477 * @return The count rounded up. A partial last word still counts as one.
478 * @note A args->bytes of 0 gives 0. A caller loops no times instead of reading one word.
479 * @note Pair this with mask.tail, which supplies the lane mask for that partial last word.
480 */
481size_t mmgr_scrut_words(const ScrutWordCfg *args);
482
483/**
484 * @brief Dispatch table instance named lane.
485 *
486 * @note The first eleven members call the matching mmgr_scrut_ function on either byte order.
487 * @note first and last are bound to lane_lo and lane_hi one way round on a little endian target and the
488 * other way on a big endian one, and so both always mean memory order.
489 */
490EMBED_TABLE_STORAGE ScrutLaneNs lane EMBED_UNUSED = {
491 .ge = mmgr_scrut_ge,
492 .le = mmgr_scrut_le,
493 .sub7 = mmgr_scrut_sub7,
494 .has_zero = mmgr_scrut_has_zero,
495 .eq = mmgr_scrut_eq,
496 .xor_ = mmgr_scrut_xor,
497 .fam_eq = mmgr_scrut_fam_eq,
498 .any_upper = mmgr_scrut_any_upper,
499 .any_digit = mmgr_scrut_any_digit,
500 .alpha = mmgr_scrut_alpha,
501 .count = mmgr_scrut_lane_count,
502#if MMGR_HW_BIG_ENDIAN
503 .first = mmgr_scrut_lane_hi,
504 .last = mmgr_scrut_lane_lo,
505#else
506 .first = mmgr_scrut_lane_lo,
507 .last = mmgr_scrut_lane_hi,
508#endif
509};
510
511/**
512 * @brief Dispatch table instance named mask.
513 *
514 * @note drop_first and drop_last are bound to drop_lo and drop_hi one way round on a little endian target
515 * and the other way on a big endian one, and so both always mean memory order.
516 * @note before reaches mmgr_scrut_lanes_before, which carries the same swap inside itself.
517 */
518EMBED_TABLE_STORAGE ScrutMaskNs mask EMBED_UNUSED = {
519 .spread = mmgr_scrut_spread,
520#if MMGR_HW_BIG_ENDIAN
521 .drop_first = mmgr_scrut_drop_hi,
522 .drop_last = mmgr_scrut_drop_lo,
523#else
524 .drop_first = mmgr_scrut_drop_lo,
525 .drop_last = mmgr_scrut_drop_hi,
526#endif
527 .bytes_below = mmgr_scrut_bytes_below,
528 .lanes_below = mmgr_scrut_lanes_below,
529 .before = mmgr_scrut_lanes_before,
530 .tail = mmgr_scrut_tail_mask,
531 .run = mmgr_scrut_run,
532 .run_edge = mmgr_scrut_run_edge,
533};
534
535/**
536 * @brief Dispatch table instance named word, with each member set to its mmgr_scrut_ function.
537 *
538 * @note No member depends on byte order, unlike the lane and mask tables above.
539 */
540EMBED_TABLE_STORAGE ScrutWordNs word EMBED_UNUSED = {
541 .load = mmgr_scrut_load,
542 .load_al = mmgr_scrut_load_al,
543 .fold_lower = mmgr_scrut_fold_lower,
544 .count = mmgr_scrut_words,
545};
546
547EMBED_END_DECLS
548
549#endif
The embedded_types headers every module builds on.
Typed loads and stores, and the proxim dispatch table.
Arguments for the lane calls.
const embed_bool ci
const embed_word word
const embed_word mask
const embed_word val
const uint8_t byte
const uint8_t fam
Type of the lane dispatch table.
embed_word(*) has_zero(const ScrutLaneCfg *args)
embed_word(*) any_upper(const ScrutLaneCfg *args)
embed_word(*) alpha(const ScrutLaneCfg *args)
embed_word(*) sub7(const ScrutLaneCfg *args)
embed_word(*) le(const ScrutLaneCfg *args)
size_t(*) count(const ScrutLaneCfg *args)
embed_word(*) any_digit(const ScrutLaneCfg *args)
embed_word(*) eq(const ScrutLaneCfg *args)
size_t(*) first(const ScrutLaneCfg *args)
size_t(*) last(const ScrutLaneCfg *args)
embed_word(*) xor_(const ScrutLaneCfg *args)
embed_word(*) ge(const ScrutLaneCfg *args)
embed_word(*) fam_eq(const ScrutLaneCfg *args)
Arguments for the mask calls.
const embed_word mask
const size_t bytes
const size_t wi
Type of the mask dispatch table.
embed_word(*) bytes_below(const ScrutMaskCfg *args)
embed_word(*) lanes_below(const ScrutMaskCfg *args)
embed_word(*) drop_last(const ScrutMaskCfg *args)
embed_word(*) spread(const ScrutMaskCfg *args)
embed_word(*) run_edge(const ScrutMaskCfg *args)
embed_word(*) tail(const ScrutMaskCfg *args)
embed_word(*) drop_first(const ScrutMaskCfg *args)
embed_word(*) before(const ScrutMaskCfg *args)
embed_word(*) run(const ScrutMaskCfg *args)
Arguments for the word calls.
const embed_word word
const size_t bytes
const void *const at
Type of the word dispatch table.
size_t(*) count(const ScrutWordCfg *args)
embed_word(*) load(const ScrutWordCfg *args)
embed_word(*) fold_lower(const ScrutWordCfg *args)
embed_word(*) load_al(const ScrutWordCfg *args)
embed_word mmgr_scrut_fam_eq(const ScrutLaneCfg *args)
Marks the lanes of args->word that match args->byte once both are reduced to the args->fam bits.
embed_word mmgr_scrut_any_digit(const ScrutLaneCfg *args)
Marks the lanes of args->word that fall in the 0x30 to 0x3F block.
embed_word mmgr_scrut_spread(const ScrutMaskCfg *args)
Widens every set lane of args->mask from its high bit to a full byte of ones.
embed_word mmgr_scrut_has_zero(const ScrutLaneCfg *args)
Marks the lanes of args->word that hold zero.
embed_word mmgr_scrut_drop_hi(const ScrutMaskCfg *args)
Clears the highest set lane of args->mask.
size_t mmgr_scrut_lane_count(const ScrutLaneCfg *args)
Counts the set lanes of args->mask.
embed_word mmgr_scrut_sub7(const ScrutLaneCfg *args)
Subtracts args->byte from every lane of args->word and keeps the low seven bits of each result.
embed_word mmgr_scrut_any_upper(const ScrutLaneCfg *args)
Marks the lanes of args->word whose MMGR_FAM_CS bits equal MMGR_FAM_CI, the 0x40 to 0x5F block.
size_t mmgr_scrut_lane_hi(const ScrutLaneCfg *args)
Returns the index of the highest set lane of args->mask.
embed_word mmgr_scrut_ge(const ScrutLaneCfg *args)
Marks the lanes of args->word that are at or above args->byte.
static const ScrutWordNs word
Dispatch table instance named word, with each member set to its mmgr_scrut_ function.
embed_word mmgr_scrut_lanes_before(const ScrutMaskCfg *args)
Returns the lanes that come before the first set lane of args->mask, in memory order.
static const ScrutLaneNs lane
Dispatch table instance named lane.
embed_word mmgr_scrut_drop_lo(const ScrutMaskCfg *args)
Clears the lowest set lane of args->mask.
static const ScrutMaskNs mask
Dispatch table instance named mask.
embed_word mmgr_scrut_run_edge(const ScrutMaskCfg *args)
Marks the lanes too near the end of a word for a run of args->bytes to fit inside it.
embed_word mmgr_scrut_le(const ScrutLaneCfg *args)
Marks the lanes of args->word that are at or below args->byte.
embed_word mmgr_scrut_bytes_below(const ScrutMaskCfg *args)
Builds a mask covering the first args->bytes bytes of a word, in memory order.
size_t mmgr_scrut_words(const ScrutWordCfg *args)
Returns how many whole words a scan of args->bytes bytes must read.
size_t mmgr_scrut_lane_lo(const ScrutLaneCfg *args)
Returns the index of the lowest set lane of args->mask.
embed_word mmgr_scrut_xor(const ScrutLaneCfg *args)
Returns the lane by lane difference of args->word and args->val.
embed_word mmgr_scrut_load(const ScrutWordCfg *args)
Loads one embed_word from args->at.
embed_word mmgr_scrut_lanes_below(const ScrutMaskCfg *args)
Builds a lane mask covering the first args->bytes lanes of a word, in memory order.
embed_word mmgr_scrut_alpha(const ScrutLaneCfg *args)
Marks the lanes of args->word holding an ASCII letter, of either case.
embed_word mmgr_scrut_load_al(const ScrutWordCfg *args)
Loads one embed_word from an aligned args->at.
embed_word mmgr_scrut_eq(const ScrutLaneCfg *args)
Marks the lanes of args->word that equal args->byte.
embed_word mmgr_scrut_run(const ScrutMaskCfg *args)
Marks the lanes of args->mask that begin a run of args->bytes set lanes.
embed_word mmgr_scrut_fold_lower(const ScrutWordCfg *args)
Returns args->word with every letter lane turned to lower case.
embed_word mmgr_scrut_tail_mask(const ScrutMaskCfg *args)
Builds the lane mask for the last partial word of a scan of args->bytes bytes.