stackstring.hpp 7.1 KB

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