Skip to content

Commit 92c8879

Browse files
ctf.core: Use intervaltree instead of map for enums
Allows o(Log(n)) instead of o(n) event creation Change-Id: I80fd9638423bc7b698339a5552b871925f01b885 Signed-off-by: Matthew Khouzam <[email protected]>
1 parent 1c1de4e commit 92c8879

File tree

1 file changed

+115
-40
lines changed
  • ctf/org.eclipse.tracecompass.ctf.core/src/org/eclipse/tracecompass/ctf/core/event/types

1 file changed

+115
-40
lines changed

ctf/org.eclipse.tracecompass.ctf.core/src/org/eclipse/tracecompass/ctf/core/event/types/EnumDeclaration.java

Lines changed: 115 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import java.nio.ByteOrder;
1818
import java.util.ArrayList;
1919
import java.util.Arrays;
20-
import java.util.Collection;
2120
import java.util.LinkedHashMap;
2221
import java.util.List;
2322
import java.util.Map;
@@ -30,9 +29,6 @@
3029
import org.eclipse.tracecompass.ctf.core.event.scope.IDefinitionScope;
3130

3231
import com.google.common.collect.ImmutableSet;
33-
import com.google.common.collect.Iterables;
34-
import com.google.common.collect.LinkedHashMultimap;
35-
import com.google.common.collect.Multimap;
3632

3733
/**
3834
* A CTF enum declaration.
@@ -100,15 +96,28 @@ public String toString() {
10096
}
10197
}
10298

99+
/**
100+
* Interval tree node for efficient range queries
101+
*/
102+
private static class IntervalNode {
103+
final Pair interval;
104+
final List<String> labels;
105+
long maxEnd;
106+
IntervalNode left, right;
107+
108+
IntervalNode(Pair interval, String label) {
109+
this.interval = interval;
110+
this.labels = new ArrayList<>();
111+
this.labels.add(label);
112+
this.maxEnd = interval.getSecond();
113+
}
114+
}
115+
103116
// ------------------------------------------------------------------------
104117
// Attributes
105118
// ------------------------------------------------------------------------
106119

107-
/**
108-
* fEnumMap key is the Pair of low and high, value is the label. Overlap of
109-
* keys in the map is allowed.
110-
*/
111-
private final Multimap<Pair, String> fEnumMap = LinkedHashMultimap.create();
120+
private IntervalNode fEnumRoot;
112121
private final IntegerDeclaration fContainerType;
113122
private Pair fLastAdded = new Pair(-1, -1);
114123

@@ -143,7 +152,7 @@ public EnumDeclaration(IntegerDeclaration containerType) {
143152
*/
144153
public EnumDeclaration(IntegerDeclaration containerType, Map<Pair, String> enumTree){
145154
fContainerType = containerType;
146-
enumTree.entrySet().forEach(entry -> fEnumMap.put(entry.getKey(), entry.getValue()));
155+
enumTree.entrySet().forEach(entry -> insert(entry.getKey(), entry.getValue()));
147156
}
148157

149158
// ------------------------------------------------------------------------
@@ -213,11 +222,35 @@ public boolean add(long low, long high, @Nullable String label) {
213222
return false;
214223
}
215224
Pair key = new Pair(low, high);
216-
fEnumMap.put(key, label);
225+
insert(key, label);
217226
fLastAdded = key;
218227
return true;
219228
}
220229

230+
private void insert(Pair interval, String label) {
231+
fEnumRoot = insertNode(fEnumRoot, interval, label);
232+
}
233+
234+
private IntervalNode insertNode(IntervalNode node, Pair interval, String label) {
235+
if (node == null) {
236+
return new IntervalNode(interval, label);
237+
}
238+
239+
if (interval.equals(node.interval)) {
240+
node.labels.add(label);
241+
return node;
242+
}
243+
244+
if (interval.getFirst() < node.interval.getFirst()) {
245+
node.left = insertNode(node.left, interval, label);
246+
} else {
247+
node.right = insertNode(node.right, interval, label);
248+
}
249+
250+
node.maxEnd = Math.max(node.maxEnd, interval.getSecond());
251+
return node;
252+
}
253+
221254
/**
222255
* Add a value following the last previously added value.
223256
*
@@ -242,14 +275,12 @@ public boolean add(@Nullable String label) {
242275
*/
243276
public @Nullable String query(long value) {
244277
List<String> strValues = new ArrayList<>();
245-
fEnumMap.forEach((k, v) -> {
246-
if (value >= k.getFirst() && value <= k.getSecond()) {
247-
strValues.add(v);
248-
}
249-
});
278+
queryIntersecting(fEnumRoot, value, strValues);
279+
250280
if (!strValues.isEmpty()) {
251281
return strValues.size() == 1 ? strValues.get(0) : strValues.toString();
252282
}
283+
253284
/*
254285
* Divide the positive value in bits and see if there is a value for all
255286
* those bits
@@ -258,22 +289,52 @@ public boolean add(@Nullable String label) {
258289
for (int i = 0; i < Long.SIZE; i++) {
259290
Long bitValue = 1L << i;
260291
if ((bitValue & value) != 0) {
261-
/*
262-
* See if there is a value for this bit where lower == upper, no
263-
* range accepted here
264-
*/
265-
Pair bitPair = new Pair(bitValue, bitValue);
266-
Collection<String> flagValues = fEnumMap.get(bitPair);
267-
if (flagValues.isEmpty()) {
268-
// No value for this bit, not an enum flag
292+
List<String> bitFlags = new ArrayList<>();
293+
queryExact(fEnumRoot, bitValue, bitFlags);
294+
if (bitFlags.isEmpty()) {
269295
return null;
270296
}
271-
flagsSet.add(flagValues.size() == 1 ? flagValues.iterator().next() : flagValues.toString());
297+
flagsSet.add(bitFlags.size() == 1 ? bitFlags.get(0) : bitFlags.toString());
272298
}
273299
}
274300
return flagsSet.isEmpty() ? null : String.join(" | ", flagsSet); //$NON-NLS-1$
275301
}
276302

