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