]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Custom number parser to speed up config parsing
authorMaria Matejka <mq@ucw.cz>
Mon, 25 Feb 2019 22:28:36 +0000 (23:28 +0100)
committerMaria Matejka <mq@ucw.cz>
Mon, 25 Feb 2019 22:28:36 +0000 (23:28 +0100)
The glibc's generic parser is slow due to its versatility. Specialized
parsers for base-10 and base-16 are much faster and we don't use other
bases.

conf/cf-lex.l
lib/Makefile
lib/ip.c
lib/string.h
lib/strtoul.c [new file with mode: 0644]

index 45327cd90dff5a3705935fbd111e4d39ff52fc74..593df9d6652c6d3d26f04eb10807f7561002d58c 100644 (file)
@@ -168,7 +168,7 @@ WHITE [ \t]
   char *e;
 
   errno = 0;
-  l = strtoul(yytext, &e, 10);
+  l = bstrtoul(yytext, &e, 10);
   if (e && (*e != ':') || (errno == ERANGE) || (l >> 32))
     cf_error("ASN out of range");
 
@@ -186,7 +186,7 @@ WHITE [ \t]
   }
 
   errno = 0;
-  l = strtoul(e+1, &e, 10);
+  l = bstrtoul(e+1, &e, 10);
   if (e && *e || (errno == ERANGE) || (l >> len2))
     cf_error("Number out of range");
   cf_lval.i64 |= l;
@@ -213,13 +213,13 @@ WHITE [ \t]
   }
 
   errno = 0;
-  l = strtoul(yytext+2, &e, 10);
+  l = bstrtoul(yytext+2, &e, 10);
   if (e && (*e != ':') || (errno == ERANGE) || (l >> len1))
     cf_error("ASN out of range");
   cf_lval.i64 |= ((u64) l) << len2;
 
   errno = 0;
-  l = strtoul(e+1, &e, 10);
+  l = bstrtoul(e+1, &e, 10);
   if (e && *e || (errno == ERANGE) || (l >> len2))
     cf_error("Number out of range");
   cf_lval.i64 |= l;
@@ -241,7 +241,7 @@ WHITE [ \t]
   cf_lval.i64 |= ((u64) ip4_to_u32(ip4)) << 16;
 
   errno = 0;
-  l = strtoul(e, &e, 10);
+  l = bstrtoul(e, &e, 10);
   if (e && *e || (errno == ERANGE) || (l >> 16))
     cf_error("Number out of range");
   cf_lval.i64 |= l;
@@ -265,7 +265,7 @@ WHITE [ \t]
   char *e;
   unsigned long int l;
   errno = 0;
-  l = strtoul(yytext+2, &e, 16);
+  l = bstrtoul(yytext+2, &e, 16);
   if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
     cf_error("Number out of range");
   cf_lval.i = l;
@@ -276,7 +276,7 @@ WHITE [ \t]
   char *e;
   unsigned long int l;
   errno = 0;
-  l = strtoul(yytext, &e, 10);
+  l = bstrtoul(yytext, &e, 10);
   if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
     cf_error("Number out of range");
   cf_lval.i = l;
index 01f3114d65b8bcf23c9a702bbabfc67e656a0c4c..18816bb3d29c04860d011bbc095c44c8b7f0bb4f 100644 (file)
@@ -1,4 +1,4 @@
-src := bitops.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c tbf.c timer.c xmalloc.c
+src := bitops.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
 obj := $(src-o-files)
 $(all-daemon)
 
index 9497248c3a1dacb82c1b7c3258cadf2757da400f..0f5a53482b66ce13c8ad1f3f27aa9076cc132e2c 100644 (file)
--- a/lib/ip.c
+++ b/lib/ip.c
@@ -245,7 +245,7 @@ ip4_pton(const char *a, ip4_addr *o)
     char *d, *c = strchr(a, '.');
     if (!c != !i)
       return 0;
-    l = strtoul(a, &d, 10);
+    l = bstrtoul(a, &d, 10);
     if (((d != c) && *d) || (l > 255))
       return 0;
     ia = (ia << 8) | l;
index 0d34f9c5769aa2b6c1bcd5c67f91048af8f64ee0..5f7c46662161c0961a18d91ef2f746153e49b5bb 100644 (file)
@@ -24,6 +24,10 @@ int buffer_vprint(buffer *buf, const char *fmt, va_list args);
 int buffer_print(buffer *buf, const char *fmt, ...);
 void buffer_puts(buffer *buf, const char *str);
 
+#define bstrtoul(str, end, base) bstrtoul##base(str, end)
+u64 bstrtoul10(const char *str, char **end);
+u64 bstrtoul16(const char *str, char **end);
+
 int patmatch(const byte *pat, const byte *str);
 
 static inline char *xbasename(const char *str)
diff --git a/lib/strtoul.c b/lib/strtoul.c
new file mode 100644 (file)
index 0000000..44a1bb1
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *     BIRD Library -- Parse numbers
+ *
+ *     (c) 2019 Maria Matejka <mq@jmq.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "nest/bird.h"
+#include "lib/string.h"
+
+#include <errno.h>
+
+#define ULI_MAX_DIV10 (UINT64_MAX / 10)
+#define ULI_MAX_MOD10 (UINT64_MAX % 10)
+
+u64
+bstrtoul10(const char *str, char **end)
+{
+  u64 out = 0;
+  for (*end = (char *) str; (**end >= '0') && (**end <= '9'); (*end)++) {
+    u64 digit = **end - '0';
+    if ((out > ULI_MAX_DIV10) ||
+       (out == ULI_MAX_DIV10) && (digit > ULI_MAX_MOD10)) {
+      errno = ERANGE;
+      return UINT64_MAX;
+    }
+    
+    out *= 10;
+    out += (**end) - '0';
+  }
+  return out;
+}
+
+u64
+bstrtoul16(const char *str, char **end)
+{
+  u64 out = 0;
+  for (int i=0; i<=(64/4); i++) {
+    switch (str[i]) {
+      case '0' ... '9':
+       out *= 16;
+       out += str[i] - '0';
+       break;
+      case 'a' ... 'f':
+       out *= 16;
+       out += str[i] + 10 - 'a';
+       break;
+      case 'A' ... 'F':
+       out *= 16;
+       out += str[i] + 10 - 'A';
+       break;
+      default:
+       *end = (char *) &(str[i]);
+       return out;
+    }
+  }
+
+  errno = ERANGE;
+  return UINT64_MAX;
+}