class TraceContextBinaryFormat {

  @Nullable static TraceContext parseBytes(byte[] bytes, @Nullable TagsBin tags) {
    if (bytes == null) throw new NullPointerException("bytes == null"); // programming error
    if (bytes.length == 0) return null;
    if (bytes[0] != VERSION) {
      Platform.get().log("Invalid input: unsupported version {0}", bytes[0], null);
      return null;
    }
    if (bytes.length < FORMAT_LENGTH - 2 /* sampled field + bit is optional */) {
      Platform.get().log("Invalid input: truncated", null);
      return null;
    }
    long traceIdHigh, traceId, spanId;
    int pos = 1;
    if (bytes[pos] == TRACE_ID_FIELD_ID) {
      pos++;
      traceIdHigh = readLong(bytes, pos);
      traceId = readLong(bytes, pos + 8);
      pos += 16;
    } else {
      Platform.get().log("Invalid input: expected trace ID at offset {0}", pos, null);
      return null;
    }
    if (bytes[pos] == SPAN_ID_FIELD_ID) {
      pos++;
      spanId = readLong(bytes, pos);
      pos += 8;
    } else {
      Platform.get().log("Invalid input: expected span ID at offset {0}", pos, null);
      return null;
    }
    // The trace options field is optional. However, when present, it should be valid.
    Boolean sampled = null;
    if (bytes.length > pos && bytes[pos] == TRACE_OPTION_FIELD_ID) {
      pos++;
      if (bytes.length < pos + 1) {
        Platform.get().log("Invalid input: truncated", null);
        return null;
      }
      sampled = bytes[pos] == 1;
    }
    TraceContext.Builder builder = TraceContext.newBuilder()
      .traceIdHigh(traceIdHigh)
      .traceId(traceId)
      .spanId(spanId);
    if (sampled != null) builder.sampled(sampled.booleanValue());
    if (tags != null) builder.extra(Collections.singletonList(tags));
    return builder.build();
  }

}