Coverage Report

Created: 2025-08-29 06:36

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