blob: fc4e3bc8e7315cc7235c1f32769a5a6135258522 [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
Yves Gerey988cc082018-10-23 12:03:01 +020013#include <cstdio>
14
Jonas Olsson6b1985d2018-07-05 11:59:48 +020015#include "rtc_base/arraysize.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "rtc_base/checks.h"
17#include "rtc_base/stringutils.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000018
19namespace rtc {
20
21/////////////////////////////////////////////////////////////////////////////
22// String Encoding Utilities
23/////////////////////////////////////////////////////////////////////////////
24
Yves Gerey665174f2018-06-19 15:03:05 +020025size_t url_decode(char* buffer,
26 size_t buflen,
27 const char* source,
28 size_t srclen) {
deadbeef37f5ecf2017-02-27 14:06:41 -080029 if (nullptr == buffer)
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000030 return srclen + 1;
31 if (buflen <= 0)
32 return 0;
33
34 unsigned char h1, h2;
35 size_t srcpos = 0, bufpos = 0;
36 while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
37 unsigned char ch = source[srcpos++];
38 if (ch == '+') {
39 buffer[bufpos++] = ' ';
Yves Gerey665174f2018-06-19 15:03:05 +020040 } else if ((ch == '%') && (srcpos + 1 < srclen) &&
41 hex_decode(source[srcpos], &h1) &&
42 hex_decode(source[srcpos + 1], &h2)) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000043 buffer[bufpos++] = (h1 << 4) | h2;
44 srcpos += 2;
45 } else {
46 buffer[bufpos++] = ch;
47 }
48 }
49 buffer[bufpos] = '\0';
50 return bufpos;
51}
52
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000053static const char HEX[] = "0123456789abcdef";
54
55char hex_encode(unsigned char val) {
henrikg91d6ede2015-09-17 00:24:34 -070056 RTC_DCHECK_LT(val, 16);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000057 return (val < 16) ? HEX[val] : '!';
58}
59
60bool hex_decode(char ch, unsigned char* val) {
61 if ((ch >= '0') && (ch <= '9')) {
62 *val = ch - '0';
Niels Möller12048c72018-10-29 12:58:48 +010063 } else if ((ch >= 'A') && (ch <= 'F')) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000064 *val = (ch - 'A') + 10;
Niels Möller12048c72018-10-29 12:58:48 +010065 } else if ((ch >= 'a') && (ch <= 'f')) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000066 *val = (ch - 'a') + 10;
67 } else {
68 return false;
69 }
70 return true;
71}
72
Yves Gerey665174f2018-06-19 15:03:05 +020073size_t hex_encode(char* buffer,
74 size_t buflen,
75 const char* csource,
76 size_t srclen) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000077 return hex_encode_with_delimiter(buffer, buflen, csource, srclen, 0);
78}
79
Yves Gerey665174f2018-06-19 15:03:05 +020080size_t hex_encode_with_delimiter(char* buffer,
81 size_t buflen,
82 const char* csource,
83 size_t srclen,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000084 char delimiter) {
Henrik Grunell84879882018-03-23 15:33:03 +010085 RTC_DCHECK(buffer); // TODO(kwiberg): estimate output size
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000086 if (buflen == 0)
87 return 0;
88
89 // Init and check bounds.
90 const unsigned char* bsource =
91 reinterpret_cast<const unsigned char*>(csource);
92 size_t srcpos = 0, bufpos = 0;
93 size_t needed = delimiter ? (srclen * 3) : (srclen * 2 + 1);
94 if (buflen < needed)
95 return 0;
96
97 while (srcpos < srclen) {
98 unsigned char ch = bsource[srcpos++];
Yves Gerey665174f2018-06-19 15:03:05 +020099 buffer[bufpos] = hex_encode((ch >> 4) & 0xF);
100 buffer[bufpos + 1] = hex_encode((ch)&0xF);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000101 bufpos += 2;
102
103 // Don't write a delimiter after the last byte.
104 if (delimiter && (srcpos < srclen)) {
105 buffer[bufpos] = delimiter;
106 ++bufpos;
107 }
108 }
109
110 // Null terminate.
111 buffer[bufpos] = '\0';
112 return bufpos;
113}
114
Peter Thatcher1cf6f812015-05-15 10:40:45 -0700115std::string hex_encode(const std::string& str) {
116 return hex_encode(str.c_str(), str.size());
117}
118
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000119std::string hex_encode(const char* source, size_t srclen) {
120 return hex_encode_with_delimiter(source, srclen, 0);
121}
122
Yves Gerey665174f2018-06-19 15:03:05 +0200123std::string hex_encode_with_delimiter(const char* source,
124 size_t srclen,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000125 char delimiter) {
126 const size_t kBufferSize = srclen * 3;
127 char* buffer = STACK_ARRAY(char, kBufferSize);
Yves Gerey665174f2018-06-19 15:03:05 +0200128 size_t length =
129 hex_encode_with_delimiter(buffer, kBufferSize, source, srclen, delimiter);
henrikg91d6ede2015-09-17 00:24:34 -0700130 RTC_DCHECK(srclen == 0 || length > 0);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000131 return std::string(buffer, length);
132}
133
Yves Gerey665174f2018-06-19 15:03:05 +0200134size_t hex_decode(char* cbuffer,
135 size_t buflen,
136 const char* source,
137 size_t srclen) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000138 return hex_decode_with_delimiter(cbuffer, buflen, source, srclen, 0);
139}
140
Yves Gerey665174f2018-06-19 15:03:05 +0200141size_t hex_decode_with_delimiter(char* cbuffer,
142 size_t buflen,
143 const char* source,
144 size_t srclen,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000145 char delimiter) {
Henrik Grunell84879882018-03-23 15:33:03 +0100146 RTC_DCHECK(cbuffer); // TODO(kwiberg): estimate output size
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000147 if (buflen == 0)
148 return 0;
149
150 // Init and bounds check.
151 unsigned char* bbuffer = reinterpret_cast<unsigned char*>(cbuffer);
152 size_t srcpos = 0, bufpos = 0;
153 size_t needed = (delimiter) ? (srclen + 1) / 3 : srclen / 2;
154 if (buflen < needed)
155 return 0;
156
157 while (srcpos < srclen) {
158 if ((srclen - srcpos) < 2) {
159 // This means we have an odd number of bytes.
160 return 0;
161 }
162
163 unsigned char h1, h2;
164 if (!hex_decode(source[srcpos], &h1) ||
165 !hex_decode(source[srcpos + 1], &h2))
166 return 0;
167
168 bbuffer[bufpos++] = (h1 << 4) | h2;
169 srcpos += 2;
170
171 // Remove the delimiter if needed.
172 if (delimiter && (srclen - srcpos) > 1) {
173 if (source[srcpos] != delimiter)
174 return 0;
175 ++srcpos;
176 }
177 }
178
179 return bufpos;
180}
181
182size_t hex_decode(char* buffer, size_t buflen, const std::string& source) {
183 return hex_decode_with_delimiter(buffer, buflen, source, 0);
184}
Yves Gerey665174f2018-06-19 15:03:05 +0200185size_t hex_decode_with_delimiter(char* buffer,
186 size_t buflen,
187 const std::string& source,
188 char delimiter) {
189 return hex_decode_with_delimiter(buffer, buflen, source.c_str(),
190 source.length(), delimiter);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000191}
192
Yves Gerey665174f2018-06-19 15:03:05 +0200193size_t transform(std::string& value,
194 size_t maxlen,
195 const std::string& source,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000196 Transform t) {
197 char* buffer = STACK_ARRAY(char, maxlen + 1);
198 size_t length = t(buffer, maxlen + 1, source.data(), source.length());
199 value.assign(buffer, length);
200 return length;
201}
202
203std::string s_transform(const std::string& source, Transform t) {
Yves Gerey665174f2018-06-19 15:03:05 +0200204 // Ask transformation function to approximate the destination size (returns
205 // upper bound)
deadbeef37f5ecf2017-02-27 14:06:41 -0800206 size_t maxlen = t(nullptr, 0, source.data(), source.length());
Yves Gerey665174f2018-06-19 15:03:05 +0200207 char* buffer = STACK_ARRAY(char, maxlen);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000208 size_t len = t(buffer, maxlen, source.data(), source.length());
209 std::string result(buffer, len);
210 return result;
211}
212
Yves Gerey665174f2018-06-19 15:03:05 +0200213size_t tokenize(const std::string& source,
214 char delimiter,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000215 std::vector<std::string>* fields) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000216 fields->clear();
217 size_t last = 0;
218 for (size_t i = 0; i < source.length(); ++i) {
219 if (source[i] == delimiter) {
220 if (i != last) {
221 fields->push_back(source.substr(last, i - last));
222 }
223 last = i + 1;
224 }
225 }
226 if (last != source.length()) {
227 fields->push_back(source.substr(last, source.length() - last));
228 }
229 return fields->size();
230}
231
deadbeef0a6c4ca2015-10-06 11:38:28 -0700232size_t tokenize_with_empty_tokens(const std::string& source,
233 char delimiter,
234 std::vector<std::string>* fields) {
235 fields->clear();
236 size_t last = 0;
237 for (size_t i = 0; i < source.length(); ++i) {
238 if (source[i] == delimiter) {
239 fields->push_back(source.substr(last, i - last));
240 last = i + 1;
241 }
242 }
243 fields->push_back(source.substr(last, source.length() - last));
244 return fields->size();
245}
246
Yves Gerey665174f2018-06-19 15:03:05 +0200247size_t tokenize_append(const std::string& source,
248 char delimiter,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000249 std::vector<std::string>* fields) {
Yves Gerey665174f2018-06-19 15:03:05 +0200250 if (!fields)
251 return 0;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000252
253 std::vector<std::string> new_fields;
254 tokenize(source, delimiter, &new_fields);
255 fields->insert(fields->end(), new_fields.begin(), new_fields.end());
256 return fields->size();
257}
258
Yves Gerey665174f2018-06-19 15:03:05 +0200259size_t tokenize(const std::string& source,
260 char delimiter,
261 char start_mark,
262 char end_mark,
263 std::vector<std::string>* fields) {
264 if (!fields)
265 return 0;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000266 fields->clear();
267
268 std::string remain_source = source;
269 while (!remain_source.empty()) {
270 size_t start_pos = remain_source.find(start_mark);
Yves Gerey665174f2018-06-19 15:03:05 +0200271 if (std::string::npos == start_pos)
272 break;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000273 std::string pre_mark;
274 if (start_pos > 0) {
275 pre_mark = remain_source.substr(0, start_pos - 1);
276 }
277
278 ++start_pos;
279 size_t end_pos = remain_source.find(end_mark, start_pos);
Yves Gerey665174f2018-06-19 15:03:05 +0200280 if (std::string::npos == end_pos)
281 break;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000282
283 // We have found the matching marks. First tokenize the pre-mask. Then add
284 // the marked part as a single field. Finally, loop back for the post-mark.
285 tokenize_append(pre_mark, delimiter, fields);
286 fields->push_back(remain_source.substr(start_pos, end_pos - start_pos));
287 remain_source = remain_source.substr(end_pos + 1);
288 }
289
290 return tokenize_append(remain_source, delimiter, fields);
291}
292
Donald Curtis144d0182015-05-15 13:14:24 -0700293bool tokenize_first(const std::string& source,
294 const char delimiter,
295 std::string* token,
296 std::string* rest) {
Donald Curtis0e07f922015-05-15 09:21:23 -0700297 // Find the first delimiter
298 size_t left_pos = source.find(delimiter);
299 if (left_pos == std::string::npos) {
300 return false;
301 }
302
303 // Look for additional occurrances of delimiter.
304 size_t right_pos = left_pos + 1;
Donald Curtis144d0182015-05-15 13:14:24 -0700305 while (source[right_pos] == delimiter) {
Donald Curtis0e07f922015-05-15 09:21:23 -0700306 right_pos++;
307 }
308
309 *token = source.substr(0, left_pos);
310 *rest = source.substr(right_pos);
311 return true;
312}
313
Diogo Real7bd1f1b2017-09-08 12:50:41 -0700314std::string join(const std::vector<std::string>& source, char delimiter) {
315 if (source.size() == 0) {
316 return std::string();
317 }
318 // Find length of the string to be returned to pre-allocate memory.
319 size_t source_string_length = 0;
320 for (size_t i = 0; i < source.size(); ++i) {
321 source_string_length += source[i].length();
322 }
323
324 // Build the joined string.
325 std::string joined_string;
326 joined_string.reserve(source_string_length + source.size() - 1);
327 for (size_t i = 0; i < source.size(); ++i) {
328 if (i != 0) {
329 joined_string += delimiter;
330 }
331 joined_string += source[i];
332 }
333 return joined_string;
334}
335
Yves Gerey665174f2018-06-19 15:03:05 +0200336size_t split(const std::string& source,
337 char delimiter,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000338 std::vector<std::string>* fields) {
henrikg91d6ede2015-09-17 00:24:34 -0700339 RTC_DCHECK(fields);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000340 fields->clear();
341 size_t last = 0;
342 for (size_t i = 0; i < source.length(); ++i) {
343 if (source[i] == delimiter) {
344 fields->push_back(source.substr(last, i - last));
345 last = i + 1;
346 }
347 }
348 fields->push_back(source.substr(last, source.length() - last));
349 return fields->size();
350}
351
Jonas Olsson6b1985d2018-07-05 11:59:48 +0200352std::string ToString(const bool b) {
353 return b ? "true" : "false";
354}
355
356std::string ToString(const char* const s) {
357 return std::string(s);
358}
359std::string ToString(const std::string s) {
360 return s;
361}
362
363std::string ToString(const short s) {
364 char buf[32];
365 const int len = std::snprintf(&buf[0], arraysize(buf), "%hd", s);
366 RTC_DCHECK_LE(len, arraysize(buf));
367 return std::string(&buf[0], len);
368}
369std::string ToString(const unsigned short s) {
370 char buf[32];
371 const int len = std::snprintf(&buf[0], arraysize(buf), "%hu", s);
372 RTC_DCHECK_LE(len, arraysize(buf));
373 return std::string(&buf[0], len);
374}
375std::string ToString(const int s) {
376 char buf[32];
377 const int len = std::snprintf(&buf[0], arraysize(buf), "%d", s);
378 RTC_DCHECK_LE(len, arraysize(buf));
379 return std::string(&buf[0], len);
380}
381std::string ToString(const unsigned int s) {
382 char buf[32];
383 const int len = std::snprintf(&buf[0], arraysize(buf), "%u", s);
384 RTC_DCHECK_LE(len, arraysize(buf));
385 return std::string(&buf[0], len);
386}
387std::string ToString(const long int s) {
388 char buf[32];
389 const int len = std::snprintf(&buf[0], arraysize(buf), "%ld", s);
390 RTC_DCHECK_LE(len, arraysize(buf));
391 return std::string(&buf[0], len);
392}
393std::string ToString(const unsigned long int s) {
394 char buf[32];
395 const int len = std::snprintf(&buf[0], arraysize(buf), "%lu", s);
396 RTC_DCHECK_LE(len, arraysize(buf));
397 return std::string(&buf[0], len);
398}
399std::string ToString(const long long int s) {
400 char buf[32];
401 const int len = std::snprintf(&buf[0], arraysize(buf), "%lld", s);
402 RTC_DCHECK_LE(len, arraysize(buf));
403 return std::string(&buf[0], len);
404}
405std::string ToString(const unsigned long long int s) {
406 char buf[32];
407 const int len = std::snprintf(&buf[0], arraysize(buf), "%llu", s);
408 RTC_DCHECK_LE(len, arraysize(buf));
409 return std::string(&buf[0], len);
410}
411
412std::string ToString(const double d) {
413 char buf[32];
414 const int len = std::snprintf(&buf[0], arraysize(buf), "%g", d);
415 RTC_DCHECK_LE(len, arraysize(buf));
416 return std::string(&buf[0], len);
417}
418
Jonas Olsson88e18482018-09-03 10:15:08 +0200419std::string ToString(const long double d) {
420 char buf[32];
421 const int len = std::snprintf(&buf[0], arraysize(buf), "%Lg", d);
422 RTC_DCHECK_LE(len, arraysize(buf));
423 return std::string(&buf[0], len);
424}
425
Jonas Olsson6b1985d2018-07-05 11:59:48 +0200426std::string ToString(const void* const p) {
427 char buf[32];
428 const int len = std::snprintf(&buf[0], arraysize(buf), "%p", p);
429 RTC_DCHECK_LE(len, arraysize(buf));
430 return std::string(&buf[0], len);
431}
432
433bool FromString(const std::string& s, bool* b) {
434 if (s == "false") {
435 *b = false;
436 return true;
437 }
438 if (s == "true") {
439 *b = true;
440 return true;
441 }
442 return false;
443}
444
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000445} // namespace rtc