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() */ |