/* * Copyright 2004 The WebRTC Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "rtc_base/stringencode.h" #include #include "rtc_base/arraysize.h" #include "rtc_base/checks.h" #include "rtc_base/stringutils.h" namespace rtc { ///////////////////////////////////////////////////////////////////////////// // String Encoding Utilities ///////////////////////////////////////////////////////////////////////////// size_t url_decode(char* buffer, size_t buflen, const char* source, size_t srclen) { if (nullptr == buffer) return srclen + 1; if (buflen <= 0) return 0; unsigned char h1, h2; size_t srcpos = 0, bufpos = 0; while ((srcpos < srclen) && (bufpos + 1 < buflen)) { unsigned char ch = source[srcpos++]; if (ch == '+') { buffer[bufpos++] = ' '; } else if ((ch == '%') && (srcpos + 1 < srclen) && hex_decode(source[srcpos], &h1) && hex_decode(source[srcpos + 1], &h2)) { buffer[bufpos++] = (h1 << 4) | h2; srcpos += 2; } else { buffer[bufpos++] = ch; } } buffer[bufpos] = '\0'; return bufpos; } static const char HEX[] = "0123456789abcdef"; char hex_encode(unsigned char val) { RTC_DCHECK_LT(val, 16); return (val < 16) ? HEX[val] : '!'; } bool hex_decode(char ch, unsigned char* val) { if ((ch >= '0') && (ch <= '9')) { *val = ch - '0'; } else if ((ch >= 'A') && (ch <= 'F')) { *val = (ch - 'A') + 10; } else if ((ch >= 'a') && (ch <= 'f')) { *val = (ch - 'a') + 10; } else { return false; } return true; } size_t hex_encode(char* buffer, size_t buflen, const char* csource, size_t srclen) { return hex_encode_with_delimiter(buffer, buflen, csource, srclen, 0); } size_t hex_encode_with_delimiter(char* buffer, size_t buflen, const char* csource, size_t srclen, char delimiter) { RTC_DCHECK(buffer); // TODO(kwiberg): estimate output size if (buflen == 0) return 0; // Init and check bounds. const unsigned char* bsource = reinterpret_cast(csource); size_t srcpos = 0, bufpos = 0; size_t needed = delimiter ? (srclen * 3) : (srclen * 2 + 1); if (buflen < needed) return 0; while (srcpos < srclen) { unsigned char ch = bsource[srcpos++]; buffer[bufpos] = hex_encode((ch >> 4) & 0xF); buffer[bufpos + 1] = hex_encode((ch)&0xF); bufpos += 2; // Don't write a delimiter after the last byte. if (delimiter && (srcpos < srclen)) { buffer[bufpos] = delimiter; ++bufpos; } } // Null terminate. buffer[bufpos] = '\0'; return bufpos; } std::string hex_encode(const std::string& str) { return hex_encode(str.c_str(), str.size()); } std::string hex_encode(const char* source, size_t srclen) { return hex_encode_with_delimiter(source, srclen, 0); } std::string hex_encode_with_delimiter(const char* source, size_t srclen, char delimiter) { const size_t kBufferSize = srclen * 3; char* buffer = STACK_ARRAY(char, kBufferSize); size_t length = hex_encode_with_delimiter(buffer, kBufferSize, source, srclen, delimiter); RTC_DCHECK(srclen == 0 || length > 0); return std::string(buffer, length); } size_t hex_decode(char* cbuffer, size_t buflen, const char* source, size_t srclen) { return hex_decode_with_delimiter(cbuffer, buflen, source, srclen, 0); } size_t hex_decode_with_delimiter(char* cbuffer, size_t buflen, const char* source, size_t srclen, char delimiter) { RTC_DCHECK(cbuffer); // TODO(kwiberg): estimate output size if (buflen == 0) return 0; // Init and bounds check. unsigned char* bbuffer = reinterpret_cast(cbuffer); size_t srcpos = 0, bufpos = 0; size_t needed = (delimiter) ? (srclen + 1) / 3 : srclen / 2; if (buflen < needed) return 0; while (srcpos < srclen) { if ((srclen - srcpos) < 2) { // This means we have an odd number of bytes. return 0; } unsigned char h1, h2; if (!hex_decode(source[srcpos], &h1) || !hex_decode(source[srcpos + 1], &h2)) return 0; bbuffer[bufpos++] = (h1 << 4) | h2; srcpos += 2; // Remove the delimiter if needed. if (delimiter && (srclen - srcpos) > 1) { if (source[srcpos] != delimiter) return 0; ++srcpos; } } return bufpos; } size_t hex_decode(char* buffer, size_t buflen, const std::string& source) { return hex_decode_with_delimiter(buffer, buflen, source, 0); } size_t hex_decode_with_delimiter(char* buffer, size_t buflen, const std::string& source, char delimiter) { return hex_decode_with_delimiter(buffer, buflen, source.c_str(), source.length(), delimiter); } size_t transform(std::string& value, size_t maxlen, const std::string& source, Transform t) { char* buffer = STACK_ARRAY(char, maxlen + 1); size_t length = t(buffer, maxlen + 1, source.data(), source.length()); value.assign(buffer, length); return length; } std::string s_transform(const std::string& source, Transform t) { // Ask transformation function to approximate the destination size (returns // upper bound) size_t maxlen = t(nullptr, 0, source.data(), source.length()); char* buffer = STACK_ARRAY(char, maxlen); size_t len = t(buffer, maxlen, source.data(), source.length()); std::string result(buffer, len); return result; } size_t tokenize(const std::string& source, char delimiter, std::vector* fields) { fields->clear(); size_t last = 0; for (size_t i = 0; i < source.length(); ++i) { if (source[i] == delimiter) { if (i != last) { fields->push_back(source.substr(last, i - last)); } last = i + 1; } } if (last != source.length()) { fields->push_back(source.substr(last, source.length() - last)); } return fields->size(); } size_t tokenize_with_empty_tokens(const std::string& source, char delimiter, std::vector* fields) { fields->clear(); size_t last = 0; for (size_t i = 0; i < source.length(); ++i) { if (source[i] == delimiter) { fields->push_back(source.substr(last, i - last)); last = i + 1; } } fields->push_back(source.substr(last, source.length() - last)); return fields->size(); } size_t tokenize_append(const std::string& source, char delimiter, std::vector* fields) { if (!fields) return 0; std::vector new_fields; tokenize(source, delimiter, &new_fields); fields->insert(fields->end(), new_fields.begin(), new_fields.end()); return fields->size(); } size_t tokenize(const std::string& source, char delimiter, char start_mark, char end_mark, std::vector* fields) { if (!fields) return 0; fields->clear(); std::string remain_source = source; while (!remain_source.empty()) { size_t start_pos = remain_source.find(start_mark); if (std::string::npos == start_pos) break; std::string pre_mark; if (start_pos > 0) { pre_mark = remain_source.substr(0, start_pos - 1); } ++start_pos; size_t end_pos = remain_source.find(end_mark, start_pos); if (std::string::npos == end_pos) break; // We have found the matching marks. First tokenize the pre-mask. Then add // the marked part as a single field. Finally, loop back for the post-mark. tokenize_append(pre_mark, delimiter, fields); fields->push_back(remain_source.substr(start_pos, end_pos - start_pos)); remain_source = remain_source.substr(end_pos + 1); } return tokenize_append(remain_source, delimiter, fields); } bool tokenize_first(const std::string& source, const char delimiter, std::string* token, std::string* rest) { // Find the first delimiter size_t left_pos = source.find(delimiter); if (left_pos == std::string::npos) { return false; } // Look for additional occurrances of delimiter. size_t right_pos = left_pos + 1; while (source[right_pos] == delimiter) { right_pos++; } *token = source.substr(0, left_pos); *rest = source.substr(right_pos); return true; } std::string join(const std::vector& source, char delimiter) { if (source.size() == 0) { return std::string(); } // Find length of the string to be returned to pre-allocate memory. size_t source_string_length = 0; for (size_t i = 0; i < source.size(); ++i) { source_string_length += source[i].length(); } // Build the joined string. std::string joined_string; joined_string.reserve(source_string_length + source.size() - 1); for (size_t i = 0; i < source.size(); ++i) { if (i != 0) { joined_string += delimiter; } joined_string += source[i]; } return joined_string; } size_t split(const std::string& source, char delimiter, std::vector* fields) { RTC_DCHECK(fields); fields->clear(); size_t last = 0; for (size_t i = 0; i < source.length(); ++i) { if (source[i] == delimiter) { fields->push_back(source.substr(last, i - last)); last = i + 1; } } fields->push_back(source.substr(last, source.length() - last)); return fields->size(); } std::string ToString(const bool b) { return b ? "true" : "false"; } std::string ToString(const char* const s) { return std::string(s); } std::string ToString(const std::string s) { return s; } std::string ToString(const short s) { char buf[32]; const int len = snprintf(&buf[0], arraysize(buf), "%hd", s); RTC_DCHECK_LE(len, arraysize(buf)); return std::string(&buf[0], len); } std::string ToString(const unsigned short s) { char buf[32]; const int len = snprintf(&buf[0], arraysize(buf), "%hu", s); RTC_DCHECK_LE(len, arraysize(buf)); return std::string(&buf[0], len); } std::string ToString(const int s) { char buf[32]; const int len = snprintf(&buf[0], arraysize(buf), "%d", s); RTC_DCHECK_LE(len, arraysize(buf)); return std::string(&buf[0], len); } std::string ToString(const unsigned int s) { char buf[32]; const int len = snprintf(&buf[0], arraysize(buf), "%u", s); RTC_DCHECK_LE(len, arraysize(buf)); return std::string(&buf[0], len); } std::string ToString(const long int s) { char buf[32]; const int len = snprintf(&buf[0], arraysize(buf), "%ld", s); RTC_DCHECK_LE(len, arraysize(buf)); return std::string(&buf[0], len); } std::string ToString(const unsigned long int s) { char buf[32]; const int len = snprintf(&buf[0], arraysize(buf), "%lu", s); RTC_DCHECK_LE(len, arraysize(buf)); return std::string(&buf[0], len); } std::string ToString(const long long int s) { char buf[32]; const int len = snprintf(&buf[0], arraysize(buf), "%lld", s); RTC_DCHECK_LE(len, arraysize(buf)); return std::string(&buf[0], len); } std::string ToString(const unsigned long long int s) { char buf[32]; const int len = snprintf(&buf[0], arraysize(buf), "%llu", s); RTC_DCHECK_LE(len, arraysize(buf)); return std::string(&buf[0], len); } std::string ToString(const double d) { char buf[32]; const int len = snprintf(&buf[0], arraysize(buf), "%g", d); RTC_DCHECK_LE(len, arraysize(buf)); return std::string(&buf[0], len); } std::string ToString(const long double d) { char buf[32]; const int len = snprintf(&buf[0], arraysize(buf), "%Lg", d); RTC_DCHECK_LE(len, arraysize(buf)); return std::string(&buf[0], len); } std::string ToString(const void* const p) { char buf[32]; const int len = snprintf(&buf[0], arraysize(buf), "%p", p); RTC_DCHECK_LE(len, arraysize(buf)); return std::string(&buf[0], len); } bool FromString(const std::string& s, bool* b) { if (s == "false") { *b = false; return true; } if (s == "true") { *b = true; return true; } return false; } } // namespace rtc