blob: 7b2eda7051f42d3e233d043e44b7fc32149bbacc [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
Steve Anton10542f22019-01-11 09:11:00 -080011#include "rtc_base/string_encode.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"
Steve Anton10542f22019-01-11 09:11:00 -080017#include "rtc_base/string_utils.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000018
19namespace rtc {
20
21/////////////////////////////////////////////////////////////////////////////
22// String Encoding Utilities
23/////////////////////////////////////////////////////////////////////////////
24
Niels Möllere7e36012019-05-23 12:10:26 +020025namespace {
26const char HEX[] = "0123456789abcdef";
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000027
Niels Möllere7e36012019-05-23 12:10:26 +020028// Convert an unsigned value from 0 to 15 to the hex character equivalent...
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000029char hex_encode(unsigned char val) {
henrikg91d6ede2015-09-17 00:24:34 -070030 RTC_DCHECK_LT(val, 16);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000031 return (val < 16) ? HEX[val] : '!';
32}
33
Niels Möllere7e36012019-05-23 12:10:26 +020034// ...and vice-versa.
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000035bool hex_decode(char ch, unsigned char* val) {
36 if ((ch >= '0') && (ch <= '9')) {
37 *val = ch - '0';
Niels Möller12048c72018-10-29 12:58:48 +010038 } else if ((ch >= 'A') && (ch <= 'F')) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000039 *val = (ch - 'A') + 10;
Niels Möller12048c72018-10-29 12:58:48 +010040 } else if ((ch >= 'a') && (ch <= 'f')) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000041 *val = (ch - 'a') + 10;
42 } else {
43 return false;
44 }
45 return true;
46}
47
Niels Möllere7e36012019-05-23 12:10:26 +020048// hex_encode, but separate each byte representation with a delimiter.
49// |delimiter| == 0 means no delimiter
50// If the buffer is too short, we return 0
Yves Gerey665174f2018-06-19 15:03:05 +020051size_t hex_encode_with_delimiter(char* buffer,
52 size_t buflen,
53 const char* csource,
54 size_t srclen,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000055 char delimiter) {
Henrik Grunell84879882018-03-23 15:33:03 +010056 RTC_DCHECK(buffer); // TODO(kwiberg): estimate output size
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000057 if (buflen == 0)
58 return 0;
59
60 // Init and check bounds.
61 const unsigned char* bsource =
62 reinterpret_cast<const unsigned char*>(csource);
63 size_t srcpos = 0, bufpos = 0;
64 size_t needed = delimiter ? (srclen * 3) : (srclen * 2 + 1);
65 if (buflen < needed)
66 return 0;
67
68 while (srcpos < srclen) {
69 unsigned char ch = bsource[srcpos++];
Yves Gerey665174f2018-06-19 15:03:05 +020070 buffer[bufpos] = hex_encode((ch >> 4) & 0xF);
71 buffer[bufpos + 1] = hex_encode((ch)&0xF);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000072 bufpos += 2;
73
74 // Don't write a delimiter after the last byte.
75 if (delimiter && (srcpos < srclen)) {
76 buffer[bufpos] = delimiter;
77 ++bufpos;
78 }
79 }
80
81 // Null terminate.
82 buffer[bufpos] = '\0';
83 return bufpos;
84}
85
Niels Möllere7e36012019-05-23 12:10:26 +020086} // namespace
87
Peter Thatcher1cf6f812015-05-15 10:40:45 -070088std::string hex_encode(const std::string& str) {
89 return hex_encode(str.c_str(), str.size());
90}
91
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000092std::string hex_encode(const char* source, size_t srclen) {
93 return hex_encode_with_delimiter(source, srclen, 0);
94}
95
Yves Gerey665174f2018-06-19 15:03:05 +020096std::string hex_encode_with_delimiter(const char* source,
97 size_t srclen,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000098 char delimiter) {
99 const size_t kBufferSize = srclen * 3;
100 char* buffer = STACK_ARRAY(char, kBufferSize);
Yves Gerey665174f2018-06-19 15:03:05 +0200101 size_t length =
102 hex_encode_with_delimiter(buffer, kBufferSize, source, srclen, delimiter);
henrikg91d6ede2015-09-17 00:24:34 -0700103 RTC_DCHECK(srclen == 0 || length > 0);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000104 return std::string(buffer, length);
105}
106
Yves Gerey665174f2018-06-19 15:03:05 +0200107size_t hex_decode(char* cbuffer,
108 size_t buflen,
109 const char* source,
110 size_t srclen) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000111 return hex_decode_with_delimiter(cbuffer, buflen, source, srclen, 0);
112}
113
Yves Gerey665174f2018-06-19 15:03:05 +0200114size_t hex_decode_with_delimiter(char* cbuffer,
115 size_t buflen,
116 const char* source,
117 size_t srclen,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000118 char delimiter) {
Henrik Grunell84879882018-03-23 15:33:03 +0100119 RTC_DCHECK(cbuffer); // TODO(kwiberg): estimate output size
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000120 if (buflen == 0)
121 return 0;
122
123 // Init and bounds check.
124 unsigned char* bbuffer = reinterpret_cast<unsigned char*>(cbuffer);
125 size_t srcpos = 0, bufpos = 0;
126 size_t needed = (delimiter) ? (srclen + 1) / 3 : srclen / 2;
127 if (buflen < needed)
128 return 0;
129
130 while (srcpos < srclen) {
131 if ((srclen - srcpos) < 2) {
132 // This means we have an odd number of bytes.
133 return 0;
134 }
135
136 unsigned char h1, h2;
137 if (!hex_decode(source[srcpos], &h1) ||
138 !hex_decode(source[srcpos + 1], &h2))
139 return 0;
140
141 bbuffer[bufpos++] = (h1 << 4) | h2;
142 srcpos += 2;
143
144 // Remove the delimiter if needed.
145 if (delimiter && (srclen - srcpos) > 1) {
146 if (source[srcpos] != delimiter)
147 return 0;
148 ++srcpos;
149 }
150 }
151
152 return bufpos;
153}
154
155size_t hex_decode(char* buffer, size_t buflen, const std::string& source) {
156 return hex_decode_with_delimiter(buffer, buflen, source, 0);
157}
Yves Gerey665174f2018-06-19 15:03:05 +0200158size_t hex_decode_with_delimiter(char* buffer,
159 size_t buflen,
160 const std::string& source,
161 char delimiter) {
162 return hex_decode_with_delimiter(buffer, buflen, source.c_str(),
163 source.length(), delimiter);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000164}
165
Yves Gerey665174f2018-06-19 15:03:05 +0200166size_t tokenize(const std::string& source,
167 char delimiter,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000168 std::vector<std::string>* fields) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000169 fields->clear();
170 size_t last = 0;
171 for (size_t i = 0; i < source.length(); ++i) {
172 if (source[i] == delimiter) {
173 if (i != last) {
174 fields->push_back(source.substr(last, i - last));
175 }
176 last = i + 1;
177 }
178 }
179 if (last != source.length()) {
180 fields->push_back(source.substr(last, source.length() - last));
181 }
182 return fields->size();
183}
184
deadbeef0a6c4ca2015-10-06 11:38:28 -0700185size_t tokenize_with_empty_tokens(const std::string& source,
186 char delimiter,
187 std::vector<std::string>* fields) {
188 fields->clear();
189 size_t last = 0;
190 for (size_t i = 0; i < source.length(); ++i) {
191 if (source[i] == delimiter) {
192 fields->push_back(source.substr(last, i - last));
193 last = i + 1;
194 }
195 }
196 fields->push_back(source.substr(last, source.length() - last));
197 return fields->size();
198}
199
Yves Gerey665174f2018-06-19 15:03:05 +0200200size_t tokenize_append(const std::string& source,
201 char delimiter,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000202 std::vector<std::string>* fields) {
Yves Gerey665174f2018-06-19 15:03:05 +0200203 if (!fields)
204 return 0;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000205
206 std::vector<std::string> new_fields;
207 tokenize(source, delimiter, &new_fields);
208 fields->insert(fields->end(), new_fields.begin(), new_fields.end());
209 return fields->size();
210}
211
Yves Gerey665174f2018-06-19 15:03:05 +0200212size_t tokenize(const std::string& source,
213 char delimiter,
214 char start_mark,
215 char end_mark,
216 std::vector<std::string>* fields) {
217 if (!fields)
218 return 0;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000219 fields->clear();
220
221 std::string remain_source = source;
222 while (!remain_source.empty()) {
223 size_t start_pos = remain_source.find(start_mark);
Yves Gerey665174f2018-06-19 15:03:05 +0200224 if (std::string::npos == start_pos)
225 break;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000226 std::string pre_mark;
227 if (start_pos > 0) {
228 pre_mark = remain_source.substr(0, start_pos - 1);
229 }
230
231 ++start_pos;
232 size_t end_pos = remain_source.find(end_mark, start_pos);
Yves Gerey665174f2018-06-19 15:03:05 +0200233 if (std::string::npos == end_pos)
234 break;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000235
236 // We have found the matching marks. First tokenize the pre-mask. Then add
237 // the marked part as a single field. Finally, loop back for the post-mark.
238 tokenize_append(pre_mark, delimiter, fields);
239 fields->push_back(remain_source.substr(start_pos, end_pos - start_pos));
240 remain_source = remain_source.substr(end_pos + 1);
241 }
242
243 return tokenize_append(remain_source, delimiter, fields);
244}
245
Donald Curtis144d0182015-05-15 13:14:24 -0700246bool tokenize_first(const std::string& source,
247 const char delimiter,
248 std::string* token,
249 std::string* rest) {
Donald Curtis0e07f922015-05-15 09:21:23 -0700250 // Find the first delimiter
251 size_t left_pos = source.find(delimiter);
252 if (left_pos == std::string::npos) {
253 return false;
254 }
255
256 // Look for additional occurrances of delimiter.
257 size_t right_pos = left_pos + 1;
Donald Curtis144d0182015-05-15 13:14:24 -0700258 while (source[right_pos] == delimiter) {
Donald Curtis0e07f922015-05-15 09:21:23 -0700259 right_pos++;
260 }
261
262 *token = source.substr(0, left_pos);
263 *rest = source.substr(right_pos);
264 return true;
265}
266
Diogo Real7bd1f1b2017-09-08 12:50:41 -0700267std::string join(const std::vector<std::string>& source, char delimiter) {
268 if (source.size() == 0) {
269 return std::string();
270 }
271 // Find length of the string to be returned to pre-allocate memory.
272 size_t source_string_length = 0;
273 for (size_t i = 0; i < source.size(); ++i) {
274 source_string_length += source[i].length();
275 }
276
277 // Build the joined string.
278 std::string joined_string;
279 joined_string.reserve(source_string_length + source.size() - 1);
280 for (size_t i = 0; i < source.size(); ++i) {
281 if (i != 0) {
282 joined_string += delimiter;
283 }
284 joined_string += source[i];
285 }
286 return joined_string;
287}
288
Yves Gerey665174f2018-06-19 15:03:05 +0200289size_t split(const std::string& source,
290 char delimiter,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000291 std::vector<std::string>* fields) {
henrikg91d6ede2015-09-17 00:24:34 -0700292 RTC_DCHECK(fields);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000293 fields->clear();
294 size_t last = 0;
295 for (size_t i = 0; i < source.length(); ++i) {
296 if (source[i] == delimiter) {
297 fields->push_back(source.substr(last, i - last));
298 last = i + 1;
299 }
300 }
301 fields->push_back(source.substr(last, source.length() - last));
302 return fields->size();
303}
304
Jonas Olsson6b1985d2018-07-05 11:59:48 +0200305std::string ToString(const bool b) {
306 return b ? "true" : "false";
307}
308
309std::string ToString(const char* const s) {
310 return std::string(s);
311}
312std::string ToString(const std::string s) {
313 return s;
314}
315
316std::string ToString(const short s) {
317 char buf[32];
318 const int len = std::snprintf(&buf[0], arraysize(buf), "%hd", s);
319 RTC_DCHECK_LE(len, arraysize(buf));
320 return std::string(&buf[0], len);
321}
322std::string ToString(const unsigned short s) {
323 char buf[32];
324 const int len = std::snprintf(&buf[0], arraysize(buf), "%hu", s);
325 RTC_DCHECK_LE(len, arraysize(buf));
326 return std::string(&buf[0], len);
327}
328std::string ToString(const int s) {
329 char buf[32];
330 const int len = std::snprintf(&buf[0], arraysize(buf), "%d", s);
331 RTC_DCHECK_LE(len, arraysize(buf));
332 return std::string(&buf[0], len);
333}
334std::string ToString(const unsigned int s) {
335 char buf[32];
336 const int len = std::snprintf(&buf[0], arraysize(buf), "%u", s);
337 RTC_DCHECK_LE(len, arraysize(buf));
338 return std::string(&buf[0], len);
339}
340std::string ToString(const long int s) {
341 char buf[32];
342 const int len = std::snprintf(&buf[0], arraysize(buf), "%ld", s);
343 RTC_DCHECK_LE(len, arraysize(buf));
344 return std::string(&buf[0], len);
345}
346std::string ToString(const unsigned long int s) {
347 char buf[32];
348 const int len = std::snprintf(&buf[0], arraysize(buf), "%lu", s);
349 RTC_DCHECK_LE(len, arraysize(buf));
350 return std::string(&buf[0], len);
351}
352std::string ToString(const long long int s) {
353 char buf[32];
354 const int len = std::snprintf(&buf[0], arraysize(buf), "%lld", s);
355 RTC_DCHECK_LE(len, arraysize(buf));
356 return std::string(&buf[0], len);
357}
358std::string ToString(const unsigned long long int s) {
359 char buf[32];
360 const int len = std::snprintf(&buf[0], arraysize(buf), "%llu", s);
361 RTC_DCHECK_LE(len, arraysize(buf));
362 return std::string(&buf[0], len);
363}
364
365std::string ToString(const double d) {
366 char buf[32];
367 const int len = std::snprintf(&buf[0], arraysize(buf), "%g", d);
368 RTC_DCHECK_LE(len, arraysize(buf));
369 return std::string(&buf[0], len);
370}
371
Jonas Olsson88e18482018-09-03 10:15:08 +0200372std::string ToString(const long double d) {
373 char buf[32];
374 const int len = std::snprintf(&buf[0], arraysize(buf), "%Lg", d);
375 RTC_DCHECK_LE(len, arraysize(buf));
376 return std::string(&buf[0], len);
377}
378
Jonas Olsson6b1985d2018-07-05 11:59:48 +0200379std::string ToString(const void* const p) {
380 char buf[32];
381 const int len = std::snprintf(&buf[0], arraysize(buf), "%p", p);
382 RTC_DCHECK_LE(len, arraysize(buf));
383 return std::string(&buf[0], len);
384}
385
386bool FromString(const std::string& s, bool* b) {
387 if (s == "false") {
388 *b = false;
389 return true;
390 }
391 if (s == "true") {
392 *b = true;
393 return true;
394 }
395 return false;
396}
397
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000398} // namespace rtc