]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MINOR] add very fast IP parsing functions
authorWilly Tarreau <w@1wt.eu>
Mon, 7 Sep 2009 09:00:31 +0000 (11:00 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 30 Mar 2010 07:59:44 +0000 (09:59 +0200)
Those functions were previouly used in my firewall log parser,
and are particularly suited for use with http headers.

include/common/standard.h
src/standard.c

index fcac72fd598a500346164e70f042f0acd1fc5ba2..762fab5624c4c12efa3027de940fef15d3b31078 100644 (file)
@@ -269,6 +269,9 @@ extern unsigned int strl2uic(const char *s, int len);
 extern int strl2ic(const char *s, int len);
 extern int strl2irc(const char *s, int len, int *ret);
 extern int strl2llrc(const char *s, int len, long long *ret);
+unsigned int inetaddr_host(const char *text);
+unsigned int inetaddr_host_lim(const char *text, const char *stop);
+unsigned int inetaddr_host_lim_ret(const char *text, char *stop, const char **ret);
 
 static inline char *cut_crlf(char *s) {
 
index 8106725938871c544cabe32d54a528efef08014e..c8b0d51b38209d17482db58879d53288b9a0a078 100644 (file)
@@ -853,6 +853,160 @@ int word_match(const char *sample, int slen, const char *word, int wlen)
        return 1;
 }
 
+/* Converts any text-formatted IPv4 address to a host-order IPv4 address. It
+ * is particularly fast because it avoids expensive operations such as
+ * multiplies, which are optimized away at the end. It requires a properly
+ * formated address though (3 points).
+ */
+unsigned int inetaddr_host(const char *text)
+{
+       const unsigned int ascii_zero = ('0' << 24) | ('0' << 16) | ('0' << 8) | '0';
+       register unsigned int dig100, dig10, dig1;
+       int s;
+       const char *p, *d;
+
+       dig1 = dig10 = dig100 = ascii_zero;
+       s = 24;
+
+       p = text;
+       while (1) {
+               if (((unsigned)(*p - '0')) <= 9) {
+                       p++;
+                       continue;
+               }
+
+               /* here, we have a complete byte between <text> and <p> (exclusive) */
+               if (p == text)
+                       goto end;
+
+               d = p - 1;
+               dig1   |= (unsigned int)(*d << s);
+               if (d == text)
+                       goto end;
+
+               d--;
+               dig10  |= (unsigned int)(*d << s);
+               if (d == text)
+                       goto end;
+
+               d--;
+               dig100 |= (unsigned int)(*d << s);
+       end:
+               if (!s || *p != '.')
+                       break;
+
+               s -= 8;
+               text = ++p;
+       }
+
+       dig100 -= ascii_zero;
+       dig10  -= ascii_zero;
+       dig1   -= ascii_zero;
+       return ((dig100 * 10) + dig10) * 10 + dig1;
+}
+
+/*
+ * Idem except the first unparsed character has to be passed in <stop>.
+ */
+unsigned int inetaddr_host_lim(const char *text, const char *stop)
+{
+       const unsigned int ascii_zero = ('0' << 24) | ('0' << 16) | ('0' << 8) | '0';
+       register unsigned int dig100, dig10, dig1;
+       int s;
+       const char *p, *d;
+
+       dig1 = dig10 = dig100 = ascii_zero;
+       s = 24;
+
+       p = text;
+       while (1) {
+               if (((unsigned)(*p - '0')) <= 9 && p < stop) {
+                       p++;
+                       continue;
+               }
+
+               /* here, we have a complete byte between <text> and <p> (exclusive) */
+               if (p == text)
+                       goto end;
+
+               d = p - 1;
+               dig1   |= (unsigned int)(*d << s);
+               if (d == text)
+                       goto end;
+
+               d--;
+               dig10  |= (unsigned int)(*d << s);
+               if (d == text)
+                       goto end;
+
+               d--;
+               dig100 |= (unsigned int)(*d << s);
+       end:
+               if (!s || p == stop || *p != '.')
+                       break;
+
+               s -= 8;
+               text = ++p;
+       }
+
+       dig100 -= ascii_zero;
+       dig10  -= ascii_zero;
+       dig1   -= ascii_zero;
+       return ((dig100 * 10) + dig10) * 10 + dig1;
+}
+
+/*
+ * Idem except the pointer to first unparsed byte is returned into <ret> which
+ * must not be NULL.
+ */
+unsigned int inetaddr_host_lim_ret(const char *text, char *stop, const char **ret)
+{
+       const unsigned int ascii_zero = ('0' << 24) | ('0' << 16) | ('0' << 8) | '0';
+       register unsigned int dig100, dig10, dig1;
+       int s;
+       const char *p, *d;
+
+       dig1 = dig10 = dig100 = ascii_zero;
+       s = 24;
+
+       p = text;
+       while (1) {
+               if (((unsigned)(*p - '0')) <= 9 && p < stop) {
+                       p++;
+                       continue;
+               }
+
+               /* here, we have a complete byte between <text> and <p> (exclusive) */
+               if (p == text)
+                       goto end;
+
+               d = p - 1;
+               dig1   |= (unsigned int)(*d << s);
+               if (d == text)
+                       goto end;
+
+               d--;
+               dig10  |= (unsigned int)(*d << s);
+               if (d == text)
+                       goto end;
+
+               d--;
+               dig100 |= (unsigned int)(*d << s);
+       end:
+               if (!s || p == stop || *p != '.')
+                       break;
+
+               s -= 8;
+               text = ++p;
+       }
+
+       *ret = p;
+       dig100 -= ascii_zero;
+       dig10  -= ascii_zero;
+       dig1   -= ascii_zero;
+       return ((dig100 * 10) + dig10) * 10 + dig1;
+}
+
 /*
  * Local variables:
  *  c-indent-level: 8