/* * This file is a part of PikoTools * and is distributed under the (new) BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2012-2021, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name Tomasz Sowa nor the names of contributors to this * project may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef headerfile_picotools_textstream_textstream #define headerfile_picotools_textstream_textstream #include #include "stream.h" #include "space/space.h" #include "date/date.h" #include "convert/inttostr.h" #include "membuffer/membuffer.h" #include "types.h" #include "utf8/utf8.h" // for snprintf #include namespace pt { /* a special class representing a stream buffer similar to std::ostringstream StringType can be either std::string or std::wstring */ template class TextStreamBase : public Stream { public: TextStreamBase(); typedef CharT char_type; typedef MemBuffer buffer_type; typedef typename buffer_type::iterator iterator; typedef typename buffer_type::const_iterator const_iterator; bool is_char_stream() const; bool is_wchar_stream() const; void clear(); bool empty() const; size_t size() const; void reserve(size_t len); size_t capacity() const; iterator begin(); iterator end(); const_iterator begin() const; const_iterator end() const; // IMPROVE ME // add cbegin(), cend(), rbegin(), rend(), crbegin(), crend() void to_str(std::string & str, bool clear_string = true) const; void to_str(std::wstring & str, bool clear_string = true) const; std::string to_str() const; std::wstring to_wstr() const; char get_char(size_t index) const; wchar_t get_wchar(size_t index) const; char_type & operator[](size_t index); char_type operator[](size_t index) const; TextStreamBase & operator<<(const char * str); TextStreamBase & operator<<(const std::string & str); TextStreamBase & operator<<(const wchar_t * str); TextStreamBase & operator<<(const std::wstring & str); TextStreamBase & operator<<(char); TextStreamBase & operator<<(unsigned char); TextStreamBase & operator<<(wchar_t); TextStreamBase & operator<<(bool); TextStreamBase & operator<<(short); TextStreamBase & operator<<(int); TextStreamBase & operator<<(long); TextStreamBase & operator<<(long long); TextStreamBase & operator<<(unsigned short); TextStreamBase & operator<<(unsigned int); TextStreamBase & operator<<(unsigned long); TextStreamBase & operator<<(unsigned long long); TextStreamBase & operator<<(float); TextStreamBase & operator<<(double); TextStreamBase & operator<<(long double); TextStreamBase & operator<<(const void *); // printing a pointer TextStreamBase & operator<<(const Stream & stream); TextStreamBase & operator<<(const Space & space); TextStreamBase & operator<<(const Date & date); template TextStreamBase & operator<<(const TextStreamBase & arg); // min width for integer output // if the output value has less digits then first zeroes are added // (0 turn off) TextStreamBase & int_min_width(size_t min_width); TextStreamBase & write(const char * buf, size_t len); TextStreamBase & write(const wchar_t * buf, size_t len); TextStreamBase & fill_up_if_needed(wchar_t fill_up_char, size_t existing_length); /* raw access */ int radix; size_t min_width_for_integers; buffer_type buffer; protected: void put_stream(const Stream & stream); }; template TextStreamBase::TextStreamBase() { clear(); } template bool TextStreamBase::is_char_stream() const { if constexpr (sizeof(char_type) == sizeof(char) ) { return true; } return false; } template bool TextStreamBase::is_wchar_stream() const { if constexpr (sizeof(char_type) == sizeof(wchar_t) ) { return true; } return false; } template void TextStreamBase::clear() { radix = 10; min_width_for_integers = 0; buffer.clear(); } template bool TextStreamBase::empty() const { return buffer.empty(); } template size_t TextStreamBase::size() const { return buffer.size(); } template void TextStreamBase::reserve(size_t len) { buffer.reserve(len); } template size_t TextStreamBase::capacity() const { return buffer.capacity(); } template typename TextStreamBase::iterator TextStreamBase::begin() { return buffer.begin(); } template typename TextStreamBase::iterator TextStreamBase::end() { return buffer.end(); } template typename TextStreamBase::const_iterator TextStreamBase::begin() const { return buffer.begin(); } template typename TextStreamBase::const_iterator TextStreamBase::end() const { return buffer.end(); } template void TextStreamBase::to_str(std::string & str, bool clear_string) const { if( clear_string ) str.clear(); if( str.capacity() < str.size() + size() ) str.reserve(str.size() + size()); if constexpr (sizeof(char_type) == sizeof(char) ) { const_iterator i = begin(); for( ; i != end() ; ++i) str += *i; } else { wide_stream_to_utf8(*this, str); } } template void TextStreamBase::to_str(std::wstring & str, bool clear_string) const { if( clear_string ) str.clear(); if( str.capacity() < str.size() + size() ) str.reserve(str.size() + size()); if constexpr (sizeof(char_type) == sizeof(wchar_t) ) { const_iterator i = begin(); for( ; i != end() ; ++i) str += *i; } else { // IMPROVE ME don't use a temporary object std::string utf8; to_str(utf8); utf8_to_wide(utf8, str, false); } } template std::string TextStreamBase::to_str() const { std::string str; to_str(str, false); return str; } template std::wstring TextStreamBase::to_wstr() const { std::wstring str; to_str(str, false); return str; } template char TextStreamBase::get_char(size_t index) const { return static_cast(buffer[index]); } template wchar_t TextStreamBase::get_wchar(size_t index) const { return static_cast(buffer[index]); } template char_type & TextStreamBase::operator[](size_t index) { return buffer[index]; } template char_type TextStreamBase::operator[](size_t index) const { return buffer[index]; } template TextStreamBase & TextStreamBase::operator<<(const char * str) { if constexpr ( sizeof(char_type) == sizeof(char) ) { for( ; *str ; ++str) buffer.append(*str); } else { utf8_to_wide(str, *this, false); } return *this; } template TextStreamBase & TextStreamBase::operator<<(const std::string & str) { operator<<(str.c_str()); return *this; } template TextStreamBase & TextStreamBase::operator<<(const wchar_t * str) { if constexpr (sizeof(char_type) == sizeof(wchar_t) ) { for( ; *str ; ++str) buffer.append(*str); } else { wide_to_utf8(str, *this); } return *this; } template TextStreamBase & TextStreamBase::operator<<(const std::wstring & str) { operator<<(str.c_str()); return *this; } template TextStreamBase & TextStreamBase::operator<<(char v) { // IMPROVEME // if char_type == 1 then if v <= 127 then put that char but if (unsigned)v > 127 put replacement character // if char_type > 1 then simply put that character buffer.append(static_cast(v)); return *this; } template TextStreamBase & TextStreamBase::operator<<(unsigned char v) { // IMPROVEME // if char_type == 1 then if v <= 127 then put that char but if v > 127 put replacement character // if char_type > 1 then simply put that character buffer.append(static_cast(v)); return *this; } template TextStreamBase & TextStreamBase::operator<<(wchar_t v) { // IMPROVEME add utf8/wide conversion, if v is from surrogate pair we can skip it buffer.append(static_cast(v)); return *this; } template TextStreamBase & TextStreamBase::operator<<(bool v) { char c = v ? '1' : '0'; buffer.append(static_cast(c)); return *this; } template TextStreamBase & TextStreamBase::operator<<(short v) { return operator<<(static_cast(v)); } template TextStreamBase & TextStreamBase::operator<<(int v) { return operator<<(static_cast(v)); } template TextStreamBase & TextStreamBase::operator<<(long v) { return operator<<(static_cast(v)); } template TextStreamBase & TextStreamBase::operator<<(long long v) { char_type buf[50]; size_t len = sizeof(buf) / sizeof(char_type); size_t lenout; if( Toa(v, buf, len, radix, &lenout) ) { fill_up_if_needed('0', lenout); buffer.append(buf, lenout); } return *this; } template TextStreamBase & TextStreamBase::operator<<(unsigned short v) { return operator<<(static_cast(v)); } template TextStreamBase & TextStreamBase::operator<<(unsigned int v) { return operator<<(static_cast(v)); } template TextStreamBase & TextStreamBase::operator<<(unsigned long v) { return operator<<(static_cast(v)); } template TextStreamBase & TextStreamBase::operator<<(unsigned long long v) { char_type buf[50]; size_t len = sizeof(buf) / sizeof(char_type); size_t lenout; if( Toa(v, buf, len, radix, &lenout) ) { fill_up_if_needed('0', lenout); buffer.append(buf, lenout); } return *this; } template TextStreamBase & TextStreamBase::operator<<(float v) { return operator<<(static_cast(v)); } template TextStreamBase & TextStreamBase::operator<<(double v) { char buf[100]; snprintf(buf, sizeof(buf)/sizeof(char), "%g", v); return operator<<(buf); } template TextStreamBase & TextStreamBase::operator<<(long double v) { char buf[100]; snprintf(buf, sizeof(buf)/sizeof(char), "%Lg", v); return operator<<(buf); } template TextStreamBase & TextStreamBase::operator<<(const void * v) { char_type buf[50]; size_t len = sizeof(buf) / sizeof(char_type); size_t lenout; buf[0] = '0'; buf[1] = 'x'; // IMPROVE ME add some minimal width? if( Toa(reinterpret_cast(v), buf+2, len-2, 16, &lenout) ) buffer.append(buf, lenout+2); return *this; } template TextStreamBase & TextStreamBase::write(const char * str, size_t len) { if constexpr ( sizeof(char_type) == sizeof(char) ) { for(size_t i=0 ; i < len ; ++i) buffer.append(str[i]); } else { utf8_to_wide(str, *this, false); } return *this; } template TextStreamBase & TextStreamBase::write(const wchar_t * str, size_t len) { if constexpr (sizeof(char_type) == sizeof(wchar_t) ) { for(size_t i=0 ; i < len ; ++i) buffer.append(str[i]); } else { wide_to_utf8(str, *this); } return *this; } template TextStreamBase & TextStreamBase::operator<<(const Stream & stream) { put_stream(stream); return *this; } template void TextStreamBase::put_stream(const Stream & stream) { if( sizeof(char_type) == sizeof(char) && stream.is_char_stream() ) { // from utf8 to utf8 for(size_t i=0 ; i < stream.size() ; ++i) { char c = stream.get_char(i); operator<<(c); } } else if( sizeof(char_type) == sizeof(wchar_t) && stream.is_char_stream() ) { // from utf8 to wide utf8_to_wide(stream, *this, false); } else if( sizeof(char_type) == sizeof(char) && stream.is_wchar_stream() ) { // from wide to utf8 wide_stream_to_utf8(stream, *this, false); } else if( sizeof(char_type) == sizeof(wchar_t) && stream.is_wchar_stream() ) { // from wide to wide for(size_t i=0 ; i < stream.size() ; ++i) { wchar_t c = stream.get_wchar(i); operator<<(c); } } else { operator<<("such conversion is not implemented"); } } template TextStreamBase & TextStreamBase::operator<<(const Space & space) { space.serialize_to_space_stream(*this, true); return *this; } template TextStreamBase & TextStreamBase::operator<<(const Date & date) { date.Serialize(*this); return *this; } template template TextStreamBase & TextStreamBase::operator<<( const TextStreamBase & arg) { /* * in the future we can have a faster implementation * which uses iterators instead of get_char() and get_wchar() methods * */ put_stream(arg); return *this; } template TextStreamBase & TextStreamBase::int_min_width(size_t min_width) { min_width_for_integers = min_width; return *this; } template TextStreamBase & TextStreamBase::fill_up_if_needed(wchar_t fill_up_char, size_t existing_length) { if( min_width_for_integers > 0 && min_width_for_integers > existing_length ) { for(size_t i = existing_length ; i < min_width_for_integers ; ++i) { buffer.append(fill_up_char); } } return *this; } } // namespace #endif