Skip to content

Commit 8cedf1e

Browse files
authored
Merge pull request #27 from Querz/inconsistencies-fix
Fix inconsistencies
2 parents 74e37d4 + 66ce529 commit 8cedf1e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+489
-402
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ ListTag<FloatTag> fl = new ListTag<>(FloatTag.class);
4242
fl.add(new FloatTag(1.234f);
4343
fl.addFloat(5.678f);
4444
```
45+
46+
#### Nesting
47+
All methods serializing instances or deserializing data track the nesting levels to prevent circular references or malicious data which could, when deserialized, result in thousands of instances causing a denial of service.
48+
49+
These methods have a parameter for the maximum nesting depth they are allowed to traverse. A value of `0` means that only the object itself, but no nested object may be processed.
50+
51+
If an instance is nested further than allowed, a [MaxDepthReachedException](src/main/java/net/querz/nbt/MaxDepthReachedException.java) will be thrown. A negative maximum depth will cause an `IllegalArgumentException`.
52+
53+
Some methods do not provide a parameter to specify the maximum depth, but instead use `Tag.DEFAULT_MAX_DEPTH` (`512`) which is also the maximum used in Minecraft.
54+
4555
---
4656
### Utility
4757
There are several utility methods to make your life easier if you use this library.
@@ -124,3 +134,8 @@ To be able to use a custom tag with deserialization, a `Supplier` and the custom
124134
```java
125135
TagFactory.registerCustomTag(90, ObjectTag::new, ObjectTag.class);
126136
```
137+
138+
#### Nesting
139+
As mentioned before, serialization and deserialization methods are provided with a parameter indicating the maximum processing depth of the structure. This is not guaranteed when using custom tags, it is the responsibility of the creator of that custom tag to call `Tag#decrementMaxDepth(int)` to correctly update the nesting depth.
140+
141+
It is also highly encouraged to document the custom tag behaviour when it does so to make users aware of the possible exceptions thrown by `Tag#decrementMaxDepth(int)`.

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ apply plugin: 'jacoco'
99

1010
group = 'net.querz.nbt'
1111
archivesBaseName = 'nbt'
12-
version = '3.0'
12+
version = '4.0'
1313
sourceCompatibility = '1.8'
1414
targetCompatibility = '1.8'
1515
compileJava.options.encoding = 'UTF-8'

src/main/java/net/querz/nbt/ArrayTag.java

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
public abstract class ArrayTag<T> extends Tag<T> {
1111

1212
public ArrayTag(T value) {
13+
super(value);
1314
if (!value.getClass().isArray()) {
1415
throw new UnsupportedOperationException("type of array tag must be an array");
1516
}
16-
setValue(value);
1717
}
1818

1919
public int length() {
@@ -27,22 +27,14 @@ public T getValue() {
2727

2828
@Override
2929
public void setValue(T value) {
30-
super.setValue(checkNull(value));
30+
super.setValue(value);
3131
}
3232

3333
@Override
34-
public String valueToString(int depth) {
34+
public String valueToString(int maxDepth) {
3535
return arrayToString("", "");
3636
}
3737

38-
@Override
39-
public int compareTo(Tag<T> other) {
40-
if (!(other instanceof ArrayTag) || this.getClass() != other.getClass()) {
41-
throw new IllegalArgumentException("array types are incompatible");
42-
}
43-
return Integer.compare(Array.getLength(getValue()), Array.getLength(other.getValue()));
44-
}
45-
4638
protected String arrayToString(String prefix, String suffix) {
4739
StringBuilder sb = new StringBuilder("[").append(prefix).append("".equals(prefix) ? "" : ";");
4840
for (int i = 0; i < length(); i++) {

src/main/java/net/querz/nbt/ByteArrayTag.java

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,33 @@
55
import java.io.IOException;
66
import java.util.Arrays;
77

8-
public class ByteArrayTag extends ArrayTag<byte[]> {
8+
public class ByteArrayTag extends ArrayTag<byte[]> implements Comparable<ByteArrayTag> {
9+
10+
public static final byte[] ZERO_VALUE = new byte[0];
911

1012
public ByteArrayTag() {
11-
super(new byte[0]);
13+
super(ZERO_VALUE);
1214
}
1315

1416
public ByteArrayTag(byte[] value) {
1517
super(value);
1618
}
1719

1820
@Override
19-
protected byte[] getEmptyValue() {
20-
return new byte[0];
21-
}
22-
23-
@Override
24-
public void serializeValue(DataOutputStream dos, int depth) throws IOException {
21+
public void serializeValue(DataOutputStream dos, int maxDepth) throws IOException {
2522
dos.writeInt(length());
2623
dos.write(getValue());
2724
}
2825

2926
@Override
30-
public void deserializeValue(DataInputStream dis, int depth) throws IOException {
27+
public void deserializeValue(DataInputStream dis, int maxDepth) throws IOException {
3128
int length = dis.readInt();
3229
setValue(new byte[length]);
3330
dis.readFully(getValue());
3431
}
3532

3633
@Override
37-
public String valueToTagString(int depth) {
34+
public String valueToTagString(int maxDepth) {
3835
return arrayToString("B", "b");
3936
}
4037

@@ -48,6 +45,11 @@ public int hashCode() {
4845
return Arrays.hashCode(getValue());
4946
}
5047

48+
@Override
49+
public int compareTo(ByteArrayTag other) {
50+
return Integer.compare(length(), other.length());
51+
}
52+
5153
@Override
5254
public ByteArrayTag clone() {
5355
return new ByteArrayTag(Arrays.copyOf(getValue(), length()));

src/main/java/net/querz/nbt/ByteTag.java

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44
import java.io.DataOutputStream;
55
import java.io.IOException;
66

7-
public class ByteTag extends NumberTag<Byte> {
7+
public class ByteTag extends NumberTag<Byte> implements Comparable<ByteTag> {
88

9-
public ByteTag() {}
9+
public static final byte ZERO_VALUE = 0;
10+
11+
public ByteTag() {
12+
super(ZERO_VALUE);
13+
}
1014

1115
public ByteTag(byte value) {
1216
super(value);
@@ -16,11 +20,6 @@ public ByteTag(boolean value) {
1620
super((byte) (value ? 1 : 0));
1721
}
1822

19-
@Override
20-
protected Byte getEmptyValue() {
21-
return 0;
22-
}
23-
2423
public boolean asBoolean() {
2524
return getValue() > 0;
2625
}
@@ -30,17 +29,17 @@ public void setValue(byte value) {
3029
}
3130

3231
@Override
33-
public void serializeValue(DataOutputStream dos, int depth) throws IOException {
32+
public void serializeValue(DataOutputStream dos, int maxDepth) throws IOException {
3433
dos.writeByte(getValue());
3534
}
3635

3736
@Override
38-
public void deserializeValue(DataInputStream dis, int depth) throws IOException {
37+
public void deserializeValue(DataInputStream dis, int maxDepth) throws IOException {
3938
setValue(dis.readByte());
4039
}
4140

4241
@Override
43-
public String valueToTagString(int depth) {
42+
public String valueToTagString(int maxDepth) {
4443
return getValue() + "b";
4544
}
4645

@@ -49,6 +48,11 @@ public boolean equals(Object other) {
4948
return super.equals(other) && asByte() == ((ByteTag) other).asByte();
5049
}
5150

51+
@Override
52+
public int compareTo(ByteTag other) {
53+
return getValue().compareTo(other.getValue());
54+
}
55+
5256
@Override
5357
public ByteTag clone() {
5458
return new ByteTag(getValue());

src/main/java/net/querz/nbt/CompoundTag.java

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,21 @@
33
import java.io.DataInputStream;
44
import java.io.DataOutputStream;
55
import java.io.IOException;
6-
import java.util.*;
6+
import java.util.Collection;
7+
import java.util.HashMap;
8+
import java.util.Iterator;
9+
import java.util.Map;
10+
import java.util.Objects;
11+
import java.util.Set;
712
import java.util.function.BiConsumer;
813

9-
public class CompoundTag extends Tag<Map<String, Tag<?>>> implements Iterable<Map.Entry<String, Tag<?>>> {
14+
public class CompoundTag extends Tag<Map<String, Tag<?>>> implements Iterable<Map.Entry<String, Tag<?>>>, Comparable<CompoundTag> {
1015

11-
public CompoundTag() {}
16+
public CompoundTag() {
17+
super(createEmptyValue());
18+
}
1219

13-
@Override
14-
protected Map<String, Tag<?>> getEmptyValue() {
20+
private static Map<String, Tag<?>> createEmptyValue() {
1521
return new HashMap<>(8);
1622
}
1723

@@ -123,56 +129,56 @@ public boolean getBoolean(String key) {
123129

124130
public byte getByte(String key) {
125131
ByteTag t = getByteTag(key);
126-
return t == null ? new ByteTag().getEmptyValue() : t.asByte();
132+
return t == null ? ByteTag.ZERO_VALUE : t.asByte();
127133
}
128134

129135
public short getShort(String key) {
130136
ShortTag t = getShortTag(key);
131-
return t == null ? new ShortTag().getEmptyValue() : t.asShort();
137+
return t == null ? ShortTag.ZERO_VALUE : t.asShort();
132138
}
133139

134140
public int getInt(String key) {
135141
IntTag t = getIntTag(key);
136-
return t == null ? new IntTag().getEmptyValue() : t.asInt();
142+
return t == null ? IntTag.ZERO_VALUE : t.asInt();
137143
}
138144

139145
public long getLong(String key) {
140146
LongTag t = getLongTag(key);
141-
return t == null ? new LongTag().getEmptyValue() : t.asLong();
147+
return t == null ? LongTag.ZERO_VALUE : t.asLong();
142148
}
143149

144150
public float getFloat(String key) {
145151
FloatTag t = getFloatTag(key);
146-
return t == null ? new FloatTag().getEmptyValue() : t.asFloat();
152+
return t == null ? FloatTag.ZERO_VALUE : t.asFloat();
147153
}
148154

149155
public double getDouble(String key) {
150156
DoubleTag t = getDoubleTag(key);
151-
return t == null ? new DoubleTag().getEmptyValue() : t.asDouble();
157+
return t == null ? DoubleTag.ZERO_VALUE : t.asDouble();
152158
}
153159

154160
public String getString(String key) {
155161
StringTag t = getStringTag(key);
156-
return t == null ? new StringTag().getEmptyValue() : t.getValue();
162+
return t == null ? StringTag.ZERO_VALUE : t.getValue();
157163
}
158164

159165
public byte[] getByteArray(String key) {
160166
ByteArrayTag t = getByteArrayTag(key);
161-
return t == null ? new ByteArrayTag().getEmptyValue() : t.getValue();
167+
return t == null ? ByteArrayTag.ZERO_VALUE : t.getValue();
162168
}
163169

164170
public int[] getIntArray(String key) {
165171
IntArrayTag t = getIntArrayTag(key);
166-
return t == null ? new IntArrayTag().getEmptyValue() : t.getValue();
172+
return t == null ? IntArrayTag.ZERO_VALUE : t.getValue();
167173
}
168174

169175
public long[] getLongArray(String key) {
170176
LongArrayTag t = getLongArrayTag(key);
171-
return t == null ? new LongArrayTag().getEmptyValue() : t.getValue();
177+
return t == null ? LongArrayTag.ZERO_VALUE : t.getValue();
172178
}
173179

174180
public Tag<?> put(String key, Tag<?> tag) {
175-
return getValue().put(checkNull(key), checkNull(tag));
181+
return getValue().put(Objects.requireNonNull(key), Objects.requireNonNull(tag));
176182
}
177183

178184
public Tag<?> putBoolean(String key, boolean value) {
@@ -204,63 +210,62 @@ public Tag<?> putDouble(String key, double value) {
204210
}
205211

206212
public Tag<?> putString(String key, String value) {
207-
return put(key, new StringTag(checkNull(value)));
213+
return put(key, new StringTag(value));
208214
}
209215

210216
public Tag<?> putByteArray(String key, byte[] value) {
211-
return put(key, new ByteArrayTag(checkNull(value)));
217+
return put(key, new ByteArrayTag(value));
212218
}
213219

214220
public Tag<?> putIntArray(String key, int[] value) {
215-
return put(key, new IntArrayTag(checkNull(value)));
221+
return put(key, new IntArrayTag(value));
216222
}
217223

218224
public Tag<?> putLongArray(String key, long[] value) {
219-
return put(key, new LongArrayTag(checkNull(value)));
225+
return put(key, new LongArrayTag(value));
220226
}
221227

222228
@Override
223-
public void serializeValue(DataOutputStream dos, int depth) throws IOException {
229+
public void serializeValue(DataOutputStream dos, int maxDepth) throws IOException {
224230
for (Map.Entry<String, Tag<?>> e : getValue().entrySet()) {
225-
dos.writeByte(e.getValue().getID());
226-
dos.writeUTF(e.getKey());
227-
e.getValue().serializeValue(dos, incrementDepth(depth));
231+
e.getValue().serialize(dos, e.getKey(), decrementMaxDepth(maxDepth));
228232
}
229-
EndTag.INSTANCE.serialize(dos, depth);
233+
EndTag.INSTANCE.serialize(dos, maxDepth);
230234
}
231235

232236
@Override
233-
public void deserializeValue(DataInputStream dis, int depth) throws IOException {
237+
public void deserializeValue(DataInputStream dis, int maxDepth) throws IOException {
238+
clear();
234239
for (int id = dis.readByte() & 0xFF; id != 0; id = dis.readByte() & 0xFF) {
235240
Tag<?> tag = TagFactory.fromID(id);
236241
String name = dis.readUTF();
237-
tag.deserializeValue(dis, incrementDepth(depth));
242+
tag.deserializeValue(dis, decrementMaxDepth(maxDepth));
238243
put(name, tag);
239244
}
240245
}
241246

242247
@Override
243-
public String valueToString(int depth) {
248+
public String valueToString(int maxDepth) {
244249
StringBuilder sb = new StringBuilder("{");
245250
boolean first = true;
246251
for (Map.Entry<String, Tag<?>> e : getValue().entrySet()) {
247252
sb.append(first ? "" : ",")
248253
.append(escapeString(e.getKey(), false)).append(":")
249-
.append(e.getValue().toString(incrementDepth(depth)));
254+
.append(e.getValue().toString(decrementMaxDepth(maxDepth)));
250255
first = false;
251256
}
252257
sb.append("}");
253258
return sb.toString();
254259
}
255260

256261
@Override
257-
public String valueToTagString(int depth) {
262+
public String valueToTagString(int maxDepth) {
258263
StringBuilder sb = new StringBuilder("{");
259264
boolean first = true;
260265
for (Map.Entry<String, Tag<?>> e : getValue().entrySet()) {
261266
sb.append(first ? "" : ",")
262267
.append(escapeString(e.getKey(), true)).append(":")
263-
.append(e.getValue().valueToTagString(incrementDepth(depth)));
268+
.append(e.getValue().valueToTagString(decrementMaxDepth(maxDepth)));
264269
first = false;
265270
}
266271
sb.append("}");
@@ -285,10 +290,7 @@ public boolean equals(Object other) {
285290
}
286291

287292
@Override
288-
public int compareTo(Tag<Map<String, Tag<?>>> o) {
289-
if (!(o instanceof CompoundTag)) {
290-
return 0;
291-
}
293+
public int compareTo(CompoundTag o) {
292294
return Integer.compare(size(), o.getValue().size());
293295
}
294296

0 commit comments

Comments
 (0)