commit f7566042d802986927b685aaab8ed523bdb352ee
parent 9350a66843371a4a0bf4a965301db7623727cbe9
Author: Robert Russell <robertrussell.72001@gmail.com>
Date: Wed, 17 Jul 2024 19:59:43 -0700
Add new module "conv" for string/number conversions
It's a work in progress, as per the TODO comments in conv.h.
Diffstat:
4 files changed, 110 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -5,6 +5,7 @@ include config.mk
SRC =\
src/alloc.c\
src/bench.c\
+ src/conv.c\
src/debug.c\
src/dict/impl/table.c\
src/log.c\
@@ -48,6 +49,7 @@ librcx.a: $(SRC:.c=.o)
src/alloc.o: src/alloc.c inc/alloc.h inc/def.h inc/log.h inc/rcx.h inc/internal/util.h config.mk
src/bench.o: src/bench.c inc/bench.h inc/def.h inc/log.h inc/rcx.h config.mk
+src/conv.o: src/conv.c inc/conv.h inc/debug.h inc/def.h inc/rcx.h config.mk
src/debug.o: src/debug.c inc/debug.h inc/def.h inc/rcx.h config.mk
src/dict/impl/table.o: src/dict/impl/table.c inc/dict/impl/table.h $(TABLE_DEPS) config.mk
src/log.o: src/log.c inc/def.h inc/log.h inc/rcx.h config.mk
diff --git a/inc/all.h b/inc/all.h
@@ -2,6 +2,7 @@
#include "alloc.h"
#include "bits.h"
+#include "conv.h"
#include "debug.h"
#include "deque.h"
#include "error.h"
diff --git a/inc/conv.h b/inc/conv.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "def.h"
+
+/* TODO: Support for more types (in particular, signed integers). */
+/* TODO: Support for conversions in the other direction. */
+/* TODO: Support for rstr's (in addition to zstr's). */
+
+#define R_CONV_BASE_MASK (((RConvOpts)1 << 6) - 1)
+
+#define R_CONV_0B ((RConvOpts)1 << 6) /* "0b"/"0B" prefix forces base 2. */
+#define R_CONV_0Q ((RConvOpts)1 << 7) /* "0q"/"0Q" prefix forces base 4. */
+#define R_CONV_0O ((RConvOpts)1 << 8) /* "0o"/"0O" prefix forces base 8. */
+#define R_CONV_0X ((RConvOpts)1 << 9) /* "0x"/"0X" prefix forces base 16. */
+
+/* RConvOpts partially controls how conversion functions operate. It consists
+ * of a base (a number in the range [1..36]) ORed with some of the R_CONV
+ * flags. As a special case, 0 means
+ * 10 | R_CONV_0B | R_CONV_0Q | R_CONV_0O | R_CONV_0X */
+typedef uint RConvOpts;
+
+int r_conv_zstr_to_u8 (u8 *i, char *s, RConvOpts opts);
+int r_conv_zstr_to_u16(u16 *i, char *s, RConvOpts opts);
+int r_conv_zstr_to_u32(u32 *i, char *s, RConvOpts opts);
+int r_conv_zstr_to_u64(u64 *i, char *s, RConvOpts opts);
diff --git a/src/conv.c b/src/conv.c
@@ -0,0 +1,82 @@
+#include <errno.h>
+
+#include "conv.h"
+#include "debug.h"
+#include "rcx.h"
+
+#define IMPL(max) \
+ u64 n; \
+ if (r_conv_zstr_to_u64(&n, s, opts) < 0) return -1; \
+ if (n >= (max)) { \
+ errno = ERANGE; \
+ return -1; \
+ } \
+ *i = n; \
+ return 0;
+
+int r_conv_zstr_to_u8 (u8 *i, char *s, RConvOpts opts) { IMPL(U8_MAX) }
+int r_conv_zstr_to_u16(u16 *i, char *s, RConvOpts opts) { IMPL(U16_MAX) }
+int r_conv_zstr_to_u32(u32 *i, char *s, RConvOpts opts) { IMPL(U32_MAX) }
+
+int
+r_conv_zstr_to_u64(u64 *i, char *s, RConvOpts opts) {
+ if (opts == 0)
+ opts = 10 | R_CONV_0B | R_CONV_0Q | R_CONV_0O | R_CONV_0X;
+
+ int base = opts & R_CONV_BASE_MASK;
+ REQUIRE(1 <= base && base <= 36);
+
+ if (s[0] == '0') {
+ int forced_base = 0;
+
+ switch (s[1]) {
+ case 'b': case 'B': if (opts & R_CONV_0B) forced_base = 2; break;
+ case 'q': case 'Q': if (opts & R_CONV_0Q) forced_base = 4; break;
+ case 'o': case 'O': if (opts & R_CONV_0O) forced_base = 8; break;
+ case 'x': case 'X': if (opts & R_CONV_0X) forced_base = 16; break;
+ }
+
+ if (forced_base) {
+ base = forced_base;
+ s += 2;
+ }
+ }
+
+ u64 n = 0;
+
+ char *t = s;
+ for (; *t; t++) {
+ uint k;
+ char c = *t;
+ if ('0' <= c && c <= '9') {
+ k = c - '0';
+ } else if ('A' <= c && c <= 'Z') {
+ k = 10u + (c - 'A');
+ } else if ('a' <= c && c <= 'z') {
+ k = 10u + (c - 'a');
+ } else {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (k >= base) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (n > U64_MAX / base || base * n > U64_MAX - k) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ n = base * n + k;
+ }
+
+ if (s == t) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *i = n;
+ return 0;
+}