BehaviorTree
Core Library to create and execute Behavior Trees
Loading...
Searching...
No Matches
simple_string.hpp
1#pragma once
2
3#include <cstdint>
4#include <cstring>
5#include <limits>
6#include <stdexcept>
7#include <string>
8#include <string_view>
9
10namespace SafeAny
11{
12
13// Read only version of String that has size 16 bytes and can store
14// in-place strings with size up to 15 bytes.
15
16// Inspired by https://github.com/elliotgoodrich/SSO-23
17
18class SimpleString
19{
20public:
21 SimpleString()
22 {
23 _storage.soo.capacity_left = CAPACITY;
24 _storage.soo.data[0] = '\0';
25 }
26
27 SimpleString(const std::string& str) : SimpleString(str.data(), str.size())
28 {}
29
30 SimpleString(const std::string_view& str) : SimpleString(str.data(), str.size())
31 {}
32
33 SimpleString(const SimpleString& other) : SimpleString(other.data(), other.size())
34 {}
35
36 SimpleString& operator=(const SimpleString& other)
37 {
38 if(this != &other)
39 {
40 this->~SimpleString();
41 createImpl(other.data(), other.size());
42 }
43 return *this;
44 }
45
46 SimpleString(SimpleString&& other) noexcept : SimpleString()
47 {
48 std::swap(_storage, other._storage);
49 }
50
51 SimpleString& operator=(SimpleString&& other) noexcept
52 {
53 if(this != &other)
54 {
55 std::swap(_storage, other._storage);
56 }
57 return *this;
58 }
59
60 SimpleString(const char* input_data) : SimpleString(input_data, strlen(input_data))
61 {}
62
63 SimpleString(const char* input_data, std::size_t size)
64 {
65 createImpl(input_data, size);
66 }
67
68 ~SimpleString()
69 {
70 if(!isSOO())
71 {
72 delete[] _storage.str.data;
73 }
74 _storage.soo.capacity_left = CAPACITY;
75 }
76
77 std::string toStdString() const
78 {
79 return size() > 0 ? std::string(data(), size()) : std::string();
80 }
81 std::string_view toStdStringView() const
82 {
83 return size() > 0 ? std::string_view(data(), size()) : std::string_view();
84 }
85
86 const char* data() const
87 {
88 if(isSOO())
89 {
90 return _storage.soo.data;
91 }
92 else
93 {
94 return _storage.str.data;
95 }
96 }
97
98 std::size_t size() const
99 {
100 if(isSOO())
101 {
102 return CAPACITY - _storage.soo.capacity_left;
103 }
104 else
105 {
106 return _storage.str.size & LONG_MASK;
107 }
108 }
109
110 bool operator==(const SimpleString& other) const
111 {
112 const size_t N = size();
113 // memcmp, not strncmp: the content may hold embedded NUL bytes, and strncmp
114 // stops at the first one, reporting two different strings of equal length as
115 // equal. It is also what operator< below compares with.
116 return other.size() == N && std::memcmp(data(), other.data(), N) == 0;
117 }
118
119 bool operator!=(const SimpleString& other) const
120 {
121 const size_t N = size();
122 return other.size() != N || std::memcmp(data(), other.data(), N) != 0;
123 }
124
125 bool operator<=(const SimpleString& other) const
126 {
127 return !(*this > other);
128 }
129
130 bool operator>=(const SimpleString& other) const
131 {
132 return !(*this < other);
133 }
134
135 bool operator<(const SimpleString& other) const
136 {
137 const size_t min_size = std::min(size(), other.size());
138 int cmp = std::memcmp(data(), other.data(), min_size);
139 if(cmp != 0)
140 {
141 return cmp < 0;
142 }
143 return size() < other.size();
144 }
145
146 bool operator>(const SimpleString& other) const
147 {
148 const size_t min_size = std::min(size(), other.size());
149 int cmp = std::memcmp(data(), other.data(), min_size);
150 if(cmp != 0)
151 {
152 return cmp > 0;
153 }
154 return size() > other.size();
155 }
156
157 bool isSOO() const
158 {
159 return (_storage.soo.capacity_left & IS_LONG_BIT) == 0;
160 }
161
162private:
163 struct String
164 {
165 char* data = nullptr;
166 std::size_t size = 0;
167 };
168
169 constexpr static std::size_t CAPACITY = 15; // sizeof(String) - 1);
170 constexpr static std::size_t IS_LONG_BIT = 1 << 7;
171 constexpr static std::size_t LONG_MASK = (~std::size_t(0)) >> 1;
172 constexpr static std::size_t MAX_SIZE = 100UL * 1024UL * 1024UL;
173
174 union
175 {
176 String str;
177
178 struct SOO
179 {
180 char data[CAPACITY];
181 uint8_t capacity_left = CAPACITY;
182 } soo;
183 } _storage = {};
184
185private:
186 void createImpl(const char* input_data, std::size_t size)
187 {
188 if(size > MAX_SIZE)
189 {
190 throw std::invalid_argument("size too large for a simple string");
191 }
192
193 if(size > CAPACITY)
194 {
195 _storage.str.size = size;
196 _storage.soo.capacity_left = IS_LONG_BIT;
197 _storage.str.data = new char[size + 1]; // NOLINT(cppcoreguidelines-owning-memory)
198 std::memcpy(_storage.str.data, input_data, size);
199 _storage.str.data[size] = '\0';
200 }
201 else
202 {
203 _storage.soo.capacity_left = uint8_t(CAPACITY - size);
204 if(size > 0)
205 {
206 std::memcpy(_storage.soo.data, input_data, size);
207 }
208 if(size < CAPACITY)
209 {
210 _storage.soo.data[size] = '\0';
211 }
212 }
213 }
214};
215
216} // namespace SafeAny
Definition: simple_string.hpp:19