Coverage Report

Created: 2025-08-28 06:37

/src/hdf5/src/H5Oalloc.c
Line
Count
Source (jump to first uncovered line)
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
 *
15
 * Created:   H5Oalloc.c
16
 *
17
 * Purpose:   Object header allocation routines.
18
 *
19
 *-------------------------------------------------------------------------
20
 */
21
22
/****************/
23
/* Module Setup */
24
/****************/
25
26
#include "H5Omodule.h" /* This source code file is part of the H5O module */
27
28
/***********/
29
/* Headers */
30
/***********/
31
#include "H5private.h"   /* Generic Functions     */
32
#include "H5Eprivate.h"  /* Error handling        */
33
#include "H5FLprivate.h" /* Free lists                           */
34
#include "H5MFprivate.h" /* File memory management    */
35
#include "H5MMprivate.h" /* Memory management     */
36
#include "H5Opkg.h"      /* Object headers      */
37
38
/****************/
39
/* Local Macros */
40
/****************/
41
42
/******************/
43
/* Local Typedefs */
44
/******************/
45
46
/********************/
47
/* Package Typedefs */
48
/********************/
49
50
/********************/
51
/* Local Prototypes */
52
/********************/
53
54
static herr_t H5O__add_gap(H5F_t *f, H5O_t *oh, unsigned chunkno, bool *chk_dirtied, size_t idx,
55
                           uint8_t *new_gap_loc, size_t new_gap_size);
56
static herr_t H5O__eliminate_gap(H5O_t *oh, bool *chk_dirtied, H5O_mesg_t *mesg, uint8_t *new_gap_loc,
57
                                 size_t new_gap_size);
58
static herr_t H5O__alloc_null(H5F_t *f, H5O_t *oh, size_t null_idx, const H5O_msg_class_t *new_type,
59
                              void *new_native, size_t new_size);
60
static htri_t H5O__alloc_extend_chunk(H5F_t *f, H5O_t *oh, unsigned chunkno, size_t size, size_t *msg_idx);
61
static herr_t H5O__alloc_find_best_nonnull(const H5F_t *f, const H5O_t *oh, size_t *size,
62
                                           H5O_msg_alloc_info_t *found_msg);
63
static herr_t H5O__alloc_new_chunk(H5F_t *f, H5O_t *oh, size_t size, size_t *new_idx);
64
static herr_t H5O__alloc_find_best_null(const H5O_t *oh, size_t size, size_t *mesg_idx);
65
static htri_t H5O__move_cont(H5F_t *f, H5O_t *oh, unsigned cont_u);
66
static htri_t H5O__move_msgs_forward(H5F_t *f, H5O_t *oh);
67
static htri_t H5O__merge_null(H5F_t *f, H5O_t *oh);
68
static htri_t H5O__remove_empty_chunks(H5F_t *f, H5O_t *oh);
69
static herr_t H5O__alloc_shrink_chunk(H5F_t *f, H5O_t *oh, unsigned chunkno);
70
71
/*********************/
72
/* Package Variables */
73
/*********************/
74
75
/* Declare extern the free list for H5O_cont_t's */
76
H5FL_EXTERN(H5O_cont_t);
77
78
/*****************************/
79
/* Library Private Variables */
80
/*****************************/
81
82
/*******************/
83
/* Local Variables */
84
/*******************/
85
86
/*-------------------------------------------------------------------------
87
 * Function:    H5O__add_gap
88
 *
89
 * Purpose:     Add a gap to a chunk
90
 *
91
 * Return:  Non-negative on success/Negative on failure
92
 *
93
 *-------------------------------------------------------------------------
94
 */
95
static herr_t
96
H5O__add_gap(H5F_t H5_ATTR_NDEBUG_UNUSED *f, H5O_t *oh, unsigned chunkno, bool *chk_dirtied, size_t idx,
97
             uint8_t *new_gap_loc, size_t new_gap_size)
98
14
{
99
14
    bool   merged_with_null;    /* Whether the gap was merged with a null message */
100
14
    size_t u;                   /* Local index variable */
101
14
    herr_t ret_value = SUCCEED; /* Return value */
102
103
14
    FUNC_ENTER_PACKAGE
104
105
    /* check args */
106
14
    assert(oh);
107
14
    assert(oh->version > H5O_VERSION_1);
108
14
    assert(chk_dirtied);
109
14
    assert(new_gap_loc);
110
14
    assert(new_gap_size);
111
112
#ifndef NDEBUG
113
    if (chunkno > 0) {
114
        unsigned chk_proxy_status = 0; /* Object header chunk proxy entry cache status */
115
116
        /* Check the object header chunk proxy's status in the metadata cache */
117
        if (H5AC_get_entry_status(f, oh->chunk[chunkno].addr, &chk_proxy_status) < 0)
118
            HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL,
119
                        "unable to check metadata cache status for object header chunk proxy");
120
121
        /* Make certain that object header is protected */
122
        assert(chk_proxy_status & H5AC_ES__IS_PROTECTED);
123
    }  /* end if */
124
#endif /* NDEBUG */
125
126
    /* Check for existing null message in chunk */
127
14
    merged_with_null = false;
128
56
    for (u = 0; u < oh->nmesgs && !merged_with_null; u++) {
129
        /* Find a null message in the chunk with the new gap */
130
        /* (a null message that's not the one we are eliminating) */
131
42
        if (H5O_NULL_ID == oh->mesg[u].type->id && oh->mesg[u].chunkno == chunkno && u != idx) {
132
            /* Sanity check - chunks with null messages shouldn't have a gap */
133
0
            assert(oh->chunk[chunkno].gap == 0);
134
135
            /* Eliminate the gap in the chunk */
136
0
            if (H5O__eliminate_gap(oh, chk_dirtied, &oh->mesg[u], new_gap_loc, new_gap_size) < 0)
137
0
                HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't eliminate gap in chunk");
138
139
            /* Set flag to indicate that the gap was handled */
140
0
            merged_with_null = true;
141
0
        } /* end if */
142
42
    }     /* end for */
143
144
    /* If we couldn't find a null message in the chunk, move the gap to the end */
145
14
    if (!merged_with_null) {
146
        /* Adjust message offsets after new gap forward in chunk */
147
56
        for (u = 0; u < oh->nmesgs; u++)
148
42
            if (oh->mesg[u].chunkno == chunkno && oh->mesg[u].raw > new_gap_loc)
149
0
                oh->mesg[u].raw -= new_gap_size;
150
151
        /* Slide raw message info forward in chunk image */
152
14
        memmove(new_gap_loc, new_gap_loc + new_gap_size,
153
14
                (size_t)((oh->chunk[chunkno].image + (oh->chunk[chunkno].size - H5O_SIZEOF_CHKSUM_OH(oh))) -
154
14
                         (new_gap_loc + new_gap_size)));
155
156
        /* Add existing gap size to new gap size */
157
14
        new_gap_size += oh->chunk[chunkno].gap;
158
159
        /* Merging with existing gap will allow for a new null message */
160
14
        if (new_gap_size >= (size_t)H5O_SIZEOF_MSGHDR_OH(oh)) {
161
0
            H5O_mesg_t *null_msg; /* Pointer to new null message */
162
163
            /* Check if we need to extend message table to hold the new null message */
164
0
            if (oh->nmesgs >= oh->alloc_nmesgs)
165
0
                if (H5O__alloc_msgs(oh, (size_t)1) < 0)
166
0
                    HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate more space for messages");
167
168
            /* Increment new gap size */
169
0
            oh->chunk[chunkno].gap += new_gap_size;
170
171
            /* Create new null message, with the tail of the previous null message */
172
0
            null_msg           = &(oh->mesg[oh->nmesgs++]);
173
0
            null_msg->type     = H5O_MSG_NULL;
174
0
            null_msg->native   = NULL;
175
0
            null_msg->raw_size = new_gap_size - (size_t)H5O_SIZEOF_MSGHDR_OH(oh);
176
0
            null_msg->raw      = (oh->chunk[chunkno].image + oh->chunk[chunkno].size) -
177
0
                            (H5O_SIZEOF_CHKSUM_OH(oh) + null_msg->raw_size);
178
0
            null_msg->chunkno = chunkno;
179
180
            /* Zero out new null message's raw data */
181
0
            if (null_msg->raw_size)
182
0
                memset(null_msg->raw, 0, null_msg->raw_size);
183
184
            /* Mark message as dirty */
185
0
            null_msg->dirty = true;
186
187
            /* Reset size of gap in chunk */
188
0
            oh->chunk[chunkno].gap = 0;
189
0
        } /* end if */
190
14
        else
191
14
            oh->chunk[chunkno].gap = new_gap_size;
192
193
        /* Mark the chunk as modified */
194
14
        *chk_dirtied = true;
195
14
    } /* end if */
196
197
14
done:
198
14
    FUNC_LEAVE_NOAPI(ret_value)
199
14
} /* H5O__add_gap() */
200
201
/*-------------------------------------------------------------------------
202
 * Function:    H5O__eliminate_gap
203
 *
204
 * Purpose:     Eliminate a gap in a chunk with a null message.
205
 *
206
 * Note:        Sometimes this happens as a result of converting an existing
207
 *              non-null message to a null message, so we zero out the gap
208
 *              here, even though it might already be zero (when we're adding
209
 *              a gap to a chunk with an existing null message).  (Mostly,
210
 *              this just simplifies the code, esp. with the necessary chunk
211
 *              locking -QAK)
212
 *
213
 * Return:  Non-negative on success/Negative on failure
214
 *
215
 *-------------------------------------------------------------------------
216
 */
217
static herr_t
218
H5O__eliminate_gap(H5O_t *oh, bool *chk_dirtied, H5O_mesg_t *mesg, uint8_t *gap_loc, size_t gap_size)
219
14
{
220
14
    uint8_t *move_start, *move_end; /* Pointers to area of messages to move */
221
14
    bool     null_before_gap;       /* Flag whether the null message is before the gap or not */
222
223
14
    FUNC_ENTER_PACKAGE_NOERR
224
225
    /* check args */
226
14
    assert(oh);
227
14
    assert(oh->version > H5O_VERSION_1);
228
14
    assert(chk_dirtied);
229
14
    assert(mesg);
230
14
    assert(gap_loc);
231
14
    assert(gap_size);
232
233
    /* Check if the null message is before or after the gap produced */
234
14
    null_before_gap = (bool)(mesg->raw < gap_loc);
235
236
    /* Set up information about region of messages to move */
237
14
    if (null_before_gap) {
238
14
        move_start = mesg->raw + mesg->raw_size;
239
14
        move_end   = gap_loc;
240
14
    } /* end if */
241
0
    else {
242
0
        move_start = gap_loc + gap_size;
243
0
        move_end   = mesg->raw - H5O_SIZEOF_MSGHDR_OH(oh);
244
0
    } /* end else */
245
246
    /* Check for messages between null message and gap */
247
14
    if (move_end > move_start) {
248
0
        unsigned u; /* Local index variable */
249
250
        /* Look for messages that need to move, to adjust raw pointers in chunk */
251
        /* (this doesn't change the moved messages 'dirty' state) */
252
0
        for (u = 0; u < oh->nmesgs; u++) {
253
0
            uint8_t *msg_start; /* Start of encoded message in chunk */
254
255
0
            msg_start = oh->mesg[u].raw - H5O_SIZEOF_MSGHDR_OH(oh);
256
0
            if (oh->mesg[u].chunkno == mesg->chunkno && (msg_start >= move_start && msg_start < move_end)) {
257
                /* Move message's raw pointer in appropriate direction */
258
0
                if (null_before_gap)
259
0
                    oh->mesg[u].raw += gap_size;
260
0
                else
261
0
                    oh->mesg[u].raw -= gap_size;
262
0
            } /* end if */
263
0
        }     /* end for */
264
265
        /* Slide raw message info in chunk image */
266
0
        if (null_before_gap)
267
            /* Slide messages down */
268
0
            memmove(move_start + gap_size, move_start, (size_t)(move_end - move_start));
269
0
        else {
270
            /* Slide messages up */
271
0
            memmove(move_start - gap_size, move_start, (size_t)(move_end - move_start));
272
273
            /* Adjust start of null message */
274
0
            mesg->raw -= gap_size;
275
0
        } /* end else */
276
0
    }
277
14
    else if (move_end == move_start && !null_before_gap) {
278
        /* Slide null message up */
279
0
        memmove(move_start - gap_size, move_start, mesg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh));
280
281
        /* Adjust start of null message */
282
0
        mesg->raw -= gap_size;
283
0
    } /* end if */
284
285
    /* Zero out addition to null message */
286
14
    memset(mesg->raw + mesg->raw_size, 0, gap_size);
287
288
    /* Adjust size of null message */
289
14
    mesg->raw_size += gap_size;
290
291
    /* Set the gap size to zero for the chunk */
292
14
    oh->chunk[mesg->chunkno].gap = 0;
293
294
    /* Mark null message as dirty */
295
14
    mesg->dirty  = true;
296
14
    *chk_dirtied = true;
297
298
14
    FUNC_LEAVE_NOAPI(SUCCEED)
299
14
} /* H5O__eliminate_gap() */
300
301
/*-------------------------------------------------------------------------
302
 *
303
 * Function:    H5O__alloc_null
304
 *
305
 * Purpose:     Allocate room for a new message from a null message
306
 *
307
 * Return:  Non-negative on success/Negative on failure
308
 *
309
 *-------------------------------------------------------------------------
310
 */
311
static herr_t
312
H5O__alloc_null(H5F_t *f, H5O_t *oh, size_t null_idx, const H5O_msg_class_t *new_type, void *new_native,
313
                size_t new_size)
314
316
{
315
316
    H5O_chunk_proxy_t *chk_proxy   = NULL;  /* Chunk that message is in */
316
316
    bool               chk_dirtied = false; /* Flags for unprotecting chunk */
317
316
    H5O_mesg_t        *alloc_msg;           /* Pointer to null message to allocate out of */
318
316
    herr_t             ret_value = SUCCEED; /* Return value */
319
320
316
    FUNC_ENTER_PACKAGE
321
322
    /* check args */
323
316
    assert(oh);
324
316
    assert(new_type);
325
316
    assert(new_size);
326
327
    /* Point to null message to allocate out of */
328
316
    alloc_msg = &oh->mesg[null_idx];
329
330
    /* Protect chunk */
331
316
    if (NULL == (chk_proxy = H5O__chunk_protect(f, oh, alloc_msg->chunkno)))
332
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header chunk");
333
334
    /* Check if there's a need to split the null message */
335
316
    if (alloc_msg->raw_size > new_size) {
336
        /* Check for producing a gap in the chunk */
337
211
        if ((alloc_msg->raw_size - new_size) < (size_t)H5O_SIZEOF_MSGHDR_OH(oh)) {
338
14
            size_t gap_size = alloc_msg->raw_size - new_size; /* Size of gap produced */
339
340
            /* Adjust the size of the null message being eliminated */
341
14
            alloc_msg->raw_size = new_size;
342
343
            /* Add the gap to the chunk */
344
14
            if (H5O__add_gap(f, oh, alloc_msg->chunkno, &chk_dirtied, null_idx,
345
14
                             alloc_msg->raw + alloc_msg->raw_size, gap_size) < 0)
346
0
                HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't insert gap in chunk");
347
14
        } /* end if */
348
197
        else {
349
197
            size_t new_mesg_size =
350
197
                new_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh); /* Total size of newly allocated message */
351
197
            H5O_mesg_t *null_msg;                            /* Pointer to new null message */
352
353
            /* Check if we need to extend message table to hold the new null message */
354
197
            if (oh->nmesgs >= oh->alloc_nmesgs) {
355
0
                if (H5O__alloc_msgs(oh, (size_t)1) < 0)
356
0
                    HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate more space for messages");
357
358
                /* "Retarget" 'alloc_msg' pointer into newly re-allocated array of messages */
359
0
                alloc_msg = &oh->mesg[null_idx];
360
0
            } /* end if */
361
362
            /* Create new null message, with the tail of the previous null message */
363
197
            null_msg           = &(oh->mesg[oh->nmesgs++]);
364
197
            null_msg->type     = H5O_MSG_NULL;
365
197
            null_msg->native   = NULL;
366
197
            null_msg->raw      = alloc_msg->raw + new_mesg_size;
367
197
            null_msg->raw_size = alloc_msg->raw_size - new_mesg_size;
368
197
            null_msg->chunkno  = alloc_msg->chunkno;
369
370
            /* Mark the message as dirty */
371
197
            null_msg->dirty = true;
372
197
            chk_dirtied     = true;
373
374
            /* Check for gap in new null message's chunk */
375
197
            if (oh->chunk[null_msg->chunkno].gap > 0) {
376
0
                unsigned null_chunkno = null_msg->chunkno; /* Chunk w/gap */
377
378
                /* Eliminate the gap in the chunk */
379
0
                if (H5O__eliminate_gap(oh, &chk_dirtied, null_msg,
380
0
                                       ((oh->chunk[null_chunkno].image + oh->chunk[null_chunkno].size) -
381
0
                                        (H5O_SIZEOF_CHKSUM_OH(oh) + oh->chunk[null_chunkno].gap)),
382
0
                                       oh->chunk[null_chunkno].gap) < 0)
383
0
                    HGOTO_ERROR(H5E_OHDR, H5E_CANTREMOVE, FAIL, "can't eliminate gap in chunk");
384
0
            } /* end if */
385
386
            /* Set the size of the new "real" message */
387
197
            alloc_msg->raw_size = new_size;
388
197
        } /* end else */
389
211
    }     /* end if */
