Coverage Report

Created: 2024-10-29 06:49

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