Coverage Report

Created: 2025-08-29 06:39

/src/c-blosc2/blosc/frame.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 "sframe.h"
13
#include "context.h"
14
#include "blosc-private.h"
15
#include "blosc2.h"
16
17
#include <sys/stat.h>
18
#if defined(_WIN32)
19
#include <windows.h>
20
#include <malloc.h>
21
// See https://github.com/Blosc/python-blosc2/issues/359#issuecomment-2625380236
22
#define stat _stat64
23
#endif  /* _WIN32 */
24
25
#include <inttypes.h>
26
#include <stdbool.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
/* Create a new (empty) frame */
39
0
blosc2_frame_s* frame_new(const char* urlpath) {
40
0
  blosc2_frame_s* new_frame = calloc(1, sizeof(blosc2_frame_s));
41
0
  if (urlpath != NULL) {
42
0
    char* new_urlpath = malloc(strlen(urlpath) + 1);  // + 1 for the trailing NULL
43
0
    new_frame->urlpath = strcpy(new_urlpath, urlpath);
44
0
    new_frame->file_offset = 0;
45
0
  }
46
0
  return new_frame;
47
0
}
48
49
50
/* Free memory from a frame. */
51
0
int frame_free(blosc2_frame_s* frame) {
52
53
0
  if (frame->cframe != NULL && !frame->avoid_cframe_free) {
54
0
    free(frame->cframe);
55
0
  }
56
57
0
  if (frame->coffsets != NULL && frame->coffsets_needs_free) {
58
0
    free(frame->coffsets);
59
0
  }
60
61
0
  if (frame->urlpath != NULL) {
62
0
    free(frame->urlpath);
63
0
  }
64
65
0
  free(frame);
66
67
0
  return 0;
68
0
}
69
70
71
0
void *new_header_frame(blosc2_schunk *schunk, blosc2_frame_s *frame) {
72
0
  if (frame == NULL) {
73
0
    return NULL;
74
0
  }
75
0
  uint8_t* h2 = calloc(FRAME_HEADER_MINLEN, 1);
76
0
  uint8_t* h2p = h2;
77
78
  // The msgpack header starts here
79
0
  *h2p = 0x90;  // fixarray...
80
0
  *h2p += 14;   // ...with 13 elements
81
0
  h2p += 1;
82
83
  // Magic number
84
0
  *h2p = 0xa0 + 8;  // str with 8 elements
85
0
  h2p += 1;
86
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
87
0
    return NULL;
88
0
  }
89
0
  strcpy((char*)h2p, "b2frame");
90
0
  h2p += 8;
91
92
  // Header size
93
0
  *h2p = 0xd2;  // int32
94
0
  h2p += 1 + 4;
95
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
96
0
    return NULL;
97
0
  }
98
99
  // Total frame size
100
0
  *h2p = 0xcf;  // uint64
101
  // Fill it with frame->len which is known *after* the creation of the frame (e.g. when updating the header)
102
0
  int64_t flen = frame->len;
103
0
  to_big(h2 + FRAME_LEN, &flen, sizeof(flen));
104
0
  h2p += 1 + 8;
105
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
106
0
    return NULL;
107
0
  }
108
109
  // Flags
110
0
  *h2p = 0xa0 + 4;  // str with 4 elements
111
0
  h2p += 1;
112
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
113
0
    return NULL;
114
0
  }
115
  // General flags
116
0
  *h2p = BLOSC2_VERSION_FRAME_FORMAT;  // version
117
0
  *h2p += 0x10;  // 64-bit offsets.  We only support this for now.
118
0
  h2p += 1;
119
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
120
0
    return NULL;
121
0
  }
122
123
  // Frame type
124
  // We only support contiguous and sparse directories frames currently
125
0
  *h2p = frame->sframe ? 1 : 0;
126
0
  h2p += 1;
127
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
128
0
    return NULL;
129
0
  }
130
131
  // Codec flags
132
0
  *h2p = schunk->compcode;
133
0
  if (schunk->compcode >= BLOSC_LAST_CODEC) {
134
0
    *h2p = BLOSC_UDCODEC_FORMAT;
135
0
  }
136
0
  *h2p += (schunk->clevel) << 4u;  // clevel
137
0
  h2p += 1;
138
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
139
0
    return NULL;
140
0
  }
141
142
  // Other flags
143
0
  *h2p = schunk->splitmode - 1;
144
0
  h2p += 1;
145
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
146
0
    return NULL;
147
0
  }
148
149
  // Uncompressed size
150
0
  *h2p = 0xd3;  // int64
151
0
  h2p += 1;
152
0
  int64_t nbytes = schunk->nbytes;
153
0
  to_big(h2p, &nbytes, sizeof(nbytes));
154
0
  h2p += 8;
155
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
156
0
    return NULL;
157
0
  }
158
159
  // Compressed size
160
0
  *h2p = 0xd3;  // int64
161
0
  h2p += 1;
162
0
  int64_t cbytes = schunk->cbytes;
163
0
  to_big(h2p, &cbytes, sizeof(cbytes));
164
0
  h2p += 8;
165
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
166
0
    return NULL;
167
0
  }
168
169
  // Type size
170
0
  *h2p = 0xd2;  // int32
171
0
  h2p += 1;
172
0
  int32_t typesize = schunk->typesize;
173
0
  to_big(h2p, &typesize, sizeof(typesize));
174
0
  h2p += 4;
175
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
176
0
    return NULL;
177
0
  }
178
179
  // Block size
180
0
  *h2p = 0xd2;  // int32
181
0
  h2p += 1;
182
0
  int32_t blocksize = schunk->blocksize;
183
0
  to_big(h2p, &blocksize, sizeof(blocksize));
184
0
  h2p += 4;
185
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
186
0
    return NULL;
187
0
  }
188
189
  // Chunk size
190
0
  *h2p = 0xd2;  // int32
191
0
  h2p += 1;
192
0
  int32_t chunksize = schunk->chunksize;
193
0
  to_big(h2p, &chunksize, sizeof(chunksize));
194
0
  h2p += 4;
195
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
196
0
    return NULL;
197
0
  }
198
199
  // Number of threads for compression
200
0
  *h2p = 0xd1;  // int16
201
0
  h2p += 1;
202
0
  int16_t nthreads = (int16_t)schunk->cctx->nthreads;
203
0
  to_big(h2p, &nthreads, sizeof(nthreads));
204
0
  h2p += 2;
205
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
206
0
    return NULL;
207
0
  }
208
209
  // Number of threads for decompression
210
0
  *h2p = 0xd1;  // int16
211
0
  h2p += 1;
212
0
  nthreads = (int16_t)schunk->dctx->nthreads;
213
0
  to_big(h2p, &nthreads, sizeof(nthreads));
214
0
  h2p += 2;
215
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
216
0
    return NULL;
217
0
  }
218
219
  // The boolean for variable-length metalayers
220
0
  *h2p = (schunk->nvlmetalayers > 0) ? (uint8_t)0xc3 : (uint8_t)0xc2;
221
0
  h2p += 1;
222
0
  if (h2p - h2 >= FRAME_HEADER_MINLEN) {
223
0
    return NULL;
224
0
  }
225
226
  // The space for FRAME_FILTER_PIPELINE
227
0
  *h2p = 0xd8;  //  fixext 16
228
0
  h2p += 1;
229
0
  if (BLOSC2_MAX_FILTERS > FRAME_FILTER_PIPELINE_MAX) {
230
0
    return NULL;
231
0
  }
232
  // Store the filter pipeline in header
233
0
  uint8_t* mp_filters = h2 + FRAME_FILTER_PIPELINE + 1;
234
0
  uint8_t* mp_meta = h2 + FRAME_FILTER_PIPELINE + 1 + FRAME_FILTER_PIPELINE_MAX;
235
0
  for (int i = 0; i < BLOSC2_MAX_FILTERS; i++) {
236
0
      mp_filters[i] = schunk->filters[i];
237
0
      mp_meta[i] = schunk->filters_meta[i];
238
0
  }
239
0
  *h2p = (uint8_t) BLOSC2_MAX_FILTERS;
240
0
  h2p += 1;
241
0
  h2p += 16;
242
243
  // User-defined codec and codec metadata
244
0
  uint8_t* udcodec = h2 + FRAME_UDCODEC;
245
0
  *udcodec = schunk->compcode;
246
0
  uint8_t* codec_meta = h2 + FRAME_CODEC_META;
247
0
  *codec_meta = schunk->compcode_meta;
248
249
0
  if (h2p - h2 != FRAME_HEADER_MINLEN) {
250
0
    return NULL;
251
0
  }
252
253
0
  int32_t hsize = FRAME_HEADER_MINLEN;
254
255
  // Now, deal with metalayers
256
0
  uint16_t nmetalayers = schunk->nmetalayers;
257
0
  if (nmetalayers > BLOSC2_MAX_METALAYERS) {
258
0
    return NULL;
259
0
  }
260
261
  // Make space for the header of metalayers (array marker, size, map of offsets)
262
0
  h2 = realloc(h2, (size_t)hsize + 1 + 1 + 2 + 1 + 2);
263
0
  h2p = h2 + hsize;
264
265
  // The msgpack header for the metalayers (array_marker, size, map of offsets, list of metalayers)
266
0
  *h2p = 0x90 + 3;  // array with 3 elements
267
0
  h2p += 1;
268
269
  // Size for the map (index) of offsets, including this uint16 size (to be filled out later on)
270
0
  *h2p = 0xcd;  // uint16
271
0
  h2p += 1 + 2;
272
273
  // Map (index) of offsets for optional metalayers
274
0
  *h2p = 0xde;  // map 16 with N keys
275
0
  h2p += 1;
276
0
  to_big(h2p, &nmetalayers, sizeof(nmetalayers));
277
0
  h2p += sizeof(nmetalayers);
278
0
  int32_t current_header_len = (int32_t)(h2p - h2);
279
0
  int32_t *offtooff = malloc(nmetalayers * sizeof(int32_t));
280
0
  for (int nmetalayer = 0; nmetalayer < nmetalayers; nmetalayer++) {
281
0
    if (frame == NULL) {
282
0
      return NULL;
283
0
    }
284
0
    blosc2_metalayer *metalayer = schunk->metalayers[nmetalayer];
285
0
    uint8_t namelen = (uint8_t) strlen(metalayer->name);
286
0
    h2 = realloc(h2, (size_t)current_header_len + 1 + namelen + 1 + 4);
287
0
    h2p = h2 + current_header_len;
288
    // Store the metalayer
289
0
    if (namelen >= (1U << 5U)) {  // metalayer strings cannot be longer than 32 bytes
290
0
      free(offtooff);
291
0
      return NULL;
292
0
    }
293
0
    *h2p = (uint8_t)0xa0 + namelen;  // str
294
0
    h2p += 1;
295
0
    memcpy(h2p, metalayer->name, namelen);
296
0
    h2p += namelen;
297
    // Space for storing the offset for the value of this metalayer
298
0
    *h2p = 0xd2;  // int32
299
0
    h2p += 1;
300
0
    offtooff[nmetalayer] = (int32_t)(h2p - h2);
301
0
    h2p += 4;
302
0
    current_header_len += 1 + namelen + 1 + 4;
303
0
  }
304
0
  int32_t hsize2 = (int32_t)(h2p - h2);
305
0
  if (hsize2 != current_header_len) {  // sanity check
306
0
    return NULL;
307
0
  }
308
309
  // Map size + int16 size
310
0
  if ((uint32_t) (hsize2 - hsize) >= (1U << 16U)) {
311
0
    return NULL;
312
0
  }
313
0
  uint16_t map_size = (uint16_t) (hsize2 - hsize);
314
0
  to_big(h2 + FRAME_IDX_SIZE, &map_size, sizeof(map_size));
315
316
  // Make space for an (empty) array
317
0
  hsize = (int32_t)(h2p - h2);
318
0
  h2 = realloc(h2, (size_t)hsize + 2 + 1 + 2);
319
0
  h2p = h2 + hsize;
320
321
  // Now, store the values in an array
322
0
  *h2p = 0xdc;  // array 16 with N elements
323
0
  h2p += 1;
324
0
  to_big(h2p, &nmetalayers, sizeof(nmetalayers));
325
0
  h2p += sizeof(nmetalayers);
326
0
  current_header_len = (int32_t)(h2p - h2);
327
0
  for (int nmetalayer = 0; nmetalayer < nmetalayers; nmetalayer++) {
328
0
    if (frame == NULL) {
329
0
      return NULL;
330
0
    }
331
0
    blosc2_metalayer *metalayer = schunk->metalayers[nmetalayer];
332
0
    h2 = realloc(h2, (size_t)current_header_len + 1 + 4 + metalayer->content_len);
333
0
    h2p = h2 + current_header_len;
334
    // Store the serialized contents for this metalayer
335
0
    *h2p = 0xc6;  // bin32
336
0
    h2p += 1;
337
0
    to_big(h2p, &(metalayer->content_len), sizeof(metalayer->content_len));
338
0
    h2p += 4;
339
0
    memcpy(h2p, metalayer->content, metalayer->content_len);  // buffer, no need to swap
340
0
    h2p += metalayer->content_len;
341
    // Update the offset now that we know it
342
0
    to_big(h2 + offtooff[nmetalayer], &current_header_len, sizeof(current_header_len));
343
0
    current_header_len += 1 + 4 + metalayer->content_len;
344
0
  }
345
0
  free(offtooff);
346
0
  hsize = (int32_t)(h2p - h2);
347
0
  if (hsize != current_header_len) {  // sanity check
348
0
    return NULL;
349
0
  }
350
351
  // Set the length of the whole header now that we know it
352
0
  to_big(h2 + FRAME_HEADER_LEN, &hsize, sizeof(hsize));
353
354
0
  return h2;
355
0
}
356
357
358
int get_header_info(blosc2_frame_s *frame, int32_t *header_len, int64_t *frame_len, int64_t *nbytes, int64_t *cbytes,
359
                    int32_t *blocksize, int32_t *chunksize, int64_t *nchunks, int32_t *typesize, uint8_t *compcode,
360
                    uint8_t *compcode_meta, uint8_t *clevel, uint8_t *filters, uint8_t *filters_meta,
361
0
                    uint8_t *splitmode, const blosc2_io *io) {
362
0
  uint8_t* framep = frame->cframe;
363
0
  uint8_t* header_ptr;
364
0
  uint8_t header[FRAME_HEADER_MINLEN];
365
366
0
  blosc2_io_cb *io_cb = blosc2_get_io_cb(io->id);
367
0
  if (io_cb == NULL) {
368
0
    BLOSC_TRACE_ERROR("Error getting the input/output API");
369
0
    return BLOSC2_ERROR_PLUGIN_IO;
370
0
  }
371
372
0
  if (frame->len <= 0) {
373
0
    return BLOSC2_ERROR_READ_BUFFER;
374
0
  }
375
376
0
  if (frame->cframe == NULL) {
377
0
    int64_t rbytes = 0;
378
0
    void* fp = NULL;
379
0
    int64_t io_pos = 0;
380
0
    if (frame->sframe) {
381
0
      fp = sframe_open_index(frame->urlpath, "rb", io);
382
0
      if (fp == NULL) {
383
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
384
0
        return BLOSC2_ERROR_FILE_OPEN;
385
0
      }
386
0
    }
387
0
    else {
388
0
      fp = io_cb->open(frame->urlpath, "rb", io->params);
389
0
      if (fp == NULL) {
390
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
391
0
        return BLOSC2_ERROR_FILE_OPEN;
392
0
      }
393
0
      io_pos = frame->file_offset;
394
0
    }
395
0
    if (io_cb->is_allocation_necessary)
396
0
      header_ptr = header;
397
0
    rbytes = io_cb->read((void**)&header_ptr, 1, FRAME_HEADER_MINLEN, io_pos, fp);
398
0
    io_cb->close(fp);
399
0
    if (rbytes != FRAME_HEADER_MINLEN) {
400
0
      return BLOSC2_ERROR_FILE_READ;
401
0
    }
402
0
    framep = header_ptr;
403
0
  }
404
405
  // Consistency check for frame type
406
0
  uint8_t frame_type = framep[FRAME_TYPE];
407
0
  if (frame->sframe) {
408
0
    if (frame_type != FRAME_DIRECTORY_TYPE) {
409
0
      return BLOSC2_ERROR_FRAME_TYPE;
410
0
    }
411
0
  } else {
412
0
    if (frame_type != FRAME_CONTIGUOUS_TYPE) {
413
0
      return BLOSC2_ERROR_FRAME_TYPE;
414
0
    }
415
0
  }
416
417
  // Fetch some internal lengths
418
0
  from_big(header_len, framep + FRAME_HEADER_LEN, sizeof(*header_len));
419
0
  if (*header_len < FRAME_HEADER_MINLEN) {
420
0
    BLOSC_TRACE_ERROR("Header length is zero or smaller than min allowed.");
421
0
    return BLOSC2_ERROR_INVALID_HEADER;
422
0
  }
423
0
  from_big(frame_len, framep + FRAME_LEN, sizeof(*frame_len));
424
0
  if (*header_len > *frame_len) {
425
0
    BLOSC_TRACE_ERROR("Header length exceeds length of the frame.");
426
0
    return BLOSC2_ERROR_INVALID_HEADER;
427
0
  }
428
0
  from_big(nbytes, framep + FRAME_NBYTES, sizeof(*nbytes));
429
0
  from_big(cbytes, framep + FRAME_CBYTES, sizeof(*cbytes));
430
0
  from_big(blocksize, framep + FRAME_BLOCKSIZE, sizeof(*blocksize));
431
0
  if (chunksize != NULL) {
432
0
    from_big(chunksize, framep + FRAME_CHUNKSIZE, sizeof(*chunksize));
433
0
  }
434
0
  if (typesize != NULL) {
435
0
    from_big(typesize, framep + FRAME_TYPESIZE, sizeof(*typesize));
436
0
    if (*typesize <= 0) {
437
0
      BLOSC_TRACE_ERROR("`typesize` cannot be zero or negative.");
438
0
      return BLOSC2_ERROR_INVALID_HEADER;
439
0
    }
440
0
  }
441
442
  // Codecs
443
0
  uint8_t frame_codecs = framep[FRAME_CODECS];
444
0
  if (clevel != NULL) {
445
0
    *clevel = frame_codecs >> 4u;
446
0
  }
447
0
  if (compcode != NULL) {
448
0
    *compcode = frame_codecs & 0xFu;
449
0
    if (*compcode == BLOSC_UDCODEC_FORMAT) {
450
0
      from_big(compcode, framep + FRAME_UDCODEC, sizeof(*compcode));
451
0
    }
452
0
  }
453
454
  // Other flags
455
0
  uint8_t other_flags = framep[FRAME_OTHER_FLAGS];
456
0
  if (splitmode != NULL) {
457
0
    *splitmode = other_flags & 0x4u;
458
0
    from_big(splitmode, framep + FRAME_OTHER_FLAGS, sizeof(*splitmode));
459
0
    *splitmode += 1;
460
0
  }
461
462
0
  if (compcode_meta != NULL) {
463
0
    from_big(compcode_meta, framep + FRAME_CODEC_META, sizeof(*compcode_meta));
464
0
  }
465
466
  // Filters
467
0
  if (filters != NULL && filters_meta != NULL) {
468
0
    uint8_t nfilters = framep[FRAME_FILTER_PIPELINE];
469
0
    if (nfilters > BLOSC2_MAX_FILTERS) {
470
0
      BLOSC_TRACE_ERROR("The number of filters in frame header are too large for Blosc2.");
471
0
      return BLOSC2_ERROR_INVALID_HEADER;
472
0
    }
473
0
    uint8_t *filters_ = framep + FRAME_FILTER_PIPELINE + 1;
474
0
    uint8_t *filters_meta_ = framep + FRAME_FILTER_PIPELINE + 1 + FRAME_FILTER_PIPELINE_MAX;
475
0
    for (int i = 0; i < nfilters; i++) {
476
0
      filters[i] = filters_[i];
477
0
      filters_meta[i] = filters_meta_[i];
478
0
    }
479
0
  }
480
481
0
  if (*nbytes > 0 && *chunksize > 0) {
482
    // We can compute the number of chunks only when the frame has actual data
483
0
    *nchunks = *nbytes / *chunksize;
484
0
    if (*nbytes % *chunksize > 0) {
485
0
      if (*nchunks == INT32_MAX) {
486
0
        BLOSC_TRACE_ERROR("Number of chunks exceeds maximum allowed.");
487
0
        return BLOSC2_ERROR_INVALID_HEADER;
488
0
      }
489
0
      *nchunks += 1;
490
0
    }
491
492
    // Sanity check for compressed sizes
493
0
    if ((*cbytes < 0) || ((int64_t)*nchunks * *chunksize < *nbytes)) {
494
0
      BLOSC_TRACE_ERROR("Invalid compressed size in frame header.");
495
0
      return BLOSC2_ERROR_INVALID_HEADER;
496
0
    }
497
0
  } else {
498
0
    *nchunks = 0;
499
0
  }
500
501
0
  return 0;
502
0
}
503
504
505
0
int64_t get_trailer_offset(blosc2_frame_s *frame, int32_t header_len, bool has_coffsets) {
506
0
  if (!has_coffsets) {
507
    // No data chunks yet
508
0
    return header_len;
509
0
  }
510
0
  return frame->len - frame->trailer_len;
511
0
}
512
513
514
// Update the length in the header
515
0
int update_frame_len(blosc2_frame_s* frame, int64_t len) {
516
0
  int rc = 1;
517
0
  blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
518
0
  if (io_cb == NULL) {
519
0
    BLOSC_TRACE_ERROR("Error getting the input/output API");
520
0
    return BLOSC2_ERROR_PLUGIN_IO;
521
0
  }
522
523
0
  if (frame->cframe != NULL) {
524
0
    to_big(frame->cframe + FRAME_LEN, &len, sizeof(int64_t));
525
0
  }
526
0
  else {
527
0
    void* fp = NULL;
528
0
    if (frame->sframe) {
529
0
      fp = sframe_open_index(frame->urlpath, "rb+",
530
0
                             frame->schunk->storage->io);
531
0
    }
532
0
    else {
533
0
      fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params);
534
0
    }
535
0
    if (fp == NULL) {
536
0
      BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
537
0
      return BLOSC2_ERROR_FILE_OPEN;
538
0
    }
539
0
    int64_t io_pos = frame->file_offset + FRAME_LEN;
540
0
    int64_t swap_len;
541
0
    to_big(&swap_len, &len, sizeof(int64_t));
542
0
    int64_t wbytes = io_cb->write(&swap_len, 1, sizeof(int64_t), io_pos, fp);
543
0
    io_cb->close(fp);
544
0
    if (wbytes != sizeof(int64_t)) {
545
0
      BLOSC_TRACE_ERROR("Cannot write the frame length in header.");
546
0
      return BLOSC2_ERROR_FILE_WRITE;
547
0
    }
548
0
  }
