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