LCOV - code coverage report
Current view: top level - snapdev - vector_streambuf.h (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 64 64
Test Date: 2026-02-16 17:48:13 Functions: 100.0 % 5 5
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright (c) 2019-2026  Made to Order Software Corp.  All Rights Reserved
       2              : //
       3              : // https://snapwebsites.org/project/snapdev
       4              : // contact@m2osw.com
       5              : //
       6              : // This program is free software: you can redistribute it and/or modify
       7              : // it under the terms of the GNU General Public License as published by
       8              : // the Free Software Foundation, either version 3 of the License, or
       9              : // (at your option) any later version.
      10              : //
      11              : // This program is distributed in the hope that it will be useful,
      12              : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13              : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14              : // GNU General Public License for more details.
      15              : //
      16              : // You should have received a copy of the GNU General Public License
      17              : // along with this program.  If not, see <https://www.gnu.org/licenses/>.
      18              : #pragma once
      19              : 
      20              : /** \file
      21              :  * \brief Capture the output to an output stream in a buffer.
      22              :  *
      23              :  * We often use this template in our tests when we expect a function to
      24              :  * generate output in a given stream. This allows us to capture that output
      25              :  * and verify it once the function being tested returns.
      26              :  */
      27              : 
      28              : // snapdev
      29              : //
      30              : #include    <snapdev/not_used.h>
      31              : 
      32              : 
      33              : // C++
      34              : //
      35              : #include    <iostream>
      36              : #include    <ostream>
      37              : //#include    <sstream>
      38              : //#include    <string>
      39              : 
      40              : 
      41              : 
      42              : namespace snapdev
      43              : {
      44              : 
      45              : 
      46              : 
      47              : /** \brief Create a streambuf where the data comes from a vector.
      48              :  *
      49              :  * This class is used to create a streambuf from the data of a vector.
      50              :  * If you are looking at capturing the output of a stream, you may be
      51              :  * interested by the ostream_to_buf instead. That other implementation
      52              :  * allows you to save the output of a stream in a string and then verify
      53              :  * that string.
      54              :  *
      55              :  * This implementation was created for input rather than output, allowing
      56              :  * you to create a buffer from the data of a vector.
      57              :  *
      58              :  * \code
      59              :  * {
      60              :  *     // Say you have a vector with data
      61              :  *     std::vector<char> data{ 1, 2, 3, 4, 5, 6, 7 };
      62              :  *
      63              :  *     // you create a vector_streambuf this way
      64              :  *     snapdev::vector_streambuf buf(data);
      65              :  *
      66              :  *     // and then an input stream this way
      67              :  *     std::istream in(&buf);
      68              :  *
      69              :  *     // now you can read & seek as usual
      70              :  *
      71              :  *     // if you want to be able to do updates, use an iostream insead
      72              :  *     std::iostream in_out(&buf);
      73              :  * }
      74              :  * \endcode
      75              :  *
      76              :  * \note
      77              :  * The function expects the vector to only contain data that can be
      78              :  * read/written as such. This means no pointers, not classes with
      79              :  * virtual functions, etc.
      80              :  *
      81              :  * \warning
      82              :  * The vector must remain available and not change in size outside
      83              :  * of this stream buffer implementation.
      84              :  *
      85              :  * \todo
      86              :  * Verify that VecT is only composed of basic types.
      87              :  *
      88              :  * \tparam VecT  The type of the vector used here.
      89              :  * \tparam CharT  The type of characters used by the stream.
      90              :  * \tparam Traits  The traits of the specified character type.
      91              :  */
      92              : template<
      93              :       typename VecT
      94              :     , typename CharT = char
      95              :     , typename Traits = std::char_traits<CharT>
      96              : > class vector_streambuf
      97              :     : public std::basic_streambuf<CharT, Traits>
      98              : {
      99              : public:
     100              :     typedef CharT                       char_type;
     101              :     typedef Traits                      traits_type;
     102              :     typedef typename Traits::int_type   int_type;
     103              :     typedef typename Traits::pos_type   pos_type;
     104              :     typedef typename Traits::off_type   off_type;
     105              : 
     106              :     typedef std::basic_streambuf<char_type, traits_type>    streambuf_type;
     107              : 
     108              :     /** \brief Initialize the streambuf from \p vec.
     109              :      *
     110              :      * This function creates a streambuf that you can then use to initialize
     111              :      * an input or output stream with.
     112              :      *
     113              :      * This version of the constructor allows to read and write to the
     114              :      * stream buffer (a.k.a. the vector). You can use it with input or
     115              :      * an output stream.
     116              :      *
     117              :      * Note that the reading and writing happen from the start of the
     118              :      * buffer unless a corresponding seek is used.
     119              :      *
     120              :      * \todo
     121              :      * Note that the overflow() function does not yet know how to grow
     122              :      * the vector. If you attempt to write past the end, an exception
     123              :      * is generated.
     124              :      *
     125              :      * \param[in] vec  The vector to use with the streambuf.
     126              :      */
     127            2 :     vector_streambuf(VecT & vec)
     128            2 :         : f_vector(vec)
     129              :     {
     130            2 :         char_type * begin(const_cast<char_type *>(reinterpret_cast<char_type const *>(vec.data())));
     131            2 :         char_type * end(begin + vec.size() * sizeof(typename VecT::value_type));
     132            2 :         this->setg(begin, begin, end);
     133            2 :         this->setp(begin, end);
     134            2 :     }
     135              : 
     136              :     /** \brief Initialize the streambuf from \p vec.
     137              :      *
     138              :      * This function creates a read-only streambuf that you can then use
     139              :      * to initialize an input stream with. Trying to write to that stream
     140              :      * generates an exception.
     141              :      *
     142              :      * \param[in] vec  The vector to use with the streambuf.
     143              :      */
     144            1 :     vector_streambuf(VecT const & vec)
     145            1 :         : f_read_only(true)
     146            1 :         , f_vector(const_cast<VecT &>(vec))
     147              :     {
     148            1 :         char_type * begin(const_cast<char_type *>(reinterpret_cast<char_type const *>(vec.data())));
     149            1 :         char_type * end(begin + vec.size() * sizeof(typename VecT::value_type));
     150            1 :         this->setg(begin, begin, end);
     151              : 
     152              :         // a write calls overflow() if the start & end pointers are the same
     153              :         //
     154            1 :         this->setp(begin, begin);
     155            1 :     }
     156              : 
     157              : protected:
     158              :     /** \brief Function called each time the output is read.
     159              :      *
     160              :      * This function is called to write one element to the vector.
     161              :      * The function saves the input character to the vector.
     162              :      *
     163              :      * \exception std::ios_base::failure
     164              :      * If the vector is read-only (const when creating the streambuf)
     165              :      * then this exception is raised. At the moment, this function is
     166              :      * not capable to grow the vector. If the end of the vector is
     167              :      * reached and one more character is written, then this exception
     168              :      * is raised.
     169              :      *
     170              :      * \param[in] c  The character to output.
     171              :      *
     172              :      * \return The input character \p c or '\0' of \p c is EOF.
     173              :      */
     174            3 :     int_type overflow(int_type c)
     175              :     {
     176            3 :         snapdev::NOT_USED(c);
     177              :         //if(f_read_only)
     178              :         {
     179            3 :             throw std::ios_base::failure("this buffer is read-only, writing to the buffer is not available.");
     180              :         }
     181              : 
     182              :         // TODO: implement growth whenever possible
     183              : 
     184              :         //if(traits_type::eq_int_type(c, Traits::eof()))
     185              :         //{
     186              :         //    return 0;
     187              :         //}
     188              : 
     189              :         //std::size_t const size(this->pptr() - this->pbase());
     190              :         //if(size >= f_vector.size())
     191              :         //{
     192              :         //    // TODO: grow the buffer
     193              :         //    //f_vector.push_back(c);
     194              :         //    //return 0;
     195              :         //    throw std::ios_base::failure("the buffer is full.");
     196              :         //}
     197              : 
     198              :         //*this->pptr() = c;
     199              :         //this->pbump(1);
     200              : 
     201              :         //return c;
     202              :     }
     203              : 
     204           45 :     virtual pos_type seekoff(
     205              :           off_type const offset
     206              :         , std::ios_base::seekdir const dir
     207              :         , std::ios_base::openmode const mode) override
     208              :     {
     209           45 :         off_type result(-1);
     210              : 
     211           45 :         if((mode & std::ios_base::in) != 0)
     212              :         {
     213           32 :             char_type * pos = nullptr;
     214           32 :             switch(dir)
     215              :             {
     216           24 :             case std::ios_base::cur:
     217           24 :                 pos = this->gptr() + offset;
     218           24 :                 break;
     219              : 
     220            1 :             case std::ios_base::end:
     221            1 :                 pos = this->egptr() + offset;
     222            1 :                 break;
     223              : 
     224            6 :             case std::ios_base::beg:
     225            6 :                 pos = this->eback() + offset;
     226            6 :                 break;
     227              : 
     228            1 :             default:
     229            1 :                 throw std::ios_base::failure("unknown direction in seekoff() -- in");
     230              : 
     231              :             }
     232           31 :             if(pos < this->eback())
     233              :             {
     234            1 :                 pos = this->eback();
     235              :             }
     236           31 :             if(pos > this->egptr())
     237              :             {
     238            1 :                 pos = this->egptr();
     239              :             }
     240           31 :             this->setg(this->eback(), pos, this->egptr());
     241           31 :             result = pos - this->eback();
     242              :         }
     243              : 
     244           44 :         if((mode & std::ios_base::out) != 0)
     245              :         {
     246           13 :             char_type * pos = nullptr;
     247           13 :             switch(dir)
     248              :             {
     249            7 :             case std::ios_base::cur:
     250            7 :                 pos = this->pptr() + offset;
     251            7 :                 break;
     252              : 
     253            1 :             case std::ios_base::end:
     254            1 :                 pos = this->epptr() + offset;
     255            1 :                 break;
     256              : 
     257            4 :             case std::ios_base::beg:
     258            4 :                 pos = this->pbase() + offset;
     259            4 :                 break;
     260              : 
     261            1 :             default:
     262            1 :                 throw std::ios_base::failure("unknown direction in seekpos() -- out");
     263              : 
     264              :             }
     265           12 :             if(pos < this->pbase())
     266              :             {
     267            1 :                 pos = this->pbase();
     268              :             }
     269           12 :             if(pos > this->epptr())
     270              :             {
     271            2 :                 pos = this->epptr();
     272              :             }
     273           12 :             this->setp(this->pbase(), this->epptr());
     274           12 :             result = pos - this->pbase();
     275           12 :             this->pbump(result);
     276              :         }
     277              : 
     278           86 :         return result;
     279              :     }
     280              : 
     281           10 :     virtual pos_type seekpos(pos_type offset, std::ios_base::openmode mode) override
     282              :     {
     283           10 :         return seekoff(offset, std::ios_base::beg, mode);
     284              :     }
     285              : 
     286              : private:
     287              :     /** \brief Whether the buffer is considered read-only or not.
     288              :      *
     289              :      * When creating the streambuf with a constant vector, this parameter
     290              :      * is set to true. This prevents writes to the file. In other words,
     291              :      * the vector will never be updated.
     292              :      */
     293              :     bool                f_read_only = false;
     294              : 
     295              :     /** \brief A reference back to the user's vector.
     296              :      *
     297              :      * This holds the reference to the vector. It is used in case you
     298              :      * try to write more data than the existing vector supports. In that
     299              :      * case, we need access to the vector to grow it.
     300              :      */
     301              :     VecT &              f_vector = VecT();
     302              : };
     303              : 
     304              : 
     305              : 
     306              : } // namespace snapdev
     307              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

Snap C++ | List of projects | List of versions