authorgravatar for mail@linusgroh.deLinus Groh <mail@linusgroh.de> 2025-02-22 10:13:24+00:00
committergravatar for mail@linusgroh.deLinus Groh <mail@linusgroh.de> 2025-02-23 11:25:35+00:00
logc44f4501e79744a2744f7e0c72ff71cdbcb62440
treefb052790e9bdbb70b9cf49ef0889da083225be29
parent5e20e9b4491dead0c56d9b0351677d5832058673

std.math.big.int: Support strings up to base 36

Co-Authored-By: samy007 <samy2014@free.fr>

2 files changed, 32 insertions(+), 9 deletions(-)

lib/std/math/big/int.zig+13-9
...@@ -57,8 +57,11 @@ pub fn calcSetStringLimbsBufferLen(base: u8, string_len: usize) usize {...@@ -57,8 +57,11 @@ pub fn calcSetStringLimbsBufferLen(base: u8, string_len: usize) usize {
57 return calcMulLimbsBufferLen(limb_count, limb_count, 2);57 return calcMulLimbsBufferLen(limb_count, limb_count, 2);
58}58}
5959
60/// Assumes `string_len` doesn't account for minus signs if the number is negative.
60pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize {61pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize {
61 return (string_len + (limb_bits / base - 1)) / (limb_bits / base);62 const base_f: f32 = @floatFromInt(base);
63 const string_len_f: f32 = @floatFromInt(string_len);
64 return 1 + @as(usize, @intFromFloat(@ceil(string_len_f * std.math.log2(base_f) / limb_bits)));
62}65}
6366
64pub fn calcPowLimbsBufferLen(a_bit_count: usize, y: usize) usize {67pub fn calcPowLimbsBufferLen(a_bit_count: usize, y: usize) usize {
...@@ -280,7 +283,7 @@ pub const Mutable = struct {...@@ -280,7 +283,7 @@ pub const Mutable = struct {
280 ///283 ///
281 /// Asserts there is enough memory for the value in `self.limbs`. An upper bound on number of limbs can284 /// Asserts there is enough memory for the value in `self.limbs`. An upper bound on number of limbs can
282 /// be determined with `calcSetStringLimbCount`.285 /// be determined with `calcSetStringLimbCount`.
283 /// Asserts the base is in the range [2, 16].286 /// Asserts the base is in the range [2, 36].
284 ///287 ///
285 /// Returns an error if the value has invalid digits for the requested base.288 /// Returns an error if the value has invalid digits for the requested base.
286 ///289 ///
...@@ -296,7 +299,8 @@ pub const Mutable = struct {...@@ -296,7 +299,8 @@ pub const Mutable = struct {
296 limbs_buffer: []Limb,299 limbs_buffer: []Limb,
297 allocator: ?Allocator,300 allocator: ?Allocator,
298 ) error{InvalidCharacter}!void {301 ) error{InvalidCharacter}!void {
299 assert(base >= 2 and base <= 16);302 assert(base >= 2);
303 assert(base <= 36);
300304
301 var i: usize = 0;305 var i: usize = 0;
302 var positive = true;306 var positive = true;
...@@ -2283,11 +2287,11 @@ pub const Const = struct {...@@ -2283,11 +2287,11 @@ pub const Const = struct {
22832287
2284 /// Converts self to a string in the requested base.2288 /// Converts self to a string in the requested base.
2285 /// Caller owns returned memory.2289 /// Caller owns returned memory.
2286 /// Asserts that `base` is in the range [2, 16].2290 /// Asserts that `base` is in the range [2, 36].
2287 /// See also `toString`, a lower level function than this.2291 /// See also `toString`, a lower level function than this.
2288 pub fn toStringAlloc(self: Const, allocator: Allocator, base: u8, case: std.fmt.Case) Allocator.Error![]u8 {2292 pub fn toStringAlloc(self: Const, allocator: Allocator, base: u8, case: std.fmt.Case) Allocator.Error![]u8 {
2289 assert(base >= 2);2293 assert(base >= 2);
2290 assert(base <= 16);2294 assert(base <= 36);
22912295
2292 if (self.eqlZero()) {2296 if (self.eqlZero()) {
2293 return allocator.dupe(u8, "0");2297 return allocator.dupe(u8, "0");
...@@ -2302,7 +2306,7 @@ pub const Const = struct {...@@ -2302,7 +2306,7 @@ pub const Const = struct {
2302 }2306 }
23032307
2304 /// Converts self to a string in the requested base.2308 /// Converts self to a string in the requested base.
2305 /// Asserts that `base` is in the range [2, 16].2309 /// Asserts that `base` is in the range [2, 36].
2306 /// `string` is a caller-provided slice of at least `sizeInBaseUpperBound` bytes,2310 /// `string` is a caller-provided slice of at least `sizeInBaseUpperBound` bytes,
2307 /// where the result is written to.2311 /// where the result is written to.
2308 /// Returns the length of the string.2312 /// Returns the length of the string.
...@@ -2312,7 +2316,7 @@ pub const Const = struct {...@@ -2312,7 +2316,7 @@ pub const Const = struct {
2312 /// See also `toStringAlloc`, a higher level function than this.2316 /// See also `toStringAlloc`, a higher level function than this.
2313 pub fn toString(self: Const, string: []u8, base: u8, case: std.fmt.Case, limbs_buffer: []Limb) usize {2317 pub fn toString(self: Const, string: []u8, base: u8, case: std.fmt.Case, limbs_buffer: []Limb) usize {
2314 assert(base >= 2);2318 assert(base >= 2);
2315 assert(base <= 16);2319 assert(base <= 36);
23162320
2317 if (self.eqlZero()) {2321 if (self.eqlZero()) {
2318 string[0] = '0';2322 string[0] = '0';
...@@ -2816,7 +2820,7 @@ pub const Managed = struct {...@@ -2816,7 +2820,7 @@ pub const Managed = struct {
2816 ///2820 ///
2817 /// self's allocator is used for temporary storage to boost multiplication performance.2821 /// self's allocator is used for temporary storage to boost multiplication performance.
2818 pub fn setString(self: *Managed, base: u8, value: []const u8) !void {2822 pub fn setString(self: *Managed, base: u8, value: []const u8) !void {
2819 if (base < 2 or base > 16) return error.InvalidBase;2823 if (base < 2 or base > 36) return error.InvalidBase;
2820 try self.ensureCapacity(calcSetStringLimbCount(base, value.len));2824 try self.ensureCapacity(calcSetStringLimbCount(base, value.len));
2821 const limbs_buffer = try self.allocator.alloc(Limb, calcSetStringLimbsBufferLen(base, value.len));2825 const limbs_buffer = try self.allocator.alloc(Limb, calcSetStringLimbsBufferLen(base, value.len));
2822 defer self.allocator.free(limbs_buffer);2826 defer self.allocator.free(limbs_buffer);
...@@ -2843,7 +2847,7 @@ pub const Managed = struct {...@@ -2843,7 +2847,7 @@ pub const Managed = struct {
2843 /// Converts self to a string in the requested base. Memory is allocated from the provided2847 /// Converts self to a string in the requested base. Memory is allocated from the provided
2844 /// allocator and not the one present in self.2848 /// allocator and not the one present in self.
2845 pub fn toString(self: Managed, allocator: Allocator, base: u8, case: std.fmt.Case) ![]u8 {2849 pub fn toString(self: Managed, allocator: Allocator, base: u8, case: std.fmt.Case) ![]u8 {
2846 if (base < 2 or base > 16) return error.InvalidBase;2850 if (base < 2 or base > 36) return error.InvalidBase;
2847 return self.toConst().toStringAlloc(allocator, base, case);2851 return self.toConst().toStringAlloc(allocator, base, case);
2848 }2852 }
28492853
lib/std/math/big/int_test.zig+19
...@@ -275,6 +275,14 @@ test "string set case insensitive number" {...@@ -275,6 +275,14 @@ test "string set case insensitive number" {
275 try testing.expect((try a.toInt(u32)) == 0xabcdef);275 try testing.expect((try a.toInt(u32)) == 0xabcdef);
276}276}
277277
278test "string set base 36" {
279 var a = try Managed.init(testing.allocator);
280 defer a.deinit();
281
282 try a.setString(36, "fifvthrv1mzt79ez9");
283 try testing.expect((try a.to(u128)) == 123456789123456789123456789);
284}
285
278test "string set bad char error" {286test "string set bad char error" {
279 var a = try Managed.init(testing.allocator);287 var a = try Managed.init(testing.allocator);
280 defer a.deinit();288 defer a.deinit();
...@@ -353,6 +361,17 @@ test "string to base 16" {...@@ -353,6 +361,17 @@ test "string to base 16" {
353 try testing.expect(mem.eql(u8, as, es));361 try testing.expect(mem.eql(u8, as, es));
354}362}
355363
364test "string to base 36" {
365 var a = try Managed.initSet(testing.allocator, 123456789123456789123456789);
366 defer a.deinit();
367
368 const as = try a.toString(testing.allocator, 36, .lower);
369 defer testing.allocator.free(as);
370 const es = "fifvthrv1mzt79ez9";
371
372 try testing.expect(mem.eql(u8, as, es));
373}
374
356test "neg string to" {375test "neg string to" {
357 var a = try Managed.initSet(testing.allocator, -123907434);376 var a = try Managed.initSet(testing.allocator, -123907434);
358 defer a.deinit();377 defer a.deinit();