549
0
  return rc;
550
0
}
551
552
553
0
int frame_update_trailer(blosc2_frame_s* frame, blosc2_schunk* schunk) {
554
0
  if (frame != NULL && frame->len == 0) {
555
0
    BLOSC_TRACE_ERROR("The trailer cannot be updated on empty frames.");
556
0
  }
557
558
  // Create the trailer in msgpack (see the frame format document)
559
0
  uint32_t trailer_len = FRAME_TRAILER_MINLEN;
560
0
  uint8_t* trailer = (uint8_t*)calloc((size_t)trailer_len, 1);
561
0
  uint8_t* ptrailer = trailer;
562
0
  *ptrailer = 0x90 + 4;  // fixarray with 4 elements
563
0
  ptrailer += 1;
564
  // Trailer format version
565
0
  *ptrailer = FRAME_TRAILER_VERSION;
566
0
  ptrailer += 1;
567
568
0
  int32_t current_trailer_len = (int32_t)(ptrailer - trailer);
569
570
  // Now, deal with variable-length metalayers
571
0
  int16_t nvlmetalayers = schunk->nvlmetalayers;
572
0
  if (nvlmetalayers < 0 || nvlmetalayers > BLOSC2_MAX_METALAYERS) {
573
0
    return -1;
574
0
  }
575
576
  // Make space for the header of metalayers (array marker, size, map of offsets)
577
0
  trailer = realloc(trailer, (size_t) current_trailer_len + 1 + 1 + 2 + 1 + 2);
578
0
  ptrailer = trailer + current_trailer_len;
579
580
  // The msgpack header for the metalayers (array_marker, size, map of offsets, list of metalayers)
581
0
  *ptrailer = 0x90 + 3;  // array with 3 elements
582
0
  ptrailer += 1;
583
584
0
  int32_t tsize = (int32_t)(ptrailer - trailer);
585
586
  // Size for the map (index) of metalayer offsets, including this uint16 size (to be filled out later on)
587
0
  *ptrailer = 0xcd;  // uint16
588
0
  ptrailer += 1 + 2;
589
590
  // Map (index) of offsets for optional metalayers
591
0
  *ptrailer = 0xde;  // map 16 with N keys
592
0
  ptrailer += 1;
593
0
  to_big(ptrailer, &nvlmetalayers, sizeof(nvlmetalayers));
594
0
  ptrailer += sizeof(nvlmetalayers);
595
0
  current_trailer_len = (int32_t)(ptrailer - trailer);
596
0
  int32_t *offtodata = malloc(nvlmetalayers * sizeof(int32_t));
597
0
  for (int nvlmetalayer = 0; nvlmetalayer < nvlmetalayers; nvlmetalayer++) {
598
0
    if (frame == NULL) {
599
0
      return -1;
600
0
    }
601
0
    blosc2_metalayer *vlmetalayer = schunk->vlmetalayers[nvlmetalayer];
602
0
    uint8_t name_len = (uint8_t) strlen(vlmetalayer->name);
603
0
    trailer = realloc(trailer, (size_t)current_trailer_len + 1 + name_len + 1 + 4);
604
0
    ptrailer = trailer + current_trailer_len;
605
    // Store the vlmetalayer
606
0
    if (name_len >= (1U << 5U)) {  // metalayer strings cannot be longer than 32 bytes
607
0
      free(offtodata);
608
0
      return -1;
609
0
    }
610
0
    *ptrailer = (uint8_t)0xa0 + name_len;  // str
611
0
    ptrailer += 1;
612
0
    memcpy(ptrailer, vlmetalayer->name, name_len);
613
0
    ptrailer += name_len;
614
    // Space for storing the offset for the value of this vlmetalayer
615
0
    *ptrailer = 0xd2;  // int32
616
0
    ptrailer += 1;
617
0
    offtodata[nvlmetalayer] = (int32_t)(ptrailer - trailer);
618
0
    ptrailer += 4;
619
0
    current_trailer_len += 1 + name_len + 1 + 4;
620
0
  }
621
0
  int32_t tsize2 = (int32_t)(ptrailer - trailer);
622
0
  if (tsize2 != current_trailer_len) {  // sanity check
623
0
    return -1;
624
0
  }
625
626
  // Map size + int16 size
627
0
  if ((uint32_t) (tsize2 - tsize) >= (1U << 16U)) {
628
0
    return -1;
629
0
  }
630
0
  uint16_t map_size = (uint16_t) (tsize2 - tsize);
631
0
  to_big(trailer + 4, &map_size, sizeof(map_size));
632
633
  // Make space for an (empty) array
634
0
  tsize = (int32_t)(ptrailer - trailer);
635
0
  trailer = realloc(trailer, (size_t) tsize + 2 + 1 + 2);
636
0
  ptrailer = trailer + tsize;
637
638
  // Now, store the values in an array
639
0
  *ptrailer = 0xdc;  // array 16 with N elements
640
0
  ptrailer += 1;
641
0
  to_big(ptrailer, &nvlmetalayers, sizeof(nvlmetalayers));
642
0
  ptrailer += sizeof(nvlmetalayers);
643
0
  current_trailer_len = (int32_t)(ptrailer - trailer);
644
0
  for (int nvlmetalayer = 0; nvlmetalayer < nvlmetalayers; nvlmetalayer++) {
645
0
    if (frame == NULL) {
646
0
      return -1;
647
0
    }
648
0
    blosc2_metalayer *vlmetalayer = schunk->vlmetalayers[nvlmetalayer];
649
0
    trailer = realloc(trailer, (size_t)current_trailer_len + 1 + 4 + vlmetalayer->content_len);
650
0
    ptrailer = trailer + current_trailer_len;
651
    // Store the serialized contents for this vlmetalayer
652
0
    *ptrailer = 0xc6;  // bin32
653
0
    ptrailer += 1;
654
0
    to_big(ptrailer, &(vlmetalayer->content_len), sizeof(vlmetalayer->content_len));
655
0
    ptrailer += 4;
656
0
    memcpy(ptrailer, vlmetalayer->content, vlmetalayer->content_len);  // buffer, no need to swap
657
0
    ptrailer += vlmetalayer->content_len;
658
    // Update the offset now that we know it
659
0
    to_big(trailer + offtodata[nvlmetalayer], &current_trailer_len, sizeof(current_trailer_len));
660
0
    current_trailer_len += 1 + 4 + vlmetalayer->content_len;
661
0
  }
662
0
  free(offtodata);
663
0
  tsize = (int32_t)(ptrailer - trailer);
664
0
  if (tsize != current_trailer_len) {  // sanity check
665
0
    return -1;
666
0
  }
667
668
0
  trailer = realloc(trailer, (size_t)current_trailer_len + 23);
669
0
  ptrailer = trailer + current_trailer_len;
670
0
  trailer_len = (ptrailer - trailer) + 23;
671
672
  // Trailer length
673
0
  *ptrailer = 0xce;  // uint32
674
0
  ptrailer += 1;
675
0
  to_big(ptrailer, &trailer_len, sizeof(uint32_t));
676
0
  ptrailer += sizeof(uint32_t);
677
  // Up to 16 bytes for frame fingerprint (using XXH3 included in https://github.com/Cyan4973/xxHash)
678
  // Maybe someone would need 256-bit in the future, but for the time being 128-bit seems like a good tradeoff
679
0
  *ptrailer = 0xd8;  // fixext 16
680
0
  ptrailer += 1;
681
0
  *ptrailer = 0;  // fingerprint type: 0 -> no fp; 1 -> 32-bit; 2 -> 64-bit; 3 -> 128-bit
682
0
  ptrailer += 1;
683
684
  // Remove call to memset when we compute an actual fingerprint
685
0
  memset(ptrailer, 0, 16);
686
  // Uncomment call to memcpy when we compute an actual fingerprint
687
  // memcpy(ptrailer, xxh3_fingerprint, sizeof(xxh3_fingerprint));
688
0
  ptrailer += 16;
689
690
  // Sanity check
691
0
  if (ptrailer - trailer != trailer_len) {
692
0
    return BLOSC2_ERROR_DATA;
693
0
  }
694
695
0
  int32_t header_len;
696
0
  int64_t frame_len;
697
0
  int64_t nbytes;
698
0
  int64_t cbytes;
699
0
  int32_t blocksize;
700
0
  int32_t chunksize;
701
0
  int64_t nchunks;
702
0
  int ret = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes,
703
0
                            &blocksize, &chunksize, &nchunks,
704
0
                            NULL, NULL, NULL, NULL, NULL, NULL, NULL,
705
0
                            frame->schunk->storage->io);
706
0
  if (ret < 0) {
707
0
    BLOSC_TRACE_ERROR("Unable to get meta info from frame.");
708
0
    return ret;
709
0
  }
710
711
0
  int64_t trailer_offset = get_trailer_offset(frame, header_len, nbytes > 0);
712
713
0
  if (trailer_offset < BLOSC_EXTENDED_HEADER_LENGTH) {
714
0
    BLOSC_TRACE_ERROR("Unable to get trailer offset in frame.");
715
0
    return BLOSC2_ERROR_READ_BUFFER;
716
0
  }
717
718
0
  blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
719
0
  if (io_cb == NULL) {
720
0
    BLOSC_TRACE_ERROR("Error getting the input/output API");
721
0
    return BLOSC2_ERROR_PLUGIN_IO;
722
0
  }
723
  // Update the trailer.  As there are no internal offsets to the trailer section,
724
  // and it is always at the end of the frame, we can just write (or overwrite) it
725
  // at the end of the frame.
726
0
  if (frame->cframe != NULL) {
727
0
    frame->cframe = realloc(frame->cframe, (size_t)(trailer_offset + trailer_len));
728
0
    if (frame->cframe == NULL) {
729
0
      BLOSC_TRACE_ERROR("Cannot realloc space for the frame.");
730
0
      return BLOSC2_ERROR_MEMORY_ALLOC;
731
0
    }
732
0
    memcpy(frame->cframe + trailer_offset, trailer, trailer_len);
733
0
  }
734
0
  else {
735
0
    void* fp = NULL;
736
0
    if (frame->sframe) {
737
0
      fp = sframe_open_index(frame->urlpath, "rb+",
738
0
                             frame->schunk->storage->io);
739
0
    }
740
0
    else {
741
0
      fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params);
742
0
    }
743
0
    if (fp == NULL) {
744
0
      BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
745
0
      return BLOSC2_ERROR_FILE_OPEN;
746
0
    }
747
0
    int64_t io_pos = frame->file_offset + trailer_offset;
748
0
    int64_t wbytes = io_cb->write(trailer, 1, trailer_len, io_pos, fp);
749
0
    if (wbytes != trailer_len) {
750
0
      BLOSC_TRACE_ERROR("Cannot write the trailer length in trailer.");
751
0
      return BLOSC2_ERROR_FILE_WRITE;
752
0
    }
753
0
    if (io_cb->truncate(fp, trailer_offset + trailer_len) != 0) {
754
0
      BLOSC_TRACE_ERROR("Cannot truncate the frame.");
755
0
      return BLOSC2_ERROR_FILE_TRUNCATE;
756
0
    }
757
0
    io_cb->close(fp);
758
759
0
  }
760
0
  free(trailer);
761
762
0
  int rc = update_frame_len(frame, trailer_offset + trailer_len);
763
0
  if (rc < 0) {
764
0
    return rc;
765
0
  }
766
0
  frame->len = trailer_offset + trailer_len;
767
0
  frame->trailer_len = trailer_len;
768
769
0
  return 1;
770
0
}
771
772
773
// Remove a file:/// prefix
774
// This is a temporary workaround for allowing to use proper URLs for local files/dirs
775
0
static char* normalize_urlpath(const char* urlpath) {
776
0
  char* localpath = strstr(urlpath, "file:///");
777
0
  if (localpath == urlpath) {
778
    // There is a file:/// prefix.  Get rid of it.
779
0
    localpath += strlen("file:///");
780
0
  }
781
0
  else {
782
0
    localpath = (char*)urlpath;
783
0
  }
784
0
  return localpath;
785
0
}
786
787
788
/* Initialize a frame out of a file */
789
0
blosc2_frame_s* frame_from_file_offset(const char* urlpath, const blosc2_io *io, int64_t offset) {
790
    // Get the length of the frame
791
0
    uint8_t* header_ptr;
792
0
    uint8_t header[FRAME_HEADER_MINLEN];
793
0
    uint8_t* trailer_ptr;
794
0
    uint8_t trailer[FRAME_TRAILER_MINLEN];
795
796
0
    void* fp = NULL;
797
0
    bool sframe = false;
798
0
    struct stat path_stat;
799
800
0
    urlpath = normalize_urlpath(urlpath);
801
802
0
    if(stat(urlpath, &path_stat) < 0) {
803
0
        BLOSC_TRACE_ERROR("Cannot get information about the path %s.", urlpath);
804
0
        return NULL;
805
0
    }
806
807
0
    blosc2_io_cb *io_cb = blosc2_get_io_cb(io->id);
808
0
    if (io_cb == NULL) {
809
0
        BLOSC_TRACE_ERROR("Error getting the input/output API");
810
0
        return NULL;
811
0
    }
812
813
0
    char* urlpath_cpy;
814
0
    if (path_stat.st_mode & S_IFDIR) {
815
0
        urlpath_cpy = malloc(strlen(urlpath) + 1);
816
0
        strcpy(urlpath_cpy, urlpath);
817
0
        char last_char = urlpath[strlen(urlpath) - 1];
818
0
        if (last_char == '\\' || last_char == '/') {
819
0
            urlpath_cpy[strlen(urlpath) - 1] = '\0';
820
0
        }
821
0
        else {
822
0
        }
823
0
        fp = sframe_open_index(urlpath_cpy, "rb", io);
824
0
        sframe = true;
825
0
    }
826
0
    else {
827
0
        urlpath_cpy = malloc(strlen(urlpath) + 1);
828
0
        strcpy(urlpath_cpy, urlpath);
829
0
        fp = io_cb->open(urlpath, "rb", io->params);
830
0
    }
831
0
    if (fp == NULL) {
832
0
      BLOSC_TRACE_ERROR("Error opening file in: %s", urlpath);
833
0
      return NULL;
834
0
    }
835
836
0
    if (io_cb->is_allocation_necessary)
837
0
      header_ptr = header;
838
0
    int64_t io_pos = offset;
839
0
    int64_t rbytes = io_cb->read((void**)&header_ptr, 1, FRAME_HEADER_MINLEN, io_pos, fp);
840
0
    if (rbytes != FRAME_HEADER_MINLEN) {
841
0
        BLOSC_TRACE_ERROR("Cannot read from file '%s'.", urlpath);
842
0
        io_cb->close(fp);
843
0
        free(urlpath_cpy);
844
0
        return NULL;
845
0
    }
846
0
    int64_t frame_len;
847
0
    to_big(&frame_len, header_ptr + FRAME_LEN, sizeof(frame_len));
848
849
0
    blosc2_frame_s* frame = calloc(1, sizeof(blosc2_frame_s));
850
0
    frame->urlpath = urlpath_cpy;
851
0
    frame->len = frame_len;
852
0
    frame->sframe = sframe;
853
0
    frame->file_offset = offset;
854
855
    // Now, the trailer length
856
0
    if (io_cb->is_allocation_necessary)
857
0
      trailer_ptr = trailer;
858
0
    io_pos = offset + frame_len - FRAME_TRAILER_MINLEN;
859
0
    rbytes = io_cb->read((void**)&trailer_ptr, 1, FRAME_TRAILER_MINLEN, io_pos, fp);
860
0
    io_cb->close(fp);
861
0
    if (rbytes != FRAME_TRAILER_MINLEN) {
862
0
        BLOSC_TRACE_ERROR("Cannot read from file '%s'.", urlpath);
863
0
        free(urlpath_cpy);
864
0
        free(frame);
865
0
        return NULL;
866
0
    }
867
0
    int trailer_offset = FRAME_TRAILER_MINLEN - FRAME_TRAILER_LEN_OFFSET;
868
0
    if (trailer_ptr[trailer_offset - 1] != 0xce) {
869
0
        BLOSC_TRACE_ERROR("Invalid trailer in file '%s'.", urlpath);
870
0
        free(urlpath_cpy);
871
0
        free(frame);
872
0
        return NULL;
873
0
    }
874
0
    uint32_t trailer_len;
875
0
    to_big(&trailer_len, trailer_ptr + trailer_offset, sizeof(trailer_len));
876
0
    frame->trailer_len = trailer_len;
877
878
0
    return frame;
879
0
}
880
881
882
/* Initialize a frame out of a contiguous frame buffer */
883
0
blosc2_frame_s* frame_from_cframe(uint8_t *cframe, int64_t len, bool copy) {
884
  // Get the length of the frame
885
0
  const uint8_t* header = cframe;
886
0
  int64_t frame_len;
887
0
  if (len < FRAME_HEADER_MINLEN) {
888
0
    return NULL;
889
0
  }
890
891
0
  from_big(&frame_len, header + FRAME_LEN, sizeof(frame_len));
892
0
  if (frame_len != len) {   // sanity check
893
0
    return NULL;
894
0
  }
895
896
0
  blosc2_frame_s* frame = calloc(1, sizeof(blosc2_frame_s));
897
0
  frame->len = frame_len;
898
0
  frame->file_offset = 0;
899
900
  // Now, the trailer length
901
0
  const uint8_t* trailer = cframe + frame_len - FRAME_TRAILER_MINLEN;
902
0
  int trailer_offset = FRAME_TRAILER_MINLEN - FRAME_TRAILER_LEN_OFFSET;
903
0
  if (trailer[trailer_offset - 1] != 0xce) {
904
0
    free(frame);
905
0
    return NULL;
906
0
  }
907
0
  uint32_t trailer_len;
908
0
  from_big(&trailer_len, trailer + trailer_offset, sizeof(trailer_len));
909
0
  frame->trailer_len = trailer_len;
910
911
0
  if (copy) {
912
0
    frame->cframe = malloc((size_t)len);
913
0
    memcpy(frame->cframe, cframe, (size_t)len);
914
0
  }
915
0
  else {
916
0
    frame->cframe = cframe;
917
0
    frame->avoid_cframe_free = true;
918
0
  }
919
920
0
  return frame;
921
0
}
922
923
924
/* Create a frame out of a super-chunk. */
925
0
int64_t frame_from_schunk(blosc2_schunk *schunk, blosc2_frame_s *frame) {
926
0
  frame->file_offset = 0;
927
0
  int64_t nchunks = schunk->nchunks;
928
0
  int64_t cbytes = schunk->cbytes;
929
0
  int32_t chunk_cbytes;
930
0
  int32_t chunk_nbytes;
931
0
  void* fp = NULL;
932
0
  int rc;
933
934
0
  uint8_t* h2 = new_header_frame(schunk, frame);
935
0
  if (h2 == NULL) {
936
0
    return BLOSC2_ERROR_DATA;
937
0
  }
938
0
  uint32_t h2len;
939
0
  from_big(&h2len, h2 + FRAME_HEADER_LEN, sizeof(h2len));
940
  // Build the offsets chunk
941
0
  int32_t chunksize = -1;
942
0
  int32_t off_cbytes = 0;
943
0
  uint64_t coffset = 0;
944
0
  int32_t off_nbytes = (int32_t) (nchunks * sizeof(int64_t));
945
0
  uint64_t* data_tmp = malloc(off_nbytes);
946
0
  bool needs_free = false;
947
0
  for (int i = 0; i < nchunks; i++) {
948
0
    uint8_t* data_chunk;
949
0
    data_chunk = schunk->data[i];
950
0
    rc = blosc2_cbuffer_sizes(data_chunk, &chunk_nbytes, &chunk_cbytes, NULL);
951
0
    if (rc < 0) {
952
0
      return rc;
953
0
    }
954
0
    data_tmp[i] = coffset;
955
0
    coffset += chunk_cbytes;
956
0
    int32_t chunksize_ = chunk_nbytes;
957
0
    if (i == 0) {
958
0
      chunksize = chunksize_;
959
0
    }
960
0
    else if (chunksize != chunksize_) {
961
      // Variable size  // TODO: update flags for this (or do not use them at all)
962
0
      chunksize = 0;
963
0
    }
964
0
    if (needs_free) {
965
0
      free(data_chunk);
966
0
    }
967
0
  }
968
0
  if ((int64_t)coffset != cbytes) {
969
0
    free(data_tmp);
970
0
    return BLOSC2_ERROR_DATA;
971
0
  }
972
0
  uint8_t *off_chunk = NULL;
973
0
  if (nchunks > 0) {
974
    // Compress the chunk of offsets
975
0
    off_chunk = malloc(off_nbytes + BLOSC2_MAX_OVERHEAD);
976
0
    blosc2_context *cctx = blosc2_create_cctx(BLOSC2_CPARAMS_DEFAULTS);
977
0
    if (cctx == NULL) {
978
0
      BLOSC_TRACE_ERROR("Error while creating the compression context");
979
0
      return BLOSC2_ERROR_NULL_POINTER;
980
0
    }
981
0
    cctx->typesize = sizeof(int64_t);
982
0
    off_cbytes = blosc2_compress_ctx(cctx, data_tmp, off_nbytes, off_chunk,
983
0
                                     off_nbytes + BLOSC2_MAX_OVERHEAD);
984
0
    blosc2_free_ctx(cctx);
985
0
    if (off_cbytes < 0) {
986
0
      free(off_chunk);
987
0
      free(h2);
988
0
      return off_cbytes;
989
0
    }
990
0
  }
991
0
  else {
992
0
    off_cbytes = 0;
993
0
  }
994
0
  free(data_tmp);
995
996
  // Now that we know them, fill the chunksize and frame length in header
997
0
  to_big(h2 + FRAME_CHUNKSIZE, &chunksize, sizeof(chunksize));
998
0
  frame->len = h2len + cbytes + off_cbytes + FRAME_TRAILER_MINLEN;
999
0
  if (frame->sframe) {
1000
0
    frame->len = h2len + off_cbytes + FRAME_TRAILER_MINLEN;
1001
0
  }
1002
0
  int64_t tbytes = frame->len;
1003
0
  to_big(h2 + FRAME_LEN, &tbytes, sizeof(tbytes));
1004
1005
0
  blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
1006
0
  if (io_cb == NULL) {
1007
0
    BLOSC_TRACE_ERROR("Error getting the input/output API");
1008
0
    return BLOSC2_ERROR_PLUGIN_IO;
1009
0
  }
1010
1011
  // Create the frame and put the header at the beginning
1012
0
  int64_t io_pos = 0;
1013
0
  if (frame->urlpath == NULL) {
1014
0
    frame->cframe = malloc((size_t)frame->len);
1015
0
    memcpy(frame->cframe, h2, h2len);
1016
0
  }
1017
0
  else {
1018
0
    if (frame->sframe) {
1019
0
      fp = sframe_open_index(frame->urlpath, "wb",
1020
0
                             frame->schunk->storage->io);
1021
0
    }
1022
0
    else {
1023
0
      fp = io_cb->open(frame->urlpath, "wb", frame->schunk->storage->io->params);
1024
0
    }
1025
0
    if (fp == NULL) {
1026
0
      BLOSC_TRACE_ERROR("Error creating file in: %s", frame->urlpath);
1027
0
      return BLOSC2_ERROR_FILE_OPEN;
1028
0
    }
1029
0
    io_cb->write(h2, h2len, 1, io_pos, fp);
1030
0
    io_pos += h2len;
1031
0
  }
1032
0
  free(h2);
1033
1034
  // Fill the frame with the actual data chunks
1035
0
  if (!frame->sframe) {
1036
0
    coffset = 0;
1037
0
    for (int i = 0; i < nchunks; i++) {
1038
0
      uint8_t* data_chunk = schunk->data[i];
1039
0
      rc = blosc2_cbuffer_sizes(data_chunk, NULL, &chunk_cbytes, NULL);
1040
0
      if (rc < 0) {
1041
0
        return rc;
1042
0
      }
1043
0
      if (frame->urlpath == NULL) {
1044
0
        memcpy(frame->cframe + h2len + coffset, data_chunk, (size_t)chunk_cbytes);
1045
0
      } else {
1046
0
        io_cb->write(data_chunk, chunk_cbytes, 1, io_pos, fp);
1047
0
        io_pos += chunk_cbytes;
1048
0
      }
1049
0
      coffset += chunk_cbytes;
1050
0
    }
1051
0
    if ((int64_t)coffset != cbytes) {
1052
0
      return BLOSC2_ERROR_FAILURE;
1053
0
    }
1054
0
  }
1055
1056
  // Copy the offsets chunk at the end of the frame
1057
0
  if (frame->urlpath == NULL) {
1058
0
    memcpy(frame->cframe + h2len + cbytes, off_chunk, off_cbytes);
1059
0
  }
1060
0
  else {
1061
0
    io_cb->write(off_chunk, off_cbytes, 1, io_pos, fp);
1062
0
    io_cb->close(fp);
1063
0
  }
1064
0
  free(off_chunk);
1065
0
  rc = frame_update_trailer(frame, schunk);
1066
0
  if (rc < 0) {
1067
0
    return rc;
1068
0
  }
1069
1070
0
  return frame->len;
1071
0
}
1072
1073
1074
// Get the compressed data offsets
1075
uint8_t* get_coffsets(blosc2_frame_s *frame, int32_t header_len, int64_t cbytes,
1076
0
                      int64_t nchunks, int32_t *off_cbytes) {
1077
0
  int32_t chunk_cbytes;
1078
0
  int rc;
1079
1080
0
  if (frame->coffsets != NULL) {
1081
0
    if (off_cbytes != NULL) {
1082
0
      rc = blosc2_cbuffer_sizes(frame->coffsets, NULL, &chunk_cbytes, NULL);
1083
0
      if (rc < 0) {
1084
0
        return NULL;
1085
0
      }
1086
0
      *off_cbytes = (int32_t)chunk_cbytes;
1087
0
    }
1088
0
    return frame->coffsets;
1089
0
  }
1090
0
  if (frame->cframe != NULL) {
1091
0
    int64_t off_pos = header_len;
1092
0
    if (cbytes < INT64_MAX - header_len) {
1093
0
      off_pos += cbytes;
1094
0
    }
1095
    // Check that there is enough room to read Blosc header
1096
0
    if (off_pos < 0 || off_pos > INT64_MAX - BLOSC_EXTENDED_HEADER_LENGTH ||
1097
0
        off_pos + BLOSC_EXTENDED_HEADER_LENGTH > frame->len) {
1098
0
      BLOSC_TRACE_ERROR("Cannot read the offsets outside of frame boundary.");
1099
0
      return NULL;
1100
0
    }
1101
    // For in-memory frames, the coffset is just one pointer away
1102
0
    uint8_t* off_start = frame->cframe + off_pos;
1103
0
    if (off_cbytes != NULL) {
1104
0
      int32_t chunk_nbytes;
1105
0
      int32_t chunk_blocksize;
1106
0
      rc = blosc2_cbuffer_sizes(off_start, &chunk_nbytes, &chunk_cbytes, &chunk_blocksize);
1107
0
      if (rc < 0) {
1108
0
        return NULL;
1109
0
      }
1110
0
      *off_cbytes = (int32_t)chunk_cbytes;
1111
0
      if (*off_cbytes < 0 || off_pos + *off_cbytes > frame->len) {
1112
0
        BLOSC_TRACE_ERROR("Cannot read the cbytes outside of frame boundary.");
1113
0
        return NULL;
1114
0
      }
1115
0
      if ((uint64_t)chunk_nbytes != nchunks * sizeof(int64_t)) {
1116
0
        BLOSC_TRACE_ERROR("The number of chunks in offset idx "
1117
0
                          "does not match the ones in the header frame.");
1118
0
        return NULL;
1119
0
      }
1120
1121
0
    }
1122
0
    return off_start;
1123
0
  }
1124
1125
0
  int64_t trailer_offset = get_trailer_offset(frame, header_len, true);
1126
1127
0
  if (trailer_offset < BLOSC_EXTENDED_HEADER_LENGTH || trailer_offset + FRAME_TRAILER_MINLEN > frame->len) {
1128
0
    BLOSC_TRACE_ERROR("Cannot read the trailer out of the frame.");
1129
0
    return NULL;
1130
0
  }
1131
1132
0
  int32_t coffsets_cbytes;
1133
0
  if (frame->sframe) {
1134
0
    coffsets_cbytes = (int32_t)(trailer_offset - (header_len + 0));
1135
0
  }
1136
0
  else {
1137
0
    coffsets_cbytes = (int32_t)(trailer_offset - (header_len + cbytes));
1138
0
  }
1139
1140
0
  if (off_cbytes != NULL) {
1141
0
    *off_cbytes = coffsets_cbytes;
1142
0
  }
1143
1144
0
  blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
1145
0
  if (io_cb == NULL) {
1146
0
    BLOSC_TRACE_ERROR("Error getting the input/output API");
1147
0
    return NULL;
1148
0
  }
1149
1150
0
  void* fp = NULL;
1151
0
  uint8_t* coffsets;
1152
0
  if (io_cb->is_allocation_necessary) {
1153
0
    coffsets = malloc((size_t)coffsets_cbytes);
1154
0
    frame->coffsets_needs_free = true;
1155
0
  }
1156
0
  else {
1157
0
    frame->coffsets_needs_free = false;
1158
0
  }
1159
  
1160
0
  int64_t io_pos = 0;
1161
0
  if (frame->sframe) {
1162
0
    fp = sframe_open_index(frame->urlpath, "rb",
1163
0
                           frame->schunk->storage->io);
1164
0
    if (fp == NULL) {
1165
0
      BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
1166
0
      return NULL;
1167
0
    }
1168
0
    io_pos = header_len + 0;
1169
0
  }
1170
0
  else {
1171
0
    fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params);
1172
0
    if (fp == NULL) {
1173
0
      BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
1174
0
      return NULL;
1175
0
    }
1176
0
    io_pos = frame->file_offset + header_len + cbytes;
1177
0
  }
