vsl_binary_io.cxx
Go to the documentation of this file.
1 // This is core/vsl/vsl_binary_io.cxx
2 #include <cstddef>
3 #include <map>
4 #include <cstdlib>
5 #include "vsl_binary_io.h"
6 //:
7 // \file
8 // \brief Functions to perform consistent binary IO within vsl
9 // \author Tim Cootes and Ian Scott
10 
11 #include <cassert>
12 #ifdef _MSC_VER
13 # include <vcl_msvc_warnings.h>
14 #endif
16 
17 template <typename TYPE>
18 void local_vsl_b_write(vsl_b_ostream& os, const TYPE n)
19 {
20  const size_t MAX_INT_BUFFER_LENGTH = VSL_MAX_ARBITRARY_INT_BUFFER_LENGTH(sizeof(TYPE));
21  unsigned char buf[ MAX_INT_BUFFER_LENGTH ] = {0};
22  const auto nbytes = (std::size_t)vsl_convert_to_arbitrary_length(&n, buf);
23  os.os().write((char*)buf, nbytes );
24 }
25 
26 template <typename TYPE>
27 void local_vsl_b_read(vsl_b_istream &is,TYPE & n)
28 {
29  const size_t MAX_INT_BUFFER_LENGTH = VSL_MAX_ARBITRARY_INT_BUFFER_LENGTH(sizeof(TYPE));
30  unsigned char buf[MAX_INT_BUFFER_LENGTH] = {0};
31  unsigned char *ptr=buf;
32  do
33  {
34  vsl_b_read(is, *ptr);
35  const std::ptrdiff_t ptr_offset_from_begin = ptr-buf;
36  if (ptr_offset_from_begin >= (std::ptrdiff_t)MAX_INT_BUFFER_LENGTH)
37  {
38  std::cerr << "I/O ERROR: vsl_b_read(vsl_b_istream &, int& )\n"
39  << " Integer too big. Likely cause either file corruption, or\n"
40  << " file was created on platform with larger integer sizes.\n"
41  << "ptr_offset_from_begin: " << ptr_offset_from_begin << " >= " << MAX_INT_BUFFER_LENGTH << std::endl;
42  is.is().clear(std::ios::badbit); // Set an unrecoverable IO error on stream
43  n = 0; //If failure occurs, then set n=0 for number of reads.
44  return;
45  }
46  }
47  while (!(*(ptr++) & 128));
49 }
50 
51 #define MACRO_MAKE_INTEGER_READ_WRITE( TYPEIN ) \
52 void vsl_b_write(vsl_b_ostream& os,TYPEIN n ) \
53 { \
54  local_vsl_b_write<TYPEIN>(os,n); \
55 } \
56  \
57 void vsl_b_read(vsl_b_istream &is,TYPEIN& n ) \
58 { \
59  local_vsl_b_read<TYPEIN>(is,n); \
60 }
61 
63 MACRO_MAKE_INTEGER_READ_WRITE(unsigned int);
65 MACRO_MAKE_INTEGER_READ_WRITE(unsigned short);
67 MACRO_MAKE_INTEGER_READ_WRITE(unsigned long);
68 #if VXL_HAS_INT_64 && !VXL_INT_64_IS_LONG
71 #endif
72 #if 0
73 MACRO_MAKE_INTEGER_READ_WRITE(std::ptrdiff_t);
74 // When the macro is ready, this test will be
75 // #if ! VCL_SIZE_T_IS_A_STANDARD_TYPE
77 // #endif
78 #endif
79 
80 #undef MACRO_MAKE_INTEGER_READ_WRITE
81 
82 void vsl_b_write(vsl_b_ostream& os, char n )
83 {
84  os.os().write( reinterpret_cast<char *>(&n), sizeof( n ) );
85 }
86 
87 void vsl_b_read(vsl_b_istream &is, char& n )
88 {
89  const int value = is.is().get();
90  n = static_cast<signed char>(value);
91 }
92 
93 void vsl_b_write(vsl_b_ostream& os, signed char n )
94 {
95  os.os().write( reinterpret_cast<char *>(&n), sizeof( n ) );
96 }
97 
98 void vsl_b_read(vsl_b_istream &is, signed char& n )
99 {
100  const int value = is.is().get();
101  n = static_cast<signed char>(value);
102 }
103 
104 
105 void vsl_b_write(vsl_b_ostream& os,unsigned char n )
106 {
107  os.os().write( reinterpret_cast<char *>(&n), 1 );
108 }
109 
110 void vsl_b_read(vsl_b_istream &is,unsigned char& n )
111 {
112  const int value = is.is().get();
113  n = static_cast<unsigned char>(value);
114 }
115 
116 
117 void vsl_b_write(vsl_b_ostream& os, const std::string& str )
118 {
119  std::string::const_iterator it;
120 
121  vsl_b_write(os,(short)str.length());
122  for ( it = str.begin(); it != str.end(); ++it )
123  vsl_b_write(os,*it);
124 }
125 
126 void vsl_b_read(vsl_b_istream &is, std::string& str )
127 {
128  std::string::iterator it;
129  std::string::size_type length;
130 
131  vsl_b_read(is,length);
132  str.resize( length );
133  for ( it = str.begin(); it != str.end(); ++it )
134  vsl_b_read(is,*it);
135 }
136 
137 // deprecated in favour of std::string version.
138 void vsl_b_write(vsl_b_ostream& os,const char *s )
139 {
140  int i = -1;
141  do {
142  i++;
143  vsl_b_write(os,s[i]);
144  } while ( s[i] != 0 );
145 }
146 
147 // deprecated in favour of std::string version.
148 // note You must preallocate enough space at \p s for expected length of string.
149 // This function is easy to crash mith a malformed data file.
150 void vsl_b_read(vsl_b_istream &is,char *s )
151 {
152  int i = -1;
153  do {
154  i++;
155  vsl_b_read(is,s[i]);
156  } while ( s[i] != 0 );
157 }
158 
159 
160 void vsl_b_write(vsl_b_ostream& os,bool b)
161 {
162  if (b)
163  vsl_b_write(os, (signed char) -1);
164  else
165  vsl_b_write(os, (signed char) 0);
166 }
167 
168 void vsl_b_read(vsl_b_istream &is,bool& b)
169 {
170  signed char c;
171  vsl_b_read(is, c);
172  b = (c != 0);
173 }
174 
175 void vsl_b_write(vsl_b_ostream& os,float n )
176 {
177  vsl_swap_bytes(reinterpret_cast<char *>(&n), sizeof( n ) );
178  os.os().write( reinterpret_cast<char *>(&n), sizeof( n ) );
179 }
180 
181 void vsl_b_read(vsl_b_istream &is,float& n )
182 {
183  is.is().read( reinterpret_cast<char *>(&n), sizeof( n ) );
184  vsl_swap_bytes(reinterpret_cast<char *>(&n), sizeof( n ) );
185 }
186 
187 void vsl_b_write(vsl_b_ostream& os,double n )
188 {
189  vsl_swap_bytes(reinterpret_cast<char *>(&n), sizeof( n ) );
190  os.os().write( reinterpret_cast<char *>(&n), sizeof( n ) );
191 }
192 
193 void vsl_b_read(vsl_b_istream &is,double& n )
194 {
195  is.is().read( reinterpret_cast<char *>(&n), sizeof( n ) );
196  vsl_swap_bytes(reinterpret_cast<char *>(&n), sizeof( n ) );
197 }
198 
199 
200 static const unsigned short vsl_magic_number_part_1=0x2c4e;
201 static const unsigned short vsl_magic_number_part_2=0x472b;
202 
203 
204 //: Create this adaptor using an existing stream
205 // The stream (os) must be open (i.e. ready to be written to) so that the
206 // IO version number can be written by this constructor.
207 // User is responsible for deleting os after deleting the adaptor
208 vsl_b_ostream::vsl_b_ostream(std::ostream *o_s): os_(o_s)
209 {
210  assert(os_ != nullptr);
212  vsl_b_write_uint_16(*this, vsl_magic_number_part_1);
213  vsl_b_write_uint_16(*this, vsl_magic_number_part_2);
214 }
215 
216 //: A reference to the adaptor's stream
217 std::ostream& vsl_b_ostream::os() const
218 {
219  assert(os_ != nullptr);
220  return *os_;
221 }
222 
223 //: Returns true if the underlying stream has its fail bit set.
225 {
226  return os_->operator!();
227 }
228 
229 
230 //: Clear the stream's record of any serialisation operations
231 // Calling this function while outputting serialisable things to stream,
232 // will mean that a second copy of an object may get stored to the stream.
234 {
235  serialisation_records_.clear();
236 }
237 
238 
239 //: Adds an object pointer to the serialisation records.
240 // Returns a unique identifier for the object.
241 //
242 // \a pointer must be non-null, so you should handle null pointers separately.
243 //
244 // You can optionally add some user-defined integer with each record
245 // If error checking is on, and the object pointer is null or already in the records,
246 // this function will abort()
248  (void *pointer, int other_data /*= 0*/)
249 {
250  assert(pointer != nullptr);
251  assert(serialisation_records_.find(pointer) == serialisation_records_.end());
252  unsigned long id = (unsigned long)serialisation_records_.size() + 1;
253  serialisation_records_[pointer] = std::make_pair(id, other_data);
254  return id;
255 }
256 
257 
258 //: Returns a unique identifier for the object.
259 // Returns 0 if there is no record of the object.
260 unsigned long vsl_b_ostream::get_serial_number(void *pointer) const
261 {
262  auto entry =
263  serialisation_records_.find(pointer);
264  if (entry == serialisation_records_.end())
265  {
266  return 0;
267  }
268  else
269  {
270  return (*entry).second.first;
271  }
272 }
273 
274 //: Set the user-defined data associated with the object
275 // If there is no record of the object, this function will return 0.
276 // However a retval of 0 does not necessarily imply that the object is
277 // unrecorded.
279 {
280  auto entry =
281  serialisation_records_.find(pointer);
282  if (entry == serialisation_records_.end())
283  {
284  return 0;
285  }
286  else
287  {
288  return (*entry).second.second;
289  }
290 }
291 
292 //: Modify the user-defined data associated with the object.
293 // If there is no record of the object, this function will abort.
295  (void *pointer, int /*other_data*/)
296 {
297  auto entry =
298  serialisation_records_.find(pointer);
299  if (entry == serialisation_records_.end())
300  {
301  std::cerr << "vsl_b_ostream::set_serialisation_other_data():\n"
302  << "No such value " << pointer << "in records.\n";
303  std::abort();
304  }
305  return (*entry).second.second;
306 }
307 
308 
309 //: destructor.
311 {
312  if (os_) delete os_;
313 }
314 
315 
316 //: Close the stream
318 {
319  assert(os_ != nullptr);
320  ((std::ofstream *)os_)->close();
322 }
323 
324 //: Create this adaptor using an existing stream.
325 // The stream (is) must be open (i.e. ready to be read from) so that the
326 // IO version number can be read by this constructor.
327 // User is responsible for deleting is after deleting the adaptor
328 vsl_b_istream::vsl_b_istream(std::istream *i_s): is_(i_s)
329 {
330  assert(is_ != nullptr);
331  if (!(*is_)) return;
332  unsigned long v=0, m1=0, m2=0;
333  vsl_b_read_uint_16(*this, v);
334  vsl_b_read_uint_16(*this, m1);
335  vsl_b_read_uint_16(*this, m2);
336 
337  // If this test fails, either the file is missing, or it is not a
338  // Binary VXL file, or it is a corrupted Binary VXL file
339  if (m2 != vsl_magic_number_part_2 || m1 != vsl_magic_number_part_1)
340  {
341  std::cerr << "\nI/O ERROR: vsl_b_istream::vsl_b_istream(std::istream *is)\n"
342  << " The input stream does not appear to be a binary VXL stream.\n"
343  << " Can't find correct magic number.\n";
344  is_->clear(std::ios::badbit); // Set an unrecoverable IO error on stream
345  }
346 
347  if (v != 1)
348  {
349  std::cerr << "\nI/O ERROR: vsl_b_istream::vsl_b_istream(std::istream *is)\n"
350  << " The stream's leading version number is "
351  << v << ". Expected value 1.\n";
352  is_->clear(std::ios::badbit); // Set an unrecoverable IO error on stream
353  }
354  version_no_ = (unsigned short)v;
355 }
356 
357 //: A reference to the adaptor's stream
358 std::istream & vsl_b_istream::is() const
359 {
360  assert(is_ != nullptr);
361  return *is_;
362 }
363 
364 
365 //: Return the version number of the IO format of the file being read.
366 unsigned short vsl_b_istream::version_no() const
367 {
368  return version_no_;
369 }
370 
371 //: Returns true if the underlying stream has its fail bit set.
373 {
374  return is_->operator!();
375 }
376 
377 //: Clear the stream's record of any serialisation operations
378 // Calling this function while inputting serialisable things from a stream,
379 // could cause errors during loading unless the records were cleared at a
380 // similar point during output.
382 {
383  serialisation_records_.clear();
384 }
385 
386 //: Adds record of object's unique serial number, and location in memory.
387 // \a pointer must be non-null, so you should handle null pointers separately.
388 //
389 // Adding a null pointer or one that already exists will cause the function to abort(),
390 // if debugging is turned on;
391 //
392 // You can also store a single integer as other data.
393 // Interpretation of this data is entirely up to the client code.
394 void vsl_b_istream::add_serialisation_record(unsigned long serial_number,
395  void *pointer, int other_data /*= 0*/)
396 {
397  assert(pointer != nullptr);
398  assert(serialisation_records_.find(serial_number) == serialisation_records_.end());
399  serialisation_records_[serial_number] = std::make_pair(pointer, other_data);
400 }
401 
402 //: Returns the pointer to the object identified by the unique serial number.
403 // Returns 0 if no record has been added.
404 void* vsl_b_istream::get_serialisation_pointer(unsigned long serial_number) const
405 {
406  auto entry =
407  serialisation_records_.find(serial_number);
408  if (entry == serialisation_records_.end())
409  {
410  return nullptr;
411  }
412  else
413  {
414  return (*entry).second.first;
415  }
416 }
417 
418 //: Returns the user defined data associated with the unique serial number
419 // Returns 0 if no record has been added.
421  (unsigned long serial_number) const
422 {
423  auto entry =
424  serialisation_records_.find(serial_number);
425  if (entry == serialisation_records_.end())
426  {
427  return 0;
428  }
429  else
430  {
431  return (*entry).second.second;
432  }
433 }
434 
435 //: Modify the user-defined data associated with the unique serial number
436 // If there is no record of the object, this function will abort.
438  (unsigned long serial_number, int /*other_data*/)
439 {
440  serialisation_records_type::const_iterator entry =
441  serialisation_records_.find(serial_number);
442  if (entry == serialisation_records_.end())
443  {
444  std::cerr << "vsl_b_istream::set_serialisation_other_data():\n"
445  << " No such value " << serial_number << "in records.\n";
446  std::abort();
447  }
448  return (*entry).second.second;
449 }
450 
451 
452 //: destructor.so that it can be overloaded
454 {
455  if (is_) delete is_;
456 }
457 
458 //: Close the stream
460 {
461  assert(is_ != nullptr);
462  ((std::ifstream *)is_)->close();
464 }
465 
466 
467 
468 //: Test to see if a stream really is a binary vsl file.
469 // \return false if we can't find magic numbers and correct version number.
470 // The file pointer is reset to the beginning on leaving this function.
471 bool vsl_b_istream_test(std::istream &is)
472 {
473  if (!is) return false;
474  is.seekg(0);
475  unsigned long v=0, m1=0, m2=0;
476 
477 // vsl_b_read_uint_16(is, v);
478 // vsl_b_read_uint_16(is, m1);
479 // vsl_b_read_uint_16(is, m2);
480 
481  is.read( ( char* )&v, 2 );
482  vsl_swap_bytes(( char* )&v, sizeof(long) );
483  is.read( ( char* )&m1, 2 );
484  vsl_swap_bytes(( char* )&m1, sizeof(long) );
485  is.read( ( char* )&m2, 2 );
486  vsl_swap_bytes(( char* )&m2, sizeof(long) );
487 
488  is.seekg(0);
489 
490  if (!is || m2 != vsl_magic_number_part_2 || m1 != vsl_magic_number_part_1 || v>1)
491  return false;
492 
493 
494  return true;
495 }
A binary output adaptor for any std::ostream.
Definition: vsl_binary_io.h:37
void vsl_b_write(vsl_b_ostream &os, char n)
Write char to vsl_b_ostream.
void vsl_b_read_uint_16(vsl_b_istream &is, unsigned long &n)
Read an unsigned int as 16 bits from vsl_b_istream.
virtual int set_serialisation_other_data(void *pointer, int other_data)
Modify the user-defined data associated with the object.
virtual int set_serialisation_other_data(unsigned long serial_number, int other_data)
Modify the user-defined data associated with the unique serial number.
vsl_b_istream(std::istream *is)
Create this adaptor using an existing stream.
void vsl_swap_bytes(char *ptr, unsigned nbyte, std::size_t nelem=1)
Perform byte swapping in situ.
~vsl_b_ofstream() override
Virtual destructor.
bool operator!() const
Returns true if the underlying stream has its fail bit set.
unsigned short version_no() const
Return the version number of the IO format of the file being read.
std::ostream * os_
The member stream.
Definition: vsl_binary_io.h:97
virtual void clear_serialisation_records()
Clear the stream's record of any serialisation operations.
virtual int get_serialisation_other_data(void *pointer) const
Set the user-defined data associated with the object.
bool vsl_b_istream_test(std::istream &is)
Test to see if a stream really is a binary vsl file.
Byte-swapping, arbitrary length integer conversion, and explicit I/O.
std::istream & is() const
A reference to the adaptor's stream.
std::size_t vsl_convert_from_arbitrary_length(const unsigned char *buffer, unsigned long *ints, std::size_t count=1)
Decode a buffer of arbitrary length integers.
serialisation_records_type serialisation_records_
The serialisation records,.
bool operator!() const
Returns true if the underlying stream has its fail bit set.
static constexpr unsigned short version_no_
The version number of the IO scheme.
virtual int get_serialisation_other_data(unsigned long serial_number) const
Returns the user defined data associated with the unique serial number.
void local_vsl_b_write(vsl_b_ostream &os, const TYPE n)
virtual unsigned long get_serial_number(void *pointer) const
Returns a unique identifier for the object.
#define MACRO_MAKE_INTEGER_READ_WRITE(TYPEIN)
virtual void * get_serialisation_pointer(unsigned long serial_number) const
Returns the pointer to the object identified by the unique serial number.
void close()
Close the stream.
void local_vsl_b_read(vsl_b_istream &is, TYPE &n)
virtual unsigned long add_serialisation_record(void *pointer, int other_data=0)
Adds an object pointer to the serialisation records.
~vsl_b_ifstream() override
Virtual destructor.so that it can be overloaded.
unsigned short version_no_
virtual void add_serialisation_record(unsigned long serial_number, void *pointer, int other_data=0)
Adds record of object's unique serial number, and location in memory.
#define VSL_MAX_ARBITRARY_INT_BUFFER_LENGTH(size_of_type)
The maximum length of buffer to use with arbitrary length integers.
void vsl_b_read(vsl_b_istream &is, char &n)
Read char from vsl_b_istream.
An adaptor for any std::istream to make it suitable for binary input.
std::size_t vsl_convert_to_arbitrary_length(const unsigned long *ints, unsigned char *buffer, std::size_t count=1)
Encode an array of ints into an arbitrary length format.
std::istream * is_
The member stream.
Set of functions, and objects to perform binary IO.
virtual void clear_serialisation_records()
Clear the stream's record of any serialisation operations.
serialisation_records_type serialisation_records_
The serialisation records.
std::ostream & os() const
A reference to the adaptor's stream.
void close()
Close the stream.
vsl_b_ostream(std::ostream *os)
Create this adaptor using an existing stream.
void vsl_b_write_uint_16(vsl_b_ostream &os, unsigned long n)
Write an unsigned int as 16 bits to vsl_b_ostream.