strencodings.cpp raw

   1  // Copyright (c) 2009-2010 Satoshi Nakamoto
   2  // Copyright (c) 2009-2022 The Limenka developers
   3  // Distributed under the MIT software license, see the accompanying
   4  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   5  
   6  #include <tinyformat.h>
   7  #include <util/strencodings.h>
   8  
   9  #include <crypto/hex_base.h>
  10  #include <span.h>
  11  
  12  #include <array>
  13  #include <cassert>
  14  #include <cstring>
  15  #include <limits>
  16  #include <optional>
  17  #include <ostream>
  18  #include <string>
  19  #include <vector>
  20  
  21  static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  22  
  23  static const std::string SAFE_CHARS[] =
  24  {
  25      CHARS_ALPHA_NUM + " .,;-_/:?@()", // SAFE_CHARS_DEFAULT
  26      CHARS_ALPHA_NUM + " .,;-_?@", // SAFE_CHARS_UA_COMMENT
  27      CHARS_ALPHA_NUM + ".-_", // SAFE_CHARS_FILENAME
  28      CHARS_ALPHA_NUM + "!*'();:@&=+$,/?#[]-_.~%", // SAFE_CHARS_URI
  29      CHARS_ALPHA_NUM + " .,;-_/:?@()!\"#$%&'*+<=>[\\]^`{|}~"  // SAFE_CHARS_PRINTABLE
  30  };
  31  
  32  std::string SanitizeString(std::string_view str, int rule, bool escape)
  33  {
  34      std::string result;
  35      for (char c : str) {
  36          if (SAFE_CHARS[rule].find(c) != std::string::npos || (c == '%' && escape)) {
  37              result.push_back(c);
  38          } else if (escape) {
  39              result += strprintf("%%%02X", c);
  40          }
  41      }
  42      return result;
  43  }
  44  
  45  bool IsHex(std::string_view str)
  46  {
  47      for (char c : str) {
  48          if (HexDigit(c) < 0) return false;
  49      }
  50      return (str.size() > 0) && (str.size()%2 == 0);
  51  }
  52  
  53  template <typename Byte>
  54  std::optional<std::vector<Byte>> TryParseHex(std::string_view str)
  55  {
  56      std::vector<Byte> vch;
  57      vch.reserve(str.size() / 2); // two hex characters form a single byte
  58  
  59      auto it = str.begin();
  60      while (it != str.end()) {
  61          if (IsSpace(*it)) {
  62              ++it;
  63              continue;
  64          }
  65          auto c1 = HexDigit(*(it++));
  66          if (it == str.end()) return std::nullopt;
  67          auto c2 = HexDigit(*(it++));
  68          if (c1 < 0 || c2 < 0) return std::nullopt;
  69          vch.push_back(Byte(c1 << 4) | Byte(c2));
  70      }
  71      return vch;
  72  }
  73  template std::optional<std::vector<std::byte>> TryParseHex(std::string_view);
  74  template std::optional<std::vector<uint8_t>> TryParseHex(std::string_view);
  75  
  76  bool SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut)
  77  {
  78      bool valid = false;
  79      size_t colon = in.find_last_of(':');
  80      // if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator
  81      bool fHaveColon = colon != in.npos;
  82      bool fBracketed = fHaveColon && (in[0] == '[' && in[colon - 1] == ']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe
  83      bool fMultiColon{fHaveColon && colon != 0 && (in.find_last_of(':', colon - 1) != in.npos)};
  84      if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) {
  85          uint16_t n;
  86          if (ParseUInt16(in.substr(colon + 1), &n)) {
  87              in = in.substr(0, colon);
  88              portOut = n;
  89              valid = (portOut != 0);
  90          }
  91      } else {
  92          valid = true;
  93      }
  94      if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') {
  95          hostOut = in.substr(1, in.size() - 2);
  96      } else {
  97          hostOut = in;
  98      }
  99  
 100      return valid;
 101  }
 102  
 103  std::string EncodeBase64(Span<const unsigned char> input)
 104  {
 105      static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 106  
 107      std::string str;
 108      str.reserve(((input.size() + 2) / 3) * 4);
 109      ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, input.begin(), input.end());
 110      while (str.size() % 4) str += '=';
 111      return str;
 112  }
 113  
 114  std::optional<std::vector<unsigned char>> DecodeBase64(std::string_view str)
 115  {
 116      static const int8_t decode64_table[256]{
 117          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 118          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 119          -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
 120          -1, -1, -1, -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
 121          15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
 122          29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
 123          49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 124          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 125          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 126          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 127          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 128          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 129          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
 130      };
 131  
 132      if (str.size() % 4 != 0) return {};
 133      /* One or two = characters at the end are permitted. */
 134      if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
 135      if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
 136  
 137      std::vector<unsigned char> ret;
 138      ret.reserve((str.size() * 3) / 4);
 139      bool valid = ConvertBits<6, 8, false>(
 140          [&](unsigned char c) { ret.push_back(c); },
 141          str.begin(), str.end(),
 142          [](char c) { return decode64_table[uint8_t(c)]; }
 143      );
 144      if (!valid) return {};
 145  
 146      return ret;
 147  }
 148  
 149  std::string EncodeBase32(Span<const unsigned char> input, bool pad)
 150  {
 151      static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
 152  
 153      std::string str;
 154      str.reserve(((input.size() + 4) / 5) * 8);
 155      ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(), input.end());
 156      if (pad) {
 157          while (str.size() % 8) {
 158              str += '=';
 159          }
 160      }
 161      return str;
 162  }
 163  
 164  std::string EncodeBase32(std::string_view str, bool pad)
 165  {
 166      return EncodeBase32(MakeUCharSpan(str), pad);
 167  }
 168  
 169  std::optional<std::vector<unsigned char>> DecodeBase32(std::string_view str)
 170  {
 171      static const int8_t decode32_table[256]{
 172          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 173          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 174          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1,
 175          -1, -1, -1, -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
 176          15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1,  0,  1,  2,
 177           3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
 178          23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 179          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 180          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 181          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 182          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 183          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 184          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
 185      };
 186  
 187      if (str.size() % 8 != 0) return {};
 188      /* 1, 3, 4, or 6 padding '=' suffix characters are permitted. */
 189      if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
 190      if (str.size() >= 2 && str.substr(str.size() - 2) == "==") str.remove_suffix(2);
 191      if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
 192      if (str.size() >= 2 && str.substr(str.size() - 2) == "==") str.remove_suffix(2);
 193  
 194      std::vector<unsigned char> ret;
 195      ret.reserve((str.size() * 5) / 8);
 196      bool valid = ConvertBits<5, 8, false>(
 197          [&](unsigned char c) { ret.push_back(c); },
 198          str.begin(), str.end(),
 199          [](char c) { return decode32_table[uint8_t(c)]; }
 200      );
 201  
 202      if (!valid) return {};
 203  
 204      return ret;
 205  }
 206  
 207  namespace {
 208  template <typename T>
 209  bool ParseIntegral(std::string_view str, T* out)
 210  {
 211      static_assert(std::is_integral<T>::value);
 212      // Replicate the exact behavior of strtol/strtoll/strtoul/strtoull when
 213      // handling leading +/- for backwards compatibility.
 214      if (str.length() >= 2 && str[0] == '+' && str[1] == '-') {
 215          return false;
 216      }
 217      const std::optional<T> opt_int = ToIntegral<T>((!str.empty() && str[0] == '+') ? str.substr(1) : str);
 218      if (!opt_int) {
 219          return false;
 220      }
 221      if (out != nullptr) {
 222          *out = *opt_int;
 223      }
 224      return true;
 225  }
 226  }; // namespace
 227  
 228  bool ParseInt32(std::string_view str, int32_t* out)
 229  {
 230      return ParseIntegral<int32_t>(str, out);
 231  }
 232  
 233  bool ParseInt64(std::string_view str, int64_t* out)
 234  {
 235      return ParseIntegral<int64_t>(str, out);
 236  }
 237  
 238  bool ParseUInt8(std::string_view str, uint8_t* out)
 239  {
 240      return ParseIntegral<uint8_t>(str, out);
 241  }
 242  
 243  bool ParseUInt16(std::string_view str, uint16_t* out)
 244  {
 245      return ParseIntegral<uint16_t>(str, out);
 246  }
 247  
 248  bool ParseUInt32(std::string_view str, uint32_t* out)
 249  {
 250      return ParseIntegral<uint32_t>(str, out);
 251  }
 252  
 253  bool ParseUInt64(std::string_view str, uint64_t* out)
 254  {
 255      return ParseIntegral<uint64_t>(str, out);
 256  }
 257  
 258  std::string FormatParagraph(std::string_view in, size_t width, size_t indent)
 259  {
 260      assert(width >= indent);
 261      std::stringstream out;
 262      size_t ptr = 0;
 263      size_t indented = 0;
 264      while (ptr < in.size())
 265      {
 266          size_t lineend = in.find_first_of('\n', ptr);
 267          if (lineend == std::string::npos) {
 268              lineend = in.size();
 269          }
 270          const size_t linelen = lineend - ptr;
 271          const size_t rem_width = width - indented;
 272          if (linelen <= rem_width) {
 273              out << in.substr(ptr, linelen + 1);
 274              ptr = lineend + 1;
 275              indented = 0;
 276          } else {
 277              size_t finalspace = in.find_last_of(" \n", ptr + rem_width);
 278              if (finalspace == std::string::npos || finalspace < ptr) {
 279                  // No place to break; just include the entire word and move on
 280                  finalspace = in.find_first_of("\n ", ptr);
 281                  if (finalspace == std::string::npos) {
 282                      // End of the string, just add it and break
 283                      out << in.substr(ptr);
 284                      break;
 285                  }
 286              }
 287              out << in.substr(ptr, finalspace - ptr) << "\n";
 288              if (in[finalspace] == '\n') {
 289                  indented = 0;
 290              } else if (indent) {
 291                  out << std::string(indent, ' ');
 292                  indented = indent;
 293              }
 294              ptr = finalspace + 1;
 295          }
 296      }
 297      return out.str();
 298  }
 299  
 300  /** Upper bound for mantissa.
 301   * 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit integer.
 302   * Larger integers cannot consist of arbitrary combinations of 0-9:
 303   *
 304   *   999999999999999999  1^18-1
 305   *  9223372036854775807  (1<<63)-1  (max int64_t)
 306   *  9999999999999999999  1^19-1     (would overflow)
 307   */
 308  static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL;
 309  
 310  /** Helper function for ParseFixedPoint */
 311  static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros)
 312  {
 313      if(ch == '0')
 314          ++mantissa_tzeros;
 315      else {
 316          for (int i=0; i<=mantissa_tzeros; ++i) {
 317              if (mantissa > (UPPER_BOUND / 10LL))
 318                  return false; /* overflow */
 319              mantissa *= 10;
 320          }
 321          mantissa += ch - '0';
 322          mantissa_tzeros = 0;
 323      }
 324      return true;
 325  }
 326  
 327  bool ParseFixedPoint(std::string_view val, int decimals, int64_t *amount_out)
 328  {
 329      int64_t mantissa = 0;
 330      int64_t exponent = 0;
 331      int mantissa_tzeros = 0;
 332      bool mantissa_sign = false;
 333      bool exponent_sign = false;
 334      int ptr = 0;
 335      int end = val.size();
 336      int point_ofs = 0;
 337  
 338      if (ptr < end && val[ptr] == '-') {
 339          mantissa_sign = true;
 340          ++ptr;
 341      }
 342      if (ptr < end)
 343      {
 344          if (val[ptr] == '0') {
 345              /* pass single 0 */
 346              ++ptr;
 347          } else if (val[ptr] >= '1' && val[ptr] <= '9') {
 348              while (ptr < end && IsDigit(val[ptr])) {
 349                  if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
 350                      return false; /* overflow */
 351                  ++ptr;
 352              }
 353          } else return false; /* missing expected digit */
 354      } else return false; /* empty string or loose '-' */
 355      if (ptr < end && val[ptr] == '.')
 356      {
 357          ++ptr;
 358          if (ptr < end && IsDigit(val[ptr]))
 359          {
 360              while (ptr < end && IsDigit(val[ptr])) {
 361                  if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
 362                      return false; /* overflow */
 363                  ++ptr;
 364                  ++point_ofs;
 365              }
 366          } else return false; /* missing expected digit */
 367      }
 368      if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E'))
 369      {
 370          ++ptr;
 371          if (ptr < end && val[ptr] == '+')
 372              ++ptr;
 373          else if (ptr < end && val[ptr] == '-') {
 374              exponent_sign = true;
 375              ++ptr;
 376          }
 377          if (ptr < end && IsDigit(val[ptr])) {
 378              while (ptr < end && IsDigit(val[ptr])) {
 379                  if (exponent > (UPPER_BOUND / 10LL))
 380                      return false; /* overflow */
 381                  exponent = exponent * 10 + val[ptr] - '0';
 382                  ++ptr;
 383              }
 384          } else return false; /* missing expected digit */
 385      }
 386      if (ptr != end)
 387          return false; /* trailing garbage */
 388  
 389      /* finalize exponent */
 390      if (exponent_sign)
 391          exponent = -exponent;
 392      exponent = exponent - point_ofs + mantissa_tzeros;
 393  
 394      /* finalize mantissa */
 395      if (mantissa_sign)
 396          mantissa = -mantissa;
 397  
 398      /* convert to one 64-bit fixed-point value */
 399      exponent += decimals;
 400      if (exponent < 0)
 401          return false; /* cannot represent values smaller than 10^-decimals */
 402      if (exponent >= 18)
 403          return false; /* cannot represent values larger than or equal to 10^(18-decimals) */
 404  
 405      for (int i=0; i < exponent; ++i) {
 406          if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL))
 407              return false; /* overflow */
 408          mantissa *= 10;
 409      }
 410      if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND)
 411          return false; /* overflow */
 412  
 413      if (amount_out)
 414          *amount_out = mantissa;
 415  
 416      return true;
 417  }
 418  
 419  std::string ToLower(std::string_view str)
 420  {
 421      std::string r;
 422      r.reserve(str.size());
 423      for (auto ch : str) r += ToLower(ch);
 424      return r;
 425  }
 426  
 427  std::string ToUpper(std::string_view str)
 428  {
 429      std::string r;
 430      r.reserve(str.size());
 431      for (auto ch : str) r += ToUpper(ch);
 432      return r;
 433  }
 434  
 435  std::string Capitalize(std::string str)
 436  {
 437      if (str.empty()) return str;
 438      str[0] = ToUpper(str.front());
 439      return str;
 440  }
 441  
 442  std::optional<uint64_t> ParseByteUnits(std::string_view str, ByteUnit default_multiplier)
 443  {
 444      if (str.empty()) {
 445          return std::nullopt;
 446      }
 447      auto multiplier = default_multiplier;
 448      char unit = str.back();
 449      switch (unit) {
 450      case 'k':
 451          multiplier = ByteUnit::k;
 452          break;
 453      case 'K':
 454          multiplier = ByteUnit::K;
 455          break;
 456      case 'm':
 457          multiplier = ByteUnit::m;
 458          break;
 459      case 'M':
 460          multiplier = ByteUnit::M;
 461          break;
 462      case 'g':
 463          multiplier = ByteUnit::g;
 464          break;
 465      case 'G':
 466          multiplier = ByteUnit::G;
 467          break;
 468      case 't':
 469          multiplier = ByteUnit::t;
 470          break;
 471      case 'T':
 472          multiplier = ByteUnit::T;
 473          break;
 474      default:
 475          unit = 0;
 476          break;
 477      }
 478  
 479      uint64_t unit_amount = static_cast<uint64_t>(multiplier);
 480      auto parsed_num = ToIntegral<uint64_t>(unit ? str.substr(0, str.size() - 1) : str);
 481      if (!parsed_num || parsed_num > std::numeric_limits<uint64_t>::max() / unit_amount) { // check overflow
 482          return std::nullopt;
 483      }
 484      return *parsed_num * unit_amount;
 485  }
 486