From 301343b5f5789c9943f3ecdecc6c8ed958e6d9d5 Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Sat, 22 Feb 2025 10:13:24 +0000 Subject: [PATCH] std.math.big.int: Support strings up to base 36 Co-Authored-By: samy007 --- lib/std/math/big/int.zig | 22 +++++++++++++--------- lib/std/math/big/int_test.zig | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 16808b9d7162..a4ab9b2f483d 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -57,8 +57,11 @@ pub fn calcSetStringLimbsBufferLen(base: u8, string_len: usize) usize { return calcMulLimbsBufferLen(limb_count, limb_count, 2); } +/// Assumes `string_len` doesn't account for minus signs if the number is negative. pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize { - return (string_len + (limb_bits / base - 1)) / (limb_bits / base); + const base_f: f32 = @floatFromInt(base); + const string_len_f: f32 = @floatFromInt(string_len); + return 1 + @as(usize, @intFromFloat(@ceil(string_len_f * std.math.log2(base_f) / limb_bits))); } pub fn calcPowLimbsBufferLen(a_bit_count: usize, y: usize) usize { @@ -280,7 +283,7 @@ pub const Mutable = struct { /// /// Asserts there is enough memory for the value in `self.limbs`. An upper bound on number of limbs can /// be determined with `calcSetStringLimbCount`. - /// Asserts the base is in the range [2, 16]. + /// Asserts the base is in the range [2, 36]. /// /// Returns an error if the value has invalid digits for the requested base. /// @@ -296,7 +299,8 @@ pub const Mutable = struct { limbs_buffer: []Limb, allocator: ?Allocator, ) error{InvalidCharacter}!void { - assert(base >= 2 and base <= 16); + assert(base >= 2); + assert(base <= 36); var i: usize = 0; var positive = true; @@ -2283,11 +2287,11 @@ pub const Const = struct { /// Converts self to a string in the requested base. /// Caller owns returned memory. - /// Asserts that `base` is in the range [2, 16]. + /// Asserts that `base` is in the range [2, 36]. /// See also `toString`, a lower level function than this. pub fn toStringAlloc(self: Const, allocator: Allocator, base: u8, case: std.fmt.Case) Allocator.Error![]u8 { assert(base >= 2); - assert(base <= 16); + assert(base <= 36); if (self.eqlZero()) { return allocator.dupe(u8, "0"); @@ -2302,7 +2306,7 @@ pub const Const = struct { } /// Converts self to a string in the requested base. - /// Asserts that `base` is in the range [2, 16]. + /// Asserts that `base` is in the range [2, 36]. /// `string` is a caller-provided slice of at least `sizeInBaseUpperBound` bytes, /// where the result is written to. /// Returns the length of the string. @@ -2312,7 +2316,7 @@ pub const Const = struct { /// See also `toStringAlloc`, a higher level function than this. pub fn toString(self: Const, string: []u8, base: u8, case: std.fmt.Case, limbs_buffer: []Limb) usize { assert(base >= 2); - assert(base <= 16); + assert(base <= 36); if (self.eqlZero()) { string[0] = '0'; @@ -2816,7 +2820,7 @@ pub const Managed = struct { /// /// self's allocator is used for temporary storage to boost multiplication performance. pub fn setString(self: *Managed, base: u8, value: []const u8) !void { - if (base < 2 or base > 16) return error.InvalidBase; + if (base < 2 or base > 36) return error.InvalidBase; try self.ensureCapacity(calcSetStringLimbCount(base, value.len)); const limbs_buffer = try self.allocator.alloc(Limb, calcSetStringLimbsBufferLen(base, value.len)); defer self.allocator.free(limbs_buffer); @@ -2843,7 +2847,7 @@ pub const Managed = struct { /// Converts self to a string in the requested base. Memory is allocated from the provided /// allocator and not the one present in self. pub fn toString(self: Managed, allocator: Allocator, base: u8, case: std.fmt.Case) ![]u8 { - if (base < 2 or base > 16) return error.InvalidBase; + if (base < 2 or base > 36) return error.InvalidBase; return self.toConst().toStringAlloc(allocator, base, case); } diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index 3118e860b990..6ff9861fc197 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -275,6 +275,14 @@ test "string set case insensitive number" { try testing.expect((try a.toInt(u32)) == 0xabcdef); } +test "string set base 36" { + var a = try Managed.init(testing.allocator); + defer a.deinit(); + + try a.setString(36, "fifvthrv1mzt79ez9"); + try testing.expect((try a.to(u128)) == 123456789123456789123456789); +} + test "string set bad char error" { var a = try Managed.init(testing.allocator); defer a.deinit(); @@ -353,6 +361,17 @@ test "string to base 16" { try testing.expect(mem.eql(u8, as, es)); } +test "string to base 36" { + var a = try Managed.initSet(testing.allocator, 123456789123456789123456789); + defer a.deinit(); + + const as = try a.toString(testing.allocator, 36, .lower); + defer testing.allocator.free(as); + const es = "fifvthrv1mzt79ez9"; + + try testing.expect(mem.eql(u8, as, es)); +} + test "neg string to" { var a = try Managed.initSet(testing.allocator, -123907434); defer a.deinit();