252 lines
8.4 KiB
C++
252 lines
8.4 KiB
C++
// 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/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());
|
|
}
|