blob: 50f7a23ea94a84566268048760221e2ae60065c6 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/strings/string_util.h"
#include <stdint.h>
#include <limits>
#include "base/macros.h"
#include "base/strings/utf_string_conversion_utils.h"
#include "base/third_party/icu/icu_utf.h"
namespace base {
namespace {
typedef uintptr_t MachineWord;
const uintptr_t kMachineWordAlignmentMask = sizeof(MachineWord) - 1;
inline bool IsAlignedToMachineWord(const void* pointer) {
return !(reinterpret_cast<MachineWord>(pointer) & kMachineWordAlignmentMask);
}
template<typename T> inline T* AlignToMachineWord(T* pointer) {
return reinterpret_cast<T*>(reinterpret_cast<MachineWord>(pointer) &
~kMachineWordAlignmentMask);
}
template<size_t size, typename CharacterType> struct NonASCIIMask;
template<> struct NonASCIIMask<4, char> {
static inline uint32_t value() { return 0x80808080U; }
};
template<> struct NonASCIIMask<8, char> {
static inline uint64_t value() { return 0x8080808080808080ULL; }
};
} // namespace
namespace {
template<typename StringType>
StringType ToLowerASCIIImpl(BasicStringPiece<StringType> str) {
StringType ret;
ret.reserve(str.size());
for (size_t i = 0; i < str.size(); i++)
ret.push_back(ToLowerASCII(str[i]));
return ret;
}
template<typename StringType>
StringType ToUpperASCIIImpl(BasicStringPiece<StringType> str) {
StringType ret;
ret.reserve(str.size());
for (size_t i = 0; i < str.size(); i++)
ret.push_back(ToUpperASCII(str[i]));
return ret;
}
} // namespace
std::string ToLowerASCII(StringPiece str) {
return ToLowerASCIIImpl<std::string>(str);
}
std::string ToUpperASCII(StringPiece str) {
return ToUpperASCIIImpl<std::string>(str);
}
template<class StringType>
int CompareCaseInsensitiveASCIIT(BasicStringPiece<StringType> a,
BasicStringPiece<StringType> b) {
// Find the first characters that aren't equal and compare them. If the end
// of one of the strings is found before a nonequal character, the lengths
// of the strings are compared.
size_t i = 0;
while (i < a.length() && i < b.length()) {
typename StringType::value_type lower_a = ToLowerASCII(a[i]);
typename StringType::value_type lower_b = ToLowerASCII(b[i]);
if (lower_a < lower_b)
return -1;
if (lower_a > lower_b)
return 1;
i++;
}
// End of one string hit before finding a different character. Expect the
// common case to be "strings equal" at this point so check that first.
if (a.length() == b.length())
return 0;
if (a.length() < b.length())
return -1;
return 1;
}
int CompareCaseInsensitiveASCII(StringPiece a, StringPiece b) {
return CompareCaseInsensitiveASCIIT<std::string>(a, b);
}
bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b) {
if (a.length() != b.length())
return false;
return CompareCaseInsensitiveASCIIT<std::string>(a, b) == 0;
}
template<typename STR>
bool ReplaceCharsT(const STR& input,
const STR& replace_chars,
const STR& replace_with,
STR* output) {
bool removed = false;
size_t replace_length = replace_with.length();
*output = input;
size_t found = output->find_first_of(replace_chars);
while (found != STR::npos) {
removed = true;
output->replace(found, 1, replace_with);
found = output->find_first_of(replace_chars, found + replace_length);
}
return removed;
}
bool ReplaceChars(const std::string& input,
const StringPiece& replace_chars,
const std::string& replace_with,
std::string* output) {
return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output);
}
template<typename Str>
TrimPositions TrimStringT(const Str& input,
BasicStringPiece<Str> trim_chars,
TrimPositions positions,
Str* output) {
// Find the edges of leading/trailing whitespace as desired. Need to use
// a StringPiece version of input to be able to call find* on it with the
// StringPiece version of trim_chars (normally the trim_chars will be a
// constant so avoid making a copy).
BasicStringPiece<Str> input_piece(input);
const size_t last_char = input.length() - 1;
const size_t first_good_char = (positions & TRIM_LEADING) ?
input_piece.find_first_not_of(trim_chars) : 0;
const size_t last_good_char = (positions & TRIM_TRAILING) ?
input_piece.find_last_not_of(trim_chars) : last_char;
// When the string was all trimmed, report that we stripped off characters
// from whichever position the caller was interested in. For empty input, we
// stripped no characters, but we still need to clear |output|.
if (input.empty() ||
(first_good_char == Str::npos) || (last_good_char == Str::npos)) {
bool input_was_empty = input.empty(); // in case output == &input
output->clear();
return input_was_empty ? TRIM_NONE : positions;
}
// Trim.
*output =
input.substr(first_good_char, last_good_char - first_good_char + 1);
// Return where we trimmed from.
return static_cast<TrimPositions>(
((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) |
((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING));
}
bool TrimString(const std::string& input,
StringPiece trim_chars,
std::string* output) {
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
}
template<typename Str>
BasicStringPiece<Str> TrimStringPieceT(BasicStringPiece<Str> input,
BasicStringPiece<Str> trim_chars,
TrimPositions positions) {
size_t begin = (positions & TRIM_LEADING) ?
input.find_first_not_of(trim_chars) : 0;
size_t end = (positions & TRIM_TRAILING) ?
input.find_last_not_of(trim_chars) + 1 : input.size();
return input.substr(begin, end - begin);
}
StringPiece TrimString(StringPiece input,
const StringPiece& trim_chars,
TrimPositions positions) {
return TrimStringPieceT(input, trim_chars, positions);
}
TrimPositions TrimWhitespaceASCII(const std::string& input,
TrimPositions positions,
std::string* output) {
return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output);
}
template <class Char>
inline bool DoIsStringASCII(const Char* characters, size_t length) {
MachineWord all_char_bits = 0;
const Char* end = characters + length;
// Prologue: align the input.
while (!IsAlignedToMachineWord(characters) && characters != end) {
all_char_bits |= *characters;
++characters;
}
// Compare the values of CPU word size.
const Char* word_end = AlignToMachineWord(end);
const size_t loop_increment = sizeof(MachineWord) / sizeof(Char);
while (characters < word_end) {
all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
characters += loop_increment;
}
// Process the remaining bytes.
while (characters != end) {
all_char_bits |= *characters;
++characters;
}
MachineWord non_ascii_bit_mask =
NonASCIIMask<sizeof(MachineWord), Char>::value();
return !(all_char_bits & non_ascii_bit_mask);
}
bool IsStringASCII(const StringPiece& str) {
return DoIsStringASCII(str.data(), str.length());
}
bool IsStringUTF8(const StringPiece& str) {
const char *src = str.data();
int32_t src_len = static_cast<int32_t>(str.length());
int32_t char_index = 0;
while (char_index < src_len) {
int32_t code_point;
CBU8_NEXT(src, char_index, src_len, code_point);
if (!IsValidCharacter(code_point))
return false;
}
return true;
}
template <class string_type>
inline typename string_type::value_type* WriteIntoT(string_type* str,
size_t length_with_null) {
DCHECK_GT(length_with_null, 1u);
str->reserve(length_with_null);
str->resize(length_with_null - 1);
return &((*str)[0]);
}
char* WriteInto(std::string* str, size_t length_with_null) {
return WriteIntoT(str, length_with_null);
}
} // namespace base