// This file is part of the ustl library, an STL implementation. // // Copyright (C) 2005 by Mike Sharov <msharov@users.sourceforge.net> // This file is free software, distributed under the MIT License. // // mostream.h #ifndef MOSTREAM_H_24A8C5397E0848216573E5670930FC9A #define MOSTREAM_H_24A8C5397E0848216573E5670930FC9A #include "memlink.h" #include "uassert.h" #include "uexception.h" #include "utf8.h" #include "uios.h" #include <typeinfo> namespace ustl { class istream; class string; /// \class ostream mostream.h ustl.h /// \ingroup BinaryStreams /// /// \brief Helper class to write packed binary streams. /// /// This class contains a set of functions to write integral types into an /// unstructured memory block. Packing binary file data can be done this /// way, for instance. aligning the data is your responsibility, and can /// be accomplished by proper ordering of writes and by calling \ref ostream::align. /// Unaligned access is usually slower by orders of magnitude and, /// on some architectures, such as PowerPC, can cause your program to crash. /// Therefore, all write functions have asserts to check alignment. /// See \ref istream documentation for rules on designing your data format. /// Overwriting the end of the stream will also cause a crash (an assert in /// debug builds). Oh, and don't be intimidated by the size of the inlines /// here. In the assembly code the compiler will usually chop everything down /// to five instructions each. /// /// Example code: /// \code /// memblock b; /// ostream os (b); /// os << boolVar << ios::talign<int>(); /// os << intVar << floatVar; /// os.write (binaryData, binaryDataSize); /// os.align(); /// b.resize (os.pos()); /// b.write_file ("test.file"); /// \endcode /// class ostream : public memlink, public ios_base { public: ostream (void); ostream (void* p, size_type n); explicit ostream (const memlink& source); inline iterator end (void) { return (memlink::end()); } inline const_iterator end (void) const { return (memlink::end()); } inline void seek (uoff_t newPos); inline void iseek (const_iterator newPos); inline void skip (size_type nBytes); inline uoff_t pos (void) const { return (m_Pos); } inline iterator ipos (void) { return (begin() + pos()); } inline const_iterator ipos (void) const { return (begin() + pos()); } inline size_type remaining (void) const; inline bool aligned (size_type grain = c_DefaultAlignment) const; void verify_remaining (const char* op, const char* type, size_t n) const; inline size_type align_size (size_type grain = c_DefaultAlignment) const; void align (size_type grain = c_DefaultAlignment); void write (const void* buffer, size_type size); inline void write (const cmemlink& buf); void write_strz (const char* str); void read (istream& is); inline void write (ostream& os) const { os.write (begin(), pos()); } void text_write (ostringstream& os) const; inline size_t stream_size (void) const { return (pos()); } void insert (iterator start, size_type size); void erase (iterator start, size_type size); void swap (ostream& os); template <typename T> inline void iwrite (const T& v); inline virtual size_type overflow (size_type = 1){ return (remaining()); } virtual void unlink (void); inline void link (void* p, size_type n) { memlink::link (p, n); } inline void link (memlink& l) { memlink::link (l.data(), l.writable_size()); } inline void link (void* f, void* l) { memlink::link (f, l); } OVERLOAD_POINTER_AND_SIZE_T_V2(link, void*) inline void relink (void* p, size_type n) { memlink::relink (p, n); m_Pos = 0; } inline void relink (memlink& l) { relink (l.data(), l.writable_size()); } inline void seekp (off_t p, seekdir d = beg); inline off_t tellp (void) const { return (pos()); } protected: inline void SetPos (uoff_t newPos) { m_Pos = newPos; } private: uoff_t m_Pos; ///< Current write position. }; //---------------------------------------------------------------------- /// \class ostream_iterator mostream.h ustl.h /// \ingroup BinaryStreamIterators /// /// \brief An iterator over an ostream to use with uSTL algorithms. /// template <typename T, typename Stream = ostream> class ostream_iterator { public: typedef T value_type; typedef ptrdiff_t difference_type; typedef value_type* pointer; typedef value_type& reference; typedef ostream::size_type size_type; public: inline explicit ostream_iterator (Stream& os) : m_Os (os) {} inline ostream_iterator (const ostream_iterator& iter) : m_Os (iter.m_Os) {} /// Writes \p v into the stream. inline ostream_iterator& operator= (const T& v) { m_Os << v; return (*this); } inline ostream_iterator& operator* (void) { return (*this); } inline ostream_iterator& operator++ (void) { return (*this); } inline ostream_iterator operator++ (int) { return (*this); } inline ostream_iterator& operator+= (size_type n) { m_Os.skip (n); return (*this); } inline bool operator== (const ostream_iterator& i) const { return (m_Os.pos() == i.m_Os.pos()); } inline bool operator< (const ostream_iterator& i) const { return (m_Os.pos() < i.m_Os.pos()); } private: Stream& m_Os; }; //---------------------------------------------------------------------- typedef ostream_iterator<utf8subchar_t> ostream_iterator_for_utf8; typedef utf8out_iterator<ostream_iterator_for_utf8> utf8ostream_iterator; /// Returns a UTF-8 adaptor writing to \p os. inline utf8ostream_iterator utf8out (ostream& os) { ostream_iterator_for_utf8 si (os); return (utf8ostream_iterator (si)); } //---------------------------------------------------------------------- /// Move the write pointer to \p newPos inline void ostream::seek (uoff_t newPos) { #ifdef WANT_STREAM_BOUNDS_CHECKING if (newPos > size()) throw stream_bounds_exception ("seekp", "byte", pos(), newPos - pos(), size()); #else assert (newPos <= size()); #endif SetPos (newPos); } /// Sets the current write position to \p newPos inline void ostream::iseek (const_iterator newPos) { seek (distance (begin(), const_cast<iterator>(newPos))); } /// Sets the current write position to \p p based on \p d. inline void ostream::seekp (off_t p, seekdir d) { switch (d) { case beg: seek (p); break; case cur: seek (pos() + p); break; case ios_base::end: seek (size() - p); break; } } /// Skips \p nBytes without writing anything. inline void ostream::skip (size_type nBytes) { seek (pos() + nBytes); } /// Returns number of bytes remaining in the write buffer. inline ostream::size_type ostream::remaining (void) const { return (size() - pos()); } /// Returns \c true if the write pointer is aligned on \p grain inline bool ostream::aligned (size_type grain) const { assert (uintptr_t(begin()) % grain == 0 && "Streams should be attached aligned at the maximum element grain to avoid bus errors."); return (pos() % grain == 0); } /// Returns the number of bytes to skip to be aligned on \p grain. inline ostream::size_type ostream::align_size (size_type grain) const { return (Align (pos(), grain) - pos()); } /// Writes the contents of \p buf into the stream as a raw dump. inline void ostream::write (const cmemlink& buf) { write (buf.begin(), buf.size()); } /// Writes type T into the stream via a direct pointer cast. template <typename T> inline void ostream::iwrite (const T& v) { assert (aligned (alignof (v))); #ifdef WANT_STREAM_BOUNDS_CHECKING verify_remaining ("write", typeid(v).name(), sizeof(T)); #else assert (remaining() >= sizeof(T)); #endif *reinterpret_cast<T*>(ipos()) = v; SetPos (pos() + sizeof(T)); } #define OSTREAM_OPERATOR(type) \ inline ostream& operator<< (ostream& os, type v) { os.iwrite(v); return (os); } template <typename T> OSTREAM_OPERATOR(T*) OSTREAM_OPERATOR(int8_t) OSTREAM_OPERATOR(uint8_t) OSTREAM_OPERATOR(int16_t) OSTREAM_OPERATOR(uint16_t) OSTREAM_OPERATOR(int32_t) OSTREAM_OPERATOR(uint32_t) OSTREAM_OPERATOR(float) OSTREAM_OPERATOR(double) OSTREAM_OPERATOR(wchar_t) #if SIZE_OF_BOOL == SIZE_OF_CHAR OSTREAM_OPERATOR(bool) #else inline ostream& operator<< (ostream& os, bool v) { os.iwrite (uint8_t(v)); return (os); } #endif #if HAVE_THREE_CHAR_TYPES OSTREAM_OPERATOR(char) #endif #if HAVE_INT64_T OSTREAM_OPERATOR(int64_t) OSTREAM_OPERATOR(uint64_t) #endif #if SIZE_OF_LONG == SIZE_OF_INT OSTREAM_OPERATOR(long) OSTREAM_OPERATOR(unsigned long) #endif #if HAVE_LONG_LONG && (!HAVE_INT64_T || SIZE_OF_LONG_LONG > 8) OSTREAM_OPERATOR(long long) OSTREAM_OPERATOR(unsigned long long) #endif } // namespace ustl #endif