/src/hdf5/src/H5Tconv_reference.c
Line | Count | Source |
1 | | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
2 | | * Copyright by The HDF Group. * |
3 | | * All rights reserved. * |
4 | | * * |
5 | | * This file is part of HDF5. The full HDF5 copyright notice, including * |
6 | | * terms governing use, modification, and redistribution, is contained in * |
7 | | * the LICENSE file, which can be found at the root of the source code * |
8 | | * distribution tree, or in https://www.hdfgroup.org/licenses. * |
9 | | * If you do not have access to either file, you may request a copy from * |
10 | | * help@hdfgroup.org. * |
11 | | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
12 | | |
13 | | /* |
14 | | * Purpose: Datatype conversion functions for reference datatypes |
15 | | */ |
16 | | |
17 | | /****************/ |
18 | | /* Module Setup */ |
19 | | /****************/ |
20 | | #include "H5Tmodule.h" /* This source code file is part of the H5T module */ |
21 | | #define H5R_FRIEND /* Suppress error about including H5Rpkg */ |
22 | | |
23 | | /***********/ |
24 | | /* Headers */ |
25 | | /***********/ |
26 | | #include "H5private.h" /* Generic Functions */ |
27 | | #include "H5Eprivate.h" /* Error handling */ |
28 | | #include "H5FLprivate.h" /* Free Lists */ |
29 | | #include "H5Rpkg.h" /* References */ |
30 | | #include "H5Tconv.h" /* Datatype conversions */ |
31 | | #include "H5Tconv_reference.h" |
32 | | |
33 | | /*******************/ |
34 | | /* Local Variables */ |
35 | | /*******************/ |
36 | | |
37 | | /* Declare a free list to manage pieces of reference data */ |
38 | | H5FL_BLK_DEFINE_STATIC(ref_seq); |
39 | | |
40 | | /*------------------------------------------------------------------------- |
41 | | * Function: H5T__conv_ref |
42 | | * |
43 | | * Purpose: Converts between reference datatypes in memory and on disk. |
44 | | * This is a soft conversion function. |
45 | | * |
46 | | * Return: Non-negative on success/Negative on failure |
47 | | * |
48 | | *------------------------------------------------------------------------- |
49 | | */ |
50 | | herr_t |
51 | | H5T__conv_ref(const H5T_t *src, const H5T_t *dst, H5T_cdata_t *cdata, |
52 | | const H5T_conv_ctx_t H5_ATTR_UNUSED *conv_ctx, size_t nelmts, size_t buf_stride, |
53 | | size_t bkg_stride, void *buf, void *bkg) |
54 | 0 | { |
55 | 0 | uint8_t *s = NULL; /* source buffer */ |
56 | 0 | uint8_t *d = NULL; /* destination buffer */ |
57 | 0 | uint8_t *b = NULL; /* background buffer */ |
58 | 0 | ssize_t s_stride = 0; /* src stride */ |
59 | 0 | ssize_t d_stride = 0; /* dst stride */ |
60 | 0 | ssize_t b_stride; /* bkg stride */ |
61 | 0 | size_t safe = 0; /* how many elements are safe to process in each pass */ |
62 | 0 | void *conv_buf = NULL; /* temporary conversion buffer */ |
63 | 0 | size_t conv_buf_size = 0; /* size of conversion buffer in bytes */ |
64 | 0 | size_t elmtno = 0; /* element number counter */ |
65 | 0 | size_t orig_d_stride = 0; /* Original destination stride (used for error handling) */ |
66 | 0 | size_t orig_nelmts = nelmts; /* Original # of elements to convert (used for error handling) */ |
67 | 0 | bool convert_forward = |
68 | 0 | true; /* Current direction of conversion (forward or backward, used for error handling) */ |
69 | 0 | bool conversions_made = |
70 | 0 | false; /* Flag to indicate conversions have been performed, used for error handling */ |
71 | 0 | herr_t ret_value = SUCCEED; /* return value */ |
72 | |
|
73 | 0 | FUNC_ENTER_PACKAGE |
74 | |
|
75 | 0 | switch (cdata->command) { |
76 | 0 | case H5T_CONV_INIT: |
77 | | /* |
78 | | * First, determine if this conversion function applies to the |
79 | | * conversion path SRC-->DST. If not, return failure; |
80 | | * otherwise initialize the `priv' field of `cdata' with |
81 | | * information that remains (almost) constant for this |
82 | | * conversion path. |
83 | | */ |
84 | 0 | if (NULL == src || NULL == dst) |
85 | 0 | HGOTO_ERROR(H5E_DATATYPE, H5E_BADTYPE, FAIL, "not a datatype"); |
86 | 0 | if (H5T_REFERENCE != src->shared->type) |
87 | 0 | HGOTO_ERROR(H5E_DATATYPE, H5E_BADTYPE, FAIL, "not a H5T_REFERENCE datatype"); |
88 | 0 | if (H5T_REFERENCE != dst->shared->type) |
89 | 0 | HGOTO_ERROR(H5E_DATATYPE, H5E_BADTYPE, FAIL, "not a H5T_REFERENCE datatype"); |
90 | | /* Only allow for source reference that is not an opaque type, destination must be opaque */ |
91 | 0 | if (!dst->shared->u.atomic.u.r.opaque) |
92 | 0 | HGOTO_ERROR(H5E_DATATYPE, H5E_BADTYPE, FAIL, "not an H5T_STD_REF datatype"); |
93 | | |
94 | | /* Reference types don't need a background buffer */ |
95 | 0 | cdata->need_bkg = H5T_BKG_NO; |
96 | 0 | break; |
97 | | |
98 | 0 | case H5T_CONV_FREE: |
99 | 0 | break; |
100 | | |
101 | 0 | case H5T_CONV_CONV: { |
102 | | /* |
103 | | * Conversion. |
104 | | */ |
105 | 0 | if (NULL == src || NULL == dst) |
106 | 0 | HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a datatype"); |
107 | | |
108 | 0 | assert(src->shared->u.atomic.u.r.cls); |
109 | | |
110 | | /* Initialize source & destination strides */ |
111 | 0 | if (buf_stride) { |
112 | 0 | assert(buf_stride >= src->shared->size); |
113 | 0 | assert(buf_stride >= dst->shared->size); |
114 | 0 | H5_CHECK_OVERFLOW(buf_stride, size_t, ssize_t); |
115 | 0 | s_stride = d_stride = (ssize_t)buf_stride; |
116 | 0 | } /* end if */ |
117 | 0 | else { |
118 | 0 | H5_CHECK_OVERFLOW(src->shared->size, size_t, ssize_t); |
119 | 0 | H5_CHECK_OVERFLOW(dst->shared->size, size_t, ssize_t); |
120 | 0 | s_stride = (ssize_t)src->shared->size; |
121 | 0 | d_stride = (ssize_t)dst->shared->size; |
122 | 0 | } /* end else */ |
123 | 0 | if (bkg) { |
124 | 0 | if (bkg_stride) |
125 | 0 | b_stride = (ssize_t)bkg_stride; |
126 | 0 | else |
127 | 0 | b_stride = d_stride; |
128 | 0 | } /* end if */ |
129 | 0 | else |
130 | 0 | b_stride = 0; |
131 | | |
132 | | /* Save info for unraveling on errors */ |
133 | 0 | orig_d_stride = (size_t)d_stride; |
134 | 0 | convert_forward = !(d_stride > s_stride); |
135 | | |
136 | | /* The outer loop of the type conversion macro, controlling which */ |
137 | | /* direction the buffer is walked */ |
138 | 0 | while (nelmts > 0) { |
139 | | /* Check if we need to go backwards through the buffer */ |
140 | 0 | if (d_stride > s_stride) { |
141 | | /* Sanity check */ |
142 | 0 | assert(s_stride > 0); |
143 | 0 | assert(d_stride > 0); |
144 | 0 | assert(b_stride >= 0); |
145 | | |
146 | | /* Compute the number of "safe" destination elements at */ |
147 | | /* the end of the buffer (Those which don't overlap with */ |
148 | | /* any source elements at the beginning of the buffer) */ |
149 | 0 | safe = |
150 | 0 | nelmts - (((nelmts * (size_t)s_stride) + ((size_t)d_stride - 1)) / (size_t)d_stride); |
151 | | |
152 | | /* If we're down to the last few elements, just wrap up */ |
153 | | /* with a "real" reverse copy */ |
154 | 0 | if (safe < 2) { |
155 | 0 | s = (uint8_t *)buf + (nelmts - 1) * (size_t)s_stride; |
156 | 0 | d = (uint8_t *)buf + (nelmts - 1) * (size_t)d_stride; |
157 | 0 | if (bkg) |
158 | 0 | b = (uint8_t *)bkg + (nelmts - 1) * (size_t)b_stride; |
159 | 0 | s_stride = -s_stride; |
160 | 0 | d_stride = -d_stride; |
161 | 0 | b_stride = -b_stride; |
162 | |
|
163 | 0 | safe = nelmts; |
164 | 0 | } /* end if */ |
165 | 0 | else { |
166 | 0 | s = (uint8_t *)buf + (nelmts - safe) * (size_t)s_stride; |
167 | 0 | d = (uint8_t *)buf + (nelmts - safe) * (size_t)d_stride; |
168 | 0 | if (bkg) |
169 | 0 | b = (uint8_t *)bkg + (nelmts - safe) * (size_t)b_stride; |
170 | 0 | } /* end else */ |
171 | 0 | } /* end if */ |
172 | 0 | else { |
173 | | /* Single forward pass over all data */ |
174 | 0 | s = d = (uint8_t *)buf; |
175 | 0 | b = (uint8_t *)bkg; |
176 | 0 | safe = nelmts; |
177 | 0 | } /* end else */ |
178 | |
|
179 | 0 | for (elmtno = 0; elmtno < safe; elmtno++) { |
180 | 0 | size_t buf_size; |
181 | 0 | bool dst_copy = false; |
182 | 0 | bool is_nil; /* Whether reference is "nil" */ |
183 | | |
184 | | /* Check for "nil" source reference */ |
185 | 0 | if ((*(src->shared->u.atomic.u.r.cls->isnull))(src->shared->u.atomic.u.r.file, s, |
186 | 0 | &is_nil) < 0) |
187 | 0 | HGOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, FAIL, |
188 | 0 | "can't check if reference data is 'nil'"); |
189 | | |
190 | 0 | if (is_nil) { |
191 | | /* Write "nil" reference to destination location */ |
192 | 0 | if ((*(dst->shared->u.atomic.u.r.cls->setnull))(dst->shared->u.atomic.u.r.file, d, |
193 | 0 | b) < 0) |
194 | 0 | HGOTO_ERROR(H5E_DATATYPE, H5E_WRITEERROR, FAIL, |
195 | 0 | "can't set reference data to 'nil'"); |
196 | 0 | } /* end else-if */ |
197 | 0 | else { |
198 | | /* Get size of references */ |
199 | 0 | if (0 == (buf_size = src->shared->u.atomic.u.r.cls->getsize( |
200 | 0 | src->shared->u.atomic.u.r.file, s, src->shared->size, |
201 | 0 | dst->shared->u.atomic.u.r.file, &dst_copy))) |
202 | 0 | HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "unable to obtain size of reference"); |
203 | | |
204 | | /* Check if conversion buffer is large enough, resize if necessary. */ |
205 | 0 | if (conv_buf_size < buf_size) { |
206 | 0 | conv_buf_size = buf_size; |
207 | 0 | if (NULL == (conv_buf = H5FL_BLK_REALLOC(ref_seq, conv_buf, conv_buf_size))) |
208 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, |
209 | 0 | "memory allocation failed for type conversion"); |
210 | 0 | memset(conv_buf, 0, conv_buf_size); |
211 | 0 | } /* end if */ |
212 | | |
213 | 0 | if (dst_copy && (src->shared->u.atomic.u.r.loc == H5T_LOC_DISK)) |
214 | 0 | H5MM_memcpy(conv_buf, s, buf_size); |
215 | 0 | else { |
216 | | /* Read reference */ |
217 | 0 | if (src->shared->u.atomic.u.r.cls->read( |
218 | 0 | src->shared->u.atomic.u.r.file, s, src->shared->size, |
219 | 0 | dst->shared->u.atomic.u.r.file, conv_buf, buf_size) < 0) |
220 | 0 | HGOTO_ERROR(H5E_DATATYPE, H5E_READERROR, FAIL, "can't read reference data"); |
221 | 0 | } /* end else */ |
222 | | |
223 | 0 | if (dst_copy && (dst->shared->u.atomic.u.r.loc == H5T_LOC_DISK)) |
224 | 0 | H5MM_memcpy(d, conv_buf, buf_size); |
225 | 0 | else { |
226 | | /* Write reference to destination location */ |
227 | 0 | if (dst->shared->u.atomic.u.r.cls->write( |
228 | 0 | src->shared->u.atomic.u.r.file, conv_buf, buf_size, |
229 | 0 | src->shared->u.atomic.u.r.rtype, dst->shared->u.atomic.u.r.file, d, |
230 | 0 | dst->shared->size, b) < 0) |
231 | 0 | HGOTO_ERROR(H5E_DATATYPE, H5E_WRITEERROR, FAIL, "can't write reference data"); |
232 | 0 | } /* end else */ |
233 | 0 | } /* end else */ |
234 | | |
235 | | /* Indicate that elements have been converted, in case of error */ |
236 | 0 | conversions_made = true; |
237 | | |
238 | | /* Advance pointers */ |
239 | 0 | s += s_stride; |
240 | 0 | d += d_stride; |
241 | |
|
242 | 0 | if (b) |
243 | 0 | b += b_stride; |
244 | 0 | } /* end for */ |
245 | | |
246 | | /* Decrement number of elements left to convert */ |
247 | 0 | nelmts -= safe; |
248 | 0 | } /* end while */ |
249 | 0 | } /* end case */ |
250 | 0 | break; |
251 | | |
252 | 0 | default: /* Some other command we don't know about yet.*/ |
253 | 0 | HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, "unknown conversion command"); |
254 | 0 | } /* end switch */ |
255 | | |
256 | 0 | done: |
257 | | /* Release converted elements on error */ |
258 | 0 | if (ret_value < 0 && conversions_made) { |
259 | 0 | H5R_ref_priv_t ref_priv; |
260 | 0 | size_t dest_count; |
261 | | |
262 | | /* Set up for first pass to destroy references */ |
263 | 0 | if (nelmts < orig_nelmts || (convert_forward && elmtno < safe)) { |
264 | 0 | dest_count = orig_nelmts - nelmts; |
265 | | |
266 | | /* Set pointer to correct location, based on direction chosen */ |
267 | 0 | if (convert_forward) { |
268 | 0 | d = (uint8_t *)buf; |
269 | 0 | dest_count += elmtno; /* Include partial iteration in first pass, for forward conversions */ |
270 | 0 | } |
271 | 0 | else |
272 | 0 | d = (uint8_t *)buf + (nelmts * orig_d_stride); |
273 | | |
274 | | /* Destroy references that have already been converted */ |
275 | 0 | while (dest_count > 0) { |
276 | 0 | memcpy(&ref_priv, d, sizeof(H5R_ref_priv_t)); |
277 | 0 | H5R__destroy(&ref_priv); /* Ignore errors at this point */ |
278 | 0 | d += orig_d_stride; |
279 | 0 | dest_count--; |
280 | 0 | } |
281 | 0 | } |
282 | | |
283 | | /* Do any remaining partial iteration, if converting backwards */ |
284 | 0 | if (!convert_forward && elmtno < safe) { |
285 | 0 | dest_count = elmtno; |
286 | | |
287 | | /* Set pointer to correct location */ |
288 | 0 | if (d_stride > 0) |
289 | 0 | d = (uint8_t *)buf + ((nelmts - safe) * orig_d_stride); |
290 | 0 | else |
291 | 0 | d = (uint8_t *)buf + ((nelmts - elmtno) * orig_d_stride); |
292 | | |
293 | | /* Destroy references that have already been converted */ |
294 | 0 | while (dest_count > 0) { |
295 | 0 | memcpy(&ref_priv, d, sizeof(H5R_ref_priv_t)); |
296 | 0 | H5R__destroy(&ref_priv); /* Ignore errors at this point */ |
297 | 0 | d += orig_d_stride; |
298 | 0 | dest_count--; |
299 | 0 | } |
300 | 0 | } |
301 | 0 | } |
302 | | |
303 | | /* Release the conversion buffer (always allocated, except on errors) */ |
304 | 0 | if (conv_buf) |
305 | 0 | conv_buf = H5FL_BLK_FREE(ref_seq, conv_buf); |
306 | |
|
307 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
308 | 0 | } /* end H5T__conv_ref() */ |