@Override public int sizeInBytes(Span value) {
    V1Span v1Span = converter.convert(value);

    int endpointSize =
        value.localEndpoint() != null ? ThriftEndpointCodec.sizeInBytes(value.localEndpoint()) : 0;

    int sizeInBytes = 3 + 8; // TRACE_ID
    if (v1Span.traceIdHigh() != 0) sizeInBytes += 3 + 8; // TRACE_ID_HIGH
    if (v1Span.parentId() != 0) sizeInBytes += 3 + 8; // PARENT_ID
    sizeInBytes += 3 + 8; // ID
    sizeInBytes += 3 + 4; // NAME
    if (value.name() != null) sizeInBytes += utf8SizeInBytes(value.name());

    // we write list thriftFields even when empty to match finagle serialization
    sizeInBytes += 3 + 5; // ANNOTATION field + list overhead
    for (int i = 0, length = v1Span.annotations().size(); i < length; i++) {
      int valueSize = utf8SizeInBytes(v1Span.annotations().get(i).value());
      sizeInBytes += ThriftAnnotationWriter.sizeInBytes(valueSize, endpointSize);
    }

    sizeInBytes += 3 + 5; // BINARY_ANNOTATION field + list overhead
    for (int i = 0, length = v1Span.binaryAnnotations().size(); i < length; i++) {
      V1BinaryAnnotation b = v1Span.binaryAnnotations().get(i);
      int keySize = utf8SizeInBytes(b.key());
      if (b.stringValue() != null) {
        int valueSize = utf8SizeInBytes(b.stringValue());
        sizeInBytes += ThriftBinaryAnnotationWriter.sizeInBytes(keySize, valueSize, endpointSize);
      } else {
        int remoteEndpointSize = ThriftEndpointCodec.sizeInBytes(b.endpoint());
        sizeInBytes += ThriftBinaryAnnotationWriter.sizeInBytes(keySize, 1, remoteEndpointSize);
      }
    }

    if (v1Span.debug() != null) sizeInBytes += 3 + 1; // DEBUG
    if (v1Span.timestamp() != 0L) sizeInBytes += 3 + 8; // TIMESTAMP
    if (v1Span.duration() != 0L) sizeInBytes += 3 + 8; // DURATION

    sizeInBytes++; // TYPE_STOP
    return sizeInBytes;
  }
This type isn't thread-safe: it re-uses state to avoid re-allocations in conversion loops.
zipkin/src/main/java/zipkin2/internal/V1ThriftSpanWriter.java