-
Notifications
You must be signed in to change notification settings - Fork 465
/
SetMarshaller.java
163 lines (147 loc) · 6.11 KB
/
SetMarshaller.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*
* Copyright (C) 2015 higherfrequencytrading.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.chronicle.hash.serialization;
import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.wire.WireIn;
import net.openhft.chronicle.wire.WireOut;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;
import static net.openhft.chronicle.hash.serialization.StatefulCopyable.copyIfNeeded;
/**
* Marshaller of {@code Set<T>}. Uses {@link HashSet} (hence default element objects' equality and
* {@code hashCode()}) as the set implementation to deserialize into.
*
* <p>This marshaller supports multimap emulation on top of Chronicle Map, that is possible but
* inefficient. See <a href="https://github.com/OpenHFT/Chronicle-Map#chronicle-map-is-not">the
* README section</a>.
*
* <p>Usage: <pre>{@code
* SetMarshaller<String> valueMarshaller = new SetMarshaller<>(
* new StringBytesReader(), CharSequenceBytesWriter.INSTANCE);
* ChronicleMap<String, Set<String>> map = ChronicleMap
* .of(String.class, (Class<Set<String>>) (Class) Set.class)
* .entries(10_000)
* .averageKey("fruits")
* .valueMarshaller(valueMarshaller)
* .averageValue(ImmutableSet.of("apples", "bananas", "grapes"))
* .create();
* }</pre>
*
* <p>Look for pre-defined element marshallers in {@link
* net.openhft.chronicle.hash.serialization.impl} package. This package is not included into
* Javadocs, but present in Chronicle Map distribution. If there are no existing marshallers for
* your {@code Set} element type, define {@link BytesReader} and {@link BytesWriter} yourself.
*
* @param <T> the element type of serialized Sets
* @see ListMarshaller
* @see MapMarshaller
*/
public final class SetMarshaller<T>
implements BytesReader<Set<T>>, BytesWriter<Set<T>>, StatefulCopyable<SetMarshaller<T>> {
/**
* Returns a {@code SetMarshaller} which uses the given set elements' serializers.
*
* @param elementReader set elements' reader
* @param elementWriter set elements' writer
* @param <T> type of set elements
* @return a {@code SetMarshaller} which uses the given set elements' serializers
*/
public static <T> SetMarshaller<T> of(
BytesReader<T> elementReader, BytesWriter<? super T> elementWriter) {
return new SetMarshaller<>(elementReader, elementWriter);
}
/**
* Returns a {@code SetMarshaller} which uses the given marshaller as both reader and writer of
* set elements. Example: <pre><code>
* ChronicleMap
* .of(String.class,{@code (Class<Set<Integer>>)} ((Class) Set.class))
* .valueMarshaller(SetMarshaller.of(IntegerMarshaller.INSTANCE))
* ...</code></pre>
* @param elementMarshaller set elements' marshaller
* @param <T> type of set elements
* @param <M> type of set elements' marshaller
* @return a {@code SetMarshaller} which uses the given set elements' marshaller
*/
public static <T, M extends BytesReader<T> & BytesWriter<? super T>> SetMarshaller<T> of(
M elementMarshaller) {
return of(elementMarshaller, elementMarshaller);
}
// Config fields
private BytesReader<T> elementReader;
private BytesWriter<? super T> elementWriter;
/** Cache field */
private transient Deque<T> orderedElements;
/**
* Constructs a {@code SetMarshaller} with the given set elements' serializers.
*
* <p>Use static factory {@link #of(BytesReader, BytesWriter)} instead of this constructor
* directly.
*
* @param elementReader set elements' reader
* @param elementWriter set elements' writer
*/
public SetMarshaller(BytesReader<T> elementReader, BytesWriter<? super T> elementWriter) {
this.elementReader = elementReader;
this.elementWriter = elementWriter;
initTransients();
}
private void initTransients() {
orderedElements = new ArrayDeque<>();
}
@NotNull
@Override
public Set<T> read(Bytes in, @Nullable Set<T> using) {
int size = in.readInt();
if (using == null) {
using = new HashSet<>((int) (size / 0.75));
for (int i = 0; i < size; i++) {
using.add(elementReader.read(in, null));
}
} else {
orderedElements.addAll(using);
using.clear();
for (int i = 0; i < size; i++) {
using.add(elementReader.read(in, orderedElements.pollFirst()));
}
orderedElements.clear(); // for GC, avoid zombie object links
}
return using;
}
@Override
public void write(Bytes out, @NotNull Set<T> toWrite) {
out.writeInt(toWrite.size());
toWrite.forEach(e -> elementWriter.write(out, e));
}
@Override
public SetMarshaller<T> copy() {
return new SetMarshaller<>(copyIfNeeded(elementReader), copyIfNeeded(elementWriter));
}
@Override
public void readMarshallable(@NotNull WireIn wireIn) {
elementReader = wireIn.read(() -> "elementReader").typedMarshallable();
elementWriter = wireIn.read(() -> "elementWriter").typedMarshallable();
initTransients();
}
@Override
public void writeMarshallable(@NotNull WireOut wireOut) {
wireOut.write(() -> "elementReader").typedMarshallable(elementReader);
wireOut.write(() -> "elementWriter").typedMarshallable(elementWriter);
}
}