@@ -1245,46 +1245,59 @@ pub fn PackWithLimits(
12451245 return val ;
12461246 }
12471247
1248+ /// Precomputed lookup table for marker byte to Markers enum conversion
1249+ /// This eliminates branch misprediction overhead from switch statements
1250+ const MARKER_LOOKUP_TABLE : [256 ]Markers = blk : {
1251+ var table : [256 ]Markers = undefined ;
1252+ var i : usize = 0 ;
1253+ while (i < 256 ) : (i += 1 ) {
1254+ const byte : u8 = @intCast (i );
1255+ table [i ] = switch (byte ) {
1256+ 0x00... 0x7f = > .POSITIVE_FIXINT ,
1257+ 0x80... 0x8f = > .FIXMAP ,
1258+ 0x90... 0x9f = > .FIXARRAY ,
1259+ 0xa0... 0xbf = > .FIXSTR ,
1260+ 0xc0 = > .NIL ,
1261+ 0xc1 = > .NIL , // Reserved byte, treat as NIL
1262+ 0xc2 = > .FALSE ,
1263+ 0xc3 = > .TRUE ,
1264+ 0xc4 = > .BIN8 ,
1265+ 0xc5 = > .BIN16 ,
1266+ 0xc6 = > .BIN32 ,
1267+ 0xc7 = > .EXT8 ,
1268+ 0xc8 = > .EXT16 ,
1269+ 0xc9 = > .EXT32 ,
1270+ 0xca = > .FLOAT32 ,
1271+ 0xcb = > .FLOAT64 ,
1272+ 0xcc = > .UINT8 ,
1273+ 0xcd = > .UINT16 ,
1274+ 0xce = > .UINT32 ,
1275+ 0xcf = > .UINT64 ,
1276+ 0xd0 = > .INT8 ,
1277+ 0xd1 = > .INT16 ,
1278+ 0xd2 = > .INT32 ,
1279+ 0xd3 = > .INT64 ,
1280+ 0xd4 = > .FIXEXT1 ,
1281+ 0xd5 = > .FIXEXT2 ,
1282+ 0xd6 = > .FIXEXT4 ,
1283+ 0xd7 = > .FIXEXT8 ,
1284+ 0xd8 = > .FIXEXT16 ,
1285+ 0xd9 = > .STR8 ,
1286+ 0xda = > .STR16 ,
1287+ 0xdb = > .STR32 ,
1288+ 0xdc = > .ARRAY16 ,
1289+ 0xdd = > .ARRAY32 ,
1290+ 0xde = > .MAP16 ,
1291+ 0xdf = > .MAP32 ,
1292+ 0xe0... 0xff = > .NEGATIVE_FIXINT ,
1293+ };
1294+ }
1295+ break :blk table ;
1296+ };
1297+
1298+ /// Fast marker type lookup using precomputed table (O(1) with no branches)
12481299 inline fn markerU8To (_ : Self , marker_u8 : u8 ) Markers {
1249- return switch (marker_u8 ) {
1250- 0x00... 0x7f = > .POSITIVE_FIXINT ,
1251- 0x80... 0x8f = > .FIXMAP ,
1252- 0x90... 0x9f = > .FIXARRAY ,
1253- 0xa0... 0xbf = > .FIXSTR ,
1254- 0xc0 = > .NIL ,
1255- 0xc1 = > .NIL , // Reserved byte, treat as NIL
1256- 0xc2 = > .FALSE ,
1257- 0xc3 = > .TRUE ,
1258- 0xc4 = > .BIN8 ,
1259- 0xc5 = > .BIN16 ,
1260- 0xc6 = > .BIN32 ,
1261- 0xc7 = > .EXT8 ,
1262- 0xc8 = > .EXT16 ,
1263- 0xc9 = > .EXT32 ,
1264- 0xca = > .FLOAT32 ,
1265- 0xcb = > .FLOAT64 ,
1266- 0xcc = > .UINT8 ,
1267- 0xcd = > .UINT16 ,
1268- 0xce = > .UINT32 ,
1269- 0xcf = > .UINT64 ,
1270- 0xd0 = > .INT8 ,
1271- 0xd1 = > .INT16 ,
1272- 0xd2 = > .INT32 ,
1273- 0xd3 = > .INT64 ,
1274- 0xd4 = > .FIXEXT1 ,
1275- 0xd5 = > .FIXEXT2 ,
1276- 0xd6 = > .FIXEXT4 ,
1277- 0xd7 = > .FIXEXT8 ,
1278- 0xd8 = > .FIXEXT16 ,
1279- 0xd9 = > .STR8 ,
1280- 0xda = > .STR16 ,
1281- 0xdb = > .STR32 ,
1282- 0xdc = > .ARRAY16 ,
1283- 0xdd = > .ARRAY32 ,
1284- 0xde = > .MAP16 ,
1285- 0xdf = > .MAP32 ,
1286- 0xe0... 0xff = > .NEGATIVE_FIXINT ,
1287- };
1300+ return MARKER_LOOKUP_TABLE [marker_u8 ];
12881301 }
12891302
12901303 fn readTypeMarker (self : Self ) ! Markers {
@@ -1805,9 +1818,55 @@ pub fn PackWithLimits(
18051818
18061819 // ========== End of State Machine Helpers ==========
18071820
1821+ /// Fast path for simple types that don't require heap allocation or complex state management
1822+ inline fn readSimpleTypeFast (self : Self , marker : Markers , marker_u8 : u8 ) ! ? Payload {
1823+ return switch (marker ) {
1824+ .NIL = > Payload { .nil = void {} },
1825+ .TRUE = > Payload { .bool = true },
1826+ .FALSE = > Payload { .bool = false },
1827+
1828+ .POSITIVE_FIXINT = > Payload { .uint = marker_u8 },
1829+ .NEGATIVE_FIXINT = > Payload { .int = @as (i8 , @bitCast (marker_u8 )) },
1830+
1831+ .UINT8 = > Payload { .uint = try self .readV8Value () },
1832+ .UINT16 = > Payload { .uint = try self .readU16Value () },
1833+ .UINT32 = > Payload { .uint = try self .readU32Value () },
1834+ .UINT64 = > Payload { .uint = try self .readU64Value () },
1835+
1836+ .INT8 = > Payload { .int = try self .readI8Value () },
1837+ .INT16 = > Payload { .int = try self .readI16Value () },
1838+ .INT32 = > Payload { .int = try self .readI32Value () },
1839+ .INT64 = > Payload { .int = try self .readI64Value () },
1840+
1841+ .FLOAT32 = > Payload { .float = try self .readF32Value () },
1842+ .FLOAT64 = > Payload { .float = try self .readF64Value () },
1843+
1844+ // Note: FIXEXT4/FIXEXT8 could be timestamps, but we need to read ext_type first
1845+ // Since we can't "unread" in the stream, we handle all EXT types in the complex path
1846+ // to avoid consuming bytes that need to be re-processed.
1847+
1848+ else = > null , // Not a simple type, needs complex handling
1849+ };
1850+ }
1851+
18081852 /// read a payload, please use payload.free to free the memory
18091853 /// This is an iterative implementation that avoids stack overflow from deep nesting
18101854 pub fn read (self : Self , allocator : Allocator ) ! Payload {
1855+ // Fast path optimization: handle simple types without state machine overhead
1856+ const first_marker_u8 = try self .readTypeMarkerU8 ();
1857+ const first_marker = self .markerU8To (first_marker_u8 );
1858+
1859+ // Try fast path for simple types (no containers, no allocation needed)
1860+ if (try self .readSimpleTypeFast (first_marker , first_marker_u8 )) | simple_payload | {
1861+ return simple_payload ;
1862+ }
1863+
1864+ // Complex types: use full iterative parser
1865+ return self .readComplex (allocator , first_marker , first_marker_u8 );
1866+ }
1867+
1868+ /// Internal iterative parser for complex types (arrays, maps, strings, etc.)
1869+ fn readComplex (self : Self , allocator : Allocator , first_marker : Markers , first_marker_u8 : u8 ) ! Payload {
18111870 // Explicit stack for iterative parsing (on heap)
18121871 var parse_stack = if (current_zig .minor == 14 )
18131872 std .ArrayList (ParseState ).init (allocator )
@@ -1818,17 +1877,26 @@ pub fn PackWithLimits(
18181877 // Root payload to return
18191878 var root : ? Payload = null ;
18201879
1880+ // Start with the already-read first marker
1881+ var marker_u8 = first_marker_u8 ;
1882+ var marker = first_marker ;
1883+
18211884 // Main loop (replaces recursion)
1885+ // Process first marker directly, then read subsequent markers in loop
1886+ var is_first = true ;
18221887 while (true ) {
18231888 // Check depth limit
18241889 if (parse_stack .items .len >= parse_limits .max_depth ) {
18251890 cleanupParseStack (& parse_stack , allocator );
18261891 return MsgPackError .MaxDepthExceeded ;
18271892 }
18281893
1829- // Read type marker
1830- const marker_u8 = try self .readTypeMarkerU8 ();
1831- const marker = self .markerU8To (marker_u8 );
1894+ // Read next type marker (skip on first iteration)
1895+ if (! is_first ) {
1896+ marker_u8 = try self .readTypeMarkerU8 ();
1897+ marker = self .markerU8To (marker_u8 );
1898+ }
1899+ is_first = false ;
18321900
18331901 // Current payload being constructed
18341902 var current_payload : Payload = undefined ;
0 commit comments