390
391
    /* Initialize the new message */
392
316
    alloc_msg->type   = new_type;
393
316
    alloc_msg->native = new_native;
394
395
    /* Mark the new message as dirty */
396
316
    alloc_msg->dirty = true;
397
316
    chk_dirtied      = true;
398
399
316
done:
400
    /* Release chunk */
401
316
    if (chk_proxy && H5O__chunk_unprotect(f, chk_proxy, chk_dirtied) < 0)
402
0
        HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect object header chunk");
403
404
316
    FUNC_LEAVE_NOAPI(ret_value)
405
316
} /* H5O__alloc_null() */
406
407
/*-------------------------------------------------------------------------
408
 *
409
 * Function:    H5O__alloc_msgs
410
 *
411
 * Purpose:     Allocate more messages for a header
412
 *
413
 * Return:  Non-negative on success/Negative on failure
414
 *
415
 *-------------------------------------------------------------------------
416
 */
417
herr_t
418
H5O__alloc_msgs(H5O_t *oh, size_t min_alloc)
419
4.95k
{
420
4.95k
    size_t      old_alloc;           /* Old number of messages allocated */
421
4.95k
    size_t      na;                  /* New number of messages allocated */
422
4.95k
    H5O_mesg_t *new_mesg;            /* Pointer to new message array */
423
4.95k
    herr_t      ret_value = SUCCEED; /* Return value */
424
425
4.95k
    FUNC_ENTER_PACKAGE
426
427
    /* check args */
428
4.95k
    assert(oh);
429
430
    /* Initialize number of messages information */
431
4.95k
    old_alloc = oh->alloc_nmesgs;
432
4.95k
    na        = oh->alloc_nmesgs + MAX(oh->alloc_nmesgs, min_alloc); /* At least double */
433
434
    /* Attempt to allocate more memory */
435
4.95k
    if (NULL == (new_mesg = H5FL_SEQ_REALLOC(H5O_mesg_t, oh->mesg, na)))
436
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");
437
438
    /* Update ohdr information */
439
4.95k
    oh->alloc_nmesgs = na;
440
4.95k
    oh->mesg         = new_mesg;
441
442
    /* Set new object header info to zeros */
443
4.95k
    memset(&oh->mesg[old_alloc], 0, (oh->alloc_nmesgs - old_alloc) * sizeof(H5O_mesg_t));
444
445
4.95k
done:
446
4.95k
    FUNC_LEAVE_NOAPI(ret_value)
447
4.95k
} /* H5O__alloc_msgs() */
448
449
/*-------------------------------------------------------------------------
450
 *
451
 * Function:    H5O__alloc_extend_chunk
452
 *
453
 * Purpose:     Attempt to extend a chunk that is allocated on disk.
454
 *
455
 *              If the extension is successful, and if the last message
456
 *    of the chunk is the null message, then that message will
457
 *    be extended with the chunk.  Otherwise a new null message
458
 *    is created.
459
 *
460
 *              f is the file in which the chunk will be written.  It is
461
 *              included to ensure that there is enough space to extend
462
 *              this chunk.
463
 *
464
 * Return:      true:   The chunk has been extended, and *msg_idx
465
 *        contains the message index for null message
466
 *        which is large enough to hold size bytes.
467
 *
468
 *    false:    The chunk cannot be extended, and *msg_idx
469
 *        is undefined.
470
 *
471
 *    FAIL:   Some internal error has been detected.
472
 *
473
 *-------------------------------------------------------------------------
474
 */
475
static htri_t
476
H5O__alloc_extend_chunk(H5F_t *f, H5O_t *oh, unsigned chunkno, size_t size, size_t *msg_idx)
477
117
{
478
117
    H5O_chunk_proxy_t *chk_proxy   = NULL;  /* Chunk that message is in */
479
117
    bool               chk_dirtied = false; /* Flag for unprotecting chunk */
480
117
    size_t             delta;               /* Change in chunk's size */
481
117
    size_t             aligned_size = H5O_ALIGN_OH(oh, size);
482
117
    uint8_t           *old_image;                 /* Old address of chunk's image in memory */
483
117
    size_t             old_size;                  /* Old size of chunk */
484
117
    htri_t             was_extended;              /* If chunk can be extended */
485
117
    size_t             extend_msg        = 0;     /* Index of null message to extend */
486
117
    bool               extended_msg      = false; /* Whether an existing message was extended */
487
117
    uint8_t            new_size_flags    = 0;     /* New chunk #0 size flags */
488
117
    bool               adjust_size_flags = false; /* Whether to adjust the chunk #0 size flags */
489
117
    size_t             extra_prfx_size   = 0;     /* Extra bytes added to object header prefix */
490
117
    size_t             u;                         /* Local index variable */
491
117
    htri_t             ret_value = true;          /* return value */
492
493
117
    FUNC_ENTER_PACKAGE
494
495
    /* check args */
496
117
    assert(f != NULL);
497
117
    assert(oh != NULL);
498
117
    assert(chunkno < oh->nchunks);
499
117
    assert(size > 0);
500
117
    assert(msg_idx != NULL);
501
117
    assert(H5_addr_defined(oh->chunk[chunkno].addr));
502
503
    /* Test to see if the specified chunk ends with a null messages.
504
     * If successful, set the index of the null message in extend_msg.
505
     */
506
1.93k
    for (u = 0; u < oh->nmesgs; u++) {
507
        /* Check for null message at end of proper chunk */
508
        /* (account for possible checksum at end of chunk) */
509
1.91k
        if (oh->mesg[u].chunkno == chunkno && H5O_NULL_ID == oh->mesg[u].type->id &&
510
1.91k
            ((oh->mesg[u].raw + oh->mesg[u].raw_size) ==
511
88
             ((oh->chunk[chunkno].image + oh->chunk[chunkno].size) -
512
88
              (oh->chunk[chunkno].gap + H5O_SIZEOF_CHKSUM_OH(oh))))) {
513
514
88
            extend_msg   = u;
515
88
            extended_msg = true;
516
88
            break;
517
88
        } /* end if */
518
1.91k
    }     /* end for */
519
520
    /* If we can extend an existing null message, adjust the delta appropriately */
521
117
    if (extended_msg) {
522
88
        assert(oh->chunk[chunkno].gap == 0);
523
88
        delta = aligned_size - oh->mesg[extend_msg].raw_size;
524
88
    } /* end if */
525
29
    else
526
29
        delta = (aligned_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh)) - oh->chunk[chunkno].gap;
527
117
    delta = H5O_ALIGN_OH(oh, delta);
528
529
    /* Check for changing the chunk #0 data size enough to need adjusting the flags */
530
117
    if (oh->version > H5O_VERSION_1 && chunkno == 0) {
531
22
        uint64_t chunk0_size;                                                     /* Size of chunk 0's data */
532
22
        size_t   orig_prfx_size = (size_t)1 << (oh->flags & H5O_HDR_CHUNK0_SIZE); /* Original prefix size */
533
534
22
        assert(oh->chunk[0].size >= (size_t)H5O_SIZEOF_HDR(oh));
535
22
        chunk0_size = oh->chunk[0].size - (size_t)H5O_SIZEOF_HDR(oh);
536
537
        /* Check for moving to a 8-byte size encoding */
538
22
        if (orig_prfx_size < 8 && (chunk0_size + delta) > 4294967295) {
539
0
            extra_prfx_size   = 8 - orig_prfx_size;
540
0
            new_size_flags    = H5O_HDR_CHUNK0_8;
541
0
            adjust_size_flags = true;
542
0
        } /* end if */
543
        /* Check for moving to a 4-byte size encoding */
544
22
        else if (orig_prfx_size < 4 && (chunk0_size + delta) > 65535) {
545
0
            extra_prfx_size   = 4 - orig_prfx_size;
546
0
            new_size_flags    = H5O_HDR_CHUNK0_4;
547
0
            adjust_size_flags = true;
548
0
        } /* end if */
549
        /* Check for moving to a 2-byte size encoding */
550
22
        else if (orig_prfx_size < 2 && (chunk0_size + delta) > 255) {
551
0
            extra_prfx_size   = 2 - orig_prfx_size;
552
0
            new_size_flags    = H5O_HDR_CHUNK0_2;
553
0
            adjust_size_flags = true;
554
0
        } /* end if */
555
22
    }     /* end if */
556
557
    /* Protect chunk */
558
117
    if (NULL == (chk_proxy = H5O__chunk_protect(f, oh, chunkno)))
559
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header chunk");
560
561
    /* Determine whether the chunk can be extended */
562
117
    was_extended = H5MF_try_extend(f, H5FD_MEM_OHDR, oh->chunk[chunkno].addr,
563
117
                                   (hsize_t)(oh->chunk[chunkno].size), (hsize_t)(delta + extra_prfx_size));
564
117
    if (was_extended < 0) /* error */
565
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTEXTEND, FAIL, "can't tell if we can extend chunk");
566
117
    else if (was_extended == false) /* can't extend -- we are done */
567
97
        HGOTO_DONE(false);
568
569
    /* Adjust object header prefix flags */
570
20
    if (adjust_size_flags) {
571
0
        oh->flags = (uint8_t)(oh->flags & ~H5O_HDR_CHUNK0_SIZE);
572
0
        oh->flags |= new_size_flags;
573
574
        /* Mark object header as dirty in cache */
575
0
        if (H5AC_mark_entry_dirty(oh) < 0)
576
0
            HGOTO_ERROR(H5E_OHDR, H5E_CANTMARKDIRTY, FAIL, "unable to mark object header as dirty");
577
0
    } /* end if */
578
579
    /* If we can extend an existing null message, take care of that */
580
20
    if (extended_msg) {
581
        /* Adjust message size of existing null message */
582
16
        oh->mesg[extend_msg].raw_size += delta;
583
16
    } /* end if */
584
    /* Create new null message for end of chunk */
585
4
    else {
586
        /* Create a new null message */
587
4
        if (oh->nmesgs >= oh->alloc_nmesgs)
588
0
            if (H5O__alloc_msgs(oh, (size_t)1) < 0)
589
0
                HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate more space for messages");
590
591
        /* Set extension message */
592
4
        extend_msg = oh->nmesgs++;
593
594
        /* Initialize new null message */
595
4
        oh->mesg[extend_msg].type   = H5O_MSG_NULL;
596
4
        oh->mesg[extend_msg].native = NULL;
597
4
        oh->mesg[extend_msg].raw    = ((oh->chunk[chunkno].image + oh->chunk[chunkno].size) -
598
4
                                    (H5O_SIZEOF_CHKSUM_OH(oh) + oh->chunk[chunkno].gap)) +
599
4
                                   H5O_SIZEOF_MSGHDR_OH(oh);
600
4
        oh->mesg[extend_msg].raw_size = (delta + oh->chunk[chunkno].gap) - (size_t)H5O_SIZEOF_MSGHDR_OH(oh);
601
4
        oh->mesg[extend_msg].chunkno  = chunkno;
602
4
    } /* end else */
603
604
    /* Mark the extended message as dirty */
605
20
    oh->mesg[extend_msg].dirty = true;
606
20
    chk_dirtied                = true;
607
608
    /* Allocate more memory space for chunk's image */
609
20
    old_image = oh->chunk[chunkno].image;
610
20
    old_size  = oh->chunk[chunkno].size;
611
20
    oh->chunk[chunkno].size += delta + extra_prfx_size;
612
20
    oh->chunk[chunkno].image = H5FL_BLK_REALLOC(chunk_image, old_image, oh->chunk[chunkno].size);
613
20
    if (NULL == oh->chunk[chunkno].image)
614
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTALLOC, FAIL, "can't reallocate extended object header chunk");
615
20
    oh->chunk[chunkno].gap = 0;
616
617
    /* Wipe new space for chunk */
618
20
    memset(oh->chunk[chunkno].image + old_size, 0, oh->chunk[chunkno].size - old_size);
619
620
    /* Move chunk 0 data up if the size flags changed */
621
20
    if (adjust_size_flags)
622
0
        memmove(oh->chunk[0].image + H5O_SIZEOF_HDR(oh) - H5O_SIZEOF_CHKSUM_OH(oh),
623
0
                oh->chunk[0].image + H5O_SIZEOF_HDR(oh) - H5O_SIZEOF_CHKSUM_OH(oh) - extra_prfx_size,
624
0
                old_size - (size_t)H5O_SIZEOF_HDR(oh) + extra_prfx_size);
625
626
    /* Spin through existing messages, adjusting them */
627
448
    for (u = 0; u < oh->nmesgs; u++) {
628
        /* Adjust raw addresses for messages in this chunk to reflect new 'image' address */
629
428
        if (oh->mesg[u].chunkno == chunkno)
630
389
            oh->mesg[u].raw = oh->chunk[chunkno].image + extra_prfx_size + (oh->mesg[u].raw - old_image);
631
632
        /* Find continuation message which points to this chunk and adjust chunk's size */
633
        /* (Chunk 0 doesn't have a continuation message that points to it,
634
         * its size is directly encoded in the object header) */
635
428
        if (chunkno > 0 && (H5O_CONT_ID == oh->mesg[u].type->id) &&
636
428
            (((H5O_cont_t *)(oh->mesg[u].native))->chunkno == chunkno)) {
637
0
            H5O_chunk_proxy_t *chk_proxy2   = NULL;                /* Chunk that continuation message is in */
638
0
            bool               chk_dirtied2 = false;               /* Flag for unprotecting chunk */
639
0
            unsigned           cont_chunkno = oh->mesg[u].chunkno; /* Chunk # for continuation message */
640
641
            /* Protect chunk containing continuation message */
642
0
            if (NULL == (chk_proxy2 = H5O__chunk_protect(f, oh, cont_chunkno)))
643
0
                HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header chunk");
644
645
            /* Adjust size in continuation message */
646
0
            assert(((H5O_cont_t *)(oh->mesg[u].native))->size == old_size);
647
0
            ((H5O_cont_t *)(oh->mesg[u].native))->size = oh->chunk[chunkno].size;
648
649
            /* Flag continuation message as dirty */
650
0
            oh->mesg[u].dirty = true;
651
0
            chk_dirtied2      = true;
652
653
            /* Release chunk containing continuation message */
654
0
            if (H5O__chunk_unprotect(f, chk_proxy2, chk_dirtied2) < 0)
655
0
                HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect object header chunk");
656
0
        } /* end if */
657
428
    }     /* end for */
