1 #include "strings/escaping.h" 2 3 #include <cassert> 4 5 #include "android-base/logging.h" 6 #include "strings/ascii_ctype.h" 7 8 namespace dynamic_depth { 9 10 // ---------------------------------------------------------------------- 11 // ptrdiff_t Base64Unescape() - base64 decoder 12 // ptrdiff_t Base64Escape() - base64 encoder 13 // ptrdiff_t WebSafeBase64Unescape() - Google's variation of base64 decoder 14 // ptrdiff_t WebSafeBase64Escape() - Google's variation of base64 encoder 15 // 16 // Check out 17 // http://tools.ietf.org/html/rfc2045 for formal description, but what we 18 // care about is that... 19 // Take the encoded stuff in groups of 4 characters and turn each 20 // character into a code 0 to 63 thus: 21 // A-Z map to 0 to 25 22 // a-z map to 26 to 51 23 // 0-9 map to 52 to 61 24 // +(- for WebSafe) maps to 62 25 // /(_ for WebSafe) maps to 63 26 // There will be four numbers, all less than 64 which can be represented 27 // by a 6 digit binary number (aaaaaa, bbbbbb, cccccc, dddddd respectively). 28 // Arrange the 6 digit binary numbers into three bytes as such: 29 // aaaaaabb bbbbcccc ccdddddd 30 // Equals signs (one or two) are used at the end of the encoded block to 31 // indicate that the text was not an integer multiple of three bytes long. 32 // ---------------------------------------------------------------------- 33 34 bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest, 35 size_t szdest, const signed char* unbase64, 36 size_t* len) { 37 static const char kPad64Equals = '='; 38 static const char kPad64Dot = '.'; 39 40 size_t destidx = 0; 41 int decode = 0; 42 int state = 0; 43 unsigned int ch = 0; 44 unsigned int temp = 0; 45 46 // If "char" is signed by default, using *src as an array index results in 47 // accessing negative array elements. Treat the input as a pointer to 48 // unsigned char to avoid this. 49 const unsigned char* src = reinterpret_cast<const unsigned char*>(src_param); 50 51 // The GET_INPUT macro gets the next input character, skipping 52 // over any whitespace, and stopping when we reach the end of the 53 // string or when we read any non-data character. The arguments are 54 // an arbitrary identifier (used as a label for goto) and the number 55 // of data bytes that must remain in the input to avoid aborting the 56 // loop. 57 #define GET_INPUT(label, remain) \ 58 label: \ 59 --szsrc; \ 60 ch = *src++; \ 61 decode = unbase64[ch]; \ 62 if (decode < 0) { \ 63 if (ascii_isspace(ch) && szsrc >= remain) goto label; \ 64 state = 4 - remain; \ 65 break; \ 66 } 67 68 // if dest is null, we're just checking to see if it's legal input 69 // rather than producing output. (I suspect this could just be done 70 // with a regexp...). We duplicate the loop so this test can be 71 // outside it instead of in every iteration. 72 73 if (dest) { 74 // This loop consumes 4 input bytes and produces 3 output bytes 75 // per iteration. We can't know at the start that there is enough 76 // data left in the string for a full iteration, so the loop may 77 // break out in the middle; if so 'state' will be set to the 78 // number of input bytes read. 79 80 while (szsrc >= 4) { 81 // We'll start by optimistically assuming that the next four 82 // bytes of the string (src[0..3]) are four good data bytes 83 // (that is, no nulls, whitespace, padding chars, or illegal 84 // chars). We need to test src[0..2] for nulls individually 85 // before constructing temp to preserve the property that we 86 // never read past a null in the string (no matter how long 87 // szsrc claims the string is). 88 89 if (!src[0] || !src[1] || !src[2] || 90 ((temp = ((unsigned(unbase64[src[0]]) << 18) | 91 (unsigned(unbase64[src[1]]) << 12) | 92 (unsigned(unbase64[src[2]]) << 6) | 93 (unsigned(unbase64[src[3]])))) & 94 0x80000000)) { 95 // Iff any of those four characters was bad (null, illegal, 96 // whitespace, padding), then temp's high bit will be set 97 // (because unbase64[] is -1 for all bad characters). 98 // 99 // We'll back up and resort to the slower decoder, which knows 100 // how to handle those cases. 101 102 GET_INPUT(first, 4); 103 temp = decode; 104 GET_INPUT(second, 3); 105 temp = (temp << 6) | decode; 106 GET_INPUT(third, 2); 107 temp = (temp << 6) | decode; 108 GET_INPUT(fourth, 1); 109 temp = (temp << 6) | decode; 110 } else { 111 // We really did have four good data bytes, so advance four 112 // characters in the string. 113 114 szsrc -= 4; 115 src += 4; 116 decode = -1; 117 ch = '\0'; 118 } 119 120 // temp has 24 bits of input, so write that out as three bytes. 121 122 if (destidx + 3 > szdest) return false; 123 dest[destidx + 2] = temp; 124 temp >>= 8; 125 dest[destidx + 1] = temp; 126 temp >>= 8; 127 dest[destidx] = temp; 128 destidx += 3; 129 } 130 } else { 131 while (szsrc >= 4) { 132 if (!src[0] || !src[1] || !src[2] || 133 ((temp = ((unsigned(unbase64[src[0]]) << 18) | 134 (unsigned(unbase64[src[1]]) << 12) | 135 (unsigned(unbase64[src[2]]) << 6) | 136 (unsigned(unbase64[src[3]])))) & 137 0x80000000)) { 138 GET_INPUT(first_no_dest, 4); 139 GET_INPUT(second_no_dest, 3); 140 GET_INPUT(third_no_dest, 2); 141 GET_INPUT(fourth_no_dest, 1); 142 } else { 143 szsrc -= 4; 144 src += 4; 145 decode = -1; 146 ch = '\0'; 147 } 148 destidx += 3; 149 } 150 } 151 152 #undef GET_INPUT 153 154 // if the loop terminated because we read a bad character, return 155 // now. 156 if (decode < 0 && ch != '\0' && ch != kPad64Equals && ch != kPad64Dot && 157 !ascii_isspace(ch)) 158 return false; 159 160 if (ch == kPad64Equals || ch == kPad64Dot) { 161 // if we stopped by hitting an '=' or '.', un-read that character -- we'll 162 // look at it again when we count to check for the proper number of 163 // equals signs at the end. 164 ++szsrc; 165 --src; 166 } else { 167 // This loop consumes 1 input byte per iteration. It's used to 168 // clean up the 0-3 input bytes remaining when the first, faster 169 // loop finishes. 'temp' contains the data from 'state' input 170 // characters read by the first loop. 171 while (szsrc > 0) { 172 --szsrc; 173 ch = *src++; 174 decode = unbase64[ch]; 175 if (decode < 0) { 176 if (ascii_isspace(ch)) { 177 continue; 178 } else if (ch == '\0') { 179 break; 180 } else if (ch == kPad64Equals || ch == kPad64Dot) { 181 // back up one character; we'll read it again when we check 182 // for the correct number of pad characters at the end. 183 ++szsrc; 184 --src; 185 break; 186 } else { 187 return false; 188 } 189 } 190 191 // Each input character gives us six bits of output. 192 temp = (temp << 6) | decode; 193 ++state; 194 if (state == 4) { 195 // If we've accumulated 24 bits of output, write that out as 196 // three bytes. 197 if (dest) { 198 if (destidx + 3 > szdest) return false; 199 dest[destidx + 2] = temp; 200 temp >>= 8; 201 dest[destidx + 1] = temp; 202 temp >>= 8; 203 dest[destidx] = temp; 204 } 205 destidx += 3; 206 state = 0; 207 temp = 0; 208 } 209 } 210 } 211 212 // Process the leftover data contained in 'temp' at the end of the input. 213 int expected_equals = 0; 214 switch (state) { 215 case 0: 216 // Nothing left over; output is a multiple of 3 bytes. 217 break; 218 219 case 1: 220 // Bad input; we have 6 bits left over. 221 return false; 222 223 case 2: 224 // Produce one more output byte from the 12 input bits we have left. 225 if (dest) { 226 if (destidx + 1 > szdest) return false; 227 temp >>= 4; 228 dest[destidx] = temp; 229 } 230 ++destidx; 231 expected_equals = 2; 232 break; 233 234 case 3: 235 // Produce two more output bytes from the 18 input bits we have left. 236 if (dest) { 237 if (destidx + 2 > szdest) return false; 238 temp >>= 2; 239 dest[destidx + 1] = temp; 240 temp >>= 8; 241 dest[destidx] = temp; 242 } 243 destidx += 2; 244 expected_equals = 1; 245 break; 246 247 default: 248 // state should have no other values at this point. 249 LOG(FATAL) << "This can't happen; base64 decoder state = " << state; 250 } 251 252 // The remainder of the string should be all whitespace, mixed with 253 // exactly 0 equals signs, or exactly 'expected_equals' equals 254 // signs. (Always accepting 0 equals signs is a google extension 255 // not covered in the RFC, as is accepting dot as the pad character.) 256 257 int equals = 0; 258 while (szsrc > 0 && *src) { 259 if (*src == kPad64Equals || *src == kPad64Dot) 260 ++equals; 261 else if (!ascii_isspace(*src)) 262 return false; 263 --szsrc; 264 ++src; 265 } 266 267 const bool ok = (equals == 0 || equals == expected_equals); 268 if (ok) *len = destidx; 269 return ok; 270 } 271 272 // The arrays below were generated by the following code 273 // #include <sys/time.h> 274 // #include <stdlib.h> 275 // #include <string.h> 276 // main() 277 // { 278 // static const char Base64[] = 279 // "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 280 // char* pos; 281 // int idx, i, j; 282 // printf(" "); 283 // for (i = 0; i < 255; i += 8) { 284 // for (j = i; j < i + 8; j++) { 285 // pos = strchr(Base64, j); 286 // if ((pos == NULL) || (j == 0)) 287 // idx = -1; 288 // else 289 // idx = pos - Base64; 290 // if (idx == -1) 291 // printf(" %2d, ", idx); 292 // else 293 // printf(" %2d/*%c*/,", idx, j); 294 // } 295 // printf("\n "); 296 // } 297 // } 298 // 299 // where the value of "Base64[]" was replaced by one of the base-64 conversion 300 // tables from the functions below. 301 static const signed char kUnBase64[] = { 302 -1, -1, -1, -1, -1, -1, -1, 303 -1, -1, -1, -1, -1, -1, -1, 304 -1, -1, -1, -1, -1, -1, -1, 305 -1, -1, -1, -1, -1, -1, -1, 306 -1, -1, -1, -1, -1, -1, -1, 307 -1, -1, -1, -1, -1, -1, -1, 308 -1, 62 /*+*/, -1, -1, -1, 63 /*/ */, 52 /*0*/, 309 53 /*1*/, 54 /*2*/, 55 /*3*/, 56 /*4*/, 57 /*5*/, 58 /*6*/, 59 /*7*/, 310 60 /*8*/, 61 /*9*/, -1, -1, -1, -1, -1, 311 -1, -1, 0 /*A*/, 1 /*B*/, 2 /*C*/, 3 /*D*/, 4 /*E*/, 312 5 /*F*/, 6 /*G*/, 07 /*H*/, 8 /*I*/, 9 /*J*/, 10 /*K*/, 11 /*L*/, 313 12 /*M*/, 13 /*N*/, 14 /*O*/, 15 /*P*/, 16 /*Q*/, 17 /*R*/, 18 /*S*/, 314 19 /*T*/, 20 /*U*/, 21 /*V*/, 22 /*W*/, 23 /*X*/, 24 /*Y*/, 25 /*Z*/, 315 -1, -1, -1, -1, -1, -1, 26 /*a*/, 316 27 /*b*/, 28 /*c*/, 29 /*d*/, 30 /*e*/, 31 /*f*/, 32 /*g*/, 33 /*h*/, 317 34 /*i*/, 35 /*j*/, 36 /*k*/, 37 /*l*/, 38 /*m*/, 39 /*n*/, 40 /*o*/, 318 41 /*p*/, 42 /*q*/, 43 /*r*/, 44 /*s*/, 45 /*t*/, 46 /*u*/, 47 /*v*/, 319 48 /*w*/, 49 /*x*/, 50 /*y*/, 51 /*z*/, -1, -1, -1, 320 -1, -1, -1, -1, -1, -1, -1, 321 -1, -1, -1, -1, -1, -1, -1, 322 -1, -1, -1, -1, -1, -1, -1, 323 -1, -1, -1, -1, -1, -1, -1, 324 -1, -1, -1, -1, -1, -1, -1, 325 -1, -1, -1, -1, -1, -1, -1, 326 -1, -1, -1, -1, -1, -1, -1, 327 -1, -1, -1, -1, -1, -1, -1, 328 -1, -1, -1, -1, -1, -1, -1, 329 -1, -1, -1, -1, -1, -1, -1, 330 -1, -1, -1, -1, -1, -1, -1, 331 -1, -1, -1, -1, -1, -1, -1, 332 -1, -1, -1, -1, -1, -1, -1, 333 -1, -1, -1, -1, -1, -1, -1, 334 -1, -1, -1, -1, -1, -1, -1, 335 -1, -1, -1, -1, -1, -1, -1, 336 -1, -1, -1, -1, -1, -1, -1, 337 -1, -1, -1, -1, -1, -1, -1, 338 -1, -1, -1, -1}; 339 static const signed char kUnWebSafeBase64[] = { 340 -1, -1, -1, -1, -1, -1, -1, 341 -1, -1, -1, -1, -1, -1, -1, 342 -1, -1, -1, -1, -1, -1, -1, 343 -1, -1, -1, -1, -1, -1, -1, 344 -1, -1, -1, -1, -1, -1, -1, 345 -1, -1, -1, -1, -1, -1, -1, 346 -1, -1, -1, 62 /*-*/, -1, -1, 52 /*0*/, 347 53 /*1*/, 54 /*2*/, 55 /*3*/, 56 /*4*/, 57 /*5*/, 58 /*6*/, 59 /*7*/, 348 60 /*8*/, 61 /*9*/, -1, -1, -1, -1, -1, 349 -1, -1, 0 /*A*/, 1 /*B*/, 2 /*C*/, 3 /*D*/, 4 /*E*/, 350 5 /*F*/, 6 /*G*/, 07 /*H*/, 8 /*I*/, 9 /*J*/, 10 /*K*/, 11 /*L*/, 351 12 /*M*/, 13 /*N*/, 14 /*O*/, 15 /*P*/, 16 /*Q*/, 17 /*R*/, 18 /*S*/, 352 19 /*T*/, 20 /*U*/, 21 /*V*/, 22 /*W*/, 23 /*X*/, 24 /*Y*/, 25 /*Z*/, 353 -1, -1, -1, -1, 63 /*_*/, -1, 26 /*a*/, 354 27 /*b*/, 28 /*c*/, 29 /*d*/, 30 /*e*/, 31 /*f*/, 32 /*g*/, 33 /*h*/, 355 34 /*i*/, 35 /*j*/, 36 /*k*/, 37 /*l*/, 38 /*m*/, 39 /*n*/, 40 /*o*/, 356 41 /*p*/, 42 /*q*/, 43 /*r*/, 44 /*s*/, 45 /*t*/, 46 /*u*/, 47 /*v*/, 357 48 /*w*/, 49 /*x*/, 50 /*y*/, 51 /*z*/, -1, -1, -1, 358 -1, -1, -1, -1, -1, -1, -1, 359 -1, -1, -1, -1, -1, -1, -1, 360 -1, -1, -1, -1, -1, -1, -1, 361 -1, -1, -1, -1, -1, -1, -1, 362 -1, -1, -1, -1, -1, -1, -1, 363 -1, -1, -1, -1, -1, -1, -1, 364 -1, -1, -1, -1, -1, -1, -1, 365 -1, -1, -1, -1, -1, -1, -1, 366 -1, -1, -1, -1, -1, -1, -1, 367 -1, -1, -1, -1, -1, -1, -1, 368 -1, -1, -1, -1, -1, -1, -1, 369 -1, -1, -1, -1, -1, -1, -1, 370 -1, -1, -1, -1, -1, -1, -1, 371 -1, -1, -1, -1, -1, -1, -1, 372 -1, -1, -1, -1, -1, -1, -1, 373 -1, -1, -1, -1, -1, -1, -1, 374 -1, -1, -1, -1, -1, -1, -1, 375 -1, -1, -1, -1, -1, -1, -1, 376 -1, -1, -1, -1}; 377 378 static bool Base64UnescapeInternal(const char* src, size_t slen, string* dest, 379 const signed char* unbase64) { 380 // Determine the size of the output string. Base64 encodes every 3 bytes into 381 // 4 characters. any leftover chars are added directly for good measure. 382 // This is documented in the base64 RFC: http://tools.ietf.org/html/rfc3548 383 const size_t dest_len = 3 * (slen / 4) + (slen % 4); 384 385 dest->resize(dest_len); 386 387 // We are getting the destination buffer by getting the beginning of the 388 // string and converting it into a char *. 389 size_t len; 390 const bool ok = 391 Base64UnescapeInternal(src, slen, dest->empty() ? NULL : &*dest->begin(), 392 dest_len, unbase64, &len); 393 if (!ok) { 394 dest->clear(); 395 return false; 396 } 397 398 // could be shorter if there was padding 399 DCHECK_LE(len, dest_len); 400 dest->erase(len); 401 402 return true; 403 } 404 405 bool Base64Unescape(const string& src, string* dest) { 406 return Base64UnescapeInternal(src.data(), src.size(), dest, kUnBase64); 407 } 408 409 bool WebSafeBase64Unescape(const string& src, string* dest) { 410 return Base64UnescapeInternal(src.data(), src.size(), dest, kUnWebSafeBase64); 411 } 412 413 // Base64Escape 414 // 415 // NOTE: We have to use an unsigned type for src because code built 416 // in the the /google tree treats characters as signed unless 417 // otherwised specified. 418 int Base64EscapeInternal(const unsigned char* src, int szsrc, char* dest, 419 int szdest, const char* base64, bool do_padding) { 420 static const char kPad64 = '='; 421 422 if (szsrc <= 0) return 0; 423 424 char* cur_dest = dest; 425 const unsigned char* cur_src = src; 426 427 // Three bytes of data encodes to four characters of cyphertext. 428 // So we can pump through three-byte chunks atomically. 429 while (szsrc > 2) { /* keep going until we have less than 24 bits */ 430 if ((szdest -= 4) < 0) return 0; 431 cur_dest[0] = base64[cur_src[0] >> 2]; 432 cur_dest[1] = base64[((cur_src[0] & 0x03) << 4) + (cur_src[1] >> 4)]; 433 cur_dest[2] = base64[((cur_src[1] & 0x0f) << 2) + (cur_src[2] >> 6)]; 434 cur_dest[3] = base64[cur_src[2] & 0x3f]; 435 436 cur_dest += 4; 437 cur_src += 3; 438 szsrc -= 3; 439 } 440 441 /* now deal with the tail (<=2 bytes) */ 442 switch (szsrc) { 443 case 0: 444 // Nothing left; nothing more to do. 445 break; 446 case 1: 447 // One byte left: this encodes to two characters, and (optionally) 448 // two pad characters to round out the four-character cypherblock. 449 if ((szdest -= 2) < 0) return 0; 450 cur_dest[0] = base64[cur_src[0] >> 2]; 451 cur_dest[1] = base64[(cur_src[0] & 0x03) << 4]; 452 cur_dest += 2; 453 if (do_padding) { 454 if ((szdest -= 2) < 0) return 0; 455 cur_dest[0] = kPad64; 456 cur_dest[1] = kPad64; 457 cur_dest += 2; 458 } 459 break; 460 case 2: 461 // Two bytes left: this encodes to three characters, and (optionally) 462 // one pad character to round out the four-character cypherblock. 463 if ((szdest -= 3) < 0) return 0; 464 cur_dest[0] = base64[cur_src[0] >> 2]; 465 cur_dest[1] = base64[((cur_src[0] & 0x03) << 4) + (cur_src[1] >> 4)]; 466 cur_dest[2] = base64[(cur_src[1] & 0x0f) << 2]; 467 cur_dest += 3; 468 if (do_padding) { 469 if ((szdest -= 1) < 0) return 0; 470 cur_dest[0] = kPad64; 471 cur_dest += 1; 472 } 473 break; 474 default: 475 // Should not be reached: blocks of 3 bytes are handled 476 // in the while loop before this switch statement. 477 CHECK(false) << "Logic problem? szsrc = " << szsrc; 478 break; 479 } 480 return (cur_dest - dest); 481 } 482 483 static const char kBase64Chars[] = 484 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 485 486 // Digit conversion. 487 static const char kHexTable[513] = 488 "000102030405060708090a0b0c0d0e0f" 489 "101112131415161718191a1b1c1d1e1f" 490 "202122232425262728292a2b2c2d2e2f" 491 "303132333435363738393a3b3c3d3e3f" 492 "404142434445464748494a4b4c4d4e4f" 493 "505152535455565758595a5b5c5d5e5f" 494 "606162636465666768696a6b6c6d6e6f" 495 "707172737475767778797a7b7c7d7e7f" 496 "808182838485868788898a8b8c8d8e8f" 497 "909192939495969798999a9b9c9d9e9f" 498 "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" 499 "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" 500 "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" 501 "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" 502 "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" 503 "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; 504 505 size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding) { 506 // Base64 encodes three bytes of input at a time. If the input is not 507 // divisible by three, we pad as appropriate. 508 // 509 // (from http://tools.ietf.org/html/rfc3548) 510 // Special processing is performed if fewer than 24 bits are available 511 // at the end of the data being encoded. A full encoding quantum is 512 // always completed at the end of a quantity. When fewer than 24 input 513 // bits are available in an input group, zero bits are added (on the 514 // right) to form an integral number of 6-bit groups. Padding at the 515 // end of the data is performed using the '=' character. Since all base 516 // 64 input is an integral number of octets, only the following cases 517 // can arise: 518 519 // Base64 encodes each three bytes of input into four bytes of output. 520 size_t len = (input_len / 3) * 4; 521 522 if (input_len % 3 == 0) { 523 // (from http://tools.ietf.org/html/rfc3548) 524 // (1) the final quantum of encoding input is an integral multiple of 24 525 // bits; here, the final unit of encoded output will be an integral 526 // multiple of 4 characters with no "=" padding, 527 } else if (input_len % 3 == 1) { 528 // (from http://tools.ietf.org/html/rfc3548) 529 // (2) the final quantum of encoding input is exactly 8 bits; here, the 530 // final unit of encoded output will be two characters followed by two 531 // "=" padding characters, or 532 len += 2; 533 if (do_padding) { 534 len += 2; 535 } 536 } else { // (input_len % 3 == 2) 537 // (from http://tools.ietf.org/html/rfc3548) 538 // (3) the final quantum of encoding input is exactly 16 bits; here, the 539 // final unit of encoded output will be three characters followed by one 540 // "=" padding character. 541 len += 3; 542 if (do_padding) { 543 len += 1; 544 } 545 } 546 547 assert(len >= input_len); // make sure we didn't overflow 548 return len; 549 } 550 551 void Base64EscapeInternal(const unsigned char* src, size_t szsrc, string* dest, 552 bool do_padding, const char* base64_chars) { 553 const size_t calc_escaped_size = 554 CalculateBase64EscapedLenInternal(szsrc, do_padding); 555 dest->resize(calc_escaped_size); 556 const int escaped_len = Base64EscapeInternal( 557 src, static_cast<int>(szsrc), dest->empty() ? NULL : &*dest->begin(), 558 static_cast<int>(dest->size()), base64_chars, do_padding); 559 DCHECK_EQ(calc_escaped_size, escaped_len); 560 dest->erase(escaped_len); 561 } 562 563 void Base64Escape(const unsigned char* src, ptrdiff_t szsrc, string* dest, 564 bool do_padding) { 565 if (szsrc < 0) return; 566 Base64EscapeInternal(src, szsrc, dest, do_padding, kBase64Chars); 567 } 568 569 // This is a templated function so that T can be either a char* or a string. 570 template <typename T> 571 static void b2a_hex_t(const unsigned char* src, T dest, ptrdiff_t num) { 572 auto dest_ptr = &dest[0]; 573 for (auto src_ptr = src; src_ptr != (src + num); ++src_ptr, dest_ptr += 2) { 574 const char* hex_p = &kHexTable[*src_ptr * 2]; 575 std::copy(hex_p, hex_p + 2, dest_ptr); 576 } 577 } 578 579 string b2a_hex(const char* b, ptrdiff_t len) { 580 string result; 581 result.resize(len << 1); 582 b2a_hex_t<string&>(reinterpret_cast<const unsigned char*>(b), result, len); 583 return result; 584 } 585 586 } // namespace dynamic_depth 587