Skip to content

Commit d509f33

Browse files
committed
Varint NBT based on mstefarov#23
Not sure why I had to disable zigzag
1 parent 2be4eaa commit d509f33

File tree

6 files changed

+432
-113
lines changed

6 files changed

+432
-113
lines changed

fNbt/NbtBinaryReader.cs

Lines changed: 156 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ internal sealed class NbtBinaryReader : BinaryReader {
1414
const int SeekBufferSize = 8*1024;
1515
readonly bool swapNeeded;
1616
readonly byte[] stringConversionBuffer = new byte[64];
17+
readonly bool varInt;
1718

1819

19-
public NbtBinaryReader([NotNull] Stream input, bool bigEndian)
20+
public NbtBinaryReader([NotNull] Stream input, bool bigEndian, bool varInt)
2021
: base(input) {
2122
swapNeeded = (BitConverter.IsLittleEndian == bigEndian);
23+
this.varInt = varInt;
2224
}
2325

2426

@@ -32,6 +34,20 @@ public NbtTagType ReadTagType() {
3234
return (NbtTagType)type;
3335
}
3436

37+
public int ReadVarInt()
38+
{
39+
return VarInt.ReadSInt32(BaseStream);
40+
}
41+
42+
public long ReadVarLong()
43+
{
44+
return VarInt.ReadSInt64(BaseStream);
45+
}
46+
47+
public int ReadLength()
48+
{
49+
return (int)VarInt.ReadUInt32(BaseStream);
50+
}
3551

3652
public override short ReadInt16() {
3753
if (swapNeeded) {
@@ -43,6 +59,8 @@ public override short ReadInt16() {
4359

4460

4561
public override int ReadInt32() {
62+
if (varInt)
63+
return ReadVarInt();
4664
if (swapNeeded) {
4765
return Swap(base.ReadInt32());
4866
} else {
@@ -52,6 +70,8 @@ public override int ReadInt32() {
5270

5371

5472
public override long ReadInt64() {
73+
if (varInt)
74+
return ReadVarLong();
5575
if (swapNeeded) {
5676
return Swap(base.ReadInt64());
5777
} else {
@@ -82,7 +102,7 @@ public override double ReadDouble() {
82102

83103

84104
public override string ReadString() {
85-
short length = ReadInt16();
105+
int length = varInt ? ReadVarInt() : ReadInt16();
86106
if (length < 0) {
87107
throw new NbtFormatException("Negative string length given!");
88108
}
@@ -138,7 +158,7 @@ public void Skip(int bytesToSkip) {
138158

139159

140160
public void SkipString() {
141-
short length = ReadInt16();
161+
int length = varInt ? ReadVarInt() : ReadInt16();
142162
if (length < 0) {
143163
throw new NbtFormatException("Negative string length given!");
144164
}
@@ -179,4 +199,137 @@ static long Swap(long v) {
179199
[CanBeNull]
180200
public TagSelector Selector { get; set; }
181201
}
202+
203+
internal static class VarInt
204+
{
205+
private static uint ReadRawVarInt32(Stream buf, int maxSize)
206+
{
207+
uint result = 0;
208+
int j = 0;
209+
int b0;
210+
211+
do
212+
{
213+
b0 = buf.ReadByte(); // -1 if EOS
214+
if (b0 < 0) throw new EndOfStreamException("Not enough bytes for VarInt");
215+
216+
result |= (uint)(b0 & 0x7f) << j++ * 7;
217+
218+
if (j > maxSize)
219+
{
220+
throw new OverflowException("VarInt too big");
221+
}
222+
} while ((b0 & 0x80) == 0x80);
223+
224+
return result;
225+
}
226+
227+
private static ulong ReadRawVarInt64(Stream buf, int maxSize)
228+
{
229+
ulong result = 0;
230+
int j = 0;
231+
int b0;
232+
233+
do
234+
{
235+
b0 = buf.ReadByte(); // -1 if EOS
236+
if (b0 < 0) throw new EndOfStreamException("Not enough bytes for VarInt");
237+
238+
result |= (ulong)(b0 & 0x7f) << j++ * 7;
239+
240+
if (j > maxSize)
241+
{
242+
throw new OverflowException("VarInt too big");
243+
}
244+
} while ((b0 & 0x80) == 0x80);
245+
246+
return result;
247+
}
248+
249+
private static void WriteRawVarInt32(Stream buf, uint value)
250+
{
251+
while ((value & -128) != 0)
252+
{
253+
buf.WriteByte((byte)((value & 0x7F) | 0x80));
254+
value >>= 7;
255+
}
256+
257+
buf.WriteByte((byte)value);
258+
}
259+
260+
private static void WriteRawVarInt64(Stream buf, ulong value)
261+
{
262+
while ((value & 0xFFFFFFFFFFFFFF80) != 0)
263+
{
264+
buf.WriteByte((byte)((value & 0x7F) | 0x80));
265+
value >>= 7;
266+
}
267+
268+
buf.WriteByte((byte)value);
269+
}
270+
271+
// Int
272+
273+
public static void WriteInt32(Stream stream, int value)
274+
{
275+
WriteRawVarInt32(stream, (uint)value);
276+
}
277+
278+
public static int ReadInt32(Stream stream)
279+
{
280+
return (int)ReadRawVarInt32(stream, 5);
281+
}
282+
283+
public static void WriteSInt32(Stream stream, int value)
284+
{
285+
WriteRawVarInt32(stream, (uint)value);
286+
}
287+
288+
public static int ReadSInt32(Stream stream)
289+
{
290+
return (int)ReadRawVarInt32(stream, 5);
291+
}
292+
293+
public static void WriteUInt32(Stream stream, uint value)
294+
{
295+
WriteRawVarInt32(stream, value);
296+
}
297+
298+
public static uint ReadUInt32(Stream stream)
299+
{
300+
return ReadRawVarInt32(stream, 5);
301+
}
302+
303+
// Long
304+
305+
public static void WriteInt64(Stream stream, long value)
306+
{
307+
WriteRawVarInt64(stream, (ulong)value);
308+
}
309+
310+
public static long ReadInt64(Stream stream)
311+
{
312+
return (long)ReadRawVarInt64(stream, 10);
313+
}
314+
315+
public static void WriteSInt64(Stream stream, long value)
316+
{
317+
WriteRawVarInt64(stream, (ulong)value);
318+
}
319+
320+
public static long ReadSInt64(Stream stream)
321+
{
322+
return (long)ReadRawVarInt64(stream, 10);
323+
}
324+
325+
public static void WriteUInt64(Stream stream, ulong value)
326+
{
327+
WriteRawVarInt64(stream, value);
328+
}
329+
330+
public static ulong ReadUInt64(Stream stream)
331+
{
332+
return ReadRawVarInt64(stream, 10);
333+
}
334+
}
182335
}

fNbt/NbtBinaryWriter.cs

Lines changed: 74 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@ public Stream BaseStream {
3636

3737
// Swap is only needed if endianness of the runtime differs from desired NBT stream
3838
readonly bool swapNeeded;
39+
readonly bool varInt;
3940

4041

41-
public NbtBinaryWriter([NotNull] Stream input, bool bigEndian) {
42+
public NbtBinaryWriter([NotNull] Stream input, bool bigEndian, bool varInt) {
4243
if (input == null) throw new ArgumentNullException("input");
4344
if (!input.CanWrite) throw new ArgumentException("Given stream must be writable", "input");
4445
stream = input;
4546
swapNeeded = (BitConverter.IsLittleEndian == bigEndian);
47+
this.varInt = varInt;
4648
}
4749

4850

@@ -69,48 +71,83 @@ public void Write(short value) {
6971
stream.Write(buffer, 0, 2);
7072
}
7173

74+
public void WriteVarInt(int value)
75+
{
76+
// VarInt is LE by default
77+
VarInt.WriteSInt32(BaseStream, value);
78+
}
79+
80+
public void WriteVarLong(long value)
81+
{
82+
VarInt.WriteSInt64(BaseStream, value);
83+
}
84+
85+
public void WriteLength(int value)
86+
{
87+
VarInt.WriteUInt32(BaseStream, (uint)value);
88+
}
89+
90+
public static short SwapInt16(short v)
91+
{
92+
return (short)(((v & 0xff) << 8) | ((v >> 8) & 0xff));
93+
}
94+
95+
public static int SwapInt32(int v)
96+
{
97+
return (int)(((SwapInt16((short)v) & 0xffff) << 0x10) |
98+
(SwapInt16((short)(v >> 0x10)) & 0xffff));
99+
}
100+
72101

73102
public void Write(int value) {
74-
unchecked {
75-
if (swapNeeded) {
76-
buffer[0] = (byte)(value >> 24);
77-
buffer[1] = (byte)(value >> 16);
78-
buffer[2] = (byte)(value >> 8);
79-
buffer[3] = (byte)value;
80-
} else {
81-
buffer[0] = (byte)value;
82-
buffer[1] = (byte)(value >> 8);
83-
buffer[2] = (byte)(value >> 16);
84-
buffer[3] = (byte)(value >> 24);
103+
if (varInt)
104+
WriteVarInt(value);
105+
else {
106+
unchecked {
107+
if (swapNeeded) {
108+
buffer[0] = (byte)(value >> 24);
109+
buffer[1] = (byte)(value >> 16);
110+
buffer[2] = (byte)(value >> 8);
111+
buffer[3] = (byte)value;
112+
} else {
113+
buffer[0] = (byte)value;
114+
buffer[1] = (byte)(value >> 8);
115+
buffer[2] = (byte)(value >> 16);
116+
buffer[3] = (byte)(value >> 24);
117+
}
85118
}
119+
stream.Write(buffer, 0, 4);
86120
}
87-
stream.Write(buffer, 0, 4);
88121
}
89122

90123

91124
public void Write(long value) {
92-
unchecked {
93-
if (swapNeeded) {
94-
buffer[0] = (byte)(value >> 56);
95-
buffer[1] = (byte)(value >> 48);
96-
buffer[2] = (byte)(value >> 40);
97-
buffer[3] = (byte)(value >> 32);
98-
buffer[4] = (byte)(value >> 24);
99-
buffer[5] = (byte)(value >> 16);
100-
buffer[6] = (byte)(value >> 8);
101-
buffer[7] = (byte)value;
102-
} else {
103-
buffer[0] = (byte)value;
104-
buffer[1] = (byte)(value >> 8);
105-
buffer[2] = (byte)(value >> 16);
106-
buffer[3] = (byte)(value >> 24);
107-
buffer[4] = (byte)(value >> 32);
108-
buffer[5] = (byte)(value >> 40);
109-
buffer[6] = (byte)(value >> 48);
110-
buffer[7] = (byte)(value >> 56);
125+
if (varInt)
126+
WriteVarLong(value);
127+
else {
128+
unchecked {
129+
if (swapNeeded) {
130+
buffer[0] = (byte)(value >> 56);
131+
buffer[1] = (byte)(value >> 48);
132+
buffer[2] = (byte)(value >> 40);
133+
buffer[3] = (byte)(value >> 32);
134+
buffer[4] = (byte)(value >> 24);
135+
buffer[5] = (byte)(value >> 16);
136+
buffer[6] = (byte)(value >> 8);
137+
buffer[7] = (byte)value;
138+
} else {
139+
buffer[0] = (byte)value;
140+
buffer[1] = (byte)(value >> 8);
141+
buffer[2] = (byte)(value >> 16);
142+
buffer[3] = (byte)(value >> 24);
143+
buffer[4] = (byte)(value >> 32);
144+
buffer[5] = (byte)(value >> 40);
145+
buffer[6] = (byte)(value >> 48);
146+
buffer[7] = (byte)(value >> 56);
147+
}
111148
}
149+
stream.Write(buffer, 0, 8);
112150
}
113-
stream.Write(buffer, 0, 8);
114151
}
115152

116153

@@ -168,7 +205,10 @@ public void Write([NotNull] string value) {
168205

169206
// Write out string length (as number of bytes)
170207
int numBytes = Encoding.GetByteCount(value);
171-
Write((short)numBytes);
208+
if (varInt)
209+
WriteLength(numBytes);
210+
else
211+
Write((short)numBytes);
172212

173213
if (numBytes <= BufferSize) {
174214
// If the string fits entirely in the buffer, encode and write it as one

0 commit comments

Comments
 (0)