658
659
    /* Resize the chunk in the cache */
660
20
    if (H5O__chunk_resize(oh, chk_proxy) < 0)
661
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTRESIZE, FAIL, "unable to resize object header chunk");
662
663
    /* Set new message index */
664
20
    *msg_idx = extend_msg;
665
666
117
done:
667
    /* Release chunk */
668
117
    if (chk_proxy && H5O__chunk_unprotect(f, chk_proxy, chk_dirtied) < 0)
669
0
        HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect object header chunk");
670
671
117
    FUNC_LEAVE_NOAPI(ret_value)
672
117
} /* H5O__alloc_extend_chunk() */
673
674
/*-------------------------------------------------------------------------
675
 * Function:    H5O__alloc_find_best_nonnull
676
 *
677
 * Purpose:     Find the best fit non-null message for a given size of message
678
 *              to allocate.
679
 *
680
 * Note:  The algorithm for finding a message to replace with a
681
 *    continuation message is still fairly limited.  It's possible
682
 *    that two (or more) messages smaller than a continuation message
683
 *    might occupy a chunk and need to be moved in order to make
684
 *    room for the continuation message.
685
 *
686
 *    Also, we aren't checking for NULL messages in front of another
687
 *    message right now...
688
 *
689
 * Return:      Success:        Index number of the null message for the
690
 *                              new chunk.  The null message will be at
691
 *                              least SIZE bytes not counting the message
692
 *                              ID or size fields.
693
 *
694
 *              Failure:        Negative
695
 *
696
 *-------------------------------------------------------------------------
697
 */
698
static herr_t
699
H5O__alloc_find_best_nonnull(const H5F_t *f, const H5O_t *oh, size_t *size, H5O_msg_alloc_info_t *found_msg)
700
36
{
701
36
    H5O_mesg_t *curr_msg;   /* Pointer to current message to operate on */
702
36
    size_t      cont_size;  /* Continuation message size */
703
36
    size_t      multi_size; /* Size of all the messages in the last chunk */
704
36
    unsigned    u;          /* Local index variable */
705
706
36
    FUNC_ENTER_PACKAGE_NOERR
707
708
    /* Check args */
709
36
    assert(f);
710
36
    assert(oh);
711
36
    assert(size);
712
36
    assert(*size > 0);
713
36
    assert(found_msg);
714
715
    /*
716
     * Find the smallest message that could be moved to make room for the
717
     * continuation message.
718
     *
719
     * Don't ever move continuation message from one chunk to another.
720
     *
721
     * Avoid moving attributes when possible to preserve their
722
     * ordering (although ordering is *not* guaranteed!).
723
     *
724
     */
725
36
    cont_size  = H5O_ALIGN_OH(oh, (size_t)(H5F_SIZEOF_ADDR(f) + H5F_SIZEOF_SIZE(f)));
726
36
    multi_size = 0;
727
703
    for (u = 0, curr_msg = &oh->mesg[0]; u < oh->nmesgs; u++, curr_msg++) {
728
        /* Don't consider continuation messages (for now) */
729
667
        if (H5O_CONT_ID != curr_msg->type->id) {
730
593
            unsigned msg_chunkno = curr_msg->chunkno; /* Chunk that the message is in */
731
593
            uint8_t *end_chunk_data =
732
593
                (oh->chunk[msg_chunkno].image + oh->chunk[msg_chunkno].size) -
733
593
                (H5O_SIZEOF_CHKSUM_OH(oh) + oh->chunk[msg_chunkno].gap); /* End of message data in chunk */
734
593
            uint8_t *end_msg    = curr_msg->raw + curr_msg->raw_size;    /* End of current message */
735
593
            size_t   gap_size   = 0; /* Size of gap after current message */
736
593
            size_t   null_size  = 0; /* Size of NULL message after current message */
737
593
            unsigned null_msgno = 0; /* Index of NULL message after current message */
738
593
            size_t   total_size;     /* Total size of available space "around" current message */
739
740
            /* Check if the message is the last one in the chunk */
741
593
            if (end_msg == end_chunk_data)
742
35
                gap_size = oh->chunk[msg_chunkno].gap;
743
558
            else {
744
558
                H5O_mesg_t *tmp_msg; /* Temp. pointer to message to operate on */
745
558
                unsigned    v;       /* Local index variable */
746
747
                /* Check for null message after this message, in same chunk */
748
25.6k
                for (v = 0, tmp_msg = &oh->mesg[0]; v < oh->nmesgs; v++, tmp_msg++) {
749
25.1k
                    if (tmp_msg->type->id == H5O_NULL_ID &&
750
25.1k
                        (tmp_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh)) == end_msg) {
751
29
                        null_msgno = v;
752
29
                        null_size  = (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + tmp_msg->raw_size;
753
29
                        break;
754
29
                    } /* end if */
755
756
                    /* XXX: Should also check for NULL message in front of current message... */
757
758
25.1k
                } /* end for */
759
558
            }     /* end else */
760
761
            /* Add up current message's total available space */
762
593
            total_size = curr_msg->raw_size + gap_size + null_size;
763
764
            /* Check if message is large enough to hold continuation info */
765
593
            if (total_size >= cont_size) {
766
44
                bool better = false; /* Whether the current message is better than a previous one */
767
768
                /* Check for first message that can be moved */
769
44
                if (found_msg->msgno < 0)
770
29
                    better = true;
771
15
                else {
772
                    /* Prioritize moving non-attributes above attributes */
773
                    /* (Even attributes with an otherwise better fit */
774
15
                    if (found_msg->id == H5O_ATTR_ID && curr_msg->type->id != H5O_ATTR_ID)
775
3
                        better = true;
776
                    /* Either two attributes, or two non-attributes */
777
12
                    else {
778
                        /* Take a smaller one */
779
12
                        if (total_size < found_msg->total_size)
780
4
                            better = true;
781
                        /* If they are the same size, choose the earliest one
782
                         * in the chunk array */
783
                        /* (Could also bias toward message earlier / later
784
                         *      chunk in, but shouldn't be a big deal - QAK, 2016/10/21)
785
                         */
786
8
                        else if (total_size == found_msg->total_size) {
787
7
                            if (msg_chunkno < found_msg->chunkno)
788
0
                                better = true;
789
7
                        } /* end else-if */
790
12
                    }     /* end else */
791
15
                }         /* end else */
792
793
                /* If we found a better message, keep its info */
794
44
                if (better) {
795
36
                    found_msg->msgno      = (int)u;
796
36
                    found_msg->id         = curr_msg->type->id;
797
36
                    found_msg->chunkno    = msg_chunkno;
798
36
                    found_msg->gap_size   = gap_size;
799
36
                    found_msg->null_size  = null_size;
800
36
                    found_msg->total_size = total_size;
801
36
                    found_msg->null_msgno = null_msgno;
802
36
                } /* end if */
803
44
            }     /* end if */
804
549
            else if (found_msg->msgno < 0 && msg_chunkno == oh->nchunks - 1)
805
                /* Keep track of the total size of smaller messages in the last
806
                 * chunk, in case we need to move more than 1 message.
807
                 */
808
404
                multi_size += curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh);
809
593
        } /* end if */
810
667
    }     /* end for */
811
812
    /*
813
     * If we must move some other message to make room for the null
814
     * message, then make sure the new chunk has enough room for that
815
     * other message.
816
     *
817
     * Move other messages first, and attributes only as a last resort.
818
     *
819
     * If all else fails, move every message in the last chunk.
820
     *
821
     */
822
36
    if (found_msg->msgno < 0)
823
7
        *size += multi_size;
824
29
    else
825
29
        *size += (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + oh->mesg[found_msg->msgno].raw_size;
826
827
36
    FUNC_LEAVE_NOAPI(SUCCEED)
828
36
} /* H5O__alloc_find_best_nonnull() */
829
830
/*-------------------------------------------------------------------------
831
 * Function:    H5O__alloc_chunk
832
 *
833
 * Purpose:     Allocates and initializes a new chunk for the object header,
834
 *    including file space.
835
 *
836
 * Return:      Success:        SUCCEED, with chunk number for the
837
 *                              new chunk and a pointer to the location in its
838
 *        image where the first message should be placed.
839
 *
840
 *              Failure:        Negative
841
 *
842
 *-------------------------------------------------------------------------
843
 */
844
herr_t
845
H5O__alloc_chunk(H5F_t *f, H5O_t *oh, size_t size, size_t found_null, const H5O_msg_alloc_info_t *found_msg,
846
                 size_t *new_idx)
847
70
{
848
70
    H5O_mesg_t        *curr_msg;            /* Pointer to current message to operate on */
849
70
    H5O_chunk_proxy_t *chk_proxy;           /* Chunk that message is in */
850
70
    size_t             cont_size;           /*continuation message size     */
851
70
    size_t             idx;                 /* Message number */
852
70
    uint8_t           *p    = NULL;         /* Pointer into new chunk image */
853
70
    H5O_cont_t        *cont = NULL;         /*native continuation message   */
854
70
    unsigned           chunkno;             /* Chunk allocated */
855
70
    haddr_t            new_chunk_addr;      /* Address of new chunk in file */
856
70
    unsigned           u;                   /* Local index variable */
857
70
    herr_t             ret_value = SUCCEED; /* Return value */
858
859
70
    FUNC_ENTER_PACKAGE
860
861
    /* check args */
862
70
    assert(f);
863
70
    assert(oh);
864
70
    assert(found_msg);
865
70
    assert(new_idx);
866
867
    /*
868
     * The total chunk size must include the requested space plus enough
869
     * for the message header.  This must be at least some minimum and
870
     * aligned properly.
871
     */
872
70
    size = MAX(H5O_MIN_SIZE, size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh));
873
70
    assert(size == H5O_ALIGN_OH(oh, size));
874
875
    /*
876
     * The total chunk size must include enough space for the checksum
877
     * on the chunk and the continuation chunk magic #. (which are only present
878
     * in later versions of the object header)
879
     */
880
70
    size += H5O_SIZEOF_CHKHDR_OH(oh);
881
882
    /* Allocate space in file to hold the new chunk */
883
70
    new_chunk_addr = H5MF_alloc(f, H5FD_MEM_OHDR, (hsize_t)size);
884
70
    if (!H5_addr_defined(new_chunk_addr))
885
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTALLOC, FAIL, "unable to allocate space for new chunk");
886
887
    /* Create the new chunk giving it a file address. */
888
70
    if (oh->nchunks >= oh->alloc_nchunks) {
889
23
        size_t       na = MAX(H5O_NCHUNKS, oh->alloc_nchunks * 2); /* Double # of chunks allocated */
890
23
        H5O_chunk_t *x;
891
892
23
        if (NULL == (x = H5FL_SEQ_REALLOC(H5O_chunk_t, oh->chunk, na)))
893
0
            HGOTO_ERROR(H5E_OHDR, H5E_CANTALLOC, FAIL, "can't allocate larger chunk array, na = %zu", na);
894
23
        oh->alloc_nchunks = na;
895
23
        oh->chunk         = x;
896
23
    } /* end if */
897
898
70
    H5_CHECKED_ASSIGN(chunkno, unsigned, oh->nchunks, size_t);
899
70
    oh->nchunks++;
900
70
    oh->chunk[chunkno].addr = new_chunk_addr;
901
70
    oh->chunk[chunkno].size = size;
902
70
    oh->chunk[chunkno].gap  = 0;
903
70
    if (NULL == (oh->chunk[chunkno].image = p = H5FL_BLK_CALLOC(chunk_image, size)))
904
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTALLOC, FAIL, "can't allocate image for chunk, size = %zu", size);
905
70
    oh->chunk[chunkno].chunk_proxy = NULL;
906
907
    /* If this is a later version of the object header format, put the magic
908
     *  # at the beginning of the chunk image.
909
     */
910
70
    if (oh->version > H5O_VERSION_1) {
911
22
        H5MM_memcpy(p, H5O_CHK_MAGIC, (size_t)H5_SIZEOF_MAGIC);
912
22
        p += H5_SIZEOF_MAGIC;
913
22
    } /* end if */
914
915
    /*
916
     * Make sure we have enough space for all possible new messages
917
     * that could be generated below.
918
     */
919
70
    if (oh->nmesgs + 3 > oh->alloc_nmesgs)
920
28
        if (H5O__alloc_msgs(oh, (size_t)3) < 0)
921
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate more space for messages");
922
923
    /* Check if we need to move multiple messages, in order to make room for the new message */
924
70
    cont_size = H5O_ALIGN_OH(oh, (size_t)(H5F_SIZEOF_ADDR(f) + H5F_SIZEOF_SIZE(f)));
925
70
    if (found_null >= oh->nmesgs) {
926
36
        if (found_msg->msgno < 0) {
927
            /* Move all non-null messages in the last chunk to the new chunk.  This
928
             * should be extremely rare so we don't care too much about minimizing
929
             * the space used.
930
             */
931
7
            H5O_mesg_t *null_msg; /* Pointer to new null message */
932
933
            /* Protect last chunk */
934
7
            if (NULL == (chk_proxy = H5O__chunk_protect(f, oh, chunkno - 1)))
935
0
                HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header chunk");
936
937
            /* Copy each message to the new location */
938
248
            for (u = 0, curr_msg = &oh->mesg[0]; u < oh->nmesgs; u++, curr_msg++)
939
248
                if (curr_msg->chunkno == chunkno - 1) {
940
248
                    if (curr_msg->type->id == H5O_NULL_ID) {
941
                        /* Delete the null message */
942
2
                        if (u < oh->nmesgs - 1)
943
2
                            memmove(curr_msg, curr_msg + 1, ((oh->nmesgs - 1) - u) * sizeof(H5O_mesg_t));
944
2
                        oh->nmesgs--;
945
2
                    } /* end if */
946
246
                    else {
947
246
                        assert(curr_msg->type->id != H5O_CONT_ID);
948
949
246
                        if (size < curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh))
950
7
                            HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, FAIL, "invalid size");
951
952
                        /* Copy the raw data */
953
239
                        H5MM_memcpy(p, curr_msg->raw - (size_t)H5O_SIZEOF_MSGHDR_OH(oh),
954
239
                                    curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh));
955
956
                        /* Update the message info */
957
239
                        curr_msg->chunkno = chunkno;
958
239
                        curr_msg->raw     = p + H5O_SIZEOF_MSGHDR_OH(oh);
959
960
                        /* Account for copied message in new chunk */
961
239
                        p += (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + curr_msg->raw_size;
962
239
                        size -= (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + curr_msg->raw_size;
963
239
                    } /* end else */
964
248
                }     /* end if */
965
966
            /* Create a null message spanning the entire last chunk */
967
0
            found_null       = oh->nmesgs++;
968
0
            null_msg         = &(oh->mesg[found_null]);
969
0
            null_msg->type   = H5O_MSG_NULL;
970
0
            null_msg->dirty  = true;
971
0
            null_msg->native = NULL;
972
0
            null_msg->raw    = oh->chunk[chunkno - 1].image +
973
0
                            ((chunkno == 1) ? H5O_SIZEOF_HDR(oh) : H5O_SIZEOF_CHKHDR_OH(oh)) -
974
0
                            H5O_SIZEOF_CHKSUM_OH(oh) + H5O_SIZEOF_MSGHDR_OH(oh);
975
0
            null_msg->raw_size =
976
0
                oh->chunk[chunkno - 1].size -
977
0
                ((chunkno == 1) ? (size_t)H5O_SIZEOF_HDR(oh) : (size_t)H5O_SIZEOF_CHKHDR_OH(oh)) -
978
0
                (size_t)H5O_SIZEOF_MSGHDR_OH(oh);
979
0
            null_msg->chunkno = chunkno - 1;
980
981
0
            assert(null_msg->raw_size >= cont_size);
982
983
            /* Remove any gap in the chunk */
984
0
            oh->chunk[chunkno - 1].gap = 0;
985
986
            /* Release chunk, marking it dirty */
987
0
            if (H5O__chunk_unprotect(f, chk_proxy, true) < 0)
988
0
                HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect object header chunk");
989
0
        } /* end if */
