escape_string.ipp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. //
  2. // Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BOOST_MYSQL_IMPL_ESCAPE_STRING_IPP
  8. #define BOOST_MYSQL_IMPL_ESCAPE_STRING_IPP
  9. #pragma once
  10. #include <boost/mysql/character_set.hpp>
  11. #include <boost/mysql/client_errc.hpp>
  12. #include <boost/mysql/error_code.hpp>
  13. #include <boost/mysql/escape_string.hpp>
  14. #include <boost/mysql/string_view.hpp>
  15. #include <boost/mysql/detail/output_string.hpp>
  16. #include <boost/mysql/impl/internal/call_next_char.hpp>
  17. namespace boost {
  18. namespace mysql {
  19. namespace detail {
  20. // A (possibly null) escape sequence of two characters. Used as the return value for escapers
  21. class escape_sequence
  22. {
  23. char data_[2]{};
  24. public:
  25. escape_sequence() = default;
  26. escape_sequence(char ch1, char ch2) noexcept : data_{ch1, ch2} {}
  27. bool is_escape() const noexcept { return data_[0] != '\0'; }
  28. string_view data() const noexcept { return string_view(data_, 2); }
  29. };
  30. // Escaper is a function object that takes a char and returns a
  31. // escape_sequence determining whether we should escape the char or not
  32. template <class Escaper>
  33. BOOST_ATTRIBUTE_NODISCARD error_code
  34. escape_impl(string_view input, character_set charset, Escaper escaper, output_string_ref output)
  35. {
  36. const char* it = input.data();
  37. const char* end = it + input.size();
  38. // The raw range is a range of contiguous characters that don't need escaping.
  39. // We only append the raw range once we find a character that needs escaping
  40. const char* raw_begin = it;
  41. while (it != end)
  42. {
  43. escape_sequence seq = escaper(*it);
  44. if (seq.is_escape())
  45. {
  46. // Dump what we already had
  47. output.append({raw_begin, it});
  48. // Output the escape sequence
  49. output.append(seq.data());
  50. // Advance
  51. ++it;
  52. // Update the start of the range that doesn't need escaping
  53. raw_begin = it;
  54. }
  55. else
  56. {
  57. // Advance with the charset function
  58. std::size_t char_size = detail::call_next_char(charset, it, end);
  59. if (char_size == 0u)
  60. return client_errc::invalid_encoding;
  61. it += char_size;
  62. }
  63. }
  64. // Dump the remaining of the string, if any
  65. output.append({raw_begin, end});
  66. // Done
  67. return error_code();
  68. }
  69. struct backslash_escaper
  70. {
  71. escape_sequence operator()(char input) const noexcept
  72. {
  73. switch (input)
  74. {
  75. case '\0': return {'\\', '0'};
  76. case '\n': return {'\\', 'n'};
  77. case '\r': return {'\\', 'r'};
  78. case '\\': return {'\\', '\\'};
  79. case '\'': return {'\\', '\''};
  80. case '"': return {'\\', '"'};
  81. case '\x1a': return {'\\', 'Z'}; // Ctrl+Z
  82. default: return escape_sequence(); // No escape
  83. }
  84. };
  85. };
  86. struct quote_escaper
  87. {
  88. char quot;
  89. quote_escaper(char q) noexcept : quot(q) {}
  90. escape_sequence operator()(char input) const noexcept
  91. {
  92. return input == quot ? escape_sequence(quot, quot) : escape_sequence();
  93. }
  94. };
  95. } // namespace detail
  96. } // namespace mysql
  97. } // namespace boost
  98. boost::mysql::error_code boost::mysql::detail::escape_string(
  99. string_view input,
  100. const format_options& opts,
  101. char escape_char,
  102. output_string_ref output
  103. )
  104. {
  105. return (escape_char == '`' || !opts.backslash_escapes)
  106. ? detail::escape_impl(input, opts.charset, quote_escaper(escape_char), output)
  107. : detail::escape_impl(input, opts.charset, backslash_escaper(), output);
  108. }
  109. #endif