E-MailRelay

gfbuf.h
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gfbuf.h
19///
20
21#ifndef G_FBUF_H
22#define G_FBUF_H
23
24#include "gdef.h"
25#include <streambuf>
26#include <functional>
27#include <vector>
28
29 namespace G
30{
31 template <typename T, int N=1024> class fbuf ;
32}
33
34//| \class G::fbuf
35/// A simple file streambuf using a "file descriptor" and three
36/// function pointers for read, write and close operations.
37///
38/// The file descriptor type is templated to allow for non-integer
39/// file descriptors, such as std::FILE. Does not support seeking.
40///
41/// Eg:
42/// \code
43/// G::fbuf<int,1024> fbuf( ::open("temp.out",O_WRONLY) , ::read , ::write , ::close ) ;
44/// std::ostream stream( &fbuf ) ;
45/// if( fbuf.file() < 0 )
46/// stream.clear( std::ios_base::failbit ) ;
47/// stream << "hello, world!\n" ;
48/// \endcode
49///
50/// The implementation inherits from std::streambuf, overriding
51/// overflow(), underflow() and sync() to operate the internal
52/// character buffer and file descriptor.
53///
54 template <typename T, int N>
55 class G::fbuf : public std::streambuf
56{
57 public:
58 using read_fn_t = std::function<ssize_t(T,char*,std::size_t)> ;
59 using write_fn_t = std::function<ssize_t(T,const char*,std::size_t)> ;
60 using close_fn_t = std::function<void(T)> ; // noexcept
61
62 explicit fbuf( read_fn_t , write_fn_t , close_fn_t ) ;
63 ///< Constructor. Use open() to initialise.
64
65 explicit fbuf( T file , read_fn_t , write_fn_t , close_fn_t ) ;
66 ///< Constructor passed an open file descriptor.
67
68 ~fbuf() override ;
69 ///< Destructor. Closes the file.
70
71 void open( T file ) ;
72 ///< Installs the given file descriptor.
73
74 T file() const ;
75 ///< Returns the current file descriptor.
76
77 protected:
78 int overflow( int c ) override ;
79 ///< Called to put a character into the output buffer.
80
81 int underflow() override ;
82 ///< Called to pull a character out of the input buffer,
83 ///< and pre-fill the input buffer if necessary.
84
85 int sync() final ;
86 ///< Called to sync the stream.
87
88public:
89 fbuf( const fbuf<T,N> & ) = delete ;
90 fbuf( fbuf<T,N> && ) = delete ;
91 fbuf<T,N> & operator=( const fbuf<T,N> & ) = delete ;
92 fbuf<T,N> & operator=( fbuf<T,N> && ) = delete ;
93
94private:
95 using traits_type = std::streambuf::traits_type ;
96 void close() ;
97
98private:
99 static constexpr int sync_ok = 0 ;
100 static constexpr int sync_fail = -1 ;
101 read_fn_t m_read_fn ;
102 write_fn_t m_write_fn ;
103 close_fn_t m_close_fn ;
104 std::vector<char> m_input ;
105 std::vector<char> m_output ;
106 bool m_file_open ;
107 T m_file ;
108} ;
109
110template <typename T,int N>
111 G::fbuf<T,N>::fbuf( G::fbuf<T,N>::read_fn_t read , G::fbuf<T,N>::write_fn_t write , G::fbuf<T,N>::close_fn_t close ) :
112 m_read_fn(read) ,
113 m_write_fn(write) ,
114 m_close_fn(close) ,
115 m_input(static_cast<std::size_t>(N)) ,
116 m_output(static_cast<std::size_t>(N)) ,
117 m_file_open(false) ,
118 m_file()
119{
120 static_assert( N > 0 , "" ) ;
121}
122
123 template <typename T,int N>
124 G::fbuf<T,N>::fbuf( T file , G::fbuf<T,N>::read_fn_t read , G::fbuf<T,N>::write_fn_t write , G::fbuf<T,N>::close_fn_t close ) :
125 m_read_fn(read) ,
126 m_write_fn(write) ,
127 m_close_fn(close) ,
128 m_input(static_cast<std::size_t>(N)) ,
129 m_output(static_cast<std::size_t>(N)) ,
130 m_file_open(false) ,
131 m_file()
132{
133 static_assert( N > 0 , "" ) ;
134 open( file ) ;
135}
136
137 namespace G
138{
139 template <typename T,int N>
140 fbuf<T,N>::~fbuf()
141 {
142 try
143 {
144 close() ;
145 }
146 catch(...) // dtor
147 {
148 }
149 }
150}
151
152 template <typename T,int N>
153 void G::fbuf<T,N>::open( T file )
154{
155 close() ;
156
157 m_file = file ;
158 m_file_open = true ;
159
160 char * input_begin = m_input.data() ;
161 setg( input_begin , input_begin , input_begin ) ;
162
163 char * output_begin = m_output.data() ;
164 char * output_end = output_begin + m_output.size() ;
165 setp( output_begin , output_end-1 ) ;
166}
167
168 template <typename T,int N>
169 void G::fbuf<T,N>::close()
170{
171 if( m_file_open )
172 {
173 sync() ;
174 m_close_fn( m_file ) ;
175 m_file_open = false ;
176 }
177}
178
179 template <typename T,int N>
180 int G::fbuf<T,N>::overflow( int c )
181{
182 if( !traits_type::eq_int_type( c , traits_type::eof() ) )
183 {
184 *pptr() = traits_type::to_char_type( c ) ;
185 pbump( 1 ) ;
186 }
187 return sync() == sync_fail ? traits_type::eof() : traits_type::not_eof(c) ;
188}
189
190 template <typename T,int N>
191 int G::fbuf<T,N>::sync()
192{
193 if( pbase() == pptr() )
194 {
195 return sync_ok ;
196 }
197 else
198 {
199 std::size_t size = pptr() - pbase() ;
200 ssize_t nwrite = m_write_fn( m_file , pbase() , size ) ;
201 if( nwrite <= 0 )
202 {
203 return sync_fail ;
204 }
205 else if( static_cast<std::size_t>(nwrite) < size )
206 {
207 std::copy( pbase()+nwrite , pptr() , pbase() ) ;
208 setp( pbase() , epptr() ) ; // reset pptr()
209 pbump( static_cast<int>(size-nwrite) ) ;
210 return sync_fail ;
211 }
212 else
213 {
214 setp( pbase() , epptr() ) ;
215 return sync_ok ;
216 }
217 }
218}
219
220 template <typename T,int N>
221 int G::fbuf<T,N>::underflow()
222{
223 if( gptr() == egptr() )
224 {
225 char * input_begin = m_input.data() ;
226 ssize_t nread = m_read_fn( m_file , input_begin , m_input.size() ) ;
227 if( nread <= 0 )
228 return traits_type::eof() ;
229 std::size_t nreadu = nread >= 0 ? static_cast<std::size_t>(nread) : std::size_t(0U) ;
230 setg( input_begin , input_begin , input_begin+nreadu ) ;
231 }
232 return traits_type::to_int_type( *gptr() ) ;
233}
234
235 template <typename T,int N>
236T G::fbuf<T,N>::file() const
237{
238 return m_file ;
239}
240
241#endif
G::fbuf
A simple file streambuf using a "file descriptor" and three function pointers for read,...
Definition: gfbuf.h:56
G::fbuf::underflow
int underflow() override
Called to pull a character out of the input buffer, and pre-fill the input buffer if necessary.
Definition: gfbuf.h:221
G::fbuf::open
void open(T file)
Installs the given file descriptor.
Definition: gfbuf.h:153
G::fbuf::overflow
int overflow(int c) override
Called to put a character into the output buffer.
Definition: gfbuf.h:180
G::fbuf::fbuf
fbuf(T file, read_fn_t, write_fn_t, close_fn_t)
Constructor passed an open file descriptor.
G::fbuf::~fbuf
~fbuf() override
Destructor. Closes the file.
Definition: gfbuf.h:140
G::fbuf::file
T file() const
Returns the current file descriptor.
Definition: gfbuf.h:236
G::fbuf::sync
int sync() final
Called to sync the stream.
Definition: gfbuf.h:191
G::fbuf::fbuf
fbuf(read_fn_t, write_fn_t, close_fn_t)
Constructor. Use open() to initialise.
G
Low-level classes.
Definition: garg.h:36
std
STL namespace.