Skip to content

Commit ed9a66c

Browse files
MatthewKhouzamarfio
authored andcommitted
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 97a2655 commit ed9a66c

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.
@@ -102,15 +98,28 @@ public String toString() {
10298
}
10399
}
104100

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

109-
/**
110-
* fEnumMap key is the Pair of low and high, value is the label. Overlap of
111-
* keys in the map is allowed.
112-
*/
113-
private final Multimap<Pair, String> fEnumMap = LinkedHashMultimap.create();
122+
private IntervalNode fEnumRoot;
114123
private final IntegerDeclaration fContainerType;
115124
private Pair fLastAdded = new Pair(-1, -1);
116125
private @Nullable String[] fCache = new String[CACHE_SIZE];
@@ -146,7 +155,7 @@ public EnumDeclaration(IntegerDeclaration containerType) {
146155
*/
147156
public EnumDeclaration(IntegerDeclaration containerType, Map<Pair, String> enumTree) {
148157
fContainerType = containerType;
149-
enumTree.entrySet().forEach(entry -> fEnumMap.put(entry.getKey(), entry.getValue()));
158+
enumTree.entrySet().forEach(entry -> insert(entry.getKey(), entry.getValue()));
150159
}
151160

152161
// ------------------------------------------------------------------------
@@ -231,11 +240,35 @@ public boolean add(long low, long high, @Nullable String label) {
231240
}
232241
}
233242
Pair key = new Pair(low, high);
234-
fEnumMap.put(key, label);
243+
insert(key, label);
235244
fLastAdded = key;
236245
return true;
237246
}
238247

248+
private void insert(Pair interval, String label) {
249+
fEnumRoot = insertNode(fEnumRoot, interval, label);
250+
}
251+
252+
private IntervalNode insertNode(IntervalNode node, Pair interval, String label) {
253+
if (node == null) {
254+
return new IntervalNode(interval, label);
255+
}
256+
257+
if (interval.equals(node.interval)) {
258+
node.labels.add(label);
259+
return node;
260+
}
261+
262+
if (interval.getFirst() < node.interval.getFirst()) {
263+
node.left = insertNode(node.left, interval, label);
264+
} else {
265+
node.right = insertNode(node.right, interval, label);
266+
}
267+
268+
node.maxEnd = Math.max(node.maxEnd, interval.getSecond());
269+
return node;
270+
}
271+
239272
/**
240273
* Add a value following the last previously added value.
241274
*
@@ -266,14 +299,12 @@ public boolean add(@Nullable String label) {
266299
return fCache[(int) value];
267300
}
268301
List<String> strValues = new ArrayList<>();
269-
fEnumMap.forEach((k, v) -> {
270-
if (value >= k.getFirst() && value <= k.getSecond()) {
271-
strValues.add(v);
272-
}
273-
});
302+
queryIntersecting(fEnumRoot, value, strValues);
303+
274304
if (!strValues.isEmpty()) {
275305
return strValues.size() == 1 ? strValues.get(0) : strValues.toString();
276306
}
307+
277308
/*
278309
* Divide the positive value in bits and see if there is a value for all
279310
* those bits
@@ -282,22 +313,52 @@ public boolean add(@Nullable String label) {
282313
for (int i = 0; i < Long.SIZE; i++) {
283314
Long bitValue = 1L << i;
284315
if ((bitValue & value) != 0) {
285-
/*
286-
* See if there is a value for this bit where lower == upper, no
287-
* range accepted here
288-
*/
289-
Pair bitPair = new Pair(bitValue, bitValue);
290-
Collection<String> flagValues = fEnumMap.get(bitPair);
291-
if (flagValues.isEmpty()) {
292-
// No value for this bit, not an enum flag
316+
List<String> bitFlags = new ArrayList<>();
317+
queryExact(fEnumRoot, bitValue, bitFlags);
318+
if (bitFlags.isEmpty()) {
293319
return null;
294320
}
295-
flagsSet.add(flagValues.size() == 1 ? flagValues.iterator().next() : flagValues.toString());
321+
flagsSet.add(bitFlags.size() == 1 ? bitFlags.get(0) : bitFlags.toString());
296322
}
297323
}
298324
return flagsSet.isEmpty() ? null : String.join(" | ", flagsSet); //$NON-NLS-1$
299325
}
300326

