stackstring.hpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. //
  2. // Copyright (c) 2012 Artyom Beilis (Tonkikh)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // https://www.boost.org/LICENSE_1_0.txt
  6. #ifndef BOOST_NOWIDE_STACKSTRING_HPP_INCLUDED
  7. #define BOOST_NOWIDE_STACKSTRING_HPP_INCLUDED
  8. #include <boost/nowide/convert.hpp>
  9. #include <boost/nowide/utf/utf.hpp>
  10. #include <cassert>
  11. #include <cstring>
  12. namespace boost {
  13. namespace nowide {
  14. ///
  15. /// \brief A class that allows to create a temporary wide or narrow UTF strings from
  16. /// wide or narrow UTF source.
  17. ///
  18. /// It uses a stack buffer if the string is short enough
  19. /// otherwise allocates a buffer on the heap.
  20. ///
  21. /// Invalid UTF characters are replaced by the substitution character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER
  22. ///
  23. /// If a NULL pointer is passed to the constructor or convert method, NULL will be returned by c_str.
  24. /// Similarly a default constructed stackstring will return NULL on calling c_str.
  25. ///
  26. template<typename CharOut = wchar_t, typename CharIn = char, size_t BufferSize = 256>
  27. class basic_stackstring
  28. {
  29. public:
  30. /// Size of the stack buffer
  31. static const size_t buffer_size = BufferSize;
  32. /// Type of the output character (converted to)
  33. using output_char = CharOut;
  34. /// Type of the input character (converted from)
  35. using input_char = CharIn;
  36. /// Creates a NULL stackstring
  37. basic_stackstring()
  38. {
  39. buffer_[0] = 0;
  40. }
  41. /// Convert the NULL terminated string input and store in internal buffer
  42. /// If input is NULL, nothing will be stored
  43. explicit basic_stackstring(const input_char* input)
  44. {
  45. convert(input);
  46. }
  47. /// Convert the sequence [begin, end) and store in internal buffer
  48. /// If begin is NULL, nothing will be stored
  49. basic_stackstring(const input_char* begin, const input_char* end)
  50. {
  51. convert(begin, end);
  52. }
  53. /// Copy construct from other
  54. basic_stackstring(const basic_stackstring& other)
  55. {
  56. *this = other;
  57. }
  58. /// Copy assign from other
  59. basic_stackstring& operator=(const basic_stackstring& other)
  60. {
  61. if(this != &other)
  62. {
  63. clear();
  64. const size_t len = other.length();
  65. if(other.uses_stack_memory())
  66. data_ = buffer_;
  67. else if(other.data_)
  68. data_ = new output_char[len + 1];
  69. else
  70. {
  71. data_ = nullptr;
  72. return *this;
  73. }
  74. std::memcpy(data_, other.data_, sizeof(output_char) * (len + 1));
  75. }
  76. return *this;
  77. }
  78. ~basic_stackstring()
  79. {
  80. clear();
  81. }
  82. /// Convert the NULL terminated string input and store in internal buffer
  83. /// If input is NULL, the current buffer will be reset to NULL
  84. output_char* convert(const input_char* input)
  85. {
  86. if(input)
  87. return convert(input, input + utf::strlen(input));
  88. clear();
  89. return get();
  90. }
  91. /// Convert the sequence [begin, end) and store in internal buffer
  92. /// If begin is NULL, the current buffer will be reset to NULL
  93. output_char* convert(const input_char* begin, const input_char* end)
  94. {
  95. clear();
  96. if(begin)
  97. {
  98. const size_t input_len = end - begin;
  99. // Minimum size required: 1 output char per input char + trailing NULL
  100. const size_t min_output_size = input_len + 1;
  101. // If there is a chance the converted string fits on stack, try it
  102. if(min_output_size <= buffer_size && utf::convert_buffer(buffer_, buffer_size, begin, end))
  103. data_ = buffer_;
  104. else
  105. {
  106. // Fallback: Allocate a buffer that is surely large enough on heap
  107. // Max size: Every input char is transcoded to the output char with maximum with + trailing NULL
  108. const size_t max_output_size = input_len * utf::utf_traits<output_char>::max_width + 1;
  109. data_ = new output_char[max_output_size];
  110. const bool success = utf::convert_buffer(data_, max_output_size, begin, end) == data_;
  111. assert(success);
  112. (void)success;
  113. }
  114. }
  115. return get();
  116. }
  117. /// Return the converted, NULL-terminated string or NULL if no string was converted
  118. output_char* get()
  119. {
  120. return data_;
  121. }
  122. /// Return the converted, NULL-terminated string or NULL if no string was converted
  123. const output_char* get() const
  124. {
  125. return data_;
  126. }
  127. /// Reset the internal buffer to NULL
  128. void clear()
  129. {
  130. if(!uses_stack_memory())
  131. delete[] data_;
  132. data_ = nullptr;
  133. }
  134. /// Swap lhs with rhs
  135. friend void swap(basic_stackstring& lhs, basic_stackstring& rhs)
  136. {
  137. if(lhs.uses_stack_memory())
  138. {
  139. if(rhs.uses_stack_memory())
  140. {
  141. for(size_t i = 0; i < buffer_size; i++)
  142. std::swap(lhs.buffer_[i], rhs.buffer_[i]);
  143. } else
  144. {
  145. lhs.data_ = rhs.data_;
  146. rhs.data_ = rhs.buffer_;
  147. for(size_t i = 0; i < buffer_size; i++)
  148. rhs.buffer_[i] = lhs.buffer_[i];
  149. }
  150. } else if(rhs.uses_stack_memory())
  151. {
  152. rhs.data_ = lhs.data_;
  153. lhs.data_ = lhs.buffer_;
  154. for(size_t i = 0; i < buffer_size; i++)
  155. lhs.buffer_[i] = rhs.buffer_[i];
  156. } else
  157. std::swap(lhs.data_, rhs.data_);
  158. }
  159. protected:
  160. /// True if the stack memory is used
  161. bool uses_stack_memory() const
  162. {
  163. return data_ == buffer_;
  164. }
  165. /// Return the current length of the string excluding the NULL terminator
  166. /// If NULL is stored returns NULL
  167. size_t length() const
  168. {
  169. if(!data_)
  170. return 0;
  171. size_t len = 0;
  172. while(data_[len])
  173. len++;
  174. return len;
  175. }
  176. private:
  177. output_char buffer_[buffer_size];
  178. output_char* data_ = nullptr;
  179. }; // basic_stackstring
  180. ///
  181. /// Convenience typedef
  182. ///
  183. using wstackstring = basic_stackstring<wchar_t, char, 256>;
  184. ///
  185. /// Convenience typedef
  186. ///
  187. using stackstring = basic_stackstring<char, wchar_t, 256>;
  188. ///
  189. /// Convenience typedef
  190. ///
  191. using wshort_stackstring = basic_stackstring<wchar_t, char, 16>;
  192. ///
  193. /// Convenience typedef
  194. ///
  195. using short_stackstring = basic_stackstring<char, wchar_t, 16>;
  196. } // namespace nowide
  197. } // namespace boost
  198. #endif