| ... | @@ -17,6 +17,27 @@ const Endian = std.builtin.Endian; | ... | @@ -17,6 +17,27 @@ const Endian = std.builtin.Endian; |
| 17 | const Signedness = std.builtin.Signedness; | 17 | const Signedness = std.builtin.Signedness; |
| 18 | const native_endian = builtin.cpu.arch.endian(); | 18 | const native_endian = builtin.cpu.arch.endian(); |
| 19 | | 19 | |
| | 20 | // Comptime-computed constants for supported bases (2 - 36) |
| | 21 | // all values are set to 0 for bases 0 - 1, to make it possible to |
| | 22 | // access a constant for a given base b using `constants.value[b]` |
| | 23 | const Constants = struct { |
| | 24 | // big_bases[b] is the biggest power of b that fit in a single Limb |
| | 25 | // i.e. big_bases[b] = b^k < 2^@bitSizeOf(Limb) and b^(k+1) >= 2^@bitSizeOf(Limb) |
| | 26 | big_bases: [37]Limb, |
| | 27 | // digits_per_limb[b] is the value of k used in the previous field |
| | 28 | digits_per_limb: [37]u8, |
| | 29 | }; |
| | 30 | const constants: Constants = blk: { |
| | 31 | @setEvalBranchQuota(2000); |
| | 32 | var digits_per_limb = [_]u8{0} ** 37; |
| | 33 | var bases = [_]Limb{0} ** 37; |
| | 34 | for (2..37) |base| { |
| | 35 | digits_per_limb[base] = @intCast(math.log(Limb, base, math.maxInt(Limb))); |
| | 36 | bases[base] = std.math.pow(Limb, base, digits_per_limb[base]); |
| | 37 | } |
| | 38 | break :blk Constants{ .big_bases = bases, .digits_per_limb = digits_per_limb }; |
| | 39 | }; |
| | 40 | |
| 20 | /// Returns the number of limbs needed to store `scalar`, which must be a | 41 | /// Returns the number of limbs needed to store `scalar`, which must be a |
| 21 | /// primitive integer or float value. | 42 | /// primitive integer or float value. |
| 22 | /// Note: A comptime-known upper bound of this value that may be used | 43 | /// Note: A comptime-known upper bound of this value that may be used |
| ... | @@ -329,23 +350,15 @@ pub const Mutable = struct { | ... | @@ -329,23 +350,15 @@ pub const Mutable = struct { |
| 329 | /// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are | 350 | /// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are |
| 330 | /// ignored and can be used as digit separators. | 351 | /// ignored and can be used as digit separators. |
| 331 | /// | 352 | /// |
| 332 | /// Asserts there is enough memory for the value in `self.limbs`. An upper bound on number of limbs can | 353 | /// There must be enough memory for the value in `self.limbs`. An upper bound on number of limbs can |
| 333 | /// be determined with `calcSetStringLimbCount`. | 354 | /// be determined with `calcSetStringLimbCount`. |
| 334 | /// Asserts the base is in the range [2, 36]. | 355 | /// Asserts the base is in the range [2, 36]. |
| 335 | /// | 356 | /// |
| 336 | /// Returns an error if the value has invalid digits for the requested base. | 357 | /// Returns an error if the value has invalid digits for the requested base. |
| 337 | /// | | |
| 338 | /// `limbs_buffer` is used for temporary storage. The size required can be found with | | |
| 339 | /// `calcSetStringLimbsBufferLen`. | | |
| 340 | /// | | |
| 341 | /// If `allocator` is provided, it will be used for temporary storage to improve | | |
| 342 | /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm. | | |
| 343 | pub fn setString( | 358 | pub fn setString( |
| 344 | self: *Mutable, | 359 | self: *Mutable, |
| 345 | base: u8, | 360 | base: u8, |
| 346 | value: []const u8, | 361 | value: []const u8, |
| 347 | limbs_buffer: []Limb, | | |
| 348 | allocator: ?Allocator, | | |
| 349 | ) error{InvalidCharacter}!void { | 362 | ) error{InvalidCharacter}!void { |
| 350 | assert(base >= 2); | 363 | assert(base >= 2); |
| 351 | assert(base <= 36); | 364 | assert(base <= 36); |
| ... | @@ -357,18 +370,41 @@ pub const Mutable = struct { | ... | @@ -357,18 +370,41 @@ pub const Mutable = struct { |
| 357 | i += 1; | 370 | i += 1; |
| 358 | } | 371 | } |
| 359 | | 372 | |
| 360 | const ap_base: Const = .{ .limbs = &[_]Limb{base}, .positive = true }; | 373 | @memset(self.limbs, 0); |
| 361 | self.set(0); | 374 | self.len = 1; |
| 362 | | 375 | |
| | 376 | var limb: Limb = 0; |
| | 377 | var j: usize = 0; |
| 363 | for (value[i..]) |ch| { | 378 | for (value[i..]) |ch| { |
| 364 | if (ch == '_') { | 379 | if (ch == '_') { |
| 365 | continue; | 380 | continue; |
| 366 | } | 381 | } |
| 367 | const d = try std.fmt.charToDigit(ch, base); | 382 | const d = try std.fmt.charToDigit(ch, base); |
| 368 | const ap_d: Const = .{ .limbs = &[_]Limb{d}, .positive = true }; | 383 | limb *= base; |
| 369 | | 384 | limb += d; |
| 370 | self.mul(self.toConst(), ap_base, limbs_buffer, allocator); | 385 | j += 1; |
| 371 | self.add(self.toConst(), ap_d); | 386 | |
| | 387 | if (j == constants.digits_per_limb[base]) { |
| | 388 | const len = @min(self.len + 1, self.limbs.len); |
| | 389 | // r = a * b = a + a * (b - 1) |
| | 390 | // we assert when self.limbs is not large enough to store the number |
| | 391 | assert(!llmulLimb(.add, self.limbs[0..len], self.limbs[0..len], constants.big_bases[base] - 1)); |
| | 392 | assert(lladdcarry(self.limbs[0..len], self.limbs[0..len], &[1]Limb{limb}) == 0); |
| | 393 | |
| | 394 | if (self.limbs.len > self.len and self.limbs[self.len] != 0) |
| | 395 | self.len += 1; |
| | 396 | j = 0; |
| | 397 | limb = 0; |
| | 398 | } |
| | 399 | } |
| | 400 | if (j > 0) { |
| | 401 | const len = @min(self.len + 1, self.limbs.len); |
| | 402 | // we assert when self.limbs is not large enough to store the number |
| | 403 | assert(!llmulLimb(.add, self.limbs[0..len], self.limbs[0..len], math.pow(Limb, base, j) - 1)); |
| | 404 | assert(lladdcarry(self.limbs[0..len], self.limbs[0..len], &[1]Limb{limb}) == 0); |
| | 405 | |
| | 406 | if (self.limbs.len > self.len and self.limbs[self.len] != 0) |
| | 407 | self.len += 1; |
| 372 | } | 408 | } |
| 373 | self.positive = positive; | 409 | self.positive = positive; |
| 374 | } | 410 | } |
| ... | @@ -2884,10 +2920,8 @@ pub const Managed = struct { | ... | @@ -2884,10 +2920,8 @@ pub const Managed = struct { |
| 2884 | pub fn setString(self: *Managed, base: u8, value: []const u8) !void { | 2920 | pub fn setString(self: *Managed, base: u8, value: []const u8) !void { |
| 2885 | if (base < 2 or base > 36) return error.InvalidBase; | 2921 | if (base < 2 or base > 36) return error.InvalidBase; |
| 2886 | try self.ensureCapacity(calcSetStringLimbCount(base, value.len)); | 2922 | try self.ensureCapacity(calcSetStringLimbCount(base, value.len)); |
| 2887 | const limbs_buffer = try self.allocator.alloc(Limb, calcSetStringLimbsBufferLen(base, value.len)); | | |
| 2888 | defer self.allocator.free(limbs_buffer); | | |
| 2889 | var m = self.toMutable(); | 2923 | var m = self.toMutable(); |
| 2890 | try m.setString(base, value, limbs_buffer, self.allocator); | 2924 | try m.setString(base, value); |
| 2891 | self.setMetadata(m.positive, m.len); | 2925 | self.setMetadata(m.positive, m.len); |
| 2892 | } | 2926 | } |
| 2893 | | 2927 | |