void processBinaryAnnotations(V1Span source) {
    zipkin2.Endpoint ca = null, sa = null, ma = null;
    for (int i = 0, length = source.binaryAnnotations.size(); i < length; i++) {
      V1BinaryAnnotation b = source.binaryAnnotations.get(i);

      // Peek to see if this is an address annotation. Strictly speaking, address annotations should
      // have a value of true (not "true" or "1"). However, there are versions of zipkin-ruby in the
      // wild that create "1" and misinterpreting confuses the question of what is the local
      // endpoint. Hence, we leniently parse.
      if ("ca".equals(b.key)) {
        ca = b.endpoint;
        continue;
      } else if ("sa".equals(b.key)) {
        sa = b.endpoint;
        continue;
      } else if ("ma".equals(b.key)) {
        ma = b.endpoint;
        continue;
      }

      Span.Builder currentSpan = forEndpoint(source, b.endpoint);

      // don't add marker "lc" tags
      if ("lc".equals(b.key) && b.stringValue.isEmpty()) continue;
      currentSpan.putTag(b.key, b.stringValue);
    }

    boolean noCoreAnnotations = cs == null && cr == null && ss == null && sr == null;
    // special-case when we are missing core annotations, but we have both address annotations
    if (noCoreAnnotations && (ca != null || sa != null)) {
      if (ca != null && sa != null) {
        forEndpoint(source, ca).remoteEndpoint(sa);
      } else if (sa != null) {
        // "sa" is a default for a remote address, don't make it a client span
        forEndpoint(source, null).remoteEndpoint(sa);
      } else { // ca != null: treat it like a server
        forEndpoint(source, null).kind(Kind.SERVER).remoteEndpoint(ca);
      }
      return;
    }

    V1Annotation server = sr != null ? sr : ss;
    if (ca != null && server != null && !ca.equals(server.endpoint)) {
      // Finagle adds a "ca" annotation on server spans for the client-port on the socket, but with
      // the same service name as "sa". Removing the service name prevents creating loopback links.
      if (hasSameServiceName(ca, server.endpoint)) {
        ca = ca.toBuilder().serviceName(null).build();
      }
      forEndpoint(source, server.endpoint).remoteEndpoint(ca);
    }
    if (sa != null) { // client span
      if (cs != null) {
        forEndpoint(source, cs.endpoint).remoteEndpoint(sa);
      } else if (cr != null) {
        forEndpoint(source, cr.endpoint).remoteEndpoint(sa);
      }
    }
    if (ma != null) { // messaging span
      // Intentionally process messaging endpoints separately in case someone accidentally shared
      // a messaging span. This will ensure both sides have the address of the broker.
      if (ms != null) forEndpoint(source, ms.endpoint).remoteEndpoint(ma);
      if (mr != null) forEndpoint(source, mr.endpoint).remoteEndpoint(ma);
    }
  }
Allows you to split a v1 span when necessary. This can be the case when reading merged
client+server spans from storage or parsing old data formats.

<p>This type isn't thread-safe: it re-uses state to avoid re-allocations in conversion loops.
zipkin/src/main/java/zipkin2/v1/V1SpanConverter.java