Skip to content

Commit 8c266a1

Browse files
committed
Optimize empty compound/list
1 parent 5bd83e5 commit 8c266a1

5 files changed

Lines changed: 62 additions & 2 deletions

File tree

dfu/src/main/java/org/enginehub/linbus/dfu/LinOps.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public LinTag<?> emptyList() {
8686

8787
@Override
8888
public LinTag<?> emptyMap() {
89-
return LinCompoundTag.builder().build();
89+
return LinCompoundTag.empty();
9090
}
9191

9292
@Override

tree/src/main/java/org/enginehub/linbus/tree/LinCompoundTag.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,21 @@ public final class LinCompoundTag extends LinTag<Map<String, ? extends LinTag<?>
5454
* @return the tag
5555
*/
5656
public static LinCompoundTag of(Map<String, ? extends LinTag<?>> value) {
57+
if (value.isEmpty()) {
58+
return EMPTY;
59+
}
5760
return new LinCompoundTag(copyImmutable(value), true);
5861
}
5962

63+
private static final LinCompoundTag EMPTY = new LinCompoundTag(Map.of(), false);
64+
65+
/**
66+
* {@return an empty compound tag}
67+
*/
68+
public static LinCompoundTag empty() {
69+
return EMPTY;
70+
}
71+
6072
/**
6173
* Creates a new builder.
6274
*
@@ -257,6 +269,9 @@ public Builder putString(String name, String value) {
257269
* @return the built tag
258270
*/
259271
public LinCompoundTag build() {
272+
if (this.collector.isEmpty()) {
273+
return EMPTY;
274+
}
260275
return new LinCompoundTag(copyImmutable(this.collector), false);
261276
}
262277
}

tree/src/main/java/org/enginehub/linbus/tree/LinListTag.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@
1818

1919
package org.enginehub.linbus.tree;
2020

21+
import org.enginehub.linbus.common.LinTagId;
2122
import org.enginehub.linbus.stream.LinStream;
2223
import org.enginehub.linbus.stream.internal.FlatteningLinStream;
2324
import org.enginehub.linbus.stream.internal.SurroundingLinStream;
2425
import org.enginehub.linbus.stream.token.LinToken;
2526

2627
import java.util.ArrayList;
28+
import java.util.Arrays;
2729
import java.util.Collection;
30+
import java.util.Comparator;
2831
import java.util.List;
2932
import java.util.Objects;
3033
import java.util.function.Function;
@@ -51,6 +54,9 @@ public final class LinListTag<T extends LinTag<?>> extends LinTag<List<T>> {
5154
public static <T extends LinTag<?>> LinListTag<T> of(
5255
LinTagType<T> elementType, List<T> value
5356
) {
57+
if (value.isEmpty()) {
58+
return empty(elementType);
59+
}
5460
for (T t : value) {
5561
if (t.type() != elementType) {
5662
throw new IllegalArgumentException("Element is not of type " + elementType.name() + " but "
@@ -60,6 +66,11 @@ public static <T extends LinTag<?>> LinListTag<T> of(
6066
return new LinListTag<>(elementType, List.copyOf(value));
6167
}
6268

69+
private static final List<LinListTag<?>> EMPTY_LISTS = Arrays.stream(LinTagId.values())
70+
.sorted(Comparator.comparingInt(LinTagId::id))
71+
.<LinListTag<?>>map(id -> new LinListTag<>(LinTagType.fromId(id), List.of()))
72+
.toList();
73+
6374
/**
6475
* Get an empty list of the given element type.
6576
*
@@ -68,7 +79,9 @@ public static <T extends LinTag<?>> LinListTag<T> of(
6879
* @return an empty list
6980
*/
7081
public static <T extends LinTag<?>> LinListTag<T> empty(LinTagType<T> elementType) {
71-
return builder(elementType).build();
82+
@SuppressWarnings("unchecked")
83+
LinListTag<T> empty = (LinListTag<T>) EMPTY_LISTS.get(elementType.id().id());
84+
return empty;
7285
}
7386

7487
/**
@@ -169,6 +182,9 @@ public Builder<T> set(int index, T tag) {
169182
* @return the built tag
170183
*/
171184
public LinListTag<T> build() {
185+
if (this.collector.isEmpty()) {
186+
return empty(this.elementType);
187+
}
172188
return new LinListTag<>(this.elementType, List.copyOf(this.collector));
173189
}
174190
}

tree/src/test/java/org/enginehub/linbus/tree/LinCompoundTagTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,22 @@ void roundTripBuilder() {
5050
assertThat(initial).isEqualTo(initial.toBuilder().build());
5151
}
5252

53+
@Test
54+
void emptyImplementation() {
55+
assertThat(LinCompoundTag.empty()).compoundValue().isEmpty();
56+
assertThat(LinCompoundTag.empty()).isSameInstanceAs(LinCompoundTag.empty());
57+
}
58+
59+
@Test
60+
void builderReturnsEmptySingleton() {
61+
assertThat(LinCompoundTag.builder().build()).isSameInstanceAs(LinCompoundTag.empty());
62+
}
63+
64+
@Test
65+
void ofEmptyReturnsEmptySingleton() {
66+
assertThat(LinCompoundTag.of(Map.of())).isSameInstanceAs(LinCompoundTag.empty());
67+
}
68+
5369
@Test
5470
void builderRemove() {
5571
var initial = LinCompoundTag.of(Map.of(

tree/src/test/java/org/enginehub/linbus/tree/LinListTagTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,19 @@ void emptyImplementation() {
9595
var empty = LinListTag.empty(LinTagType.stringTag());
9696
assertThat(empty).listValue().isEmpty();
9797
assertThat(empty.elementType()).isEqualTo(LinTagType.stringTag());
98+
assertThat(empty).isSameInstanceAs(LinListTag.empty(LinTagType.stringTag()));
99+
}
100+
101+
@Test
102+
void builderReturnsEmptySingleton() {
103+
assertThat(LinListTag.builder(LinTagType.stringTag()).build())
104+
.isSameInstanceAs(LinListTag.empty(LinTagType.stringTag()));
105+
}
106+
107+
@Test
108+
void ofEmptyReturnsEmptySingleton() {
109+
assertThat(LinListTag.of(LinTagType.stringTag(), List.of()))
110+
.isSameInstanceAs(LinListTag.empty(LinTagType.stringTag()));
98111
}
99112

100113
@Test

0 commit comments

Comments
 (0)