303+
private void queryIntersecting(IntervalNode node, long value, List<String> result) {
304+
if (node == null || node.maxEnd < value) {
305+
return;
306+
}
307+
308+
if (value >= node.interval.getFirst() && value <= node.interval.getSecond()) {
309+
result.addAll(node.labels);
310+
}
311+
312+
if (node.left != null && node.left.maxEnd >= value) {
313+
queryIntersecting(node.left, value, result);
314+
}
315+
316+
queryIntersecting(node.right, value, result);
317+
}
318+
319+
private void queryExact(IntervalNode node, long value, List<String> result) {
320+
if (node == null) {
321+
return;
322+
}
323+
324+
if (node.interval.getFirst() == value && node.interval.getSecond() == value) {
325+
result.addAll(node.labels);
326+
}
327+
328+
if (value < node.interval.getFirst()) {
329+
queryExact(node.left, value, result);
330+
} else if (value > node.interval.getFirst()) {
331+
queryExact(node.right, value, result);
332+
} else {
333+
queryExact(node.left, value, result);
334+
queryExact(node.right, value, result);
335+
}
336+
}
337+
277338
/**
278339
* Get a copy of the lookup table.
279340
*
@@ -283,19 +344,41 @@ public boolean add(@Nullable String label) {
283344
*/
284345
public Map<Pair, String> getLookupTable() {
285346
Map<Pair, String> table = new LinkedHashMap<>();
286-
fEnumMap.asMap().forEach((k, v) -> {
287-
table.put(k, v.size() == 1 ? v.iterator().next() : v.toString());
288-
});
347+
collectEntries(fEnumRoot, table);
289348
return table;
290349
}
291350

351+
private void collectEntries(IntervalNode node, Map<Pair, String> table) {
352+
if (node == null) {
353+
return;
354+
}
355+
356+
String value = node.labels.size() == 1 ? node.labels.get(0) : node.labels.toString();
357+
table.put(node.interval, value);
358+
359+
collectEntries(node.left, table);
360+
collectEntries(node.right, table);
361+
}
362+
292363
/**
293364
* Gets a set of labels of the enum
294365
*
295366
* @return A set of labels of the enum, can be empty but not null
296367
*/
297368
public Set<String> getLabels() {
298-
return ImmutableSet.copyOf(fEnumMap.values());
369+
List<String> labels = new ArrayList<>();
370+
collectLabels(fEnumRoot, labels);
371+
return ImmutableSet.copyOf(labels);
372+
}
373+
374+
private void collectLabels(IntervalNode node, List<String> labels) {
375+
if (node == null) {
376+
return;
377+
}
378+
379+
labels.addAll(node.labels);
380+
collectLabels(node.left, labels);
381+
collectLabels(node.right, labels);
299382
}
300383

301384
@Override
@@ -313,7 +396,7 @@ public String toString() {
313396

314397
@Override
315398
public int hashCode() {
316-
return Objects.hash(fContainerType, fEnumMap);
399+
return Objects.hash(fContainerType, getLookupTable());
317400
}
318401

319402
@Override
@@ -331,11 +414,7 @@ public boolean equals(@Nullable Object obj) {
331414
if (!fContainerType.equals(other.fContainerType)) {
332415
return false;
333416
}
334-
/*
335-
* Must iterate through the entry sets as the comparator used in the enum tree
336-
* does not respect the contract
337-
*/
338-
return Iterables.elementsEqual(fEnumMap.entries(), other.fEnumMap.entries());
417+
return Objects.equals(getLookupTable(), other.getLookupTable());
339418
}
340419

341420
@Override
@@ -353,11 +432,7 @@ public boolean isBinaryEquivalent(@Nullable IDeclaration obj) {
353432
if (!fContainerType.isBinaryEquivalent(other.fContainerType)) {
354433
return false;
355434
}
356-
/*
357-
* Must iterate through the entry sets as the comparator used in the enum tree
358-
* does not respect the contract
359-
*/
360-
return Iterables.elementsEqual(fEnumMap.entries(), other.fEnumMap.entries());
435+
return Objects.equals(getLookupTable(), other.getLookupTable());
361436
}
362437

363438
}

0 commit comments

Comments
 (0)