text: /**
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #include "core/span.h"
  #include "receiver/receiver.h"
  #include "sampler/sampler.h"
  #include "util/cmp.h"
  #include "util/log.h"
  #include "util/rand.h"
  #include "util/string.h"
  #include "util/time.h"
  
  #include <inttypes.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  
  /**
   * @file span.c
   *
   * Implementation of HTrace spans.
   */
  
  struct htrace_span *htrace_span_alloc(const char *desc,
                  uint64_t begin_ms, struct htrace_span_id *span_id)
  {
      struct htrace_span *span;
  
      span = malloc(sizeof(*span));
      if (!span) {
          return NULL;
      }
      span->desc = strdup(desc);
      if (!span->desc) {
          free(span);
          return NULL;
      }
      span->begin_ms = begin_ms;
      span->end_ms = 0;
      htrace_span_id_copy(&span->span_id, span_id);
      span->trid = NULL;
      span->num_parents = 0;
      htrace_span_id_clear(&span->parent.single);
      span->parent.list = NULL;
      return span;
  }
  
  void htrace_span_free(struct htrace_span *span)
  {
      if (!span) {
          return;
      }
      free(span->desc);
      free(span->trid);
      if (span->num_parents > 1) {
          free(span->parent.list);
      }
      free(span);
  }
  
  typedef int (*qsort_fn_t)(const void *, const void *);
  
  void htrace_span_sort_and_dedupe_parents(struct htrace_span *span)
  {
      int i, j, num_parents = span->num_parents;
      struct htrace_span_id prev;
  
      if (num_parents <= 1) {
          return;
      }
      qsort(span->parent.list, num_parents, sizeof(struct htrace_span_id),
            (qsort_fn_t)htrace_span_id_compare);
      prev = span->parent.list[0];
      htrace_span_id_copy(&prev, &span->parent.list[0]);
      j = 1;
      for (i = 1; i < num_parents; i++) {
          if (htrace_span_id_compare(&prev, span->parent.list + i) != 0) {
              htrace_span_id_copy(&prev, span->parent.list + i);
              htrace_span_id_copy(span->parent.list + j, span->parent.list + i);
              j++;
          }
      }
      span->num_parents = j;
      if (j == 1) {
          // After deduplication, there is now only one entry.  Switch to the
          // optimized no-malloc representation for 1 entry.
          free(span->parent.list);
          span->parent.single = prev;
      } else if (j != num_parents) {
          // After deduplication, there are now fewer entries.  Use realloc to
          // shrink the size of our dynamic allocation if possible.
          struct htrace_span_id *nlist =
              realloc(span->parent.list, sizeof(struct htrace_span_id) * j);
          if (nlist) {
              span->parent.list = nlist;
          }
      }
  }
  
  /**
   * Translate the span to a JSON string.
   *
   * This function can be called in two ways.  With buf == NULL, we will determine
   * the size of the buffer that would be required to hold a JSON string
   * containing the span contents.  With buf non-NULL, we will write the span
   * contents to the provided buffer.
   *
   * @param span              The span
   * @param max               The maximum number of bytes to write to buf.
   * @param buf               If non-NULL, where the string will be written.
   *
   * @return                  The number of bytes that the span json would take
   *                          up if it were written out.
   */
  static int span_json_sprintf_impl(const struct htrace_span *span,
                                    int max, char *buf)
  {
      int num_parents, i, ret = 0;
      const char *prefix = "";
      char sbuf[HTRACE_SPAN_ID_STRING_LENGTH + 1];
  
      // Note that we have validated the description and process ID strings to
      // make sure they don't contain anything evil.  So we don't need to escape
      // them here.
  
      htrace_span_id_to_str(&span->span_id, sbuf, sizeof(sbuf));
      ret += fwdprintf(&buf, &max, "{\"a\":\"%s\",\"b\":%" PRId64
                   ",\"e\":%" PRId64",", sbuf, span->begin_ms,
                   span->end_ms);
      if (span->desc[0]) {
          ret += fwdprintf(&buf, &max, "\"d\":\"%s\",", span->desc);
      }
      if (span->trid) {
          ret += fwdprintf(&buf, &max, "\"r\":\"%s\",", span->trid);
      }
      num_parents = span->num_parents;
      if (num_parents == 0) {
          ret += fwdprintf(&buf, &max, "\"p\":[]");
      } else if (num_parents == 1) {
          htrace_span_id_to_str(&span->parent.single, sbuf, sizeof(sbuf));
          ret += fwdprintf(&buf, &max, "\"p\":[\"%s\"]", sbuf);
      } else if (num_parents > 1) {
          ret += fwdprintf(&buf, &max, "\"p\":[");
          for (i = 0; i < num_parents; i++) {
              htrace_span_id_to_str(span->parent.list + i, sbuf, sizeof(sbuf));
              ret += fwdprintf(&buf, &max, "%s\"%s\"", prefix, sbuf);
              prefix = ",";
          }
          ret += fwdprintf(&buf, &max, "]");
      }
      ret += fwdprintf(&buf, &max, "}");
      // Add one to 'ret' to take into account the terminating null that we
      // need to write.
      return ret + 1;
  }
  
  int span_json_size(const struct htrace_span *span)
  {
      return span_json_sprintf_impl(span, 0, NULL);
  }
  
  void span_json_sprintf(const struct htrace_span *span, int max, void *buf)
  {
      span_json_sprintf_impl(span, max, buf);
  }
  
  int span_write_msgpack(const struct htrace_span *span, cmp_ctx_t *ctx)
  {
      int i, num_parents;
      uint16_t map_size =
          1 + // desc
          1 + // begin_ms
          1 + // end_ms
          1; // span_id
  
      num_parents = span->num_parents;
      if (span->trid) {
          map_size++;
      }
      if (num_parents > 0) {
          map_size++;
      }
      if (!cmp_write_map16(ctx, map_size)) {
          return 0;
      }
      if (!cmp_write_fixstr(ctx, "a", 1)) {
          return 0;
      }
      if (!htrace_span_id_write_msgpack(&span->span_id, ctx)) {
          return 0;
      }
      if (!cmp_write_fixstr(ctx, "d", 1)) {
          return 0;
      }
      if (!cmp_write_str16(ctx, span->desc, strlen(span->desc))) {
          return 0;
      }
      if (!cmp_write_fixstr(ctx, "b", 1)) {
          return 0;
      }
      if (!cmp_write_u64(ctx, span->begin_ms)) {
          return 0;
      }
      if (!cmp_write_fixstr(ctx, "e", 1)) {
          return 0;
      }
      if (!cmp_write_u64(ctx, span->end_ms)) {
          return 0;
      }
      if (span->trid) {
          if (!cmp_write_fixstr(ctx, "r", 1)) {
              return 0;
          }
          if (!cmp_write_str16(ctx, span->trid, strlen(span->trid))) {
              return 0;
          }
      }
      if (num_parents > 0) {
          if (!cmp_write_fixstr(ctx, "p", 1)) {
              return 0;
          }
          if (!cmp_write_array16(ctx, num_parents)) {
              return 0;
          }
          if (num_parents == 1) {
              if (!htrace_span_id_write_msgpack(&span->parent.single, ctx)) {
                  return 0;
              }
          } else {
              for (i = 0; i < num_parents; i++) {
                  if (!htrace_span_id_write_msgpack(span->parent.list + i, ctx)) {
                      return 0;
                  }
              }
          }
      }
      return 1;
  }
  
  // vim:ts=4:sw=4:et
  