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