// Copyright 2008 and onwards Google, Inc.
//
// #status: RECOMMENDED
// #category: operations on strings
// #summary: Merges strings or numbers with no delimiter.
//
#ifndef STRINGS_STRCAT_H_
#define STRINGS_STRCAT_H_

#include <string>
using std::string;

#include "gutil/integral_types.h"
#include "gutil/strings/numbers.h"
#include "gutil/strings/stringpiece.h"

// The AlphaNum type was designed to be used as the parameter type for StrCat().
// I suppose that any routine accepting either a string or a number could accept
// it.  The basic idea is that by accepting a "const AlphaNum &" as an argument
// to your function, your callers will automagically convert bools, integers,
// and floating point values to strings for you.
//
// Conversion from 8-bit values is not accepted because if it were, then an
// attempt to pass ':' instead of ":" might result in a 58 ending up in your
// result.
//
// Bools convert to "0" or "1".
//
// Floating point values are converted to a string which, if passed to strtod(),
// would produce the exact same original double (except in case of NaN; all NaNs
// are considered the same value). We try to keep the string short but it's not
// guaranteed to be as short as possible.
//
// This class has implicit constructors.
// Style guide exception granted:
// http://goto/style-guide-exception-20978288
//
struct AlphaNum {
  StringPiece piece;
  char digits[kFastToBufferSize];

  // No bool ctor -- bools convert to an integral type.
  // A bool ctor would also convert incoming pointers (bletch).

  AlphaNum(int32 i32)  // NOLINT(runtime/explicit)
      : piece(digits, FastInt32ToBufferLeft(i32, digits) - &digits[0]) {}
  AlphaNum(uint32 u32)  // NOLINT(runtime/explicit)
      : piece(digits, FastUInt32ToBufferLeft(u32, digits) - &digits[0]) {}
  AlphaNum(int64 i64)  // NOLINT(runtime/explicit)
      : piece(digits, FastInt64ToBufferLeft(i64, digits) - &digits[0]) {}
  AlphaNum(uint64 u64)  // NOLINT(runtime/explicit)
      : piece(digits, FastUInt64ToBufferLeft(u64, digits) - &digits[0]) {}

#if defined(__APPLE__)
  AlphaNum(size_t size)  // NOLINT(runtime/explicit)
      : piece(digits, FastUInt64ToBufferLeft(size, digits) - &digits[0]) {}
#endif

  AlphaNum(float f)  // NOLINT(runtime/explicit)
    : piece(digits, strlen(FloatToBuffer(f, digits))) {}
  AlphaNum(double f)  // NOLINT(runtime/explicit)
    : piece(digits, strlen(DoubleToBuffer(f, digits))) {}

  AlphaNum(const char *c_str) : piece(c_str) {}  // NOLINT(runtime/explicit)
  AlphaNum(StringPiece pc)
      : piece(std::move(pc)) {}            // NOLINT(runtime/explicit)
  AlphaNum(const string &s) : piece(s) {}  // NOLINT(runtime/explicit)

  StringPiece::size_type size() const { return piece.size(); }
  const char *data() const { return piece.data(); }

 private:
  // Use ":" not ':'
  AlphaNum(char c);  // NOLINT(runtime/explicit)
};

extern AlphaNum gEmptyAlphaNum;

// ----------------------------------------------------------------------
// StrCat()
//    This merges the given strings or numbers, with no delimiter.  This
//    is designed to be the fastest possible way to construct a string out
//    of a mix of raw C strings, StringPieces, strings, bool values,
//    and numeric values.
//
//    Don't use this for user-visible strings.  The localization process
//    works poorly on strings built up out of fragments.
//
//    For clarity and performance, don't use StrCat when appending to a
//    string.  In particular, avoid using any of these (anti-)patterns:
//      str.append(StrCat(...)
//      str += StrCat(...)
//      str = StrCat(str, ...)
//    where the last is the worse, with the potential to change a loop
//    from a linear time operation with O(1) dynamic allocations into a
//    quadratic time operation with O(n) dynamic allocations.  StrAppend
//    is a better choice than any of the above, subject to the restriction
//    of StrAppend(&str, a, b, c, ...) that none of the a, b, c, ... may
//    be a reference into str.
// ----------------------------------------------------------------------

string StrCat(const AlphaNum &a);
string StrCat(const AlphaNum &a, const AlphaNum &b);
string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c);
string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
              const AlphaNum &d);
string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
              const AlphaNum &d, const AlphaNum &e);
string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
              const AlphaNum &d, const AlphaNum &e, const AlphaNum &f);
string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
              const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
              const AlphaNum &g);
string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
              const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
              const AlphaNum &g, const AlphaNum &h);

namespace strings {
namespace internal {

// Do not call directly - this is not part of the public API.
string StrCatNineOrMore(const AlphaNum *a1, ...);

}  // namespace internal
}  // namespace strings

