rcx

library of miscellaneous bits of C code
git clone git://git.rr3.xyz/rcx
Log | Files | Refs | README | LICENSE

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:
MMakefile | 2++
Minc/all.h | 1+
Ainc/conv.h | 25+++++++++++++++++++++++++
Asrc/conv.c | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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; +}