1178
0
  int64_t rbytes = io_cb->read((void**)&coffsets, 1, coffsets_cbytes, io_pos, fp);
1179
0
  io_cb->close(fp);
1180
0
  if (rbytes != coffsets_cbytes) {
1181
0
    BLOSC_TRACE_ERROR("Cannot read the offsets out of the frame.");
1182
0
    if (frame->coffsets_needs_free)
1183
0
      free(coffsets);
1184
0
    return NULL;
1185
0
  }
1186
0
  frame->coffsets = coffsets;
1187
0
  return coffsets;
1188
0
}
1189
1190
1191
// Get the data offsets from a frame
1192
0
int64_t* blosc2_frame_get_offsets(blosc2_schunk *schunk) {
1193
0
  if (schunk->frame == NULL) {
1194
0
    BLOSC_TRACE_ERROR("This function needs a frame.");
1195
0
    return NULL;
1196
0
  }
1197
0
  blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame;
1198
1199
  // Get header info
1200
0
  int32_t header_len;
1201
0
  int64_t frame_len;
1202
0
  int64_t nbytes;
1203
0
  int64_t cbytes;
1204
0
  int32_t blocksize;
1205
0
  int32_t chunksize;
1206
0
  int64_t nchunks;
1207
0
  int ret = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes,
1208
0
                            &blocksize, &chunksize, &nchunks,
1209
0
                            NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1210
0
                            frame->schunk->storage->io);
1211
0
  if (ret < 0) {
1212
0
    BLOSC_TRACE_ERROR("Cannot get the header info for the frame.");
1213
0
    return NULL;
1214
0
  }
1215
1216
0
  int32_t off_nbytes = (int32_t) (nchunks * sizeof(int64_t));
1217
0
  int64_t* offsets = (int64_t *) malloc((size_t)off_nbytes);
1218
1219
0
  int32_t coffsets_cbytes = 0;
1220
0
  uint8_t *coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &coffsets_cbytes);
1221
  // Decompress offsets
1222
0
  blosc2_dparams off_dparams = BLOSC2_DPARAMS_DEFAULTS;
1223
0
  blosc2_context *dctx = blosc2_create_dctx(off_dparams);
1224
0
  if (dctx == NULL) {
1225
0
    BLOSC_TRACE_ERROR("Error while creating the decompression context");
1226
0
    return NULL;
1227
0
  }
1228
0
  int32_t prev_nbytes = blosc2_decompress_ctx(dctx, coffsets, coffsets_cbytes,
1229
0
                                              offsets, off_nbytes);
1230
0
  blosc2_free_ctx(dctx);
1231
0
  if (prev_nbytes < 0) {
1232
0
    free(offsets);
1233
0
    BLOSC_TRACE_ERROR("Cannot decompress the offsets chunk.");
1234
0
    return NULL;
1235
0
  }
1236
0
  return offsets;
1237
0
}
1238
1239
1240
0
int frame_update_header(blosc2_frame_s* frame, blosc2_schunk* schunk, bool new) {
1241
0
  uint8_t* framep = frame->cframe;
1242
0
  uint8_t* header_ptr;
1243
0
  uint8_t header[FRAME_HEADER_MINLEN];
1244
1245
0
  if (frame->len <= 0) {
1246
0
    return BLOSC2_ERROR_INVALID_PARAM;
1247
0
  }
1248
1249
0
  if (new && schunk->cbytes > 0) {
1250
0
    BLOSC_TRACE_ERROR("New metalayers cannot be added after actual data "
1251
0
                      "has been appended.");
1252
0
    return BLOSC2_ERROR_INVALID_PARAM;
1253
0
  }
1254
1255
0
  blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
1256
0
  if (io_cb == NULL) {
1257
0
    BLOSC_TRACE_ERROR("Error getting the input/output API");
1258
0
    return BLOSC2_ERROR_PLUGIN_IO;
1259
0
  }
1260
1261
0
  if (frame->cframe == NULL) {
1262
0
    int64_t rbytes = 0;
1263
0
    void* fp = NULL;
1264
0
    int64_t io_pos = 0;
1265
0
    if (frame->sframe) {
1266
0
      fp = sframe_open_index(frame->urlpath, "rb+",
1267
0
                             frame->schunk->storage->io);
1268
0
      if (fp == NULL) {
1269
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
1270
0
        return BLOSC2_ERROR_FILE_OPEN;
1271
0
      }
1272
0
    }
1273
0
    else {
1274
0
      fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params);
1275
0
      if (fp == NULL) {
1276
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
1277
0
        return BLOSC2_ERROR_FILE_OPEN;
1278
0
      }
1279
0
      io_pos = frame->file_offset;
1280
0
    }
1281
0
    if (fp != NULL) {
1282
0
      if (io_cb->is_allocation_necessary)
1283
0
        header_ptr = header;
1284
0
      rbytes = io_cb->read((void**)&header_ptr, 1, FRAME_HEADER_MINLEN, io_pos, fp);
1285
0
      io_cb->close(fp);
1286
0
    }
1287
0
    (void) rbytes;
1288
0
    if (rbytes != FRAME_HEADER_MINLEN) {
1289
0
      return BLOSC2_ERROR_FILE_WRITE;
1290
0
    }
1291
0
    framep = header_ptr;
1292
0
  }
1293
0
  uint32_t prev_h2len;
1294
0
  from_big(&prev_h2len, framep + FRAME_HEADER_LEN, sizeof(prev_h2len));
1295
1296
  // Build a new header
1297
0
  uint8_t* h2 = new_header_frame(schunk, frame);
1298
0
  uint32_t h2len;
1299
0
  from_big(&h2len, h2 + FRAME_HEADER_LEN, sizeof(h2len));
1300
1301
  // The frame length is outdated when adding a new metalayer, so update it
1302
0
  if (new) {
1303
0
    int64_t frame_len = h2len;  // at adding time, we only have to worry of the header for now
1304
0
    to_big(h2 + FRAME_LEN, &frame_len, sizeof(frame_len));
1305
0
    frame->len = frame_len;
1306
0
  }
1307
1308
0
  if (!new && prev_h2len != h2len) {
1309
0
    BLOSC_TRACE_ERROR("The new metalayer sizes should be equal the existing ones.");
1310
0
    return BLOSC2_ERROR_DATA;
1311
0
  }
1312
1313
0
  void* fp = NULL;
1314
0
  if (frame->cframe == NULL) {
1315
    // Write updated header down to file
1316
0
    if (frame->sframe) {
1317
0
      fp = sframe_open_index(frame->urlpath, "rb+",
1318
0
                             frame->schunk->storage->io);
1319
0
    }
1320
0
    else {
1321
0
      fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params);
1322
0
    }
1323
0
    if (fp == NULL) {
1324
0
      BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
1325
0
      return BLOSC2_ERROR_FILE_OPEN;
1326
0
    }
1327
0
    int64_t io_pos = frame->file_offset;
1328
0
    io_cb->write(h2, h2len, 1, io_pos, fp);
1329
0
    io_cb->close(fp);
1330
0
  }
1331
0
  else {
1332
0
    if (new) {
1333
0
      frame->cframe = realloc(frame->cframe, h2len);
1334
0
    }
1335
0
    memcpy(frame->cframe, h2, h2len);
1336
0
  }
1337
0
  free(h2);
1338
1339
0
  return 1;
1340
0
}
1341
1342
1343
static int get_meta_from_header(blosc2_frame_s* frame, blosc2_schunk* schunk, uint8_t* header,
1344
0
                                int32_t header_len) {
1345
0
  BLOSC_UNUSED_PARAM(frame);
1346
0
  int64_t header_pos = FRAME_IDX_SIZE;
1347
1348
  // Get the size for the index of metalayers
1349
0
  uint16_t idx_size;
1350
0
  header_pos += sizeof(idx_size);
1351
0
  if (header_len < header_pos) {
1352
0
    return BLOSC2_ERROR_READ_BUFFER;
1353
0
  }
1354
0
  from_big(&idx_size, header + FRAME_IDX_SIZE, sizeof(idx_size));
1355
1356
  // Get the actual index of metalayers
1357
0
  uint8_t* metalayers_idx = header + FRAME_IDX_SIZE + 2;
1358
0
  header_pos += 1;
1359
0
  if (header_len < header_pos) {
1360
0
    return BLOSC2_ERROR_READ_BUFFER;
1361
0
  }
1362
0
  if (metalayers_idx[0] != 0xde) {   // sanity check
1363
0
    return BLOSC2_ERROR_DATA;
1364
0
  }
1365
0
  uint8_t* idxp = metalayers_idx + 1;
1366
0
  uint16_t nmetalayers;
1367
0
  header_pos += sizeof(nmetalayers);
1368
0
  if (header_len < header_pos) {
1369
0
    return BLOSC2_ERROR_READ_BUFFER;
1370
0
  }
1371
0
  from_big(&nmetalayers, idxp, sizeof(uint16_t));
1372
0
  idxp += 2;
1373
0
  if (nmetalayers > BLOSC2_MAX_METALAYERS) {
1374
0
    return BLOSC2_ERROR_DATA;
1375
0
  }
1376
0
  schunk->nmetalayers = nmetalayers;
1377
1378
  // Populate the metalayers and its serialized values
1379
0
  for (int nmetalayer = 0; nmetalayer < nmetalayers; nmetalayer++) {
1380
0
    header_pos += 1;
1381
0
    if (header_len < header_pos) {
1382
0
      return BLOSC2_ERROR_READ_BUFFER;
1383
0
    }
1384
0
    if ((*idxp & 0xe0u) != 0xa0u) {   // sanity check
1385
0
      return BLOSC2_ERROR_DATA;
1386
0
    }
1387
0
    blosc2_metalayer* metalayer = calloc(1, sizeof(blosc2_metalayer));
1388
0
    schunk->metalayers[nmetalayer] = metalayer;
1389
1390
    // Populate the metalayer string
1391
0
    uint8_t nslen = *idxp & (uint8_t)0x1F;
1392
0
    idxp += 1;
1393
0
    header_pos += nslen;
1394
0
    if (header_len < header_pos) {
1395
0
      return BLOSC2_ERROR_READ_BUFFER;
1396
0
    }
1397
0
    char* ns = malloc((size_t)nslen + 1);
1398
0
    memcpy(ns, idxp, nslen);
1399
0
    ns[nslen] = '\0';
1400
0
    idxp += nslen;
1401
0
    metalayer->name = ns;
1402
1403
    // Populate the serialized value for this metalayer
1404
    // Get the offset
1405
0
    header_pos += 1;
1406
0
    if (header_len < header_pos) {
1407
0
      return BLOSC2_ERROR_READ_BUFFER;
1408
0
    }
1409
0
    if ((*idxp & 0xffu) != 0xd2u) {   // sanity check
1410
0
      return BLOSC2_ERROR_DATA;
1411
0
    }
1412
0
    idxp += 1;
1413
0
    int32_t offset;
1414
0
    header_pos += sizeof(offset);
1415
0
    if (header_len < header_pos) {
1416
0
      return BLOSC2_ERROR_READ_BUFFER;
1417
0
    }
1418
0
    from_big(&offset, idxp, sizeof(offset));
1419
0
    idxp += 4;
1420
0
    if (offset < 0 || offset >= header_len) {
1421
      // Offset is less than zero or exceeds header length
1422
0
      return BLOSC2_ERROR_DATA;
1423
0
    }
1424
    // Go to offset and see if we have the correct marker
1425
0
    uint8_t* content_marker = header + offset;
1426
0
    if (header_len < offset + 1 + 4) {
1427
0
      return BLOSC2_ERROR_READ_BUFFER;
1428
0
    }
1429
0
    if (*content_marker != 0xc6) {
1430
0
      return BLOSC2_ERROR_DATA;
1431
0
    }
1432
1433
    // Read the size of the content
1434
0
    int32_t content_len;
1435
0
    from_big(&content_len, content_marker + 1, sizeof(content_len));
1436
0
    if (content_len < 0) {
1437
0
      return BLOSC2_ERROR_DATA;
1438
0
    }
1439
0
    metalayer->content_len = content_len;
1440
1441
    // Finally, read the content
1442
0
    if (header_len < offset + 1 + 4 + content_len) {
1443
0
      return BLOSC2_ERROR_READ_BUFFER;
1444
0
    }
1445
0
    char* content = malloc((size_t)content_len);
1446
0
    memcpy(content, content_marker + 1 + 4, (size_t)content_len);
1447
0
    metalayer->content = (uint8_t*)content;
1448
0
  }
1449
1450
0
  return 1;
1451
0
}
1452
1453
0
int frame_get_metalayers(blosc2_frame_s* frame, blosc2_schunk* schunk) {
1454
0
  int32_t header_len;
1455
0
  int64_t frame_len;
1456
0
  int64_t nbytes;
1457
0
  int64_t cbytes;
1458
0
  int32_t blocksize;
1459
0
  int32_t chunksize;
1460
0
  int64_t nchunks;
1461
0
  int ret = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes,
1462
0
                            &blocksize, &chunksize, &nchunks,
1463
0
                            NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1464
0
                            schunk->storage->io);
1465
0
  if (ret < 0) {
1466
0
    BLOSC_TRACE_ERROR("Unable to get the header info from frame.");
1467
0
    return ret;
1468
0
  }
1469
1470
  // Get the header
1471
0
  uint8_t* header = NULL;
1472
0
  bool needs_free = false;
