1 2(********************************************************************) 3(* *) 4(* encoding.s7i Encoding and decoding functions *) 5(* Copyright (C) 2007, 2008, 2011, 2013, 2015, 2016 Thomas Mertes *) 6(* Copyright (C) 2019 - 2021 Thomas Mertes *) 7(* *) 8(* This file is part of the Seed7 Runtime Library. *) 9(* *) 10(* The Seed7 Runtime Library is free software; you can *) 11(* redistribute it and/or modify it under the terms of the GNU *) 12(* Lesser General Public License as published by the Free Software *) 13(* Foundation; either version 2.1 of the License, or (at your *) 14(* option) any later version. *) 15(* *) 16(* The Seed7 Runtime Library is distributed in the hope that it *) 17(* will be useful, but WITHOUT ANY WARRANTY; without even the *) 18(* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR *) 19(* PURPOSE. See the GNU Lesser General Public License for more *) 20(* details. *) 21(* *) 22(* You should have received a copy of the GNU Lesser General *) 23(* Public License along with this program; if not, write to the *) 24(* Free Software Foundation, Inc., 51 Franklin Street, *) 25(* Fifth Floor, Boston, MA 02110-1301, USA. *) 26(* *) 27(********************************************************************) 28 29 30include "chartype.s7i"; 31include "bytedata.s7i"; 32 33 34(** 35 * Encode a string with the Base64 encoding. 36 * Base64 encodes a byte string as ASCII string. This is done by 37 * taking packs of 6-bits and translating them into a radix-64 38 * representation. The radix-64 digits are encoded with letters 39 * (upper case followed by lower case), digits and the characters 40 * '+' and '/'. 41 * @return the Base64 encoded string. 42 * @exception RANGE_ERROR If characters beyond '\255;' are present. 43 *) 44const func string: toBase64 (in string: byteStri) is func 45 result 46 var string: base64 is ""; 47 local 48 const string: coding is "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 49 var integer: index is 1; 50 var integer: subIndex is 1; 51 var char: ch is ' '; 52 var integer: threeBytes is 0; 53 var string: fourBytes is " "; 54 var integer: posToAddNewline is 58; 55 begin 56 for index range 1 to length(byteStri) step 3 do 57 threeBytes := 0; 58 for subIndex range index to index + 2 do 59 threeBytes <<:= 8; 60 if subIndex <= length(byteStri) then 61 ch := byteStri[subIndex]; 62 if ch >= '\256;' then 63 raise RANGE_ERROR; 64 end if; 65 threeBytes +:= ord(ch); 66 end if; 67 end for; 68 fourBytes @:= [1] coding[succ( threeBytes >> 18)]; 69 fourBytes @:= [2] coding[succ((threeBytes >> 12) mod 64)]; 70 fourBytes @:= [3] coding[succ((threeBytes >> 6) mod 64)]; 71 fourBytes @:= [4] coding[succ( threeBytes mod 64)]; 72 if index = posToAddNewline then 73 base64 &:= "\n"; 74 posToAddNewline +:= 57; 75 end if; 76 base64 &:= fourBytes; 77 end for; 78 index := length(base64); 79 if length(byteStri) rem 3 = 2 then 80 base64 @:= [index] '='; 81 elsif length(byteStri) rem 3 = 1 then 82 base64 @:= [pred(index)] "=="; 83 end if; 84 end func; 85 86 87(** 88 * Decode a Base64 encoded string. 89 * @param base64 Base64 encoded string without leading or trailing 90 * whitespace characters. 91 * @return the decoded string. 92 * @exception RANGE_ERROR If ''base64'' is not in Base64 format. 93 *) 94const func string: fromBase64 (in string: base64) is func 95 result 96 var string: decoded is ""; 97 local 98 const array integer: decode is [] ( # -1 is illegal 99 62, -1, -1, -1, 63, # + / 100 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, # 0 - 9 101 -1, -1, -1, 0, -1, -1, -1, # = 102 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, # A - M 103 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, # N - Z 104 -1, -1, -1, -1, -1, -1, 105 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, # a - m 106 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51); # n - z 107 var integer: index is 1; 108 var integer: subIndex is 1; 109 var integer: number is 0; 110 var integer: fourBytes is 0; 111 var string: threeBytes is " "; 112 begin 113 while index <= length(base64) - 3 do 114 if base64[index] >= '+' then 115 fourBytes := 0; 116 for subIndex range index to index + 3 do 117 number := decode[ord(base64[subIndex]) - ord(pred('+'))]; 118 if number = -1 then 119 raise RANGE_ERROR; 120 end if; 121 fourBytes := (fourBytes << 6) + number; 122 end for; 123 threeBytes @:= [1] chr( fourBytes >> 16); 124 threeBytes @:= [2] chr((fourBytes >> 8) mod 256); 125 threeBytes @:= [3] chr( fourBytes mod 256); 126 decoded &:= threeBytes; 127 index +:= 4; 128 elsif base64[index] = '\n' then 129 incr(index); 130 elsif base64[index] = '\r' and base64[succ(index)] = '\n' then 131 index +:= 2; 132 else 133 raise RANGE_ERROR; 134 end if; 135 end while; 136 if index <> succ(length(base64)) then 137 raise RANGE_ERROR; 138 end if; 139 if base64[length(base64) - 1 len 2] = "==" then 140 decoded := decoded[.. length(decoded) - 2]; 141 elsif length(base64) >= 1 and base64[length(base64)] = '=' then 142 decoded := decoded[.. pred(length(decoded))]; 143 end if; 144 end func; 145 146 147(** 148 * Encode a string with the Quoted-printable encoding. 149 * Quoted-printable encodes a byte string as ASCII string. This 150 * is done by encoding printable ASCII characters except '=' as 151 * themself. Other byte values are encoded with '=' followed by two 152 * hexadecimal digits representing the byte's numeric value. 153 * @return the encoded string. 154 * @exception RANGE_ERROR If characters beyond '\255;' are present. 155 *) 156const func string: toQuotedPrintable (in string: byteStri) is func 157 result 158 var string: quoted is ""; 159 local 160 var integer: index is 0; 161 var integer: startPos is 1; 162 var integer: counter is 1; 163 var char: ch is ' '; 164 begin 165 for index range 1 to length(byteStri) do 166 ch := byteStri[index]; 167 if ch >= '\256;' then 168 raise RANGE_ERROR; 169 elsif ch = '\n' or (ch = '\r' and 170 index < length(byteStri) and byteStri[succ(index)] = '\n') then 171 if index > 1 then 172 ch := byteStri[pred(index)]; 173 if ch = ' ' or ch = '\t' then 174 quoted &:= byteStri[startPos .. index - 2]; 175 if counter >= 76 then 176 quoted &:= "=\n"; 177 counter := 1; 178 end if; 179 quoted &:= "=" <& ord(byteStri[pred(index)]) RADIX 16 lpad0 2; 180 counter +:= 3; 181 startPos := index; 182 end if; 183 end if; 184 counter := 1; 185 elsif ch >= '\127;' or ch = '=' or (ch < ' ' and ch <> '\9;') then 186 quoted &:= byteStri[startPos .. pred(index)]; 187 if counter >= 74 then 188 quoted &:= "=\n"; 189 counter := 1; 190 end if; 191 quoted &:= "=" <& ord(ch) RADIX 16 lpad0 2; 192 startPos := succ(index); 193 counter +:= 3; 194 elsif counter >= 76 then 195 quoted &:= byteStri[startPos .. pred(index)] & "=\n"; 196 startPos := index; 197 counter := 2; 198 else 199 incr(counter); 200 end if; 201 end for; 202 quoted &:= byteStri[startPos ..]; 203 end func; 204 205 206(** 207 * Decode a quoted-printable encoded string. 208 * @return the decoded string. 209 * @exception RANGE_ERROR If ''quoted'' is not in quoted-printable format. 210 *) 211const func string: fromQuotedPrintable (in string: quoted) is func 212 result 213 var string: decoded is ""; 214 local 215 var integer: startPos is 1; 216 var integer: equalSignPos is 0; 217 var string: twoChars is ""; 218 begin 219 equalSignPos := pos(quoted, "="); 220 while equalSignPos <> 0 do 221 decoded &:= quoted[startPos .. pred(equalSignPos)]; 222 if equalSignPos < length(quoted) and 223 quoted[succ(equalSignPos)] = '\n' then 224 startPos := equalSignPos + 2; 225 elsif equalSignPos <= length(quoted) - 2 then 226 twoChars := quoted[succ(equalSignPos) len 2]; 227 if twoChars[1] in hexdigit_char then 228 decoded &:= chr(integer(twoChars, 16)); 229 elsif twoChars <> "\r\n" then 230 raise RANGE_ERROR; 231 end if; 232 startPos := equalSignPos + 3; 233 else 234 raise RANGE_ERROR; 235 end if; 236 equalSignPos := pos(quoted, "=", startPos); 237 end while; 238 decoded &:= quoted[startPos ..]; 239 end func; 240 241 242(** 243 * Encode a string with uuencoding. 244 * Uuencode encodes a byte string as ASCII string. This is done 245 * by taking packs of 6-bits and translating them into a radix-64 246 * representation. The radix-64 digits are encoded with consecutive 247 * ASCII characters starting from ' ' (which represents 0). Every 248 * line starts with a radix-64 digit character indicating the number 249 * of data bytes encoded on that line. Some newer uuencode tools use 250 * grave accent ('`') instead of space (' ') to encode 0. This can 251 * be emulated by using: replace(toUuencoded(source), " ", "`"). 252 * @return the encoded string. 253 * @exception RANGE_ERROR If characters beyond '\255;' are present. 254 *) 255const func string: toUuencoded (in string: byteStri) is func 256 result 257 var string: uuencoded is ""; 258 local 259 var integer: index is 1; 260 var integer: subIndex is 1; 261 var char: ch is ' '; 262 var integer: threeBytes is 0; 263 var string: fourBytes is " "; 264 var integer: posToAddNewline is 43; 265 begin 266 if length(byteStri) <> 0 then 267 if length(byteStri) < 45 then 268 uuencoded &:= chr(32 + length(byteStri)); 269 else 270 uuencoded &:= "M"; 271 end if; 272 for index range 1 to length(byteStri) step 3 do 273 threeBytes := 0; 274 for subIndex range index to index + 2 do 275 threeBytes <<:= 8; 276 if subIndex <= length(byteStri) then 277 ch := byteStri[subIndex]; 278 if ch >= '\256;' then 279 raise RANGE_ERROR; 280 end if; 281 threeBytes +:= ord(ch); 282 end if; 283 end for; 284 fourBytes @:= [1] chr(32 + (threeBytes >> 18)); 285 fourBytes @:= [2] chr(32 + (threeBytes >> 12) mod 64); 286 fourBytes @:= [3] chr(32 + (threeBytes >> 6) mod 64); 287 fourBytes @:= [4] chr(32 + threeBytes mod 64); 288 uuencoded &:= fourBytes; 289 if index = posToAddNewline and length(byteStri) > index + 2 then 290 if length(byteStri) - index - 2 < 45 then 291 uuencoded &:= "\n" <& chr(32 + length(byteStri) - index - 2); 292 else 293 uuencoded &:= "\nM"; 294 end if; 295 posToAddNewline +:= 45; 296 end if; 297 end for; 298 uuencoded &:= "\n"; 299 end if; 300 uuencoded &:= "`\n"; 301 end func; 302 303 304(** 305 * Decode an uuencoded string. 306 * Space (' ') and grave accent ('`') both are used to encode 0. 307 * @return the decoded string. 308 * @exception RANGE_ERROR If ''uuencoded'' is not in uuencoded format. 309 *) 310const func string: fromUuencoded (in string: uuencoded) is func 311 result 312 var string: decoded is ""; 313 local 314 var integer: lineLength is 1; 315 var integer: index is 1; 316 var integer: subIndex is 1; 317 var integer: number is 0; 318 var integer: fourBytes is 0; 319 var string: threeBytes is " "; 320 begin 321 lineLength := ord(uuencoded[1]) - 32; 322 while lineLength <> 0 and lineLength <> 64 do 323 incr(index); 324 while lineLength >= 1 do 325 fourBytes := 0; 326 for subIndex range index to index + 3 do 327 number := ord(uuencoded[subIndex]) - 32; 328 if number = 64 then 329 number := 0; 330 elsif number < 0 or number > 64 then 331 raise RANGE_ERROR; 332 end if; 333 fourBytes := (fourBytes << 6) + number; 334 end for; 335 threeBytes @:= [1] chr( fourBytes >> 16); 336 threeBytes @:= [2] chr((fourBytes >> 8) mod 256); 337 threeBytes @:= [3] chr( fourBytes mod 256); 338 decoded &:= threeBytes[ .. lineLength]; 339 lineLength -:= 3; 340 index +:= 4; 341 end while; 342 while index <= length(uuencoded) and uuencoded[index] <> '\n' do 343 incr(index); 344 end while; 345 if index < length(uuencoded) then 346 incr(index); 347 lineLength := ord(uuencoded[index]) - 32; 348 else 349 lineLength := 0; 350 end if; 351 end while; 352 end func; 353 354 355(** 356 * Encode a string with percent-encoding. 357 * Percent-encoding encodes a byte string as ASCII string. This is done 358 * by encoding all characters, which are not in the set of unreserved 359 * characters (A-Z, a-z, 0-9 - _ . ~). The encoding uses a percent sign 360 * ('%') followed by two hexadecimal digits, which represent the ordinal 361 * value of the encoded character. 362 * @return the encoded string. 363 * @exception RANGE_ERROR If characters beyond '\255;' are present. 364 *) 365const func string: toPercentEncoded (in string: byteStri) is func 366 result 367 var string: percentEncoded is ""; 368 local 369 const set of char: unreservedChars is alphanum_char | {'-', '_', '.', '~'}; 370 var integer: pos is 0; 371 var integer: start is 1; 372 var char: ch is ' '; 373 begin 374 for ch key pos range byteStri do 375 if ch > '\255;' then 376 raise RANGE_ERROR; 377 elsif ch not in unreservedChars then 378 percentEncoded &:= byteStri[start .. pred(pos)]; 379 percentEncoded &:= "%" <& ord(ch) RADIX 16 lpad0 2; 380 start := succ(pos); 381 end if; 382 end for; 383 percentEncoded &:= byteStri[start ..]; 384 end func; 385 386 387(** 388 * Decode a percent-encoded string. 389 * Percent-encoding encodes a byte string as ASCII string. It uses 390 * the percent sign ('%') followed by two hexadecimal digits to 391 * encode characters that otherwise would not be allowed in an 392 * URL. Allowed URL characters are encoded as themself. 393 * @return the decoded string. 394 *) 395const func string: fromPercentEncoded (in string: percentEncoded) is func 396 result 397 var string: decoded is ""; 398 local 399 var integer: pos is 0; 400 begin 401 decoded := percentEncoded; 402 pos := pos(decoded, '%', succ(pos)); 403 while pos <> 0 do 404 if pos <= length(decoded) - 2 and 405 decoded[succ(pos)] in hexdigit_char and 406 decoded[pos + 2] in hexdigit_char then 407 decoded := decoded[.. pred(pos)] & 408 str(chr(integer(decoded[succ(pos) len 2], 16))) & 409 decoded[pos + 3 ..]; 410 end if; 411 pos := pos(decoded, '%', succ(pos)); 412 end while; 413 end func; 414 415 416(** 417 * Encode a string with URL encoding. 418 * URL encoding encodes a byte string as ASCII string. This is done 419 * by encoding all characters, which are not in the set of unreserved 420 * characters (A-Z, a-z, 0-9 - _ . ~). The encoding uses a percent sign 421 * ('%') followed by two hexadecimal digits, which represent the ordinal 422 * value of the encoded character. A plus sign ('+') is used to encode 423 * a space (' '). 424 * @return the encoded string. 425 * @exception RANGE_ERROR If characters beyond '\255;' are present. 426 *) 427const func string: toUrlEncoded (in string: byteStri) is func 428 result 429 var string: urlEncoded is ""; 430 local 431 const set of char: unreservedChars is alphanum_char | {'-', '_', '.', '~'}; 432 var integer: pos is 0; 433 var integer: start is 1; 434 var char: ch is ' '; 435 begin 436 for ch key pos range byteStri do 437 if ch > '\255;' then 438 raise RANGE_ERROR; 439 elsif ch = ' ' then 440 urlEncoded &:= byteStri[start .. pred(pos)]; 441 urlEncoded &:= '+'; 442 start := succ(pos); 443 elsif ch not in unreservedChars then 444 urlEncoded &:= byteStri[start .. pred(pos)]; 445 urlEncoded &:= "%" <& ord(ch) RADIX 16 lpad0 2; 446 start := succ(pos); 447 end if; 448 end for; 449 urlEncoded &:= byteStri[start ..]; 450 end func; 451 452 453(** 454 * Decode an URL encoded string. 455 * URL encoding encodes a byte string as ASCII string. It uses 456 * the percent sign ('%') followed by two hexadecimal digits to 457 * encode characters that otherwise would not be allowed in an 458 * URL. A plus sign ('+') is used to encode a space (' '). 459 * Allowed URL characters are encoded as themself. 460 * @return the decoded string. 461 *) 462const func string: fromUrlEncoded (in string: urlEncoded) is func 463 result 464 var string: decoded is ""; 465 local 466 var integer: pos is 0; 467 var integer: start is 1; 468 var char: ch is ' '; 469 begin 470 for ch key pos range urlEncoded do 471 if ch = '%' and pos <= length(urlEncoded) - 2 and 472 urlEncoded[succ(pos)] in hexdigit_char and 473 urlEncoded[pos + 2] in hexdigit_char then 474 decoded &:= urlEncoded[start .. pred(pos)]; 475 decoded &:= chr(integer(urlEncoded[succ(pos) len 2], 16)); 476 pos +:= 2; 477 start := succ(pos); 478 elsif ch = '+' then 479 decoded &:= urlEncoded[start .. pred(pos)]; 480 decoded &:= ' '; 481 start := succ(pos); 482 end if; 483 end for; 484 decoded &:= urlEncoded[start ..]; 485 end func; 486 487 488(** 489 * Encode a string with the Ascii85 encoding. 490 * Ascii85 encodes a byte string as ASCII string. This is done by 491 * encoded every four bytes with five printable ASCII characters. 492 * Five radix 85 digits provide enough possible values to encode 493 * the possible values of four bytes. The radix 85 digits are encoded 494 * with the characters '!' (encodes 0) through 'u' (encodes 84). 495 * If the last block of the byte string contains fewer than 4 bytes, 496 * the block is padded with up to three null bytes before encoding. 497 * After encoding, as many bytes as were added as padding are removed 498 * from the end of the output. In files the end of an Ascii85 encoding 499 * is marked with "~>" (this end marker is not added by toAscii85). 500 * @return the Ascii85 encoded string. 501 * @exception RANGE_ERROR If characters beyond '\255;' are present. 502 *) 503const func string: toAscii85 (in string: byteStri) is func 504 result 505 var string: ascii85 is ""; 506 local 507 var integer: index is 0; 508 var integer: subIndex is 0; 509 var integer: fourBytes is 0; 510 var string: fiveBytes is " "; 511 var char: ch is ' '; 512 begin 513 for index range 1 to length(byteStri) step 4 do 514 fourBytes := 0; 515 for subIndex range index to index + 3 do 516 fourBytes <<:= 8; 517 if subIndex <= length(byteStri) then 518 ch := byteStri[subIndex]; 519 if ch >= '\256;' then 520 raise RANGE_ERROR; 521 end if; 522 fourBytes +:= ord(ch); 523 end if; 524 end for; 525 if fourBytes = 0 then 526 ascii85 &:= 'z'; 527 else 528 for subIndex range 5 downto 1 do 529 fiveBytes @:= [subIndex] chr(ord('!') + fourBytes rem 85); 530 fourBytes := fourBytes div 85; 531 end for; 532 ascii85 &:= fiveBytes; 533 end if; 534 end for; 535 if length(byteStri) rem 4 <> 0 then 536 ascii85 := ascii85[.. length(ascii85) - 4 + length(byteStri) rem 4]; 537 end if; 538 end func; 539 540 541(** 542 * Decode a Ascii85 encoded string. 543 * Every block of five radix 85 characters is decoded to four bytes. 544 * Radix 85 characters are between '!' (encodes 0) and 'u' (encodes 84). 545 * The character 'z' is used to encode a block of four zero bytes. 546 * White space in the Ascii85 encoded string is ignored. 547 * The last block is padded to 5 bytes with the Ascii85 character "u", 548 * and as many bytes as were added as padding are omitted from the 549 * end of the output. 550 * @return the decoded string. 551 * @exception RANGE_ERROR If ''ascii85'' is not in Ascii85 format. 552 *) 553const func string: fromAscii85 (in string: ascii85) is func 554 result 555 var string: decoded is ""; 556 local 557 const set of char: whiteSpace is {'\0;', '\t', '\n', '\f', '\r', ' '}; 558 var char: ch is ' '; 559 var integer: digitIndex is 0; 560 var integer: base85Number is 0; 561 var integer: idx is 0; 562 begin 563 for ch range ascii85 until ch = '~' do 564 if ch >= '!' and ch <= 'u' then 565 incr(digitIndex); 566 base85Number := base85Number * 85 + (ord(ch) - ord('!')); 567 if digitIndex = 5 then 568 decoded &:= bytes(base85Number, UNSIGNED, BE, 4); 569 digitIndex := 0; 570 base85Number := 0; 571 end if; 572 elsif ch = 'z' and digitIndex = 0 then 573 decoded &:= "\0;\0;\0;\0;"; 574 elsif ch not in whiteSpace then 575 raise RANGE_ERROR; 576 end if; 577 end for; 578 if digitIndex <> 0 then 579 for idx range 1 to 5 - digitIndex do 580 base85Number := base85Number * 85 + 84; 581 end for; 582 decoded &:= bytes(base85Number, UNSIGNED, BE, 4)[.. pred(digitIndex)]; 583 end if; 584 end func; 585 586 587(** 588 * Encode a number with a positional numeric system. 589 * The encoded string starts with the most significant digit. 590 * @param number BigInteger number to be encoded. 591 * @param digits The digits used by the positional numeric system. 592 * @return the encoded string. 593 *) 594const func string: toBase (in bigInteger: number, in string: digits) is func 595 result 596 var string: encoded is ""; 597 local 598 var bigInteger: base is 0_; 599 var quotRem: quotientAndRemainder is quotRem.value; 600 begin 601 base := bigInteger(length(digits)); 602 quotientAndRemainder.quotient := number; 603 while quotientAndRemainder.quotient <> 0_ do 604 quotientAndRemainder := quotientAndRemainder.quotient divRem base; 605 encoded &:= digits[succ(ord(quotientAndRemainder.remainder))]; 606 end while; 607 encoded := reverse(encoded); 608 end func; 609 610 611(** 612 * Decode a string that has been encoded with a positional numeric system. 613 * The encoded string starts with the most significant digit. 614 * @param encoded String containing the encoded data. 615 * @param digits The digits used by the positional numeric system. 616 * @return the decoded bigInteger Number. 617 * @exception RANGE_ERROR If characters are present, that are not found 618 * in ''digits''. 619 *) 620const func bigInteger: fromBaseToBigInt (in string: encoded, in string: digits) is func 621 result 622 var bigInteger: number is 0_; 623 local 624 var bigInteger: base is 0_; 625 var bigInteger: factor is 1_; 626 var integer: index is 0; 627 var integer: digit is 0; 628 begin 629 base := bigInteger(length(digits)); 630 for index range length(encoded) downto 1 do 631 digit := pred(pos(digits, encoded[index])); 632 if digit = -1 then 633 raise RANGE_ERROR; 634 else 635 number +:= factor * bigInteger(digit); 636 factor *:= base; 637 end if; 638 end for; 639 end func; 640 641 642(** 643 * Encode a string of bytes with a positional numeric system. 644 * The encoded string starts with the most significant digit. 645 * Leading zero bytes in ''byteStri'' are converted into leading 646 * zero digits in the encoded string. 647 * @param byteStri String of bytes to be encoded. 648 * @param digits The digits used by the positional numeric system. 649 * @return the encoded string. 650 * @exception RANGE_ERROR If characters beyond '\255;' are present. 651 *) 652const func string: toBase (in string: byteStri, in string: digits) is func 653 result 654 var string: encoded is ""; 655 local 656 var integer: index is 1; 657 begin 658 encoded := toBase(bytes2BigInt(byteStri, UNSIGNED, BE), digits); 659 while index <= length(byteStri) and byteStri[index] = '\0;' do 660 incr(index); 661 end while; 662 if index >= 1 then 663 encoded := "1" mult pred(index) & encoded; 664 end if; 665 end func; 666 667 668(** 669 * Decode a string that has been encoded with a positional numeric system. 670 * The encoded string starts with the most significant digit. 671 * Leading zero digits in the encoded string are converted into leading 672 * zero bytes in the decoded string. 673 * @param encoded String containing the encoded data. 674 * @param digits The digits used by the positional numeric system. 675 * @return the decoded string. 676 * @exception RANGE_ERROR If characters are present, that are not found 677 * in ''digits''. 678 *) 679const func string: fromBase (in string: encoded, in string: digits) is func 680 result 681 var string: decoded is ""; 682 local 683 var bigInteger: number is 0_; 684 var integer: index is 1; 685 begin 686 number := fromBaseToBigInt(encoded, digits); 687 if number <> 0_ then 688 decoded := bytes(number, UNSIGNED, BE); 689 end if; 690 while index <= length(encoded) and encoded[index] = '1' do 691 incr(index); 692 end while; 693 if index >= 1 then 694 decoded := "\0;" mult pred(index) & decoded; 695 end if; 696 end func; 697 698 699const string: defaultBase58Digits is "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; 700 701 702(** 703 * Encode a string with the Base58 encoding used by Bitcoin. 704 * Leading zero bytes ('\0;') in ''byteStri'' are converted into 705 * leading zero digits ('1') in the encoded string. 706 * @param byteStri String of bytes to be encoded. 707 * @return the encoded string. 708 * @exception RANGE_ERROR If characters beyond '\255;' are present. 709 *) 710const func string: toBase58 (in string: byteStri) is 711 return toBase(byteStri, defaultBase58Digits); 712 713 714(** 715 * Decode a Base58 encoded string. 716 * Leading '1' characters in ''base58'' are converted into 717 * leading zero bytes ('\0;') in the decoded string. 718 * @param base58 String containing Base58 encoded data. 719 * @return the decoded string. 720 * @exception RANGE_ERROR If characters are present, that are not valid 721 * in the Base58 encoding used by Bitcoin. 722 *) 723const func string: fromBase58 (in string: base58) is 724 return fromBase(base58, defaultBase58Digits); 725