blob: 6a065cb8b2f3f9f6578ae332a582faa882027dd2 [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
51size_t utf8_decode(const char* source, size_t srclen, unsigned long* value) {
52 const unsigned char* s = reinterpret_cast<const unsigned char*>(source);
Yves Gerey665174f2018-06-19 15:03:05 +020053 if ((s[0] & 0x80) == 0x00) { // Check s[0] == 0xxxxxxx
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000054 *value = s[0];
55 return 1;
56 }
57 if ((srclen < 2) || ((s[1] & 0xC0) != 0x80)) { // Check s[1] != 10xxxxxx
58 return 0;
59 }
60 // Accumulate the trailer byte values in value16, and combine it with the
61 // relevant bits from s[0], once we've determined the sequence length.
62 unsigned long value16 = (s[1] & 0x3F);
Yves Gerey665174f2018-06-19 15:03:05 +020063 if ((s[0] & 0xE0) == 0xC0) { // Check s[0] == 110xxxxx
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000064 *value = ((s[0] & 0x1F) << 6) | value16;
65 return 2;
66 }
67 if ((srclen < 3) || ((s[2] & 0xC0) != 0x80)) { // Check s[2] != 10xxxxxx
68 return 0;
69 }
70 value16 = (value16 << 6) | (s[2] & 0x3F);
Yves Gerey665174f2018-06-19 15:03:05 +020071 if ((s[0] & 0xF0) == 0xE0) { // Check s[0] == 1110xxxx
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000072 *value = ((s[0] & 0x0F) << 12) | value16;
73 return 3;
74 }
75 if ((srclen < 4) || ((s[3] & 0xC0) != 0x80)) { // Check s[3] != 10xxxxxx
76 return 0;
77 }
78 value16 = (value16 << 6) | (s[3] & 0x3F);
Yves Gerey665174f2018-06-19 15:03:05 +020079 if ((s[0] & 0xF8) == 0xF0) { // Check s[0] == 11110xxx
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000080 *value = ((s[0] & 0x07) << 18) | value16;
81 return 4;
82 }
83 return 0;
84}
85
86size_t utf8_encode(char* buffer, size_t buflen, unsigned long value) {
87 if ((value <= 0x7F) && (buflen >= 1)) {
88 buffer[0] = static_cast<unsigned char>(value);
89 return 1;
90 }
91 if ((value <= 0x7FF) && (buflen >= 2)) {
92 buffer[0] = 0xC0 | static_cast<unsigned char>(value >> 6);
93 buffer[1] = 0x80 | static_cast<unsigned char>(value & 0x3F);
94 return 2;
95 }
96 if ((value <= 0xFFFF) && (buflen >= 3)) {
97 buffer[0] = 0xE0 | static_cast<unsigned char>(value >> 12);
98 buffer[1] = 0x80 | static_cast<unsigned char>((value >> 6) & 0x3F);
99 buffer[2] = 0x80 | static_cast<unsigned char>(value & 0x3F);
100 return 3;
101 }
102 if ((value <= 0x1FFFFF) && (buflen >= 4)) {
103 buffer[0] = 0xF0 | static_cast<unsigned char>(value >> 18);
104 buffer[1] = 0x80 | static_cast<unsigned char>((value >> 12) & 0x3F);
105 buffer[2] = 0x80 | static_cast<unsigned char>((value >> 6) & 0x3F);
106 buffer[3] = 0x80 | static_cast<unsigned char>(value & 0x3F);
107 return 4;
108 }
109 return 0;
110}
111
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000112static const char HEX[] = "0123456789abcdef";
113
114char hex_encode(unsigned char val) {
henrikg91d6ede2015-09-17 00:24:34 -0700115 RTC_DCHECK_LT(val, 16);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000116 return (val < 16) ? HEX[val] : '!';
117}
118
119bool hex_decode(char ch, unsigned char* val) {
120 if ((ch >= '0') && (ch <= '9')) {
121 *val = ch - '0';
122 } else if ((ch >= 'A') && (ch <= 'Z')) {
123 *val = (ch - 'A') + 10;
124 } else if ((ch >= 'a') && (ch <= 'z')) {
125 *val = (ch - 'a') + 10;
126 } else {
127 return false;
128 }
129 return true;
130}
131
Yves Gerey665174f2018-06-19 15:03:05 +0200132size_t hex_encode(char* buffer,
133 size_t buflen,
134 const char* csource,
135 size_t srclen) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000136 return hex_encode_with_delimiter(buffer, buflen, csource, srclen, 0);
137}
138
Yves Gerey665174f2018-06-19 15:03:05 +0200139size_t hex_encode_with_delimiter(char* buffer,
140 size_t buflen,
141 const char* csource,
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(buffer); // TODO(kwiberg): estimate output size
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000145 if (buflen == 0)
146 return 0;
147
148 // Init and check bounds.
149 const unsigned char* bsource =
150 reinterpret_cast<const unsigned char*>(csource);
151 size_t srcpos = 0, bufpos = 0;
152 size_t needed = delimiter ? (srclen * 3) : (srclen * 2 + 1);
153 if (buflen < needed)
154 return 0;
155
156 while (srcpos < srclen) {
157 unsigned char ch = bsource[srcpos++];
Yves Gerey665174f2018-06-19 15:03:05 +0200158 buffer[bufpos] = hex_encode((ch >> 4) & 0xF);
159 buffer[bufpos + 1] = hex_encode((ch)&0xF);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000160 bufpos += 2;
161
162 // Don't write a delimiter after the last byte.
163 if (delimiter && (srcpos < srclen)) {
164 buffer[bufpos] = delimiter;
165 ++bufpos;
166 }
167 }
168
169 // Null terminate.
170 buffer[bufpos] = '\0';
171 return bufpos;
172}
173
Peter Thatcher1cf6f812015-05-15 10:40:45 -0700174std::string hex_encode(const std::string& str) {
175 return hex_encode(str.c_str(), str.size());
176}
177
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000178std::string hex_encode(const char* source, size_t srclen) {
179 return hex_encode_with_delimiter(source, srclen, 0);
180}
181
Yves Gerey665174f2018-06-19 15:03:05 +0200182std::string hex_encode_with_delimiter(const char* source,
183 size_t srclen,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000184 char delimiter) {
185 const size_t kBufferSize = srclen * 3;
186 char* buffer = STACK_ARRAY(char, kBufferSize);
Yves Gerey665174f2018-06-19 15:03:05 +0200187 size_t length =
188 hex_encode_with_delimiter(buffer, kBufferSize, source, srclen, delimiter);
henrikg91d6ede2015-09-17 00:24:34 -0700189 RTC_DCHECK(srclen == 0 || length > 0);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000190 return std::string(buffer, length);
191}
192
Yves Gerey665174f2018-06-19 15:03:05 +0200193size_t hex_decode(char* cbuffer,
194 size_t buflen,
195 const char* source,
196 size_t srclen) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000197 return hex_decode_with_delimiter(cbuffer, buflen, source, srclen, 0);
198}
199
Yves Gerey665174f2018-06-19 15:03:05 +0200200size_t hex_decode_with_delimiter(char* cbuffer,
201 size_t buflen,
202 const char* source,
203 size_t srclen,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000204 char delimiter) {
Henrik Grunell84879882018-03-23 15:33:03 +0100205 RTC_DCHECK(cbuffer); // TODO(kwiberg): estimate output size
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000206 if (buflen == 0)
207 return 0;
208
209 // Init and bounds check.
210 unsigned char* bbuffer = reinterpret_cast<unsigned char*>(cbuffer);
211 size_t srcpos = 0, bufpos = 0;
212 size_t needed = (delimiter) ? (srclen + 1) / 3 : srclen / 2;
213 if (buflen < needed)
214 return 0;
215
216 while (srcpos < srclen) {
217 if ((srclen - srcpos) < 2) {
218 // This means we have an odd number of bytes.
219 return 0;
220 }
221
222 unsigned char h1, h2;
223 if (!hex_decode(source[srcpos], &h1) ||
224 !hex_decode(source[srcpos + 1], &h2))
225 return 0;
226
227 bbuffer[bufpos++] = (h1 << 4) | h2;
228 srcpos += 2;
229
230 // Remove the delimiter if needed.
231 if (delimiter && (srclen - srcpos) > 1) {
232 if (source[srcpos] != delimiter)
233 return 0;
234 ++srcpos;
235 }
236 }
237
238 return bufpos;
239}
240
241size_t hex_decode(char* buffer, size_t buflen, const std::string& source) {
242 return hex_decode_with_delimiter(buffer, buflen, source, 0);
243}
Yves Gerey665174f2018-06-19 15:03:05 +0200244size_t hex_decode_with_delimiter(char* buffer,
245 size_t buflen,
246 const std::string& source,
247 char delimiter) {
248 return hex_decode_with_delimiter(buffer, buflen, source.c_str(),
249 source.length(), delimiter);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000250}
251
Yves Gerey665174f2018-06-19 15:03:05 +0200252size_t transform(std::string& value,
253 size_t maxlen,
254 const std::string& source,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000255 Transform t) {
256 char* buffer = STACK_ARRAY(char, maxlen + 1);
257 size_t length = t(buffer, maxlen + 1, source.data(), source.length());
258 value.assign(buffer, length);
259 return length;
260}
261
262std::string s_transform(const std::string& source, Transform t) {
Yves Gerey665174f2018-06-19 15:03:05 +0200263 // Ask transformation function to approximate the destination size (returns
264 // upper bound)
deadbeef37f5ecf2017-02-27 14:06:41 -0800265 size_t maxlen = t(nullptr, 0, source.data(), source.length());
Yves Gerey665174f2018-06-19 15:03:05 +0200266 char* buffer = STACK_ARRAY(char, maxlen);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000267 size_t len = t(buffer, maxlen, source.data(), source.length());
268 std::string result(buffer, len);
269 return result;
270}
271
Yves Gerey665174f2018-06-19 15:03:05 +0200272size_t tokenize(const std::string& source,
273 char delimiter,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000274 std::vector<std::string>* fields) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000275 fields->clear();
276 size_t last = 0;
277 for (size_t i = 0; i < source.length(); ++i) {
278 if (source[i] == delimiter) {
279 if (i != last) {
280 fields->push_back(source.substr(last, i - last));
281 }
282 last = i + 1;
283 }
284 }
285 if (last != source.length()) {
286 fields->push_back(source.substr(last, source.length() - last));
287 }
288 return fields->size();
289}
290
deadbeef0a6c4ca2015-10-06 11:38:28 -0700291size_t tokenize_with_empty_tokens(const std::string& source,
292 char delimiter,
293 std::vector<std::string>* fields) {
294 fields->clear();
295 size_t last = 0;
296 for (size_t i = 0; i < source.length(); ++i) {
297 if (source[i] == delimiter) {
298 fields->push_back(source.substr(last, i - last));
299 last = i + 1;
300 }
301 }
302 fields->push_back(source.substr(last, source.length() - last));
303 return fields->size();
304}
305
Yves Gerey665174f2018-06-19 15:03:05 +0200306size_t tokenize_append(const std::string& source,
307 char delimiter,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000308 std::vector<std::string>* fields) {
Yves Gerey665174f2018-06-19 15:03:05 +0200309 if (!fields)
310 return 0;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000311
312 std::vector<std::string> new_fields;
313 tokenize(source, delimiter, &new_fields);
314 fields->insert(fields->end(), new_fields.begin(), new_fields.end());
315 return fields->size();
316}
317
Yves Gerey665174f2018-06-19 15:03:05 +0200318size_t tokenize(const std::string& source,
319 char delimiter,
320 char start_mark,
321 char end_mark,
322 std::vector<std::string>* fields) {
323 if (!fields)
324 return 0;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000325 fields->clear();
326
327 std::string remain_source = source;
328 while (!remain_source.empty()) {
329 size_t start_pos = remain_source.find(start_mark);
Yves Gerey665174f2018-06-19 15:03:05 +0200330 if (std::string::npos == start_pos)
331 break;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000332 std::string pre_mark;
333 if (start_pos > 0) {
334 pre_mark = remain_source.substr(0, start_pos - 1);
335 }
336
337 ++start_pos;
338 size_t end_pos = remain_source.find(end_mark, start_pos);
Yves Gerey665174f2018-06-19 15:03:05 +0200339 if (std::string::npos == end_pos)
340 break;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000341
342 // We have found the matching marks. First tokenize the pre-mask. Then add
343 // the marked part as a single field. Finally, loop back for the post-mark.
344 tokenize_append(pre_mark, delimiter, fields);
345 fields->push_back(remain_source.substr(start_pos, end_pos - start_pos));
346 remain_source = remain_source.substr(end_pos + 1);
347 }
348
349 return tokenize_append(remain_source, delimiter, fields);
350}
351
Donald Curtis144d0182015-05-15 13:14:24 -0700352bool tokenize_first(const std::string& source,
353 const char delimiter,
354 std::string* token,
355 std::string* rest) {
Donald Curtis0e07f922015-05-15 09:21:23 -0700356 // Find the first delimiter
357 size_t left_pos = source.find(delimiter);
358 if (left_pos == std::string::npos) {
359 return false;
360 }
361
362 // Look for additional occurrances of delimiter.
363 size_t right_pos = left_pos + 1;
Donald Curtis144d0182015-05-15 13:14:24 -0700364 while (source[right_pos] == delimiter) {
Donald Curtis0e07f922015-05-15 09:21:23 -0700365 right_pos++;
366 }
367
368 *token = source.substr(0, left_pos);
369 *rest = source.substr(right_pos);
370 return true;
371}
372
Diogo Real7bd1f1b2017-09-08 12:50:41 -0700373std::string join(const std::vector<std::string>& source, char delimiter) {
374 if (source.size() == 0) {
375 return std::string();
376 }
377 // Find length of the string to be returned to pre-allocate memory.
378 size_t source_string_length = 0;
379 for (size_t i = 0; i < source.size(); ++i) {
380 source_string_length += source[i].length();
381 }
382
383 // Build the joined string.
384 std::string joined_string;
385 joined_string.reserve(source_string_length + source.size() - 1);
386 for (size_t i = 0; i < source.size(); ++i) {
387 if (i != 0) {
388 joined_string += delimiter;
389 }
390 joined_string += source[i];
391 }
392 return joined_string;
393}
394
Yves Gerey665174f2018-06-19 15:03:05 +0200395size_t split(const std::string& source,
396 char delimiter,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000397 std::vector<std::string>* fields) {
henrikg91d6ede2015-09-17 00:24:34 -0700398 RTC_DCHECK(fields);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000399 fields->clear();
400 size_t last = 0;
401 for (size_t i = 0; i < source.length(); ++i) {
402 if (source[i] == delimiter) {
403 fields->push_back(source.substr(last, i - last));
404 last = i + 1;
405 }
406 }
407 fields->push_back(source.substr(last, source.length() - last));
408 return fields->size();
409}
410
Jonas Olsson6b1985d2018-07-05 11:59:48 +0200411std::string ToString(const bool b) {
412 return b ? "true" : "false";
413}
414
415std::string ToString(const char* const s) {
416 return std::string(s);
417}
418std::string ToString(const std::string s) {
419 return s;
420}
421
422std::string ToString(const short s) {
423 char buf[32];
424 const int len = std::snprintf(&buf[0], arraysize(buf), "%hd", s);
425 RTC_DCHECK_LE(len, arraysize(buf));
426 return std::string(&buf[0], len);
427}
428std::string ToString(const unsigned short s) {
429 char buf[32];
430 const int len = std::snprintf(&buf[0], arraysize(buf), "%hu", s);
431 RTC_DCHECK_LE(len, arraysize(buf));
432 return std::string(&buf[0], len);
433}
434std::string ToString(const int s) {
435 char buf[32];
436 const int len = std::snprintf(&buf[0], arraysize(buf), "%d", s);
437 RTC_DCHECK_LE(len, arraysize(buf));
438 return std::string(&buf[0], len);
439}
440std::string ToString(const unsigned int s) {
441 char buf[32];
442 const int len = std::snprintf(&buf[0], arraysize(buf), "%u", s);
443 RTC_DCHECK_LE(len, arraysize(buf));
444 return std::string(&buf[0], len);
445}
446std::string ToString(const long int s) {
447 char buf[32];
448 const int len = std::snprintf(&buf[0], arraysize(buf), "%ld", s);
449 RTC_DCHECK_LE(len, arraysize(buf));
450 return std::string(&buf[0], len);
451}
452std::string ToString(const unsigned long int s) {
453 char buf[32];
454 const int len = std::snprintf(&buf[0], arraysize(buf), "%lu", s);
455 RTC_DCHECK_LE(len, arraysize(buf));
456 return std::string(&buf[0], len);
457}
458std::string ToString(const long long int s) {
459 char buf[32];
460 const int len = std::snprintf(&buf[0], arraysize(buf), "%lld", s);
461 RTC_DCHECK_LE(len, arraysize(buf));
462 return std::string(&buf[0], len);
463}
464std::string ToString(const unsigned long long int s) {
465 char buf[32];
466 const int len = std::snprintf(&buf[0], arraysize(buf), "%llu", s);
467 RTC_DCHECK_LE(len, arraysize(buf));
468 return std::string(&buf[0], len);
469}
470
471std::string ToString(const double d) {
472 char buf[32];
473 const int len = std::snprintf(&buf[0], arraysize(buf), "%g", d);
474 RTC_DCHECK_LE(len, arraysize(buf));
475 return std::string(&buf[0], len);
476}
477
Jonas Olsson88e18482018-09-03 10:15:08 +0200478std::string ToString(const long double d) {
479 char buf[32];
480 const int len = std::snprintf(&buf[0], arraysize(buf), "%Lg", d);
481 RTC_DCHECK_LE(len, arraysize(buf));
482 return std::string(&buf[0], len);
483}
484
Jonas Olsson6b1985d2018-07-05 11:59:48 +0200485std::string ToString(const void* const p) {
486 char buf[32];
487 const int len = std::snprintf(&buf[0], arraysize(buf), "%p", p);
488 RTC_DCHECK_LE(len, arraysize(buf));
489 return std::string(&buf[0], len);
490}
491
492bool FromString(const std::string& s, bool* b) {
493 if (s == "false") {
494 *b = false;
495 return true;
496 }
497 if (s == "true") {
498 *b = true;
499 return true;
500 }
501 return false;
502}
503
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000504} // namespace rtc