1+ package io .github .retrooper .packetevents .impl .netty .buffer ;
2+
3+ import io .netty .buffer .ByteBuf ;
4+
5+ public final class FastNettyUtils {
6+
7+ //Src: https://github.com/PaperMC/Velocity/blob/9cfcfcf2ed5712e792114a3ab824670e25e23526/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java#L82
8+ public static int readVarInt (final ByteBuf buf ) {
9+ if (buf .readableBytes () < 4 )
10+ return readVarIntSmallBuffer (buf );
11+
12+ // take the last three bytes and check if any of them have the high bit set
13+ final int wholeOrMore = buf .getIntLE (buf .readerIndex ());
14+ final int atStop = ~wholeOrMore & 0x808080 ;
15+ if (atStop == 0 ) throw new IllegalArgumentException ("VarInt too big" );
16+
17+ final int bitsToKeep = Integer .numberOfTrailingZeros (atStop ) + 1 ;
18+ buf .skipBytes (bitsToKeep >> 3 );
19+
20+ // https://github.com/netty/netty/pull/14050#issuecomment-2107750734
21+ int preservedBytes = wholeOrMore & (atStop ^ (atStop - 1 ));
22+
23+ // https://github.com/netty/netty/pull/14050#discussion_r1597896639
24+ preservedBytes = (preservedBytes & 0x007F007F ) | ((preservedBytes & 0x00007F00 ) >> 1 );
25+ preservedBytes = (preservedBytes & 0x00003FFF ) | ((preservedBytes & 0x3FFF0000 ) >> 2 );
26+ return preservedBytes ;
27+ }
28+
29+ private static int readVarIntSmallBuffer (ByteBuf buf ) {
30+ switch (buf .readableBytes ()) {
31+ case 3 :
32+ return readVarInt3Bytes (buf );
33+ case 2 :
34+ return readVarInt2Bytes (buf );
35+ case 1 : {
36+ byte val = buf .readByte ();
37+ //check if it has the continuation bit set
38+ if ((val & -128 ) != 0 ) throw new IllegalArgumentException ("VarInt too big for 1 byte" );
39+ return val ;
40+ }
41+ case 0 :
42+ return 0 ;//I guess 0? Or an Exception?
43+ default :
44+ throw new AssertionError ("how" );
45+ }
46+ }
47+
48+ private static int readVarInt3Bytes (final ByteBuf buf ) {
49+ // Read 3 bytes in little-endian order
50+ final int wholeOrMore = buf .getMediumLE (buf .readerIndex ()); // Reads 3 bytes as an int
51+ final int atStop = ~wholeOrMore & 0x808080 ; // Check for stop bits
52+
53+ // If no stop bits are found, throw an exception
54+ if (atStop == 0 ) throw new IllegalArgumentException ("VarInt too big for 3 bytes" );
55+
56+ // Find the position of the first stop bit
57+ final int bitsToKeep = Integer .numberOfTrailingZeros (atStop ) + 1 ;
58+ buf .skipBytes (bitsToKeep >> 3 ); // Skip the processed bytes
59+
60+ // Extract and preserve the valid bytes
61+ int preservedBytes = wholeOrMore & (atStop ^ (atStop - 1 ));
62+
63+ // Compact the 7-bit chunks
64+ preservedBytes = (preservedBytes & 0x007F007F ) | ((preservedBytes & 0x00007F00 ) >> 1 );
65+ preservedBytes = (preservedBytes & 0x00003FFF ) | ((preservedBytes & 0x3FFF0000 ) >> 2 );
66+
67+ return preservedBytes ;
68+ }
69+
70+ private static int readVarInt2Bytes (final ByteBuf buf ) {
71+ // Read 2 bytes in little-endian order
72+ final int wholeOrMore = buf .getShortLE (buf .readerIndex ()); // Reads 2 bytes as an integer
73+ final int atStop = ~wholeOrMore & 0x8080 ; // Identify stop bits in the two bytes
74+
75+ // If no stop bits are found, the VarInt is too large
76+ if (atStop == 0 ) throw new IllegalArgumentException ("VarInt too big for 2 bytes" );
77+
78+ // Find the first stop bit
79+ final int bitsToKeep = Integer .numberOfTrailingZeros (atStop ) + 1 ;
80+ buf .skipBytes (bitsToKeep >> 3 ); // Skip the number of processed bytes
81+
82+ // Extract and preserve the relevant 7-bit chunks
83+ int preservedBytes = wholeOrMore & (atStop ^ (atStop - 1 ));
84+
85+ // Compact the 7-bit chunks into a single integer
86+ preservedBytes = (preservedBytes & 0x007F ) | ((preservedBytes & 0x7F00 ) >> 1 );
87+
88+ return preservedBytes ;
89+ }
90+
91+ }
0 commit comments