Skip to content

Commit

Permalink
Optimised Tags merge.
Browse files Browse the repository at this point in the history
  • Loading branch information
mstyura committed May 23, 2024
1 parent 0b93af8 commit dfbdde3
Showing 1 changed file with 85 additions and 31 deletions.
116 changes: 85 additions & 31 deletions micrometer-core/src/main/java/io/micrometer/core/instrument/Tags.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,42 @@
*/
public final class Tags implements Iterable<Tag> {

private static final Tags EMPTY = new Tags(new Tag[] {});
private static final Tags EMPTY = new Tags(new Tag[] {}, 0);

private final Tag[] tags;
private final Tag[] sortedSet;

private int last;
private final int length;

private Tags(Tag[] tags) {
this.tags = tags;
Arrays.sort(this.tags);
dedup();
private Tags(Tag[] sortedSet, int length) {
assert isSortedSet(sortedSet, length) : "bug on caller side: construction invariant violated";
this.sortedSet = sortedSet;
this.length = length;
}

private void dedup() {
private static boolean isSortedSet(Tag[] sortedSet, int length) {
if (length > sortedSet.length) {
return false;
}
for (int i = 0; i < length - 1; i++) {
int cmp = sortedSet[i].compareTo(sortedSet[i + 1]);
if (cmp >= 0) {
return false;
}
}
return true;
}

private static Tags make(Tag[] tags) {
Arrays.sort(tags);
int len = dedup(tags);
return new Tags(tags, len);
}

private static int dedup(Tag[] tags) {
int n = tags.length;

if (n == 0 || n == 1) {
last = n;
return;
return n;
}

// index of next unique element
Expand All @@ -62,7 +80,47 @@ private void dedup() {
tags[j++] = tags[i];

tags[j++] = tags[n - 1];
last = j;
return j;
}

private Tags merged(Tags other) {
if (other.length == 0) {
return this;
}
if (Objects.equals(this, other)) {
return this;
}
Tag[] sortedSet = new Tag[this.length + other.length];
int sortedIdx = 0, thisIdx = 0, otherIdx = 0;
while (thisIdx < this.length && otherIdx < other.length) {
int cmp = this.sortedSet[thisIdx].compareTo(other.sortedSet[otherIdx]);
if (cmp > 0) {
sortedSet[sortedIdx] = other.sortedSet[otherIdx];
otherIdx++;
}
else if (cmp < 0) {
sortedSet[sortedIdx] = this.sortedSet[thisIdx];
thisIdx++;
}
else {
// In case of key conflict prefer tag from other set
sortedSet[sortedIdx] = other.sortedSet[otherIdx];
thisIdx++;
otherIdx++;
}
sortedIdx++;
}
int thisRemaining = this.length - thisIdx;
if (thisRemaining > 0) {
System.arraycopy(this.sortedSet, thisIdx, sortedSet, sortedIdx, thisRemaining);
sortedIdx += thisRemaining;
}
int otherRemaining = other.length - otherIdx;
if (otherIdx < other.sortedSet.length) {
System.arraycopy(other.sortedSet, otherIdx, sortedSet, sortedIdx, otherRemaining);
sortedIdx += otherRemaining;
}
return new Tags(sortedSet, sortedIdx);
}

/**
Expand Down Expand Up @@ -99,10 +157,7 @@ public Tags and(@Nullable Tag... tags) {
if (blankVarargs(tags)) {
return this;
}
Tag[] newTags = new Tag[last + tags.length];
System.arraycopy(this.tags, 0, newTags, 0, last);
System.arraycopy(tags, 0, newTags, last, tags.length);
return new Tags(newTags);
return and(make(tags));
}

/**
Expand All @@ -116,11 +171,10 @@ public Tags and(@Nullable Iterable<? extends Tag> tags) {
return this;
}

if (this.tags.length == 0) {
if (this.length == 0) {
return Tags.of(tags);
}

return and(Tags.of(tags).tags);
return merged(Tags.of(tags));
}

@Override
Expand All @@ -134,12 +188,12 @@ private class ArrayIterator implements Iterator<Tag> {

@Override
public boolean hasNext() {
return currentIndex < last;
return currentIndex < length;
}

@Override
public Tag next() {
return tags[currentIndex++];
return sortedSet[currentIndex++];
}

@Override
Expand All @@ -151,7 +205,7 @@ public void remove() {

@Override
public Spliterator<Tag> spliterator() {
return Spliterators.spliterator(tags, 0, last, Spliterator.IMMUTABLE | Spliterator.ORDERED
return Spliterators.spliterator(sortedSet, 0, length, Spliterator.IMMUTABLE | Spliterator.ORDERED
| Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.SORTED);
}

Expand All @@ -166,8 +220,8 @@ public Stream<Tag> stream() {
@Override
public int hashCode() {
int result = 1;
for (int i = 0; i < last; i++) {
result = 31 * result + tags[i].hashCode();
for (int i = 0; i < length; i++) {
result = 31 * result + sortedSet[i].hashCode();
}
return result;
}
Expand All @@ -178,14 +232,14 @@ public boolean equals(@Nullable Object obj) {
}

private boolean tagsEqual(Tags obj) {
if (tags == obj.tags)
if (sortedSet == obj.sortedSet)
return true;

if (last != obj.last)
if (length != obj.length)
return false;

for (int i = 0; i < last; i++) {
if (!tags[i].equals(obj.tags[i]))
for (int i = 0; i < length; i++) {
if (!sortedSet[i].equals(obj.sortedSet[i]))
return false;
}

Expand Down Expand Up @@ -229,10 +283,10 @@ else if (tags instanceof Tags) {
}
else if (tags instanceof Collection) {
Collection<? extends Tag> tagsCollection = (Collection<? extends Tag>) tags;
return new Tags(tagsCollection.toArray(new Tag[0]));
return make(tagsCollection.toArray(new Tag[0]));
}
else {
return new Tags(StreamSupport.stream(tags.spliterator(), false).toArray(Tag[]::new));
return make(StreamSupport.stream(tags.spliterator(), false).toArray(Tag[]::new));
}
}

Expand All @@ -244,7 +298,7 @@ else if (tags instanceof Collection) {
* @return a new {@code Tags} instance
*/
public static Tags of(String key, String value) {
return new Tags(new Tag[] { Tag.of(key, value) });
return new Tags(new Tag[] { Tag.of(key, value) }, 1);
}

/**
Expand All @@ -264,7 +318,7 @@ public static Tags of(@Nullable String... keyValues) {
for (int i = 0; i < keyValues.length; i += 2) {
tags[i / 2] = Tag.of(keyValues[i], keyValues[i + 1]);
}
return new Tags(tags);
return make(tags);
}

private static boolean blankVarargs(@Nullable Object[] args) {
Expand Down

0 comments on commit dfbdde3

Please sign in to comment.