1385 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1385 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C++
		
	
	
	
| // 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 <string>
 | |
| #include <algorithm>
 | |
| #include <codecvt>
 | |
| #include <ostream>
 | |
| #include <istream>
 | |
| #include <cstring>
 | |
| #include <cstdio>
 | |
| 
 | |
| namespace kiwano
 | |
| {
 | |
| 	//
 | |
| 	// String
 | |
| 	// Lightweight std::wstring<>-like class
 | |
| 	//
 | |
| 	class String
 | |
| 	{
 | |
| 	public:
 | |
| 		// Iterator
 | |
| 		template <typename _Ty>
 | |
| 		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<value_type>;
 | |
| 		using const_iterator			= iterator_impl<const value_type>;
 | |
| 		using reverse_iterator			= std::reverse_iterator<iterator>;
 | |
| 		using const_reverse_iterator	= std::reverse_iterator<const_iterator>;
 | |
| 		using char_traits				= std::char_traits<value_type>;
 | |
| 		using allocator					= std::allocator<value_type>;
 | |
| 
 | |
| 		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 <typename _Iter>
 | |
| 		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<size_type>(-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 <typename _Iter>
 | |
| 		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<typename ..._Args>
 | |
| 		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<size_type>(-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 <typename _Iter>
 | |
| 		void assign_iter(_Iter first, _Iter last)
 | |
| 		{
 | |
| 			size_type diff = static_cast<size_type>(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<String::value_type>& operator<<(std::basic_ostream<String::value_type>& os, const String & str);
 | |
| 	std::basic_istream<String::value_type>& operator>>(std::basic_istream<String::value_type>& is, String & str);
 | |
| 
 | |
| 	std::basic_ostream<char>& operator<<(std::basic_ostream<char>& os, const String& str);
 | |
| 	std::basic_istream<char>& operator>>(std::basic_istream<char>& 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<typename ..._Args>
 | |
| 	String format_wstring(const wchar_t* const fmt, _Args&&... args);
 | |
| }
 | |
| 
 | |
| namespace kiwano
 | |
| {
 | |
| 	//
 | |
| 	// details of String
 | |
| 	//
 | |
| 
 | |
| 	namespace __string_details
 | |
| 	{
 | |
| 		template<class _Traits>
 | |
| 		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<size_t>(-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<size_t>(matches_end - iter), *second);
 | |
| 				if (!iter)
 | |
| 				{
 | |
| 					return static_cast<size_t>(-1);
 | |
| 				}
 | |
| 
 | |
| 				if (typename _Traits::compare(iter, second, count) == 0)
 | |
| 				{
 | |
| 					return static_cast<size_t>(iter - first);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		template<class _Traits>
 | |
| 		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<size_t>(iter - first);
 | |
| 					}
 | |
| 					if (iter == first)
 | |
| 					{
 | |
| 						break;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			return static_cast<size_t>(-1);
 | |
| 		}
 | |
| 
 | |
| 		class chs_codecvt
 | |
| 			: public std::codecvt_byname<wchar_t, char, std::mbstate_t>
 | |
| 		{
 | |
| 		public:
 | |
| 			chs_codecvt() : codecvt_byname("chs") {}
 | |
| 
 | |
| 			static inline std::wstring string_to_wide(std::string const& str)
 | |
| 			{
 | |
| 				std::wstring_convert<chs_codecvt> conv;
 | |
| 				return conv.from_bytes(str);
 | |
| 			}
 | |
| 
 | |
| 			static inline std::string wide_to_string(std::wstring const& str)
 | |
| 			{
 | |
| 				std::wstring_convert<chs_codecvt> 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<size_t>(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<String::char_traits>(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<String::char_traits>(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<bool*>(&operable_), *const_cast<bool*>(&rhs.operable_));
 | |
| 	}
 | |
| 
 | |
| 	inline void String::discard_const_data()
 | |
| 	{
 | |
| 		if (!operable_)
 | |
| 		{
 | |
| 			// force to enable operability
 | |
| 			*const_cast<bool*>(&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<typename ..._Args>
 | |
| 	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<String::value_type>& operator<<(std::basic_ostream<String::value_type>& os, const String & str)
 | |
| 	{
 | |
| 		using ostream = std::basic_ostream<String::value_type, String::char_traits>;
 | |
| 		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<size_type>(os.width()) <= str_size) ? 0 : static_cast<size_type>(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<String::value_type>& operator>>(std::basic_istream<String::value_type>& is, String & str)
 | |
| 	{
 | |
| 		using ctype = std::ctype<String::value_type>;
 | |
| 		using istream = std::basic_istream<String::value_type, String::char_traits>;
 | |
| 		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<ctype>(is.getloc());
 | |
| 			str.erase();
 | |
| 			try
 | |
| 			{
 | |
| 				size_type size = (0 < is.width() && static_cast<size_type>(is.width()) < str.max_size()) ? static_cast<size_type>(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<char>& operator<<(std::basic_ostream<char>& os, const String& str)
 | |
| 	{
 | |
| 		return os << str.to_string();
 | |
| 	}
 | |
| 
 | |
| 	inline std::basic_istream<char>& operator>>(std::basic_istream<char>& is, String& str)
 | |
| 	{
 | |
| 		std::string tmp;
 | |
| 		if (is >> tmp)
 | |
| 		{
 | |
| 			str = tmp;
 | |
| 		}
 | |
| 		return is;
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// details of to_string functions
 | |
| 	//
 | |
| 
 | |
| 	namespace __to_string_detail
 | |
| 	{
 | |
| 		template<typename _Ty>
 | |
| 		inline String FloatingToString(const wchar_t *fmt, _Ty val);
 | |
| 
 | |
| 		template <typename _Ty>
 | |
| 		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<typename ..._Args>
 | |
| 	inline String format_wstring(const wchar_t* const fmt, _Args&&... args)
 | |
| 	{
 | |
| 		const auto len = static_cast<String::size_type>(::_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<typename _Ty>
 | |
| 		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 <typename _Ty>
 | |
| 		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) noexcept
 | |
| 	{
 | |
| 		lhs.swap(rhs);
 | |
| 	}
 | |
| }
 |