// Copyright (c) 2016-2018 Kiwano - Nomango // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #pragma once #include #include #include #include #include #include #include namespace kiwano { template class basic_string; using string = basic_string; using wstring = basic_string; // String for kiwano using String = wstring; } namespace kiwano { // // basic_string<> // Lightweight std::basic_string<>-like class // When using basic_string<> with a c-style string (char* or wchar_t*), constructor and operator=() just hold // a pointer to the character array but don't copy its content, considering performance issues. // Use assign() and basic_string<>::cstr() to work fine with c-style strings. // template class basic_string { public: // Iterator template struct iterator_impl { using iterator_category = typename std::iterator_traits<_Ty*>::iterator_category; using value_type = typename std::iterator_traits<_Ty*>::value_type; using difference_type = typename std::iterator_traits<_Ty*>::difference_type; using pointer = typename std::iterator_traits<_Ty*>::pointer; using reference = typename std::iterator_traits<_Ty*>::reference; // disable warning 4996 using _Unchecked_type = _Ty; inline iterator_impl(pointer base = nullptr) : base_(base) {} inline reference operator*() const { return *base_; } inline pointer base() const { return base_; } inline iterator_impl& operator++() { ++base_; return (*this); } inline iterator_impl operator++(int) { iterator_impl old = (*this); ++(*this); return old; } inline iterator_impl& operator--() { --base_; return (*this); } inline iterator_impl operator--(int) { iterator_impl old = (*this); --(*this); return old; } inline const iterator_impl operator+(difference_type off) const { return iterator_impl(base_ + off); } inline const iterator_impl operator-(difference_type off) const { return iterator_impl(base_ - off); } inline iterator_impl& operator+=(difference_type off) { base_ += off; return (*this); } inline iterator_impl& operator-=(difference_type off) { base_ -= off; return (*this); } inline difference_type operator-(iterator_impl const& other) const { return base_ - other.base_; } inline bool operator==(iterator_impl const& other) const { return base_ == other.base_; } inline bool operator!=(iterator_impl const& other) const { return !(*this == other); } inline bool operator<(iterator_impl const& other) const { return base_ < other.base_; } inline bool operator<=(iterator_impl const& other) const { return base_ <= other.base_; } inline bool operator>(iterator_impl const& other) const { return base_ > other.base_; } inline bool operator>=(iterator_impl const& other) const { return base_ >= other.base_; } inline reference operator[](difference_type off) { return *(base_ + off); } inline const reference operator[](difference_type off) const { return *(base_ + off); } inline operator bool() const { return base_ != nullptr; } private: pointer base_{ nullptr }; }; public: using value_type = _CharTy; using char_type = value_type; using size_type = size_t; using reference = value_type &; using const_reference = const value_type &; using iterator = iterator_impl; using const_iterator = iterator_impl; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; using traits_type = std::char_traits; using allocator_type = std::allocator; basic_string(); basic_string(const char_type* cstr, bool const_str = true); basic_string(const char_type* cstr, size_type count); basic_string(size_type count, char_type ch); basic_string(std::basic_string const& str); basic_string(basic_string const& rhs); basic_string(basic_string const& rhs, size_type pos, size_type count = npos); basic_string(basic_string && rhs) noexcept; ~basic_string(); template basic_string(_Iter first, _Iter last) : basic_string() { assign_iter(first, last); } inline const char_type* c_str() const { return empty() ? empty_cstr : const_str_; } inline const char_type* data() const { return empty() ? empty_cstr : const_str_; } inline char_type at(size_t i) const { return (*this)[i]; } inline size_type size() const { return size_; } inline size_type length() const { return size(); } inline size_type capacity() const { return capacity_; } inline size_type max_size() const { return (static_cast(-1) / sizeof(value_type)); } inline bool empty() const { return size_ == 0; } inline void clear() { discard_const_data(); if (str_) { str_[0] = value_type(); } size_ = 0; } void reserve(const size_type new_cap = 0); inline void resize(const size_type new_size, const char_type ch = value_type()) { check_operability(); if (new_size < size_) str_[size_ = new_size] = value_type(); else append(new_size - size_, ch); } int compare(const char_type* const str) const; inline int compare(basic_string const& str) const { return compare(str.c_str()); } basic_string& append(size_type count, char_type ch); basic_string& append(const char_type* cstr, size_type count); basic_string& append(basic_string const& other, size_type pos, size_type count = npos); inline basic_string& append(const char_type* cstr) { return append(cstr, traits_type::length(cstr)); } inline basic_string& append(basic_string const& other) { return append(other.const_str_, 0, npos); } inline basic_string& append(std::basic_string const& other) { return append(other.c_str()); } size_type find(const char_type ch, size_type offset = 0) const; size_type find(const char_type* const str, size_type offset, size_type count) const; inline size_type find(basic_string const& str, size_type offset = 0) const { return find(str.c_str(), offset, str.size()); } inline size_type find(const char_type* const str, size_type offset = 0) const { return find(str, offset, traits_type::length(str)); } size_type find_first_of(const char_type* const str, size_type offset, size_type count) const; inline size_type find_first_of(const char_type ch, size_type offset = 0) const { return find(ch, offset); } inline size_type find_first_of(basic_string const& str, size_type offset = 0) const { return find_first_of(str.c_str(), offset, str.size()); } inline size_type find_first_of(const char_type* const str, size_type offset = 0) const { return find_first_of(str, offset, traits_type::length(str)); } size_type find_last_of(const char_type ch, size_type pos = npos) const; size_type find_last_of(const char_type* const str, size_type pos, size_type count) const; inline size_type find_last_of(basic_string const& str, size_type pos = npos) const { return find_first_of(str.c_str(), pos, str.size()); } inline size_type find_last_of(const char_type* const str, size_type pos = npos) const { return find_first_of(str, pos, traits_type::length(str)); } basic_string& replace(size_type pos, size_type count, const char_type* cstr, size_type count2); basic_string& replace(size_type pos, size_type count, size_type count2, const char_type ch); inline basic_string& replace(size_type pos, size_type count, const basic_string& str) { return replace(pos, count, str.c_str(), str.size()); } inline basic_string& replace(size_type pos, size_type count, const char_type* cstr) { return replace(pos, count, cstr, traits_type::length(cstr)); } inline basic_string& replace(const_iterator first, const_iterator last, const basic_string& str) { return replace(first, last, str.c_str(), str.size()); } inline basic_string& replace(const_iterator first, const_iterator last, const char_type* cstr) { return replace(first, last, cstr, traits_type::length(cstr)); } inline basic_string& replace(const_iterator first, const_iterator last, const char_type* cstr, size_type count) { return replace(first - cbegin(), last - first, cstr, count); } inline basic_string& replace(const_iterator first, const_iterator last, size_type count2, const char_type ch) { return replace(first - cbegin(), last - first, count2, ch); } basic_string& assign(size_type count, const char_type ch); basic_string& assign(const char_type* cstr, size_type count); inline basic_string& assign(const char_type* cstr) { basic_string(cstr, false).swap(*this); return *this; } inline basic_string& assign(basic_string const& rhs) { basic_string{ rhs }.swap(*this); return *this; } inline basic_string& assign(std::basic_string const& rhs) { basic_string{ rhs }.swap(*this); return *this; } basic_string& assign(basic_string const& rhs, size_type pos, size_type count = npos); template inline basic_string& assign(_Iter first, _Iter last) { assign_iter(first, last); return(*this); } basic_string& erase(size_type offset = 0, size_type count = npos); iterator erase(const const_iterator where) { size_type off = where - cbegin(); erase(off, 1); return begin().base() + off; } iterator erase(const const_iterator first, const const_iterator last) { size_type off = first - cbegin(); erase(first - cbegin(), last - first); return begin().base() + off; } basic_string substr(size_type pos = 0, size_type count = npos) const { return basic_string(*this, pos, count); } basic_string& insert(size_type index, size_type count, char_type ch); basic_string& insert(size_type index, const char_type* s, size_type count); basic_string& insert(size_type index, const basic_string& str, size_type off, size_type count = npos); inline basic_string& insert(size_type index, const char_type* s) { return insert(index, s, traits_type::length(s)); } inline basic_string& insert(size_type index, const basic_string& str) { return insert(index, str, 0, str.size()); } inline iterator insert(const_iterator pos, size_type count, char_type ch) { size_type off = pos - cbegin(); insert(off, count, ch); return begin().base() + off; } inline iterator insert(const_iterator pos, char_type ch) { return insert(pos, 1, ch); } inline void push_back(const char_type ch) { append(1, ch); } inline char_type pop_back() { if (empty()) throw std::out_of_range("pop_back() called on empty string"); check_operability(); char_type ch = str_[--size_]; str_[size_] = value_type(); return ch; } size_type copy(char_type* cstr, size_type count, size_type pos = 0) const; void swap(basic_string& rhs) noexcept; size_t hash() const; public: static basic_string parse(int val); static basic_string parse(unsigned int val); static basic_string parse(long val); static basic_string parse(unsigned long val); static basic_string parse(long long val); static basic_string parse(unsigned long long val); static basic_string parse(float val); static basic_string parse(double val); static basic_string parse(long double val); template static basic_string format(const char_type* fmt, _Args&&... args); static inline basic_string cstr(const char_type* cstr) { return basic_string(cstr, false); } public: inline iterator begin() { check_operability(); return iterator(str_); } inline const_iterator begin() const { return const_iterator(const_str_); } inline const_iterator cbegin() const { return begin(); } inline iterator end() { check_operability(); return iterator(str_ + size_); } inline const_iterator end() const { return const_iterator(const_str_ + size_); } inline const_iterator cend() const { return end(); } inline reverse_iterator rbegin() { check_operability(); return reverse_iterator(end()); } inline const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } inline const_reverse_iterator crbegin() const { return rbegin(); } inline reverse_iterator rend() { check_operability(); return reverse_iterator(begin()); } inline const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } inline const_reverse_iterator crend() const { return rend(); } inline reference front() { if (empty()) throw std::out_of_range("front() called on empty string"); check_operability(); return str_[0]; } inline const_reference front() const { if (empty()) throw std::out_of_range("front() called on empty string"); return const_str_[0]; } inline reference back() { if (empty()) throw std::out_of_range("back() called on empty string"); check_operability(); return str_[size_ - 1]; } inline const_reference back() const { if (empty()) throw std::out_of_range("back() called on empty string"); return const_str_[size_ - 1]; } public: inline char_type operator[](size_type off) const { if (off >= size_) throw std::out_of_range("string subscript out of range"); return const_str_[off]; } inline char_type& operator[](size_type off) { if (off >= size_) throw std::out_of_range("string subscript out of range"); check_operability(); return str_[off]; } public: inline const basic_string operator+(const char_type ch) const { return basic_string{ *this }.append(1, ch); } inline const basic_string operator+(const char_type* cstr) const { return basic_string{ *this }.append(cstr); } inline const basic_string operator+(basic_string const& rhs) const { return basic_string{ *this }.append(rhs); } inline basic_string& operator+=(const char_type ch) { return append(1, ch); } inline basic_string& operator+=(const char_type* cstr) { return append(cstr); } inline basic_string& operator+=(basic_string const& rhs) { return append(rhs); } public: inline basic_string& operator=(const char_type* cstr) { if (const_str_ != cstr) basic_string{ cstr }.swap(*this); return *this; } inline basic_string& operator=(std::basic_string const& rhs) { basic_string{ rhs }.swap(*this); return *this; } inline basic_string& operator=(basic_string const& rhs) { if (this != &rhs) basic_string{ rhs }.swap(*this); return *this; } inline basic_string& operator=(basic_string && rhs) noexcept { if (this != &rhs) basic_string{ rhs }.swap(*this); return *this; } public: static const size_type npos; static const char_type empty_cstr[1]; static inline allocator_type& get_allocator() { static allocator_type allocator_; return allocator_; } private: char_type* allocate(size_type count); void deallocate(char_type*& ptr, size_type count); void destroy(); void discard_const_data(); void check_operability(); void check_offset(size_type offset) const { if (offset > size()) throw std::out_of_range("invalid string position"); } size_type clamp_suffix_size(size_type off, size_type count) const { return std::min(size() - off, count); } template void assign_iter(_Iter first, _Iter last) { size_type diff = static_cast(std::distance(first, last)); if (diff == 0) return; discard_const_data(); if (diff > capacity_) { destroy(); str_ = allocate(diff + 1); capacity_ = diff; } size_ = diff; for (size_type index = 0; first != last; ++first, ++index) { traits_type::assign(str_[index], traits_type::to_char_type(*first)); } traits_type::assign(str_[size_], value_type()); } private: union { struct { value_type* str_; }; struct { const value_type* const_str_; }; }; size_type size_; size_type capacity_; const bool operable_; }; // static members template const typename basic_string<_CharTy>::size_type basic_string<_CharTy>::npos = static_cast::size_type>(-1); template const typename basic_string<_CharTy>::char_type basic_string<_CharTy>::empty_cstr[1] = { 0 }; // // operator== for basic_string // template inline bool operator==(basic_string<_CharTy> const& lhs, basic_string<_CharTy> const& rhs) { return lhs.compare(rhs) == 0; } template inline bool operator==(const typename basic_string<_CharTy>::char_type* lhs, basic_string<_CharTy> const& rhs) { return rhs.compare(lhs) == 0; } template inline bool operator==(basic_string<_CharTy> const& lhs, const typename basic_string<_CharTy>::char_type* rhs) { return lhs.compare(rhs) == 0; } // // operator!= for basic_string // template inline bool operator!=(basic_string<_CharTy> const& lhs, basic_string<_CharTy> const& rhs) { return lhs.compare(rhs) != 0; } template inline bool operator!=(const typename basic_string<_CharTy>::char_type* lhs, basic_string<_CharTy> const& rhs) { return rhs.compare(lhs) != 0; } template inline bool operator!=(basic_string<_CharTy> const& lhs, const typename basic_string<_CharTy>::char_type* rhs) { return lhs.compare(rhs) != 0; } // // operator+ for basic_string // template inline basic_string<_CharTy> operator+(const typename basic_string<_CharTy>::char_type* lhs, basic_string<_CharTy> const& rhs) { return basic_string<_CharTy>{ lhs } + rhs; } // // operator<> for basic_string // template inline bool operator<(basic_string<_CharTy> const& lhs, basic_string<_CharTy> const& rhs) { return lhs.compare(rhs) < 0; } template inline bool operator>(basic_string<_CharTy> const& lhs, basic_string<_CharTy> const& rhs) { return lhs.compare(rhs) > 0; } template inline bool operator<=(basic_string<_CharTy> const& lhs, basic_string<_CharTy> const& rhs) { return lhs.compare(rhs) <= 0; } template inline bool operator>=(basic_string<_CharTy> const& lhs, basic_string<_CharTy> const& rhs) { return lhs.compare(rhs) >= 0; } // // operator<<>> for basic_string // template std::basic_ostream::char_type>& operator<<(std::basic_ostream::char_type>& os, const basic_string<_CharTy>& str); template std::basic_istream::char_type>& operator>>(std::basic_istream::char_type>& is, basic_string<_CharTy>& str); // // to_string functions // template basic_string<_CharTy> to_basic_string(int val); template basic_string<_CharTy> to_basic_string(unsigned int val); template basic_string<_CharTy> to_basic_string(long val); template basic_string<_CharTy> to_basic_string(unsigned long val); template basic_string<_CharTy> to_basic_string(long long val); template basic_string<_CharTy> to_basic_string(unsigned long long val); template basic_string<_CharTy> to_basic_string(float val); template basic_string<_CharTy> to_basic_string(double val); template basic_string<_CharTy> to_basic_string(long double val); // // format_wstring // template basic_string format_string(const char* const fmt, _Args&&... args); template basic_string format_string(const wchar_t* const fmt, _Args&& ... args); } namespace kiwano { // // details of basic_string // namespace __string_details { template size_t TraitsFind( const typename _Traits::char_type* first, size_t first_size, size_t offset, const typename _Traits::char_type* second, size_t count) { if (count > first_size || offset > first_size - count) { return static_cast(-1); } if (count == 0) { return offset; } const auto matches_end = first + (first_size - count) + 1; for (auto iter = first + offset; ; ++iter) { iter = typename _Traits::find(iter, static_cast(matches_end - iter), *second); if (!iter) { return static_cast(-1); } if (typename _Traits::compare(iter, second, count) == 0) { return static_cast(iter - first); } } } template size_t TraitsFindLastOf( const typename _Traits::char_type* first, const size_t first_size, const size_t pos, const typename _Traits::char_type* second, const size_t count) { if (count != 0 && first_size != 0) { for (auto iter = first + std::min(pos, first_size - 1); ; --iter) { if (typename _Traits::find(second, count, *iter)) { return static_cast(iter - first); } if (iter == first) { break; } } } return static_cast(-1); } } template inline basic_string<_CharTy>::basic_string() : str_(nullptr) , size_(0) , capacity_(0) , operable_(true) { } template inline basic_string<_CharTy>::basic_string(const char_type * cstr, bool const_str) : operable_(!const_str) , size_(0) , capacity_(0) , str_(nullptr) { if (cstr == nullptr) return; if (operable_) { assign(cstr, traits_type::length(cstr)); } else { const_str_ = cstr; size_ = traits_type::length(cstr); capacity_ = size_; } } template inline basic_string<_CharTy>::basic_string(const char_type * cstr, size_type count) : basic_string() { assign(cstr, count); } template inline basic_string<_CharTy>::basic_string(size_type count, char_type ch) : basic_string() { assign(count, ch); } template inline basic_string<_CharTy>::basic_string(basic_string const & rhs) : basic_string(rhs.const_str_, !rhs.operable_) { } template inline basic_string<_CharTy>::basic_string(basic_string const & rhs, size_type pos, size_type count) : basic_string() { assign(rhs, pos, count); } template inline basic_string<_CharTy>::basic_string(std::basic_string const& str) : basic_string(str.c_str(), false) { } template inline basic_string<_CharTy>::basic_string(basic_string && rhs) noexcept : str_(rhs.str_) , size_(rhs.size_) , capacity_(rhs.capacity_) , operable_(rhs.operable_) { rhs.str_ = nullptr; rhs.size_ = rhs.capacity_ = 0; } template inline basic_string<_CharTy>::~basic_string() { destroy(); } template inline basic_string<_CharTy> & basic_string<_CharTy>::assign(size_type count, const char_type ch) { discard_const_data(); if (count != 0) { if (count > capacity_) { destroy(); str_ = allocate(count + 1); capacity_ = count; } size_ = count; traits_type::assign(str_, size_, ch); traits_type::assign(str_[size_], value_type()); } else { clear(); } return (*this); } template inline basic_string<_CharTy> & basic_string<_CharTy>::assign(const char_type * cstr, size_type count) { discard_const_data(); if (cstr && count) { if (count > capacity_) { destroy(); str_ = allocate(count + 1); capacity_ = count; } size_ = count; traits_type::move(str_, cstr, size_); traits_type::assign(str_[size_], value_type()); } else { clear(); } return (*this); } template inline basic_string<_CharTy>& basic_string<_CharTy>::assign(basic_string const& rhs, size_type pos, size_type count) { if (count == 0 || pos > rhs.size()) { clear(); return (*this); } discard_const_data(); count = rhs.clamp_suffix_size(pos, count); if (count > capacity_) { destroy(); str_ = allocate(count + 1); capacity_ = count; } size_ = count; traits_type::move(str_, rhs.begin().base() + pos, size_); traits_type::assign(str_[size_], value_type()); return (*this); } template inline basic_string<_CharTy> & basic_string<_CharTy>::erase(size_type offset, size_type count) { if (count == 0) return (*this); check_offset(offset); check_operability(); count = clamp_suffix_size(offset, count); if (count == 0) { clear(); return (*this); } size_type new_size = size_ - count; iterator erase_at = begin().base() + offset; traits_type::move(erase_at.base(), erase_at.base() + count, new_size - offset + 1); return (*this); } template inline basic_string<_CharTy> & basic_string<_CharTy>::insert(size_type index, size_type count, char_type ch) { if (count == 0) return (*this); if (index >= size()) return append(count, ch); check_operability(); char_type* const old_ptr = str_; const size_type old_size = size_; const size_type old_capacity = capacity_; const size_type suffix_size = old_size - index + 1; size_ = old_size + count; if (size_ > old_capacity) { capacity_ = size_; char_type* new_ptr = allocate(capacity_ + 1); char_type* const insert_at = new_ptr + index; traits_type::move(new_ptr, old_ptr, index); // (0) - (index) traits_type::assign(insert_at, count, ch); // (index) - (index + count) traits_type::move(insert_at + count, old_ptr + index, suffix_size); // (index + count) - (old_size - index) deallocate(str_, old_capacity + 1); str_ = new_ptr; } else { char_type* const insert_at = old_ptr + index; traits_type::move(insert_at + count, old_ptr + index, suffix_size); traits_type::assign(insert_at, count, ch); } return (*this); } template inline basic_string<_CharTy> & basic_string<_CharTy>::insert(size_type index, const char_type * cstr, size_type count) { if (count == 0) return (*this); if (index >= size()) return append(cstr, count); check_operability(); char_type* const old_ptr = str_; const size_type old_size = size_; const size_type old_capacity = capacity_; const size_type suffix_size = old_size - index + 1; size_ = old_size + count; if (size_ > old_capacity) { capacity_ = size_; char_type* new_ptr = allocate(capacity_ + 1); char_type* const insert_at = new_ptr + index; traits_type::move(new_ptr, old_ptr, index); // (0) - (index) traits_type::move(insert_at, cstr, count); // (index) - (index + count) traits_type::move(insert_at + count, old_ptr + index, suffix_size); // (index + count) - (old_size - index) deallocate(str_, old_capacity + 1); str_ = new_ptr; } else { char_type* const insert_at = old_ptr + index; traits_type::move(insert_at + count, old_ptr + index, suffix_size); traits_type::move(insert_at, cstr, count); } return (*this); } template inline basic_string<_CharTy> & basic_string<_CharTy>::insert(size_type index, const basic_string & str, size_type off, size_type count) { if (count == 0 || off > str.size()) return (*this); if (index >= size()) return append(str, off, count); check_operability(); count = clamp_suffix_size(off, count); char_type* const old_ptr = str_; const size_type old_size = size_; const size_type old_capacity = capacity_; const size_type suffix_size = old_size - index + 1; size_ = old_size + count; if (size_ > old_capacity) { capacity_ = size_; char_type* new_ptr = allocate(capacity_ + 1); char_type* const insert_at = new_ptr + index; traits_type::move(new_ptr, old_ptr, index); // (0) - (index) traits_type::move(insert_at, str.begin().base() + off, count); // (index) - (index + count) traits_type::move(insert_at + count, old_ptr + index, suffix_size); // (index + count) - (old_size - index) deallocate(str_, old_capacity + 1); str_ = new_ptr; } else { char_type* const insert_at = old_ptr + index; traits_type::move(insert_at + count, old_ptr + index, suffix_size); traits_type::move(insert_at, str.begin().base() + off, count); } return (*this); } template inline basic_string<_CharTy> & basic_string<_CharTy>::append(size_type count, char_type ch) { check_operability(); size_t new_size = size_ + count; size_t new_cap = new_size + 1; char_type* new_str = allocate(new_cap); traits_type::move(new_str, str_, size_); traits_type::assign(new_str + size_, count, ch); traits_type::assign(new_str[new_size], value_type()); destroy(); str_ = new_str; size_ = new_size; capacity_ = new_cap; return (*this); } template inline basic_string<_CharTy> & basic_string<_CharTy>::append(const char_type * cstr, size_type count) { check_operability(); size_t new_size = size_ + count; size_t new_cap = new_size + 1; char_type* new_str = allocate(new_cap); traits_type::move(new_str, str_, size_); traits_type::move(new_str + size_, cstr, count); traits_type::assign(new_str[new_size], value_type()); destroy(); str_ = new_str; size_ = new_size; capacity_ = new_cap; return (*this); } template inline basic_string<_CharTy> & basic_string<_CharTy>::append(basic_string const & other, size_type pos, size_type count) { check_operability(); if (pos >= other.size()) return (*this); count = other.clamp_suffix_size(pos, count); size_t new_size = size_ + count; size_t new_cap = new_size + 1; char_type* new_str = allocate(new_cap); traits_type::move(new_str, str_, size_); traits_type::move(new_str + size_, other.begin().base() + pos, count); traits_type::assign(new_str[new_size], value_type()); destroy(); str_ = new_str; size_ = new_size; capacity_ = new_cap; return (*this); } template inline void basic_string<_CharTy>::reserve(const size_type new_cap) { if (new_cap <= capacity_) return; check_operability(); char_type* new_str = allocate(new_cap); traits_type::move(new_str, str_, capacity_); destroy(); str_ = new_str; capacity_ = new_cap; } template inline size_t basic_string<_CharTy>::hash() const { static size_t fnv_prime = 16777619U; size_t fnv_offset_basis = 2166136261U; for (size_t index = 0; index < size_; ++index) { fnv_offset_basis ^= static_cast(const_str_[index]); fnv_offset_basis *= fnv_prime; } return fnv_offset_basis; } template inline int basic_string<_CharTy>::compare(const char_type * const str) const { size_type count1 = size(); size_type count2 = traits_type::length(str); size_type rlen = std::min(count1, count2); int ret = traits_type::compare(const_str_, str, rlen); if (ret != 0) return ret; if (count1 < count2) return -1; if (count1 > count2) return 1; return 0; } template inline typename basic_string<_CharTy>::size_type basic_string<_CharTy>::find(const char_type ch, size_type offset) const { if (offset >= size_) return basic_string<_CharTy>::npos; const_iterator citer = traits_type::find(cbegin().base() + offset, size_, ch); return citer ? (citer - cbegin()) : basic_string<_CharTy>::npos; } template inline typename basic_string<_CharTy>::size_type basic_string<_CharTy>::find(const char_type * const str, size_type offset, size_type count) const { if (offset >= size_) return basic_string<_CharTy>::npos; return __string_details::TraitsFind::traits_type>(const_str_, size_, offset, str, count); } template inline typename basic_string<_CharTy>::size_type basic_string<_CharTy>::find_first_of(const char_type * const str, size_type offset, size_type count) const { if (offset >= size_) return basic_string<_CharTy>::npos; const_iterator citer = std::find_first_of(cbegin().base() + offset, cend().base(), str, str + count); return (citer != cend()) ? (citer - cbegin()) : basic_string<_CharTy>::npos; } template inline typename basic_string<_CharTy>::size_type basic_string<_CharTy>::find_last_of(const char_type ch, size_type pos) const { if (pos == 0 || pos > size_ || pos == npos) return npos; const_reverse_iterator criter = std::find(crbegin(), crend(), ch); return (criter != crend()) ? (criter.base() - cbegin()) : basic_string<_CharTy>::npos; } template inline typename basic_string<_CharTy>::size_type basic_string<_CharTy>::find_last_of(const char_type * const str, size_type pos, size_type count) const { if (pos == 0 || pos > size_ || pos == npos) return npos; return __string_details::TraitsFindLastOf::traits_type>(const_str_, size_, pos, str, count); } template inline basic_string<_CharTy> & basic_string<_CharTy>::replace(size_type pos, size_type count, const char_type * cstr, size_type count2) { check_offset(pos); check_operability(); count = clamp_suffix_size(pos, count); if (count == count2) { traits_type::move(str_ + pos, cstr, count2); return (*this); } char_type* new_ptr = nullptr; char_type* const old_ptr = str_; const size_type old_size = size_; const size_type old_capacity = capacity_; const size_type suffix_size = old_size - count - pos + 1; if (count < count2 && (old_size + count2 - count) > capacity_) { const size_type growth = count2 - count; size_ = old_size + growth; capacity_ = size_; new_ptr = allocate(capacity_ + 1); traits_type::move(new_ptr, old_ptr, pos); } else { size_ = old_size - (count - count2); } char_type* const insert_at = (new_ptr ? new_ptr : old_ptr) + pos; traits_type::move(insert_at, cstr, count2); traits_type::move(insert_at + count2, old_ptr + count, suffix_size); if (new_ptr) { deallocate(str_, old_capacity + 1); str_ = new_ptr; } return (*this); } template inline basic_string<_CharTy> & basic_string<_CharTy>::replace(size_type pos, size_type count, size_type count2, const char_type ch) { check_offset(pos); check_operability(); count = clamp_suffix_size(pos, count); if (count == count2) { traits_type::assign(str_ + pos, count2, ch); return (*this); } char_type* new_ptr = nullptr; char_type* const old_ptr = str_; const size_type old_size = size_; const size_type old_capacity = capacity_; const size_type suffix_size = old_size - count - pos + 1; if (count < count2 && (old_size + count2 - count) > capacity_) { const size_type growth = count2 - count; size_ = old_size + growth; capacity_ = size_; new_ptr = allocate(capacity_ + 1); traits_type::move(new_ptr, old_ptr, pos); } else { size_ = old_size - (count - count2); } char_type* const insert_at = (new_ptr ? new_ptr : old_ptr) + pos; traits_type::assign(insert_at, count2, ch); traits_type::move(insert_at + count2, old_ptr + count, suffix_size); if (new_ptr) { deallocate(str_, old_capacity + 1); str_ = new_ptr; } return (*this); } template inline typename basic_string<_CharTy>::size_type basic_string<_CharTy>::copy(char_type * cstr, size_type count, size_type pos) const { if (count == 0 || cstr == const_str_) return 0; check_offset(pos); count = clamp_suffix_size(pos, count); traits_type::move(cstr, cbegin().base() + pos, count); return count; } template inline typename basic_string<_CharTy>::char_type* basic_string<_CharTy>::allocate(size_type count) { return get_allocator().allocate(count); } template inline void basic_string<_CharTy>::deallocate(char_type*& ptr, size_type count) { get_allocator().deallocate(ptr, count); ptr = nullptr; } template inline void basic_string<_CharTy>::destroy() { if (operable_ && str_) { deallocate(str_, capacity_ + 1); } else { const_str_ = nullptr; } size_ = capacity_ = 0; } template inline void basic_string<_CharTy>::swap(basic_string & rhs) noexcept { std::swap(const_str_, rhs.const_str_); std::swap(size_, rhs.size_); std::swap(capacity_, rhs.capacity_); // swap const datas std::swap(*const_cast(&operable_), *const_cast(&rhs.operable_)); } template inline void basic_string<_CharTy>::discard_const_data() { if (!operable_) { // force to enable operability *const_cast(&operable_) = true; const_str_ = nullptr; capacity_ = size_ = 0; } } template inline void basic_string<_CharTy>::check_operability() { if (!operable_) { // create a new string, then swap it with self basic_string(const_str_, false).swap(*this); } } // // details of basic_string<>::parese // template inline basic_string<_CharTy> basic_string<_CharTy>::parse(int val) { return ::kiwano::to_basic_string(val); } template inline basic_string<_CharTy> basic_string<_CharTy>::parse(unsigned int val) { return ::kiwano::to_basic_string(val); } template inline basic_string<_CharTy> basic_string<_CharTy>::parse(long val) { return ::kiwano::to_basic_string(val); } template inline basic_string<_CharTy> basic_string<_CharTy>::parse(unsigned long val) { return ::kiwano::to_basic_string(val); } template inline basic_string<_CharTy> basic_string<_CharTy>::parse(long long val) { return ::kiwano::to_basic_string(val); } template inline basic_string<_CharTy> basic_string<_CharTy>::parse(unsigned long long val) { return ::kiwano::to_basic_string(val); } template inline basic_string<_CharTy> basic_string<_CharTy>::parse(float val) { return ::kiwano::to_basic_string(val); } template inline basic_string<_CharTy> basic_string<_CharTy>::parse(double val) { return ::kiwano::to_basic_string(val); } template inline basic_string<_CharTy> basic_string<_CharTy>::parse(long double val) { return ::kiwano::to_basic_string(val); } // // details of basic_string::format // template template inline basic_string<_CharTy> basic_string<_CharTy>::format(const char_type* fmt, _Args&& ... args) { return ::kiwano::format_string(fmt, std::forward<_Args>(args)...); } // // details of operator<<>> // template inline std::basic_ostream::char_type>& operator<<(std::basic_ostream::char_type>& os, const basic_string<_CharTy>& str) { using ostream = std::basic_ostream::char_type, typename basic_string<_CharTy>::traits_type>; using size_type = typename basic_string<_CharTy>::size_type; using traits = typename basic_string<_CharTy>::traits_type; const ostream::sentry ok(os); std::ios_base::iostate state = std::ios_base::goodbit; if (!ok) { state |= std::ios_base::badbit; } else { const auto str_size = str.size(); size_type pad = (os.width() <= 0 || static_cast(os.width()) <= str_size) ? 0 : static_cast(os.width()) - str_size; try { if ((os.flags() & std::ios_base::adjustfield) != std::ios_base::left) { for (; 0 < pad; --pad) { if (traits::eq_int_type(traits::eof(), os.rdbuf()->sputc(os.fill()))) { state |= std::ios_base::badbit; break; } } } if (state == std::ios_base::goodbit && os.rdbuf()->sputn(str.data(), (std::streamsize)str_size) != (std::streamsize)str_size) { state |= std::ios_base::badbit; } else { for (; 0 < pad; --pad) { if (traits::eq_int_type(traits::eof(), os.rdbuf()->sputc(os.fill()))) { state |= std::ios_base::badbit; break; } } } os.width(0); } catch (...) { os.setstate(std::ios_base::badbit, true); } } os.setstate(state); return (os); } template inline std::basic_istream::char_type>& operator>>(std::basic_istream::char_type>& is, basic_string<_CharTy>& str) { using ctype = std::ctype::char_type>; using istream = std::basic_istream::char_type, typename basic_string<_CharTy>::traits_type>; using size_type = typename basic_string<_CharTy>::size_type; using traits = typename basic_string<_CharTy>::traits_type; bool changed = false; const istream::sentry ok(is); std::ios_base::iostate state = std::ios_base::goodbit; if (ok) { const ctype& ctype_fac = std::use_facet(is.getloc()); str.erase(); try { size_type size = (0 < is.width() && static_cast(is.width()) < str.max_size()) ? static_cast(is.width()) : str.max_size(); typename traits::int_type meta = is.rdbuf()->sgetc(); for (; 0 < size; --size, meta = is.rdbuf()->snextc()) { if (traits::eq_int_type(traits::eof(), meta)) { state |= std::ios_base::eofbit; break; } else if (ctype_fac.is(ctype::space, traits::to_char_type(meta))) { break; } else { str.push_back(traits::to_char_type(meta)); changed = true; } } } catch (...) { is.setstate(std::ios_base::badbit, true); } } is.width(0); if (!changed) state |= std::ios_base::failbit; is.setstate(state); return is; } // // details of to_string functions // namespace __to_string_detail { template struct FloatingToString { // template // static basic_string<_CharTy> convert(const _Ty val); }; template struct IntegralToString { // template // static basic_string<_CharTy> convert(const _Ty val); }; } template inline basic_string<_CharTy> to_basic_string(int val) { return (__to_string_detail::IntegralToString<_CharTy>::convert(val)); } template inline basic_string<_CharTy> to_basic_string(unsigned int val) { return (__to_string_detail::IntegralToString<_CharTy>::convert(val)); } template inline basic_string<_CharTy> to_basic_string(long val) { return (__to_string_detail::IntegralToString<_CharTy>::convert(val)); } template inline basic_string<_CharTy> to_basic_string(unsigned long val) { return (__to_string_detail::IntegralToString<_CharTy>::convert(val)); } template inline basic_string<_CharTy> to_basic_string(long long val) { return (__to_string_detail::IntegralToString<_CharTy>::convert(val)); } template inline basic_string<_CharTy> to_basic_string(unsigned long long val) { return (__to_string_detail::IntegralToString<_CharTy>::convert(val)); } template inline basic_string<_CharTy> to_basic_string(float val) { return (__to_string_detail::FloatingToString<_CharTy>::convert(val)); } template inline basic_string<_CharTy> to_basic_string(double val) { return (__to_string_detail::FloatingToString<_CharTy>::convert(val)); } template inline basic_string<_CharTy> to_basic_string(long double val) { return (__to_string_detail::FloatingToString<_CharTy>::convert(val)); } template inline basic_string format_string(const char* const fmt, _Args&& ... args) { using string_type = basic_string; const auto len = static_cast(::_scprintf(fmt, std::forward<_Args>(args)...)); if (len) { string_type str(len, '\0'); ::sprintf_s(&str[0], len + 1, fmt, std::forward<_Args>(args)...); return str; } return string_type{}; } template inline basic_string format_string(const wchar_t* const fmt, _Args&&... args) { using string_type = basic_string; const auto len = static_cast(::_scwprintf(fmt, std::forward<_Args>(args)...)); if (len) { string_type str(len, L'\0'); ::swprintf_s(&str[0], len + 1, fmt, std::forward<_Args>(args)...); return str; } return string_type{}; } namespace __to_string_detail { template _Elem* __IntegerToStringBufferEnd(const _Ty val, _Elem* const buffer_end) { using _UTy = std::make_unsigned_t<_Ty>; _Elem* next = buffer_end; auto uval = static_cast<_UTy>(val); if (val < 0) uval = 0 - uval; do { *--next = static_cast<_Elem>('0' + uval % 10); uval /= 10; } while (uval != 0); if (val < 0) * --next = static_cast<_Elem>('-'); return next; } template <> struct IntegralToString { template static basic_string convert(const _Ty val) { static_assert(std::is_integral<_Ty>::value, "_Ty must be integral"); using _Elem = typename basic_string::traits_type::char_type; _Elem buffer[21]; _Elem* const buffer_end = std::end(buffer); _Elem* buffer_begin = __IntegerToStringBufferEnd(val, buffer_end); return basic_string(buffer_begin, buffer_end); } }; template <> struct IntegralToString { template static basic_string convert(const _Ty val) { static_assert(std::is_integral<_Ty>::value, "_Ty must be integral"); using _Elem = typename basic_string::traits_type::char_type; _Elem buffer[21]; _Elem* const buffer_end = std::end(buffer); _Elem* buffer_begin = __IntegerToStringBufferEnd(val, buffer_end); return basic_string(buffer_begin, buffer_end); } }; template<> struct FloatingToString { static inline basic_string convert(const float val) { return format_string(L"%g", val); } static inline basic_string convert(const double val) { return format_string(L"%g", val); } static inline basic_string convert(const long double val) { return format_string(L"%Lg", val); } }; template<> struct FloatingToString { static inline basic_string convert(const float val) { return format_string("%g", val); } static inline basic_string convert(const double val) { return format_string("%g", val); } static inline basic_string convert(const long double val) { return format_string("%Lg", val); } }; } } namespace kiwano { template class string_convert { enum { BUFFER_INCREASE = 8, BUFFER_MAX = 16 }; public: using byte_string = kiwano::basic_string; using wide_string = kiwano::basic_string<_Elem>; using codecvt_type = _Codecvt; using state_type = typename codecvt_type::state_type; using int_type = typename wide_string::traits_type::int_type; string_convert() : string_convert(new codecvt_type) { } explicit string_convert(const codecvt_type* cvt) : state_{} , cvt_(cvt) , loc_() , conv_num_(0) { loc_ = std::locale(loc_, cvt_); } virtual ~string_convert() { } size_t converted() const noexcept { return conv_num_; } state_type state() const { return state_; } wide_string from_bytes(char _Byte) { return from_bytes(&_Byte, &_Byte + 1); } wide_string from_bytes(const char* ptr) { return from_bytes(ptr, ptr + std::strlen(ptr)); } wide_string from_bytes(const byte_string& byte_str) { const char* ptr = byte_str.c_str(); return from_bytes(ptr, ptr + byte_str.size()); } wide_string from_bytes(const char* first, const char* last) { wide_string wbuf, wstr; const char* first_save = first; state_ = state_type{}; wbuf.append((std::size_t) BUFFER_INCREASE, (_Elem) '\0'); for (conv_num_ = 0; first != last; conv_num_ = static_cast(first - first_save)) { _Elem* dest = &*wbuf.begin(); _Elem* dnext; switch (cvt_->in(state_, first, last, first, dest, dest + wbuf.size(), dnext)) { case codecvt_type::partial: case codecvt_type::ok: { if (dest < dnext) { wstr.append(dest, static_cast(dnext - dest)); } else if (wbuf.size() < BUFFER_MAX) { wbuf.append(static_cast(BUFFER_INCREASE), '\0'); } else { throw (std::range_error("bad conversion")); } break; } case codecvt_type::noconv: { // no conversion, just copy code values for (; first != last; ++first) { wstr.push_back((_Elem)(unsigned char)* first); } break; } default: throw (std::range_error("bad conversion")); } } return wstr; } byte_string to_bytes(_Elem _Char) { return to_bytes(&_Char, &_Char + 1); } byte_string to_bytes(const _Elem* _Wptr) { const _Elem* _Next = _Wptr; while ((int_type)* _Next != 0) { ++_Next; } return to_bytes(_Wptr, _Next); } byte_string to_bytes(const wide_string& _Wstr) { const _Elem* _Wptr = _Wstr.c_str(); return to_bytes(_Wptr, _Wptr + _Wstr.size()); } byte_string to_bytes(const _Elem* first, const _Elem* last) { byte_string bbuf, bstr; const _Elem* first_save = first; state_ = state_type{}; bbuf.append((std::size_t) BUFFER_INCREASE, '\0'); for (conv_num_ = 0; first != last; conv_num_ = static_cast(first - first_save)) { char* dest = &*bbuf.begin(); char* dnext; switch (cvt_->out(state_, first, last, first, dest, dest + bbuf.size(), dnext)) { case codecvt_type::partial: case codecvt_type::ok: { if (dest < dnext) { bstr.append(dest, (std::size_t)(dnext - dest)); } else if (bbuf.size() < BUFFER_MAX) { bbuf.append((std::size_t) BUFFER_INCREASE, '\0'); } else { throw (std::range_error("bad conversion")); } break; } case codecvt_type::noconv: { // no conversion, just copy code values for (; first != last; ++first) { bstr.push_back((char)(int_type)* first); } break; } default: throw (std::range_error("bad conversion")); } } return bstr; } string_convert(const string_convert&) = delete; string_convert& operator=(const string_convert&) = delete; private: const codecvt_type* cvt_; std::locale loc_; state_type state_; size_t conv_num_; }; class chs_codecvt : public std::codecvt_byname { public: chs_codecvt() : codecvt_byname("chs") {} static inline kiwano::wstring string_to_wide(kiwano::string const& str) { string_convert conv; return conv.from_bytes(str); } static inline kiwano::string wide_to_string(kiwano::wstring const& str) { string_convert conv; return conv.to_bytes(str); } }; inline kiwano::wstring string_to_wide(kiwano::string const& str) { return kiwano::chs_codecvt::string_to_wide(str); } inline kiwano::string wide_to_string(kiwano::wstring const& str) { return kiwano::chs_codecvt::wide_to_string(str); } } namespace std { template<> struct hash<::kiwano::string> { inline size_t operator()(const kiwano::string& key) const { return key.hash(); } }; template<> struct hash<::kiwano::wstring> { inline size_t operator()(const kiwano::wstring& key) const { return key.hash(); } }; } namespace std { template<> inline void swap<::kiwano::string>(::kiwano::string& lhs, ::kiwano::string& rhs) noexcept { lhs.swap(rhs); } template<> inline void swap<::kiwano::wstring>(::kiwano::wstring& lhs, ::kiwano::wstring& rhs) noexcept { lhs.swap(rhs); } }