| ... | @@ -1298,6 +1298,76 @@ pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: Endian) | ... | @@ -1298,6 +1298,76 @@ pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: Endian) |
| 1298 | return result; | 1298 | return result; |
| 1299 | } | 1299 | } |
| 1300 | | 1300 | |
| | 1301 | /// Loads an integer from packed memory with provided bit_count, bit_offset, and signedness. |
| | 1302 | /// Asserts that T is large enough to store the read value. |
| | 1303 | /// |
| | 1304 | /// Example: |
| | 1305 | /// const T = packed struct(u16){ a: u3, b: u7, c: u6 }; |
| | 1306 | /// var st = T{ .a = 1, .b = 2, .c = 4 }; |
| | 1307 | /// const b_field = readVarPackedInt(u64, std.mem.asBytes(&st), @bitOffsetOf(T, "b"), 7, builtin.cpu.arch.endian(), .unsigned); |
| | 1308 | /// |
| | 1309 | pub fn readVarPackedInt( |
| | 1310 | comptime T: type, |
| | 1311 | bytes: []const u8, |
| | 1312 | bit_offset: usize, |
| | 1313 | bit_count: usize, |
| | 1314 | endian: std.builtin.Endian, |
| | 1315 | signedness: std.builtin.Signedness, |
| | 1316 | ) T { |
| | 1317 | const uN = std.meta.Int(.unsigned, @bitSizeOf(T)); |
| | 1318 | const iN = std.meta.Int(.signed, @bitSizeOf(T)); |
| | 1319 | const Log2N = std.math.Log2Int(T); |
| | 1320 | |
| | 1321 | const read_size = (bit_count + (bit_offset % 8) + 7) / 8; |
| | 1322 | const bit_shift = @intCast(u3, bit_offset % 8); |
| | 1323 | const pad = @intCast(Log2N, @bitSizeOf(T) - bit_count); |
| | 1324 | |
| | 1325 | const lowest_byte = switch (endian) { |
| | 1326 | .Big => bytes.len - (bit_offset / 8) - read_size, |
| | 1327 | .Little => bit_offset / 8, |
| | 1328 | }; |
| | 1329 | const read_bytes = bytes[lowest_byte..][0..read_size]; |
| | 1330 | |
| | 1331 | if (@bitSizeOf(T) <= 8) { |
| | 1332 | // These are the same shifts/masks we perform below, but adds `@truncate`/`@intCast` |
| | 1333 | // where needed since int is smaller than a byte. |
| | 1334 | const value = if (read_size == 1) b: { |
| | 1335 | break :b @truncate(uN, read_bytes[0] >> bit_shift); |
| | 1336 | } else b: { |
| | 1337 | const i: u1 = @boolToInt(endian == .Big); |
| | 1338 | const head = @truncate(uN, read_bytes[i] >> bit_shift); |
| | 1339 | const tail_shift = @intCast(Log2N, @as(u4, 8) - bit_shift); |
| | 1340 | const tail = @truncate(uN, read_bytes[1 - i]); |
| | 1341 | break :b (tail << tail_shift) | head; |
| | 1342 | }; |
| | 1343 | switch (signedness) { |
| | 1344 | .signed => return @intCast(T, (@bitCast(iN, value) << pad) >> pad), |
| | 1345 | .unsigned => return @intCast(T, (@bitCast(uN, value) << pad) >> pad), |
| | 1346 | } |
| | 1347 | } |
| | 1348 | |
| | 1349 | // Copy the value out (respecting endianness), accounting for bit_shift |
| | 1350 | var int: uN = 0; |
| | 1351 | switch (endian) { |
| | 1352 | .Big => { |
| | 1353 | for (read_bytes[0 .. read_size - 1]) |elem| { |
| | 1354 | int = elem | (int << 8); |
| | 1355 | } |
| | 1356 | int = (read_bytes[read_size - 1] >> bit_shift) | (int << (@as(u4, 8) - bit_shift)); |
| | 1357 | }, |
| | 1358 | .Little => { |
| | 1359 | int = read_bytes[0] >> bit_shift; |
| | 1360 | for (read_bytes[1..]) |elem, i| { |
| | 1361 | int |= (@as(uN, elem) << @intCast(Log2N, (8 * (i + 1) - bit_shift))); |
| | 1362 | } |
| | 1363 | }, |
| | 1364 | } |
| | 1365 | switch (signedness) { |
| | 1366 | .signed => return @intCast(T, (@bitCast(iN, int) << pad) >> pad), |
| | 1367 | .unsigned => return @intCast(T, (@bitCast(uN, int) << pad) >> pad), |
| | 1368 | } |
| | 1369 | } |
| | 1370 | |
| 1301 | /// Reads an integer from memory with bit count specified by T. | 1371 | /// Reads an integer from memory with bit count specified by T. |
| 1302 | /// The bit count of T must be evenly divisible by 8. | 1372 | /// The bit count of T must be evenly divisible by 8. |
| 1303 | /// This function cannot fail and cannot cause undefined behavior. | 1373 | /// This function cannot fail and cannot cause undefined behavior. |
| ... | @@ -1365,6 +1435,84 @@ pub fn readInt(comptime T: type, bytes: *const [@divExact(@typeInfo(T).Int.bits, | ... | @@ -1365,6 +1435,84 @@ pub fn readInt(comptime T: type, bytes: *const [@divExact(@typeInfo(T).Int.bits, |
| 1365 | } | 1435 | } |
| 1366 | } | 1436 | } |
| 1367 | | 1437 | |
| | 1438 | fn readPackedIntLittle(comptime T: type, bytes: []const u8, bit_offset: usize) T { |
| | 1439 | const uN = std.meta.Int(.unsigned, @bitSizeOf(T)); |
| | 1440 | const Log2N = std.math.Log2Int(T); |
| | 1441 | |
| | 1442 | const bit_count = @as(usize, @bitSizeOf(T)); |
| | 1443 | const bit_shift = @intCast(u3, bit_offset % 8); |
| | 1444 | |
| | 1445 | const load_size = (bit_count + 7) / 8; |
| | 1446 | const load_tail_bits = @intCast(u3, (load_size * 8) - bit_count); |
| | 1447 | const LoadInt = std.meta.Int(.unsigned, load_size * 8); |
| | 1448 | |
| | 1449 | if (bit_count == 0) |
| | 1450 | return 0; |
| | 1451 | |
| | 1452 | // Read by loading a LoadInt, and then follow it up with a 1-byte read |
| | 1453 | // of the tail if bit_offset pushed us over a byte boundary. |
| | 1454 | const read_bytes = bytes[bit_offset / 8 ..]; |
| | 1455 | const val = @truncate(uN, readIntLittle(LoadInt, read_bytes[0..load_size]) >> bit_shift); |
| | 1456 | if (bit_shift > load_tail_bits) { |
| | 1457 | const tail_bits = @intCast(Log2N, bit_shift - load_tail_bits); |
| | 1458 | const tail_byte = read_bytes[load_size]; |
| | 1459 | const tail_truncated = if (bit_count < 8) @truncate(uN, tail_byte) else @as(uN, tail_byte); |
| | 1460 | return @bitCast(T, val | (tail_truncated << (@truncate(Log2N, bit_count) -% tail_bits))); |
| | 1461 | } else return @bitCast(T, val); |
| | 1462 | } |
| | 1463 | |
| | 1464 | fn readPackedIntBig(comptime T: type, bytes: []const u8, bit_offset: usize) T { |
| | 1465 | const uN = std.meta.Int(.unsigned, @bitSizeOf(T)); |
| | 1466 | const Log2N = std.math.Log2Int(T); |
| | 1467 | |
| | 1468 | const bit_count = @as(usize, @bitSizeOf(T)); |
| | 1469 | const bit_shift = @intCast(u3, bit_offset % 8); |
| | 1470 | const byte_count = (@as(usize, bit_shift) + bit_count + 7) / 8; |
| | 1471 | |
| | 1472 | const load_size = (bit_count + 7) / 8; |
| | 1473 | const load_tail_bits = @intCast(u3, (load_size * 8) - bit_count); |
| | 1474 | const LoadInt = std.meta.Int(.unsigned, load_size * 8); |
| | 1475 | |
| | 1476 | if (bit_count == 0) |
| | 1477 | return 0; |
| | 1478 | |
| | 1479 | // Read by loading a LoadInt, and then follow it up with a 1-byte read |
| | 1480 | // of the tail if bit_offset pushed us over a byte boundary. |
| | 1481 | const end = bytes.len - (bit_offset / 8); |
| | 1482 | const read_bytes = bytes[(end - byte_count)..end]; |
| | 1483 | const val = @truncate(uN, readIntBig(LoadInt, bytes[(end - load_size)..end][0..load_size]) >> bit_shift); |
| | 1484 | if (bit_shift > load_tail_bits) { |
| | 1485 | const tail_bits = @intCast(Log2N, bit_shift - load_tail_bits); |
| | 1486 | const tail_byte = if (bit_count < 8) @truncate(uN, read_bytes[0]) else @as(uN, read_bytes[0]); |
| | 1487 | return @bitCast(T, val | (tail_byte << (@truncate(Log2N, bit_count) -% tail_bits))); |
| | 1488 | } else return @bitCast(T, val); |
| | 1489 | } |
| | 1490 | |
| | 1491 | pub const readPackedIntNative = switch (native_endian) { |
| | 1492 | .Little => readPackedIntLittle, |
| | 1493 | .Big => readPackedIntBig, |
| | 1494 | }; |
| | 1495 | |
| | 1496 | pub const readPackedIntForeign = switch (native_endian) { |
| | 1497 | .Little => readPackedIntBig, |
| | 1498 | .Big => readPackedIntLittle, |
| | 1499 | }; |
| | 1500 | |
| | 1501 | /// Loads an integer from packed memory. |
| | 1502 | /// Asserts that buffer contains at least bit_offset + @bitSizeOf(T) bits. |
| | 1503 | /// |
| | 1504 | /// Example: |
| | 1505 | /// const T = packed struct(u16){ a: u3, b: u7, c: u6 }; |
| | 1506 | /// var st = T{ .a = 1, .b = 2, .c = 4 }; |
| | 1507 | /// const b_field = readPackedInt(u7, std.mem.asBytes(&st), @bitOffsetOf(T, "b"), builtin.cpu.arch.endian()); |
| | 1508 | /// |
| | 1509 | pub fn readPackedInt(comptime T: type, bytes: []const u8, bit_offset: usize, endian: Endian) T { |
| | 1510 | switch (endian) { |
| | 1511 | .Little => return readPackedIntLittle(T, bytes, bit_offset), |
| | 1512 | .Big => return readPackedIntBig(T, bytes, bit_offset), |
| | 1513 | } |
| | 1514 | } |
| | 1515 | |
| 1368 | /// Asserts that bytes.len >= @typeInfo(T).Int.bits / 8. Reads the integer starting from index 0 | 1516 | /// Asserts that bytes.len >= @typeInfo(T).Int.bits / 8. Reads the integer starting from index 0 |
| 1369 | /// and ignores extra bytes. | 1517 | /// and ignores extra bytes. |
| 1370 | /// The bit count of T must be evenly divisible by 8. | 1518 | /// The bit count of T must be evenly divisible by 8. |
| ... | @@ -1447,6 +1595,100 @@ pub fn writeInt(comptime T: type, buffer: *[@divExact(@typeInfo(T).Int.bits, 8)] | ... | @@ -1447,6 +1595,100 @@ pub fn writeInt(comptime T: type, buffer: *[@divExact(@typeInfo(T).Int.bits, 8)] |
| 1447 | } | 1595 | } |
| 1448 | } | 1596 | } |
| 1449 | | 1597 | |
| | 1598 | pub fn writePackedIntLittle(comptime T: type, bytes: []u8, bit_offset: usize, value: T) void { |
| | 1599 | const uN = std.meta.Int(.unsigned, @bitSizeOf(T)); |
| | 1600 | const Log2N = std.math.Log2Int(T); |
| | 1601 | |
| | 1602 | const bit_count = @as(usize, @bitSizeOf(T)); |
| | 1603 | const bit_shift = @intCast(u3, bit_offset % 8); |
| | 1604 | |
| | 1605 | const store_size = (@bitSizeOf(T) + 7) / 8; |
| | 1606 | const store_tail_bits = @intCast(u3, (store_size * 8) - bit_count); |
| | 1607 | const StoreInt = std.meta.Int(.unsigned, store_size * 8); |
| | 1608 | |
| | 1609 | if (bit_count == 0) |
| | 1610 | return; |
| | 1611 | |
| | 1612 | // Write by storing a StoreInt, and then follow it up with a 1-byte tail |
| | 1613 | // if bit_offset pushed us over a byte boundary. |
| | 1614 | const write_bytes = bytes[bit_offset / 8 ..]; |
| | 1615 | const head = write_bytes[0] & ((@as(u8, 1) << bit_shift) - 1); |
| | 1616 | |
| | 1617 | var write_value = (@as(StoreInt, @bitCast(uN, value)) << bit_shift) | @intCast(StoreInt, head); |
| | 1618 | if (bit_shift > store_tail_bits) { |
| | 1619 | const tail_len = @intCast(Log2N, bit_shift - store_tail_bits); |
| | 1620 | write_bytes[store_size] &= ~((@as(u8, 1) << @intCast(u3, tail_len)) - 1); |
| | 1621 | write_bytes[store_size] |= @intCast(u8, (@bitCast(uN, value) >> (@truncate(Log2N, bit_count) -% tail_len))); |
| | 1622 | } else if (bit_shift < store_tail_bits) { |
| | 1623 | const tail_len = store_tail_bits - bit_shift; |
| | 1624 | const tail = write_bytes[store_size - 1] & (@as(u8, 0xfe) << (7 - tail_len)); |
| | 1625 | write_value |= @as(StoreInt, tail) << (8 * (store_size - 1)); |
| | 1626 | } |
| | 1627 | |
| | 1628 | writeIntLittle(StoreInt, write_bytes[0..store_size], write_value); |
| | 1629 | } |
| | 1630 | |
| | 1631 | pub fn writePackedIntBig(comptime T: type, bytes: []u8, bit_offset: usize, value: T) void { |
| | 1632 | const uN = std.meta.Int(.unsigned, @bitSizeOf(T)); |
| | 1633 | const Log2N = std.math.Log2Int(T); |
| | 1634 | |
| | 1635 | const bit_count = @as(usize, @bitSizeOf(T)); |
| | 1636 | const bit_shift = @intCast(u3, bit_offset % 8); |
| | 1637 | const byte_count = (bit_shift + bit_count + 7) / 8; |
| | 1638 | |
| | 1639 | const store_size = (@bitSizeOf(T) + 7) / 8; |
| | 1640 | const store_tail_bits = @intCast(u3, (store_size * 8) - bit_count); |
| | 1641 | const StoreInt = std.meta.Int(.unsigned, store_size * 8); |
| | 1642 | |
| | 1643 | if (bit_count == 0) |
| | 1644 | return; |
| | 1645 | |
| | 1646 | // Write by storing a StoreInt, and then follow it up with a 1-byte tail |
| | 1647 | // if bit_offset pushed us over a byte boundary. |
| | 1648 | const end = bytes.len - (bit_offset / 8); |
| | 1649 | const write_bytes = bytes[(end - byte_count)..end]; |
| | 1650 | const head = write_bytes[byte_count - 1] & ((@as(u8, 1) << bit_shift) - 1); |
| | 1651 | |
| | 1652 | var write_value = (@as(StoreInt, @bitCast(uN, value)) << bit_shift) | @intCast(StoreInt, head); |
| | 1653 | if (bit_shift > store_tail_bits) { |
| | 1654 | const tail_len = @intCast(Log2N, bit_shift - store_tail_bits); |
| | 1655 | write_bytes[0] &= ~((@as(u8, 1) << @intCast(u3, tail_len)) - 1); |
| | 1656 | write_bytes[0] |= @intCast(u8, (@bitCast(uN, value) >> (@truncate(Log2N, bit_count) -% tail_len))); |
| | 1657 | } else if (bit_shift < store_tail_bits) { |
| | 1658 | const tail_len = store_tail_bits - bit_shift; |
| | 1659 | const tail = write_bytes[0] & (@as(u8, 0xfe) << (7 - tail_len)); |
| | 1660 | write_value |= @as(StoreInt, tail) << (8 * (store_size - 1)); |
| | 1661 | } |
| | 1662 | |
| | 1663 | writeIntBig(StoreInt, write_bytes[(byte_count - store_size)..][0..store_size], write_value); |
| | 1664 | } |
| | 1665 | |
| | 1666 | pub const writePackedIntNative = switch (native_endian) { |
| | 1667 | .Little => writePackedIntLittle, |
| | 1668 | .Big => writePackedIntBig, |
| | 1669 | }; |
| | 1670 | |
| | 1671 | pub const writePackedIntForeign = switch (native_endian) { |
| | 1672 | .Little => writePackedIntBig, |
| | 1673 | .Big => writePackedIntLittle, |
| | 1674 | }; |
| | 1675 | |
| | 1676 | /// Stores an integer to packed memory. |
| | 1677 | /// Asserts that buffer contains at least bit_offset + @bitSizeOf(T) bits. |
| | 1678 | /// |
| | 1679 | /// Example: |
| | 1680 | /// const T = packed struct(u16){ a: u3, b: u7, c: u6 }; |
| | 1681 | /// var st = T{ .a = 1, .b = 2, .c = 4 }; |
| | 1682 | /// // st.b = 0x7f; |
| | 1683 | /// writePackedInt(u7, std.mem.asBytes(&st), @bitOffsetOf(T, "b"), 0x7f, builtin.cpu.arch.endian()); |
| | 1684 | /// |
| | 1685 | pub fn writePackedInt(comptime T: type, bytes: []u8, bit_offset: usize, value: T, endian: Endian) void { |
| | 1686 | switch (endian) { |
| | 1687 | .Little => writePackedIntLittle(T, bytes, bit_offset, value), |
| | 1688 | .Big => writePackedIntBig(T, bytes, bit_offset, value), |
| | 1689 | } |
| | 1690 | } |
| | 1691 | |
| 1450 | /// Writes a twos-complement little-endian integer to memory. | 1692 | /// Writes a twos-complement little-endian integer to memory. |
| 1451 | /// Asserts that buf.len >= @typeInfo(T).Int.bits / 8. | 1693 | /// Asserts that buf.len >= @typeInfo(T).Int.bits / 8. |
| 1452 | /// The bit count of T must be divisible by 8. | 1694 | /// The bit count of T must be divisible by 8. |
| ... | @@ -1523,6 +1765,69 @@ pub fn writeIntSlice(comptime T: type, buffer: []u8, value: T, endian: Endian) v | ... | @@ -1523,6 +1765,69 @@ pub fn writeIntSlice(comptime T: type, buffer: []u8, value: T, endian: Endian) v |
| 1523 | }; | 1765 | }; |
| 1524 | } | 1766 | } |
| 1525 | | 1767 | |
| | 1768 | /// Stores an integer to packed memory with provided bit_count, bit_offset, and signedness. |
| | 1769 | /// If negative, the written value is sign-extended. |
| | 1770 | /// |
| | 1771 | /// Example: |
| | 1772 | /// const T = packed struct(u16){ a: u3, b: u7, c: u6 }; |
| | 1773 | /// var st = T{ .a = 1, .b = 2, .c = 4 }; |
| | 1774 | /// // st.b = 0x7f; |
| | 1775 | /// var value: u64 = 0x7f; |
| | 1776 | /// writeVarPackedInt(std.mem.asBytes(&st), @bitOffsetOf(T, "b"), 7, value, builtin.cpu.arch.endian()); |
| | 1777 | /// |
| | 1778 | pub fn writeVarPackedInt(bytes: []u8, bit_offset: usize, bit_count: usize, value: anytype, endian: std.builtin.Endian) void { |
| | 1779 | const T = @TypeOf(value); |
| | 1780 | const uN = std.meta.Int(.unsigned, @bitSizeOf(T)); |
| | 1781 | const Log2N = std.math.Log2Int(T); |
| | 1782 | |
| | 1783 | const bit_shift = @intCast(u3, bit_offset % 8); |
| | 1784 | const write_size = (bit_count + bit_shift + 7) / 8; |
| | 1785 | const lowest_byte = switch (endian) { |
| | 1786 | .Big => bytes.len - (bit_offset / 8) - write_size, |
| | 1787 | .Little => bit_offset / 8, |
| | 1788 | }; |
| | 1789 | const write_bytes = bytes[lowest_byte..][0..write_size]; |
| | 1790 | |
| | 1791 | if (write_size == 1) { |
| | 1792 | // Single byte writes are handled specially, since we need to mask bits |
| | 1793 | // on both ends of the byte. |
| | 1794 | const mask = (@as(u8, 0xff) >> @intCast(u3, 8 - bit_count)); |
| | 1795 | const new_bits = @intCast(u8, @bitCast(uN, value) & mask) << bit_shift; |
| | 1796 | write_bytes[0] = (write_bytes[0] & ~(mask << bit_shift)) | new_bits; |
| | 1797 | return; |
| | 1798 | } |
| | 1799 | |
| | 1800 | var remaining: T = value; |
| | 1801 | |
| | 1802 | // Iterate bytes forward for Little-endian, backward for Big-endian |
| | 1803 | const delta: i2 = if (endian == .Big) -1 else 1; |
| | 1804 | const start = if (endian == .Big) @intCast(isize, write_bytes.len - 1) else 0; |
| | 1805 | |
| | 1806 | var i: isize = start; // isize for signed index arithmetic |
| | 1807 | |
| | 1808 | // Write first byte, using a mask to protects bits preceding bit_offset |
| | 1809 | const head_mask = @as(u8, 0xff) >> bit_shift; |
| | 1810 | write_bytes[@intCast(usize, i)] &= ~(head_mask << bit_shift); |
| | 1811 | write_bytes[@intCast(usize, i)] |= @intCast(u8, @bitCast(uN, remaining) & head_mask) << bit_shift; |
| | 1812 | remaining >>= @intCast(Log2N, @as(u4, 8) - bit_shift); |
| | 1813 | i += delta; |
| | 1814 | |
| | 1815 | // Write bytes[1..bytes.len - 1] |
| | 1816 | if (@bitSizeOf(T) > 8) { |
| | 1817 | const loop_end = start + delta * (@intCast(isize, write_size) - 1); |
| | 1818 | while (i != loop_end) : (i += delta) { |
| | 1819 | write_bytes[@intCast(usize, i)] = @truncate(u8, @bitCast(uN, remaining)); |
| | 1820 | remaining >>= 8; |
| | 1821 | } |
| | 1822 | } |
| | 1823 | |
| | 1824 | // Write last byte, using a mask to protect bits following bit_offset + bit_count |
| | 1825 | const following_bits = -%@truncate(u3, bit_shift + bit_count); |
| | 1826 | const tail_mask = (@as(u8, 0xff) << following_bits) >> following_bits; |
| | 1827 | write_bytes[@intCast(usize, i)] &= ~tail_mask; |
| | 1828 | write_bytes[@intCast(usize, i)] |= @intCast(u8, @bitCast(uN, remaining) & tail_mask); |
| | 1829 | } |
| | 1830 | |
| 1526 | test "writeIntBig and writeIntLittle" { | 1831 | test "writeIntBig and writeIntLittle" { |
| 1527 | var buf0: [0]u8 = undefined; | 1832 | var buf0: [0]u8 = undefined; |
| 1528 | var buf1: [1]u8 = undefined; | 1833 | var buf1: [1]u8 = undefined; |
| ... | @@ -3393,3 +3698,158 @@ pub fn alignInSlice(slice: anytype, comptime new_alignment: usize) ?AlignedSlice | ... | @@ -3393,3 +3698,158 @@ pub fn alignInSlice(slice: anytype, comptime new_alignment: usize) ?AlignedSlice |
| 3393 | const aligned_slice = bytesAsSlice(Element, aligned_bytes[0..slice_length_bytes]); | 3698 | const aligned_slice = bytesAsSlice(Element, aligned_bytes[0..slice_length_bytes]); |
| 3394 | return @alignCast(new_alignment, aligned_slice); | 3699 | return @alignCast(new_alignment, aligned_slice); |
| 3395 | } | 3700 | } |
| | 3701 | |
| | 3702 | test "read/write(Var)PackedInt" { |
| | 3703 | const foreign_endian: Endian = if (native_endian == .Big) .Little else .Big; |
| | 3704 | const expect = std.testing.expect; |
| | 3705 | var prng = std.rand.DefaultPrng.init(1234); |
| | 3706 | const random = prng.random(); |
| | 3707 | |
| | 3708 | @setEvalBranchQuota(10_000); |
| | 3709 | inline for ([_]type{ u8, u16, u32, u128 }) |BackingType| { |
| | 3710 | for ([_]BackingType{ |
| | 3711 | @as(BackingType, 0), // all zeros |
| | 3712 | -%@as(BackingType, 1), // all ones |
| | 3713 | random.int(BackingType), // random |
| | 3714 | random.int(BackingType), // random |
| | 3715 | random.int(BackingType), // random |
| | 3716 | }) |init_value| { |
| | 3717 | const uTs = [_]type{ u1, u3, u7, u8, u9, u10, u15, u16, u86 }; |
| | 3718 | const iTs = [_]type{ i1, i3, i7, i8, i9, i10, i15, i16, i86 }; |
| | 3719 | inline for (uTs ++ iTs) |PackedType| { |
| | 3720 | if (@bitSizeOf(PackedType) > @bitSizeOf(BackingType)) |
| | 3721 | continue; |
| | 3722 | |
| | 3723 | const iPackedType = std.meta.Int(.signed, @bitSizeOf(PackedType)); |
| | 3724 | const uPackedType = std.meta.Int(.unsigned, @bitSizeOf(PackedType)); |
| | 3725 | const Log2T = std.math.Log2Int(BackingType); |
| | 3726 | |
| | 3727 | const offset_at_end = @bitSizeOf(BackingType) - @bitSizeOf(PackedType); |
| | 3728 | for ([_]usize{ 0, 1, 7, 8, 9, 10, 15, 16, 86, offset_at_end }) |offset| { |
| | 3729 | if (offset > offset_at_end or offset == @bitSizeOf(BackingType)) |
| | 3730 | continue; |
| | 3731 | |
| | 3732 | for ([_]PackedType{ |
| | 3733 | ~@as(PackedType, 0), // all ones: -1 iN / maxInt uN |
| | 3734 | @as(PackedType, 0), // all zeros: 0 iN / 0 uN |
| | 3735 | @bitCast(PackedType, @as(iPackedType, math.maxInt(iPackedType))), // maxInt iN |
| | 3736 | @bitCast(PackedType, @as(iPackedType, math.minInt(iPackedType))), // maxInt iN |
| | 3737 | random.int(PackedType), // random |
| | 3738 | random.int(PackedType), // random |
| | 3739 | }) |write_value| { |
| | 3740 | { // Fixed-size Read/Write (Native-endian) |
| | 3741 | |
| | 3742 | // Initialize Value |
| | 3743 | var value: BackingType = init_value; |
| | 3744 | |
| | 3745 | // Read |
| | 3746 | const read_value1 = readPackedInt(PackedType, asBytes(&value), offset, native_endian); |
| | 3747 | try expect(read_value1 == @bitCast(PackedType, @truncate(uPackedType, value >> @intCast(Log2T, offset)))); |
| | 3748 | |
| | 3749 | // Write |
| | 3750 | writePackedInt(PackedType, asBytes(&value), offset, write_value, native_endian); |
| | 3751 | try expect(write_value == @bitCast(PackedType, @truncate(uPackedType, value >> @intCast(Log2T, offset)))); |
| | 3752 | |
| | 3753 | // Read again |
| | 3754 | const read_value2 = readPackedInt(PackedType, asBytes(&value), offset, native_endian); |
| | 3755 | try expect(read_value2 == write_value); |
| | 3756 | |
| | 3757 | // Verify bits outside of the target integer are unmodified |
| | 3758 | const diff_bits = init_value ^ value; |
| | 3759 | if (offset != offset_at_end) |
| | 3760 | try expect(diff_bits >> @intCast(Log2T, offset + @bitSizeOf(PackedType)) == 0); |
| | 3761 | if (offset != 0) |
| | 3762 | try expect(diff_bits << @intCast(Log2T, @bitSizeOf(BackingType) - offset) == 0); |
| | 3763 | } |
| | 3764 | |
| | 3765 | { // Fixed-size Read/Write (Foreign-endian) |
| | 3766 | |
| | 3767 | // Initialize Value |
| | 3768 | var value: BackingType = @byteSwap(init_value); |
| | 3769 | |
| | 3770 | // Read |
| | 3771 | const read_value1 = readPackedInt(PackedType, asBytes(&value), offset, foreign_endian); |
| | 3772 | try expect(read_value1 == @bitCast(PackedType, @truncate(uPackedType, @byteSwap(value) >> @intCast(Log2T, offset)))); |
| | 3773 | |
| | 3774 | // Write |
| | 3775 | writePackedInt(PackedType, asBytes(&value), offset, write_value, foreign_endian); |
| | 3776 | try expect(write_value == @bitCast(PackedType, @truncate(uPackedType, @byteSwap(value) >> @intCast(Log2T, offset)))); |
| | 3777 | |
| | 3778 | // Read again |
| | 3779 | const read_value2 = readPackedInt(PackedType, asBytes(&value), offset, foreign_endian); |
| | 3780 | try expect(read_value2 == write_value); |
| | 3781 | |
| | 3782 | // Verify bits outside of the target integer are unmodified |
| | 3783 | const diff_bits = init_value ^ @byteSwap(value); |
| | 3784 | if (offset != offset_at_end) |
| | 3785 | try expect(diff_bits >> @intCast(Log2T, offset + @bitSizeOf(PackedType)) == 0); |
| | 3786 | if (offset != 0) |
| | 3787 | try expect(diff_bits << @intCast(Log2T, @bitSizeOf(BackingType) - offset) == 0); |
| | 3788 | } |
| | 3789 | |
| | 3790 | const signedness = @typeInfo(PackedType).Int.signedness; |
| | 3791 | const NextPowerOfTwoInt = std.meta.Int(signedness, comptime try std.math.ceilPowerOfTwo(u16, @bitSizeOf(PackedType))); |
| | 3792 | const ui64 = std.meta.Int(signedness, 64); |
| | 3793 | inline for ([_]type{ PackedType, NextPowerOfTwoInt, ui64 }) |U| { |
| | 3794 | { // Variable-size Read/Write (Native-endian) |
| | 3795 | |
| | 3796 | if (@bitSizeOf(U) < @bitSizeOf(PackedType)) |
| | 3797 | continue; |
| | 3798 | |
| | 3799 | // Initialize Value |
| | 3800 | var value: BackingType = init_value; |
| | 3801 | |
| | 3802 | // Read |
| | 3803 | const read_value1 = readVarPackedInt(U, asBytes(&value), offset, @bitSizeOf(PackedType), native_endian, signedness); |
| | 3804 | try expect(read_value1 == @bitCast(PackedType, @truncate(uPackedType, value >> @intCast(Log2T, offset)))); |
| | 3805 | |
| | 3806 | // Write |
| | 3807 | writeVarPackedInt(asBytes(&value), offset, @bitSizeOf(PackedType), @as(U, write_value), native_endian); |
| | 3808 | try expect(write_value == @bitCast(PackedType, @truncate(uPackedType, value >> @intCast(Log2T, offset)))); |
| | 3809 | |
| | 3810 | // Read again |
| | 3811 | const read_value2 = readVarPackedInt(U, asBytes(&value), offset, @bitSizeOf(PackedType), native_endian, signedness); |
| | 3812 | try expect(read_value2 == write_value); |
| | 3813 | |
| | 3814 | // Verify bits outside of the target integer are unmodified |
| | 3815 | const diff_bits = init_value ^ value; |
| | 3816 | if (offset != offset_at_end) |
| | 3817 | try expect(diff_bits >> @intCast(Log2T, offset + @bitSizeOf(PackedType)) == 0); |
| | 3818 | if (offset != 0) |
| | 3819 | try expect(diff_bits << @intCast(Log2T, @bitSizeOf(BackingType) - offset) == 0); |
| | 3820 | } |
| | 3821 | |
| | 3822 | { // Variable-size Read/Write (Foreign-endian) |
| | 3823 | |
| | 3824 | if (@bitSizeOf(U) < @bitSizeOf(PackedType)) |
| | 3825 | continue; |
| | 3826 | |
| | 3827 | // Initialize Value |
| | 3828 | var value: BackingType = @byteSwap(init_value); |
| | 3829 | |
| | 3830 | // Read |
| | 3831 | const read_value1 = readVarPackedInt(U, asBytes(&value), offset, @bitSizeOf(PackedType), foreign_endian, signedness); |
| | 3832 | try expect(read_value1 == @bitCast(PackedType, @truncate(uPackedType, @byteSwap(value) >> @intCast(Log2T, offset)))); |
| | 3833 | |
| | 3834 | // Write |
| | 3835 | writeVarPackedInt(asBytes(&value), offset, @bitSizeOf(PackedType), @as(U, write_value), foreign_endian); |
| | 3836 | try expect(write_value == @bitCast(PackedType, @truncate(uPackedType, @byteSwap(value) >> @intCast(Log2T, offset)))); |
| | 3837 | |
| | 3838 | // Read again |
| | 3839 | const read_value2 = readVarPackedInt(U, asBytes(&value), offset, @bitSizeOf(PackedType), foreign_endian, signedness); |
| | 3840 | try expect(read_value2 == write_value); |
| | 3841 | |
| | 3842 | // Verify bits outside of the target integer are unmodified |
| | 3843 | const diff_bits = init_value ^ @byteSwap(value); |
| | 3844 | if (offset != offset_at_end) |
| | 3845 | try expect(diff_bits >> @intCast(Log2T, offset + @bitSizeOf(PackedType)) == 0); |
| | 3846 | if (offset != 0) |
| | 3847 | try expect(diff_bits << @intCast(Log2T, @bitSizeOf(BackingType) - offset) == 0); |
| | 3848 | } |
| | 3849 | } |
| | 3850 | } |
| | 3851 | } |
| | 3852 | } |
| | 3853 | } |
| | 3854 | } |
| | 3855 | } |