990
29
        else {
991
            /* Move message (that will be replaced with continuation message)
992
             *  to new chunk, if necessary.
993
             */
994
29
            H5O_mesg_t *null_msg; /* Pointer to new null message */
995
996
            /* Protect chunk */
997
29
            if (NULL == (chk_proxy = H5O__chunk_protect(f, oh, oh->mesg[found_msg->msgno].chunkno)))
998
0
                HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header chunk");
999
1000
            /* Create null message for space that message to copy currently occupies */
1001
29
            found_null         = oh->nmesgs++;
1002
29
            null_msg           = &(oh->mesg[found_null]);
1003
29
            null_msg->type     = H5O_MSG_NULL;
1004
29
            null_msg->native   = NULL;
1005
29
            null_msg->raw      = oh->mesg[found_msg->msgno].raw;
1006
29
            null_msg->raw_size = oh->mesg[found_msg->msgno].raw_size;
1007
29
            null_msg->chunkno  = oh->mesg[found_msg->msgno].chunkno;
1008
1009
            /* Copy the message to move (& its prefix) to its new location */
1010
29
            H5MM_memcpy(p, oh->mesg[found_msg->msgno].raw - H5O_SIZEOF_MSGHDR_OH(oh),
1011
29
                        oh->mesg[found_msg->msgno].raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh));
1012
1013
            /* Switch moved message to point to new location */
1014
29
            oh->mesg[found_msg->msgno].raw     = p + H5O_SIZEOF_MSGHDR_OH(oh);
1015
29
            oh->mesg[found_msg->msgno].chunkno = chunkno;
1016
1017
            /* Account for copied message in new chunk */
1018
29
            p += (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + oh->mesg[found_msg->msgno].raw_size;
1019
29
            size -= (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + oh->mesg[found_msg->msgno].raw_size;
1020
1021
            /* Add any available space after the message to move to the new null message */
1022
29
            if (found_msg->gap_size > 0) {
1023
                /* Absorb a gap after the moved message */
1024
0
                assert(oh->chunk[null_msg->chunkno].gap == found_msg->gap_size);
1025
0
                null_msg->raw_size += found_msg->gap_size;
1026
0
                oh->chunk[null_msg->chunkno].gap = 0;
1027
0
            } /* end if */
1028
29
            else if (found_msg->null_size > 0) {
1029
25
                H5O_mesg_t *old_null_msg =
1030
25
                    &oh->mesg[found_msg->null_msgno]; /* Pointer to NULL message to eliminate */
1031
1032
                /* Absorb a null message after the moved message */
1033
25
                assert((null_msg->raw + null_msg->raw_size) ==
1034
25
                       (old_null_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh)));
1035
25
                null_msg->raw_size += found_msg->null_size;
1036
1037
                /* Release any information/memory for message */
1038
25
                H5O__msg_free_mesg(old_null_msg);
1039
1040
                /* Remove null message from list of messages */
1041
25
                if (found_msg->null_msgno < (oh->nmesgs - 1))
1042
25
                    memmove(old_null_msg, old_null_msg + 1,
1043
25
                            ((oh->nmesgs - 1) - found_msg->null_msgno) * sizeof(H5O_mesg_t));
1044
1045
                /* Decrement # of messages */
1046
                /* (Don't bother reducing size of message array for now -QAK) */
1047
25
                oh->nmesgs--;
1048
1049
                /* Adjust message index for new NULL message */
1050
25
                found_null--;
1051
25
            } /* end if */
1052
1053
            /* Mark the new null message as dirty */
1054
29
            null_msg->dirty = true;
1055
1056
            /* Release chunk, marking it dirty */
1057
29
            if (H5O__chunk_unprotect(f, chk_proxy, true) < 0)
1058
0
                HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect object header chunk");
1059
29
        } /* end if */
1060
36
    }     /* end if */
1061
1062
    /* Create null message for [rest of] space in new chunk */
1063
    /* (account for chunk's magic # & checksum) */
1064
63
    idx                    = oh->nmesgs++;
1065
63
    oh->mesg[idx].type     = H5O_MSG_NULL;
1066
63
    oh->mesg[idx].dirty    = true;
1067
63
    oh->mesg[idx].native   = NULL;
1068
63
    oh->mesg[idx].raw      = p + H5O_SIZEOF_MSGHDR_OH(oh);
1069
63
    oh->mesg[idx].raw_size = size - (size_t)(H5O_SIZEOF_CHKHDR_OH(oh) + H5O_SIZEOF_MSGHDR_OH(oh));
1070
63
    oh->mesg[idx].chunkno  = chunkno;
1071
1072
    /* Insert the new chunk into the cache */
1073
63
    if (H5O__chunk_add(f, oh, chunkno, oh->mesg[found_null].chunkno) < 0)
1074
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't add new chunk to cache");
1075
1076
    /* Initialize the continuation information */
1077
63
    if (NULL == (cont = H5FL_MALLOC(H5O_cont_t)))
1078
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");
1079
63
    cont->addr    = oh->chunk[chunkno].addr;
1080
63
    cont->size    = oh->chunk[chunkno].size;
1081
63
    cont->chunkno = chunkno;
1082
1083
    /* Split the null message and point at continuation message */
1084
63
    if (H5O__alloc_null(f, oh, found_null, H5O_MSG_CONT, cont, cont_size) < 0)
1085
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't split null message");
1086
1087
    /* Set new message index value */
1088
63
    *new_idx = idx;
1089
1090
70
done:
1091
70
    FUNC_LEAVE_NOAPI(ret_value)
1092
70
} /* H5O__alloc_chunk() */
1093
1094
/*-------------------------------------------------------------------------
1095
 * Function:    H5O__alloc_new_chunk
1096
 *
1097
 * Purpose:     Allocates a new chunk for the object header, including
1098
 *    file space.
1099
 *
1100
 *              One of the other chunks will get an object continuation
1101
 *    message.  If there isn't room in any other chunk for the
1102
 *    object continuation message, then some message from
1103
 *    another chunk is moved into this chunk to make room.
1104
 *
1105
 *              SIZE need not be aligned.
1106
 *
1107
 * Note:  The algorithm for finding a message to replace with a
1108
 *    continuation message is still fairly limited.  It's possible
1109
 *    that two (or more) messages smaller than a continuation message
1110
 *    might occupy a chunk and need to be moved in order to make
1111
 *    room for the continuation message.
1112
 *
1113
 *    Also, we aren't checking for NULL messages in front of another
1114
 *    message right now...
1115
 *
1116
 * Return:      Success:        Index number of the null message for the
1117
 *                              new chunk.  The null message will be at
1118
 *                              least SIZE bytes not counting the message
1119
 *                              ID or size fields.
1120
 *
1121
 *              Failure:        Negative
1122
 *
1123
 *-------------------------------------------------------------------------
1124
 */
1125
static herr_t
1126
H5O__alloc_new_chunk(H5F_t *f, H5O_t *oh, size_t size, size_t *new_idx)
1127
70
{
1128
70
    size_t               cont_size;           /*continuation message size     */
1129
70
    size_t               idx;                 /* Message number */
1130
70
    H5O_msg_alloc_info_t found_msg;           /* Best fit non-null message */
1131
70
    herr_t               ret_value = SUCCEED; /* Return value */
1132
1133
70
    FUNC_ENTER_PACKAGE
1134
1135
    /* check args */
1136
70
    assert(oh);
1137
70
    assert(size > 0);
1138
70
    size = H5O_ALIGN_OH(oh, size);
1139
1140
    /* Find the smallest null message that could hold a continuation message */
1141
70
    idx       = oh->nmesgs;
1142
70
    cont_size = H5O_ALIGN_OH(oh, (size_t)(H5F_SIZEOF_ADDR(f) + H5F_SIZEOF_SIZE(f)));
1143
70
    if (H5O__alloc_find_best_null(oh, cont_size, &idx) < 0)
1144
0
        HGOTO_ERROR(H5E_OHDR, H5E_NOTFOUND, FAIL, "error while locating best null header message");
1145
1146
    /* If we couldn't find a null message, locate the best message to move to new chunk */
1147
70
    if (idx >= oh->nmesgs) {
1148
36
        found_msg.msgno = -1;
1149
36
        if (H5O__alloc_find_best_nonnull(f, oh, &size, &found_msg) < 0)
1150
0
            HGOTO_ERROR(H5E_OHDR, H5E_NOTFOUND, FAIL, "error while locating best non-null header message");
1151
36
    } /* end if */
1152
1153
    /* Allocate and initialize new chunk in the file */
1154
70
    if (H5O__alloc_chunk(f, oh, size, idx, &found_msg, new_idx) < 0)
1155
7
        HGOTO_ERROR(H5E_OHDR, H5E_CANTALLOC, FAIL, "can't allocate new object header chunk");
1156
1157
70
done:
1158
70
    FUNC_LEAVE_NOAPI(ret_value)
1159
70
} /* H5O__alloc_new_chunk() */
1160
1161
/*-------------------------------------------------------------------------
1162
 * Function:    H5O__alloc_find_best_null
1163
 *
1164
 * Purpose:     Find the best fit null message for a given size of message
1165
 *              to allocate.
1166
 *
1167
 * Return:  Non-negative on success/Negative on failure
1168
 *
1169
 *-------------------------------------------------------------------------
1170
 */
1171
static herr_t
1172
H5O__alloc_find_best_null(const H5O_t *oh, size_t size, size_t *mesg_idx)
1173
330
{
1174
330
    size_t  idx;        /* Index of message which fits allocation */
1175
330
    ssize_t found_null; /* Best fit null message         */
1176
1177
330
    FUNC_ENTER_PACKAGE_NOERR
1178
1179
    /* check args */
1180
330
    assert(oh);
1181
330
    assert(size > 0);
1182
330
    assert(mesg_idx);
1183
1184
    /* Find the smallest null message that could hold the new object header message */
1185
330
    found_null = -1;
1186
7.96k
    for (idx = 0; idx < oh->nmesgs; idx++) {
1187
7.63k
        if (H5O_NULL_ID == oh->mesg[idx].type->id) {
1188
            /* If we found an exact fit, use it */
1189
360
            if (oh->mesg[idx].raw_size == size) {
1190
                /* Keep first exact fit */
1191
20
                if (found_null < 0)
1192
15
                    found_null = (ssize_t)idx;
1193
5
                else
1194
                    /* If we've got more than one exact fit, choose the one in the earliest chunk */
1195
5
                    if (oh->mesg[idx].chunkno < oh->mesg[found_null].chunkno) {
1196
4
                        found_null = (ssize_t)idx;
1197
1198
                        /* If we found an exact fit in object header chunk #0, we can get out */
1199
                        /* (Could extend this to look for earliest message in
1200
                         *      chunk #0 - QAK, 2016/10/21)
1201
                         */
1202
4
                        if (0 == oh->mesg[idx].chunkno)
1203
3
                            break;
1204
4
                    } /* end if */
1205
20
            }         /* end if */
1206
            /* Look for null message that's larger than needed */
1207
340
            else if (oh->mesg[idx].raw_size > size) {
1208
                /* Keep first one found */
1209
204
                if (found_null < 0)
1210
189
                    found_null = (ssize_t)idx;
1211
                /* Check for better fit */
1212
15
                else if (oh->mesg[idx].raw_size < oh->mesg[found_null].raw_size) {
1213
9
                    found_null = (ssize_t)idx;
1214
9
                }
1215
6
                else {
1216
                    /* If they are the same size, choose the one in the earliest chunk */
1217
6
                    if (oh->mesg[idx].raw_size == oh->mesg[found_null].raw_size) {
1218
3
                        if (oh->mesg[idx].chunkno < oh->mesg[found_null].chunkno)
1219
0
                            found_null = (ssize_t)idx;
1220
3
                    } /* end if */
1221
6
                }     /* end else */
1222
204
            }         /* end else-if */
1223
            /* else: Ignore too-small null messages */
1224
360
        } /* end if */
1225
7.63k
    }     /* end for */
1226
330
    if (found_null >= 0)
1227
204
        *mesg_idx = (size_t)found_null;
1228
1229
330
    FUNC_LEAVE_NOAPI(SUCCEED)
1230
330
} /* H5O__alloc_find_best_null() */
1231
1232
/*-------------------------------------------------------------------------
1233
 * Function:    H5O__alloc
1234
 *
1235
 * Purpose:     Allocate enough space in the object header for this message.
1236
 *
1237
 * Return:  Non-negative on success/Negative on failure
1238
 *
1239
 *-------------------------------------------------------------------------
1240
 */
1241
herr_t
1242
H5O__alloc(H5F_t *f, H5O_t *oh, const H5O_msg_class_t *type, const void *mesg, size_t *mesg_idx)
1243
260
{
1244
260
    size_t raw_size;            /* Raw size of message */
1245
260
    size_t aligned_size;        /* Size of message including alignment */
1246
260
    size_t idx;                 /* Index of message which fits allocation */
1247
260
    herr_t ret_value = SUCCEED; /* Return value */
1248
1249
260
    FUNC_ENTER_PACKAGE
1250
1251
    /* check args */
1252
260
    assert(oh);
1253
260
    assert(type);
1254
260
    assert(mesg);
1255
260
    assert(mesg_idx);
1256
1257
    /* Compute the size needed to store the message in the object header */
1258
260
    raw_size = (type->raw_size)(f, false, mesg);
1259
260
    if (0 == raw_size)
1260
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "can't compute object header message size");
1261
260
    if (raw_size >= H5O_MESG_MAX_SIZE)
1262
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "object header message is too large");
1263
260
    aligned_size = H5O_ALIGN_OH(oh, raw_size);
1264
1265
    /* Find the smallest null message that could hold the new object header message */
1266
260
    idx = oh->nmesgs;
1267
260
    if (H5O__alloc_find_best_null(oh, aligned_size, &idx) < 0)
1268
0
        HGOTO_ERROR(H5E_OHDR, H5E_NOTFOUND, FAIL, "error while locating best null header message");
1269
1270
    /* if we didn't find one, then allocate more header space */
1271
260
    if (idx >= oh->nmesgs) {
1272
90
        unsigned chunkno;
1273
1274
        /* check to see if we can extend one of the chunks.  If we can,
1275
         * do so.  Otherwise, we will have to allocate a new chunk.
1276
         *
1277
         * Note that in this new version of this function, all chunks
1278
         * must have file space allocated to them.
1279
         */
1280
187
        for (chunkno = 0; chunkno < oh->nchunks; chunkno++) {
1281
117
            htri_t tri_result; /* Status from attempting to extend chunk */
1282
1283
117
            if ((tri_result = H5O__alloc_extend_chunk(f, oh, chunkno, raw_size, &idx)) < 0)
1284
0
                HGOTO_ERROR(H5E_OHDR, H5E_CANTEXTEND, FAIL, "can't extend existing chunk");
1285
117
            if (tri_result == true)
1286
20
                break;
1287
117
        } /* end for */
1288
1289
        /* If we were not able to extend a chunk, create a new one */
1290
90
        if (idx >= oh->nmesgs)
1291
70
            if (H5O__alloc_new_chunk(f, oh, raw_size, &idx) < 0)
1292
7
                HGOTO_ERROR(H5E_OHDR, H5E_NOSPACE, FAIL, "unable to create a new object header data chunk");
1293
90
    } /* end if */
1294
253
    assert(idx < oh->nmesgs);
1295
1296
    /* Split the null message and point at continuation message */
1297
253
    if (H5O__alloc_null(f, oh, idx, type, NULL, aligned_size) < 0)
1298
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't split null message");
1299
1300
    /* Mark object header as dirty in cache */
1301
253
    if (H5AC_mark_entry_dirty(oh) < 0)
1302
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTMARKDIRTY, FAIL, "unable to mark object header as dirty");
1303
1304
    /* Set message index value */
1305
253
    *mesg_idx = idx;
1306
1307
260
done:
1308
260
    FUNC_LEAVE_NOAPI(ret_value)
