VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
grvectors.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2017 Graeme Walker
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 // grvectors.cpp
19 //
20 
21 #include "gdef.h"
22 #include "grvectors.h"
23 #include "glimits.h"
24 #include "gtest.h"
25 #include "gdebug.h"
26 #include <algorithm>
27 #include <stdexcept>
28 #include <limits>
29 
31  m_vectors(vectors) ,
32  m_current(m_vectors.begin()) ,
33  m_end(m_vectors.end()) ,
34  m_size(imagebuffer::size_of(m_vectors))
35 {
36  if( m_current != m_end )
37  setg_imp( cbuf_head() , cbuf_head() , cbuf_tail() ) ;
38 }
39 
41 {
42 }
43 
45 {
46  if( m_vectors.empty() || m_current == m_end )
47  return EOF ;
48 
49  if( gptr() == egptr() )
50  {
51  next_buf() ;
52  setg_imp( cbuf_head() , cbuf_head() , cbuf_tail() ) ;
53  }
54 
55  return m_current == m_end ? EOF : static_cast<int>(static_cast<unsigned char>(*gptr())) ;
56 }
57 
58 const char * Gr::imp::vectors_streambuf::cbuf_head() const
59 {
60  return m_current == m_vectors.end() ? nullptr : &(*m_current)[0] ;
61 }
62 
63 const char * Gr::imp::vectors_streambuf::cbuf_tail() const
64 {
65  return m_current == m_vectors.end() ? nullptr : ( &(*m_current)[0] + (*m_current).size() ) ;
66 }
67 
68 size_t Gr::imp::vectors_streambuf::cbuf_available() const
69 {
70  return cbuf_available( gptr() ) ;
71 }
72 
73 size_t Gr::imp::vectors_streambuf::cbuf_available( const char * g ) const
74 {
75  G_ASSERT( g == nullptr || ( g >= cbuf_head() && g <= cbuf_tail() ) ) ;
76  return ( g >= cbuf_head() && g < cbuf_tail() ) ? (cbuf_tail()-g) : 0U ;
77 }
78 
79 size_t Gr::imp::vectors_streambuf::cbuf_offset( const char * g ) const
80 {
81  G_ASSERT( g == nullptr || ( g >= cbuf_head() && g <= cbuf_tail() ) ) ;
82  return ( g >= cbuf_head() && g < cbuf_tail() ) ? (g-cbuf_head()) : 0U ;
83 }
84 
85 size_t Gr::imp::vectors_streambuf::cbuf_offset() const
86 {
87  return cbuf_offset( gptr() ) ;
88 }
89 
90 bool Gr::imp::vectors_streambuf::next_buf()
91 {
92  for( ++m_current ; m_current != m_end && (*m_current).empty() ; )
93  ++m_current ;
94  return m_current != m_end ;
95 }
96 
97 std::streamsize Gr::imp::vectors_streambuf::xsgetn( char * p , std::streamsize n )
98 {
99 #if 0
100  // use the base class to be slow-but-sure
101  return std::streambuf::xsgetn( p , n ) ;
102 #else
103 
104  G_ASSERT( gptr() >= cbuf_head() && gptr() <= cbuf_tail() ) ;
105 
106  const char * g = gptr() ;
107  if( n > 0 && g == cbuf_tail() )
108  {
109  next_buf() ;
110  g = cbuf_head() ;
111  }
112 
113  std::streamsize rc = 0U ;
114  size_t x = 0U ; // last memcpy size
115  size_t co = cbuf_offset( g ) ; // offset into original buffer
116  size_t a = cbuf_available( g ) ;
117 
118  while( n > 0 && a > 0 )
119  {
120  x = std::min( static_cast<size_t>(n) , a ) ;
121  std::memcpy( p , g , x ) ;
122  p += x ;
123  n -= x ;
124  rc += x ;
125  if( n > 0 )
126  {
127  co = 0U ;
128  if( !next_buf() ) break ;
129  g = &(*m_current)[0] ;
130  a = (*m_current).size() ;
131  }
132  }
133 
134  if( n > 0 )
135  {
136  G_ASSERT( m_current == m_end ) ;
137  setg_imp( nullptr , nullptr , nullptr ) ;
138  }
139  else
140  {
141  G_ASSERT( m_current != m_end ) ;
142  G_ASSERT( (x+co) <= (*m_current).size() ) ;
143  setg_imp( cbuf_head() , cbuf_head()+co+x , cbuf_tail() ) ;
144  }
145  return rc ;
146 }
147 #endif
148 
149 void Gr::imp::vectors_streambuf::setg_imp( const char * start , const char * get , const char * end )
150 {
151  setg( const_cast<char*>(start) , const_cast<char*>(get) , const_cast<char*>(end) ) ;
152 }
153 
154 std::streampos Gr::imp::vectors_streambuf::seekpos( std::streampos pos , std::ios_base::openmode which )
155 {
156  return seekoff( pos , std::ios_base::beg , which ) ;
157 }
158 
159 std::streampos Gr::imp::vectors_streambuf::seekoff( std::streamoff off , std::ios_base::seekdir way , std::ios_base::openmode which )
160 {
161  if( which == std::ios_base::in )
162  {
163  if( way == std::ios_base::beg && off >= 0 )
164  {
165  return seek( off ) ;
166  }
167  else if( way == std::ios_base::end && off <= 0 && -off <= m_size )
168  {
169  return seek( m_size + off ) ;
170  }
171  else if( way == std::ios_base::cur )
172  {
173  return seek( pos() + off ) ;
174  }
175  }
176  return -1 ;
177 }
178 
179 std::streampos Gr::imp::vectors_streambuf::pos() const
180 {
181  std::streampos n = 0 ;
182  for( Vectors::const_iterator p = m_vectors.begin() ; p != m_current && p != m_vectors.end() ; ++p )
183  {
184  n += (*p).size() ;
185  }
186  return n + gptr() - eback() ;
187 }
188 
189 std::streampos Gr::imp::vectors_streambuf::seek( std::streamoff off )
190 {
191  if( off > m_size )
192  {
193  return -1 ;
194  }
195  if( off == m_size )
196  {
197  setg_imp( nullptr , nullptr , nullptr ) ;
198  return m_size ; // ?
199  }
200  else if( off < 0 )
201  {
202  return -1 ;
203  }
204  else
205  {
206  Vectors::const_iterator p = m_vectors.begin() ;
207  for( ; p != m_end && off >= static_cast<std::streamoff>((*p).size()) ; ++p )
208  {
209  off -= (*p).size() ;
210  }
211  if( p == m_end )
212  {
213  setg_imp( nullptr , nullptr , nullptr ) ;
214  return -1 ;
215  }
216  else
217  {
218  m_current = p ;
219  G_ASSERT( size_t(off) < (*m_current).size() ) ;
220  setg_imp( cbuf_head() , cbuf_head()+off , cbuf_tail() ) ;
221  return off ;
222  }
223  }
224 }
225 
226 // ==
227 
228 namespace
229 {
230  template <typename T>
231  size_t to_sizet( T n , bool do_throw = true , size_t default_ = 0U )
232  {
233  if( n <= 0 )
234  return 0U ;
235 
236  size_t s = static_cast<size_t>(n) ; // narrow
237 
238  bool failed = false ;
239  if( (s+1U) == 0U )
240  failed = true ; // eg. streamoff(~size_t(0))
241 
242  const size_t top_bit = size_t(1U) << (sizeof(size_t)*8-1) ;
243  if( (s+1U) & top_bit )
244  failed = true ; // eg. streamoff(size_t(-1))
245 
246  if( sizeof(T) > sizeof(n) )
247  {
248  const T nn = static_cast<T>(s) ; // widen back
249  if( n != nn )
250  failed = true ;
251  }
252 
253  if( failed )
254  {
255  if( do_throw )
256  throw std::runtime_error( "numeric overflow" ) ;
257  else
258  s = default_ ;
259  }
260 
261  return s ;
262  }
263 }
264 
265 std::istream & operator>>( std::istream & stream , Gr::Vectors & vectors )
266 {
267  size_t buffer_size = static_cast<size_t>( G::limits::file_buffer ) ;
268  if( G::Test::enabled("tiny-image-buffers") )
269  buffer_size = size_t(10U) ;
270 
271  // get the stream size
272  size_t stream_size = 0U ;
273  if( vectors.empty() )
274  {
275  std::streampos pos = stream.tellg() ;
276  if( pos >= 0 )
277  {
278  stream.seekg( 0 , std::ios_base::end ) ;
279  std::streampos endpos = stream.tellg() ;
280  if( endpos >= 0 && endpos > pos )
281  {
282  std::streamoff diff = endpos - pos ;
283  stream_size = to_sizet( diff , false ) ;
284  }
285  stream.seekg( pos ) ;
286  if( stream.tellg() != pos )
287  throw std::runtime_error( "Gr::Vectors::operator>>: stream repositioning error" ) ;
288  }
289  }
290 
291  // reserve the outer vector
292  if( stream_size > 0U )
293  {
294  const size_t reservation = (stream_size+buffer_size-1U) / buffer_size ;
295  const size_t sanity = 100000U ;
296  if( reservation < sanity )
297  vectors.reserve( std::min(vectors.max_size(),reservation) ) ;
298  }
299 
300  // read into existing chunks, or add new chunks as necessary -- try to
301  // match the stream size exactly, but still allow for the stream to be
302  // bigger than its pre-determined size
303  //
304  G_ASSERT( (stream_size+1U) != 0U ) ;
305  size_t i = 0U ;
306  size_t n = stream_size ; // expected size remaining still to read
307  bool over_n = false ; // already got what was expected
308  for( ; stream.good() ; i++ )
309  {
310  if( i >= vectors.size() )
311  {
312  vectors.push_back( std::vector<char>() ) ; // emplace_back
313  std::vector<char> & buffer = vectors.back() ;
314  if( n > 0U && !over_n )
315  buffer.resize( std::min(buffer_size,n+1U) ) ; // add one to avoid an extra go round at eof
316  else
317  buffer.resize( buffer_size ) ;
318  }
319 
320  if( !vectors[i].empty() )
321  {
322  stream.read( &(vectors[i])[0] , vectors[i].size() ) ;
323  std::streamsize stream_gcount = stream.gcount() ;
324  if( stream_gcount <= 0 )
325  break ;
326 
327  size_t gcount = to_sizet( stream_gcount ) ;
328  vectors[i].resize( gcount ) ;
329 
330  if( gcount >= n )
331  n = 0U , over_n = true ;
332  else
333  n -= gcount ;
334  }
335  }
336  vectors.resize( i ) ;
337 
338  // read() sets the failbit if asked to read more than what's
339  // available, but we only want eof in that case
340  if( stream.eof() && stream.fail() && !stream.bad() )
341  stream.clear( std::ios_base::eofbit ) ;
342 
343  return stream ;
344 }
345 
346 std::ostream & operator<<( std::ostream & stream , const Gr::Vectors & vectors )
347 {
348  Gr::Vectors::const_iterator const end = vectors.end() ;
349  for( Gr::Vectors::const_iterator p = vectors.begin() ; p != end ; ++p )
350  stream.write( &(*p)[0] , (*p).size() ) ;
351  return stream ;
352 }
353 
354 /// \file grvectors.cpp
std::vector< std::vector< char > > Vectors
A candidate for Gr::ImageBuffer that uses nested std::vectors.
Definition: grvectors.h:39
virtual std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which) override
Override from std::streambuf.
Definition: grvectors.cpp:159
virtual std::streampos seekpos(std::streampos pos, std::ios_base::openmode which) override
Override from std::streambuf.
Definition: grvectors.cpp:154
static bool enabled()
Returns true if test features are enabled.
Definition: gtest.cpp:49
virtual std::streamsize xsgetn(char *s, std::streamsize n) override
Override from std::streambuf.
Definition: grvectors.cpp:97
virtual int underflow() override
Override from std::streambuf.
Definition: grvectors.cpp:44
virtual ~vectors_streambuf()
Destructor.
Definition: grvectors.cpp:40
vectors_streambuf(const Vectors &)
Constructor.
Definition: grvectors.cpp:30