// 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 { // // String // Lightweight std::wstring<>-like class // class 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 = wchar_t; 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 char_traits = std::char_traits; using allocator = std::allocator; String(); String(const wchar_t* cstr, bool const_str = true); String(const wchar_t* cstr, size_type count); String(size_type count, wchar_t ch); String(const char* cstr); String(std::string const& str); String(std::wstring const& str); String(String const& rhs); String(String const& rhs, size_type pos, size_type count = npos); String(String && rhs) noexcept; ~String(); template String(_Iter first, _Iter last) : String() { assign_iter(first, last); } inline const wchar_t* c_str() const { return const_str_ ? const_str_ : L""; } inline const wchar_t* data() const { return const_str_ ? const_str_ : L""; } inline wchar_t 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 wchar_t ch = value_type()) { check_operability(); if (new_size < size_) str_[size_ = new_size] = value_type(); else append(new_size - size_, ch); } int compare(const wchar_t* const str) const; inline int compare(String const& str) const { return compare(str.c_str()); } String& append(size_type count, wchar_t ch); String& append(const wchar_t* cstr, size_type count); String& append(String const& other, size_type pos, size_type count = npos); inline String& append(const wchar_t* cstr) { return append(cstr, char_traits::length(cstr)); } inline String& append(std::wstring const& str) { return append(str.c_str()); } inline String& append(String const& other) { return append(other.const_str_, 0, npos); } size_type find(const wchar_t ch, size_type offset = 0) const; size_type find(const wchar_t* const str, size_type offset, size_type count) const; inline size_type find(String const& str, size_type offset = 0) const { return find(str.c_str(), offset, str.size()); } inline size_type find(const wchar_t* const str, size_type offset = 0) const { return find(str, offset, char_traits::length(str)); } size_type find_first_of(const wchar_t* const str, size_type offset, size_type count) const; inline size_type find_first_of(const wchar_t ch, size_type offset = 0) const { return find(ch, offset); } inline size_type find_first_of(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 wchar_t* const str, size_type offset = 0) const { return find_first_of(str, offset, char_traits::length(str)); } size_type find_last_of(const wchar_t ch, size_type pos = npos) const; size_type find_last_of(const wchar_t* const str, size_type pos, size_type count) const; inline size_type find_last_of(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 wchar_t* const str, size_type pos = npos) const { return find_first_of(str, pos, char_traits::length(str)); } String& replace(size_type pos, size_type count, const wchar_t* cstr, size_type count2); String& replace(size_type pos, size_type count, size_type count2, const wchar_t ch); inline String& replace(size_type pos, size_type count, const String& str) { return replace(pos, count, str.c_str(), str.size()); } inline String& replace(size_type pos, size_type count, const wchar_t* cstr) { return replace(pos, count, cstr, char_traits::length(cstr)); } inline String& replace(const_iterator first, const_iterator last, const String& str) { return replace(first, last, str.c_str(), str.size()); } inline String& replace(const_iterator first, const_iterator last, const wchar_t* cstr) { return replace(first, last, cstr, char_traits::length(cstr)); } inline String& replace(const_iterator first, const_iterator last, const wchar_t* cstr, size_type count){ return replace(first - cbegin(), last - first, cstr, count); } inline String& replace(const_iterator first, const_iterator last, size_type count2, const wchar_t ch) { return replace(first - cbegin(), last - first, count2, ch); } String& assign(size_type count, const wchar_t ch); String& assign(const wchar_t* cstr, size_type count); inline String& assign(const wchar_t* cstr, bool const_str = true) { String(cstr, const_str).swap(*this); return *this; } inline String& assign(std::wstring const& str) { String{ str }.swap(*this); return *this; } inline String& assign(String const& rhs) { String{ rhs }.swap(*this); return *this; } String& assign(String const& rhs, size_type pos, size_type count = npos); template inline String& assign(_Iter first, _Iter last) { assign_iter(first, last); return(*this); } 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; } String substr(size_type pos = 0, size_type count = npos) const { return String(*this, pos, count); } String& insert(size_type index, size_type count, wchar_t ch); String& insert(size_type index, const wchar_t* s, size_type count); String& insert(size_type index, const String& str, size_type off, size_type count = npos); inline String& insert(size_type index, const wchar_t* s) { return insert(index, s, char_traits::length(s)); } inline String& insert(size_type index, const String& str) { return insert(index, str, 0, str.size()); } inline iterator insert(const_iterator pos, size_type count, wchar_t ch) { size_type off = pos - cbegin(); insert(off, count, ch); return begin().base() + off; } inline iterator insert(const_iterator pos, wchar_t ch) { return insert(pos, 1, ch); } inline void push_back(const wchar_t ch) { append(1, ch); } inline wchar_t pop_back() { if (empty()) throw std::out_of_range("pop_back() called on empty string"); check_operability(); wchar_t ch = str_[--size_]; str_[size_] = value_type(); return ch; } size_type copy(wchar_t* cstr, size_type count, size_type pos = 0) const; std::string to_string() const; std::wstring to_wstring() const; void swap(String& rhs) noexcept; size_t hash() const; public: static String parse(int val); static String parse(unsigned int val); static String parse(long val); static String parse(unsigned long val); static String parse(long long val); static String parse(unsigned long long val); static String parse(float val); static String parse(double val); static String parse(long double val); template static String format(const wchar_t* const fmt, _Args&&... args); 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 wchar_t operator[](size_type off) const { if(off >= size_) throw std::out_of_range("string subscript out of range"); return const_str_[off]; } inline wchar_t& 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 String operator+(const wchar_t ch) const { return String{ *this }.append(1, ch); } inline const String operator+(const wchar_t* cstr) const { return String{ *this }.append(cstr); } inline const String operator+(std::wstring const& str) const { return String{ *this }.append(str); } inline const String operator+(String const& rhs) const { return String{ *this }.append(rhs); } inline String& operator+=(const wchar_t ch) { return append(1, ch); } inline String& operator+=(const wchar_t* cstr) { return append(cstr); } inline String& operator+=(std::wstring const& str) { return append(str); } inline String& operator+=(String const& rhs) { return append(rhs); } public: inline String& operator=(const wchar_t* cstr) { if (const_str_ != cstr) String{ cstr }.swap(*this); return *this; } inline String& operator=(std::wstring const& str) { String{ str }.swap(*this); return *this; } inline String& operator=(String const& rhs) { if (this != &rhs) String{ rhs }.swap(*this); return *this; } inline String& operator=(String && rhs) noexcept { if (this != &rhs) String{ rhs }.swap(*this); return *this; } public: static const String::size_type npos = static_cast(-1); static inline allocator& get_allocator() { static allocator allocator_; return allocator_; } private: wchar_t* allocate(size_type count); void deallocate(wchar_t*& 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) { char_traits::assign(str_[index], char_traits::to_char_type(*first)); } char_traits::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_; }; // // operator== for String // inline bool operator==(String const& lhs, String const& rhs) { return lhs.compare(rhs) == 0; } inline bool operator==(const wchar_t* lhs, String const& rhs) { return rhs.compare(lhs) == 0; } inline bool operator==(String const& lhs, const wchar_t* rhs) { return lhs.compare(rhs) == 0; } inline bool operator==(const char* lhs, String const& rhs) { return rhs.compare(String(lhs)) == 0; } inline bool operator==(String const& lhs, const char* rhs) { return lhs.compare(String(rhs)) == 0; } // // operator!= for String // inline bool operator!=(String const& lhs, String const& rhs) { return lhs.compare(rhs) != 0; } inline bool operator!=(const wchar_t* lhs, String const& rhs) { return rhs.compare(lhs) != 0; } inline bool operator!=(String const& lhs, const wchar_t* rhs) { return lhs.compare(rhs) != 0; } inline bool operator!=(const char* lhs, String const& rhs) { return rhs.compare(String(lhs)) != 0; } inline bool operator!=(String const& lhs, const char* rhs) { return lhs.compare(String(rhs)) != 0; } // // operator+ for String // inline String operator+(const wchar_t* lhs, String const& rhs) { return String{ lhs } + rhs; } // // operator<> for String // inline bool operator<(String const& lhs, String const& rhs) { return lhs.compare(rhs) < 0; } inline bool operator>(String const& lhs, String const& rhs) { return lhs.compare(rhs) > 0; } inline bool operator<=(String const& lhs, String const& rhs) { return lhs.compare(rhs) <= 0; } inline bool operator>=(String const& lhs, String const& rhs) { return lhs.compare(rhs) >= 0; } // // operator<<>> for String // std::basic_ostream& operator<<(std::basic_ostream& os, const String & str); std::basic_istream& operator>>(std::basic_istream& is, String & str); std::basic_ostream& operator<<(std::basic_ostream& os, const String& str); std::basic_istream& operator>>(std::basic_istream& is, String& str); // // to_string functions // String to_wstring(int val); String to_wstring(unsigned int val); String to_wstring(long val); String to_wstring(unsigned long val); String to_wstring(long long val); String to_wstring(unsigned long long val); String to_wstring(float val); String to_wstring(double val); String to_wstring(long double val); // // format_wstring // template String format_wstring(const wchar_t* const fmt, _Args&&... args); } namespace kiwano { // // details of 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); } class chs_codecvt : public std::codecvt_byname { public: chs_codecvt() : codecvt_byname("chs") {} static inline std::wstring string_to_wide(std::string const& str) { std::wstring_convert conv; return conv.from_bytes(str); } static inline std::string wide_to_string(std::wstring const& str) { std::wstring_convert conv; return conv.to_bytes(str); } }; } inline String::String() : str_(nullptr) , size_(0) , capacity_(0) , operable_(true) { } inline String::String(const wchar_t * cstr, bool const_str) : operable_(!const_str) , size_(0) , capacity_(0) , str_(nullptr) { if (cstr == nullptr) return; if (operable_) { assign(cstr, char_traits::length(cstr)); } else { const_str_ = cstr; size_ = char_traits::length(cstr); capacity_ = size_; } } inline String::String(const wchar_t * cstr, size_type count) : String() { assign(cstr, count); } inline String::String(size_type count, wchar_t ch) : String() { assign(count, ch); } inline String::String(const char * cstr) : String() { if (cstr && cstr[0]) { try { std::wstring wide_string = __string_details::chs_codecvt::string_to_wide(cstr); assign(wide_string); } catch (std::range_error& e) { // bad conversion (void)e; } } } inline String::String(std::string const & str) : String(str.c_str()) { } inline String::String(std::wstring const & str) : String(str.c_str(), false) { } inline String::String(String const & rhs) : String(rhs.const_str_, !rhs.operable_) { } inline String::String(String const & rhs, size_type pos, size_type count) : String() { assign(rhs, pos, count); } inline String::String(String && rhs) noexcept : str_(rhs.str_) , size_(rhs.size_) , capacity_(rhs.capacity_) , operable_(rhs.operable_) { rhs.str_ = nullptr; rhs.size_ = rhs.capacity_ = 0; } inline String::~String() { destroy(); } inline String & String::assign(size_type count, const wchar_t ch) { discard_const_data(); if (count != 0) { if (count > capacity_) { destroy(); str_ = allocate(count + 1); capacity_ = count; } size_ = count; char_traits::assign(str_, count, ch); char_traits::assign(str_[size_], value_type()); } else { clear(); } return (*this); } inline String & String::assign(const wchar_t * cstr, size_type count) { discard_const_data(); if (cstr && count) { if (count > capacity_) { destroy(); str_ = allocate(count + 1); capacity_ = count; } size_ = count; char_traits::move(str_, cstr, size_); char_traits::assign(str_[size_], value_type()); } else { clear(); } return (*this); } inline String& String::assign(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; char_traits::move(str_, rhs.begin().base() + pos, size_); char_traits::assign(str_[size_], value_type()); return (*this); } inline String & String::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; char_traits::move(erase_at.base(), erase_at.base() + count, new_size - offset + 1); return (*this); } inline String & String::insert(size_type index, size_type count, wchar_t ch) { if (count == 0) return (*this); if (index >= size()) return append(count, ch); check_operability(); wchar_t* 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_; wchar_t* new_ptr = allocate(capacity_ + 1); wchar_t* const insert_at = new_ptr + index; char_traits::move(new_ptr, old_ptr, index); // (0) - (index) char_traits::assign(insert_at, count, ch); // (index) - (index + count) char_traits::move(insert_at + count, old_ptr + index, suffix_size); // (index + count) - (old_size - index) deallocate(str_, old_capacity + 1); str_ = new_ptr; } else { wchar_t* const insert_at = old_ptr + index; char_traits::move(insert_at + count, old_ptr + index, suffix_size); char_traits::assign(insert_at, count, ch); } return (*this); } inline String & String::insert(size_type index, const wchar_t * cstr, size_type count) { if (count == 0) return (*this); if (index >= size()) return append(cstr, count); check_operability(); wchar_t* 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_; wchar_t* new_ptr = allocate(capacity_ + 1); wchar_t* const insert_at = new_ptr + index; char_traits::move(new_ptr, old_ptr, index); // (0) - (index) char_traits::move(insert_at, cstr, count); // (index) - (index + count) char_traits::move(insert_at + count, old_ptr + index, suffix_size); // (index + count) - (old_size - index) deallocate(str_, old_capacity + 1); str_ = new_ptr; } else { wchar_t* const insert_at = old_ptr + index; char_traits::move(insert_at + count, old_ptr + index, suffix_size); char_traits::move(insert_at, cstr, count); } return (*this); } inline String & String::insert(size_type index, const 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); wchar_t* 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_; wchar_t* new_ptr = allocate(capacity_ + 1); wchar_t* const insert_at = new_ptr + index; char_traits::move(new_ptr, old_ptr, index); // (0) - (index) char_traits::move(insert_at, str.begin().base() + off, count); // (index) - (index + count) char_traits::move(insert_at + count, old_ptr + index, suffix_size); // (index + count) - (old_size - index) deallocate(str_, old_capacity + 1); str_ = new_ptr; } else { wchar_t* const insert_at = old_ptr + index; char_traits::move(insert_at + count, old_ptr + index, suffix_size); char_traits::move(insert_at, str.begin().base() + off, count); } return (*this); } inline String & String::append(size_type count, wchar_t ch) { check_operability(); size_t new_size = size_ + count; size_t new_cap = new_size + 1; wchar_t* new_str = allocate(new_cap); char_traits::move(new_str, str_, size_); char_traits::assign(new_str + size_, count, ch); char_traits::assign(new_str[new_size], value_type()); destroy(); str_ = new_str; size_ = new_size; capacity_ = new_cap; return (*this); } inline String & String::append(const wchar_t * cstr, size_type count) { check_operability(); size_t new_size = size_ + count; size_t new_cap = new_size + 1; wchar_t* new_str = allocate(new_cap); char_traits::move(new_str, str_, size_); char_traits::move(new_str + size_, cstr, count); char_traits::assign(new_str[new_size], value_type()); destroy(); str_ = new_str; size_ = new_size; capacity_ = new_cap; return (*this); } inline String & String::append(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; wchar_t* new_str = allocate(new_cap); char_traits::move(new_str, str_, size_); char_traits::move(new_str + size_, other.begin().base() + pos, count); char_traits::assign(new_str[new_size], value_type()); destroy(); str_ = new_str; size_ = new_size; capacity_ = new_cap; return (*this); } inline void String::reserve(const size_type new_cap) { if (new_cap <= capacity_) return; check_operability(); wchar_t* new_str = allocate(new_cap); char_traits::move(new_str, str_, capacity_); destroy(); str_ = new_str; capacity_ = new_cap; } inline size_t String::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; } inline int String::compare(const wchar_t * const str) const { size_type count1 = size(); size_type count2 = char_traits::length(str); size_type rlen = std::min(count1, count2); int ret = char_traits::compare(const_str_, str, rlen); if (ret != 0) return ret; if (count1 < count2) return -1; if (count1 > count2) return 1; return 0; } inline String::size_type String::find(const wchar_t ch, size_type offset) const { if (offset >= size_) return String::npos; const_iterator citer = char_traits::find(cbegin().base() + offset, size_, ch); return citer ? (citer - cbegin()) : String::npos; } inline String::size_type String::find(const wchar_t * const str, size_type offset, size_type count) const { if (offset >= size_) return String::npos; return __string_details::TraitsFind(const_str_, size_, offset, str, count); } inline String::size_type String::find_first_of(const wchar_t * const str, size_type offset, size_type count) const { if (offset >= size_) return String::npos; const_iterator citer = std::find_first_of(cbegin().base() + offset, cend().base(), str, str + count); return (citer != cend()) ? (citer - cbegin()) : String::npos; } inline String::size_type String::find_last_of(const wchar_t 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()) : String::npos; } inline String::size_type String::find_last_of(const wchar_t * const str, size_type pos, size_type count) const { if (pos == 0 || pos > size_ || pos == npos) return npos; return __string_details::TraitsFindLastOf(const_str_, size_, pos, str, count); } inline String & String::replace(size_type pos, size_type count, const wchar_t * cstr, size_type count2) { check_offset(pos); check_operability(); count = clamp_suffix_size(pos, count); if (count == count2) { char_traits::move(str_ + pos, cstr, count2); return (*this); } wchar_t* new_ptr = nullptr; wchar_t* 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); char_traits::move(new_ptr, old_ptr, pos); } else { size_ = old_size - (count - count2); } wchar_t* const insert_at = (new_ptr ? new_ptr : old_ptr) + pos; char_traits::move(insert_at, cstr, count2); char_traits::move(insert_at + count2, old_ptr + count, suffix_size); if (new_ptr) { deallocate(str_, old_capacity + 1); str_ = new_ptr; } return (*this); } inline String & String::replace(size_type pos, size_type count, size_type count2, const wchar_t ch) { check_offset(pos); check_operability(); count = clamp_suffix_size(pos, count); if (count == count2) { char_traits::assign(str_ + pos, count2, ch); return (*this); } wchar_t* new_ptr = nullptr; wchar_t* 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); char_traits::move(new_ptr, old_ptr, pos); } else { size_ = old_size - (count - count2); } wchar_t* const insert_at = (new_ptr ? new_ptr : old_ptr) + pos; char_traits::assign(insert_at, count2, ch); char_traits::move(insert_at + count2, old_ptr + count, suffix_size); if (new_ptr) { deallocate(str_, old_capacity + 1); str_ = new_ptr; } return (*this); } inline String::size_type String::copy(wchar_t * 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); char_traits::move(cstr, cbegin().base() + pos, count); return count; } inline std::string String::to_string() const { if (const_str_ && size_) { try { std::string string = __string_details::chs_codecvt::wide_to_string(const_str_); return string; } catch (std::range_error& e) { // bad conversion (void)e; } } return std::string(); } inline std::wstring String::to_wstring() const { return std::wstring(const_str_); } inline wchar_t * String::allocate(size_type count) { return get_allocator().allocate(count); } inline void String::deallocate(wchar_t*& ptr, size_type count) { get_allocator().deallocate(ptr, count); ptr = nullptr; } inline void String::destroy() { if (operable_ && str_) { deallocate(str_, capacity_ + 1); } else { const_str_ = nullptr; } size_ = capacity_ = 0; } inline void String::swap(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_)); } inline void String::discard_const_data() { if (!operable_) { // force to enable operability *const_cast(&operable_) = true; const_str_ = nullptr; capacity_ = size_ = 0; } } inline void String::check_operability() { if (!operable_) { // create a new string, then swap it with self String(const_str_, false).swap(*this); } } // // details of String::parese // inline String String::parse(int val) { return ::kiwano::to_wstring(val); } inline String String::parse(unsigned int val) { return ::kiwano::to_wstring(val); } inline String String::parse(long val) { return ::kiwano::to_wstring(val); } inline String String::parse(unsigned long val) { return ::kiwano::to_wstring(val); } inline String String::parse(long long val) { return ::kiwano::to_wstring(val); } inline String String::parse(unsigned long long val) { return ::kiwano::to_wstring(val); } inline String String::parse(float val) { return ::kiwano::to_wstring(val); } inline String String::parse(double val) { return ::kiwano::to_wstring(val); } inline String String::parse(long double val) { return ::kiwano::to_wstring(val); } template inline String String::format(const wchar_t* const fmt, _Args&&... args) { return ::kiwano::format_wstring(fmt, std::forward<_Args>(args)...); } // // details of operator<<>> // inline std::basic_ostream& operator<<(std::basic_ostream& os, const String & str) { using ostream = std::basic_ostream; using size_type = String::size_type; using traits = String::char_traits; 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); } inline std::basic_istream& operator>>(std::basic_istream& is, String & str) { using ctype = std::ctype; using istream = std::basic_istream; using size_type = String::size_type; using traits = String::char_traits; 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(); 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; } inline std::basic_ostream& operator<<(std::basic_ostream& os, const String& str) { return os << str.to_string(); } inline std::basic_istream& operator>>(std::basic_istream& is, String& str) { std::string tmp; if (is >> tmp) { str = tmp; } return is; } // // details of to_string functions // namespace __to_string_detail { template inline String FloatingToString(const wchar_t *fmt, _Ty val); template inline String IntegralToString(const _Ty val); } inline String to_wstring(int val) { return (__to_string_detail::IntegralToString(val)); } inline String to_wstring(unsigned int val) { return (__to_string_detail::IntegralToString(val)); } inline String to_wstring(long val) { return (__to_string_detail::IntegralToString(val)); } inline String to_wstring(unsigned long val) { return (__to_string_detail::IntegralToString(val)); } inline String to_wstring(long long val) { return (__to_string_detail::IntegralToString(val)); } inline String to_wstring(unsigned long long val) { return (__to_string_detail::IntegralToString(val)); } inline String to_wstring(float val) { return (__to_string_detail::FloatingToString(L"%f", val)); } inline String to_wstring(double val) { return (__to_string_detail::FloatingToString(L"%f", val)); } inline String to_wstring(long double val) { return (__to_string_detail::FloatingToString(L"%Lf", val)); } template inline String format_wstring(const wchar_t* const fmt, _Args&&... args) { const auto len = static_cast(::_scwprintf(fmt, std::forward<_Args>(args)...)); if (len) { String str(len, L'\0'); ::swprintf_s(&str[0], len + 1, fmt, std::forward<_Args>(args)...); return str; } return String{}; } namespace __to_string_detail { template inline String FloatingToString(const wchar_t *fmt, _Ty val) { static_assert(std::is_floating_point<_Ty>::value, "_Ty must be floating point"); return format_wstring(fmt, val); } template inline String IntegralToString(const _Ty val) { static_assert(std::is_integral<_Ty>::value, "_Ty must be integral"); using _UTy = std::make_unsigned_t<_Ty>; using _Elem = String::char_traits::char_type; _Elem buffer[21]; _Elem* const buffer_end = std::end(buffer); _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 String(next, buffer_end); } } } namespace std { template<> struct hash<::kiwano::String> { inline size_t operator()(const kiwano::String& key) const { return key.hash(); } }; template<> inline void swap<::kiwano::String>(::kiwano::String& lhs, ::kiwano::String& rhs) { lhs.swap(rhs); } }