| ... | ... | @@ -17,6 +17,27 @@ const Endian = std.builtin.Endian; |
| 17 | 17 | const Signedness = std.builtin.Signedness; |
| 18 | 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 | 41 | /// Returns the number of limbs needed to store `scalar`, which must be a |
| 21 | 42 | /// primitive integer or float value. |
| 22 | 43 | /// Note: A comptime-known upper bound of this value that may be used |
| ... | ... | @@ -329,23 +350,15 @@ pub const Mutable = struct { |
| 329 | 350 | /// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are |
| 330 | 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 | 354 | /// be determined with `calcSetStringLimbCount`. |
| 334 | 355 | /// Asserts the base is in the range [2, 36]. |
| 335 | 356 | /// |
| 336 | 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 | 358 | pub fn setString( |
| 344 | 359 | self: *Mutable, |
| 345 | 360 | base: u8, |
| 346 | 361 | value: []const u8, |
| 347 | | limbs_buffer: []Limb, |
| 348 | | allocator: ?Allocator, |
| 349 | 362 | ) error{InvalidCharacter}!void { |
| 350 | 363 | assert(base >= 2); |
| 351 | 364 | assert(base <= 36); |
| ... | ... | @@ -357,18 +370,41 @@ pub const Mutable = struct { |
| 357 | 370 | i += 1; |
| 358 | 371 | } |
| 359 | 372 | |
| 360 | | const ap_base: Const = .{ .limbs = &[_]Limb{base}, .positive = true }; |
| 361 | | self.set(0); |
| 373 | @memset(self.limbs, 0); |
| 374 | self.len = 1; |
| 362 | 375 | |
| 376 | var limb: Limb = 0; |
| 377 | var j: usize = 0; |
| 363 | 378 | for (value[i..]) |ch| { |
| 364 | 379 | if (ch == '_') { |
| 365 | 380 | continue; |
| 366 | 381 | } |
| 367 | 382 | const d = try std.fmt.charToDigit(ch, base); |
| 368 | | const ap_d: Const = .{ .limbs = &[_]Limb{d}, .positive = true }; |
| 369 | | |
| 370 | | self.mul(self.toConst(), ap_base, limbs_buffer, allocator); |
| 371 | | self.add(self.toConst(), ap_d); |
| 383 | limb *= base; |
| 384 | limb += d; |
| 385 | j += 1; |
| 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 | 409 | self.positive = positive; |
| 374 | 410 | } |
| ... | ... | @@ -2884,10 +2920,8 @@ pub const Managed = struct { |
| 2884 | 2920 | pub fn setString(self: *Managed, base: u8, value: []const u8) !void { |
| 2885 | 2921 | if (base < 2 or base > 36) return error.InvalidBase; |
| 2886 | 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 | 2923 | var m = self.toMutable(); |
| 2890 | | try m.setString(base, value, limbs_buffer, self.allocator); |
| 2924 | try m.setString(base, value); |
| 2891 | 2925 | self.setMetadata(m.positive, m.len); |
| 2892 | 2926 | } |
| 2893 | 2927 | |