Coverage Report

Created: 2025-11-24 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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() */