1309
260
} /* H5O__alloc() */
1310
1311
/*-------------------------------------------------------------------------
1312
 *
1313
 * Function:    H5O__release_mesg
1314
 *
1315
 * Purpose:     Convert a message into a null message
1316
 *
1317
 * Return:  Non-negative on success/Negative on failure
1318
 *
1319
 *-------------------------------------------------------------------------
1320
 */
1321
herr_t
1322
H5O__release_mesg(H5F_t *f, H5O_t *oh, H5O_mesg_t *mesg, bool adj_link)
1323
997
{
1324
997
    H5O_chunk_proxy_t *chk_proxy   = NULL;    /* Chunk that message is in */
1325
997
    bool               chk_dirtied = false;   /* Flag for unprotecting chunk */
1326
997
    herr_t             ret_value   = SUCCEED; /* Return value */
1327
1328
997
    FUNC_ENTER_PACKAGE
1329
1330
    /* check args */
1331
997
    assert(f);
1332
997
    assert(oh);
1333
997
    assert(mesg);
1334
1335
    /* Check if we should operate on the message */
1336
997
    if (adj_link)
1337
        /* Free any space referred to in the file from this message */
1338
760
        if (H5O__delete_mesg(f, oh, mesg) < 0)
1339
25
            HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL,
1340
997
                        "unable to delete file space for object header message");
1341
1342
    /* Protect chunk */
1343
972
    if (NULL == (chk_proxy = H5O__chunk_protect(f, oh, mesg->chunkno)))
1344
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to protect object header chunk");
1345
1346
    /* Free any native information */
1347
972
    H5O__msg_free_mesg(mesg);
1348
1349
    /* Change message type to nil and zero it */
1350
972
    mesg->type = H5O_MSG_NULL;
1351
972
    assert(mesg->raw + mesg->raw_size <= (oh->chunk[mesg->chunkno].image + oh->chunk[mesg->chunkno].size) -
1352
972
                                             (H5O_SIZEOF_CHKSUM_OH(oh) + oh->chunk[mesg->chunkno].gap));
1353
972
    memset(mesg->raw, 0, mesg->raw_size);
1354
1355
    /* Clear message flags */
1356
972
    mesg->flags = 0;
1357
1358
    /* Mark the message as modified */
1359
972
    mesg->dirty = true;
1360
972
    chk_dirtied = true;
1361
1362
    /* Check if chunk has a gap currently */
1363
972
    if (oh->chunk[mesg->chunkno].gap) {
1364
        /* Eliminate the gap in the chunk */
1365
14
        if (H5O__eliminate_gap(oh, &chk_dirtied, mesg,
1366
14
                               ((oh->chunk[mesg->chunkno].image + oh->chunk[mesg->chunkno].size) -
1367
14
                                (H5O_SIZEOF_CHKSUM_OH(oh) + oh->chunk[mesg->chunkno].gap)),
1368
14
                               oh->chunk[mesg->chunkno].gap) < 0)
1369
0
            HGOTO_ERROR(H5E_OHDR, H5E_CANTREMOVE, FAIL, "can't eliminate gap in chunk");
1370
14
    } /* end if */
1371
1372
997
done:
1373
    /* Release chunk, if not already done */
1374
997
    if (chk_proxy && H5O__chunk_unprotect(f, chk_proxy, chk_dirtied) < 0)
1375
0
        HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect object header chunk");
1376
1377
997
    FUNC_LEAVE_NOAPI(ret_value)
1378
997
} /* H5O__release_mesg() */
1379
1380
/*-------------------------------------------------------------------------
1381
 * Function:    H5O__move_cont
1382
 *
1383
 * Purpose:     Check and move message(s) forward into a continuation message
1384
 *
1385
 * Return:      Success:        non-negative (true/false)
1386
 *              Failure:        negative
1387
 *
1388
 *-------------------------------------------------------------------------
1389
 */
1390
static htri_t
1391
H5O__move_cont(H5F_t *f, H5O_t *oh, unsigned cont_u)
1392
45.6k
{
1393
45.6k
    H5O_chunk_proxy_t *chk_proxy = NULL;    /* Chunk that continuation message is in */
1394
45.6k
    H5O_mesg_t        *cont_msg;            /* Pointer to the continuation message */
1395
45.6k
    unsigned           deleted_chunkno;     /* Chunk # to delete */
1396
45.6k
    bool               chk_dirtied = false; /* Flags for unprotecting chunk */
1397
45.6k
    htri_t             ret_value   = true;  /* Return value */
1398
1399
45.6k
    FUNC_ENTER_PACKAGE
1400
1401
    /* Check arguments. */
1402
45.6k
    assert(f);
1403
45.6k
    assert(oh);
1404
1405
    /* Get initial information */
1406
45.6k
    cont_msg = &oh->mesg[cont_u];
1407
45.6k
    H5O_LOAD_NATIVE(f, 0, oh, cont_msg, FAIL)
1408
45.6k
    deleted_chunkno = ((H5O_cont_t *)(cont_msg->native))->chunkno;
1409
1410
    /* Check if continuation message is pointing to the last chunk */
1411
45.6k
    if (deleted_chunkno == (oh->nchunks - 1)) {
1412
11.8k
        size_t      nonnull_size; /* Total size of nonnull messages in the chunk pointed to by cont message */
1413
11.8k
        H5O_mesg_t *curr_msg;     /* Pointer to the current message to operate on */
1414
11.8k
        size_t      gap_size;     /* Size of gap produced */
1415
11.8k
        size_t      v;            /* Local index variable */
1416
1417
        /* Spin through messages */
1418
11.8k
        nonnull_size = 0;
1419
1.41M
        for (v = 0, curr_msg = &oh->mesg[0]; v < oh->nmesgs; v++, curr_msg++) {
1420
1.40M
            if (curr_msg->chunkno == deleted_chunkno) {
1421
                /* Find size of all non-null messages in the chunk pointed to by the continuation message */
1422
534k
                if (curr_msg->type->id != H5O_NULL_ID) {
1423
328k
                    assert(curr_msg->type->id != H5O_CONT_ID);
1424
328k
                    nonnull_size += curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh);
1425
328k
                } /* end if */
1426
534k
            }     /* end if */
1427
1.40M
        }         /* end for */
1428
1429
        /* Size of gap in chunk w/continuation message */
1430
11.8k
        gap_size = oh->chunk[cont_msg->chunkno].gap;
1431
1432
        /* Check if messages can fit into the continuation message + gap size */
1433
        /* (Could count any null messages in the chunk w/the continuation
1434
         *      message also, but that is pretty complex. -QAK)
1435
         */
1436
11.8k
        if (nonnull_size &&
1437
11.8k
            nonnull_size <= (gap_size + cont_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh))) {
1438
237
            uint8_t *move_start, *move_end; /* Pointers to area of messages to move */
1439
237
            unsigned cont_chunkno;          /* Chunk number for continuation message */
1440
1441
            /* Get continuation info */
1442
237
            move_start   = cont_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh);
1443
237
            move_end     = cont_msg->raw + cont_msg->raw_size;
1444
237
            cont_chunkno = cont_msg->chunkno;
1445
1446
            /* Convert continuation message into a null message.  Do not delete
1447
             * the target chunk yet, so we can still copy messages from it. */
1448
237
            if (H5O__release_mesg(f, oh, cont_msg, false) < 0)
1449
0
                HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, "unable to convert into null message");
1450
1451
            /* Protect chunk */
1452
237
            if (NULL == (chk_proxy = H5O__chunk_protect(f, oh, cont_chunkno)))
1453
0
                HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to protect object header chunk");
1454
1455
            /* Move message(s) forward into continuation message */
1456
10.4k
            for (v = 0, curr_msg = &oh->mesg[0]; v < oh->nmesgs; v++, curr_msg++)
1457
                /* Look for messages in chunk to delete */
1458
10.2k
                if (curr_msg->chunkno == deleted_chunkno) {
1459
                    /* Move messages out of chunk to delete */
1460
1.92k
                    if (curr_msg->type->id != H5O_NULL_ID) {
1461
526
                        size_t move_size; /* Size of the message to be moved */
1462
1463
                        /* Compute size of message to move */
1464
526
                        move_size = curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh);
1465
1466
                        /* Move message out of deleted chunk */
1467
526
                        H5MM_memcpy(move_start, curr_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh), move_size);
1468
526
                        curr_msg->raw     = move_start + H5O_SIZEOF_MSGHDR_OH(oh);
1469
526
                        curr_msg->chunkno = cont_chunkno;
1470
526
                        chk_dirtied       = true;
1471
1472
                        /* Adjust location to move messages to */
1473
526
                        move_start += move_size;
1474
526
                    } /* end else */
1475
1.92k
                }     /* end if */
1476
1477
            /* Delete the target chunk */
1478
237
            if (H5O__chunk_delete(f, oh, deleted_chunkno) < 0)
1479
11
                HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, "unable to remove chunk from cache");
1480
1481
226
            assert(move_start <= (move_end + gap_size));
1482
1483
            /* Check if there is space remaining in the continuation message */
1484
            /* (The remaining space can be gap or a null message) */
1485
226
            gap_size += (size_t)(move_end - move_start);
1486
226
            if (gap_size >= (size_t)H5O_SIZEOF_MSGHDR_OH(oh)) {
1487
                /* Adjust size of null (was continuation) message */
1488
148
                cont_msg->raw_size = gap_size - (size_t)H5O_SIZEOF_MSGHDR_OH(oh);
1489
148
                cont_msg->raw      = move_start + H5O_SIZEOF_MSGHDR_OH(oh);
1490
148
                cont_msg->dirty    = true;
1491
148
                chk_dirtied        = true;
1492
148
            } /* end if */
1493
78
            else {
1494
                /* Check if there is space that should be a gap */
1495
78
                if (gap_size > 0) {
1496
                    /* Convert remnant into gap in chunk */
1497
0
                    if (H5O__add_gap(f, oh, cont_chunkno, &chk_dirtied, cont_u, move_start, gap_size) < 0)
1498
0
                        HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't insert gap in chunk");
1499
0
                } /* end if */
1500
1501
                /* Release any information/memory for continuation message */
1502
78
                H5O__msg_free_mesg(cont_msg);
1503
78
                if (cont_u < (oh->nmesgs - 1))
1504
78
                    memmove(&oh->mesg[cont_u], &oh->mesg[cont_u + 1],
1505
78
                            ((oh->nmesgs - 1) - cont_u) * sizeof(H5O_mesg_t));
1506
78
                oh->nmesgs--;
1507
78
            } /* end else */
1508
1509
            /* Move message(s) forward into continuation message */
1510
            /*      Note: unsigned v wrapping around at the end */
1511
10.1k
            for (v = oh->nmesgs - 1, curr_msg = &oh->mesg[v]; v < oh->nmesgs; v--, curr_msg--)
1512
                /* Look for messages in chunk to delete */
1513
9.97k
                if (curr_msg->chunkno == deleted_chunkno) {
1514
                    /* Remove all null messages in deleted chunk from list of messages */
1515
1.35k
                    if (curr_msg->type->id == H5O_NULL_ID) {
1516
                        /* Release any information/memory for message */
1517
1.35k
                        H5O__msg_free_mesg(curr_msg);
1518
1.35k
                        chk_dirtied = true;
1519
1520
                        /* Remove from message list */
1521
1.35k
                        if (v < (oh->nmesgs - 1))
1522
873
                            memmove(&oh->mesg[v], &oh->mesg[v + 1],
1523
873
                                    ((oh->nmesgs - 1) - v) * sizeof(H5O_mesg_t));
1524
1.35k
                        oh->nmesgs--;
1525
1.35k
                    } /* end if */
1526
1.35k
                }     /* end if */
1527
1528
            /* Remove chunk from list of chunks */
1529
226
            oh->chunk[deleted_chunkno].image = H5FL_BLK_FREE(chunk_image, oh->chunk[deleted_chunkno].image);
1530
226
            oh->nchunks--;
1531
226
        } /* end if */
1532
11.6k
        else
1533
11.6k
            ret_value = false;
1534
11.8k
    } /* end if */
1535
33.8k
    else
1536
33.8k
        ret_value = false;
1537
1538
45.6k
done:
1539
    /* Release chunk, if not already done */
1540
45.6k
    if (chk_proxy && H5O__chunk_unprotect(f, chk_proxy, chk_dirtied) < 0)
1541
0
        HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect object header chunk");
1542
1543
45.6k
    FUNC_LEAVE_NOAPI(ret_value)
1544
45.6k
} /* H5O__move_cont() */
1545
1546
/*-------------------------------------------------------------------------
1547
 *
1548
 * Function:    H5O__move_msgs_forward
1549
 *
1550
 * Purpose:     Move messages toward first chunk
1551
 *
1552
 * Return:  Non-negative on success/Negative on failure
1553
 *
1554
 *-------------------------------------------------------------------------
1555
 */
1556
static htri_t
1557
H5O__move_msgs_forward(H5F_t *f, H5O_t *oh)
1558
924
{
1559
924
    H5O_chunk_proxy_t *null_chk_proxy      = NULL;  /* Chunk that null message is in */
1560
924
    H5O_chunk_proxy_t *curr_chk_proxy      = NULL;  /* Chunk that message is in */
1561
924
    H5O_chunk_proxy_t *cont_targ_chk_proxy = NULL;  /* Chunk that continuation message points to */
1562
924
    bool               null_chk_dirtied    = false; /* Flags for unprotecting null chunk */
1563
924
    bool               curr_chk_dirtied    = false; /* Flags for unprotecting curr chunk */
1564
924
    bool               packed_msg;                  /* Flag to indicate that messages were packed */
1565
924
    bool               did_packing = false;         /* Whether any messages were packed */
1566
924
    htri_t             ret_value   = FAIL;          /* Return value */
1567
1568
924
    FUNC_ENTER_PACKAGE
1569
1570
    /* check args */
1571
924
    assert(oh);
1572
1573
    /* Loop until no messages packed */
1574
    /* (Double loop is not very efficient, but it would be some extra work to
1575
     *      add a list of messages to each chunk -QAK)
1576
     */
1577
32.2k
    do {
1578
32.2k
        H5O_mesg_t *curr_msg; /* Pointer to current message to operate on */
1579
32.2k
        unsigned    u;        /* Local index variable */
1580
1581
        /* Reset packed messages flag */
1582
32.2k
        packed_msg = false;
1583
1584
        /* Scan through messages for messages that can be moved earlier in chunks */
1585
1.12M
        for (u = 0, curr_msg = &oh->mesg[0]; u < oh->nmesgs; u++, curr_msg++) {
1586
1.12M
            if (H5O_NULL_ID == curr_msg->type->id) {
1587
239k
                H5O_chunk_t *chunk; /* Pointer to chunk that null message is in */
1588
1589
                /* Check if null message is not last in chunk */
1590
239k
                chunk = &(oh->chunk[curr_msg->chunkno]);
1591
239k
                if ((curr_msg->raw + curr_msg->raw_size) !=
1592
239k
                    ((chunk->image + chunk->size) - (H5O_SIZEOF_CHKSUM_OH(oh) + chunk->gap))) {
1593
224k
                    H5O_mesg_t *nonnull_msg; /* Pointer to current message to operate on */
1594
224k
                    unsigned    v;           /* Local index variable */
1595
1596
                    /* Loop over messages again, looking for the message in the chunk after the null message
1597
                     */
1598
11.0M
                    for (v = 0, nonnull_msg = &oh->mesg[0]; v < oh->nmesgs; v++, nonnull_msg++) {
1599
                        /* Locate message that is immediately after the null message */
1600
11.0M
                        if ((curr_msg->chunkno == nonnull_msg->chunkno) &&
1601
11.0M
                            ((curr_msg->raw + curr_msg->raw_size) ==
1602
3.68M
                             (nonnull_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh)))) {
1603
                            /* Don't swap messages if the second message is also a null message */
1604
                            /* (We'll merge them together later, in another routine) */
1605
224k
                            if (H5O_NULL_ID != nonnull_msg->type->id) {
1606
                                /* Protect chunk */
1607
33.7k
                                if (NULL == (null_chk_proxy = H5O__chunk_protect(f, oh, curr_msg->chunkno)))
1608
0
                                    HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL,
1609
33.7k
                                                "unable to load object header chunk");
1610
1611
                                /* Copy raw data for non-null message to new location */
1612
33.7k
                                memmove(curr_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh),
1613
33.7k
                                        nonnull_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh),
1614
33.7k
                                        nonnull_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh));
1615
1616
                                /* Adjust non-null message's offset in chunk */
1617
33.7k
                                nonnull_msg->raw = curr_msg->raw;
1618
1619
                                /* Adjust null message's offset in chunk */
1620
33.7k
                                curr_msg->raw =
1621
33.7k
                                    nonnull_msg->raw + nonnull_msg->raw_size + H5O_SIZEOF_MSGHDR_OH(oh);
1622
1623
                                /* Mark null message dirty */
1624
                                /* (since we need to re-encode its message header) */
1625
33.7k
                                curr_msg->dirty = true;
1626
1627
                                /* Release chunk, marking it dirty */
1628
33.7k
                                if (H5O__chunk_unprotect(f, null_chk_proxy, true) < 0)
1629
0
                                    HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL,
1630
33.7k
                                                "unable to unprotect object header chunk");
1631
33.7k
                                null_chk_proxy = NULL;
1632
1633
                                /* Set the flag to indicate that the null message
1634
                                 * was packed - if its not at the end its chunk,
1635
                                 * we'll move it again on the next pass.
1636
                                 */
1637
33.7k
                                packed_msg = true;
1638
33.7k
                            } /* end if */
1639
1640
                            /* Break out of loop */
1641
224k
                            break;
1642
224k
                        } /* end if */
