openMSX
StringOp.cc
Go to the documentation of this file.
1 #include "StringOp.hh"
2 #include "MSXException.hh"
3 #include <algorithm>
4 #include <limits>
5 #include <cassert>
6 #include <cstdlib>
7 #include <stdexcept>
8 
9 using std::string;
10 using std::transform;
11 using std::vector;
12 using std::set;
13 
14 namespace StringOp {
15 
16 // class Builder
17 
18 Builder::Builder() = default;
19 
20 Builder::~Builder() = default;
21 
22 Builder& Builder::operator<<(const std::string& t)
23 {
24  buf += t; return *this;
25 }
27 {
28  buf.append(t.data(), t.size()); return *this;
29 }
31 {
32  buf += t; return *this;
33 }
34 Builder& Builder::operator<<(unsigned char t)
35 {
36  return operator<<(unsigned(t));
37 }
38 Builder& Builder::operator<<(unsigned short t)
39 {
40  buf += toString(t); return *this;
41 }
43 {
44  buf += toString(t); return *this;
45 }
46 Builder& Builder::operator<<(unsigned long t)
47 {
48  buf += toString(t); return *this;
49 }
50 Builder& Builder::operator<<(unsigned long long t)
51 {
52  buf += toString(t); return *this;
53 }
55 {
56  buf += t; return *this;
57 }
59 {
60  buf += toString(t); return *this;
61 }
63 {
64  buf += toString(t); return *this;
65 }
67 {
68  buf += toString(t); return *this;
69 }
71 {
72  buf += toString(t); return *this;
73 }
75 {
76  buf += toString(t); return *this;
77 }
79 {
80  buf += toString(t); return *this;
81 }
82 
83 
84 // Returns a fast type that is (at least) big enough to hold the absolute value
85 // of values of the given type. (It always returns 'unsigned' except for 64-bit
86 // integers it returns unsigned long long).
87 template<typename T> struct FastUnsigned { using type = unsigned; };
88 template<> struct FastUnsigned<long long> { using type = unsigned long long; };
89 template<> struct FastUnsigned<unsigned long long> { using type = unsigned long long; };
90 template<> struct FastUnsigned<long> { using type = unsigned long; };
91 template<> struct FastUnsigned<unsigned long> { using type = unsigned long; };
92 
93 // This does the equivalent of
94 // unsigned u = (t < 0) ? -t : t;
95 // but it avoids a compiler warning on the operations
96 // 't < 0' and '-t'
97 // when 't' is actually an unsigned type.
98 template<bool IS_SIGNED> struct AbsHelper;
99 template<> struct AbsHelper<true> {
100  template<typename T>
101  inline typename FastUnsigned<T>::type operator()(T t) const {
102  return (t < 0) ? -t : t;
103  }
104 };
105 template<> struct AbsHelper<false> {
106  template<typename T>
107  inline typename FastUnsigned<T>::type operator()(T t) const {
108  return t;
109  }
110 };
111 
112 // Does the equivalent of if (t < 0) *--p = '-';
113 // but it avoids a compiler warning on 't < 0' when 't' is an unsigned type.
114 template<bool IS_SIGNED> struct PutSign;
115 template<> struct PutSign<true> {
116  template<typename T> inline void operator()(T t, char*& p) const {
117  if (t < 0) *--p = '-';
118  }
119 };
120 template<> struct PutSign<false> {
121  template<typename T> inline void operator()(T /*t*/, char*& /*p*/) const {
122  // nothing
123  }
124 };
125 
126 // This routine is inspired by boost::lexical_cast. It's much faster than a
127 // generic version using std::stringstream. See this page for some numbers:
128 // http://www.boost.org/doc/libs/1_47_0/libs/conversion/lexical_cast.htm#performance
129 template<typename T> static inline string toStringImpl(T t)
130 {
131  static const bool IS_SIGNED = std::numeric_limits<T>::is_signed;
132  static const unsigned BUF_SIZE = 1 + std::numeric_limits<T>::digits10
133  + (IS_SIGNED ? 1 : 0);
134 
135  char buf[BUF_SIZE];
136  char* p = &buf[BUF_SIZE];
137 
138  AbsHelper<IS_SIGNED> absHelper;
139  typename FastUnsigned<T>::type a = absHelper(t);
140  do {
141  *--p = '0' + (a % 10);
142  a /= 10;
143  } while (a);
144 
145  PutSign<IS_SIGNED> putSign;
146  putSign(t, p);
147 
148  return string(p, &buf[BUF_SIZE] - p);
149 }
150 string toString(long long a) { return toStringImpl(a); }
151 string toString(unsigned long long a) { return toStringImpl(a); }
152 string toString(long a) { return toStringImpl(a); }
153 string toString(unsigned long a) { return toStringImpl(a); }
154 string toString(int a) { return toStringImpl(a); }
155 string toString(unsigned a) { return toStringImpl(a); }
156 string toString(short a) { return toStringImpl(a); }
157 string toString(unsigned short a) { return toStringImpl(a); }
158 string toString(char a) { return string(1, a); }
159 string toString(signed char a) { return string(1, a); }
160 string toString(unsigned char a) { return string(1, a); }
161 string toString(bool a) { return string(1, '0' + a); }
162 
163 static inline char hexDigit(unsigned x)
164 {
165  return (x < 10) ? ('0' + x) : ('a' + x - 10);
166 }
167 string toHexString(unsigned x, unsigned width)
168 {
169  assert((0 < width) && (width <= 8));
170 
171  char buf[8];
172  char* p = &buf[8];
173  int i = width;
174  do {
175  *--p = hexDigit(x & 15);
176  x >>= 4;
177  } while (--i);
178  return string(p, width);
179 }
180 
181 int stringToInt(const string& str)
182 {
183  return strtol(str.c_str(), nullptr, 0);
184 }
185 bool stringToInt(const string& str, int& result)
186 {
187  char* endptr;
188  result = strtol(str.c_str(), &endptr, 0);
189  return *endptr == '\0';
190 }
191 
192 unsigned stringToUint(const string& str)
193 {
194  return strtoul(str.c_str(), nullptr, 0);
195 }
196 bool stringToUint(const string& str, unsigned& result)
197 {
198  char* endptr;
199  result = strtoul(str.c_str(), &endptr, 0);
200  return *endptr == '\0';
201 }
202 
203 uint64_t stringToUint64(const string& str)
204 {
205  return strtoull(str.c_str(), nullptr, 0);
206 }
207 
209 {
210  if (str == "1") return true;
211  if ((str.size() == 4) && (strncasecmp(str.data(), "true", 4) == 0))
212  return true;
213  if ((str.size() == 3) && (strncasecmp(str.data(), "yes", 3) == 0))
214  return true;
215  return false;
216 }
217 
218 double stringToDouble(const string& str)
219 {
220  return strtod(str.c_str(), nullptr);
221 }
222 bool stringToDouble(const string& str, double& result)
223 {
224  char* endptr;
225  result = strtod(str.c_str(), &endptr);
226  return *endptr == '\0';
227 }
228 
229 string toLower(string_ref str)
230 {
231  string result = str.str();
232  transform(begin(result), end(result), begin(result), ::tolower);
233  return result;
234 }
235 
237 {
238  return total.starts_with(part);
239 }
240 bool startsWith(string_ref total, char part)
241 {
242  return !total.empty() && (total.front() == part);
243 }
244 
245 bool endsWith(string_ref total, string_ref part)
246 {
247  return total.ends_with(part);
248 }
249 bool endsWith(string_ref total, char part)
250 {
251  return !total.empty() && (total.back() == part);
252 }
253 
254 void trimRight(string& str, const char* chars)
255 {
256  auto pos = str.find_last_not_of(chars);
257  if (pos != string::npos) {
258  str.erase(pos + 1);
259  } else {
260  str.clear();
261  }
262 }
263 void trimRight(string& str, char chars)
264 {
265  auto pos = str.find_last_not_of(chars);
266  if (pos != string::npos) {
267  str.erase(pos + 1);
268  } else {
269  str.clear();
270  }
271 }
272 void trimRight(string_ref& str, string_ref chars)
273 {
274  while (!str.empty() && (chars.find(str.back()) != string_ref::npos)) {
275  str.pop_back();
276  }
277 }
278 void trimRight(string_ref& str, char chars)
279 {
280  while (!str.empty() && (str.back() == chars)) {
281  str.pop_back();
282  }
283 }
284 
285 void trimLeft(string& str, const char* chars)
286 {
287  str.erase(0, str.find_first_not_of(chars));
288 }
289 void trimLeft(string& str, char chars)
290 {
291  str.erase(0, str.find_first_not_of(chars));
292 }
293 void trimLeft(string_ref& str, string_ref chars)
294 {
295  while (!str.empty() && (chars.find(str.front()) != string_ref::npos)) {
296  str.pop_front();
297  }
298 }
299 void trimLeft(string_ref& str, char chars)
300 {
301  while (!str.empty() && (str.front() == chars)) {
302  str.pop_front();
303  }
304 }
305 
306 void trim(string_ref& str, string_ref chars)
307 {
308  trimRight(str, chars);
309  trimLeft (str, chars);
310 }
311 
312 void trim(string_ref& str, char chars)
313 {
314  trimRight(str, chars);
315  trimLeft (str, chars);
316 }
317 
318 void splitOnFirst(string_ref str, string_ref chars, string_ref& first, string_ref& last)
319 {
320  auto pos = str.find_first_of(chars);
321  if (pos == string_ref::npos) {
322  first = str;
323  last.clear();
324  } else {
325  first = str.substr(0, pos);
326  last = str.substr(pos + 1);
327  }
328 }
329 void splitOnFirst(string_ref str, char chars, string_ref& first, string_ref& last)
330 {
331  auto pos = str.find_first_of(chars);
332  if (pos == string_ref::npos) {
333  first = str;
334  last.clear();
335  } else {
336  first = str.substr(0, pos);
337  last = str.substr(pos + 1);
338  }
339 }
340 
341 void splitOnLast(string_ref str, string_ref chars, string_ref& first, string_ref& last)
342 {
343  auto pos = str.find_last_of(chars);
344  if (pos == string_ref::npos) {
345  first.clear();
346  last = str;
347  } else {
348  first = str.substr(0, pos);
349  last = str.substr(pos + 1);
350  }
351 }
352 void splitOnLast(string_ref str, char chars, string_ref& first, string_ref& last)
353 {
354  auto pos = str.find_last_of(chars);
355  if (pos == string_ref::npos) {
356  first.clear();
357  last = str;
358  } else {
359  first = str.substr(0, pos);
360  last = str.substr(pos + 1);
361  }
362 }
363 
364 vector<string_ref> split(string_ref str, char chars)
365 {
366  vector<string_ref> result;
367  while (!str.empty()) {
368  string_ref first, last;
369  splitOnFirst(str, chars, first, last);
370  result.push_back(first);
371  str = last;
372  }
373  return result;
374 }
375 
376 string join(const vector<string_ref>& elems, char separator)
377 {
378  if (elems.empty()) return {};
379 
380  auto it = begin(elems);
381  Builder result;
382  result << *it;
383  for (++it; it != end(elems); ++it) {
384  result << separator;
385  result << *it;
386  }
387  return result;
388 }
389 
390 static unsigned parseNumber(string_ref str)
391 {
392  trim(str, " \t");
393  if (!str.empty()) {
394  try {
395  return fast_stou(str);
396  } catch (std::invalid_argument&) {
397  // parse error
398  }
399  }
400  throw openmsx::MSXException("Invalid integer: " + str);
401 }
402 
403 static void insert(unsigned x, set<unsigned>& result, unsigned min, unsigned max)
404 {
405  if ((x < min) || (x > max)) {
406  throw openmsx::MSXException("Out of range");
407  }
408  result.insert(x);
409 }
410 
411 static void parseRange2(string_ref str, set<unsigned>& result,
412  unsigned min, unsigned max)
413 {
414  // trimRight only: here we only care about all spaces
415  trimRight(str, " \t");
416  if (str.empty()) return;
417 
418  auto pos = str.find('-');
419  if (pos == string_ref::npos) {
420  insert(parseNumber(str), result, min, max);
421  } else {
422  unsigned begin = parseNumber(str.substr(0, pos));
423  unsigned end = parseNumber(str.substr(pos + 1));
424  if (end < begin) {
425  std::swap(begin, end);
426  }
427  for (unsigned i = begin; i <= end; ++i) {
428  insert(i, result, min, max);
429  }
430  }
431 }
432 
433 set<unsigned> parseRange(string_ref str, unsigned min, unsigned max)
434 {
435  set<unsigned> result;
436  while (true) {
437  auto next = str.find(',');
438  string_ref sub = (next == string_ref::npos)
439  ? str
440  : str.substr(0, next++);
441  parseRange2(sub, result, min, max);
442  if (next == string_ref::npos) break;
443  str = str.substr(next);
444  }
445  return result;
446 }
447 
448 #if defined(__APPLE__)
449 
450 std::string fromCFString(CFStringRef str)
451 {
452  // Try the quick route first.
453  const char *cstr = CFStringGetCStringPtr(str, kCFStringEncodingUTF8);
454  if (cstr) {
455  // String was already in UTF8 encoding.
456  return std::string(cstr);
457  }
458 
459  // Convert to UTF8 encoding.
460  CFIndex len = CFStringGetLength(str);
461  CFRange range = CFRangeMake(0, len);
462  CFIndex usedBufLen = 0;
463  CFStringGetBytes(
464  str, range, kCFStringEncodingUTF8, '?', false, nullptr, len, &usedBufLen);
465  UInt8 buffer[usedBufLen];
466  CFStringGetBytes(
467  str, range, kCFStringEncodingUTF8, '?', false, buffer, len, &usedBufLen);
468  return std::string(reinterpret_cast<const char *>(buffer), usedBufLen);
469 }
470 
471 #endif
472 
473 } // namespace StringOp
string_ref::const_iterator end(const string_ref &x)
Definition: string_ref.hh:167
string toHexString(unsigned x, unsigned width)
Definition: StringOp.cc:167
bool stringToBool(string_ref str)
Definition: StringOp.cc:208
void pop_front()
Definition: string_ref.hh:92
size_type find_last_of(string_ref s) const
Definition: string_ref.cc:101
string toLower(string_ref str)
Definition: StringOp.cc:229
bool ends_with(string_ref x) const
Definition: string_ref.cc:126
vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:268
void splitOnFirst(string_ref str, string_ref chars, string_ref &first, string_ref &last)
Definition: StringOp.cc:318
void splitOnLast(string_ref str, string_ref chars, string_ref &first, string_ref &last)
Definition: StringOp.cc:341
string toString(long long a)
Definition: StringOp.cc:150
Builder & operator<<(const std::string &t)
Definition: StringOp.cc:22
void pop_back()
Definition: string_ref.hh:91
void trimLeft(string &str, const char *chars)
Definition: StringOp.cc:285
string join(const vector< string_ref > &elems, char separator)
Definition: StringOp.cc:376
set< unsigned > parseRange(string_ref str, unsigned min, unsigned max)
Definition: StringOp.cc:433
This class implements a subset of the proposal for std::string_ref (proposed for the next c++ standar...
Definition: string_ref.hh:18
vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:277
bool empty() const
Definition: string_ref.hh:56
void trimRight(string &str, const char *chars)
Definition: StringOp.cc:254
const char * data() const
Definition: string_ref.hh:68
bool starts_with(string_ref x) const
Definition: string_ref.cc:116
bool startsWith(string_ref total, string_ref part)
Definition: StringOp.cc:236
void operator()(T t, char *&p) const
Definition: StringOp.cc:116
unsigned fast_stou(string_ref s)
Definition: string_ref.cc:145
std::string str() const
Definition: string_ref.cc:12
static const size_type npos
Definition: string_ref.hh:26
void clear()
Definition: string_ref.hh:75
string_ref substr(size_type pos, size_type n=npos) const
Definition: string_ref.cc:32
vector< string_ref > split(string_ref str, char chars)
Definition: StringOp.cc:364
FastUnsigned< T >::type operator()(T t) const
Definition: StringOp.cc:101
int stringToInt(const string &str)
Definition: StringOp.cc:181
char back() const
Definition: string_ref.hh:67
double stringToDouble(const string &str)
Definition: StringOp.cc:218
size_type size() const
Definition: string_ref.hh:55
void trim(string_ref &str, string_ref chars)
Definition: StringOp.cc:306
size_type find_first_of(string_ref s) const
Definition: string_ref.cc:87
unsigned stringToUint(const string &str)
Definition: StringOp.cc:192
uint64_t stringToUint64(const string &str)
Definition: StringOp.cc:203
string_ref::const_iterator begin(const string_ref &x)
Definition: string_ref.hh:166
char front() const
Definition: string_ref.hh:66
void operator()(T, char *&) const
Definition: StringOp.cc:121
bool endsWith(string_ref total, string_ref part)
Definition: StringOp.cc:245
FastUnsigned< T >::type operator()(T t) const
Definition: StringOp.cc:107
size_type find(string_ref s) const
Definition: string_ref.cc:38