/src/c-blosc2/blosc/schunk.c
Line | Count | Source |
1 | | /********************************************************************* |
2 | | Blosc - Blocked Shuffling and Compression Library |
3 | | |
4 | | Copyright (c) 2021 Blosc Development Team <blosc@blosc.org> |
5 | | https://blosc.org |
6 | | License: BSD 3-Clause (see LICENSE.txt) |
7 | | |
8 | | See LICENSE.txt for details about copyright and rights to use. |
9 | | **********************************************************************/ |
10 | | |
11 | | #include "frame.h" |
12 | | #include "stune.h" |
13 | | #include "blosc-private.h" |
14 | | #include "context.h" |
15 | | #include "blosc2/tuners-registry.h" |
16 | | #include "blosc2.h" |
17 | | |
18 | | #if defined(_WIN32) |
19 | | #include <windows.h> |
20 | | #include <direct.h> |
21 | | #include <malloc.h> |
22 | | #define mkdir(D, M) _mkdir(D) |
23 | | #else |
24 | | #include <unistd.h> |
25 | | #endif /* _WIN32 */ |
26 | | |
27 | | #include <sys/stat.h> |
28 | | |
29 | | #include <inttypes.h> |
30 | | #include <limits.h> |
31 | | #include <stdio.h> |
32 | | #include <stdint.h> |
33 | | #include <stdlib.h> |
34 | | #include <string.h> |
35 | | |
36 | | /* If C11 is supported, use it's built-in aligned allocation. */ |
37 | | #if __STDC_VERSION__ >= 201112L |
38 | | #include <stdalign.h> |
39 | | #endif |
40 | | |
41 | | static int schunk_get_chunk_flags2(blosc2_schunk *schunk, int64_t nchunk, uint8_t *chunk_flags2); |
42 | | |
43 | | |
44 | 25.1k | static int validate_nchunk(blosc2_schunk *schunk, int64_t nchunk, bool allow_end, const char *func_name) { |
45 | 25.1k | if (nchunk < 0) { |
46 | 0 | BLOSC_TRACE_ERROR("nchunk ('%" PRId64 "') is negative in %s.", nchunk, func_name); |
47 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
48 | 0 | } |
49 | | |
50 | 25.1k | if (allow_end) { |
51 | 0 | if (nchunk > schunk->nchunks) { |
52 | 0 | BLOSC_TRACE_ERROR("nchunk ('%" PRId64 "') is out of range [0, %" PRId64 "] in %s.", |
53 | 0 | nchunk, schunk->nchunks, func_name); |
54 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
55 | 0 | } |
56 | 0 | } |
57 | 25.1k | else { |
58 | 25.1k | if (nchunk >= schunk->nchunks) { |
59 | 0 | BLOSC_TRACE_ERROR("nchunk ('%" PRId64 "') exceeds the number of chunks " |
60 | 0 | "('%" PRId64 "') in %s.", nchunk, schunk->nchunks, func_name); |
61 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
62 | 0 | } |
63 | 25.1k | } |
64 | | |
65 | 25.1k | return BLOSC2_ERROR_SUCCESS; |
66 | 25.1k | } |
67 | | |
68 | | |
69 | | /* Get the cparams associated with a super-chunk */ |
70 | 9.25k | int blosc2_schunk_get_cparams(blosc2_schunk *schunk, blosc2_cparams **cparams) { |
71 | 9.25k | *cparams = calloc(1, sizeof(blosc2_cparams)); |
72 | 9.25k | (*cparams)->schunk = schunk; |
73 | 64.7k | for (int i = 0; i < BLOSC2_MAX_FILTERS; i++) { |
74 | 55.5k | (*cparams)->filters[i] = schunk->filters[i]; |
75 | 55.5k | (*cparams)->filters_meta[i] = schunk->filters_meta[i]; |
76 | 55.5k | } |
77 | 9.25k | (*cparams)->compcode = schunk->compcode; |
78 | 9.25k | (*cparams)->compcode_meta = schunk->compcode_meta; |
79 | 9.25k | (*cparams)->clevel = schunk->clevel; |
80 | 9.25k | (*cparams)->typesize = schunk->typesize; |
81 | 9.25k | (*cparams)->blocksize = schunk->blocksize; |
82 | 9.25k | (*cparams)->splitmode = schunk->splitmode; |
83 | 9.25k | (*cparams)->use_dict = schunk->use_dict; |
84 | 9.25k | if (schunk->cctx == NULL) { |
85 | 9.25k | (*cparams)->nthreads = blosc2_get_nthreads(); |
86 | 9.25k | } |
87 | 0 | else { |
88 | 0 | (*cparams)->nthreads = (int16_t)schunk->cctx->nthreads; |
89 | 0 | } |
90 | 9.25k | return 0; |
91 | 9.25k | } |
92 | | |
93 | | |
94 | | /* Get the dparams associated with a super-chunk */ |
95 | 9.24k | int blosc2_schunk_get_dparams(blosc2_schunk *schunk, blosc2_dparams **dparams) { |
96 | 9.24k | *dparams = calloc(1, sizeof(blosc2_dparams)); |
97 | 9.24k | (*dparams)->schunk = schunk; |
98 | 9.24k | if (schunk->dctx == NULL) { |
99 | 9.24k | (*dparams)->nthreads = blosc2_get_nthreads(); |
100 | 9.24k | } |
101 | 0 | else { |
102 | 0 | (*dparams)->nthreads = schunk->dctx->nthreads; |
103 | 0 | } |
104 | 9.24k | return 0; |
105 | 9.24k | } |
106 | | |
107 | | |
108 | 3.10k | int update_schunk_properties(struct blosc2_schunk* schunk) { |
109 | 3.10k | blosc2_cparams* cparams = schunk->storage->cparams; |
110 | 3.10k | blosc2_dparams* dparams = schunk->storage->dparams; |
111 | | |
112 | 21.7k | for (int i = 0; i < BLOSC2_MAX_FILTERS; i++) { |
113 | 18.6k | schunk->filters[i] = cparams->filters[i]; |
114 | 18.6k | schunk->filters_meta[i] = cparams->filters_meta[i]; |
115 | 18.6k | } |
116 | 3.10k | schunk->compcode = cparams->compcode; |
117 | 3.10k | schunk->compcode_meta = cparams->compcode_meta; |
118 | 3.10k | schunk->clevel = cparams->clevel; |
119 | 3.10k | schunk->splitmode = cparams->splitmode; |
120 | 3.10k | schunk->use_dict = (uint8_t)cparams->use_dict; |
121 | 3.10k | schunk->typesize = cparams->typesize; |
122 | 3.10k | schunk->blocksize = cparams->blocksize; |
123 | 3.10k | schunk->chunksize = -1; |
124 | 3.10k | schunk->flags2 = 0; |
125 | 3.10k | schunk->tuner_params = cparams->tuner_params; |
126 | 3.10k | schunk->tuner_id = cparams->tuner_id; |
127 | 3.10k | if (cparams->tuner_id == BLOSC_BTUNE) { |
128 | 0 | cparams->use_dict = 0; |
129 | 0 | } |
130 | | /* The compression context */ |
131 | 3.10k | if (schunk->cctx != NULL) { |
132 | 0 | blosc2_free_ctx(schunk->cctx); |
133 | 0 | } |
134 | 3.10k | cparams->schunk = schunk; |
135 | 3.10k | schunk->cctx = blosc2_create_cctx(*cparams); |
136 | 3.10k | if (schunk->cctx == NULL) { |
137 | 0 | BLOSC_TRACE_ERROR("Could not create compression ctx"); |
138 | 0 | return BLOSC2_ERROR_NULL_POINTER; |
139 | 0 | } |
140 | | |
141 | | /* The decompression context */ |
142 | 3.10k | if (schunk->dctx != NULL) { |
143 | 0 | blosc2_free_ctx(schunk->dctx); |
144 | 0 | } |
145 | 3.10k | dparams->schunk = schunk; |
146 | 3.10k | schunk->dctx = blosc2_create_dctx(*dparams); |
147 | 3.10k | if (schunk->dctx == NULL) { |
148 | 0 | BLOSC_TRACE_ERROR("Could not create decompression ctx"); |
149 | 0 | return BLOSC2_ERROR_NULL_POINTER; |
150 | 0 | } |
151 | | |
152 | 3.10k | return BLOSC2_ERROR_SUCCESS; |
153 | 3.10k | } |
154 | | |
155 | | |
156 | 0 | static bool file_exists (char *filename) { |
157 | 0 | struct stat buffer; |
158 | 0 | return (stat (filename, &buffer) == 0); |
159 | 0 | } |
160 | | |
161 | | |
162 | | /* Create a new super-chunk */ |
163 | 3.10k | blosc2_schunk* blosc2_schunk_new(blosc2_storage *storage) { |
164 | 3.10k | blosc2_schunk* schunk = calloc(1, sizeof(blosc2_schunk)); |
165 | 3.10k | schunk->version = 0; /* pre-first version */ |
166 | 3.10k | schunk->view = false; /* not a view by default */ |
167 | | |
168 | | // Get the storage with proper defaults |
169 | 3.10k | schunk->storage = get_new_storage(storage, &BLOSC2_CPARAMS_DEFAULTS, &BLOSC2_DPARAMS_DEFAULTS, &BLOSC2_IO_DEFAULTS); |
170 | | // Update the (local variable) storage |
171 | 3.10k | storage = schunk->storage; |
172 | | |
173 | 3.10k | char* tradeoff = getenv("BTUNE_TRADEOFF"); |
174 | 3.10k | if (tradeoff != NULL) { |
175 | | // If BTUNE_TRADEOFF passed, automatically use btune |
176 | 0 | storage->cparams->tuner_id = BLOSC_BTUNE; |
177 | 0 | } |
178 | | |
179 | | // ...and update internal properties |
180 | 3.10k | if (update_schunk_properties(schunk) < 0) { |
181 | 0 | BLOSC_TRACE_ERROR("Error when updating schunk properties"); |
182 | 0 | return NULL; |
183 | 0 | } |
184 | | |
185 | 3.10k | if (!storage->contiguous && storage->urlpath != NULL){ |
186 | 0 | char* urlpath; |
187 | 0 | char last_char = storage->urlpath[strlen(storage->urlpath) - 1]; |
188 | 0 | urlpath = malloc(strlen(storage->urlpath) + 1); |
189 | 0 | strcpy(urlpath, storage->urlpath); |
190 | 0 | if (last_char == '\\' || last_char == '/') { |
191 | 0 | urlpath[strlen(storage->urlpath) - 1] = '\0'; |
192 | 0 | } |
193 | | // Create directory |
194 | 0 | if (mkdir(urlpath, 0777) == -1) { |
195 | 0 | BLOSC_TRACE_ERROR("Error during the creation of the directory, maybe it already exists."); |
196 | 0 | return NULL; |
197 | 0 | } |
198 | | // We want a sparse (directory) frame as storage |
199 | 0 | blosc2_frame_s* frame = frame_new(urlpath); |
200 | 0 | free(urlpath); |
201 | 0 | if (frame == NULL) { |
202 | 0 | BLOSC_TRACE_ERROR("Error creating sparse frame."); |
203 | 0 | return NULL; |
204 | 0 | } |
205 | 0 | frame->sframe = true; |
206 | 0 | frame_set_locking(frame, schunk->storage->io); |
207 | | // Initialize frame (basically, encode the header) |
208 | 0 | frame->schunk = schunk; |
209 | 0 | int64_t frame_len = frame_from_schunk(schunk, frame); |
210 | 0 | if (frame_len < 0) { |
211 | 0 | BLOSC_TRACE_ERROR("Error during the conversion of schunk to frame."); |
212 | 0 | return NULL; |
213 | 0 | } |
214 | 0 | schunk->frame = (blosc2_frame*)frame; |
215 | 0 | } |
216 | 3.10k | if (storage->contiguous){ |
217 | | // We want a contiguous frame as storage |
218 | 0 | if (storage->urlpath != NULL) { |
219 | 0 | if (file_exists(storage->urlpath)) { |
220 | 0 | BLOSC_TRACE_ERROR("You are trying to overwrite an existing frame. Remove it first!"); |
221 | 0 | return NULL; |
222 | 0 | } |
223 | 0 | } |
224 | 0 | blosc2_frame_s* frame = frame_new(storage->urlpath); |
225 | 0 | if (frame == NULL) { |
226 | 0 | BLOSC_TRACE_ERROR("Error creating contiguous frame."); |
227 | 0 | return NULL; |
228 | 0 | } |
229 | 0 | frame->sframe = false; |
230 | 0 | frame_set_locking(frame, schunk->storage->io); |
231 | | // Initialize frame (basically, encode the header) |
232 | 0 | frame->schunk = schunk; |
233 | 0 | int64_t frame_len = frame_from_schunk(schunk, frame); |
234 | 0 | if (frame_len < 0) { |
235 | 0 | BLOSC_TRACE_ERROR("Error during the conversion of schunk to frame."); |
236 | 0 | return NULL; |
237 | 0 | } |
238 | 0 | schunk->frame = (blosc2_frame*)frame; |
239 | 0 | } |
240 | | |
241 | 3.10k | return schunk; |
242 | 3.10k | } |
243 | | |
244 | | |
245 | | /* Create a copy of a super-chunk */ |
246 | 0 | blosc2_schunk* blosc2_schunk_copy(blosc2_schunk *schunk, blosc2_storage *storage) { |
247 | 0 | if (schunk == NULL) { |
248 | 0 | BLOSC_TRACE_ERROR("Can not copy a NULL `schunk`."); |
249 | 0 | return NULL; |
250 | 0 | } |
251 | | |
252 | | // Check if cparams are equals |
253 | 0 | bool cparams_equal = true; |
254 | 0 | blosc2_cparams cparams = {0}; |
255 | 0 | if (storage->cparams == NULL) { |
256 | | // When cparams are not specified, just use the same of schunk |
257 | 0 | cparams.typesize = schunk->cctx->typesize; |
258 | 0 | cparams.clevel = schunk->cctx->clevel; |
259 | 0 | cparams.compcode = schunk->cctx->compcode; |
260 | 0 | cparams.compcode_meta = schunk->cctx->compcode_meta; |
261 | 0 | cparams.splitmode = schunk->cctx->splitmode; |
262 | 0 | cparams.use_dict = schunk->cctx->use_dict; |
263 | 0 | cparams.blocksize = schunk->cctx->blocksize; |
264 | 0 | memcpy(cparams.filters, schunk->cctx->filters, BLOSC2_MAX_FILTERS); |
265 | 0 | memcpy(cparams.filters_meta, schunk->cctx->filters_meta, BLOSC2_MAX_FILTERS); |
266 | 0 | storage->cparams = &cparams; |
267 | 0 | } |
268 | 0 | else { |
269 | 0 | cparams = *storage->cparams; |
270 | 0 | } |
271 | 0 | if (cparams.blocksize == 0) { |
272 | | // TODO: blocksize should be read from schunk->blocksize |
273 | | // For this, it should be updated during the first append |
274 | | // (or change API to make this a property during schunk creation). |
275 | 0 | cparams.blocksize = schunk->cctx->blocksize; |
276 | 0 | } |
277 | |
|
278 | 0 | if (cparams.typesize != schunk->cctx->typesize || |
279 | 0 | cparams.clevel != schunk->cctx->clevel || |
280 | 0 | cparams.compcode != schunk->cctx->compcode || |
281 | 0 | cparams.use_dict != schunk->cctx->use_dict || |
282 | 0 | cparams.blocksize != schunk->cctx->blocksize || |
283 | | // In case of prefilters or postfilters, force their execution. |
284 | 0 | schunk->cctx->prefilter != NULL || |
285 | 0 | schunk->dctx->postfilter != NULL) { |
286 | 0 | cparams_equal = false; |
287 | 0 | } |
288 | 0 | for (int i = 0; i < BLOSC2_MAX_FILTERS; ++i) { |
289 | 0 | if (cparams.filters[i] != schunk->cctx->filters[i] || |
290 | 0 | cparams.filters_meta[i] != schunk->cctx->filters_meta[i]) { |
291 | 0 | cparams_equal = false; |
292 | 0 | } |
293 | 0 | } |
294 | | |
295 | | // Create new schunk |
296 | 0 | blosc2_schunk *new_schunk = blosc2_schunk_new(storage); |
297 | 0 | if (new_schunk == NULL) { |
298 | 0 | BLOSC_TRACE_ERROR("Can not create a new schunk"); |
299 | 0 | return NULL; |
300 | 0 | } |
301 | | // Set the chunksize for the schunk, as it cannot be derived from storage |
302 | 0 | new_schunk->chunksize = schunk->chunksize; |
303 | 0 | new_schunk->flags2 = schunk->flags2; |
304 | | |
305 | | // Copy metalayers |
306 | 0 | for (int nmeta = 0; nmeta < schunk->nmetalayers; ++nmeta) { |
307 | 0 | blosc2_metalayer *meta = schunk->metalayers[nmeta]; |
308 | 0 | if (blosc2_meta_add(new_schunk, meta->name, meta->content, meta->content_len) < 0) { |
309 | 0 | BLOSC_TRACE_ERROR("Can not add %s `metalayer`.", meta->name); |
310 | 0 | return NULL; |
311 | 0 | } |
312 | 0 | } |
313 | | |
314 | | // Copy chunks |
315 | 0 | bool uses_vlblocks = (schunk->flags2 & BLOSC2_VL_BLOCKS) != 0; |
316 | |
|
317 | 0 | if (cparams_equal || uses_vlblocks) { |
318 | 0 | for (int nchunk = 0; nchunk < schunk->nchunks; ++nchunk) { |
319 | 0 | uint8_t *chunk; |
320 | 0 | bool needs_free; |
321 | 0 | int rc = blosc2_schunk_get_chunk(schunk, nchunk, &chunk, &needs_free); |
322 | 0 | if (rc < 0) { |
323 | 0 | BLOSC_TRACE_ERROR("Can not get the `chunk` %d.", nchunk); |
324 | 0 | return NULL; |
325 | 0 | } |
326 | 0 | rc = blosc2_schunk_append_chunk(new_schunk, chunk, !needs_free); |
327 | 0 | if (rc < 0) { |
328 | 0 | BLOSC_TRACE_ERROR("Can not append the `chunk` into super-chunk."); |
329 | 0 | return NULL; |
330 | 0 | } |
331 | 0 | } |
332 | 0 | } else { |
333 | 0 | int32_t chunksize = schunk->chunksize == -1 ? 0 : schunk->chunksize; |
334 | 0 | uint8_t *buffer = malloc(chunksize); |
335 | 0 | for (int nchunk = 0; nchunk < schunk->nchunks; ++nchunk) { |
336 | 0 | if (blosc2_schunk_decompress_chunk(schunk, nchunk, buffer, schunk->chunksize) < 0) { |
337 | 0 | BLOSC_TRACE_ERROR("Can not decompress the `chunk` %d.", nchunk); |
338 | 0 | return NULL; |
339 | 0 | } |
340 | 0 | if (blosc2_schunk_append_buffer(new_schunk, buffer, schunk->chunksize) < 0) { |
341 | 0 | BLOSC_TRACE_ERROR("Can not append the `buffer` into super-chunk."); |
342 | 0 | return NULL; |
343 | 0 | } |
344 | 0 | } |
345 | 0 | free(buffer); |
346 | 0 | } |
347 | | |
348 | | // Copy vlmetalayers |
349 | 0 | for (int nmeta = 0; nmeta < schunk->nvlmetalayers; ++nmeta) { |
350 | 0 | uint8_t *content = NULL; |
351 | 0 | int32_t content_len; |
352 | 0 | char* name = schunk->vlmetalayers[nmeta]->name; |
353 | 0 | if (blosc2_vlmeta_get(schunk, name, &content, &content_len) < 0) { |
354 | | // Passing the (previously uninitialized) content pointer forward ended |
355 | | // in a bogus free that aborted the process; bail out instead. |
356 | 0 | BLOSC_TRACE_ERROR("Can not get %s `vlmetalayer`.", name); |
357 | 0 | return NULL; |
358 | 0 | } |
359 | 0 | if (blosc2_vlmeta_add(new_schunk, name, content, content_len, NULL) < 0) { |
360 | 0 | BLOSC_TRACE_ERROR("Can not add %s `vlmetalayer`.", name); |
361 | 0 | free(content); |
362 | 0 | return NULL; |
363 | 0 | } |
364 | 0 | free(content); |
365 | 0 | } |
366 | 0 | return new_schunk; |
367 | 0 | } |
368 | | |
369 | | |
370 | | /* Open an existing super-chunk that is on-disk (no copy is made). */ |
371 | 0 | blosc2_schunk* blosc2_schunk_open_udio(const char* urlpath, const blosc2_io *udio) { |
372 | 0 | return blosc2_schunk_open_offset_udio(urlpath, 0, udio); |
373 | 0 | } |
374 | | |
375 | | // frame_from_file_offset()'s bootstrap read (path stat + header + trailer) |
376 | | // happens before any lock is taken, so it can race a concurrent writer |
377 | | // growing or rewriting the frame (e.g. a stat() size snapshot made stale by |
378 | | // a header already advertising the writer's new, larger frame length) and |
379 | | // fail outright instead of returning a frame to refresh under lock. Under |
380 | | // the locking contract such a failure is expected to be transient, so |
381 | | // retry a bounded number of times with a short backoff before giving up; |
382 | | // without locking requested this is the pre-existing single-attempt |
383 | | // behavior (a concurrent writer without locking is already out of the SWMR |
384 | | // contract). Used for every bootstrap/re-read of the frame header in |
385 | | // blosc2_schunk_open_offset_udio() below -- a compressed-size change on an |
386 | | // in-place update can rewrite the frame layout just like growth does, so |
387 | | // this race is not limited to append/resize. |
388 | | static blosc2_frame_s* frame_from_file_offset_retrying( |
389 | 0 | const char* urlpath, const blosc2_io *udio, int64_t offset, int max_attempts) { |
390 | 0 | blosc2_frame_s* frame = NULL; |
391 | 0 | for (int attempt = 0; attempt < max_attempts; attempt++) { |
392 | 0 | frame = frame_from_file_offset(urlpath, udio, offset); |
393 | 0 | if (frame != NULL || attempt + 1 == max_attempts) { |
394 | 0 | break; |
395 | 0 | } |
396 | | #if defined(_WIN32) |
397 | | Sleep(1); |
398 | | #else |
399 | 0 | usleep(1000); |
400 | 0 | #endif |
401 | 0 | } |
402 | 0 | return frame; |
403 | 0 | } |
404 | | |
405 | 0 | blosc2_schunk* blosc2_schunk_open_offset_udio(const char* urlpath, int64_t offset, const blosc2_io *udio) { |
406 | 0 | if (urlpath == NULL) { |
407 | 0 | BLOSC_TRACE_ERROR("You need to supply a urlpath."); |
408 | 0 | return NULL; |
409 | 0 | } |
410 | | |
411 | 0 | bool retry_on_race = frame_locking_requested(udio); |
412 | 0 | const int max_attempts = retry_on_race ? 50 : 1; |
413 | 0 | blosc2_frame_s* frame = frame_from_file_offset_retrying(urlpath, udio, offset, max_attempts); |
414 | 0 | if (frame == NULL) { |
415 | 0 | blosc2_io_cb *io_cb = blosc2_get_io_cb(udio->id); |
416 | 0 | if (io_cb == NULL) { |
417 | 0 | BLOSC_TRACE_ERROR("Error getting the input/output API"); |
418 | 0 | return NULL; |
419 | 0 | } |
420 | 0 | int rc = io_cb->destroy(udio->params); |
421 | 0 | if (rc < 0) { |
422 | 0 | BLOSC_TRACE_ERROR("Cannot destroy the input/output object."); |
423 | 0 | } |
424 | 0 | return NULL; |
425 | 0 | } |
426 | | // Guard the initial read of the frame against concurrent writers |
427 | 0 | if (frame_lock(frame, false) < 0) { |
428 | 0 | frame_free(frame); |
429 | 0 | return NULL; |
430 | 0 | } |
431 | 0 | if (frame->force_refresh) { |
432 | | // The bootstrap read in frame_from_file_offset() happened before the lock |
433 | | // acquisition above, so a writer may have mutated the frame in between |
434 | | // (the generation counter cannot tell a first acquisition from a raced |
435 | | // one, so this also triggers on every open of a previously-mutated frame; |
436 | | // one extra header read at open time is a fair price). Re-read the frame |
437 | | // now, taking the fresh handle's own shared lock *before* releasing the |
438 | | // current one so that no writer can interleave. Same transient race as |
439 | | // the bootstrap read above, so the same bounded retry applies. |
440 | 0 | blosc2_frame_s* fresh = frame_from_file_offset_retrying(urlpath, udio, offset, max_attempts); |
441 | 0 | int lock_rc = (fresh == NULL) ? BLOSC2_ERROR_FILE_READ : frame_lock(fresh, false); |
442 | 0 | frame_free(frame); // closing its lock fd releases the old shared lock |
443 | 0 | if (fresh == NULL) { |
444 | 0 | return NULL; |
445 | 0 | } |
446 | 0 | if (lock_rc < 0) { |
447 | 0 | frame_free(fresh); |
448 | 0 | return NULL; |
449 | 0 | } |
450 | | // The re-read ran under a continuously-held shared lock, so it is |
451 | | // current: the seq mismatch of a first acquisition is not a mutation |
452 | 0 | fresh->force_refresh = false; |
453 | 0 | frame = fresh; |
454 | 0 | } |
455 | 0 | blosc2_schunk* schunk = frame_to_schunk(frame, false, udio); |
456 | 0 | if (schunk == NULL) { |
457 | 0 | BLOSC_TRACE_ERROR("Error converting frame to super-chunk"); |
458 | | // frame_to_schunk() has already freed the frame on failure; closing its |
459 | | // lock fd released the OS lock, so no frame_unlock() here |
460 | 0 | return NULL; |
461 | 0 | } |
462 | 0 | frame_unlock(frame); |
463 | | |
464 | | // Set the storage with proper defaults |
465 | 0 | size_t pathlen = strlen(urlpath); |
466 | 0 | schunk->storage->urlpath = malloc(pathlen + 1); |
467 | 0 | strcpy(schunk->storage->urlpath, urlpath); |
468 | 0 | schunk->storage->contiguous = !frame->sframe; |
469 | |
|
470 | 0 | return schunk; |
471 | 0 | } |
472 | | |
473 | 0 | blosc2_schunk* blosc2_schunk_open(const char* urlpath) { |
474 | 0 | return blosc2_schunk_open_udio(urlpath, &BLOSC2_IO_DEFAULTS); |
475 | 0 | } |
476 | | |
477 | 0 | blosc2_schunk* blosc2_schunk_open_offset(const char* urlpath, int64_t offset) { |
478 | 0 | return blosc2_schunk_open_offset_udio(urlpath, offset, &BLOSC2_IO_DEFAULTS); |
479 | 0 | } |
480 | | |
481 | 0 | int64_t blosc2_schunk_to_buffer(blosc2_schunk* schunk, uint8_t** dest, bool* needs_free) { |
482 | 0 | blosc2_frame_s* frame; |
483 | 0 | int64_t cframe_len; |
484 | | |
485 | | // Initialize defaults in case of errors |
486 | 0 | *dest = NULL; |
487 | 0 | *needs_free = false; |
488 | |
|
489 | 0 | if ((schunk->storage->contiguous == true) && (schunk->storage->urlpath == NULL)) { |
490 | 0 | frame = (blosc2_frame_s*)(schunk->frame); |
491 | 0 | *dest = frame->cframe; |
492 | 0 | cframe_len = frame->len; |
493 | 0 | *needs_free = false; |
494 | 0 | } |
495 | 0 | else { |
496 | | // Copy to a contiguous storage |
497 | 0 | blosc2_storage frame_storage = {.contiguous=true}; |
498 | 0 | blosc2_schunk* schunk_copy = blosc2_schunk_copy(schunk, &frame_storage); |
499 | 0 | if (schunk_copy == NULL) { |
500 | 0 | BLOSC_TRACE_ERROR("Error during the conversion of schunk to buffer."); |
501 | 0 | return BLOSC2_ERROR_SCHUNK_COPY; |
502 | 0 | } |
503 | 0 | frame = (blosc2_frame_s*)(schunk_copy->frame); |
504 | 0 | *dest = frame->cframe; |
505 | 0 | cframe_len = frame->len; |
506 | 0 | *needs_free = true; |
507 | 0 | frame->avoid_cframe_free = true; |
508 | 0 | blosc2_schunk_free(schunk_copy); |
509 | 0 | } |
510 | | |
511 | 0 | return cframe_len; |
512 | |
|
513 | 0 | } |
514 | | |
515 | | |
516 | | /* Write an in-memory frame out to a file. */ |
517 | 0 | int64_t frame_to_file(blosc2_frame_s* frame, const char* urlpath) { |
518 | 0 | blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); |
519 | 0 | if (io_cb == NULL) { |
520 | 0 | BLOSC_TRACE_ERROR("Error getting the input/output API"); |
521 | 0 | return BLOSC2_ERROR_PLUGIN_IO; |
522 | 0 | } |
523 | 0 | void* fp = io_cb->open(urlpath, "wb", frame->schunk->storage->io->params); |
524 | 0 | if (fp == NULL) { |
525 | 0 | BLOSC_TRACE_ERROR("Cannot open %s for writing.", urlpath); |
526 | 0 | return BLOSC2_ERROR_FILE_OPEN; |
527 | 0 | } |
528 | 0 | int64_t io_pos = 0; |
529 | 0 | int64_t nitems = io_cb->write(frame->cframe, frame->len, 1, io_pos, fp); |
530 | 0 | io_cb->close(fp); |
531 | 0 | if (nitems != 1) { |
532 | 0 | BLOSC_TRACE_ERROR("Cannot write the frame to %s.", urlpath); |
533 | 0 | return BLOSC2_ERROR_FILE_WRITE; |
534 | 0 | } |
535 | 0 | return nitems * frame->len; |
536 | 0 | } |
537 | | |
538 | | |
539 | | /* Append an in-memory frame to a file. */ |
540 | 0 | int64_t append_frame_to_file(blosc2_frame_s* frame, const char* urlpath) { |
541 | 0 | blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); |
542 | 0 | if (io_cb == NULL) { |
543 | 0 | BLOSC_TRACE_ERROR("Error getting the input/output API"); |
544 | 0 | return BLOSC2_ERROR_PLUGIN_IO; |
545 | 0 | } |
546 | | /* "rb+" rather than "ab": the write below passes an explicit position, and |
547 | | POSIX ignores it on an O_APPEND descriptor (every write lands at EOF) |
548 | | while Windows honours it. Without O_APPEND the position means the same |
549 | | thing on both. "rb+" will not create, so create the file first when it |
550 | | is not there yet -- which is what "ab" used to cover. Creating with "ab" |
551 | | rather than "wb+": should the "rb+" below have failed for some reason |
552 | | other than a missing file, "ab" leaves its contents alone where "wb+" |
553 | | would truncate them away. |
554 | | |
555 | | Note this gives up O_APPEND's atomic land-at-EOF: two processes appending |
556 | | to the same file concurrently can now write at the same offset. They |
557 | | could not use the result before either -- the offset returned here comes |
558 | | from a size() that the other process invalidates just the same -- and |
559 | | Windows never had the atomicity to begin with, since it honours the |
560 | | explicit position even on an append handle. */ |
561 | 0 | void* fp = io_cb->open(urlpath, "rb+", frame->schunk->storage->io->params); |
562 | 0 | if (fp == NULL) { |
563 | 0 | fp = io_cb->open(urlpath, "ab", frame->schunk->storage->io->params); |
564 | 0 | if (fp != NULL) { |
565 | 0 | io_cb->close(fp); |
566 | 0 | fp = io_cb->open(urlpath, "rb+", frame->schunk->storage->io->params); |
567 | 0 | } |
568 | 0 | } |
569 | 0 | if (fp == NULL) { |
570 | 0 | BLOSC_TRACE_ERROR("Cannot open %s for appending.", urlpath); |
571 | 0 | return BLOSC2_ERROR_FILE_OPEN; |
572 | 0 | } |
573 | | |
574 | 0 | int64_t io_pos = io_cb->size(fp); |
575 | 0 | if (io_pos < 0) { |
576 | 0 | io_cb->close(fp); |
577 | 0 | BLOSC_TRACE_ERROR("Cannot determine the size of %s.", urlpath); |
578 | 0 | return BLOSC2_ERROR_FILE_READ; |
579 | 0 | } |
580 | 0 | int64_t nitems = io_cb->write(frame->cframe, frame->len, 1, io_pos, fp); |
581 | 0 | io_cb->close(fp); |
582 | 0 | if (nitems != 1) { |
583 | 0 | BLOSC_TRACE_ERROR("Cannot append the frame to %s.", urlpath); |
584 | 0 | return BLOSC2_ERROR_FILE_WRITE; |
585 | 0 | } |
586 | 0 | return io_pos; |
587 | 0 | } |
588 | | |
589 | | |
590 | | /* Write super-chunk out to a file. */ |
591 | 0 | int64_t blosc2_schunk_to_file(blosc2_schunk* schunk, const char* urlpath) { |
592 | 0 | if (urlpath == NULL) { |
593 | 0 | BLOSC_TRACE_ERROR("urlpath cannot be NULL"); |
594 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
595 | 0 | } |
596 | | |
597 | | // Accelerated path for in-memory frames |
598 | 0 | if (schunk->storage->contiguous && schunk->storage->urlpath == NULL) { |
599 | 0 | int64_t len = frame_to_file((blosc2_frame_s*)(schunk->frame), urlpath); |
600 | 0 | if (len <= 0) { |
601 | 0 | BLOSC_TRACE_ERROR("Error writing to file"); |
602 | 0 | return len; |
603 | 0 | } |
604 | 0 | return len; |
605 | 0 | } |
606 | | |
607 | | // Copy to a contiguous file |
608 | 0 | blosc2_storage frame_storage = {.contiguous=true, .urlpath=(char*)urlpath}; |
609 | 0 | blosc2_schunk* schunk_copy = blosc2_schunk_copy(schunk, &frame_storage); |
610 | 0 | if (schunk_copy == NULL) { |
611 | 0 | BLOSC_TRACE_ERROR("Error during the conversion of schunk to buffer."); |
612 | 0 | return BLOSC2_ERROR_SCHUNK_COPY; |
613 | 0 | } |
614 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)(schunk_copy->frame); |
615 | 0 | int64_t frame_len = frame->len; |
616 | 0 | blosc2_schunk_free(schunk_copy); |
617 | 0 | return frame_len; |
618 | 0 | } |
619 | | |
620 | | |
621 | | /* Append a super-chunk to a file. */ |
622 | 0 | int64_t blosc2_schunk_append_file(blosc2_schunk* schunk, const char* urlpath) { |
623 | 0 | if (urlpath == NULL) { |
624 | 0 | BLOSC_TRACE_ERROR("urlpath cannot be NULL"); |
625 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
626 | 0 | } |
627 | | |
628 | | // Accelerated path for in-memory frames |
629 | 0 | if (schunk->storage->contiguous && schunk->storage->urlpath == NULL) { |
630 | 0 | int64_t offset = append_frame_to_file((blosc2_frame_s*)(schunk->frame), urlpath); |
631 | 0 | if (offset <= 0) { |
632 | 0 | BLOSC_TRACE_ERROR("Error writing to file"); |
633 | 0 | return offset; |
634 | 0 | } |
635 | 0 | return offset; |
636 | 0 | } |
637 | | |
638 | | // Copy to a contiguous file |
639 | 0 | blosc2_storage frame_storage = {.contiguous=true, .urlpath=NULL}; |
640 | 0 | blosc2_schunk* schunk_copy = blosc2_schunk_copy(schunk, &frame_storage); |
641 | 0 | if (schunk_copy == NULL) { |
642 | 0 | BLOSC_TRACE_ERROR("Error during the conversion of schunk to buffer."); |
643 | 0 | return BLOSC2_ERROR_SCHUNK_COPY; |
644 | 0 | } |
645 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)(schunk_copy->frame); |
646 | 0 | int64_t offset = append_frame_to_file(frame, urlpath); |
647 | 0 | blosc2_schunk_free(schunk_copy); |
648 | 0 | return offset; |
649 | 0 | } |
650 | | |
651 | | |
652 | | /* Free all memory from a super-chunk. */ |
653 | 12.3k | void schunk_free_metalayers(blosc2_schunk *schunk) { |
654 | 12.3k | for (int i = 0; i < schunk->nmetalayers; i++) { |
655 | 16 | if (schunk->metalayers[i] != NULL) { |
656 | 6 | free(schunk->metalayers[i]->name); |
657 | 6 | free(schunk->metalayers[i]->content); |
658 | 6 | free(schunk->metalayers[i]); |
659 | 6 | schunk->metalayers[i] = NULL; |
660 | 6 | } |
661 | 16 | } |
662 | 12.3k | schunk->nmetalayers = 0; |
663 | 12.3k | } |
664 | | |
665 | | |
666 | 12.3k | void schunk_free_vlmetalayers(blosc2_schunk *schunk) { |
667 | 43.3k | for (int i = 0; i < schunk->nvlmetalayers; i++) { |
668 | 30.9k | if (schunk->vlmetalayers[i] != NULL) { |
669 | 333 | free(schunk->vlmetalayers[i]->name); |
670 | 333 | free(schunk->vlmetalayers[i]->content); |
671 | 333 | free(schunk->vlmetalayers[i]); |
672 | 333 | schunk->vlmetalayers[i] = NULL; |
673 | 333 | } |
674 | 30.9k | } |
675 | 12.3k | schunk->nvlmetalayers = 0; |
676 | 12.3k | } |
677 | | |
678 | | |
679 | 12.3k | int blosc2_schunk_free(blosc2_schunk *schunk) { |
680 | 12.3k | int err = 0; |
681 | | |
682 | | // If it is a view, the data belongs to original array and should not be freed |
683 | 12.3k | if (schunk->data != NULL && !schunk->view) { |
684 | 52.8k | for (int i = 0; i < schunk->nchunks; i++) { |
685 | 49.7k | free(schunk->data[i]); |
686 | 49.7k | } |
687 | 3.09k | free(schunk->data); |
688 | 3.09k | } |
689 | 12.3k | if (schunk->cctx != NULL) |
690 | 12.3k | blosc2_free_ctx(schunk->cctx); |
691 | 12.3k | if (schunk->dctx != NULL) |
692 | 12.3k | blosc2_free_ctx(schunk->dctx); |
693 | 12.3k | if (schunk->blockshape != NULL) |
694 | 0 | free(schunk->blockshape); |
695 | | |
696 | 12.3k | schunk_free_metalayers(schunk); |
697 | | |
698 | 12.3k | if (schunk->storage != NULL) { |
699 | 12.3k | blosc2_io_cb *io_cb = blosc2_get_io_cb(schunk->storage->io->id); |
700 | 12.3k | if (io_cb != NULL) { |
701 | 12.3k | int rc = io_cb->destroy(schunk->storage->io->params); |
702 | 12.3k | if (rc < 0) { |
703 | 0 | BLOSC_TRACE_ERROR("Could not free the I/O resources."); |
704 | 0 | err = 1; |
705 | 0 | } |
706 | 12.3k | } |
707 | | |
708 | 12.3k | if (schunk->storage->urlpath != NULL) { |
709 | 0 | free(schunk->storage->urlpath); |
710 | 0 | } |
711 | 12.3k | free(schunk->storage->cparams); |
712 | 12.3k | free(schunk->storage->dparams); |
713 | 12.3k | free(schunk->storage->io); |
714 | 12.3k | free(schunk->storage); |
715 | 12.3k | } |
716 | | |
717 | | // If it is a view, the frame belongs to original array and should not be freed |
718 | 12.3k | if (schunk->frame != NULL && !schunk->view) { |
719 | 9.27k | frame_free((blosc2_frame_s *) schunk->frame); |
720 | 9.27k | } |
721 | | |
722 | 12.3k | schunk_free_vlmetalayers(schunk); |
723 | | |
724 | 12.3k | free(schunk); |
725 | | |
726 | 12.3k | return err; |
727 | 12.3k | } |
728 | | |
729 | | |
730 | | /* Create a super-chunk out of a contiguous frame buffer */ |
731 | 9.34k | blosc2_schunk* blosc2_schunk_from_buffer(uint8_t *cframe, int64_t len, bool copy) { |
732 | | // Check that the buffer actually comes from a cframe |
733 | 9.34k | if (cframe == NULL || len < FRAME_HEADER_MINLEN) { |
734 | 0 | return NULL; |
735 | 0 | } |
736 | 9.34k | char *magic_number = (char *)cframe + FRAME_HEADER_MAGIC; |
737 | 9.34k | if (memcmp(magic_number, "b2frame", sizeof("b2frame")) != 0) { |
738 | 52 | return NULL; |
739 | 52 | } |
740 | 9.29k | blosc2_frame_s* frame = frame_from_cframe(cframe, len, false); |
741 | 9.29k | if (frame == NULL) { |
742 | 11 | return NULL; |
743 | 11 | } |
744 | 9.27k | blosc2_schunk* schunk = frame_to_schunk(frame, copy, &BLOSC2_IO_DEFAULTS); |
745 | 9.27k | if (schunk && copy) { |
746 | | // Super-chunk has its own copy of frame |
747 | 0 | frame_free(frame); |
748 | 0 | } |
749 | 9.27k | return schunk; |
750 | 9.29k | } |
751 | | |
752 | | |
753 | | /* Set whether freeing this super-chunk should avoid freeing its contiguous frame buffer */ |
754 | 0 | void blosc2_schunk_avoid_cframe_free(blosc2_schunk *schunk, bool avoid_cframe_free) { |
755 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
756 | 0 | if (frame) { |
757 | 0 | frame_avoid_cframe_free(frame, avoid_cframe_free); |
758 | 0 | } |
759 | 0 | } |
760 | | |
761 | | |
762 | | /* Hold the exclusive frame lock across several operations: the operations |
763 | | between blosc2_schunk_lock() and blosc2_schunk_unlock() nest on the |
764 | | already-held lock (via the frame's lock depth counter) instead of |
765 | | re-acquiring it, so the whole bracket is atomic against other handles. |
766 | | No-ops (success) when locking is not enabled on the handle. */ |
767 | 0 | int blosc2_schunk_lock(blosc2_schunk *schunk) { |
768 | 0 | return frame_lock((blosc2_frame_s*)schunk->frame, true); |
769 | 0 | } |
770 | | |
771 | | |
772 | 0 | int blosc2_schunk_unlock(blosc2_schunk *schunk) { |
773 | 0 | return frame_unlock((blosc2_frame_s*)schunk->frame); |
774 | 0 | } |
775 | | |
776 | | |
777 | | /* Re-sync the cached counters (nchunks, nbytes, cbytes) and metalayers of a |
778 | | disk-based super-chunk when another handle changed it behind our back. |
779 | | A no-op for in-memory super-chunks. */ |
780 | 0 | int blosc2_schunk_refresh(blosc2_schunk *schunk) { |
781 | 0 | if (schunk == NULL) { |
782 | 0 | return BLOSC2_ERROR_NULL_POINTER; |
783 | 0 | } |
784 | 0 | if (schunk->frame == NULL) { |
785 | 0 | return 0; |
786 | 0 | } |
787 | 0 | return frame_check_stale((blosc2_frame_s *) schunk->frame); |
788 | 0 | } |
789 | | |
790 | | |
791 | | /* Fill an empty frame with special values (fast path). */ |
792 | | int64_t blosc2_schunk_fill_special(blosc2_schunk* schunk, int64_t nitems, int special_value, |
793 | 0 | int32_t chunksize) { |
794 | 0 | if (nitems == 0) { |
795 | 0 | return 0; |
796 | 0 | } |
797 | | |
798 | 0 | int32_t typesize = schunk->typesize; |
799 | 0 | if (nitems < 0 || chunksize <= 0 || typesize <= 0) { |
800 | 0 | BLOSC_TRACE_ERROR("Invalid special fill parameters."); |
801 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
802 | 0 | } |
803 | | |
804 | 0 | if ((schunk->nbytes > 0) || (schunk->cbytes > 0)) { |
805 | 0 | BLOSC_TRACE_ERROR("Filling with special values only works on empty super-chunks"); |
806 | 0 | return BLOSC2_ERROR_FRAME_SPECIAL; |
807 | 0 | } |
808 | | |
809 | | // Compute the number of chunks and the length of the offsets chunk |
810 | 0 | int32_t chunkitems = chunksize / typesize; |
811 | 0 | if (chunkitems <= 0) { |
812 | 0 | BLOSC_TRACE_ERROR("chunksize must be >= typesize for special fill."); |
813 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
814 | 0 | } |
815 | 0 | int64_t nchunks = nitems / chunkitems; |
816 | 0 | if (nchunks > INT_MAX) { |
817 | 0 | BLOSC_TRACE_ERROR("nitems is too large. Try increasing the chunksize."); |
818 | 0 | return BLOSC2_ERROR_SCHUNK_SPECIAL; |
819 | 0 | } |
820 | 0 | if (nitems > INT64_MAX / typesize) { |
821 | 0 | BLOSC_TRACE_ERROR("nitems is too large for nbytes accounting."); |
822 | 0 | return BLOSC2_ERROR_SCHUNK_SPECIAL; |
823 | 0 | } |
824 | 0 | int32_t leftover_items = (int32_t)(nitems % chunkitems); |
825 | |
|
826 | 0 | if (schunk->frame == NULL) { |
827 | | // Build the special chunks |
828 | 0 | int32_t leftover_size = leftover_items * typesize; |
829 | 0 | void* chunk = malloc(BLOSC_EXTENDED_HEADER_LENGTH); |
830 | 0 | void* chunk2 = malloc(BLOSC_EXTENDED_HEADER_LENGTH); |
831 | 0 | blosc2_cparams* cparams; |
832 | 0 | blosc2_schunk_get_cparams(schunk, &cparams); |
833 | 0 | int csize, csize2; |
834 | 0 | switch (special_value) { |
835 | 0 | case BLOSC2_SPECIAL_ZERO: |
836 | 0 | csize = blosc2_chunk_zeros(*cparams, chunksize, chunk, BLOSC_EXTENDED_HEADER_LENGTH); |
837 | 0 | csize2 = blosc2_chunk_zeros(*cparams, leftover_size, chunk2, BLOSC_EXTENDED_HEADER_LENGTH); |
838 | 0 | break; |
839 | 0 | case BLOSC2_SPECIAL_UNINIT: |
840 | 0 | csize = blosc2_chunk_uninit(*cparams, chunksize, chunk, BLOSC_EXTENDED_HEADER_LENGTH); |
841 | 0 | csize2 = blosc2_chunk_uninit(*cparams, leftover_size, chunk2, BLOSC_EXTENDED_HEADER_LENGTH); |
842 | 0 | break; |
843 | 0 | case BLOSC2_SPECIAL_NAN: |
844 | 0 | csize = blosc2_chunk_nans(*cparams, chunksize, chunk, BLOSC_EXTENDED_HEADER_LENGTH); |
845 | 0 | csize2 = blosc2_chunk_nans(*cparams, leftover_size, chunk2, BLOSC_EXTENDED_HEADER_LENGTH); |
846 | 0 | break; |
847 | 0 | default: |
848 | 0 | BLOSC_TRACE_ERROR("Only zeros, NaNs or non-initialized values are supported."); |
849 | 0 | return BLOSC2_ERROR_SCHUNK_SPECIAL; |
850 | 0 | } |
851 | 0 | free(cparams); |
852 | 0 | if (csize < 0 || csize2 < 0) { |
853 | 0 | BLOSC_TRACE_ERROR("Error creating special chunks."); |
854 | 0 | return BLOSC2_ERROR_SCHUNK_SPECIAL; |
855 | 0 | } |
856 | | |
857 | 0 | for (int nchunk = 0; nchunk < nchunks; nchunk++) { |
858 | 0 | int64_t nchunk_ = blosc2_schunk_append_chunk(schunk, chunk, true); |
859 | 0 | if (nchunk_ != nchunk + 1) { |
860 | 0 | BLOSC_TRACE_ERROR("Error appending special chunks."); |
861 | 0 | return BLOSC2_ERROR_SCHUNK_SPECIAL; |
862 | 0 | } |
863 | 0 | } |
864 | | |
865 | 0 | if (leftover_items) { |
866 | 0 | int64_t nchunk_ = blosc2_schunk_append_chunk(schunk, chunk2, true); |
867 | 0 | if (nchunk_ != nchunks + 1) { |
868 | 0 | BLOSC_TRACE_ERROR("Error appending last special chunk."); |
869 | 0 | return BLOSC2_ERROR_SCHUNK_SPECIAL; |
870 | 0 | } |
871 | 0 | } |
872 | 0 | free(chunk); |
873 | 0 | free(chunk2); |
874 | 0 | } |
875 | 0 | else { |
876 | | /* Fill an empty frame with special values (fast path). */ |
877 | 0 | blosc2_frame_s *frame = (blosc2_frame_s *) schunk->frame; |
878 | 0 | int64_t total_chunks = nchunks + (leftover_items ? 1 : 0); |
879 | 0 | if (!blosc2_nchunks_to_offsets_nbytes(total_chunks, NULL)) { |
880 | 0 | BLOSC_TRACE_ERROR("Too many chunks for frame offsets representation."); |
881 | 0 | return BLOSC2_ERROR_FRAME_SPECIAL; |
882 | 0 | } |
883 | | |
884 | 0 | int64_t old_nchunks = schunk->nchunks; |
885 | 0 | int64_t old_nbytes = schunk->nbytes; |
886 | 0 | int32_t old_chunksize = schunk->chunksize; |
887 | | |
888 | | /* Update counters (necessary for the frame_fill_special() logic) */ |
889 | 0 | if (leftover_items) { |
890 | 0 | nchunks += 1; |
891 | 0 | } |
892 | 0 | schunk->chunksize = chunksize; |
893 | 0 | schunk->nchunks = nchunks; |
894 | 0 | schunk->nbytes = nitems * typesize; |
895 | 0 | int rc = frame_lock(frame, true); |
896 | 0 | if (rc < 0) { |
897 | 0 | schunk->chunksize = old_chunksize; |
898 | 0 | schunk->nchunks = old_nchunks; |
899 | 0 | schunk->nbytes = old_nbytes; |
900 | 0 | return rc; |
901 | 0 | } |
902 | 0 | int64_t frame_len = frame_fill_special(frame, nitems, special_value, chunksize, schunk); |
903 | 0 | frame_unlock(frame); |
904 | 0 | if (frame_len < 0) { |
905 | 0 | schunk->chunksize = old_chunksize; |
906 | 0 | schunk->nchunks = old_nchunks; |
907 | 0 | schunk->nbytes = old_nbytes; |
908 | 0 | BLOSC_TRACE_ERROR("Error creating special frame."); |
909 | 0 | return frame_len; |
910 | 0 | } |
911 | 0 | } |
912 | | |
913 | 0 | return schunk->nchunks; |
914 | 0 | } |
915 | | |
916 | 46.6k | static int schunk_get_chunk_nbytes(blosc2_schunk *schunk, int64_t nchunk, int32_t *chunk_nbytes) { |
917 | 46.6k | int rc; |
918 | 46.6k | if (schunk->frame == NULL) { |
919 | 46.6k | return blosc2_cbuffer_sizes(schunk->data[nchunk], chunk_nbytes, NULL, NULL); |
920 | 46.6k | } |
921 | | |
922 | 46.6k | bool needs_free; |
923 | 0 | uint8_t *chunk; |
924 | 0 | rc = frame_get_lazychunk((blosc2_frame_s *)schunk->frame, nchunk, &chunk, &needs_free); |
925 | 0 | if (rc < 0) { |
926 | 0 | return rc; |
927 | 0 | } |
928 | 0 | rc = blosc2_cbuffer_sizes(chunk, chunk_nbytes, NULL, NULL); |
929 | 0 | if (needs_free) { |
930 | 0 | free(chunk); |
931 | 0 | } |
932 | 0 | return rc; |
933 | 0 | } |
934 | | |
935 | | /* The flags2 byte lives in the extended header, at offset 0x1e, so it only |
936 | | exists when the chunk actually carries one. Whether it does is recorded in |
937 | | the flags byte, the same way read_chunk_header() decides it -- not in the |
938 | | chunk size: a Blosc1-style chunk with 16 or more bytes of payload is 32 bytes |
939 | | or longer, yet offset 0x1e is compressed data there, and reading it as flags2 |
940 | | can mark a schunk as using variable-length blocks out of thin air. The size |
941 | | check stays as the bound that keeps a short chunk from being over-read. */ |
942 | 96.3k | static uint8_t get_chunk_flags2(const uint8_t *chunk, int32_t chunk_cbytes) { |
943 | 96.3k | uint8_t flags = chunk[BLOSC2_CHUNK_FLAGS]; |
944 | 96.3k | bool extended_header = (flags & BLOSC_DOSHUFFLE) && (flags & BLOSC_DOBITSHUFFLE); |
945 | 96.3k | if (!extended_header || chunk_cbytes < BLOSC_EXTENDED_HEADER_LENGTH) { |
946 | 0 | return 0; |
947 | 0 | } |
948 | 96.3k | return chunk[BLOSC2_CHUNK_BLOSC2_FLAGS2]; |
949 | 96.3k | } |
950 | | |
951 | 46.6k | static int schunk_get_chunk_flags2(blosc2_schunk *schunk, int64_t nchunk, uint8_t *chunk_flags2) { |
952 | 46.6k | if (schunk->frame == NULL) { |
953 | 46.6k | int32_t chunk_cbytes; |
954 | 46.6k | int rc = blosc2_cbuffer_sizes(schunk->data[nchunk], NULL, &chunk_cbytes, NULL); |
955 | 46.6k | if (rc < 0) { |
956 | 0 | return rc; |
957 | 0 | } |
958 | 46.6k | *chunk_flags2 = get_chunk_flags2(schunk->data[nchunk], chunk_cbytes); |
959 | 46.6k | return 0; |
960 | 46.6k | } |
961 | | |
962 | 46.6k | bool needs_free; |
963 | 0 | uint8_t *chunk; |
964 | 0 | int rc = frame_get_chunk((blosc2_frame_s *)schunk->frame, nchunk, &chunk, &needs_free); |
965 | 0 | if (rc < 0) { |
966 | 0 | return rc; |
967 | 0 | } |
968 | 0 | *chunk_flags2 = get_chunk_flags2(chunk, rc); |
969 | 0 | if (needs_free) { |
970 | 0 | free(chunk); |
971 | 0 | } |
972 | 0 | return 0; |
973 | 0 | } |
974 | | |
975 | | /* Append an existing chunk into a super-chunk. */ |
976 | 49.7k | static int64_t schunk_append_chunk_unlocked(blosc2_schunk *schunk, uint8_t *chunk, bool copy) { |
977 | 49.7k | int32_t chunk_nbytes; |
978 | 49.7k | int32_t chunk_cbytes; |
979 | 49.7k | int64_t nchunks = schunk->nchunks; |
980 | | |
981 | 49.7k | int rc = blosc2_cbuffer_sizes(chunk, &chunk_nbytes, &chunk_cbytes, NULL); |
982 | 49.7k | if (rc < 0) { |
983 | 0 | return rc; |
984 | 0 | } |
985 | 49.7k | uint8_t flags2 = get_chunk_flags2(chunk, chunk_cbytes); |
986 | 49.7k | bool chunk_vlblocks = (flags2 & BLOSC2_VL_BLOCKS) != 0; |
987 | 49.7k | if (nchunks > 0) { |
988 | 46.6k | uint8_t first_flags2; |
989 | 46.6k | rc = schunk_get_chunk_flags2(schunk, 0, &first_flags2); |
990 | 46.6k | if (rc < 0) { |
991 | 0 | return rc; |
992 | 0 | } |
993 | 46.6k | if (((first_flags2 & BLOSC2_VL_BLOCKS) != 0) != chunk_vlblocks) { |
994 | 0 | BLOSC_TRACE_ERROR("schunks cannot mix regular chunks and VL-block chunks."); |
995 | 0 | return BLOSC2_ERROR_CHUNK_APPEND; |
996 | 0 | } |
997 | 46.6k | } |
998 | 3.09k | else { |
999 | 3.09k | schunk->flags2 = flags2; |
1000 | 3.09k | } |
1001 | | |
1002 | 49.7k | int32_t chunksize = schunk->chunksize; |
1003 | 49.7k | bool variable_chunksize = (chunksize == 0); |
1004 | 49.7k | if (chunksize == -1) { |
1005 | 3.09k | schunk->chunksize = chunk_nbytes; // The super-chunk is initialized now |
1006 | 3.09k | chunksize = schunk->chunksize; |
1007 | 3.09k | } |
1008 | 49.7k | if (!variable_chunksize && nchunks > 0) { |
1009 | 46.6k | int32_t last_nbytes; |
1010 | 46.6k | rc = schunk_get_chunk_nbytes(schunk, nchunks - 1, &last_nbytes); |
1011 | 46.6k | if (rc < 0) { |
1012 | 0 | return rc; |
1013 | 0 | } |
1014 | 46.6k | if (last_nbytes < chunksize || chunk_nbytes > chunksize) { |
1015 | 0 | variable_chunksize = true; |
1016 | 0 | schunk->chunksize = 0; |
1017 | 0 | } |
1018 | 46.6k | } |
1019 | 49.7k | if (!variable_chunksize && chunksize > 0 && chunk_nbytes > chunksize) { |
1020 | 0 | BLOSC_TRACE_ERROR("Appending chunks that have different lengths in the same schunk " |
1021 | 0 | "is not supported yet: %d > %d.", chunk_nbytes, chunksize); |
1022 | 0 | return BLOSC2_ERROR_CHUNK_APPEND; |
1023 | 0 | } |
1024 | | |
1025 | | /* Update counters */ |
1026 | 49.7k | schunk->current_nchunk = nchunks; |
1027 | 49.7k | schunk->nchunks = nchunks + 1; |
1028 | 49.7k | schunk->nbytes += chunk_nbytes; |
1029 | 49.7k | if (schunk->frame == NULL) { |
1030 | 49.7k | schunk->cbytes += chunk_cbytes; |
1031 | 49.7k | } else { |
1032 | | // A frame |
1033 | 0 | int special_value = (chunk[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK; |
1034 | 0 | switch (special_value) { |
1035 | 0 | case BLOSC2_SPECIAL_ZERO: |
1036 | 0 | case BLOSC2_SPECIAL_NAN: |
1037 | 0 | case BLOSC2_SPECIAL_UNINIT: |
1038 | 0 | schunk->cbytes += 0; |
1039 | 0 | break; |
1040 | 0 | default: |
1041 | 0 | schunk->cbytes += chunk_cbytes; |
1042 | 0 | } |
1043 | 0 | } |
1044 | | |
1045 | 49.7k | if (copy) { |
1046 | | // Make a copy of the chunk |
1047 | 0 | uint8_t *chunk_copy = malloc(chunk_cbytes); |
1048 | 0 | memcpy(chunk_copy, chunk, chunk_cbytes); |
1049 | 0 | chunk = chunk_copy; |
1050 | 0 | } |
1051 | | |
1052 | | // Update super-chunk or frame |
1053 | 49.7k | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1054 | 49.7k | if (frame == NULL) { |
1055 | 49.7k | if (!copy && (chunk_cbytes < chunk_nbytes)) { |
1056 | | // We still want to do a shrink of the chunk |
1057 | 34.1k | chunk = realloc(chunk, chunk_cbytes); |
1058 | 34.1k | } |
1059 | | |
1060 | | /* Make space for appending the copy of the chunk and do it */ |
1061 | 49.7k | if ((nchunks + 1) * sizeof(void *) > schunk->data_len) { |
1062 | | // Extend the data pointer by one memory page (4k) |
1063 | 3.13k | schunk->data_len += 4096; // must be a multiple of sizeof(void*) |
1064 | 3.13k | schunk->data = realloc(schunk->data, schunk->data_len); |
1065 | 3.13k | } |
1066 | 49.7k | schunk->data[nchunks] = chunk; |
1067 | 49.7k | } |
1068 | 0 | else { |
1069 | 0 | if (frame_append_chunk(frame, chunk, schunk) == NULL) { |
1070 | 0 | BLOSC_TRACE_ERROR("Problems appending a chunk."); |
1071 | 0 | return BLOSC2_ERROR_CHUNK_APPEND; |
1072 | 0 | } |
1073 | 0 | } |
1074 | 49.7k | return schunk->nchunks; |
1075 | 49.7k | } |
1076 | | |
1077 | | |
1078 | | /* Append an existing @p chunk to a super-chunk. */ |
1079 | 49.7k | int64_t blosc2_schunk_append_chunk(blosc2_schunk *schunk, uint8_t *chunk, bool copy) { |
1080 | 49.7k | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1081 | 49.7k | int rc = frame_lock(frame, true); |
1082 | 49.7k | if (rc < 0) { |
1083 | 0 | return rc; |
1084 | 0 | } |
1085 | | // Sync the cached counters before the delta below is applied to them, so a |
1086 | | // handle whose lock acquisition just flagged staleness does not persist |
1087 | | // wrong nbytes/cbytes/nchunks (see plans/todo-locking-swmr.md item 1). |
1088 | 49.7k | rc = frame_check_stale(frame); |
1089 | 49.7k | if (rc < 0) { |
1090 | 0 | frame_unlock(frame); |
1091 | 0 | return rc; |
1092 | 0 | } |
1093 | 49.7k | int64_t nchunks = schunk_append_chunk_unlocked(schunk, chunk, copy); |
1094 | 49.7k | frame_unlock(frame); |
1095 | 49.7k | return nchunks; |
1096 | 49.7k | } |
1097 | | |
1098 | | |
1099 | | /* Insert an existing @p chunk in a specified position on a super-chunk */ |
1100 | 0 | static int64_t schunk_insert_chunk_unlocked(blosc2_schunk *schunk, int64_t nchunk, uint8_t *chunk, bool copy) { |
1101 | 0 | int rc = validate_nchunk(schunk, nchunk, true, "blosc2_schunk_insert_chunk"); |
1102 | 0 | if (rc < 0) { |
1103 | 0 | return rc; |
1104 | 0 | } |
1105 | | |
1106 | 0 | int32_t chunk_nbytes; |
1107 | 0 | int32_t chunk_cbytes; |
1108 | 0 | int64_t nchunks = schunk->nchunks; |
1109 | |
|
1110 | 0 | rc = blosc2_cbuffer_sizes(chunk, &chunk_nbytes, &chunk_cbytes, NULL); |
1111 | 0 | if (rc < 0) { |
1112 | 0 | return rc; |
1113 | 0 | } |
1114 | 0 | uint8_t flags2 = get_chunk_flags2(chunk, chunk_cbytes); |
1115 | 0 | bool chunk_vlblocks = (flags2 & BLOSC2_VL_BLOCKS) != 0; |
1116 | 0 | if (nchunks > 0) { |
1117 | 0 | uint8_t first_flags2; |
1118 | 0 | rc = schunk_get_chunk_flags2(schunk, 0, &first_flags2); |
1119 | 0 | if (rc < 0) { |
1120 | 0 | return rc; |
1121 | 0 | } |
1122 | 0 | if (((first_flags2 & BLOSC2_VL_BLOCKS) != 0) != chunk_vlblocks) { |
1123 | 0 | BLOSC_TRACE_ERROR("schunks cannot mix regular chunks and VL-block chunks."); |
1124 | 0 | return BLOSC2_ERROR_CHUNK_INSERT; |
1125 | 0 | } |
1126 | 0 | } |
1127 | 0 | else { |
1128 | 0 | schunk->flags2 = flags2; |
1129 | 0 | } |
1130 | | |
1131 | 0 | int32_t chunksize = schunk->chunksize; |
1132 | 0 | bool variable_chunksize = (chunksize == 0); |
1133 | 0 | if (chunksize == -1) { |
1134 | 0 | schunk->chunksize = chunk_nbytes; // The super-chunk is initialized now |
1135 | 0 | chunksize = schunk->chunksize; |
1136 | 0 | } |
1137 | |
|
1138 | 0 | if (!variable_chunksize && nchunks > 0) { |
1139 | 0 | int32_t last_nbytes; |
1140 | 0 | rc = schunk_get_chunk_nbytes(schunk, nchunks - 1, &last_nbytes); |
1141 | 0 | if (rc < 0) { |
1142 | 0 | return rc; |
1143 | 0 | } |
1144 | 0 | if (last_nbytes < chunksize || chunk_nbytes > chunksize || |
1145 | 0 | (nchunk != nchunks && chunk_nbytes != chunksize)) { |
1146 | 0 | variable_chunksize = true; |
1147 | 0 | schunk->chunksize = 0; |
1148 | 0 | } |
1149 | 0 | } |
1150 | 0 | if (!variable_chunksize && chunksize > 0 && chunk_nbytes > chunksize) { |
1151 | 0 | BLOSC_TRACE_ERROR("Inserting chunks that have different lengths in the same schunk " |
1152 | 0 | "is not supported yet: %d > %d.", chunk_nbytes, chunksize); |
1153 | 0 | return BLOSC2_ERROR_CHUNK_INSERT; |
1154 | 0 | } |
1155 | | |
1156 | | /* Update counters */ |
1157 | 0 | schunk->current_nchunk = nchunk; |
1158 | 0 | schunk->nchunks = nchunks + 1; |
1159 | 0 | schunk->nbytes += chunk_nbytes; |
1160 | 0 | if (schunk->frame == NULL) { |
1161 | 0 | schunk->cbytes += chunk_cbytes; |
1162 | 0 | } else { |
1163 | | // A frame |
1164 | 0 | int special_value = (chunk[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK; |
1165 | 0 | switch (special_value) { |
1166 | 0 | case BLOSC2_SPECIAL_ZERO: |
1167 | 0 | case BLOSC2_SPECIAL_NAN: |
1168 | 0 | case BLOSC2_SPECIAL_UNINIT: |
1169 | 0 | schunk->cbytes += 0; |
1170 | 0 | break; |
1171 | 0 | default: |
1172 | 0 | schunk->cbytes += chunk_cbytes; |
1173 | 0 | } |
1174 | 0 | } |
1175 | | |
1176 | 0 | if (copy) { |
1177 | | // Make a copy of the chunk |
1178 | 0 | uint8_t *chunk_copy = malloc(chunk_cbytes); |
1179 | 0 | memcpy(chunk_copy, chunk, chunk_cbytes); |
1180 | 0 | chunk = chunk_copy; |
1181 | 0 | } |
1182 | | |
1183 | | // Update super-chunk or frame |
1184 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1185 | 0 | if (frame == NULL) { |
1186 | 0 | if (!copy && (chunk_cbytes < chunk_nbytes)) { |
1187 | | // We still want to do a shrink of the chunk |
1188 | 0 | chunk = realloc(chunk, chunk_cbytes); |
1189 | 0 | } |
1190 | | |
1191 | | // Make space for appending the copy of the chunk and do it |
1192 | 0 | if ((nchunks + 1) * sizeof(void *) > schunk->data_len) { |
1193 | | // Extend the data pointer by one memory page (4k) |
1194 | 0 | schunk->data_len += 4096; // must be a multiple of sizeof(void*) |
1195 | 0 | schunk->data = realloc(schunk->data, schunk->data_len); |
1196 | 0 | } |
1197 | | |
1198 | | // Reorder the offsets and insert the new chunk |
1199 | 0 | for (int64_t i = nchunks; i > nchunk; --i) { |
1200 | 0 | schunk->data[i] = schunk->data[i-1]; |
1201 | 0 | } |
1202 | 0 | schunk->data[nchunk] = chunk; |
1203 | 0 | } |
1204 | | |
1205 | 0 | else { |
1206 | 0 | if (frame_insert_chunk(frame, nchunk, chunk, schunk) == NULL) { |
1207 | 0 | BLOSC_TRACE_ERROR("Problems inserting a chunk in a frame."); |
1208 | 0 | return BLOSC2_ERROR_CHUNK_INSERT; |
1209 | 0 | } |
1210 | 0 | } |
1211 | 0 | return schunk->nchunks; |
1212 | 0 | } |
1213 | | |
1214 | | |
1215 | | /* Insert an existing @p chunk in a specified position on a super-chunk. */ |
1216 | 0 | int64_t blosc2_schunk_insert_chunk(blosc2_schunk *schunk, int64_t nchunk, uint8_t *chunk, bool copy) { |
1217 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1218 | 0 | int rc = frame_lock(frame, true); |
1219 | 0 | if (rc < 0) { |
1220 | 0 | return rc; |
1221 | 0 | } |
1222 | | // See blosc2_schunk_append_chunk() for why this must happen before the |
1223 | | // counter deltas below are applied. |
1224 | 0 | rc = frame_check_stale(frame); |
1225 | 0 | if (rc < 0) { |
1226 | 0 | frame_unlock(frame); |
1227 | 0 | return rc; |
1228 | 0 | } |
1229 | 0 | int64_t nchunks = schunk_insert_chunk_unlocked(schunk, nchunk, chunk, copy); |
1230 | 0 | frame_unlock(frame); |
1231 | 0 | return nchunks; |
1232 | 0 | } |
1233 | | |
1234 | | |
1235 | 0 | static int64_t schunk_update_chunk_unlocked(blosc2_schunk *schunk, int64_t nchunk, uint8_t *chunk, bool copy) { |
1236 | 0 | int rc = validate_nchunk(schunk, nchunk, false, "blosc2_schunk_update_chunk"); |
1237 | 0 | if (rc < 0) { |
1238 | 0 | return rc; |
1239 | 0 | } |
1240 | | |
1241 | 0 | int32_t chunk_nbytes; |
1242 | 0 | int32_t chunk_cbytes; |
1243 | |
|
1244 | 0 | rc = blosc2_cbuffer_sizes(chunk, &chunk_nbytes, &chunk_cbytes, NULL); |
1245 | 0 | if (rc < 0) { |
1246 | 0 | return rc; |
1247 | 0 | } |
1248 | 0 | uint8_t flags2 = get_chunk_flags2(chunk, chunk_cbytes); |
1249 | 0 | bool chunk_vlblocks = (flags2 & BLOSC2_VL_BLOCKS) != 0; |
1250 | 0 | if (schunk->nchunks > 1 || (schunk->nchunks == 1 && nchunk != 0)) { |
1251 | 0 | int64_t ref_nchunk = (nchunk == 0) ? 1 : 0; |
1252 | 0 | uint8_t ref_flags2; |
1253 | 0 | rc = schunk_get_chunk_flags2(schunk, ref_nchunk, &ref_flags2); |
1254 | 0 | if (rc < 0) { |
1255 | 0 | return rc; |
1256 | 0 | } |
1257 | 0 | if (((ref_flags2 & BLOSC2_VL_BLOCKS) != 0) != chunk_vlblocks) { |
1258 | 0 | BLOSC_TRACE_ERROR("schunks cannot mix regular chunks and VL-block chunks."); |
1259 | 0 | return BLOSC2_ERROR_CHUNK_UPDATE; |
1260 | 0 | } |
1261 | 0 | } |
1262 | 0 | else { |
1263 | 0 | schunk->flags2 = flags2; |
1264 | 0 | } |
1265 | | |
1266 | 0 | int32_t chunksize = schunk->chunksize; |
1267 | 0 | bool variable_chunksize = (chunksize == 0); |
1268 | 0 | if (chunksize == -1) { |
1269 | 0 | schunk->chunksize = chunk_nbytes; // The super-chunk is initialized now |
1270 | 0 | chunksize = schunk->chunksize; |
1271 | 0 | } |
1272 | |
|
1273 | 0 | if (!variable_chunksize && (chunk_nbytes > chunksize || |
1274 | 0 | (nchunk != schunk->nchunks - 1 && chunk_nbytes != chunksize))) { |
1275 | 0 | variable_chunksize = true; |
1276 | 0 | schunk->chunksize = 0; |
1277 | 0 | } |
1278 | 0 | if (!variable_chunksize && chunksize > 0 && chunk_nbytes > chunksize) { |
1279 | 0 | BLOSC_TRACE_ERROR("Updating chunks having different lengths in the same schunk " |
1280 | 0 | "is not supported yet: %d > %d.", chunk_nbytes, chunksize); |
1281 | 0 | return BLOSC2_ERROR_CHUNK_UPDATE; |
1282 | 0 | } |
1283 | | |
1284 | 0 | bool needs_free; |
1285 | 0 | uint8_t *chunk_old; |
1286 | 0 | int err = blosc2_schunk_get_chunk(schunk, nchunk, &chunk_old, &needs_free); |
1287 | 0 | if (err < 0) { |
1288 | 0 | BLOSC_TRACE_ERROR("%" PRId64 " chunk can not be obtained from schunk.", nchunk); |
1289 | 0 | return -1; |
1290 | 0 | } |
1291 | 0 | int32_t chunk_nbytes_old = 0; |
1292 | 0 | int32_t chunk_cbytes_old = 0; |
1293 | 0 | schunk->current_nchunk = nchunk; |
1294 | |
|
1295 | 0 | if (chunk_old != 0) { |
1296 | 0 | rc = blosc2_cbuffer_sizes(chunk_old, &chunk_nbytes_old, &chunk_cbytes_old, NULL); |
1297 | 0 | if (rc < 0) { |
1298 | 0 | return rc; |
1299 | 0 | } |
1300 | 0 | if (chunk_cbytes_old == BLOSC2_MAX_OVERHEAD) { |
1301 | 0 | chunk_cbytes_old = 0; |
1302 | 0 | } |
1303 | 0 | } |
1304 | 0 | if (needs_free) { |
1305 | 0 | free(chunk_old); |
1306 | 0 | } |
1307 | |
|
1308 | 0 | if (copy) { |
1309 | | // Make a copy of the chunk |
1310 | 0 | uint8_t *chunk_copy = malloc(chunk_cbytes); |
1311 | 0 | memcpy(chunk_copy, chunk, chunk_cbytes); |
1312 | 0 | chunk = chunk_copy; |
1313 | 0 | } |
1314 | |
|
1315 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)(schunk->frame); |
1316 | 0 | if (schunk->frame == NULL) { |
1317 | | /* Update counters */ |
1318 | 0 | schunk->nbytes += chunk_nbytes; |
1319 | 0 | schunk->nbytes -= chunk_nbytes_old; |
1320 | 0 | schunk->cbytes += chunk_cbytes; |
1321 | 0 | schunk->cbytes -= chunk_cbytes_old; |
1322 | 0 | } else { |
1323 | | // A frame |
1324 | 0 | int special_value = (chunk[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK; |
1325 | 0 | schunk->nbytes += chunk_nbytes; |
1326 | 0 | schunk->nbytes -= chunk_nbytes_old; |
1327 | 0 | switch (special_value) { |
1328 | 0 | case BLOSC2_SPECIAL_ZERO: |
1329 | 0 | case BLOSC2_SPECIAL_NAN: |
1330 | 0 | case BLOSC2_SPECIAL_UNINIT: |
1331 | 0 | schunk->cbytes -= chunk_cbytes_old; |
1332 | 0 | break; |
1333 | 0 | default: |
1334 | 0 | schunk->cbytes += chunk_cbytes; |
1335 | 0 | schunk->cbytes -= chunk_cbytes_old; |
1336 | 0 | } |
1337 | 0 | } |
1338 | | |
1339 | | // Update super-chunk or frame |
1340 | 0 | if (schunk->frame == NULL) { |
1341 | 0 | if (!copy && (chunk_cbytes < chunk_nbytes)) { |
1342 | | // We still want to do a shrink of the chunk |
1343 | 0 | chunk = realloc(chunk, chunk_cbytes); |
1344 | 0 | } |
1345 | | |
1346 | | // Free old chunk and add reference to new chunk |
1347 | 0 | if (schunk->data[nchunk] != 0) { |
1348 | 0 | free(schunk->data[nchunk]); |
1349 | 0 | } |
1350 | 0 | schunk->data[nchunk] = chunk; |
1351 | 0 | } |
1352 | 0 | else { |
1353 | 0 | if (frame_update_chunk(frame, nchunk, chunk, schunk) == NULL) { |
1354 | 0 | BLOSC_TRACE_ERROR("Problems updating a chunk in a frame."); |
1355 | 0 | return BLOSC2_ERROR_CHUNK_UPDATE; |
1356 | 0 | } |
1357 | 0 | } |
1358 | | |
1359 | 0 | return schunk->nchunks; |
1360 | 0 | } |
1361 | | |
1362 | | /* Update the chunk at a specified position of a super-chunk. */ |
1363 | 0 | int64_t blosc2_schunk_update_chunk(blosc2_schunk *schunk, int64_t nchunk, uint8_t *chunk, bool copy) { |
1364 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1365 | 0 | int rc = frame_lock(frame, true); |
1366 | 0 | if (rc < 0) { |
1367 | 0 | return rc; |
1368 | 0 | } |
1369 | 0 | int64_t nchunks = schunk_update_chunk_unlocked(schunk, nchunk, chunk, copy); |
1370 | 0 | frame_unlock(frame); |
1371 | 0 | return nchunks; |
1372 | 0 | } |
1373 | | |
1374 | | |
1375 | 0 | static int64_t schunk_delete_chunk_unlocked(blosc2_schunk *schunk, int64_t nchunk) { |
1376 | 0 | int rc = validate_nchunk(schunk, nchunk, false, "blosc2_schunk_delete_chunk"); |
1377 | 0 | if (rc < 0) { |
1378 | 0 | return rc; |
1379 | 0 | } |
1380 | | |
1381 | 0 | bool needs_free; |
1382 | 0 | uint8_t *chunk_old; |
1383 | 0 | int err = blosc2_schunk_get_chunk(schunk, nchunk, &chunk_old, &needs_free); |
1384 | 0 | if (err < 0) { |
1385 | 0 | BLOSC_TRACE_ERROR("%" PRId64 "chunk can not be obtained from schunk.", nchunk); |
1386 | 0 | return -1; |
1387 | 0 | } |
1388 | 0 | int32_t chunk_nbytes_old = 0; |
1389 | 0 | int32_t chunk_cbytes_old = 0; |
1390 | 0 | schunk->current_nchunk = nchunk; |
1391 | |
|
1392 | 0 | if (chunk_old != 0) { |
1393 | 0 | rc = blosc2_cbuffer_sizes(chunk_old, &chunk_nbytes_old, &chunk_cbytes_old, NULL); |
1394 | 0 | if (rc < 0) { |
1395 | 0 | return rc; |
1396 | 0 | } |
1397 | 0 | if (chunk_cbytes_old == BLOSC2_MAX_OVERHEAD) { |
1398 | 0 | chunk_cbytes_old = 0; |
1399 | 0 | } |
1400 | 0 | } |
1401 | 0 | if (needs_free) { |
1402 | 0 | free(chunk_old); |
1403 | 0 | } |
1404 | |
|
1405 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)(schunk->frame); |
1406 | 0 | schunk->nchunks -= 1; |
1407 | 0 | if (schunk->nchunks == 0) { |
1408 | 0 | schunk->flags2 = 0; |
1409 | 0 | } |
1410 | 0 | if (schunk->frame == NULL) { |
1411 | | /* Update counters */ |
1412 | 0 | schunk->nbytes -= chunk_nbytes_old; |
1413 | 0 | schunk->cbytes -= chunk_cbytes_old; |
1414 | 0 | } else { |
1415 | | // A frame |
1416 | 0 | schunk->nbytes -= chunk_nbytes_old; |
1417 | 0 | if (frame->sframe) { |
1418 | 0 | schunk->cbytes -= chunk_cbytes_old; |
1419 | 0 | } |
1420 | 0 | } |
1421 | | |
1422 | | // Update super-chunk or frame |
1423 | 0 | if (schunk->frame == NULL) { |
1424 | | // Free old chunk |
1425 | 0 | if (schunk->data[nchunk] != 0) { |
1426 | 0 | free(schunk->data[nchunk]); |
1427 | 0 | } |
1428 | | // Reorder the offsets and insert the new chunk |
1429 | 0 | for (int64_t i = nchunk; i < schunk->nchunks; i++) { |
1430 | 0 | schunk->data[i] = schunk->data[i + 1]; |
1431 | 0 | } |
1432 | 0 | schunk->data[schunk->nchunks] = NULL; |
1433 | |
|
1434 | 0 | } |
1435 | 0 | else { |
1436 | 0 | if (frame_delete_chunk(frame, nchunk, schunk) == NULL) { |
1437 | 0 | BLOSC_TRACE_ERROR("Problems deleting a chunk in a frame."); |
1438 | 0 | return BLOSC2_ERROR_CHUNK_UPDATE; |
1439 | 0 | } |
1440 | 0 | } |
1441 | 0 | return schunk->nchunks; |
1442 | 0 | } |
1443 | | |
1444 | | |
1445 | | /* Delete the chunk at a specified position of a super-chunk. */ |
1446 | 0 | int64_t blosc2_schunk_delete_chunk(blosc2_schunk *schunk, int64_t nchunk) { |
1447 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1448 | 0 | int rc = frame_lock(frame, true); |
1449 | 0 | if (rc < 0) { |
1450 | 0 | return rc; |
1451 | 0 | } |
1452 | 0 | int64_t nchunks = schunk_delete_chunk_unlocked(schunk, nchunk); |
1453 | 0 | frame_unlock(frame); |
1454 | 0 | return nchunks; |
1455 | 0 | } |
1456 | | |
1457 | | |
1458 | | /* Append a data buffer to a super-chunk. */ |
1459 | 49.7k | int64_t blosc2_schunk_append_buffer(blosc2_schunk *schunk, const void *src, int32_t nbytes) { |
1460 | 49.7k | uint8_t* chunk = malloc(nbytes + BLOSC2_MAX_OVERHEAD); |
1461 | 49.7k | schunk->current_nchunk = schunk->nchunks; |
1462 | | /* Compress the src buffer using super-chunk context */ |
1463 | 49.7k | int cbytes = blosc2_compress_ctx(schunk->cctx, src, nbytes, chunk, |
1464 | 49.7k | nbytes + BLOSC2_MAX_OVERHEAD); |
1465 | 49.7k | if (cbytes < 0) { |
1466 | 0 | free(chunk); |
1467 | 0 | return cbytes; |
1468 | 0 | } |
1469 | | // We don't need a copy of the chunk, as it will be shrunk if necessary |
1470 | 49.7k | int64_t nchunks = blosc2_schunk_append_chunk(schunk, chunk, false); |
1471 | 49.7k | if (nchunks < 0) { |
1472 | 0 | BLOSC_TRACE_ERROR("Error appending a buffer in super-chunk"); |
1473 | 0 | return nchunks; |
1474 | 0 | } |
1475 | | |
1476 | 49.7k | return nchunks; |
1477 | 49.7k | } |
1478 | | |
1479 | | |
1480 | | /* Decompress and return a chunk that is part of a super-chunk. */ |
1481 | | int blosc2_schunk_decompress_chunk(blosc2_schunk *schunk, int64_t nchunk, |
1482 | 25.1k | void *dest, int32_t nbytes) { |
1483 | 25.1k | int rc = validate_nchunk(schunk, nchunk, false, "blosc2_schunk_decompress_chunk"); |
1484 | 25.1k | if (rc < 0) { |
1485 | 0 | return rc; |
1486 | 0 | } |
1487 | | |
1488 | 25.1k | int32_t chunk_nbytes; |
1489 | 25.1k | int32_t chunk_cbytes; |
1490 | 25.1k | int chunksize; |
1491 | 25.1k | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1492 | | |
1493 | 25.1k | schunk->current_nchunk = nchunk; |
1494 | 25.1k | if (frame == NULL) { |
1495 | 15.3k | uint8_t* src = schunk->data[nchunk]; |
1496 | 15.3k | if (src == 0) { |
1497 | 0 | return 0; |
1498 | 0 | } |
1499 | | |
1500 | 15.3k | rc = blosc2_cbuffer_sizes(src, &chunk_nbytes, &chunk_cbytes, NULL); |
1501 | 15.3k | if (rc < 0) { |
1502 | 0 | return rc; |
1503 | 0 | } |
1504 | | |
1505 | 15.3k | if (nbytes < chunk_nbytes) { |
1506 | 490 | BLOSC_TRACE_ERROR("Buffer size is too small for the decompressed buffer " |
1507 | 490 | "('%d' bytes, but '%d' are needed).", nbytes, chunk_nbytes); |
1508 | 490 | return BLOSC2_ERROR_INVALID_PARAM; |
1509 | 490 | } |
1510 | | |
1511 | 14.8k | chunksize = blosc2_decompress_ctx(schunk->dctx, src, chunk_cbytes, dest, nbytes); |
1512 | 14.8k | if (chunksize < 0 || chunksize != chunk_nbytes) { |
1513 | 0 | BLOSC_TRACE_ERROR("Error in decompressing chunk."); |
1514 | 0 | if (chunksize < 0) |
1515 | 0 | return chunksize; |
1516 | 0 | return BLOSC2_ERROR_FAILURE; |
1517 | 0 | } |
1518 | 14.8k | } else { |
1519 | 9.79k | rc = frame_lock(frame, false); |
1520 | 9.79k | if (rc < 0) { |
1521 | 0 | return rc; |
1522 | 0 | } |
1523 | 9.79k | chunksize = frame_decompress_chunk(schunk->dctx, frame, nchunk, dest, nbytes); |
1524 | 9.79k | frame_unlock(frame); |
1525 | 9.79k | if (chunksize < 0) { |
1526 | 335 | return chunksize; |
1527 | 335 | } |
1528 | 9.79k | } |
1529 | 24.3k | return chunksize; |
1530 | 25.1k | } |
1531 | | |
1532 | | |
1533 | | /* Return a compressed chunk that is part of a super-chunk in the `chunk` parameter. |
1534 | | * If the super-chunk is backed by a frame that is disk-based, a buffer is allocated for the |
1535 | | * (compressed) chunk, and hence a free is needed. You can check if the chunk requires a free |
1536 | | * with the `needs_free` parameter. |
1537 | | * If the chunk does not need a free, it means that a pointer to the location in the super-chunk |
1538 | | * (or the backing in-memory frame) is returned in the `chunk` parameter. |
1539 | | * |
1540 | | * The size of the (compressed) chunk is returned. If some problem is detected, a negative code |
1541 | | * is returned instead. |
1542 | | */ |
1543 | 0 | int blosc2_schunk_get_chunk(blosc2_schunk *schunk, int64_t nchunk, uint8_t **chunk, bool *needs_free) { |
1544 | 0 | int rc = validate_nchunk(schunk, nchunk, false, "blosc2_schunk_get_chunk"); |
1545 | 0 | if (rc < 0) { |
1546 | 0 | return rc; |
1547 | 0 | } |
1548 | | |
1549 | 0 | if (ctx_uses_parallel_backend(schunk->dctx)) { |
1550 | 0 | blosc2_pthread_mutex_lock(&schunk->dctx->nchunk_mutex); |
1551 | 0 | schunk->current_nchunk = nchunk; |
1552 | 0 | blosc2_pthread_mutex_unlock(&schunk->dctx->nchunk_mutex); |
1553 | 0 | } |
1554 | 0 | else { |
1555 | 0 | schunk->current_nchunk = nchunk; |
1556 | 0 | } |
1557 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1558 | 0 | if (frame != NULL) { |
1559 | 0 | rc = frame_lock(frame, false); |
1560 | 0 | if (rc < 0) { |
1561 | 0 | return rc; |
1562 | 0 | } |
1563 | 0 | rc = frame_get_chunk(frame, nchunk, chunk, needs_free); |
1564 | 0 | frame_unlock(frame); |
1565 | 0 | return rc; |
1566 | 0 | } |
1567 | | |
1568 | 0 | *chunk = schunk->data[nchunk]; |
1569 | 0 | if (*chunk == 0) { |
1570 | 0 | *needs_free = 0; |
1571 | 0 | return 0; |
1572 | 0 | } |
1573 | | |
1574 | 0 | *needs_free = false; |
1575 | 0 | int32_t chunk_cbytes; |
1576 | 0 | rc = blosc2_cbuffer_sizes(*chunk, NULL, &chunk_cbytes, NULL); |
1577 | 0 | if (rc < 0) { |
1578 | 0 | return rc; |
1579 | 0 | } |
1580 | 0 | return (int)chunk_cbytes; |
1581 | 0 | } |
1582 | | |
1583 | | |
1584 | | /* Return a compressed chunk that is part of a super-chunk in the `chunk` parameter. |
1585 | | * If the super-chunk is backed by a frame that is disk-based, a buffer is allocated for the |
1586 | | * (compressed) chunk, and hence a free is needed. You can check if the chunk requires a free |
1587 | | * with the `needs_free` parameter. |
1588 | | * If the chunk does not need a free, it means that a pointer to the location in the super-chunk |
1589 | | * (or the backing in-memory frame) is returned in the `chunk` parameter. |
1590 | | * |
1591 | | * The size of the (compressed) chunk is returned. If some problem is detected, a negative code |
1592 | | * is returned instead. |
1593 | | */ |
1594 | 0 | int blosc2_schunk_get_lazychunk(blosc2_schunk *schunk, int64_t nchunk, uint8_t **chunk, bool *needs_free) { |
1595 | 0 | int rc = validate_nchunk(schunk, nchunk, false, "blosc2_schunk_get_lazychunk"); |
1596 | 0 | if (rc < 0) { |
1597 | 0 | return rc; |
1598 | 0 | } |
1599 | | |
1600 | 0 | if (ctx_uses_parallel_backend(schunk->dctx)) { |
1601 | 0 | blosc2_pthread_mutex_lock(&schunk->dctx->nchunk_mutex); |
1602 | 0 | schunk->current_nchunk = nchunk; |
1603 | 0 | blosc2_pthread_mutex_unlock(&schunk->dctx->nchunk_mutex); |
1604 | 0 | } |
1605 | 0 | else { |
1606 | 0 | schunk->current_nchunk = nchunk; |
1607 | 0 | } |
1608 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1609 | 0 | if (schunk->frame != NULL) { |
1610 | 0 | rc = frame_lock(frame, false); |
1611 | 0 | if (rc < 0) { |
1612 | 0 | return rc; |
1613 | 0 | } |
1614 | 0 | rc = frame_get_lazychunk(frame, nchunk, chunk, needs_free); |
1615 | 0 | frame_unlock(frame); |
1616 | 0 | return rc; |
1617 | 0 | } |
1618 | | |
1619 | 0 | *chunk = schunk->data[nchunk]; |
1620 | 0 | if (*chunk == 0) { |
1621 | 0 | *needs_free = 0; |
1622 | 0 | return 0; |
1623 | 0 | } |
1624 | | |
1625 | 0 | *needs_free = false; |
1626 | 0 | int32_t chunk_cbytes; |
1627 | 0 | rc = blosc2_cbuffer_sizes(*chunk, NULL, &chunk_cbytes, NULL); |
1628 | 0 | if (rc < 0) { |
1629 | 0 | return rc; |
1630 | 0 | } |
1631 | 0 | return (int)chunk_cbytes; |
1632 | 0 | } |
1633 | | |
1634 | | |
1635 | | int blosc2_schunk_get_vlblock(blosc2_schunk *schunk, int64_t nchunk, int32_t nblock, |
1636 | 0 | uint8_t **dest, int32_t *destsize) { |
1637 | 0 | if (schunk == NULL || dest == NULL || destsize == NULL) { |
1638 | 0 | BLOSC_TRACE_ERROR("schunk, dest, and destsize must not be NULL."); |
1639 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1640 | 0 | } |
1641 | | |
1642 | 0 | uint8_t *chunk = NULL; |
1643 | 0 | bool needs_free = false; |
1644 | 0 | int cbytes = blosc2_schunk_get_chunk(schunk, nchunk, &chunk, &needs_free); |
1645 | 0 | if (cbytes < 0) { |
1646 | 0 | return cbytes; |
1647 | 0 | } |
1648 | 0 | if (chunk == NULL || cbytes == 0) { |
1649 | 0 | BLOSC_TRACE_ERROR("Chunk %" PRId64 " is not initialized.", nchunk); |
1650 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1651 | 0 | } |
1652 | | |
1653 | 0 | int result = blosc2_vldecompress_block_ctx(schunk->dctx, chunk, cbytes, |
1654 | 0 | nblock, dest, destsize); |
1655 | 0 | if (needs_free) { |
1656 | 0 | free(chunk); |
1657 | 0 | } |
1658 | 0 | return result; |
1659 | 0 | } |
1660 | | |
1661 | | |
1662 | 0 | int blosc2_schunk_get_slice_buffer(blosc2_schunk *schunk, int64_t start, int64_t stop, void *buffer) { |
1663 | 0 | int64_t byte_start = start * schunk->typesize; |
1664 | 0 | int64_t byte_stop = stop * schunk->typesize; |
1665 | 0 | int64_t nchunk_start = byte_start / schunk->chunksize; |
1666 | 0 | int32_t chunk_start = (int32_t) (byte_start % schunk->chunksize); // 0 indexed |
1667 | 0 | int32_t chunk_stop; // 0 indexed |
1668 | 0 | if (byte_stop >= (nchunk_start + 1) * schunk->chunksize) { |
1669 | 0 | chunk_stop = schunk->chunksize; |
1670 | 0 | } |
1671 | 0 | else { |
1672 | 0 | chunk_stop = (int32_t) (byte_stop % schunk->chunksize); |
1673 | 0 | } |
1674 | |
|
1675 | 0 | uint8_t *dst_ptr = (uint8_t *) buffer; |
1676 | 0 | bool needs_free; |
1677 | 0 | uint8_t *chunk; |
1678 | 0 | int32_t cbytes; |
1679 | 0 | int64_t nchunk = nchunk_start; |
1680 | 0 | int64_t nbytes_read = 0; |
1681 | 0 | int32_t nbytes; |
1682 | 0 | int32_t chunksize = schunk->chunksize; |
1683 | |
|
1684 | 0 | while (nbytes_read < ((stop - start) * schunk->typesize)) { |
1685 | 0 | cbytes = blosc2_schunk_get_lazychunk(schunk, nchunk, &chunk, &needs_free); |
1686 | 0 | if (cbytes < 0) { |
1687 | 0 | BLOSC_TRACE_ERROR("Cannot get lazychunk ('%" PRId64 "').", nchunk); |
1688 | 0 | return BLOSC2_ERROR_FAILURE; |
1689 | 0 | } |
1690 | 0 | int32_t blocksize = sw32_(chunk + BLOSC2_CHUNK_BLOCKSIZE); |
1691 | |
|
1692 | 0 | int32_t nblock_start = (int32_t) (chunk_start / blocksize); |
1693 | 0 | int32_t nblock_stop = (int32_t) ((chunk_stop - 1) / blocksize); |
1694 | 0 | if (nchunk == (schunk->nchunks - 1) && schunk->nbytes % schunk->chunksize != 0) { |
1695 | 0 | chunksize = schunk->nbytes % schunk->chunksize; |
1696 | 0 | } |
1697 | 0 | int32_t nblocks = chunksize / blocksize; |
1698 | 0 | if (chunksize % blocksize != 0) { |
1699 | 0 | nblocks++; |
1700 | 0 | } |
1701 | |
|
1702 | 0 | if (chunk_start == 0 && chunk_stop == chunksize) { |
1703 | | // Avoid memcpy |
1704 | 0 | nbytes = blosc2_decompress_ctx(schunk->dctx, chunk, cbytes, dst_ptr, chunksize); |
1705 | 0 | if (nbytes < 0) { |
1706 | 0 | BLOSC_TRACE_ERROR("Cannot decompress chunk ('%" PRId64 "').", nchunk); |
1707 | 0 | if (needs_free) { |
1708 | 0 | free(chunk); |
1709 | 0 | } |
1710 | 0 | return BLOSC2_ERROR_FAILURE; |
1711 | 0 | } |
1712 | 0 | } |
1713 | 0 | else { |
1714 | | // After extensive timing I have not been able to see lots of situations where |
1715 | | // a maskout read is better than a getitem one. Disabling for now. |
1716 | | // if (nblock_start != nblock_stop) { |
1717 | 0 | if (false) { |
1718 | 0 | uint8_t *data = malloc(chunksize); |
1719 | | /* We have more than 1 block to read, so use a masked read */ |
1720 | 0 | bool *block_maskout = calloc(nblocks, 1); |
1721 | 0 | for (int32_t nblock = 0; nblock < nblocks; nblock++) { |
1722 | 0 | if ((nblock < nblock_start) || (nblock > nblock_stop)) { |
1723 | 0 | block_maskout[nblock] = true; |
1724 | 0 | } |
1725 | 0 | } |
1726 | 0 | if (blosc2_set_maskout(schunk->dctx, block_maskout, nblocks) < 0) { |
1727 | 0 | BLOSC_TRACE_ERROR("Cannot set maskout"); |
1728 | 0 | return BLOSC2_ERROR_FAILURE; |
1729 | 0 | } |
1730 | | |
1731 | 0 | nbytes = blosc2_decompress_ctx(schunk->dctx, chunk, cbytes, data, chunksize); |
1732 | 0 | if (nbytes < 0) { |
1733 | 0 | BLOSC_TRACE_ERROR("Cannot decompress chunk ('%" PRId64 "').", nchunk); |
1734 | 0 | return BLOSC2_ERROR_FAILURE; |
1735 | 0 | } |
1736 | 0 | nbytes = chunk_stop - chunk_start; |
1737 | 0 | memcpy(dst_ptr, &data[chunk_start], nbytes); |
1738 | 0 | free(block_maskout); |
1739 | 0 | free(data); |
1740 | 0 | } |
1741 | 0 | else { |
1742 | | /* Less than 1 block to read; use a getitem call. Counting in bytes |
1743 | | keeps this right for typesizes above BLOSC_MAX_TYPESIZE, which chunks |
1744 | | record as 1. */ |
1745 | 0 | int32_t nbytes_wanted = chunk_stop - chunk_start; |
1746 | 0 | nbytes = blosc2_getitem_bytes_ctx(schunk->dctx, chunk, cbytes, chunk_start, |
1747 | 0 | nbytes_wanted, dst_ptr, nbytes_wanted); |
1748 | 0 | if (nbytes < 0) { |
1749 | 0 | BLOSC_TRACE_ERROR("Cannot get item from ('%" PRId64 "') chunk.", nchunk); |
1750 | 0 | if (needs_free) { |
1751 | 0 | free(chunk); |
1752 | 0 | } |
1753 | 0 | return BLOSC2_ERROR_FAILURE; |
1754 | 0 | } |
1755 | 0 | if (nbytes != nbytes_wanted) { |
1756 | 0 | BLOSC_TRACE_ERROR("Short read (%d out of %d bytes) in ('%" PRId64 "') chunk.", |
1757 | 0 | nbytes, nbytes_wanted, nchunk); |
1758 | 0 | if (needs_free) { |
1759 | 0 | free(chunk); |
1760 | 0 | } |
1761 | 0 | return BLOSC2_ERROR_FAILURE; |
1762 | 0 | } |
1763 | 0 | } |
1764 | 0 | } |
1765 | | |
1766 | 0 | dst_ptr += nbytes; |
1767 | 0 | nbytes_read += nbytes; |
1768 | 0 | nchunk++; |
1769 | |
|
1770 | 0 | if (needs_free) { |
1771 | 0 | free(chunk); |
1772 | 0 | } |
1773 | 0 | chunk_start = 0; |
1774 | 0 | if (byte_stop >= (nchunk + 1) * chunksize) { |
1775 | 0 | chunk_stop = chunksize; |
1776 | 0 | } |
1777 | 0 | else { |
1778 | 0 | chunk_stop = (int32_t)(byte_stop % chunksize); |
1779 | 0 | } |
1780 | 0 | } |
1781 | | |
1782 | 0 | return BLOSC2_ERROR_SUCCESS; |
1783 | 0 | } |
1784 | | |
1785 | | |
1786 | | typedef struct { |
1787 | | int64_t coord; |
1788 | | int64_t out_index; |
1789 | | } sparse_coord_entry; |
1790 | | |
1791 | | typedef struct { |
1792 | | int64_t nchunk; |
1793 | | uint8_t *chunk; |
1794 | | int cbytes; |
1795 | | bool needs_free; |
1796 | | } sparse_chunk_entry; |
1797 | | |
1798 | | typedef struct { |
1799 | | int64_t first; |
1800 | | int64_t count; |
1801 | | int64_t chunk_index; |
1802 | | int32_t nblock; |
1803 | | } sparse_block_task; |
1804 | | |
1805 | | typedef struct { |
1806 | | const sparse_coord_entry *entries; |
1807 | | const sparse_block_task *tasks; |
1808 | | const sparse_chunk_entry *chunks; |
1809 | | int64_t ntasks; |
1810 | | uint8_t *buffer; |
1811 | | int32_t typesize; |
1812 | | int32_t blocksize; |
1813 | | int64_t chunk_nitems; |
1814 | | int64_t next_task; |
1815 | | int error; |
1816 | | blosc2_pthread_mutex_t mutex; |
1817 | | } sparse_work; |
1818 | | |
1819 | | typedef struct { |
1820 | | sparse_work *work; |
1821 | | blosc2_context *dctx; |
1822 | | uint8_t *block; |
1823 | | } sparse_worker; |
1824 | | |
1825 | 0 | static int sparse_coord_entry_cmp(const void *a, const void *b) { |
1826 | 0 | const sparse_coord_entry *ea = (const sparse_coord_entry *)a; |
1827 | 0 | const sparse_coord_entry *eb = (const sparse_coord_entry *)b; |
1828 | 0 | if (ea->coord < eb->coord) return -1; |
1829 | 0 | if (ea->coord > eb->coord) return 1; |
1830 | 0 | if (ea->out_index < eb->out_index) return -1; |
1831 | 0 | if (ea->out_index > eb->out_index) return 1; |
1832 | 0 | return 0; |
1833 | 0 | } |
1834 | | |
1835 | 0 | static void sparse_work_set_error(sparse_work *work, int error) { |
1836 | 0 | blosc2_pthread_mutex_lock(&work->mutex); |
1837 | 0 | if (work->error == 0) { |
1838 | 0 | work->error = error; |
1839 | 0 | } |
1840 | 0 | blosc2_pthread_mutex_unlock(&work->mutex); |
1841 | 0 | } |
1842 | | |
1843 | 0 | static void sparse_worker_func(void *arg) { |
1844 | 0 | sparse_worker *worker = (sparse_worker *)arg; |
1845 | 0 | sparse_work *work = worker->work; |
1846 | |
|
1847 | 0 | while (true) { |
1848 | 0 | blosc2_pthread_mutex_lock(&work->mutex); |
1849 | 0 | if (work->error < 0 || work->next_task >= work->ntasks) { |
1850 | 0 | blosc2_pthread_mutex_unlock(&work->mutex); |
1851 | 0 | break; |
1852 | 0 | } |
1853 | 0 | int64_t task_index = work->next_task++; |
1854 | 0 | blosc2_pthread_mutex_unlock(&work->mutex); |
1855 | |
|
1856 | 0 | const sparse_block_task *task = &work->tasks[task_index]; |
1857 | 0 | const sparse_chunk_entry *chunk_entry = &work->chunks[task->chunk_index]; |
1858 | 0 | int nbytes = blosc2_decompress_block_ctx(worker->dctx, chunk_entry->chunk, chunk_entry->cbytes, |
1859 | 0 | task->nblock, worker->block, work->blocksize); |
1860 | 0 | if (nbytes < 0) { |
1861 | 0 | sparse_work_set_error(work, nbytes); |
1862 | 0 | break; |
1863 | 0 | } |
1864 | | |
1865 | 0 | for (int64_t i = task->first; i < task->first + task->count; ++i) { |
1866 | 0 | const sparse_coord_entry *entry = &work->entries[i]; |
1867 | 0 | int64_t item_in_chunk = entry->coord % work->chunk_nitems; |
1868 | 0 | int64_t byte_in_chunk = item_in_chunk * work->typesize; |
1869 | 0 | int32_t block_offset = (int32_t)(byte_in_chunk % work->blocksize); |
1870 | 0 | if (block_offset < 0 || block_offset > nbytes - work->typesize) { |
1871 | 0 | sparse_work_set_error(work, BLOSC2_ERROR_DATA); |
1872 | 0 | return; |
1873 | 0 | } |
1874 | 0 | memcpy(work->buffer + entry->out_index * work->typesize, |
1875 | 0 | worker->block + block_offset, |
1876 | 0 | (size_t)work->typesize); |
1877 | 0 | } |
1878 | 0 | } |
1879 | | |
1880 | 0 | return; |
1881 | 0 | } |
1882 | | |
1883 | 0 | static int schunk_get_sparse_getitem(blosc2_schunk *schunk, int64_t ncoords, const int64_t* coords, void *buffer) { |
1884 | 0 | int64_t nitems = schunk->nbytes / schunk->typesize; |
1885 | 0 | int64_t chunk_nitems = schunk->chunksize / schunk->typesize; |
1886 | 0 | uint8_t *dst_ptr = (uint8_t *)buffer; |
1887 | |
|
1888 | 0 | for (int64_t i = 0; i < ncoords; ++i) { |
1889 | 0 | int64_t coord = coords[i]; |
1890 | 0 | if (coord < 0 || coord >= nitems) { |
1891 | 0 | BLOSC_TRACE_ERROR("Coordinate out of bounds."); |
1892 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1893 | 0 | } |
1894 | | |
1895 | 0 | int64_t nchunk = coord / chunk_nitems; |
1896 | | // count in bytes, so that a typesize above BLOSC_MAX_TYPESIZE (which chunks |
1897 | | // record as 1) needs no special casing here |
1898 | 0 | int32_t start = (int32_t)((coord % chunk_nitems) * schunk->typesize); |
1899 | 0 | uint8_t *chunk = NULL; |
1900 | 0 | bool needs_free = false; |
1901 | 0 | int cbytes = blosc2_schunk_get_lazychunk(schunk, nchunk, &chunk, &needs_free); |
1902 | 0 | if (cbytes <= 0) { |
1903 | 0 | BLOSC_TRACE_ERROR("Cannot get lazychunk ('%" PRId64 "').", nchunk); |
1904 | 0 | return BLOSC2_ERROR_FAILURE; |
1905 | 0 | } |
1906 | | |
1907 | 0 | int nbytes = blosc2_getitem_bytes_ctx(schunk->dctx, chunk, cbytes, start, |
1908 | 0 | schunk->typesize, dst_ptr, schunk->typesize); |
1909 | 0 | if (needs_free) { |
1910 | 0 | free(chunk); |
1911 | 0 | } |
1912 | 0 | if (nbytes != schunk->typesize) { |
1913 | 0 | BLOSC_TRACE_ERROR("Cannot get item from ('%" PRId64 "') chunk.", nchunk); |
1914 | 0 | return BLOSC2_ERROR_FAILURE; |
1915 | 0 | } |
1916 | 0 | dst_ptr += schunk->typesize; |
1917 | 0 | } |
1918 | | |
1919 | 0 | return BLOSC2_ERROR_SUCCESS; |
1920 | 0 | } |
1921 | | |
1922 | 0 | int blosc2_schunk_get_sparse_buffer(blosc2_schunk *schunk, int64_t ncoords, const int64_t* coords, void *buffer) { |
1923 | 0 | if (schunk == NULL) { |
1924 | 0 | BLOSC_TRACE_ERROR("schunk must not be NULL."); |
1925 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1926 | 0 | } |
1927 | 0 | if (ncoords < 0) { |
1928 | 0 | BLOSC_TRACE_ERROR("ncoords must be non-negative."); |
1929 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1930 | 0 | } |
1931 | 0 | if (ncoords == 0) { |
1932 | 0 | return BLOSC2_ERROR_SUCCESS; |
1933 | 0 | } |
1934 | 0 | if (coords == NULL || buffer == NULL) { |
1935 | 0 | BLOSC_TRACE_ERROR("coords and buffer must not be NULL when ncoords > 0."); |
1936 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1937 | 0 | } |
1938 | 0 | if (schunk->typesize <= 0) { |
1939 | 0 | BLOSC_TRACE_ERROR("schunk must have a positive typesize."); |
1940 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1941 | 0 | } |
1942 | 0 | if (schunk->chunksize <= 0) { |
1943 | 0 | BLOSC_TRACE_ERROR("blosc2_schunk_get_sparse_buffer does not support variable-length chunks yet."); |
1944 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1945 | 0 | } |
1946 | 0 | if (schunk->blocksize <= 0 || (schunk->flags2 & BLOSC2_VL_BLOCKS)) { |
1947 | 0 | BLOSC_TRACE_ERROR("blosc2_schunk_get_sparse_buffer does not support variable-length blocks yet."); |
1948 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1949 | 0 | } |
1950 | 0 | if ((schunk->chunksize % schunk->typesize) != 0 || (schunk->blocksize % schunk->typesize) != 0) { |
1951 | 0 | BLOSC_TRACE_ERROR("chunksize and blocksize must be multiples of typesize."); |
1952 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1953 | 0 | } |
1954 | | |
1955 | 0 | for (int i = 0; i < BLOSC2_MAX_FILTERS; ++i) { |
1956 | 0 | if (schunk->filters[i] == BLOSC_DELTA) { |
1957 | 0 | return schunk_get_sparse_getitem(schunk, ncoords, coords, buffer); |
1958 | 0 | } |
1959 | 0 | } |
1960 | 0 | if (schunk->dctx != NULL && schunk->dctx->postfilter != NULL) { |
1961 | 0 | return schunk_get_sparse_getitem(schunk, ncoords, coords, buffer); |
1962 | 0 | } |
1963 | 0 | if (ncoords <= 1) { |
1964 | 0 | return schunk_get_sparse_getitem(schunk, ncoords, coords, buffer); |
1965 | 0 | } |
1966 | | |
1967 | 0 | int rc = BLOSC2_ERROR_SUCCESS; |
1968 | 0 | int64_t nitems = schunk->nbytes / schunk->typesize; |
1969 | 0 | int64_t chunk_nitems = schunk->chunksize / schunk->typesize; |
1970 | 0 | sparse_coord_entry *entries = NULL; |
1971 | 0 | sparse_block_task *tasks = NULL; |
1972 | 0 | sparse_chunk_entry *chunks = NULL; |
1973 | 0 | sparse_worker *workers = NULL; |
1974 | 0 | int16_t nthreads = 1; |
1975 | 0 | int64_t ntasks = 0; |
1976 | 0 | int64_t nchunks = 0; |
1977 | 0 | sparse_work work; |
1978 | 0 | memset(&work, 0, sizeof(work)); |
1979 | |
|
1980 | 0 | if (chunk_nitems <= 0 || chunk_nitems > INT32_MAX) { |
1981 | 0 | BLOSC_TRACE_ERROR("Invalid chunk item count."); |
1982 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1983 | 0 | } |
1984 | | |
1985 | 0 | entries = calloc((size_t)ncoords, sizeof(sparse_coord_entry)); |
1986 | 0 | if (entries == NULL) { |
1987 | 0 | rc = BLOSC2_ERROR_MEMORY_ALLOC; |
1988 | 0 | goto cleanup; |
1989 | 0 | } |
1990 | | |
1991 | 0 | for (int64_t i = 0; i < ncoords; ++i) { |
1992 | 0 | int64_t coord = coords[i]; |
1993 | 0 | if (coord < 0 || coord >= nitems) { |
1994 | 0 | BLOSC_TRACE_ERROR("Coordinate out of bounds."); |
1995 | 0 | rc = BLOSC2_ERROR_INVALID_PARAM; |
1996 | 0 | goto cleanup; |
1997 | 0 | } |
1998 | 0 | entries[i].coord = coord; |
1999 | 0 | entries[i].out_index = i; |
2000 | 0 | } |
2001 | | |
2002 | 0 | qsort(entries, (size_t)ncoords, sizeof(sparse_coord_entry), sparse_coord_entry_cmp); |
2003 | | |
2004 | | /* First pass over sorted entries: count touched chunks and selected block tasks. */ |
2005 | 0 | for (int64_t i = 0; i < ncoords;) { |
2006 | 0 | int64_t nchunk = entries[i].coord / chunk_nitems; |
2007 | 0 | ++nchunks; |
2008 | 0 | while (i < ncoords && entries[i].coord / chunk_nitems == nchunk) { |
2009 | 0 | int64_t item_in_chunk = entries[i].coord % chunk_nitems; |
2010 | 0 | int64_t byte_in_chunk = item_in_chunk * schunk->typesize; |
2011 | 0 | int32_t nblock = (int32_t)(byte_in_chunk / schunk->blocksize); |
2012 | 0 | while (i < ncoords && |
2013 | 0 | entries[i].coord / chunk_nitems == nchunk && |
2014 | 0 | (int32_t)(((entries[i].coord % chunk_nitems) * schunk->typesize) / schunk->blocksize) == nblock) { |
2015 | 0 | ++i; |
2016 | 0 | } |
2017 | 0 | ++ntasks; |
2018 | 0 | } |
2019 | 0 | } |
2020 | |
|
2021 | 0 | tasks = calloc((size_t)ntasks, sizeof(sparse_block_task)); |
2022 | 0 | chunks = calloc((size_t)nchunks, sizeof(sparse_chunk_entry)); |
2023 | 0 | if (tasks == NULL || chunks == NULL) { |
2024 | 0 | rc = BLOSC2_ERROR_MEMORY_ALLOC; |
2025 | 0 | goto cleanup; |
2026 | 0 | } |
2027 | | |
2028 | | /* Second pass: fetch touched chunks and build one task per selected block. */ |
2029 | 0 | int64_t chunk_index = 0; |
2030 | 0 | int64_t task_index = 0; |
2031 | 0 | for (int64_t i = 0; i < ncoords;) { |
2032 | 0 | int64_t nchunk = entries[i].coord / chunk_nitems; |
2033 | 0 | uint8_t *chunk = NULL; |
2034 | 0 | bool needs_free = false; |
2035 | 0 | int cbytes = blosc2_schunk_get_lazychunk(schunk, nchunk, &chunk, &needs_free); |
2036 | 0 | if (cbytes <= 0) { |
2037 | 0 | BLOSC_TRACE_ERROR("Cannot get lazychunk ('%" PRId64 "').", nchunk); |
2038 | 0 | rc = BLOSC2_ERROR_FAILURE; |
2039 | 0 | goto cleanup; |
2040 | 0 | } |
2041 | 0 | chunks[chunk_index].nchunk = nchunk; |
2042 | 0 | chunks[chunk_index].chunk = chunk; |
2043 | 0 | chunks[chunk_index].cbytes = cbytes; |
2044 | 0 | chunks[chunk_index].needs_free = needs_free; |
2045 | |
|
2046 | 0 | while (i < ncoords && entries[i].coord / chunk_nitems == nchunk) { |
2047 | 0 | int64_t item_in_chunk = entries[i].coord % chunk_nitems; |
2048 | 0 | int64_t byte_in_chunk = item_in_chunk * schunk->typesize; |
2049 | 0 | int32_t nblock = (int32_t)(byte_in_chunk / schunk->blocksize); |
2050 | 0 | int64_t first = i; |
2051 | 0 | while (i < ncoords && |
2052 | 0 | entries[i].coord / chunk_nitems == nchunk && |
2053 | 0 | (int32_t)(((entries[i].coord % chunk_nitems) * schunk->typesize) / schunk->blocksize) == nblock) { |
2054 | 0 | ++i; |
2055 | 0 | } |
2056 | 0 | tasks[task_index].first = first; |
2057 | 0 | tasks[task_index].count = i - first; |
2058 | 0 | tasks[task_index].chunk_index = chunk_index; |
2059 | 0 | tasks[task_index].nblock = nblock; |
2060 | 0 | ++task_index; |
2061 | 0 | } |
2062 | 0 | ++chunk_index; |
2063 | 0 | } |
2064 | | |
2065 | 0 | nthreads = schunk->dctx != NULL ? schunk->dctx->nthreads : blosc2_get_nthreads(); |
2066 | 0 | if (nthreads < 1) { |
2067 | 0 | nthreads = 1; |
2068 | 0 | } |
2069 | 0 | if ((int64_t)nthreads > ntasks) { |
2070 | 0 | nthreads = (int16_t)ntasks; |
2071 | 0 | } |
2072 | |
|
2073 | 0 | workers = calloc((size_t)nthreads, sizeof(sparse_worker)); |
2074 | 0 | if (workers == NULL) { |
2075 | 0 | rc = BLOSC2_ERROR_MEMORY_ALLOC; |
2076 | 0 | goto cleanup; |
2077 | 0 | } |
2078 | | |
2079 | 0 | work.entries = entries; |
2080 | 0 | work.tasks = tasks; |
2081 | 0 | work.chunks = chunks; |
2082 | 0 | work.ntasks = ntasks; |
2083 | 0 | work.buffer = (uint8_t *)buffer; |
2084 | 0 | work.typesize = schunk->typesize; |
2085 | 0 | work.blocksize = schunk->blocksize; |
2086 | 0 | work.chunk_nitems = chunk_nitems; |
2087 | 0 | work.next_task = 0; |
2088 | 0 | work.error = 0; |
2089 | 0 | blosc2_pthread_mutex_init(&work.mutex, NULL); |
2090 | |
|
2091 | 0 | for (int16_t i = 0; i < nthreads; ++i) { |
2092 | 0 | blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; |
2093 | 0 | dparams.nthreads = 1; |
2094 | 0 | dparams.schunk = schunk; |
2095 | 0 | workers[i].work = &work; |
2096 | 0 | workers[i].dctx = blosc2_create_dctx(dparams); |
2097 | 0 | workers[i].block = malloc((size_t)schunk->blocksize); |
2098 | 0 | if (workers[i].dctx == NULL || workers[i].block == NULL) { |
2099 | 0 | rc = BLOSC2_ERROR_MEMORY_ALLOC; |
2100 | 0 | goto cleanup_work_mutex; |
2101 | 0 | } |
2102 | 0 | } |
2103 | | |
2104 | 0 | if (nthreads == 1) { |
2105 | 0 | sparse_worker_func(&workers[0]); |
2106 | 0 | } |
2107 | 0 | else { |
2108 | 0 | int err = blosc2_run_parallel(nthreads, sparse_worker_func, sizeof(sparse_worker), workers); |
2109 | 0 | if (err < 0) { |
2110 | 0 | sparse_work_set_error(&work, err); |
2111 | 0 | } |
2112 | 0 | } |
2113 | |
|
2114 | 0 | if (work.error < 0) { |
2115 | 0 | rc = work.error; |
2116 | 0 | } |
2117 | |
|
2118 | 0 | cleanup_work_mutex: |
2119 | 0 | blosc2_pthread_mutex_destroy(&work.mutex); |
2120 | 0 | cleanup: |
2121 | 0 | if (workers != NULL) { |
2122 | 0 | int16_t nworkers = nthreads > 0 ? nthreads : 1; |
2123 | 0 | for (int16_t i = 0; i < nworkers; ++i) { |
2124 | 0 | if (workers[i].dctx != NULL) { |
2125 | 0 | blosc2_free_ctx(workers[i].dctx); |
2126 | 0 | } |
2127 | 0 | free(workers[i].block); |
2128 | 0 | } |
2129 | 0 | } |
2130 | 0 | if (chunks != NULL) { |
2131 | 0 | for (int64_t i = 0; i < nchunks; ++i) { |
2132 | 0 | if (chunks[i].needs_free) { |
2133 | 0 | free(chunks[i].chunk); |
2134 | 0 | } |
2135 | 0 | } |
2136 | 0 | } |
2137 | 0 | free(workers); |
2138 | 0 | free(chunks); |
2139 | 0 | free(tasks); |
2140 | 0 | free(entries); |
2141 | |
|
2142 | 0 | return rc; |
2143 | 0 | } |
2144 | | |
2145 | | |
2146 | 0 | int blosc2_schunk_set_slice_buffer(blosc2_schunk *schunk, int64_t start, int64_t stop, void *buffer) { |
2147 | 0 | int64_t byte_start = start * schunk->typesize; |
2148 | 0 | int64_t byte_stop = stop * schunk->typesize; |
2149 | 0 | int64_t nchunk_start = byte_start / schunk->chunksize; |
2150 | 0 | int32_t chunk_start = (int32_t) (byte_start % schunk->chunksize); |
2151 | 0 | int32_t chunk_stop; |
2152 | 0 | if (byte_stop >= (nchunk_start + 1) * schunk->chunksize) { |
2153 | 0 | chunk_stop = schunk->chunksize; |
2154 | 0 | } |
2155 | 0 | else { |
2156 | 0 | chunk_stop = (int32_t) (byte_stop % schunk->chunksize); |
2157 | 0 | } |
2158 | |
|
2159 | 0 | uint8_t *src_ptr = (uint8_t *) buffer; |
2160 | 0 | int64_t nchunk = nchunk_start; |
2161 | 0 | int64_t nbytes_written = 0; |
2162 | 0 | int32_t nbytes; |
2163 | 0 | uint8_t *data = malloc(schunk->chunksize); |
2164 | 0 | int64_t nchunks; |
2165 | 0 | int32_t chunksize = schunk->chunksize; |
2166 | |
|
2167 | 0 | while (nbytes_written < ((stop - start) * schunk->typesize)) { |
2168 | 0 | if (chunk_start == 0 && |
2169 | 0 | (chunk_stop == schunk->chunksize || chunk_stop == schunk->nbytes % schunk->chunksize)) { |
2170 | 0 | if (chunk_stop == schunk->nbytes % schunk->chunksize) { |
2171 | 0 | chunksize = chunk_stop; |
2172 | 0 | } |
2173 | 0 | uint8_t *chunk = malloc(chunksize + BLOSC2_MAX_OVERHEAD); |
2174 | 0 | if (blosc2_compress_ctx(schunk->cctx, src_ptr, chunksize, chunk, chunksize + BLOSC2_MAX_OVERHEAD) < 0) { |
2175 | 0 | BLOSC_TRACE_ERROR("Cannot compress data of chunk ('%" PRId64 "').", nchunk); |
2176 | 0 | return BLOSC2_ERROR_FAILURE; |
2177 | 0 | } |
2178 | 0 | nchunks = blosc2_schunk_update_chunk(schunk, nchunk, chunk, false); |
2179 | 0 | if (nchunks != schunk->nchunks) { |
2180 | 0 | BLOSC_TRACE_ERROR("Cannot update chunk ('%" PRId64 "').", nchunk); |
2181 | 0 | return BLOSC2_ERROR_CHUNK_UPDATE; |
2182 | 0 | } |
2183 | 0 | } |
2184 | 0 | else { |
2185 | 0 | nbytes = blosc2_schunk_decompress_chunk(schunk, nchunk, data, schunk->chunksize); |
2186 | 0 | if (nbytes < 0) { |
2187 | 0 | BLOSC_TRACE_ERROR("Cannot decompress chunk ('%" PRId64 "').", nchunk); |
2188 | 0 | return BLOSC2_ERROR_FAILURE; |
2189 | 0 | } |
2190 | 0 | memcpy(&data[chunk_start], src_ptr, chunk_stop - chunk_start); |
2191 | 0 | uint8_t *chunk = malloc(nbytes + BLOSC2_MAX_OVERHEAD); |
2192 | 0 | if (blosc2_compress_ctx(schunk->cctx, data, nbytes, chunk, nbytes + BLOSC2_MAX_OVERHEAD) < 0) { |
2193 | 0 | BLOSC_TRACE_ERROR("Cannot compress data of chunk ('%" PRId64 "').", nchunk); |
2194 | 0 | return BLOSC2_ERROR_FAILURE; |
2195 | 0 | } |
2196 | 0 | nchunks = blosc2_schunk_update_chunk(schunk, nchunk, chunk, false); |
2197 | 0 | if (nchunks != schunk->nchunks) { |
2198 | 0 | BLOSC_TRACE_ERROR("Cannot update chunk ('%" PRId64 "').", nchunk); |
2199 | 0 | return BLOSC2_ERROR_CHUNK_UPDATE; |
2200 | 0 | } |
2201 | 0 | } |
2202 | 0 | nchunk++; |
2203 | 0 | nbytes_written += chunk_stop - chunk_start; |
2204 | 0 | src_ptr += chunk_stop - chunk_start; |
2205 | 0 | chunk_start = 0; |
2206 | 0 | if (byte_stop >= (nchunk + 1) * schunk->chunksize) { |
2207 | 0 | chunk_stop = schunk->chunksize; |
2208 | 0 | } |
2209 | 0 | else { |
2210 | 0 | chunk_stop = (int32_t) (byte_stop % schunk->chunksize); |
2211 | 0 | } |
2212 | 0 | } |
2213 | 0 | free(data); |
2214 | |
|
2215 | 0 | return BLOSC2_ERROR_SUCCESS; |
2216 | 0 | } |
2217 | | |
2218 | | |
2219 | 0 | int64_t schunk_get_slice_nchunks(blosc2_schunk *schunk, int64_t start, int64_t stop, int64_t **chunks_idx) { |
2220 | 0 | BLOSC_ERROR_NULL(schunk, BLOSC2_ERROR_NULL_POINTER); |
2221 | 0 | if (schunk->nchunks == 0){ |
2222 | 0 | *chunks_idx = NULL; |
2223 | 0 | return 0; |
2224 | 0 | } |
2225 | 0 | int64_t byte_start = start * schunk->typesize; |
2226 | 0 | int64_t byte_stop = stop * schunk->typesize; |
2227 | 0 | int64_t nchunk_start = byte_start / schunk->chunksize; |
2228 | 0 | int64_t nchunk_stop = byte_stop / schunk->chunksize; |
2229 | 0 | if (byte_stop % schunk->chunksize != 0) { |
2230 | 0 | nchunk_stop++; |
2231 | 0 | } |
2232 | 0 | int64_t nchunk = nchunk_start; |
2233 | 0 | int64_t nchunks = nchunk_stop - nchunk_start; |
2234 | 0 | *chunks_idx = malloc(nchunks * sizeof(int64_t)); |
2235 | 0 | int64_t *ptr = *chunks_idx; |
2236 | 0 | for (int64_t i = 0; i < nchunks; ++i) { |
2237 | 0 | ptr[i] = nchunk; |
2238 | 0 | nchunk++; |
2239 | 0 | } |
2240 | 0 | return nchunks; |
2241 | 0 | } |
2242 | | |
2243 | | /* Reorder the chunk offsets of an existing super-chunk. */ |
2244 | 0 | int blosc2_schunk_reorder_offsets(blosc2_schunk *schunk, int64_t *offsets_order) { |
2245 | | // Check that the offsets order are correct |
2246 | 0 | bool *index_check = (bool *) calloc(schunk->nchunks, sizeof(bool)); |
2247 | 0 | for (int i = 0; i < schunk->nchunks; ++i) { |
2248 | 0 | int64_t index = offsets_order[i]; |
2249 | 0 | if (index < 0 || index >= schunk->nchunks) { |
2250 | 0 | BLOSC_TRACE_ERROR("Index is out of range (negative or >= number of chunks)."); |
2251 | 0 | free(index_check); |
2252 | 0 | return BLOSC2_ERROR_DATA; |
2253 | 0 | } |
2254 | 0 | if (index_check[index] == false) { |
2255 | 0 | index_check[index] = true; |
2256 | 0 | } else { |
2257 | 0 | BLOSC_TRACE_ERROR("Index is yet used."); |
2258 | 0 | free(index_check); |
2259 | 0 | return BLOSC2_ERROR_DATA; |
2260 | 0 | } |
2261 | 0 | } |
2262 | 0 | free(index_check); |
2263 | |
|
2264 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
2265 | 0 | if (frame != NULL) { |
2266 | 0 | int rc = frame_lock(frame, true); |
2267 | 0 | if (rc < 0) { |
2268 | 0 | return rc; |
2269 | 0 | } |
2270 | 0 | rc = frame_reorder_offsets(frame, offsets_order, schunk); |
2271 | 0 | frame_unlock(frame); |
2272 | 0 | return rc; |
2273 | 0 | } |
2274 | 0 | uint8_t **offsets = schunk->data; |
2275 | | |
2276 | | // Make a copy of the chunk offsets and reorder it |
2277 | 0 | uint8_t **offsets_copy = malloc(schunk->data_len); |
2278 | 0 | memcpy(offsets_copy, offsets, schunk->data_len); |
2279 | |
|
2280 | 0 | for (int i = 0; i < schunk->nchunks; ++i) { |
2281 | 0 | offsets[i] = offsets_copy[offsets_order[i]]; |
2282 | 0 | } |
2283 | 0 | free(offsets_copy); |
2284 | |
|
2285 | 0 | return 0; |
2286 | 0 | } |
2287 | | |
2288 | | |
2289 | | // Get the length (in bytes) of the internal frame of the super-chunk |
2290 | 0 | int64_t blosc2_schunk_frame_len(blosc2_schunk* schunk) { |
2291 | 0 | int64_t len; |
2292 | 0 | blosc2_frame_s* frame_s = (blosc2_frame_s*)(schunk->frame); |
2293 | 0 | if (frame_s != NULL) { |
2294 | 0 | len = frame_s->len; |
2295 | 0 | } |
2296 | 0 | else { |
2297 | | // No attached frame, but we can still come with an estimate |
2298 | 0 | len = (int64_t) (schunk->cbytes + schunk->nchunks * sizeof(int64_t)); |
2299 | 0 | } |
2300 | |
|
2301 | 0 | return len; |
2302 | 0 | } |
2303 | | |
2304 | | |
2305 | | /** |
2306 | | * @brief Flush metalayers content into a possible attached frame. |
2307 | | * |
2308 | | * @param schunk The super-chunk to which the flush should be applied. |
2309 | | * |
2310 | | * @return If successful, a 0 is returned. Else, return a negative value. |
2311 | | */ |
2312 | | // Initially, this was a public function, but as it is really meant to be used only |
2313 | | // in the schunk_add_metalayer(), I decided to convert it into private and call it |
2314 | | // implicitly instead of requiring the user to do so. The only drawback is that |
2315 | | // each add operation requires a complete frame re-build, but as users should need |
2316 | | // very few metalayers, this overhead should be negligible in practice. |
2317 | 0 | int metalayer_flush(blosc2_schunk* schunk) { |
2318 | 0 | int rc = BLOSC2_ERROR_SUCCESS; |
2319 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
2320 | 0 | if (frame == NULL) { |
2321 | 0 | return rc; |
2322 | 0 | } |
2323 | 0 | rc = frame_lock(frame, true); |
2324 | 0 | if (rc < 0) { |
2325 | 0 | return rc; |
2326 | 0 | } |
2327 | 0 | rc = frame_update_header(frame, schunk, true); |
2328 | 0 | if (rc < 0) { |
2329 | 0 | BLOSC_TRACE_ERROR("Unable to update metalayers into frame."); |
2330 | 0 | frame_unlock(frame); |
2331 | 0 | return rc; |
2332 | 0 | } |
2333 | 0 | rc = frame_update_trailer(frame, schunk); |
2334 | 0 | if (rc < 0) { |
2335 | 0 | BLOSC_TRACE_ERROR("Unable to update trailer into frame."); |
2336 | 0 | frame_unlock(frame); |
2337 | 0 | return rc; |
2338 | 0 | } |
2339 | 0 | frame_unlock(frame); |
2340 | 0 | return rc; |
2341 | 0 | } |
2342 | | |
2343 | | |
2344 | | /* Add content into a new metalayer. |
2345 | | * |
2346 | | * If successful, return the index of the new metalayer. Else, return a negative value. |
2347 | | */ |
2348 | 0 | int blosc2_meta_add(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len) { |
2349 | 0 | if (schunk == NULL || name == NULL) { |
2350 | 0 | BLOSC_TRACE_ERROR("Invalid parameters."); |
2351 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2352 | 0 | } |
2353 | 0 | int nmetalayer = blosc2_meta_exists(schunk, name); |
2354 | 0 | if (nmetalayer >= 0) { |
2355 | 0 | BLOSC_TRACE_ERROR("Metalayer \"%s\" already exists.", name); |
2356 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2357 | 0 | } |
2358 | 0 | if (content_len < 0) { |
2359 | 0 | BLOSC_TRACE_ERROR("Metalayer content length must not be negative."); |
2360 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2361 | 0 | } |
2362 | 0 | if (content_len > 0 && content == NULL) { |
2363 | 0 | BLOSC_TRACE_ERROR("Metalayer content pointer must not be NULL when content length is positive."); |
2364 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2365 | 0 | } |
2366 | | |
2367 | | // Add the metalayer |
2368 | 0 | blosc2_metalayer *metalayer = malloc(sizeof(blosc2_metalayer)); |
2369 | 0 | BLOSC_ERROR_NULL(metalayer, BLOSC2_ERROR_MEMORY_ALLOC); |
2370 | | |
2371 | 0 | char* name_ = malloc(strlen(name) + 1); |
2372 | 0 | if (name_ == NULL) { |
2373 | 0 | free(metalayer); |
2374 | 0 | BLOSC_TRACE_ERROR("Unable to allocate metalayer name buffer."); |
2375 | 0 | return BLOSC2_ERROR_MEMORY_ALLOC; |
2376 | 0 | } |
2377 | 0 | strcpy(name_, name); |
2378 | 0 | metalayer->name = name_; |
2379 | |
|
2380 | 0 | uint8_t* content_buf = malloc((size_t)content_len); |
2381 | 0 | if (content_buf == NULL && content_len > 0) { |
2382 | 0 | free(name_); |
2383 | 0 | free(metalayer); |
2384 | 0 | BLOSC_TRACE_ERROR("Unable to allocate metalayer content buffer."); |
2385 | 0 | return BLOSC2_ERROR_MEMORY_ALLOC; |
2386 | 0 | } |
2387 | 0 | if (content_len > 0) { |
2388 | 0 | memcpy(content_buf, content, content_len); |
2389 | 0 | } |
2390 | 0 | metalayer->content = content_buf; |
2391 | 0 | metalayer->content_len = content_len; |
2392 | 0 | schunk->metalayers[schunk->nmetalayers] = metalayer; |
2393 | 0 | schunk->nmetalayers += 1; |
2394 | |
|
2395 | 0 | int rc = metalayer_flush(schunk); |
2396 | 0 | if (rc < 0) { |
2397 | 0 | return rc; |
2398 | 0 | } |
2399 | 0 | schunk->change_tick++; |
2400 | |
|
2401 | 0 | return schunk->nmetalayers - 1; |
2402 | 0 | } |
2403 | | |
2404 | | |
2405 | | /* Update the content of an existing metalayer. |
2406 | | * |
2407 | | * If successful, return the index of the new metalayer. Else, return a negative value. |
2408 | | */ |
2409 | 0 | int blosc2_meta_update(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len) { |
2410 | 0 | if (schunk == NULL || name == NULL) { |
2411 | 0 | BLOSC_TRACE_ERROR("Invalid parameters."); |
2412 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2413 | 0 | } |
2414 | 0 | int nmetalayer = blosc2_meta_exists(schunk, name); |
2415 | 0 | if (nmetalayer < 0) { |
2416 | 0 | BLOSC_TRACE_ERROR("Metalayer \"%s\" not found.", name); |
2417 | 0 | return nmetalayer; |
2418 | 0 | } |
2419 | 0 | if (content_len < 0) { |
2420 | 0 | BLOSC_TRACE_ERROR("Metalayer content length must not be negative."); |
2421 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2422 | 0 | } |
2423 | 0 | if (content_len > 0 && content == NULL) { |
2424 | 0 | BLOSC_TRACE_ERROR("Metalayer content pointer must not be NULL when content length is positive."); |
2425 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2426 | 0 | } |
2427 | | |
2428 | 0 | blosc2_metalayer *metalayer = schunk->metalayers[nmetalayer]; |
2429 | 0 | if (content_len > metalayer->content_len) { |
2430 | 0 | BLOSC_TRACE_ERROR("`content_len` cannot exceed the existing size of %d bytes.", metalayer->content_len); |
2431 | 0 | return nmetalayer; |
2432 | 0 | } |
2433 | | |
2434 | | // Update the contents of the metalayer |
2435 | 0 | if (content_len > 0) { |
2436 | 0 | memcpy(metalayer->content, content, content_len); |
2437 | 0 | } |
2438 | | |
2439 | | // Update the metalayers in frame (as size has not changed, we don't need to update the trailer) |
2440 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
2441 | 0 | if (frame != NULL) { |
2442 | 0 | int rc = frame_update_header(frame, schunk, false); |
2443 | 0 | if (rc < 0) { |
2444 | 0 | BLOSC_TRACE_ERROR("Unable to update meta info from frame."); |
2445 | 0 | return rc; |
2446 | 0 | } |
2447 | 0 | } |
2448 | 0 | schunk->change_tick++; |
2449 | |
|
2450 | 0 | return nmetalayer; |
2451 | 0 | } |
2452 | | |
2453 | | |
2454 | | /* Find whether the schunk has a variable-length metalayer or not. |
2455 | | * |
2456 | | * If successful, return the index of the variable-length metalayer. Else, return a negative value. |
2457 | | */ |
2458 | 0 | int blosc2_vlmeta_exists(blosc2_schunk *schunk, const char *name) { |
2459 | 0 | if (strlen(name) > BLOSC2_METALAYER_NAME_MAXLEN) { |
2460 | 0 | BLOSC_TRACE_ERROR("Variable-length metalayer names cannot be larger than %d chars.", BLOSC2_METALAYER_NAME_MAXLEN); |
2461 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2462 | 0 | } |
2463 | | // Re-sync the cached vlmetalayers if another handle rewrote the frame, so |
2464 | | // that a vlmetalayer added or deleted elsewhere is (not) found. The other |
2465 | | // vlmeta entry points (add/get/update/delete) all pass through here, so |
2466 | | // this is the single sync point for the vlmetalayers. |
2467 | 0 | if (schunk->frame != NULL) { |
2468 | 0 | int rc = frame_check_stale((blosc2_frame_s*)schunk->frame); |
2469 | 0 | if (rc < 0) { |
2470 | 0 | return rc; |
2471 | 0 | } |
2472 | 0 | } |
2473 | | |
2474 | 0 | for (int nvlmetalayer = 0; nvlmetalayer < schunk->nvlmetalayers; nvlmetalayer++) { |
2475 | 0 | if (strcmp(name, schunk->vlmetalayers[nvlmetalayer]->name) == 0) { |
2476 | 0 | return nvlmetalayer; |
2477 | 0 | } |
2478 | 0 | } |
2479 | 0 | return BLOSC2_ERROR_NOT_FOUND; |
2480 | 0 | } |
2481 | | |
2482 | 0 | int vlmetalayer_flush(blosc2_schunk* schunk) { |
2483 | 0 | int rc = BLOSC2_ERROR_SUCCESS; |
2484 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
2485 | 0 | if (frame == NULL) { |
2486 | 0 | return rc; |
2487 | 0 | } |
2488 | 0 | rc = frame_lock(frame, true); |
2489 | 0 | if (rc < 0) { |
2490 | 0 | return rc; |
2491 | 0 | } |
2492 | 0 | rc = frame_update_header(frame, schunk, false); |
2493 | 0 | if (rc < 0) { |
2494 | 0 | BLOSC_TRACE_ERROR("Unable to update metalayers into frame."); |
2495 | 0 | frame_unlock(frame); |
2496 | 0 | return rc; |
2497 | 0 | } |
2498 | 0 | rc = frame_update_trailer(frame, schunk); |
2499 | 0 | if (rc < 0) { |
2500 | 0 | BLOSC_TRACE_ERROR("Unable to update trailer into frame."); |
2501 | 0 | frame_unlock(frame); |
2502 | 0 | return rc; |
2503 | 0 | } |
2504 | 0 | frame_unlock(frame); |
2505 | 0 | return rc; |
2506 | 0 | } |
2507 | | |
2508 | | /* Add content into a new variable-length metalayer. |
2509 | | * |
2510 | | * If successful, return the index of the new variable-length metalayer. Else, return a negative value. |
2511 | | */ |
2512 | | int blosc2_vlmeta_add(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len, |
2513 | 0 | blosc2_cparams *cparams) { |
2514 | 0 | if (schunk == NULL || name == NULL) { |
2515 | 0 | BLOSC_TRACE_ERROR("Invalid parameters."); |
2516 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2517 | 0 | } |
2518 | 0 | int nvlmetalayer = blosc2_vlmeta_exists(schunk, name); |
2519 | 0 | if (nvlmetalayer >= 0) { |
2520 | 0 | BLOSC_TRACE_ERROR("Variable-length metalayer \"%s\" already exists.", name); |
2521 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2522 | 0 | } |
2523 | 0 | if (content_len < 0) { |
2524 | 0 | BLOSC_TRACE_ERROR("Variable-length metalayer content length must not be negative."); |
2525 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2526 | 0 | } |
2527 | 0 | if (content_len > 0 && content == NULL) { |
2528 | 0 | BLOSC_TRACE_ERROR("Variable-length metalayer content pointer must not be NULL when content length is positive."); |
2529 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2530 | 0 | } |
2531 | | |
2532 | | // Add the vlmetalayer |
2533 | 0 | blosc2_metalayer *vlmetalayer = malloc(sizeof(blosc2_metalayer)); |
2534 | 0 | BLOSC_ERROR_NULL(vlmetalayer, BLOSC2_ERROR_MEMORY_ALLOC); |
2535 | 0 | vlmetalayer->name = strdup(name); |
2536 | 0 | if (vlmetalayer->name == NULL) { |
2537 | 0 | free(vlmetalayer); |
2538 | 0 | BLOSC_TRACE_ERROR("Unable to allocate variable-length metalayer name buffer."); |
2539 | 0 | return BLOSC2_ERROR_MEMORY_ALLOC; |
2540 | 0 | } |
2541 | | |
2542 | 0 | int64_t destsize = (int64_t)content_len + BLOSC2_MAX_OVERHEAD; |
2543 | 0 | if (destsize > INT32_MAX) { |
2544 | 0 | free(vlmetalayer->name); |
2545 | 0 | free(vlmetalayer); |
2546 | 0 | BLOSC_TRACE_ERROR("Variable-length metalayer size is too large."); |
2547 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2548 | 0 | } |
2549 | 0 | size_t destsize_sz = (size_t)destsize; |
2550 | 0 | uint8_t* content_buf = malloc(destsize_sz); |
2551 | 0 | if (content_buf == NULL) { |
2552 | 0 | free(vlmetalayer->name); |
2553 | 0 | free(vlmetalayer); |
2554 | 0 | BLOSC_TRACE_ERROR("Unable to allocate variable-length metalayer buffer."); |
2555 | 0 | return BLOSC2_ERROR_MEMORY_ALLOC; |
2556 | 0 | } |
2557 | | |
2558 | 0 | blosc2_context *cctx; |
2559 | 0 | if (cparams != NULL) { |
2560 | 0 | cctx = blosc2_create_cctx(*cparams); |
2561 | 0 | } else { |
2562 | 0 | cctx = blosc2_create_cctx(BLOSC2_CPARAMS_DEFAULTS); |
2563 | 0 | } |
2564 | 0 | if (cctx == NULL) { |
2565 | 0 | free(content_buf); |
2566 | 0 | free(vlmetalayer->name); |
2567 | 0 | free(vlmetalayer); |
2568 | 0 | BLOSC_TRACE_ERROR("Error while creating the compression context"); |
2569 | 0 | return BLOSC2_ERROR_NULL_POINTER; |
2570 | 0 | } |
2571 | | |
2572 | 0 | int csize = blosc2_compress_ctx(cctx, content, content_len, content_buf, (int32_t)destsize); |
2573 | 0 | if (csize < 0) { |
2574 | 0 | blosc2_free_ctx(cctx); |
2575 | 0 | free(content_buf); |
2576 | 0 | free(vlmetalayer->name); |
2577 | 0 | free(vlmetalayer); |
2578 | 0 | BLOSC_TRACE_ERROR("Can not compress the `%s` variable-length metalayer.", name); |
2579 | 0 | return csize; |
2580 | 0 | } |
2581 | 0 | blosc2_free_ctx(cctx); |
2582 | |
|
2583 | 0 | uint8_t *compressed_buf = realloc(content_buf, csize); |
2584 | 0 | if (compressed_buf == NULL && csize > 0) { |
2585 | 0 | compressed_buf = content_buf; |
2586 | 0 | } |
2587 | 0 | vlmetalayer->content = compressed_buf; |
2588 | 0 | vlmetalayer->content_len = csize; |
2589 | 0 | schunk->vlmetalayers[schunk->nvlmetalayers] = vlmetalayer; |
2590 | 0 | schunk->nvlmetalayers += 1; |
2591 | | |
2592 | | // Propagate to frames |
2593 | 0 | int rc = vlmetalayer_flush(schunk); |
2594 | 0 | if (rc < 0) { |
2595 | 0 | schunk->nvlmetalayers -= 1; |
2596 | 0 | schunk->vlmetalayers[schunk->nvlmetalayers] = NULL; |
2597 | 0 | free(vlmetalayer->content); |
2598 | 0 | free(vlmetalayer->name); |
2599 | 0 | free(vlmetalayer); |
2600 | 0 | BLOSC_TRACE_ERROR("Can not propagate de `%s` variable-length metalayer to a frame.", name); |
2601 | 0 | return rc; |
2602 | 0 | } |
2603 | 0 | schunk->change_tick++; |
2604 | |
|
2605 | 0 | return schunk->nvlmetalayers - 1; |
2606 | 0 | } |
2607 | | |
2608 | | |
2609 | | int blosc2_vlmeta_get(blosc2_schunk *schunk, const char *name, uint8_t **content, |
2610 | 0 | int32_t *content_len) { |
2611 | 0 | if (schunk == NULL || name == NULL || content == NULL || content_len == NULL) { |
2612 | 0 | BLOSC_TRACE_ERROR("Invalid parameters."); |
2613 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2614 | 0 | } |
2615 | | // blosc2_vlmeta_exists() re-syncs the cached vlmetalayers if another handle |
2616 | | // rewrote the frame |
2617 | 0 | int nvlmetalayer = blosc2_vlmeta_exists(schunk, name); |
2618 | 0 | if (nvlmetalayer < 0) { |
2619 | 0 | BLOSC_TRACE_ERROR("User metalayer \"%s\" not found.", name); |
2620 | 0 | return nvlmetalayer; |
2621 | 0 | } |
2622 | 0 | blosc2_metalayer *meta = schunk->vlmetalayers[nvlmetalayer]; |
2623 | 0 | int32_t nbytes, cbytes; |
2624 | 0 | blosc2_cbuffer_sizes(meta->content, &nbytes, &cbytes, NULL); |
2625 | 0 | if (cbytes != meta->content_len) { |
2626 | 0 | BLOSC_TRACE_ERROR("User metalayer \"%s\" is corrupted.", meta->name); |
2627 | 0 | return BLOSC2_ERROR_DATA; |
2628 | 0 | } |
2629 | 0 | if (nbytes < 0) { |
2630 | 0 | BLOSC_TRACE_ERROR("User metalayer \"%s\" has corrupted decompressed size %d.", meta->name, nbytes); |
2631 | 0 | return BLOSC2_ERROR_DATA; |
2632 | 0 | } |
2633 | 0 | *content_len = nbytes; |
2634 | 0 | if (nbytes == 0) { |
2635 | 0 | *content = NULL; |
2636 | 0 | } else { |
2637 | 0 | *content = malloc((size_t) nbytes); |
2638 | 0 | if (*content == NULL) { |
2639 | 0 | BLOSC_TRACE_ERROR("Unable to allocate variable-length metalayer content buffer."); |
2640 | 0 | *content_len = 0; |
2641 | 0 | return BLOSC2_ERROR_MEMORY_ALLOC; |
2642 | 0 | } |
2643 | 0 | } |
2644 | 0 | blosc2_context *dctx = blosc2_create_dctx(*schunk->storage->dparams); |
2645 | 0 | if (dctx == NULL) { |
2646 | 0 | if (*content != NULL) { |
2647 | 0 | free(*content); |
2648 | 0 | *content = NULL; |
2649 | 0 | } |
2650 | 0 | *content_len = 0; |
2651 | 0 | BLOSC_TRACE_ERROR("Error while creating the decompression context"); |
2652 | 0 | return BLOSC2_ERROR_NULL_POINTER; |
2653 | 0 | } |
2654 | 0 | int nbytes_ = blosc2_decompress_ctx(dctx, meta->content, meta->content_len, *content, nbytes); |
2655 | 0 | blosc2_free_ctx(dctx); |
2656 | 0 | if (nbytes_ != nbytes) { |
2657 | 0 | if (*content != NULL) { |
2658 | 0 | free(*content); |
2659 | 0 | *content = NULL; |
2660 | 0 | } |
2661 | 0 | *content_len = 0; |
2662 | 0 | BLOSC_TRACE_ERROR("User metalayer \"%s\" is corrupted.", meta->name); |
2663 | 0 | return BLOSC2_ERROR_READ_BUFFER; |
2664 | 0 | } |
2665 | 0 | return nvlmetalayer; |
2666 | 0 | } |
2667 | | |
2668 | | int blosc2_vlmeta_update(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len, |
2669 | 0 | blosc2_cparams *cparams) { |
2670 | 0 | if (schunk == NULL || name == NULL) { |
2671 | 0 | BLOSC_TRACE_ERROR("Invalid parameters."); |
2672 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2673 | 0 | } |
2674 | 0 | int nvlmetalayer = blosc2_vlmeta_exists(schunk, name); |
2675 | 0 | if (nvlmetalayer < 0) { |
2676 | 0 | BLOSC_TRACE_ERROR("User vlmetalayer \"%s\" not found.", name); |
2677 | 0 | return nvlmetalayer; |
2678 | 0 | } |
2679 | 0 | if (content_len < 0) { |
2680 | 0 | BLOSC_TRACE_ERROR("Variable-length metalayer content length must not be negative."); |
2681 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2682 | 0 | } |
2683 | 0 | if (content_len > 0 && content == NULL) { |
2684 | 0 | BLOSC_TRACE_ERROR("Variable-length metalayer content pointer must not be NULL when content length is positive."); |
2685 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2686 | 0 | } |
2687 | | |
2688 | 0 | blosc2_metalayer *vlmetalayer = schunk->vlmetalayers[nvlmetalayer]; |
2689 | 0 | int64_t destsize = (int64_t)content_len + BLOSC2_MAX_OVERHEAD; |
2690 | 0 | if (destsize > INT32_MAX) { |
2691 | 0 | BLOSC_TRACE_ERROR("Variable-length metalayer size is too large."); |
2692 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
2693 | 0 | } |
2694 | 0 | size_t destsize_sz = (size_t)destsize; |
2695 | 0 | uint8_t* content_buf = malloc(destsize_sz); |
2696 | 0 | if (content_buf == NULL && content_len > 0) { |
2697 | 0 | BLOSC_TRACE_ERROR("Unable to allocate variable-length metalayer buffer."); |
2698 | 0 | return BLOSC2_ERROR_MEMORY_ALLOC; |
2699 | 0 | } |
2700 | | |
2701 | 0 | blosc2_context *cctx; |
2702 | 0 | if (cparams != NULL) { |
2703 | 0 | cctx = blosc2_create_cctx(*cparams); |
2704 | 0 | } else { |
2705 | 0 | cctx = blosc2_create_cctx(BLOSC2_CPARAMS_DEFAULTS); |
2706 | 0 | } |
2707 | 0 | if (cctx == NULL) { |
2708 | 0 | free(content_buf); |
2709 | 0 | BLOSC_TRACE_ERROR("Error while creating the compression context"); |
2710 | 0 | return BLOSC2_ERROR_NULL_POINTER; |
2711 | 0 | } |
2712 | | |
2713 | 0 | int csize = blosc2_compress_ctx(cctx, content, content_len, content_buf, (int32_t)destsize); |
2714 | 0 | if (csize < 0) { |
2715 | 0 | blosc2_free_ctx(cctx); |
2716 | 0 | free(content_buf); |
2717 | 0 | BLOSC_TRACE_ERROR("Can not compress the `%s` variable-length metalayer.", name); |
2718 | 0 | return csize; |
2719 | 0 | } |
2720 | 0 | blosc2_free_ctx(cctx); |
2721 | |
|
2722 | 0 | uint8_t* compressed_buf = realloc(content_buf, csize); |
2723 | 0 | if (compressed_buf == NULL && csize > 0) { |
2724 | 0 | compressed_buf = content_buf; |
2725 | 0 | } |
2726 | 0 | uint8_t* old_content = vlmetalayer->content; |
2727 | 0 | vlmetalayer->content = compressed_buf; |
2728 | 0 | vlmetalayer->content_len = csize; |
2729 | 0 | free(old_content); |
2730 | | |
2731 | | // Propagate to frames |
2732 | 0 | int rc = vlmetalayer_flush(schunk); |
2733 | 0 | if (rc < 0) { |
2734 | 0 | BLOSC_TRACE_ERROR("Can not propagate de `%s` variable-length metalayer to a frame.", name); |
2735 | 0 | return rc; |
2736 | 0 | } |
2737 | 0 | schunk->change_tick++; |
2738 | |
|
2739 | 0 | return nvlmetalayer; |
2740 | 0 | } |
2741 | | |
2742 | 0 | int blosc2_vlmeta_delete(blosc2_schunk *schunk, const char *name) { |
2743 | 0 | int nvlmetalayer = blosc2_vlmeta_exists(schunk, name); |
2744 | 0 | if (nvlmetalayer < 0) { |
2745 | 0 | BLOSC_TRACE_ERROR("User vlmetalayer \"%s\" not found.", name); |
2746 | 0 | return nvlmetalayer; |
2747 | 0 | } |
2748 | | |
2749 | 0 | blosc2_metalayer *vlmetalayer = schunk->vlmetalayers[nvlmetalayer]; |
2750 | 0 | for (int i = nvlmetalayer; i < (schunk->nvlmetalayers - 1); i++) { |
2751 | 0 | schunk->vlmetalayers[i] = schunk->vlmetalayers[i + 1]; |
2752 | 0 | } |
2753 | 0 | schunk->vlmetalayers[schunk->nvlmetalayers - 1] = NULL; |
2754 | 0 | free(vlmetalayer->name); |
2755 | 0 | free(vlmetalayer->content); |
2756 | 0 | free(vlmetalayer); |
2757 | 0 | schunk->nvlmetalayers--; |
2758 | | |
2759 | | // Propagate to frames |
2760 | 0 | int rc = vlmetalayer_flush(schunk); |
2761 | 0 | if (rc < 0) { |
2762 | 0 | BLOSC_TRACE_ERROR("Can not propagate de `%s` variable-length metalayer to a frame.", name); |
2763 | 0 | return rc; |
2764 | 0 | } |
2765 | 0 | schunk->change_tick++; |
2766 | |
|
2767 | 0 | return schunk->nvlmetalayers; |
2768 | 0 | } |
2769 | | |
2770 | | |
2771 | 0 | int blosc2_vlmeta_get_names(blosc2_schunk *schunk, char **names) { |
2772 | 0 | int16_t nvlmetalayers = schunk->nvlmetalayers; |
2773 | |
|
2774 | 0 | for (int i = 0; i < nvlmetalayers; ++i) { |
2775 | 0 | names[i] = schunk->vlmetalayers[i]->name; |
2776 | 0 | } |
2777 | |
|
2778 | 0 | return nvlmetalayers; |
2779 | 0 | } |