| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("../../std.zig"); |
| 2 | const builtin = @import("builtin"); |
| 2 | 3 | const math = std.math; |
| 3 | 4 | const Limb = std.math.big.Limb; |
| 4 | 5 | const limb_bits = @typeInfo(Limb).Int.bits; |
| ... | ... | @@ -14,6 +15,7 @@ const minInt = std.math.minInt; |
| 14 | 15 | const assert = std.debug.assert; |
| 15 | 16 | const Endian = std.builtin.Endian; |
| 16 | 17 | const Signedness = std.builtin.Signedness; |
| 18 | const native_endian = builtin.cpu.arch.endian(); |
| 17 | 19 | |
| 18 | 20 | const debug_safety = false; |
| 19 | 21 | |
| ... | ... | @@ -1621,6 +1623,15 @@ pub const Mutable = struct { |
| 1621 | 1623 | } |
| 1622 | 1624 | } |
| 1623 | 1625 | |
| 1626 | /// Read the value of `x` from `buffer` |
| 1627 | /// Asserts that `buffer` and `bit_count` are large enough to store the value. |
| 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. |
| 1624 | 1635 | pub fn readTwosComplement( |
| 1625 | 1636 | x: *Mutable, |
| 1626 | 1637 | buffer: []const u8, |
| ... | ... | @@ -1634,26 +1645,77 @@ pub const Mutable = struct { |
| 1634 | 1645 | x.positive = true; |
| 1635 | 1646 | return; |
| 1636 | 1647 | } |
| 1637 | | // zig fmt: off |
| 1638 | | switch (signedness) { |
| 1639 | | .signed => { |
| 1640 | | if (bit_count <= 8) return x.set(mem.readInt( i8, buffer[0.. 1], endian)); |
| 1641 | | if (bit_count <= 16) return x.set(mem.readInt( i16, buffer[0.. 2], endian)); |
| 1642 | | if (bit_count <= 32) return x.set(mem.readInt( i32, buffer[0.. 4], endian)); |
| 1643 | | if (bit_count <= 64) return x.set(mem.readInt( i64, buffer[0.. 8], endian)); |
| 1644 | | if (bit_count <= 128) return x.set(mem.readInt(i128, buffer[0..16], endian)); |
| 1645 | | }, |
| 1646 | | .unsigned => { |
| 1647 | | if (bit_count <= 8) return x.set(mem.readInt( u8, buffer[0.. 1], endian)); |
| 1648 | | if (bit_count <= 16) return x.set(mem.readInt( u16, buffer[0.. 2], endian)); |
| 1649 | | if (bit_count <= 32) return x.set(mem.readInt( u32, buffer[0.. 4], endian)); |
| 1650 | | if (bit_count <= 64) return x.set(mem.readInt( u64, buffer[0.. 8], endian)); |
| 1651 | | if (bit_count <= 128) return x.set(mem.readInt(u128, buffer[0..16], endian)); |
| 1652 | | }, |
| 1648 | |
| 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); |
| 1656 | |
| 1657 | // Check whether the input is negative |
| 1658 | var positive = true; |
| 1659 | if (signedness == .signed) { |
| 1660 | var last_byte = switch (endian) { |
| 1661 | .Little => ((bit_count + 7) / 8) - 1, |
| 1662 | .Big => byte_count - ((bit_count + 7) / 8), |
| 1663 | }; |
| 1664 | |
| 1665 | const sign_bit = @as(u8, 1) << @intCast(u3, (bit_count - 1) % 8); |
| 1666 | positive = ((buffer[last_byte] & sign_bit) == 0); |
| 1667 | } |
| 1668 | |
| 1669 | // Copy all complete limbs |
| 1670 | var carry: u1 = if (positive) 0 else 1; |
| 1671 | var limb_index: usize = 0; |
| 1672 | while (limb_index < bit_count / @bitSizeOf(Limb)) : (limb_index += 1) { |
| 1673 | var buf_index = switch (endian) { |
| 1674 | .Little => @sizeOf(Limb) * limb_index, |
| 1675 | .Big => byte_count - (limb_index + 1) * @sizeOf(Limb), |
| 1676 | }; |
| 1677 | |
| 1678 | const limb_buf = @ptrCast(*const [@sizeOf(Limb)]u8, buffer[buf_index..]); |
| 1679 | var limb = mem.readInt(Limb, limb_buf, endian); |
| 1680 | |
| 1681 | // 2's complement (bitwise not, then add carry bit) |
| 1682 | if (!positive) carry = @boolToInt(@addWithOverflow(Limb, ~limb, carry, &limb)); |
| 1683 | x.limbs[limb_index] = limb; |
| 1653 | 1684 | } |
| 1654 | | // zig fmt: on |
| 1655 | 1685 | |
| 1656 | | @panic("TODO implement std lib big int readTwosComplement"); |
| 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 | }; |
| 1695 | |
| 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 | }); |
| 1706 | |
| 1707 | // 2's complement (bitwise not, then add carry bit) |
| 1708 | if (!positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb); |
| 1709 | |
| 1710 | // Mask off any unused bits |
| 1711 | const mask = (@as(Limb, 1) << bits_left) -% 1; // 0b0..01..1 with (bits_left) trailing ones |
| 1712 | limb &= mask; |
| 1713 | |
| 1714 | x.limbs[limb_count - 1] = limb; |
| 1715 | } |
| 1716 | x.positive = positive; |
| 1717 | x.len = limb_count; |
| 1718 | x.normalize(x.len); |
| 1657 | 1719 | } |
| 1658 | 1720 | |
| 1659 | 1721 | /// Normalize a possible sequence of leading zeros. |
| ... | ... | @@ -1806,7 +1868,7 @@ pub const Const = struct { |
| 1806 | 1868 | .Int => |info| { |
| 1807 | 1869 | const UT = std.meta.Int(.unsigned, info.bits); |
| 1808 | 1870 | |
| 1809 | | if (self.bitCountTwosComp() > info.bits) { |
| 1871 | if (!self.fitsInTwosComp(info.signedness, info.bits)) { |
| 1810 | 1872 | return error.TargetTooSmall; |
| 1811 | 1873 | } |
| 1812 | 1874 | |
| ... | ... | @@ -2013,27 +2075,69 @@ pub const Const = struct { |
| 2013 | 2075 | return s.len; |
| 2014 | 2076 | } |
| 2015 | 2077 | |
| 2078 | /// Write the value of `x` into `buffer` |
| 2016 | 2079 | /// Asserts that `buffer` and `bit_count` are large enough to store the value. |
| 2080 | /// |
| 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. |
| 2017 | 2087 | pub fn writeTwosComplement(x: Const, buffer: []u8, bit_count: usize, endian: Endian) void { |
| 2018 | 2088 | if (bit_count == 0) return; |
| 2019 | 2089 | |
| 2020 | | // zig fmt: off |
| 2021 | | if (x.positive) { |
| 2022 | | if (bit_count <= 8) return mem.writeInt( u8, buffer[0.. 1], x.to( u8) catch unreachable, endian); |
| 2023 | | if (bit_count <= 16) return mem.writeInt( u16, buffer[0.. 2], x.to( u16) catch unreachable, endian); |
| 2024 | | if (bit_count <= 32) return mem.writeInt( u32, buffer[0.. 4], x.to( u32) catch unreachable, endian); |
| 2025 | | if (bit_count <= 64) return mem.writeInt( u64, buffer[0.. 8], x.to( u64) catch unreachable, endian); |
| 2026 | | if (bit_count <= 128) return mem.writeInt(u128, buffer[0..16], x.to(u128) catch unreachable, endian); |
| 2027 | | } else { |
| 2028 | | if (bit_count <= 8) return mem.writeInt( i8, buffer[0.. 1], x.to( i8) catch unreachable, endian); |
| 2029 | | if (bit_count <= 16) return mem.writeInt( i16, buffer[0.. 2], x.to( i16) catch unreachable, endian); |
| 2030 | | if (bit_count <= 32) return mem.writeInt( i32, buffer[0.. 4], x.to( i32) catch unreachable, endian); |
| 2031 | | if (bit_count <= 64) return mem.writeInt( i64, buffer[0.. 8], x.to( i64) catch unreachable, endian); |
| 2032 | | if (bit_count <= 128) return mem.writeInt(i128, buffer[0..16], x.to(i128) catch unreachable, endian); |
| 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; |
| 2033 | 2093 | } |
| 2034 | | // zig fmt: on |
| 2094 | assert(buffer.len >= byte_count); |
| 2095 | assert(x.fitsInTwosComp(if (x.positive) .unsigned else .signed, bit_count)); |
| 2096 | |
| 2097 | // Copy all complete limbs |
| 2098 | var carry: u1 = if (x.positive) 0 else 1; |
| 2099 | var limb_index: usize = 0; |
| 2100 | while (limb_index < byte_count / @sizeOf(Limb)) : (limb_index += 1) { |
| 2101 | var buf_index = switch (endian) { |
| 2102 | .Little => @sizeOf(Limb) * limb_index, |
| 2103 | .Big => byte_count - (limb_index + 1) * @sizeOf(Limb), |
| 2104 | }; |
| 2035 | 2105 | |
| 2036 | | @panic("TODO implement std lib big int writeTwosComplement for larger than 128 bits"); |
| 2106 | var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0; |
| 2107 | // 2's complement (bitwise not, then add carry bit) |
| 2108 | if (!x.positive) carry = @boolToInt(@addWithOverflow(Limb, ~limb, carry, &limb)); |
| 2109 | |
| 2110 | var limb_buf = @ptrCast(*[@sizeOf(Limb)]u8, buffer[buf_index..]); |
| 2111 | mem.writeInt(Limb, limb_buf, limb, endian); |
| 2112 | } |
| 2113 | |
| 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 | |
| 2123 | 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) |
| 2125 | if (!x.positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb); |
| 2126 | |
| 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; |
| 2140 | } |
| 2037 | 2141 | } |
| 2038 | 2142 | |
| 2039 | 2143 | /// Returns `math.Order.lt`, `math.Order.eq`, `math.Order.gt` if |