1473
0
  if (frame->cframe != NULL) {
1474
0
    header = frame->cframe;
1475
0
  } else {
1476
0
    int64_t rbytes = 0;
1477
0
    blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
1478
0
    if (io_cb == NULL) {
1479
0
      BLOSC_TRACE_ERROR("Error getting the input/output API");
1480
0
      return BLOSC2_ERROR_PLUGIN_IO;
1481
0
    }
1482
1483
0
    if (io_cb->is_allocation_necessary) {
1484
0
      header = malloc(header_len);
1485
0
      needs_free = true;
1486
0
    }
1487
0
    else {
1488
0
      needs_free = false;
1489
0
    }
1490
1491
0
    void* fp = NULL;
1492
0
    int64_t io_pos = 0;
1493
0
    if (frame->sframe) {
1494
0
      fp = sframe_open_index(frame->urlpath, "rb",
1495
0
                             frame->schunk->storage->io);
1496
0
      if (fp == NULL) {
1497
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
1498
0
        return BLOSC2_ERROR_FILE_OPEN;
1499
0
      }
1500
0
    }
1501
0
    else {
1502
0
      fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params);
1503
0
      if (fp == NULL) {
1504
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
1505
0
        return BLOSC2_ERROR_FILE_OPEN;
1506
0
      }
1507
0
      io_pos = frame->file_offset;
1508
0
    }
1509
0
    if (fp != NULL) {
1510
0
      rbytes = io_cb->read((void**)&header, 1, header_len, io_pos, fp);
1511
0
      io_cb->close(fp);
1512
0
    }
1513
0
    if (rbytes != header_len) {
1514
0
      BLOSC_TRACE_ERROR("Cannot access the header out of the frame.");
1515
0
      if (needs_free)
1516
0
        free(header);
1517
0
      return BLOSC2_ERROR_FILE_READ;
1518
0
    }
1519
0
  }
1520
1521
0
  ret = get_meta_from_header(frame, schunk, header, header_len);
1522
1523
0
  if (frame->cframe == NULL && needs_free) {
1524
0
    free(header);
1525
0
  }
1526
1527
0
  return ret;
1528
0
}
1529
1530
static int get_vlmeta_from_trailer(blosc2_frame_s* frame, blosc2_schunk* schunk, uint8_t* trailer,
1531
0
                                   int32_t trailer_len) {
1532
1533
0
  BLOSC_UNUSED_PARAM(frame);
1534
0
  int64_t trailer_pos = FRAME_TRAILER_VLMETALAYERS + 2;
1535
0
  uint8_t* idxp = trailer + trailer_pos;
1536
1537
  // Get the size for the index of metalayers
1538
0
  trailer_pos += 2;
1539
0
  if (trailer_len < trailer_pos) {
1540
0
    return BLOSC2_ERROR_READ_BUFFER;
1541
0
  }
1542
0
  uint16_t idx_size;
1543
0
  from_big(&idx_size, idxp, sizeof(idx_size));
1544
0
  idxp += 2;
1545
1546
0
  trailer_pos += 1;
1547
  // Get the actual index of metalayers
1548
0
  if (trailer_len < trailer_pos) {
1549
0
    return BLOSC2_ERROR_READ_BUFFER;
1550
0
  }
1551
0
  if (idxp[0] != 0xde) {   // sanity check
1552
0
    return BLOSC2_ERROR_DATA;
1553
0
  }
1554
0
  idxp += 1;
1555
1556
0
  int16_t nmetalayers;
1557
0
  trailer_pos += sizeof(nmetalayers);
1558
0
  if (trailer_len < trailer_pos) {
1559
0
    return BLOSC2_ERROR_READ_BUFFER;
1560
0
  }
1561
0
  from_big(&nmetalayers, idxp, sizeof(uint16_t));
1562
0
  idxp += 2;
1563
0
  if (nmetalayers > BLOSC2_MAX_VLMETALAYERS) {
1564
0
    return BLOSC2_ERROR_DATA;
1565
0
  }
1566
0
  schunk->nvlmetalayers = nmetalayers;
1567
1568
  // Populate the metalayers and its serialized values
1569
0
  for (int nmetalayer = 0; nmetalayer < nmetalayers; nmetalayer++) {
1570
0
    trailer_pos += 1;
1571
0
    if (trailer_len < trailer_pos) {
1572
0
      return BLOSC2_ERROR_READ_BUFFER;
1573
0
    }
1574
0
    if ((*idxp & 0xe0u) != 0xa0u) {   // sanity check
1575
0
      return BLOSC2_ERROR_DATA;
1576
0
    }
1577
0
    blosc2_metalayer* metalayer = calloc(1, sizeof(blosc2_metalayer));
1578
0
    schunk->vlmetalayers[nmetalayer] = metalayer;
1579
1580
    // Populate the metalayer string
1581
0
    uint8_t nslen = *idxp & (uint8_t)0x1F;
1582
0
    idxp += 1;
1583
0
    trailer_pos += nslen;
1584
0
    if (trailer_len < trailer_pos) {
1585
0
      return BLOSC2_ERROR_READ_BUFFER;
1586
0
    }
1587
0
    char* ns = malloc((size_t)nslen + 1);
1588
0
    memcpy(ns, idxp, nslen);
1589
0
    ns[nslen] = '\0';
1590
0
    idxp += nslen;
1591
0
    metalayer->name = ns;
1592
1593
    // Populate the serialized value for this metalayer
1594
    // Get the offset
1595
0
    trailer_pos += 1;
1596
0
    if (trailer_len < trailer_pos) {
1597
0
      return BLOSC2_ERROR_READ_BUFFER;
1598
0
    }
1599
0
    if ((*idxp & 0xffu) != 0xd2u) {   // sanity check
1600
0
      return BLOSC2_ERROR_DATA;
1601
0
    }
1602
0
    idxp += 1;
1603
0
    int32_t offset;
1604
0
    trailer_pos += sizeof(offset);
1605
0
    if (trailer_len < trailer_pos) {
1606
0
      return BLOSC2_ERROR_READ_BUFFER;
1607
0
    }
1608
0
    from_big(&offset, idxp, sizeof(offset));
1609
0
    idxp += 4;
1610
0
    if (offset < 0 || offset >= trailer_len) {
1611
      // Offset is less than zero or exceeds trailer length
1612
0
      return BLOSC2_ERROR_DATA;
1613
0
    }
1614
    // Go to offset and see if we have the correct marker
1615
0
    uint8_t* content_marker = trailer + offset;
1616
0
    if (trailer_len < offset + 1 + 4) {
1617
0
      return BLOSC2_ERROR_READ_BUFFER;
1618
0
    }
1619
0
    if (*content_marker != 0xc6) {
1620
0
      return BLOSC2_ERROR_DATA;
1621
0
    }
1622
1623
    // Read the size of the content
1624
0
    int32_t content_len;
1625
0
    from_big(&content_len, content_marker + 1, sizeof(content_len));
1626
0
    if (content_len < 0) {
1627
0
      return BLOSC2_ERROR_DATA;
1628
0
    }
1629
0
    metalayer->content_len = content_len;
1630
1631
    // Finally, read the content
1632
0
    if (trailer_len < offset + 1 + 4 + content_len) {
1633
0
      return BLOSC2_ERROR_READ_BUFFER;
1634
0
    }
1635
0
    char* content = malloc((size_t)content_len);
1636
0
    memcpy(content, content_marker + 1 + 4, (size_t)content_len);
1637
0
    metalayer->content = (uint8_t*)content;
1638
0
  }
1639
0
  return 1;
1640
0
}
1641
1642
0
int frame_get_vlmetalayers(blosc2_frame_s* frame, blosc2_schunk* schunk) {
1643
0
  int32_t header_len;
1644
0
  int64_t frame_len;
1645
0
  int64_t nbytes;
1646
0
  int64_t cbytes;
1647
0
  int32_t blocksize;
1648
0
  int32_t chunksize;
1649
0
  int64_t nchunks;
1650
0
  int ret = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes,
1651
0
                            &blocksize, &chunksize, &nchunks,
1652
0
                            NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1653
0
                            schunk->storage->io);
1654
0
  if (ret < 0) {
1655
0
    BLOSC_TRACE_ERROR("Unable to get the trailer info from frame.");
1656
0
    return ret;
1657
0
  }
1658
1659
0
  int64_t trailer_offset = get_trailer_offset(frame, header_len, nbytes > 0);
1660
0
  int32_t trailer_len = (int32_t) frame->trailer_len;
1661
1662
0
  if (trailer_offset < BLOSC_EXTENDED_HEADER_LENGTH || trailer_offset + trailer_len > frame->len) {
1663
0
    BLOSC_TRACE_ERROR("Cannot access the trailer out of the frame.");
1664
0
    return BLOSC2_ERROR_READ_BUFFER;
1665
0
  }
1666
1667
  // Get the trailer
1668
0
  uint8_t* trailer = NULL;
1669
0
  bool needs_free = false;
1670
0
  if (frame->cframe != NULL) {
1671
0
    trailer = frame->cframe + trailer_offset;
1672
0
  } else {
1673
0
    int64_t rbytes = 0;
1674
1675
0
    blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
1676
0
    if (io_cb == NULL) {
1677
0
      BLOSC_TRACE_ERROR("Error getting the input/output API");
1678
0
      return BLOSC2_ERROR_PLUGIN_IO;
1679
0
    }
1680
1681
0
    if (io_cb->is_allocation_necessary) {
1682
0
      trailer = malloc(trailer_len);
1683
0
      needs_free = true;
1684
0
    }
1685
0
    else {
1686
0
      needs_free = false;
1687
0
    }
1688
1689
0
    void* fp = NULL;
1690
0
    int64_t io_pos = 0;
1691
0
    if (frame->sframe) {
1692
0
      char* eframe_name = malloc(strlen(frame->urlpath) + strlen("/chunks.b2frame") + 1);
1693
0
      sprintf(eframe_name, "%s/chunks.b2frame", frame->urlpath);
1694
0
      fp = io_cb->open(eframe_name, "rb", frame->schunk->storage->io->params);
1695
0
      if (fp == NULL) {
1696
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", eframe_name);
1697
0
        free(eframe_name);
1698
0
        return BLOSC2_ERROR_FILE_OPEN;
1699
0
      }
1700
0
      free(eframe_name);
1701
0
      io_pos = trailer_offset;
1702
0
    }
1703
0
    else {
1704
0
      fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params);
1705
0
      if (fp == NULL) {
1706
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
1707
0
        return BLOSC2_ERROR_FILE_OPEN;
1708
0
      }
1709
0
      io_pos = frame->file_offset + trailer_offset;
1710
0
    }
1711
0
    if (fp != NULL) {
1712
0
      rbytes = io_cb->read((void**)&trailer, 1, trailer_len, io_pos, fp);
1713
0
      io_cb->close(fp);
1714
0
    }
1715
0
    if (rbytes != trailer_len) {
1716
0
      BLOSC_TRACE_ERROR("Cannot access the trailer out of the fileframe.");
1717
0
      if (needs_free)
1718
0
        free(trailer);
1719
0
      return BLOSC2_ERROR_FILE_READ;
1720
0
    }
1721
0
  }
1722
1723
0
  ret = get_vlmeta_from_trailer(frame, schunk, trailer, trailer_len);
1724
1725
0
  if (frame->cframe == NULL && needs_free) {
1726
0
    free(trailer);
1727
0
  }
1728
1729
0
  return ret;
1730
0
}
1731
1732
1733
blosc2_storage* get_new_storage(const blosc2_storage* storage,
1734
                                const blosc2_cparams* cdefaults,
1735
                                const blosc2_dparams* ddefaults,
1736
0
                                const blosc2_io* iodefaults) {
1737
1738
0
  blosc2_storage* new_storage = (blosc2_storage*)calloc(1, sizeof(blosc2_storage));
1739
0
  memcpy(new_storage, storage, sizeof(blosc2_storage));
1740
0
  if (storage->urlpath != NULL) {
1741
0
    char* urlpath = normalize_urlpath(storage->urlpath);
1742
0
    new_storage->urlpath = malloc(strlen(urlpath) + 1);
1743
0
    strcpy(new_storage->urlpath, urlpath);
1744
0
  }
1745
1746
  // cparams
1747
0
  blosc2_cparams* cparams = malloc(sizeof(blosc2_cparams));
1748
0
  if (storage->cparams != NULL) {
1749
0
    memcpy(cparams, storage->cparams, sizeof(blosc2_cparams));
1750
0
  } else {
1751
0
    memcpy(cparams, cdefaults, sizeof(blosc2_cparams));
1752
0
  }
1753
0
  new_storage->cparams = cparams;
1754
1755
  // dparams
1756
0
  blosc2_dparams* dparams = malloc(sizeof(blosc2_dparams));
1757
0
  if (storage->dparams != NULL) {
1758
0
    memcpy(dparams, storage->dparams, sizeof(blosc2_dparams));
1759
0
  }
1760
0
  else {
1761
0
    memcpy(dparams, ddefaults, sizeof(blosc2_dparams));
1762
0
  }
1763
0
  new_storage->dparams = dparams;
1764
1765
  // iodefaults
1766
0
  blosc2_io* udio = malloc(sizeof(blosc2_io));
1767
0
  if (storage->io != NULL) {
1768
0
    memcpy(udio, storage->io, sizeof(blosc2_io));
1769
0
  }
1770
0
  else {
1771
0
    memcpy(udio, iodefaults, sizeof(blosc2_io));
1772
0
  }
1773
0
  new_storage->io = udio;
1774
1775
0
  return new_storage;
1776
0
}
1777
1778
1779
/* Get a super-chunk out of a frame */
1780
0
blosc2_schunk* frame_to_schunk(blosc2_frame_s* frame, bool copy, const blosc2_io *udio) {
1781
0
  int32_t header_len;
1782
0
  int64_t frame_len;
1783
0
  int rc;
1784
0
  blosc2_schunk* schunk = calloc(1, sizeof(blosc2_schunk));
1785
0
  schunk->frame = (blosc2_frame*)frame;
1786
0
  frame->schunk = schunk;
1787
1788
0
  rc = get_header_info(frame, &header_len, &frame_len, &schunk->nbytes,
1789
0
                       &schunk->cbytes, &schunk->blocksize,
1790
0
                       &schunk->chunksize, &schunk->nchunks, &schunk->typesize,
1791
0
                       &schunk->compcode, &schunk->compcode_meta, &schunk->clevel, schunk->filters,
1792
0
                       schunk->filters_meta, &schunk->splitmode, udio);
1793
0
  if (rc < 0) {
1794
0
    BLOSC_TRACE_ERROR("Unable to get meta info from frame.");
1795
0
    blosc2_schunk_free(schunk);
1796
0
    return NULL;
1797
0
  }
1798
0
  int64_t nchunks = schunk->nchunks;
1799
0
  int64_t nbytes = schunk->nbytes;
1800
0
  (void) nbytes;
1801
0
  int64_t cbytes = schunk->cbytes;
1802
1803
  // Compression and decompression contexts
1804
0
  blosc2_cparams *cparams;
1805
0
  blosc2_schunk_get_cparams(schunk, &cparams);
1806
0
  schunk->cctx = blosc2_create_cctx(*cparams);
1807
0
  if (schunk->cctx == NULL) {
1808
0
    BLOSC_TRACE_ERROR("Error while creating the compression context");
1809
0
    return NULL;
1810
0
  }
1811
0
  blosc2_dparams *dparams;
1812
0
  blosc2_schunk_get_dparams(schunk, &dparams);
1813
0
  schunk->dctx = blosc2_create_dctx(*dparams);
1814
0
  if (schunk->dctx == NULL) {
1815
0
    BLOSC_TRACE_ERROR("Error while creating the decompression context");
1816
0
    return NULL;
1817
0
  }
1818
0
  blosc2_storage storage = {.contiguous = copy ? false : true};
1819
0
  schunk->storage = get_new_storage(&storage, cparams, dparams, udio);
1820
0
  free(cparams);
1821
0
  free(dparams);
1822
0
  if (!copy) {
1823
0
    goto out;
1824
0
  }
1825
1826
  // We are not attached to a frame anymore
1827
0
  schunk->frame = NULL;
1828
1829
0
  if (nchunks == 0) {
1830
0
    frame->schunk = NULL;
1831
0
    goto out;
1832
0
  }
1833
1834
  // Get the compressed offsets
1835
0
  int32_t coffsets_cbytes = 0;
1836
0
  uint8_t* coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &coffsets_cbytes);
1837
0
  if (coffsets == NULL) {
1838
0
    blosc2_schunk_free(schunk);
1839
0
    BLOSC_TRACE_ERROR("Cannot get the offsets for the frame.");
1840
0
    return NULL;
1841
0
  }
1842
1843
  // Decompress offsets
1844
0
  blosc2_dparams off_dparams = BLOSC2_DPARAMS_DEFAULTS;
1845
0
  blosc2_context *dctx = blosc2_create_dctx(off_dparams);
1846
0
  if (dctx == NULL) {
1847
0
    BLOSC_TRACE_ERROR("Error while creating the decompression context");
1848
0
    return NULL;
1849
0
  }
1850
0
  int64_t* offsets = (int64_t *) malloc((size_t)nchunks * sizeof(int64_t));
1851
0
  int32_t off_nbytes = blosc2_decompress_ctx(dctx, coffsets, coffsets_cbytes,
1852
0
                                             offsets, (int32_t)(nchunks * sizeof(int64_t)));
1853
0
  blosc2_free_ctx(dctx);
1854
0
  if (off_nbytes < 0) {
1855
0
    free(offsets);
1856
0
    blosc2_schunk_free(schunk);
1857
0
    BLOSC_TRACE_ERROR("Cannot decompress the offsets chunk.");
1858
0
    return NULL;
1859
0
  }
1860
1861
  // We want the contiguous schunk, so create the actual data chunks (and, while doing this,
1862
  // get a guess at the blocksize used in this frame)
1863
0
  int64_t acc_nbytes = 0;
1864
0
  int64_t acc_cbytes = 0;
1865
0
  int32_t blocksize = 0;
1866
0
  int32_t chunk_nbytes;
1867
0
  int32_t chunk_cbytes;
1868
0
  int32_t chunk_blocksize;
1869
0
  size_t prev_alloc = BLOSC_EXTENDED_HEADER_LENGTH;
1870
0
  uint8_t* data_chunk = NULL;
1871
0
  bool needs_free = false;
1872
0
  const blosc2_io_cb *io_cb = blosc2_get_io_cb(udio->id);
1873
0
  if (io_cb == NULL) {
1874
0
    blosc2_schunk_free(schunk);
1875
0
    BLOSC_TRACE_ERROR("Error getting the input/output API");
1876
0
    return NULL;
1877
0
  }
1878
1879
0
  void* fp = NULL;
1880
0
  if (frame->cframe == NULL) {
1881
0
    if (io_cb->is_allocation_necessary) {
1882
0
      data_chunk = malloc((size_t)prev_alloc);
1883
0
      needs_free = true;
1884
0
    }
1885
0
    else {
1886
0
      needs_free = false;
1887
0
    }
1888
    
1889
0
    if (!frame->sframe) {
1890
      // If not the chunks won't be in the frame
1891
0
      fp = io_cb->open(frame->urlpath, "rb", udio->params);
1892
0
      if (fp == NULL) {
1893
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
1894
0
        rc = BLOSC2_ERROR_FILE_OPEN;
1895
0
        goto end;
1896
0
      }
1897
0
    }
1898
0
  }
1899
0
  schunk->data = malloc(nchunks * sizeof(void*));
1900
0
  for (int i = 0; i < nchunks; i++) {
1901
0
    if (frame->cframe != NULL) {
1902
0
      if (needs_free) {
1903
0
        free(data_chunk);
1904
0
      }
1905
0
      if (offsets[i] < 0) {
1906
0
        int64_t rbytes = frame_get_chunk(frame, i, &data_chunk, &needs_free);
1907
0
        if (rbytes < 0) {
1908
0
          break;
1909
0
        }
1910
0
      }
1911
0
      else {
1912
0
       data_chunk = frame->cframe + header_len + offsets[i];
1913
0
      }
1914
0
      rc = blosc2_cbuffer_sizes(data_chunk, NULL, &chunk_cbytes, NULL);
1915
0
      if (rc < 0) {
1916
0
        break;
1917
0
      }
1918
0
    }
1919
0
    else {
1920
0
      int64_t rbytes;
1921
0
      if (frame->sframe) {
1922
0
        if (needs_free) {
1923
0
          free(data_chunk);
1924
0
        }
1925
0
        rbytes = frame_get_chunk(frame, i, &data_chunk, &needs_free);
1926
0
        if (rbytes < 0) {
1927
0
          break;
1928
0
        }
1929
0
      }
1930
0
      else {
1931
0
        int64_t io_pos = frame->file_offset + header_len + offsets[i];
1932
0
        rbytes = io_cb->read((void**)&data_chunk, 1, BLOSC_EXTENDED_HEADER_LENGTH, io_pos, fp);
1933
0
      }
1934
0
      if (rbytes != BLOSC_EXTENDED_HEADER_LENGTH) {
1935
0
        rc = BLOSC2_ERROR_READ_BUFFER;
1936
0
        break;
1937
0
      }
1938
0
      rc = blosc2_cbuffer_sizes(data_chunk, NULL, &chunk_cbytes, NULL);
1939
0
      if (rc < 0) {
1940
0
        break;
1941
0
      }
1942
0
      if (chunk_cbytes > (int32_t)prev_alloc) {
1943
0
        if (io_cb->is_allocation_necessary)
1944
0
          data_chunk = realloc(data_chunk, chunk_cbytes);
1945
0
        if (data_chunk == NULL) {
1946
0
          BLOSC_TRACE_ERROR("Cannot realloc space for the data_chunk.");
1947
0
          rc = BLOSC2_ERROR_MEMORY_ALLOC;
1948
0
          break;
1949
0
        }
1950
0
        prev_alloc = chunk_cbytes;
1951
0
      }
1952
0
      if (!frame->sframe) {
1953
0
        int64_t io_pos = frame->file_offset + header_len + offsets[i];
1954
0
        rbytes = io_cb->read((void**)&data_chunk, 1, chunk_cbytes, io_pos, fp);
1955
0
        if (rbytes != chunk_cbytes) {
1956
0
          rc = BLOSC2_ERROR_READ_BUFFER;
1957
0
          break;
1958
0
        }
1959
0
      }
1960
0
    }
1961
0
    uint8_t* new_chunk = malloc(chunk_cbytes);
1962
0
    memcpy(new_chunk, data_chunk, chunk_cbytes);
1963
0
    schunk->data[i] = new_chunk;
1964
0
    rc = blosc2_cbuffer_sizes(data_chunk, &chunk_nbytes, NULL, &chunk_blocksize);
1965
0
    if (rc < 0) {
1966
0
      break;
1967
0
    }
1968
0
    acc_nbytes += chunk_nbytes;
1969
0
    acc_cbytes += chunk_cbytes;
1970
0
    if (i == 0) {
1971
0
      blocksize = chunk_blocksize;
1972
0
    }
1973
0
    else if (blocksize != chunk_blocksize) {
1974
      // Blocksize varies
1975
0
      blocksize = 0;
1976
0
    }
1977
0
  }
