~sircmpwn/hare-dev

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
2 2

[PATCH hare v2] strconv: Merge functions with their -b versions

Details
Message ID
<20240410175103.2493-1-yyp@disroot.org>
DKIM signature
pass
Download raw message
Patch: +214 -371
This is a breaking change: all calls to stoub and similar functions should drop
the -b suffix.

Signed-off-by: Alexey Yerin <yyp@disroot.org>
---
v1 -> v2: Remove "If the base is not specified, base 10 is used" from
          documentation as this is already clear from the prototype
 encoding/hex/hex.ha  |   4 +-
 fmt/print.ha         |   7 ++-
 format/tar/reader.ha |   4 +-
 hare/lex/lex.ha      |   6 +--
 net/ip/ip.ha         |   2 +-
 net/uri/parse.ha     |   2 +-
 strconv/itos.ha      |  93 +++++++++++++--------------------
 strconv/numeric.ha   |  95 +++++++++++++---------------------
 strconv/stof.ha      |  90 ++++++++++++++------------------
 strconv/stoi.ha      |  77 ++++++++++------------------
 strconv/stou.ha      |  84 ++++++++++--------------------
 strconv/utos.ha      | 119 +++++++++++++++----------------------------
 uuid/uuid.ha         |   2 +-
 13 files changed, 214 insertions(+), 371 deletions(-)

