blob: b91fab9f4688f5bbc05fede3f23ed9f77e9e5c47 [file] [log] [blame]
// Copyright 2008 and onwards Google Inc. All rights reserved.
#include "gutil/strings/strcat.h"
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <common/logging.h>
#include "gutil/logging-inl.h"
#include "gutil/gscoped_ptr.h"
#include "gutil/strings/ascii_ctype.h"
#include "gutil/strings/escaping.h"
#include "gutil/stl_util.h"
AlphaNum gEmptyAlphaNum("");
// ----------------------------------------------------------------------
// StrCat()
// This merges the given strings or integers, 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, and integer values.
// ----------------------------------------------------------------------
// Append is merely a version of memcpy that returns the address of the byte
// after the area just overwritten. It comes in multiple flavors to minimize
// call overhead.
static char *Append1(char *out, const AlphaNum &x) {
memcpy(out, x.data(), x.size());
return out + x.size();
}
static char *Append2(char *out, const AlphaNum &x1, const AlphaNum &x2) {
memcpy(out, x1.data(), x1.size());
out += x1.size();
memcpy(out, x2.data(), x2.size());
return out + x2.size();
}
static char *Append4(char *out,
const AlphaNum &x1, const AlphaNum &x2,
const AlphaNum &x3, const AlphaNum &x4) {
memcpy(out, x1.data(), x1.size());
out += x1.size();
memcpy(out, x2.data(), x2.size());
out += x2.size();
memcpy(out, x3.data(), x3.size());
out += x3.size();
memcpy(out, x4.data(), x4.size());
return out + x4.size();
}
string StrCat(const AlphaNum &a) {
return string(a.data(), a.size());
}
string StrCat(const AlphaNum &a, const AlphaNum &b) {
string result;
STLStringResizeUninitialized(&result, a.size() + b.size());
char *const begin = &*result.begin();
char *out = Append2(begin, a, b);
DCHECK_EQ(out, begin + result.size());
return result;
}
string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c) {
string result;
STLStringResizeUninitialized(&result, a.size() + b.size() + c.size());
char *const begin = &*result.begin();
char *out = Append2(begin, a, b);
out = Append1(out, c);
DCHECK_EQ(out, begin + result.size());
return result;
}
string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
const AlphaNum &d) {
string result;
STLStringResizeUninitialized(&result,
a.size() + b.size() + c.size() + d.size());
char *const begin = &*result.begin();
char *out = Append4(begin, a, b, c, d);
DCHECK_EQ(out, begin + result.size());
return result;
}
string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
const AlphaNum &d, const AlphaNum &e) {
string result;
STLStringResizeUninitialized(&result,
a.size() + b.size() + c.size() + d.size() + e.size());
char *const begin = &*result.begin();
char *out = Append4(begin, a, b, c, d);
out = Append1(out, e);
DCHECK_EQ(out, begin + result.size());
return result;
}
string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
const AlphaNum &d, const AlphaNum &e, const AlphaNum &f) {
string result;
STLStringResizeUninitialized(&result,
a.size() + b.size() + c.size() + d.size() + e.size() + f.size());
char *const begin = &*result.begin();
char *out = Append4(begin, a, b, c, d);
out = Append2(out, e, f);
DCHECK_EQ(out, begin + result.size());
return result;
}
string StrCat(const AlphaNum &a, const AlphaNum &b, const AlphaNum &c,
const AlphaNum &d, const AlphaNum &e, const AlphaNum &f,
const AlphaNum &g) {
string result;
STLStringResizeUninitialized(&result,
a.size() + b.size() + c.size() + d.size() + e.size()
+ f.size() + g.size());
char *const begin = &*result.begin();
char *out = Append4(begin, a, b, c, d);
out = Append2(out, e, f);
out = Append1(out, g);
DCHECK_EQ(out, begin + result.size());
return result;
}
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) {
string result;
STLStringResizeUninitialized(&result,
a.size() + b.size() + c.size() + d.size() + e.size()
+ f.size() + g.size() + h.size());
char *const begin = &*result.begin();
char *out = Append4(begin, a, b, c, d);
out = Append4(out, e, f, g, h);
DCHECK_EQ(out, begin + result.size());
return result;
}
namespace strings {
namespace internal {
// StrCat with this many params is exceedingly rare, but it has been
// requested... therefore we'll rely on default arguments to make calling
// slightly less efficient, to preserve code size.
string StrCatNineOrMore(const AlphaNum *a, ...) {
string result;
va_list args;
va_start(args, a);
size_t size = a->size();
while (const AlphaNum *arg = va_arg(args, const AlphaNum *)) {
size += arg->size();
}
STLStringResizeUninitialized(&result, size);
va_end(args);
va_start(args, a);
char *const begin = &*result.begin();
char *out = Append1(begin, *a);
while (const AlphaNum *arg = va_arg(args, const AlphaNum *)) {
out = Append1(out, *arg);
}
va_end(args);
DCHECK_EQ(out, begin + size);
return result;
}
} // namespace internal
} // namespace strings
// It's possible to call StrAppend with a StringPiece that is itself a fragment
// of the string we're appending to. However the results of this are random.
// Therefore, check for this in debug mode. Use unsigned math so we only have
// to do one comparison.
#define DCHECK_NO_OVERLAP(dest, src) \
DCHECK_GT(uintptr_t((src).data() - (dest).data()), uintptr_t((dest).size()))
void StrAppend(string *result, const AlphaNum &a) {
DCHECK_NO_OVERLAP(*result, a);
result->append(a.data(), a.size());
}
void StrAppend(string *result, const AlphaNum &a, const AlphaNum &b) {
DCHECK_NO_OVERLAP(*result, a);
DCHECK_NO_OVERLAP(*result, b);
string::size_type old_size = result->size();
STLStringResizeUninitialized(result, old_size + a.size() + b.size());
char *const begin = &*result->begin();
char *out = Append2(begin + old_size, a, b);
DCHECK_EQ(out, begin + result->size());
}
void StrAppend(string *result,
const AlphaNum &a, const AlphaNum &b, const AlphaNum &c) {
DCHECK_NO_OVERLAP(*result, a);
DCHECK_NO_OVERLAP(*result, b);
DCHECK_NO_OVERLAP(*result, c);
string::size_type old_size = result->size();
STLStringResizeUninitialized(result,
old_size + a.size() + b.size() + c.size());
char *const begin = &*result->begin();
char *out = Append2(begin + old_size, a, b);
out = Append1(out, c);
DCHECK_EQ(out, begin + result->size());
}
void StrAppend(string *result,
const AlphaNum &a, const AlphaNum &b,
const AlphaNum &c, const AlphaNum &d) {
DCHECK_NO_OVERLAP(*result, a);
DCHECK_NO_OVERLAP(*result, b);
DCHECK_NO_OVERLAP(*result, c);
DCHECK_NO_OVERLAP(*result, d);
string::size_type old_size = result->size();
STLStringResizeUninitialized(result,
old_size + a.size() + b.size() + c.size() + d.size());
char *const begin = &*result->begin();
char *out = Append4(begin + old_size, a, b, c, d);
DCHECK_EQ(out, begin + result->size());
}
// StrAppend with this many params is even rarer than with StrCat.
// Therefore we'll again rely on default arguments to make calling
// slightly less efficient, to preserve code size.
void StrAppend(string *result,
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) {
DCHECK_NO_OVERLAP(*result, a);
DCHECK_NO_OVERLAP(*result, b);
DCHECK_NO_OVERLAP(*result, c);
DCHECK_NO_OVERLAP(*result, d);
DCHECK_NO_OVERLAP(*result, e);
DCHECK_NO_OVERLAP(*result, f);
DCHECK_NO_OVERLAP(*result, g);
DCHECK_NO_OVERLAP(*result, h);
DCHECK_NO_OVERLAP(*result, i);
string::size_type old_size = result->size();
STLStringResizeUninitialized(result,
old_size + a.size() + b.size() + c.size() + d.size()
+ e.size() + f.size() + g.size() + h.size() + i.size());
char *const begin = &*result->begin();
char *out = Append4(begin + old_size, a, b, c, d);
out = Append4(out, e, f, g, h);
out = Append1(out, i);
DCHECK_EQ(out, begin + result->size());
}