327+
private void queryIntersecting(IntervalNode node, long value, List<String> result) {
328+
if (node == null || node.maxEnd < value) {
329+
return;
330+
}
331+
332+
if (value >= node.interval.getFirst() && value <= node.interval.getSecond()) {
333+
result.addAll(node.labels);
334+
}
335+
336+
if (node.left != null && node.left.maxEnd >= value) {
337+
queryIntersecting(node.left, value, result);
338+
}
339+
340+
queryIntersecting(node.right, value, result);
341+
}
342+
343+
private void queryExact(IntervalNode node, long value, List<String> result) {
344+
if (node == null) {
345+
return;
346+
}
347+
348+
if (node.interval.getFirst() == value && node.interval.getSecond() == value) {
349+
result.addAll(node.labels);
350+
}
351+
352+
if (value < node.interval.getFirst()) {
353+
queryExact(node.left, value, result);
354+
} else if (value > node.interval.getFirst()) {
355+
queryExact(node.right, value, result);
356+
} else {
357+
queryExact(node.left, value, result);
358+
queryExact(node.right, value, result);
359+
}
360+
}
361+
301362
/**
302363
* Get a copy of the lookup table.
303364
*
@@ -307,19 +368,41 @@ public boolean add(@Nullable String label) {
307368
*/
308369
public Map<Pair, String> getLookupTable() {
309370
Map<Pair, String> table = new LinkedHashMap<>();
310-
fEnumMap.asMap().forEach((k, v) -> {
311-
table.put(k, v.size() == 1 ? v.iterator().next() : v.toString());
312-
});
371+
collectEntries(fEnumRoot, table);
313372
return table;
314373
}
315374

375+
private void collectEntries(IntervalNode node, Map<Pair, String> table) {
376+
if (node == null) {
377+
return;
378+
}
379+
380+
String value = node.labels.size() == 1 ? node.labels.get(0) : node.labels.toString();
381+
table.put(node.interval, value);
382+
383+
collectEntries(node.left, table);
384+
collectEntries(node.right, table);
385+
}
386+
316387
/**
317388
* Gets a set of labels of the enum
318389
*
319390
* @return A set of labels of the enum, can be empty but not null
320391
*/
321392
public Set<String> getLabels() {
322-
return ImmutableSet.copyOf(fEnumMap.values());
393+
List<String> labels = new ArrayList<>();
394+
collectLabels(fEnumRoot, labels);
395+
return ImmutableSet.copyOf(labels);
396+
}
397+
398+
private void collectLabels(IntervalNode node, List<String> labels) {
399+
if (node == null) {
400+
return;
401+
}
402+
403+
labels.addAll(node.labels);
404+
collectLabels(node.left, labels);
405+
collectLabels(node.right, labels);
323406
}
324407

325408
@Override
@@ -337,7 +420,7 @@ public String toString() {
337420

338421
@Override
339422
public int hashCode() {
340-
return Objects.hash(fContainerType, fEnumMap);
423+
return Objects.hash(fContainerType, getLookupTable());
341424
}
342425

343426
@Override
@@ -355,11 +438,7 @@ public boolean equals(@Nullable Object obj) {
355438
if (!fContainerType.equals(other.fContainerType)) {
356439
return false;
357440
}
358-
/*
359-
* Must iterate through the entry sets as the comparator used in the
360-
* enum tree does not respect the contract
361-
*/
362-
return Iterables.elementsEqual(fEnumMap.entries(), other.fEnumMap.entries());
441+
return Objects.equals(getLookupTable(), other.getLookupTable());
363442
}
364443

365444
@Override
@@ -377,11 +456,7 @@ public boolean isBinaryEquivalent(@Nullable IDeclaration obj) {
377456
if (!fContainerType.isBinaryEquivalent(other.fContainerType)) {
378457
return false;
379458
}
380-
/*
381-
* Must iterate through the entry sets as the comparator used in the
382-
* enum tree does not respect the contract
383-
*/
384-
return Iterables.elementsEqual(fEnumMap.entries(), other.fEnumMap.entries());
459+
return Objects.equals(getLookupTable(), other.getLookupTable());
385460
}
386461

387462
}

0 commit comments

Comments
 (0)