| ... | @@ -1624,18 +1624,16 @@ pub const Mutable = struct { | ... | @@ -1624,18 +1624,16 @@ pub const Mutable = struct { |
| 1624 | } | 1624 | } |
| 1625 | | 1625 | |
| 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 function | 1629 | /// 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 | } |
| 1648 | | 1646 | |
| 1649 | // byte_count is the total amount of bytes to read from buffer | 1647 | // 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 <= Limb | 1649 | 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); | | |
| 1656 | | 1652 | |
| 1657 | // Check whether the input is negative | 1653 | // 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 | }; |
| 1664 | | 1660 | |
| 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 | }; |
| 1677 | | 1673 | |
| 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 | } |
| 1685 | | 1681 | |
| 1686 | // Copy any remaining bytes, using the nearest power-of-two integer that is large enough | 1682 | // 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 | }; | | |
| 1695 | | 1686 | |
| 1696 | var limb = @intCast(Limb, blk: { | 1687 | while (bytes_read != byte_count) { |
| 1697 | // zig fmt: off | 1688 | 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: on | 1694 | 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 | } |
| 1706 | | 1703 | |
| 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); |
| 1709 | | 1706 | |
| 1710 | // Mask off any unused bits | 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 | limb &= mask; | 1710 | limb &= mask; |
| 1713 | | 1711 | |
| 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 | } |
| 2077 | | 2075 | |
| 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 function | 2079 | /// `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; | | |
| 2089 | | 2083 | |
| 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)); |
| 2096 | | 2089 | |
| ... | @@ -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 | }; |
| 2105 | | 2098 | |
| 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 | } |
| 2113 | | 2106 | |
| 2114 | // Copy any remaining bytes | 2107 | // 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); |
| 2126 | | 2113 | |
| 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 | } |
| 2142 | | 2139 | |