From: Willy Tarreau Date: Wed, 4 Nov 2026 10:28:25 +0000 (+0100) Subject: MINOR: ist: add ist_find_outside() to find offending characters X-Git-Tag: v3.5-dev11~39 X-Git-Url: https://git.kaiwu.me/$%7BGITURL%7D/doc/static/gitweb.js?a=commitdiff_plain;h=b43f64659cbae88256c961c9e8e39e270901b738;p=haproxy.git MINOR: ist: add ist_find_outside() to find offending characters For some syntax checks it's interesting to find offending characters that are outside an interval. Unfortunately for now we only limit outselves to searching control chars < 0x20 but that's not always sufficient. Let's just implement a fast scan for characters outside a range (min and max may not be more than 0x7f apart though). Just like ist_find_range(), it reads one word at a time and finishes per byte when found or on false positives. It's optimized for non-matching inputs. --- diff --git a/include/import/ist.h b/include/import/ist.h index d127785c6..cee5c8030 100644 --- a/include/import/ist.h +++ b/include/import/ist.h @@ -861,6 +861,44 @@ static inline const char *ist_find_range(const struct ist ist, unsigned char min return NULL; } +/* Returns a pointer to the first character from that is outside of the + * range [min:max] inclusive, or NULL if none is present. The function is + * optimized for strings having no such chars by processing up to sizeof(long) + * bytes at once on architectures supporting efficient unaligned accesses. + * It requires that (max - min) < 0x80, and does support them being >= 0x80 + * just like input bytes. It's optimized for min and max to be known at build + * time. + */ +static inline const char *ist_find_outside(const struct ist ist, unsigned char min, unsigned char max) +{ + const union { unsigned long v; } __attribute__((packed)) *u; + const char *curr = (void *)ist.ptr - sizeof(long); + const char *last = curr + ist.len; + unsigned long m = (~0UL / 255) * 0x80; /* 0x8080...8080 */ + unsigned long l1 = ((~0UL / 255) * min); /* 0x...*/ + unsigned long l2 = ((~0UL / 255) * max); /* 0x...*/ + unsigned long r; + + do { + curr += sizeof(long); + if (curr > last) + break; + u = (void *)curr; + r = ((u->v - l1) | (l2 - u->v)) & m; + /* now r has 0x80 for each offending byte */ + } while (!r); + + last += sizeof(long); // we started with last off by this + if (__builtin_expect(curr < last, 0)) { + do { + if ((unsigned char)(*curr - min) > (unsigned char)(max - min)) + return curr; + curr++; + } while (curr < last); + } + return NULL; +} + /* looks for first occurrence of character in string and returns * the tail of the string starting with this character, or (ist.end,0) if not * found.