1978
1979
  // We are not attached to a schunk anymore
1980
0
  frame->schunk = NULL;
1981
1982
0
  end:
1983
0
  if (needs_free) {
1984
0
    free(data_chunk);
1985
0
  }
1986
0
  if (frame->cframe == NULL) {
1987
0
    if (!frame->sframe) {
1988
0
      io_cb->close(fp);
1989
0
    }
1990
0
  }
1991
0
  free(offsets);
1992
1993
  // cframes and sframes have different ways to store chunks with special values:
1994
  // 1) cframes represent special chunks as negative offsets
1995
  // 2) sframes does not have the concept of offsets, but rather of data pointers (.data)
1996
  //    so they always have a pointer to a special chunk
1997
  // This is why cframes and sframes have different cbytes and hence, we cannot enforce acc_bytes == schunk->cbytes
1998
  // In the future, maybe we could provide special meanings for .data[i] > 0x7FFFFFFF, but not there yet
1999
  // if (rc < 0 || acc_nbytes != nbytes || acc_cbytes != cbytes) {
2000
0
  if (rc < 0 || acc_nbytes != nbytes) {
2001
0
    blosc2_schunk_free(schunk);
2002
0
    return NULL;
2003
0
  }
2004
  // Update counters
2005
0
  schunk->cbytes = acc_cbytes;
2006
0
  schunk->blocksize = blocksize;
2007
2008
0
  out:
2009
0
  rc = frame_get_metalayers(frame, schunk);
2010
0
  if (rc < 0) {
2011
0
    blosc2_schunk_free(schunk);
2012
0
    BLOSC_TRACE_ERROR("Cannot access the metalayers.");
2013
0
    return NULL;
2014
0
  }
2015
2016
0
  rc = frame_get_vlmetalayers(frame, schunk);
2017
0
  if (rc < 0) {
2018
0
    blosc2_schunk_free(schunk);
2019
0
    BLOSC_TRACE_ERROR("Cannot access the vlmetalayers.");
2020
0
    return NULL;
2021
0
  }
2022
2023
0
  return schunk;
2024
0
}
2025
2026
2027
0
void frame_avoid_cframe_free(blosc2_frame_s* frame, bool avoid_cframe_free) {
2028
0
  frame->avoid_cframe_free = avoid_cframe_free;
2029
0
}
2030
2031
2032
struct csize_idx {
2033
    int32_t val;
2034
    int32_t idx;
2035
};
2036
2037
// Helper function for qsorting block offsets
2038
0
int sort_offset(const void* a, const void* b) {
2039
0
  int32_t a_ = ((struct csize_idx*)a)->val;
2040
0
  int32_t b_ = ((struct csize_idx*)b)->val;
2041
0
  return a_ - b_;
2042
0
}
2043
2044
2045
int get_coffset(blosc2_frame_s* frame, int32_t header_len, int64_t cbytes,
2046
0
                int64_t nchunk, int64_t nchunks, int64_t *offset) {
2047
0
  int32_t off_cbytes;
2048
  // Get the offset to nchunk
2049
0
  uint8_t *coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &off_cbytes);
2050
0
  if (coffsets == NULL) {
2051
0
    BLOSC_TRACE_ERROR("Cannot get the offset for chunk %" PRId64 " for the frame.", nchunk);
2052
0
    return BLOSC2_ERROR_DATA;
2053
0
  }
2054
2055
  // Get the 64-bit offset
2056
0
  int rc = blosc2_getitem(coffsets, off_cbytes, (int32_t)nchunk, 1, offset, (int32_t)sizeof(int64_t));
2057
0
  if (rc < 0) {
2058
0
    BLOSC_TRACE_ERROR("Problems retrieving a chunk offset.");
2059
0
  } else if (!frame->sframe && *offset > frame->len) {
2060
0
    BLOSC_TRACE_ERROR("Cannot read chunk %" PRId64 " outside of frame boundary.", nchunk);
2061
0
    rc = BLOSC2_ERROR_READ_BUFFER;
2062
0
  }
2063
2064
0
  return rc;
2065
0
}
2066
2067
2068
// Detect and return a chunk with special values in offsets (only zeros, NaNs and non initialized)
2069
int frame_special_chunk(int64_t special_value, int32_t nbytes, int32_t typesize, int32_t blocksize,
2070
0
                        uint8_t** chunk, int32_t cbytes, bool *needs_free) {
2071
0
  int rc = 0;
2072
0
  *chunk = malloc(cbytes);
2073
0
  *needs_free = true;
2074
2075
  // Detect the kind of special value
2076
0
  uint64_t zeros_mask = (uint64_t) BLOSC2_SPECIAL_ZERO << (8 * 7);  // chunk of zeros
2077
0
  uint64_t nans_mask = (uint64_t) BLOSC2_SPECIAL_NAN << (8 * 7);  // chunk of NaNs
2078
0
  uint64_t uninit_mask = (uint64_t) BLOSC2_SPECIAL_UNINIT << (8 * 7);  // chunk of uninit values
2079
2080
0
  blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS;
2081
0
  cparams.typesize = typesize;
2082
0
  cparams.blocksize = blocksize;
2083
0
  if (special_value & zeros_mask) {
2084
0
    rc = blosc2_chunk_zeros(cparams, nbytes, *chunk, cbytes);
2085
0
    if (rc < 0) {
2086
0
      BLOSC_TRACE_ERROR("Error creating a zero chunk");
2087
0
    }
2088
0
  }
2089
0
  else if (special_value & uninit_mask) {
2090
0
    rc = blosc2_chunk_uninit(cparams, nbytes, *chunk, cbytes);
2091
0
    if (rc < 0) {
2092
0
      BLOSC_TRACE_ERROR("Error creating a non initialized chunk");
2093
0
    }
2094
0
  }
2095
0
  else if (special_value & nans_mask) {
2096
0
    rc = blosc2_chunk_nans(cparams, nbytes, *chunk, cbytes);
2097
0
    if (rc < 0) {
2098
0
      BLOSC_TRACE_ERROR("Error creating a nan chunk");
2099
0
    }
2100
0
  }
2101
0
  else {
2102
0
    BLOSC_TRACE_ERROR("Special value not recognized: %" PRId64 "", special_value);
2103
0
    rc = BLOSC2_ERROR_DATA;
2104
0
  }
2105
2106
0
  if (rc < 0) {
2107
0
    free(*chunk);
2108
0
    *needs_free = false;
2109
0
    *chunk = NULL;
2110
0
  }
2111
2112
0
  return rc;
2113
0
}
2114
2115
2116
/* Return a compressed chunk that is part of a frame in the `chunk` parameter.
2117
 * If the frame is disk-based, a buffer is allocated for the (compressed) chunk,
2118
 * and hence a free is needed.  You can check if the chunk requires a free with the `needs_free`
2119
 * parameter.
2120
 * If the chunk does not need a free, it means that a pointer to the location in frame is returned
2121
 * in the `chunk` parameter.
2122
 *
2123
 * The size of the (compressed) chunk is returned.  If some problem is detected, a negative code
2124
 * is returned instead.
2125
*/
2126
0
int frame_get_chunk(blosc2_frame_s *frame, int64_t nchunk, uint8_t **chunk, bool *needs_free) {
2127
0
  int32_t header_len;
2128
0
  int64_t frame_len;
2129
0
  int64_t nbytes;
2130
0
  int64_t cbytes;
2131
0
  int32_t blocksize;
2132
0
  int32_t chunksize;
2133
0
  int64_t nchunks;
2134
0
  int32_t typesize;
2135
0
  int64_t offset;
2136
0
  int32_t chunk_cbytes;
2137
0
  int rc;
2138
2139
0
  *chunk = NULL;
2140
0
  *needs_free = false;
2141
0
  rc = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes,
2142
0
                       &blocksize, &chunksize, &nchunks,
2143
0
                       &typesize, NULL, NULL, NULL, NULL, NULL, NULL,
2144
0
                       frame->schunk->storage->io);
2145
0
  if (rc < 0) {
2146
0
    BLOSC_TRACE_ERROR("Unable to get meta info from frame.");
2147
0
    return rc;
2148
0
  }
2149
2150
0
  if ((nchunks > 0) && (nchunk >= nchunks)) {
2151
0
    BLOSC_TRACE_ERROR("nchunk ('%" PRId64 "') exceeds the number of chunks "
2152
0
                    "('%" PRId64 "') in frame.", nchunk, nchunks);
2153
0
    return BLOSC2_ERROR_INVALID_PARAM;
2154
0
  }
2155
2156
  // Get the offset to nchunk
2157
0
  rc = get_coffset(frame, header_len, cbytes, nchunk, nchunks, &offset);
2158
0
  if (rc < 0) {
2159
0
    BLOSC_TRACE_ERROR("Unable to get offset to chunk %" PRId64 ".", nchunk);
2160
0
    return rc;
2161
0
  }
2162
2163
0
  if (offset < 0) {
2164
    // Special value
2165
0
    chunk_cbytes = BLOSC_EXTENDED_HEADER_LENGTH;
2166
0
    int32_t chunksize_ = chunksize;
2167
0
    if ((nchunk == nchunks - 1) && (nbytes % chunksize)) {
2168
      // Last chunk is incomplete.  Compute its actual size.
2169
0
      chunksize_ = (int32_t) (nbytes % chunksize);
2170
0
    }
2171
0
    rc = frame_special_chunk(offset, chunksize_, typesize, blocksize, chunk, chunk_cbytes, needs_free);
2172
0
    if (rc < 0) {
2173
0
      return rc;
2174
0
    }
2175
0
    goto end;
2176
0
  }
2177
2178
0
  if (frame->sframe) {
2179
    // Sparse on-disk
2180
0
    nchunk = offset;
2181
0
    return sframe_get_chunk(frame, nchunk, chunk, needs_free);
2182
0
  }
2183
2184
0
  blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
2185
0
  if (io_cb == NULL) {
2186
0
    BLOSC_TRACE_ERROR("Error getting the input/output API");
2187
0
    return BLOSC2_ERROR_PLUGIN_IO;
2188
0
  }
2189
2190
0
  if (frame->cframe == NULL) {
2191
0
    uint8_t* header_ptr;
2192
0
    uint8_t header[BLOSC_EXTENDED_HEADER_LENGTH];
2193
0
    void* fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params);
2194
0
    if (fp == NULL) {
2195
0
      BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
2196
0
      return BLOSC2_ERROR_FILE_OPEN;
2197
0
    }
2198
0
    if (io_cb->is_allocation_necessary)
2199
0
      header_ptr = header;
2200
0
    int64_t io_pos = frame->file_offset + header_len + offset;
2201
0
    int64_t rbytes = io_cb->read((void**)&header_ptr, 1, sizeof(header), io_pos, fp);
2202
0
    if (rbytes != BLOSC_EXTENDED_HEADER_LENGTH) {
2203
0
      BLOSC_TRACE_ERROR("Cannot read the cbytes for chunk in the frame.");
2204
0
      io_cb->close(fp);
2205
0
      return BLOSC2_ERROR_FILE_READ;
2206
0
    }
2207
0
    rc = blosc2_cbuffer_sizes(header_ptr, NULL, &chunk_cbytes, NULL);
2208
0
    if (rc < 0) {
2209
0
      BLOSC_TRACE_ERROR("Cannot read the cbytes for chunk in the frame.");
2210
0
      io_cb->close(fp);
2211
0
      return rc;
2212
0
    }
2213
0
    if (io_cb->is_allocation_necessary) {
2214
0
      *chunk = malloc(chunk_cbytes);
2215
0
      *needs_free = true;
2216
0
    }
2217
0
    else {
2218
0
      *needs_free = false;
2219
0
    }
2220
    
2221
0
    io_pos = frame->file_offset + header_len + offset;
2222
0
    rbytes = io_cb->read((void**)chunk, 1, chunk_cbytes, io_pos, fp);
2223
0
    io_cb->close(fp);
2224
0
    if (rbytes != chunk_cbytes) {
2225
0
      BLOSC_TRACE_ERROR("Cannot read the chunk out of the frame.");
2226
0
      return BLOSC2_ERROR_FILE_READ;
2227
0
    }
2228
2229
0
  } else {
2230
    // The chunk is in memory and just one pointer away
2231
0
    *chunk = frame->cframe + header_len + offset;
2232
0
    rc = blosc2_cbuffer_sizes(*chunk, NULL, &chunk_cbytes, NULL);
2233
0
    if (rc < 0) {
2234
0
      return rc;
2235
0
    }
2236
0
  }
2237
2238
0
  end:
2239
0
  return (int32_t)chunk_cbytes;
2240
0
}
2241
2242
2243
/* Return a compressed chunk that is part of a frame in the `chunk` parameter.
2244
 * If the frame is disk-based, a buffer is allocated for the (lazy) chunk,
2245
 * and hence a free is needed.  You can check if the chunk requires a free with the `needs_free`
2246
 * parameter.
2247
 * If the chunk does not need a free, it means that the frame is in memory and that just a
2248
 * pointer to the location of the chunk in memory is returned.
2249
 *
2250
 * The size of the (compressed, potentially lazy) chunk is returned.  If some problem is detected,
2251
 * a negative code is returned instead.
2252
*/
2253
0
int frame_get_lazychunk(blosc2_frame_s *frame, int64_t nchunk, uint8_t **chunk, bool *needs_free) {
2254
0
  int32_t header_len;
2255
0
  int64_t frame_len;
2256
0
  int64_t nbytes;
2257
0
  int64_t cbytes;
2258
0
  int32_t blocksize;
2259
0
  int32_t chunksize;
2260
0
  int64_t nchunks;
2261
0
  int32_t typesize;
2262
0
  int32_t lazychunk_cbytes;
2263
0
  int64_t offset;
2264
0
  void* fp = NULL;
2265
2266
0
  *chunk = NULL;
2267
0
  *needs_free = false;
2268
0
  int rc = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes,
2269
0
                           &blocksize, &chunksize, &nchunks,
2270
0
                           &typesize, NULL, NULL, NULL, NULL, NULL, NULL,
2271
0
                           frame->schunk->storage->io);
2272
0
  if (rc < 0) {
2273
0
    BLOSC_TRACE_ERROR("Unable to get meta info from frame.");
2274
0
    return rc;
2275
0
  }
2276
2277
0
  if (nchunk >= nchunks) {
2278
0
    BLOSC_TRACE_ERROR("nchunk ('%" PRId64 "') exceeds the number of chunks "
2279
0
                      "('%" PRId64 "') in frame.", nchunk, nchunks);
2280
0
    return BLOSC2_ERROR_INVALID_PARAM;
2281
0
  }
2282
2283
  // Get the offset to nchunk
2284
0
  rc = get_coffset(frame, header_len, cbytes, nchunk, nchunks, &offset);
2285
0
  if (rc < 0) {
2286
0
    BLOSC_TRACE_ERROR("Unable to get offset to chunk %" PRId64 ".", nchunk);
2287
0
    return rc;
2288
0
  }
2289
2290
0
  if (offset < 0) {
2291
    // Special value
2292
0
    lazychunk_cbytes = BLOSC_EXTENDED_HEADER_LENGTH;
2293
0
    int32_t chunksize_ = chunksize;
2294
0
    if ((nchunk == nchunks - 1) && (nbytes % chunksize)) {
2295
      // Last chunk is incomplete.  Compute its actual size.
2296
0
      chunksize_ = (int32_t) (nbytes % chunksize);
2297
0
    }
2298
0
    rc = frame_special_chunk(offset, chunksize_, typesize, blocksize, chunk,
2299
0
                             (int32_t)lazychunk_cbytes, needs_free);
2300
0
    goto end;
2301
0
  }
2302
2303
0
  blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
2304
0
  if (io_cb == NULL) {
2305
0
    BLOSC_TRACE_ERROR("Error getting the input/output API");
2306
0
    rc = BLOSC2_ERROR_PLUGIN_IO;
2307
0
    goto end;
2308
0
  }
2309
2310
0
  if (frame->cframe == NULL) {
2311
    // TODO: make this portable across different endianness
2312
    // Get info for building a lazy chunk
2313
0
    int32_t chunk_nbytes;
2314
0
    int32_t chunk_cbytes;
2315
0
    int32_t chunk_blocksize;
2316
0
    uint8_t* header_ptr;
2317
0
    uint8_t header[BLOSC_EXTENDED_HEADER_LENGTH];
2318
0
    int64_t io_pos = 0;
2319
0
    if (frame->sframe) {
2320
      // The chunk is not in the frame
2321
0
      fp = sframe_open_chunk(frame->urlpath, offset, "rb",
2322
0
                             frame->schunk->storage->io);
2323
0
      if (fp == NULL) {
2324
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
2325
0
        return BLOSC2_ERROR_FILE_OPEN;
2326
0
      }
2327
0
    }
2328
0
    else {
2329
0
      fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params);
2330
0
      if (fp == NULL) {
2331
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
2332
0
        return BLOSC2_ERROR_FILE_OPEN;
2333
0
      }
2334
0
      io_pos = frame->file_offset + header_len + offset;
2335
0
    }
2336
0
    if (io_cb->is_allocation_necessary)
2337
0
      header_ptr = header;
2338
0
    int64_t rbytes = io_cb->read((void**)&header_ptr, 1, BLOSC_EXTENDED_HEADER_LENGTH, io_pos, fp);
2339
0
    if (rbytes != BLOSC_EXTENDED_HEADER_LENGTH) {
2340
0
      BLOSC_TRACE_ERROR("Cannot read the header for chunk in the frame.");
2341
0
      rc = BLOSC2_ERROR_FILE_READ;
2342
0
      goto end;
2343
0
    }
2344
0
    rc = blosc2_cbuffer_sizes(header_ptr, &chunk_nbytes, &chunk_cbytes, &chunk_blocksize);
2345
0
    if (rc < 0) {
2346
0
      goto end;
2347
0
    }
2348
0
    size_t nblocks = chunk_nbytes / chunk_blocksize;
2349
0
    size_t leftover_block = chunk_nbytes % chunk_blocksize;
2350
0
    nblocks = leftover_block ? nblocks + 1 : nblocks;
2351
    // Allocate space for the lazy chunk
2352
0
    int32_t trailer_len;