1643
11.0M
                    }     /* end for */
1644
                    /* Should have been message after null message */
1645
224k
                    assert(v < oh->nmesgs);
1646
224k
                } /* end if */
1647
239k
            }     /* end if */
1648
883k
            else {
1649
883k
                H5O_mesg_t *null_msg; /* Pointer to current message to operate on */
1650
883k
                size_t      v;        /* Local index variable */
1651
1652
                /* Check if messages in chunk pointed to can replace continuation message */
1653
883k
                if (H5O_CONT_ID == curr_msg->type->id) {
1654
45.6k
                    htri_t status; /* Status from moving messages */
1655
1656
45.6k
                    if ((status = H5O__move_cont(f, oh, u)) < 0)
1657
11
                        HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL,
1658
45.6k
                                    "Error in moving messages into cont message");
1659
45.6k
                    else if (status > 0) { /* Message(s) got moved into "continuation" message */
1660
226
                        packed_msg = true;
1661
226
                        break;
1662
226
                    } /* end else-if */
1663
45.6k
                }     /* end if */
1664
1665
                /* Loop over messages again, looking for large enough null message in earlier chunk */
1666
123M
                for (v = 0, null_msg = &oh->mesg[0]; v < oh->nmesgs; v++, null_msg++) {
1667
122M
                    if (H5O_NULL_ID == null_msg->type->id && curr_msg->chunkno > null_msg->chunkno &&
1668
122M
                        curr_msg->raw_size <= null_msg->raw_size) {
1669
2.33k
                        unsigned old_chunkno; /* Old message information */
1670
2.33k
                        uint8_t *old_raw;
1671
1672
                        /* Keep old information about non-null message */
1673
2.33k
                        old_chunkno = curr_msg->chunkno;
1674
2.33k
                        old_raw     = curr_msg->raw;
1675
1676
                        /* Protect chunks */
1677
2.33k
                        if (NULL == (null_chk_proxy = H5O__chunk_protect(f, oh, null_msg->chunkno)))
1678
0
                            HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL,
1679
2.33k
                                        "unable to load object header chunk");
1680
2.33k
                        if (NULL == (curr_chk_proxy = H5O__chunk_protect(f, oh, curr_msg->chunkno)))
1681
0
                            HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL,
1682
2.33k
                                        "unable to load object header chunk");
1683
1684
                        /* If the message being moved is a continuation
1685
                         * message and we are doing SWMR writes, we must
1686
                         * update the flush dependencies */
1687
2.33k
                        if (oh->swmr_write && (H5O_CONT_ID == curr_msg->type->id)) {
1688
0
                            void *null_chk_mdc_obj; /* The metadata cache object for the null_msg chunk */
1689
1690
                            /* Point to the metadata cache object for the
1691
                             * null message chunk, oh if in chunk 0 or the
1692
                             * proxy otherwise */
1693
0
                            null_chk_mdc_obj = (null_msg->chunkno == 0 ? (void *)oh : (void *)null_chk_proxy);
1694
1695
                            /* The other chunks involved should never be
1696
                             * chunk 0 */
1697
0
                            assert(curr_msg->chunkno > 0);
1698
0
                            assert(((H5O_cont_t *)(curr_msg->native))->chunkno > 0);
1699
1700
                            /* Protect continuation message target chunk */
1701
0
                            if (NULL == (cont_targ_chk_proxy = H5O__chunk_protect(
1702
0
                                             f, oh, ((H5O_cont_t *)(curr_msg->native))->chunkno)))
1703
0
                                HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL,
1704
0
                                            "unable to load object header chunk");
1705
1706
                            /* Check for flush dependency on previous continuation chunk */
1707
                            /* (As opposed to chunk 0) */
1708
0
                            if (cont_targ_chk_proxy->fd_parent) {
1709
                                /* Remove flush dependency on old continuation
1710
                                 * message chunk */
1711
0
                                assert(cont_targ_chk_proxy);
1712
0
                                assert(curr_chk_proxy);
1713
0
                                assert((void *)curr_chk_proxy == cont_targ_chk_proxy->fd_parent);
1714
1715
0
                                if (H5AC_destroy_flush_dependency(curr_chk_proxy, cont_targ_chk_proxy) < 0)
1716
0
                                    HGOTO_ERROR(H5E_OHDR, H5E_CANTUNDEPEND, FAIL,
1717
0
                                                "unable to destroy flush dependency");
1718
1719
0
                                cont_targ_chk_proxy->fd_parent = NULL;
1720
0
                            } /* end if */
1721
1722
                            /* Avoid (another) flush dependency on chunk 0 */
1723
0
                            if (0 != null_msg->chunkno) {
1724
                                /* Sanity checks */
1725
0
                                assert(null_chk_mdc_obj);
1726
0
                                assert(((H5C_cache_entry_t *)null_chk_mdc_obj)->type);
1727
0
                                assert(((H5C_cache_entry_t *)null_chk_mdc_obj)->type->id == H5AC_OHDR_CHK_ID);
1728
1729
                                /* Create flush dependency on new continuation
1730
                                 * message chunk */
1731
0
                                if (H5AC_create_flush_dependency(null_chk_mdc_obj, cont_targ_chk_proxy) < 0)
1732
0
                                    HGOTO_ERROR(H5E_OHDR, H5E_CANTDEPEND, FAIL,
1733
0
                                                "unable to create flush dependency");
1734
1735
0
                                cont_targ_chk_proxy->fd_parent = null_chk_mdc_obj;
1736
0
                            } /* end if */
1737
1738
                            /* Unprotect continuation message target chunk */
1739
0
                            if (H5O__chunk_unprotect(f, cont_targ_chk_proxy, false) < 0)
1740
0
                                HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL,
1741
0
                                            "unable to unprotect object header chunk");
1742
0
                            cont_targ_chk_proxy = NULL;
1743
0
                        } /* end if */
1744
1745
                        /* Copy raw data for non-null message to new chunk */
1746
2.33k
                        H5MM_memcpy(null_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh),
1747
2.33k
                                    curr_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh),
1748
2.33k
                                    curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh));
1749
1750
                        /* Point non-null message at null message's space */
1751
2.33k
                        curr_msg->chunkno = null_msg->chunkno;
1752
2.33k
                        curr_msg->raw     = null_msg->raw;
1753
2.33k
                        curr_chk_dirtied  = true;
1754
1755
                        /* Change information for null message */
1756
2.33k
                        if (curr_msg->raw_size == null_msg->raw_size) {
1757
                            /* Point null message at old non-null space */
1758
                            /* (Instead of freeing it and allocating new message) */
1759
934
                            null_msg->chunkno = old_chunkno;
1760
934
                            null_msg->raw     = old_raw;
1761
1762
                            /* Mark null message dirty */
1763
934
                            null_msg->dirty  = true;
1764
934
                            null_chk_dirtied = true;
1765
1766
                            /* Release current chunk, marking it dirty */
1767
934
                            if (H5O__chunk_unprotect(f, curr_chk_proxy, curr_chk_dirtied) < 0)
1768
0
                                HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL,
1769
934
                                            "unable to unprotect object header chunk");
1770
934
                            curr_chk_proxy   = NULL;
1771
934
                            curr_chk_dirtied = false;
1772
1773
                            /* Check for gap in null message's chunk */
1774
934
                            if (oh->chunk[old_chunkno].gap > 0) {
1775
                                /* Eliminate the gap in the chunk */
1776
0
                                if (H5O__eliminate_gap(
1777
0
                                        oh, &null_chk_dirtied, null_msg,
1778
0
                                        ((oh->chunk[old_chunkno].image + oh->chunk[old_chunkno].size) -
1779
0
                                         (H5O_SIZEOF_CHKSUM_OH(oh) + oh->chunk[old_chunkno].gap)),
1780
0
                                        oh->chunk[old_chunkno].gap) < 0)
1781
0
                                    HGOTO_ERROR(H5E_OHDR, H5E_CANTREMOVE, FAIL,
1782
0
                                                "can't eliminate gap in chunk");
1783
0
                            } /* end if */
1784
1785
                            /* Release null chunk, marking it dirty */
1786
934
                            if (H5O__chunk_unprotect(f, null_chk_proxy, null_chk_dirtied) < 0)
1787
0
                                HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL,
1788
934
                                            "unable to unprotect object header chunk");
1789
934
                            null_chk_proxy   = NULL;
1790
934
                            null_chk_dirtied = false;
1791
934
                        } /* end if */
1792
1.39k
                        else {
1793
1.39k
                            size_t new_null_msg; /* Message index for new null message */
1794
1795
                            /* Check if null message is large enough to still exist */
1796
1.39k
                            if ((null_msg->raw_size - curr_msg->raw_size) <
1797
1.39k
                                (size_t)H5O_SIZEOF_MSGHDR_OH(oh)) {
1798
0
                                size_t gap_size =
1799
0
                                    null_msg->raw_size - curr_msg->raw_size; /* Size of gap produced */
1800
1801
                                /* Adjust the size of the null message being eliminated */
1802
0
                                null_msg->raw_size = curr_msg->raw_size;
1803
1804
                                /* Mark null message dirty */
1805
0
                                null_msg->dirty  = true;
1806
0
                                null_chk_dirtied = true;
1807
1808
                                /* Add the gap to the chunk */
1809
0
                                if (H5O__add_gap(f, oh, null_msg->chunkno, &null_chk_dirtied, v,
1810
0
                                                 null_msg->raw + null_msg->raw_size, gap_size) < 0)
1811
0
                                    HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't insert gap in chunk");
1812
1813
                                /* Reuse message # for new null message taking place of non-null message */
1814
0
                                new_null_msg = v;
1815
0
                            } /* end if */
1816
1.39k
                            else {
1817
                                /* Adjust null message's size & offset */
1818
1.39k
                                null_msg->raw += curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh);
1819
1.39k
                                null_msg->raw_size -= curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh);
1820
1821
                                /* Mark null message dirty */
1822
1.39k
                                null_msg->dirty  = true;
1823
1.39k
                                null_chk_dirtied = true;
1824
1825
                                /* Create new null message for previous location of non-null message */
1826
1.39k
                                if (oh->nmesgs >= oh->alloc_nmesgs) {
1827
20
                                    if (H5O__alloc_msgs(oh, (size_t)1) < 0)
1828
0
                                        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
1829
20
                                                    "can't allocate more space for messages");
1830
1831
                                    /* "Retarget" 'curr_msg' pointer into newly re-allocated array of messages
1832
                                     */
1833
20
                                    curr_msg = &oh->mesg[u];
1834
20
                                } /* end if */
1835
1836
                                /* Get message # for new null message */
1837
1.39k
                                new_null_msg = oh->nmesgs++;
1838
1.39k
                            } /* end else */
1839
1840
                            /* Release null message's chunk, marking it dirty */
1841
1.39k
                            if (H5O__chunk_unprotect(f, null_chk_proxy, null_chk_dirtied) < 0)
1842
0
                                HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL,
1843
1.39k
                                            "unable to unprotect object header chunk");
1844
1.39k
                            null_chk_proxy   = NULL;
1845
1.39k
                            null_chk_dirtied = false;
1846
1847
                            /* Initialize new null message to take over non-null message's location */
1848
1.39k
                            oh->mesg[new_null_msg].type     = H5O_MSG_NULL;
1849
1.39k
                            oh->mesg[new_null_msg].native   = NULL;
1850
1.39k
                            oh->mesg[new_null_msg].raw      = old_raw;
1851
1.39k
                            oh->mesg[new_null_msg].raw_size = curr_msg->raw_size;
1852
1.39k
                            oh->mesg[new_null_msg].chunkno  = old_chunkno;
1853
1854
                            /* Mark new null message dirty */
1855
1.39k
                            oh->mesg[new_null_msg].dirty = true;
1856
1.39k
                            curr_chk_dirtied             = true;
1857
1858
                            /* Check for gap in new null message's chunk */
1859
1.39k
                            if (oh->chunk[old_chunkno].gap > 0) {
1860
                                /* Eliminate the gap in the chunk */
1861
0
                                if (H5O__eliminate_gap(
1862
0
                                        oh, &curr_chk_dirtied, &oh->mesg[new_null_msg],
1863
0
                                        ((oh->chunk[old_chunkno].image + oh->chunk[old_chunkno].size) -
1864
0
                                         (H5O_SIZEOF_CHKSUM_OH(oh) + oh->chunk[old_chunkno].gap)),
1865
0
                                        oh->chunk[old_chunkno].gap) < 0)
1866
0
                                    HGOTO_ERROR(H5E_OHDR, H5E_CANTREMOVE, FAIL,
1867
0
                                                "can't eliminate gap in chunk");
1868
0
                            } /* end if */
1869
1870
                            /* Release new null message's chunk, marking it dirty */
1871
1.39k
                            if (H5O__chunk_unprotect(f, curr_chk_proxy, curr_chk_dirtied) < 0)
1872
0
                                HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL,
1873
1.39k
                                            "unable to unprotect object header chunk");
1874
1.39k
                            curr_chk_proxy   = NULL;
1875
1.39k
                            curr_chk_dirtied = false;
1876
1.39k
                        } /* end else */
1877
1878
                        /* Indicate that we packed messages */
1879
2.33k
                        packed_msg = true;
1880
1881
                        /* Break out of loop */
1882
                        /* (If it's possible to move message to even earlier chunk
1883
                         *      we'll get it on the next pass - QAK)
1884
                         */
1885
2.33k
                        break;
1886
2.33k
                    } /* end if */
1887
122M
                }     /* end for */
1888
1889
                /* If we packed messages, get out of loop and start over */
1890
                /* (Don't know if this has any benefit one way or the other -QAK) */
1891
882k
                if (packed_msg)
1892
30.3k
                    break;
1893
882k
            } /* end else */
1894
1.12M
        }     /* end for */
1895
1896
        /* If we did any packing, remember that */
1897
32.2k
        if (packed_msg)
1898
31.3k
            did_packing = true;
1899
32.2k
    } while (packed_msg);
1900
1901
    /* Set return value */
1902
913
    ret_value = (htri_t)did_packing;
