1 // "Handle" classes useful when throwing exceptions
2 
3 // buffer holds aligned memory for header, suitable for bitstream r/w (word-aligned)
4 class AlignedBufferHandle {
5   public:
6     size_t buffer_size_bytes;
7     // uint64 alignment guarantees bitstream alignment
8     uint64* buffer;
9 
10     // can copy a header into aligned buffer
11     AlignedBufferHandle(const zfp::array::header* h = 0) {
12       size_t num_64bit_entries = DIV_ROUND_UP(ZFP_HEADER_SIZE_BITS, CHAR_BIT * sizeof(uint64));
13       buffer = new uint64[num_64bit_entries];
14       buffer_size_bytes = num_64bit_entries * sizeof(uint64);
15 
16       if (h)
17         memcpy(buffer, h->buffer, BITS_TO_BYTES(ZFP_HEADER_SIZE_BITS));
18     }
19 
~AlignedBufferHandle()20     ~AlignedBufferHandle() {
21       delete[] buffer;
22     }
23 
copy_to_header(zfp::array::header * h)24     void copy_to_header(zfp::array::header* h) {
25       memcpy(h, buffer, BITS_TO_BYTES(ZFP_HEADER_SIZE_BITS));
26     }
27 };
28 
29 // redirect zfp_stream->bitstream to header while object remains in scope
30 class DualBitstreamHandle {
31   public:
32     bitstream* old_bs;
33     bitstream* new_bs;
34     zfp_stream* zfp;
35 
DualBitstreamHandle(zfp_stream * zfp,AlignedBufferHandle & abh)36     DualBitstreamHandle(zfp_stream* zfp, AlignedBufferHandle& abh) :
37       zfp(zfp)
38     {
39       old_bs = zfp_stream_bit_stream(zfp);
40       new_bs = stream_open(abh.buffer, abh.buffer_size_bytes);
41 
42       stream_rewind(new_bs);
43       zfp_stream_set_bit_stream(zfp, new_bs);
44     }
45 
~DualBitstreamHandle()46     ~DualBitstreamHandle() {
47       zfp_stream_set_bit_stream(zfp, old_bs);
48       stream_close(new_bs);
49     }
50 };
51 
52 class ZfpFieldHandle {
53   public:
54     zfp_field* field;
55 
ZfpFieldHandle()56     ZfpFieldHandle() {
57       field = zfp_field_alloc();
58     }
59 
ZfpFieldHandle(zfp_type type,int nx,int ny,int nz)60     ZfpFieldHandle(zfp_type type, int nx, int ny, int nz) {
61       field = zfp_field_3d(0, type, nx, ny, nz);
62     }
63 
~ZfpFieldHandle()64     ~ZfpFieldHandle() {
65       zfp_field_free(field);
66     }
67 };
68 
69 class ZfpStreamHandle {
70   public:
71     bitstream* bs;
72     zfp_stream* stream;
73 
ZfpStreamHandle(AlignedBufferHandle & abh)74     ZfpStreamHandle(AlignedBufferHandle& abh) {
75       bs = stream_open(abh.buffer, abh.buffer_size_bytes);
76       stream = zfp_stream_open(bs);
77     }
78 
~ZfpStreamHandle()79     ~ZfpStreamHandle() {
80       zfp_stream_close(stream);
81       stream_close(bs);
82     }
83 };
84 
85 // verify buffer is large enough, with what header describes
is_valid_buffer_size(const zfp_stream * stream,uint nx,uint ny,uint nz,size_t expected_buffer_size_bytes)86 static bool is_valid_buffer_size(const zfp_stream* stream, uint nx, uint ny, uint nz, size_t expected_buffer_size_bytes)
87 {
88   uint mx = ((std::max(nx, 1u)) + 3) / 4;
89   uint my = ((std::max(ny, 1u)) + 3) / 4;
90   uint mz = ((std::max(nz, 1u)) + 3) / 4;
91   size_t blocks = (size_t)mx * (size_t)my * (size_t)mz;
92   // no rounding because fixed-rate wra implies rate is multiple of word size
93   size_t described_buffer_size_bytes = blocks * stream->maxbits / CHAR_BIT;
94 
95   return expected_buffer_size_bytes >= described_buffer_size_bytes;
96 }
97 
read_header_contents(const zfp::array::header & header,size_t expected_buffer_size_bytes,uint & dims,zfp_type & type,double & rate,uint n[4])98 static void read_header_contents(const zfp::array::header& header, size_t expected_buffer_size_bytes, uint& dims, zfp_type& type, double& rate, uint n[4])
99 {
100   // create zfp_stream and zfp_field structs to call C API zfp_read_header()
101   AlignedBufferHandle abh;
102   memcpy(abh.buffer, header.buffer, BITS_TO_BYTES(ZFP_HEADER_SIZE_BITS));
103 
104   ZfpStreamHandle zsh(abh);
105   ZfpFieldHandle zfh;
106 
107   if (!zfp_read_header(zsh.stream, zfh.field, ZFP_HEADER_FULL))
108     throw zfp::array::header::exception("Invalid ZFP header.");
109 
110   // gather metadata
111   dims = zfp_field_dimensionality(zfh.field);
112   type = zfp_field_type(zfh.field);
113 
114   uint num_block_entries = 1u << (2 * dims);
115   rate = (double)zsh.stream->maxbits / num_block_entries;
116 
117   zfp_field_size(zfh.field, n);
118 
119   // validate header
120   std::string err_msg = "";
121   verify_header_contents(zsh.stream, zfh.field, err_msg);
122 
123   if (!err_msg.empty())
124     throw zfp::array::header::exception(err_msg);
125 
126   if (expected_buffer_size_bytes && !is_valid_buffer_size(zsh.stream, zfh.field->nx, zfh.field->ny, zfh.field->nz, expected_buffer_size_bytes))
127     throw zfp::array::header::exception("ZFP header expects a longer buffer than what was passed in.");
128 }
129 
130 // verifies metadata on zfp_stream and zfp_field describe a valid compressed array
verify_header_contents(const zfp_stream * stream,const zfp_field * field,std::string & err_msg)131 static void verify_header_contents(const zfp_stream* stream, const zfp_field* field, std::string& err_msg)
132 {
133   // verify read-header contents
134   zfp_type type = zfp_field_type(field);
135   if (type != zfp_type_float && type != zfp_type_double)
136     zfp::array::header::concat_sentence(err_msg, "ZFP compressed arrays do not yet support scalar types beyond floats and doubles.");
137 
138   uint dims = zfp_field_dimensionality(field);
139   if (dims < 1 || dims > 3)
140     zfp::array::header::concat_sentence(err_msg, "ZFP compressed arrays do not yet support dimensionalities beyond 1, 2, and 3.");
141 
142   if (zfp_stream_compression_mode(stream) != zfp_mode_fixed_rate)
143     zfp::array::header::concat_sentence(err_msg, "ZFP header specified a non fixed-rate mode, unsupported by this object.");
144 }
145