// Support 9 or more arguments
inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k, const AlphaNum &l) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, &l, null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k, const AlphaNum &l,
                     const AlphaNum &m) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, &l, &m, null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k, const AlphaNum &l,
                     const AlphaNum &m, const AlphaNum &n) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, &l, &m, &n, null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k, const AlphaNum &l,
                     const AlphaNum &m, const AlphaNum &n, const AlphaNum &o) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, &l, &m, &n, &o,
                                             null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k, const AlphaNum &l,
                     const AlphaNum &m, const AlphaNum &n, const AlphaNum &o,
                     const AlphaNum &p) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, &l, &m, &n, &o, &p,
                                             null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k, const AlphaNum &l,
                     const AlphaNum &m, const AlphaNum &n, const AlphaNum &o,
                     const AlphaNum &p, const AlphaNum &q) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, &l, &m, &n, &o, &p, &q,
                                             null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k, const AlphaNum &l,
                     const AlphaNum &m, const AlphaNum &n, const AlphaNum &o,
                     const AlphaNum &p, const AlphaNum &q, const AlphaNum &r) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, &l, &m, &n, &o, &p, &q, &r,
                                             null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k, const AlphaNum &l,
                     const AlphaNum &m, const AlphaNum &n, const AlphaNum &o,
                     const AlphaNum &p, const AlphaNum &q, const AlphaNum &r,
                     const AlphaNum &s) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, &l, &m, &n, &o, &p, &q, &r,
                                             &s, null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k, const AlphaNum &l,
                     const AlphaNum &m, const AlphaNum &n, const AlphaNum &o,
                     const AlphaNum &p, const AlphaNum &q, const AlphaNum &r,
                     const AlphaNum &s, const AlphaNum &t) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, &l, &m, &n, &o, &p, &q, &r,
                                             &s, &t, null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k, const AlphaNum &l,
                     const AlphaNum &m, const AlphaNum &n, const AlphaNum &o,
                     const AlphaNum &p, const AlphaNum &q, const AlphaNum &r,
                     const AlphaNum &s, const AlphaNum &t, const AlphaNum &u) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, &l, &m, &n, &o, &p, &q, &r,
                                             &s, &t, &u, null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k, const AlphaNum &l,
                     const AlphaNum &m, const AlphaNum &n, const AlphaNum &o,
                     const AlphaNum &p, const AlphaNum &q, const AlphaNum &r,
                     const AlphaNum &s, const AlphaNum &t, const AlphaNum &u,
                     const AlphaNum &v) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, &l, &m, &n, &o, &p, &q, &r,
                                             &s, &t, &u, &v, null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k, const AlphaNum &l,
                     const AlphaNum &m, const AlphaNum &n, const AlphaNum &o,
                     const AlphaNum &p, const AlphaNum &q, const AlphaNum &r,
                     const AlphaNum &s, const AlphaNum &t, const AlphaNum &u,
                     const AlphaNum &v, const AlphaNum &w) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, &l, &m, &n, &o, &p, &q, &r,
                                             &s, &t, &u, &v, &w, null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k, const AlphaNum &l,
                     const AlphaNum &m, const AlphaNum &n, const AlphaNum &o,
                     const AlphaNum &p, const AlphaNum &q, const AlphaNum &r,
                     const AlphaNum &s, const AlphaNum &t, const AlphaNum &u,
                     const AlphaNum &v, const AlphaNum &w, const AlphaNum &x) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, &l, &m, &n, &o, &p, &q, &r,
                                             &s, &t, &u, &v, &w, &x,
                                             null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k, const AlphaNum &l,
                     const AlphaNum &m, const AlphaNum &n, const AlphaNum &o,
                     const AlphaNum &p, const AlphaNum &q, const AlphaNum &r,
                     const AlphaNum &s, const AlphaNum &t, const AlphaNum &u,
                     const AlphaNum &v, const AlphaNum &w, const AlphaNum &x,
                     const AlphaNum &y) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, &l, &m, &n, &o, &p, &q, &r,
                                             &s, &t, &u, &v, &w, &x, &y,
                                             null_alphanum);
}

inline string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
                     const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
                     const AlphaNum &g, const AlphaNum &h, const AlphaNum &i,
                     const AlphaNum &j, const AlphaNum &k, const AlphaNum &l,
                     const AlphaNum &m, const AlphaNum &n, const AlphaNum &o,
                     const AlphaNum &p, const AlphaNum &q, const AlphaNum &r,
                     const AlphaNum &s, const AlphaNum &t, const AlphaNum &u,
                     const AlphaNum &v, const AlphaNum &w, const AlphaNum &x,
                     const AlphaNum &y, const AlphaNum &z) {
  const AlphaNum* null_alphanum = NULL;
  return strings::internal::StrCatNineOrMore(&a, &b, &c, &d, &e, &f, &g, &h, &i,
                                             &j, &k, &l, &m, &n, &o, &p, &q, &r,
                                             &s, &t, &u, &v, &w, &x, &y, &z,
                                             null_alphanum);
}

// ----------------------------------------------------------------------
// StrAppend()
//    Same as above, but adds the output to the given string.
//    WARNING: For speed, StrAppend does not try to check each of its input
//    arguments to be sure that they are not a subset of the string being
//    appended to.  That is, while this will work:
//
//    string s = "foo";
//    s += s;
//
//    This will not (necessarily) work:
//
//    string s = "foo";
//    StrAppend(&s, s);
//
//    Note: while StrCat supports appending up to 12 arguments, StrAppend
//    is currently limited to 9.  That's rarely an issue except when
//    automatically transforming StrCat to StrAppend, and can easily be
//    worked around as consecutive calls to StrAppend are quite efficient.
// ----------------------------------------------------------------------

void StrAppend(string *dest,      const AlphaNum &a);
void StrAppend(string *dest,      const AlphaNum &a, const AlphaNum &b);
void StrAppend(string *dest,      const AlphaNum &a, const AlphaNum &b,
               const AlphaNum &c);
void StrAppend(string *dest,      const AlphaNum &a, const AlphaNum &b,
               const AlphaNum &c, const AlphaNum &d);

// Support up to 9 params by using a default empty AlphaNum.
void StrAppend(string *dest,      const AlphaNum &a, const AlphaNum &b,
               const AlphaNum &c, const AlphaNum &d, const AlphaNum &e,
               const AlphaNum &f = gEmptyAlphaNum,
               const AlphaNum &g = gEmptyAlphaNum,
               const AlphaNum &h = gEmptyAlphaNum,
               const AlphaNum &i = gEmptyAlphaNum);

#endif  // STRINGS_STRCAT_H_
