// AUTOGENERATED BY glean_parser.  DO NOT EDIT.
{# The rendered source is autogenerated, but this
Jinja2 template is not. Please file bugs! #}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/glean/bindings/GleanJSMetricsLookup.h"

#include "mozilla/PerfectHash.h"
#include "mozilla/Maybe.h"
#include "mozilla/glean/bindings/MetricTypes.h"
#include "mozilla/glean/fog_ffi_generated.h"
#include "nsString.h"

#define GLEAN_INDEX_BITS ({{index_bits}})
#define GLEAN_TYPE_BITS ({{type_bits}})
#define GLEAN_ID_BITS ({{id_bits}})
#define GLEAN_TYPE_ID(id) ((id) >> GLEAN_ID_BITS)
#define GLEAN_METRIC_ID(id) ((id) & ((1ULL << GLEAN_ID_BITS) - 1))
#define GLEAN_OFFSET(entry) (entry & ((1ULL << GLEAN_INDEX_BITS) - 1))

namespace mozilla::glean {

// The category lookup table's entry type
using category_entry_t = uint32_t;
// The metric lookup table's entry type
// This is a bitpacked type with {{index_bits}} bits available to index into
// the string table, {{type_bits}} bits available to signify the metric type,
// and the remaining {{id_bits}} bits devoted to {{id_signal_bits}} "signal"
// bits to signify important characteristics (metric's a labeled metric's
// submetric, metric's been registered at runtime) and {{id_bits - id_signal_bits}} bits
// for built-in metric ids.
// Gives room for {{2 ** (id_bits - id_signal_bits)}} of each combination of
// characteristics (which hopefully will prove to be enough).
using metric_entry_t = uint64_t;

static_assert(GLEAN_INDEX_BITS + GLEAN_TYPE_BITS + GLEAN_ID_BITS == sizeof(metric_entry_t) * 8, "Index, Type, and ID bits need to fit into a metric_entry_t");
static_assert(GLEAN_TYPE_BITS + GLEAN_ID_BITS <= sizeof(uint32_t) * 8, "Metric Types and IDs need to fit into at most 32 bits");
static_assert({{ categories|length }} < UINT32_MAX, "Too many metric categories generated.");
static_assert({{ metric_id_mapping|length }} < {{2 ** (id_bits - id_signal_bits)}}, "Too many metrics generated. Need room for {{id_signal_bits}} signal bits.");
static_assert({{ metric_type_ids|length }} < {{2 ** type_bits}}, "Too many different metric types.");

already_AddRefed<GleanMetric> NewMetricFromId(uint32_t id, nsISupports* aParent) {
  uint32_t typeId = GLEAN_TYPE_ID(id);
  uint32_t metricId = GLEAN_METRIC_ID(id);

  switch (typeId) {
    {% for (type_name, subtype_name), (type_id, original_type) in metric_type_ids %}
    case {{ type_id }}: /* {{ original_type }} */
    {
      return MakeAndAddRef<{{type_name}}>(metricId{% if subtype_name|length > 0 %}, {{ type_id }}{% endif %}, aParent);
    }
    {% endfor %}
    default:
    MOZ_ASSERT_UNREACHABLE("Invalid type ID reached when trying to instantiate a new metric");
    return nullptr;
  }
}

/**
 * Create a submetric instance for a labeled metric of the provided type and id for the given label.
 * Assigns or retrieves an id for the submetric from the SDK.
 *
 * @param aParentTypeId - The type of the parent labeled metric identified as a number generated during codegen.
 *                        Only used to identify which X of LabeledX you are so that X can be created here.
 * @param aParentMetricId - The metric id for the parent labeled metric.
 * @param aLabel - The label for the submetric. Might not adhere to the SDK label format.
 * @param aSubmetricId - an outparam which is assigned the submetric's SDK-generated submetric id.
 *                       Used only by GIFFT.
 */
already_AddRefed<GleanMetric> NewSubMetricFromIds(uint32_t aParentTypeId,
                                           uint32_t aParentMetricId,
                                           const nsACString& aLabel,
                                           uint32_t* aSubmetricId,
                                           nsISupports* aParent) {
  switch (aParentTypeId) {
    {% for (type_name, subtype_name), (type_id, original_type) in metric_type_ids %}
    {% if subtype_name|length > 0 %}
    case {{ type_id }}: { /* {{ original_type }} */
      auto id = impl::fog_{{original_type}}_get(aParentMetricId, &aLabel);
      *aSubmetricId = id;
      return MakeAndAddRef<{{subtype_name}}>(id, aParent);
    }
    {% endif %}
    {% endfor %}
    default: {
      MOZ_ASSERT_UNREACHABLE("Invalid type ID for submetric.");
      return nullptr;
    }
  }
}

static Maybe<uint32_t> category_result_check(const nsACString& aKey, category_entry_t entry);
static Maybe<uint32_t> metric_result_check(const nsACString& aKey, metric_entry_t entry);

{{ category_string_table }}
static_assert(sizeof(gCategoryStringTable) < UINT32_MAX, "Category string table is too large.");

{{ category_by_name_lookup }}

{{ metric_string_table }}
static_assert(sizeof(gMetricStringTable) < {{2 ** index_bits}}, "Metric string table is too large.");

{{ metric_by_name_lookup }}

/**
 * Get a category's name from the string table.
 */
const char* GetCategoryName(category_entry_t entry) {
  MOZ_ASSERT(entry < sizeof(gCategoryStringTable), "Entry identifier offset larger than string table");
  return &gCategoryStringTable[entry];
}

/**
 * Get a metric's identifier from the string table.
 */
const char* GetMetricIdentifier(metric_entry_t entry) {
  uint32_t offset = GLEAN_OFFSET(entry);
  MOZ_ASSERT(offset < sizeof(gMetricStringTable), "Entry identifier offset larger than string table");
  return &gMetricStringTable[offset];
}

// WARNING: This WILL break if the definition of a `metric_entry_t` changes.
// This needs to be updated whenever the lookup macros in
// GleanJSMetricsLookup.cpp change (i.e. at the top of this file).
//
// Looking up a metric name involved indexing into the `gMetricStringTable` with
// a valid `metric_entry_t` index. A metric_id (32-bit int) is contained as a
// subset of the bits within a `metric_entry_t`, so we enumerate over the list
// of possible indicies, given in `sMetricNameByLookupEntries`. This is a little
// brittle, as how a `metric_entry_t` encodes a metric_id is an implementation
// detail that we shouldn't heavily rely on. However, if profiles start failing,
// or giving nonsensical names, it's probably this relationship that needs to be
// fixed.
/**
 * Look up the string identifier of a metric by its 32-bit id.
 * Returns nullptr if we couldn't find a matching identifier for the id.
 */
extern "C" const char* FOG_GetMetricIdentifier(
    uint32_t metric_id) {
  for (metric_entry_t entry : sMetricByNameLookupEntries) {
    if (static_cast<uint32_t>(GLEAN_METRIC_ID(entry >> GLEAN_INDEX_BITS)) == metric_id) {
      return GetMetricIdentifier(entry);
    }
  }

  // We didn't find our metric_id in the list of metric entries.
  return nullptr;
}

/**
 * Check that the found entry is pointing to the right key
 * and return it.
 * Or return `Nothing()` if the entry was not found.
 */
static Maybe<uint32_t> category_result_check(const nsACString& aKey, category_entry_t entry) {
  if (MOZ_UNLIKELY(entry > sizeof(gCategoryStringTable))) {
    return Nothing();
  }
  if (aKey.EqualsASCII(gCategoryStringTable + entry)) {
    return Some(entry);
  }
  return Nothing();
}

/**
 * Check if the found entry index is pointing to the right key
 * and return the corresponding metric ID.
 * Or return `Nothing()` if the entry was not found.
 */
static Maybe<uint32_t> metric_result_check(const nsACString& aKey, uint64_t entry) {
  uint32_t metricId = entry >> GLEAN_INDEX_BITS;
  uint32_t offset = GLEAN_OFFSET(entry);

  if (offset > sizeof(gMetricStringTable)) {
    return Nothing();
  }

  if (aKey.EqualsASCII(gMetricStringTable + offset)) {
    return Some(metricId);
  }

  return Nothing();
}


#undef GLEAN_INDEX_BITS
#undef GLEAN_ID_BITS
#undef GLEAN_TYPE_ID
#undef GLEAN_METRIC_ID
#undef GLEAN_OFFSET

} // namespace mozilla::glean
