Coverage Report

Created: 2025-07-18 06:47

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