| author | |
| committer | |
| log | 7b72fc6bbc5554643bc27933310899e32783b81b |
| tree | c367a314c4e46e4bcefce3d5f9ab21addc3620cc |
| parent | eeb043f5833db03ac3250f9943ba8be0b518432f |
Big-int functions were updated to respect the provided abi_size, rather
than inferring a potentially incorrect abi_size implicitly.
In combination with the convention that any required padding bits are
added on the MSB end, this means that exotic integers can potentially
have a well-defined memory layout.3 files changed, 166 insertions(+), 79 deletions(-)
lib/std/math/big/int.zig+69-72| ... | ... | @@ -1624,18 +1624,16 @@ pub const Mutable = struct { |
| 1624 | 1624 | } |
| 1625 | 1625 | |
| 1626 | 1626 | /// Read the value of `x` from `buffer` |
| 1627 | /// Asserts that `buffer` and `bit_count` are large enough to store the value. | |
| 1627 | /// Asserts that `buffer`, `abi_size`, and `bit_count` are large enough to store the value. | |
| 1628 | 1628 | /// |
| 1629 | /// For integers with a well-defined layout (e.g. all power-of-two integers), this function | |
| 1630 | /// reads from `buffer` as if it were the contents of @ptrCast([]const u8, &x), where the | |
| 1631 | /// slice length is taken to be @sizeOf(std.meta.Int(signedness, <bit_count>)) | |
| 1632 | /// | |
| 1633 | /// For integers with a non-well-defined layout, `buffer` must have been created by | |
| 1634 | /// writeTwosComplement. | |
| 1629 | /// The contents of `buffer` are interpreted as if they were the contents of | |
| 1630 | /// @ptrCast(*[abi_size]const u8, &x). Byte ordering is determined by `endian` | |
| 1631 | /// and any required padding bits are expected on the MSB end. | |
| 1635 | 1632 | pub fn readTwosComplement( |
| 1636 | 1633 | x: *Mutable, |
| 1637 | 1634 | buffer: []const u8, |
| 1638 | 1635 | bit_count: usize, |
| 1636 | abi_size: usize, | |
| 1639 | 1637 | endian: Endian, |
| 1640 | 1638 | signedness: Signedness, |
| 1641 | 1639 | ) void { |
| ... | ... | @@ -1646,20 +1644,18 @@ pub const Mutable = struct { |
| 1646 | 1644 | return; |
| 1647 | 1645 | } |
| 1648 | 1646 | |
| 1649 | // byte_count is the total amount of bytes to read from buffer | |
| 1650 | var byte_count = @sizeOf(Limb) * (bit_count / @bitSizeOf(Limb)); | |
| 1651 | if (bit_count % @bitSizeOf(Limb) != 0) { // Round up to a power-of-two integer <= Limb | |
| 1652 | byte_count += (std.math.ceilPowerOfTwoAssert(usize, bit_count % @bitSizeOf(Limb)) + 7) / 8; | |
| 1653 | } | |
| 1654 | ||
| 1655 | const limb_count = calcTwosCompLimbCount(8 * byte_count); | |
| 1647 | // byte_count is our total read size: it cannot exceed abi_size, | |
| 1648 | // but may be less as long as it includes the required bits | |
| 1649 | const limb_count = calcTwosCompLimbCount(bit_count); | |
| 1650 | const byte_count = std.math.min(abi_size, @sizeOf(Limb) * limb_count); | |
| 1651 | assert(8 * byte_count >= bit_count); | |
| 1656 | 1652 | |
| 1657 | 1653 | // Check whether the input is negative |
| 1658 | 1654 | var positive = true; |
| 1659 | 1655 | if (signedness == .signed) { |
| 1660 | 1656 | var last_byte = switch (endian) { |
| 1661 | 1657 | .Little => ((bit_count + 7) / 8) - 1, |
| 1662 | .Big => byte_count - ((bit_count + 7) / 8), | |
| 1658 | .Big => abi_size - ((bit_count + 7) / 8), | |
| 1663 | 1659 | }; |
| 1664 | 1660 | |
| 1665 | 1661 | const sign_bit = @as(u8, 1) << @intCast(u3, (bit_count - 1) % 8); |
| ... | ... | @@ -1672,7 +1668,7 @@ pub const Mutable = struct { |
| 1672 | 1668 | while (limb_index < bit_count / @bitSizeOf(Limb)) : (limb_index += 1) { |
| 1673 | 1669 | var buf_index = switch (endian) { |
| 1674 | 1670 | .Little => @sizeOf(Limb) * limb_index, |
| 1675 | .Big => byte_count - (limb_index + 1) * @sizeOf(Limb), | |
| 1671 | .Big => abi_size - (limb_index + 1) * @sizeOf(Limb), | |
| 1676 | 1672 | }; |
| 1677 | 1673 | |
| 1678 | 1674 | const limb_buf = @ptrCast(*const [@sizeOf(Limb)]u8, buffer[buf_index..]); |
| ... | ... | @@ -1683,32 +1679,34 @@ pub const Mutable = struct { |
| 1683 | 1679 | x.limbs[limb_index] = limb; |
| 1684 | 1680 | } |
| 1685 | 1681 | |
| 1686 | // Copy any remaining bytes, using the nearest power-of-two integer that is large enough | |
| 1687 | const bits_left = @intCast(Log2Limb, bit_count % @bitSizeOf(Limb)); | |
| 1688 | if (bits_left != 0) { | |
| 1689 | const bytes_read = limb_index * @sizeOf(Limb); | |
| 1690 | const bytes_left = byte_count - bytes_read; | |
| 1691 | var buffer_left = switch (endian) { | |
| 1692 | .Little => buffer[bytes_read..], | |
| 1693 | .Big => buffer[0..], | |
| 1694 | }; | |
| 1682 | // Copy the remaining N bytes (N <= @sizeOf(Limb)) | |
| 1683 | var bytes_read = limb_index * @sizeOf(Limb); | |
| 1684 | if (bytes_read != byte_count) { | |
| 1685 | var limb: Limb = 0; | |
| 1695 | 1686 | |
| 1696 | var limb = @intCast(Limb, blk: { | |
| 1697 | // zig fmt: off | |
| 1698 | if (bytes_left == 1) break :blk mem.readInt( u8, buffer_left[0.. 1], endian); | |
| 1699 | if (bytes_left == 2) break :blk mem.readInt( u16, buffer_left[0.. 2], endian); | |
| 1700 | if (bytes_left == 4) break :blk mem.readInt( u32, buffer_left[0.. 4], endian); | |
| 1701 | if (bytes_left == 8) break :blk mem.readInt( u64, buffer_left[0.. 8], endian); | |
| 1702 | if (bytes_left == 16) break :blk mem.readInt(u128, buffer_left[0..16], endian); | |
| 1703 | // zig fmt: on | |
| 1704 | unreachable; | |
| 1705 | }); | |
| 1687 | while (bytes_read != byte_count) { | |
| 1688 | const read_size = std.math.floorPowerOfTwo(usize, byte_count - bytes_read); | |
| 1689 | var int_buffer = switch (endian) { | |
| 1690 | .Little => buffer[bytes_read..], | |
| 1691 | .Big => buffer[(abi_size - bytes_read - read_size)..], | |
| 1692 | }; | |
| 1693 | limb |= @intCast(Limb, switch (read_size) { | |
| 1694 | 1 => mem.readInt(u8, int_buffer[0..1], endian), | |
| 1695 | 2 => mem.readInt(u16, int_buffer[0..2], endian), | |
| 1696 | 4 => mem.readInt(u32, int_buffer[0..4], endian), | |
| 1697 | 8 => mem.readInt(u64, int_buffer[0..8], endian), | |
| 1698 | 16 => mem.readInt(u128, int_buffer[0..16], endian), | |
| 1699 | else => unreachable, | |
| 1700 | }) << @intCast(Log2Limb, 8 * (bytes_read % @sizeOf(Limb))); | |
| 1701 | bytes_read += read_size; | |
| 1702 | } | |
| 1706 | 1703 | |
| 1707 | 1704 | // 2's complement (bitwise not, then add carry bit) |
| 1708 | 1705 | if (!positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb); |
| 1709 | 1706 | |
| 1710 | 1707 | // Mask off any unused bits |
| 1711 | const mask = (@as(Limb, 1) << bits_left) -% 1; // 0b0..01..1 with (bits_left) trailing ones | |
| 1708 | const valid_bits = @intCast(Log2Limb, bit_count % @bitSizeOf(Limb)); | |
| 1709 | const mask = (@as(Limb, 1) << valid_bits) -% 1; // 0b0..01..1 with (valid_bits_in_limb) trailing ones | |
| 1712 | 1710 | limb &= mask; |
| 1713 | 1711 | |
| 1714 | 1712 | x.limbs[limb_count - 1] = limb; |
| ... | ... | @@ -2076,21 +2074,16 @@ pub const Const = struct { |
| 2076 | 2074 | } |
| 2077 | 2075 | |
| 2078 | 2076 | /// Write the value of `x` into `buffer` |
| 2079 | /// Asserts that `buffer` and `bit_count` are large enough to store the value. | |
| 2077 | /// Asserts that `buffer`, `abi_size`, and `bit_count` are large enough to store the value. | |
| 2080 | 2078 | /// |
| 2081 | /// For integers with a well-defined layout (e.g. all power-of-two integers), this function | |
| 2082 | /// can be thought of as writing to `buffer` the contents of @ptrCast([]const u8, &x), | |
| 2083 | /// where the slice length is taken to be @sizeOf(std.meta.Int(_,<bit_count>)) | |
| 2084 | /// | |
| 2085 | /// For integers with a non-well-defined layout, the only requirement is that readTwosComplement | |
| 2086 | /// on the same buffer creates an equivalent big integer. | |
| 2087 | pub fn writeTwosComplement(x: Const, buffer: []u8, bit_count: usize, endian: Endian) void { | |
| 2088 | if (bit_count == 0) return; | |
| 2079 | /// `buffer` is filled so that its contents match what would be observed via | |
| 2080 | /// @ptrCast(*[abi_size]const u8, &x). Byte ordering is determined by `endian`, | |
| 2081 | /// and any required padding bits are added on the MSB end. | |
| 2082 | pub fn writeTwosComplement(x: Const, buffer: []u8, bit_count: usize, abi_size: usize, endian: Endian) void { | |
| 2089 | 2083 | |
| 2090 | var byte_count = @sizeOf(Limb) * (bit_count / @bitSizeOf(Limb)); | |
| 2091 | if (bit_count % @bitSizeOf(Limb) != 0) { | |
| 2092 | byte_count += (std.math.ceilPowerOfTwoAssert(usize, bit_count % @bitSizeOf(Limb)) + 7) / 8; | |
| 2093 | } | |
| 2084 | // byte_count is our total write size | |
| 2085 | const byte_count = abi_size; | |
| 2086 | assert(8 * byte_count >= bit_count); | |
| 2094 | 2087 | assert(buffer.len >= byte_count); |
| 2095 | 2088 | assert(x.fitsInTwosComp(if (x.positive) .unsigned else .signed, bit_count)); |
| 2096 | 2089 | |
| ... | ... | @@ -2100,7 +2093,7 @@ pub const Const = struct { |
| 2100 | 2093 | while (limb_index < byte_count / @sizeOf(Limb)) : (limb_index += 1) { |
| 2101 | 2094 | var buf_index = switch (endian) { |
| 2102 | 2095 | .Little => @sizeOf(Limb) * limb_index, |
| 2103 | .Big => byte_count - (limb_index + 1) * @sizeOf(Limb), | |
| 2096 | .Big => abi_size - (limb_index + 1) * @sizeOf(Limb), | |
| 2104 | 2097 | }; |
| 2105 | 2098 | |
| 2106 | 2099 | var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0; |
| ... | ... | @@ -2111,32 +2104,36 @@ pub const Const = struct { |
| 2111 | 2104 | mem.writeInt(Limb, limb_buf, limb, endian); |
| 2112 | 2105 | } |
| 2113 | 2106 | |
| 2114 | // Copy any remaining bytes | |
| 2115 | if (byte_count % @sizeOf(Limb) != 0) { | |
| 2116 | const bytes_read = limb_index * @sizeOf(Limb); | |
| 2117 | const bytes_left = byte_count - bytes_read; | |
| 2118 | var buffer_left = switch (endian) { | |
| 2119 | .Little => buffer[bytes_read..], | |
| 2120 | .Big => buffer[0..], | |
| 2121 | }; | |
| 2122 | ||
| 2107 | // Copy the remaining N bytes (N < @sizeOf(Limb)) | |
| 2108 | var bytes_written = limb_index * @sizeOf(Limb); | |
| 2109 | if (bytes_written != byte_count) { | |
| 2123 | 2110 | var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0; |
| 2124 | 2111 | // 2's complement (bitwise not, then add carry bit) |
| 2125 | 2112 | if (!x.positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb); |
| 2126 | 2113 | |
| 2127 | if (bytes_left == 1) { | |
| 2128 | mem.writeInt(u8, buffer_left[0..1], @truncate(u8, limb), endian); | |
| 2129 | } else if (@sizeOf(Limb) > 1 and bytes_left == 2) { | |
| 2130 | mem.writeInt(u16, buffer_left[0..2], @truncate(u16, limb), endian); | |
| 2131 | } else if (@sizeOf(Limb) > 2 and bytes_left == 4) { | |
| 2132 | mem.writeInt(u32, buffer_left[0..4], @truncate(u32, limb), endian); | |
| 2133 | } else if (@sizeOf(Limb) > 4 and bytes_left == 8) { | |
| 2134 | mem.writeInt(u64, buffer_left[0..8], @truncate(u64, limb), endian); | |
| 2135 | } else if (@sizeOf(Limb) > 8 and bytes_left == 16) { | |
| 2136 | mem.writeInt(u128, buffer_left[0..16], @truncate(u128, limb), endian); | |
| 2137 | } else if (@sizeOf(Limb) > 16) { | |
| 2138 | @compileError("@sizeOf(Limb) exceeded supported range"); | |
| 2139 | } else unreachable; | |
| 2114 | while (bytes_written != byte_count) { | |
| 2115 | const write_size = std.math.floorPowerOfTwo(usize, byte_count - bytes_written); | |
| 2116 | var int_buffer = switch (endian) { | |
| 2117 | .Little => buffer[bytes_written..], | |
| 2118 | .Big => buffer[(abi_size - bytes_written - write_size)..], | |
| 2119 | }; | |
| 2120 | ||
| 2121 | if (write_size == 1) { | |
| 2122 | mem.writeInt(u8, int_buffer[0..1], @truncate(u8, limb), endian); | |
| 2123 | } else if (@sizeOf(Limb) >= 2 and write_size == 2) { | |
| 2124 | mem.writeInt(u16, int_buffer[0..2], @truncate(u16, limb), endian); | |
| 2125 | } else if (@sizeOf(Limb) >= 4 and write_size == 4) { | |
| 2126 | mem.writeInt(u32, int_buffer[0..4], @truncate(u32, limb), endian); | |
| 2127 | } else if (@sizeOf(Limb) >= 8 and write_size == 8) { | |
| 2128 | mem.writeInt(u64, int_buffer[0..8], @truncate(u64, limb), endian); | |
| 2129 | } else if (@sizeOf(Limb) >= 16 and write_size == 16) { | |
| 2130 | mem.writeInt(u128, int_buffer[0..16], @truncate(u128, limb), endian); | |
| 2131 | } else if (@sizeOf(Limb) >= 32) { | |
| 2132 | @compileError("@sizeOf(Limb) exceeded supported range"); | |
| 2133 | } else unreachable; | |
| 2134 | limb >>= @intCast(Log2Limb, 8 * write_size); | |
| 2135 | bytes_written += write_size; | |
| 2136 | } | |
| 2140 | 2137 | } |
| 2141 | 2138 | } |
| 2142 | 2139 |
lib/std/math/big/int_test.zig+91-4| ... | ... | @@ -2498,16 +2498,103 @@ test "big int conversion read/write twos complement" { |
| 2498 | 2498 | defer testing.allocator.free(buffer1); |
| 2499 | 2499 | |
| 2500 | 2500 | const endians = [_]std.builtin.Endian{ .Little, .Big }; |
| 2501 | const abi_size = 64; | |
| 2501 | 2502 | |
| 2502 | 2503 | for (endians) |endian| { |
| 2503 | 2504 | // Writing to buffer and back should not change anything |
| 2504 | a.toConst().writeTwosComplement(buffer1, 493, endian); | |
| 2505 | m.readTwosComplement(buffer1, 493, endian, .unsigned); | |
| 2505 | a.toConst().writeTwosComplement(buffer1, 493, abi_size, endian); | |
| 2506 | m.readTwosComplement(buffer1, 493, abi_size, endian, .unsigned); | |
| 2506 | 2507 | try testing.expect(m.toConst().order(a.toConst()) == .eq); |
| 2507 | 2508 | |
| 2508 | 2509 | // Equivalent to @bitCast(i493, @as(u493, intMax(u493)) |
| 2509 | a.toConst().writeTwosComplement(buffer1, 493, endian); | |
| 2510 | m.readTwosComplement(buffer1, 493, endian, .signed); | |
| 2510 | a.toConst().writeTwosComplement(buffer1, 493, abi_size, endian); | |
| 2511 | m.readTwosComplement(buffer1, 493, abi_size, endian, .signed); | |
| 2511 | 2512 | try testing.expect(m.toConst().orderAgainstScalar(-1) == .eq); |
| 2512 | 2513 | } |
| 2513 | 2514 | } |
| 2515 | ||
| 2516 | test "big int conversion read twos complement with padding" { | |
| 2517 | var a = try Managed.initSet(testing.allocator, 0x01_02030405_06070809_0a0b0c0d); | |
| 2518 | defer a.deinit(); | |
| 2519 | ||
| 2520 | var buffer1 = try testing.allocator.alloc(u8, 16); | |
| 2521 | defer testing.allocator.free(buffer1); | |
| 2522 | @memset(buffer1.ptr, 0xaa, buffer1.len); | |
| 2523 | ||
| 2524 | // writeTwosComplement: | |
| 2525 | // (1) should not write beyond buffer[0..abi_size] | |
| 2526 | // (2) should correctly order bytes based on the provided endianness | |
| 2527 | // (3) should sign-extend any bits from bit_count to 8 * abi_size | |
| 2528 | ||
| 2529 | var bit_count: usize = 12 * 8 + 1; | |
| 2530 | a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Little); | |
| 2531 | try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0xaa, 0xaa, 0xaa })); | |
| 2532 | a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Big); | |
| 2533 | try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xaa, 0xaa, 0xaa })); | |
| 2534 | a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Little); | |
| 2535 | try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0, 0x0, 0x0 })); | |
| 2536 | a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Big); | |
| 2537 | try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0x0, 0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd })); | |
| 2538 | ||
| 2539 | @memset(buffer1.ptr, 0xaa, buffer1.len); | |
| 2540 | try a.set(-0x01_02030405_06070809_0a0b0c0d); | |
| 2541 | bit_count = 12 * 8 + 2; | |
| 2542 | ||
| 2543 | a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Little); | |
| 2544 | try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xaa, 0xaa, 0xaa })); | |
| 2545 | a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Big); | |
| 2546 | try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3, 0xaa, 0xaa, 0xaa })); | |
| 2547 | a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Little); | |
| 2548 | try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0xff })); | |
| 2549 | a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Big); | |
| 2550 | try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 })); | |
| 2551 | } | |
| 2552 | ||
| 2553 | test "big int conversion write twos complement with padding" { | |
| 2554 | var a = try Managed.initSet(testing.allocator, 0x01_ffffffff_ffffffff_ffffffff); | |
| 2555 | defer a.deinit(); | |
| 2556 | ||
| 2557 | var m = a.toMutable(); | |
| 2558 | ||
| 2559 | // readTwosComplement: | |
| 2560 | // (1) should not read beyond buffer[0..abi_size] | |
| 2561 | // (2) should correctly interpret bytes based on the provided endianness | |
| 2562 | // (3) should ignore any bits from bit_count to 8 * abi_size | |
| 2563 | ||
| 2564 | var bit_count: usize = 12 * 8 + 1; | |
| 2565 | var buffer: []const u8 = undefined; | |
| 2566 | ||
| 2567 | buffer = &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0xb }; | |
| 2568 | m.readTwosComplement(buffer, bit_count, 13, .Little, .unsigned); | |
| 2569 | try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq); | |
| 2570 | ||
| 2571 | buffer = &[_]u8{ 0xb, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd }; | |
| 2572 | m.readTwosComplement(buffer, bit_count, 13, .Big, .unsigned); | |
| 2573 | try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq); | |
| 2574 | ||
| 2575 | buffer = &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0xab, 0xaa, 0xaa, 0xaa }; | |
| 2576 | m.readTwosComplement(buffer, bit_count, 16, .Little, .unsigned); | |
| 2577 | try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq); | |
| 2578 | ||
| 2579 | buffer = &[_]u8{ 0xaa, 0xaa, 0xaa, 0xab, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd }; | |
| 2580 | m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned); | |
| 2581 | try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq); | |
| 2582 | ||
| 2583 | bit_count = 12 * 8 + 2; | |
| 2584 | ||
| 2585 | buffer = &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0x02 }; | |
| 2586 | m.readTwosComplement(buffer, bit_count, 13, .Little, .signed); | |
| 2587 | try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq); | |
| 2588 | ||
| 2589 | buffer = &[_]u8{ 0x02, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 }; | |
| 2590 | m.readTwosComplement(buffer, bit_count, 13, .Big, .signed); | |
| 2591 | try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq); | |
| 2592 | ||
| 2593 | buffer = &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0x02, 0xaa, 0xaa, 0xaa }; | |
| 2594 | m.readTwosComplement(buffer, bit_count, 16, .Little, .signed); | |
| 2595 | try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq); | |
| 2596 | ||
| 2597 | buffer = &[_]u8{ 0xaa, 0xaa, 0xaa, 0x02, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 }; | |
| 2598 | m.readTwosComplement(buffer, bit_count, 16, .Big, .signed); | |
| 2599 | try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq); | |
| 2600 | } |
src/value.zig+6-3| ... | ... | @@ -1046,7 +1046,8 @@ pub const Value = extern union { |
| 1046 | 1046 | var bigint_buffer: BigIntSpace = undefined; |
| 1047 | 1047 | const bigint = val.toBigInt(&bigint_buffer); |
| 1048 | 1048 | const bits = ty.intInfo(target).bits; |
| 1049 | bigint.writeTwosComplement(buffer, bits, target.cpu.arch.endian()); | |
| 1049 | const abi_size = ty.abiSize(target); | |
| 1050 | bigint.writeTwosComplement(buffer, bits, abi_size, target.cpu.arch.endian()); | |
| 1050 | 1051 | }, |
| 1051 | 1052 | .Enum => { |
| 1052 | 1053 | var enum_buffer: Payload.U64 = undefined; |
| ... | ... | @@ -1054,7 +1055,8 @@ pub const Value = extern union { |
| 1054 | 1055 | var bigint_buffer: BigIntSpace = undefined; |
| 1055 | 1056 | const bigint = int_val.toBigInt(&bigint_buffer); |
| 1056 | 1057 | const bits = ty.intInfo(target).bits; |
| 1057 | bigint.writeTwosComplement(buffer, bits, target.cpu.arch.endian()); | |
| 1058 | const abi_size = ty.abiSize(target); | |
| 1059 | bigint.writeTwosComplement(buffer, bits, abi_size, target.cpu.arch.endian()); | |
| 1058 | 1060 | }, |
| 1059 | 1061 | .Float => switch (ty.floatBits(target)) { |
| 1060 | 1062 | 16 => return floatWriteToMemory(f16, val.toFloat(f16), target, buffer), |
| ... | ... | @@ -1096,8 +1098,9 @@ pub const Value = extern union { |
| 1096 | 1098 | const Limb = std.math.big.Limb; |
| 1097 | 1099 | const limb_count = (buffer.len + @sizeOf(Limb) - 1) / @sizeOf(Limb); |
| 1098 | 1100 | const limbs_buffer = try arena.alloc(Limb, limb_count); |
| 1101 | const abi_size = ty.abiSize(target); | |
| 1099 | 1102 | var bigint = BigIntMutable.init(limbs_buffer, 0); |
| 1100 | bigint.readTwosComplement(buffer, int_info.bits, endian, int_info.signedness); | |
| 1103 | bigint.readTwosComplement(buffer, int_info.bits, abi_size, endian, int_info.signedness); | |
| 1101 | 1104 | return fromBigInt(arena, bigint.toConst()); |
| 1102 | 1105 | }, |
| 1103 | 1106 | .Float => switch (ty.floatBits(target)) { |