/* 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/. */

#ifndef XPCOM_THREADS_STATICSTRING_H_
#define XPCOM_THREADS_STATICSTRING_H_

#include <cstddef>

#include "mozilla/Attributes.h"

// from "nsStringFwd.h"
template <typename T>
class nsTLiteralString;
using nsLiteralCString = nsTLiteralString<char>;

namespace mozilla {
// class StaticString
//
// Wrapper type containing a C-style string which is guaranteed[*] to have been
// created (potentially indirectly) from static data. Primarily intended for
// text that may eventually be sent up via telemetry, to avoid the possibility
// of accidentally exfiltrating PII.
//
// `StaticString`, like `template <size_t N> const char (&str)[N]`, can be
// freely and implicitly converted to `const char *`, to simplify its use as a
// drop-in replacement when detemplatizing functions.
//
// ### Comparison/contrast with `nsLiteralCString`
//
// Concretely, `StaticString` is smaller than `nsLiteralCString`, as it does not
// store the string-length. It's also trivial to construct a `StaticString` from
// the variable `__func__` or the preprocessor token `__FILE__`, which would
// require additional work to be used with the latter's `_ns` constructor.
//
// Conventionally, the primary intended use case of `StaticString` is subtly
// different from that of `nsLiteralCString`:
//   * `StaticString` is intended for correctness (etc.) in contexts where the
//     consumer of the string requires that it be static.
//   * `nsLiteralCString` is more for efficiency in contexts where the source
//     string _happens to be_ static, but in which the consumer does not care
//     (and so accepts `nsACString const &` or similar).
//
// This is not a hard rule, however, and is notably bent in dom::Promise. (See
// comments below.)
//
// Both are trivially-copyable/-movable/-destructible, guaranteed non-null, and
// can only contain static data.
//
// #### Footnotes
//
// [*] ```
//     CHORUS:   "What, never?"
//     CAPTAIN:  "Well... hardly ever!"
//     CHORUS:   "He's hardly ever sick of C!"
//         -- Gilbert & Sullivan, _H.M.S. Pinafore_ (emended)
// ```
//
class StaticString {
  /* TODO(C++20): convert `constexpr` to `consteval` wherever possible. */
  const char* mStr;  // guaranteed nonnull

 public:
  template <size_t N>
  constexpr MOZ_IMPLICIT StaticString(const char (&str)[N]) : mStr(str) {}

  // `nsLiteralCString` has the same guarantees as `StaticString` (both in being
  // nonnull and containing only static data), so it's safe to construct either
  // from the other.
  //
  // At present we only support construction of a `StaticString` from an
  // `nsLiteralCString`, since this is zero-cost (the converse would not be),
  // and is probably the simplest way to support dom::Promise's interoperation
  // with MozPromise.
  //
  // (A more principled approach, in some sense, would be to create a third type
  // `StaticStringWithLength` (or whatever) acting as the lattice-join of the
  // two, which could then decay to either one as necessary. This is overkill
  // for our current goals... but might be worthwhile if, _e.g._, you really
  // need to get `__func__` into an `nsLiteralCString` rather than just an
  // `nsDependentCString` for some reason.)
  //
  constexpr explicit StaticString(nsLiteralCString const& str);

  constexpr StaticString(StaticString const&) = default;
  constexpr StaticString(StaticString&&) = default;
  ~StaticString() = default;

  constexpr MOZ_IMPLICIT operator const char*() const { return mStr; }

  // Not normally needed, but useful for variadic logging functions.
  constexpr const char* get() const { return mStr; }
};

// Under the covers, StaticString is as lightweight as a single pointer: it does
// not store the length of its deta.
static_assert(sizeof(StaticString) == sizeof(const char*));
static_assert(alignof(StaticString) == alignof(const char*));
}  // namespace mozilla

#endif