2353
0
    int32_t special_type = (header_ptr[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK;
2354
0
    int memcpyed = header_ptr[BLOSC2_CHUNK_FLAGS] & (uint8_t) BLOSC_MEMCPYED;
2355
2356
0
    int32_t trailer_offset = BLOSC_EXTENDED_HEADER_LENGTH;
2357
0
    size_t streams_offset = BLOSC_EXTENDED_HEADER_LENGTH;
2358
0
    if (special_type == 0) {
2359
      // Regular values have offsets for blocks
2360
0
      trailer_offset += (int32_t) (nblocks * sizeof(int32_t));
2361
0
      if (memcpyed) {
2362
0
        streams_offset += 0;
2363
0
      } else {
2364
0
        streams_offset += nblocks * sizeof(int32_t);
2365
0
      }
2366
0
      trailer_len = (int32_t) (sizeof(int32_t) + sizeof(int64_t) + nblocks * sizeof(int32_t));
2367
0
      lazychunk_cbytes = trailer_offset + trailer_len;
2368
0
    }
2369
0
    else if (special_type == BLOSC2_SPECIAL_VALUE) {
2370
0
      trailer_offset += typesize;
2371
0
      streams_offset += typesize;
2372
0
      trailer_len = 0;
2373
0
      lazychunk_cbytes = trailer_offset + trailer_len;
2374
0
    }
2375
0
    else {
2376
0
      rc = BLOSC2_ERROR_INVALID_HEADER;
2377
0
      goto end;
2378
0
    }
2379
2380
    // Read just the full header and bstarts section too (lazy partial length)
2381
0
    if (frame->sframe) {
2382
0
      io_pos = 0;
2383
0
    }
2384
0
    else {
2385
0
      io_pos = frame->file_offset + header_len + offset;
2386
0
    }
2387
2388
    // The case here is a bit special because more memory is allocated than read from the file
2389
    // and the chunk is modified after reading. Due to the modification, we cannot directly use
2390
    // the memory provided by the io
2391
0
    *chunk = malloc(lazychunk_cbytes);
2392
0
    *needs_free = true;
2393
2394
0
    if (io_cb->is_allocation_necessary) {
2395
0
      rbytes = io_cb->read((void**)chunk, 1, (int64_t)streams_offset, io_pos, fp);
2396
0
    }
2397
0
    else {
2398
0
      uint8_t* chunk_ptr;
2399
0
      rbytes = io_cb->read((void**)&chunk_ptr, 1, (int64_t)streams_offset, io_pos, fp);
2400
0
      memcpy(*chunk, chunk_ptr, streams_offset);
2401
0
    }
2402
    
2403
0
    if (rbytes != (int64_t)streams_offset) {
2404
0
      BLOSC_TRACE_ERROR("Cannot read the (lazy) chunk out of the frame.");
2405
0
      rc = BLOSC2_ERROR_FILE_READ;
2406
0
      goto end;
2407
0
    }
2408
0
    if (special_type == BLOSC2_SPECIAL_VALUE) {
2409
      // Value runlen is not returning a lazy chunk.  We are done.
2410
0
      goto end;
2411
0
    }
2412
2413
    // Mark chunk as lazy
2414
0
    uint8_t* blosc2_flags = *chunk + BLOSC2_CHUNK_BLOSC2_FLAGS;
2415
0
    *blosc2_flags |= 0x08U;
2416
2417
    // Add the trailer (currently, nchunk + offset + block_csizes)
2418
0
    if (frame->sframe) {
2419
0
      *(int32_t*)(*chunk + trailer_offset) = (int32_t)offset;   // offset is nchunk for sframes
2420
0
      *(int64_t*)(*chunk + trailer_offset + sizeof(int32_t)) = offset;
2421
0
    }
2422
0
    else {
2423
0
      *(int32_t*)(*chunk + trailer_offset) = (int32_t)nchunk;
2424
0
      *(int64_t*)(*chunk + trailer_offset + sizeof(int32_t)) = header_len + offset;
2425
0
    }
2426
2427
0
    int32_t* block_csizes = malloc(nblocks * sizeof(int32_t));
2428
2429
0
    if (memcpyed) {
2430
      // When memcpyed the blocksizes are trivial to compute
2431
0
      for (int i = 0; i < (int)nblocks - 1; i++) {
2432
0
        block_csizes[i] = (int)chunk_blocksize;
2433
0
      }
2434
      // The last block could be incomplete, mainly due to the fact that the block size is not divisible
2435
      // by the typesize
2436
0
      block_csizes[nblocks - 1] = (int32_t)leftover_block ? (int32_t)leftover_block : chunk_blocksize;
2437
0
    }
2438
0
    else {
2439
      // In regular, compressed chunks, we need to sort the bstarts (they can be out
2440
      // of order because of multi-threading), and get a reverse index too.
2441
0
      memcpy(block_csizes, *chunk + BLOSC_EXTENDED_HEADER_LENGTH, nblocks * sizeof(int32_t));
2442
      // Helper structure to keep track of original indexes
2443
0
      struct csize_idx *csize_idx = malloc(nblocks * sizeof(struct csize_idx));
2444
0
      for (int n = 0; n < (int)nblocks; n++) {
2445
0
        csize_idx[n].val = block_csizes[n];
2446
0
        csize_idx[n].idx = n;
2447
0
      }
2448
0
      qsort(csize_idx, nblocks, sizeof(struct csize_idx), &sort_offset);
2449
      // Compute the actual csizes
2450
0
      int idx;
2451
0
      for (int n = 0; n < (int)nblocks - 1; n++) {
2452
0
        idx = csize_idx[n].idx;
2453
0
        block_csizes[idx] = csize_idx[n + 1].val - csize_idx[n].val;
2454
0
      }
2455
0
      idx = csize_idx[nblocks - 1].idx;
2456
0
      block_csizes[idx] = (int)chunk_cbytes - csize_idx[nblocks - 1].val;
2457
0
      free(csize_idx);
2458
0
    }
2459
    // Copy the csizes at the end of the trailer
2460
0
    void *trailer_csizes = *chunk + lazychunk_cbytes - nblocks * sizeof(int32_t);
2461
0
    memcpy(trailer_csizes, block_csizes, nblocks * sizeof(int32_t));
2462
0
    free(block_csizes);
2463
0
  } else {
2464
    // The chunk is in memory and just one pointer away
2465
0
    int64_t chunk_header_offset = header_len + offset;
2466
0
    int64_t chunk_cbytes_offset = chunk_header_offset + BLOSC_MIN_HEADER_LENGTH;
2467
2468
0
    *chunk = frame->cframe + chunk_header_offset;
2469
2470
0
    if (chunk_cbytes_offset > frame->len) {
2471
0
      BLOSC_TRACE_ERROR("Cannot read the header for chunk in the (contiguous) frame.");
2472
0
      rc = BLOSC2_ERROR_READ_BUFFER;
2473
0
    } else {
2474
0
      rc = blosc2_cbuffer_sizes(*chunk, NULL, &lazychunk_cbytes, NULL);
2475
0
      if (rc && chunk_cbytes_offset + lazychunk_cbytes > frame_len) {
2476
0
        BLOSC_TRACE_ERROR("Compressed bytes exceed beyond frame length.");
2477
0
        rc = BLOSC2_ERROR_READ_BUFFER;
2478
0
      }
2479
0
    }
2480
0
  }
2481
2482
0
  end:
2483
0
  if (fp != NULL) {
2484
0
    io_cb->close(fp);
2485
0
  }
2486
0
  if (rc < 0) {
2487
0
    if (*needs_free) {
2488
0
      free(*chunk);
2489
0
      *chunk = NULL;
2490
0
      *needs_free = false;
2491
0
    }
2492
0
    return rc;
2493
0
  }
2494
2495
0
  return (int)lazychunk_cbytes;
2496
0
}
2497
2498
2499
/* Fill an empty frame with special values (fast path). */
2500
int64_t frame_fill_special(blosc2_frame_s* frame, int64_t nitems, int special_value,
2501
0
                       int32_t chunksize, blosc2_schunk* schunk) {
2502
0
  int32_t header_len;
2503
0
  int64_t frame_len;
2504
0
  int64_t nbytes;
2505
0
  int64_t cbytes;
2506
0
  int32_t blocksize;
2507
0
  int32_t typesize;
2508
0
  int64_t nchunks;
2509
2510
0
  int rc = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes, &blocksize, NULL,
2511
0
                           &nchunks, &typesize, NULL, NULL, NULL, NULL, NULL, NULL,
2512
0
                           schunk->storage->io);
2513
0
  if (rc < 0) {
2514
0
    BLOSC_TRACE_ERROR("Unable to get meta info from frame.");
2515
0
    return BLOSC2_ERROR_DATA;
2516
0
  }
2517
2518
0
  if (nitems == 0) {
2519
0
    return frame_len;
2520
0
  }
2521
2522
0
  if ((nitems / chunksize) > INT_MAX) {
2523
0
    BLOSC_TRACE_ERROR("nitems is too large.  Try increasing the chunksize.");
2524
0
    return BLOSC2_ERROR_FRAME_SPECIAL;
2525
0
  }
2526
2527
0
  if ((nbytes > 0) || (cbytes > 0)) {
2528
0
    BLOSC_TRACE_ERROR("Filling with special values only works on empty frames");
2529
0
    return BLOSC2_ERROR_FRAME_SPECIAL;
2530
0
  }
2531
2532
  // Compute the number of chunks and the length of the offsets chunk
2533
0
  int32_t chunkitems = chunksize / typesize;
2534
0
  nchunks = nitems / chunkitems;
2535
0
  int32_t leftover_items = (int32_t)(nitems % chunkitems);
2536
0
  if (leftover_items) {
2537
0
    nchunks += 1;
2538
0
  }
2539
2540
0
  blosc2_cparams* cparams;
2541
0
  blosc2_schunk_get_cparams(schunk, &cparams);
2542
2543
  // Build the offsets with a special chunk
2544
0
  int new_off_cbytes = BLOSC_EXTENDED_HEADER_LENGTH + sizeof(int64_t);
2545
0
  uint8_t* off_chunk = malloc(new_off_cbytes);
2546
0
  uint64_t offset_value = ((uint64_t)1 << 63);
2547
0
  uint8_t* sample_chunk = malloc(BLOSC_EXTENDED_HEADER_LENGTH);
2548
0
  int csize;
2549
0
  switch (special_value) {
2550
0
    case BLOSC2_SPECIAL_ZERO:
2551
0
      offset_value += (uint64_t) BLOSC2_SPECIAL_ZERO << (8 * 7);
2552
0
      csize = blosc2_chunk_zeros(*cparams, chunksize, sample_chunk, BLOSC_EXTENDED_HEADER_LENGTH);
2553
0
      break;
2554
0
    case BLOSC2_SPECIAL_UNINIT:
2555
0
      offset_value += (uint64_t) BLOSC2_SPECIAL_UNINIT << (8 * 7);
2556
0
      csize = blosc2_chunk_uninit(*cparams, chunksize, sample_chunk, BLOSC_EXTENDED_HEADER_LENGTH);
2557
0
      break;
2558
0
    case BLOSC2_SPECIAL_NAN:
2559
0
      offset_value += (uint64_t)BLOSC2_SPECIAL_NAN << (8 * 7);
2560
0
      csize = blosc2_chunk_nans(*cparams, chunksize, sample_chunk, BLOSC_EXTENDED_HEADER_LENGTH);
2561
0
      break;
2562
0
    default:
2563
0
      BLOSC_TRACE_ERROR("Only zeros, NaNs or non-initialized values are supported.");
2564
0
      return BLOSC2_ERROR_FRAME_SPECIAL;
2565
0
  }
2566
0
  if (csize < 0) {
2567
0
    BLOSC_TRACE_ERROR("Error creating sample chunk");
2568
0
    return BLOSC2_ERROR_FRAME_SPECIAL;
2569
0
  }
2570
0
  cparams->typesize = sizeof(int64_t);  // change it to offsets typesize
2571
  // cparams->blocksize = 0;   // automatic blocksize
2572
0
  cparams->blocksize = 8 * 2 * 1024;  // based on experiments with create_frame.c bench
2573
0
  cparams->clevel = 5;
2574
0
  cparams->compcode = BLOSC_BLOSCLZ;
2575
0
  int32_t special_nbytes = (int32_t) (nchunks * sizeof(int64_t));
2576
0
  rc = blosc2_chunk_repeatval(*cparams, special_nbytes, off_chunk, new_off_cbytes, &offset_value);
2577
0
  free(cparams);
2578
0
  if (rc < 0) {
2579
0
    BLOSC_TRACE_ERROR("Error creating a special offsets chunk");
2580
0
    return BLOSC2_ERROR_DATA;
2581
0
  }
2582
2583
  // Get the blocksize associated to the sample chunk
2584
0
  blosc2_cbuffer_sizes(sample_chunk, NULL, NULL, &blocksize);
2585
0
  free(sample_chunk);
2586
  // and use it for the super-chunk
2587
0
  schunk->blocksize = blocksize;
2588
  // schunk->blocksize = 0;  // for experimenting with automatic blocksize
2589
2590
  // We have the new offsets; update the frame.
2591
0
  blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
2592
0
  if (io_cb == NULL) {
2593
0
    BLOSC_TRACE_ERROR("Error getting the input/output API");
2594
0
    return BLOSC2_ERROR_PLUGIN_IO;
2595
0
  }
2596
2597
0
  int64_t new_frame_len = header_len + new_off_cbytes + frame->trailer_len;
2598
0
  void* fp = NULL;
2599
0
  if (frame->cframe != NULL) {
2600
0
    uint8_t* framep = frame->cframe;
2601
    /* Make space for the new chunk and copy it */
2602
0
    frame->cframe = framep = realloc(framep, (size_t)new_frame_len);
2603
0
    if (framep == NULL) {
2604
0
      BLOSC_TRACE_ERROR("Cannot realloc space for the frame.");
2605
0
      return BLOSC2_ERROR_FRAME_SPECIAL;
2606
0
    }
2607
    /* Copy the offsets */
2608
0
    memcpy(framep + header_len, off_chunk, (size_t)new_off_cbytes);
2609
0
  }
2610
0
  else {
2611
0
    size_t wbytes;
2612
0
    int64_t io_pos = 0;
2613
0
    if (frame->sframe) {
2614
      // Update the offsets chunk in the chunks frame
2615
0
      fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io);
2616
0
      if (fp == NULL) {
2617
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
2618
0
        return BLOSC2_ERROR_FILE_OPEN;
2619
0
      }
2620
0
      io_pos = frame->file_offset + header_len;
2621
0
    }
2622
0
    else {
2623
      // Regular frame
2624
0
      fp = io_cb->open(frame->urlpath, "rb+", schunk->storage->io->params);
2625
0
      if (fp == NULL) {
2626
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
2627
0
        return BLOSC2_ERROR_FILE_OPEN;
2628
0
      }
2629
0
      io_pos = frame->file_offset + header_len + cbytes;
2630
0
    }
2631
0
    wbytes = io_cb->write(off_chunk, 1, new_off_cbytes, io_pos, fp);  // the new offsets
2632
0
    io_cb->close(fp);
2633
0
    if (wbytes != (size_t)new_off_cbytes) {
2634
0
      BLOSC_TRACE_ERROR("Cannot write the offsets to frame.");
2635
0
      return BLOSC2_ERROR_FRAME_SPECIAL;
2636
0
    }
2637
0
  }
2638
2639
  // Invalidate the cache for chunk offsets
2640
0
  if (frame->coffsets != NULL) {
2641
0
    if (frame->coffsets_needs_free)
2642
0
      free(frame->coffsets);
2643
0
    frame->coffsets = NULL;
2644
0
  }
2645
0
  free(off_chunk);
2646
2647
0
  frame->len = new_frame_len;
2648
0
  rc = frame_update_header(frame, schunk, false);
2649
0
  if (rc < 0) {
2650
0
    return BLOSC2_ERROR_FRAME_SPECIAL;
2651
0
  }
2652
2653
0
  rc = frame_update_trailer(frame, schunk);
2654
0
  if (rc < 0) {
2655
0
    return BLOSC2_ERROR_FRAME_SPECIAL;
2656
0
  }
2657
2658
0
  return frame->len;
2659
0
}
2660
2661
2662
/* Append an existing chunk into a frame. */
2663
0
void* frame_append_chunk(blosc2_frame_s* frame, void* chunk, blosc2_schunk* schunk) {
2664
0
  int8_t* chunk_ = chunk;
2665
0
  int32_t header_len;
2666
0
  int64_t frame_len;
2667
0
  int64_t nbytes;
2668
0
  int64_t cbytes;
2669
0
  int32_t blocksize;
2670
0
  int32_t chunksize;
2671
0
  int64_t nchunks;
2672
0
  int rc = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes, &blocksize, &chunksize,
2673
0
                           &nchunks, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2674
0
                           frame->schunk->storage->io);
2675
0
  if (rc < 0) {
2676
0
    BLOSC_TRACE_ERROR("Unable to get meta info from frame.");
2677
0
    return NULL;
2678
0
  }
2679
2680
  /* The uncompressed and compressed sizes start at byte 4 and 12 */
2681
0
  int32_t chunk_nbytes;
2682
0
  int32_t chunk_cbytes;
2683
0
  rc = blosc2_cbuffer_sizes(chunk, &chunk_nbytes, &chunk_cbytes, NULL);
2684
0
  if (rc < 0) {
2685
0
    return NULL;
2686
0
  }
2687
2688
0
  if ((nchunks > 0) && (chunk_nbytes > chunksize)) {
2689
0
    BLOSC_TRACE_ERROR("Appending chunks with a larger chunksize than frame is "
2690
0
                      "not allowed yet %d != %d.", chunk_nbytes, chunksize);
2691
0
    return NULL;
2692
0
  }
2693
2694
  // Check that we are not appending a small chunk after another small chunk
2695
0
  int32_t chunk_nbytes_last;
2696
0
  if (chunksize == 0 && (nchunks > 0) && (chunk_nbytes < chunksize)) {
2697
0
    uint8_t* last_chunk;
2698
0
    bool needs_free;
2699
0
    rc = frame_get_lazychunk(frame, nchunks - 1, &last_chunk, &needs_free);
2700
0
    if (rc < 0) {
2701
0
      BLOSC_TRACE_ERROR("Cannot get the last chunk (in position %" PRId64 ").", nchunks - 1);
2702
0
    } else {
2703
0
      rc = blosc2_cbuffer_sizes(last_chunk, &chunk_nbytes_last, NULL, NULL);
2704
0
    }
2705
0
    if (needs_free) {
2706
0
      free(last_chunk);
2707
0
    }
2708
0
    if (rc < 0) {
2709
0
      return NULL;
2710
0
    }
2711
0
    if ((chunk_nbytes_last < chunksize) && (nbytes < chunksize)) {
2712
0
      BLOSC_TRACE_ERROR("Appending two consecutive chunks with a chunksize smaller "
2713
0
                        "than the frame chunksize is not allowed yet: %d != %d.",
2714
0
                        chunk_nbytes, chunksize);
2715
0
      return NULL;
2716
0
    }
2717
0
  }
2718
2719
  // Get the current offsets and add one more
2720
0
  int32_t off_nbytes = (int32_t) ((nchunks + 1) * sizeof(int64_t));
2721
0
  int64_t* offsets = (int64_t *) malloc((size_t)off_nbytes);
2722
0
  if (nchunks > 0) {
2723
0
    int32_t coffsets_cbytes;
2724
0
    uint8_t *coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &coffsets_cbytes);
2725
0
    if (coffsets == NULL) {
2726
0
      BLOSC_TRACE_ERROR("Cannot get the offsets for the frame.");
2727
0
      free(offsets);
2728
0
      return NULL;
2729
0
    }
2730
0
    if (coffsets_cbytes == 0) {
2731
0
      coffsets_cbytes = (int32_t)cbytes;
2732
0
    }
2733
2734
    // Decompress offsets
2735
0
    blosc2_dparams off_dparams = BLOSC2_DPARAMS_DEFAULTS;
2736
0
    blosc2_context *dctx = blosc2_create_dctx(off_dparams);
2737
0
    if (dctx == NULL) {
2738
0
      BLOSC_TRACE_ERROR("Error while creating the decompression context");
2739
0
      return NULL;
2740
0
    }
2741
0
    int32_t prev_nbytes = blosc2_decompress_ctx(dctx, coffsets, coffsets_cbytes, offsets,
2742
0
                                                off_nbytes);
2743
0
    blosc2_free_ctx(dctx);
2744
0
    if (prev_nbytes < 0) {
2745
0
      free(offsets);
2746
0
      BLOSC_TRACE_ERROR("Cannot decompress the offsets chunk.");
2747
0
      return NULL;
2748
0
    }
2749
0
  }
2750
2751
  // Add the new offset
2752
0
  int64_t sframe_chunk_id = -1;
2753
0
  int special_value = (chunk_[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK;
2754
0
  uint64_t offset_value = ((uint64_t)1 << 63);
2755
0
  switch (special_value) {
2756
0
    case BLOSC2_SPECIAL_ZERO:
2757
      // Zero chunk.  Code it in a special way.
2758
0
      offset_value += (uint64_t) BLOSC2_SPECIAL_ZERO << (8 * 7);  // chunk of zeros
2759
0
      to_little(offsets + nchunks, &offset_value, sizeof(uint64_t));
2760
0
      chunk_cbytes = 0;   // we don't need to store the chunk
2761
0
      break;
2762
0
    case BLOSC2_SPECIAL_UNINIT:
2763
      // Non initizalized values chunk.  Code it in a special way.
2764
0
      offset_value += (uint64_t) BLOSC2_SPECIAL_UNINIT << (8 * 7);  // chunk of uninit values
2765
0
      to_little(offsets + nchunks, &offset_value, sizeof(uint64_t));
2766
0
      chunk_cbytes = 0;   // we don't need to store the chunk
2767
0
      break;
2768
0
    case BLOSC2_SPECIAL_NAN:
2769
      // NaN chunk.  Code it in a special way.
2770
0
      offset_value += (uint64_t)BLOSC2_SPECIAL_NAN << (8 * 7);  // chunk of NANs
2771
0
      to_little(offsets + nchunks, &offset_value, sizeof(uint64_t));
2772
0
      chunk_cbytes = 0;   // we don't need to store the chunk
2773
0
      break;
2774
0
    default:
2775
0
      if (frame->sframe) {
2776
        // Compute the sframe_chunk_id value
2777
0
        for (int i = 0; i < nchunks; ++i) {
2778
0
          if (offsets[i] > sframe_chunk_id) {
2779
0
            sframe_chunk_id = offsets[i];
2780
0
          }
2781
0
        }
2782
0
        offsets[nchunks] = ++sframe_chunk_id;
2783
0
      }
2784
0
      else {
2785
0
        offsets[nchunks] = cbytes;
2786
0
      }
2787
0
  }
2788
2789
  // Re-compress the offsets again
2790
0
  blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS;
2791
0
  cparams.splitmode = BLOSC_NEVER_SPLIT;
2792
0
  cparams.typesize = sizeof(int64_t);
2793
0
  cparams.blocksize = 16 * 1024;  // based on experiments with create_frame.c bench
2794
0
  cparams.nthreads = 4;  // 4 threads seems a decent default for nowadays CPUs
2795
0
  cparams.compcode = BLOSC_BLOSCLZ;
2796
0
  blosc2_context* cctx = blosc2_create_cctx(cparams);
2797
0
  if (cctx == NULL) {
2798
0
    BLOSC_TRACE_ERROR("Error while creating the compression context");
2799
0
    return NULL;
2800
0
  }
2801
0
  cctx->typesize = sizeof(int64_t);  // override a possible BLOSC_TYPESIZE env variable (or chaos may appear)
2802
0
  void* off_chunk = malloc((size_t)off_nbytes + BLOSC2_MAX_OVERHEAD);
2803
0
  int32_t new_off_cbytes = blosc2_compress_ctx(cctx, offsets, off_nbytes,
2804
0
                                               off_chunk, off_nbytes + BLOSC2_MAX_OVERHEAD);
2805
0
  blosc2_free_ctx(cctx);
2806
0
  free(offsets);
2807
0
  if (new_off_cbytes < 0) {
2808
0
    free(off_chunk);
2809
0
    return NULL;
2810
0
  }
2811
  // printf("%f\n", (double) off_nbytes / new_off_cbytes);
2812
2813
0
  int64_t new_cbytes = cbytes + chunk_cbytes;
2814
0
  int64_t new_frame_len;
2815
0
  if (frame->sframe) {
2816
0
    new_frame_len = header_len + 0 + new_off_cbytes + frame->trailer_len;
2817
0
  }
2818
0
  else {
2819
0
    new_frame_len = header_len + new_cbytes + new_off_cbytes + frame->trailer_len;
2820
0
  }
2821
2822
0
  void* fp = NULL;
2823
0
  if (frame->cframe != NULL) {
2824
0
    uint8_t* framep = frame->cframe;
2825
    /* Make space for the new chunk and copy it */
2826
0
    frame->cframe = framep = realloc(framep, (size_t)new_frame_len);
2827
0
    if (framep == NULL) {
2828
0
      BLOSC_TRACE_ERROR("Cannot realloc space for the frame.");
2829
0
      return NULL;
2830
0
    }
2831
    /* Copy the chunk */
2832
0
    memcpy(framep + header_len + cbytes, chunk, (size_t)chunk_cbytes);
2833
    /* Copy the offsets */
2834
0
    memcpy(framep + header_len + new_cbytes, off_chunk, (size_t)new_off_cbytes);
2835
0
  }
2836
0
  else {
2837
0
    int64_t wbytes;
2838
0
    int64_t io_pos = 0;
2839
0
    blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
2840
0
    if (io_cb == NULL) {
2841
0
      BLOSC_TRACE_ERROR("Error getting the input/output API");
2842
0
      return NULL;
2843
0
    }
2844
2845
0
    if (frame->sframe) {
2846
      // Update the offsets chunk in the chunks frame
2847
0
      if (chunk_cbytes != 0) {
2848
0
        if (sframe_chunk_id < 0) {
2849
0
          BLOSC_TRACE_ERROR("The chunk id (%" PRId64 ") is not correct", sframe_chunk_id);
2850
0
          return NULL;
2851
0
        }
2852
0
        if (sframe_create_chunk(frame, chunk, sframe_chunk_id, chunk_cbytes) == NULL) {
2853
0
          BLOSC_TRACE_ERROR("Cannot write the full chunk.");
2854
0
          return NULL;
2855
0
        }
2856
0
      }
2857
0
      fp = sframe_open_index(frame->urlpath, "rb+",
2858
0
                             frame->schunk->storage->io);
2859
0
      if (fp == NULL) {
2860
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
2861
0
        return NULL;
2862
0
      }
2863
0
      io_pos = frame->file_offset + header_len;
2864
0
    }
2865
0
    else {
2866
      // Regular frame
2867
0
      fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params);
2868
0
      if (fp == NULL) {
2869
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
2870
0
        return NULL;
2871
0
      }
2872
0
      io_pos = frame->file_offset + header_len + cbytes;
2873
0
      wbytes = io_cb->write(chunk, 1, chunk_cbytes, io_pos, fp);  // the new chunk
2874
0
      io_pos += chunk_cbytes;
2875
0
      if (wbytes != chunk_cbytes) {
2876
0
        BLOSC_TRACE_ERROR("Cannot write the full chunk to frame.");
2877
0
        io_cb->close(fp);
2878
0
        return NULL;
2879
0
      }
2880
0
    }
2881
0
    wbytes = io_cb->write(off_chunk, 1, new_off_cbytes, io_pos, fp);  // the new offsets
2882
0
    io_cb->close(fp);
2883
0
    if (wbytes != new_off_cbytes) {
2884
0
      BLOSC_TRACE_ERROR("Cannot write the offsets to frame.");
2885
0
      return NULL;
2886
0
    }
2887
0
  }
2888
  // Invalidate the cache for chunk offsets
2889
0
  if (frame->coffsets != NULL) {
2890
0
    if (frame->coffsets_needs_free)
2891
0
      free(frame->coffsets);
2892
0
    frame->coffsets = NULL;
2893
0
  }
2894
0
  free(chunk);  // chunk has always to be a copy when reaching here...
2895
0
  free(off_chunk);
2896
2897
0
  frame->len = new_frame_len;
2898
0
  rc = frame_update_header(frame, schunk, false);
2899
0
  if (rc < 0) {
2900
0
    return NULL;
2901
0
  }
2902
2903
0
  rc = frame_update_trailer(frame, schunk);
2904
0
  if (rc < 0) {
2905
0
    return NULL;
2906
0
  }
2907
2908
0
  return frame;
2909
0
}
2910
2911
2912
0
void* frame_insert_chunk(blosc2_frame_s* frame, int64_t nchunk, void* chunk, blosc2_schunk* schunk) {
2913
0
  uint8_t* chunk_ = chunk;
2914
0
  int32_t header_len;
2915
0
  int64_t frame_len;
2916
0
  int64_t nbytes;
2917
0
  int64_t cbytes;
2918
0
  int32_t blocksize;
2919
0
  int32_t chunksize;
2920
0
  int64_t nchunks;
2921
0
  int rc = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes,
2922
0
                           &blocksize, &chunksize, &nchunks,
2923
0
                           NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2924
0
                           frame->schunk->storage->io);
