libostd/ostd/string.hh

1098 lines
28 KiB
C++
Raw Normal View History

2015-05-27 22:43:13 +02:00
/* String for OctaSTD.
*
* This file is part of OctaSTD. See COPYING.md for futher information.
*/
2015-07-13 21:08:55 +02:00
#ifndef OSTD_STRING_HH
#define OSTD_STRING_HH
2015-05-27 22:43:13 +02:00
2015-05-27 23:15:24 +02:00
#include <stdio.h>
2015-05-27 22:43:13 +02:00
#include <stddef.h>
2015-07-13 21:08:55 +02:00
#include "ostd/utility.hh"
#include "ostd/range.hh"
#include "ostd/vector.hh"
2015-07-20 03:15:12 +02:00
#include "ostd/functional.hh"
#include "ostd/type_traits.hh"
2015-05-27 22:43:13 +02:00
2015-07-13 21:07:14 +02:00
namespace ostd {
static constexpr Size npos = -1;
2015-05-27 22:43:13 +02:00
template<typename T, typename A = Allocator<T>>
class StringBase;
template<typename T>
2015-07-21 23:06:23 +02:00
struct CharRangeBase: InputRange<
CharRangeBase<T>, ContiguousRangeTag, T
> {
2015-07-21 22:16:38 +02:00
private:
struct Nat {};
public:
2015-07-21 23:06:23 +02:00
CharRangeBase(): p_beg(nullptr), p_end(nullptr) {};
template<typename U>
CharRangeBase(T *beg, U end, EnableIf<
(IsPointer<U> || IsNullPointer<U>) && IsConvertible<U, T *>, Nat
> = Nat()): p_beg(beg), p_end(end) {}
2015-07-21 23:06:23 +02:00
CharRangeBase(T *beg, Size n): p_beg(beg), p_end(beg + n) {}
/* TODO: traits for utf-16/utf-32 string lengths, for now assume char */
template<typename U>
CharRangeBase(
U beg, EnableIf<IsConvertible<U, T *> && !IsArray<U>, Nat> = Nat()
): p_beg(beg), p_end(static_cast<T *>(beg) + (beg ? strlen(beg) : 0)) {}
CharRangeBase(Nullptr): p_beg(nullptr), p_end(nullptr) {}
template<typename U, Size N>
CharRangeBase(U (&beg)[N], EnableIf<IsConvertible<U *, T *>, Nat> = Nat()):
p_beg(beg), p_end(beg + N - (beg[N - 1] == '\0'))
{}
template<typename U, typename A>
2016-06-23 20:18:35 +02:00
CharRangeBase(StringBase<U, A> const &s, EnableIf<
IsConvertible<U *, T *>, Nat
> = Nat()):
p_beg(s.data()), p_end(s.data() + s.size())
{}
template<typename U, typename = EnableIf<IsConvertible<U *, T *>>>
2016-06-23 20:18:35 +02:00
CharRangeBase(CharRangeBase<U> const &v):
p_beg(&v[0]), p_end(&v[v.size()])
{}
2015-06-17 03:00:39 +02:00
2016-06-23 20:18:35 +02:00
CharRangeBase &operator=(CharRangeBase const &v) {
p_beg = v.p_beg; p_end = v.p_end; return *this;
}
template<typename A>
2016-06-23 20:18:35 +02:00
CharRangeBase &operator=(StringBase<T, A> const &s) {
p_beg = s.data(); p_end = s.data() + s.size(); return *this;
}
/* TODO: traits for utf-16/utf-32 string lengths, for now assume char */
2015-07-21 23:06:23 +02:00
CharRangeBase &operator=(T *s) {
2015-08-07 00:57:40 +02:00
p_beg = s; p_end = s + (s ? strlen(s) : 0); return *this;
}
bool empty() const { return p_beg == p_end; }
bool pop_front() {
if (p_beg == p_end) {
return false;
}
++p_beg;
return true;
}
bool push_front() { --p_beg; return true; }
Size pop_front_n(Size n) {
Size olen = p_end - p_beg;
p_beg += n;
if (p_beg > p_end) {
p_beg = p_end;
return olen;
}
return n;
}
Size push_front_n(Size n) { p_beg -= n; return true; }
T &front() const { return *p_beg; }
2016-06-23 20:18:35 +02:00
bool equals_front(CharRangeBase const &range) const {
return p_beg == range.p_beg;
}
2016-06-23 20:18:35 +02:00
Ptrdiff distance_front(CharRangeBase const &range) const {
return range.p_beg - p_beg;
}
bool pop_back() {
if (p_end == p_beg) {
return false;
}
--p_end;
return true;
}
bool push_back() { ++p_end; return true; }
Size pop_back_n(Size n) {
Size olen = p_end - p_beg;
p_end -= n;
if (p_end < p_beg) {
p_end = p_beg;
return olen;
}
return n;
}
Size push_back_n(Size n) { p_end += n; return true; }
T &back() const { return *(p_end - 1); }
2016-06-23 20:18:35 +02:00
bool equals_back(CharRangeBase const &range) const {
return p_end == range.p_end;
}
2016-06-23 20:18:35 +02:00
Ptrdiff distance_back(CharRangeBase const &range) const {
return range.p_end - p_end;
}
Size size() const { return p_end - p_beg; }
2015-07-21 23:06:23 +02:00
CharRangeBase slice(Size start, Size end) const {
return CharRangeBase(p_beg + start, p_beg + end);
}
T &operator[](Size i) const { return p_beg[i]; }
2015-07-01 22:12:45 +02:00
bool put(T v) {
if (empty()) {
return false;
}
*(p_beg++) = v;
2015-07-01 22:12:45 +02:00
return true;
}
2016-06-23 20:18:35 +02:00
Size put_n(T const *p, Size n) {
2016-03-23 23:11:15 +01:00
Size an = ostd::min(n, size());
memcpy(p_beg, p, an * sizeof(T));
p_beg += an;
return an;
}
T *data() { return p_beg; }
2016-06-23 20:18:35 +02:00
T const *data() const { return p_beg; }
Size to_hash() const {
2015-07-20 03:15:12 +02:00
return detail::mem_hash(data(), size());
}
/* non-range */
2016-06-23 20:18:35 +02:00
int compare(CharRangeBase<T const> s) const {
2016-08-17 19:18:12 +02:00
ostd::Size s1 = size(), s2 = s.size();
2016-08-18 01:34:20 +02:00
int ret;
2016-08-17 19:18:12 +02:00
if (!s1 || !s2) {
2016-08-18 01:34:20 +02:00
goto diffsize;
2016-08-17 19:18:12 +02:00
}
2016-08-18 01:34:20 +02:00
if ((ret = memcmp(data(), s.data(), ostd::min(s1, s2)))) {
return ret;
}
diffsize:
return (s1 < s2) ? -1 : ((s1 > s2) ? 1 : 0);
}
2016-03-23 23:32:30 +01:00
template<typename R>
EnableIf<IsOutputRange<R>, Size> copy(R &&orange, Size n = -1) {
return orange.put_n(data(), ostd::min(n, size()));
}
Size copy(RemoveCv<T> *p, Size n = -1) {
Size c = ostd::min(n, size());
memcpy(p, data(), c * sizeof(T));
return c;
}
private:
T *p_beg, *p_end;
};
using CharRange = CharRangeBase<char>;
2016-06-23 20:18:35 +02:00
using ConstCharRange = CharRangeBase<char const>;
inline bool operator==(ConstCharRange lhs, ConstCharRange rhs) {
return !lhs.compare(rhs);
}
inline bool operator!=(ConstCharRange lhs, ConstCharRange rhs) {
return lhs.compare(rhs);
}
inline bool operator<(ConstCharRange lhs, ConstCharRange rhs) {
return lhs.compare(rhs) < 0;
}
inline bool operator>(ConstCharRange lhs, ConstCharRange rhs) {
return lhs.compare(rhs) > 0;
}
inline bool operator<=(ConstCharRange lhs, ConstCharRange rhs) {
return lhs.compare(rhs) <= 0;
}
inline bool operator>=(ConstCharRange lhs, ConstCharRange rhs) {
return lhs.compare(rhs) >= 0;
}
inline bool starts_with(ConstCharRange a, ConstCharRange b) {
if (a.size() < b.size()) {
return false;
}
return a.slice(0, b.size()) == b;
}
template<typename T, typename A>
2015-06-04 00:07:57 +02:00
class StringBase {
using StrPair = detail::CompressedPair<AllocatorPointer<A>, A>;
2015-05-27 22:43:13 +02:00
2015-07-13 21:07:14 +02:00
ostd::Size p_len, p_cap;
StrPair p_buf;
template<typename R>
void ctor_from_range(R &range, EnableIf<
2016-01-13 18:42:37 +01:00
IsFiniteRandomAccessRange<R> && IsSame<T, RemoveCv<RangeValue<R>>>, bool
> = true) {
if (range.empty()) {
return;
}
RangeSize<R> l = range.size();
reserve(l);
p_len = l;
range.copy(p_buf.first(), l);
p_buf.first()[l] = '\0';
}
template<typename R>
void ctor_from_range(R &range, EnableIf<
2016-01-13 18:42:37 +01:00
!IsFiniteRandomAccessRange<R> || !IsSame<T, RemoveCv<RangeValue<R>>>,
bool
> = true) {
if (range.empty()) {
return;
}
Size i = 0;
for (; !range.empty(); range.pop_front()) {
reserve(i + 1);
allocator_construct(
p_buf.second(), &p_buf.first()[i],
range.front()
);
++i;
p_len = i;
}
p_buf.first()[p_len] = '\0';
2015-06-04 00:07:57 +02:00
}
2015-05-27 22:43:13 +02:00
2015-06-04 00:07:57 +02:00
public:
2015-07-13 21:07:14 +02:00
using Size = ostd::Size;
using Difference = Ptrdiff;
2015-06-08 01:55:08 +02:00
using Value = T;
using Reference = T &;
2016-06-23 20:18:35 +02:00
using ConstReference = T const &;
using Pointer = AllocatorPointer<A>;
using ConstPointer = AllocatorConstPointer<A>;
2015-07-21 23:06:23 +02:00
using Range = CharRangeBase<T>;
2016-06-23 20:18:35 +02:00
using ConstRange = CharRangeBase<T const>;
2015-06-08 01:55:08 +02:00
using Allocator = A;
2015-06-04 00:07:57 +02:00
StringBase(A const &a = A()):
p_len(0), p_cap(0),
p_buf(reinterpret_cast<Pointer>(&p_len), a)
{}
2016-06-23 20:18:35 +02:00
explicit StringBase(Size n, T val = T(), A const &al = A()):
StringBase(al)
{
if (!n) {
return;
}
p_buf.first() = allocator_allocate(p_buf.second(), n + 1);
p_len = p_cap = n;
Pointer cur = p_buf.first(), last = p_buf.first() + n;
while (cur != last) {
*cur++ = val;
}
*cur = '\0';
}
StringBase(StringBase const &s):
p_len(0), p_cap(0), p_buf(reinterpret_cast<Pointer>(&p_len),
allocator_container_copy(s.p_buf.second()))
{
if (!s.p_len) {
return;
}
reserve(s.p_len);
p_len = s.p_len;
memcpy(p_buf.first(), s.p_buf.first(), (p_len + 1) * sizeof(T));
}
StringBase(StringBase const &s, A const &a):
p_len(0), p_cap(0), p_buf(reinterpret_cast<Pointer>(&p_len), a)
{
if (!s.p_len) {
return;
}
reserve(s.p_len);
p_len = s.p_len;
memcpy(p_buf.first(), s.p_buf.first(), (p_len + 1) * sizeof(T));
}
StringBase(StringBase &&s):
p_len(s.p_len), p_cap(s.p_cap),
p_buf(s.p_buf.first(), move(s.p_buf.second()))
{
s.p_len = s.p_cap = 0;
2016-07-02 05:56:23 +02:00
s.p_buf.first() = reinterpret_cast<Pointer>(&s.p_len);
}
StringBase(StringBase &&s, A const &a):
p_len(0), p_cap(0), p_buf(reinterpret_cast<Pointer>(&p_len), a)
{
if (!s.p_len) {
return;
}
if (a != s.p_buf.second()) {
reserve(s.p_cap);
p_len = s.p_len;
memcpy(p_buf.first(), s.p_buf.first(), (p_len + 1) * sizeof(T));
return;
}
p_buf.first() = s.p_buf.first();
p_len = s.p_len;
p_cap = s.p_cap;
s.p_len = s.p_cap = 0;
s.p_buf.first() = &s.p_cap;
}
2015-06-04 00:07:57 +02:00
StringBase(
StringBase const &s, Size pos, Size len = npos, A const &a = A()
): StringBase(a) {
Size end = (len == npos) ? s.size() : (pos + len);
Size nch = (end - pos);
reserve(nch);
memcpy(p_buf.first(), s.p_buf.first() + pos, nch);
p_len += nch;
p_buf.first()[p_len] = '\0';
2015-06-04 00:07:57 +02:00
}
2015-05-29 02:02:19 +02:00
2015-06-04 00:07:57 +02:00
/* TODO: traits for utf-16/utf-32 string lengths, for now assume char */
2016-06-23 20:18:35 +02:00
StringBase(ConstRange v, A const &a = A()): StringBase(a) {
if (!v.size()) {
return;
}
reserve(v.size());
memcpy(p_buf.first(), &v[0], v.size());
p_buf.first()[v.size()] = '\0';
p_len = v.size();
}
template<typename U>
2016-06-23 20:18:35 +02:00
StringBase(U v, EnableIf<
IsConvertible<U, Value const *> && !IsArray<U>, A
> const &a = A()): StringBase(ConstRange(v), a) {}
template<typename U, Size N>
2016-06-23 20:18:35 +02:00
StringBase(U (&v)[N], EnableIf<
IsConvertible<U *, Value const *>, A
> const &a = A()): StringBase(ConstRange(v), a) {}
template<typename R, typename = EnableIf<
IsInputRange<R> && IsConvertible<RangeReference<R>, Value>
>>
StringBase(R range, A const &a = A()): StringBase(a) {
ctor_from_range(range);
2015-06-04 00:07:57 +02:00
}
2015-05-27 22:43:13 +02:00
~StringBase() {
if (!p_cap) {
return;
}
allocator_deallocate(p_buf.second(), p_buf.first(), p_cap + 1);
}
void clear() {
if (!p_len) {
return;
}
p_len = 0;
*p_buf.first() = '\0';
}
2015-05-27 22:43:13 +02:00
2016-06-23 20:18:35 +02:00
StringBase &operator=(StringBase const &v) {
if (this == &v) {
return *this;
}
clear();
if (AllocatorPropagateOnContainerCopyAssignment<A>) {
if ((p_buf.second() != v.p_buf.second()) && p_cap) {
allocator_deallocate(p_buf.second(), p_buf.first(), p_cap);
p_cap = 0;
2016-07-02 05:56:23 +02:00
p_buf.first() = reinterpret_cast<Pointer>(&p_len);
}
p_buf.second() = v.p_buf.second();
}
reserve(v.p_cap);
p_len = v.p_len;
if (p_len) {
memcpy(p_buf.first(), v.p_buf.first(), p_len);
p_buf.first()[p_len] = '\0';
} else {
p_buf.first() = reinterpret_cast<Pointer>(&p_len);
}
2015-06-04 00:07:57 +02:00
return *this;
}
2015-06-10 01:16:25 +02:00
StringBase &operator=(StringBase &&v) {
clear();
if (p_cap) {
allocator_deallocate(p_buf.second(), p_buf.first(), p_cap);
}
if (AllocatorPropagateOnContainerMoveAssignment<A>) {
p_buf.second() = v.p_buf.second();
}
p_len = v.p_len;
p_cap = v.p_cap;
p_buf.~StrPair();
new (&p_buf) StrPair(v.disown(), move(v.p_buf.second()));
if (!p_cap) {
p_buf.first() = reinterpret_cast<Pointer>(&p_len);
}
2015-06-04 00:07:57 +02:00
return *this;
}
StringBase &operator=(ConstRange v) {
reserve(v.size());
if (v.size()) {
memcpy(p_buf.first(), &v[0], v.size());
}
p_buf.first()[v.size()] = '\0';
p_len = v.size();
2015-06-04 00:07:57 +02:00
return *this;
}
template<typename U>
EnableIf<
IsConvertible<U, Value const *> && !IsArray<U>, StringBase &
> operator=(U v) {
return operator=(ConstRange(v));
}
template<typename U, Size N>
EnableIf<
IsConvertible<U *, Value const *>, StringBase &
> operator=(U (&v)[N]) {
return operator=(ConstRange(v));
}
template<typename R, typename = EnableIf<
IsInputRange<R> && IsConvertible<RangeReference<R>, Value>
2016-06-23 20:18:35 +02:00
>> StringBase &operator=(R const &r) {
clear();
ctor_from_range(r);
return *this;
}
2015-05-27 22:43:13 +02:00
void resize(Size n, T v = T()) {
if (!n) {
clear();
return;
}
Size l = p_len;
reserve(n);
p_len = n;
for (Size i = l; i < p_len; ++i) {
p_buf.first()[i] = T(v);
}
p_buf.first()[l] = '\0';
2015-06-04 00:07:57 +02:00
}
2015-05-27 22:43:13 +02:00
void reserve(Size n) {
if (n <= p_cap) {
return;
}
Size oc = p_cap;
if (!oc) {
p_cap = max(n, Size(8));
} else {
while (p_cap < n) p_cap *= 2;
}
Pointer tmp = allocator_allocate(p_buf.second(), p_cap + 1);
if (oc > 0) {
memcpy(tmp, p_buf.first(), (p_len + 1) * sizeof(T));
allocator_deallocate(p_buf.second(), p_buf.first(), oc + 1);
}
tmp[p_len] = '\0';
p_buf.first() = tmp;
2015-06-04 00:07:57 +02:00
}
2015-05-27 22:43:13 +02:00
2015-08-07 04:12:00 +02:00
T &operator[](Size i) { return p_buf.first()[i]; }
2016-06-23 20:18:35 +02:00
T const &operator[](Size i) const { return p_buf.first()[i]; }
2015-05-27 22:43:13 +02:00
2015-08-07 04:12:00 +02:00
T &at(Size i) { return p_buf.first()[i]; }
2016-06-23 20:18:35 +02:00
T const &at(Size i) const { return p_buf.first()[i]; }
2015-05-27 22:43:13 +02:00
2015-08-07 04:12:00 +02:00
T &front() { return p_buf.first()[0]; }
2016-06-23 20:18:35 +02:00
T const &front() const { return p_buf.first()[0]; };
2015-05-27 22:43:13 +02:00
2015-08-07 04:12:00 +02:00
T &back() { return p_buf.first()[size() - 1]; }
2016-06-23 20:18:35 +02:00
T const &back() const { return p_buf.first()[size() - 1]; }
2015-05-27 23:15:24 +02:00
Value *data() { return p_buf.first(); }
2016-06-23 20:18:35 +02:00
Value const *data() const { return p_buf.first(); }
2015-05-29 02:02:19 +02:00
Size size() const {
return p_len;
2015-06-04 00:07:57 +02:00
}
2015-05-29 02:02:19 +02:00
Size capacity() const {
return p_cap;
2015-06-04 00:07:57 +02:00
}
2015-05-29 02:02:19 +02:00
2015-08-07 04:12:00 +02:00
void advance(Size s) { p_len += s; }
Size length() const {
/* TODO: unicode */
return size();
}
2015-06-04 00:07:57 +02:00
bool empty() const { return (size() == 0); }
2015-05-29 02:02:19 +02:00
2015-07-18 18:01:11 +02:00
Value *disown() {
Pointer r = p_buf.first();
p_buf.first() = nullptr;
p_len = p_cap = 0;
2016-07-02 05:56:23 +02:00
return reinterpret_cast<Value *>(r);
2015-07-18 18:01:11 +02:00
}
2015-06-04 23:57:06 +02:00
void push(T v) {
reserve(p_len + 1);
p_buf.first()[p_len++] = v;
p_buf.first()[p_len] = '\0';
2015-06-04 00:07:57 +02:00
}
2015-05-29 02:02:19 +02:00
StringBase &append(ConstRange r) {
if (!r.size()) {
return *this;
}
reserve(p_len + r.size());
memcpy(p_buf.first() + p_len, &r[0], r.size());
p_len += r.size();
p_buf.first()[p_len] = '\0';
2015-06-04 00:07:57 +02:00
return *this;
}
2015-05-27 22:43:13 +02:00
StringBase &append(Size n, T c) {
if (!n) {
return *this;
}
reserve(p_len + n);
for (Size i = 0; i < n; ++i) {
p_buf.first()[p_len + i] = c;
}
p_len += n;
p_buf.first()[p_len] = '\0';
2015-06-04 00:07:57 +02:00
return *this;
}
2015-05-27 23:05:51 +02:00
template<typename R, typename = EnableIf<
IsInputRange<R> && IsConvertible<RangeReference<R>, Value> &&
!IsConvertible<R, ConstRange>
>> StringBase &append(R range) {
Size nadd = 0;
for (; !range.empty(); range.pop_front()) {
reserve(p_len + nadd + 1);
p_buf.first()[p_len + nadd++] = range.front();
}
p_len += nadd;
p_buf.first()[p_len] = '\0';
2015-06-04 00:07:57 +02:00
return *this;
}
StringBase &operator+=(ConstRange r) {
return append(r);
2015-06-04 00:07:57 +02:00
}
2015-06-10 01:16:25 +02:00
StringBase &operator+=(T c) {
2016-05-22 16:31:11 +02:00
return append(1, c);
}
template<typename R>
2016-06-23 20:18:35 +02:00
StringBase &operator+=(R const &v) {
return append(v);
}
int compare(ConstRange r) const {
return iter().compare(r);
2015-06-14 03:36:20 +02:00
}
2015-06-26 22:01:16 +02:00
Range iter() {
return Range(p_buf.first(), size());
2015-06-04 00:07:57 +02:00
}
2015-06-26 22:01:16 +02:00
ConstRange iter() const {
return ConstRange(p_buf.first(), size());
2015-05-27 23:05:51 +02:00
}
2015-06-26 22:01:16 +02:00
ConstRange citer() const {
return ConstRange(p_buf.dfirst(), size());
2015-06-09 22:18:43 +02:00
}
Range iter_cap() {
return Range(p_buf.first(), capacity());
}
2015-06-04 00:07:57 +02:00
void swap(StringBase &v) {
detail::swap_adl(p_len, v.p_len);
detail::swap_adl(p_cap, v.p_cap);
detail::swap_adl(p_buf.first(), v.p_buf.first());
if (AllocatorPropagateOnContainerSwap<A>) {
detail::swap_adl(p_buf.second(), v.p_buf.second());
}
2015-05-28 20:58:05 +02:00
}
Size to_hash() const {
2015-06-26 22:01:16 +02:00
return iter().to_hash();
}
2015-06-12 21:13:27 +02:00
A get_allocator() const {
return p_buf.second();
2015-06-12 21:13:27 +02:00
}
2015-06-04 00:07:57 +02:00
};
2015-06-08 01:55:08 +02:00
using String = StringBase<char>;
2015-06-04 00:07:57 +02:00
2015-07-18 02:02:13 +02:00
/* string literals */
inline namespace literals {
inline namespace string_literals {
2016-06-23 20:18:35 +02:00
inline String operator "" _s(char const *str, Size len) {
return String(ConstCharRange(str, len));
2015-07-18 02:02:13 +02:00
}
2016-06-23 20:18:35 +02:00
inline ConstCharRange operator "" _S(char const *str, Size len) {
return ConstCharRange(str, len);
}
}
}
2015-07-18 02:02:13 +02:00
namespace detail {
template<
typename T, bool = IsConvertible<T, ConstCharRange>,
bool = IsConvertible<T, char>
>
struct ConcatPut;
template<typename T, bool B>
struct ConcatPut<T, true, B> {
template<typename R>
static bool put(R &sink, ConstCharRange v) {
return v.size() && (sink.put_n(&v[0], v.size()) == v.size());
}
};
template<typename T>
struct ConcatPut<T, false, true> {
template<typename R>
static bool put(R &sink, char v) {
return sink.put(v);
}
};
}
template<typename R, typename T, typename F>
2016-06-23 20:18:35 +02:00
bool concat(R &&sink, T const &v, ConstCharRange sep, F func) {
2015-07-13 21:07:14 +02:00
auto range = ostd::iter(v);
if (range.empty()) {
return true;
}
2015-06-04 00:07:57 +02:00
for (;;) {
if (!detail::ConcatPut<
decltype(func(range.front()))
>::put(sink, func(range.front()))) {
return false;
}
2015-06-04 00:07:57 +02:00
range.pop_front();
if (range.empty()) {
break;
}
sink.put_n(&sep[0], sep.size());
2015-06-04 00:07:57 +02:00
}
return true;
2015-06-04 00:07:57 +02:00
}
2015-05-28 20:58:05 +02:00
template<typename R, typename T>
2016-06-23 20:18:35 +02:00
bool concat(R &&sink, T const &v, ConstCharRange sep = " ") {
2015-07-13 21:07:14 +02:00
auto range = ostd::iter(v);
if (range.empty()) {
return true;
}
2015-06-04 00:07:57 +02:00
for (;;) {
ConstCharRange ret = range.front();
if (!ret.size() || (sink.put_n(&ret[0], ret.size()) != ret.size())) {
return false;
}
2015-06-04 00:07:57 +02:00
range.pop_front();
if (range.empty()) {
break;
}
sink.put_n(&sep[0], sep.size());
2015-05-28 20:58:05 +02:00
}
return true;
2015-06-04 00:07:57 +02:00
}
template<typename R, typename T, typename F>
bool concat(R &&sink, std::initializer_list<T> v, ConstCharRange sep, F func) {
return concat(sink, ostd::iter(v), sep, func);
2015-07-12 16:55:41 +02:00
}
template<typename R, typename T>
2015-07-25 03:07:51 +02:00
bool concat(R &&sink, std::initializer_list<T> v, ConstCharRange sep = " ") {
return concat(sink, ostd::iter(v), sep);
2015-06-04 00:07:57 +02:00
}
namespace detail {
template<typename R>
struct TostrRange: OutputRange<TostrRange<R>, char> {
TostrRange() = delete;
TostrRange(R &out): p_out(out), p_written(0) {}
bool put(char v) {
bool ret = p_out.put(v);
p_written += ret;
return ret;
}
2016-06-23 20:18:35 +02:00
Size put_n(char const *v, Size n) {
Size ret = p_out.put_n(v, n);
p_written += ret;
return ret;
}
Size put_string(ConstCharRange r) {
return put_n(&r[0], r.size());
}
Size get_written() const { return p_written; }
private:
R &p_out;
Size p_written;
};
template<typename T, typename R>
static auto test_stringify(int) ->
BoolConstant<IsSame<decltype(declval<T>().stringify()), String>>;
template<typename T, typename R>
2016-06-23 20:18:35 +02:00
static True test_stringify(decltype(declval<T const &>().to_string
(declval<R &>())) *);
template<typename, typename>
static False test_stringify(...);
template<typename T, typename R>
constexpr bool StringifyTest = decltype(test_stringify<T, R>(0))::value;
2015-07-11 20:54:51 +02:00
template<typename T>
static True test_iterable(decltype(ostd::iter(declval<T>())) *);
template<typename>
static False test_iterable(...);
template<typename T>
constexpr bool IterableTest = decltype(test_iterable<T>(0))::value;
2015-06-04 00:07:57 +02:00
}
2015-05-28 03:38:52 +02:00
template<typename T, typename = void>
struct ToString;
template<typename T>
struct ToString<T, EnableIf<detail::IterableTest<T>>> {
2015-07-11 21:21:49 +02:00
using Argument = RemoveCv<RemoveReference<T>>;
2015-06-08 01:55:08 +02:00
using Result = String;
2015-05-28 03:38:52 +02:00
2016-06-23 20:18:35 +02:00
String operator()(T const &v) const {
2015-06-04 00:07:57 +02:00
String ret("{");
auto x = appender<String>();
if (concat(x, ostd::iter(v), ", ", ToString<
RemoveConst<RemoveReference<
2015-07-13 21:07:14 +02:00
RangeReference<decltype(ostd::iter(v))>
>>
>())) {
ret += x.get();
}
2015-06-04 00:07:57 +02:00
ret += "}";
return ret;
2015-06-04 00:07:57 +02:00
}
};
2015-05-28 20:58:05 +02:00
template<typename T>
struct ToString<T, EnableIf<
detail::StringifyTest<T, detail::TostrRange<AppenderRange<String>>>
>> {
2015-07-11 21:21:49 +02:00
using Argument = RemoveCv<RemoveReference<T>>;
using Result = String;
2015-05-28 03:38:52 +02:00
2016-06-23 20:18:35 +02:00
String operator()(T const &v) const {
auto app = appender<String>();
detail::TostrRange<AppenderRange<String>> sink(app);
if (!v.to_string(sink)) {
return String();
}
return move(app.get());
2015-06-04 00:07:57 +02:00
}
};
2015-05-27 23:15:24 +02:00
2015-06-04 00:07:57 +02:00
namespace detail {
2015-06-04 23:57:06 +02:00
template<typename T>
2016-06-23 20:18:35 +02:00
void str_printf(String &s, char const *fmt, T v) {
2015-06-04 00:07:57 +02:00
char buf[256];
int n = snprintf(buf, sizeof(buf), fmt, v);
s.clear();
s.reserve(n);
if (n >= int(sizeof(buf))) {
snprintf(s.data(), n + 1, fmt, v);
} else if (n > 0) {
memcpy(s.data(), buf, n + 1);
} else {
s.clear();
2015-05-28 02:44:21 +02:00
}
2016-07-02 05:56:23 +02:00
*reinterpret_cast<Size *>(&s) = n;
2015-05-28 02:26:48 +02:00
}
2015-06-04 00:07:57 +02:00
}
2015-05-28 02:26:48 +02:00
template<>
struct ToString<bool> {
2015-06-08 01:55:08 +02:00
using Argument = bool;
using Result = String;
2015-06-04 00:07:57 +02:00
String operator()(bool b) {
return b ? "true" : "false";
2015-06-04 00:07:57 +02:00
}
};
template<>
struct ToString<char> {
2015-06-08 01:55:08 +02:00
using Argument = char;
using Result = String;
2015-06-04 00:07:57 +02:00
String operator()(char c) {
String ret;
ret.push(c);
return ret;
2015-06-04 00:07:57 +02:00
}
};
2015-07-13 21:08:55 +02:00
#define OSTD_TOSTR_NUM(T, fmt) \
template<> \
struct ToString<T> { \
2015-06-08 01:55:08 +02:00
using Argument = T; \
using Result = String; \
2015-06-04 23:57:06 +02:00
String operator()(T v) { \
2015-06-04 00:07:57 +02:00
String ret; \
detail::str_printf(ret, fmt, v); \
return ret; \
2015-06-04 00:07:57 +02:00
} \
};
2015-07-13 21:08:55 +02:00
OSTD_TOSTR_NUM(sbyte, "%d")
OSTD_TOSTR_NUM(int, "%d")
OSTD_TOSTR_NUM(int &, "%d")
OSTD_TOSTR_NUM(long, "%ld")
OSTD_TOSTR_NUM(float, "%f")
OSTD_TOSTR_NUM(double, "%f")
OSTD_TOSTR_NUM(byte, "%u")
OSTD_TOSTR_NUM(uint, "%u")
OSTD_TOSTR_NUM(ulong, "%lu")
OSTD_TOSTR_NUM(llong, "%lld")
OSTD_TOSTR_NUM(ullong, "%llu")
OSTD_TOSTR_NUM(ldouble, "%Lf")
#undef OSTD_TOSTR_NUM
2015-06-04 00:07:57 +02:00
template<typename T>
struct ToString<T *> {
2015-06-08 01:55:08 +02:00
using Argument = T *;
using Result = String;
2015-06-04 00:07:57 +02:00
String operator()(Argument v) {
String ret;
detail::str_printf(ret, "%p", v);
return ret;
2015-05-28 20:58:05 +02:00
}
2015-06-04 00:07:57 +02:00
};
2015-05-28 20:58:05 +02:00
template<>
struct ToString<char const *> {
2016-06-23 20:18:35 +02:00
using Argument = char const *;
2015-07-10 02:17:08 +02:00
using Result = String;
2016-06-23 20:18:35 +02:00
String operator()(char const *s) {
2015-07-10 02:17:08 +02:00
return String(s);
}
};
template<>
struct ToString<char *> {
2015-07-11 21:18:46 +02:00
using Argument = char *;
using Result = String;
String operator()(char *s) {
return String(s);
}
};
template<>
struct ToString<String> {
using Argument = String;
2015-06-08 01:55:08 +02:00
using Result = String;
2016-06-23 20:18:35 +02:00
String operator()(Argument const &s) {
2015-06-04 00:07:57 +02:00
return s;
}
};
template<>
struct ToString<CharRange> {
2015-07-21 23:06:23 +02:00
using Argument = CharRange;
using Result = String;
2016-06-23 20:18:35 +02:00
String operator()(Argument const &s) {
return String(s);
}
};
template<>
struct ToString<ConstCharRange> {
2015-07-21 23:06:23 +02:00
using Argument = ConstCharRange;
using Result = String;
2016-06-23 20:18:35 +02:00
String operator()(Argument const &s) {
return String(s);
}
};
template<typename T, typename U>
struct ToString<Pair<T, U>> {
using Argument = Pair<T, U>;
2015-06-08 01:55:08 +02:00
using Result = String;
2016-06-23 20:18:35 +02:00
String operator()(Argument const &v) {
2015-06-04 00:07:57 +02:00
String ret("{");
2016-03-28 00:49:27 +02:00
ret += ToString<RemoveCv<RemoveReference<T>>>()(v.first);
2015-06-04 00:07:57 +02:00
ret += ", ";
2016-03-28 00:49:27 +02:00
ret += ToString<RemoveCv<RemoveReference<U>>>()(v.second);
ret += "}";
return ret;
}
};
namespace detail {
template<Size I, Size N>
struct TupleToString {
template<typename T>
2016-06-23 20:18:35 +02:00
static void append(String &ret, T const &tup) {
2016-03-28 00:49:27 +02:00
ret += ", ";
ret += ToString<RemoveCv<RemoveReference<
decltype(ostd::get<I>(tup))
>>>()(ostd::get<I>(tup));
2016-03-28 00:49:27 +02:00
TupleToString<I + 1, N>::append(ret, tup);
}
};
template<Size N>
struct TupleToString<N, N> {
template<typename T>
2016-06-23 20:18:35 +02:00
static void append(String &, T const &) {}
2016-03-28 00:49:27 +02:00
};
template<Size N>
struct TupleToString<0, N> {
template<typename T>
2016-06-23 20:18:35 +02:00
static void append(String &ret, T const &tup) {
2016-03-28 00:49:27 +02:00
ret += ToString<RemoveCv<RemoveReference<
decltype(ostd::get<0>(tup))
>>>()(ostd::get<0>(tup));
2016-03-28 00:49:27 +02:00
TupleToString<1, N>::append(ret, tup);
}
};
}
template<typename ...T>
struct ToString<Tuple<T...>> {
2016-03-28 00:49:27 +02:00
using Argument = Tuple<T...>;
using Result = String;
2016-06-23 20:18:35 +02:00
String operator()(Argument const &v) {
2016-03-28 00:49:27 +02:00
String ret("{");
detail::TupleToString<0, sizeof...(T)>::append(ret, v);
2015-06-04 00:07:57 +02:00
ret += "}";
return ret;
2015-05-28 02:26:48 +02:00
}
2015-06-04 00:07:57 +02:00
};
template<typename T>
2016-06-23 20:18:35 +02:00
typename ToString<T>::Result to_string(T const &v) {
2016-03-28 00:49:27 +02:00
return ToString<RemoveCv<RemoveReference<T>>>()(v);
2015-05-27 22:43:13 +02:00
}
2015-06-04 23:57:06 +02:00
template<typename T>
String to_string(std::initializer_list<T> init) {
2015-07-11 22:54:58 +02:00
return to_string(iter(init));
2015-06-04 00:07:57 +02:00
}
2016-05-07 18:25:40 +02:00
/* TODO: rvalue ref versions for rhs when we have efficient prepend */
2016-06-23 20:18:35 +02:00
inline String operator+(String const &lhs, ConstCharRange rhs) {
2016-05-07 18:25:40 +02:00
String ret(lhs); ret += rhs; return ret;
}
2016-06-23 20:18:35 +02:00
inline String operator+(ConstCharRange lhs, String const &rhs) {
String ret(lhs); ret += rhs; return ret;
2016-05-07 18:25:40 +02:00
}
2016-06-23 20:18:35 +02:00
inline String operator+(String const &lhs, char rhs) {
2016-05-07 18:25:40 +02:00
String ret(lhs); ret += rhs; return ret;
}
2016-06-23 20:18:35 +02:00
inline String operator+(char lhs, String const &rhs) {
String ret(lhs); ret += rhs; return ret;
}
inline String operator+(String &&lhs, ConstCharRange rhs) {
2016-05-07 18:25:40 +02:00
String ret(move(lhs)); ret += rhs; return ret;
}
inline String operator+(String &&lhs, char rhs) {
String ret(move(lhs)); ret += rhs; return ret;
2016-05-07 18:25:40 +02:00
}
template<typename R>
struct TempCString {
private:
2016-03-24 22:52:16 +01:00
RemoveCv<RangeValue<R>> *p_buf;
bool p_allocated;
public:
TempCString() = delete;
2016-06-23 20:18:35 +02:00
TempCString(TempCString const &) = delete;
TempCString(TempCString &&s): p_buf(s.p_buf), p_allocated(s.p_allocated) {
s.p_buf = nullptr;
s.p_allocated = false;
}
2016-03-24 22:52:16 +01:00
TempCString(R input, RemoveCv<RangeValue<R>> *sbuf, Size bufsize)
: p_buf(nullptr), p_allocated(false) {
if (input.empty()) {
return;
}
if (input.size() >= bufsize) {
2016-03-24 22:52:16 +01:00
p_buf = new RemoveCv<RangeValue<R>>[input.size() + 1];
p_allocated = true;
} else {
p_buf = sbuf;
}
p_buf[input.copy(p_buf)] = '\0';
}
~TempCString() {
if (p_allocated) {
delete[] p_buf;
}
}
2016-06-23 20:18:35 +02:00
TempCString &operator=(TempCString const &) = delete;
TempCString &operator=(TempCString &&s) {
swap(s);
return *this;
}
2016-06-23 20:18:35 +02:00
operator RemoveCv<RangeValue<R>> const *() const { return p_buf; }
RemoveCv<RangeValue<R>> const *get() const { return p_buf; }
void swap(TempCString &s) {
detail::swap_adl(p_buf, s.p_buf);
detail::swap_adl(p_allocated, s.p_allocated);
}
};
template<typename R>
inline TempCString<R> to_temp_cstr(
R input, RemoveCv<RangeValue<R>> *buf, Size bufsize
) {
return TempCString<R>(input, buf, bufsize);
}
2015-07-13 21:07:14 +02:00
} /* namespace ostd */
2015-06-04 00:07:57 +02:00
2016-02-07 22:17:15 +01:00
#endif