diff --git a/encoding/hex/hex.ha b/encoding/hex/hex.ha
index b4378bce..259eb06e 100644
--- a/encoding/hex/hex.ha
+++ b/encoding/hex/hex.ha
@@ -42,7 +42,7 @@ fn encode_writer(s: *io::stream, in: const []u8) (size | io::error) = {
	};
	let z = 0z;
	for (let i = 0z; i < len(in); i += 1) {
		const r = strconv::u8tosb(in[i], strconv::base::HEX_LOWER);
		const r = strconv::u8tos(in[i], strconv::base::HEX_LOWER);
		if (len(r) == 1) {
			match(fmt::fprint(s.out, "0")) {
			case let b: size =>
@@ -151,7 +151,7 @@ fn decode_reader(s: *io::stream, out: []u8) (size | io::EOF | io::error) = {
	const l = nr / 2;
	for (let i = 0z; i < l; i += 1) {
		const oct = strings::fromutf8_unsafe(buf[i * 2..i * 2 + 2]);
		const u = match (strconv::stou8b(oct, 16)) {
		const u = match (strconv::stou8(oct, 16)) {
		case (strconv::invalid | strconv::overflow) =>
			s.state = errors::invalid;
			return errors::invalid;
diff --git a/fmt/print.ha b/fmt/print.ha
index e1422c91..0b5aa4ca 100644
--- a/fmt/print.ha
+++ b/fmt/print.ha
@@ -90,14 +90,13 @@ case let s: str =>
case let b: bool =>
	return io::write(out, strings::toutf8(if (b) "true" else "false"));
case let p: uintptr =>
	const s = strconv::uptrtosb(p, mod.base);
	const s = strconv::uptrtos(p, mod.base);
	return io::write(out, strings::toutf8(s));
case let v: nullable *opaque =>
	match (v) {
	case let v: *opaque =>
		let z = io::write(out, strings::toutf8("0x"))?;
		const s = strconv::uptrtosb(v: uintptr,
			strconv::base::HEX_LOWER);
		const s = strconv::uptrtos(v: uintptr, strconv::base::HEX_LOWER);
		z += io::write(out, strings::toutf8(s))?;
		return z;
	case null =>
@@ -126,7 +125,7 @@ case let i: types::integer =>
		};
	};

	let i = strconv::integertosb(i, mod.base);
	let i = strconv::integertos(i, mod.base);
	let pad = if (mod.prec < len(sign) + len(i)) {
		yield 0z;
	} else {
diff --git a/format/tar/reader.ha b/format/tar/reader.ha
index c7f7208f..c466b0c4 100644
--- a/format/tar/reader.ha
+++ b/format/tar/reader.ha
@@ -187,7 +187,7 @@ fn readstr(rd: *memio::stream, ln: size) str = {

fn readoct(rd: *memio::stream, ln: size) (uint | invalid) = {
	const string = readstr(rd, ln);
	match (strconv::stoub(string, strconv::base::OCT)) {
	match (strconv::stou(string, strconv::base::OCT)) {
	case let u: uint =>
		return u;
	case =>
@@ -197,7 +197,7 @@ fn readoct(rd: *memio::stream, ln: size) (uint | invalid) = {

fn readsize(rd: *memio::stream, ln: size) (size | invalid) = {
	const string = readstr(rd, ln);
	match (strconv::stozb(string, strconv::base::OCT)) {
	match (strconv::stoz(string, strconv::base::OCT)) {
	case let z: size =>
		return z;
	case =>
diff --git a/hare/lex/lex.ha b/hare/lex/lex.ha
index 3907a3cf..85af7aa0 100644
--- a/hare/lex/lex.ha
+++ b/hare/lex/lex.ha
@@ -171,7 +171,7 @@ fn lex_unicode(lex: *lexer, loc: location, n: size) (rune | error) = {
		buf[i] = r: u8;
	};
	let s = strings::fromutf8_unsafe(buf[..n]);
	return strconv::stou32b(s, strconv::base::HEX) as u32: rune;
	return strconv::stou32(s, strconv::base::HEX) as u32: rune;
};

fn lex_rune(lex: *lexer, loc: location) (rune | error) = {
@@ -600,9 +600,9 @@ fn lex_literal(lex: *lexer) (token | error) = {
	let val = switch (suff) {
	case ltok::LIT_F32, ltok::LIT_F64, ltok::LIT_FCONST =>
		val = strings::fromutf8(chars[..floatend])!;
		yield strconv::stof64b(val, base);
		yield strconv::stof64(val, base);
	case =>
		yield strconv::stou64b(val, base);
		yield strconv::stou64(val, base);
	};
	let val = match (val) {
	case let val: u64 =>
diff --git a/net/ip/ip.ha b/net/ip/ip.ha
index 7aaa61bf..346ffaef 100644
--- a/net/ip/ip.ha
+++ b/net/ip/ip.ha
@@ -112,7 +112,7 @@ export fn parsev6(st: str) (addr6 | invalid) = {
			ells = i;
			continue;
		};
		match (strconv::stou16b(s, 16)) {
		match (strconv::stou16(s, strconv::base::HEX)) {
		case let val: u16 =>
			endian::beputu16(ret[i..], val);
			i += 2;
diff --git a/net/uri/parse.ha b/net/uri/parse.ha
index 6eacc251..74eddd35 100644
--- a/net/uri/parse.ha
+++ b/net/uri/parse.ha
@@ -318,7 +318,7 @@ fn percent_decode_static(out: io::handle, s: str) (void | invalid) = {
					memio::appendrune(&tmp, r)!;
				};

				match (strconv::stou8b(memio::string(&tmp)!,
				match (strconv::stou8(memio::string(&tmp)!,
					strconv::base::HEX)) {
				case let ord: u8 =>
					append(percent_data, ord);
diff --git a/strconv/itos.ha b/strconv/itos.ha
index 658117a5..a8c2e842 100644
--- a/strconv/itos.ha
+++ b/strconv/itos.ha
@@ -5,16 +5,16 @@ use bytes;
use strings;
use types;

// Converts an i64 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i64tosb(i: i64, b: base) const str = {
// Converts an i64 to a string. The return value is statically allocated and
// will be overwritten on subsequent calls; see [[strings::dup]] to duplicate
// the result.
export fn i64tos(i: i64, b: base = base::DEC) const str = {
	static assert(types::I64_MAX == 9223372036854775807);
	if (b == base::DEFAULT) {
		b = base::DEC;
	};

	if (i >= 0) return u64tosb(i: u64, b);
	if (i >= 0) return u64tos(i: u64, b);

	static let buf: [65]u8 = [0...]; // 64 binary digits plus -

@@ -23,7 +23,7 @@ export fn i64tosb(i: i64, b: base) const str = {
	buf[0] = '-';
	s.length = 1;

	let u = strings::toutf8(u64tosb((-i): u64, b));
	let u = strings::toutf8(u64tos((-i): u64, b));
	assert(len(u) < len(buf));

	buf[1..len(u) + 1] = u[..];
@@ -32,60 +32,35 @@ export fn i64tosb(i: i64, b: base) const str = {
	return *(&s: *str);
};

// Converts an i32 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i32tosb(i: i32, b: base) const str = i64tosb(i, b);

// Converts an i16 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i16tosb(i: i16, b: base) const str = i64tosb(i, b);

// Converts an i8 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i8tosb(i: i8, b: base) const str = i64tosb(i, b);

// Converts an int to a string in the given base. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [[strings::dup]] to duplicate the result.
export fn itosb(i: int, b: base) const str = i64tosb(i, b);

// Converts an i64 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i64tos(i: i64) const str = i64tosb(i, base::DEC);

// Converts an i32 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i32tos(i: i32) const str = i64tos(i);

// Converts an i16 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i16tos(i: i16) const str = i64tos(i);

// Converts an i8 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i8tos(i: i8) const str = i64tos(i);

// Converts an int to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn itos(i: int) const str = i64tos(i);

@test fn itosb() void = {
	assert("11010" == i64tosb(0b11010, base::BIN));
	assert("1234567" == i64tosb(0o1234567, base::OCT));
	assert("123456789" == i64tosb(123456789, base::DEC));
	assert("123456789ABCDEF" == i64tosb(0x123456789ABCDEF, base::HEX));
	assert("123456789ABCDEF" == i64tosb(0x123456789ABCDEF, base::HEX_UPPER));
	assert("123456789abcdef" == i64tosb(0x123456789ABCDEF, base::HEX_LOWER));
// Converts an i32 to a string. The return value is statically allocated and
// will be overwritten on subsequent calls; see [[strings::dup]] to duplicate
// the result.
export fn i32tos(i: i32, b: base = base::DEC) const str = i64tos(i, b);

// Converts an i16 to a string. The return value is statically allocated and
// will be overwritten on subsequent calls; see [[strings::dup]] to duplicate
// the result.
export fn i16tos(i: i16, b: base = base::DEC) const str = i64tos(i, b);

// Converts an i8 to a string. The return value is statically allocated and will
// be overwritten on subsequent calls; see [[strings::dup]] to duplicate the
// result.
export fn i8tos(i: i8, b: base = base::DEC) const str = i64tos(i, b);

// Converts an int to a string. The return value is statically allocated and
// will be overwritten on subsequent calls; see [[strings::dup]] to duplicate
// the result.
export fn itos(i: int, b: base = base::DEC) const str = i64tos(i, b);

@test fn itos_bases() void = {
	assert("11010" == i64tos(0b11010, base::BIN));
	assert("1234567" == i64tos(0o1234567, base::OCT));
	assert("123456789" == i64tos(123456789, base::DEC));
	assert("123456789ABCDEF" == i64tos(0x123456789ABCDEF, base::HEX));
	assert("123456789ABCDEF" == i64tos(0x123456789ABCDEF, base::HEX_UPPER));
	assert("123456789abcdef" == i64tos(0x123456789ABCDEF, base::HEX_LOWER));
	assert("-1000000000000000000000000000000000000000000000000000000000000000"
		== i64tosb(types::I64_MIN, base::BIN));
		== i64tos(types::I64_MIN, base::BIN));
};

@test fn itos() void = {
diff --git a/strconv/numeric.ha b/strconv/numeric.ha
index e1612f5c..2bacac85 100644
--- a/strconv/numeric.ha
+++ b/strconv/numeric.ha
@@ -3,75 +3,60 @@

use types;

// Converts any [[types::signed]] to a string in a given base. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [[strings::dup]] to duplicate the result.
export fn signedtosb(n: types::signed, b: base) const str = {
// Converts any [[types::signed]] to a string. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]]
// to duplicate the result. If base is not specified, base 10 is used.
export fn signedtos(n: types::signed, b: base = base::DEC) const str = {
	match (n) {
	case let i: int =>
		return itosb(i, b);
		return itos(i, b);
	case let i: i8 =>
		return i8tosb(i, b);
		return i8tos(i, b);
	case let i: i16 =>
		return i16tosb(i, b);
		return i16tos(i, b);
	case let i: i32 =>
		return i32tosb(i, b);
		return i32tos(i, b);
	case let i: i64 =>
		return i64tosb(i, b);
		return i64tos(i, b);
	};
};

// Converts any [[types::signed]] to a string in base 10. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [[strings::dup]] to duplicate the result.
export fn signedtos(n: types::signed) const str = signedtosb(n, base::DEC);

// Converts any [[types::unsigned]] to a string in a given base. The return value
// is statically allocated and will be overwritten on subsequent calls; see
// [[strings::dup]] to duplicate the result.
export fn unsignedtosb(n: types::unsigned, b: base) const str = {
// Converts any [[types::unsigned]] to a string. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]]
// to duplicate the result. If base is not specified, base 10 is used.
export fn unsignedtos(n: types::unsigned, b: base = base::DEC) const str = {
	match (n) {
	case let u: size =>
		return ztosb(u, b);
		return ztos(u, b);
	case let u: uint =>
		return utosb(u, b);
		return utos(u, b);
	case let u: u8 =>
		return u8tosb(u, b);
		return u8tos(u, b);
	case let u: u16 =>
		return u16tosb(u, b);
		return u16tos(u, b);
	case let u: u32 =>
		return u32tosb(u, b);
		return u32tos(u, b);
	case let u: u64 =>
		return u64tosb(u, b);
		return u64tos(u, b);
	};
};

// Converts any [[types::unsigned]] to a string in base 10. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [[strings::dup]] to duplicate the result.
export fn unsignedtos(n: types::unsigned) const str = unsignedtosb(n, base::DEC);

// Converts any [[types::integer]] to a string in a given base. The return value
// is statically allocated and will be overwritten on subsequent calls; see
// [[strings::dup]] to duplicate the result.
export fn integertosb(n: types::integer, b: base) const str = {
// Converts any [[types::integer]] to a string. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]]
// to duplicate the result. If base is not specified, base 10 is used.
export fn integertos(n: types::integer, b: base = base::DEC) const str = {
	match (n) {
	case let s: types::signed =>
		return signedtosb(s, b);
		return signedtos(s, b);
	case let u: types::unsigned =>
		return unsignedtosb(u, b);
		return unsignedtos(u, b);
	};
};

// Converts any [[types::integer]] to a string in base 10. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [[strings::dup]] to duplicate the result.
export fn integertos(n: types::integer) const str = integertosb(n, base::DEC);

// Converts any [[types::floating]] to a string in a given base. The return value
// is statically allocated and will be overwritten on subsequent calls; see
// [[strings::dup]] to duplicate the result.
export fn floatingtosb(n: types::floating, b: base) const str = {
// Converts any [[types::floating]] to a string. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]]
// to duplicate the result. If base is not specified, base 10 is used.
export fn floatingtos(n: types::floating, b: base = base::DEC) const str = {
	if (b == base::DEFAULT) {
		b = base::DEC;
	};
@@ -84,28 +69,18 @@ export fn floatingtosb(n: types::floating, b: base) const str = {
	};
};

// Converts any [[types::floating]] to a string in base 10. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [[strings::dup]] to duplicate the result.
export fn floatingtos(n: types::floating) const str = floatingtosb(n, base::DEC);

// Converts any [[types::numeric]] to a string in a given base. The return value
// is statically allocated and will be overwritten on subsequent calls; see
// [[strings::dup]] to duplicate the result.
export fn numerictosb(n: types::numeric, b: base) const str = {
// Converts any [[types::numeric]] to a string. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]]
// to duplicate the result. If base is not specified, base 10 is used.
export fn numerictos(n: types::numeric, b: base = base::DEC) const str = {
	match (n) {
	case let i: types::integer =>
		return integertosb(i, b);
		return integertos(i, b);
	case let f: types::floating =>
		return floatingtosb(f, b);
		return floatingtos(f, b);
	};
};

// Converts any [[types::numeric]] to a string in base 10. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [[strings::dup]] to duplicate the result.
export fn numerictos(n: types::numeric) const str = numerictosb(n, base::DEC);

@test fn numeric() void = {
	const cases: [_]types::numeric = [
		42u8, 1337u16, 1337u32, 1337u64, 42i8, -42i8, 1337i16, -1337i16,
diff --git a/strconv/stof.ha b/strconv/stof.ha
index f5ae2fa5..563a1470 100644
--- a/strconv/stof.ha
+++ b/strconv/stof.ha
@@ -619,14 +619,15 @@ fn special(s: str) (f32 | void) = {
	};
};

// Converts a string to a f64 in [[base::DEC]] or [[base::HEX]]. If the string
// is not a syntactically well-formed floating-point number, [[invalid]] is
// returned. If the string represents a floating-point number that is larger
// than the largest finite f64 number, [[overflow]] is returned. Zero is
// returned if the string represents a floating-point number that is smaller
// than the f64 number nearest to zero with respective sign.
// Recognizes "Infinity", "+Infinity", "-Infinity", and "NaN", case insensitive.
export fn stof64b(s: str, b: base) (f64 | invalid | overflow) = {
// Converts a string to a f64 in [[base::DEC]] or [[base::HEX]]. If base is not
// provided, [[base::DEC]] is used. If the string is not a syntactically
// well-formed floating-point number, [[invalid]] is returned. If the string
// represents a floating-point number that is larger than the largest finite
// f64 number, [[overflow]] is returned. Zero is returned if the string
// represents a floating-point number that is smaller than the f64 number
// nearest to zero with respective sign. Recognizes "Infinity", "+Infinity",
// "-Infinity", and "NaN", case insensitive.
export fn stof64(s: str, b: base = base::DEC) (f64 | invalid | overflow) = {
	if (b == base::DEFAULT) b = base::DEC;
	assert(b == base::DEC || b == base::HEX);

@@ -660,14 +661,15 @@ export fn stof64b(s: str, b: base) (f64 | invalid | overflow) = {
	return math::f64frombits(n);
};

// Converts a string to a f32 in [[base::DEC]] or [[base::HEX]]. If the string
// is not a syntactically well-formed floating-point number, [[invalid]] is
// returned. If the string represents a floating-point number that is larger
// than the largest finite f32 number, [[overflow]] is returned. Zero is
// returned if the string represents a floating-point number that is smaller
// than the f32 number nearest to zero with respective sign.
// Recognizes "Infinity", "+Infinity", "-Infinity", and "NaN", case insensitive.
export fn stof32b(s: str, b: base) (f32 | invalid | overflow) = {
// Converts a string to a f32 in [[base::DEC]] or [[base::HEX]]. If base is not
// provided, [[base::DEC]] is used. If the string is not a syntactically
// well-formed floating-point number, [[invalid]] is returned. If the string
// represents a floating-point number that is larger than the largest finite
// f32 number, [[overflow]] is returned. Zero is returned if the string
// represents a floating-point number that is smaller than the f32 number
// nearest to zero with respective sign. Recognizes "Infinity", "+Infinity",
// "-Infinity", and "NaN", case insensitive.
export fn stof32(s: str, b: base = base::DEC) (f32 | invalid | overflow) = {
	if (b == base::DEFAULT) b = base::DEC;
	assert(b == base::DEC || b == base::HEX);

@@ -702,24 +704,6 @@ export fn stof32b(s: str, b: base) (f32 | invalid | overflow) = {
};


// Converts a string to a f64. If the string is not a syntactically well-formed
// floating-point number in base 10, [[invalid]] is returned. If the string
// represents a floating-point number that is larger than the largest finite f64
// number, [[overflow]] is returned. Zero is returned if the string represents a
// floating-point number that is smaller than the f64 number nearest to zero
// with respective sign.
// Recognizes "Infinity", "+Infinity", "-Infinity", and "NaN", case insensitive.
export fn stof64(s: str) (f64 | invalid | overflow) = stof64b(s, base::DEC);

// Converts a string to a f32. If the string is not a syntactically well-formed
// floating-point number in base 10, [[invalid]] is returned. If the string
// represents a floating-point number that is larger than the largest finite f32
// number, [[overflow]] is returned. Zero is returned if the string represents a
// floating-point number that is smaller than the f32 number nearest to zero
// with respective sign.
// Recognizes "Infinity", "+Infinity", "-Infinity", and "NaN", case insensitive.
export fn stof32(s: str) (f32 | invalid | overflow) = stof32b(s, base::DEC);

@test fn stof64() void = {
	assert(stof64("0"): f64 == 0.0);
	assert(stof64("200"): f64 == 200.0);
@@ -781,27 +765,27 @@ export fn stof32(s: str) (f32 | invalid | overflow) = stof32b(s, base::DEC);
};

@test fn stofhex() void = {
	assert(stof64b("0p0", base::HEX)! == 0x0.0p0);
	assert(stof64b("1p0", base::HEX)! == 0x1.0p0);
	assert(stof64b("-1p0", base::HEX)! == -0x1.0p0);
	assert(stof64b("1.fp-2", base::HEX)! == 0x1.fp-2);
	assert(stof64b("1.fffffffffffffp+1023", base::HEX)!
	assert(stof64("0p0", base::HEX)! == 0x0.0p0);
	assert(stof64("1p0", base::HEX)! == 0x1.0p0);
	assert(stof64("-1p0", base::HEX)! == -0x1.0p0);
	assert(stof64("1.fp-2", base::HEX)! == 0x1.fp-2);
	assert(stof64("1.fffffffffffffp+1023", base::HEX)!
		== math::F64_MAX_NORMAL);
	assert(stof64b("1.0000000000000p-1022", base::HEX)!
	assert(stof64("1.0000000000000p-1022", base::HEX)!
		== math::F64_MIN_NORMAL);
	assert(stof64b("0.0000000000001p-1022", base::HEX)!
	assert(stof64("0.0000000000001p-1022", base::HEX)!
		== math::F64_MIN);
	assert(stof64b("1p+1024", base::HEX) is overflow);
	assert(stof64b("0.00000000000001p-1022", base::HEX)! == 0.0);

	assert(stof32b("0p0", base::HEX)! == 0x0.0p0);
	assert(stof32b("1p0", base::HEX)! == 0x1.0p0);
	assert(stof32b("-1p0", base::HEX)! == -0x1.0p0);
	assert(stof32b("1.fp-2", base::HEX)! == 0x1.fp-2);
	assert(stof32b("1.fffffd586b834p+127", base::HEX)!
	assert(stof64("1p+1024", base::HEX) is overflow);
	assert(stof64("0.00000000000001p-1022", base::HEX)! == 0.0);

	assert(stof32("0p0", base::HEX)! == 0x0.0p0);
	assert(stof32("1p0", base::HEX)! == 0x1.0p0);
	assert(stof32("-1p0", base::HEX)! == -0x1.0p0);
	assert(stof32("1.fp-2", base::HEX)! == 0x1.fp-2);
	assert(stof32("1.fffffd586b834p+127", base::HEX)!
		== math::F32_MAX_NORMAL);
	assert(stof32b("1.0p-126", base::HEX)! == math::F32_MIN_NORMAL);
	assert(stof32b("1.6p-150", base::HEX)! == math::F32_MIN);
	assert(stof32b("1.0p+128", base::HEX) is overflow);
	assert(stof32b("1.0p-151", base::HEX)! == 0.0);
	assert(stof32("1.0p-126", base::HEX)! == math::F32_MIN_NORMAL);
	assert(stof32("1.6p-150", base::HEX)! == math::F32_MIN);
	assert(stof32("1.0p+128", base::HEX) is overflow);
	assert(stof32("1.0p-151", base::HEX)! == 0.0);
};
diff --git a/strconv/stoi.ha b/strconv/stoi.ha
index 9014f3fb..ca3898a7 100644
--- a/strconv/stoi.ha
+++ b/strconv/stoi.ha
@@ -4,10 +4,10 @@
use strings;
use types;

// Converts a string to an i64 in the given base. Returns [[invalid]] if the
// string is empty or contains invalid characters. Returns [[overflow]] if the
// number is too large to be represented by an i64.
export fn stoi64b(s: str, base: base) (i64 | invalid | overflow) = {
// Converts a string to an i64. Returns [[invalid]] if the string is empty or
// contains invalid characters. Returns [[overflow]] if the number is too large
// to be represented by an i64.
export fn stoi64(s: str, base: base = base::DEC) (i64 | invalid | overflow) = {
	let (sign, u) = parseint(s, base)?;
	// Two's complement: I64_MIN = -I64_MAX - 1
	let max = if (sign) types::I64_MAX: u64 + 1 else types::I64_MAX: u64;
@@ -23,62 +23,37 @@ fn stoiminmax(
	min: i64,
	max: i64,
) (i64 | invalid | overflow) = {
	const n = stoi64b(s, base)?;
	const n = stoi64(s, base)?;
	if (n < min || n > max) {
		return overflow;
	};
	return n;
};

// Converts a string to an i32 in the given base. Returns [[invalid]] if the
// string is empty or contains invalid characters. Returns [[overflow]] if the
// number is too large to be represented by an i32.
export fn stoi32b(s: str, base: base) (i32 | invalid | overflow) =
// Converts a string to an i32. Returns [[invalid]] if the string is empty or
// contains invalid characters. Returns [[overflow]] if the number is too large
// to be represented by an i32.
export fn stoi32(s: str, base: base = base::DEC) (i32 | invalid | overflow) =
	stoiminmax(s, base, types::I32_MIN, types::I32_MAX)?: i32;

// Converts a string to an i16 in the given base. Returns [[invalid]] if the
// string is empty or contains invalid characters. Returns [[overflow]] if the
// number is too large to be represented by an i16.
export fn stoi16b(s: str, base: base) (i16 | invalid | overflow) =
// Converts a string to an i16. Returns [[invalid]] if the string is empty or
// contains invalid characters. Returns [[overflow]] if the number is too large
// to be represented by an i16.
export fn stoi16(s: str, base: base = base::DEC) (i16 | invalid | overflow) =
	stoiminmax(s, base, types::I16_MIN, types::I16_MAX)?: i16;

// Converts a string to an i8 in the given base. Returns [[invalid]] if the
// string is empty or contains invalid characters. Returns [[overflow]] if the
// number is too large to be represented by an i8.
export fn stoi8b(s: str, base: base) (i8 | invalid | overflow) =
// Converts a string to an i8. Returns [[invalid]] if the string is empty or
// contains invalid characters. Returns [[overflow]] if the number is too large
// to be represented by an i8.
export fn stoi8(s: str, base: base = base::DEC) (i8 | invalid | overflow) =
	stoiminmax(s, base, types::I8_MIN, types::I8_MAX)?: i8;

// Converts a string to an int in the given base. Returns [[invalid]] if the
// string is empty or contains invalid characters. Returns [[overflow]] if the
// number is too large to be represented by an int.
export fn stoib(s: str, base: base) (int | invalid | overflow) =
// Converts a string to an int. Returns [[invalid]] if the string is empty or
// contains invalid characters. Returns [[overflow]] if the number is too large
// to be represented by an int.
export fn stoi(s: str, base: base = base::DEC) (int | invalid | overflow) =
	stoiminmax(s, base, types::INT_MIN, types::INT_MAX)?: int;

// Converts a string to an i64 in base 10. Returns [[invalid]] if the string is
// empty or contains invalid characters. Returns [[overflow]] if the number is
// too large to be represented by an i64.
export fn stoi64(s: str) (i64 | invalid | overflow) = stoi64b(s, base::DEC);

// Converts a string to an i32 in base 10. Returns [[invalid]] if the string is
// empty or contains invalid characters. Returns [[overflow]] if the number is
// too large to be represented by an i32.
export fn stoi32(s: str) (i32 | invalid | overflow) = stoi32b(s, base::DEC);

// Converts a string to an i16 in base 10. Returns [[invalid]] if the string is
// empty or contains invalid characters. Returns [[overflow]] if the number is
// too large to be represented by an i16.
export fn stoi16(s: str) (i16 | invalid | overflow) = stoi16b(s, base::DEC);

// Converts a string to an i8 in base 10. Returns [[invalid]] if the string is
// empty or contains invalid characters. Returns [[overflow]] if the number is
// too large to be represented by an i8.
export fn stoi8(s: str) (i8 | invalid | overflow) = stoi8b(s, base::DEC);

// Converts a string to an int in base 10. Returns [[invalid]] if the string is
// empty or contains invalid characters. Returns [[overflow]] if the number is
// too large to be represented by an int.
export fn stoi(s: str) (int | invalid | overflow) = stoib(s, base::DEC);

@test fn stoi() void = {
	assert(stoi64("") as invalid == 0);
	assert(stoi64("abc") as invalid == 0);
@@ -104,9 +79,9 @@ export fn stoi(s: str) (int | invalid | overflow) = stoib(s, base::DEC);
	assert(stoi32("-2147483648") as i32 == -2147483648);
};

@test fn stoib() void = {
	assert(stoi64b("-7f", 16) as i64 == -0x7f);
	assert(stoi64b("7F", 16) as i64 == 0x7f);
	assert(stoi64b("37", 8) as i64 == 0o37);
	assert(stoi64b("-110101", 2) as i64 == -0b110101);
@test fn stoi_bases() void = {
	assert(stoi64("-7f", 16) as i64 == -0x7f);
	assert(stoi64("7F", 16) as i64 == 0x7f);
	assert(stoi64("37", 8) as i64 == 0o37);
	assert(stoi64("-110101", 2) as i64 == -0b110101);
};
diff --git a/strconv/stou.ha b/strconv/stou.ha
index 1b924118..24d58446 100644
--- a/strconv/stou.ha
+++ b/strconv/stou.ha
@@ -63,10 +63,10 @@ fn parseint(s: str, base: base) ((bool, u64) | invalid | overflow) = {
	return (sign, n);
};

// Converts a string to a u64 in the given base. Returns [[invalid]] if the
// string is empty or contains invalid characters. Returns [[overflow]] if the
// number is too large to be represented by a u64.
export fn stou64b(s: str, base: base) (u64 | invalid | overflow) = {
// Converts a string to a u64. Returns [[invalid]] if the string is empty or
// contains invalid characters. Returns [[overflow]] if the number is too large
// to be represented by a u64.
export fn stou64(s: str, base: base = base::DEC) (u64 | invalid | overflow) = {
	let (sign, u) = parseint(s, base)?;
	if (sign) {
		return overflow;
@@ -75,73 +75,43 @@ export fn stou64b(s: str, base: base) (u64 | invalid | overflow) = {
};

fn stoumax(s: str, base: base, max: u64) (u64 | invalid | overflow) = {
	const n = stou64b(s, base)?;
	const n = stou64(s, base)?;
	if (n > max) {
		return overflow;
	};
	return n;
};

// Converts a string to a u32 in the given base. Returns [[invalid]] if the
// string is empty or contains invalid characters. Returns [[overflow]] if the
// number is too large to be represented by a u32.
export fn stou32b(s: str, base: base) (u32 | invalid | overflow) =
// Converts a string to a u32. Returns [[invalid]] if the string is empty or
// contains invalid characters. Returns [[overflow]] if the number is too large
// to be represented by a u32.
export fn stou32(s: str, base: base = base::DEC) (u32 | invalid | overflow) =
	stoumax(s, base, types::U32_MAX)?: u32;

// Converts a string to a u16 in the given base. Returns [[invalid]] if the
// string is empty or contains invalid characters. Returns [[overflow]] if the
// number is too large to be represented by a u16.
export fn stou16b(s: str, base: base) (u16 | invalid | overflow) =
// Converts a string to a u16. Returns [[invalid]] if the string is empty or
// contains invalid characters. Returns [[overflow]] if the number is too large
// to be represented by a u16.
export fn stou16(s: str, base: base = base::DEC) (u16 | invalid | overflow) =
	stoumax(s, base, types::U16_MAX)?: u16;

// Converts a string to a u8 in the given base. Returns [[invalid]] if the
// string is empty or contains invalid characters. Returns [[overflow]] if the
// number is too large to be represented by a u8.
export fn stou8b(s: str, base: base) (u8 | invalid | overflow) =
// Converts a string to a u8. Returns [[invalid]] if the string is empty or
// contains invalid characters. Returns [[overflow]] if the number is too large
// to be represented by a u8.
export fn stou8(s: str, base: base = base::DEC) (u8 | invalid | overflow) =
	stoumax(s, base, types::U8_MAX)?: u8;

// Converts a string to a uint in the given base. Returns [[invalid]] if the
// string is empty or contains invalid characters. Returns [[overflow]] if the
// number is too large to be represented by a uint.
export fn stoub(s: str, base: base) (uint | invalid | overflow) =
export fn stou(s: str, base: base = base::DEC) (uint | invalid | overflow) =
	stoumax(s, base, types::UINT_MAX)?: uint;

// Converts a string to a size in the given base. Returns [[invalid]] if the
// string is empty or contains invalid characters. Returns [[overflow]] if the
// number is too large to be represented by a size.
export fn stozb(s: str, base: base) (size | invalid | overflow) =
// Converts a string to a size. Returns [[invalid]] if the string is empty or
// contains invalid characters. Returns [[overflow]] if the number is too large
// to be represented by a size.
export fn stoz(s: str, base: base = base::DEC) (size | invalid | overflow) =
	stoumax(s, base, types::SIZE_MAX)?: size;

// Converts a string to a u64 in base 10. Returns [[invalid]] if the string is
// empty or contains invalid characters. Returns [[overflow]] if the number is
// too large to be represented by a u64.
export fn stou64(s: str) (u64 | invalid | overflow) = stou64b(s, base::DEC);

// Converts a string to a u32 in base 10. Returns [[invalid]] if the string is
// empty or contains invalid characters. Returns [[overflow]] if the number is
// too large to be represented by a u32.
export fn stou32(s: str) (u32 | invalid | overflow) = stou32b(s, base::DEC);

// Converts a string to a u16 in base 10. Returns [[invalid]] if the string is
// empty or contains invalid characters. Returns [[overflow]] if the number is
// too large to be represented by a u16.
export fn stou16(s: str) (u16 | invalid | overflow) = stou16b(s, base::DEC);

// Converts a string to a u8 in base 10. Returns [[invalid]] if the string is
// empty or contains invalid characters. Returns [[overflow]] if the number is
// too large to be represented by a u8.
export fn stou8(s: str) (u8 | invalid | overflow) = stou8b(s, base::DEC);

// Converts a string to a uint in base 10. Returns [[invalid]] if the string is
// empty or contains invalid characters. Returns [[overflow]] if the number is
// too large to be represented by a uint.
export fn stou(s: str) (uint | invalid | overflow) = stoub(s, base::DEC);

// Converts a string to a size in base 10. Returns [[invalid]] if the string is
// empty or contains invalid characters. Returns [[overflow]] if the number is
// too large to be represented by a size.
export fn stoz(s: str) (size | invalid | overflow) = stozb(s, base::DEC);

@test fn stou() void = {
	assert(stou64("") as invalid == 0);
	assert(stou64("+") as invalid == 1);
@@ -158,9 +128,9 @@ export fn stoz(s: str) (size | invalid | overflow) = stozb(s, base::DEC);
	assert(stou64("18446744073709551615") as u64 == 18446744073709551615);
};

@test fn stoub() void = {
	assert(stou64b("7f", 16) as u64 == 0x7f);
	assert(stou64b("7F", 16) as u64 == 0x7f);
	assert(stou64b("37", 8) as u64 == 0o37);
	assert(stou64b("110101", 2) as u64 == 0b110101);
@test fn stou_bases() void = {
	assert(stou64("7f", 16) as u64 == 0x7f);
	assert(stou64("7F", 16) as u64 == 0x7f);
	assert(stou64("37", 8) as u64 == 0o37);
	assert(stou64("110101", 2) as u64 == 0b110101);
};
diff --git a/strconv/utos.ha b/strconv/utos.ha
index b1062ffa..32645ffb 100644
--- a/strconv/utos.ha
+++ b/strconv/utos.ha
@@ -4,10 +4,10 @@
use bytes;
use types;

// Converts a u64 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u64tosb(u: u64, b: base) const str = {
// Converts a u64 to a string. The return value is statically allocated and will
// be overwritten on subsequent calls; see [[strings::dup]] to duplicate the
// result.
export fn u64tos(u: u64, b: base = base::DEC) const str = {
	static assert(types::U64_MAX == 18446744073709551615);
	static let buf: [64]u8 = [0...]; // 64 binary digits

@@ -41,80 +41,45 @@ export fn u64tosb(u: u64, b: base) const str = {
	return *(&s: *str);
};

// Converts a u32 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u32tosb(u: u32, b: base) const str = u64tosb(u, b);

// Converts a u16 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u16tosb(u: u16, b: base) const str = u64tosb(u, b);

// Converts a u8 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u8tosb(u: u8, b: base) const str = u64tosb(u, b);

// Converts a uint to a string in the given base. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [[strings::dup]] to duplicate the result.
export fn utosb(u: uint, b: base) const str = u64tosb(u, b);

// Converts a size to a string in the given base. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [[strings::dup]] to duplicate the result.
export fn ztosb(u: size, b: base) const str = u64tosb(u, b);

// Converts a uintptr to a string in the given base. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [[strings::dup]] to duplicate the result.
export fn uptrtosb(uptr: uintptr, b: base) const str = u64tosb(uptr: u64, b);

// Converts a u64 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u64tos(u: u64) const str = u64tosb(u, base::DEC);

// Converts a u32 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u32tos(u: u32) const str = u64tos(u);

// Converts a u16 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u16tos(u: u16) const str = u64tos(u);

// Converts a u8 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u8tos(u: u8) const str = u64tos(u);

// Converts a uint to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn utos(u: uint) const str = u64tos(u);

// Converts a size to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn ztos(z: size) const str = u64tos(z);

// Converts a uintptr to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn uptrtos(uptr: uintptr) const str = u64tos(uptr: u64);

@test fn utosb() void = {
	assert("11010" == u64tosb(0b11010, base::BIN));
	assert("1234567" == u64tosb(0o1234567, base::OCT));
	assert("123456789" == u64tosb(123456789, base::DEC));
	assert("123456789ABCDEF" == u64tosb(0x123456789ABCDEF, base::HEX));
	assert("123456789ABCDEF" == u64tosb(0x123456789ABCDEF, base::HEX_UPPER));
	assert("123456789abcdef" == u64tosb(0x123456789ABCDEF, base::HEX_LOWER));
// Converts a u32 to a string. The return value is statically allocated and will
// be overwritten on subsequent calls; see [[strings::dup]] to duplicate the
// result.
export fn u32tos(u: u32, b: base = base::DEC) const str = u64tos(u, b);

// Converts a u16 to a string. The return value is statically allocated and will
// be overwritten on subsequent calls; see [[strings::dup]] to duplicate the
// result.
export fn u16tos(u: u16, b: base = base::DEC) const str = u64tos(u, b);

// Converts a u8 to a string. The return value is statically allocated and will
// be overwritten on subsequent calls; see [[strings::dup]] to duplicate the
// result.
export fn u8tos(u: u8, b: base = base::DEC) const str = u64tos(u, b);

// Converts a uint to a string. The return value is statically allocated and
// will be overwritten on subsequent calls; see [[strings::dup]] to duplicate
// the result.
export fn utos(u: uint, b: base = base::DEC) const str = u64tos(u, b);

// Converts a size to a string. The return value is statically allocated and
// will be overwritten on subsequent calls; see [[strings::dup]] to duplicate
// the result.
export fn ztos(u: size, b: base = base::DEC) const str = u64tos(u, b);

// Converts a uintptr to a string. The return value is statically allocated and
// will be overwritten on subsequent calls; see [[strings::dup]] to duplicate
// the result.
export fn uptrtos(uptr: uintptr, b: base = base::DEC) const str = u64tos(uptr: u64, b);

@test fn utos_bases() void = {
	assert("11010" == u64tos(0b11010, base::BIN));
	assert("1234567" == u64tos(0o1234567, base::OCT));
	assert("123456789" == u64tos(123456789, base::DEC));
	assert("123456789ABCDEF" == u64tos(0x123456789ABCDEF, base::HEX));
	assert("123456789ABCDEF" == u64tos(0x123456789ABCDEF, base::HEX_UPPER));
	assert("123456789abcdef" == u64tos(0x123456789ABCDEF, base::HEX_LOWER));
	assert("1111111111111111111111111111111111111111111111111111111111111111"
		== u64tosb(types::U64_MAX, base::BIN));
		== u64tos(types::U64_MAX, base::BIN));
};

@test fn utos() void = {
diff --git a/uuid/uuid.ha b/uuid/uuid.ha
index 4fccedbd..7e62b710 100644
--- a/uuid/uuid.ha
+++ b/uuid/uuid.ha
@@ -115,7 +115,7 @@ export fn decode(in: io::handle) (uuid | invalid | io::error) = {
			return invalid;
		case let z: size => void;
		};
		u[i] = match (strconv::stou8b(
		u[i] = match (strconv::stou8(
				strings::fromutf8_unsafe(buf),
				strconv::base::HEX)) {
		case strconv::overflow =>
-- 
2.44.0

[hare/patches] build success

builds.sr.ht <builds@sr.ht>
Details
Message ID
<D0GMU6SSARMG.2UYCEPYI6GAFY@fra01>
In-Reply-To
<20240410175103.2493-1-yyp@disroot.org> (view parent)
DKIM signature
missing
Download raw message
hare/patches: SUCCESS in 1m7s

[strconv: Merge functions with their -b versions][0] v2 from [Alexey Yerin][1]

[0]: https://lists.sr.ht/~sircmpwn/hare-dev/patches/50883
[1]: yyp@disroot.org

✓ #1192327 SUCCESS hare/patches/freebsd.yml https://builds.sr.ht/~sircmpwn/job/1192327
✓ #1192328 SUCCESS hare/patches/openbsd.yml https://builds.sr.ht/~sircmpwn/job/1192328
✓ #1192326 SUCCESS hare/patches/alpine.yml  https://builds.sr.ht/~sircmpwn/job/1192326
Details
Message ID
<D0JV0164D2FY.3O3RNAHSMX6GW@cmpwn.com>
In-Reply-To
<20240410175103.2493-1-yyp@disroot.org> (view parent)
DKIM signature
pass
Download raw message
Thanks!

To git@git.sr.ht:~sircmpwn/hare
   9a18b3d1..c0432c64  master -> master
Reply to thread Export thread (mbox)