/src/c-blosc2/blosc/schunk.c
Line | Count | Source (jump to first uncovered line) |
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 "blosc2/tuners-registry.h" |
15 | | #include "blosc2.h" |
16 | | |
17 | | #if defined(_WIN32) |
18 | | #include <windows.h> |
19 | | #include <direct.h> |
20 | | #include <malloc.h> |
21 | | #define mkdir(D, M) _mkdir(D) |
22 | | #endif /* _WIN32 */ |
23 | | |
24 | | #include <sys/stat.h> |
25 | | |
26 | | #include <inttypes.h> |
27 | | #include <stdio.h> |
28 | | #include <stdint.h> |
29 | | #include <stdlib.h> |
30 | | #include <string.h> |
31 | | |
32 | | /* If C11 is supported, use it's built-in aligned allocation. */ |
33 | | #if __STDC_VERSION__ >= 201112L |
34 | | #include <stdalign.h> |
35 | | #endif |
36 | | |
37 | | |
38 | | /* Get the cparams associated with a super-chunk */ |
39 | 1 | int blosc2_schunk_get_cparams(blosc2_schunk *schunk, blosc2_cparams **cparams) { |
40 | 1 | *cparams = calloc(1, sizeof(blosc2_cparams)); |
41 | 1 | (*cparams)->schunk = schunk; |
42 | 7 | for (int i = 0; i < BLOSC2_MAX_FILTERS; i++) { |
43 | 6 | (*cparams)->filters[i] = schunk->filters[i]; |
44 | 6 | (*cparams)->filters_meta[i] = schunk->filters_meta[i]; |
45 | 6 | } |
46 | 1 | (*cparams)->compcode = schunk->compcode; |
47 | 1 | (*cparams)->compcode_meta = schunk->compcode_meta; |
48 | 1 | (*cparams)->clevel = schunk->clevel; |
49 | 1 | (*cparams)->typesize = schunk->typesize; |
50 | 1 | (*cparams)->blocksize = schunk->blocksize; |
51 | 1 | (*cparams)->splitmode = schunk->splitmode; |
52 | 1 | if (schunk->cctx == NULL) { |
53 | 1 | (*cparams)->nthreads = blosc2_get_nthreads(); |
54 | 1 | } |
55 | 0 | else { |
56 | 0 | (*cparams)->nthreads = (int16_t)schunk->cctx->nthreads; |
57 | 0 | } |
58 | 1 | return 0; |
59 | 1 | } |
60 | | |
61 | | |
62 | | /* Get the dparams associated with a super-chunk */ |
63 | 1 | int blosc2_schunk_get_dparams(blosc2_schunk *schunk, blosc2_dparams **dparams) { |
64 | 1 | *dparams = calloc(1, sizeof(blosc2_dparams)); |
65 | 1 | (*dparams)->schunk = schunk; |
66 | 1 | if (schunk->dctx == NULL) { |
67 | 1 | (*dparams)->nthreads = blosc2_get_nthreads(); |
68 | 1 | } |
69 | 0 | else { |
70 | 0 | (*dparams)->nthreads = schunk->dctx->nthreads; |
71 | 0 | } |
72 | 1 | return 0; |
73 | 1 | } |
74 | | |
75 | | |
76 | 2.25k | int update_schunk_properties(struct blosc2_schunk* schunk) { |
77 | 2.25k | blosc2_cparams* cparams = schunk->storage->cparams; |
78 | 2.25k | blosc2_dparams* dparams = schunk->storage->dparams; |
79 | | |
80 | 15.8k | for (int i = 0; i < BLOSC2_MAX_FILTERS; i++) { |
81 | 13.5k | schunk->filters[i] = cparams->filters[i]; |
82 | 13.5k | schunk->filters_meta[i] = cparams->filters_meta[i]; |
83 | 13.5k | } |
84 | 2.25k | schunk->compcode = cparams->compcode; |
85 | 2.25k | schunk->compcode_meta = cparams->compcode_meta; |
86 | 2.25k | schunk->clevel = cparams->clevel; |
87 | 2.25k | schunk->splitmode = cparams->splitmode; |
88 | 2.25k | schunk->typesize = cparams->typesize; |
89 | 2.25k | schunk->blocksize = cparams->blocksize; |
90 | 2.25k | schunk->chunksize = -1; |
91 | 2.25k | schunk->tuner_params = cparams->tuner_params; |
92 | 2.25k | schunk->tuner_id = cparams->tuner_id; |
93 | 2.25k | if (cparams->tuner_id == BLOSC_BTUNE) { |
94 | 0 | cparams->use_dict = 0; |
95 | 0 | } |
96 | | /* The compression context */ |
97 | 2.25k | if (schunk->cctx != NULL) { |
98 | 0 | blosc2_free_ctx(schunk->cctx); |
99 | 0 | } |
100 | 2.25k | cparams->schunk = schunk; |
101 | 2.25k | schunk->cctx = blosc2_create_cctx(*cparams); |
102 | 2.25k | if (schunk->cctx == NULL) { |
103 | 0 | BLOSC_TRACE_ERROR("Could not create compression ctx"); |
104 | 0 | return BLOSC2_ERROR_NULL_POINTER; |
105 | 0 | } |
106 | | |
107 | | /* The decompression context */ |
108 | 2.25k | if (schunk->dctx != NULL) { |
109 | 0 | blosc2_free_ctx(schunk->dctx); |
110 | 0 | } |
111 | 2.25k | dparams->schunk = schunk; |
112 | 2.25k | schunk->dctx = blosc2_create_dctx(*dparams); |
113 | 2.25k | if (schunk->dctx == NULL) { |
114 | 0 | BLOSC_TRACE_ERROR("Could not create decompression ctx"); |
115 | 0 | return BLOSC2_ERROR_NULL_POINTER; |
116 | 0 | } |
117 | | |
118 | 2.25k | return BLOSC2_ERROR_SUCCESS; |
119 | 2.25k | } |
120 | | |
121 | | |
122 | 0 | static bool file_exists (char *filename) { |
123 | 0 | struct stat buffer; |
124 | 0 | return (stat (filename, &buffer) == 0); |
125 | 0 | } |
126 | | |
127 | | |
128 | | /* Create a new super-chunk */ |
129 | 2.25k | blosc2_schunk* blosc2_schunk_new(blosc2_storage *storage) { |
130 | 2.25k | blosc2_schunk* schunk = calloc(1, sizeof(blosc2_schunk)); |
131 | 2.25k | schunk->version = 0; /* pre-first version */ |
132 | | |
133 | | // Get the storage with proper defaults |
134 | 2.25k | schunk->storage = get_new_storage(storage, &BLOSC2_CPARAMS_DEFAULTS, &BLOSC2_DPARAMS_DEFAULTS, &BLOSC2_IO_DEFAULTS); |
135 | | // Update the (local variable) storage |
136 | 2.25k | storage = schunk->storage; |
137 | | |
138 | 2.25k | char* tradeoff = getenv("BTUNE_TRADEOFF"); |
139 | 2.25k | if (tradeoff != NULL) { |
140 | | // If BTUNE_TRADEOFF passed, automatically use btune |
141 | 0 | storage->cparams->tuner_id = BLOSC_BTUNE; |
142 | 0 | } |
143 | | |
144 | | // ...and update internal properties |
145 | 2.25k | if (update_schunk_properties(schunk) < 0) { |
146 | 0 | BLOSC_TRACE_ERROR("Error when updating schunk properties"); |
147 | 0 | return NULL; |
148 | 0 | } |
149 | | |
150 | 2.25k | if (!storage->contiguous && storage->urlpath != NULL){ |
151 | 0 | char* urlpath; |
152 | 0 | char last_char = storage->urlpath[strlen(storage->urlpath) - 1]; |
153 | 0 | urlpath = malloc(strlen(storage->urlpath) + 1); |
154 | 0 | strcpy(urlpath, storage->urlpath); |
155 | 0 | if (last_char == '\\' || last_char == '/') { |
156 | 0 | urlpath[strlen(storage->urlpath) - 1] = '\0'; |
157 | 0 | } |
158 | | // Create directory |
159 | 0 | if (mkdir(urlpath, 0777) == -1) { |
160 | 0 | BLOSC_TRACE_ERROR("Error during the creation of the directory, maybe it already exists."); |
161 | 0 | return NULL; |
162 | 0 | } |
163 | | // We want a sparse (directory) frame as storage |
164 | 0 | blosc2_frame_s* frame = frame_new(urlpath); |
165 | 0 | free(urlpath); |
166 | 0 | frame->sframe = true; |
167 | | // Initialize frame (basically, encode the header) |
168 | 0 | frame->schunk = schunk; |
169 | 0 | int64_t frame_len = frame_from_schunk(schunk, frame); |
170 | 0 | if (frame_len < 0) { |
171 | 0 | BLOSC_TRACE_ERROR("Error during the conversion of schunk to frame."); |
172 | 0 | return NULL; |
173 | 0 | } |
174 | 0 | schunk->frame = (blosc2_frame*)frame; |
175 | 0 | } |
176 | 2.25k | if (storage->contiguous){ |
177 | | // We want a contiguous frame as storage |
178 | 0 | if (storage->urlpath != NULL) { |
179 | 0 | if (file_exists(storage->urlpath)) { |
180 | 0 | BLOSC_TRACE_ERROR("You are trying to overwrite an existing frame. Remove it first!"); |
181 | 0 | return NULL; |
182 | 0 | } |
183 | 0 | } |
184 | 0 | blosc2_frame_s* frame = frame_new(storage->urlpath); |
185 | 0 | frame->sframe = false; |
186 | | // Initialize frame (basically, encode the header) |
187 | 0 | frame->schunk = schunk; |
188 | 0 | int64_t frame_len = frame_from_schunk(schunk, frame); |
189 | 0 | if (frame_len < 0) { |
190 | 0 | BLOSC_TRACE_ERROR("Error during the conversion of schunk to frame."); |
191 | 0 | return NULL; |
192 | 0 | } |
193 | 0 | schunk->frame = (blosc2_frame*)frame; |
194 | 0 | } |
195 | | |
196 | 2.25k | return schunk; |
197 | 2.25k | } |
198 | | |
199 | | |
200 | | /* Create a copy of a super-chunk */ |
201 | 0 | blosc2_schunk* blosc2_schunk_copy(blosc2_schunk *schunk, blosc2_storage *storage) { |
202 | 0 | if (schunk == NULL) { |
203 | 0 | BLOSC_TRACE_ERROR("Can not copy a NULL `schunk`."); |
204 | 0 | return NULL; |
205 | 0 | } |
206 | | |
207 | | // Check if cparams are equals |
208 | 0 | bool cparams_equal = true; |
209 | 0 | blosc2_cparams cparams = {0}; |
210 | 0 | if (storage->cparams == NULL) { |
211 | | // When cparams are not specified, just use the same of schunk |
212 | 0 | cparams.typesize = schunk->cctx->typesize; |
213 | 0 | cparams.clevel = schunk->cctx->clevel; |
214 | 0 | cparams.compcode = schunk->cctx->compcode; |
215 | 0 | cparams.compcode_meta = schunk->cctx->compcode_meta; |
216 | 0 | cparams.splitmode = schunk->cctx->splitmode; |
217 | 0 | cparams.use_dict = schunk->cctx->use_dict; |
218 | 0 | cparams.blocksize = schunk->cctx->blocksize; |
219 | 0 | memcpy(cparams.filters, schunk->cctx->filters, BLOSC2_MAX_FILTERS); |
220 | 0 | memcpy(cparams.filters_meta, schunk->cctx->filters_meta, BLOSC2_MAX_FILTERS); |
221 | 0 | storage->cparams = &cparams; |
222 | 0 | } |
223 | 0 | else { |
224 | 0 | cparams = *storage->cparams; |
225 | 0 | } |
226 | 0 | if (cparams.blocksize == 0) { |
227 | | // TODO: blocksize should be read from schunk->blocksize |
228 | | // For this, it should be updated during the first append |
229 | | // (or change API to make this a property during schunk creation). |
230 | 0 | cparams.blocksize = schunk->cctx->blocksize; |
231 | 0 | } |
232 | |
|
233 | 0 | if (cparams.typesize != schunk->cctx->typesize || |
234 | 0 | cparams.clevel != schunk->cctx->clevel || |
235 | 0 | cparams.compcode != schunk->cctx->compcode || |
236 | 0 | cparams.use_dict != schunk->cctx->use_dict || |
237 | 0 | cparams.blocksize != schunk->cctx->blocksize || |
238 | | // In case of prefilters or postfilters, force their execution. |
239 | 0 | schunk->cctx->prefilter != NULL || |
240 | 0 | schunk->dctx->postfilter != NULL) { |
241 | 0 | cparams_equal = false; |
242 | 0 | } |
243 | 0 | for (int i = 0; i < BLOSC2_MAX_FILTERS; ++i) { |
244 | 0 | if (cparams.filters[i] != schunk->cctx->filters[i] || |
245 | 0 | cparams.filters_meta[i] != schunk->cctx->filters_meta[i]) { |
246 | 0 | cparams_equal = false; |
247 | 0 | } |
248 | 0 | } |
249 | | |
250 | | // Create new schunk |
251 | 0 | blosc2_schunk *new_schunk = blosc2_schunk_new(storage); |
252 | 0 | if (new_schunk == NULL) { |
253 | 0 | BLOSC_TRACE_ERROR("Can not create a new schunk"); |
254 | 0 | return NULL; |
255 | 0 | } |
256 | | |
257 | | // Copy metalayers |
258 | 0 | for (int nmeta = 0; nmeta < schunk->nmetalayers; ++nmeta) { |
259 | 0 | blosc2_metalayer *meta = schunk->metalayers[nmeta]; |
260 | 0 | if (blosc2_meta_add(new_schunk, meta->name, meta->content, meta->content_len) < 0) { |
261 | 0 | BLOSC_TRACE_ERROR("Can not add %s `metalayer`.", meta->name); |
262 | 0 | return NULL; |
263 | 0 | } |
264 | 0 | } |
265 | | |
266 | | // Copy chunks |
267 | 0 | if (cparams_equal) { |
268 | 0 | for (int nchunk = 0; nchunk < schunk->nchunks; ++nchunk) { |
269 | 0 | uint8_t *chunk; |
270 | 0 | bool needs_free; |
271 | 0 | if (blosc2_schunk_get_chunk(schunk, nchunk, &chunk, &needs_free) < 0) { |
272 | 0 | BLOSC_TRACE_ERROR("Can not get the `chunk` %d.", nchunk); |
273 | 0 | return NULL; |
274 | 0 | } |
275 | 0 | if (blosc2_schunk_append_chunk(new_schunk, chunk, !needs_free) < 0) { |
276 | 0 | BLOSC_TRACE_ERROR("Can not append the `chunk` into super-chunk."); |
277 | 0 | return NULL; |
278 | 0 | } |
279 | 0 | } |
280 | 0 | } else { |
281 | 0 | int32_t chunksize = schunk->chunksize == -1 ? 0 : schunk->chunksize; |
282 | 0 | uint8_t *buffer = malloc(chunksize); |
283 | 0 | for (int nchunk = 0; nchunk < schunk->nchunks; ++nchunk) { |
284 | 0 | if (blosc2_schunk_decompress_chunk(schunk, nchunk, buffer, schunk->chunksize) < 0) { |
285 | 0 | BLOSC_TRACE_ERROR("Can not decompress the `chunk` %d.", nchunk); |
286 | 0 | return NULL; |
287 | 0 | } |
288 | 0 | if (blosc2_schunk_append_buffer(new_schunk, buffer, schunk->chunksize) < 0) { |
289 | 0 | BLOSC_TRACE_ERROR("Can not append the `buffer` into super-chunk."); |
290 | 0 | return NULL; |
291 | 0 | } |
292 | 0 | } |
293 | 0 | free(buffer); |
294 | 0 | } |
295 | | |
296 | | // Copy vlmetalayers |
297 | 0 | for (int nmeta = 0; nmeta < schunk->nvlmetalayers; ++nmeta) { |
298 | 0 | uint8_t *content; |
299 | 0 | int32_t content_len; |
300 | 0 | char* name = schunk->vlmetalayers[nmeta]->name; |
301 | 0 | if (blosc2_vlmeta_get(schunk, name, &content, &content_len) < 0) { |
302 | 0 | BLOSC_TRACE_ERROR("Can not get %s `vlmetalayer`.", name); |
303 | 0 | } |
304 | 0 | if (blosc2_vlmeta_add(new_schunk, name, content, content_len, NULL) < 0) { |
305 | 0 | BLOSC_TRACE_ERROR("Can not add %s `vlmetalayer`.", name); |
306 | 0 | return NULL; |
307 | 0 | } |
308 | 0 | free(content); |
309 | 0 | } |
310 | 0 | return new_schunk; |
311 | 0 | } |
312 | | |
313 | | |
314 | | /* Open an existing super-chunk that is on-disk (no copy is made). */ |
315 | 0 | blosc2_schunk* blosc2_schunk_open_udio(const char* urlpath, const blosc2_io *udio) { |
316 | 0 | return blosc2_schunk_open_offset_udio(urlpath, 0, udio); |
317 | 0 | } |
318 | | |
319 | 0 | blosc2_schunk* blosc2_schunk_open_offset_udio(const char* urlpath, int64_t offset, const blosc2_io *udio) { |
320 | 0 | if (urlpath == NULL) { |
321 | 0 | BLOSC_TRACE_ERROR("You need to supply a urlpath."); |
322 | 0 | return NULL; |
323 | 0 | } |
324 | | |
325 | 0 | blosc2_frame_s* frame = frame_from_file_offset(urlpath, udio, offset); |
326 | 0 | if (frame == NULL) { |
327 | 0 | blosc2_io_cb *io_cb = blosc2_get_io_cb(udio->id); |
328 | 0 | if (io_cb == NULL) { |
329 | 0 | BLOSC_TRACE_ERROR("Error getting the input/output API"); |
330 | 0 | return NULL; |
331 | 0 | } |
332 | 0 | int rc = io_cb->destroy(udio->params); |
333 | 0 | if (rc < 0) { |
334 | 0 | BLOSC_TRACE_ERROR("Cannot destroy the input/output object."); |
335 | 0 | } |
336 | 0 | return NULL; |
337 | 0 | } |
338 | 0 | blosc2_schunk* schunk = frame_to_schunk(frame, false, udio); |
339 | | |
340 | | // Set the storage with proper defaults |
341 | 0 | size_t pathlen = strlen(urlpath); |
342 | 0 | schunk->storage->urlpath = malloc(pathlen + 1); |
343 | 0 | strcpy(schunk->storage->urlpath, urlpath); |
344 | 0 | schunk->storage->contiguous = !frame->sframe; |
345 | |
|
346 | 0 | return schunk; |
347 | 0 | } |
348 | | |
349 | 0 | blosc2_schunk* blosc2_schunk_open(const char* urlpath) { |
350 | 0 | return blosc2_schunk_open_udio(urlpath, &BLOSC2_IO_DEFAULTS); |
351 | 0 | } |
352 | | |
353 | 0 | blosc2_schunk* blosc2_schunk_open_offset(const char* urlpath, int64_t offset) { |
354 | 0 | return blosc2_schunk_open_offset_udio(urlpath, offset, &BLOSC2_IO_DEFAULTS); |
355 | 0 | } |
356 | | |
357 | 0 | int64_t blosc2_schunk_to_buffer(blosc2_schunk* schunk, uint8_t** dest, bool* needs_free) { |
358 | 0 | blosc2_frame_s* frame; |
359 | 0 | int64_t cframe_len; |
360 | | |
361 | | // Initialize defaults in case of errors |
362 | 0 | *dest = NULL; |
363 | 0 | *needs_free = false; |
364 | |
|
365 | 0 | if ((schunk->storage->contiguous == true) && (schunk->storage->urlpath == NULL)) { |
366 | 0 | frame = (blosc2_frame_s*)(schunk->frame); |
367 | 0 | *dest = frame->cframe; |
368 | 0 | cframe_len = frame->len; |
369 | 0 | *needs_free = false; |
370 | 0 | } |
371 | 0 | else { |
372 | | // Copy to a contiguous storage |
373 | 0 | blosc2_storage frame_storage = {.contiguous=true}; |
374 | 0 | blosc2_schunk* schunk_copy = blosc2_schunk_copy(schunk, &frame_storage); |
375 | 0 | if (schunk_copy == NULL) { |
376 | 0 | BLOSC_TRACE_ERROR("Error during the conversion of schunk to buffer."); |
377 | 0 | return BLOSC2_ERROR_SCHUNK_COPY; |
378 | 0 | } |
379 | 0 | frame = (blosc2_frame_s*)(schunk_copy->frame); |
380 | 0 | *dest = frame->cframe; |
381 | 0 | cframe_len = frame->len; |
382 | 0 | *needs_free = true; |
383 | 0 | frame->avoid_cframe_free = true; |
384 | 0 | blosc2_schunk_free(schunk_copy); |
385 | 0 | } |
386 | | |
387 | 0 | return cframe_len; |
388 | |
|
389 | 0 | } |
390 | | |
391 | | |
392 | | /* Write an in-memory frame out to a file. */ |
393 | 0 | int64_t frame_to_file(blosc2_frame_s* frame, const char* urlpath) { |
394 | 0 | blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); |
395 | 0 | if (io_cb == NULL) { |
396 | 0 | BLOSC_TRACE_ERROR("Error getting the input/output API"); |
397 | 0 | return BLOSC2_ERROR_PLUGIN_IO; |
398 | 0 | } |
399 | 0 | void* fp = io_cb->open(urlpath, "wb", frame->schunk->storage->io); |
400 | 0 | int64_t io_pos = 0; |
401 | 0 | int64_t nitems = io_cb->write(frame->cframe, frame->len, 1, io_pos, fp); |
402 | 0 | io_cb->close(fp); |
403 | 0 | return nitems * frame->len; |
404 | 0 | } |
405 | | |
406 | | |
407 | | /* Append an in-memory frame to a file. */ |
408 | 0 | int64_t append_frame_to_file(blosc2_frame_s* frame, const char* urlpath) { |
409 | 0 | blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); |
410 | 0 | if (io_cb == NULL) { |
411 | 0 | BLOSC_TRACE_ERROR("Error getting the input/output API"); |
412 | 0 | return BLOSC2_ERROR_PLUGIN_IO; |
413 | 0 | } |
414 | 0 | void* fp = io_cb->open(urlpath, "ab", frame->schunk->storage->io); |
415 | |
|
416 | 0 | int64_t io_pos = io_cb->size(fp); |
417 | 0 | io_cb->write(frame->cframe, frame->len, 1, io_pos, fp); |
418 | 0 | io_cb->close(fp); |
419 | 0 | return io_pos; |
420 | 0 | } |
421 | | |
422 | | |
423 | | /* Write super-chunk out to a file. */ |
424 | 0 | int64_t blosc2_schunk_to_file(blosc2_schunk* schunk, const char* urlpath) { |
425 | 0 | if (urlpath == NULL) { |
426 | 0 | BLOSC_TRACE_ERROR("urlpath cannot be NULL"); |
427 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
428 | 0 | } |
429 | | |
430 | | // Accelerated path for in-memory frames |
431 | 0 | if (schunk->storage->contiguous && schunk->storage->urlpath == NULL) { |
432 | 0 | int64_t len = frame_to_file((blosc2_frame_s*)(schunk->frame), urlpath); |
433 | 0 | if (len <= 0) { |
434 | 0 | BLOSC_TRACE_ERROR("Error writing to file"); |
435 | 0 | return len; |
436 | 0 | } |
437 | 0 | return len; |
438 | 0 | } |
439 | | |
440 | | // Copy to a contiguous file |
441 | 0 | blosc2_storage frame_storage = {.contiguous=true, .urlpath=(char*)urlpath}; |
442 | 0 | blosc2_schunk* schunk_copy = blosc2_schunk_copy(schunk, &frame_storage); |
443 | 0 | if (schunk_copy == NULL) { |
444 | 0 | BLOSC_TRACE_ERROR("Error during the conversion of schunk to buffer."); |
445 | 0 | return BLOSC2_ERROR_SCHUNK_COPY; |
446 | 0 | } |
447 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)(schunk_copy->frame); |
448 | 0 | int64_t frame_len = frame->len; |
449 | 0 | blosc2_schunk_free(schunk_copy); |
450 | 0 | return frame_len; |
451 | 0 | } |
452 | | |
453 | | |
454 | | /* Append a super-chunk to a file. */ |
455 | 0 | int64_t blosc2_schunk_append_file(blosc2_schunk* schunk, const char* urlpath) { |
456 | 0 | if (urlpath == NULL) { |
457 | 0 | BLOSC_TRACE_ERROR("urlpath cannot be NULL"); |
458 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
459 | 0 | } |
460 | | |
461 | | // Accelerated path for in-memory frames |
462 | 0 | if (schunk->storage->contiguous && schunk->storage->urlpath == NULL) { |
463 | 0 | int64_t offset = append_frame_to_file((blosc2_frame_s*)(schunk->frame), urlpath); |
464 | 0 | if (offset <= 0) { |
465 | 0 | BLOSC_TRACE_ERROR("Error writing to file"); |
466 | 0 | return offset; |
467 | 0 | } |
468 | 0 | return offset; |
469 | 0 | } |
470 | | |
471 | | // Copy to a contiguous file |
472 | 0 | blosc2_storage frame_storage = {.contiguous=true, .urlpath=NULL}; |
473 | 0 | blosc2_schunk* schunk_copy = blosc2_schunk_copy(schunk, &frame_storage); |
474 | 0 | if (schunk_copy == NULL) { |
475 | 0 | BLOSC_TRACE_ERROR("Error during the conversion of schunk to buffer."); |
476 | 0 | return BLOSC2_ERROR_SCHUNK_COPY; |
477 | 0 | } |
478 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)(schunk_copy->frame); |
479 | 0 | int64_t offset = append_frame_to_file(frame, urlpath); |
480 | 0 | blosc2_schunk_free(schunk_copy); |
481 | 0 | return offset; |
482 | 0 | } |
483 | | |
484 | | |
485 | | /* Free all memory from a super-chunk. */ |
486 | 2.25k | int blosc2_schunk_free(blosc2_schunk *schunk) { |
487 | 2.25k | if (schunk->data != NULL) { |
488 | 44.6k | for (int i = 0; i < schunk->nchunks; i++) { |
489 | 42.3k | free(schunk->data[i]); |
490 | 42.3k | } |
491 | 2.25k | free(schunk->data); |
492 | 2.25k | } |
493 | 2.25k | if (schunk->cctx != NULL) |
494 | 2.25k | blosc2_free_ctx(schunk->cctx); |
495 | 2.25k | if (schunk->dctx != NULL) |
496 | 2.25k | blosc2_free_ctx(schunk->dctx); |
497 | 2.25k | if (schunk->blockshape != NULL) |
498 | 0 | free(schunk->blockshape); |
499 | | |
500 | 2.25k | if (schunk->nmetalayers > 0) { |
501 | 0 | for (int i = 0; i < schunk->nmetalayers; i++) { |
502 | 0 | if (schunk->metalayers[i] != NULL) { |
503 | 0 | if (schunk->metalayers[i]->name != NULL) |
504 | 0 | free(schunk->metalayers[i]->name); |
505 | 0 | if (schunk->metalayers[i]->content != NULL) |
506 | 0 | free(schunk->metalayers[i]->content); |
507 | 0 | free(schunk->metalayers[i]); |
508 | 0 | } |
509 | 0 | } |
510 | 0 | schunk->nmetalayers = 0; |
511 | 0 | } |
512 | | |
513 | 2.25k | if (schunk->storage != NULL) { |
514 | 2.25k | blosc2_io_cb *io_cb = blosc2_get_io_cb(schunk->storage->io->id); |
515 | 2.25k | if (io_cb != NULL) { |
516 | 2.25k | int rc = io_cb->destroy(schunk->storage->io->params); |
517 | 2.25k | if (rc < 0) { |
518 | 0 | BLOSC_TRACE_ERROR("Could not free the I/O resources."); |
519 | 0 | } |
520 | 2.25k | } |
521 | | |
522 | 2.25k | if (schunk->storage->urlpath != NULL) { |
523 | 0 | free(schunk->storage->urlpath); |
524 | 0 | } |
525 | 2.25k | free(schunk->storage->cparams); |
526 | 2.25k | free(schunk->storage->dparams); |
527 | 2.25k | free(schunk->storage->io); |
528 | 2.25k | free(schunk->storage); |
529 | 2.25k | } |
530 | | |
531 | 2.25k | if (schunk->frame != NULL) { |
532 | 1 | frame_free((blosc2_frame_s *) schunk->frame); |
533 | 1 | } |
534 | | |
535 | 2.25k | if (schunk->nvlmetalayers > 0) { |
536 | 2 | for (int i = 0; i < schunk->nvlmetalayers; ++i) { |
537 | 1 | if (schunk->vlmetalayers[i] != NULL) { |
538 | 1 | if (schunk->vlmetalayers[i]->name != NULL) |
539 | 1 | free(schunk->vlmetalayers[i]->name); |
540 | 1 | if (schunk->vlmetalayers[i]->content != NULL) |
541 | 1 | free(schunk->vlmetalayers[i]->content); |
542 | 1 | free(schunk->vlmetalayers[i]); |
543 | 1 | } |
544 | 1 | } |
545 | 1 | } |
546 | | |
547 | 2.25k | free(schunk); |
548 | | |
549 | 2.25k | return 0; |
550 | 2.25k | } |
551 | | |
552 | | |
553 | | /* Create a super-chunk out of a contiguous frame buffer */ |
554 | 7 | blosc2_schunk* blosc2_schunk_from_buffer(uint8_t *cframe, int64_t len, bool copy) { |
555 | 7 | blosc2_frame_s* frame = frame_from_cframe(cframe, len, false); |
556 | 7 | if (frame == NULL) { |
557 | 6 | return NULL; |
558 | 6 | } |
559 | | // Check that the buffer actually comes from a cframe |
560 | 1 | char *magic_number = (char *)cframe; |
561 | 1 | magic_number += FRAME_HEADER_MAGIC; |
562 | 1 | if (strcmp(magic_number, "b2frame\0") != 0) { |
563 | 0 | frame_free(frame); |
564 | 0 | return NULL; |
565 | 0 | } |
566 | 1 | blosc2_schunk* schunk = frame_to_schunk(frame, copy, &BLOSC2_IO_DEFAULTS); |
567 | 1 | if (schunk && copy) { |
568 | | // Super-chunk has its own copy of frame |
569 | 0 | frame_free(frame); |
570 | 0 | } |
571 | 1 | return schunk; |
572 | 1 | } |
573 | | |
574 | | |
575 | | /* Create a super-chunk out of a contiguous frame buffer */ |
576 | 0 | void blosc2_schunk_avoid_cframe_free(blosc2_schunk *schunk, bool avoid_cframe_free) { |
577 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
578 | 0 | frame_avoid_cframe_free(frame, avoid_cframe_free); |
579 | 0 | } |
580 | | |
581 | | |
582 | | /* Fill an empty frame with special values (fast path). */ |
583 | | int64_t blosc2_schunk_fill_special(blosc2_schunk* schunk, int64_t nitems, int special_value, |
584 | 0 | int32_t chunksize) { |
585 | 0 | if (nitems == 0) { |
586 | 0 | return 0; |
587 | 0 | } |
588 | | |
589 | 0 | int32_t typesize = schunk->typesize; |
590 | |
|
591 | 0 | if ((nitems * typesize / chunksize) > INT_MAX) { |
592 | 0 | BLOSC_TRACE_ERROR("nitems is too large. Try increasing the chunksize."); |
593 | 0 | return BLOSC2_ERROR_SCHUNK_SPECIAL; |
594 | 0 | } |
595 | | |
596 | 0 | if ((schunk->nbytes > 0) || (schunk->cbytes > 0)) { |
597 | 0 | BLOSC_TRACE_ERROR("Filling with special values only works on empty super-chunks"); |
598 | 0 | return BLOSC2_ERROR_FRAME_SPECIAL; |
599 | 0 | } |
600 | | |
601 | | // Compute the number of chunks and the length of the offsets chunk |
602 | 0 | int32_t chunkitems = chunksize / typesize; |
603 | 0 | int64_t nchunks = nitems / chunkitems; |
604 | 0 | int32_t leftover_items = (int32_t)(nitems % chunkitems); |
605 | |
|
606 | 0 | if (schunk->frame == NULL) { |
607 | | // Build the special chunks |
608 | 0 | int32_t leftover_size = leftover_items * typesize; |
609 | 0 | void* chunk = malloc(BLOSC_EXTENDED_HEADER_LENGTH); |
610 | 0 | void* chunk2 = malloc(BLOSC_EXTENDED_HEADER_LENGTH); |
611 | 0 | blosc2_cparams* cparams; |
612 | 0 | blosc2_schunk_get_cparams(schunk, &cparams); |
613 | 0 | int csize, csize2; |
614 | 0 | switch (special_value) { |
615 | 0 | case BLOSC2_SPECIAL_ZERO: |
616 | 0 | csize = blosc2_chunk_zeros(*cparams, chunksize, chunk, BLOSC_EXTENDED_HEADER_LENGTH); |
617 | 0 | csize2 = blosc2_chunk_zeros(*cparams, leftover_size, chunk2, BLOSC_EXTENDED_HEADER_LENGTH); |
618 | 0 | break; |
619 | 0 | case BLOSC2_SPECIAL_UNINIT: |
620 | 0 | csize = blosc2_chunk_uninit(*cparams, chunksize, chunk, BLOSC_EXTENDED_HEADER_LENGTH); |
621 | 0 | csize2 = blosc2_chunk_uninit(*cparams, leftover_size, chunk2, BLOSC_EXTENDED_HEADER_LENGTH); |
622 | 0 | break; |
623 | 0 | case BLOSC2_SPECIAL_NAN: |
624 | 0 | csize = blosc2_chunk_nans(*cparams, chunksize, chunk, BLOSC_EXTENDED_HEADER_LENGTH); |
625 | 0 | csize2 = blosc2_chunk_nans(*cparams, leftover_size, chunk2, BLOSC_EXTENDED_HEADER_LENGTH); |
626 | 0 | break; |
627 | 0 | default: |
628 | 0 | BLOSC_TRACE_ERROR("Only zeros, NaNs or non-initialized values are supported."); |
629 | 0 | return BLOSC2_ERROR_SCHUNK_SPECIAL; |
630 | 0 | } |
631 | 0 | free(cparams); |
632 | 0 | if (csize < 0 || csize2 < 0) { |
633 | 0 | BLOSC_TRACE_ERROR("Error creating special chunks."); |
634 | 0 | return BLOSC2_ERROR_SCHUNK_SPECIAL; |
635 | 0 | } |
636 | | |
637 | 0 | for (int nchunk = 0; nchunk < nchunks; nchunk++) { |
638 | 0 | int64_t nchunk_ = blosc2_schunk_append_chunk(schunk, chunk, true); |
639 | 0 | if (nchunk_ != nchunk + 1) { |
640 | 0 | BLOSC_TRACE_ERROR("Error appending special chunks."); |
641 | 0 | return BLOSC2_ERROR_SCHUNK_SPECIAL; |
642 | 0 | } |
643 | 0 | } |
644 | | |
645 | 0 | if (leftover_items) { |
646 | 0 | int64_t nchunk_ = blosc2_schunk_append_chunk(schunk, chunk2, true); |
647 | 0 | if (nchunk_ != nchunks + 1) { |
648 | 0 | BLOSC_TRACE_ERROR("Error appending last special chunk."); |
649 | 0 | return BLOSC2_ERROR_SCHUNK_SPECIAL; |
650 | 0 | } |
651 | 0 | } |
652 | 0 | free(chunk); |
653 | 0 | free(chunk2); |
654 | 0 | } |
655 | 0 | else { |
656 | | /* Fill an empty frame with special values (fast path). */ |
657 | 0 | blosc2_frame_s *frame = (blosc2_frame_s *) schunk->frame; |
658 | | /* Update counters (necessary for the frame_fill_special() logic) */ |
659 | 0 | if (leftover_items) { |
660 | 0 | nchunks += 1; |
661 | 0 | } |
662 | 0 | schunk->chunksize = chunksize; |
663 | 0 | schunk->nchunks = nchunks; |
664 | 0 | schunk->nbytes = nitems * typesize; |
665 | 0 | int64_t frame_len = frame_fill_special(frame, nitems, special_value, chunksize, schunk); |
666 | 0 | if (frame_len < 0) { |
667 | 0 | BLOSC_TRACE_ERROR("Error creating special frame."); |
668 | 0 | return frame_len; |
669 | 0 | } |
670 | 0 | } |
671 | | |
672 | 0 | return schunk->nchunks; |
673 | 0 | } |
674 | | |
675 | | /* Append an existing chunk into a super-chunk. */ |
676 | 42.3k | int64_t blosc2_schunk_append_chunk(blosc2_schunk *schunk, uint8_t *chunk, bool copy) { |
677 | 42.3k | int32_t chunk_nbytes; |
678 | 42.3k | int32_t chunk_cbytes; |
679 | 42.3k | int64_t nchunks = schunk->nchunks; |
680 | | |
681 | 42.3k | int rc = blosc2_cbuffer_sizes(chunk, &chunk_nbytes, &chunk_cbytes, NULL); |
682 | 42.3k | if (rc < 0) { |
683 | 0 | return rc; |
684 | 0 | } |
685 | | |
686 | 42.3k | if (schunk->chunksize == -1) { |
687 | 2.25k | schunk->chunksize = chunk_nbytes; // The super-chunk is initialized now |
688 | 2.25k | } |
689 | 42.3k | if (chunk_nbytes > schunk->chunksize) { |
690 | 0 | BLOSC_TRACE_ERROR("Appending chunks that have different lengths in the same schunk " |
691 | 0 | "is not supported yet: %d > %d.", chunk_nbytes, schunk->chunksize); |
692 | 0 | return BLOSC2_ERROR_CHUNK_APPEND; |
693 | 0 | } |
694 | | |
695 | | /* Update counters */ |
696 | 42.3k | schunk->current_nchunk = nchunks; |
697 | 42.3k | schunk->nchunks = nchunks + 1; |
698 | 42.3k | schunk->nbytes += chunk_nbytes; |
699 | 42.3k | if (schunk->frame == NULL) { |
700 | 42.3k | schunk->cbytes += chunk_cbytes; |
701 | 42.3k | } else { |
702 | | // A frame |
703 | 0 | int special_value = (chunk[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK; |
704 | 0 | switch (special_value) { |
705 | 0 | case BLOSC2_SPECIAL_ZERO: |
706 | 0 | case BLOSC2_SPECIAL_NAN: |
707 | 0 | case BLOSC2_SPECIAL_UNINIT: |
708 | 0 | schunk->cbytes += 0; |
709 | 0 | break; |
710 | 0 | default: |
711 | 0 | schunk->cbytes += chunk_cbytes; |
712 | 0 | } |
713 | 0 | } |
714 | | |
715 | 42.3k | if (copy) { |
716 | | // Make a copy of the chunk |
717 | 0 | uint8_t *chunk_copy = malloc(chunk_cbytes); |
718 | 0 | memcpy(chunk_copy, chunk, chunk_cbytes); |
719 | 0 | chunk = chunk_copy; |
720 | 0 | } |
721 | | |
722 | | // Update super-chunk or frame |
723 | 42.3k | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
724 | 42.3k | if (frame == NULL) { |
725 | | // Check that we are not appending a small chunk after another small chunk |
726 | 42.3k | if ((schunk->nchunks > 1) && (chunk_nbytes < schunk->chunksize)) { |
727 | 402 | uint8_t* last_chunk = schunk->data[nchunks - 1]; |
728 | 402 | int32_t last_nbytes; |
729 | 402 | rc = blosc2_cbuffer_sizes(last_chunk, &last_nbytes, NULL, NULL); |
730 | 402 | if (rc < 0) { |
731 | 0 | return rc; |
732 | 0 | } |
733 | 402 | if ((last_nbytes < schunk->chunksize) && (chunk_nbytes < schunk->chunksize)) { |
734 | 0 | BLOSC_TRACE_ERROR( |
735 | 0 | "Appending two consecutive chunks with a chunksize smaller than the schunk chunksize " |
736 | 0 | "is not allowed yet: %d != %d.", chunk_nbytes, schunk->chunksize); |
737 | 0 | return BLOSC2_ERROR_CHUNK_APPEND; |
738 | 0 | } |
739 | 402 | } |
740 | | |
741 | 42.3k | if (!copy && (chunk_cbytes < chunk_nbytes)) { |
742 | | // We still want to do a shrink of the chunk |
743 | 26.4k | chunk = realloc(chunk, chunk_cbytes); |
744 | 26.4k | } |
745 | | |
746 | | /* Make space for appending the copy of the chunk and do it */ |
747 | 42.3k | if ((nchunks + 1) * sizeof(void *) > schunk->data_len) { |
748 | | // Extend the data pointer by one memory page (4k) |
749 | 2.28k | schunk->data_len += 4096; // must be a multiple of sizeof(void*) |
750 | 2.28k | schunk->data = realloc(schunk->data, schunk->data_len); |
751 | 2.28k | } |
752 | 42.3k | schunk->data[nchunks] = chunk; |
753 | 42.3k | } |
754 | 0 | else { |
755 | 0 | if (frame_append_chunk(frame, chunk, schunk) == NULL) { |
756 | 0 | BLOSC_TRACE_ERROR("Problems appending a chunk."); |
757 | 0 | return BLOSC2_ERROR_CHUNK_APPEND; |
758 | 0 | } |
759 | 0 | } |
760 | 42.3k | return schunk->nchunks; |
761 | 42.3k | } |
762 | | |
763 | | |
764 | | /* Insert an existing @p chunk in a specified position on a super-chunk */ |
765 | 0 | int64_t blosc2_schunk_insert_chunk(blosc2_schunk *schunk, int64_t nchunk, uint8_t *chunk, bool copy) { |
766 | 0 | int32_t chunk_nbytes; |
767 | 0 | int32_t chunk_cbytes; |
768 | 0 | int64_t nchunks = schunk->nchunks; |
769 | |
|
770 | 0 | int rc = blosc2_cbuffer_sizes(chunk, &chunk_nbytes, &chunk_cbytes, NULL); |
771 | 0 | if (rc < 0) { |
772 | 0 | return rc; |
773 | 0 | } |
774 | | |
775 | 0 | if (schunk->chunksize == -1) { |
776 | 0 | schunk->chunksize = chunk_nbytes; // The super-chunk is initialized now |
777 | 0 | } |
778 | |
|
779 | 0 | if (chunk_nbytes > schunk->chunksize) { |
780 | 0 | BLOSC_TRACE_ERROR("Inserting chunks that have different lengths in the same schunk " |
781 | 0 | "is not supported yet: %d > %d.", chunk_nbytes, schunk->chunksize); |
782 | 0 | return BLOSC2_ERROR_CHUNK_INSERT; |
783 | 0 | } |
784 | | |
785 | | /* Update counters */ |
786 | 0 | schunk->current_nchunk = nchunk; |
787 | 0 | schunk->nchunks = nchunks + 1; |
788 | 0 | schunk->nbytes += chunk_nbytes; |
789 | 0 | if (schunk->frame == NULL) { |
790 | 0 | schunk->cbytes += chunk_cbytes; |
791 | 0 | } else { |
792 | | // A frame |
793 | 0 | int special_value = (chunk[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK; |
794 | 0 | switch (special_value) { |
795 | 0 | case BLOSC2_SPECIAL_ZERO: |
796 | 0 | case BLOSC2_SPECIAL_NAN: |
797 | 0 | case BLOSC2_SPECIAL_UNINIT: |
798 | 0 | schunk->cbytes += 0; |
799 | 0 | break; |
800 | 0 | default: |
801 | 0 | schunk->cbytes += chunk_cbytes; |
802 | 0 | } |
803 | 0 | } |
804 | | |
805 | 0 | if (copy) { |
806 | | // Make a copy of the chunk |
807 | 0 | uint8_t *chunk_copy = malloc(chunk_cbytes); |
808 | 0 | memcpy(chunk_copy, chunk, chunk_cbytes); |
809 | 0 | chunk = chunk_copy; |
810 | 0 | } |
811 | | |
812 | | // Update super-chunk or frame |
813 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
814 | 0 | if (frame == NULL) { |
815 | | // Check that we are not appending a small chunk after another small chunk |
816 | 0 | if ((schunk->nchunks > 0) && (chunk_nbytes < schunk->chunksize)) { |
817 | 0 | uint8_t* last_chunk = schunk->data[nchunks - 1]; |
818 | 0 | int32_t last_nbytes; |
819 | 0 | rc = blosc2_cbuffer_sizes(last_chunk, &last_nbytes, NULL, NULL); |
820 | 0 | if (rc < 0) { |
821 | 0 | return rc; |
822 | 0 | } |
823 | 0 | if ((last_nbytes < schunk->chunksize) && (chunk_nbytes < schunk->chunksize)) { |
824 | 0 | BLOSC_TRACE_ERROR("Appending two consecutive chunks with a chunksize smaller " |
825 | 0 | "than the schunk chunksize is not allowed yet: %d != %d", |
826 | 0 | chunk_nbytes, schunk->chunksize); |
827 | 0 | return BLOSC2_ERROR_CHUNK_APPEND; |
828 | 0 | } |
829 | 0 | } |
830 | | |
831 | 0 | if (!copy && (chunk_cbytes < chunk_nbytes)) { |
832 | | // We still want to do a shrink of the chunk |
833 | 0 | chunk = realloc(chunk, chunk_cbytes); |
834 | 0 | } |
835 | | |
836 | | // Make space for appending the copy of the chunk and do it |
837 | 0 | if ((nchunks + 1) * sizeof(void *) > schunk->data_len) { |
838 | | // Extend the data pointer by one memory page (4k) |
839 | 0 | schunk->data_len += 4096; // must be a multiple of sizeof(void*) |
840 | 0 | schunk->data = realloc(schunk->data, schunk->data_len); |
841 | 0 | } |
842 | | |
843 | | // Reorder the offsets and insert the new chunk |
844 | 0 | for (int64_t i = nchunks; i > nchunk; --i) { |
845 | 0 | schunk->data[i] = schunk->data[i-1]; |
846 | 0 | } |
847 | 0 | schunk->data[nchunk] = chunk; |
848 | 0 | } |
849 | | |
850 | 0 | else { |
851 | 0 | if (frame_insert_chunk(frame, nchunk, chunk, schunk) == NULL) { |
852 | 0 | BLOSC_TRACE_ERROR("Problems inserting a chunk in a frame."); |
853 | 0 | return BLOSC2_ERROR_CHUNK_INSERT; |
854 | 0 | } |
855 | 0 | } |
856 | 0 | return schunk->nchunks; |
857 | 0 | } |
858 | | |
859 | | |
860 | 0 | int64_t blosc2_schunk_update_chunk(blosc2_schunk *schunk, int64_t nchunk, uint8_t *chunk, bool copy) { |
861 | 0 | int32_t chunk_nbytes; |
862 | 0 | int32_t chunk_cbytes; |
863 | |
|
864 | 0 | int rc = blosc2_cbuffer_sizes(chunk, &chunk_nbytes, &chunk_cbytes, NULL); |
865 | 0 | if (rc < 0) { |
866 | 0 | return rc; |
867 | 0 | } |
868 | | |
869 | 0 | if (schunk->chunksize == -1) { |
870 | 0 | schunk->chunksize = chunk_nbytes; // The super-chunk is initialized now |
871 | 0 | } |
872 | |
|
873 | 0 | if (schunk->chunksize != 0 && (chunk_nbytes > schunk->chunksize || |
874 | 0 | (chunk_nbytes < schunk->chunksize && nchunk != schunk->nchunks - 1))) { |
875 | 0 | BLOSC_TRACE_ERROR("Updating chunks having different lengths in the same schunk " |
876 | 0 | "is not supported yet (unless it's the last one and smaller):" |
877 | 0 | " %d > %d.", chunk_nbytes, schunk->chunksize); |
878 | 0 | return BLOSC2_ERROR_CHUNK_UPDATE; |
879 | 0 | } |
880 | | |
881 | 0 | bool needs_free; |
882 | 0 | uint8_t *chunk_old; |
883 | 0 | int err = blosc2_schunk_get_chunk(schunk, nchunk, &chunk_old, &needs_free); |
884 | 0 | if (err < 0) { |
885 | 0 | BLOSC_TRACE_ERROR("%" PRId64 " chunk can not be obtained from schunk.", nchunk); |
886 | 0 | return -1; |
887 | 0 | } |
888 | 0 | int32_t chunk_nbytes_old = 0; |
889 | 0 | int32_t chunk_cbytes_old = 0; |
890 | 0 | schunk->current_nchunk = nchunk; |
891 | |
|
892 | 0 | if (chunk_old != 0) { |
893 | 0 | rc = blosc2_cbuffer_sizes(chunk_old, &chunk_nbytes_old, &chunk_cbytes_old, NULL); |
894 | 0 | if (rc < 0) { |
895 | 0 | return rc; |
896 | 0 | } |
897 | 0 | if (chunk_cbytes_old == BLOSC2_MAX_OVERHEAD) { |
898 | 0 | chunk_cbytes_old = 0; |
899 | 0 | } |
900 | 0 | } |
901 | 0 | if (needs_free) { |
902 | 0 | free(chunk_old); |
903 | 0 | } |
904 | |
|
905 | 0 | if (copy) { |
906 | | // Make a copy of the chunk |
907 | 0 | uint8_t *chunk_copy = malloc(chunk_cbytes); |
908 | 0 | memcpy(chunk_copy, chunk, chunk_cbytes); |
909 | 0 | chunk = chunk_copy; |
910 | 0 | } |
911 | |
|
912 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)(schunk->frame); |
913 | 0 | if (schunk->frame == NULL) { |
914 | | /* Update counters */ |
915 | 0 | schunk->nbytes += chunk_nbytes; |
916 | 0 | schunk->nbytes -= chunk_nbytes_old; |
917 | 0 | schunk->cbytes += chunk_cbytes; |
918 | 0 | schunk->cbytes -= chunk_cbytes_old; |
919 | 0 | } else { |
920 | | // A frame |
921 | 0 | int special_value = (chunk[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK; |
922 | 0 | switch (special_value) { |
923 | 0 | case BLOSC2_SPECIAL_ZERO: |
924 | 0 | case BLOSC2_SPECIAL_NAN: |
925 | 0 | case BLOSC2_SPECIAL_UNINIT: |
926 | 0 | schunk->nbytes += chunk_nbytes; |
927 | 0 | schunk->nbytes -= chunk_nbytes_old; |
928 | 0 | if (frame->sframe) { |
929 | 0 | schunk->cbytes -= chunk_cbytes_old; |
930 | 0 | } |
931 | 0 | break; |
932 | 0 | default: |
933 | | /* Update counters */ |
934 | 0 | schunk->nbytes += chunk_nbytes; |
935 | 0 | schunk->nbytes -= chunk_nbytes_old; |
936 | 0 | schunk->cbytes += chunk_cbytes; |
937 | 0 | if (frame->sframe) { |
938 | 0 | schunk->cbytes -= chunk_cbytes_old; |
939 | 0 | } |
940 | 0 | else { |
941 | 0 | if (chunk_cbytes_old >= chunk_cbytes) { |
942 | 0 | schunk->cbytes -= chunk_cbytes; |
943 | 0 | } |
944 | 0 | } |
945 | 0 | } |
946 | 0 | } |
947 | | |
948 | | // Update super-chunk or frame |
949 | 0 | if (schunk->frame == NULL) { |
950 | 0 | if (!copy && (chunk_cbytes < chunk_nbytes)) { |
951 | | // We still want to do a shrink of the chunk |
952 | 0 | chunk = realloc(chunk, chunk_cbytes); |
953 | 0 | } |
954 | | |
955 | | // Free old chunk and add reference to new chunk |
956 | 0 | if (schunk->data[nchunk] != 0) { |
957 | 0 | free(schunk->data[nchunk]); |
958 | 0 | } |
959 | 0 | schunk->data[nchunk] = chunk; |
960 | 0 | } |
961 | 0 | else { |
962 | 0 | if (frame_update_chunk(frame, nchunk, chunk, schunk) == NULL) { |
963 | 0 | BLOSC_TRACE_ERROR("Problems updating a chunk in a frame."); |
964 | 0 | return BLOSC2_ERROR_CHUNK_UPDATE; |
965 | 0 | } |
966 | 0 | } |
967 | | |
968 | 0 | return schunk->nchunks; |
969 | 0 | } |
970 | | |
971 | 0 | int64_t blosc2_schunk_delete_chunk(blosc2_schunk *schunk, int64_t nchunk) { |
972 | 0 | int rc; |
973 | 0 | if (schunk->nchunks < nchunk) { |
974 | 0 | BLOSC_TRACE_ERROR("The schunk has not enough chunks (%" PRId64 ")!", schunk->nchunks); |
975 | 0 | } |
976 | | |
977 | 0 | bool needs_free; |
978 | 0 | uint8_t *chunk_old; |
979 | 0 | int err = blosc2_schunk_get_chunk(schunk, nchunk, &chunk_old, &needs_free); |
980 | 0 | if (err < 0) { |
981 | 0 | BLOSC_TRACE_ERROR("%" PRId64 "chunk can not be obtained from schunk.", nchunk); |
982 | 0 | return -1; |
983 | 0 | } |
984 | 0 | int32_t chunk_nbytes_old = 0; |
985 | 0 | int32_t chunk_cbytes_old = 0; |
986 | 0 | schunk->current_nchunk = nchunk; |
987 | |
|
988 | 0 | if (chunk_old != 0) { |
989 | 0 | rc = blosc2_cbuffer_sizes(chunk_old, &chunk_nbytes_old, &chunk_cbytes_old, NULL); |
990 | 0 | if (rc < 0) { |
991 | 0 | return rc; |
992 | 0 | } |
993 | 0 | if (chunk_cbytes_old == BLOSC2_MAX_OVERHEAD) { |
994 | 0 | chunk_cbytes_old = 0; |
995 | 0 | } |
996 | 0 | } |
997 | 0 | if (needs_free) { |
998 | 0 | free(chunk_old); |
999 | 0 | } |
1000 | |
|
1001 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)(schunk->frame); |
1002 | 0 | schunk->nchunks -= 1; |
1003 | 0 | if (schunk->frame == NULL) { |
1004 | | /* Update counters */ |
1005 | 0 | schunk->nbytes -= chunk_nbytes_old; |
1006 | 0 | schunk->cbytes -= chunk_cbytes_old; |
1007 | 0 | } else { |
1008 | | // A frame |
1009 | 0 | schunk->nbytes -= chunk_nbytes_old; |
1010 | 0 | if (frame->sframe) { |
1011 | 0 | schunk->cbytes -= chunk_cbytes_old; |
1012 | 0 | } |
1013 | 0 | } |
1014 | | |
1015 | | // Update super-chunk or frame |
1016 | 0 | if (schunk->frame == NULL) { |
1017 | | // Free old chunk |
1018 | 0 | if (schunk->data[nchunk] != 0) { |
1019 | 0 | free(schunk->data[nchunk]); |
1020 | 0 | } |
1021 | | // Reorder the offsets and insert the new chunk |
1022 | 0 | for (int64_t i = nchunk; i < schunk->nchunks; i++) { |
1023 | 0 | schunk->data[i] = schunk->data[i + 1]; |
1024 | 0 | } |
1025 | 0 | schunk->data[schunk->nchunks] = NULL; |
1026 | |
|
1027 | 0 | } |
1028 | 0 | else { |
1029 | 0 | if (frame_delete_chunk(frame, nchunk, schunk) == NULL) { |
1030 | 0 | BLOSC_TRACE_ERROR("Problems deleting a chunk in a frame."); |
1031 | 0 | return BLOSC2_ERROR_CHUNK_UPDATE; |
1032 | 0 | } |
1033 | 0 | } |
1034 | 0 | return schunk->nchunks; |
1035 | 0 | } |
1036 | | |
1037 | | |
1038 | | /* Append a data buffer to a super-chunk. */ |
1039 | 42.3k | int64_t blosc2_schunk_append_buffer(blosc2_schunk *schunk, const void *src, int32_t nbytes) { |
1040 | 42.3k | uint8_t* chunk = malloc(nbytes + BLOSC2_MAX_OVERHEAD); |
1041 | 42.3k | schunk->current_nchunk = schunk->nchunks; |
1042 | | /* Compress the src buffer using super-chunk context */ |
1043 | 42.3k | int cbytes = blosc2_compress_ctx(schunk->cctx, src, nbytes, chunk, |
1044 | 42.3k | nbytes + BLOSC2_MAX_OVERHEAD); |
1045 | 42.3k | if (cbytes < 0) { |
1046 | 0 | free(chunk); |
1047 | 0 | return cbytes; |
1048 | 0 | } |
1049 | | // We don't need a copy of the chunk, as it will be shrunk if necessary |
1050 | 42.3k | int64_t nchunks = blosc2_schunk_append_chunk(schunk, chunk, false); |
1051 | 42.3k | if (nchunks < 0) { |
1052 | 0 | BLOSC_TRACE_ERROR("Error appending a buffer in super-chunk"); |
1053 | 0 | return nchunks; |
1054 | 0 | } |
1055 | | |
1056 | 42.3k | return nchunks; |
1057 | 42.3k | } |
1058 | | |
1059 | | |
1060 | | /* Decompress and return a chunk that is part of a super-chunk. */ |
1061 | | int blosc2_schunk_decompress_chunk(blosc2_schunk *schunk, int64_t nchunk, |
1062 | 9.60k | void *dest, int32_t nbytes) { |
1063 | 9.60k | int32_t chunk_nbytes; |
1064 | 9.60k | int32_t chunk_cbytes; |
1065 | 9.60k | int chunksize; |
1066 | 9.60k | int rc; |
1067 | 9.60k | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1068 | | |
1069 | 9.60k | schunk->current_nchunk = nchunk; |
1070 | 9.60k | if (frame == NULL) { |
1071 | 9.60k | if (nchunk >= schunk->nchunks) { |
1072 | 0 | BLOSC_TRACE_ERROR("nchunk ('%" PRId64 "') exceeds the number of chunks " |
1073 | 0 | "('%" PRId64 "') in super-chunk.", nchunk, schunk->nchunks); |
1074 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1075 | 0 | } |
1076 | 9.60k | uint8_t* src = schunk->data[nchunk]; |
1077 | 9.60k | if (src == 0) { |
1078 | 0 | return 0; |
1079 | 0 | } |
1080 | | |
1081 | 9.60k | rc = blosc2_cbuffer_sizes(src, &chunk_nbytes, &chunk_cbytes, NULL); |
1082 | 9.60k | if (rc < 0) { |
1083 | 0 | return rc; |
1084 | 0 | } |
1085 | | |
1086 | 9.60k | if (nbytes < chunk_nbytes) { |
1087 | 402 | BLOSC_TRACE_ERROR("Buffer size is too small for the decompressed buffer " |
1088 | 0 | "('%d' bytes, but '%d' are needed).", nbytes, chunk_nbytes); |
1089 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1090 | 402 | } |
1091 | | |
1092 | 9.19k | chunksize = blosc2_decompress_ctx(schunk->dctx, src, chunk_cbytes, dest, nbytes); |
1093 | 9.19k | if (chunksize < 0 || chunksize != chunk_nbytes) { |
1094 | 0 | BLOSC_TRACE_ERROR("Error in decompressing chunk."); |
1095 | 0 | if (chunksize < 0) |
1096 | 0 | return chunksize; |
1097 | 0 | return BLOSC2_ERROR_FAILURE; |
1098 | 0 | } |
1099 | 9.19k | } else { |
1100 | 2 | chunksize = frame_decompress_chunk(schunk->dctx, frame, nchunk, dest, nbytes); |
1101 | 2 | if (chunksize < 0) { |
1102 | 1 | return chunksize; |
1103 | 1 | } |
1104 | 2 | } |
1105 | 9.19k | return chunksize; |
1106 | 9.60k | } |
1107 | | |
1108 | | |
1109 | | /* Return a compressed chunk that is part of a super-chunk in the `chunk` parameter. |
1110 | | * If the super-chunk is backed by a frame that is disk-based, a buffer is allocated for the |
1111 | | * (compressed) chunk, and hence a free is needed. You can check if the chunk requires a free |
1112 | | * with the `needs_free` parameter. |
1113 | | * If the chunk does not need a free, it means that a pointer to the location in the super-chunk |
1114 | | * (or the backing in-memory frame) is returned in the `chunk` parameter. |
1115 | | * |
1116 | | * The size of the (compressed) chunk is returned. If some problem is detected, a negative code |
1117 | | * is returned instead. |
1118 | | */ |
1119 | 0 | int blosc2_schunk_get_chunk(blosc2_schunk *schunk, int64_t nchunk, uint8_t **chunk, bool *needs_free) { |
1120 | 0 | if (schunk->dctx->threads_started > 1) { |
1121 | 0 | pthread_mutex_lock(&schunk->dctx->nchunk_mutex); |
1122 | 0 | schunk->current_nchunk = nchunk; |
1123 | 0 | pthread_mutex_unlock(&schunk->dctx->nchunk_mutex); |
1124 | 0 | } |
1125 | 0 | else { |
1126 | 0 | schunk->current_nchunk = nchunk; |
1127 | 0 | } |
1128 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1129 | 0 | if (frame != NULL) { |
1130 | 0 | return frame_get_chunk(frame, nchunk, chunk, needs_free); |
1131 | 0 | } |
1132 | | |
1133 | 0 | if (nchunk >= schunk->nchunks) { |
1134 | 0 | BLOSC_TRACE_ERROR("nchunk ('%" PRId64 "') exceeds the number of chunks " |
1135 | 0 | "('%" PRId64 "') in schunk.", nchunk, schunk->nchunks); |
1136 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1137 | 0 | } |
1138 | | |
1139 | 0 | *chunk = schunk->data[nchunk]; |
1140 | 0 | if (*chunk == 0) { |
1141 | 0 | *needs_free = 0; |
1142 | 0 | return 0; |
1143 | 0 | } |
1144 | | |
1145 | 0 | *needs_free = false; |
1146 | 0 | int32_t chunk_cbytes; |
1147 | 0 | int rc = blosc2_cbuffer_sizes(*chunk, NULL, &chunk_cbytes, NULL); |
1148 | 0 | if (rc < 0) { |
1149 | 0 | return rc; |
1150 | 0 | } |
1151 | 0 | return (int)chunk_cbytes; |
1152 | 0 | } |
1153 | | |
1154 | | |
1155 | | /* Return a compressed chunk that is part of a super-chunk in the `chunk` parameter. |
1156 | | * If the super-chunk is backed by a frame that is disk-based, a buffer is allocated for the |
1157 | | * (compressed) chunk, and hence a free is needed. You can check if the chunk requires a free |
1158 | | * with the `needs_free` parameter. |
1159 | | * If the chunk does not need a free, it means that a pointer to the location in the super-chunk |
1160 | | * (or the backing in-memory frame) is returned in the `chunk` parameter. |
1161 | | * |
1162 | | * The size of the (compressed) chunk is returned. If some problem is detected, a negative code |
1163 | | * is returned instead. |
1164 | | */ |
1165 | 0 | int blosc2_schunk_get_lazychunk(blosc2_schunk *schunk, int64_t nchunk, uint8_t **chunk, bool *needs_free) { |
1166 | 0 | if (schunk->dctx->threads_started > 1) { |
1167 | 0 | pthread_mutex_lock(&schunk->dctx->nchunk_mutex); |
1168 | 0 | schunk->current_nchunk = nchunk; |
1169 | 0 | pthread_mutex_unlock(&schunk->dctx->nchunk_mutex); |
1170 | 0 | } |
1171 | 0 | else { |
1172 | 0 | schunk->current_nchunk = nchunk; |
1173 | 0 | } |
1174 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1175 | 0 | if (schunk->frame != NULL) { |
1176 | 0 | return frame_get_lazychunk(frame, nchunk, chunk, needs_free); |
1177 | 0 | } |
1178 | | |
1179 | 0 | if (nchunk >= schunk->nchunks) { |
1180 | 0 | BLOSC_TRACE_ERROR("nchunk ('%" PRId64 "') exceeds the number of chunks " |
1181 | 0 | "('%" PRId64 "') in schunk.", nchunk, schunk->nchunks); |
1182 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1183 | 0 | } |
1184 | | |
1185 | 0 | *chunk = schunk->data[nchunk]; |
1186 | 0 | if (*chunk == 0) { |
1187 | 0 | *needs_free = 0; |
1188 | 0 | return 0; |
1189 | 0 | } |
1190 | | |
1191 | 0 | *needs_free = false; |
1192 | 0 | int32_t chunk_cbytes; |
1193 | 0 | int rc = blosc2_cbuffer_sizes(*chunk, NULL, &chunk_cbytes, NULL); |
1194 | 0 | if (rc < 0) { |
1195 | 0 | return rc; |
1196 | 0 | } |
1197 | 0 | return (int)chunk_cbytes; |
1198 | 0 | } |
1199 | | |
1200 | | |
1201 | 0 | int blosc2_schunk_get_slice_buffer(blosc2_schunk *schunk, int64_t start, int64_t stop, void *buffer) { |
1202 | 0 | int64_t byte_start = start * schunk->typesize; |
1203 | 0 | int64_t byte_stop = stop * schunk->typesize; |
1204 | 0 | int64_t nchunk_start = byte_start / schunk->chunksize; |
1205 | 0 | int32_t chunk_start = (int32_t) (byte_start % schunk->chunksize); // 0 indexed |
1206 | 0 | int32_t chunk_stop; // 0 indexed |
1207 | 0 | if (byte_stop >= (nchunk_start + 1) * schunk->chunksize) { |
1208 | 0 | chunk_stop = schunk->chunksize; |
1209 | 0 | } |
1210 | 0 | else { |
1211 | 0 | chunk_stop = (int32_t) (byte_stop % schunk->chunksize); |
1212 | 0 | } |
1213 | |
|
1214 | 0 | uint8_t *dst_ptr = (uint8_t *) buffer; |
1215 | 0 | bool needs_free; |
1216 | 0 | uint8_t *chunk; |
1217 | 0 | int32_t cbytes; |
1218 | 0 | int64_t nchunk = nchunk_start; |
1219 | 0 | int64_t nbytes_read = 0; |
1220 | 0 | int32_t nbytes; |
1221 | 0 | int32_t chunksize = schunk->chunksize; |
1222 | |
|
1223 | 0 | while (nbytes_read < ((stop - start) * schunk->typesize)) { |
1224 | 0 | cbytes = blosc2_schunk_get_lazychunk(schunk, nchunk, &chunk, &needs_free); |
1225 | 0 | if (cbytes < 0) { |
1226 | 0 | BLOSC_TRACE_ERROR("Cannot get lazychunk ('%" PRId64 "').", nchunk); |
1227 | 0 | return BLOSC2_ERROR_FAILURE; |
1228 | 0 | } |
1229 | 0 | int32_t blocksize = sw32_(chunk + BLOSC2_CHUNK_BLOCKSIZE); |
1230 | |
|
1231 | 0 | int32_t nblock_start = (int32_t) (chunk_start / blocksize); |
1232 | 0 | int32_t nblock_stop = (int32_t) ((chunk_stop - 1) / blocksize); |
1233 | 0 | if (nchunk == (schunk->nchunks - 1) && schunk->nbytes % schunk->chunksize != 0) { |
1234 | 0 | chunksize = schunk->nbytes % schunk->chunksize; |
1235 | 0 | } |
1236 | 0 | int32_t nblocks = chunksize / blocksize; |
1237 | 0 | if (chunksize % blocksize != 0) { |
1238 | 0 | nblocks++; |
1239 | 0 | } |
1240 | |
|
1241 | 0 | if (chunk_start == 0 && chunk_stop == chunksize) { |
1242 | | // Avoid memcpy |
1243 | 0 | nbytes = blosc2_decompress_ctx(schunk->dctx, chunk, cbytes, dst_ptr, chunksize); |
1244 | 0 | if (nbytes < 0) { |
1245 | 0 | BLOSC_TRACE_ERROR("Cannot decompress chunk ('%" PRId64 "').", nchunk); |
1246 | 0 | return BLOSC2_ERROR_FAILURE; |
1247 | 0 | } |
1248 | 0 | } |
1249 | 0 | else { |
1250 | | // After extensive timing I have not been able to see lots of situations where |
1251 | | // a maskout read is better than a getitem one. Disabling for now. |
1252 | | // if (nblock_start != nblock_stop) { |
1253 | 0 | if (false) { |
1254 | 0 | uint8_t *data = malloc(chunksize); |
1255 | | /* We have more than 1 block to read, so use a masked read */ |
1256 | 0 | bool *block_maskout = calloc(nblocks, 1); |
1257 | 0 | for (int32_t nblock = 0; nblock < nblocks; nblock++) { |
1258 | 0 | if ((nblock < nblock_start) || (nblock > nblock_stop)) { |
1259 | 0 | block_maskout[nblock] = true; |
1260 | 0 | } |
1261 | 0 | } |
1262 | 0 | if (blosc2_set_maskout(schunk->dctx, block_maskout, nblocks) < 0) { |
1263 | 0 | BLOSC_TRACE_ERROR("Cannot set maskout"); |
1264 | 0 | return BLOSC2_ERROR_FAILURE; |
1265 | 0 | } |
1266 | | |
1267 | 0 | nbytes = blosc2_decompress_ctx(schunk->dctx, chunk, cbytes, data, chunksize); |
1268 | 0 | if (nbytes < 0) { |
1269 | 0 | BLOSC_TRACE_ERROR("Cannot decompress chunk ('%" PRId64 "').", nchunk); |
1270 | 0 | return BLOSC2_ERROR_FAILURE; |
1271 | 0 | } |
1272 | 0 | nbytes = chunk_stop - chunk_start; |
1273 | 0 | memcpy(dst_ptr, &data[chunk_start], nbytes); |
1274 | 0 | free(block_maskout); |
1275 | 0 | free(data); |
1276 | 0 | } |
1277 | 0 | else { |
1278 | | /* Less than 1 block to read; use a getitem call */ |
1279 | 0 | nbytes = blosc2_getitem_ctx(schunk->dctx, chunk, cbytes, (int32_t) (chunk_start / schunk->typesize), |
1280 | 0 | (chunk_stop - chunk_start) / schunk->typesize, dst_ptr, chunksize); |
1281 | 0 | if (nbytes < 0) { |
1282 | 0 | BLOSC_TRACE_ERROR("Cannot get item from ('%" PRId64 "') chunk.", nchunk); |
1283 | 0 | return BLOSC2_ERROR_FAILURE; |
1284 | 0 | } |
1285 | 0 | } |
1286 | 0 | } |
1287 | | |
1288 | 0 | dst_ptr += nbytes; |
1289 | 0 | nbytes_read += nbytes; |
1290 | 0 | nchunk++; |
1291 | |
|
1292 | 0 | if (needs_free) { |
1293 | 0 | free(chunk); |
1294 | 0 | } |
1295 | 0 | chunk_start = 0; |
1296 | 0 | if (byte_stop >= (nchunk + 1) * chunksize) { |
1297 | 0 | chunk_stop = chunksize; |
1298 | 0 | } |
1299 | 0 | else { |
1300 | 0 | chunk_stop = (int32_t)(byte_stop % chunksize); |
1301 | 0 | } |
1302 | 0 | } |
1303 | | |
1304 | 0 | return BLOSC2_ERROR_SUCCESS; |
1305 | 0 | } |
1306 | | |
1307 | | |
1308 | 0 | int blosc2_schunk_set_slice_buffer(blosc2_schunk *schunk, int64_t start, int64_t stop, void *buffer) { |
1309 | 0 | int64_t byte_start = start * schunk->typesize; |
1310 | 0 | int64_t byte_stop = stop * schunk->typesize; |
1311 | 0 | int64_t nchunk_start = byte_start / schunk->chunksize; |
1312 | 0 | int32_t chunk_start = (int32_t) (byte_start % schunk->chunksize); |
1313 | 0 | int32_t chunk_stop; |
1314 | 0 | if (byte_stop >= (nchunk_start + 1) * schunk->chunksize) { |
1315 | 0 | chunk_stop = schunk->chunksize; |
1316 | 0 | } |
1317 | 0 | else { |
1318 | 0 | chunk_stop = (int32_t) (byte_stop % schunk->chunksize); |
1319 | 0 | } |
1320 | |
|
1321 | 0 | uint8_t *src_ptr = (uint8_t *) buffer; |
1322 | 0 | int64_t nchunk = nchunk_start; |
1323 | 0 | int64_t nbytes_written = 0; |
1324 | 0 | int32_t nbytes; |
1325 | 0 | uint8_t *data = malloc(schunk->chunksize); |
1326 | 0 | int64_t nchunks; |
1327 | 0 | int32_t chunksize = schunk->chunksize; |
1328 | |
|
1329 | 0 | while (nbytes_written < ((stop - start) * schunk->typesize)) { |
1330 | 0 | if (chunk_start == 0 && |
1331 | 0 | (chunk_stop == schunk->chunksize || chunk_stop == schunk->nbytes % schunk->chunksize)) { |
1332 | 0 | if (chunk_stop == schunk->nbytes % schunk->chunksize) { |
1333 | 0 | chunksize = chunk_stop; |
1334 | 0 | } |
1335 | 0 | uint8_t *chunk = malloc(chunksize + BLOSC2_MAX_OVERHEAD); |
1336 | 0 | if (blosc2_compress_ctx(schunk->cctx, src_ptr, chunksize, chunk, chunksize + BLOSC2_MAX_OVERHEAD) < 0) { |
1337 | 0 | BLOSC_TRACE_ERROR("Cannot compress data of chunk ('%" PRId64 "').", nchunk); |
1338 | 0 | return BLOSC2_ERROR_FAILURE; |
1339 | 0 | } |
1340 | 0 | nchunks = blosc2_schunk_update_chunk(schunk, nchunk, chunk, false); |
1341 | 0 | if (nchunks != schunk->nchunks) { |
1342 | 0 | BLOSC_TRACE_ERROR("Cannot update chunk ('%" PRId64 "').", nchunk); |
1343 | 0 | return BLOSC2_ERROR_CHUNK_UPDATE; |
1344 | 0 | } |
1345 | 0 | } |
1346 | 0 | else { |
1347 | 0 | nbytes = blosc2_schunk_decompress_chunk(schunk, nchunk, data, schunk->chunksize); |
1348 | 0 | if (nbytes < 0) { |
1349 | 0 | BLOSC_TRACE_ERROR("Cannot decompress chunk ('%" PRId64 "').", nchunk); |
1350 | 0 | return BLOSC2_ERROR_FAILURE; |
1351 | 0 | } |
1352 | 0 | memcpy(&data[chunk_start], src_ptr, chunk_stop - chunk_start); |
1353 | 0 | uint8_t *chunk = malloc(nbytes + BLOSC2_MAX_OVERHEAD); |
1354 | 0 | if (blosc2_compress_ctx(schunk->cctx, data, nbytes, chunk, nbytes + BLOSC2_MAX_OVERHEAD) < 0) { |
1355 | 0 | BLOSC_TRACE_ERROR("Cannot compress data of chunk ('%" PRId64 "').", nchunk); |
1356 | 0 | return BLOSC2_ERROR_FAILURE; |
1357 | 0 | } |
1358 | 0 | nchunks = blosc2_schunk_update_chunk(schunk, nchunk, chunk, false); |
1359 | 0 | if (nchunks != schunk->nchunks) { |
1360 | 0 | BLOSC_TRACE_ERROR("Cannot update chunk ('%" PRId64 "').", nchunk); |
1361 | 0 | return BLOSC2_ERROR_CHUNK_UPDATE; |
1362 | 0 | } |
1363 | 0 | } |
1364 | 0 | nchunk++; |
1365 | 0 | nbytes_written += chunk_stop - chunk_start; |
1366 | 0 | src_ptr += chunk_stop - chunk_start; |
1367 | 0 | chunk_start = 0; |
1368 | 0 | if (byte_stop >= (nchunk + 1) * schunk->chunksize) { |
1369 | 0 | chunk_stop = schunk->chunksize; |
1370 | 0 | } |
1371 | 0 | else { |
1372 | 0 | chunk_stop = (int32_t) (byte_stop % schunk->chunksize); |
1373 | 0 | } |
1374 | 0 | } |
1375 | 0 | free(data); |
1376 | |
|
1377 | 0 | return BLOSC2_ERROR_SUCCESS; |
1378 | 0 | } |
1379 | | |
1380 | | |
1381 | 0 | int schunk_get_slice_nchunks(blosc2_schunk *schunk, int64_t start, int64_t stop, int64_t **chunks_idx) { |
1382 | 0 | BLOSC_ERROR_NULL(schunk, BLOSC2_ERROR_NULL_POINTER); |
1383 | | |
1384 | 0 | int64_t byte_start = start * schunk->typesize; |
1385 | 0 | int64_t byte_stop = stop * schunk->typesize; |
1386 | 0 | int64_t nchunk_start = byte_start / schunk->chunksize; |
1387 | 0 | int64_t nchunk_stop = byte_stop / schunk->chunksize; |
1388 | 0 | if (byte_stop % schunk->chunksize != 0) { |
1389 | 0 | nchunk_stop++; |
1390 | 0 | } |
1391 | 0 | int64_t nchunk = nchunk_start; |
1392 | 0 | int nchunks = (int)(nchunk_stop - nchunk_start); |
1393 | 0 | *chunks_idx = malloc(nchunks * sizeof(int64_t)); |
1394 | 0 | int64_t *ptr = *chunks_idx; |
1395 | 0 | for (int64_t i = 0; i < nchunks; ++i) { |
1396 | 0 | ptr[i] = nchunk; |
1397 | 0 | nchunk++; |
1398 | 0 | } |
1399 | 0 | return nchunks; |
1400 | 0 | } |
1401 | | |
1402 | | /* Reorder the chunk offsets of an existing super-chunk. */ |
1403 | 0 | int blosc2_schunk_reorder_offsets(blosc2_schunk *schunk, int64_t *offsets_order) { |
1404 | | // Check that the offsets order are correct |
1405 | 0 | bool *index_check = (bool *) calloc(schunk->nchunks, sizeof(bool)); |
1406 | 0 | for (int i = 0; i < schunk->nchunks; ++i) { |
1407 | 0 | int64_t index = offsets_order[i]; |
1408 | 0 | if (index >= schunk->nchunks) { |
1409 | 0 | BLOSC_TRACE_ERROR("Index is bigger than the number of chunks."); |
1410 | 0 | free(index_check); |
1411 | 0 | return BLOSC2_ERROR_DATA; |
1412 | 0 | } |
1413 | 0 | if (index_check[index] == false) { |
1414 | 0 | index_check[index] = true; |
1415 | 0 | } else { |
1416 | 0 | BLOSC_TRACE_ERROR("Index is yet used."); |
1417 | 0 | free(index_check); |
1418 | 0 | return BLOSC2_ERROR_DATA; |
1419 | 0 | } |
1420 | 0 | } |
1421 | 0 | free(index_check); |
1422 | |
|
1423 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1424 | 0 | if (frame != NULL) { |
1425 | 0 | return frame_reorder_offsets(frame, offsets_order, schunk); |
1426 | 0 | } |
1427 | 0 | uint8_t **offsets = schunk->data; |
1428 | | |
1429 | | // Make a copy of the chunk offsets and reorder it |
1430 | 0 | uint8_t **offsets_copy = malloc(schunk->data_len); |
1431 | 0 | memcpy(offsets_copy, offsets, schunk->data_len); |
1432 | |
|
1433 | 0 | for (int i = 0; i < schunk->nchunks; ++i) { |
1434 | 0 | offsets[i] = offsets_copy[offsets_order[i]]; |
1435 | 0 | } |
1436 | 0 | free(offsets_copy); |
1437 | |
|
1438 | 0 | return 0; |
1439 | 0 | } |
1440 | | |
1441 | | |
1442 | | // Get the length (in bytes) of the internal frame of the super-chunk |
1443 | 0 | int64_t blosc2_schunk_frame_len(blosc2_schunk* schunk) { |
1444 | 0 | int64_t len; |
1445 | 0 | blosc2_frame_s* frame_s = (blosc2_frame_s*)(schunk->frame); |
1446 | 0 | if (frame_s != NULL) { |
1447 | 0 | len = frame_s->len; |
1448 | 0 | } |
1449 | 0 | else { |
1450 | | // No attached frame, but we can still come with an estimate |
1451 | 0 | len = (int64_t) (schunk->cbytes + schunk->nchunks * sizeof(int64_t)); |
1452 | 0 | } |
1453 | |
|
1454 | 0 | return len; |
1455 | 0 | } |
1456 | | |
1457 | | |
1458 | | /** |
1459 | | * @brief Flush metalayers content into a possible attached frame. |
1460 | | * |
1461 | | * @param schunk The super-chunk to which the flush should be applied. |
1462 | | * |
1463 | | * @return If successful, a 0 is returned. Else, return a negative value. |
1464 | | */ |
1465 | | // Initially, this was a public function, but as it is really meant to be used only |
1466 | | // in the schunk_add_metalayer(), I decided to convert it into private and call it |
1467 | | // implicitly instead of requiring the user to do so. The only drawback is that |
1468 | | // each add operation requires a complete frame re-build, but as users should need |
1469 | | // very few metalayers, this overhead should be negligible in practice. |
1470 | 0 | int metalayer_flush(blosc2_schunk* schunk) { |
1471 | 0 | int rc = BLOSC2_ERROR_SUCCESS; |
1472 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1473 | 0 | if (frame == NULL) { |
1474 | 0 | return rc; |
1475 | 0 | } |
1476 | 0 | rc = frame_update_header(frame, schunk, true); |
1477 | 0 | if (rc < 0) { |
1478 | 0 | BLOSC_TRACE_ERROR("Unable to update metalayers into frame."); |
1479 | 0 | return rc; |
1480 | 0 | } |
1481 | 0 | rc = frame_update_trailer(frame, schunk); |
1482 | 0 | if (rc < 0) { |
1483 | 0 | BLOSC_TRACE_ERROR("Unable to update trailer into frame."); |
1484 | 0 | return rc; |
1485 | 0 | } |
1486 | 0 | return rc; |
1487 | 0 | } |
1488 | | |
1489 | | |
1490 | | /* Add content into a new metalayer. |
1491 | | * |
1492 | | * If successful, return the index of the new metalayer. Else, return a negative value. |
1493 | | */ |
1494 | 0 | int blosc2_meta_add(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len) { |
1495 | 0 | int nmetalayer = blosc2_meta_exists(schunk, name); |
1496 | 0 | if (nmetalayer >= 0) { |
1497 | 0 | BLOSC_TRACE_ERROR("Metalayer \"%s\" already exists.", name); |
1498 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1499 | 0 | } |
1500 | | |
1501 | | // Add the metalayer |
1502 | 0 | blosc2_metalayer *metalayer = malloc(sizeof(blosc2_metalayer)); |
1503 | 0 | char* name_ = malloc(strlen(name) + 1); |
1504 | 0 | strcpy(name_, name); |
1505 | 0 | metalayer->name = name_; |
1506 | 0 | uint8_t* content_buf = malloc((size_t)content_len); |
1507 | 0 | memcpy(content_buf, content, content_len); |
1508 | 0 | metalayer->content = content_buf; |
1509 | 0 | metalayer->content_len = content_len; |
1510 | 0 | schunk->metalayers[schunk->nmetalayers] = metalayer; |
1511 | 0 | schunk->nmetalayers += 1; |
1512 | |
|
1513 | 0 | int rc = metalayer_flush(schunk); |
1514 | 0 | if (rc < 0) { |
1515 | 0 | return rc; |
1516 | 0 | } |
1517 | | |
1518 | 0 | return schunk->nmetalayers - 1; |
1519 | 0 | } |
1520 | | |
1521 | | |
1522 | | /* Update the content of an existing metalayer. |
1523 | | * |
1524 | | * If successful, return the index of the new metalayer. Else, return a negative value. |
1525 | | */ |
1526 | 0 | int blosc2_meta_update(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len) { |
1527 | 0 | int nmetalayer = blosc2_meta_exists(schunk, name); |
1528 | 0 | if (nmetalayer < 0) { |
1529 | 0 | BLOSC_TRACE_ERROR("Metalayer \"%s\" not found.", name); |
1530 | 0 | return nmetalayer; |
1531 | 0 | } |
1532 | | |
1533 | 0 | blosc2_metalayer *metalayer = schunk->metalayers[nmetalayer]; |
1534 | 0 | if (content_len > metalayer->content_len) { |
1535 | 0 | BLOSC_TRACE_ERROR("`content_len` cannot exceed the existing size of %d bytes.", metalayer->content_len); |
1536 | 0 | return nmetalayer; |
1537 | 0 | } |
1538 | | |
1539 | | // Update the contents of the metalayer |
1540 | 0 | memcpy(metalayer->content, content, content_len); |
1541 | | |
1542 | | // Update the metalayers in frame (as size has not changed, we don't need to update the trailer) |
1543 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1544 | 0 | if (frame != NULL) { |
1545 | 0 | int rc = frame_update_header(frame, schunk, false); |
1546 | 0 | if (rc < 0) { |
1547 | 0 | BLOSC_TRACE_ERROR("Unable to update meta info from frame."); |
1548 | 0 | return rc; |
1549 | 0 | } |
1550 | 0 | } |
1551 | | |
1552 | 0 | return nmetalayer; |
1553 | 0 | } |
1554 | | |
1555 | | |
1556 | | /* Find whether the schunk has a variable-length metalayer or not. |
1557 | | * |
1558 | | * If successful, return the index of the variable-length metalayer. Else, return a negative value. |
1559 | | */ |
1560 | 0 | int blosc2_vlmeta_exists(blosc2_schunk *schunk, const char *name) { |
1561 | 0 | if (strlen(name) > BLOSC2_METALAYER_NAME_MAXLEN) { |
1562 | 0 | BLOSC_TRACE_ERROR("Variable-length metalayer names cannot be larger than %d chars.", BLOSC2_METALAYER_NAME_MAXLEN); |
1563 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1564 | 0 | } |
1565 | | |
1566 | 0 | for (int nvlmetalayer = 0; nvlmetalayer < schunk->nvlmetalayers; nvlmetalayer++) { |
1567 | 0 | if (strcmp(name, schunk->vlmetalayers[nvlmetalayer]->name) == 0) { |
1568 | 0 | return nvlmetalayer; |
1569 | 0 | } |
1570 | 0 | } |
1571 | 0 | return BLOSC2_ERROR_NOT_FOUND; |
1572 | 0 | } |
1573 | | |
1574 | 0 | int vlmetalayer_flush(blosc2_schunk* schunk) { |
1575 | 0 | int rc = BLOSC2_ERROR_SUCCESS; |
1576 | 0 | blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; |
1577 | 0 | if (frame == NULL) { |
1578 | 0 | return rc; |
1579 | 0 | } |
1580 | 0 | rc = frame_update_header(frame, schunk, false); |
1581 | 0 | if (rc < 0) { |
1582 | 0 | BLOSC_TRACE_ERROR("Unable to update metalayers into frame."); |
1583 | 0 | return rc; |
1584 | 0 | } |
1585 | 0 | rc = frame_update_trailer(frame, schunk); |
1586 | 0 | if (rc < 0) { |
1587 | 0 | BLOSC_TRACE_ERROR("Unable to update trailer into frame."); |
1588 | 0 | return rc; |
1589 | 0 | } |
1590 | 0 | return rc; |
1591 | 0 | } |
1592 | | |
1593 | | /* Add content into a new variable-length metalayer. |
1594 | | * |
1595 | | * If successful, return the index of the new variable-length metalayer. Else, return a negative value. |
1596 | | */ |
1597 | | int blosc2_vlmeta_add(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len, |
1598 | 0 | blosc2_cparams *cparams) { |
1599 | 0 | int nvlmetalayer = blosc2_vlmeta_exists(schunk, name); |
1600 | 0 | if (nvlmetalayer >= 0) { |
1601 | 0 | BLOSC_TRACE_ERROR("Variable-length metalayer \"%s\" already exists.", name); |
1602 | 0 | return BLOSC2_ERROR_INVALID_PARAM; |
1603 | 0 | } |
1604 | | |
1605 | | // Add the vlmetalayer |
1606 | 0 | blosc2_metalayer *vlmetalayer = malloc(sizeof(blosc2_metalayer)); |
1607 | 0 | vlmetalayer->name = strdup(name); |
1608 | 0 | uint8_t* content_buf = malloc((size_t) content_len + BLOSC2_MAX_OVERHEAD); |
1609 | |
|
1610 | 0 | blosc2_context *cctx; |
1611 | 0 | if (cparams != NULL) { |
1612 | 0 | cctx = blosc2_create_cctx(*cparams); |
1613 | 0 | } else { |
1614 | 0 | cctx = blosc2_create_cctx(BLOSC2_CPARAMS_DEFAULTS); |
1615 | 0 | } |
1616 | 0 | if (cctx == NULL) { |
1617 | 0 | BLOSC_TRACE_ERROR("Error while creating the compression context"); |
1618 | 0 | return BLOSC2_ERROR_NULL_POINTER; |
1619 | 0 | } |
1620 | | |
1621 | 0 | int csize = blosc2_compress_ctx(cctx, content, content_len, content_buf, content_len + BLOSC2_MAX_OVERHEAD); |
1622 | 0 | if (csize < 0) { |
1623 | 0 | BLOSC_TRACE_ERROR("Can not compress the `%s` variable-length metalayer.", name); |
1624 | 0 | return csize; |
1625 | 0 | } |
1626 | 0 | blosc2_free_ctx(cctx); |
1627 | |
|
1628 | 0 | vlmetalayer->content = realloc(content_buf, csize); |
1629 | 0 | vlmetalayer->content_len = csize; |
1630 | 0 | schunk->vlmetalayers[schunk->nvlmetalayers] = vlmetalayer; |
1631 | 0 | schunk->nvlmetalayers += 1; |
1632 | | |
1633 | | // Propagate to frames |
1634 | 0 | int rc = vlmetalayer_flush(schunk); |
1635 | 0 | if (rc < 0) { |
1636 | 0 | BLOSC_TRACE_ERROR("Can not propagate de `%s` variable-length metalayer to a frame.", name); |
1637 | 0 | return rc; |
1638 | 0 | } |
1639 | | |
1640 | 0 | return schunk->nvlmetalayers - 1; |
1641 | 0 | } |
1642 | | |
1643 | | |
1644 | | int blosc2_vlmeta_get(blosc2_schunk *schunk, const char *name, uint8_t **content, |
1645 | 0 | int32_t *content_len) { |
1646 | 0 | int nvlmetalayer = blosc2_vlmeta_exists(schunk, name); |
1647 | 0 | if (nvlmetalayer < 0) { |
1648 | 0 | BLOSC_TRACE_ERROR("User metalayer \"%s\" not found.", name); |
1649 | 0 | return nvlmetalayer; |
1650 | 0 | } |
1651 | 0 | blosc2_metalayer *meta = schunk->vlmetalayers[nvlmetalayer]; |
1652 | 0 | int32_t nbytes, cbytes; |
1653 | 0 | blosc2_cbuffer_sizes(meta->content, &nbytes, &cbytes, NULL); |
1654 | 0 | if (cbytes != meta->content_len) { |
1655 | 0 | BLOSC_TRACE_ERROR("User metalayer \"%s\" is corrupted.", meta->name); |
1656 | 0 | return BLOSC2_ERROR_DATA; |
1657 | 0 | } |
1658 | 0 | *content_len = nbytes; |
1659 | 0 | *content = malloc((size_t) nbytes); |
1660 | 0 | blosc2_context *dctx = blosc2_create_dctx(*schunk->storage->dparams); |
1661 | 0 | if (dctx == NULL) { |
1662 | 0 | BLOSC_TRACE_ERROR("Error while creating the decompression context"); |
1663 | 0 | return BLOSC2_ERROR_NULL_POINTER; |
1664 | 0 | } |
1665 | 0 | int nbytes_ = blosc2_decompress_ctx(dctx, meta->content, meta->content_len, *content, nbytes); |
1666 | 0 | blosc2_free_ctx(dctx); |
1667 | 0 | if (nbytes_ != nbytes) { |
1668 | 0 | BLOSC_TRACE_ERROR("User metalayer \"%s\" is corrupted.", meta->name); |
1669 | 0 | return BLOSC2_ERROR_READ_BUFFER; |
1670 | 0 | } |
1671 | 0 | return nvlmetalayer; |
1672 | 0 | } |
1673 | | |
1674 | | int blosc2_vlmeta_update(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len, |
1675 | 0 | blosc2_cparams *cparams) { |
1676 | 0 | int nvlmetalayer = blosc2_vlmeta_exists(schunk, name); |
1677 | 0 | if (nvlmetalayer < 0) { |
1678 | 0 | BLOSC_TRACE_ERROR("User vlmetalayer \"%s\" not found.", name); |
1679 | 0 | return nvlmetalayer; |
1680 | 0 | } |
1681 | | |
1682 | 0 | blosc2_metalayer *vlmetalayer = schunk->vlmetalayers[nvlmetalayer]; |
1683 | 0 | free(vlmetalayer->content); |
1684 | 0 | uint8_t* content_buf = malloc((size_t) content_len + BLOSC2_MAX_OVERHEAD); |
1685 | |
|
1686 | 0 | blosc2_context *cctx; |
1687 | 0 | if (cparams != NULL) { |
1688 | 0 | cctx = blosc2_create_cctx(*cparams); |
1689 | 0 | } else { |
1690 | 0 | cctx = blosc2_create_cctx(BLOSC2_CPARAMS_DEFAULTS); |
1691 | 0 | } |
1692 | 0 | if (cctx == NULL) { |
1693 | 0 | BLOSC_TRACE_ERROR("Error while creating the compression context"); |
1694 | 0 | return BLOSC2_ERROR_NULL_POINTER; |
1695 | 0 | } |
1696 | | |
1697 | 0 | int csize = blosc2_compress_ctx(cctx, content, content_len, content_buf, content_len + BLOSC2_MAX_OVERHEAD); |
1698 | 0 | if (csize < 0) { |
1699 | 0 | BLOSC_TRACE_ERROR("Can not compress the `%s` variable-length metalayer.", name); |
1700 | 0 | return csize; |
1701 | 0 | } |
1702 | 0 | blosc2_free_ctx(cctx); |
1703 | |
|
1704 | 0 | vlmetalayer->content = realloc(content_buf, csize); |
1705 | 0 | vlmetalayer->content_len = csize; |
1706 | | |
1707 | | // Propagate to frames |
1708 | 0 | int rc = vlmetalayer_flush(schunk); |
1709 | 0 | if (rc < 0) { |
1710 | 0 | BLOSC_TRACE_ERROR("Can not propagate de `%s` variable-length metalayer to a frame.", name); |
1711 | 0 | return rc; |
1712 | 0 | } |
1713 | | |
1714 | 0 | return nvlmetalayer; |
1715 | 0 | } |
1716 | | |
1717 | 0 | int blosc2_vlmeta_delete(blosc2_schunk *schunk, const char *name) { |
1718 | 0 | int nvlmetalayer = blosc2_vlmeta_exists(schunk, name); |
1719 | 0 | if (nvlmetalayer < 0) { |
1720 | 0 | BLOSC_TRACE_ERROR("User vlmetalayer \"%s\" not found.", name); |
1721 | 0 | return nvlmetalayer; |
1722 | 0 | } |
1723 | | |
1724 | 0 | blosc2_metalayer *vlmetalayer = schunk->vlmetalayers[nvlmetalayer]; |
1725 | 0 | for (int i = nvlmetalayer; i < (schunk->nvlmetalayers - 1); i++) { |
1726 | 0 | schunk->vlmetalayers[i] = schunk->vlmetalayers[i + 1]; |
1727 | 0 | } |
1728 | 0 | free(vlmetalayer->content); |
1729 | 0 | schunk->nvlmetalayers--; |
1730 | | |
1731 | | // Propagate to frames |
1732 | 0 | int rc = vlmetalayer_flush(schunk); |
1733 | 0 | if (rc < 0) { |
1734 | 0 | BLOSC_TRACE_ERROR("Can not propagate de `%s` variable-length metalayer to a frame.", name); |
1735 | 0 | return rc; |
1736 | 0 | } |
1737 | | |
1738 | 0 | return schunk->nvlmetalayers; |
1739 | 0 | } |
1740 | | |
1741 | | |
1742 | 0 | int blosc2_vlmeta_get_names(blosc2_schunk *schunk, char **names) { |
1743 | 0 | int16_t nvlmetalayers = schunk->nvlmetalayers; |
1744 | |
|
1745 | 0 | for (int i = 0; i < nvlmetalayers; ++i) { |
1746 | 0 | names[i] = schunk->vlmetalayers[i]->name; |
1747 | 0 | } |
1748 | |
|
1749 | 0 | return nvlmetalayers; |
1750 | 0 | } |