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