1903
1904
924
done:
1905
924
    if (ret_value < 0) {
1906
11
        if (null_chk_proxy && H5O__chunk_unprotect(f, null_chk_proxy, null_chk_dirtied) < 0)
1907
0
            HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect null object header chunk");
1908
11
        if (curr_chk_proxy && H5O__chunk_unprotect(f, curr_chk_proxy, curr_chk_dirtied) < 0)
1909
0
            HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect current object header chunk");
1910
11
        if (cont_targ_chk_proxy && H5O__chunk_unprotect(f, cont_targ_chk_proxy, false) < 0)
1911
0
            HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL,
1912
11
                        "unable to unprotect continuation message target object header chunk");
1913
11
    } /* end if */
1914
913
    else
1915
913
        assert(!null_chk_proxy && !curr_chk_proxy && !cont_targ_chk_proxy);
1916
1917
924
    FUNC_LEAVE_NOAPI(ret_value)
1918
924
} /* H5O__move_msgs_forward() */
1919
1920
/*-------------------------------------------------------------------------
1921
 *
1922
 * Function:    H5O__merge_null
1923
 *
1924
 * Purpose:     Merge neighboring null messages in an object header
1925
 *
1926
 * Return:  Non-negative on success/Negative on failure
1927
 *
1928
 *-------------------------------------------------------------------------
1929
 */
1930
static htri_t
1931
H5O__merge_null(H5F_t *f, H5O_t *oh)
1932
913
{
1933
913
    bool   merged_msg;          /* Flag to indicate that messages were merged */
1934
913
    bool   did_merging = false; /* Whether any messages were merged */
1935
913
    htri_t ret_value   = FAIL;  /* Return value */
1936
1937
913
    FUNC_ENTER_PACKAGE
1938
1939
    /* check args */
1940
913
    assert(oh != NULL);
1941
1942
    /* Loop until no messages merged */
1943
    /* (Double loop is not very efficient, but it would be some extra work to add
1944
     *      a list of messages to each chunk -QAK)
1945
     */
1946
3.68k
    do {
1947
3.68k
        H5O_mesg_t *curr_msg; /* Pointer to current message to operate on */
1948
3.68k
        unsigned    u;        /* Local index variable */
1949
1950
        /* Reset merged messages flag */
1951
3.68k
        merged_msg = false;
1952
1953
        /* Scan messages for adjacent null messages & merge them */
1954
58.3k
        for (u = 0, curr_msg = &oh->mesg[0]; u < oh->nmesgs; u++, curr_msg++) {
1955
57.4k
            if (H5O_NULL_ID == curr_msg->type->id) {
1956
6.46k
                H5O_mesg_t *curr_msg2; /* Pointer to current message to operate on */
1957
6.46k
                unsigned    v;         /* Local index variable */
1958
1959
                /* Should be no gaps in chunk with null message */
1960
6.46k
                assert(oh->chunk[curr_msg->chunkno].gap == 0);
1961
1962
                /* Loop over messages again, looking for null message in same chunk */
1963
321k
                for (v = 0, curr_msg2 = &oh->mesg[0]; v < oh->nmesgs; v++, curr_msg2++) {
1964
318k
                    if (u != v && H5O_NULL_ID == curr_msg2->type->id &&
1965
318k
                        curr_msg->chunkno == curr_msg2->chunkno) {
1966
10.2k
                        ssize_t adj_raw      = 0; /* Amount to adjust raw message pointer */
1967
10.2k
                        size_t  adj_raw_size = 0; /* Amount to adjust raw message size */
1968
1969
                        /* Check for second message after first message */
1970
10.2k
                        if ((curr_msg->raw + curr_msg->raw_size) ==
1971
10.2k
                            (curr_msg2->raw - H5O_SIZEOF_MSGHDR_OH(oh))) {
1972
                            /* Extend first null message length to cover second null message */
1973
1.68k
                            adj_raw      = 0;
1974
1.68k
                            adj_raw_size = (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + curr_msg2->raw_size;
1975
1976
                            /* Message has been merged */
1977
1.68k
                            merged_msg = true;
1978
1.68k
                        } /* end if */
1979
                        /* Check for second message before first message */
1980
8.56k
                        else if ((curr_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh)) ==
1981
8.56k
                                 (curr_msg2->raw + curr_msg2->raw_size)) {
1982
                            /* Adjust first message address and extend length to cover second message */
1983
1.09k
                            adj_raw = -((ssize_t)((size_t)H5O_SIZEOF_MSGHDR_OH(oh) + curr_msg2->raw_size));
1984
1.09k
                            adj_raw_size = (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + curr_msg2->raw_size;
1985
1986
                            /* Message has been merged */
1987
1.09k
                            merged_msg = true;
1988
1.09k
                        } /* end if */
1989
1990
                        /* Second message has been merged, delete it */
1991
10.2k
                        if (merged_msg) {
1992
2.77k
                            H5O_chunk_proxy_t *curr_chk_proxy; /* Chunk that message is in */
1993
2.77k
                            htri_t             result;
1994
1995
                            /* Release any information/memory for second message */
1996
2.77k
                            H5O__msg_free_mesg(curr_msg2);
1997
1998
                            /* Protect chunk */
1999
2.77k
                            if (NULL == (curr_chk_proxy = H5O__chunk_protect(f, oh, curr_msg->chunkno)))
2000
0
                                HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL,
2001
2.77k
                                            "unable to load object header chunk");
2002
2003
                            /* Adjust first message address and extend length to cover second message */
2004
2.77k
                            curr_msg->raw += adj_raw;
2005
2.77k
                            curr_msg->raw_size += adj_raw_size;
2006
2007
                            /* Mark first message as dirty */
2008
2.77k
                            curr_msg->dirty = true;
2009
2010
                            /* Release new null message's chunk, marking it dirty */
2011
2.77k
                            if (H5O__chunk_unprotect(f, curr_chk_proxy, true) < 0)
2012
0
                                HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL,
2013
2.77k
                                            "unable to unprotect object header chunk");
2014
2015
                            /* Remove second message from list of messages */
2016
2.77k
                            if (v < (oh->nmesgs - 1))
2017
2.41k
                                memmove(&oh->mesg[v], &oh->mesg[v + 1],
2018
2.41k
                                        ((oh->nmesgs - 1) - v) * sizeof(H5O_mesg_t));
2019
2020
                            /* Decrement # of messages */
2021
                            /* (Don't bother reducing size of message array for now -QAK) */
2022
2.77k
                            oh->nmesgs--;
2023
2024
                            /* The merge null message might span the entire chunk: scan for empty chunk to
2025
                             * remove */
2026
2.77k
                            if ((result = H5O__remove_empty_chunks(f, oh)) < 0)
2027
3
                                HGOTO_ERROR(H5E_OHDR, H5E_CANTPACK, FAIL, "can't remove empty chunk");
2028
2.77k
                            else if (result > 0)
2029
                                /* Get out of loop */
2030
119
                                break;
2031
2032
                            /* If the merged message is too large, shrink the chunk */
2033
2.65k
                            if (curr_msg->raw_size >= H5O_MESG_MAX_SIZE)
2034
0
                                if (H5O__alloc_shrink_chunk(f, oh, curr_msg->chunkno) < 0)
2035
0
                                    HGOTO_ERROR(H5E_OHDR, H5E_CANTPACK, FAIL, "unable to shrink chunk");
2036
2037
                            /* Get out of loop */
2038
2.65k
                            break;
2039
2.65k
                        } /* end if */
2040
10.2k
                    }     /* end if */
2041
318k
                }         /* end for */
2042
2043
                /* Get out of loop if we merged messages */
2044
6.46k
                if (merged_msg)
2045
2.77k
                    break;
2046
6.46k
            } /* end if */
2047
57.4k
        }     /* end for */
2048
2049
        /* If we did any merging, remember that */
2050
3.68k
        if (merged_msg)
2051
2.77k
            did_merging = true;
2052
3.68k
    } while (merged_msg);
2053
2054
    /* Set return value */
2055
910
    ret_value = (htri_t)did_merging;
2056
2057
913
done:
2058
913
    FUNC_LEAVE_NOAPI(ret_value)
2059
913
} /* H5O__merge_null() */
2060
2061
/*-------------------------------------------------------------------------
2062
 *
2063
 * Function:    H5O__remove_empty_chunks
2064
 *
2065
 * Purpose:     Attempt to eliminate empty chunks from object header.
2066
 *
2067
 *              This examines a chunk to see if it's empty
2068
 *              and removes it (and the continuation message that points to it)
2069
 *              from the object header.
2070
 *
2071
 * Return:  Non-negative on success/Negative on failure
2072
 *
2073
 *-------------------------------------------------------------------------
2074
 */
2075
static htri_t
2076
H5O__remove_empty_chunks(H5F_t *f, H5O_t *oh)
2077
3.68k
{
2078
3.68k
    bool   deleted_chunk;        /* Whether to a chunk was deleted */
2079
3.68k
    bool   did_deleting = false; /* Whether any chunks were deleted */
2080
3.68k
    htri_t ret_value    = FAIL;  /* Return value */
2081
2082
3.68k
    FUNC_ENTER_PACKAGE
2083
2084
    /* check args */
2085
3.68k
    assert(oh != NULL);
2086
2087
    /* Loop until no chunks are freed */
2088
3.81k
    do {
2089
3.81k
        H5O_mesg_t *null_msg; /* Pointer to null message found */
2090
3.81k
        H5O_mesg_t *cont_msg; /* Pointer to continuation message found */
2091
3.81k
        unsigned    u, v;     /* Local index variables */
2092
2093
        /* Reset 'chunk deleted' flag */
2094
3.81k
        deleted_chunk = false;
2095
2096
        /* Scan messages for null messages that fill an entire chunk */
2097
206k
        for (u = 0, null_msg = &oh->mesg[0]; u < oh->nmesgs; u++, null_msg++) {
2098
            /* If a null message takes up an entire object header chunk (and
2099
             * its not the "base" chunk), delete that chunk from object header
2100
             */
2101
202k
            if (H5O_NULL_ID == null_msg->type->id && null_msg->chunkno > 0 &&
2102
202k
                ((size_t)H5O_SIZEOF_MSGHDR_OH(oh) + null_msg->raw_size) ==
2103
40.3k
                    (oh->chunk[null_msg->chunkno].size - H5O_SIZEOF_CHKHDR_OH(oh))) {
2104
126
                H5O_mesg_t *curr_msg;        /* Pointer to current message to operate on */
2105
126
                unsigned    null_msg_no;     /* Message # for null message */
2106
126
                unsigned    deleted_chunkno; /* Chunk # to delete */
2107
2108
                /* Locate continuation message that points to chunk */
2109
1.68k
                for (v = 0, cont_msg = &oh->mesg[0]; v < oh->nmesgs; v++, cont_msg++) {
2110
1.68k
                    if (H5O_CONT_ID == cont_msg->type->id) {
2111
                        /* Decode current continuation message if necessary */
2112
232
                        H5O_LOAD_NATIVE(f, 0, oh, cont_msg, FAIL)
2113
2114
                        /* Check if the chunkno needs to be set */
2115
                        /* (should only occur when the continuation message is first decoded) */
2116
232
                        if (0 == ((H5O_cont_t *)(cont_msg->native))->chunkno) {
2117
0
                            unsigned w; /* Local index variable */
2118
2119
                            /* Find chunk that this continuation message points to */
2120
0
                            for (w = 0; w < oh->nchunks; w++)
2121
0
                                if (oh->chunk[w].addr == ((H5O_cont_t *)(cont_msg->native))->addr) {
2122
0
                                    ((H5O_cont_t *)(cont_msg->native))->chunkno = w;
2123
0
                                    break;
2124
0
                                } /* end if */
2125
0
                            assert(((H5O_cont_t *)(cont_msg->native))->chunkno > 0);
2126
0
                        } /* end if */
2127
2128
                        /* Check for correct chunk to delete */
2129
232
                        if (oh->chunk[null_msg->chunkno].addr == ((H5O_cont_t *)(cont_msg->native))->addr)
2130
124
                            break;
2131
232
                    } /* end if */
2132
1.68k
                }     /* end for */
2133
                /* Must be a continuation message that points to chunk containing null message */
2134
126
                assert(v < oh->nmesgs);
2135
126
                assert(cont_msg);
2136
126
                assert(((H5O_cont_t *)(cont_msg->native))->chunkno == null_msg->chunkno);
2137
2138
                /* Initialize information about null message */
2139
126
                null_msg_no     = u;
2140
126
                deleted_chunkno = null_msg->chunkno;
2141
2142
                /* Convert continuation message into a null message */
2143
126
                if (H5O__release_mesg(f, oh, cont_msg, true) < 0)
2144
3
                    HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, "unable to convert into null message");
2145
2146
                /*
2147
                 * Remove chunk from object header's data structure
2148
                 */
2149
2150
                /* Free memory for chunk image */
2151
123
                oh->chunk[null_msg->chunkno].image =
2152
123
                    H5FL_BLK_FREE(chunk_image, oh->chunk[null_msg->chunkno].image);
2153
2154
                /* Remove chunk from list of chunks */
2155
123
                if (null_msg->chunkno < (oh->nchunks - 1)) {
2156
31
                    memmove(&oh->chunk[null_msg->chunkno], &oh->chunk[null_msg->chunkno + 1],
2157
31
                            ((oh->nchunks - 1) - null_msg->chunkno) * sizeof(H5O_chunk_t));
2158
2159
                    /* Adjust chunk number for any chunk proxies that are in the cache */
2160
73
                    for (u = null_msg->chunkno; u < (oh->nchunks - 1); u++) {
2161
42
                        unsigned chk_proxy_status = 0; /* Metadata cache status of chunk proxy for chunk */
2162
2163
                        /* Check the chunk proxy's status in the metadata cache */
2164
42
                        if (H5AC_get_entry_status(f, oh->chunk[u].addr, &chk_proxy_status) < 0)
2165
0
                            HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL,
2166
42
                                        "unable to check metadata cache status for chunk proxy");
2167
2168
                        /* If the entry is in the cache, update its chunk index */
2169
42
                        if (chk_proxy_status & H5AC_ES__IN_CACHE) {
2170
42
                            if (H5O__chunk_update_idx(f, oh, u) < 0)
2171
0
                                HGOTO_ERROR(H5E_OHDR, H5E_CANTSET, FAIL,
2172
42
                                            "unable to update index for chunk proxy");
2173
42
                        } /* end if */
2174
42
                    }     /* end for */
2175
31
                }         /* end if */
2176
2177
                /* Decrement # of chunks */
2178
                /* (Don't bother reducing size of chunk array for now -QAK) */
2179
123
                oh->nchunks--;
2180
2181
                /*
2182
                 * Delete null message (in empty chunk that was be freed) from list of messages
2183
                 */
2184
2185
                /* Release any information/memory for message */
2186
123
                H5O__msg_free_mesg(null_msg);
2187
2188
                /* Remove null message from list of messages */
2189
123
                if (null_msg_no < (oh->nmesgs - 1))
2190
97
                    memmove(&oh->mesg[null_msg_no], &oh->mesg[null_msg_no + 1],
2191
97
                            ((oh->nmesgs - 1) - null_msg_no) * sizeof(H5O_mesg_t));
2192
2193
                /* Decrement # of messages */
2194
                /* (Don't bother reducing size of message array for now -QAK) */
2195
123
                oh->nmesgs--;
2196
2197
                /* Adjust chunk # for messages in chunks after deleted chunk */
2198
5.54k
                for (u = 0, curr_msg = &oh->mesg[0]; u < oh->nmesgs; u++, curr_msg++) {
2199
                    /* Sanity check - there should be no messages in deleted chunk */
2200
5.42k
                    assert(curr_msg->chunkno != deleted_chunkno);
2201
2202
                    /* Adjust chunk index for messages in later chunks */
2203
5.42k
                    if (curr_msg->chunkno > deleted_chunkno)
2204
411
                        curr_msg->chunkno--;
2205
2206
                    /* Check for continuation message */
2207
5.42k
                    if (H5O_CONT_ID == curr_msg->type->id) {
2208
                        /* Decode current continuation message if necessary */
2209
585
                        H5O_LOAD_NATIVE(f, 0, oh, curr_msg, FAIL)
2210
2211
                        /* Check if the chunkno needs to be set */
2212
                        /* (should only occur when the continuation message is first decoded) */
2213
585
                        if (0 == ((H5O_cont_t *)(curr_msg->native))->chunkno) {
2214
0
                            unsigned w; /* Local index variable */
2215
2216
                            /* Find chunk that this continuation message points to */
2217
0
                            for (w = 0; w < oh->nchunks; w++)
2218
0
                                if (oh->chunk[w].addr == ((H5O_cont_t *)(curr_msg->native))->addr) {
2219
0
                                    ((H5O_cont_t *)(curr_msg->native))->chunkno = w;
2220
0
                                    break;
2221
0
                                } /* end if */
2222
0
                            assert(((H5O_cont_t *)(curr_msg->native))->chunkno > 0);
2223
0
                        } /* end if */
2224
585
                        else {
2225
                            /* Check for pointer to chunk after deleted chunk */
2226
585
                            if (((H5O_cont_t *)(curr_msg->native))->chunkno > deleted_chunkno)
2227
488
                                ((H5O_cont_t *)(curr_msg->native))->chunkno--;
2228
585
                        } /* end else */
2229
585
                    }     /* end if */
2230
5.42k
                }         /* end for */
2231
2232
                /* Found chunk to delete */
2233
123
                deleted_chunk = true;
2234
123
                break;
2235
123
            } /* end if */
2236
202k
        }     /* end for */
