Bulk memory work, a word at a time.
Anywhere you would call memcpy, memmove, memcmp, memchr or memset and want the SWAR implementation and a bound you state at the call.
Both forms exist: mmgr_memor_cpy is the same function. See The dispatch table idiom.
cpy does not handle overlap, and there is no single move. There are two, and which one is safe depends on which way the regions lie: move_up when dst is above src, move_down when it is below. move_down is cpy, copying forward is already the safe direction that way, and so the table points both at the same function.
There is no zero entry. Use set with .val = 0.
LTO matters here. mmgr_memor_chr over 512 bytes measured 610 cycles with the SWAR entries out-of-line and 187 when the linker could inline them.
Generated reference · Dropping in for <string.h> to redirect libc's names here
Every entry takes a read cap and never runs past it.
Any string operation on data that came from outside your program: a wire, a file, a user. The cap is the point: there is no entry here that scans forward until it happens to find a zero.
.ci is case-insensitivity and not a success flag.
find returns a pointer and not an offset. NULL when the needle is not there.
The parse entries report where they stopped and not whether they succeeded. to_long and the rest write the first byte they did not consume to .end. Equal to .src means nothing was parsed.
to_long and to_ulong return embed_iword and embed_word, so they parse to the target's own word width. On a 16-bit build to_ulong saturates at 65535 and not at 4294967295.
The cap is a cap instead of a length. len with .cap = 64 returns at most 64 regardless of whether a terminator was found. If you need to know which happened, compare against the cap.
find anchors on the rarest byte of the needle, using Anchor cost: where a search starts. For that reason it is fast on real text and why the profile matters.
Except for a needle of one or two bytes, which is settled by a mask chain instead: one broadcast per needle byte, every start position in a word decided at once, nothing to verify afterwards. There is no rare byte to find in two bytes and no rest to prove, and so the anchor's cost table would be read before a haystack byte is. The chain also costs the same whatever the haystack holds, where an anchor costs more when its byte is common, measured against a needle whose first byte turns up every fifteen bytes, MMgr does not move and ROM strstr loses 10%. Case folding always takes the anchor path; the chain compares raw bytes. See MMGR_FIND_CHAIN_MAX in Every compile-time knob.
Character classes as sixteen-byte bitmaps.
Available: MMGR_ASCII_NUM, MMGR_ASCII_ALPHA, MMGR_ASCII_ALNUM, MMGR_ASCII_UPPER, MMGR_ASCII_LOWER, MMGR_ASCII_HEX, MMGR_ASCII_PUNCT, MMGR_ASCII_SPACE, MMGR_ASCII_PRINT, MMGR_ASCII_CTRL.
Why sixteen bytes and not two uint64_t. Because a uint64_t is not available as a single value on a 16-bit build. Byte-indexed, the same table works at every carrier width, the constraint that shapes half the decisions in this library.
Header-only, no dispatch table, no state. Generated reference
A byte-frequency cost table. Substring search anchors on the rarest byte of the needle and not the first.
Searching for "Content-Length:" by scanning for 'C' finds a candidate on nearly every line of English text, and each one costs a full comparison. Scanning for '-', or whichever byte is rarest in the expected input, finds far fewer candidates and rejects them sooner.
The table records how common each byte is, and so the search can pick.
One CMake option taking a value and not a set of defines: -DMMGR_ANCORAE_FORMA=english. It selects the single translation unit that carries the table (src/impensa_ancorae_acus/CMakeLists.txt:7-15), and so the four not chosen are not in the image:
| value | tuned for |
|---|---|
| generic | generic, the default |
| english | English prose |
| uri | URIs and paths |
| inet | network protocol text |
| route | routing tables |
Picking the wrong one costs speed and never correctness. The search still finds what is there; it just tests more candidates on the way. So this is a tuning knob, not a configuration you have to get right before the code works.
Header-only. Generated reference