| ... | @@ -1911,35 +1911,162 @@ pub fn writeSleb128(w: *Writer, value: anytype) Error!void { | ... | @@ -1911,35 +1911,162 @@ pub fn writeSleb128(w: *Writer, value: anytype) Error!void { |
| 1911 | | 1911 | |
| 1912 | /// Write a single integer as LEB128 to the given writer. | 1912 | /// Write a single integer as LEB128 to the given writer. |
| 1913 | pub fn writeLeb128(w: *Writer, value: anytype) Error!void { | 1913 | pub fn writeLeb128(w: *Writer, value: anytype) Error!void { |
| 1914 | const value_info = @typeInfo(@TypeOf(value)).int; | 1914 | const T = @TypeOf(value); |
| 1915 | try w.writeMultipleOf7Leb128(@as(@Int( | 1915 | const info = switch (@typeInfo(T)) { |
| 1916 | value_info.signedness, | 1916 | .int => |info| info, |
| 1917 | @max(std.mem.alignForwardAnyAlign(u16, value_info.bits, 7), 7), | 1917 | else => @compileError(@typeName(T) ++ " not supported"), |
| 1918 | ), value)); | 1918 | }; |
| 1919 | } | | |
| 1920 | | 1919 | |
| 1921 | fn writeMultipleOf7Leb128(w: *Writer, value: anytype) Error!void { | 1920 | const BoundInt = @Int(info.signedness, 7); |
| 1922 | const value_info = @typeInfo(@TypeOf(value)).int; | 1921 | if (info.bits <= 7 or (value >= std.math.minInt(BoundInt) and value <= std.math.maxInt(BoundInt))) { |
| 1923 | const Byte = packed struct(u8) { bits: u7, more: bool }; | 1922 | const Bits = @Int(info.signedness, 8); |
| 1924 | var bytes: [@divExact(value_info.bits, 7)]Byte = undefined; | 1923 | const byte = switch (info.signedness) { |
| 1925 | var remaining = value; | 1924 | .signed => @as(Bits, @intCast(value)) & 0x7F, |
| 1926 | for (&bytes, 1..) |*byte, len| { | 1925 | .unsigned => @as(Bits, @intCast(value)), |
| 1927 | const more = switch (value_info.signedness) { | | |
| 1928 | .signed => remaining >> 6 != remaining >> (value_info.bits - 1), | | |
| 1929 | .unsigned => remaining > std.math.maxInt(u7), | | |
| 1930 | }; | 1926 | }; |
| 1931 | byte.* = .{ | 1927 | try w.writeByte(@bitCast(byte)); |
| 1932 | .bits = @bitCast(@as( | 1928 | return; |
| 1933 | @Int(value_info.signedness, 7), | 1929 | } |
| 1934 | @truncate(remaining), | 1930 | |
| 1935 | )), | 1931 | const Byte = packed struct { bits: u7, more: bool }; |
| 1936 | .more = more, | 1932 | const Int = std.math.ByteAlignedInt(T); |
| | 1933 | |
| | 1934 | const max_bytes = @divFloor(info.bits - 1, 7) + 1; |
| | 1935 | |
| | 1936 | const sign_value = value >> (info.bits - 1); |
| | 1937 | var val: Int = value; |
| | 1938 | for (0..max_bytes) |_| { |
| | 1939 | const more = switch (info.signedness) { |
| | 1940 | .signed => val >> 6 != sign_value, |
| | 1941 | .unsigned => val > std.math.maxInt(u7), |
| 1937 | }; | 1942 | }; |
| 1938 | if (value_info.bits > 7) remaining >>= 7; | 1943 | |
| 1939 | if (!more) return w.writeAll(@ptrCast(bytes[0..len])); | 1944 | try w.writeByte(@bitCast(@as(Byte, .{ |
| | 1945 | .bits = @intCast(val & 0x7F), |
| | 1946 | .more = more, |
| | 1947 | }))); |
| | 1948 | |
| | 1949 | if (!more) return; |
| | 1950 | |
| | 1951 | val >>= 7; |
| 1940 | } else unreachable; | 1952 | } else unreachable; |
| 1941 | } | 1953 | } |
| 1942 | | 1954 | |
| | 1955 | test "serialize signed LEB128" { |
| | 1956 | // Small values |
| | 1957 | try testLeb128Encoding(i7, 9, "\x09"); |
| | 1958 | try testLeb128Encoding(i64, 125, "\xFD\x00"); |
| | 1959 | |
| | 1960 | try testLeb128Encoding(i7, -34, "\x5E"); |
| | 1961 | try testLeb128Encoding(i64, -3, "\x7D"); |
| | 1962 | |
| | 1963 | // Random values |
| | 1964 | try testLeb128Encoding(i16, 19373, "\xAD\x97\x01"); |
| | 1965 | try testLeb128Encoding(i32, 1628839242, "\xCA\xBA\xD8\x88\x06"); |
| | 1966 | try testLeb128Encoding(i64, 3789169920125966546, "\xD2\xB1\xD0\xD5\xF6\xBE\xF5\xCA\x34"); |
| | 1967 | try testLeb128Encoding(i128, 704622239050934257305893323522763588, "\xC4\xD6\x83\xC7\xE3\x91\x95\xC3\x96\x80\x8D\xA5\xF5\xDF\xA3\xDA\x87\x01"); |
| | 1968 | |
| | 1969 | try testLeb128Encoding(i16, -14558, "\xA2\x8E\x7F"); |
| | 1970 | try testLeb128Encoding(i32, -1702738165, "\x8B\x8E\x89\xD4\x79"); |
| | 1971 | try testLeb128Encoding(i64, -1709126996960612298, "\xB6\xE0\x87\xB1\xD3\xC1\xFD\xA3\x68"); |
| | 1972 | try testLeb128Encoding(i128, -113498719181566012704681230050325944039, "\x99\xD2\x80\xBC\xE6\x95\xBC\xC8\xDE\xB4\x9D\x81\x9F\xCA\xC6\xF8\x9C\xD5\x7E"); |
| | 1973 | |
| | 1974 | // {min,max} values |
| | 1975 | try testLeb128Encoding(i16, std.math.maxInt(i16), "\xFF\xFF\x01"); |
| | 1976 | try testLeb128Encoding(i32, std.math.maxInt(i32), "\xFF\xFF\xFF\xFF\x07"); |
| | 1977 | try testLeb128Encoding(i64, std.math.maxInt(i64), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00"); |
| | 1978 | try testLeb128Encoding(i128, std.math.maxInt(i128), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"); |
| | 1979 | |
| | 1980 | try testLeb128Encoding(i16, std.math.minInt(i16), "\x80\x80\x7E"); |
| | 1981 | try testLeb128Encoding(i32, std.math.minInt(i32), "\x80\x80\x80\x80\x78"); |
| | 1982 | try testLeb128Encoding(i64, std.math.minInt(i64), "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x7F"); |
| | 1983 | try testLeb128Encoding(i128, std.math.minInt(i128), "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x7E"); |
| | 1984 | |
| | 1985 | // Specific cases |
| | 1986 | try testLeb128Encoding(i0, 0, "\x00"); |
| | 1987 | try testLeb128Encoding(i8, 0, "\x00"); |
| | 1988 | |
| | 1989 | try testLeb128Encoding(i2, -1, "\x7F"); |
| | 1990 | try testLeb128Encoding(i8, -1, "\x7F"); |
| | 1991 | |
| | 1992 | try testLeb128Encoding(i2, 1, "\x01"); |
| | 1993 | try testLeb128Encoding(i8, 1, "\x01"); |
| | 1994 | |
| | 1995 | // Encode byte boundaries |
| | 1996 | try testLeb128Encoding(i7, std.math.maxInt(i7), "\x3F"); |
| | 1997 | try testLeb128Encoding(i8, std.math.maxInt(i7) + 1, "\xC0\x00"); |
| | 1998 | try testLeb128Encoding(i14, std.math.maxInt(i14), "\xFF\x3F"); |
| | 1999 | try testLeb128Encoding(i15, std.math.maxInt(i14) + 1, "\x80\xC0\x00"); |
| | 2000 | try testLeb128Encoding(i49, std.math.maxInt(i49), "\xFF\xFF\xFF\xFF\xFF\xFF\x3F"); |
| | 2001 | try testLeb128Encoding(i50, std.math.maxInt(i49) + 1, "\x80\x80\x80\x80\x80\x80\xC0\x00"); |
| | 2002 | try testLeb128Encoding(i56, std.math.maxInt(i56), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x3F"); |
| | 2003 | try testLeb128Encoding(i57, std.math.maxInt(i56) + 1, "\x80\x80\x80\x80\x80\x80\x80\xC0\x00"); |
| | 2004 | try testLeb128Encoding(i63, std.math.maxInt(i63), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x3F"); |
| | 2005 | try testLeb128Encoding(i64, std.math.maxInt(i63) + 1, "\x80\x80\x80\x80\x80\x80\x80\x80\xC0\x00"); |
| | 2006 | |
| | 2007 | try testLeb128Encoding(i7, std.math.minInt(i7), "\x40"); |
| | 2008 | try testLeb128Encoding(i8, std.math.minInt(i7) - 1, "\xBF\x7F"); |
| | 2009 | try testLeb128Encoding(i14, std.math.minInt(i14), "\x80\x40"); |
| | 2010 | try testLeb128Encoding(i15, std.math.minInt(i14) - 1, "\xFF\xBF\x7F"); |
| | 2011 | try testLeb128Encoding(i49, std.math.minInt(i49), "\x80\x80\x80\x80\x80\x80\x40"); |
| | 2012 | try testLeb128Encoding(i50, std.math.minInt(i49) - 1, "\xFF\xFF\xFF\xFF\xFF\xFF\xBF\x7F"); |
| | 2013 | try testLeb128Encoding(i56, std.math.minInt(i56), "\x80\x80\x80\x80\x80\x80\x80\x40"); |
| | 2014 | try testLeb128Encoding(i57, std.math.minInt(i56) - 1, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xBF\x7F"); |
| | 2015 | try testLeb128Encoding(i63, std.math.minInt(i63), "\x80\x80\x80\x80\x80\x80\x80\x80\x40"); |
| | 2016 | try testLeb128Encoding(i64, std.math.minInt(i63) - 1, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xBF\x7F"); |
| | 2017 | } |
| | 2018 | |
| | 2019 | test "serialize unsigned LEB128" { |
| | 2020 | // Small values |
| | 2021 | try testLeb128Encoding(u7, 12, "\x0C"); |
| | 2022 | try testLeb128Encoding(u64, 201, "\xC9\x01"); |
| | 2023 | |
| | 2024 | // Random values |
| | 2025 | try testLeb128Encoding(u8, 254, "\xFE\x01"); |
| | 2026 | try testLeb128Encoding(u16, 30241, "\xA1\xEC\x01"); |
| | 2027 | try testLeb128Encoding(u32, 2173531193, "\xB9\xE8\xB5\x8C\x08"); |
| | 2028 | try testLeb128Encoding(u64, 18321125691115744902, "\x86\xDD\xF2\x81\xF2\xD7\xED\xA0\xFE\x01"); |
| | 2029 | try testLeb128Encoding(u128, 122619209508942982841456325819614676193, "\xE1\x89\xF3\xD9\xE3\xAD\xEC\xF4\x98\x95\xF8\xBB\xD7\xB8\xF2\xCC\xBF\xB8\x01"); |
| | 2030 | |
| | 2031 | // Max values |
| | 2032 | try testLeb128Encoding(u8, std.math.maxInt(u8), "\xFF\x01"); |
| | 2033 | try testLeb128Encoding(u16, std.math.maxInt(u16), "\xFF\xFF\x03"); |
| | 2034 | try testLeb128Encoding(u32, std.math.maxInt(u32), "\xFF\xFF\xFF\xFF\x0F"); |
| | 2035 | try testLeb128Encoding(u64, std.math.maxInt(u64), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"); |
| | 2036 | try testLeb128Encoding(u128, std.math.maxInt(u128), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x03"); |
| | 2037 | |
| | 2038 | // Specific cases |
| | 2039 | try testLeb128Encoding(u0, 0, "\x00"); |
| | 2040 | try testLeb128Encoding(u1, 0, "\x00"); |
| | 2041 | try testLeb128Encoding(u8, 0, "\x00"); |
| | 2042 | |
| | 2043 | try testLeb128Encoding(u1, 1, "\x01"); |
| | 2044 | try testLeb128Encoding(u8, 1, "\x01"); |
| | 2045 | |
| | 2046 | // Encode byte boundaries |
| | 2047 | try testLeb128Encoding(u7, std.math.maxInt(u7), "\x7F"); |
| | 2048 | try testLeb128Encoding(u8, std.math.maxInt(u7) + 1, "\x80\x01"); |
| | 2049 | try testLeb128Encoding(u14, std.math.maxInt(u14), "\xFF\x7F"); |
| | 2050 | try testLeb128Encoding(u15, std.math.maxInt(u14) + 1, "\x80\x80\x01"); |
| | 2051 | try testLeb128Encoding(u49, std.math.maxInt(u49), "\xFF\xFF\xFF\xFF\xFF\xFF\x7F"); |
| | 2052 | try testLeb128Encoding(u50, std.math.maxInt(u49) + 1, "\x80\x80\x80\x80\x80\x80\x80\x01"); |
| | 2053 | try testLeb128Encoding(u56, std.math.maxInt(u56), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F"); |
| | 2054 | try testLeb128Encoding(u57, std.math.maxInt(u56) + 1, "\x80\x80\x80\x80\x80\x80\x80\x80\x01"); |
| | 2055 | try testLeb128Encoding(u63, std.math.maxInt(u63), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F"); |
| | 2056 | try testLeb128Encoding(u64, std.math.maxInt(u63) + 1, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01"); |
| | 2057 | } |
| | 2058 | |
| | 2059 | fn testLeb128Encoding(comptime T: type, value: T, encoding: []const u8) !void { |
| | 2060 | const info = @typeInfo(T).int; |
| | 2061 | const max_bytes = @divFloor(info.bits -| 1, 7) + 1; |
| | 2062 | var bytes: [max_bytes]u8 = undefined; |
| | 2063 | |
| | 2064 | var fw: Writer = .fixed(&bytes); |
| | 2065 | try writeLeb128(&fw, value); |
| | 2066 | |
| | 2067 | try std.testing.expectEqualSlices(u8, encoding, fw.buffered()); |
| | 2068 | } |
| | 2069 | |
| 1943 | test "printValue max_depth" { | 2070 | test "printValue max_depth" { |
| 1944 | const Vec2 = struct { | 2071 | const Vec2 = struct { |
| 1945 | const SelfType = @This(); | 2072 | const SelfType = @This(); |