nds2-client - ClientDeveloper  0.16.8
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
span_reader.hh
Go to the documentation of this file.
1 //
2 // Created by jonathan.hanks on 5/4/18.
3 //
4 
5 #ifndef NDS2_CLIENT_COMMONSPAN_READER_HH
6 #define NDS2_CLIENT_COMMONSPAN_READER_HH
7 
8 #include <algorithm>
9 #include <stdexcept>
10 
11 #include "common/utils.hh"
12 
13 namespace nds_impl
14 {
15  namespace common
16  {
17 
18  class SpanReader
19  {
20  public:
22 
23  SpanReader( span_type& s ) : s_{ s }, cur_{ 0 }
24  {
25  }
26 
27  char*
28  write_all( const char* begin, const char* end )
29  {
30  throw std::runtime_error(
31  "Write operations not supported on the spanreader" );
32  }
33 
41  char*
42  read_available( char* start, char* end )
43  {
44  span_type::size_type len_requested = end - start;
45 
46  if ( remaining( ) == 0 )
47  {
48  throw std::range_error( "No more data to read" );
49  }
50 
51  if ( remaining( ) < len_requested )
52  len_requested = remaining( );
53  std::copy( cur_data( ), cur_data( ) + len_requested, start );
54  consume( len_requested );
55  return start + len_requested;
56  }
57 
64  char*
65  read_exactly( char* start, const char* end )
66  {
67  span_type::size_type len_requested = end - start;
68 
69  auto result = peek( start, end );
70  consume( len_requested );
71  return result;
72  }
73 
84  char*
85  peek( char* start, const char* end )
86  {
87  span_type::size_type len_requested = end - start;
88 
89  while ( remaining( ) < len_requested )
90  {
91  throw std::range_error( "Insufficient data for request" );
92  }
93  std::copy( cur_data( ), cur_data( ) + len_requested, start );
94  return start + len_requested;
95  }
96 
108  template < typename It >
109  std::vector< char >
110  read_until( It begin_set, It end_set )
111  {
112  std::vector< char > result;
113 
114  span_type::size_type cur = 0;
115  auto rem = remaining( );
116  for ( bool match = false; !match; ++cur, --rem )
117  {
118  if ( rem == 0 )
119  {
120  throw std::range_error(
121  "Insufficient data for request" );
122  }
123 
124  if ( std::find( begin_set, end_set, cur_data( )[ cur ] ) !=
125  end_set )
126  {
127  match = true;
128  }
129  }
130  result.resize( cur );
131  std::copy( cur_data( ), cur_data( ) + cur, result.begin( ) );
132  consume( cur );
133  return result;
134  }
135 
148  template < typename It, typename OutIt >
149  OutIt
150  read_until( It begin_set,
151  It end_set,
152  OutIt dest_start,
153  OutIt dest_end )
154  {
155  span_type::size_type cur = 0;
156  auto rem = remaining( );
157  OutIt dest_cur = dest_start;
158  bool match = false;
159  for ( ; !match && dest_cur != dest_end;
160  ++cur, --rem, ++dest_cur )
161  {
162  if ( rem == 0 )
163  {
164  throw std::range_error(
165  "Insufficient data for request" );
166  }
167 
168  if ( std::find( begin_set, end_set, cur_data( )[ cur ] ) !=
169  end_set )
170  {
171  match = true;
172  }
173  }
174  if ( match )
175  {
176  std::copy( cur_data( ), cur_data( ) + cur, dest_start );
177  }
178  else
179  {
180  throw std::range_error(
181  "Unable to find matching sequence" );
182  }
183  consume( cur );
184  return dest_cur;
185  }
186 
187  private:
188  void
190  {
191  if ( count > remaining( ) )
192  {
193  count = remaining( );
194  }
195  cur_ += count;
196  }
197 
200  {
201  return s_.size( ) - cur_;
202  }
203 
204  char*
206  {
207  return s_.data( ) + cur_;
208  }
209 
212  };
213  }
214 }
215 
217 // Tests only after this point.
219 
220 #ifdef _NDS_IMPL_ENABLE_CATCH_TESTS_
221 
222 #include <string>
223 #include "catch.hpp"
224 
225 TEST_CASE( "You can create a span reader class", "[common,span_reader]" )
226 {
227  std::string tmp{ "abc" };
228  nds_impl::common::Span< char > s{ const_cast< char* >( tmp.data( ) ),
229  tmp.size( ) };
231 }
232 
233 TEST_CASE( "You can cannot write to a span_reader", "[common,span_reader]" )
234 {
235  std::string tmp{ "abc" };
236  nds_impl::common::Span< char > s{ const_cast< char* >( tmp.data( ) ),
237  tmp.size( ) };
239  std::string tmp2{ "ABC" };
240  REQUIRE_THROWS( r.write_all( tmp2.data( ), tmp2.data( ) + tmp2.size( ) ) );
241 }
242 
243 TEST_CASE( "You can read from a span reader", "[span_reader,read]" )
244 {
245  std::vector< char > dest( 20 );
246 
247  auto hello = std::string{ "hello world!" };
248  nds_impl::common::Span< char > s{ const_cast< char* >( hello.data( ) ),
249  hello.size( ) };
251 
252  auto end = r.read_available( dest.data( ), dest.data( ) + dest.size( ) );
253  REQUIRE( end != dest.data( ) );
254  REQUIRE( end == dest.data( ) + hello.size( ) );
255  auto output = std::string( dest.data( ), end );
256  REQUIRE( output == hello );
257 }
258 
259 TEST_CASE( "You can read the data in small segments from a span reader",
260  "[span_reader,read]" )
261 {
262  std::vector< char > dest( 5 );
263 
264  auto hello = std::string{ "hello world!" };
265  nds_impl::common::Span< char > s{ const_cast< char* >( hello.data( ) ),
266  hello.size( ) };
267 
269 
270  auto end = r.read_available( dest.data( ), dest.data( ) + dest.size( ) );
271  REQUIRE( end != dest.data( ) );
272  REQUIRE( end == dest.data( ) + dest.size( ) );
273  auto output = std::string( dest.data( ), end );
274  REQUIRE( output == hello.substr( 0, dest.size( ) ) );
275 
276  end = r.read_available( dest.data( ), dest.data( ) + dest.size( ) );
277  REQUIRE( end != dest.data( ) );
278  REQUIRE( end == dest.data( ) + dest.size( ) );
279  output = std::string( dest.data( ), end );
280  REQUIRE( output == hello.substr( dest.size( ), dest.size( ) ) );
281 }
282 
283 TEST_CASE( "You can ask for an exact amount of bytes to be returned from a "
284  "span_reader",
285  "[span_reader,read]" )
286 {
287  std::vector< char > dest( 10 );
288 
289  auto input = std::string{ "0123456789" };
290  nds_impl::common::Span< char > s{ const_cast< char* >( input.data( ) ),
291  input.size( ) };
293 
294  auto stride = 4;
295  auto end = r.read_exactly( dest.data( ), dest.data( ) + stride );
296  REQUIRE( end == dest.data( ) + stride );
297 
298  REQUIRE_THROWS(
299  r.read_exactly( dest.data( ), dest.data( ) + dest.size( ) ) );
300 }
301 
302 TEST_CASE( "You can peek at an exact amount of data without consuming it from "
303  "a span reader",
304  "[span_reader,read]" )
305 {
306  std::vector< char > dest( 10 );
307  auto input = std::string{ "0123456789" };
308 
309  nds_impl::common::Span< char > s{ const_cast< char* >( input.data( ) ),
310  input.size( ) };
312 
313  auto stride = 4;
314  auto end = r.peek( dest.data( ), dest.data( ) + stride );
315  REQUIRE( end == dest.data( ) + stride );
316  REQUIRE( std::equal( dest.data( ), dest.data( ) + stride, input.data( ) ) );
317 
318  // clear the buffer
319  std::fill( dest.begin( ), dest.end( ), 0 );
320  REQUIRE(
321  !std::equal( dest.data( ), dest.data( ) + stride, input.data( ) ) );
322 
323  end = r.read_exactly( dest.data( ), dest.data( ) + stride );
324  REQUIRE( end == dest.data( ) + stride );
325  REQUIRE( std::equal( dest.data( ), dest.data( ) + stride, input.data( ) ) );
326 }
327 
328 TEST_CASE( "You can ask a span reader to read until a sequence is found",
329  "[buffered,read_until]" )
330 {
331  auto input = std::string{ "0123456789" };
332  nds_impl::common::Span< char > s{ const_cast< char* >( input.data( ) ),
333  input.size( ) };
335 
336  auto terminators = std::string( "59" );
337  auto result = r.read_until( terminators.begin( ), terminators.end( ) );
338  REQUIRE( std::string( result.begin( ), result.end( ) ) ==
339  std::string( "012345" ) );
340 }
341 
342 TEST_CASE( "You can ask a span reader to read until a sequence is found, "
343  "with a bound",
344  "[span_reader,read_until]" )
345 {
346  std::vector< char > dest( 5 );
347  auto input = std::string{ "0123456789abcdef" };
348  nds_impl::common::Span< char > s{ const_cast< char* >( input.data( ) ),
349  input.size( ) };
351 
352  std::fill( dest.begin( ), dest.end( ), 0 );
353  auto terminators = std::string( "29" );
354  auto end = r.read_until(
355  terminators.begin( ), terminators.end( ), dest.begin( ), dest.end( ) );
356  auto length = std::distance( dest.begin( ), end );
357  REQUIRE( length == 3 );
358  REQUIRE( std::string( dest.data( ), length ) == std::string( "012" ) );
359 }
360 
361 TEST_CASE(
362  "A bounded span_reader read_until will throw an exception if it cannot "
363  "find a sequence",
364  "[span_reader,read_until]" )
365 {
366  std::vector< char > dest( 5 );
367  auto input = std::string{ "0123456789abcdef" };
368  nds_impl::common::Span< char > s{ const_cast< char* >( input.data( ) ),
369  input.size( ) };
371 
372  auto terminators = std::string( "Z" );
373  REQUIRE_THROWS_AS( r.read_until( terminators.begin( ),
374  terminators.end( ),
375  dest.begin( ),
376  dest.end( ) ),
377  std::range_error );
378 }
379 
380 TEST_CASE(
381  "A bounded span_reader read_until will throw an exception if it cannot "
382  "find a sequence when input is empty",
383  "[span_reader,read_until]" )
384 {
385  std::vector< char > dest( 5 );
386  auto input = std::string{ "" };
387  nds_impl::common::Span< char > s{ const_cast< char* >( input.data( ) ),
388  input.size( ) };
390 
391  auto terminators = std::string( "Z" );
392  REQUIRE_THROWS_AS( r.read_until( terminators.begin( ),
393  terminators.end( ),
394  dest.begin( ),
395  dest.end( ) ),
396  std::runtime_error );
397 }
398 
399 #endif // _NDS_IMPL_ENABLE_CATCH_TESTS_
400 
401 #endif // NDS2_CLIENT_COMMON_SPAN_READER_HH
nds_impl::common::Span< char > span_type
Definition: span_reader.hh:21
char * peek(char *start, const char *end)
Definition: span_reader.hh:85
void consume(span_type::size_type count)
Definition: span_reader.hh:189
TEST_CASE("daq_strlcpy copies strings safely when buffers are sufficiently large")
Definition: test_bsd_string.cc:9
std::vector< char > read_until(It begin_set, It end_set)
Definition: span_reader.hh:110
span_type::size_type cur_
Definition: span_reader.hh:211
char * cur_data()
Definition: span_reader.hh:205
span_type::size_type remaining()
Definition: span_reader.hh:199
char * read_exactly(char *start, const char *end)
Definition: span_reader.hh:65
SpanReader(span_type &s)
Definition: span_reader.hh:23
Definition: span_reader.hh:18
span_type s_
Definition: span_reader.hh:210
OutIt read_until(It begin_set, It end_set, OutIt dest_start, OutIt dest_end)
Definition: span_reader.hh:150
std::size_t size_type
Definition: utils.hh:139
char * write_all(const char *begin, const char *end)
Definition: span_reader.hh:28
char * read_available(char *start, char *end)
Definition: span_reader.hh:42
pointer data() const
Definition: utils.hh:169
size_type size() const
Definition: utils.hh:154