Line | Count | Source |
1 | | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
2 | | * Copyright by The HDF Group. * |
3 | | * All rights reserved. * |
4 | | * * |
5 | | * This file is part of HDF5. The full HDF5 copyright notice, including * |
6 | | * terms governing use, modification, and redistribution, is contained in * |
7 | | * the LICENSE file, which can be found at the root of the source code * |
8 | | * distribution tree, or in https://www.hdfgroup.org/licenses. * |
9 | | * If you do not have access to either file, you may request a copy from * |
10 | | * help@hdfgroup.org. * |
11 | | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
12 | | |
13 | | /*------------------------------------------------------------------------- |
14 | | * |
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 | 0 | { |
99 | 0 | bool merged_with_null; /* Whether the gap was merged with a null message */ |
100 | 0 | size_t u; /* Local index variable */ |
101 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
102 | |
|
103 | 0 | FUNC_ENTER_PACKAGE |
104 | | |
105 | | /* check args */ |
106 | 0 | assert(oh); |
107 | 0 | assert(oh->version > H5O_VERSION_1); |
108 | 0 | assert(chk_dirtied); |
109 | 0 | assert(new_gap_loc); |
110 | 0 | 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 | 0 | merged_with_null = false; |
128 | 0 | 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 | 0 | 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 | 0 | } /* end for */ |
143 | | |
144 | | /* If we couldn't find a null message in the chunk, move the gap to the end */ |
145 | 0 | if (!merged_with_null) { |
146 | | /* Adjust message offsets after new gap forward in chunk */ |
147 | 0 | for (u = 0; u < oh->nmesgs; u++) |
148 | 0 | 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 | 0 | memmove(new_gap_loc, new_gap_loc + new_gap_size, |
153 | 0 | (size_t)((oh->chunk[chunkno].image + (oh->chunk[chunkno].size - H5O_SIZEOF_CHKSUM_OH(oh))) - |
154 | 0 | (new_gap_loc + new_gap_size))); |
155 | | |
156 | | /* Add existing gap size to new gap size */ |
157 | 0 | new_gap_size += oh->chunk[chunkno].gap; |
158 | | |
159 | | /* Merging with existing gap will allow for a new null message */ |
160 | 0 | 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 | 0 | else |
191 | 0 | oh->chunk[chunkno].gap = new_gap_size; |
192 | | |
193 | | /* Mark the chunk as modified */ |
194 | 0 | *chk_dirtied = true; |
195 | 0 | } /* end if */ |
196 | | |
197 | 0 | done: |
198 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
199 | 0 | } /* 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 | 0 | { |
220 | 0 | uint8_t *move_start, *move_end; /* Pointers to area of messages to move */ |
221 | 0 | bool null_before_gap; /* Flag whether the null message is before the gap or not */ |
222 | |
|
223 | 0 | FUNC_ENTER_PACKAGE_NOERR |
224 | | |
225 | | /* check args */ |
226 | 0 | assert(oh); |
227 | 0 | assert(oh->version > H5O_VERSION_1); |
228 | 0 | assert(chk_dirtied); |
229 | 0 | assert(mesg); |
230 | 0 | assert(gap_loc); |
231 | 0 | assert(gap_size); |
232 | | |
233 | | /* Check if the null message is before or after the gap produced */ |
234 | 0 | null_before_gap = (bool)(mesg->raw < gap_loc); |
235 | | |
236 | | /* Set up information about region of messages to move */ |
237 | 0 | if (null_before_gap) { |
238 | 0 | move_start = mesg->raw + mesg->raw_size; |
239 | 0 | move_end = gap_loc; |
240 | 0 | } /* 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 | 0 | 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 | 0 | 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 | 0 | memset(mesg->raw + mesg->raw_size, 0, gap_size); |
287 | | |
288 | | /* Adjust size of null message */ |
289 | 0 | mesg->raw_size += gap_size; |
290 | | |
291 | | /* Set the gap size to zero for the chunk */ |
292 | 0 | oh->chunk[mesg->chunkno].gap = 0; |
293 | | |
294 | | /* Mark null message as dirty */ |
295 | 0 | mesg->dirty = true; |
296 | 0 | *chk_dirtied = true; |
297 | |
|
298 | 0 | FUNC_LEAVE_NOAPI(SUCCEED) |
299 | 0 | } /* 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 | 0 | { |
315 | 0 | H5O_chunk_proxy_t *chk_proxy = NULL; /* Chunk that message is in */ |
316 | 0 | bool chk_dirtied = false; /* Flags for unprotecting chunk */ |
317 | 0 | H5O_mesg_t *alloc_msg; /* Pointer to null message to allocate out of */ |
318 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
319 | |
|
320 | 0 | FUNC_ENTER_PACKAGE |
321 | | |
322 | | /* check args */ |
323 | 0 | assert(oh); |
324 | 0 | assert(new_type); |
325 | 0 | assert(new_size); |
326 | | |
327 | | /* Point to null message to allocate out of */ |
328 | 0 | alloc_msg = &oh->mesg[null_idx]; |
329 | | |
330 | | /* Protect chunk */ |
331 | 0 | 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 | 0 | if (alloc_msg->raw_size > new_size) { |
336 | | /* Check for producing a gap in the chunk */ |
337 | 0 | if ((alloc_msg->raw_size - new_size) < (size_t)H5O_SIZEOF_MSGHDR_OH(oh)) { |
338 | 0 | 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 | 0 | alloc_msg->raw_size = new_size; |
342 | | |
343 | | /* Add the gap to the chunk */ |
344 | 0 | if (H5O__add_gap(f, oh, alloc_msg->chunkno, &chk_dirtied, null_idx, |
345 | 0 | 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 | 0 | } /* end if */ |
348 | 0 | else { |
349 | 0 | size_t new_mesg_size = |
350 | 0 | new_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh); /* Total size of newly allocated message */ |
351 | 0 | 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 | 0 | 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 | 0 | null_msg = &(oh->mesg[oh->nmesgs++]); |
364 | 0 | null_msg->type = H5O_MSG_NULL; |
365 | 0 | null_msg->native = NULL; |
366 | 0 | null_msg->raw = alloc_msg->raw + new_mesg_size; |
367 | 0 | null_msg->raw_size = alloc_msg->raw_size - new_mesg_size; |
368 | 0 | null_msg->chunkno = alloc_msg->chunkno; |
369 | | |
370 | | /* Mark the message as dirty */ |
371 | 0 | null_msg->dirty = true; |
372 | 0 | chk_dirtied = true; |
373 | | |
374 | | /* Check for gap in new null message's chunk */ |
375 | 0 | 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 | 0 | alloc_msg->raw_size = new_size; |
388 | 0 | } /* end else */ |
389 | 0 | } /* end if */ |
390 | | |
391 | | /* Initialize the new message */ |
392 | 0 | alloc_msg->type = new_type; |
393 | 0 | alloc_msg->native = new_native; |
394 | | |
395 | | /* Mark the new message as dirty */ |
396 | 0 | alloc_msg->dirty = true; |
397 | 0 | chk_dirtied = true; |
398 | |
|
399 | 0 | done: |
400 | | /* Release chunk */ |
401 | 0 | 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 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
405 | 0 | } /* 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 | 0 | { |
420 | 0 | size_t old_alloc; /* Old number of messages allocated */ |
421 | 0 | size_t na; /* New number of messages allocated */ |
422 | 0 | H5O_mesg_t *new_mesg; /* Pointer to new message array */ |
423 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
424 | |
|
425 | 0 | FUNC_ENTER_PACKAGE |
426 | | |
427 | | /* check args */ |
428 | 0 | assert(oh); |
429 | | |
430 | | /* Initialize number of messages information */ |
431 | 0 | old_alloc = oh->alloc_nmesgs; |
432 | 0 | na = oh->alloc_nmesgs + MAX(oh->alloc_nmesgs, min_alloc); /* At least double */ |
433 | | |
434 | | /* Attempt to allocate more memory */ |
435 | 0 | 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 | 0 | oh->alloc_nmesgs = na; |
440 | 0 | oh->mesg = new_mesg; |
441 | | |
442 | | /* Set new object header info to zeros */ |
443 | 0 | memset(&oh->mesg[old_alloc], 0, (oh->alloc_nmesgs - old_alloc) * sizeof(H5O_mesg_t)); |
444 | |
|
445 | 0 | done: |
446 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
447 | 0 | } /* 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 | 0 | { |
478 | 0 | H5O_chunk_proxy_t *chk_proxy = NULL; /* Chunk that message is in */ |
479 | 0 | bool chk_dirtied = false; /* Flag for unprotecting chunk */ |
480 | 0 | size_t delta; /* Change in chunk's size */ |
481 | 0 | size_t aligned_size = H5O_ALIGN_OH(oh, size); |
482 | 0 | uint8_t *old_image; /* Old address of chunk's image in memory */ |
483 | 0 | size_t old_size; /* Old size of chunk */ |
484 | 0 | htri_t was_extended; /* If chunk can be extended */ |
485 | 0 | size_t extend_msg = 0; /* Index of null message to extend */ |
486 | 0 | bool extended_msg = false; /* Whether an existing message was extended */ |
487 | 0 | uint8_t new_size_flags = 0; /* New chunk #0 size flags */ |
488 | 0 | bool adjust_size_flags = false; /* Whether to adjust the chunk #0 size flags */ |
489 | 0 | size_t extra_prfx_size = 0; /* Extra bytes added to object header prefix */ |
490 | 0 | size_t u; /* Local index variable */ |
491 | 0 | htri_t ret_value = true; /* return value */ |
492 | |
|
493 | 0 | FUNC_ENTER_PACKAGE |
494 | | |
495 | | /* check args */ |
496 | 0 | assert(f != NULL); |
497 | 0 | assert(oh != NULL); |
498 | 0 | assert(chunkno < oh->nchunks); |
499 | 0 | assert(size > 0); |
500 | 0 | assert(msg_idx != NULL); |
501 | 0 | 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 | 0 | 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 | 0 | if (oh->mesg[u].chunkno == chunkno && H5O_NULL_ID == oh->mesg[u].type->id && |
510 | 0 | ((oh->mesg[u].raw + oh->mesg[u].raw_size) == |
511 | 0 | ((oh->chunk[chunkno].image + oh->chunk[chunkno].size) - |
512 | 0 | (oh->chunk[chunkno].gap + H5O_SIZEOF_CHKSUM_OH(oh))))) { |
513 | |
|
514 | 0 | extend_msg = u; |
515 | 0 | extended_msg = true; |
516 | 0 | break; |
517 | 0 | } /* end if */ |
518 | 0 | } /* end for */ |
519 | | |
520 | | /* If we can extend an existing null message, adjust the delta appropriately */ |
521 | 0 | if (extended_msg) { |
522 | 0 | assert(oh->chunk[chunkno].gap == 0); |
523 | 0 | delta = aligned_size - oh->mesg[extend_msg].raw_size; |
524 | 0 | } /* end if */ |
525 | 0 | else |
526 | 0 | delta = (aligned_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh)) - oh->chunk[chunkno].gap; |
527 | 0 | delta = H5O_ALIGN_OH(oh, delta); |
528 | | |
529 | | /* Check for changing the chunk #0 data size enough to need adjusting the flags */ |
530 | 0 | if (oh->version > H5O_VERSION_1 && chunkno == 0) { |
531 | 0 | uint64_t chunk0_size; /* Size of chunk 0's data */ |
532 | 0 | size_t orig_prfx_size = (size_t)1 << (oh->flags & H5O_HDR_CHUNK0_SIZE); /* Original prefix size */ |
533 | |
|
534 | 0 | assert(oh->chunk[0].size >= (size_t)H5O_SIZEOF_HDR(oh)); |
535 | 0 | chunk0_size = oh->chunk[0].size - (size_t)H5O_SIZEOF_HDR(oh); |
536 | | |
537 | | /* Check for moving to a 8-byte size encoding */ |
538 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | } /* end if */ |
556 | | |
557 | | /* Protect chunk */ |
558 | 0 | 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 | 0 | was_extended = H5MF_try_extend(f, H5FD_MEM_OHDR, oh->chunk[chunkno].addr, |
563 | 0 | (hsize_t)(oh->chunk[chunkno].size), (hsize_t)(delta + extra_prfx_size)); |
564 | 0 | if (was_extended < 0) /* error */ |
565 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTEXTEND, FAIL, "can't tell if we can extend chunk"); |
566 | 0 | else if (was_extended == false) /* can't extend -- we are done */ |
567 | 0 | HGOTO_DONE(false); |
568 | | |
569 | | /* Adjust object header prefix flags */ |
570 | 0 | 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 | 0 | if (extended_msg) { |
581 | | /* Adjust message size of existing null message */ |
582 | 0 | oh->mesg[extend_msg].raw_size += delta; |
583 | 0 | } /* end if */ |
584 | | /* Create new null message for end of chunk */ |
585 | 0 | else { |
586 | | /* Create a new null message */ |
587 | 0 | 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 | 0 | extend_msg = oh->nmesgs++; |
593 | | |
594 | | /* Initialize new null message */ |
595 | 0 | oh->mesg[extend_msg].type = H5O_MSG_NULL; |
596 | 0 | oh->mesg[extend_msg].native = NULL; |
597 | 0 | oh->mesg[extend_msg].raw = ((oh->chunk[chunkno].image + oh->chunk[chunkno].size) - |
598 | 0 | (H5O_SIZEOF_CHKSUM_OH(oh) + oh->chunk[chunkno].gap)) + |
599 | 0 | H5O_SIZEOF_MSGHDR_OH(oh); |
600 | 0 | oh->mesg[extend_msg].raw_size = (delta + oh->chunk[chunkno].gap) - (size_t)H5O_SIZEOF_MSGHDR_OH(oh); |
601 | 0 | oh->mesg[extend_msg].chunkno = chunkno; |
602 | 0 | } /* end else */ |
603 | | |
604 | | /* Mark the extended message as dirty */ |
605 | 0 | oh->mesg[extend_msg].dirty = true; |
606 | 0 | chk_dirtied = true; |
607 | | |
608 | | /* Allocate more memory space for chunk's image */ |
609 | 0 | old_image = oh->chunk[chunkno].image; |
610 | 0 | old_size = oh->chunk[chunkno].size; |
611 | 0 | oh->chunk[chunkno].size += delta + extra_prfx_size; |
612 | 0 | oh->chunk[chunkno].image = H5FL_BLK_REALLOC(chunk_image, old_image, oh->chunk[chunkno].size); |
613 | 0 | if (NULL == oh->chunk[chunkno].image) |
614 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTALLOC, FAIL, "can't reallocate extended object header chunk"); |
615 | 0 | oh->chunk[chunkno].gap = 0; |
616 | | |
617 | | /* Wipe new space for chunk */ |
618 | 0 | 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 | 0 | 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 | 0 | for (u = 0; u < oh->nmesgs; u++) { |
628 | | /* Adjust raw addresses for messages in this chunk to reflect new 'image' address */ |
629 | 0 | if (oh->mesg[u].chunkno == chunkno) |
630 | 0 | 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 | 0 | if (chunkno > 0 && (H5O_CONT_ID == oh->mesg[u].type->id) && |
636 | 0 | (((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 | 0 | } /* end for */ |
658 | | |
659 | | /* Resize the chunk in the cache */ |
660 | 0 | 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 | 0 | *msg_idx = extend_msg; |
665 | |
|
666 | 0 | done: |
667 | | /* Release chunk */ |
668 | 0 | 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 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
672 | 0 | } /* 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 | 0 | { |
701 | 0 | H5O_mesg_t *curr_msg; /* Pointer to current message to operate on */ |
702 | 0 | size_t cont_size; /* Continuation message size */ |
703 | 0 | size_t multi_size; /* Size of all the messages in the last chunk */ |
704 | 0 | unsigned u; /* Local index variable */ |
705 | |
|
706 | 0 | FUNC_ENTER_PACKAGE_NOERR |
707 | | |
708 | | /* Check args */ |
709 | 0 | assert(f); |
710 | 0 | assert(oh); |
711 | 0 | assert(size); |
712 | 0 | assert(*size > 0); |
713 | 0 | 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 | 0 | cont_size = H5O_ALIGN_OH(oh, (size_t)(H5F_SIZEOF_ADDR(f) + H5F_SIZEOF_SIZE(f))); |
726 | 0 | multi_size = 0; |
727 | 0 | for (u = 0, curr_msg = &oh->mesg[0]; u < oh->nmesgs; u++, curr_msg++) { |
728 | | /* Don't consider continuation messages (for now) */ |
729 | 0 | if (H5O_CONT_ID != curr_msg->type->id) { |
730 | 0 | unsigned msg_chunkno = curr_msg->chunkno; /* Chunk that the message is in */ |
731 | 0 | uint8_t *end_chunk_data = |
732 | 0 | (oh->chunk[msg_chunkno].image + oh->chunk[msg_chunkno].size) - |
733 | 0 | (H5O_SIZEOF_CHKSUM_OH(oh) + oh->chunk[msg_chunkno].gap); /* End of message data in chunk */ |
734 | 0 | uint8_t *end_msg = curr_msg->raw + curr_msg->raw_size; /* End of current message */ |
735 | 0 | size_t gap_size = 0; /* Size of gap after current message */ |
736 | 0 | size_t null_size = 0; /* Size of NULL message after current message */ |
737 | 0 | unsigned null_msgno = 0; /* Index of NULL message after current message */ |
738 | 0 | 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 | 0 | if (end_msg == end_chunk_data) |
742 | 0 | gap_size = oh->chunk[msg_chunkno].gap; |
743 | 0 | else { |
744 | 0 | H5O_mesg_t *tmp_msg; /* Temp. pointer to message to operate on */ |
745 | 0 | unsigned v; /* Local index variable */ |
746 | | |
747 | | /* Check for null message after this message, in same chunk */ |
748 | 0 | for (v = 0, tmp_msg = &oh->mesg[0]; v < oh->nmesgs; v++, tmp_msg++) { |
749 | 0 | if (tmp_msg->type->id == H5O_NULL_ID && |
750 | 0 | (tmp_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh)) == end_msg) { |
751 | 0 | null_msgno = v; |
752 | 0 | null_size = (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + tmp_msg->raw_size; |
753 | 0 | break; |
754 | 0 | } /* end if */ |
755 | | |
756 | | /* XXX: Should also check for NULL message in front of current message... */ |
757 | |
|
758 | 0 | } /* end for */ |
759 | 0 | } /* end else */ |
760 | | |
761 | | /* Add up current message's total available space */ |
762 | 0 | total_size = curr_msg->raw_size + gap_size + null_size; |
763 | | |
764 | | /* Check if message is large enough to hold continuation info */ |
765 | 0 | if (total_size >= cont_size) { |
766 | 0 | bool better = false; /* Whether the current message is better than a previous one */ |
767 | | |
768 | | /* Check for first message that can be moved */ |
769 | 0 | if (found_msg->msgno < 0) |
770 | 0 | better = true; |
771 | 0 | else { |
772 | | /* Prioritize moving non-attributes above attributes */ |
773 | | /* (Even attributes with an otherwise better fit */ |
774 | 0 | if (found_msg->id == H5O_ATTR_ID && curr_msg->type->id != H5O_ATTR_ID) |
775 | 0 | better = true; |
776 | | /* Either two attributes, or two non-attributes */ |
777 | 0 | else { |
778 | | /* Take a smaller one */ |
779 | 0 | if (total_size < found_msg->total_size) |
780 | 0 | 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 | 0 | else if (total_size == found_msg->total_size) { |
787 | 0 | if (msg_chunkno < found_msg->chunkno) |
788 | 0 | better = true; |
789 | 0 | } /* end else-if */ |
790 | 0 | } /* end else */ |
791 | 0 | } /* end else */ |
792 | | |
793 | | /* If we found a better message, keep its info */ |
794 | 0 | if (better) { |
795 | 0 | found_msg->msgno = (int)u; |
796 | 0 | found_msg->id = curr_msg->type->id; |
797 | 0 | found_msg->chunkno = msg_chunkno; |
798 | 0 | found_msg->gap_size = gap_size; |
799 | 0 | found_msg->null_size = null_size; |
800 | 0 | found_msg->total_size = total_size; |
801 | 0 | found_msg->null_msgno = null_msgno; |
802 | 0 | } /* end if */ |
803 | 0 | } /* end if */ |
804 | 0 | 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 | 0 | multi_size += curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh); |
809 | 0 | } /* end if */ |
810 | 0 | } /* 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 | 0 | if (found_msg->msgno < 0) |
823 | 0 | *size += multi_size; |
824 | 0 | else |
825 | 0 | *size += (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + oh->mesg[found_msg->msgno].raw_size; |
826 | |
|
827 | 0 | FUNC_LEAVE_NOAPI(SUCCEED) |
828 | 0 | } /* 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 | 0 | { |
848 | 0 | H5O_mesg_t *curr_msg; /* Pointer to current message to operate on */ |
849 | 0 | H5O_chunk_proxy_t *chk_proxy; /* Chunk that message is in */ |
850 | 0 | size_t cont_size; /*continuation message size */ |
851 | 0 | size_t idx; /* Message number */ |
852 | 0 | uint8_t *p = NULL; /* Pointer into new chunk image */ |
853 | 0 | H5O_cont_t *cont = NULL; /*native continuation message */ |
854 | 0 | unsigned chunkno; /* Chunk allocated */ |
855 | 0 | haddr_t new_chunk_addr; /* Address of new chunk in file */ |
856 | 0 | unsigned u; /* Local index variable */ |
857 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
858 | |
|
859 | 0 | FUNC_ENTER_PACKAGE |
860 | | |
861 | | /* check args */ |
862 | 0 | assert(f); |
863 | 0 | assert(oh); |
864 | 0 | assert(found_msg); |
865 | 0 | 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 | 0 | size = MAX(H5O_MIN_SIZE, size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh)); |
873 | 0 | 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 | 0 | size += H5O_SIZEOF_CHKHDR_OH(oh); |
881 | | |
882 | | /* Allocate space in file to hold the new chunk */ |
883 | 0 | new_chunk_addr = H5MF_alloc(f, H5FD_MEM_OHDR, (hsize_t)size); |
884 | 0 | 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 | 0 | if (oh->nchunks >= oh->alloc_nchunks) { |
889 | 0 | size_t na = MAX(H5O_NCHUNKS, oh->alloc_nchunks * 2); /* Double # of chunks allocated */ |
890 | 0 | H5O_chunk_t *x; |
891 | |
|
892 | 0 | 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 | 0 | oh->alloc_nchunks = na; |
895 | 0 | oh->chunk = x; |
896 | 0 | } /* end if */ |
897 | | |
898 | 0 | H5_CHECKED_ASSIGN(chunkno, unsigned, oh->nchunks, size_t); |
899 | 0 | oh->nchunks++; |
900 | 0 | oh->chunk[chunkno].addr = new_chunk_addr; |
901 | 0 | oh->chunk[chunkno].size = size; |
902 | 0 | oh->chunk[chunkno].gap = 0; |
903 | 0 | 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 | 0 | 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 | 0 | if (oh->version > H5O_VERSION_1) { |
911 | 0 | H5MM_memcpy(p, H5O_CHK_MAGIC, (size_t)H5_SIZEOF_MAGIC); |
912 | 0 | p += H5_SIZEOF_MAGIC; |
913 | 0 | } /* end if */ |
914 | | |
915 | | /* |
916 | | * Make sure we have enough space for all possible new messages |
917 | | * that could be generated below. |
918 | | */ |
919 | 0 | if (oh->nmesgs + 3 > oh->alloc_nmesgs) |
920 | 0 | 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 | 0 | cont_size = H5O_ALIGN_OH(oh, (size_t)(H5F_SIZEOF_ADDR(f) + H5F_SIZEOF_SIZE(f))); |
925 | 0 | if (found_null >= oh->nmesgs) { |
926 | 0 | 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 | 0 | H5O_mesg_t *null_msg; /* Pointer to new null message */ |
932 | | |
933 | | /* Protect last chunk */ |
934 | 0 | 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 | 0 | for (u = 0, curr_msg = &oh->mesg[0]; u < oh->nmesgs; u++, curr_msg++) |
939 | 0 | if (curr_msg->chunkno == chunkno - 1) { |
940 | 0 | if (curr_msg->type->id == H5O_NULL_ID) { |
941 | | /* Delete the null message. Defer actual deletion so we don't interfere with a higher |
942 | | * level of recursion by moving the messages. */ |
943 | 0 | curr_msg->type = H5O_MSG_DELETED; |
944 | 0 | oh->num_deleted_mesgs++; |
945 | 0 | } |
946 | 0 | else { |
947 | 0 | assert(curr_msg->type->id != H5O_CONT_ID); |
948 | |
|
949 | 0 | if (size < curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh)) |
950 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, FAIL, "invalid size"); |
951 | | |
952 | | /* Copy the raw data */ |
953 | 0 | H5MM_memcpy(p, curr_msg->raw - (size_t)H5O_SIZEOF_MSGHDR_OH(oh), |
954 | 0 | curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh)); |
955 | | |
956 | | /* Update the message info */ |
957 | 0 | curr_msg->chunkno = chunkno; |
958 | 0 | curr_msg->raw = p + H5O_SIZEOF_MSGHDR_OH(oh); |
959 | | |
960 | | /* Account for copied message in new chunk */ |
961 | 0 | p += (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + curr_msg->raw_size; |
962 | 0 | size -= (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + curr_msg->raw_size; |
963 | 0 | } /* end else */ |
964 | 0 | } /* 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 | 0 | else { |
991 | | /* Move message (that will be replaced with continuation message) |
992 | | * to new chunk, if necessary. |
993 | | */ |
994 | 0 | H5O_mesg_t *null_msg; /* Pointer to new null message */ |
995 | | |
996 | | /* Protect chunk */ |
997 | 0 | 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 | 0 | found_null = oh->nmesgs++; |
1002 | 0 | null_msg = &(oh->mesg[found_null]); |
1003 | 0 | null_msg->type = H5O_MSG_NULL; |
1004 | 0 | null_msg->native = NULL; |
1005 | 0 | null_msg->raw = oh->mesg[found_msg->msgno].raw; |
1006 | 0 | null_msg->raw_size = oh->mesg[found_msg->msgno].raw_size; |
1007 | 0 | null_msg->chunkno = oh->mesg[found_msg->msgno].chunkno; |
1008 | | |
1009 | | /* Copy the message to move (& its prefix) to its new location */ |
1010 | 0 | H5MM_memcpy(p, oh->mesg[found_msg->msgno].raw - H5O_SIZEOF_MSGHDR_OH(oh), |
1011 | 0 | 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 | 0 | oh->mesg[found_msg->msgno].raw = p + H5O_SIZEOF_MSGHDR_OH(oh); |
1015 | 0 | oh->mesg[found_msg->msgno].chunkno = chunkno; |
1016 | | |
1017 | | /* Account for copied message in new chunk */ |
1018 | 0 | p += (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + oh->mesg[found_msg->msgno].raw_size; |
1019 | 0 | 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 | 0 | 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 | 0 | else if (found_msg->null_size > 0) { |
1029 | 0 | H5O_mesg_t *old_null_msg = |
1030 | 0 | &oh->mesg[found_msg->null_msgno]; /* Pointer to NULL message to eliminate */ |
1031 | | |
1032 | | /* Absorb a null message after the moved message */ |
1033 | 0 | assert((null_msg->raw + null_msg->raw_size) == |
1034 | 0 | (old_null_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh))); |
1035 | 0 | null_msg->raw_size += found_msg->null_size; |
1036 | | |
1037 | | /* Release any information/memory for message */ |
1038 | 0 | H5O__msg_free_mesg(old_null_msg); |
1039 | | |
1040 | | /* Delete null message from list of messages. Defer actual deletion so we don't interfere with |
1041 | | * a higher level of recursion by moving the messages. */ |
1042 | 0 | old_null_msg->type = H5O_MSG_DELETED; |
1043 | 0 | oh->num_deleted_mesgs++; |
1044 | 0 | } /* end if */ |
1045 | | |
1046 | | /* Mark the new null message as dirty */ |
1047 | 0 | null_msg->dirty = true; |
1048 | | |
1049 | | /* Release chunk, marking it dirty */ |
1050 | 0 | if (H5O__chunk_unprotect(f, chk_proxy, true) < 0) |
1051 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect object header chunk"); |
1052 | 0 | } /* end if */ |
1053 | 0 | } /* end if */ |
1054 | | |
1055 | | /* Create null message for [rest of] space in new chunk */ |
1056 | | /* (account for chunk's magic # & checksum) */ |
1057 | 0 | idx = oh->nmesgs++; |
1058 | 0 | oh->mesg[idx].type = H5O_MSG_NULL; |
1059 | 0 | oh->mesg[idx].dirty = true; |
1060 | 0 | oh->mesg[idx].native = NULL; |
1061 | 0 | oh->mesg[idx].raw = p + H5O_SIZEOF_MSGHDR_OH(oh); |
1062 | 0 | oh->mesg[idx].raw_size = size - (size_t)(H5O_SIZEOF_CHKHDR_OH(oh) + H5O_SIZEOF_MSGHDR_OH(oh)); |
1063 | 0 | oh->mesg[idx].chunkno = chunkno; |
1064 | | |
1065 | | /* Insert the new chunk into the cache */ |
1066 | 0 | if (H5O__chunk_add(f, oh, chunkno, oh->mesg[found_null].chunkno) < 0) |
1067 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't add new chunk to cache"); |
1068 | | |
1069 | | /* Initialize the continuation information */ |
1070 | 0 | if (NULL == (cont = H5FL_MALLOC(H5O_cont_t))) |
1071 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
1072 | 0 | cont->addr = oh->chunk[chunkno].addr; |
1073 | 0 | cont->size = oh->chunk[chunkno].size; |
1074 | 0 | cont->chunkno = chunkno; |
1075 | | |
1076 | | /* Split the null message and point at continuation message */ |
1077 | 0 | if (H5O__alloc_null(f, oh, found_null, H5O_MSG_CONT, cont, cont_size) < 0) |
1078 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't split null message"); |
1079 | | |
1080 | | /* Set new message index value */ |
1081 | 0 | *new_idx = idx; |
1082 | |
|
1083 | 0 | done: |
1084 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1085 | 0 | } /* H5O__alloc_chunk() */ |
1086 | | |
1087 | | /*------------------------------------------------------------------------- |
1088 | | * Function: H5O__alloc_new_chunk |
1089 | | * |
1090 | | * Purpose: Allocates a new chunk for the object header, including |
1091 | | * file space. |
1092 | | * |
1093 | | * One of the other chunks will get an object continuation |
1094 | | * message. If there isn't room in any other chunk for the |
1095 | | * object continuation message, then some message from |
1096 | | * another chunk is moved into this chunk to make room. |
1097 | | * |
1098 | | * SIZE need not be aligned. |
1099 | | * |
1100 | | * Note: The algorithm for finding a message to replace with a |
1101 | | * continuation message is still fairly limited. It's possible |
1102 | | * that two (or more) messages smaller than a continuation message |
1103 | | * might occupy a chunk and need to be moved in order to make |
1104 | | * room for the continuation message. |
1105 | | * |
1106 | | * Also, we aren't checking for NULL messages in front of another |
1107 | | * message right now... |
1108 | | * |
1109 | | * Return: Success: Index number of the null message for the |
1110 | | * new chunk. The null message will be at |
1111 | | * least SIZE bytes not counting the message |
1112 | | * ID or size fields. |
1113 | | * |
1114 | | * Failure: Negative |
1115 | | * |
1116 | | *------------------------------------------------------------------------- |
1117 | | */ |
1118 | | static herr_t |
1119 | | H5O__alloc_new_chunk(H5F_t *f, H5O_t *oh, size_t size, size_t *new_idx) |
1120 | 0 | { |
1121 | 0 | size_t cont_size; /*continuation message size */ |
1122 | 0 | size_t idx; /* Message number */ |
1123 | 0 | H5O_msg_alloc_info_t found_msg; /* Best fit non-null message */ |
1124 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
1125 | |
|
1126 | 0 | FUNC_ENTER_PACKAGE |
1127 | | |
1128 | | /* check args */ |
1129 | 0 | assert(oh); |
1130 | 0 | assert(size > 0); |
1131 | 0 | size = H5O_ALIGN_OH(oh, size); |
1132 | | |
1133 | | /* Find the smallest null message that could hold a continuation message */ |
1134 | 0 | idx = oh->nmesgs; |
1135 | 0 | cont_size = H5O_ALIGN_OH(oh, (size_t)(H5F_SIZEOF_ADDR(f) + H5F_SIZEOF_SIZE(f))); |
1136 | 0 | if (H5O__alloc_find_best_null(oh, cont_size, &idx) < 0) |
1137 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_NOTFOUND, FAIL, "error while locating best null header message"); |
1138 | | |
1139 | | /* If we couldn't find a null message, locate the best message to move to new chunk */ |
1140 | 0 | if (idx >= oh->nmesgs) { |
1141 | 0 | found_msg.msgno = -1; |
1142 | 0 | if (H5O__alloc_find_best_nonnull(f, oh, &size, &found_msg) < 0) |
1143 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_NOTFOUND, FAIL, "error while locating best non-null header message"); |
1144 | 0 | } /* end if */ |
1145 | | |
1146 | | /* Allocate and initialize new chunk in the file */ |
1147 | 0 | if (H5O__alloc_chunk(f, oh, size, idx, &found_msg, new_idx) < 0) |
1148 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTALLOC, FAIL, "can't allocate new object header chunk"); |
1149 | | |
1150 | 0 | done: |
1151 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1152 | 0 | } /* H5O__alloc_new_chunk() */ |
1153 | | |
1154 | | /*------------------------------------------------------------------------- |
1155 | | * Function: H5O__alloc_find_best_null |
1156 | | * |
1157 | | * Purpose: Find the best fit null message for a given size of message |
1158 | | * to allocate. |
1159 | | * |
1160 | | * Return: Non-negative on success/Negative on failure |
1161 | | * |
1162 | | *------------------------------------------------------------------------- |
1163 | | */ |
1164 | | static herr_t |
1165 | | H5O__alloc_find_best_null(const H5O_t *oh, size_t size, size_t *mesg_idx) |
1166 | 0 | { |
1167 | 0 | size_t idx; /* Index of message which fits allocation */ |
1168 | 0 | ssize_t found_null; /* Best fit null message */ |
1169 | |
|
1170 | 0 | FUNC_ENTER_PACKAGE_NOERR |
1171 | | |
1172 | | /* check args */ |
1173 | 0 | assert(oh); |
1174 | 0 | assert(size > 0); |
1175 | 0 | assert(mesg_idx); |
1176 | | |
1177 | | /* Find the smallest null message that could hold the new object header message */ |
1178 | 0 | found_null = -1; |
1179 | 0 | for (idx = 0; idx < oh->nmesgs; idx++) { |
1180 | 0 | if (H5O_NULL_ID == oh->mesg[idx].type->id) { |
1181 | | /* If we found an exact fit, use it */ |
1182 | 0 | if (oh->mesg[idx].raw_size == size) { |
1183 | | /* Keep first exact fit */ |
1184 | 0 | if (found_null < 0) |
1185 | 0 | found_null = (ssize_t)idx; |
1186 | 0 | else |
1187 | | /* If we've got more than one exact fit, choose the one in the earliest chunk */ |
1188 | 0 | if (oh->mesg[idx].chunkno < oh->mesg[found_null].chunkno) { |
1189 | 0 | found_null = (ssize_t)idx; |
1190 | | |
1191 | | /* If we found an exact fit in object header chunk #0, we can get out */ |
1192 | | /* (Could extend this to look for earliest message in |
1193 | | * chunk #0 - QAK, 2016/10/21) |
1194 | | */ |
1195 | 0 | if (0 == oh->mesg[idx].chunkno) |
1196 | 0 | break; |
1197 | 0 | } /* end if */ |
1198 | 0 | } /* end if */ |
1199 | | /* Look for null message that's larger than needed */ |
1200 | 0 | else if (oh->mesg[idx].raw_size > size) { |
1201 | | /* Keep first one found */ |
1202 | 0 | if (found_null < 0) |
1203 | 0 | found_null = (ssize_t)idx; |
1204 | | /* Check for better fit */ |
1205 | 0 | else if (oh->mesg[idx].raw_size < oh->mesg[found_null].raw_size) { |
1206 | 0 | found_null = (ssize_t)idx; |
1207 | 0 | } |
1208 | 0 | else { |
1209 | | /* If they are the same size, choose the one in the earliest chunk */ |
1210 | 0 | if (oh->mesg[idx].raw_size == oh->mesg[found_null].raw_size) { |
1211 | 0 | if (oh->mesg[idx].chunkno < oh->mesg[found_null].chunkno) |
1212 | 0 | found_null = (ssize_t)idx; |
1213 | 0 | } /* end if */ |
1214 | 0 | } /* end else */ |
1215 | 0 | } /* end else-if */ |
1216 | | /* else: Ignore too-small null messages */ |
1217 | 0 | } /* end if */ |
1218 | 0 | } /* end for */ |
1219 | 0 | if (found_null >= 0) |
1220 | 0 | *mesg_idx = (size_t)found_null; |
1221 | |
|
1222 | 0 | FUNC_LEAVE_NOAPI(SUCCEED) |
1223 | 0 | } /* H5O__alloc_find_best_null() */ |
1224 | | |
1225 | | /*------------------------------------------------------------------------- |
1226 | | * Function: H5O__alloc |
1227 | | * |
1228 | | * Purpose: Allocate enough space in the object header for this message. |
1229 | | * |
1230 | | * Return: Non-negative on success/Negative on failure |
1231 | | * |
1232 | | *------------------------------------------------------------------------- |
1233 | | */ |
1234 | | herr_t |
1235 | | H5O__alloc(H5F_t *f, H5O_t *oh, const H5O_msg_class_t *type, const void *mesg, size_t *mesg_idx) |
1236 | 0 | { |
1237 | 0 | size_t raw_size; /* Raw size of message */ |
1238 | 0 | size_t aligned_size; /* Size of message including alignment */ |
1239 | 0 | size_t idx; /* Index of message which fits allocation */ |
1240 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
1241 | |
|
1242 | 0 | FUNC_ENTER_PACKAGE |
1243 | | |
1244 | | /* check args */ |
1245 | 0 | assert(oh); |
1246 | 0 | assert(type); |
1247 | 0 | assert(mesg); |
1248 | 0 | assert(mesg_idx); |
1249 | | |
1250 | | /* Compute the size needed to store the message in the object header */ |
1251 | 0 | raw_size = (type->raw_size)(f, false, mesg); |
1252 | 0 | if (0 == raw_size) |
1253 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "can't compute object header message size"); |
1254 | 0 | if (raw_size >= H5O_MESG_MAX_SIZE) |
1255 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "object header message is too large"); |
1256 | 0 | aligned_size = H5O_ALIGN_OH(oh, raw_size); |
1257 | | |
1258 | | /* Find the smallest null message that could hold the new object header message */ |
1259 | 0 | idx = oh->nmesgs; |
1260 | 0 | if (H5O__alloc_find_best_null(oh, aligned_size, &idx) < 0) |
1261 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_NOTFOUND, FAIL, "error while locating best null header message"); |
1262 | | |
1263 | | /* if we didn't find one, then allocate more header space */ |
1264 | 0 | if (idx >= oh->nmesgs) { |
1265 | 0 | unsigned chunkno; |
1266 | | |
1267 | | /* check to see if we can extend one of the chunks. If we can, |
1268 | | * do so. Otherwise, we will have to allocate a new chunk. |
1269 | | * |
1270 | | * Note that in this new version of this function, all chunks |
1271 | | * must have file space allocated to them. |
1272 | | */ |
1273 | 0 | for (chunkno = 0; chunkno < oh->nchunks; chunkno++) { |
1274 | 0 | htri_t tri_result; /* Status from attempting to extend chunk */ |
1275 | |
|
1276 | 0 | if ((tri_result = H5O__alloc_extend_chunk(f, oh, chunkno, raw_size, &idx)) < 0) |
1277 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTEXTEND, FAIL, "can't extend existing chunk"); |
1278 | 0 | if (tri_result == true) |
1279 | 0 | break; |
1280 | 0 | } /* end for */ |
1281 | | |
1282 | | /* If we were not able to extend a chunk, create a new one */ |
1283 | 0 | if (idx >= oh->nmesgs) |
1284 | 0 | if (H5O__alloc_new_chunk(f, oh, raw_size, &idx) < 0) |
1285 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_NOSPACE, FAIL, "unable to create a new object header data chunk"); |
1286 | 0 | } /* end if */ |
1287 | 0 | assert(idx < oh->nmesgs); |
1288 | | |
1289 | | /* Split the null message and point at continuation message */ |
1290 | 0 | if (H5O__alloc_null(f, oh, idx, type, NULL, aligned_size) < 0) |
1291 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't split null message"); |
1292 | | |
1293 | | /* Mark object header as dirty in cache */ |
1294 | 0 | if (H5AC_mark_entry_dirty(oh) < 0) |
1295 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTMARKDIRTY, FAIL, "unable to mark object header as dirty"); |
1296 | | |
1297 | | /* Set message index value */ |
1298 | 0 | *mesg_idx = idx; |
1299 | |
|
1300 | 0 | done: |
1301 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1302 | 0 | } /* H5O__alloc() */ |
1303 | | |
1304 | | /*------------------------------------------------------------------------- |
1305 | | * |
1306 | | * Function: H5O__release_mesg |
1307 | | * |
1308 | | * Purpose: Convert a message into a null message |
1309 | | * |
1310 | | * Return: Non-negative on success/Negative on failure |
1311 | | * |
1312 | | *------------------------------------------------------------------------- |
1313 | | */ |
1314 | | herr_t |
1315 | | H5O__release_mesg(H5F_t *f, H5O_t *oh, H5O_mesg_t *mesg, bool adj_link) |
1316 | 0 | { |
1317 | 0 | H5O_chunk_proxy_t *chk_proxy = NULL; /* Chunk that message is in */ |
1318 | 0 | bool chk_dirtied = false; /* Flag for unprotecting chunk */ |
1319 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
1320 | |
|
1321 | 0 | FUNC_ENTER_PACKAGE |
1322 | | |
1323 | | /* check args */ |
1324 | 0 | assert(f); |
1325 | 0 | assert(oh); |
1326 | 0 | assert(mesg); |
1327 | | |
1328 | | /* Check if we should operate on the message */ |
1329 | 0 | if (adj_link) |
1330 | | /* Free any space referred to in the file from this message */ |
1331 | 0 | if (H5O__delete_mesg(f, oh, mesg) < 0) |
1332 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, |
1333 | 0 | "unable to delete file space for object header message"); |
1334 | | |
1335 | | /* Protect chunk */ |
1336 | 0 | if (NULL == (chk_proxy = H5O__chunk_protect(f, oh, mesg->chunkno))) |
1337 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to protect object header chunk"); |
1338 | | |
1339 | | /* Free any native information */ |
1340 | 0 | H5O__msg_free_mesg(mesg); |
1341 | | |
1342 | | /* Change message type to nil and zero it */ |
1343 | 0 | mesg->type = H5O_MSG_NULL; |
1344 | 0 | assert(mesg->raw + mesg->raw_size <= (oh->chunk[mesg->chunkno].image + oh->chunk[mesg->chunkno].size) - |
1345 | 0 | (H5O_SIZEOF_CHKSUM_OH(oh) + oh->chunk[mesg->chunkno].gap)); |
1346 | 0 | memset(mesg->raw, 0, mesg->raw_size); |
1347 | | |
1348 | | /* Clear message flags */ |
1349 | 0 | mesg->flags = 0; |
1350 | | |
1351 | | /* Mark the message as modified */ |
1352 | 0 | mesg->dirty = true; |
1353 | 0 | chk_dirtied = true; |
1354 | | |
1355 | | /* Check if chunk has a gap currently */ |
1356 | 0 | if (oh->chunk[mesg->chunkno].gap) { |
1357 | | /* Eliminate the gap in the chunk */ |
1358 | 0 | if (H5O__eliminate_gap(oh, &chk_dirtied, mesg, |
1359 | 0 | ((oh->chunk[mesg->chunkno].image + oh->chunk[mesg->chunkno].size) - |
1360 | 0 | (H5O_SIZEOF_CHKSUM_OH(oh) + oh->chunk[mesg->chunkno].gap)), |
1361 | 0 | oh->chunk[mesg->chunkno].gap) < 0) |
1362 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTREMOVE, FAIL, "can't eliminate gap in chunk"); |
1363 | 0 | } /* end if */ |
1364 | | |
1365 | 0 | done: |
1366 | | /* Release chunk, if not already done */ |
1367 | 0 | if (chk_proxy && H5O__chunk_unprotect(f, chk_proxy, chk_dirtied) < 0) |
1368 | 0 | HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect object header chunk"); |
1369 | |
|
1370 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1371 | 0 | } /* H5O__release_mesg() */ |
1372 | | |
1373 | | /*------------------------------------------------------------------------- |
1374 | | * Function: H5O__move_cont |
1375 | | * |
1376 | | * Purpose: Check and move message(s) forward into a continuation message |
1377 | | * |
1378 | | * Return: Success: non-negative (true/false) |
1379 | | * Failure: negative |
1380 | | * |
1381 | | *------------------------------------------------------------------------- |
1382 | | */ |
1383 | | static htri_t |
1384 | | H5O__move_cont(H5F_t *f, H5O_t *oh, unsigned cont_u) |
1385 | 0 | { |
1386 | 0 | H5O_chunk_proxy_t *chk_proxy = NULL; /* Chunk that continuation message is in */ |
1387 | 0 | H5O_mesg_t *cont_msg; /* Pointer to the continuation message */ |
1388 | 0 | unsigned deleted_chunkno; /* Chunk # to delete */ |
1389 | 0 | bool chk_dirtied = false; /* Flags for unprotecting chunk */ |
1390 | 0 | htri_t ret_value = true; /* Return value */ |
1391 | |
|
1392 | 0 | FUNC_ENTER_PACKAGE |
1393 | | |
1394 | | /* Check arguments. */ |
1395 | 0 | assert(f); |
1396 | 0 | assert(oh); |
1397 | | |
1398 | | /* Get initial information */ |
1399 | 0 | cont_msg = &oh->mesg[cont_u]; |
1400 | 0 | H5O_LOAD_NATIVE(f, 0, oh, cont_msg, FAIL) |
1401 | 0 | deleted_chunkno = ((H5O_cont_t *)(cont_msg->native))->chunkno; |
1402 | | |
1403 | | /* Check if continuation message is pointing to the last chunk */ |
1404 | 0 | if (deleted_chunkno == (oh->nchunks - 1)) { |
1405 | 0 | size_t nonnull_size; /* Total size of nonnull messages in the chunk pointed to by cont message */ |
1406 | 0 | H5O_mesg_t *curr_msg; /* Pointer to the current message to operate on */ |
1407 | 0 | size_t gap_size; /* Size of gap produced */ |
1408 | 0 | size_t v; /* Local index variable */ |
1409 | | |
1410 | | /* Spin through messages */ |
1411 | 0 | nonnull_size = 0; |
1412 | 0 | for (v = 0, curr_msg = &oh->mesg[0]; v < oh->nmesgs; v++, curr_msg++) { |
1413 | 0 | if (curr_msg->chunkno == deleted_chunkno) { |
1414 | | /* Find size of all non-null messages in the chunk pointed to by the continuation message */ |
1415 | 0 | if (curr_msg->type->id != H5O_NULL_ID) { |
1416 | 0 | assert(curr_msg->type->id != H5O_CONT_ID); |
1417 | 0 | nonnull_size += curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh); |
1418 | 0 | } /* end if */ |
1419 | 0 | } /* end if */ |
1420 | 0 | } /* end for */ |
1421 | | |
1422 | | /* Size of gap in chunk w/continuation message */ |
1423 | 0 | gap_size = oh->chunk[cont_msg->chunkno].gap; |
1424 | | |
1425 | | /* Check if messages can fit into the continuation message + gap size */ |
1426 | | /* (Could count any null messages in the chunk w/the continuation |
1427 | | * message also, but that is pretty complex. -QAK) |
1428 | | */ |
1429 | 0 | if (nonnull_size && |
1430 | 0 | nonnull_size <= (gap_size + cont_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh))) { |
1431 | 0 | uint8_t *move_start, *move_end; /* Pointers to area of messages to move */ |
1432 | 0 | unsigned cont_chunkno; /* Chunk number for continuation message */ |
1433 | | |
1434 | | /* Get continuation info */ |
1435 | 0 | move_start = cont_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh); |
1436 | 0 | move_end = cont_msg->raw + cont_msg->raw_size; |
1437 | 0 | cont_chunkno = cont_msg->chunkno; |
1438 | | |
1439 | | /* Convert continuation message into a null message. Do not delete |
1440 | | * the target chunk yet, so we can still copy messages from it. */ |
1441 | 0 | if (H5O__release_mesg(f, oh, cont_msg, false) < 0) |
1442 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, "unable to convert into null message"); |
1443 | | |
1444 | | /* Protect chunk */ |
1445 | 0 | if (NULL == (chk_proxy = H5O__chunk_protect(f, oh, cont_chunkno))) |
1446 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to protect object header chunk"); |
1447 | | |
1448 | | /* Move message(s) forward into continuation message */ |
1449 | 0 | for (v = 0, curr_msg = &oh->mesg[0]; v < oh->nmesgs; v++, curr_msg++) |
1450 | | /* Look for messages in chunk to delete */ |
1451 | 0 | if (curr_msg->chunkno == deleted_chunkno) { |
1452 | | /* Move messages out of chunk to delete */ |
1453 | 0 | if (curr_msg->type->id != H5O_NULL_ID) { |
1454 | 0 | size_t move_size; /* Size of the message to be moved */ |
1455 | | |
1456 | | /* Compute size of message to move */ |
1457 | 0 | move_size = curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh); |
1458 | | |
1459 | | /* Move message out of deleted chunk */ |
1460 | 0 | H5MM_memcpy(move_start, curr_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh), move_size); |
1461 | 0 | curr_msg->raw = move_start + H5O_SIZEOF_MSGHDR_OH(oh); |
1462 | 0 | curr_msg->chunkno = cont_chunkno; |
1463 | 0 | chk_dirtied = true; |
1464 | | |
1465 | | /* Adjust location to move messages to */ |
1466 | 0 | move_start += move_size; |
1467 | 0 | } /* end else */ |
1468 | 0 | } /* end if */ |
1469 | | |
1470 | | /* Delete the target chunk */ |
1471 | 0 | if (H5O__chunk_delete(f, oh, deleted_chunkno) < 0) |
1472 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, "unable to remove chunk from cache"); |
1473 | | |
1474 | 0 | assert(move_start <= (move_end + gap_size)); |
1475 | | |
1476 | | /* Check if there is space remaining in the continuation message */ |
1477 | | /* (The remaining space can be gap or a null message) */ |
1478 | 0 | gap_size += (size_t)(move_end - move_start); |
1479 | 0 | if (gap_size >= (size_t)H5O_SIZEOF_MSGHDR_OH(oh)) { |
1480 | | /* Adjust size of null (was continuation) message */ |
1481 | 0 | cont_msg->raw_size = gap_size - (size_t)H5O_SIZEOF_MSGHDR_OH(oh); |
1482 | 0 | cont_msg->raw = move_start + H5O_SIZEOF_MSGHDR_OH(oh); |
1483 | 0 | cont_msg->dirty = true; |
1484 | 0 | chk_dirtied = true; |
1485 | 0 | } /* end if */ |
1486 | 0 | else { |
1487 | | /* Check if there is space that should be a gap */ |
1488 | 0 | if (gap_size > 0) { |
1489 | | /* Convert remnant into gap in chunk */ |
1490 | 0 | if (H5O__add_gap(f, oh, cont_chunkno, &chk_dirtied, cont_u, move_start, gap_size) < 0) |
1491 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't insert gap in chunk"); |
1492 | 0 | } /* end if */ |
1493 | | |
1494 | | /* Release any information/memory for continuation message */ |
1495 | 0 | H5O__msg_free_mesg(cont_msg); |
1496 | 0 | if (cont_u < (oh->nmesgs - 1)) |
1497 | 0 | memmove(&oh->mesg[cont_u], &oh->mesg[cont_u + 1], |
1498 | 0 | ((oh->nmesgs - 1) - cont_u) * sizeof(H5O_mesg_t)); |
1499 | 0 | oh->nmesgs--; |
1500 | 0 | } /* end else */ |
1501 | | |
1502 | | /* Move message(s) forward into continuation message */ |
1503 | | /* Note: unsigned v wrapping around at the end */ |
1504 | 0 | for (v = oh->nmesgs - 1, curr_msg = &oh->mesg[v]; v < oh->nmesgs; v--, curr_msg--) |
1505 | | /* Look for messages in chunk to delete */ |
1506 | 0 | if (curr_msg->chunkno == deleted_chunkno) { |
1507 | | /* Remove all null messages in deleted chunk from list of messages */ |
1508 | 0 | if (curr_msg->type->id == H5O_NULL_ID) { |
1509 | | /* Release any information/memory for message */ |
1510 | 0 | H5O__msg_free_mesg(curr_msg); |
1511 | 0 | chk_dirtied = true; |
1512 | | |
1513 | | /* Remove from message list */ |
1514 | 0 | if (v < (oh->nmesgs - 1)) |
1515 | 0 | memmove(&oh->mesg[v], &oh->mesg[v + 1], |
1516 | 0 | ((oh->nmesgs - 1) - v) * sizeof(H5O_mesg_t)); |
1517 | 0 | oh->nmesgs--; |
1518 | 0 | } /* end if */ |
1519 | 0 | } /* end if */ |
1520 | | |
1521 | | /* Remove chunk from list of chunks */ |
1522 | 0 | oh->chunk[deleted_chunkno].image = H5FL_BLK_FREE(chunk_image, oh->chunk[deleted_chunkno].image); |
1523 | 0 | oh->nchunks--; |
1524 | 0 | } /* end if */ |
1525 | 0 | else |
1526 | 0 | ret_value = false; |
1527 | 0 | } /* end if */ |
1528 | 0 | else |
1529 | 0 | ret_value = false; |
1530 | | |
1531 | 0 | done: |
1532 | | /* Release chunk, if not already done */ |
1533 | 0 | if (chk_proxy && H5O__chunk_unprotect(f, chk_proxy, chk_dirtied) < 0) |
1534 | 0 | HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect object header chunk"); |
1535 | |
|
1536 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1537 | 0 | } /* H5O__move_cont() */ |
1538 | | |
1539 | | /*------------------------------------------------------------------------- |
1540 | | * |
1541 | | * Function: H5O__move_msgs_forward |
1542 | | * |
1543 | | * Purpose: Move messages toward first chunk |
1544 | | * |
1545 | | * Return: Non-negative on success/Negative on failure |
1546 | | * |
1547 | | *------------------------------------------------------------------------- |
1548 | | */ |
1549 | | static htri_t |
1550 | | H5O__move_msgs_forward(H5F_t *f, H5O_t *oh) |
1551 | 0 | { |
1552 | 0 | H5O_chunk_proxy_t *null_chk_proxy = NULL; /* Chunk that null message is in */ |
1553 | 0 | H5O_chunk_proxy_t *curr_chk_proxy = NULL; /* Chunk that message is in */ |
1554 | 0 | H5O_chunk_proxy_t *cont_targ_chk_proxy = NULL; /* Chunk that continuation message points to */ |
1555 | 0 | bool null_chk_dirtied = false; /* Flags for unprotecting null chunk */ |
1556 | 0 | bool curr_chk_dirtied = false; /* Flags for unprotecting curr chunk */ |
1557 | 0 | bool packed_msg; /* Flag to indicate that messages were packed */ |
1558 | 0 | bool did_packing = false; /* Whether any messages were packed */ |
1559 | 0 | htri_t ret_value = FAIL; /* Return value */ |
1560 | |
|
1561 | 0 | FUNC_ENTER_PACKAGE |
1562 | | |
1563 | | /* check args */ |
1564 | 0 | assert(oh); |
1565 | | |
1566 | | /* Loop until no messages packed */ |
1567 | | /* (Double loop is not very efficient, but it would be some extra work to |
1568 | | * add a list of messages to each chunk -QAK) |
1569 | | */ |
1570 | 0 | do { |
1571 | 0 | H5O_mesg_t *curr_msg; /* Pointer to current message to operate on */ |
1572 | 0 | unsigned u; /* Local index variable */ |
1573 | | |
1574 | | /* Reset packed messages flag */ |
1575 | 0 | packed_msg = false; |
1576 | | |
1577 | | /* Scan through messages for messages that can be moved earlier in chunks */ |
1578 | 0 | for (u = 0, curr_msg = &oh->mesg[0]; u < oh->nmesgs; u++, curr_msg++) { |
1579 | 0 | if (H5O_NULL_ID == curr_msg->type->id) { |
1580 | 0 | H5O_chunk_t *chunk; /* Pointer to chunk that null message is in */ |
1581 | | |
1582 | | /* Check if null message is not last in chunk */ |
1583 | 0 | chunk = &(oh->chunk[curr_msg->chunkno]); |
1584 | 0 | if ((curr_msg->raw + curr_msg->raw_size) != |
1585 | 0 | ((chunk->image + chunk->size) - (H5O_SIZEOF_CHKSUM_OH(oh) + chunk->gap))) { |
1586 | 0 | H5O_mesg_t *nonnull_msg; /* Pointer to current message to operate on */ |
1587 | 0 | unsigned v; /* Local index variable */ |
1588 | | |
1589 | | /* Loop over messages again, looking for the message in the chunk after the null message |
1590 | | */ |
1591 | 0 | for (v = 0, nonnull_msg = &oh->mesg[0]; v < oh->nmesgs; v++, nonnull_msg++) { |
1592 | | /* Locate message that is immediately after the null message */ |
1593 | 0 | if ((curr_msg->chunkno == nonnull_msg->chunkno) && |
1594 | 0 | ((curr_msg->raw + curr_msg->raw_size) == |
1595 | 0 | (nonnull_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh)))) { |
1596 | | /* Don't swap messages if the second message is also a null message */ |
1597 | | /* (We'll merge them together later, in another routine) */ |
1598 | 0 | if (H5O_NULL_ID != nonnull_msg->type->id) { |
1599 | | /* Protect chunk */ |
1600 | 0 | if (NULL == (null_chk_proxy = H5O__chunk_protect(f, oh, curr_msg->chunkno))) |
1601 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, |
1602 | 0 | "unable to load object header chunk"); |
1603 | | |
1604 | | /* Copy raw data for non-null message to new location */ |
1605 | 0 | memmove(curr_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh), |
1606 | 0 | nonnull_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh), |
1607 | 0 | nonnull_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh)); |
1608 | | |
1609 | | /* Adjust non-null message's offset in chunk */ |
1610 | 0 | nonnull_msg->raw = curr_msg->raw; |
1611 | | |
1612 | | /* Adjust null message's offset in chunk */ |
1613 | 0 | curr_msg->raw = |
1614 | 0 | nonnull_msg->raw + nonnull_msg->raw_size + H5O_SIZEOF_MSGHDR_OH(oh); |
1615 | | |
1616 | | /* Mark null message dirty */ |
1617 | | /* (since we need to re-encode its message header) */ |
1618 | 0 | curr_msg->dirty = true; |
1619 | | |
1620 | | /* Release chunk, marking it dirty */ |
1621 | 0 | if (H5O__chunk_unprotect(f, null_chk_proxy, true) < 0) |
1622 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, |
1623 | 0 | "unable to unprotect object header chunk"); |
1624 | 0 | null_chk_proxy = NULL; |
1625 | | |
1626 | | /* Set the flag to indicate that the null message |
1627 | | * was packed - if its not at the end its chunk, |
1628 | | * we'll move it again on the next pass. |
1629 | | */ |
1630 | 0 | packed_msg = true; |
1631 | 0 | } /* end if */ |
1632 | | |
1633 | | /* Break out of loop */ |
1634 | 0 | break; |
1635 | 0 | } /* end if */ |
1636 | 0 | } /* end for */ |
1637 | | /* Should have been message after null message */ |
1638 | 0 | assert(v < oh->nmesgs); |
1639 | 0 | } /* end if */ |
1640 | 0 | } /* end if */ |
1641 | 0 | else { |
1642 | 0 | H5O_mesg_t *null_msg; /* Pointer to current message to operate on */ |
1643 | 0 | size_t v; /* Local index variable */ |
1644 | | |
1645 | | /* Check if messages in chunk pointed to can replace continuation message */ |
1646 | 0 | if (H5O_CONT_ID == curr_msg->type->id) { |
1647 | 0 | htri_t status; /* Status from moving messages */ |
1648 | |
|
1649 | 0 | if ((status = H5O__move_cont(f, oh, u)) < 0) |
1650 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, |
1651 | 0 | "Error in moving messages into cont message"); |
1652 | 0 | else if (status > 0) { /* Message(s) got moved into "continuation" message */ |
1653 | 0 | packed_msg = true; |
1654 | 0 | break; |
1655 | 0 | } /* end else-if */ |
1656 | 0 | } /* end if */ |
1657 | | |
1658 | | /* Loop over messages again, looking for large enough null message in earlier chunk */ |
1659 | 0 | for (v = 0, null_msg = &oh->mesg[0]; v < oh->nmesgs; v++, null_msg++) { |
1660 | 0 | if (H5O_NULL_ID == null_msg->type->id && curr_msg->chunkno > null_msg->chunkno && |
1661 | 0 | curr_msg->raw_size <= null_msg->raw_size) { |
1662 | 0 | unsigned old_chunkno; /* Old message information */ |
1663 | 0 | uint8_t *old_raw; |
1664 | | |
1665 | | /* Keep old information about non-null message */ |
1666 | 0 | old_chunkno = curr_msg->chunkno; |
1667 | 0 | old_raw = curr_msg->raw; |
1668 | | |
1669 | | /* Protect chunks */ |
1670 | 0 | if (NULL == (null_chk_proxy = H5O__chunk_protect(f, oh, null_msg->chunkno))) |
1671 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, |
1672 | 0 | "unable to load object header chunk"); |
1673 | 0 | if (NULL == (curr_chk_proxy = H5O__chunk_protect(f, oh, curr_msg->chunkno))) |
1674 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, |
1675 | 0 | "unable to load object header chunk"); |
1676 | | |
1677 | | /* If the message being moved is a continuation |
1678 | | * message and we are doing SWMR writes, we must |
1679 | | * update the flush dependencies */ |
1680 | 0 | if (oh->swmr_write && (H5O_CONT_ID == curr_msg->type->id)) { |
1681 | 0 | void *null_chk_mdc_obj; /* The metadata cache object for the null_msg chunk */ |
1682 | | |
1683 | | /* Point to the metadata cache object for the |
1684 | | * null message chunk, oh if in chunk 0 or the |
1685 | | * proxy otherwise */ |
1686 | 0 | null_chk_mdc_obj = (null_msg->chunkno == 0 ? (void *)oh : (void *)null_chk_proxy); |
1687 | | |
1688 | | /* The other chunks involved should never be |
1689 | | * chunk 0 */ |
1690 | 0 | assert(curr_msg->chunkno > 0); |
1691 | 0 | assert(((H5O_cont_t *)(curr_msg->native))->chunkno > 0); |
1692 | | |
1693 | | /* Protect continuation message target chunk */ |
1694 | 0 | if (NULL == (cont_targ_chk_proxy = H5O__chunk_protect( |
1695 | 0 | f, oh, ((H5O_cont_t *)(curr_msg->native))->chunkno))) |
1696 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, |
1697 | 0 | "unable to load object header chunk"); |
1698 | | |
1699 | | /* Check for flush dependency on previous continuation chunk */ |
1700 | | /* (As opposed to chunk 0) */ |
1701 | 0 | if (cont_targ_chk_proxy->fd_parent) { |
1702 | | /* Remove flush dependency on old continuation |
1703 | | * message chunk */ |
1704 | 0 | assert(cont_targ_chk_proxy); |
1705 | 0 | assert(curr_chk_proxy); |
1706 | 0 | assert((void *)curr_chk_proxy == cont_targ_chk_proxy->fd_parent); |
1707 | |
|
1708 | 0 | if (H5AC_destroy_flush_dependency(curr_chk_proxy, cont_targ_chk_proxy) < 0) |
1709 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTUNDEPEND, FAIL, |
1710 | 0 | "unable to destroy flush dependency"); |
1711 | | |
1712 | 0 | cont_targ_chk_proxy->fd_parent = NULL; |
1713 | 0 | } /* end if */ |
1714 | | |
1715 | | /* Avoid (another) flush dependency on chunk 0 */ |
1716 | 0 | if (0 != null_msg->chunkno) { |
1717 | | /* Sanity checks */ |
1718 | 0 | assert(null_chk_mdc_obj); |
1719 | 0 | assert(((H5C_cache_entry_t *)null_chk_mdc_obj)->type); |
1720 | 0 | assert(((H5C_cache_entry_t *)null_chk_mdc_obj)->type->id == H5AC_OHDR_CHK_ID); |
1721 | | |
1722 | | /* Create flush dependency on new continuation |
1723 | | * message chunk */ |
1724 | 0 | if (H5AC_create_flush_dependency(null_chk_mdc_obj, cont_targ_chk_proxy) < 0) |
1725 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTDEPEND, FAIL, |
1726 | 0 | "unable to create flush dependency"); |
1727 | | |
1728 | 0 | cont_targ_chk_proxy->fd_parent = null_chk_mdc_obj; |
1729 | 0 | } /* end if */ |
1730 | | |
1731 | | /* Unprotect continuation message target chunk */ |
1732 | 0 | if (H5O__chunk_unprotect(f, cont_targ_chk_proxy, false) < 0) |
1733 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, |
1734 | 0 | "unable to unprotect object header chunk"); |
1735 | 0 | cont_targ_chk_proxy = NULL; |
1736 | 0 | } /* end if */ |
1737 | | |
1738 | | /* Copy raw data for non-null message to new chunk */ |
1739 | 0 | H5MM_memcpy(null_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh), |
1740 | 0 | curr_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh), |
1741 | 0 | curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh)); |
1742 | | |
1743 | | /* Point non-null message at null message's space */ |
1744 | 0 | curr_msg->chunkno = null_msg->chunkno; |
1745 | 0 | curr_msg->raw = null_msg->raw; |
1746 | 0 | curr_chk_dirtied = true; |
1747 | | |
1748 | | /* Change information for null message */ |
1749 | 0 | if (curr_msg->raw_size == null_msg->raw_size) { |
1750 | | /* Point null message at old non-null space */ |
1751 | | /* (Instead of freeing it and allocating new message) */ |
1752 | 0 | null_msg->chunkno = old_chunkno; |
1753 | 0 | null_msg->raw = old_raw; |
1754 | | |
1755 | | /* Mark null message dirty */ |
1756 | 0 | null_msg->dirty = true; |
1757 | 0 | null_chk_dirtied = true; |
1758 | | |
1759 | | /* Release current chunk, marking it dirty */ |
1760 | 0 | if (H5O__chunk_unprotect(f, curr_chk_proxy, curr_chk_dirtied) < 0) |
1761 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, |
1762 | 0 | "unable to unprotect object header chunk"); |
1763 | 0 | curr_chk_proxy = NULL; |
1764 | 0 | curr_chk_dirtied = false; |
1765 | | |
1766 | | /* Check for gap in null message's chunk */ |
1767 | 0 | if (oh->chunk[old_chunkno].gap > 0) { |
1768 | | /* Eliminate the gap in the chunk */ |
1769 | 0 | if (H5O__eliminate_gap( |
1770 | 0 | oh, &null_chk_dirtied, null_msg, |
1771 | 0 | ((oh->chunk[old_chunkno].image + oh->chunk[old_chunkno].size) - |
1772 | 0 | (H5O_SIZEOF_CHKSUM_OH(oh) + oh->chunk[old_chunkno].gap)), |
1773 | 0 | oh->chunk[old_chunkno].gap) < 0) |
1774 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTREMOVE, FAIL, |
1775 | 0 | "can't eliminate gap in chunk"); |
1776 | 0 | } /* end if */ |
1777 | | |
1778 | | /* Release null chunk, marking it dirty */ |
1779 | 0 | if (H5O__chunk_unprotect(f, null_chk_proxy, null_chk_dirtied) < 0) |
1780 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, |
1781 | 0 | "unable to unprotect object header chunk"); |
1782 | 0 | null_chk_proxy = NULL; |
1783 | 0 | null_chk_dirtied = false; |
1784 | 0 | } /* end if */ |
1785 | 0 | else { |
1786 | 0 | size_t new_null_msg; /* Message index for new null message */ |
1787 | | |
1788 | | /* Check if null message is large enough to still exist */ |
1789 | 0 | if ((null_msg->raw_size - curr_msg->raw_size) < |
1790 | 0 | (size_t)H5O_SIZEOF_MSGHDR_OH(oh)) { |
1791 | 0 | size_t gap_size = |
1792 | 0 | null_msg->raw_size - curr_msg->raw_size; /* Size of gap produced */ |
1793 | | |
1794 | | /* Adjust the size of the null message being eliminated */ |
1795 | 0 | null_msg->raw_size = curr_msg->raw_size; |
1796 | | |
1797 | | /* Mark null message dirty */ |
1798 | 0 | null_msg->dirty = true; |
1799 | 0 | null_chk_dirtied = true; |
1800 | | |
1801 | | /* Add the gap to the chunk */ |
1802 | 0 | if (H5O__add_gap(f, oh, null_msg->chunkno, &null_chk_dirtied, v, |
1803 | 0 | null_msg->raw + null_msg->raw_size, gap_size) < 0) |
1804 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't insert gap in chunk"); |
1805 | | |
1806 | | /* Reuse message # for new null message taking place of non-null message */ |
1807 | 0 | new_null_msg = v; |
1808 | 0 | } /* end if */ |
1809 | 0 | else { |
1810 | | /* Adjust null message's size & offset */ |
1811 | 0 | null_msg->raw += curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh); |
1812 | 0 | null_msg->raw_size -= curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh); |
1813 | | |
1814 | | /* Mark null message dirty */ |
1815 | 0 | null_msg->dirty = true; |
1816 | 0 | null_chk_dirtied = true; |
1817 | | |
1818 | | /* Create new null message for previous location of non-null message */ |
1819 | 0 | if (oh->nmesgs >= oh->alloc_nmesgs) { |
1820 | 0 | if (H5O__alloc_msgs(oh, (size_t)1) < 0) |
1821 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, |
1822 | 0 | "can't allocate more space for messages"); |
1823 | | |
1824 | | /* "Retarget" 'curr_msg' pointer into newly re-allocated array of messages |
1825 | | */ |
1826 | 0 | curr_msg = &oh->mesg[u]; |
1827 | 0 | } /* end if */ |
1828 | | |
1829 | | /* Get message # for new null message */ |
1830 | 0 | new_null_msg = oh->nmesgs++; |
1831 | 0 | } /* end else */ |
1832 | | |
1833 | | /* Release null message's chunk, marking it dirty */ |
1834 | 0 | if (H5O__chunk_unprotect(f, null_chk_proxy, null_chk_dirtied) < 0) |
1835 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, |
1836 | 0 | "unable to unprotect object header chunk"); |
1837 | 0 | null_chk_proxy = NULL; |
1838 | 0 | null_chk_dirtied = false; |
1839 | | |
1840 | | /* Initialize new null message to take over non-null message's location */ |
1841 | 0 | oh->mesg[new_null_msg].type = H5O_MSG_NULL; |
1842 | 0 | oh->mesg[new_null_msg].native = NULL; |
1843 | 0 | oh->mesg[new_null_msg].raw = old_raw; |
1844 | 0 | oh->mesg[new_null_msg].raw_size = curr_msg->raw_size; |
1845 | 0 | oh->mesg[new_null_msg].chunkno = old_chunkno; |
1846 | | |
1847 | | /* Mark new null message dirty */ |
1848 | 0 | oh->mesg[new_null_msg].dirty = true; |
1849 | 0 | curr_chk_dirtied = true; |
1850 | | |
1851 | | /* Check for gap in new null message's chunk */ |
1852 | 0 | if (oh->chunk[old_chunkno].gap > 0) { |
1853 | | /* Eliminate the gap in the chunk */ |
1854 | 0 | if (H5O__eliminate_gap( |
1855 | 0 | oh, &curr_chk_dirtied, &oh->mesg[new_null_msg], |
1856 | 0 | ((oh->chunk[old_chunkno].image + oh->chunk[old_chunkno].size) - |
1857 | 0 | (H5O_SIZEOF_CHKSUM_OH(oh) + oh->chunk[old_chunkno].gap)), |
1858 | 0 | oh->chunk[old_chunkno].gap) < 0) |
1859 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTREMOVE, FAIL, |
1860 | 0 | "can't eliminate gap in chunk"); |
1861 | 0 | } /* end if */ |
1862 | | |
1863 | | /* Release new null message's chunk, marking it dirty */ |
1864 | 0 | if (H5O__chunk_unprotect(f, curr_chk_proxy, curr_chk_dirtied) < 0) |
1865 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, |
1866 | 0 | "unable to unprotect object header chunk"); |
1867 | 0 | curr_chk_proxy = NULL; |
1868 | 0 | curr_chk_dirtied = false; |
1869 | 0 | } /* end else */ |
1870 | | |
1871 | | /* Indicate that we packed messages */ |
1872 | 0 | packed_msg = true; |
1873 | | |
1874 | | /* Break out of loop */ |
1875 | | /* (If it's possible to move message to even earlier chunk |
1876 | | * we'll get it on the next pass - QAK) |
1877 | | */ |
1878 | 0 | break; |
1879 | 0 | } /* end if */ |
1880 | 0 | } /* end for */ |
1881 | | |
1882 | | /* If we packed messages, get out of loop and start over */ |
1883 | | /* (Don't know if this has any benefit one way or the other -QAK) */ |
1884 | 0 | if (packed_msg) |
1885 | 0 | break; |
1886 | 0 | } /* end else */ |
1887 | 0 | } /* end for */ |
1888 | | |
1889 | | /* If we did any packing, remember that */ |
1890 | 0 | if (packed_msg) |
1891 | 0 | did_packing = true; |
1892 | 0 | } while (packed_msg); |
1893 | | |
1894 | | /* Set return value */ |
1895 | 0 | ret_value = (htri_t)did_packing; |
1896 | |
|
1897 | 0 | done: |
1898 | 0 | if (ret_value < 0) { |
1899 | 0 | if (null_chk_proxy && H5O__chunk_unprotect(f, null_chk_proxy, null_chk_dirtied) < 0) |
1900 | 0 | HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect null object header chunk"); |
1901 | 0 | if (curr_chk_proxy && H5O__chunk_unprotect(f, curr_chk_proxy, curr_chk_dirtied) < 0) |
1902 | 0 | HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect current object header chunk"); |
1903 | 0 | if (cont_targ_chk_proxy && H5O__chunk_unprotect(f, cont_targ_chk_proxy, false) < 0) |
1904 | 0 | HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, |
1905 | 0 | "unable to unprotect continuation message target object header chunk"); |
1906 | 0 | } /* end if */ |
1907 | 0 | else |
1908 | 0 | assert(!null_chk_proxy && !curr_chk_proxy && !cont_targ_chk_proxy); |
1909 | |
|
1910 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1911 | 0 | } /* H5O__move_msgs_forward() */ |
1912 | | |
1913 | | /*------------------------------------------------------------------------- |
1914 | | * |
1915 | | * Function: H5O__merge_null |
1916 | | * |
1917 | | * Purpose: Merge neighboring null messages in an object header |
1918 | | * |
1919 | | * Return: Non-negative on success/Negative on failure |
1920 | | * |
1921 | | *------------------------------------------------------------------------- |
1922 | | */ |
1923 | | static htri_t |
1924 | | H5O__merge_null(H5F_t *f, H5O_t *oh) |
1925 | 0 | { |
1926 | 0 | bool merged_msg; /* Flag to indicate that messages were merged */ |
1927 | 0 | bool did_merging = false; /* Whether any messages were merged */ |
1928 | 0 | htri_t ret_value = FAIL; /* Return value */ |
1929 | |
|
1930 | 0 | FUNC_ENTER_PACKAGE |
1931 | | |
1932 | | /* check args */ |
1933 | 0 | assert(oh != NULL); |
1934 | | |
1935 | | /* Loop until no messages merged */ |
1936 | | /* (Double loop is not very efficient, but it would be some extra work to add |
1937 | | * a list of messages to each chunk -QAK) |
1938 | | */ |
1939 | 0 | do { |
1940 | 0 | H5O_mesg_t *curr_msg; /* Pointer to current message to operate on */ |
1941 | 0 | unsigned u; /* Local index variable */ |
1942 | | |
1943 | | /* Reset merged messages flag */ |
1944 | 0 | merged_msg = false; |
1945 | | |
1946 | | /* Scan messages for adjacent null messages & merge them */ |
1947 | 0 | for (u = 0, curr_msg = &oh->mesg[0]; u < oh->nmesgs; u++, curr_msg++) { |
1948 | 0 | if (H5O_NULL_ID == curr_msg->type->id) { |
1949 | 0 | H5O_mesg_t *curr_msg2; /* Pointer to current message to operate on */ |
1950 | 0 | unsigned v; /* Local index variable */ |
1951 | | |
1952 | | /* Should be no gaps in chunk with null message */ |
1953 | 0 | assert(oh->chunk[curr_msg->chunkno].gap == 0); |
1954 | | |
1955 | | /* Loop over messages again, looking for null message in same chunk */ |
1956 | 0 | for (v = 0, curr_msg2 = &oh->mesg[0]; v < oh->nmesgs; v++, curr_msg2++) { |
1957 | 0 | if (u != v && H5O_NULL_ID == curr_msg2->type->id && |
1958 | 0 | curr_msg->chunkno == curr_msg2->chunkno) { |
1959 | 0 | ssize_t adj_raw = 0; /* Amount to adjust raw message pointer */ |
1960 | 0 | size_t adj_raw_size = 0; /* Amount to adjust raw message size */ |
1961 | | |
1962 | | /* Check for second message after first message */ |
1963 | 0 | if ((curr_msg->raw + curr_msg->raw_size) == |
1964 | 0 | (curr_msg2->raw - H5O_SIZEOF_MSGHDR_OH(oh))) { |
1965 | | /* Extend first null message length to cover second null message */ |
1966 | 0 | adj_raw = 0; |
1967 | 0 | adj_raw_size = (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + curr_msg2->raw_size; |
1968 | | |
1969 | | /* Message has been merged */ |
1970 | 0 | merged_msg = true; |
1971 | 0 | } /* end if */ |
1972 | | /* Check for second message before first message */ |
1973 | 0 | else if ((curr_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh)) == |
1974 | 0 | (curr_msg2->raw + curr_msg2->raw_size)) { |
1975 | | /* Adjust first message address and extend length to cover second message */ |
1976 | 0 | adj_raw = -((ssize_t)((size_t)H5O_SIZEOF_MSGHDR_OH(oh) + curr_msg2->raw_size)); |
1977 | 0 | adj_raw_size = (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + curr_msg2->raw_size; |
1978 | | |
1979 | | /* Message has been merged */ |
1980 | 0 | merged_msg = true; |
1981 | 0 | } /* end if */ |
1982 | | |
1983 | | /* Second message has been merged, delete it */ |
1984 | 0 | if (merged_msg) { |
1985 | 0 | H5O_chunk_proxy_t *curr_chk_proxy; /* Chunk that message is in */ |
1986 | 0 | htri_t result; |
1987 | | |
1988 | | /* Release any information/memory for second message */ |
1989 | 0 | H5O__msg_free_mesg(curr_msg2); |
1990 | | |
1991 | | /* Protect chunk */ |
1992 | 0 | if (NULL == (curr_chk_proxy = H5O__chunk_protect(f, oh, curr_msg->chunkno))) |
1993 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, |
1994 | 0 | "unable to load object header chunk"); |
1995 | | |
1996 | | /* Adjust first message address and extend length to cover second message */ |
1997 | 0 | curr_msg->raw += adj_raw; |
1998 | 0 | curr_msg->raw_size += adj_raw_size; |
1999 | | |
2000 | | /* Mark first message as dirty */ |
2001 | 0 | curr_msg->dirty = true; |
2002 | | |
2003 | | /* Release new null message's chunk, marking it dirty */ |
2004 | 0 | if (H5O__chunk_unprotect(f, curr_chk_proxy, true) < 0) |
2005 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, |
2006 | 0 | "unable to unprotect object header chunk"); |
2007 | | |
2008 | | /* Remove second message from list of messages */ |
2009 | 0 | if (v < (oh->nmesgs - 1)) |
2010 | 0 | memmove(&oh->mesg[v], &oh->mesg[v + 1], |
2011 | 0 | ((oh->nmesgs - 1) - v) * sizeof(H5O_mesg_t)); |
2012 | | |
2013 | | /* Decrement # of messages */ |
2014 | | /* (Don't bother reducing size of message array for now -QAK) */ |
2015 | 0 | oh->nmesgs--; |
2016 | | |
2017 | | /* The merge null message might span the entire chunk: scan for empty chunk to |
2018 | | * remove */ |
2019 | 0 | if ((result = H5O__remove_empty_chunks(f, oh)) < 0) |
2020 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTPACK, FAIL, "can't remove empty chunk"); |
2021 | 0 | else if (result > 0) |
2022 | | /* Get out of loop */ |
2023 | 0 | break; |
2024 | | |
2025 | | /* If the merged message is too large, shrink the chunk */ |
2026 | 0 | if (curr_msg->raw_size >= H5O_MESG_MAX_SIZE) |
2027 | 0 | if (H5O__alloc_shrink_chunk(f, oh, curr_msg->chunkno) < 0) |
2028 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTPACK, FAIL, "unable to shrink chunk"); |
2029 | | |
2030 | | /* Get out of loop */ |
2031 | 0 | break; |
2032 | 0 | } /* end if */ |
2033 | 0 | } /* end if */ |
2034 | 0 | } /* end for */ |
2035 | | |
2036 | | /* Get out of loop if we merged messages */ |
2037 | 0 | if (merged_msg) |
2038 | 0 | break; |
2039 | 0 | } /* end if */ |
2040 | 0 | } /* end for */ |
2041 | | |
2042 | | /* If we did any merging, remember that */ |
2043 | 0 | if (merged_msg) |
2044 | 0 | did_merging = true; |
2045 | 0 | } while (merged_msg); |
2046 | | |
2047 | | /* Set return value */ |
2048 | 0 | ret_value = (htri_t)did_merging; |
2049 | |
|
2050 | 0 | done: |
2051 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
2052 | 0 | } /* H5O__merge_null() */ |
2053 | | |
2054 | | /*------------------------------------------------------------------------- |
2055 | | * |
2056 | | * Function: H5O__remove_empty_chunks |
2057 | | * |
2058 | | * Purpose: Attempt to eliminate empty chunks from object header. |
2059 | | * |
2060 | | * This examines a chunk to see if it's empty |
2061 | | * and removes it (and the continuation message that points to it) |
2062 | | * from the object header. |
2063 | | * |
2064 | | * Return: Non-negative on success/Negative on failure |
2065 | | * |
2066 | | *------------------------------------------------------------------------- |
2067 | | */ |
2068 | | static htri_t |
2069 | | H5O__remove_empty_chunks(H5F_t *f, H5O_t *oh) |
2070 | 0 | { |
2071 | 0 | bool deleted_chunk; /* Whether to a chunk was deleted */ |
2072 | 0 | bool did_deleting = false; /* Whether any chunks were deleted */ |
2073 | 0 | htri_t ret_value = FAIL; /* Return value */ |
2074 | |
|
2075 | 0 | FUNC_ENTER_PACKAGE |
2076 | | |
2077 | | /* check args */ |
2078 | 0 | assert(oh != NULL); |
2079 | | |
2080 | | /* Loop until no chunks are freed */ |
2081 | 0 | do { |
2082 | 0 | H5O_mesg_t *null_msg; /* Pointer to null message found */ |
2083 | 0 | H5O_mesg_t *cont_msg; /* Pointer to continuation message found */ |
2084 | 0 | unsigned u, v; /* Local index variables */ |
2085 | | |
2086 | | /* Reset 'chunk deleted' flag */ |
2087 | 0 | deleted_chunk = false; |
2088 | | |
2089 | | /* Scan messages for null messages that fill an entire chunk */ |
2090 | 0 | for (u = 0, null_msg = &oh->mesg[0]; u < oh->nmesgs; u++, null_msg++) { |
2091 | | /* If a null message takes up an entire object header chunk (and |
2092 | | * its not the "base" chunk), delete that chunk from object header |
2093 | | */ |
2094 | 0 | if (H5O_NULL_ID == null_msg->type->id && null_msg->chunkno > 0 && |
2095 | 0 | ((size_t)H5O_SIZEOF_MSGHDR_OH(oh) + null_msg->raw_size) == |
2096 | 0 | (oh->chunk[null_msg->chunkno].size - H5O_SIZEOF_CHKHDR_OH(oh))) { |
2097 | 0 | H5O_mesg_t *curr_msg; /* Pointer to current message to operate on */ |
2098 | 0 | unsigned null_msg_no; /* Message # for null message */ |
2099 | 0 | unsigned deleted_chunkno; /* Chunk # to delete */ |
2100 | | |
2101 | | /* Locate continuation message that points to chunk */ |
2102 | 0 | for (v = 0, cont_msg = &oh->mesg[0]; v < oh->nmesgs; v++, cont_msg++) { |
2103 | 0 | if (H5O_CONT_ID == cont_msg->type->id) { |
2104 | | /* Decode current continuation message if necessary */ |
2105 | 0 | H5O_LOAD_NATIVE(f, 0, oh, cont_msg, FAIL) |
2106 | | |
2107 | | /* Check if the chunkno needs to be set */ |
2108 | | /* (should only occur when the continuation message is first decoded) */ |
2109 | 0 | if (0 == ((H5O_cont_t *)(cont_msg->native))->chunkno) { |
2110 | 0 | unsigned w; /* Local index variable */ |
2111 | | |
2112 | | /* Find chunk that this continuation message points to */ |
2113 | 0 | for (w = 0; w < oh->nchunks; w++) |
2114 | 0 | if (oh->chunk[w].addr == ((H5O_cont_t *)(cont_msg->native))->addr) { |
2115 | 0 | ((H5O_cont_t *)(cont_msg->native))->chunkno = w; |
2116 | 0 | break; |
2117 | 0 | } /* end if */ |
2118 | 0 | assert(((H5O_cont_t *)(cont_msg->native))->chunkno > 0); |
2119 | 0 | } /* end if */ |
2120 | | |
2121 | | /* Check for correct chunk to delete */ |
2122 | 0 | if (oh->chunk[null_msg->chunkno].addr == ((H5O_cont_t *)(cont_msg->native))->addr) |
2123 | 0 | break; |
2124 | 0 | } /* end if */ |
2125 | 0 | } /* end for */ |
2126 | | /* Must be a continuation message that points to chunk containing null message */ |
2127 | 0 | assert(v < oh->nmesgs); |
2128 | 0 | assert(cont_msg); |
2129 | 0 | assert(((H5O_cont_t *)(cont_msg->native))->chunkno == null_msg->chunkno); |
2130 | | |
2131 | | /* Initialize information about null message */ |
2132 | 0 | null_msg_no = u; |
2133 | 0 | deleted_chunkno = null_msg->chunkno; |
2134 | | |
2135 | | /* Convert continuation message into a null message */ |
2136 | 0 | if (H5O__release_mesg(f, oh, cont_msg, true) < 0) |
2137 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTDELETE, FAIL, "unable to convert into null message"); |
2138 | | |
2139 | | /* |
2140 | | * Remove chunk from object header's data structure |
2141 | | */ |
2142 | | |
2143 | | /* Free memory for chunk image */ |
2144 | 0 | oh->chunk[null_msg->chunkno].image = |
2145 | 0 | H5FL_BLK_FREE(chunk_image, oh->chunk[null_msg->chunkno].image); |
2146 | | |
2147 | | /* Remove chunk from list of chunks */ |
2148 | 0 | if (null_msg->chunkno < (oh->nchunks - 1)) { |
2149 | 0 | memmove(&oh->chunk[null_msg->chunkno], &oh->chunk[null_msg->chunkno + 1], |
2150 | 0 | ((oh->nchunks - 1) - null_msg->chunkno) * sizeof(H5O_chunk_t)); |
2151 | | |
2152 | | /* Adjust chunk number for any chunk proxies that are in the cache */ |
2153 | 0 | for (u = null_msg->chunkno; u < (oh->nchunks - 1); u++) { |
2154 | 0 | unsigned chk_proxy_status = 0; /* Metadata cache status of chunk proxy for chunk */ |
2155 | | |
2156 | | /* Check the chunk proxy's status in the metadata cache */ |
2157 | 0 | if (H5AC_get_entry_status(f, oh->chunk[u].addr, &chk_proxy_status) < 0) |
2158 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, |
2159 | 0 | "unable to check metadata cache status for chunk proxy"); |
2160 | | |
2161 | | /* If the entry is in the cache, update its chunk index */ |
2162 | 0 | if (chk_proxy_status & H5AC_ES__IN_CACHE) { |
2163 | 0 | if (H5O__chunk_update_idx(f, oh, u) < 0) |
2164 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTSET, FAIL, |
2165 | 0 | "unable to update index for chunk proxy"); |
2166 | 0 | } /* end if */ |
2167 | 0 | } /* end for */ |
2168 | 0 | } /* end if */ |
2169 | | |
2170 | | /* Decrement # of chunks */ |
2171 | | /* (Don't bother reducing size of chunk array for now -QAK) */ |
2172 | 0 | oh->nchunks--; |
2173 | | |
2174 | | /* |
2175 | | * Delete null message (in empty chunk that was be freed) from list of messages |
2176 | | */ |
2177 | | |
2178 | | /* Release any information/memory for message */ |
2179 | 0 | H5O__msg_free_mesg(null_msg); |
2180 | | |
2181 | | /* Remove null message from list of messages */ |
2182 | 0 | if (null_msg_no < (oh->nmesgs - 1)) |
2183 | 0 | memmove(&oh->mesg[null_msg_no], &oh->mesg[null_msg_no + 1], |
2184 | 0 | ((oh->nmesgs - 1) - null_msg_no) * sizeof(H5O_mesg_t)); |
2185 | | |
2186 | | /* Decrement # of messages */ |
2187 | | /* (Don't bother reducing size of message array for now -QAK) */ |
2188 | 0 | oh->nmesgs--; |
2189 | | |
2190 | | /* Adjust chunk # for messages in chunks after deleted chunk */ |
2191 | 0 | for (u = 0, curr_msg = &oh->mesg[0]; u < oh->nmesgs; u++, curr_msg++) { |
2192 | | /* Sanity check - there should be no messages in deleted chunk */ |
2193 | 0 | assert(curr_msg->chunkno != deleted_chunkno); |
2194 | | |
2195 | | /* Adjust chunk index for messages in later chunks */ |
2196 | 0 | if (curr_msg->chunkno > deleted_chunkno) |
2197 | 0 | curr_msg->chunkno--; |
2198 | | |
2199 | | /* Check for continuation message */ |
2200 | 0 | if (H5O_CONT_ID == curr_msg->type->id) { |
2201 | | /* Decode current continuation message if necessary */ |
2202 | 0 | H5O_LOAD_NATIVE(f, 0, oh, curr_msg, FAIL) |
2203 | | |
2204 | | /* Check if the chunkno needs to be set */ |
2205 | | /* (should only occur when the continuation message is first decoded) */ |
2206 | 0 | if (0 == ((H5O_cont_t *)(curr_msg->native))->chunkno) { |
2207 | 0 | unsigned w; /* Local index variable */ |
2208 | | |
2209 | | /* Find chunk that this continuation message points to */ |
2210 | 0 | for (w = 0; w < oh->nchunks; w++) |
2211 | 0 | if (oh->chunk[w].addr == ((H5O_cont_t *)(curr_msg->native))->addr) { |
2212 | 0 | ((H5O_cont_t *)(curr_msg->native))->chunkno = w; |
2213 | 0 | break; |
2214 | 0 | } /* end if */ |
2215 | 0 | assert(((H5O_cont_t *)(curr_msg->native))->chunkno > 0); |
2216 | 0 | } /* end if */ |
2217 | 0 | else { |
2218 | | /* Check for pointer to chunk after deleted chunk */ |
2219 | 0 | if (((H5O_cont_t *)(curr_msg->native))->chunkno > deleted_chunkno) |
2220 | 0 | ((H5O_cont_t *)(curr_msg->native))->chunkno--; |
2221 | 0 | } /* end else */ |
2222 | 0 | } /* end if */ |
2223 | 0 | } /* end for */ |
2224 | | |
2225 | | /* Found chunk to delete */ |
2226 | 0 | deleted_chunk = true; |
2227 | 0 | break; |
2228 | 0 | } /* end if */ |
2229 | 0 | } /* end for */ |
2230 | | |
2231 | | /* If we deleted any chunks, remember that */ |
2232 | 0 | if (deleted_chunk) |
2233 | 0 | did_deleting = true; |
2234 | 0 | } while (deleted_chunk); |
2235 | | |
2236 | | /* Set return value */ |
2237 | 0 | ret_value = (htri_t)did_deleting; |
2238 | |
|
2239 | 0 | done: |
2240 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
2241 | 0 | } /* H5O__remove_empty_chunks() */ |
2242 | | |
2243 | | /*------------------------------------------------------------------------- |
2244 | | * |
2245 | | * Function: H5O__condense_header |
2246 | | * |
2247 | | * Purpose: Attempt to eliminate empty chunks from object header. |
2248 | | * |
2249 | | * Return: Non-negative on success/Negative on failure |
2250 | | * |
2251 | | *------------------------------------------------------------------------- |
2252 | | */ |
2253 | | herr_t |
2254 | | H5O__condense_header(H5F_t *f, H5O_t *oh) |
2255 | 0 | { |
2256 | 0 | bool rescan_header; /* Whether to rescan header */ |
2257 | 0 | htri_t result; /* Result from packing/merging/etc */ |
2258 | 0 | herr_t ret_value = SUCCEED; /* return value */ |
2259 | |
|
2260 | 0 | FUNC_ENTER_PACKAGE |
2261 | | |
2262 | | /* check args */ |
2263 | 0 | assert(oh != NULL); |
2264 | | |
2265 | | /* First remove all deleted messages from the object header */ |
2266 | 0 | for (unsigned u = 0; oh->num_deleted_mesgs > 0 && u < oh->nmesgs;) |
2267 | 0 | if (oh->mesg[u].type->id == H5O_DELETED_ID) { |
2268 | 0 | if (u < (oh->nmesgs - 1)) |
2269 | 0 | memmove(&oh->mesg[u], &oh->mesg[u + 1], ((oh->nmesgs - 1) - u) * sizeof(H5O_mesg_t)); |
2270 | 0 | oh->nmesgs--; |
2271 | 0 | oh->num_deleted_mesgs--; |
2272 | 0 | } |
2273 | 0 | else |
2274 | 0 | u++; |
2275 | 0 | assert(oh->num_deleted_mesgs == 0); |
2276 | | |
2277 | | /* Loop until no changed to the object header messages & chunks */ |
2278 | 0 | do { |
2279 | | /* Reset 'rescan chunks' flag */ |
2280 | 0 | rescan_header = false; |
2281 | | |
2282 | | /* Scan for messages that can be moved earlier in chunks */ |
2283 | 0 | result = H5O__move_msgs_forward(f, oh); |
2284 | 0 | if (result < 0) |
2285 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTPACK, FAIL, "can't move header messages forward"); |
2286 | 0 | if (result > 0) |
2287 | 0 | rescan_header = true; |
2288 | | |
2289 | | /* Scan for adjacent null messages & merge them */ |
2290 | 0 | result = H5O__merge_null(f, oh); |
2291 | 0 | if (result < 0) |
2292 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTPACK, FAIL, "can't pack null header messages"); |
2293 | 0 | if (result > 0) |
2294 | 0 | rescan_header = true; |
2295 | | |
2296 | | /* Scan for empty chunks to remove */ |
2297 | 0 | result = H5O__remove_empty_chunks(f, oh); |
2298 | 0 | if (result < 0) |
2299 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTPACK, FAIL, "can't remove empty chunk"); |
2300 | 0 | if (result > 0) |
2301 | 0 | rescan_header = true; |
2302 | 0 | } while (rescan_header); |
2303 | | #ifdef H5O_DEBUG |
2304 | | H5O__assert(oh); |
2305 | | #endif /* H5O_DEBUG */ |
2306 | | |
2307 | 0 | done: |
2308 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
2309 | 0 | } /* H5O__condense_header() */ |
2310 | | |
2311 | | /*------------------------------------------------------------------------- |
2312 | | * |
2313 | | * Function: H5O__alloc_shrink_chunk |
2314 | | * |
2315 | | * Purpose: Shrinks a chunk, removing all null messages and any gap. |
2316 | | * |
2317 | | * Return: Non-negative on success/Negative on failure |
2318 | | * |
2319 | | *------------------------------------------------------------------------- |
2320 | | */ |
2321 | | static herr_t |
2322 | | H5O__alloc_shrink_chunk(H5F_t *f, H5O_t *oh, unsigned chunkno) |
2323 | 0 | { |
2324 | 0 | H5O_chunk_t *chunk = &oh->chunk[chunkno]; /* Chunk to shrink */ |
2325 | 0 | H5O_chunk_proxy_t *chk_proxy = NULL; /* Metadata cache proxy for chunk to shrink */ |
2326 | 0 | H5O_mesg_t *curr_msg; /* Current message to examine */ |
2327 | 0 | uint8_t *old_image = chunk->image; /* Old address of chunk's image in memory */ |
2328 | 0 | size_t old_size = chunk->size; /* Old size of chunk */ |
2329 | 0 | size_t new_size = chunk->size - chunk->gap; /* Size of shrunk chunk */ |
2330 | 0 | size_t total_msg_size; /* Size of the messages in this chunk */ |
2331 | 0 | size_t min_chunk_size = H5O_ALIGN_OH(oh, H5O_MIN_SIZE); /* Minimum chunk size */ |
2332 | 0 | size_t sizeof_chksum = H5O_SIZEOF_CHKSUM_OH(oh); /* Size of chunk checksum */ |
2333 | 0 | size_t sizeof_msghdr = H5O_SIZEOF_MSGHDR_OH(oh); /* Size of message header */ |
2334 | 0 | uint8_t new_size_flags = 0; /* New chunk #0 size flags */ |
2335 | 0 | bool adjust_size_flags = false; /* Whether to adjust the chunk #0 size flags */ |
2336 | 0 | size_t less_prfx_size = 0; /* Bytes removed from object header prefix */ |
2337 | 0 | size_t u; /* Index */ |
2338 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
2339 | |
|
2340 | 0 | FUNC_ENTER_PACKAGE |
2341 | | |
2342 | | /* check args */ |
2343 | 0 | assert(f); |
2344 | 0 | assert(oh); |
2345 | | |
2346 | | /* Protect chunk */ |
2347 | 0 | if (NULL == (chk_proxy = H5O__chunk_protect(f, oh, chunkno))) |
2348 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to protect object header chunk"); |
2349 | | |
2350 | | /* Loop backwards to increase the chance of seeing more null messages at the |
2351 | | * end of the chunk. Note that we rely on unsigned u wrapping around at the |
2352 | | * end. |
2353 | | */ |
2354 | 0 | for (u = oh->nmesgs - 1, curr_msg = &oh->mesg[u]; u < oh->nmesgs; u--, curr_msg--) { |
2355 | 0 | if ((H5O_NULL_ID == curr_msg->type->id) && (chunkno == curr_msg->chunkno)) { |
2356 | 0 | size_t shrink_size = curr_msg->raw_size + sizeof_msghdr; /* Amount to shrink the chunk by */ |
2357 | | |
2358 | | /* If the current message is not at the end of the chunk, copy the |
2359 | | * data after it (except the checksum). |
2360 | | */ |
2361 | 0 | if (curr_msg->raw + curr_msg->raw_size < old_image + new_size - sizeof_chksum) { |
2362 | 0 | unsigned v; /* Index */ |
2363 | 0 | H5O_mesg_t *curr_msg2; |
2364 | 0 | uint8_t *src = curr_msg->raw + curr_msg->raw_size; /* Source location */ |
2365 | | |
2366 | | /* Slide down the raw data */ |
2367 | 0 | memmove(curr_msg->raw - sizeof_msghdr, src, |
2368 | 0 | (size_t)(old_image + new_size - sizeof_chksum - src)); |
2369 | | |
2370 | | /* Update the raw data pointers for messages after this one */ |
2371 | 0 | for (v = 0, curr_msg2 = &oh->mesg[0]; v < oh->nmesgs; v++, curr_msg2++) |
2372 | 0 | if ((chunkno == curr_msg2->chunkno) && (curr_msg2->raw > curr_msg->raw)) |
2373 | 0 | curr_msg2->raw -= shrink_size; |
2374 | 0 | } /* end if */ |
2375 | | |
2376 | | /* Adjust the new chunk size */ |
2377 | 0 | new_size -= shrink_size; |
2378 | | |
2379 | | /* Release any information/memory for the message */ |
2380 | 0 | H5O__msg_free_mesg(curr_msg); |
2381 | | |
2382 | | /* Remove the deleted null message from list of messages */ |
2383 | 0 | if (u < (oh->nmesgs - 1)) |
2384 | 0 | memmove(&oh->mesg[u], &oh->mesg[u + 1], ((oh->nmesgs - 1) - u) * sizeof(H5O_mesg_t)); |
2385 | | |
2386 | | /* Decrement # of messages */ |
2387 | | /* (Don't bother reducing size of message array for now) */ |
2388 | 0 | oh->nmesgs--; |
2389 | 0 | } /* end if */ |
2390 | 0 | } /* end for */ |
2391 | | |
2392 | | /* Check if the chunk is too small, extend if necessary */ |
2393 | 0 | total_msg_size = new_size - (size_t)(chunkno == 0 ? H5O_SIZEOF_HDR(oh) : H5O_SIZEOF_CHKHDR_OH(oh)); |
2394 | 0 | if (total_msg_size < min_chunk_size) { |
2395 | 0 | assert(oh->alloc_nmesgs > oh->nmesgs); |
2396 | 0 | oh->nmesgs++; |
2397 | | |
2398 | | /* Initialize new null message to make the chunk large enough */ |
2399 | 0 | curr_msg = &oh->mesg[oh->nmesgs - 1]; |
2400 | 0 | curr_msg->type = H5O_MSG_NULL; |
2401 | 0 | curr_msg->dirty = true; |
2402 | 0 | curr_msg->native = NULL; |
2403 | 0 | curr_msg->raw = old_image + new_size + sizeof_msghdr - sizeof_chksum; |
2404 | 0 | curr_msg->raw_size = |
2405 | 0 | MAX(H5O_ALIGN_OH(oh, min_chunk_size - total_msg_size), sizeof_msghdr) - sizeof_msghdr; |
2406 | 0 | curr_msg->chunkno = chunkno; |
2407 | | |
2408 | | /* update the new chunk size */ |
2409 | 0 | new_size += curr_msg->raw_size + sizeof_msghdr; |
2410 | 0 | } /* end if */ |
2411 | | |
2412 | | /* Check for changing the chunk #0 data size enough to need adjusting the flags */ |
2413 | 0 | if (oh->version > H5O_VERSION_1 && chunkno == 0) { |
2414 | 0 | uint64_t chunk0_newsize = new_size - (size_t)H5O_SIZEOF_HDR(oh); /* New size of chunk 0's data */ |
2415 | 0 | size_t orig_prfx_size = (size_t)1 << (oh->flags & H5O_HDR_CHUNK0_SIZE); /* Original prefix size */ |
2416 | | |
2417 | | /* Check for moving to a 1-byte size encoding */ |
2418 | 0 | if (orig_prfx_size > 1 && chunk0_newsize <= 255) { |
2419 | 0 | less_prfx_size = orig_prfx_size - 1; |
2420 | 0 | new_size_flags = H5O_HDR_CHUNK0_1; |
2421 | 0 | adjust_size_flags = true; |
2422 | 0 | } /* end if */ |
2423 | | /* Check for moving to a 2-byte size encoding */ |
2424 | 0 | else if (orig_prfx_size > 2 && chunk0_newsize <= 65535) { |
2425 | 0 | less_prfx_size = orig_prfx_size - 2; |
2426 | 0 | new_size_flags = H5O_HDR_CHUNK0_2; |
2427 | 0 | adjust_size_flags = true; |
2428 | 0 | } /* end if */ |
2429 | | /* Check for moving to a 4-byte size encoding */ |
2430 | 0 | else if (orig_prfx_size > 4 && chunk0_newsize <= 4294967295) { |
2431 | 0 | less_prfx_size = orig_prfx_size - 4; |
2432 | 0 | new_size_flags = H5O_HDR_CHUNK0_4; |
2433 | 0 | adjust_size_flags = true; |
2434 | 0 | } /* end if */ |
2435 | 0 | } /* end if */ |
2436 | |
|
2437 | 0 | if (adjust_size_flags) { |
2438 | | /* Adjust object header prefix flags */ |
2439 | 0 | oh->flags = (uint8_t)(oh->flags & ~H5O_HDR_CHUNK0_SIZE); |
2440 | 0 | oh->flags |= new_size_flags; |
2441 | | |
2442 | | /* Slide chunk 0 data down */ |
2443 | 0 | memmove(chunk->image + H5O_SIZEOF_HDR(oh) - sizeof_chksum, |
2444 | 0 | chunk->image + H5O_SIZEOF_HDR(oh) - sizeof_chksum + less_prfx_size, |
2445 | 0 | new_size - (size_t)H5O_SIZEOF_HDR(oh)); |
2446 | | |
2447 | | /* Adjust chunk size */ |
2448 | 0 | new_size -= less_prfx_size; |
2449 | 0 | } /* end if */ |
2450 | | |
2451 | | /* Allocate less memory space for chunk's image */ |
2452 | 0 | chunk->size = new_size; |
2453 | 0 | chunk->image = H5FL_BLK_REALLOC(chunk_image, old_image, chunk->size); |
2454 | 0 | chunk->gap = 0; |
2455 | 0 | if (NULL == oh->chunk[chunkno].image) |
2456 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
2457 | | |
2458 | | /* Spin through existing messages, adjusting them */ |
2459 | 0 | for (u = 0, curr_msg = &oh->mesg[0]; u < oh->nmesgs; u++, curr_msg++) { |
2460 | 0 | if (adjust_size_flags || (chunk->image != old_image)) |
2461 | | /* Adjust raw addresses for messages in this chunk to reflect new 'image' address */ |
2462 | 0 | if (curr_msg->chunkno == chunkno) |
2463 | 0 | curr_msg->raw = chunk->image - less_prfx_size + (curr_msg->raw - old_image); |
2464 | | |
2465 | | /* Find continuation message which points to this chunk and adjust chunk's size */ |
2466 | | /* (Chunk 0 doesn't have a continuation message that points to it and |
2467 | | * its size is directly encoded in the object header) */ |
2468 | 0 | if (chunkno > 0 && (H5O_CONT_ID == curr_msg->type->id) && |
2469 | 0 | (((H5O_cont_t *)(curr_msg->native))->chunkno == chunkno)) { |
2470 | 0 | H5O_chunk_proxy_t *cont_chk_proxy; /* Chunk that message is in */ |
2471 | | |
2472 | | /* Protect chunk */ |
2473 | 0 | if (NULL == (cont_chk_proxy = H5O__chunk_protect(f, oh, curr_msg->chunkno))) |
2474 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to protect object header chunk"); |
2475 | | |
2476 | | /* Adjust size of continuation message */ |
2477 | 0 | assert(((H5O_cont_t *)(curr_msg->native))->size == old_size); |
2478 | 0 | ((H5O_cont_t *)(curr_msg->native))->size = chunk->size; |
2479 | | |
2480 | | /* Flag continuation message as dirty */ |
2481 | 0 | curr_msg->dirty = true; |
2482 | | |
2483 | | /* Release chunk, marking it dirty */ |
2484 | 0 | if (H5O__chunk_unprotect(f, cont_chk_proxy, true) < 0) |
2485 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect object header chunk"); |
2486 | 0 | } /* end if */ |
2487 | 0 | } /* end for */ |
2488 | | |
2489 | 0 | assert(new_size <= old_size); |
2490 | | |
2491 | | /* Resize the chunk in the cache */ |
2492 | 0 | if (H5O__chunk_resize(oh, chk_proxy) < 0) |
2493 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTRESIZE, FAIL, "unable to resize object header chunk"); |
2494 | | |
2495 | | /* Free the unused space in the file */ |
2496 | 0 | if (H5MF_xfree(f, H5FD_MEM_OHDR, chunk->addr + new_size, (hsize_t)(old_size - new_size)) < 0) |
2497 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTFREE, FAIL, "unable to shrink object header chunk"); |
2498 | | |
2499 | 0 | done: |
2500 | | /* Release chunk, marking it dirty */ |
2501 | 0 | if (chk_proxy && H5O__chunk_unprotect(f, chk_proxy, true) < 0) |
2502 | 0 | HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect object header chunk"); |
2503 | |
|
2504 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
2505 | 0 | } /* H5O__alloc_shrink_chunk() */ |