2237
2238
        /* If we deleted any chunks, remember that */
2239
3.80k
        if (deleted_chunk)
2240
123
            did_deleting = true;
2241
3.80k
    } while (deleted_chunk);
2242
2243
    /* Set return value */
2244
3.68k
    ret_value = (htri_t)did_deleting;
2245
2246
3.68k
done:
2247
3.68k
    FUNC_LEAVE_NOAPI(ret_value)
2248
3.68k
} /* H5O__remove_empty_chunks() */
2249
2250
/*-------------------------------------------------------------------------
2251
 *
2252
 * Function:    H5O__condense_header
2253
 *
2254
 * Purpose:     Attempt to eliminate empty chunks from object header.
2255
 *
2256
 * Return:  Non-negative on success/Negative on failure
2257
 *
2258
 *-------------------------------------------------------------------------
2259
 */
2260
herr_t
2261
H5O__condense_header(H5F_t *f, H5O_t *oh)
2262
429
{
2263
429
    bool   rescan_header;       /* Whether to rescan header */
2264
429
    htri_t result;              /* Result from packing/merging/etc */
2265
429
    herr_t ret_value = SUCCEED; /* return value */
2266
2267
429
    FUNC_ENTER_PACKAGE
2268
2269
    /* check args */
2270
429
    assert(oh != NULL);
2271
2272
    /* Loop until no changed to the object header messages & chunks */
2273
924
    do {
2274
        /* Reset 'rescan chunks' flag */
2275
924
        rescan_header = false;
2276
2277
        /* Scan for messages that can be moved earlier in chunks */
2278
924
        result = H5O__move_msgs_forward(f, oh);
2279
924
        if (result < 0)
2280
11
            HGOTO_ERROR(H5E_OHDR, H5E_CANTPACK, FAIL, "can't move header messages forward");
2281
913
        if (result > 0)
2282
486
            rescan_header = true;
2283
2284
        /* Scan for adjacent null messages & merge them */
2285
913
        result = H5O__merge_null(f, oh);
2286
913
        if (result < 0)
2287
3
            HGOTO_ERROR(H5E_OHDR, H5E_CANTPACK, FAIL, "can't pack null header messages");
2288
910
        if (result > 0)
2289
444
            rescan_header = true;
2290
2291
        /* Scan for empty chunks to remove */
2292
910
        result = H5O__remove_empty_chunks(f, oh);
2293
910
        if (result < 0)
2294
0
            HGOTO_ERROR(H5E_OHDR, H5E_CANTPACK, FAIL, "can't remove empty chunk");
2295
910
        if (result > 0)
2296
4
            rescan_header = true;
2297
910
    } while (rescan_header);
2298
#ifdef H5O_DEBUG
2299
    H5O__assert(oh);
2300
#endif /* H5O_DEBUG */
2301
2302
429
done:
2303
429
    FUNC_LEAVE_NOAPI(ret_value)
2304
429
} /* H5O__condense_header() */
2305
2306
/*-------------------------------------------------------------------------
2307
 *
2308
 * Function:    H5O__alloc_shrink_chunk
2309
 *
2310
 * Purpose:     Shrinks a chunk, removing all null messages and any gap.
2311
 *
2312
 * Return:  Non-negative on success/Negative on failure
2313
 *
2314
 *-------------------------------------------------------------------------
2315
 */
2316
static herr_t
2317
H5O__alloc_shrink_chunk(H5F_t *f, H5O_t *oh, unsigned chunkno)
2318
0
{
2319
0
    H5O_chunk_t       *chunk     = &oh->chunk[chunkno];      /* Chunk to shrink */
2320
0
    H5O_chunk_proxy_t *chk_proxy = NULL;                     /* Metadata cache proxy for chunk to shrink */
2321
0
    H5O_mesg_t        *curr_msg;                             /* Current message to examine */
2322
0
    uint8_t           *old_image = chunk->image;             /* Old address of chunk's image in memory */
2323
0
    size_t             old_size  = chunk->size;              /* Old size of chunk */
2324
0
    size_t             new_size  = chunk->size - chunk->gap; /* Size of shrunk chunk */
2325
0
    size_t             total_msg_size;                       /* Size of the messages in this chunk */
2326
0
    size_t             min_chunk_size    = H5O_ALIGN_OH(oh, H5O_MIN_SIZE); /* Minimum chunk size */
2327
0
    size_t             sizeof_chksum     = H5O_SIZEOF_CHKSUM_OH(oh);       /* Size of chunk checksum */
2328
0
    size_t             sizeof_msghdr     = H5O_SIZEOF_MSGHDR_OH(oh);       /* Size of message header */
2329
0
    uint8_t            new_size_flags    = 0;                              /* New chunk #0 size flags */
2330
0
    bool               adjust_size_flags = false; /* Whether to adjust the chunk #0 size flags */
2331
0
    size_t             less_prfx_size    = 0;     /* Bytes removed from object header prefix */
2332
0
    size_t             u;                         /* Index */
2333
0
    herr_t             ret_value = SUCCEED;       /* Return value */
2334
2335
0
    FUNC_ENTER_PACKAGE
2336
2337
    /* check args */
2338
0
    assert(f);
2339
0
    assert(oh);
2340
2341
    /* Protect chunk */
2342
0
    if (NULL == (chk_proxy = H5O__chunk_protect(f, oh, chunkno)))
2343
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to protect object header chunk");
2344
2345
    /* Loop backwards to increase the chance of seeing more null messages at the
2346
     * end of the chunk.  Note that we rely on unsigned u wrapping around at the
2347
     * end.
2348
     */
2349
0
    for (u = oh->nmesgs - 1, curr_msg = &oh->mesg[u]; u < oh->nmesgs; u--, curr_msg--) {
2350
0
        if ((H5O_NULL_ID == curr_msg->type->id) && (chunkno == curr_msg->chunkno)) {
2351
0
            size_t shrink_size = curr_msg->raw_size + sizeof_msghdr; /* Amount to shrink the chunk by */
2352
2353
            /* If the current message is not at the end of the chunk, copy the
2354
             * data after it (except the checksum).
2355
             */
2356
0
            if (curr_msg->raw + curr_msg->raw_size < old_image + new_size - sizeof_chksum) {
2357
0
                unsigned    v; /* Index */
2358
0
                H5O_mesg_t *curr_msg2;
2359
0
                uint8_t    *src = curr_msg->raw + curr_msg->raw_size; /* Source location */
2360
2361
                /* Slide down the raw data */
2362
0
                memmove(curr_msg->raw - sizeof_msghdr, src,
2363
0
                        (size_t)(old_image + new_size - sizeof_chksum - src));
2364
2365
                /* Update the raw data pointers for messages after this one */
2366
0
                for (v = 0, curr_msg2 = &oh->mesg[0]; v < oh->nmesgs; v++, curr_msg2++)
2367
0
                    if ((chunkno == curr_msg2->chunkno) && (curr_msg2->raw > curr_msg->raw))
2368
0
                        curr_msg2->raw -= shrink_size;
2369
0
            } /* end if */
2370
2371
            /* Adjust the new chunk size */
2372
0
            new_size -= shrink_size;
2373
2374
            /* Release any information/memory for the message */
2375
0
            H5O__msg_free_mesg(curr_msg);
2376
2377
            /* Remove the deleted null message from list of messages */
2378
0
            if (u < (oh->nmesgs - 1))
2379
0
                memmove(&oh->mesg[u], &oh->mesg[u + 1], ((oh->nmesgs - 1) - u) * sizeof(H5O_mesg_t));
2380
2381
            /* Decrement # of messages */
2382
            /* (Don't bother reducing size of message array for now) */
2383
0
            oh->nmesgs--;
2384
0
        } /* end if */
2385
0
    }     /* end for */
2386
2387
    /* Check if the chunk is too small, extend if necessary */
2388
0
    total_msg_size = new_size - (size_t)(chunkno == 0 ? H5O_SIZEOF_HDR(oh) : H5O_SIZEOF_CHKHDR_OH(oh));
2389
0
    if (total_msg_size < min_chunk_size) {
2390
0
        assert(oh->alloc_nmesgs > oh->nmesgs);
2391
0
        oh->nmesgs++;
2392
2393
        /* Initialize new null message to make the chunk large enough */
2394
0
        curr_msg         = &oh->mesg[oh->nmesgs - 1];
2395
0
        curr_msg->type   = H5O_MSG_NULL;
2396
0
        curr_msg->dirty  = true;
2397
0
        curr_msg->native = NULL;
2398
0
        curr_msg->raw    = old_image + new_size + sizeof_msghdr - sizeof_chksum;
2399
0
        curr_msg->raw_size =
2400
0
            MAX(H5O_ALIGN_OH(oh, min_chunk_size - total_msg_size), sizeof_msghdr) - sizeof_msghdr;
2401
0
        curr_msg->chunkno = chunkno;
2402
2403
        /* update the new chunk size */
2404
0
        new_size += curr_msg->raw_size + sizeof_msghdr;
2405
0
    } /* end if */
2406
2407
    /* Check for changing the chunk #0 data size enough to need adjusting the flags */
2408
0
    if (oh->version > H5O_VERSION_1 && chunkno == 0) {
2409
0
        uint64_t chunk0_newsize = new_size - (size_t)H5O_SIZEOF_HDR(oh); /* New size of chunk 0's data */
2410
0
        size_t   orig_prfx_size = (size_t)1 << (oh->flags & H5O_HDR_CHUNK0_SIZE); /* Original prefix size */
2411
2412
        /* Check for moving to a 1-byte size encoding */
2413
0
        if (orig_prfx_size > 1 && chunk0_newsize <= 255) {
2414
0
            less_prfx_size    = orig_prfx_size - 1;
2415
0
            new_size_flags    = H5O_HDR_CHUNK0_1;
2416
0
            adjust_size_flags = true;
2417
0
        } /* end if */
2418
        /* Check for moving to a 2-byte size encoding */
2419
0
        else if (orig_prfx_size > 2 && chunk0_newsize <= 65535) {
2420
0
            less_prfx_size    = orig_prfx_size - 2;
2421
0
            new_size_flags    = H5O_HDR_CHUNK0_2;
2422
0
            adjust_size_flags = true;
2423
0
        } /* end if */
2424
        /* Check for moving to a 4-byte size encoding */
2425
0
        else if (orig_prfx_size > 4 && chunk0_newsize <= 4294967295) {
2426
0
            less_prfx_size    = orig_prfx_size - 4;
2427
0
            new_size_flags    = H5O_HDR_CHUNK0_4;
2428
0
            adjust_size_flags = true;
2429
0
        } /* end if */
2430
0
    }     /* end if */
2431
2432
0
    if (adjust_size_flags) {
2433
        /* Adjust object header prefix flags */
2434
0
        oh->flags = (uint8_t)(oh->flags & ~H5O_HDR_CHUNK0_SIZE);
2435
0
        oh->flags |= new_size_flags;
2436
2437
        /* Slide chunk 0 data down */
2438
0
        memmove(chunk->image + H5O_SIZEOF_HDR(oh) - sizeof_chksum,
2439
0
                chunk->image + H5O_SIZEOF_HDR(oh) - sizeof_chksum + less_prfx_size,
2440
0
                new_size - (size_t)H5O_SIZEOF_HDR(oh));
2441
2442
        /* Adjust chunk size */
2443
0
        new_size -= less_prfx_size;
2444
0
    } /* end if */
2445
2446
    /* Allocate less memory space for chunk's image */
2447
0
    chunk->size  = new_size;
2448
0
    chunk->image = H5FL_BLK_REALLOC(chunk_image, old_image, chunk->size);
2449
0
    chunk->gap   = 0;
2450
0
    if (NULL == oh->chunk[chunkno].image)
2451
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");
2452
2453
    /* Spin through existing messages, adjusting them */
2454
0
    for (u = 0, curr_msg = &oh->mesg[0]; u < oh->nmesgs; u++, curr_msg++) {
2455
0
        if (adjust_size_flags || (chunk->image != old_image))
2456
            /* Adjust raw addresses for messages in this chunk to reflect new 'image' address */
2457
0
            if (curr_msg->chunkno == chunkno)
2458
0
                curr_msg->raw = chunk->image - less_prfx_size + (curr_msg->raw - old_image);
2459
2460
        /* Find continuation message which points to this chunk and adjust chunk's size */
2461
        /* (Chunk 0 doesn't have a continuation message that points to it and
2462
         * its size is directly encoded in the object header) */
2463
0
        if (chunkno > 0 && (H5O_CONT_ID == curr_msg->type->id) &&
2464
0
            (((H5O_cont_t *)(curr_msg->native))->chunkno == chunkno)) {
2465
0
            H5O_chunk_proxy_t *cont_chk_proxy; /* Chunk that message is in */
2466
2467
            /* Protect chunk */
2468
0
            if (NULL == (cont_chk_proxy = H5O__chunk_protect(f, oh, curr_msg->chunkno)))
2469
0
                HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to protect object header chunk");
2470
2471
            /* Adjust size of continuation message */
2472
0
            assert(((H5O_cont_t *)(curr_msg->native))->size == old_size);
2473
0
            ((H5O_cont_t *)(curr_msg->native))->size = chunk->size;
2474
2475
            /* Flag continuation message as dirty */
2476
0
            curr_msg->dirty = true;
2477
2478
            /* Release chunk, marking it dirty */
2479
0
            if (H5O__chunk_unprotect(f, cont_chk_proxy, true) < 0)
2480
0
                HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect object header chunk");
2481
0
        } /* end if */
2482
0
    }     /* end for */
2483
2484
0
    assert(new_size <= old_size);
2485
2486
    /* Resize the chunk in the cache */
2487
0
    if (H5O__chunk_resize(oh, chk_proxy) < 0)
2488
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTRESIZE, FAIL, "unable to resize object header chunk");
2489
2490
    /* Free the unused space in the file */
2491
0
    if (H5MF_xfree(f, H5FD_MEM_OHDR, chunk->addr + new_size, (hsize_t)(old_size - new_size)) < 0)
2492
0
        HGOTO_ERROR(H5E_OHDR, H5E_CANTFREE, FAIL, "unable to shrink object header chunk");
2493
2494
0
done:
2495
    /* Release chunk, marking it dirty */
2496
0
    if (chk_proxy && H5O__chunk_unprotect(f, chk_proxy, true) < 0)
2497
0
        HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect object header chunk");
2498
2499
0
    FUNC_LEAVE_NOAPI(ret_value)
2500
0
} /* H5O__alloc_shrink_chunk() */