-
Notifications
You must be signed in to change notification settings - Fork 478
Expand file tree
/
Copy pathData.java
More file actions
197 lines (181 loc) · 8.68 KB
/
Copy pathData.java
File metadata and controls
197 lines (181 loc) · 8.68 KB
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
* Copyright 2013-2026 chronicle.software; SPDX-License-Identifier: Apache-2.0
*/
package net.openhft.chronicle.hash;
import net.openhft.chronicle.algo.hashing.LongHashFunction;
import net.openhft.chronicle.bytes.BytesStore;
import net.openhft.chronicle.bytes.BytesUtil;
import net.openhft.chronicle.bytes.RandomDataInput;
import net.openhft.chronicle.bytes.RandomDataOutput;
import net.openhft.chronicle.map.ChronicleMap;
import net.openhft.chronicle.map.MapEntry;
import net.openhft.chronicle.map.MapQueryContext;
import net.openhft.chronicle.set.ChronicleSet;
import org.jetbrains.annotations.Nullable;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Objects;
import static net.openhft.chronicle.algo.bytes.Access.checkedRandomDataInputAccess;
/**
* Dual bytes/object access to keys or values (for {@link ChronicleMap}) and elements (for {@link
* ChronicleSet}) throughout the Chronicle Map library.
* <p>
* Bytes access: {@link #bytes()} + {@link #offset()} + {@link #size()}.
* <p>
* Object access: {@link #get()}.
* <p>
* In most cases, each particular {@code Data} wraps either some object or some bytes. Object
* is marshalled to bytes lazily on demand, and bytes are lazily deserialized to object,
* accordingly.
*
* @param <T> type of the accessed objects
*/
@SuppressWarnings("rawtypes")
public interface Data<T> {
/**
* Utility method, compares two {@code Data} instances represent equivalent bytes sequences:
* by comparing {@linkplain #size() their sizes}, then calling {@link
* #equivalent(RandomDataInput, long) d1.equivalent(d2.bytes(), d2.offset())}.
*
* @param d1 the first {@code Data} to compare
* @param d2 the second {@code Data} to compare
* @return if the given {@code Data} instances represent equivalent bytes sequences
*/
static boolean bytesEquivalent(Data<?> d1, Data<?> d2) {
return d1.size() == d2.size() && d1.equivalent(d2.bytes(), d2.offset());
}
/**
* Returns the accessor object to the {@code Data}'s bytes.
* <p>
* If this {@code Data} wraps some bytes, this method just returns a reference to that bytes.
* <p>
* If this {@code Data} wraps an object, this method performs serialization internally and
* returns a reference to the output buffer, caching the result for subsequent calls of this
* method.
* <p>
* For safety, this interface returns read-only object, because it could expose bytes source
* that must be immutable, e. g. a `char[]` array behind a {@code String}. But in cases when the
* {@code Data} instance wraps off-heap bytes, e. g. {@link MapEntry#value()}, it is allowed to
* cast the object, returned from this method, to {@link BytesStore}, and write into the
* off-heap memory. You should only ensure that current context (e. g. {@link MapQueryContext})
* is locked exclusively, in order to avoid data races.
*/
RandomDataInput bytes();
/**
* Returns the offset to the {@code Data}'s bytes sequence within the {@link RandomDataInput},
* returned from {@link #bytes()} method. For example, the first byte of the bytes
* representation of some {@code data} is {@code data.bytes().readByte(data.offset())}.
*/
long offset();
/**
* Returns the size of this {@code Data}'s bytes sequence. It spans from {@link #offset()} to
* {@code offset() + size() - 1} bytes within the {@link RandomDataInput}, returned from
* {@link #bytes()} method.
*/
long size();
/**
* Computes hash code on the bytes representation of this {@code Data}, using the given hash
* function.
*
* @param f the hash function to compute hash code using
* @return the hash code of this {@code Data}'s bytes, computed by the given function
*/
default long hash(LongHashFunction f) {
return f.hash(bytes(), checkedRandomDataInputAccess(), offset(), size());
}
/**
* Compares bytes of this {@code Data} to the given bytes {@code source}, starting from the
* given offset.
* <p>
* Default implementation compares {@link #bytes()} of this {@code Data}, but custom
* implementation may only check if {@linkplain #get() object} of this {@code Data} <i>would</i>
* be serialized to the same bytes sequence, if this {@code Data} wraps an object and obtaining
* {@link #bytes()} requires serialization internally.
*
* @param source the bytes source, to compare this {@code Data}'s bytes with
* @param sourceOffset the offset in the bytes source, the bytes sequence starts from
* @return {@code true} if the given bytes sequence is equivalent to this {@code Data}'s bytes,
* byte-by-byte
*/
default boolean equivalent(RandomDataInput source, long sourceOffset) {
return BytesUtil.bytesEqual(source, sourceOffset, bytes(), offset(), size());
}
/**
* Writes bytes of this {@code Data} to the given {@code target} from the given {@code
* targetOffset}.
* <p>
* Default implementation copies {@link #bytes()} of this {@code Data} using standard IO
* methods of {@code RandomDataInput} and {@code RandomDataOutput}, but custom implementation
* may write directly from {@linkplain #get() object}, if this {@code Data} is object-based and
* obtaining {@link #bytes()} requires serialization internally. This allows to avoid double
* copy.
*
* @param target the destination to write this data bytes to
* @param targetOffset the offset in the target, to write the bytes from.
*/
default void writeTo(RandomDataOutput target, long targetOffset) {
target.write(targetOffset, Objects.requireNonNull(bytes()), offset(), size());
}
/**
* Returns object view of this {@code Data}.
* <p>
* If this {@code Data} wraps some object, this method just returns that object.
* <p>
* If this {@code Data} wraps some bytes, this method performs deserialization internally
* and returns the resulting on-heap object, caching it for subsequent calls of this method. The
* returned object could be reused, therefore it is <i>generally disallowed</i> to use the
* object, returned from this method, <i>outside</i> some context, or a block, synchronized with
* locks, or lambda, etc., which provided the access to this {@code Data} instance.
*/
T get();
/**
* Deserialize and return an object from the {@code Data}'s bytes, trying to reuse the given
* {@code using} object (might be {@code null}). This method either returns the given {@code
* using} object back (if reuse is possible) or creates a new object, rather than some
* internally cached and reused one, therefore it is <i>always allowed</i> to use the object,
* returned from this method, <i>outside</i> some context, or a block, synchronized with locks,
* or lambda, etc., which provided the access to this {@code Data} instance.
*/
T getUsing(@Nullable T using);
/**
* {@code Data} implementations should override {@link Object#hashCode()} with delegation to
* this method. Computes {@code Data}'s hash code by applying a hash function to {@code Data}'s
* <i>bytes</i> representation.
*/
default int dataHashCode() {
return (int) hash(LongHashFunction.xx_r39());
}
/**
* {@code Data} implementations should override {@link Object#equals(Object)} with delegation to
* this method. Compares {@code Data}s' <i>bytes</i> representations.
*/
default boolean dataEquals(Object obj) {
return obj != null &&
obj instanceof Data &&
Data.bytesEquivalent(this, (Data<?>) obj);
}
/**
* {@code Data} implementations should override {@link Object#toString()} with delegation to
* this method. Delegates to {@code Data}'s <i>object</i> {@code toString()}, i. e. equivalent
* to calling {@code get().toString()} on this {@code Data} instance with some fallback, if
* {@code get()} method throws an exception.
*/
default String dataToString() {
try {
return get().toString();
} catch (Exception e) {
StringBuilder sb = new StringBuilder();
sb.append("failed to deserialize object from data with bytes: [");
RandomDataInput bs = bytes();
for (long off = offset(), lim = offset() + size(); off < lim; off++) {
sb.append(bs.printable(off));
}
sb.append("], exception: ");
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionAsString = sw.toString();
sb.append(exceptionAsString);
return sb.toString();
}
}
}