]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: ist: add ist_find_outside() to find offending characters
authorWilly Tarreau <w@1wt.eu>
Wed, 4 Nov 2026 10:28:25 +0000 (11:28 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 4 Nov 2026 21:45:32 +0000 (22:45 +0100)
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.

include/import/ist.h

index d127785c630e2c4aaea0b8ee88e45517c2ef002a..cee5c8030419d344787e1e9f9c16e8a05c6550c4 100644 (file)
@@ -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 <ist> 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<min><min>...<min>*/
+       unsigned long l2 = ((~0UL / 255) * max);  /* 0x<max><max>...<max>*/
+       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 <chr> in string <ist> and returns
  * the tail of the string starting with this character, or (ist.end,0) if not
  * found.