mirror of
https://github.com/ziglang/zig.git
synced 2026-01-21 06:45:24 +00:00
Merge pull request #10858 from topolarity/stage2-bitcast
stage2 sema: Fix sign handling of exotic integers in `@bitCast`
This commit is contained in:
commit
736b9dcdd6
@ -1,4 +1,5 @@
|
||||
const std = @import("../../std.zig");
|
||||
const builtin = @import("builtin");
|
||||
const math = std.math;
|
||||
const Limb = std.math.big.Limb;
|
||||
const limb_bits = @typeInfo(Limb).Int.bits;
|
||||
@ -14,6 +15,7 @@ const minInt = std.math.minInt;
|
||||
const assert = std.debug.assert;
|
||||
const Endian = std.builtin.Endian;
|
||||
const Signedness = std.builtin.Signedness;
|
||||
const native_endian = builtin.cpu.arch.endian();
|
||||
|
||||
const debug_safety = false;
|
||||
|
||||
@ -1621,10 +1623,17 @@ pub const Mutable = struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the value of `x` from `buffer`
|
||||
/// Asserts that `buffer`, `abi_size`, and `bit_count` are large enough to store the value.
|
||||
///
|
||||
/// The contents of `buffer` are interpreted as if they were the contents of
|
||||
/// @ptrCast(*[abi_size]const u8, &x). Byte ordering is determined by `endian`
|
||||
/// and any required padding bits are expected on the MSB end.
|
||||
pub fn readTwosComplement(
|
||||
x: *Mutable,
|
||||
buffer: []const u8,
|
||||
bit_count: usize,
|
||||
abi_size: usize,
|
||||
endian: Endian,
|
||||
signedness: Signedness,
|
||||
) void {
|
||||
@ -1634,26 +1643,77 @@ pub const Mutable = struct {
|
||||
x.positive = true;
|
||||
return;
|
||||
}
|
||||
// zig fmt: off
|
||||
switch (signedness) {
|
||||
.signed => {
|
||||
if (bit_count <= 8) return x.set(mem.readInt( i8, buffer[0.. 1], endian));
|
||||
if (bit_count <= 16) return x.set(mem.readInt( i16, buffer[0.. 2], endian));
|
||||
if (bit_count <= 32) return x.set(mem.readInt( i32, buffer[0.. 4], endian));
|
||||
if (bit_count <= 64) return x.set(mem.readInt( i64, buffer[0.. 8], endian));
|
||||
if (bit_count <= 128) return x.set(mem.readInt(i128, buffer[0..16], endian));
|
||||
},
|
||||
.unsigned => {
|
||||
if (bit_count <= 8) return x.set(mem.readInt( u8, buffer[0.. 1], endian));
|
||||
if (bit_count <= 16) return x.set(mem.readInt( u16, buffer[0.. 2], endian));
|
||||
if (bit_count <= 32) return x.set(mem.readInt( u32, buffer[0.. 4], endian));
|
||||
if (bit_count <= 64) return x.set(mem.readInt( u64, buffer[0.. 8], endian));
|
||||
if (bit_count <= 128) return x.set(mem.readInt(u128, buffer[0..16], endian));
|
||||
},
|
||||
}
|
||||
// zig fmt: on
|
||||
|
||||
@panic("TODO implement std lib big int readTwosComplement");
|
||||
// byte_count is our total read size: it cannot exceed abi_size,
|
||||
// but may be less as long as it includes the required bits
|
||||
const limb_count = calcTwosCompLimbCount(bit_count);
|
||||
const byte_count = std.math.min(abi_size, @sizeOf(Limb) * limb_count);
|
||||
assert(8 * byte_count >= bit_count);
|
||||
|
||||
// Check whether the input is negative
|
||||
var positive = true;
|
||||
if (signedness == .signed) {
|
||||
var last_byte = switch (endian) {
|
||||
.Little => ((bit_count + 7) / 8) - 1,
|
||||
.Big => abi_size - ((bit_count + 7) / 8),
|
||||
};
|
||||
|
||||
const sign_bit = @as(u8, 1) << @intCast(u3, (bit_count - 1) % 8);
|
||||
positive = ((buffer[last_byte] & sign_bit) == 0);
|
||||
}
|
||||
|
||||
// Copy all complete limbs
|
||||
var carry: u1 = if (positive) 0 else 1;
|
||||
var limb_index: usize = 0;
|
||||
while (limb_index < bit_count / @bitSizeOf(Limb)) : (limb_index += 1) {
|
||||
var buf_index = switch (endian) {
|
||||
.Little => @sizeOf(Limb) * limb_index,
|
||||
.Big => abi_size - (limb_index + 1) * @sizeOf(Limb),
|
||||
};
|
||||
|
||||
const limb_buf = @ptrCast(*const [@sizeOf(Limb)]u8, buffer[buf_index..]);
|
||||
var limb = mem.readInt(Limb, limb_buf, endian);
|
||||
|
||||
// 2's complement (bitwise not, then add carry bit)
|
||||
if (!positive) carry = @boolToInt(@addWithOverflow(Limb, ~limb, carry, &limb));
|
||||
x.limbs[limb_index] = limb;
|
||||
}
|
||||
|
||||
// Copy the remaining N bytes (N <= @sizeOf(Limb))
|
||||
var bytes_read = limb_index * @sizeOf(Limb);
|
||||
if (bytes_read != byte_count) {
|
||||
var limb: Limb = 0;
|
||||
|
||||
while (bytes_read != byte_count) {
|
||||
const read_size = std.math.floorPowerOfTwo(usize, byte_count - bytes_read);
|
||||
var int_buffer = switch (endian) {
|
||||
.Little => buffer[bytes_read..],
|
||||
.Big => buffer[(abi_size - bytes_read - read_size)..],
|
||||
};
|
||||
limb |= @intCast(Limb, switch (read_size) {
|
||||
1 => mem.readInt(u8, int_buffer[0..1], endian),
|
||||
2 => mem.readInt(u16, int_buffer[0..2], endian),
|
||||
4 => mem.readInt(u32, int_buffer[0..4], endian),
|
||||
8 => mem.readInt(u64, int_buffer[0..8], endian),
|
||||
16 => mem.readInt(u128, int_buffer[0..16], endian),
|
||||
else => unreachable,
|
||||
}) << @intCast(Log2Limb, 8 * (bytes_read % @sizeOf(Limb)));
|
||||
bytes_read += read_size;
|
||||
}
|
||||
|
||||
// 2's complement (bitwise not, then add carry bit)
|
||||
if (!positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb);
|
||||
|
||||
// Mask off any unused bits
|
||||
const valid_bits = @intCast(Log2Limb, bit_count % @bitSizeOf(Limb));
|
||||
const mask = (@as(Limb, 1) << valid_bits) -% 1; // 0b0..01..1 with (valid_bits_in_limb) trailing ones
|
||||
limb &= mask;
|
||||
|
||||
x.limbs[limb_count - 1] = limb;
|
||||
}
|
||||
x.positive = positive;
|
||||
x.len = limb_count;
|
||||
x.normalize(x.len);
|
||||
}
|
||||
|
||||
/// Normalize a possible sequence of leading zeros.
|
||||
@ -1806,7 +1866,7 @@ pub const Const = struct {
|
||||
.Int => |info| {
|
||||
const UT = std.meta.Int(.unsigned, info.bits);
|
||||
|
||||
if (self.bitCountTwosComp() > info.bits) {
|
||||
if (!self.fitsInTwosComp(info.signedness, info.bits)) {
|
||||
return error.TargetTooSmall;
|
||||
}
|
||||
|
||||
@ -2013,27 +2073,68 @@ pub const Const = struct {
|
||||
return s.len;
|
||||
}
|
||||
|
||||
/// Asserts that `buffer` and `bit_count` are large enough to store the value.
|
||||
pub fn writeTwosComplement(x: Const, buffer: []u8, bit_count: usize, endian: Endian) void {
|
||||
if (bit_count == 0) return;
|
||||
/// Write the value of `x` into `buffer`
|
||||
/// Asserts that `buffer`, `abi_size`, and `bit_count` are large enough to store the value.
|
||||
///
|
||||
/// `buffer` is filled so that its contents match what would be observed via
|
||||
/// @ptrCast(*[abi_size]const u8, &x). Byte ordering is determined by `endian`,
|
||||
/// and any required padding bits are added on the MSB end.
|
||||
pub fn writeTwosComplement(x: Const, buffer: []u8, bit_count: usize, abi_size: usize, endian: Endian) void {
|
||||
|
||||
// zig fmt: off
|
||||
if (x.positive) {
|
||||
if (bit_count <= 8) return mem.writeInt( u8, buffer[0.. 1], x.to( u8) catch unreachable, endian);
|
||||
if (bit_count <= 16) return mem.writeInt( u16, buffer[0.. 2], x.to( u16) catch unreachable, endian);
|
||||
if (bit_count <= 32) return mem.writeInt( u32, buffer[0.. 4], x.to( u32) catch unreachable, endian);
|
||||
if (bit_count <= 64) return mem.writeInt( u64, buffer[0.. 8], x.to( u64) catch unreachable, endian);
|
||||
if (bit_count <= 128) return mem.writeInt(u128, buffer[0..16], x.to(u128) catch unreachable, endian);
|
||||
} else {
|
||||
if (bit_count <= 8) return mem.writeInt( i8, buffer[0.. 1], x.to( i8) catch unreachable, endian);
|
||||
if (bit_count <= 16) return mem.writeInt( i16, buffer[0.. 2], x.to( i16) catch unreachable, endian);
|
||||
if (bit_count <= 32) return mem.writeInt( i32, buffer[0.. 4], x.to( i32) catch unreachable, endian);
|
||||
if (bit_count <= 64) return mem.writeInt( i64, buffer[0.. 8], x.to( i64) catch unreachable, endian);
|
||||
if (bit_count <= 128) return mem.writeInt(i128, buffer[0..16], x.to(i128) catch unreachable, endian);
|
||||
// byte_count is our total write size
|
||||
const byte_count = abi_size;
|
||||
assert(8 * byte_count >= bit_count);
|
||||
assert(buffer.len >= byte_count);
|
||||
assert(x.fitsInTwosComp(if (x.positive) .unsigned else .signed, bit_count));
|
||||
|
||||
// Copy all complete limbs
|
||||
var carry: u1 = if (x.positive) 0 else 1;
|
||||
var limb_index: usize = 0;
|
||||
while (limb_index < byte_count / @sizeOf(Limb)) : (limb_index += 1) {
|
||||
var buf_index = switch (endian) {
|
||||
.Little => @sizeOf(Limb) * limb_index,
|
||||
.Big => abi_size - (limb_index + 1) * @sizeOf(Limb),
|
||||
};
|
||||
|
||||
var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0;
|
||||
// 2's complement (bitwise not, then add carry bit)
|
||||
if (!x.positive) carry = @boolToInt(@addWithOverflow(Limb, ~limb, carry, &limb));
|
||||
|
||||
var limb_buf = @ptrCast(*[@sizeOf(Limb)]u8, buffer[buf_index..]);
|
||||
mem.writeInt(Limb, limb_buf, limb, endian);
|
||||
}
|
||||
// zig fmt: on
|
||||
|
||||
@panic("TODO implement std lib big int writeTwosComplement for larger than 128 bits");
|
||||
// Copy the remaining N bytes (N < @sizeOf(Limb))
|
||||
var bytes_written = limb_index * @sizeOf(Limb);
|
||||
if (bytes_written != byte_count) {
|
||||
var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0;
|
||||
// 2's complement (bitwise not, then add carry bit)
|
||||
if (!x.positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb);
|
||||
|
||||
while (bytes_written != byte_count) {
|
||||
const write_size = std.math.floorPowerOfTwo(usize, byte_count - bytes_written);
|
||||
var int_buffer = switch (endian) {
|
||||
.Little => buffer[bytes_written..],
|
||||
.Big => buffer[(abi_size - bytes_written - write_size)..],
|
||||
};
|
||||
|
||||
if (write_size == 1) {
|
||||
mem.writeInt(u8, int_buffer[0..1], @truncate(u8, limb), endian);
|
||||
} else if (@sizeOf(Limb) >= 2 and write_size == 2) {
|
||||
mem.writeInt(u16, int_buffer[0..2], @truncate(u16, limb), endian);
|
||||
} else if (@sizeOf(Limb) >= 4 and write_size == 4) {
|
||||
mem.writeInt(u32, int_buffer[0..4], @truncate(u32, limb), endian);
|
||||
} else if (@sizeOf(Limb) >= 8 and write_size == 8) {
|
||||
mem.writeInt(u64, int_buffer[0..8], @truncate(u64, limb), endian);
|
||||
} else if (@sizeOf(Limb) >= 16 and write_size == 16) {
|
||||
mem.writeInt(u128, int_buffer[0..16], @truncate(u128, limb), endian);
|
||||
} else if (@sizeOf(Limb) >= 32) {
|
||||
@compileError("@sizeOf(Limb) exceeded supported range");
|
||||
} else unreachable;
|
||||
limb >>= @intCast(Log2Limb, 8 * write_size);
|
||||
bytes_written += write_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `math.Order.lt`, `math.Order.eq`, `math.Order.gt` if
|
||||
|
||||
@ -2486,3 +2486,206 @@ test "big int popcount" {
|
||||
|
||||
try testing.expect(a.toConst().orderAgainstScalar(16) == .eq);
|
||||
}
|
||||
|
||||
test "big int conversion read/write twos complement" {
|
||||
var a = try Managed.initSet(testing.allocator, (1 << 493) - 1);
|
||||
defer a.deinit();
|
||||
var b = try Managed.initSet(testing.allocator, (1 << 493) - 1);
|
||||
defer b.deinit();
|
||||
var m = b.toMutable();
|
||||
|
||||
var buffer1 = try testing.allocator.alloc(u8, 64);
|
||||
defer testing.allocator.free(buffer1);
|
||||
|
||||
const endians = [_]std.builtin.Endian{ .Little, .Big };
|
||||
const abi_size = 64;
|
||||
|
||||
for (endians) |endian| {
|
||||
// Writing to buffer and back should not change anything
|
||||
a.toConst().writeTwosComplement(buffer1, 493, abi_size, endian);
|
||||
m.readTwosComplement(buffer1, 493, abi_size, endian, .unsigned);
|
||||
try testing.expect(m.toConst().order(a.toConst()) == .eq);
|
||||
|
||||
// Equivalent to @bitCast(i493, @as(u493, intMax(u493))
|
||||
a.toConst().writeTwosComplement(buffer1, 493, abi_size, endian);
|
||||
m.readTwosComplement(buffer1, 493, abi_size, endian, .signed);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(-1) == .eq);
|
||||
}
|
||||
}
|
||||
|
||||
test "big int conversion read twos complement with padding" {
|
||||
var a = try Managed.initSet(testing.allocator, 0x01_02030405_06070809_0a0b0c0d);
|
||||
defer a.deinit();
|
||||
|
||||
var buffer1 = try testing.allocator.alloc(u8, 16);
|
||||
defer testing.allocator.free(buffer1);
|
||||
@memset(buffer1.ptr, 0xaa, buffer1.len);
|
||||
|
||||
// writeTwosComplement:
|
||||
// (1) should not write beyond buffer[0..abi_size]
|
||||
// (2) should correctly order bytes based on the provided endianness
|
||||
// (3) should sign-extend any bits from bit_count to 8 * abi_size
|
||||
|
||||
var bit_count: usize = 12 * 8 + 1;
|
||||
a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Little);
|
||||
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 }));
|
||||
a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Big);
|
||||
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 }));
|
||||
a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Little);
|
||||
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 }));
|
||||
a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Big);
|
||||
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 }));
|
||||
|
||||
@memset(buffer1.ptr, 0xaa, buffer1.len);
|
||||
try a.set(-0x01_02030405_06070809_0a0b0c0d);
|
||||
bit_count = 12 * 8 + 2;
|
||||
|
||||
a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Little);
|
||||
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 }));
|
||||
a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Big);
|
||||
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 }));
|
||||
a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Little);
|
||||
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 }));
|
||||
a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Big);
|
||||
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 }));
|
||||
}
|
||||
|
||||
test "big int write twos complement +/- zero" {
|
||||
var a = try Managed.initSet(testing.allocator, 0x0);
|
||||
defer a.deinit();
|
||||
var m = a.toMutable();
|
||||
|
||||
var buffer1 = try testing.allocator.alloc(u8, 16);
|
||||
defer testing.allocator.free(buffer1);
|
||||
@memset(buffer1.ptr, 0xaa, buffer1.len);
|
||||
|
||||
var bit_count: usize = 0;
|
||||
|
||||
// Test zero
|
||||
|
||||
m.toConst().writeTwosComplement(buffer1, bit_count, 13, .Little);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));
|
||||
m.toConst().writeTwosComplement(buffer1, bit_count, 13, .Big);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));
|
||||
m.toConst().writeTwosComplement(buffer1, bit_count, 16, .Little);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));
|
||||
m.toConst().writeTwosComplement(buffer1, bit_count, 16, .Big);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));
|
||||
|
||||
@memset(buffer1.ptr, 0xaa, buffer1.len);
|
||||
m.positive = false;
|
||||
|
||||
// Test negative zero
|
||||
|
||||
m.toConst().writeTwosComplement(buffer1, bit_count, 13, .Little);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));
|
||||
m.toConst().writeTwosComplement(buffer1, bit_count, 13, .Big);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));
|
||||
m.toConst().writeTwosComplement(buffer1, bit_count, 16, .Little);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));
|
||||
m.toConst().writeTwosComplement(buffer1, bit_count, 16, .Big);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));
|
||||
}
|
||||
|
||||
test "big int conversion write twos complement with padding" {
|
||||
var a = try Managed.initSet(testing.allocator, 0x01_ffffffff_ffffffff_ffffffff);
|
||||
defer a.deinit();
|
||||
|
||||
var m = a.toMutable();
|
||||
|
||||
// readTwosComplement:
|
||||
// (1) should not read beyond buffer[0..abi_size]
|
||||
// (2) should correctly interpret bytes based on the provided endianness
|
||||
// (3) should ignore any bits from bit_count to 8 * abi_size
|
||||
|
||||
var bit_count: usize = 12 * 8 + 1;
|
||||
var buffer: []const u8 = undefined;
|
||||
|
||||
// Test 0x01_02030405_06070809_0a0b0c0d
|
||||
|
||||
buffer = &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0xb };
|
||||
m.readTwosComplement(buffer, bit_count, 13, .Little, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq);
|
||||
|
||||
buffer = &[_]u8{ 0xb, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd };
|
||||
m.readTwosComplement(buffer, bit_count, 13, .Big, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq);
|
||||
|
||||
buffer = &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0xab, 0xaa, 0xaa, 0xaa };
|
||||
m.readTwosComplement(buffer, bit_count, 16, .Little, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq);
|
||||
|
||||
buffer = &[_]u8{ 0xaa, 0xaa, 0xaa, 0xab, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd };
|
||||
m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq);
|
||||
|
||||
bit_count = 12 * 8 + 2;
|
||||
|
||||
// Test -0x01_02030405_06070809_0a0b0c0d
|
||||
|
||||
buffer = &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0x02 };
|
||||
m.readTwosComplement(buffer, bit_count, 13, .Little, .signed);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq);
|
||||
|
||||
buffer = &[_]u8{ 0x02, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 };
|
||||
m.readTwosComplement(buffer, bit_count, 13, .Big, .signed);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq);
|
||||
|
||||
buffer = &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0x02, 0xaa, 0xaa, 0xaa };
|
||||
m.readTwosComplement(buffer, bit_count, 16, .Little, .signed);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq);
|
||||
|
||||
buffer = &[_]u8{ 0xaa, 0xaa, 0xaa, 0x02, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 };
|
||||
m.readTwosComplement(buffer, bit_count, 16, .Big, .signed);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq);
|
||||
|
||||
// Test 0
|
||||
|
||||
buffer = &([_]u8{0} ** 16);
|
||||
m.readTwosComplement(buffer, bit_count, 13, .Little, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
|
||||
m.readTwosComplement(buffer, bit_count, 13, .Big, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
|
||||
m.readTwosComplement(buffer, bit_count, 16, .Little, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
|
||||
m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
|
||||
|
||||
bit_count = 0;
|
||||
buffer = &([_]u8{0xaa} ** 16);
|
||||
m.readTwosComplement(buffer, bit_count, 13, .Little, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
|
||||
m.readTwosComplement(buffer, bit_count, 13, .Big, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
|
||||
m.readTwosComplement(buffer, bit_count, 16, .Little, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
|
||||
m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
|
||||
}
|
||||
|
||||
test "big int conversion write twos complement zero" {
|
||||
var a = try Managed.initSet(testing.allocator, 0x01_ffffffff_ffffffff_ffffffff);
|
||||
defer a.deinit();
|
||||
|
||||
var m = a.toMutable();
|
||||
|
||||
// readTwosComplement:
|
||||
// (1) should not read beyond buffer[0..abi_size]
|
||||
// (2) should correctly interpret bytes based on the provided endianness
|
||||
// (3) should ignore any bits from bit_count to 8 * abi_size
|
||||
|
||||
var bit_count: usize = 12 * 8 + 1;
|
||||
var buffer: []const u8 = undefined;
|
||||
|
||||
buffer = &([_]u8{0} ** 13);
|
||||
m.readTwosComplement(buffer, bit_count, 13, .Little, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
|
||||
m.readTwosComplement(buffer, bit_count, 13, .Big, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
|
||||
|
||||
buffer = &([_]u8{0} ** 16);
|
||||
m.readTwosComplement(buffer, bit_count, 16, .Little, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
|
||||
m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned);
|
||||
try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
|
||||
}
|
||||
|
||||
@ -313,12 +313,12 @@ void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bi
|
||||
}
|
||||
|
||||
if (digit_index == 0) break;
|
||||
digit_index -= 1;
|
||||
if (digit_index == last_digit_index) {
|
||||
buf_index += bytes_in_last_digit;
|
||||
} else {
|
||||
buf_index += 8;
|
||||
}
|
||||
digit_index -= 1;
|
||||
}
|
||||
} else {
|
||||
size_t digit_count = (bit_count + 63) / 64;
|
||||
|
||||
@ -1046,7 +1046,8 @@ pub const Value = extern union {
|
||||
var bigint_buffer: BigIntSpace = undefined;
|
||||
const bigint = val.toBigInt(&bigint_buffer);
|
||||
const bits = ty.intInfo(target).bits;
|
||||
bigint.writeTwosComplement(buffer, bits, target.cpu.arch.endian());
|
||||
const abi_size = @intCast(usize, ty.abiSize(target));
|
||||
bigint.writeTwosComplement(buffer, bits, abi_size, target.cpu.arch.endian());
|
||||
},
|
||||
.Enum => {
|
||||
var enum_buffer: Payload.U64 = undefined;
|
||||
@ -1054,7 +1055,8 @@ pub const Value = extern union {
|
||||
var bigint_buffer: BigIntSpace = undefined;
|
||||
const bigint = int_val.toBigInt(&bigint_buffer);
|
||||
const bits = ty.intInfo(target).bits;
|
||||
bigint.writeTwosComplement(buffer, bits, target.cpu.arch.endian());
|
||||
const abi_size = @intCast(usize, ty.abiSize(target));
|
||||
bigint.writeTwosComplement(buffer, bits, abi_size, target.cpu.arch.endian());
|
||||
},
|
||||
.Float => switch (ty.floatBits(target)) {
|
||||
16 => return floatWriteToMemory(f16, val.toFloat(f16), target, buffer),
|
||||
@ -1093,10 +1095,12 @@ pub const Value = extern union {
|
||||
.Int => {
|
||||
const int_info = ty.intInfo(target);
|
||||
const endian = target.cpu.arch.endian();
|
||||
// TODO use a correct amount of limbs
|
||||
const limbs_buffer = try arena.alloc(std.math.big.Limb, 2);
|
||||
const Limb = std.math.big.Limb;
|
||||
const limb_count = (buffer.len + @sizeOf(Limb) - 1) / @sizeOf(Limb);
|
||||
const limbs_buffer = try arena.alloc(Limb, limb_count);
|
||||
const abi_size = @intCast(usize, ty.abiSize(target));
|
||||
var bigint = BigIntMutable.init(limbs_buffer, 0);
|
||||
bigint.readTwosComplement(buffer, int_info.bits, endian, int_info.signedness);
|
||||
bigint.readTwosComplement(buffer, int_info.bits, abi_size, endian, int_info.signedness);
|
||||
return fromBigInt(arena, bigint.toConst());
|
||||
},
|
||||
.Float => switch (ty.floatBits(target)) {
|
||||
|
||||
@ -3,27 +3,66 @@ const builtin = @import("builtin");
|
||||
const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const maxInt = std.math.maxInt;
|
||||
const minInt = std.math.minInt;
|
||||
const native_endian = builtin.target.cpu.arch.endian();
|
||||
|
||||
test "@bitCast i32 -> u32" {
|
||||
try testBitCast_i32_u32();
|
||||
comptime try testBitCast_i32_u32();
|
||||
test "@bitCast iX -> uX (32, 64)" {
|
||||
const bit_values = [_]usize{ 32, 64 };
|
||||
|
||||
inline for (bit_values) |bits| {
|
||||
try testBitCast(bits);
|
||||
comptime try testBitCast(bits);
|
||||
}
|
||||
}
|
||||
|
||||
fn testBitCast_i32_u32() !void {
|
||||
try expect(conv(-1) == maxInt(u32));
|
||||
try expect(conv2(maxInt(u32)) == -1);
|
||||
test "@bitCast iX -> uX (8, 16, 128)" {
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
const bit_values = [_]usize{ 8, 16, 128 };
|
||||
|
||||
inline for (bit_values) |bits| {
|
||||
try testBitCast(bits);
|
||||
comptime try testBitCast(bits);
|
||||
}
|
||||
}
|
||||
|
||||
fn conv(x: i32) u32 {
|
||||
return @bitCast(u32, x);
|
||||
}
|
||||
fn conv2(x: u32) i32 {
|
||||
return @bitCast(i32, x);
|
||||
test "@bitCast iX -> uX exotic integers" {
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
|
||||
const bit_values = [_]usize{ 1, 48, 27, 512, 493, 293, 125, 204, 112 };
|
||||
|
||||
inline for (bit_values) |bits| {
|
||||
try testBitCast(bits);
|
||||
comptime try testBitCast(bits);
|
||||
}
|
||||
}
|
||||
|
||||
test "bitcast result to _" {
|
||||
_ = @bitCast(u8, @as(i8, 1));
|
||||
fn testBitCast(comptime N: usize) !void {
|
||||
const iN = std.meta.Int(.signed, N);
|
||||
const uN = std.meta.Int(.unsigned, N);
|
||||
|
||||
try expect(conv_iN(N, -1) == maxInt(uN));
|
||||
try expect(conv_uN(N, maxInt(uN)) == -1);
|
||||
|
||||
try expect(conv_iN(N, maxInt(iN)) == maxInt(iN));
|
||||
try expect(conv_uN(N, maxInt(iN)) == maxInt(iN));
|
||||
|
||||
try expect(conv_uN(N, 1 << (N - 1)) == minInt(iN));
|
||||
try expect(conv_iN(N, minInt(iN)) == (1 << (N - 1)));
|
||||
|
||||
try expect(conv_uN(N, 0) == 0);
|
||||
try expect(conv_iN(N, 0) == 0);
|
||||
|
||||
try expect(conv_iN(N, -0) == 0);
|
||||
}
|
||||
|
||||
fn conv_iN(comptime N: usize, x: std.meta.Int(.signed, N)) std.meta.Int(.unsigned, N) {
|
||||
return @bitCast(std.meta.Int(.unsigned, N), x);
|
||||
}
|
||||
|
||||
fn conv_uN(comptime N: usize, x: std.meta.Int(.unsigned, N)) std.meta.Int(.signed, N) {
|
||||
return @bitCast(std.meta.Int(.signed, N), x);
|
||||
}
|
||||
|
||||
test "nested bitcast" {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user