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