authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-02-13 11:54:37-07:00
committergravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-02-13 13:26:59-07:00
log7b72fc6bbc5554643bc27933310899e32783b81b
treec367a314c4e46e4bcefce3d5f9ab21addc3620cc
parenteeb043f5833db03ac3250f9943ba8be0b518432f

Add `abi_size` parameter to read/writeTwosComplement

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,18 +1624,16 @@ pub const Mutable = struct {
1624 }1624 }
16251625
1626 /// Read the value of `x` from `buffer`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 function1629 /// The contents of `buffer` are interpreted as if they were the contents of
1630 /// reads from `buffer` as if it were the contents of @ptrCast([]const u8, &x), where the 1630 /// @ptrCast(*[abi_size]const u8, &x). Byte ordering is determined by `endian`
1631 /// slice length is taken to be @sizeOf(std.meta.Int(signedness, <bit_count>))1631 /// and any required padding bits are expected on the MSB end.
1632 ///
1633 /// For integers with a non-well-defined layout, `buffer` must have been created by
1634 /// writeTwosComplement.
1635 pub fn readTwosComplement(1632 pub fn readTwosComplement(
1636 x: *Mutable,1633 x: *Mutable,
1637 buffer: []const u8,1634 buffer: []const u8,
1638 bit_count: usize,1635 bit_count: usize,
1636 abi_size: usize,
1639 endian: Endian,1637 endian: Endian,
1640 signedness: Signedness,1638 signedness: Signedness,
1641 ) void {1639 ) void {
...@@ -1646,20 +1644,18 @@ pub const Mutable = struct {...@@ -1646,20 +1644,18 @@ pub const Mutable = struct {
1646 return;1644 return;
1647 }1645 }
16481646
1649 // byte_count is the total amount of bytes to read from buffer1647 // byte_count is our total read size: it cannot exceed abi_size,
1650 var byte_count = @sizeOf(Limb) * (bit_count / @bitSizeOf(Limb));1648 // but may be less as long as it includes the required bits
1651 if (bit_count % @bitSizeOf(Limb) != 0) { // Round up to a power-of-two integer <= Limb1649 const limb_count = calcTwosCompLimbCount(bit_count);
1652 byte_count += (std.math.ceilPowerOfTwoAssert(usize, bit_count % @bitSizeOf(Limb)) + 7) / 8;1650 const byte_count = std.math.min(abi_size, @sizeOf(Limb) * limb_count);
1653 }1651 assert(8 * byte_count >= bit_count);
1654
1655 const limb_count = calcTwosCompLimbCount(8 * byte_count);
16561652
1657 // Check whether the input is negative1653 // Check whether the input is negative
1658 var positive = true;1654 var positive = true;
1659 if (signedness == .signed) {1655 if (signedness == .signed) {
1660 var last_byte = switch (endian) {1656 var last_byte = switch (endian) {
1661 .Little => ((bit_count + 7) / 8) - 1,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 };
16641660
1665 const sign_bit = @as(u8, 1) << @intCast(u3, (bit_count - 1) % 8);1661 const sign_bit = @as(u8, 1) << @intCast(u3, (bit_count - 1) % 8);
...@@ -1672,7 +1668,7 @@ pub const Mutable = struct {...@@ -1672,7 +1668,7 @@ pub const Mutable = struct {
1672 while (limb_index < bit_count / @bitSizeOf(Limb)) : (limb_index += 1) {1668 while (limb_index < bit_count / @bitSizeOf(Limb)) : (limb_index += 1) {
1673 var buf_index = switch (endian) {1669 var buf_index = switch (endian) {
1674 .Little => @sizeOf(Limb) * limb_index,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 };
16771673
1678 const limb_buf = @ptrCast(*const [@sizeOf(Limb)]u8, buffer[buf_index..]);1674 const limb_buf = @ptrCast(*const [@sizeOf(Limb)]u8, buffer[buf_index..]);
...@@ -1683,32 +1679,34 @@ pub const Mutable = struct {...@@ -1683,32 +1679,34 @@ pub const Mutable = struct {
1683 x.limbs[limb_index] = limb;1679 x.limbs[limb_index] = limb;
1684 }1680 }
16851681
1686 // Copy any remaining bytes, using the nearest power-of-two integer that is large enough1682 // Copy the remaining N bytes (N <= @sizeOf(Limb))
1687 const bits_left = @intCast(Log2Limb, bit_count % @bitSizeOf(Limb));1683 var bytes_read = limb_index * @sizeOf(Limb);
1688 if (bits_left != 0) {1684 if (bytes_read != byte_count) {
1689 const bytes_read = limb_index * @sizeOf(Limb);1685 var limb: Limb = 0;
1690 const bytes_left = byte_count - bytes_read;
1691 var buffer_left = switch (endian) {
1692 .Little => buffer[bytes_read..],
1693 .Big => buffer[0..],
1694 };
16951686
1696 var limb = @intCast(Limb, blk: {1687 while (bytes_read != byte_count) {
1697 // zig fmt: off1688 const read_size = std.math.floorPowerOfTwo(usize, byte_count - bytes_read);
1698 if (bytes_left == 1) break :blk mem.readInt( u8, buffer_left[0.. 1], endian);1689 var int_buffer = switch (endian) {
1699 if (bytes_left == 2) break :blk mem.readInt( u16, buffer_left[0.. 2], endian);1690 .Little => buffer[bytes_read..],
1700 if (bytes_left == 4) break :blk mem.readInt( u32, buffer_left[0.. 4], endian);1691 .Big => buffer[(abi_size - bytes_read - read_size)..],
1701 if (bytes_left == 8) break :blk mem.readInt( u64, buffer_left[0.. 8], endian);1692 };
1702 if (bytes_left == 16) break :blk mem.readInt(u128, buffer_left[0..16], endian);1693 limb |= @intCast(Limb, switch (read_size) {
1703 // zig fmt: on1694 1 => mem.readInt(u8, int_buffer[0..1], endian),
1704 unreachable;1695 2 => mem.readInt(u16, int_buffer[0..2], endian),
1705 });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 }
17061703
1707 // 2's complement (bitwise not, then add carry bit)1704 // 2's complement (bitwise not, then add carry bit)
1708 if (!positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb);1705 if (!positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb);
17091706
1710 // Mask off any unused bits1707 // Mask off any unused bits
1711 const mask = (@as(Limb, 1) << bits_left) -% 1; // 0b0..01..1 with (bits_left) trailing ones1708 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 limb &= mask;1710 limb &= mask;
17131711
1714 x.limbs[limb_count - 1] = limb;1712 x.limbs[limb_count - 1] = limb;
...@@ -2076,21 +2074,16 @@ pub const Const = struct {...@@ -2076,21 +2074,16 @@ pub const Const = struct {
2076 }2074 }
20772075
2078 /// Write the value of `x` into `buffer`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 function2079 /// `buffer` is filled so that its contents match what would be observed via
2082 /// can be thought of as writing to `buffer` the contents of @ptrCast([]const u8, &x), 2080 /// @ptrCast(*[abi_size]const u8, &x). Byte ordering is determined by `endian`,
2083 /// where the slice length is taken to be @sizeOf(std.meta.Int(_,<bit_count>))2081 /// and any required padding bits are added on the MSB end.
2084 ///2082 pub fn writeTwosComplement(x: Const, buffer: []u8, bit_count: usize, abi_size: usize, endian: Endian) void {
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;
20892083
2090 var byte_count = @sizeOf(Limb) * (bit_count / @bitSizeOf(Limb));2084 // byte_count is our total write size
2091 if (bit_count % @bitSizeOf(Limb) != 0) {2085 const byte_count = abi_size;
2092 byte_count += (std.math.ceilPowerOfTwoAssert(usize, bit_count % @bitSizeOf(Limb)) + 7) / 8;2086 assert(8 * byte_count >= bit_count);
2093 }
2094 assert(buffer.len >= byte_count);2087 assert(buffer.len >= byte_count);
2095 assert(x.fitsInTwosComp(if (x.positive) .unsigned else .signed, bit_count));2088 assert(x.fitsInTwosComp(if (x.positive) .unsigned else .signed, bit_count));
20962089
...@@ -2100,7 +2093,7 @@ pub const Const = struct {...@@ -2100,7 +2093,7 @@ pub const Const = struct {
2100 while (limb_index < byte_count / @sizeOf(Limb)) : (limb_index += 1) {2093 while (limb_index < byte_count / @sizeOf(Limb)) : (limb_index += 1) {
2101 var buf_index = switch (endian) {2094 var buf_index = switch (endian) {
2102 .Little => @sizeOf(Limb) * limb_index,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 };
21052098
2106 var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0;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,32 +2104,36 @@ pub const Const = struct {
2111 mem.writeInt(Limb, limb_buf, limb, endian);2104 mem.writeInt(Limb, limb_buf, limb, endian);
2112 }2105 }
21132106
2114 // Copy any remaining bytes2107 // Copy the remaining N bytes (N < @sizeOf(Limb))
2115 if (byte_count % @sizeOf(Limb) != 0) {2108 var bytes_written = limb_index * @sizeOf(Limb);
2116 const bytes_read = limb_index * @sizeOf(Limb);2109 if (bytes_written != byte_count) {
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
2123 var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0;2110 var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0;
2124 // 2's complement (bitwise not, then add carry bit)2111 // 2's complement (bitwise not, then add carry bit)
2125 if (!x.positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb);2112 if (!x.positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb);
21262113
2127 if (bytes_left == 1) {2114 while (bytes_written != byte_count) {
2128 mem.writeInt(u8, buffer_left[0..1], @truncate(u8, limb), endian);2115 const write_size = std.math.floorPowerOfTwo(usize, byte_count - bytes_written);
2129 } else if (@sizeOf(Limb) > 1 and bytes_left == 2) {2116 var int_buffer = switch (endian) {
2130 mem.writeInt(u16, buffer_left[0..2], @truncate(u16, limb), endian);2117 .Little => buffer[bytes_written..],
2131 } else if (@sizeOf(Limb) > 2 and bytes_left == 4) {2118 .Big => buffer[(abi_size - bytes_written - write_size)..],
2132 mem.writeInt(u32, buffer_left[0..4], @truncate(u32, limb), endian);2119 };
2133 } else if (@sizeOf(Limb) > 4 and bytes_left == 8) {2120
2134 mem.writeInt(u64, buffer_left[0..8], @truncate(u64, limb), endian);2121 if (write_size == 1) {
2135 } else if (@sizeOf(Limb) > 8 and bytes_left == 16) {2122 mem.writeInt(u8, int_buffer[0..1], @truncate(u8, limb), endian);
2136 mem.writeInt(u128, buffer_left[0..16], @truncate(u128, limb), endian);2123 } else if (@sizeOf(Limb) >= 2 and write_size == 2) {
2137 } else if (@sizeOf(Limb) > 16) {2124 mem.writeInt(u16, int_buffer[0..2], @truncate(u16, limb), endian);
2138 @compileError("@sizeOf(Limb) exceeded supported range");2125 } else if (@sizeOf(Limb) >= 4 and write_size == 4) {
2139 } else unreachable;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 }
21422139
lib/std/math/big/int_test.zig+91-4
...@@ -2498,16 +2498,103 @@ test "big int conversion read/write twos complement" {...@@ -2498,16 +2498,103 @@ test "big int conversion read/write twos complement" {
2498 defer testing.allocator.free(buffer1);2498 defer testing.allocator.free(buffer1);
24992499
2500 const endians = [_]std.builtin.Endian{ .Little, .Big };2500 const endians = [_]std.builtin.Endian{ .Little, .Big };
2501 const abi_size = 64;
25012502
2502 for (endians) |endian| {2503 for (endians) |endian| {
2503 // Writing to buffer and back should not change anything2504 // Writing to buffer and back should not change anything
2504 a.toConst().writeTwosComplement(buffer1, 493, endian);2505 a.toConst().writeTwosComplement(buffer1, 493, abi_size, endian);
2505 m.readTwosComplement(buffer1, 493, endian, .unsigned);2506 m.readTwosComplement(buffer1, 493, abi_size, endian, .unsigned);
2506 try testing.expect(m.toConst().order(a.toConst()) == .eq);2507 try testing.expect(m.toConst().order(a.toConst()) == .eq);
25072508
2508 // Equivalent to @bitCast(i493, @as(u493, intMax(u493))2509 // Equivalent to @bitCast(i493, @as(u493, intMax(u493))
2509 a.toConst().writeTwosComplement(buffer1, 493, endian);2510 a.toConst().writeTwosComplement(buffer1, 493, abi_size, endian);
2510 m.readTwosComplement(buffer1, 493, endian, .signed);2511 m.readTwosComplement(buffer1, 493, abi_size, endian, .signed);
2511 try testing.expect(m.toConst().orderAgainstScalar(-1) == .eq);2512 try testing.expect(m.toConst().orderAgainstScalar(-1) == .eq);
2512 }2513 }
2513}2514}
2515
2516test "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
2553test "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,7 +1046,8 @@ pub const Value = extern union {
1046 var bigint_buffer: BigIntSpace = undefined;1046 var bigint_buffer: BigIntSpace = undefined;
1047 const bigint = val.toBigInt(&bigint_buffer);1047 const bigint = val.toBigInt(&bigint_buffer);
1048 const bits = ty.intInfo(target).bits;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 .Enum => {1052 .Enum => {
1052 var enum_buffer: Payload.U64 = undefined;1053 var enum_buffer: Payload.U64 = undefined;
...@@ -1054,7 +1055,8 @@ pub const Value = extern union {...@@ -1054,7 +1055,8 @@ pub const Value = extern union {
1054 var bigint_buffer: BigIntSpace = undefined;1055 var bigint_buffer: BigIntSpace = undefined;
1055 const bigint = int_val.toBigInt(&bigint_buffer);1056 const bigint = int_val.toBigInt(&bigint_buffer);
1056 const bits = ty.intInfo(target).bits;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 .Float => switch (ty.floatBits(target)) {1061 .Float => switch (ty.floatBits(target)) {
1060 16 => return floatWriteToMemory(f16, val.toFloat(f16), target, buffer),1062 16 => return floatWriteToMemory(f16, val.toFloat(f16), target, buffer),
...@@ -1096,8 +1098,9 @@ pub const Value = extern union {...@@ -1096,8 +1098,9 @@ pub const Value = extern union {
1096 const Limb = std.math.big.Limb;1098 const Limb = std.math.big.Limb;
1097 const limb_count = (buffer.len + @sizeOf(Limb) - 1) / @sizeOf(Limb);1099 const limb_count = (buffer.len + @sizeOf(Limb) - 1) / @sizeOf(Limb);
1098 const limbs_buffer = try arena.alloc(Limb, limb_count);1100 const limbs_buffer = try arena.alloc(Limb, limb_count);
1101 const abi_size = ty.abiSize(target);
1099 var bigint = BigIntMutable.init(limbs_buffer, 0);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 return fromBigInt(arena, bigint.toConst());1104 return fromBigInt(arena, bigint.toConst());
1102 },1105 },
1103 .Float => switch (ty.floatBits(target)) {1106 .Float => switch (ty.floatBits(target)) {