Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions dd-trace-api/src/main/java/datadog/trace/api/DD64bTraceId.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,26 +46,6 @@ public static DD64bTraceId from(String s) throws NumberFormatException {
return DD64bTraceId.create(LongStringUtils.parseUnsignedLong(s), s);
}

/**
* Like {@link #from(String)}, but returns {@code null} instead of throwing when the given {@code
* String} is not a plain decimal unsigned 64 bit id. Stricter than {@link #from(String)}: a
* leading {@code +} and non-ASCII digits are rejected.
*
* <p>Internal API for parsing untrusted input, such as propagation headers, on the request path.
* Not intended for use outside the tracer.
*
* @param s String of unsigned 64 bit id
* @return DDTraceId, or {@code null} if the {@code String} is not a valid id
*/
public static DD64bTraceId fromOrNull(String s) {
int len = s == null ? 0 : s.length();
long id = LongStringUtils.parseUnsignedLongOrSentinel(s, 0, len, 0L);
if (id == 0L && !LongStringUtils.isUnsignedLongZero(s, 0, len)) {
return null;
}
return DD64bTraceId.create(id, s);
}

/**
* Create a new {@code DDTraceId} from the given {@code String} hex representation of the unsigned
* 64 bit id.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,85 +81,6 @@ private static int firstNonZeroCharacter(CharSequence s, int start) {
* @throws NumberFormatException
*/
Comment thread
PerfectSlayer marked this conversation as resolved.
public static long parseUnsignedLong(String s) throws NumberFormatException {
long result = parseUnsignedLongOrSentinel(s, 0, s == null ? 0 : s.length(), 0L);
if (result != 0L) {
return result;
}
// Zero is ambiguous with the invalid sentinel. Let the strict parser decide: it accepts input
// the non-throwing parser rejects (a leading '+', non-ASCII digits) and builds the exception.
return parseUnsignedLongStrict(s);
}

/**
* Parse the decimal representation of the unsigned 64 bit long from the {@code CharSequence}
* without throwing, for untrusted input such as propagation headers.
*
* <p>Accepts 1 to 20 ASCII digits only: no sign, no whitespace, no non-ASCII digits. An unsigned
* 64 bit value uses every bit, so no return value can mean "invalid" on its own. Callers pick an
* {@code ifInvalid} that is impossible or harmless for their use, e.g. {@code 0} for trace and
* span ids. With {@code 0} as {@code ifInvalid}, {@link #isUnsignedLongZero} tells a real zero
* apart from invalid input.
*
* @param s CharSequence containing the decimal digits
* @param start the start index of the decimal value
* @param len the length of the decimal value
* @param ifInvalid the value returned for null, empty, malformed or out of range input
* @return the parsed long, or {@code ifInvalid}
*/
public static long parseUnsignedLongOrSentinel(
CharSequence s, int start, int len, long ifInvalid) {
if (s == null || len <= 0 || len > 20 || start < 0 || start + len > s.length()) {
return ifInvalid;
}
int end = start + len;
// Signed 64 bits holds any 18 digit number, so the first 18 digits cannot overflow
int fastEnd = Math.min(end, start + 18);
long result = 0;
int i = start;
for (; i < fastEnd; i++) {
int d = s.charAt(i) - '0';
if (d < 0 || d > 9) {
return ifInvalid;
}
result = result * 10 + d;
}
for (; i < end; i++) {
int d = s.charAt(i) - '0';
if (d < 0 || d > 9 || Long.compareUnsigned(result, MAX_FIRST_PART) > 0) {
return ifInvalid;
}
long scaled = result * 10;
result = scaled + d;
if (Long.compareUnsigned(result, scaled) < 0) {
return ifInvalid;
}
}
return result;
}

/**
* Whether {@code s[start, start + len)} is a zero that {@link #parseUnsignedLongOrSentinel}
* accepts: 1 to 20 {@code '0'} characters. Callers that use {@code 0} as the invalid sentinel
* check this only when the parse returned {@code 0}.
*
* @param s CharSequence containing the decimal digits
* @param start the start index of the decimal value
* @param len the length of the decimal value
* @return true if the range parses to zero, false if it is zero-valued only as the sentinel
*/
public static boolean isUnsignedLongZero(CharSequence s, int start, int len) {
if (s == null || len <= 0 || len > 20 || start < 0 || start + len > s.length()) {
return false;
}
for (int i = start, end = start + len; i < end; i++) {
if (s.charAt(i) != '0') {
return false;
}
}
return true;
}

private static long parseUnsignedLongStrict(String s) throws NumberFormatException {
if (s == null) {
throw new NumberFormatException("s can't be null");
}
Expand Down
41 changes: 0 additions & 41 deletions dd-trace-api/src/test/java/datadog/trace/api/DDTraceIdTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;

Expand Down Expand Up @@ -71,46 +70,6 @@ void failParsingIllegal64BitIdStringRepresentation(String stringId) {
assertThrows(NumberFormatException.class, () -> DD64bTraceId.from(stringId));
}

@TableTest({
"scenario | stringId | expectedId ",
"zero | '0' | DD64bTraceId.ZERO ",
"one | '1' | DD64bTraceId.ONE ",
"max | '18446744073709551615' | DD64bTraceId.MAX ",
"long max | '9223372036854775807' | DD64bTraceId.LONG_MAX",
"long max plus one | '9223372036854775808' | DD64bTraceId.LONG_MIN"
})
@ParameterizedTest(name = "fromOrNull parses valid 64-bit id String representation [{index}]")
void fromOrNullParsesValid64BitIdStringRepresentation(String stringId, DD64bTraceId expectedId) {
DD64bTraceId ddid = DD64bTraceId.fromOrNull(stringId);

assertEquals(expectedId, ddid);
assertEquals(stringId, ddid.toString());
}

@Test
void fromOrNullKeepsTheParsedStringForToString() {
String stringId = "1234567890";

assertSame(stringId, DD64bTraceId.fromOrNull(stringId).toString());
}

@ParameterizedTest(name = "fromOrNull returns null for illegal 64-bit id String [{index}]")
@NullSource
@ValueSource(
strings = {
"",
"-1",
"+1",
"18446744073709551616",
"18446744073709551625",
"184467440737095516150",
"18446744073709551a1",
"184467440737095511a"
})
void fromOrNullReturnsNullForIllegal64BitIdString(String stringId) {
assertNull(DD64bTraceId.fromOrNull(stringId));
}

@TableTest({
"scenario | hexId | expectedId ",
"zero | '0' | DD64bTraceId.ZERO ",
Expand Down

This file was deleted.

Loading
Loading