/*
 *	Measure the time it takes for __android_log_is_loggable.
 */
static void BM_is_loggable(benchmark::State& state) {
  static const char logd[] = "logd";

  while (state.KeepRunning()) {
    __android_log_is_loggable_len(ANDROID_LOG_WARN, logd, strlen(logd),
                                  ANDROID_LOG_VERBOSE);
  }
}
// assumption: mMsg == NULL
size_t LogBufferElement::populateDroppedMessage(char*& buffer, LogBuffer* parent,
                                                bool lastSame) {
    static const char tag[] = "chatty";

    if (!__android_log_is_loggable_len(ANDROID_LOG_INFO, tag, strlen(tag),
                                       ANDROID_LOG_VERBOSE)) {
        return 0;
    }

    static const char format_uid[] = "uid=%u%s%s %s %u line%s";
    parent->wrlock();
    const char* name = parent->uidToName(mUid);
    parent->unlock();
    const char* commName = android::tidToName(mTid);
    if (!commName && (mTid != mPid)) {
        commName = android::tidToName(mPid);
    }
    if (!commName) {
        parent->wrlock();
        commName = parent->pidToName(mPid);
        parent->unlock();
    }
    if (name && name[0] && commName && (name[0] == commName[0])) {
        size_t len = strlen(name + 1);
        if (!strncmp(name + 1, commName + 1, len)) {
            if (commName[len + 1] == '\0') {
                free(const_cast<char*>(commName));
                commName = nullptr;
            } else {
                free(const_cast<char*>(name));
                name = nullptr;
            }
        }
    }
    if (name) {
        char* buf = nullptr;
        asprintf(&buf, "(%s)", name);
        if (buf) {
            free(const_cast<char*>(name));
            name = buf;
        }
    }
    if (commName) {
        char* buf = nullptr;
        asprintf(&buf, " %s", commName);
        if (buf) {
            free(const_cast<char*>(commName));
            commName = buf;
        }
    }
    // identical to below to calculate the buffer size required
    const char* type = lastSame ? "identical" : "expire";
    size_t len = snprintf(nullptr, 0, format_uid, mUid, name ? name : "",
                          commName ? commName : "", type, getDropped(),
                          (getDropped() > 1) ? "s" : "");

    size_t hdrLen;
    if (isBinary()) {
        hdrLen = sizeof(android_log_event_string_t);
    } else {
        hdrLen = 1 + sizeof(tag);
    }

    buffer = static_cast<char*>(calloc(1, hdrLen + len + 1));
    if (!buffer) {
        free(const_cast<char*>(name));
        free(const_cast<char*>(commName));
        return 0;
    }

    size_t retval = hdrLen + len;
    if (isBinary()) {
        android_log_event_string_t* event =
            reinterpret_cast<android_log_event_string_t*>(buffer);

        event->header.tag = htole32(CHATTY_LOG_TAG);
        event->type = EVENT_TYPE_STRING;
        event->length = htole32(len);
    } else {
        ++retval;
        buffer[0] = ANDROID_LOG_INFO;
        strcpy(buffer + 1, tag);
    }

    snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
             commName ? commName : "", type, getDropped(),
             (getDropped() > 1) ? "s" : "");
    free(const_cast<char*>(name));
    free(const_cast<char*>(commName));

    return retval;
}
// Look up tagname, generate one if necessary, and return a tag
LIBLOG_ABI_PUBLIC int android_lookupEventTagNum(EventTagMap* map,
                                                const char* tagname,
                                                const char* format, int prio) {
  const char* ep = endOfTag(tagname);
  size_t len = ep - tagname;
  if (!len || *ep) {
    errno = EINVAL;
    return -1;
  }

  if ((prio != ANDROID_LOG_UNKNOWN) && (prio < ANDROID_LOG_SILENT) &&
      !__android_log_is_loggable_len(prio, tagname, len,
                                     __android_log_is_debuggable()
                                         ? ANDROID_LOG_VERBOSE
                                         : ANDROID_LOG_DEBUG)) {
    errno = EPERM;
    return -1;
  }

  if (!format) format = "";
  ssize_t fmtLen = strlen(format);
  int ret = map->find(TagFmt(
      std::make_pair(MapString(tagname, len), MapString(format, fmtLen))));
  if (ret != -1) return ret;

  // call event tag service to arrange for a new tag
  char* buf = NULL;
  // Can not use android::base::StringPrintf, asprintf + free instead.
  static const char command_template[] = "getEventTag name=%s format=\"%s\"";
  ret = asprintf(&buf, command_template, tagname, format);
  if (ret > 0) {
    // Add some buffer margin for an estimate of the full return content.
    char* cp;
    size_t size =
        ret - strlen(command_template) +
        strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
    if (size > (size_t)ret) {
      cp = static_cast<char*>(realloc(buf, size));
      if (cp) {
        buf = cp;
      } else {
        size = ret;
      }
    } else {
      size = ret;
    }
    // Ask event log tag service for an allocation
    if (__send_log_msg(buf, size) >= 0) {
      buf[size - 1] = '\0';
      unsigned long val = strtoul(buf, &cp, 10);        // return size
      if ((buf != cp) && (val > 0) && (*cp == '\n')) {  // truncation OK
        val = strtoul(cp + 1, &cp, 10);                 // allocated tag number
        if ((val > 0) && (val < UINT32_MAX) && (*cp == '\t')) {
          free(buf);
          ret = val;
          // cache
          map->emplaceUnique(ret, TagFmt(std::make_pair(
                                      MapString(std::string(tagname, len)),
                                      MapString(std::string(format, fmtLen)))));
          return ret;
        }
      }
    }
    free(buf);
  }

  // Hail Mary
  ret = map->find(MapString(tagname, len));
  if (ret == -1) errno = ESRCH;
  return ret;
}