2925
0
  if (rc < 0) {
2926
0
    BLOSC_TRACE_ERROR("Unable to get meta info from frame.");
2927
0
    return NULL;
2928
0
  }
2929
0
  int32_t chunk_cbytes;
2930
0
  rc = blosc2_cbuffer_sizes(chunk_, NULL, &chunk_cbytes, NULL);
2931
0
  if (rc < 0) {
2932
0
    return NULL;
2933
0
  }
2934
2935
  // Get the current offsets
2936
0
  int32_t off_nbytes = (int32_t) ((nchunks + 1) * sizeof(int64_t));
2937
0
  int64_t* offsets = (int64_t *) malloc((size_t)off_nbytes);
2938
0
  if (nchunks > 0) {
2939
0
    int32_t coffsets_cbytes = 0;
2940
0
    uint8_t *coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &coffsets_cbytes);
2941
0
    if (coffsets == NULL) {
2942
0
      BLOSC_TRACE_ERROR("Cannot get the offsets for the frame.");
2943
0
      return NULL;
2944
0
    }
2945
0
    if (coffsets_cbytes == 0) {
2946
0
      coffsets_cbytes = (int32_t)cbytes;
2947
0
    }
2948
2949
    // Decompress offsets
2950
0
    blosc2_dparams off_dparams = BLOSC2_DPARAMS_DEFAULTS;
2951
0
    blosc2_context *dctx = blosc2_create_dctx(off_dparams);
2952
0
    if (dctx == NULL) {
2953
0
      BLOSC_TRACE_ERROR("Error while creating the decompression context");
2954
0
      return NULL;
2955
0
    }
2956
0
    int32_t prev_nbytes = blosc2_decompress_ctx(dctx, coffsets, coffsets_cbytes, offsets, off_nbytes);
2957
0
    blosc2_free_ctx(dctx);
2958
0
    if (prev_nbytes < 0) {
2959
0
      free(offsets);
2960
0
      BLOSC_TRACE_ERROR("Cannot decompress the offsets chunk.");
2961
0
      return NULL;
2962
0
    }
2963
0
  }
2964
2965
  // TODO: Improvement: Check if new chunk is smaller than previous one
2966
2967
  // Move offsets
2968
0
  for (int64_t i = nchunks; i > nchunk; i--) {
2969
0
    offsets[i] = offsets[i - 1];
2970
0
  }
2971
  // Add the new offset
2972
0
  int64_t sframe_chunk_id = -1;
2973
0
  int special_value = (chunk_[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK;
2974
0
  uint64_t offset_value = ((uint64_t)1 << 63);
2975
0
  switch (special_value) {
2976
0
    case BLOSC2_SPECIAL_ZERO:
2977
      // Zero chunk.  Code it in a special way.
2978
0
      offset_value += (uint64_t)BLOSC2_SPECIAL_ZERO << (8 * 7);  // indicate a chunk of zeros
2979
0
      to_little(offsets + nchunk, &offset_value, sizeof(uint64_t));
2980
0
      chunk_cbytes = 0;   // we don't need to store the chunk
2981
0
      break;
2982
0
    case BLOSC2_SPECIAL_UNINIT:
2983
      // Non initizalized values chunk.  Code it in a special way.
2984
0
      offset_value += (uint64_t) BLOSC2_SPECIAL_UNINIT << (8 * 7);  // chunk of uninit values
2985
0
      to_little(offsets + nchunk, &offset_value, sizeof(uint64_t));
2986
0
      chunk_cbytes = 0;   // we don't need to store the chunk
2987
0
      break;
2988
0
    case BLOSC2_SPECIAL_NAN:
2989
      // NaN chunk.  Code it in a special way.
2990
0
      offset_value += (uint64_t)BLOSC2_SPECIAL_NAN << (8 * 7);  // indicate a chunk of NANs
2991
0
      to_little(offsets + nchunk, &offset_value, sizeof(uint64_t));
2992
0
      chunk_cbytes = 0;   // we don't need to store the chunk
2993
0
      break;
2994
0
    default:
2995
0
      if (frame->sframe) {
2996
0
        for (int i = 0; i <= nchunks; ++i) {
2997
          // offsets[nchunk] is still uninitialized here
2998
0
          if (i != nchunk && offsets[i] > sframe_chunk_id) {
2999
0
            sframe_chunk_id = offsets[i];
3000
0
          }
3001
0
        }
3002
0
        offsets[nchunk] = ++sframe_chunk_id;
3003
0
      }
3004
0
      else {
3005
0
        offsets[nchunk] = cbytes;
3006
0
      }
3007
0
  }
3008
3009
  // Re-compress the offsets again
3010
0
  blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS;
3011
0
  cparams.splitmode = BLOSC_NEVER_SPLIT;
3012
0
  cparams.typesize = sizeof(int64_t);
3013
0
  cparams.blocksize = 16 * 1024;  // based on experiments with create_frame.c bench
3014
0
  cparams.nthreads = 4;  // 4 threads seems a decent default for nowadays CPUs
3015
0
  cparams.compcode = BLOSC_BLOSCLZ;
3016
0
  blosc2_context* cctx = blosc2_create_cctx(cparams);
3017
0
  if (cctx == NULL) {
3018
0
    BLOSC_TRACE_ERROR("Error while creating the compression context");
3019
0
    return NULL;
3020
0
  }
3021
0
  void* off_chunk = malloc((size_t)off_nbytes + BLOSC2_MAX_OVERHEAD);
3022
0
  int32_t new_off_cbytes = blosc2_compress_ctx(cctx, offsets, off_nbytes,
3023
0
                                               off_chunk, off_nbytes + BLOSC2_MAX_OVERHEAD);
3024
0
  blosc2_free_ctx(cctx);
3025
3026
0
  free(offsets);
3027
0
  if (new_off_cbytes < 0) {
3028
0
    free(off_chunk);
3029
0
    return NULL;
3030
0
  }
3031
3032
0
  int64_t new_cbytes = cbytes + chunk_cbytes;
3033
3034
0
  int64_t new_frame_len;
3035
0
  if (frame->sframe) {
3036
0
    new_frame_len = header_len + 0 + new_off_cbytes + frame->trailer_len;
3037
0
  }
3038
0
  else {
3039
0
    new_frame_len = header_len + new_cbytes + new_off_cbytes + frame->trailer_len;
3040
0
  }
3041
3042
  // Add the chunk and update meta
3043
0
  void* fp = NULL;
3044
0
  if (frame->cframe != NULL) {
3045
0
    uint8_t* framep = frame->cframe;
3046
    /* Make space for the new chunk and copy it */
3047
0
    frame->cframe = framep = realloc(framep, (size_t)new_frame_len);
3048
0
    if (framep == NULL) {
3049
0
      BLOSC_TRACE_ERROR("Cannot realloc space for the frame.");
3050
0
      return NULL;
3051
0
    }
3052
    /* Copy the chunk */
3053
0
    memcpy(framep + header_len + cbytes, chunk, (size_t)chunk_cbytes);
3054
    /* Copy the offsets */
3055
0
    memcpy(framep + header_len + new_cbytes, off_chunk, (size_t)new_off_cbytes);
3056
0
  } else {
3057
0
    int64_t wbytes;
3058
3059
0
    int64_t io_pos = 0;
3060
0
    blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
3061
0
    if (io_cb == NULL) {
3062
0
      BLOSC_TRACE_ERROR("Error getting the input/output API");
3063
0
      return NULL;
3064
0
    }
3065
3066
0
    if (frame->sframe) {
3067
0
      if (chunk_cbytes != 0) {
3068
0
        if (sframe_chunk_id < 0) {
3069
0
          BLOSC_TRACE_ERROR("The chunk id (%" PRId64 ") is not correct", sframe_chunk_id);
3070
0
          return NULL;
3071
0
        }
3072
0
        if (sframe_create_chunk(frame, chunk, sframe_chunk_id, chunk_cbytes) == NULL) {
3073
0
          BLOSC_TRACE_ERROR("Cannot write the full chunk.");
3074
0
          return NULL;
3075
0
        }
3076
0
      }
3077
      // Update the offsets chunk in the chunks frame
3078
0
      fp = sframe_open_index(frame->urlpath, "rb+",
3079
0
                             frame->schunk->storage->io);
3080
0
      if (fp == NULL) {
3081
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
3082
0
        return NULL;
3083
0
      }
3084
0
      io_pos = frame->file_offset + header_len + 0;
3085
0
    }
3086
0
    else {
3087
      // Regular frame
3088
0
      fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params);
3089
0
      if (fp == NULL) {
3090
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
3091
0
        return NULL;
3092
0
      }
3093
0
      io_pos = frame->file_offset + header_len + cbytes;
3094
0
      wbytes = io_cb->write(chunk, 1, chunk_cbytes, io_pos, fp);  // the new chunk
3095
0
      io_pos += chunk_cbytes;
3096
0
      if (wbytes != chunk_cbytes) {
3097
0
        BLOSC_TRACE_ERROR("Cannot write the full chunk to frame.");
3098
0
        io_cb->close(fp);
3099
0
        return NULL;
3100
0
      }
3101
0
    }
3102
0
    wbytes = io_cb->write(off_chunk, 1, new_off_cbytes, io_pos, fp);  // the new offsets
3103
0
    io_cb->close(fp);
3104
0
    if (wbytes != new_off_cbytes) {
3105
0
      BLOSC_TRACE_ERROR("Cannot write the offsets to frame.");
3106
0
      return NULL;
3107
0
    }
3108
    // Invalidate the cache for chunk offsets
3109
0
    if (frame->coffsets != NULL) {
3110
0
      if (frame->coffsets_needs_free)
3111
0
        free(frame->coffsets);
3112
0
      frame->coffsets = NULL;
3113
0
    }
3114
0
  }
3115
0
  free(chunk);  // chunk has always to be a copy when reaching here...
3116
0
  free(off_chunk);
3117
3118
0
  frame->len = new_frame_len;
3119
0
  rc = frame_update_header(frame, schunk, false);
3120
0
  if (rc < 0) {
3121
0
    return NULL;
3122
0
  }
3123
3124
0
  rc = frame_update_trailer(frame, schunk);
3125
0
  if (rc < 0) {
3126
0
    return NULL;
3127
0
  }
3128
3129
0
  return frame;
3130
0
}
3131
3132
3133
0
void* frame_update_chunk(blosc2_frame_s* frame, int64_t nchunk, void* chunk, blosc2_schunk* schunk) {
3134
0
  uint8_t *chunk_ = (uint8_t *) chunk;
3135
0
  int32_t header_len;
3136
0
  int64_t frame_len;
3137
0
  int64_t nbytes;
3138
0
  int64_t cbytes;
3139
0
  int32_t blocksize;
3140
0
  int32_t chunksize;
3141
0
  int64_t nchunks;
3142
0
  int rc = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes,
3143
0
                           &blocksize, &chunksize, &nchunks,
3144
0
                           NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3145
0
                           frame->schunk->storage->io);
3146
0
  if (rc < 0) {
3147
0
    BLOSC_TRACE_ERROR("Unable to get meta info from frame.");
3148
0
    return NULL;
3149
0
  }
3150
0
  if (nchunk >= nchunks) {
3151
0
    BLOSC_TRACE_ERROR("The chunk must already exist.");
3152
0
    return NULL;
3153
0
  }
3154
3155
0
  int32_t chunk_cbytes;
3156
0
  rc = blosc2_cbuffer_sizes(chunk, NULL, &chunk_cbytes, NULL);
3157
0
  if (rc < 0) {
3158
0
    return NULL;
3159
0
  }
3160
3161
  // Get the current offsets
3162
0
  int32_t off_nbytes = (int32_t) (nchunks * sizeof(int64_t));
3163
0
  int64_t* offsets = (int64_t *) malloc((size_t)off_nbytes);
3164
0
  if (nchunks > 0) {
3165
0
    int32_t coffsets_cbytes = 0;
3166
0
    uint8_t *coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &coffsets_cbytes);
3167
0
    if (coffsets == NULL) {
3168
0
      BLOSC_TRACE_ERROR("Cannot get the offsets for the frame.");
3169
0
      return NULL;
3170
0
    }
3171
0
    if (coffsets_cbytes == 0) {
3172
0
      coffsets_cbytes = (int32_t)cbytes;
3173
0
    }
3174
3175
    // Decompress offsets
3176
0
    blosc2_dparams off_dparams = BLOSC2_DPARAMS_DEFAULTS;
3177
0
    blosc2_context *dctx = blosc2_create_dctx(off_dparams);
3178
0
    if (dctx == NULL) {
3179
0
      BLOSC_TRACE_ERROR("Error while creating the decompression context");
3180
0
      return NULL;
3181
0
    }
3182
0
    int32_t prev_nbytes = blosc2_decompress_ctx(dctx, coffsets, coffsets_cbytes, offsets, off_nbytes);
3183
0
    blosc2_free_ctx(dctx);
3184
0
    if (prev_nbytes < 0) {
3185
0
      free(offsets);
3186
0
      BLOSC_TRACE_ERROR("Cannot decompress the offsets chunk.");
3187
0
      return NULL;
3188
0
    }
3189
0
  }
3190
0
  int32_t cbytes_old = 0;
3191
0
  int64_t old_offset = 0;
3192
0
  if (!frame->sframe) {
3193
    // See how big would be the space
3194
0
    old_offset = offsets[nchunk];
3195
0
    bool needs_free;
3196
0
    uint8_t *chunk_old;
3197
0
    int err = blosc2_schunk_get_chunk(schunk, nchunk, &chunk_old, &needs_free);
3198
0
    if (err < 0) {
3199
0
      BLOSC_TRACE_ERROR("%" PRId64 " chunk can not be obtained from schunk.", nchunk);
3200
0
      return NULL;
3201
0
    }
3202
3203
0
    if (chunk_old == NULL) {
3204
0
      cbytes_old = 0;
3205
0
    }
3206
0
    else {
3207
0
      cbytes_old = sw32_(chunk_old + BLOSC2_CHUNK_CBYTES);
3208
0
      if (cbytes_old == BLOSC2_MAX_OVERHEAD) {
3209
0
        cbytes_old = 0;
3210
0
      }
3211
0
    }
3212
0
    if (needs_free) {
3213
0
      free(chunk_old);
3214
0
    }
3215
0
  }
3216
3217
  // Add the new offset
3218
0
  int64_t sframe_chunk_id = -1;
3219
0
  if (frame->sframe) {
3220
0
    if (offsets[nchunk] < 0) {
3221
0
      sframe_chunk_id = -1;
3222
0
    }
3223
0
    else {
3224
      // In case there was a reorder in a sframe
3225
0
      sframe_chunk_id = offsets[nchunk];
3226
0
    }
3227
0
  }
