From 94822322d20a79455e97f6233b29c82fa81d3558 Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Mon, 12 Jan 2026 14:33:29 -0500 Subject: [PATCH 01/10] Updating tests to account for API changes --- .../core/taginterceptor/TagInterceptor.java | 2 +- .../main/java/datadog/trace/api/TagMap.java | 668 ++++++++++++++++-- .../datadog/trace/api/TagMapFuzzTest.java | 6 +- .../java/datadog/trace/api/TagMapTest.java | 2 +- 4 files changed, 614 insertions(+), 64 deletions(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java b/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java index 3da653c5398..f2c6640d721 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java @@ -85,7 +85,7 @@ public TagInterceptor( } public boolean needsIntercept(TagMap map) { - for (TagMap.Entry entry : map) { + for (TagMap.EntryReader entry : map) { if (needsIntercept(entry.tag())) return true; } return false; diff --git a/internal-api/src/main/java/datadog/trace/api/TagMap.java b/internal-api/src/main/java/datadog/trace/api/TagMap.java index 79fc79e4c4d..5423de22e66 100644 --- a/internal-api/src/main/java/datadog/trace/api/TagMap.java +++ b/internal-api/src/main/java/datadog/trace/api/TagMap.java @@ -43,7 +43,7 @@ *
  • adaptive collision * */ -public interface TagMap extends Map, Iterable { +public interface TagMap extends Map, Iterable { /** Immutable empty TagMap - similar to {@link Collections#emptyMap()} */ TagMap EMPTY = TagMapFactory.INSTANCE.empty(); @@ -166,7 +166,7 @@ static Ledger ledger(int size) { void set(String tag, double value); - void set(Entry newEntry); + void set(EntryReader newEntry); /** sets the value while returning the prior Entry */ Entry getAndSet(String tag, Object value); @@ -237,14 +237,16 @@ static Ledger ledger(int size) { * , but with less allocation */ @Override - Iterator iterator(); + Iterator iterator(); - Stream stream(); + Stream stream(); + + EntryIterator entryIterator(); /** * Visits each Entry in this TagMap This method is more efficient than {@link TagMap#iterator()} */ - void forEach(Consumer consumer); + void forEach(Consumer consumer); /** * Version of forEach that takes an extra context object that is passed as the first argument to @@ -252,7 +254,7 @@ static Ledger ledger(int size) { * *

    The intention is to use this method to avoid using a capturing lambda */ - void forEach(T thisObj, BiConsumer consumer); + void forEach(T thisObj, BiConsumer consumer); /** * Version of forEach that takes two extra context objects that are passed as the first two @@ -260,7 +262,8 @@ static Ledger ledger(int size) { * *

    The intention is to use this method to avoid using a capturing lambda */ - void forEach(T thisObj, U otherObj, TriConsumer consumer); + void forEach( + T thisObj, U otherObj, TriConsumer consumer); /** Clears the TagMap */ void clear(); @@ -307,12 +310,7 @@ public boolean isRemoval() { } } - final class Entry extends EntryChange implements Map.Entry { - /* - * Special value used for Objects that haven't been type checked yet. - * These objects might be primitive box objects. - */ - public static final byte ANY = 0; + interface EntryReader { public static final byte OBJECT = 1; /* @@ -331,6 +329,54 @@ final class Entry extends EntryChange implements Map.Entry { public static final byte FLOAT = 8; public static final byte DOUBLE = 9; + String tag(); + + byte type(); + + boolean is(byte type); + + boolean isNumericPrimitive(); + + boolean isNumber(); + + boolean isObject(); + + Object objectValue(); + + String stringValue(); + + boolean booleanValue(); + + int intValue(); + + long longValue(); + + float floatValue(); + + double doubleValue(); + + Map.Entry mapEntry(); + + Entry entry(); + } + + interface EntryIterator extends EntryReader { + boolean hasNext(); + + boolean next(); + } + + interface EntryChangeIterator extends EntryIterator { + boolean isRemoval(); + } + + final class Entry extends EntryChange implements Map.Entry, EntryReader { + /* + * Special value used for Objects that haven't been type checked yet. + * These objects might be primitive box objects. + */ + static final byte ANY = 0; + static Entry newAnyEntry(Map.Entry entry) { return newAnyEntry(entry.getKey(), entry.getValue()); } @@ -400,7 +446,7 @@ static Entry newDoubleEntry(String tag, Double box) { // no type checks are done during construction. // Any Object entries are initially marked as type ANY, prim set to 0, and the Object put into // obj - // If an ANY entry is later type checked or request as a primitive, then the ANY will be + // If an ANY entry is later type checked or requested as a primitive, then the ANY will be // resolved // to the correct type. @@ -436,10 +482,22 @@ int hash() { return hash; } + @Override + public Entry entry() { + return this; + } + + @Override + public java.util.Map.Entry mapEntry() { + return this; + } + + @Override public byte type() { return this.resolveAny(); } + @Override public boolean is(byte type) { byte curType = this.rawType; if (curType == type) { @@ -451,6 +509,7 @@ public boolean is(byte type) { } } + @Override public boolean isNumericPrimitive() { byte curType = this.rawType; if (_isNumericPrimitive(curType)) { @@ -462,6 +521,7 @@ public boolean isNumericPrimitive() { } } + @Override public boolean isNumber() { byte curType = this.rawType; return _isNumericPrimitive(curType) || (this.rawObj instanceof Number); @@ -517,6 +577,7 @@ private void _setPrim(byte type, long prim) { this.rawType = type; } + @Override public boolean isObject() { return this.is(OBJECT); } @@ -525,6 +586,7 @@ public boolean isRemoval() { return false; } + @Override public Object objectValue() { if (this.rawObj != null) { return this.rawObj; @@ -562,6 +624,7 @@ public Object objectValue() { return this.rawObj; } + @Override public boolean booleanValue() { byte type = this.rawType; @@ -597,6 +660,7 @@ public boolean booleanValue() { return false; } + @Override public int intValue() { byte type = this.rawType; @@ -632,6 +696,7 @@ public int intValue() { return 0; } + @Override public long longValue() { byte type = this.rawType; @@ -667,6 +732,7 @@ public long longValue() { return 0; } + @Override public float floatValue() { byte type = this.rawType; @@ -702,6 +768,7 @@ public float floatValue() { return 0F; } + @Override public double doubleValue() { byte type = this.rawType; @@ -737,6 +804,7 @@ public double doubleValue() { return 0D; } + @Override public String stringValue() { String strCache = this.strCache; if (strCache != null) { @@ -1266,8 +1334,8 @@ public boolean containsKey(Object key) { @Override public boolean containsValue(Object value) { // This could be optimized - but probably isn't called enough to be worth it - for (Entry entry : this) { - if (entry.objectValue().equals(value)) return true; + for (EntryReader entryReader : this) { + if (entryReader.objectValue().equals(value)) return true; } return false; } @@ -1317,8 +1385,8 @@ public Object put(String tag, Object value) { } @Override - public void set(TagMap.Entry newEntry) { - this.getAndSet(newEntry); + public void set(TagMap.EntryReader newEntryReader) { + this.getAndSet(newEntryReader.entry()); } @Override @@ -1727,17 +1795,22 @@ public TagMap immutableCopy() { } @Override - public Iterator iterator() { - return new EntryIterator(this); + public Iterator iterator() { + return new EntryReaderIterator(this); } @Override - public Stream stream() { + public Stream stream() { return StreamSupport.stream(spliterator(), false); } @Override - public void forEach(Consumer consumer) { + public EntryIterator entryIterator() { + return new EntryIteratorImpl(this); + } + + @Override + public void forEach(Consumer consumer) { Object[] thisBuckets = this.buckets; for (int i = 0; i < thisBuckets.length; ++i) { @@ -1756,7 +1829,7 @@ public void forEach(Consumer consumer) { } @Override - public void forEach(T thisObj, BiConsumer consumer) { + public void forEach(T thisObj, BiConsumer consumer) { Object[] thisBuckets = this.buckets; for (int i = 0; i < thisBuckets.length; ++i) { @@ -1776,7 +1849,7 @@ public void forEach(T thisObj, BiConsumer consumer) @Override public void forEach( - T thisObj, U otherObj, TriConsumer consumer) { + T thisObj, U otherObj, TriConsumer consumer) { Object[] thisBuckets = this.buckets; for (int i = 0; i < thisBuckets.length; ++i) { @@ -1932,14 +2005,14 @@ String toPrettyString() { StringBuilder ledger = new StringBuilder(128); ledger.append('{'); - for (Entry entry : this) { + for (EntryReader entry : this) { if (first) { first = false; } else { ledger.append(", "); } - ledger.append(entry.tag).append('=').append(entry.stringValue()); + ledger.append(entry.tag()).append('=').append(entry.stringValue()); } ledger.append('}'); return ledger.toString(); @@ -1973,7 +2046,7 @@ String toInternalString() { return ledger.toString(); } - abstract static class MapIterator implements Iterator { + abstract static class IteratorBase { private final Object[] buckets; private Entry nextEntry; @@ -1983,12 +2056,11 @@ abstract static class MapIterator implements Iterator { private BucketGroup group = null; private int groupIndex = 0; - MapIterator(OptimizedTagMap map) { + IteratorBase(OptimizedTagMap map) { this.buckets = map.buckets; } - @Override - public boolean hasNext() { + public final boolean hasNext() { if (this.nextEntry != null) return true; while (this.bucketIndex < this.buckets.length) { @@ -1999,7 +2071,7 @@ public boolean hasNext() { return false; } - Entry nextEntry() { + final Entry nextEntryOrThrowNoSuchElement() { if (this.nextEntry != null) { Entry nextEntry = this.nextEntry; this.nextEntry = null; @@ -2013,7 +2085,17 @@ Entry nextEntry() { } } - private Entry advance() { + final Entry nextEntryOrNull() { + if (this.nextEntry != null) { + Entry nextEntry = this.nextEntry; + this.nextEntry = null; + return nextEntry; + } + + return this.hasNext() ? this.nextEntry : null; + } + + private final Entry advance() { while (this.bucketIndex < this.buckets.length) { if (this.group != null) { for (++this.groupIndex; this.groupIndex < BucketGroup.LEN; ++this.groupIndex) { @@ -2047,14 +2129,103 @@ private Entry advance() { } } - static final class EntryIterator extends MapIterator { - EntryIterator(OptimizedTagMap map) { + static final class EntryReaderIterator extends IteratorBase implements Iterator { + EntryReaderIterator(OptimizedTagMap map) { super(map); } @Override - public Entry next() { - return this.nextEntry(); + public EntryReader next() { + return this.nextEntryOrThrowNoSuchElement(); + } + } + + static final class EntryIteratorImpl extends IteratorBase implements EntryIterator { + private TagMap.Entry curEntry; + + EntryIteratorImpl(OptimizedTagMap map) { + super(map); + } + + @Override + public boolean next() { + this.curEntry = this.nextEntryOrNull(); + return (this.curEntry != null); + } + + @Override + public String tag() { + return this.curEntry.tag(); + } + + @Override + public byte type() { + return this.curEntry.type(); + } + + @Override + public boolean is(byte type) { + return this.curEntry.is(type); + } + + @Override + public boolean isNumber() { + return this.curEntry.isNumber(); + } + + @Override + public boolean isNumericPrimitive() { + return this.curEntry.isNumericPrimitive(); + } + + @Override + public boolean isObject() { + return this.curEntry.isObject(); + } + + @Override + public boolean booleanValue() { + return this.curEntry.booleanValue(); + } + + @Override + public int intValue() { + return this.curEntry.intValue(); + } + + @Override + public long longValue() { + return this.curEntry.longValue(); + } + + @Override + public float floatValue() { + return this.curEntry.floatValue(); + } + + @Override + public double doubleValue() { + return this.curEntry.doubleValue(); + } + + @Override + public Object objectValue() { + return this.curEntry.objectValue(); + } + + @Override + public String stringValue() { + return this.curEntry.stringValue(); + } + + @Override + public Entry entry() { + return this.curEntry; + } + + @Override + public Map.Entry mapEntry() { + return this.curEntry; } } @@ -2406,39 +2577,40 @@ Entry _remove(int hash, String tag) { return existingEntry; } - void forEachInChain(Consumer consumer) { + void forEachInChain(Consumer consumer) { for (BucketGroup curGroup = this; curGroup != null; curGroup = curGroup.prev) { curGroup._forEach(consumer); } } - void _forEach(Consumer consumer) { + void _forEach(Consumer consumer) { if (this.entry0 != null) consumer.accept(this.entry0); if (this.entry1 != null) consumer.accept(this.entry1); if (this.entry2 != null) consumer.accept(this.entry2); if (this.entry3 != null) consumer.accept(this.entry3); } - void forEachInChain(T thisObj, BiConsumer consumer) { + void forEachInChain(T thisObj, BiConsumer consumer) { for (BucketGroup curGroup = this; curGroup != null; curGroup = curGroup.prev) { curGroup._forEach(thisObj, consumer); } } - void _forEach(T thisObj, BiConsumer consumer) { + void _forEach(T thisObj, BiConsumer consumer) { if (this.entry0 != null) consumer.accept(thisObj, this.entry0); if (this.entry1 != null) consumer.accept(thisObj, this.entry1); if (this.entry2 != null) consumer.accept(thisObj, this.entry2); if (this.entry3 != null) consumer.accept(thisObj, this.entry3); } - void forEachInChain(T thisObj, U otherObj, TriConsumer consumer) { + void forEachInChain( + T thisObj, U otherObj, TriConsumer consumer) { for (BucketGroup curGroup = this; curGroup != null; curGroup = curGroup.prev) { curGroup._forEach(thisObj, otherObj, consumer); } } - void _forEach(T thisObj, U otherObj, TriConsumer consumer) { + void _forEach(T thisObj, U otherObj, TriConsumer consumer) { if (this.entry0 != null) consumer.accept(thisObj, otherObj, this.entry0); if (this.entry1 != null) consumer.accept(thisObj, otherObj, this.entry1); if (this.entry2 != null) consumer.accept(thisObj, otherObj, this.entry2); @@ -2574,14 +2746,14 @@ public Iterator iterator() { } } - static final class KeysIterator extends MapIterator { + static final class KeysIterator extends IteratorBase implements Iterator { KeysIterator(OptimizedTagMap map) { super(map); } @Override public String next() { - return this.nextEntry().tag(); + return this.nextEntryOrThrowNoSuchElement().tag(); } } @@ -2613,18 +2785,112 @@ public Iterator iterator() { } } - static final class ValuesIterator extends MapIterator { + static final class ValuesIterator extends IteratorBase implements Iterator { ValuesIterator(OptimizedTagMap map) { super(map); } @Override public Object next() { - return this.nextEntry().objectValue(); + return this.nextEntryOrThrowNoSuchElement().objectValue(); } } } +final class EntryReadingHelper implements TagMap.EntryReader { + private Map.Entry mapEntry; + private String tag; + private Object value; + + void set(String tag, Object value) { + this.mapEntry = null; + this.tag = tag; + this.value = value; + } + + void set(Map.Entry mapEntry) { + this.mapEntry = mapEntry; + this.tag = mapEntry.getKey(); + this.value = mapEntry.getValue(); + } + + @Override + public String tag() { + return this.tag; + } + + @Override + public byte type() { + return TagValueConversions.typeOf(this.value); + } + + @Override + public boolean is(byte type) { + return TagValueConversions.isA(this.value, type); + } + + @Override + public boolean isNumber() { + return TagValueConversions.isNumber(this.value); + } + + @Override + public boolean isNumericPrimitive() { + return TagValueConversions.isNumericPrimitive(this.value); + } + + @Override + public boolean isObject() { + return TagValueConversions.isObject(this.value); + } + + @Override + public boolean booleanValue() { + return TagValueConversions.toBoolean(this.value); + } + + @Override + public int intValue() { + return TagValueConversions.toInt(this.value); + } + + @Override + public long longValue() { + return TagValueConversions.toLong(this.value); + } + + @Override + public float floatValue() { + return TagValueConversions.toFloat(this.value); + } + + @Override + public double doubleValue() { + return TagValueConversions.toDouble(this.value); + } + + @Override + public String stringValue() { + return TagValueConversions.toString(this.value); + } + + @Override + public Object objectValue() { + return this.value; + } + + @Override + public TagMap.Entry entry() { + return TagMap.Entry.newAnyEntry(this.tag, this.value); + } + + @Override + public Map.Entry mapEntry() { + Map.Entry mapEntry = this.mapEntry; + return (mapEntry != null) ? mapEntry : this.entry(); + } +} + final class LegacyTagMap extends HashMap implements TagMap { private static final long serialVersionUID = 77473435283123683L; @@ -2688,25 +2954,35 @@ public void fillStringMap(Map stringMap) { } @Override - public void forEach(Consumer consumer) { + public void forEach(Consumer consumer) { + EntryReadingHelper entryReadingHelper = new EntryReadingHelper(); + + // TODO: optimize to take advantage of EntryReader for (Map.Entry entry : this.entrySet()) { consumer.accept(TagMap.Entry.newAnyEntry(entry)); } } @Override - public void forEach( - T thisObj, BiConsumer consumer) { + public void forEach(T thisObj, BiConsumer consumer) { + EntryReadingHelper entryReadingHelper = new EntryReadingHelper(); + for (Map.Entry entry : this.entrySet()) { - consumer.accept(thisObj, TagMap.Entry.newAnyEntry(entry)); + entryReadingHelper.set(entry); + + consumer.accept(thisObj, entryReadingHelper); } } @Override public void forEach( - T thisObj, U otherObj, TriConsumer consumer) { + T thisObj, U otherObj, TriConsumer consumer) { + EntryReadingHelper entryReadingHelper = new EntryReadingHelper(); + for (Map.Entry entry : this.entrySet()) { - consumer.accept(thisObj, otherObj, TagMap.Entry.newAnyEntry(entry)); + entryReadingHelper.set(entry); + + consumer.accept(thisObj, otherObj, entryReadingHelper); } } @@ -2911,8 +3187,8 @@ public void set(String tag, Object value) { } @Override - public void set(TagMap.Entry newEntry) { - this.put(newEntry.tag(), newEntry.objectValue()); + public void set(TagMap.EntryReader newEntryReader) { + this.put(newEntryReader.tag(), newEntryReader.objectValue()); } @Override @@ -2989,20 +3265,27 @@ public TagMap immutableCopy() { } @Override - public Iterator iterator() { + public Iterator iterator() { return new IteratorImpl(this); } @Override - public Stream stream() { + public Stream stream() { return StreamSupport.stream(this.spliterator(), false); } - private static final class IteratorImpl implements Iterator { + @Override + public TagMap.EntryIterator entryIterator() { + return new EntryIteratorImpl(this); + } + + private static final class IteratorImpl implements Iterator { private final Iterator> wrappedIter; + private final EntryReadingHelper entryReadingHelper; IteratorImpl(LegacyTagMap legacyMap) { this.wrappedIter = legacyMap.entrySet().iterator(); + this.entryReadingHelper = new EntryReadingHelper(); } @Override @@ -3011,8 +3294,275 @@ public boolean hasNext() { } @Override - public TagMap.Entry next() { - return TagMap.Entry.newAnyEntry(this.wrappedIter.next()); + public TagMap.EntryReader next() { + Map.Entry entry = this.wrappedIter.next(); + this.entryReadingHelper.set(entry.getKey(), entry.getValue()); + + return this.entryReadingHelper; + } + } + + private static final class EntryIteratorImpl implements EntryIterator { + private final Iterator> wrappedIter; + private Map.Entry curEntry; + + EntryIteratorImpl(LegacyTagMap legacyMap) { + this.wrappedIter = legacyMap.entrySet().iterator(); + } + + @Override + public boolean hasNext() { + return this.wrappedIter.hasNext(); + } + + @Override + public boolean next() { + boolean hasNext = this.wrappedIter.hasNext(); + if (!hasNext) return false; + + this.curEntry = this.wrappedIter.next(); + return true; + } + + @Override + public String tag() { + return this.curEntry.getKey(); + } + + @Override + public byte type() { + return TagValueConversions.typeOf(this.curEntry.getValue()); + } + + @Override + public boolean is(byte type) { + return TagValueConversions.isA(this.curEntry.getValue(), type); + } + + @Override + public boolean isNumber() { + return TagValueConversions.isNumber(this.curEntry.getValue()); + } + + @Override + public boolean isNumericPrimitive() { + return TagValueConversions.isNumericPrimitive(this.curEntry.getValue()); + } + + @Override + public boolean isObject() { + return TagValueConversions.isObject(this.curEntry.getValue()); + } + + @Override + public TagMap.Entry entry() { + return TagMap.Entry.newAnyEntry(this.curEntry); + } + + @Override + public Map.Entry mapEntry() { + return this.curEntry; + } + + @Override + public boolean booleanValue() { + return TagValueConversions.toBoolean(this.curEntry.getValue()); + } + + @Override + public int intValue() { + return TagValueConversions.toInt(this.curEntry.getValue()); + } + + @Override + public long longValue() { + return TagValueConversions.toLong(this.curEntry.getValue()); + } + + @Override + public float floatValue() { + return TagValueConversions.toFloat(this.curEntry.getValue()); + } + + @Override + public double doubleValue() { + return TagValueConversions.toDouble(this.curEntry.getValue()); } + + @Override + public Object objectValue() { + return this.curEntry.getValue(); + } + + @Override + public String stringValue() { + return TagValueConversions.toString(this.curEntry.getValue()); + } + } +} + +final class TagValueConversions { + TagValueConversions() {} + + static byte typeOf(Object value) { + if (value instanceof Integer) { + return TagMap.EntryReader.INT; + } else if (value instanceof Long) { + return TagMap.EntryReader.LONG; + } else if (value instanceof Double) { + return TagMap.EntryReader.DOUBLE; + } else if (value instanceof Float) { + return TagMap.EntryReader.FLOAT; + } else if (value instanceof Boolean) { + return TagMap.EntryReader.BOOLEAN; + } else if (value instanceof Short) { + return TagMap.EntryReader.SHORT; + } else if (value instanceof Byte) { + return TagMap.EntryReader.BYTE; + } else if (value instanceof Character) { + return TagMap.EntryReader.CHAR; + } else { + return TagMap.EntryReader.OBJECT; + } + } + + static boolean isA(Object value, byte type) { + switch (type) { + case TagMap.EntryReader.BYTE: + return (value instanceof Byte); + + case TagMap.EntryReader.BOOLEAN: + return (value instanceof Boolean); + + case TagMap.EntryReader.CHAR: + return (value instanceof Character); + + case TagMap.EntryReader.SHORT: + return (value instanceof Short); + + case TagMap.EntryReader.INT: + return (value instanceof Integer); + + case TagMap.EntryReader.LONG: + return (value instanceof Long); + + case TagMap.EntryReader.FLOAT: + return (value instanceof Float); + + case TagMap.EntryReader.DOUBLE: + return (value instanceof Double); + + case TagMap.EntryReader.OBJECT: + return true; + + default: + return false; + } + } + + static boolean isNumber(Object value) { + return (value instanceof Number); + } + + static boolean isNumericPrimitive(Object value) { + return (value instanceof Integer) + || (value instanceof Long) + || (value instanceof Double) + || (value instanceof Float) + || (value instanceof Short) + || (value instanceof Byte); + } + + static boolean isObject(Object value) { + return (value instanceof Integer) + || (value instanceof Long) + || (value instanceof Double) + || (value instanceof Float) + || (value instanceof Boolean) + || (value instanceof Character) + || (value instanceof Short) + || (value instanceof Byte); + } + + static boolean toBooleanOrDefault(Object value, boolean defaultValue) { + return (value == null) ? defaultValue : toBoolean(value); + } + + static boolean toBoolean(Object value) { + if (value instanceof Boolean) { + return (Boolean) value; + } else if (value instanceof Number) { + return ((Number) value).intValue() != 0; + } else { + return false; + } + } + + static int toIntOrDefault(Object value, int defaultValue) { + return (value == null) ? defaultValue : toInt(value); + } + + static int toInt(Object value) { + if (value instanceof Integer) { + return (Integer) value; + } else if (value instanceof Number) { + return ((Number) value).intValue(); + } else if (value instanceof Boolean) { + return (Boolean) value ? 1 : 0; + } else { + return 0; + } + } + + static long toLong(Object value) { + if (value instanceof Long) { + return (Long) value; + } else if (value instanceof Number) { + return ((Number) value).longValue(); + } else if (value instanceof Boolean) { + return (Boolean) value ? 1L : 0L; + } else { + return 0L; + } + } + + static long toLongOrDefault(Object value, long defaultValue) { + return (value == null) ? defaultValue : toLong(value); + } + + static float toFloat(Object value) { + if (value instanceof Float) { + return (Float) value; + } else if (value instanceof Number) { + return ((Number) value).floatValue(); + } else if (value instanceof Boolean) { + return (Boolean) value ? 1F : 0F; + } else { + return 0F; + } + } + + static float toFloatOrDefault(Object value, float defaultValue) { + return (value == null) ? defaultValue : toFloat(value); + } + + static double toDouble(Object value) { + if (value instanceof Double) { + return (Double) value; + } else if (value instanceof Number) { + return ((Number) value).doubleValue(); + } else if (value instanceof Boolean) { + return (Boolean) value ? 1D : 0D; + } else { + return 0D; + } + } + + static double toDoubleOrDefault(Object value, double defaultValue) { + return (value == null) ? defaultValue : toDouble(value); + } + + static String toString(Object value) { + return value.toString(); } } diff --git a/internal-api/src/test/java/datadog/trace/api/TagMapFuzzTest.java b/internal-api/src/test/java/datadog/trace/api/TagMapFuzzTest.java index 692aaaf37f3..48254ae9bd1 100644 --- a/internal-api/src/test/java/datadog/trace/api/TagMapFuzzTest.java +++ b/internal-api/src/test/java/datadog/trace/api/TagMapFuzzTest.java @@ -1023,7 +1023,7 @@ static final void assertMapEquals(Map expected, OptimizedTagMap assertEquals(expectedEntry.getValue(), actualEntry.getValue()); } - for (TagMap.Entry actualEntry : actual) { + for (TagMap.EntryReader actualEntry : actual) { Object expectedValue = expected.get(actualEntry.tag()); assertEquals(expectedValue, actualEntry.objectValue()); } @@ -1378,7 +1378,7 @@ protected void _applyToExpectedMap(Map expectedMap) { @Override public void verifyTestMap(TagMap expectedMap) { - for (TagMap.Entry entry : this.tagMap) { + for (TagMap.EntryReader entry : this.tagMap) { assertEquals(entry.objectValue(), expectedMap.get(entry.tag()), "key=" + entry.tag()); } } @@ -1417,7 +1417,7 @@ public void verifyTestMap(TagMap expectedMap) { // ledger may contain multiple updates of the same key // easier to produce a TagMap and check against it - for (TagMap.Entry entry : this.ledger.buildImmutable()) { + for (TagMap.EntryReader entry : this.ledger.buildImmutable()) { assertEquals(entry.objectValue(), expectedMap.get(entry.tag()), "key=" + entry.tag()); } } diff --git a/internal-api/src/test/java/datadog/trace/api/TagMapTest.java b/internal-api/src/test/java/datadog/trace/api/TagMapTest.java index 0c89086d84e..f48c2adc0dd 100644 --- a/internal-api/src/test/java/datadog/trace/api/TagMapTest.java +++ b/internal-api/src/test/java/datadog/trace/api/TagMapTest.java @@ -787,7 +787,7 @@ public void iterator(TagMapType mapType) { TagMap map = createTagMap(mapType, size); Set keys = new HashSet<>(); - for (TagMap.Entry entry : map) { + for (TagMap.EntryReader entry : map) { // makes sure that each key is visited once and only once assertTrue(keys.add(entry.tag())); } From 8720c240a78d5681167bd8ad001c3860ac6a269f Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Mon, 12 Jan 2026 15:45:42 -0500 Subject: [PATCH 02/10] Adding tagIterator and valueIterator --- .../main/java/datadog/trace/api/TagMap.java | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/internal-api/src/main/java/datadog/trace/api/TagMap.java b/internal-api/src/main/java/datadog/trace/api/TagMap.java index 5423de22e66..b9113138e56 100644 --- a/internal-api/src/main/java/datadog/trace/api/TagMap.java +++ b/internal-api/src/main/java/datadog/trace/api/TagMap.java @@ -86,10 +86,14 @@ static Ledger ledger(int size) { /** Inefficiently implemented for optimized TagMap */ @Deprecated Set keySet(); + + Iterator tagIterator(); /** Inefficiently implemented for optimized TagMap - requires boxing primitives */ @Deprecated Collection values(); + + Iterator valueIterator(); // @Deprecated -- not deprecated until OptimizedTagMap becomes the default Set> entrySet(); @@ -1344,11 +1348,21 @@ public boolean containsValue(Object value) { public Set keySet() { return new Keys(this); } + + @Override + public Iterator tagIterator() { + return new KeysIterator(this); + } @Override public Collection values() { return new Values(this); } + + @Override + public Iterator valueIterator() { + return new ValuesIterator(this); + } @Override public Set> entrySet() { @@ -2940,6 +2954,16 @@ public void checkWriteAccess() { public TagMap copy() { return new LegacyTagMap(this); } + + @Override + public Iterator tagIterator() { + return this.keySet().iterator(); + } + + @Override + public Iterator valueIterator() { + return this.values().iterator(); + } @Override public void fillMap(Map map) { @@ -2959,7 +2983,9 @@ public void forEach(Consumer consumer) { // TODO: optimize to take advantage of EntryReader for (Map.Entry entry : this.entrySet()) { - consumer.accept(TagMap.Entry.newAnyEntry(entry)); + entryReadingHelper.set(entry); + + consumer.accept(entryReadingHelper); } } From fbec328efec96daa436653a3b1065c3e78e680e6 Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Mon, 12 Jan 2026 15:46:06 -0500 Subject: [PATCH 03/10] Adding checks for EntryIterator --- .../test/java/datadog/trace/api/TagMapTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/internal-api/src/test/java/datadog/trace/api/TagMapTest.java b/internal-api/src/test/java/datadog/trace/api/TagMapTest.java index f48c2adc0dd..e683b506013 100644 --- a/internal-api/src/test/java/datadog/trace/api/TagMapTest.java +++ b/internal-api/src/test/java/datadog/trace/api/TagMapTest.java @@ -20,6 +20,8 @@ import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; +import datadog.trace.api.TagMap.EntryIterator; + public class TagMapTest { // size is chosen to make sure to stress all types of collisions in the Map static final int MANY_SIZE = 256; @@ -1005,6 +1007,14 @@ static int count(Iterator iter) { } return count; } + + static int count(EntryIterator entryIter) { + int count; + for ( count = 0; entryIter.next(); ++count ) { + // nop + } + return count; + } static final void assertBoolean(boolean expected, TagMap map, String key) { assertEquals(expected, map.getBoolean(key)); @@ -1064,8 +1074,14 @@ static final void assertSize(int size, TagMap map) { assertEquals(size, count(map)); assertEquals(size, map.keySet().size()); assertEquals(size, map.values().size()); + assertEquals(size, count(map.keySet())); + assertEquals(size, count(map.tagIterator())); + + assertEquals(size, count(map.values().iterator())); assertEquals(size, count(map.values())); + + assertEquals(size, count(map.entryIterator())); } static void assertEmptiness(TagMapScenario scenario, TagMap map) { From 2864bacbb9b9dbf2e0f8662ca39f1daf6c3d9fdf Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Mon, 12 Jan 2026 16:02:27 -0500 Subject: [PATCH 04/10] Removing EntryIterator and EntryChangeIterator EntryIterator and EntryChangeIterator are arguably redundant --- .../main/java/datadog/trace/api/TagMap.java | 211 +----------------- .../java/datadog/trace/api/TagMapTest.java | 12 - 2 files changed, 1 insertion(+), 222 deletions(-) diff --git a/internal-api/src/main/java/datadog/trace/api/TagMap.java b/internal-api/src/main/java/datadog/trace/api/TagMap.java index b9113138e56..224a06e6e1e 100644 --- a/internal-api/src/main/java/datadog/trace/api/TagMap.java +++ b/internal-api/src/main/java/datadog/trace/api/TagMap.java @@ -245,8 +245,6 @@ static Ledger ledger(int size) { Stream stream(); - EntryIterator entryIterator(); - /** * Visits each Entry in this TagMap This method is more efficient than {@link TagMap#iterator()} */ @@ -364,16 +362,6 @@ interface EntryReader { Entry entry(); } - interface EntryIterator extends EntryReader { - boolean hasNext(); - - boolean next(); - } - - interface EntryChangeIterator extends EntryIterator { - boolean isRemoval(); - } - final class Entry extends EntryChange implements Map.Entry, EntryReader { /* * Special value used for Objects that haven't been type checked yet. @@ -1818,11 +1806,6 @@ public Stream stream() { return StreamSupport.stream(spliterator(), false); } - @Override - public EntryIterator entryIterator() { - return new EntryIteratorImpl(this); - } - @Override public void forEach(Consumer consumer) { Object[] thisBuckets = this.buckets; @@ -2153,96 +2136,7 @@ public EntryReader next() { return this.nextEntryOrThrowNoSuchElement(); } } - - static final class EntryIteratorImpl extends IteratorBase implements EntryIterator { - private TagMap.Entry curEntry; - - EntryIteratorImpl(OptimizedTagMap map) { - super(map); - } - - @Override - public boolean next() { - this.curEntry = this.nextEntryOrNull(); - return (this.curEntry != null); - } - - @Override - public String tag() { - return this.curEntry.tag(); - } - - @Override - public byte type() { - return this.curEntry.type(); - } - - @Override - public boolean is(byte type) { - return this.curEntry.is(type); - } - - @Override - public boolean isNumber() { - return this.curEntry.isNumber(); - } - - @Override - public boolean isNumericPrimitive() { - return this.curEntry.isNumericPrimitive(); - } - - @Override - public boolean isObject() { - return this.curEntry.isObject(); - } - - @Override - public boolean booleanValue() { - return this.curEntry.booleanValue(); - } - - @Override - public int intValue() { - return this.curEntry.intValue(); - } - - @Override - public long longValue() { - return this.curEntry.longValue(); - } - - @Override - public float floatValue() { - return this.curEntry.floatValue(); - } - - @Override - public double doubleValue() { - return this.curEntry.doubleValue(); - } - - @Override - public Object objectValue() { - return this.curEntry.objectValue(); - } - - @Override - public String stringValue() { - return this.curEntry.stringValue(); - } - - @Override - public Entry entry() { - return this.curEntry; - } - - @Override - public Map.Entry mapEntry() { - return this.curEntry; - } - } - + /** * BucketGroup is a compromise for performance over a linked list or array * @@ -3300,11 +3194,6 @@ public Stream stream() { return StreamSupport.stream(this.spliterator(), false); } - @Override - public TagMap.EntryIterator entryIterator() { - return new EntryIteratorImpl(this); - } - private static final class IteratorImpl implements Iterator { private final Iterator> wrappedIter; private final EntryReadingHelper entryReadingHelper; @@ -3327,104 +3216,6 @@ public TagMap.EntryReader next() { return this.entryReadingHelper; } } - - private static final class EntryIteratorImpl implements EntryIterator { - private final Iterator> wrappedIter; - private Map.Entry curEntry; - - EntryIteratorImpl(LegacyTagMap legacyMap) { - this.wrappedIter = legacyMap.entrySet().iterator(); - } - - @Override - public boolean hasNext() { - return this.wrappedIter.hasNext(); - } - - @Override - public boolean next() { - boolean hasNext = this.wrappedIter.hasNext(); - if (!hasNext) return false; - - this.curEntry = this.wrappedIter.next(); - return true; - } - - @Override - public String tag() { - return this.curEntry.getKey(); - } - - @Override - public byte type() { - return TagValueConversions.typeOf(this.curEntry.getValue()); - } - - @Override - public boolean is(byte type) { - return TagValueConversions.isA(this.curEntry.getValue(), type); - } - - @Override - public boolean isNumber() { - return TagValueConversions.isNumber(this.curEntry.getValue()); - } - - @Override - public boolean isNumericPrimitive() { - return TagValueConversions.isNumericPrimitive(this.curEntry.getValue()); - } - - @Override - public boolean isObject() { - return TagValueConversions.isObject(this.curEntry.getValue()); - } - - @Override - public TagMap.Entry entry() { - return TagMap.Entry.newAnyEntry(this.curEntry); - } - - @Override - public Map.Entry mapEntry() { - return this.curEntry; - } - - @Override - public boolean booleanValue() { - return TagValueConversions.toBoolean(this.curEntry.getValue()); - } - - @Override - public int intValue() { - return TagValueConversions.toInt(this.curEntry.getValue()); - } - - @Override - public long longValue() { - return TagValueConversions.toLong(this.curEntry.getValue()); - } - - @Override - public float floatValue() { - return TagValueConversions.toFloat(this.curEntry.getValue()); - } - - @Override - public double doubleValue() { - return TagValueConversions.toDouble(this.curEntry.getValue()); - } - - @Override - public Object objectValue() { - return this.curEntry.getValue(); - } - - @Override - public String stringValue() { - return TagValueConversions.toString(this.curEntry.getValue()); - } - } } final class TagValueConversions { diff --git a/internal-api/src/test/java/datadog/trace/api/TagMapTest.java b/internal-api/src/test/java/datadog/trace/api/TagMapTest.java index e683b506013..28e1b6bd682 100644 --- a/internal-api/src/test/java/datadog/trace/api/TagMapTest.java +++ b/internal-api/src/test/java/datadog/trace/api/TagMapTest.java @@ -20,8 +20,6 @@ import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; -import datadog.trace.api.TagMap.EntryIterator; - public class TagMapTest { // size is chosen to make sure to stress all types of collisions in the Map static final int MANY_SIZE = 256; @@ -1007,14 +1005,6 @@ static int count(Iterator iter) { } return count; } - - static int count(EntryIterator entryIter) { - int count; - for ( count = 0; entryIter.next(); ++count ) { - // nop - } - return count; - } static final void assertBoolean(boolean expected, TagMap map, String key) { assertEquals(expected, map.getBoolean(key)); @@ -1080,8 +1070,6 @@ static final void assertSize(int size, TagMap map) { assertEquals(size, count(map.values().iterator())); assertEquals(size, count(map.values())); - - assertEquals(size, count(map.entryIterator())); } static void assertEmptiness(TagMapScenario scenario, TagMap map) { From 43bbea18a87a85ea755c35c5cb0b618f1793947b Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Mon, 12 Jan 2026 16:03:27 -0500 Subject: [PATCH 05/10] spotless --- .../main/java/datadog/trace/api/TagMap.java | 24 +++++++++---------- .../java/datadog/trace/api/TagMapTest.java | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/internal-api/src/main/java/datadog/trace/api/TagMap.java b/internal-api/src/main/java/datadog/trace/api/TagMap.java index 224a06e6e1e..53fcbe0da18 100644 --- a/internal-api/src/main/java/datadog/trace/api/TagMap.java +++ b/internal-api/src/main/java/datadog/trace/api/TagMap.java @@ -86,13 +86,13 @@ static Ledger ledger(int size) { /** Inefficiently implemented for optimized TagMap */ @Deprecated Set keySet(); - + Iterator tagIterator(); /** Inefficiently implemented for optimized TagMap - requires boxing primitives */ @Deprecated Collection values(); - + Iterator valueIterator(); // @Deprecated -- not deprecated until OptimizedTagMap becomes the default @@ -1336,20 +1336,20 @@ public boolean containsValue(Object value) { public Set keySet() { return new Keys(this); } - + @Override public Iterator tagIterator() { - return new KeysIterator(this); + return new KeysIterator(this); } @Override public Collection values() { return new Values(this); } - + @Override public Iterator valueIterator() { - return new ValuesIterator(this); + return new ValuesIterator(this); } @Override @@ -2136,7 +2136,7 @@ public EntryReader next() { return this.nextEntryOrThrowNoSuchElement(); } } - + /** * BucketGroup is a compromise for performance over a linked list or array * @@ -2848,15 +2848,15 @@ public void checkWriteAccess() { public TagMap copy() { return new LegacyTagMap(this); } - + @Override public Iterator tagIterator() { - return this.keySet().iterator(); + return this.keySet().iterator(); } - + @Override public Iterator valueIterator() { - return this.values().iterator(); + return this.values().iterator(); } @Override @@ -2878,7 +2878,7 @@ public void forEach(Consumer consumer) { // TODO: optimize to take advantage of EntryReader for (Map.Entry entry : this.entrySet()) { entryReadingHelper.set(entry); - + consumer.accept(entryReadingHelper); } } diff --git a/internal-api/src/test/java/datadog/trace/api/TagMapTest.java b/internal-api/src/test/java/datadog/trace/api/TagMapTest.java index 28e1b6bd682..ce5a6551164 100644 --- a/internal-api/src/test/java/datadog/trace/api/TagMapTest.java +++ b/internal-api/src/test/java/datadog/trace/api/TagMapTest.java @@ -1064,10 +1064,10 @@ static final void assertSize(int size, TagMap map) { assertEquals(size, count(map)); assertEquals(size, map.keySet().size()); assertEquals(size, map.values().size()); - + assertEquals(size, count(map.keySet())); assertEquals(size, count(map.tagIterator())); - + assertEquals(size, count(map.values().iterator())); assertEquals(size, count(map.values())); } From 3c0997feb47b2869164dde4479d9d5de9fab872b Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Fri, 16 Jan 2026 09:29:18 -0500 Subject: [PATCH 06/10] Refining handling of primitive types Fixed bug TagValueConversions.toBoolean Could cause LegacyTagMap.EntryReader to produce incorrect answers to some queries For simplicity, now treating Byte and Short as Integer. That will make calling code doing primitive handling simpler. Fleshing out tests -- more tests to come --- .../main/java/datadog/trace/api/TagMap.java | 37 ++- .../datadog/trace/api/TagMapEntryTest.java | 263 +++++++++++++----- .../trace/api/TagValueConversionsTest.java | 85 ++++++ 3 files changed, 295 insertions(+), 90 deletions(-) create mode 100644 internal-api/src/test/java/datadog/trace/api/TagValueConversionsTest.java diff --git a/internal-api/src/main/java/datadog/trace/api/TagMap.java b/internal-api/src/main/java/datadog/trace/api/TagMap.java index 82a4a36d96a..edbea0e2071 100644 --- a/internal-api/src/main/java/datadog/trace/api/TagMap.java +++ b/internal-api/src/main/java/datadog/trace/api/TagMap.java @@ -319,13 +319,13 @@ interface EntryReader { * Non-numeric primitive types */ public static final byte BOOLEAN = 2; - public static final byte CHAR = 3; + static final byte CHAR_RESERVED = 3; /* * Numeric constants - deliberately arranged to allow for checking by using type >= BYTE */ - public static final byte BYTE = 4; - public static final byte SHORT = 5; + static final byte BYTE_RESERVED = 4; + static final byte SHORT_RESERVED = 5; public static final byte INT = 6; public static final byte LONG = 7; public static final byte FLOAT = 8; @@ -520,7 +520,7 @@ public boolean isNumber() { } static boolean _isNumericPrimitive(byte type) { - return (type >= BYTE); + return (type >= BYTE_RESERVED); } private byte resolveAny() { @@ -3234,11 +3234,11 @@ static byte typeOf(Object value) { } else if (value instanceof Boolean) { return TagMap.EntryReader.BOOLEAN; } else if (value instanceof Short) { - return TagMap.EntryReader.SHORT; + return TagMap.EntryReader.INT; } else if (value instanceof Byte) { - return TagMap.EntryReader.BYTE; + return TagMap.EntryReader.INT; } else if (value instanceof Character) { - return TagMap.EntryReader.CHAR; + return TagMap.EntryReader.OBJECT; } else { return TagMap.EntryReader.OBJECT; } @@ -3246,20 +3246,11 @@ static byte typeOf(Object value) { static boolean isA(Object value, byte type) { switch (type) { - case TagMap.EntryReader.BYTE: - return (value instanceof Byte); - case TagMap.EntryReader.BOOLEAN: return (value instanceof Boolean); - case TagMap.EntryReader.CHAR: - return (value instanceof Character); - - case TagMap.EntryReader.SHORT: - return (value instanceof Short); - case TagMap.EntryReader.INT: - return (value instanceof Integer); + return (value instanceof Integer) || (value instanceof Short) || (value instanceof Byte); case TagMap.EntryReader.LONG: return (value instanceof Long); @@ -3292,14 +3283,16 @@ static boolean isNumericPrimitive(Object value) { } static boolean isObject(Object value) { - return (value instanceof Integer) + boolean isSupportedPrimitive = (value instanceof Integer) || (value instanceof Long) || (value instanceof Double) || (value instanceof Float) || (value instanceof Boolean) - || (value instanceof Character) || (value instanceof Short) || (value instanceof Byte); + + // Char is just treated as Object + return !isSupportedPrimitive; } static boolean toBooleanOrDefault(Object value, boolean defaultValue) { @@ -3310,7 +3303,11 @@ static boolean toBoolean(Object value) { if (value instanceof Boolean) { return (Boolean) value; } else if (value instanceof Number) { - return ((Number) value).intValue() != 0; + // NOTE: This cannot be intValue() because intValue of larger types is 0 when + // the actual value would be less than Integer.MIN_VALUE, so using doubleValue. + + // While this is a bit ugly, coerced toBoolean is uncommon + return ((Number) value).doubleValue() != 0D; } else { return false; } diff --git a/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java b/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java index 1fc401d4db3..83f7e7497a2 100644 --- a/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java +++ b/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java @@ -1,5 +1,6 @@ package datadog.trace.api; +import static org.junit.Assert.assertSame; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -36,11 +37,11 @@ public class TagMapEntryTest { public void isNumericPrimitive() { assertFalse(TagMap.Entry._isNumericPrimitive(TagMap.Entry.ANY)); assertFalse(TagMap.Entry._isNumericPrimitive(TagMap.Entry.BOOLEAN)); - assertFalse(TagMap.Entry._isNumericPrimitive(TagMap.Entry.CHAR)); + assertFalse(TagMap.Entry._isNumericPrimitive(TagMap.Entry.CHAR_RESERVED)); assertFalse(TagMap.Entry._isNumericPrimitive(TagMap.Entry.OBJECT)); - assertTrue(TagMap.Entry._isNumericPrimitive(TagMap.Entry.BYTE)); - assertTrue(TagMap.Entry._isNumericPrimitive(TagMap.Entry.SHORT)); + assertTrue(TagMap.Entry._isNumericPrimitive(TagMap.Entry.BYTE_RESERVED)); + assertTrue(TagMap.Entry._isNumericPrimitive(TagMap.Entry.SHORT_RESERVED)); assertTrue(TagMap.Entry._isNumericPrimitive(TagMap.Entry.INT)); assertTrue(TagMap.Entry._isNumericPrimitive(TagMap.Entry.LONG)); assertTrue(TagMap.Entry._isNumericPrimitive(TagMap.Entry.FLOAT)); @@ -88,6 +89,7 @@ public void booleanEntry(boolean value) { checkValue(value, entry), checkFalse(entry::isNumericPrimitive), checkFalse(entry::isNumber), + checkFalse(entry::isObject), checkType(TagMap.Entry.BOOLEAN, entry))); } @@ -103,6 +105,7 @@ public void booleanEntry_boxed(boolean value) { checkValue(value, entry), checkFalse(entry::isNumericPrimitive), checkFalse(entry::isNumber), + checkFalse(entry::isObject), checkType(TagMap.Entry.BOOLEAN, entry))); } @@ -118,6 +121,7 @@ public void anyEntry_boolean(boolean value) { checkValue(value, entry), checkFalse(entry::isNumericPrimitive), checkFalse(entry::isNumber), + checkFalse(entry::isObject), checkType(TagMap.Entry.BOOLEAN, entry), checkValue(value, entry))); } @@ -364,15 +368,23 @@ public Thread newThread(Runnable r) { }); static final void test( - Supplier entrySupplier, byte rawType, Function checks) { + Supplier entrySupplier, byte rawType, Function checkSupplier) { + + Function combinedCheckSupplier = (entry) -> { + return multiCheck( + checkSupplier.apply(entry), + checkSame(entry, entry.entry()), + checkSame(entry, entry.mapEntry())); + }; + // repeat the test several times to exercise different orderings in this thread for (int i = 0; i < 10; ++i) { - testSingleThreaded(entrySupplier, rawType, checks); + testSingleThreaded(entrySupplier, rawType, combinedCheckSupplier); } // same for multi-threaded for (int i = 0; i < 5; ++i) { - testMultiThreaded(entrySupplier, rawType, checks); + testMultiThreaded(entrySupplier, rawType, combinedCheckSupplier); } } @@ -435,22 +447,65 @@ static final void testMultiThreaded( static final void assertChecks(Check check) { check.check(); } - + static final Check checkKey(String expected, TagMap.Entry entry) { return multiCheck(checkEquals(expected, entry::tag), checkEquals(expected, entry::getKey)); } - + + static final Supplier of(Supplier supplier, String identifier) { + return new Supplier() { + @Override + public T get() { + return supplier.get(); + } + + @Override + public String toString() { + return identifier; + } + }; + } + + static final Function of(Function func, String identifier) { + return new Function() { + @Override + public O apply(I input) { + return func.apply(input); + } + + @Override + public String toString() { + return identifier; + } + }; + } + + static final Supplier of(Function output, Supplier input) { + return of(output, input, output.toString() + "(" + input.toString() + ")"); + } + + static final Supplier of(Function output, Supplier input, String identifier) { + return of(() -> output.apply(input.get()), identifier); + } + static final Check checkIsNumericPrimitive(TagMap.Entry entry) { return multiCheck( - checkTrue(entry::isNumericPrimitive), - checkTrue(entry::isNumber), - checkInstanceOf(Number.class, entry)); + checkTrue(entry::isNumericPrimitive, "Entry::isNumericPrimitive"), + checkTrue(entry::isNumber, "Entry::isNumber"), + checkInstanceOf(Number.class, entry), + checkFalse(entry::isObject, "Entry::isObject"), + checkTrue(of(TagValueConversions::isNumber, entry::objectValue), "isNumber(Object)"), + checkTrue(of(TagValueConversions::isNumericPrimitive, entry::objectValue), "isNumericPrimitive(Object)"), + checkFalse(of(TagValueConversions::isObject, entry::objectValue), "isObject(Object)")); } static final Check checkIsBigNumber(TagMap.Entry entry) { return multiCheck( - checkFalse(entry::isNumericPrimitive), - checkTrue(entry::isNumber), + checkFalse(entry::isNumericPrimitive, "Entry::isNumericPrimitive"), + checkFalse(of(TagValueConversions::isNumericPrimitive, entry::objectValue), "isNumericPrimitive(Object)"), + checkTrue(entry::isNumber, "Entry::isNumber"), + checkTrue(entry::isObject, "Entry::isObject"), + checkTrue(of(TagValueConversions::isNumber, entry::objectValue), "isNumber(Object)"), checkInstanceOf(Number.class, entry)); } @@ -463,116 +518,184 @@ static final Check checkValue(Object expected, TagMap.Entry entry) { static final Check checkValue(boolean expected, TagMap.Entry entry) { return multiCheck( - checkEquals(expected, entry::booleanValue), - checkEquals(Boolean.valueOf(expected), entry::objectValue), - checkEquals(expected ? 1 : 0, entry::intValue), - checkEquals(expected ? 1L : 0L, entry::longValue), - checkEquals(expected ? 1D : 0D, entry::doubleValue), - checkEquals(expected ? 1F : 0F, entry::floatValue), - checkEquals(Boolean.toString(expected), entry::stringValue)); + checkEquals(expected, entry::booleanValue, "Entry::booleanValue"), + checkEquals(Boolean.valueOf(expected), entry::objectValue, "Entry::objectValue"), + checkEquals(expected ? 1 : 0, entry::intValue, "Entry::intValue"), + checkEquals(expected ? 1 : 0, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), + checkEquals(expected ? 1L : 0L, entry::longValue, "Entry::longValue"), + checkEquals(expected ? 1L : 0L, of(TagValueConversions::toLong, entry::objectValue), "toLong(Object)"), + checkEquals(expected ? 1D : 0D, entry::doubleValue, "Entry::doubleValue"), + checkEquals(expected ? 1D : 0D, of(TagValueConversions::toDouble, entry::objectValue), "toDouble(Object)"), + checkEquals(expected ? 1F : 0F, entry::floatValue, "Entry::floatValue"), + checkEquals(expected ? 1F : 0F, of(TagValueConversions::toFloat, entry::objectValue), "toFloat(Object)"), + checkEquals(Boolean.toString(expected), entry::stringValue, "Entry::stringValue"), + checkEquals(Boolean.toString(expected), of(TagValueConversions::toString, entry::objectValue), "toString(Object)")); } static final Check checkValue(int expected, TagMap.Entry entry) { return multiCheck( - checkEquals(expected, entry::intValue), - checkEquals((long) expected, entry::longValue), - checkEquals((float) expected, entry::floatValue), - checkEquals((double) expected, entry::doubleValue), - checkEquals(Integer.valueOf(expected), entry::objectValue), - checkEquals(expected != 0, entry::booleanValue), - checkEquals(Integer.toString(expected), entry::stringValue)); + checkEquals(expected, entry::intValue, "Entry::intValue"), + checkEquals(Integer.valueOf(expected), entry::objectValue, "Entry::objectValue"), + checkEquals((long) expected, entry::longValue, "Entry::longValue"), + checkEquals((long)expected, of(TagValueConversions::toLong, entry::objectValue), "toLong(Object)"), + checkEquals((float) expected, entry::floatValue, "Entry::floatValue"), + checkEquals((float)expected, of(TagValueConversions::toFloat, entry::objectValue), "toFloat(Object)"), + checkEquals((double) expected, entry::doubleValue, "Entry::doubleValue"), + checkEquals((double)expected, of(TagValueConversions::toDouble, entry::objectValue), "toDouble(Object)"), + checkEquals(expected != 0, entry::booleanValue, "Entry::booleanValue"), + checkEquals(expected != 0, of(TagValueConversions::toBoolean, entry::objectValue), "toBoolean(Object)"), + checkEquals(Integer.toString(expected), entry::stringValue, "Entry::stringValue"), + checkEquals(Integer.toString(expected), of(TagValueConversions::toString, entry::objectValue), "toString(Object)")); } static final Check checkValue(long expected, TagMap.Entry entry) { return multiCheck( - checkEquals(expected, entry::longValue), - checkEquals((int) expected, entry::intValue), - checkEquals((float) expected, entry::floatValue), - checkEquals((double) expected, entry::doubleValue), - checkEquals(Long.valueOf(expected), entry::objectValue), - checkEquals(expected != 0L, entry::booleanValue), - checkEquals(Long.toString(expected), entry::stringValue)); + checkEquals(expected, entry::longValue, "Entry::longValue"), + checkEquals(Long.valueOf(expected), entry::objectValue, "Entry::objectValue"), + checkEquals((int) expected, entry::intValue, "Entry::intValue"), + checkEquals((int)expected, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), + checkEquals((float) expected, entry::floatValue, "Entry::floatValue"), + checkEquals((float)expected, of(TagValueConversions::toFloat, entry::objectValue), "toFloat(Object)"), + checkEquals((double) expected, entry::doubleValue, "Entry::doubleValue"), + checkEquals((double)expected, of(TagValueConversions::toDouble, entry::objectValue), "toDouble(Object)"), + checkEquals(expected != 0L, entry::booleanValue, "Entry::booleanValue"), + checkEquals(expected != 0L, of(TagValueConversions::toBoolean, entry::objectValue), "toBoolean(Object)"), + checkEquals(Long.toString(expected), entry::stringValue, "Entry::stringValue"), + checkEquals(Long.toString(expected), of(TagValueConversions::toString, entry::objectValue), "toString(Object)")); } static final Check checkValue(double expected, TagMap.Entry entry) { return multiCheck( - checkEquals(expected, entry::doubleValue), - checkEquals((int) expected, entry::intValue), - checkEquals((long) expected, entry::longValue), - checkEquals((float) expected, entry::floatValue), - checkEquals(Double.valueOf(expected), entry::objectValue), - checkEquals(expected != 0D, entry::booleanValue), - checkEquals(Double.toString(expected), entry::stringValue)); + checkEquals(expected, entry::doubleValue, "Entry::doubleValue"), + checkEquals(Double.valueOf(expected), entry::objectValue, "Entry::objectValue"), + checkEquals((int) expected, entry::intValue, "Entry::intValue"), + checkEquals((int)expected, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), + checkEquals((long) expected, entry::longValue, "Entry::longValue"), + checkEquals((long)expected, of(TagValueConversions::toLong, entry::objectValue), "toLong(Object)"), + checkEquals((float) expected, entry::floatValue, "Entry::floatValue"), + checkEquals((float)expected, of(TagValueConversions::toFloat, entry::objectValue), "toFloat(Object)"), + checkEquals(expected != 0D, entry::booleanValue, "Entry::booleanValue"), + checkEquals(expected != 0D, of(TagValueConversions::toBoolean, entry::objectValue), "toBoolean(Object)"), + checkEquals(Double.toString(expected), entry::stringValue, "Entry::stringValue"), + checkEquals(Double.toString(expected), of(TagValueConversions::toString, entry::objectValue), "toString(Object)")); + } + + static final Check checkValue(float expected, TagMap.Entry entry) { + return multiCheck( + checkEquals(expected, entry::floatValue, "Entry::floatValue"), + checkEquals(Float.valueOf(expected), entry::objectValue, "Entry::objectValue"), + checkEquals((int) expected, entry::intValue, "Entry::intValue"), + checkEquals((int)expected, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), + checkEquals((long) expected, entry::longValue, "Entry::longValue"), + checkEquals((long)expected, of(TagValueConversions::toLong, entry::objectValue), "toLong(Object)"), + checkEquals((double) expected, entry::doubleValue, "Entry::doubleValue"), + checkEquals((double)expected, of(TagValueConversions::toDouble, entry::objectValue), "toDouble(Object)"), + checkEquals(expected != 0F, entry::booleanValue, "Entry::booleanValue"), + checkEquals(expected != 0F, of(TagValueConversions::toBoolean, entry::objectValue), "toBoolean(Object)"), + checkEquals(Float.toString(expected), entry::stringValue, "Entry::stringValue"), + checkEquals(Float.toString(expected), of(TagValueConversions::toString, entry::objectValue), "toString(Object)")); } public static Check checkNumber(Number number, TagMap.Entry entry) { return multiCheck( checkEquals(number, entry::objectValue), checkEquals(number.intValue(), entry::intValue), + checkEquals(number.intValue(), of(TagValueConversions::toInt, entry::objectValue)), checkEquals(number.longValue(), entry::longValue), + checkEquals(number.longValue(), of(TagValueConversions::toLong, entry::objectValue)), checkEquals(number.floatValue(), entry::floatValue), + checkEquals(number.floatValue(), of(TagValueConversions::toFloat, entry::objectValue)), checkEquals(number.doubleValue(), entry::doubleValue), - checkEquals(number.toString(), entry::stringValue)); - } - - static final Check checkValue(float expected, TagMap.Entry entry) { - return multiCheck( - checkEquals(expected, entry::floatValue), - checkEquals((int) expected, entry::intValue), - checkEquals((long) expected, entry::longValue), - checkEquals((double) expected, entry::doubleValue), - checkEquals(expected != 0F, entry::booleanValue), - checkEquals(Float.valueOf(expected), entry::objectValue), - checkEquals(Float.toString(expected), entry::stringValue)); + checkEquals(number.doubleValue(), of(TagValueConversions::toDouble, entry::objectValue)), + checkEquals(number.toString(), entry::stringValue), + checkEquals(number.toString(), of(TagValueConversions::toString, entry::objectValue))); } static final Check checkInstanceOf(Class klass, TagMap.Entry entry) { - return () -> - assertTrue( - klass.isAssignableFrom(entry.objectValue().getClass()), + return checkTrue( + () -> klass.isAssignableFrom(entry.objectValue().getClass()), "instanceof " + klass.getSimpleName()); } static final Check checkType(byte entryType, TagMap.Entry entry) { - return () -> assertTrue(entry.is(entryType), "type is " + entryType); + // TODO: TVC checks + return multiCheck( + checkTrue(() -> entry.is(entryType), "type is " + entryType), + checkEquals(entryType, entry::type)); } static final Check multiCheck(Check... checks) { return new MultipartCheck(checks); } + + static final Check checkSame(Object expected, Object actual) { + return () -> assertSame(expected, actual); + } static final Check checkFalse(Supplier actual) { - return () -> assertFalse(actual.get(), actual.toString()); + return checkFalse(actual, actual.toString()); + } + + static final Check checkFalse(Supplier actual, String identifier) { + return () -> assertFalse(actual.get(), identifier); } static final Check checkTrue(Supplier actual) { - return () -> assertTrue(actual.get(), actual.toString()); + return checkTrue(actual, actual.toString()); + } + + static final Check checkTrue(Supplier actual, String identifier) { + return () -> assertTrue(actual.get(), identifier); } static final Check checkEquals(float expected, Supplier actual) { - return () -> assertEquals(expected, actual.get().floatValue(), actual.toString()); + return checkEquals(expected, actual, actual.toString()); } - + + static final Check checkEquals(float expected, Supplier actual, String identifier) { + return () -> assertEquals(expected, actual.get().floatValue(), identifier); + } + static final Check checkEquals(int expected, Supplier actual) { - return () -> assertEquals(expected, actual.get().intValue(), actual.toString()); + return checkEquals(expected, actual, actual.toString()); } - + + static final Check checkEquals(int expected, Supplier actual, String identifier) { + return () -> assertEquals(expected, actual.get().intValue(), identifier); + } + static final Check checkEquals(double expected, Supplier actual) { - return () -> assertEquals(expected, actual.get().doubleValue(), actual.toString()); + return checkEquals(expected, actual, actual.toString()); + } + + static final Check checkEquals(double expected, Supplier actual, String identifier) { + return () -> assertEquals(expected, actual.get().doubleValue(), identifier); } static final Check checkEquals(long expected, Supplier actual) { - return () -> assertEquals(expected, actual.get().longValue(), actual.toString()); + return checkEquals(expected, actual, actual.toString()); + } + + static final Check checkEquals(long expected, Supplier actual, String identifier) { + return () -> assertEquals(expected, actual.get().longValue(), identifier); } static final Check checkEquals(boolean expected, Supplier actual) { - return () -> assertEquals(expected, actual.get().booleanValue(), actual.toString()); + return checkEquals(expected, actual, actual.toString()); } - - static final Check checkEquals(Object expected, Supplier actual) { - return () -> assertEquals(expected, actual.get(), actual.toString()); + + static final Check checkEquals(boolean expected, Supplier actual, String identifier) { + return () -> assertEquals(expected, actual.get().booleanValue(), identifier); } - + + static final Check checkEquals(T expected, Supplier actual) { + return checkEquals(expected, actual, actual.toString()); + } + + static final Check checkEquals(T expected, Supplier actual, String identifier) { + return () -> assertEquals(expected, actual.get(), identifier); + } + @FunctionalInterface interface Check { void check(); diff --git a/internal-api/src/test/java/datadog/trace/api/TagValueConversionsTest.java b/internal-api/src/test/java/datadog/trace/api/TagValueConversionsTest.java new file mode 100644 index 00000000000..c597b4eccd0 --- /dev/null +++ b/internal-api/src/test/java/datadog/trace/api/TagValueConversionsTest.java @@ -0,0 +1,85 @@ +package datadog.trace.api; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class TagValueConversionsTest { + @ParameterizedTest + @ValueSource(booleans = {false, true}) + public void boolean_(boolean value) { + Boolean box = Boolean.valueOf(value); + + assertEquals(TagMap.EntryReader.BOOLEAN, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.BOOLEAN)); + assertFalse(TagValueConversions.isNumericPrimitive(box)); + assertFalse(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals(value, TagValueConversions.toBoolean(box)); + assertEquals(value ? 1 : 0, TagValueConversions.toInt(box)); + assertEquals(value ? 1L : 0L, TagValueConversions.toLong(box)); + assertEquals(value ? 1F : 0F, TagValueConversions.toFloat(box)); + assertEquals(value ? 1D : 0D, TagValueConversions.toDouble(box)); + + assertEquals(Boolean.toString(value), TagValueConversions.toString(box)); + } + + @ParameterizedTest + @ValueSource(ints = {Integer.MIN_VALUE, -256, -128, -1, 0, 1, 128, 256, Integer.MAX_VALUE}) + public void int_(int value) { + Integer box = Integer.valueOf(value); + + assertEquals(TagMap.EntryReader.INT, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.INT)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals(value, TagValueConversions.toInt(box)); + assertEquals((long)value, TagValueConversions.toLong(box)); + assertEquals((float)value, TagValueConversions.toFloat(box)); + assertEquals((double)value, TagValueConversions.toDouble(box)); + + assertEquals(value != 0, TagValueConversions.toBoolean(box)); + assertEquals(Integer.toString(value), TagValueConversions.toString(box)); + } + + @ParameterizedTest + @ValueSource( + longs = { + Long.MIN_VALUE, + Integer.MIN_VALUE, + -1_048_576L, + -256L, + -128L, + -1L, + 0L, + 1L, + 128L, + 256L, + 1_048_576L, + Integer.MAX_VALUE, + Long.MAX_VALUE + }) + public void long_(long value) { + Long box = Long.valueOf(value); + + assertEquals(TagMap.EntryReader.LONG, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.LONG)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals(value, TagValueConversions.toLong(box)); + assertEquals((int)value, TagValueConversions.toInt(box)); + assertEquals((float)value, TagValueConversions.toFloat(box)); + assertEquals((double)value, TagValueConversions.toDouble(box)); + + assertEquals(value != 0L, TagValueConversions.toBoolean(box)); + assertEquals(Long.toString(value), TagValueConversions.toString(box)); + } +} From b192679cc90e3e0fcd684441e48ddf632d4eb22b Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Fri, 16 Jan 2026 09:30:50 -0500 Subject: [PATCH 07/10] Refining comment in toBoolean --- internal-api/src/main/java/datadog/trace/api/TagMap.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal-api/src/main/java/datadog/trace/api/TagMap.java b/internal-api/src/main/java/datadog/trace/api/TagMap.java index edbea0e2071..8feba42d8ba 100644 --- a/internal-api/src/main/java/datadog/trace/api/TagMap.java +++ b/internal-api/src/main/java/datadog/trace/api/TagMap.java @@ -3304,7 +3304,8 @@ static boolean toBoolean(Object value) { return (Boolean) value; } else if (value instanceof Number) { // NOTE: This cannot be intValue() because intValue of larger types is 0 when - // the actual value would be less than Integer.MIN_VALUE, so using doubleValue. + // the actual value would be less than Integer.MIN_VALUE or for floating point + // types is very close to zero, so using doubleValue instead. // While this is a bit ugly, coerced toBoolean is uncommon return ((Number) value).doubleValue() != 0D; From cffac879522de27d90ebbdc3793ac2b7467c154e Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Fri, 16 Jan 2026 09:39:14 -0500 Subject: [PATCH 08/10] Adding more TagValueConversionTest-s Coverage for byte, short, float, and double --- .../trace/api/TagValueConversionsTest.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/internal-api/src/test/java/datadog/trace/api/TagValueConversionsTest.java b/internal-api/src/test/java/datadog/trace/api/TagValueConversionsTest.java index c597b4eccd0..7aa191fc81e 100644 --- a/internal-api/src/test/java/datadog/trace/api/TagValueConversionsTest.java +++ b/internal-api/src/test/java/datadog/trace/api/TagValueConversionsTest.java @@ -48,6 +48,46 @@ public void int_(int value) { assertEquals(Integer.toString(value), TagValueConversions.toString(box)); } + @ParameterizedTest + @ValueSource(bytes = {Byte.MIN_VALUE, -32, -1, 0, 1, 32, Byte.MAX_VALUE}) + public void byte_(byte value) { + Byte box = Byte.valueOf(value); + + assertEquals(TagMap.EntryReader.INT, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.INT)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals((int)value, TagValueConversions.toInt(box)); + assertEquals((long)value, TagValueConversions.toLong(box)); + assertEquals((float)value, TagValueConversions.toFloat(box)); + assertEquals((double)value, TagValueConversions.toDouble(box)); + + assertEquals(value != 0, TagValueConversions.toBoolean(box)); + assertEquals(Byte.toString(value), TagValueConversions.toString(box)); + } + + @ParameterizedTest + @ValueSource(shorts = {Short.MIN_VALUE, -256, -128, -1, 0, 1, 128, 256, Short.MAX_VALUE}) + public void short_(short value) { + Short box = Short.valueOf(value); + + assertEquals(TagMap.EntryReader.INT, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.INT)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals((int)value, TagValueConversions.toInt(box)); + assertEquals((long)value, TagValueConversions.toLong(box)); + assertEquals((float)value, TagValueConversions.toFloat(box)); + assertEquals((double)value, TagValueConversions.toDouble(box)); + + assertEquals(value != 0, TagValueConversions.toBoolean(box)); + assertEquals(Short.toString(value), TagValueConversions.toString(box)); + } + @ParameterizedTest @ValueSource( longs = { @@ -82,4 +122,45 @@ public void long_(long value) { assertEquals(value != 0L, TagValueConversions.toBoolean(box)); assertEquals(Long.toString(value), TagValueConversions.toString(box)); } + + @ParameterizedTest + @ValueSource(floats = {Float.MIN_VALUE, -1F, 0F, 1F, 2.171828F, 3.1415F, Float.MAX_VALUE}) + public void float_(float value) { + Float box = Float.valueOf(value); + + assertEquals(TagMap.EntryReader.FLOAT, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.FLOAT)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals(value, TagValueConversions.toFloat(box)); + assertEquals((int)value, TagValueConversions.toInt(box)); + assertEquals((long)value, TagValueConversions.toLong(box)); + assertEquals((double)value, TagValueConversions.toDouble(box)); + + assertEquals(value != 0F, TagValueConversions.toBoolean(box)); + assertEquals(Float.toString(value), TagValueConversions.toString(box)); + } + + @ParameterizedTest + @ValueSource( + doubles = {Double.MIN_VALUE, Float.MIN_VALUE, -1D, 0D, 1D, Math.E, Math.PI, Double.MAX_VALUE}) + public void double_(double value) { + Double box = Double.valueOf(value); + + assertEquals(TagMap.EntryReader.DOUBLE, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.DOUBLE)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals(value, TagValueConversions.toDouble(box)); + assertEquals((int)value, TagValueConversions.toInt(box)); + assertEquals((long)value, TagValueConversions.toLong(box)); + assertEquals((float)value, TagValueConversions.toFloat(box)); + + assertEquals(value != 0D, TagValueConversions.toBoolean(box)); + assertEquals(Double.toString(value), TagValueConversions.toString(box)); + } } From 8c51f7ff4627401717bcbd943a81dc20f4e210ab Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Fri, 16 Jan 2026 09:43:32 -0500 Subject: [PATCH 09/10] Adding Entry tests for byte and short boxes --- .../datadog/trace/api/TagMapEntryTest.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java b/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java index 83f7e7497a2..6a725187054 100644 --- a/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java +++ b/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java @@ -155,6 +155,36 @@ public void intEntry_boxed(int value) { checkInstanceOf(Number.class, entry), checkType(TagMap.Entry.INT, entry))); } + + @ParameterizedTest + @ValueSource(shorts = {Short.MIN_VALUE, -256, -128, -1, 0, 1, 128, 256, Short.MAX_VALUE}) + public void intEntry_boxedShort(short value) { + test( + () -> TagMap.Entry.newIntEntry("foo", Short.valueOf(value)), + TagMap.Entry.INT, + (entry) -> + multiCheck( + checkKey("foo", entry), + checkValue(value, entry), + checkIsNumericPrimitive(entry), + checkInstanceOf(Number.class, entry), + checkType(TagMap.Entry.INT, entry))); + } + + @ParameterizedTest + @ValueSource(bytes = {Byte.MIN_VALUE, -32, -1, 0, 1, 32, Byte.MAX_VALUE}) + public void intEntry_boxedByte(byte value) { + test( + () -> TagMap.Entry.newIntEntry("foo", Byte.valueOf(value)), + TagMap.Entry.INT, + (entry) -> + multiCheck( + checkKey("foo", entry), + checkValue(value, entry), + checkIsNumericPrimitive(entry), + checkInstanceOf(Number.class, entry), + checkType(TagMap.Entry.INT, entry))); + } @ParameterizedTest @ValueSource(ints = {Integer.MIN_VALUE, -256, -128, -1, 0, 1, 128, 256, Integer.MAX_VALUE}) From 17b1255ca38be934085e653f32cac6dcca1ecd6d Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Fri, 16 Jan 2026 10:11:16 -0500 Subject: [PATCH 10/10] spotless --- .../main/java/datadog/trace/api/TagMap.java | 23 +- .../datadog/trace/api/TagMapEntryTest.java | 246 +++++++++++------- .../trace/api/TagValueConversionsTest.java | 222 ++++++++-------- 3 files changed, 279 insertions(+), 212 deletions(-) diff --git a/internal-api/src/main/java/datadog/trace/api/TagMap.java b/internal-api/src/main/java/datadog/trace/api/TagMap.java index 8feba42d8ba..10fe96c5fca 100644 --- a/internal-api/src/main/java/datadog/trace/api/TagMap.java +++ b/internal-api/src/main/java/datadog/trace/api/TagMap.java @@ -3283,14 +3283,15 @@ static boolean isNumericPrimitive(Object value) { } static boolean isObject(Object value) { - boolean isSupportedPrimitive = (value instanceof Integer) - || (value instanceof Long) - || (value instanceof Double) - || (value instanceof Float) - || (value instanceof Boolean) - || (value instanceof Short) - || (value instanceof Byte); - + boolean isSupportedPrimitive = + (value instanceof Integer) + || (value instanceof Long) + || (value instanceof Double) + || (value instanceof Float) + || (value instanceof Boolean) + || (value instanceof Short) + || (value instanceof Byte); + // Char is just treated as Object return !isSupportedPrimitive; } @@ -3303,10 +3304,10 @@ static boolean toBoolean(Object value) { if (value instanceof Boolean) { return (Boolean) value; } else if (value instanceof Number) { - // NOTE: This cannot be intValue() because intValue of larger types is 0 when - // the actual value would be less than Integer.MIN_VALUE or for floating point + // NOTE: This cannot be intValue() because intValue of larger types is 0 when + // the actual value would be less than Integer.MIN_VALUE or for floating point // types is very close to zero, so using doubleValue instead. - + // While this is a bit ugly, coerced toBoolean is uncommon return ((Number) value).doubleValue() != 0D; } else { diff --git a/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java b/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java index 6a725187054..13da8723b60 100644 --- a/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java +++ b/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java @@ -155,7 +155,7 @@ public void intEntry_boxed(int value) { checkInstanceOf(Number.class, entry), checkType(TagMap.Entry.INT, entry))); } - + @ParameterizedTest @ValueSource(shorts = {Short.MIN_VALUE, -256, -128, -1, 0, 1, 128, 256, Short.MAX_VALUE}) public void intEntry_boxedShort(short value) { @@ -170,7 +170,7 @@ public void intEntry_boxedShort(short value) { checkInstanceOf(Number.class, entry), checkType(TagMap.Entry.INT, entry))); } - + @ParameterizedTest @ValueSource(bytes = {Byte.MIN_VALUE, -32, -1, 0, 1, 32, Byte.MAX_VALUE}) public void intEntry_boxedByte(byte value) { @@ -400,13 +400,14 @@ public Thread newThread(Runnable r) { static final void test( Supplier entrySupplier, byte rawType, Function checkSupplier) { - Function combinedCheckSupplier = (entry) -> { - return multiCheck( - checkSupplier.apply(entry), - checkSame(entry, entry.entry()), - checkSame(entry, entry.mapEntry())); - }; - + Function combinedCheckSupplier = + (entry) -> { + return multiCheck( + checkSupplier.apply(entry), + checkSame(entry, entry.entry()), + checkSame(entry, entry.mapEntry())); + }; + // repeat the test several times to exercise different orderings in this thread for (int i = 0; i < 10; ++i) { testSingleThreaded(entrySupplier, rawType, combinedCheckSupplier); @@ -477,47 +478,47 @@ static final void testMultiThreaded( static final void assertChecks(Check check) { check.check(); } - + static final Check checkKey(String expected, TagMap.Entry entry) { return multiCheck(checkEquals(expected, entry::tag), checkEquals(expected, entry::getKey)); } - + static final Supplier of(Supplier supplier, String identifier) { - return new Supplier() { - @Override - public T get() { - return supplier.get(); - } - - @Override - public String toString() { - return identifier; - } - }; - } - + return new Supplier() { + @Override + public T get() { + return supplier.get(); + } + + @Override + public String toString() { + return identifier; + } + }; + } + static final Function of(Function func, String identifier) { - return new Function() { - @Override - public O apply(I input) { - return func.apply(input); - } - - @Override - public String toString() { - return identifier; - } - }; - } - + return new Function() { + @Override + public O apply(I input) { + return func.apply(input); + } + + @Override + public String toString() { + return identifier; + } + }; + } + static final Supplier of(Function output, Supplier input) { - return of(output, input, output.toString() + "(" + input.toString() + ")"); + return of(output, input, output.toString() + "(" + input.toString() + ")"); } - + static final Supplier of(Function output, Supplier input, String identifier) { - return of(() -> output.apply(input.get()), identifier); + return of(() -> output.apply(input.get()), identifier); } - + static final Check checkIsNumericPrimitive(TagMap.Entry entry) { return multiCheck( checkTrue(entry::isNumericPrimitive, "Entry::isNumericPrimitive"), @@ -525,14 +526,18 @@ static final Check checkIsNumericPrimitive(TagMap.Entry entry) { checkInstanceOf(Number.class, entry), checkFalse(entry::isObject, "Entry::isObject"), checkTrue(of(TagValueConversions::isNumber, entry::objectValue), "isNumber(Object)"), - checkTrue(of(TagValueConversions::isNumericPrimitive, entry::objectValue), "isNumericPrimitive(Object)"), + checkTrue( + of(TagValueConversions::isNumericPrimitive, entry::objectValue), + "isNumericPrimitive(Object)"), checkFalse(of(TagValueConversions::isObject, entry::objectValue), "isObject(Object)")); } static final Check checkIsBigNumber(TagMap.Entry entry) { return multiCheck( checkFalse(entry::isNumericPrimitive, "Entry::isNumericPrimitive"), - checkFalse(of(TagValueConversions::isNumericPrimitive, entry::objectValue), "isNumericPrimitive(Object)"), + checkFalse( + of(TagValueConversions::isNumericPrimitive, entry::objectValue), + "isNumericPrimitive(Object)"), checkTrue(entry::isNumber, "Entry::isNumber"), checkTrue(entry::isObject, "Entry::isObject"), checkTrue(of(TagValueConversions::isNumber, entry::objectValue), "isNumber(Object)"), @@ -551,15 +556,28 @@ static final Check checkValue(boolean expected, TagMap.Entry entry) { checkEquals(expected, entry::booleanValue, "Entry::booleanValue"), checkEquals(Boolean.valueOf(expected), entry::objectValue, "Entry::objectValue"), checkEquals(expected ? 1 : 0, entry::intValue, "Entry::intValue"), - checkEquals(expected ? 1 : 0, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), + checkEquals( + expected ? 1 : 0, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), checkEquals(expected ? 1L : 0L, entry::longValue, "Entry::longValue"), - checkEquals(expected ? 1L : 0L, of(TagValueConversions::toLong, entry::objectValue), "toLong(Object)"), + checkEquals( + expected ? 1L : 0L, + of(TagValueConversions::toLong, entry::objectValue), + "toLong(Object)"), checkEquals(expected ? 1D : 0D, entry::doubleValue, "Entry::doubleValue"), - checkEquals(expected ? 1D : 0D, of(TagValueConversions::toDouble, entry::objectValue), "toDouble(Object)"), + checkEquals( + expected ? 1D : 0D, + of(TagValueConversions::toDouble, entry::objectValue), + "toDouble(Object)"), checkEquals(expected ? 1F : 0F, entry::floatValue, "Entry::floatValue"), - checkEquals(expected ? 1F : 0F, of(TagValueConversions::toFloat, entry::objectValue), "toFloat(Object)"), + checkEquals( + expected ? 1F : 0F, + of(TagValueConversions::toFloat, entry::objectValue), + "toFloat(Object)"), checkEquals(Boolean.toString(expected), entry::stringValue, "Entry::stringValue"), - checkEquals(Boolean.toString(expected), of(TagValueConversions::toString, entry::objectValue), "toString(Object)")); + checkEquals( + Boolean.toString(expected), + of(TagValueConversions::toString, entry::objectValue), + "toString(Object)")); } static final Check checkValue(int expected, TagMap.Entry entry) { @@ -567,15 +585,28 @@ static final Check checkValue(int expected, TagMap.Entry entry) { checkEquals(expected, entry::intValue, "Entry::intValue"), checkEquals(Integer.valueOf(expected), entry::objectValue, "Entry::objectValue"), checkEquals((long) expected, entry::longValue, "Entry::longValue"), - checkEquals((long)expected, of(TagValueConversions::toLong, entry::objectValue), "toLong(Object)"), + checkEquals( + (long) expected, of(TagValueConversions::toLong, entry::objectValue), "toLong(Object)"), checkEquals((float) expected, entry::floatValue, "Entry::floatValue"), - checkEquals((float)expected, of(TagValueConversions::toFloat, entry::objectValue), "toFloat(Object)"), + checkEquals( + (float) expected, + of(TagValueConversions::toFloat, entry::objectValue), + "toFloat(Object)"), checkEquals((double) expected, entry::doubleValue, "Entry::doubleValue"), - checkEquals((double)expected, of(TagValueConversions::toDouble, entry::objectValue), "toDouble(Object)"), + checkEquals( + (double) expected, + of(TagValueConversions::toDouble, entry::objectValue), + "toDouble(Object)"), checkEquals(expected != 0, entry::booleanValue, "Entry::booleanValue"), - checkEquals(expected != 0, of(TagValueConversions::toBoolean, entry::objectValue), "toBoolean(Object)"), + checkEquals( + expected != 0, + of(TagValueConversions::toBoolean, entry::objectValue), + "toBoolean(Object)"), checkEquals(Integer.toString(expected), entry::stringValue, "Entry::stringValue"), - checkEquals(Integer.toString(expected), of(TagValueConversions::toString, entry::objectValue), "toString(Object)")); + checkEquals( + Integer.toString(expected), + of(TagValueConversions::toString, entry::objectValue), + "toString(Object)")); } static final Check checkValue(long expected, TagMap.Entry entry) { @@ -583,15 +614,28 @@ static final Check checkValue(long expected, TagMap.Entry entry) { checkEquals(expected, entry::longValue, "Entry::longValue"), checkEquals(Long.valueOf(expected), entry::objectValue, "Entry::objectValue"), checkEquals((int) expected, entry::intValue, "Entry::intValue"), - checkEquals((int)expected, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), + checkEquals( + (int) expected, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), checkEquals((float) expected, entry::floatValue, "Entry::floatValue"), - checkEquals((float)expected, of(TagValueConversions::toFloat, entry::objectValue), "toFloat(Object)"), + checkEquals( + (float) expected, + of(TagValueConversions::toFloat, entry::objectValue), + "toFloat(Object)"), checkEquals((double) expected, entry::doubleValue, "Entry::doubleValue"), - checkEquals((double)expected, of(TagValueConversions::toDouble, entry::objectValue), "toDouble(Object)"), + checkEquals( + (double) expected, + of(TagValueConversions::toDouble, entry::objectValue), + "toDouble(Object)"), checkEquals(expected != 0L, entry::booleanValue, "Entry::booleanValue"), - checkEquals(expected != 0L, of(TagValueConversions::toBoolean, entry::objectValue), "toBoolean(Object)"), + checkEquals( + expected != 0L, + of(TagValueConversions::toBoolean, entry::objectValue), + "toBoolean(Object)"), checkEquals(Long.toString(expected), entry::stringValue, "Entry::stringValue"), - checkEquals(Long.toString(expected), of(TagValueConversions::toString, entry::objectValue), "toString(Object)")); + checkEquals( + Long.toString(expected), + of(TagValueConversions::toString, entry::objectValue), + "toString(Object)")); } static final Check checkValue(double expected, TagMap.Entry entry) { @@ -599,31 +643,53 @@ static final Check checkValue(double expected, TagMap.Entry entry) { checkEquals(expected, entry::doubleValue, "Entry::doubleValue"), checkEquals(Double.valueOf(expected), entry::objectValue, "Entry::objectValue"), checkEquals((int) expected, entry::intValue, "Entry::intValue"), - checkEquals((int)expected, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), + checkEquals( + (int) expected, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), checkEquals((long) expected, entry::longValue, "Entry::longValue"), - checkEquals((long)expected, of(TagValueConversions::toLong, entry::objectValue), "toLong(Object)"), + checkEquals( + (long) expected, of(TagValueConversions::toLong, entry::objectValue), "toLong(Object)"), checkEquals((float) expected, entry::floatValue, "Entry::floatValue"), - checkEquals((float)expected, of(TagValueConversions::toFloat, entry::objectValue), "toFloat(Object)"), + checkEquals( + (float) expected, + of(TagValueConversions::toFloat, entry::objectValue), + "toFloat(Object)"), checkEquals(expected != 0D, entry::booleanValue, "Entry::booleanValue"), - checkEquals(expected != 0D, of(TagValueConversions::toBoolean, entry::objectValue), "toBoolean(Object)"), + checkEquals( + expected != 0D, + of(TagValueConversions::toBoolean, entry::objectValue), + "toBoolean(Object)"), checkEquals(Double.toString(expected), entry::stringValue, "Entry::stringValue"), - checkEquals(Double.toString(expected), of(TagValueConversions::toString, entry::objectValue), "toString(Object)")); + checkEquals( + Double.toString(expected), + of(TagValueConversions::toString, entry::objectValue), + "toString(Object)")); } - + static final Check checkValue(float expected, TagMap.Entry entry) { return multiCheck( checkEquals(expected, entry::floatValue, "Entry::floatValue"), checkEquals(Float.valueOf(expected), entry::objectValue, "Entry::objectValue"), checkEquals((int) expected, entry::intValue, "Entry::intValue"), - checkEquals((int)expected, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), + checkEquals( + (int) expected, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), checkEquals((long) expected, entry::longValue, "Entry::longValue"), - checkEquals((long)expected, of(TagValueConversions::toLong, entry::objectValue), "toLong(Object)"), + checkEquals( + (long) expected, of(TagValueConversions::toLong, entry::objectValue), "toLong(Object)"), checkEquals((double) expected, entry::doubleValue, "Entry::doubleValue"), - checkEquals((double)expected, of(TagValueConversions::toDouble, entry::objectValue), "toDouble(Object)"), + checkEquals( + (double) expected, + of(TagValueConversions::toDouble, entry::objectValue), + "toDouble(Object)"), checkEquals(expected != 0F, entry::booleanValue, "Entry::booleanValue"), - checkEquals(expected != 0F, of(TagValueConversions::toBoolean, entry::objectValue), "toBoolean(Object)"), + checkEquals( + expected != 0F, + of(TagValueConversions::toBoolean, entry::objectValue), + "toBoolean(Object)"), checkEquals(Float.toString(expected), entry::stringValue, "Entry::stringValue"), - checkEquals(Float.toString(expected), of(TagValueConversions::toString, entry::objectValue), "toString(Object)")); + checkEquals( + Float.toString(expected), + of(TagValueConversions::toString, entry::objectValue), + "toString(Object)")); } public static Check checkNumber(Number number, TagMap.Entry entry) { @@ -643,59 +709,59 @@ public static Check checkNumber(Number number, TagMap.Entry entry) { static final Check checkInstanceOf(Class klass, TagMap.Entry entry) { return checkTrue( - () -> klass.isAssignableFrom(entry.objectValue().getClass()), - "instanceof " + klass.getSimpleName()); + () -> klass.isAssignableFrom(entry.objectValue().getClass()), + "instanceof " + klass.getSimpleName()); } static final Check checkType(byte entryType, TagMap.Entry entry) { - // TODO: TVC checks + // TODO: TVC checks return multiCheck( - checkTrue(() -> entry.is(entryType), "type is " + entryType), - checkEquals(entryType, entry::type)); + checkTrue(() -> entry.is(entryType), "type is " + entryType), + checkEquals(entryType, entry::type)); } static final Check multiCheck(Check... checks) { return new MultipartCheck(checks); } - + static final Check checkSame(Object expected, Object actual) { - return () -> assertSame(expected, actual); + return () -> assertSame(expected, actual); } static final Check checkFalse(Supplier actual) { return checkFalse(actual, actual.toString()); - } + } static final Check checkFalse(Supplier actual, String identifier) { - return () -> assertFalse(actual.get(), identifier); + return () -> assertFalse(actual.get(), identifier); } static final Check checkTrue(Supplier actual) { return checkTrue(actual, actual.toString()); } - + static final Check checkTrue(Supplier actual, String identifier) { - return () -> assertTrue(actual.get(), identifier); + return () -> assertTrue(actual.get(), identifier); } static final Check checkEquals(float expected, Supplier actual) { return checkEquals(expected, actual, actual.toString()); } - + static final Check checkEquals(float expected, Supplier actual, String identifier) { return () -> assertEquals(expected, actual.get().floatValue(), identifier); } - + static final Check checkEquals(int expected, Supplier actual) { return checkEquals(expected, actual, actual.toString()); } - + static final Check checkEquals(int expected, Supplier actual, String identifier) { return () -> assertEquals(expected, actual.get().intValue(), identifier); } - + static final Check checkEquals(double expected, Supplier actual) { - return checkEquals(expected, actual, actual.toString()); + return checkEquals(expected, actual, actual.toString()); } static final Check checkEquals(double expected, Supplier actual, String identifier) { @@ -705,7 +771,7 @@ static final Check checkEquals(double expected, Supplier actual, String static final Check checkEquals(long expected, Supplier actual) { return checkEquals(expected, actual, actual.toString()); } - + static final Check checkEquals(long expected, Supplier actual, String identifier) { return () -> assertEquals(expected, actual.get().longValue(), identifier); } @@ -713,19 +779,19 @@ static final Check checkEquals(long expected, Supplier actual, String iden static final Check checkEquals(boolean expected, Supplier actual) { return checkEquals(expected, actual, actual.toString()); } - + static final Check checkEquals(boolean expected, Supplier actual, String identifier) { return () -> assertEquals(expected, actual.get().booleanValue(), identifier); } - + static final Check checkEquals(T expected, Supplier actual) { return checkEquals(expected, actual, actual.toString()); } - + static final Check checkEquals(T expected, Supplier actual, String identifier) { return () -> assertEquals(expected, actual.get(), identifier); } - + @FunctionalInterface interface Check { void check(); diff --git a/internal-api/src/test/java/datadog/trace/api/TagValueConversionsTest.java b/internal-api/src/test/java/datadog/trace/api/TagValueConversionsTest.java index 7aa191fc81e..48b04419bfc 100644 --- a/internal-api/src/test/java/datadog/trace/api/TagValueConversionsTest.java +++ b/internal-api/src/test/java/datadog/trace/api/TagValueConversionsTest.java @@ -11,83 +11,83 @@ public class TagValueConversionsTest { @ParameterizedTest @ValueSource(booleans = {false, true}) public void boolean_(boolean value) { - Boolean box = Boolean.valueOf(value); - - assertEquals(TagMap.EntryReader.BOOLEAN, TagValueConversions.typeOf(box)); - assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.BOOLEAN)); - assertFalse(TagValueConversions.isNumericPrimitive(box)); - assertFalse(TagValueConversions.isNumber(box)); - assertFalse(TagValueConversions.isObject(box)); - - assertEquals(value, TagValueConversions.toBoolean(box)); - assertEquals(value ? 1 : 0, TagValueConversions.toInt(box)); - assertEquals(value ? 1L : 0L, TagValueConversions.toLong(box)); - assertEquals(value ? 1F : 0F, TagValueConversions.toFloat(box)); - assertEquals(value ? 1D : 0D, TagValueConversions.toDouble(box)); - - assertEquals(Boolean.toString(value), TagValueConversions.toString(box)); + Boolean box = Boolean.valueOf(value); + + assertEquals(TagMap.EntryReader.BOOLEAN, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.BOOLEAN)); + assertFalse(TagValueConversions.isNumericPrimitive(box)); + assertFalse(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals(value, TagValueConversions.toBoolean(box)); + assertEquals(value ? 1 : 0, TagValueConversions.toInt(box)); + assertEquals(value ? 1L : 0L, TagValueConversions.toLong(box)); + assertEquals(value ? 1F : 0F, TagValueConversions.toFloat(box)); + assertEquals(value ? 1D : 0D, TagValueConversions.toDouble(box)); + + assertEquals(Boolean.toString(value), TagValueConversions.toString(box)); } - + @ParameterizedTest @ValueSource(ints = {Integer.MIN_VALUE, -256, -128, -1, 0, 1, 128, 256, Integer.MAX_VALUE}) public void int_(int value) { - Integer box = Integer.valueOf(value); - - assertEquals(TagMap.EntryReader.INT, TagValueConversions.typeOf(box)); - assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.INT)); - assertTrue(TagValueConversions.isNumericPrimitive(box)); - assertTrue(TagValueConversions.isNumber(box)); - assertFalse(TagValueConversions.isObject(box)); - - assertEquals(value, TagValueConversions.toInt(box)); - assertEquals((long)value, TagValueConversions.toLong(box)); - assertEquals((float)value, TagValueConversions.toFloat(box)); - assertEquals((double)value, TagValueConversions.toDouble(box)); - - assertEquals(value != 0, TagValueConversions.toBoolean(box)); - assertEquals(Integer.toString(value), TagValueConversions.toString(box)); + Integer box = Integer.valueOf(value); + + assertEquals(TagMap.EntryReader.INT, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.INT)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals(value, TagValueConversions.toInt(box)); + assertEquals((long) value, TagValueConversions.toLong(box)); + assertEquals((float) value, TagValueConversions.toFloat(box)); + assertEquals((double) value, TagValueConversions.toDouble(box)); + + assertEquals(value != 0, TagValueConversions.toBoolean(box)); + assertEquals(Integer.toString(value), TagValueConversions.toString(box)); } - + @ParameterizedTest @ValueSource(bytes = {Byte.MIN_VALUE, -32, -1, 0, 1, 32, Byte.MAX_VALUE}) public void byte_(byte value) { - Byte box = Byte.valueOf(value); - - assertEquals(TagMap.EntryReader.INT, TagValueConversions.typeOf(box)); - assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.INT)); - assertTrue(TagValueConversions.isNumericPrimitive(box)); - assertTrue(TagValueConversions.isNumber(box)); - assertFalse(TagValueConversions.isObject(box)); - - assertEquals((int)value, TagValueConversions.toInt(box)); - assertEquals((long)value, TagValueConversions.toLong(box)); - assertEquals((float)value, TagValueConversions.toFloat(box)); - assertEquals((double)value, TagValueConversions.toDouble(box)); - - assertEquals(value != 0, TagValueConversions.toBoolean(box)); - assertEquals(Byte.toString(value), TagValueConversions.toString(box)); + Byte box = Byte.valueOf(value); + + assertEquals(TagMap.EntryReader.INT, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.INT)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals((int) value, TagValueConversions.toInt(box)); + assertEquals((long) value, TagValueConversions.toLong(box)); + assertEquals((float) value, TagValueConversions.toFloat(box)); + assertEquals((double) value, TagValueConversions.toDouble(box)); + + assertEquals(value != 0, TagValueConversions.toBoolean(box)); + assertEquals(Byte.toString(value), TagValueConversions.toString(box)); } - + @ParameterizedTest @ValueSource(shorts = {Short.MIN_VALUE, -256, -128, -1, 0, 1, 128, 256, Short.MAX_VALUE}) public void short_(short value) { - Short box = Short.valueOf(value); - - assertEquals(TagMap.EntryReader.INT, TagValueConversions.typeOf(box)); - assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.INT)); - assertTrue(TagValueConversions.isNumericPrimitive(box)); - assertTrue(TagValueConversions.isNumber(box)); - assertFalse(TagValueConversions.isObject(box)); - - assertEquals((int)value, TagValueConversions.toInt(box)); - assertEquals((long)value, TagValueConversions.toLong(box)); - assertEquals((float)value, TagValueConversions.toFloat(box)); - assertEquals((double)value, TagValueConversions.toDouble(box)); - - assertEquals(value != 0, TagValueConversions.toBoolean(box)); - assertEquals(Short.toString(value), TagValueConversions.toString(box)); + Short box = Short.valueOf(value); + + assertEquals(TagMap.EntryReader.INT, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.INT)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals((int) value, TagValueConversions.toInt(box)); + assertEquals((long) value, TagValueConversions.toLong(box)); + assertEquals((float) value, TagValueConversions.toFloat(box)); + assertEquals((double) value, TagValueConversions.toDouble(box)); + + assertEquals(value != 0, TagValueConversions.toBoolean(box)); + assertEquals(Short.toString(value), TagValueConversions.toString(box)); } - + @ParameterizedTest @ValueSource( longs = { @@ -106,61 +106,61 @@ public void short_(short value) { Long.MAX_VALUE }) public void long_(long value) { - Long box = Long.valueOf(value); - - assertEquals(TagMap.EntryReader.LONG, TagValueConversions.typeOf(box)); - assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.LONG)); - assertTrue(TagValueConversions.isNumericPrimitive(box)); - assertTrue(TagValueConversions.isNumber(box)); - assertFalse(TagValueConversions.isObject(box)); - - assertEquals(value, TagValueConversions.toLong(box)); - assertEquals((int)value, TagValueConversions.toInt(box)); - assertEquals((float)value, TagValueConversions.toFloat(box)); - assertEquals((double)value, TagValueConversions.toDouble(box)); - - assertEquals(value != 0L, TagValueConversions.toBoolean(box)); - assertEquals(Long.toString(value), TagValueConversions.toString(box)); + Long box = Long.valueOf(value); + + assertEquals(TagMap.EntryReader.LONG, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.LONG)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals(value, TagValueConversions.toLong(box)); + assertEquals((int) value, TagValueConversions.toInt(box)); + assertEquals((float) value, TagValueConversions.toFloat(box)); + assertEquals((double) value, TagValueConversions.toDouble(box)); + + assertEquals(value != 0L, TagValueConversions.toBoolean(box)); + assertEquals(Long.toString(value), TagValueConversions.toString(box)); } - + @ParameterizedTest @ValueSource(floats = {Float.MIN_VALUE, -1F, 0F, 1F, 2.171828F, 3.1415F, Float.MAX_VALUE}) public void float_(float value) { - Float box = Float.valueOf(value); - - assertEquals(TagMap.EntryReader.FLOAT, TagValueConversions.typeOf(box)); - assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.FLOAT)); - assertTrue(TagValueConversions.isNumericPrimitive(box)); - assertTrue(TagValueConversions.isNumber(box)); - assertFalse(TagValueConversions.isObject(box)); - - assertEquals(value, TagValueConversions.toFloat(box)); - assertEquals((int)value, TagValueConversions.toInt(box)); - assertEquals((long)value, TagValueConversions.toLong(box)); - assertEquals((double)value, TagValueConversions.toDouble(box)); - - assertEquals(value != 0F, TagValueConversions.toBoolean(box)); - assertEquals(Float.toString(value), TagValueConversions.toString(box)); + Float box = Float.valueOf(value); + + assertEquals(TagMap.EntryReader.FLOAT, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.FLOAT)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals(value, TagValueConversions.toFloat(box)); + assertEquals((int) value, TagValueConversions.toInt(box)); + assertEquals((long) value, TagValueConversions.toLong(box)); + assertEquals((double) value, TagValueConversions.toDouble(box)); + + assertEquals(value != 0F, TagValueConversions.toBoolean(box)); + assertEquals(Float.toString(value), TagValueConversions.toString(box)); } - + @ParameterizedTest @ValueSource( doubles = {Double.MIN_VALUE, Float.MIN_VALUE, -1D, 0D, 1D, Math.E, Math.PI, Double.MAX_VALUE}) public void double_(double value) { - Double box = Double.valueOf(value); - - assertEquals(TagMap.EntryReader.DOUBLE, TagValueConversions.typeOf(box)); - assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.DOUBLE)); - assertTrue(TagValueConversions.isNumericPrimitive(box)); - assertTrue(TagValueConversions.isNumber(box)); - assertFalse(TagValueConversions.isObject(box)); - - assertEquals(value, TagValueConversions.toDouble(box)); - assertEquals((int)value, TagValueConversions.toInt(box)); - assertEquals((long)value, TagValueConversions.toLong(box)); - assertEquals((float)value, TagValueConversions.toFloat(box)); - - assertEquals(value != 0D, TagValueConversions.toBoolean(box)); - assertEquals(Double.toString(value), TagValueConversions.toString(box)); + Double box = Double.valueOf(value); + + assertEquals(TagMap.EntryReader.DOUBLE, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.DOUBLE)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals(value, TagValueConversions.toDouble(box)); + assertEquals((int) value, TagValueConversions.toInt(box)); + assertEquals((long) value, TagValueConversions.toLong(box)); + assertEquals((float) value, TagValueConversions.toFloat(box)); + + assertEquals(value != 0D, TagValueConversions.toBoolean(box)); + assertEquals(Double.toString(value), TagValueConversions.toString(box)); } }