blob: 36c3f91236f17a133549cd9ad8994230d52541ce [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "rtc_base/stringencode.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000012
Jonas Olsson6b1985d2018-07-05 11:59:48 +020013#include "rtc_base/arraysize.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020014#include "rtc_base/checks.h"
15#include "rtc_base/stringutils.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000016
17namespace rtc {
18
19/////////////////////////////////////////////////////////////////////////////
20// String Encoding Utilities
21/////////////////////////////////////////////////////////////////////////////
22
Yves Gerey665174f2018-06-19 15:03:05 +020023size_t url_decode(char* buffer,
24 size_t buflen,
25 const char* source,
26 size_t srclen) {
deadbeef37f5ecf2017-02-27 14:06:41 -080027 if (nullptr == buffer)
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000028 return srclen + 1;
29 if (buflen <= 0)
30 return 0;
31
32 unsigned char h1, h2;
33 size_t srcpos = 0, bufpos = 0;
34 while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
35 unsigned char ch = source[srcpos++];
36 if (ch == '+') {
37 buffer[bufpos++] = ' ';
Yves Gerey665174f2018-06-19 15:03:05 +020038 } else if ((ch == '%') && (srcpos + 1 < srclen) &&
39 hex_decode(source[srcpos], &h1) &&
40 hex_decode(source[srcpos + 1], &h2)) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000041 buffer[bufpos++] = (h1 << 4) | h2;
42 srcpos += 2;
43 } else {
44 buffer[bufpos++] = ch;
45 }
46 }
47 buffer[bufpos] = '\0';
48 return bufpos;
49}
50
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000051static const char HEX[] = "0123456789abcdef";
52
53char hex_encode(unsigned char val) {
henrikg91d6ede2015-09-17 00:24:34 -070054 RTC_DCHECK_LT(val, 16);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000055 return (val < 16) ? HEX[val] : '!';
56}
57
58bool hex_decode(char ch, unsigned char* val) {
59 if ((ch >= '0') && (ch <= '9')) {
60 *val = ch - '0';
61 } else if ((ch >= 'A') && (ch <= 'Z')) {
62 *val = (ch - 'A') + 10;
63 } else if ((ch >= 'a') && (ch <= 'z')) {
64 *val = (ch - 'a') + 10;
65 } else {
66 return false;
67 }
68 return true;
69}
70
Yves Gerey665174f2018-06-19 15:03:05 +020071size_t hex_encode(char* buffer,
72 size_t buflen,
73 const char* csource,
74 size_t srclen) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000075 return hex_encode_with_delimiter(buffer, buflen, csource, srclen, 0);
76}
77
Yves Gerey665174f2018-06-19 15:03:05 +020078size_t hex_encode_with_delimiter(char* buffer,
79 size_t buflen,
80 const char* csource,
81 size_t srclen,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000082 char delimiter) {
Henrik Grunell84879882018-03-23 15:33:03 +010083 RTC_DCHECK(buffer); // TODO(kwiberg): estimate output size
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000084 if (buflen == 0)
85 return 0;
86
87 // Init and check bounds.
88 const unsigned char* bsource =
89 reinterpret_cast<const unsigned char*>(csource);
90 size_t srcpos = 0, bufpos = 0;
91 size_t needed = delimiter ? (srclen * 3) : (srclen * 2 + 1);
92 if (buflen < needed)
93 return 0;
94
95 while (srcpos < srclen) {
96 unsigned char ch = bsource[srcpos++];
Yves Gerey665174f2018-06-19 15:03:05 +020097 buffer[bufpos] = hex_encode((ch >> 4) & 0xF);
98 buffer[bufpos + 1] = hex_encode((ch)&0xF);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000099 bufpos += 2;
100
101 // Don't write a delimiter after the last byte.
102 if (delimiter && (srcpos < srclen)) {
103 buffer[bufpos] = delimiter;
104 ++bufpos;
105 }
106 }
107
108 // Null terminate.
109 buffer[bufpos] = '\0';
110 return bufpos;
111}
112
Peter Thatcher1cf6f812015-05-15 10:40:45 -0700113std::string hex_encode(const std::string& str) {
114 return hex_encode(str.c_str(), str.size());
115}
116
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000117std::string hex_encode(const char* source, size_t srclen) {
118 return hex_encode_with_delimiter(source, srclen, 0);
119}
120
Yves Gerey665174f2018-06-19 15:03:05 +0200121std::string hex_encode_with_delimiter(const char* source,
122 size_t srclen,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000123 char delimiter) {
124 const size_t kBufferSize = srclen * 3;
125 char* buffer = STACK_ARRAY(char, kBufferSize);
Yves Gerey665174f2018-06-19 15:03:05 +0200126 size_t length =
127 hex_encode_with_delimiter(buffer, kBufferSize, source, srclen, delimiter);
henrikg91d6ede2015-09-17 00:24:34 -0700128 RTC_DCHECK(srclen == 0 || length > 0);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000129 return std::string(buffer, length);
130}
131
Yves Gerey665174f2018-06-19 15:03:05 +0200132size_t hex_decode(char* cbuffer,
133 size_t buflen,
134 const char* source,
135 size_t srclen) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000136 return hex_decode_with_delimiter(cbuffer, buflen, source, srclen, 0);
137}
138
Yves Gerey665174f2018-06-19 15:03:05 +0200139size_t hex_decode_with_delimiter(char* cbuffer,
140 size_t buflen,
141 const char* source,
142 size_t srclen,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000143 char delimiter) {
Henrik Grunell84879882018-03-23 15:33:03 +0100144 RTC_DCHECK(cbuffer); // TODO(kwiberg): estimate output size
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000145 if (buflen == 0)
146 return 0;
147
148 // Init and bounds check.
149 unsigned char* bbuffer = reinterpret_cast<unsigned char*>(cbuffer);
150 size_t srcpos = 0, bufpos = 0;
151 size_t needed = (delimiter) ? (srclen + 1) / 3 : srclen / 2;
152 if (buflen < needed)
153 return 0;
154
155 while (srcpos < srclen) {
156 if ((srclen - srcpos) < 2) {
157 // This means we have an odd number of bytes.
158 return 0;
159 }
160
161 unsigned char h1, h2;
162 if (!hex_decode(source[srcpos], &h1) ||
163 !hex_decode(source[srcpos + 1], &h2))
164 return 0;
165
166 bbuffer[bufpos++] = (h1 << 4) | h2;
167 srcpos += 2;
168
169 // Remove the delimiter if needed.
170 if (delimiter && (srclen - srcpos) > 1) {
171 if (source[srcpos] != delimiter)
172 return 0;
173 ++srcpos;
174 }
175 }
176
177 return bufpos;
178}
179
180size_t hex_decode(char* buffer, size_t buflen, const std::string& source) {
181 return hex_decode_with_delimiter(buffer, buflen, source, 0);
182}
Yves Gerey665174f2018-06-19 15:03:05 +0200183size_t hex_decode_with_delimiter(char* buffer,
184 size_t buflen,
185 const std::string& source,
186 char delimiter) {
187 return hex_decode_with_delimiter(buffer, buflen, source.c_str(),
188 source.length(), delimiter);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000189}
190
Yves Gerey665174f2018-06-19 15:03:05 +0200191size_t transform(std::string& value,
192 size_t maxlen,
193 const std::string& source,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000194 Transform t) {
195 char* buffer = STACK_ARRAY(char, maxlen + 1);
196 size_t length = t(buffer, maxlen + 1, source.data(), source.length());
197 value.assign(buffer, length);
198 return length;
199}
200
201std::string s_transform(const std::string& source, Transform t) {
Yves Gerey665174f2018-06-19 15:03:05 +0200202 // Ask transformation function to approximate the destination size (returns
203 // upper bound)
deadbeef37f5ecf2017-02-27 14:06:41 -0800204 size_t maxlen = t(nullptr, 0, source.data(), source.length());
Yves Gerey665174f2018-06-19 15:03:05 +0200205 char* buffer = STACK_ARRAY(char, maxlen);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000206 size_t len = t(buffer, maxlen, source.data(), source.length());
207 std::string result(buffer, len);
208 return result;
209}
210
Yves Gerey665174f2018-06-19 15:03:05 +0200211size_t tokenize(const std::string& source,
212 char delimiter,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000213 std::vector<std::string>* fields) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000214 fields->clear();
215 size_t last = 0;
216 for (size_t i = 0; i < source.length(); ++i) {
217 if (source[i] == delimiter) {
218 if (i != last) {
219 fields->push_back(source.substr(last, i - last));
220 }
221 last = i + 1;
222 }
223 }
224 if (last != source.length()) {
225 fields->push_back(source.substr(last, source.length() - last));
226 }
227 return fields->size();
228}
229
deadbeef0a6c4ca2015-10-06 11:38:28 -0700230size_t tokenize_with_empty_tokens(const std::string& source,
231 char delimiter,
232 std::vector<std::string>* fields) {
233 fields->clear();
234 size_t last = 0;
235 for (size_t i = 0; i < source.length(); ++i) {
236 if (source[i] == delimiter) {
237 fields->push_back(source.substr(last, i - last));
238 last = i + 1;
239 }
240 }
241 fields->push_back(source.substr(last, source.length() - last));
242 return fields->size();
243}
244
Yves Gerey665174f2018-06-19 15:03:05 +0200245size_t tokenize_append(const std::string& source,
246 char delimiter,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000247 std::vector<std::string>* fields) {
Yves Gerey665174f2018-06-19 15:03:05 +0200248 if (!fields)
249 return 0;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000250
251 std::vector<std::string> new_fields;
252 tokenize(source, delimiter, &new_fields);
253 fields->insert(fields->end(), new_fields.begin(), new_fields.end());
254 return fields->size();
255}
256
Yves Gerey665174f2018-06-19 15:03:05 +0200257size_t tokenize(const std::string& source,
258 char delimiter,
259 char start_mark,
260 char end_mark,
261 std::vector<std::string>* fields) {
262 if (!fields)
263 return 0;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000264 fields->clear();
265
266 std::string remain_source = source;
267 while (!remain_source.empty()) {
268 size_t start_pos = remain_source.find(start_mark);
Yves Gerey665174f2018-06-19 15:03:05 +0200269 if (std::string::npos == start_pos)
270 break;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000271 std::string pre_mark;
272 if (start_pos > 0) {
273 pre_mark = remain_source.substr(0, start_pos - 1);
274 }
275
276 ++start_pos;
277 size_t end_pos = remain_source.find(end_mark, start_pos);
Yves Gerey665174f2018-06-19 15:03:05 +0200278 if (std::string::npos == end_pos)
279 break;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000280
281 // We have found the matching marks. First tokenize the pre-mask. Then add
282 // the marked part as a single field. Finally, loop back for the post-mark.
283 tokenize_append(pre_mark, delimiter, fields);
284 fields->push_back(remain_source.substr(start_pos, end_pos - start_pos));
285 remain_source = remain_source.substr(end_pos + 1);
286 }
287
288 return tokenize_append(remain_source, delimiter, fields);
289}
290
Donald Curtis144d0182015-05-15 13:14:24 -0700291bool tokenize_first(const std::string& source,
292 const char delimiter,
293 std::string* token,
294 std::string* rest) {
Donald Curtis0e07f922015-05-15 09:21:23 -0700295 // Find the first delimiter
296 size_t left_pos = source.find(delimiter);
297 if (left_pos == std::string::npos) {
298 return false;
299 }
300
301 // Look for additional occurrances of delimiter.
302 size_t right_pos = left_pos + 1;
Donald Curtis144d0182015-05-15 13:14:24 -0700303 while (source[right_pos] == delimiter) {
Donald Curtis0e07f922015-05-15 09:21:23 -0700304 right_pos++;
305 }
306
307 *token = source.substr(0, left_pos);
308 *rest = source.substr(right_pos);
309 return true;
310}
311
Diogo Real7bd1f1b2017-09-08 12:50:41 -0700312std::string join(const std::vector<std::string>& source, char delimiter) {
313 if (source.size() == 0) {
314 return std::string();
315 }
316 // Find length of the string to be returned to pre-allocate memory.
317 size_t source_string_length = 0;
318 for (size_t i = 0; i < source.size(); ++i) {
319 source_string_length += source[i].length();
320 }
321
322 // Build the joined string.
323 std::string joined_string;
324 joined_string.reserve(source_string_length + source.size() - 1);
325 for (size_t i = 0; i < source.size(); ++i) {
326 if (i != 0) {
327 joined_string += delimiter;
328 }
329 joined_string += source[i];
330 }
331 return joined_string;
332}
333
Yves Gerey665174f2018-06-19 15:03:05 +0200334size_t split(const std::string& source,
335 char delimiter,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000336 std::vector<std::string>* fields) {
henrikg91d6ede2015-09-17 00:24:34 -0700337 RTC_DCHECK(fields);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000338 fields->clear();
339 size_t last = 0;
340 for (size_t i = 0; i < source.length(); ++i) {
341 if (source[i] == delimiter) {
342 fields->push_back(source.substr(last, i - last));
343 last = i + 1;
344 }
345 }
346 fields->push_back(source.substr(last, source.length() - last));
347 return fields->size();
348}
349
Jonas Olsson6b1985d2018-07-05 11:59:48 +0200350std::string ToString(const bool b) {
351 return b ? "true" : "false";
352}
353
354std::string ToString(const char* const s) {
355 return std::string(s);
356}
357std::string ToString(const std::string s) {
358 return s;
359}
360
361std::string ToString(const short s) {
362 char buf[32];
363 const int len = std::snprintf(&buf[0], arraysize(buf), "%hd", s);
364 RTC_DCHECK_LE(len, arraysize(buf));
365 return std::string(&buf[0], len);
366}
367std::string ToString(const unsigned short s) {
368 char buf[32];
369 const int len = std::snprintf(&buf[0], arraysize(buf), "%hu", s);
370 RTC_DCHECK_LE(len, arraysize(buf));
371 return std::string(&buf[0], len);
372}
373std::string ToString(const int s) {
374 char buf[32];
375 const int len = std::snprintf(&buf[0], arraysize(buf), "%d", s);
376 RTC_DCHECK_LE(len, arraysize(buf));
377 return std::string(&buf[0], len);
378}
379std::string ToString(const unsigned int s) {
380 char buf[32];
381 const int len = std::snprintf(&buf[0], arraysize(buf), "%u", s);
382 RTC_DCHECK_LE(len, arraysize(buf));
383 return std::string(&buf[0], len);
384}
385std::string ToString(const long int s) {
386 char buf[32];
387 const int len = std::snprintf(&buf[0], arraysize(buf), "%ld", s);
388 RTC_DCHECK_LE(len, arraysize(buf));
389 return std::string(&buf[0], len);
390}
391std::string ToString(const unsigned long int s) {
392 char buf[32];
393 const int len = std::snprintf(&buf[0], arraysize(buf), "%lu", s);
394 RTC_DCHECK_LE(len, arraysize(buf));
395 return std::string(&buf[0], len);
396}
397std::string ToString(const long long int s) {
398 char buf[32];
399 const int len = std::snprintf(&buf[0], arraysize(buf), "%lld", s);
400 RTC_DCHECK_LE(len, arraysize(buf));
401 return std::string(&buf[0], len);
402}
403std::string ToString(const unsigned long long int s) {
404 char buf[32];
405 const int len = std::snprintf(&buf[0], arraysize(buf), "%llu", s);
406 RTC_DCHECK_LE(len, arraysize(buf));
407 return std::string(&buf[0], len);
408}
409
410std::string ToString(const double d) {
411 char buf[32];
412 const int len = std::snprintf(&buf[0], arraysize(buf), "%g", d);
413 RTC_DCHECK_LE(len, arraysize(buf));
414 return std::string(&buf[0], len);
415}
416
Jonas Olsson88e18482018-09-03 10:15:08 +0200417std::string ToString(const long double d) {
418 char buf[32];
419 const int len = std::snprintf(&buf[0], arraysize(buf), "%Lg", d);
420 RTC_DCHECK_LE(len, arraysize(buf));
421 return std::string(&buf[0], len);
422}
423
Jonas Olsson6b1985d2018-07-05 11:59:48 +0200424std::string ToString(const void* const p) {
425 char buf[32];
426 const int len = std::snprintf(&buf[0], arraysize(buf), "%p", p);
427 RTC_DCHECK_LE(len, arraysize(buf));
428 return std::string(&buf[0], len);
429}
430
431bool FromString(const std::string& s, bool* b) {
432 if (s == "false") {
433 *b = false;
434 return true;
435 }
436 if (s == "true") {
437 *b = true;
438 return true;
439 }
440 return false;
441}
442
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000443} // namespace rtc