3228
0
  int special_value = (chunk_[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK;
3229
0
  uint64_t offset_value = ((uint64_t)1 << 63);
3230
0
  switch (special_value) {
3231
0
    case BLOSC2_SPECIAL_ZERO:
3232
      // Zero chunk.  Code it in a special way.
3233
0
      offset_value += (uint64_t)BLOSC2_SPECIAL_ZERO << (8 * 7);  // indicate a chunk of zeros
3234
0
      to_little(offsets + nchunk, &offset_value, sizeof(uint64_t));
3235
0
      chunk_cbytes = 0;   // we don't need to store the chunk
3236
0
      break;
3237
0
    case BLOSC2_SPECIAL_UNINIT:
3238
      // Non initizalized values chunk.  Code it in a special way.
3239
0
      offset_value += (uint64_t)BLOSC2_SPECIAL_UNINIT << (8 * 7);  // indicate a chunk of uninit values
3240
0
      to_little(offsets + nchunk, &offset_value, sizeof(uint64_t));
3241
0
      chunk_cbytes = 0;   // we don't need to store the chunk
3242
0
      break;
3243
0
    case BLOSC2_SPECIAL_NAN:
3244
      // NaN chunk.  Code it in a special way.
3245
0
      offset_value += (uint64_t)BLOSC2_SPECIAL_NAN << (8 * 7);  // indicate a chunk of NANs
3246
0
      to_little(offsets + nchunk, &offset_value, sizeof(uint64_t));
3247
0
      chunk_cbytes = 0;   // we don't need to store the chunk
3248
0
      break;
3249
0
    default:
3250
0
      if (frame->sframe) {
3251
0
        if (sframe_chunk_id < 0) {
3252
0
          for (int i = 0; i < nchunks; ++i) {
3253
0
            if (offsets[i] > sframe_chunk_id) {
3254
0
              sframe_chunk_id = offsets[i];
3255
0
            }
3256
0
          }
3257
0
          offsets[nchunk] = ++sframe_chunk_id;
3258
0
        }
3259
0
      }
3260
0
      else {
3261
        // Add the new offset
3262
0
        offsets[nchunk] = cbytes;
3263
0
      }
3264
0
  }
3265
3266
0
  if (!frame->sframe && chunk_cbytes != 0 && cbytes_old >= chunk_cbytes) {
3267
0
    offsets[nchunk] = old_offset;
3268
0
    cbytes = old_offset;
3269
0
  }
3270
  // Re-compress the offsets again
3271
0
  blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS;
3272
0
  cparams.splitmode = BLOSC_NEVER_SPLIT;
3273
0
  cparams.typesize = sizeof(int64_t);
3274
0
  cparams.blocksize = 16 * 1024;  // based on experiments with create_frame.c bench
3275
0
  cparams.nthreads = 4;  // 4 threads seems a decent default for nowadays CPUs
3276
0
  cparams.compcode = BLOSC_BLOSCLZ;
3277
0
  blosc2_context* cctx = blosc2_create_cctx(cparams);
3278
0
  if (cctx == NULL) {
3279
0
    BLOSC_TRACE_ERROR("Error while creating the compression context");
3280
0
    return NULL;
3281
0
  }
3282
0
  void* off_chunk = malloc((size_t)off_nbytes + BLOSC2_MAX_OVERHEAD);
3283
0
  int32_t new_off_cbytes = blosc2_compress_ctx(cctx, offsets, off_nbytes,
3284
0
                                               off_chunk, off_nbytes + BLOSC2_MAX_OVERHEAD);
3285
0
  blosc2_free_ctx(cctx);
3286
3287
0
  free(offsets);
3288
0
  if (new_off_cbytes < 0) {
3289
0
    free(off_chunk);
3290
0
    return NULL;
3291
0
  }
3292
3293
0
  int64_t new_cbytes = schunk->cbytes;
3294
0
  int64_t new_frame_len;
3295
0
  if (frame->sframe) {
3296
    // The chunk is not stored in the frame
3297
0
    new_frame_len = header_len + 0 + new_off_cbytes + frame->trailer_len;
3298
0
  }
3299
0
  else {
3300
0
    new_frame_len = header_len + new_cbytes + new_off_cbytes + frame->trailer_len;
3301
0
  }
3302
3303
0
  void* fp = NULL;
3304
0
  if (frame->cframe != NULL) {
3305
0
    uint8_t* framep = frame->cframe;
3306
    /* Make space for the new chunk and copy it */
3307
0
    frame->cframe = framep = realloc(framep, (size_t)new_frame_len);
3308
0
    if (framep == NULL) {
3309
0
      BLOSC_TRACE_ERROR("Cannot realloc space for the frame.");
3310
0
      return NULL;
3311
0
    }
3312
    /* Copy the chunk */
3313
0
    memcpy(framep + header_len + cbytes, chunk, (size_t)chunk_cbytes);
3314
    /* Copy the offsets */
3315
0
    memcpy(framep + header_len + new_cbytes, off_chunk, (size_t)new_off_cbytes);
3316
0
  } else {
3317
0
    int64_t wbytes;
3318
3319
0
    int64_t io_pos = 0;
3320
0
    blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
3321
0
    if (io_cb == NULL) {
3322
0
      BLOSC_TRACE_ERROR("Error getting the input/output API");
3323
0
      return NULL;
3324
0
    }
3325
3326
0
    if (frame->sframe) {
3327
      // Create the chunks file, if it's a special value this will delete its old content
3328
0
      if (sframe_chunk_id >= 0) {
3329
0
        if (sframe_create_chunk(frame, chunk, sframe_chunk_id, chunk_cbytes) == NULL) {
3330
0
          BLOSC_TRACE_ERROR("Cannot write the full chunk.");
3331
0
          return NULL;
3332
0
        }
3333
0
      }
3334
      // Update the offsets chunk in the chunks frame
3335
0
      fp = sframe_open_index(frame->urlpath, "rb+",
3336
0
                             frame->schunk->storage->io);
3337
0
      if (fp == NULL) {
3338
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
3339
0
        return NULL;
3340
0
      }
3341
0
      io_pos = frame->file_offset + header_len + 0;
3342
0
    }
3343
0
    else {
3344
      // Regular frame
3345
0
      fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params);
3346
0
      if (fp == NULL) {
3347
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
3348
0
        return NULL;
3349
0
      }
3350
0
      io_pos = frame->file_offset + header_len + cbytes;
3351
0
      wbytes = io_cb->write(chunk, 1, chunk_cbytes, io_pos, fp);  // the new chunk
3352
0
      io_pos += chunk_cbytes;
3353
0
      if (wbytes != chunk_cbytes) {
3354
0
        BLOSC_TRACE_ERROR("Cannot write the full chunk to frame.");
3355
0
        io_cb->close(fp);
3356
0
        return NULL;
3357
0
      }
3358
0
      io_pos = frame->file_offset + header_len + new_cbytes;
3359
0
    }
3360
0
    wbytes = io_cb->write(off_chunk, 1, new_off_cbytes, io_pos, fp);  // the new offsets
3361
0
    io_cb->close(fp);
3362
0
    if (wbytes != new_off_cbytes) {
3363
0
      BLOSC_TRACE_ERROR("Cannot write the offsets to frame.");
3364
0
      return NULL;
3365
0
    }
3366
    // Invalidate the cache for chunk offsets
3367
0
    if (frame->coffsets != NULL) {
3368
0
      if (frame->coffsets_needs_free)
3369
0
        free(frame->coffsets);
3370
0
      frame->coffsets = NULL;
3371
0
    }
3372
0
  }
3373
0
  free(chunk);  // chunk has always to be a copy when reaching here...
3374
0
  free(off_chunk);
3375
3376
0
  frame->len = new_frame_len;
3377
0
  rc = frame_update_header(frame, schunk, false);
3378
0
  if (rc < 0) {
3379
0
    return NULL;
3380
0
  }
3381
3382
0
  rc = frame_update_trailer(frame, schunk);
3383
0
  if (rc < 0) {
3384
0
    return NULL;
3385
0
  }
3386
3387
0
  return frame;
3388
0
}
3389
3390
3391
0
void* frame_delete_chunk(blosc2_frame_s* frame, int64_t nchunk, blosc2_schunk* schunk) {
3392
0
  int32_t header_len;
3393
0
  int64_t frame_len;
3394
0
  int64_t nbytes;
3395
0
  int64_t cbytes;
3396
0
  int32_t blocksize;
3397
0
  int32_t chunksize;
3398
0
  int64_t nchunks;
3399
0
  int rc = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes,
3400
0
                           &blocksize, &chunksize,  &nchunks,
3401
0
                           NULL, NULL, NULL, NULL, NULL, NULL, NULL, frame->schunk->storage->io);
3402
0
  if (rc < 0) {
3403
0
    BLOSC_TRACE_ERROR("Unable to get meta info from frame.");
3404
0
    return NULL;
3405
0
  }
3406
3407
  // Get the current offsets
3408
0
  int32_t off_nbytes = (int32_t) (nchunks * sizeof(int64_t));
3409
0
  int64_t* offsets = (int64_t *) malloc((size_t)off_nbytes);
3410
0
  if (nchunks > 0) {
3411
0
    int32_t coffsets_cbytes = 0;
3412
0
    uint8_t *coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &coffsets_cbytes);
3413
0
    if (coffsets == NULL) {
3414
0
      BLOSC_TRACE_ERROR("Cannot get the offsets for the frame.");
3415
0
      return NULL;
3416
0
    }
3417
0
    if (coffsets_cbytes == 0) {
3418
0
      coffsets_cbytes = (int32_t)cbytes;
3419
0
    }
3420
3421
    // Decompress offsets
3422
0
    blosc2_dparams off_dparams = BLOSC2_DPARAMS_DEFAULTS;
3423
0
    blosc2_context *dctx = blosc2_create_dctx(off_dparams);
3424
0
    if (dctx == NULL) {
3425
0
      BLOSC_TRACE_ERROR("Error while creating the decompression context");
3426
0
      return NULL;
3427
0
    }
3428
0
    int32_t prev_nbytes = blosc2_decompress_ctx(dctx, coffsets, coffsets_cbytes, offsets, off_nbytes);
3429
0
    blosc2_free_ctx(dctx);
3430
0
    if (prev_nbytes < 0) {
3431
0
      free(offsets);
3432
0
      BLOSC_TRACE_ERROR("Cannot decompress the offsets chunk.");
3433
0
      return NULL;
3434
0
    }
3435
0
  }
3436
3437
  // Delete the new offset
3438
0
  for (int64_t i = nchunk; i < nchunks - 1; i++) {
3439
0
    offsets[i] = offsets[i + 1];
3440
0
  }
3441
0
  offsets[nchunks - 1] = 0;
3442
3443
  // Re-compress the offsets again
3444
0
  blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS;
3445
0
  cparams.splitmode = BLOSC_NEVER_SPLIT;
3446
0
  cparams.typesize = sizeof(int64_t);
3447
0
  cparams.blocksize = 16 * 1024;  // based on experiments with create_frame.c bench
3448
0
  cparams.nthreads = 4;  // 4 threads seems a decent default for nowadays CPUs
3449
0
  cparams.compcode = BLOSC_BLOSCLZ;
3450
0
  blosc2_context* cctx = blosc2_create_cctx(cparams);
3451
0
  if (cctx == NULL) {
3452
0
    BLOSC_TRACE_ERROR("Error while creating the compression context");
3453
0
    return NULL;
3454
0
  }
3455
0
  void* off_chunk = malloc((size_t)off_nbytes + BLOSC2_MAX_OVERHEAD);
3456
0
  int32_t new_off_cbytes = blosc2_compress_ctx(cctx, offsets, off_nbytes - (int32_t)sizeof(int64_t),
3457
0
                                               off_chunk, off_nbytes + BLOSC2_MAX_OVERHEAD);
3458
0
  blosc2_free_ctx(cctx);
3459
3460
0
  free(offsets);
3461
0
  if (new_off_cbytes < 0) {
3462
0
    free(off_chunk);
3463
0
    return NULL;
3464
0
  }
3465
3466
0
  int64_t new_cbytes = cbytes;
3467
3468
0
  int64_t new_frame_len;
3469
0
  if (frame->sframe) {
3470
0
    new_frame_len = header_len + 0 + new_off_cbytes + frame->trailer_len;
3471
0
  }
3472
0
  else {
3473
0
    new_frame_len = header_len + new_cbytes + new_off_cbytes + frame->trailer_len;
3474
0
  }
3475
3476
  // Add the chunk and update meta
3477
0
  FILE* fp = NULL;
3478
0
  if (frame->cframe != NULL) {
3479
0
    uint8_t* framep = frame->cframe;
3480
    /* Make space for the new chunk and copy it */
3481
0
    frame->cframe = framep = realloc(framep, (size_t)new_frame_len);
3482
0
    if (framep == NULL) {
3483
0
      BLOSC_TRACE_ERROR("Cannot realloc space for the frame.");
3484
0
      return NULL;
3485
0
    }
3486
    /* Copy the offsets */
3487
0
    memcpy(framep + header_len + new_cbytes, off_chunk, (size_t)new_off_cbytes);
3488
0
  } else {
3489
0
    blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
3490
0
    if (io_cb == NULL) {
3491
0
      BLOSC_TRACE_ERROR("Error getting the input/output API");
3492
0
      return NULL;
3493
0
    }
3494
3495
0
    size_t wbytes;
3496
0
    int64_t io_pos = 0;
3497
0
    if (frame->sframe) {
3498
0
      int64_t offset;
3499
0
      rc = get_coffset(frame, header_len, cbytes, nchunk, nchunks, &offset);
3500
0
      if (rc < 0) {
3501
0
        BLOSC_TRACE_ERROR("Unable to get offset to chunk %" PRId64 ".", nchunk);
3502
0
        return NULL;
3503
0
      }
3504
0
      if (offset >= 0){
3505
        // Remove the chunk file only if it is not a special value chunk
3506
0
        int err = sframe_delete_chunk(frame->urlpath, offset);
3507
0
        if (err != 0) {
3508
0
          BLOSC_TRACE_ERROR("Unable to delete chunk!");
3509
0
          return NULL;
3510
0
        }
3511
0
      }
3512
      // Update the offsets chunk in the chunks frame
3513
0
      fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io);
3514
0
      if (fp == NULL) {
3515
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
3516
0
        return NULL;
3517
0
      }
3518
0
      io_pos = frame->file_offset + header_len + 0;
3519
0
    }
3520
0
    else {
3521
      // Regular frame
3522
0
      fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io);
3523
0
      if (fp == NULL) {
3524
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
3525
0
        return NULL;
3526
0
      }
3527
0
      io_pos = frame->file_offset + header_len + cbytes;
3528
0
    }
3529
0
    wbytes = io_cb->write(off_chunk, 1, new_off_cbytes, io_pos, fp);  // the new offsets
3530
0
    io_cb->close(fp);
3531
0
    if (wbytes != (size_t)new_off_cbytes) {
3532
0
      BLOSC_TRACE_ERROR("Cannot write the offsets to frame.");
3533
0
      return NULL;
3534
0
    }
3535
    // Invalidate the cache for chunk offsets
3536
0
    if (frame->coffsets != NULL) {
3537
0
      if (frame->coffsets_needs_free)
3538
0
        free(frame->coffsets);
3539
0
      frame->coffsets = NULL;
3540
0
    }
3541
0
  }
3542
0
  free(off_chunk);
3543
3544
0
  frame->len = new_frame_len;
3545
0
  rc = frame_update_header(frame, schunk, false);
3546
0
  if (rc < 0) {
3547
0
    return NULL;
3548
0
  }
3549
3550
0
  rc = frame_update_trailer(frame, schunk);
3551
0
  if (rc < 0) {
3552
0
    return NULL;
3553
0
  }
3554
3555
0
  return frame;
3556
0
}
3557
3558
3559
0
int frame_reorder_offsets(blosc2_frame_s* frame, const int64_t* offsets_order, blosc2_schunk* schunk) {
3560
  // Get header info
3561
0
  int32_t header_len;
3562
0
  int64_t frame_len;
3563
0
  int64_t nbytes;
3564
0
  int64_t cbytes;
3565
0
  int32_t blocksize;
3566
0
  int32_t chunksize;
3567
0
  int64_t nchunks;
3568
0
  int ret = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes,
3569
0
                            &blocksize, &chunksize, &nchunks,
3570
0
                            NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3571
0
                            frame->schunk->storage->io);
3572
0
  if (ret < 0) {
3573
0
      BLOSC_TRACE_ERROR("Cannot get the header info for the frame.");
3574
0
      return ret;
3575
0
  }
3576
3577
  // Get the current offsets and add one more
3578
0
  int32_t off_nbytes = (int32_t) (nchunks * sizeof(int64_t));
3579
0
  int64_t* offsets = (int64_t *) malloc((size_t)off_nbytes);
3580
3581
0
  int32_t coffsets_cbytes = 0;
3582
0
  uint8_t *coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &coffsets_cbytes);
3583
0
  if (coffsets == NULL) {
3584
0
    BLOSC_TRACE_ERROR("Cannot get the offsets for the frame.");
3585
0
    free(offsets);
3586
0
    return BLOSC2_ERROR_DATA;
3587
0
  }
3588
3589
  // Decompress offsets
3590
0
  blosc2_dparams off_dparams = BLOSC2_DPARAMS_DEFAULTS;
3591
0
  blosc2_context *dctx = blosc2_create_dctx(off_dparams);
3592
0
  if (dctx == NULL) {
3593
0
    BLOSC_TRACE_ERROR("Error while creating the decompression context");
3594
0
    return BLOSC2_ERROR_NULL_POINTER;
3595
0
  }
3596
0
  int32_t prev_nbytes = blosc2_decompress_ctx(dctx, coffsets, coffsets_cbytes,
3597
0
                                              offsets, off_nbytes);
3598
0
  blosc2_free_ctx(dctx);
3599
0
  if (prev_nbytes < 0) {
3600
0
    free(offsets);
3601
0
    BLOSC_TRACE_ERROR("Cannot decompress the offsets chunk.");
3602
0
    return prev_nbytes;
3603
0
  }
3604
3605
  // Make a copy of the chunk offsets and reorder it
3606
0
  int64_t *offsets_copy = malloc(prev_nbytes);
3607
0
  memcpy(offsets_copy, offsets, prev_nbytes);
3608
3609
0
  for (int i = 0; i < nchunks; ++i) {
3610
0
    offsets[i] = offsets_copy[offsets_order[i]];
3611
0
  }
3612
0
  free(offsets_copy);
3613
3614
  // Re-compress the offsets again
3615
0
  blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS;
3616
0
  cparams.splitmode = BLOSC_NEVER_SPLIT;
3617
0
  cparams.typesize = sizeof(int64_t);
3618
0
  cparams.blocksize = 16 * 1024;  // based on experiments with create_frame.c bench
3619
0
  cparams.nthreads = 4;  // 4 threads seems a decent default for nowadays CPUs
3620
0
  cparams.compcode = BLOSC_BLOSCLZ;
3621
0
  blosc2_context* cctx = blosc2_create_cctx(cparams);
3622
0
  if (cctx == NULL) {
3623
0
    BLOSC_TRACE_ERROR("Error while creating the compression context");
3624
0
    return BLOSC2_ERROR_NULL_POINTER;
3625
0
  }
3626
0
  void* off_chunk = malloc((size_t)off_nbytes + BLOSC2_MAX_OVERHEAD);
3627
0
  int32_t new_off_cbytes = blosc2_compress_ctx(cctx, offsets, off_nbytes,
3628
0
                                               off_chunk, off_nbytes + BLOSC2_MAX_OVERHEAD);
3629
0
  blosc2_free_ctx(cctx);
3630
3631
0
  if (new_off_cbytes < 0) {
3632
0
    free(offsets);
3633
0
    free(off_chunk);
3634
0
    return new_off_cbytes;
3635
0
  }
3636
0
  free(offsets);
3637
0
  int64_t new_frame_len;
3638
0
  if (frame->sframe) {
3639
    // The chunks are not in the frame
3640
0
    new_frame_len = header_len + 0 + new_off_cbytes + frame->trailer_len;
3641
0
  }
3642
0
  else {
3643
0
    new_frame_len = header_len + cbytes + new_off_cbytes + frame->trailer_len;
3644
0
  }
3645
3646
0
  if (frame->cframe != NULL) {
3647
0
    uint8_t* framep = frame->cframe;
3648
    /* Make space for the new chunk and copy it */
3649
0
    frame->cframe = framep = realloc(framep, (size_t)new_frame_len);
3650
0
    if (framep == NULL) {
3651
0
      BLOSC_TRACE_ERROR("Cannot realloc space for the frame.");
3652
0
      return BLOSC2_ERROR_MEMORY_ALLOC;
3653
0
    }
3654
    /* Copy the offsets */
3655
0
    memcpy(framep + header_len + cbytes, off_chunk, (size_t)new_off_cbytes);
3656
0
  }
3657
0
  else {
3658
0
    void* fp = NULL;
3659
3660
0
    blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
3661
0
    if (io_cb == NULL) {
3662
0
      BLOSC_TRACE_ERROR("Error getting the input/output API");
3663
0
      return BLOSC2_ERROR_PLUGIN_IO;
3664
0
    }
3665
3666
0
    int64_t io_pos = 0;
3667
0
    if (frame->sframe) {
3668
      // Update the offsets chunk in the chunks frame
3669
0
      fp = sframe_open_index(frame->urlpath, "rb+",
3670
0
                             frame->schunk->storage->io);
3671
0
      if (fp == NULL) {
3672
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
3673
0
        return BLOSC2_ERROR_FILE_OPEN;
3674
0
      }
3675
0
      io_pos = frame->file_offset + header_len + 0;
3676
0
    }
3677
0
    else {
3678
      // Regular frame
3679
0
      fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params);
3680
0
      if (fp == NULL) {
3681
0
        BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath);
3682
0
        return BLOSC2_ERROR_FILE_OPEN;
3683
0
      }
3684
0
      io_pos = frame->file_offset + header_len + cbytes;
3685
0
    }
3686
0
    int64_t wbytes = io_cb->write(off_chunk, 1, new_off_cbytes, io_pos, fp);  // the new offsets
3687
0
    io_cb->close(fp);
3688
0
    if (wbytes != new_off_cbytes) {
3689
0
      BLOSC_TRACE_ERROR("Cannot write the offsets to frame.");
3690
0
      return BLOSC2_ERROR_FILE_WRITE;
3691
0
    }
3692
0
  }
3693
3694
  // Invalidate the cache for chunk offsets
3695
0
  if (frame->coffsets != NULL) {
3696
0
    if (frame->coffsets_needs_free)
3697
0
      free(frame->coffsets);
3698
0
    frame->coffsets = NULL;
3699
0
  }
3700
0
  free(off_chunk);
3701
3702
0
  frame->len = new_frame_len;
3703
0
  int rc = frame_update_header(frame, schunk, false);
3704
0
  if (rc < 0) {
3705
0
    return rc;
3706
0
  }
3707
3708
0
  rc = frame_update_trailer(frame, schunk);
3709
0
  if (rc < 0) {
3710
0
    return rc;
3711
0
  }
3712
3713
0
  return 0;
3714
0
}
3715
3716
3717
/* Decompress and return a chunk that is part of a frame. */
3718
0
int frame_decompress_chunk(blosc2_context *dctx, blosc2_frame_s* frame, int64_t nchunk, void *dest, int32_t nbytes) {
3719
0
  uint8_t* src;
3720
0
  bool needs_free;
3721
0
  int32_t chunk_nbytes;
3722
0
  int32_t chunk_cbytes;
3723
0
  int rc;
3724
3725
  // Use a lazychunk here in order to do a potential parallel read.
3726
0
  rc = frame_get_lazychunk(frame, nchunk, &src, &needs_free);
3727
0
  if (rc < 0) {
3728
0
    BLOSC_TRACE_ERROR("Cannot get the chunk in position %" PRId64 ".", nchunk);
3729
0
    goto end;
3730
0
  }
3731
0
  chunk_cbytes = rc;
3732
0
  if (chunk_cbytes < (signed)sizeof(int32_t)) {
3733
    /* Not enough input to read `nbytes` */
3734
0
    rc = BLOSC2_ERROR_READ_BUFFER;
3735
0
  }
3736
3737
0
  rc = blosc2_cbuffer_sizes(src, &chunk_nbytes, &chunk_cbytes, NULL);
3738
0
  if (rc < 0) {
3739
0
    goto end;
3740
0
  }
3741
3742
  /* Create a buffer for destination */
3743
0
  if (chunk_nbytes > nbytes) {
3744
0
    BLOSC_TRACE_ERROR("Not enough space for decompressing in dest.");
3745
0
    rc = BLOSC2_ERROR_WRITE_BUFFER;
3746
0
    goto end;
3747
0
  }
3748
  /* And decompress it */
3749
0
  dctx->header_overhead = BLOSC_EXTENDED_HEADER_LENGTH;
3750
0
  int chunksize = rc = blosc2_decompress_ctx(dctx, src, chunk_cbytes, dest, nbytes);
3751
0
  if (chunksize < 0 || chunksize != chunk_nbytes) {
3752
0
    BLOSC_TRACE_ERROR("Error in decompressing chunk.");
3753
0
    if (chunksize >= 0)
3754
0
      rc = BLOSC2_ERROR_FAILURE;
3755
0
  }
3756
0
  end:
3757
0
  if (needs_free) {
3758
0
    free(src);
3759
0
  }
3760
0
  return rc;
3761
0
}