1 // This is core/vsl/vsl_block_binary.cxx
2 //:
3 // \file
4 // \brief Set of functions to do binary IO on a block of values.
5 // \author Ian Scott, ISBE Manchester, Feb 2003
6 
7 #include <cstddef>
8 #include <new>
9 #include <algorithm>
10 #include <cstdlib>
11 #include "vsl_block_binary.h"
12 #ifdef _MSC_VER
13 #  include "vcl_msvc_warnings.h"
14 #endif
15 #include <cassert>
16 
17 struct vsl_block_t
18 {
19   char * ptr;
20   std::size_t size;
21 };
22 
23 vsl_block_t
allocate_up_to(std::size_t nbytes)24 allocate_up_to(std::size_t nbytes)
25 {
26   vsl_block_t block = { nullptr, nbytes };
27   while (true)
28   {
29     try
30     {
31       block.ptr = new char[block.size];
32     }
33     catch (const std::bad_alloc &)
34     {}
35     if (block.ptr)
36       return block;
37     block.size /= 2;
38   }
39 }
40 
41 
42 //: Error checking.
43 void
vsl_block_binary_read_confirm_specialisation(vsl_b_istream & is,bool specialised)44 vsl_block_binary_read_confirm_specialisation(vsl_b_istream & is, bool specialised)
45 {
46   if (!is)
47     return;
48   bool b;
49   vsl_b_read(is, b);
50   if (b != specialised)
51   {
52     std::cerr << "I/O ERROR: vsl_block_binary_read()\n";
53     if (specialised)
54       std::cerr << "           Data was saved using unspecialised slow form and is being loaded\n"
55                 << "           using specialised fast form.\n\n";
56     else
57       std::cerr << "           Data was saved using specialised fast form and is being loaded\n"
58                 << "           using unspecialised slow form.\n\n";
59 
60     is.is().clear(std::ios::badbit); // Set an unrecoverable IO error on stream
61   }
62 }
63 
64 
65 /////////////////////////////////////////////////////////////////////////
66 
67 //: Write a block of floats to a vsl_b_ostream
68 template <class T>
69 void
vsl_block_binary_write_float_impl(vsl_b_ostream & os,const T * begin,std::size_t nelems)70 vsl_block_binary_write_float_impl(vsl_b_ostream & os, const T * begin, std::size_t nelems)
71 {
72   vsl_b_write(os, true); // Error check that this is a specialised version
73 
74   const std::size_t wanted = sizeof(T) * nelems;
75   vsl_block_t block = allocate_up_to(wanted);
76 
77   // multiple-block version works equally efficiently with single block
78   const std::size_t items_per_block = block.size / sizeof(T);
79 
80   // convert and save the data from the start.
81   while (nelems > 0)
82   {
83     std::size_t items = std::min(items_per_block, nelems);
84     std::size_t bytes = sizeof(T) * items;
85     vsl_swap_bytes_to_buffer((const char *)begin, (char *)block.ptr, sizeof(T), items);
86     os.os().write(block.ptr, bytes);
87     begin += items;
88     nelems -= items;
89   }
90   delete[] block.ptr;
91 }
92 
93 //: Read a block of floats from a vsl_b_ostream
94 template <class T>
95 void
vsl_block_binary_read_float_impl(vsl_b_istream & is,T * begin,std::size_t nelems)96 vsl_block_binary_read_float_impl(vsl_b_istream & is, T * begin, std::size_t nelems)
97 {
98   // There are no complications here, to deal with low memory,
99   // because the byte swapping can be done in place.
100   vsl_block_binary_read_confirm_specialisation(is, true);
101   if (!is)
102     return;
103   is.is().read((char *)begin, nelems * sizeof(T));
104   vsl_swap_bytes((char *)begin, sizeof(T), nelems);
105 }
106 
107 /////////////////////////////////////////////////////////////////////////
108 //: Write a block of signed ints to a vsl_b_ostream
109 template <class T>
110 void
vsl_block_binary_write_int_impl(vsl_b_ostream & os,const T * begin,std::size_t nelems)111 vsl_block_binary_write_int_impl(vsl_b_ostream & os, const T * begin, std::size_t nelems)
112 {
113 
114   vsl_b_write(os, true); // Error check that this is a specialised version
115 
116   const std::size_t wanted = VSL_MAX_ARBITRARY_INT_BUFFER_LENGTH(sizeof(T)) * nelems;
117   vsl_block_t block = allocate_up_to(wanted);
118 
119   if (block.size == wanted)
120   {
121     // Do simple single block version
122     std::size_t nbytes = vsl_convert_to_arbitrary_length(begin, (unsigned char *)block.ptr, nelems);
123     vsl_b_write(os, nbytes);
124     os.os().write(block.ptr, nbytes);
125   }
126   else
127   {
128     // Do multiple-block version
129     const std::size_t items_per_block = block.size / VSL_MAX_ARBITRARY_INT_BUFFER_LENGTH(sizeof(T));
130     std::size_t n = nelems; // Number of items still to be converted.
131     const T * p = begin;    // Pointer to next block of data to be converted.
132     assert(n > items_per_block);
133     // Convert the data - just counting bytes for now.
134     std::size_t n_bytes = 0;
135     while (n > 0)
136     {
137       std::size_t items = std::min(items_per_block, n);
138       n_bytes += vsl_convert_to_arbitrary_length(p, (unsigned char *)block.ptr, items);
139       p += items;
140       n -= items;
141     }
142 
143     vsl_b_write(os, n_bytes);
144     n = nelems;
145     p = begin;
146 
147     // Now convert and save the data from the start.
148     while (n > 0)
149     {
150       std::size_t items = std::min(items_per_block, n);
151       std::size_t bytes = vsl_convert_to_arbitrary_length(p, (unsigned char *)block.ptr, items);
152       os.os().write(block.ptr, bytes);
153       p += items;
154       n -= items;
155     }
156   }
157   delete[] block.ptr;
158 }
159 
160 /////////////////////////////////////////////////////////////////////////
161 
162 //: Read a block of signed ints from a vsl_b_istream
163 template <class T>
164 void
vsl_block_binary_read_int_impl(vsl_b_istream & is,T * begin,std::size_t nelems)165 vsl_block_binary_read_int_impl(vsl_b_istream & is, T * begin, std::size_t nelems)
166 {
167   vsl_block_binary_read_confirm_specialisation(is, true);
168   if (!is)
169     return;
170   std::size_t nbytes;
171   vsl_b_read(is, nbytes);
172   if (nbytes == 0)
173     return;
174 
175 
176   vsl_block_t block = allocate_up_to(nbytes);
177 
178   std::size_t n_bytes_converted = 0;
179   if (block.size == nbytes)
180   {
181     // Do simple single block version
182     is.is().read(block.ptr, block.size);
183     n_bytes_converted = vsl_convert_from_arbitrary_length((unsigned char *)block.ptr, begin, nelems);
184   }
185   else // Do multi-block version
186   {
187     std::size_t offset = 0;
188     std::size_t bytes_left = nbytes;
189     std::size_t bytes_read = 0;
190     while (nelems > 0)
191     {
192       assert(offset < block.size);
193 
194       // fill block beyond offset with as much as possible.
195       std::size_t bytes = std::min((std::size_t)nbytes - bytes_read, block.size - offset);
196       is.is().read(block.ptr + offset, bytes);
197       bytes_read += bytes;
198 
199       if (!is)
200         break;
201 
202       // count number of ints in block.
203       std::size_t elems = 0;
204       for (unsigned char *p = (unsigned char *)block.ptr, *p_end = p + bytes + offset; p != p_end; ++p)
205         elems += *p >> 7;
206 
207       if (elems > nelems)
208       {
209         std::cerr << "\nI/O ERROR: vsl_block_binary_read(.., int*,..)"
210                   << " Corrupted data stream\n";
211         is.is().clear(std::ios::badbit); // Set an unrecoverable IO error on stream
212         break;
213       }
214 
215       // convert ints;
216       std::size_t bytes_converted = vsl_convert_from_arbitrary_length((unsigned char *)block.ptr, begin, elems);
217       nelems -= elems;
218       begin += elems;
219 
220       offset = (bytes + offset) - bytes_converted; // avoid overflow.
221       n_bytes_converted += bytes_converted;
222       bytes_left -= bytes_converted;
223 
224       // shift remaining (offset) bytes to front of block.
225       std::memcpy(block.ptr, block.ptr + bytes_converted, offset);
226     }
227     if (bytes_left != 0 || nelems != 0 || bytes_read != nbytes)
228     {
229       std::cerr << "\nI/O ERROR: vsl_block_binary_read(.., int*,..)"
230                 << " Corrupted data stream\n";
231       is.is().clear(std::ios::badbit); // Set an unrecoverable IO error on stream
232     }
233   }
234   if (n_bytes_converted != nbytes)
235   {
236     std::cerr << "\nI/O ERROR: vsl_block_binary_read(.., int*,..)"
237               << " Corrupted data stream\n";
238     is.is().clear(std::ios::badbit); // Set an unrecoverable IO error on stream
239   }
240   delete[] block.ptr;
241 }
242 
243 /////////////////////////////////////////////////////////////////////////
244 
245 //: Write a block of bytes to a vsl_b_ostream
246 template <class T>
247 void
vsl_block_binary_write_byte_impl(vsl_b_ostream & os,const T * begin,std::size_t nelems)248 vsl_block_binary_write_byte_impl(vsl_b_ostream & os, const T * begin, std::size_t nelems)
249 {
250   vsl_b_write(os, true); // Error check that this is a specialised version
251   os.os().write((char *)begin, nelems);
252 }
253 
254 //: Read a block of bytes from a vsl_b_ostream
255 template <class T>
256 void
vsl_block_binary_read_byte_impl(vsl_b_istream & is,T * begin,std::size_t nelems)257 vsl_block_binary_read_byte_impl(vsl_b_istream & is, T * begin, std::size_t nelems)
258 {
259   // There are no complications here, to deal with low memory,
260   // because the load is done in place.
261   vsl_block_binary_read_confirm_specialisation(is, true);
262   if (!is)
263     return;
264   is.is().read((char *)begin, nelems);
265 }
266 
267 
268 // Instantiate templates for POD types.
269 
270 template void
271 vsl_block_binary_write_float_impl(vsl_b_ostream &, const double *, std::size_t);
272 template void
273 vsl_block_binary_write_float_impl(vsl_b_ostream &, const float *, std::size_t);
274 
275 template void
276 vsl_block_binary_read_float_impl(vsl_b_istream &, double *, std::size_t);
277 template void
278 vsl_block_binary_read_float_impl(vsl_b_istream &, float *, std::size_t);
279 
280 template void
281 vsl_block_binary_write_int_impl(vsl_b_ostream &, const long *, std::size_t);
282 template void
283 vsl_block_binary_write_int_impl(vsl_b_ostream &, const unsigned long *, std::size_t);
284 template void
285 vsl_block_binary_write_int_impl(vsl_b_ostream &, const int *, std::size_t);
286 template void
287 vsl_block_binary_write_int_impl(vsl_b_ostream &, const unsigned int *, std::size_t);
288 template void
289 vsl_block_binary_write_int_impl(vsl_b_ostream &, const short *, std::size_t);
290 template void
291 vsl_block_binary_write_int_impl(vsl_b_ostream &, const unsigned short *, std::size_t);
292 
293 template void
294 vsl_block_binary_read_int_impl(vsl_b_istream &, long *, std::size_t);
295 template void
296 vsl_block_binary_read_int_impl(vsl_b_istream &, unsigned long *, std::size_t);
297 template void
298 vsl_block_binary_read_int_impl(vsl_b_istream &, int *, std::size_t);
299 template void
300 vsl_block_binary_read_int_impl(vsl_b_istream &, unsigned int *, std::size_t);
301 template void
302 vsl_block_binary_read_int_impl(vsl_b_istream &, short *, std::size_t);
303 template void
304 vsl_block_binary_read_int_impl(vsl_b_istream &, unsigned short *, std::size_t);
305 
306 template void
307 vsl_block_binary_write_byte_impl(vsl_b_ostream &, const signed char *, std::size_t);
308 template void
309 vsl_block_binary_write_byte_impl(vsl_b_ostream &, const unsigned char *, std::size_t);
310 
311 template void
312 vsl_block_binary_read_byte_impl(vsl_b_istream &, signed char *, std::size_t);
313 template void
314 vsl_block_binary_read_byte_impl(vsl_b_istream &, unsigned char *, std::size_t);
315 
316 #if VXL_INT_64_IS_LONGLONG
317 template void
318 vsl_block_binary_write_int_impl(vsl_b_ostream &, const vxl_int_64 *, std::size_t);
319 template void
320 vsl_block_binary_write_int_impl(vsl_b_ostream &, const vxl_uint_64 *, std::size_t);
321 template void
322 vsl_block_binary_read_int_impl(vsl_b_istream &, vxl_int_64 *, std::size_t);
323 template void
324 vsl_block_binary_read_int_impl(vsl_b_istream &, vxl_uint_64 *, std::size_t);
325 #endif // VXL_HAS_INT_64 && !VXL_INT_64_IS_LONG
326