Coverage Report

Created: 2026-04-12 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/c-blosc2/blosc/b2nd.c
Line
Count
Source
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 "b2nd.h"
12
#include "context.h"
13
#include "blosc2/blosc2-common.h"
14
#include "blosc2.h"
15
16
#include <inttypes.h>
17
#include <stdlib.h>
18
#include <stdint.h>
19
#include <string.h>
20
21
22
int b2nd_serialize_meta(int8_t ndim, const int64_t *shape, const int32_t *chunkshape,
23
                        const int32_t *blockshape, const char *dtype, int8_t dtype_format,
24
0
                        uint8_t **smeta) {
25
0
  if (dtype == NULL) {
26
0
    dtype = B2ND_DEFAULT_DTYPE;
27
0
  }
28
  // dtype checks
29
0
  if (dtype_format < 0) {
30
0
    BLOSC_TRACE_ERROR("dtype_format cannot be negative");
31
0
    BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
32
0
  }
33
0
  size_t dtype_len0 = strlen(dtype);
34
0
  if (dtype_len0 > INT32_MAX) {
35
0
    BLOSC_TRACE_ERROR("dtype is too large (len > %d)", INT32_MAX);
36
0
    BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
37
0
  }
38
0
  const int32_t dtype_len = (int32_t) dtype_len0;
39
  // Allocate space for b2nd metalayer
40
0
  int32_t max_smeta_len = (int32_t) (1 + 1 + 1 + (1 + ndim * (1 + sizeof(int64_t))) +
41
0
                                     (1 + ndim * (1 + sizeof(int32_t))) + (1 + ndim * (1 + sizeof(int32_t))) +
42
0
                                     1 + 1 + sizeof(int32_t) + dtype_len);
43
0
  *smeta = malloc((size_t) max_smeta_len);
44
0
  BLOSC_ERROR_NULL(*smeta, BLOSC2_ERROR_MEMORY_ALLOC);
45
0
  uint8_t *pmeta = *smeta;
46
47
  // Build an array with 7 entries (version, ndim, shape, chunkshape, blockshape, dtype_format, dtype)
48
0
  *pmeta++ = 0x90 + 7;
49
50
  // version entry
51
0
  *pmeta++ = B2ND_METALAYER_VERSION;  // positive fixnum (7-bit positive integer)
52
53
  // ndim entry
54
0
  *pmeta++ = (uint8_t) ndim;  // positive fixnum (7-bit positive integer)
55
56
  // shape entry
57
0
  *pmeta++ = (uint8_t) (0x90) + ndim;  // fix array with ndim elements
58
0
  for (uint8_t i = 0; i < ndim; i++) {
59
0
    *pmeta++ = 0xd3;  // int64
60
0
    swap_store(pmeta, shape + i, sizeof(int64_t));
61
0
    pmeta += sizeof(int64_t);
62
0
  }
63
64
  // chunkshape entry
65
0
  *pmeta++ = (uint8_t) (0x90) + ndim;  // fix array with ndim elements
66
0
  for (uint8_t i = 0; i < ndim; i++) {
67
0
    *pmeta++ = 0xd2;  // int32
68
0
    swap_store(pmeta, chunkshape + i, sizeof(int32_t));
69
0
    pmeta += sizeof(int32_t);
70
0
  }
71
72
  // blockshape entry
73
0
  *pmeta++ = (uint8_t) (0x90) + ndim;  // fix array with ndim elements
74
0
  for (uint8_t i = 0; i < ndim; i++) {
75
0
    *pmeta++ = 0xd2;  // int32
76
0
    swap_store(pmeta, blockshape + i, sizeof(int32_t));
77
0
    pmeta += sizeof(int32_t);
78
0
  }
79
80
  // dtype entry
81
0
  *pmeta++ = dtype_format;  // positive fixint (7-bit positive integer)
82
0
  *pmeta++ = (uint8_t) (0xdb);  // str with up to 2^31 elements
83
0
  swap_store(pmeta, &dtype_len, sizeof(int32_t));
84
0
  pmeta += sizeof(int32_t);
85
0
  memcpy(pmeta, dtype, dtype_len);
86
0
  pmeta += dtype_len;
87
88
0
  int32_t slen = (int32_t) (pmeta - *smeta);
89
0
  if (max_smeta_len != slen) {
90
0
    BLOSC_TRACE_ERROR("meta length is inconsistent!");
91
0
    return BLOSC2_ERROR_FAILURE;
92
0
  }
93
94
0
  return (int)slen;
95
0
}
96
97
98
int b2nd_deserialize_meta(const uint8_t *smeta, int32_t smeta_len, int8_t *ndim, int64_t *shape,
99
0
                          int32_t *chunkshape, int32_t *blockshape, char **dtype, int8_t *dtype_format) {
100
0
  const uint8_t *pmeta = smeta;
101
102
  // Check that we have an array with 7 entries (version, ndim, shape, chunkshape, blockshape, dtype_format, dtype)
103
0
  pmeta += 1;
104
105
  // version entry
106
  // int8_t version = (int8_t)pmeta[0];  // positive fixnum (7-bit positive integer) commented to avoid warning
107
0
  pmeta += 1;
108
109
  // ndim entry
110
0
  *ndim = (int8_t) pmeta[0];
111
0
  int8_t ndim_aux = *ndim;  // positive fixnum (7-bit positive integer)
112
0
  if (ndim_aux < 0 || ndim_aux > B2ND_MAX_DIM) {
113
0
    BLOSC_TRACE_ERROR("ndim %d is out of range", ndim_aux);
114
0
    return BLOSC2_ERROR_FAILURE;
115
0
  }
116
0
  pmeta += 1;
117
118
  // shape entry
119
  // Initialize to ones, as required by b2nd
120
0
  for (int i = 0; i < ndim_aux; i++) shape[i] = 1;
121
0
  pmeta += 1;
122
0
  for (int8_t i = 0; i < ndim_aux; i++) {
123
0
    pmeta += 1;
124
0
    swap_store(shape + i, pmeta, sizeof(int64_t));
125
0
    pmeta += sizeof(int64_t);
126
0
  }
127
128
  // chunkshape entry
129
  // Initialize to ones, as required by b2nd
130
0
  for (int i = 0; i < ndim_aux; i++) chunkshape[i] = 1;
131
0
  pmeta += 1;
132
0
  for (int8_t i = 0; i < ndim_aux; i++) {
133
0
    pmeta += 1;
134
0
    swap_store(chunkshape + i, pmeta, sizeof(int32_t));
135
0
    pmeta += sizeof(int32_t);
136
0
  }
137
138
  // blockshape entry
139
  // Initialize to ones, as required by b2nd
140
0
  for (int i = 0; i < ndim_aux; i++) blockshape[i] = 1;
141
0
  pmeta += 1;
142
0
  for (int8_t i = 0; i < ndim_aux; i++) {
143
0
    pmeta += 1;
144
0
    swap_store(blockshape + i, pmeta, sizeof(int32_t));
145
0
    pmeta += sizeof(int32_t);
146
0
  }
147
148
  // dtype entry
149
0
  if (dtype_format == NULL || dtype == NULL) {
150
0
    return (int32_t)(pmeta - smeta);
151
0
  }
152
0
  if (pmeta - smeta < smeta_len) {
153
    // dtype info is here
154
0
    *dtype_format = (int8_t) *(pmeta++);
155
0
    pmeta += 1;
156
0
    int dtype_len;
157
0
    swap_store(&dtype_len, pmeta, sizeof(int32_t));
158
0
    pmeta += sizeof(int32_t);
159
0
    *dtype = (char*)malloc(dtype_len + 1);
160
0
    char* dtype_ = *dtype;
161
0
    memcpy(dtype_, (char*)pmeta, dtype_len);
162
0
    dtype_[dtype_len] = '\0';
163
0
    pmeta += dtype_len;
164
0
  }
165
0
  else {
166
    // dtype is mandatory in b2nd metalayer, but this is mainly meant as
167
    // a fall-back for deprecated caterva headers
168
0
    *dtype = NULL;
169
0
    *dtype_format = 0;
170
0
  }
171
172
0
  int32_t slen = (int32_t) (pmeta - smeta);
173
0
  return (int)slen;
174
0
}
175
176
177
178
int update_shape(b2nd_array_t *array, int8_t ndim, const int64_t *shape,
179
0
                 const int32_t *chunkshape, const int32_t *blockshape) {
180
0
  array->ndim = ndim;
181
0
  array->nitems = 1;
182
0
  array->extnitems = 1;
183
0
  array->extchunknitems = 1;
184
0
  array->chunknitems = 1;
185
0
  array->blocknitems = 1;
186
0
  for (int i = 0; i < B2ND_MAX_DIM; ++i) {
187
0
    if (i < ndim) {
188
0
      array->shape[i] = shape[i];
189
0
      array->chunkshape[i] = chunkshape[i];
190
0
      array->blockshape[i] = blockshape[i];
191
0
      if (array->chunkshape[i] != 0) {
192
0
        if (shape[i] % array->chunkshape[i] == 0) {
193
0
          array->extshape[i] = shape[i];
194
0
        } else {
195
0
          array->extshape[i] = shape[i] + chunkshape[i] - shape[i] % chunkshape[i];
196
0
        }
197
0
        if (chunkshape[i] % blockshape[i] == 0) {
198
0
          array->extchunkshape[i] = chunkshape[i];
199
0
        } else {
200
0
          array->extchunkshape[i] =
201
0
                  chunkshape[i] + blockshape[i] - chunkshape[i] % blockshape[i];
202
0
        }
203
0
      } else {
204
0
        array->extchunkshape[i] = chunkshape[i];
205
0
        array->extshape[i] = 0;
206
0
      }
207
0
    } else {
208
0
      array->blockshape[i] = 1;
209
0
      array->chunkshape[i] = 1;
210
0
      array->extshape[i] = 1;
211
0
      array->extchunkshape[i] = 1;
212
0
      array->shape[i] = 1;
213
0
    }
214
0
    array->nitems *= array->shape[i];
215
0
    array->extnitems *= array->extshape[i];
216
0
    array->extchunknitems *= array->extchunkshape[i];
217
0
    array->chunknitems *= array->chunkshape[i];
218
0
    array->blocknitems *= array->blockshape[i];
219
0
  }
220
221
  // Compute strides
222
0
  if (ndim > 0) {
223
0
    array->item_array_strides[ndim - 1] = 1;
224
0
    array->item_extchunk_strides[ndim - 1] = 1;
225
0
    array->item_chunk_strides[ndim - 1] = 1;
226
0
    array->item_block_strides[ndim - 1] = 1;
227
0
    array->block_chunk_strides[ndim - 1] = 1;
228
0
    array->chunk_array_strides[ndim - 1] = 1;
229
0
  }
230
0
  for (int i = ndim - 2; i >= 0; --i) {
231
0
    if (shape[i + 1] != 0) {
232
0
      array->item_array_strides[i] = array->item_array_strides[i + 1] * array->shape[i + 1];
233
0
      array->item_extchunk_strides[i] =
234
0
              array->item_extchunk_strides[i + 1] * array->extchunkshape[i + 1];
235
0
      array->item_chunk_strides[i] =
236
0
              array->item_chunk_strides[i + 1] * array->chunkshape[i + 1];
237
0
      array->item_block_strides[i] =
238
0
              array->item_block_strides[i + 1] * array->blockshape[i + 1];
239
0
      array->block_chunk_strides[i] = array->block_chunk_strides[i + 1] *
240
0
                                      (array->extchunkshape[i + 1] /
241
0
                                       array->blockshape[i + 1]);
242
0
      array->chunk_array_strides[i] = array->chunk_array_strides[i + 1] *
243
0
                                      (array->extshape[i + 1] * array->chunkshape[i + 1]);
244
0
    } else {
245
0
      array->item_array_strides[i] = 0;
246
0
      array->item_extchunk_strides[i] = 0;
247
0
      array->item_chunk_strides[i] = 0;
248
0
      array->item_block_strides[i] = 0;
249
0
      array->block_chunk_strides[i] = 0;
250
0
      array->chunk_array_strides[i] = 0;
251
0
    }
252
0
  }
253
0
  if (array->sc) {
254
0
    uint8_t *smeta = NULL;
255
    // Serialize the dimension info ...
256
0
    int32_t smeta_len =
257
0
            b2nd_serialize_meta(array->ndim, array->shape, array->chunkshape, array->blockshape,
258
0
                                array->dtype, array->dtype_format, &smeta);
259
0
    if (smeta_len < 0) {
260
0
      BLOSC_TRACE_ERROR("Error during serializing dims info for Blosc2 NDim");
261
0
      BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
262
0
    }
263
    // ... and update it in its metalayer
264
0
    if (blosc2_meta_exists(array->sc, "b2nd") < 0) {
265
0
      if (blosc2_meta_add(array->sc, "b2nd", smeta, smeta_len) < 0) {
266
0
        BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
267
0
      }
268
0
    } else {
269
0
      if (blosc2_meta_update(array->sc, "b2nd", smeta, smeta_len) < 0) {
270
0
        BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
271
0
      }
272
0
    }
273
0
    free(smeta);
274
0
  }
275
276
0
  return BLOSC2_ERROR_SUCCESS;
277
0
}
278
279
280
0
int array_without_schunk(b2nd_context_t *ctx, b2nd_array_t **array) {
281
  /* Create a b2nd_array_t buffer */
282
0
  (*array) = (b2nd_array_t *) malloc(sizeof(b2nd_array_t));
283
0
  BLOSC_ERROR_NULL(*array, BLOSC2_ERROR_MEMORY_ALLOC);
284
285
0
  (*array)->sc = NULL;
286
287
0
  (*array)->ndim = ctx->ndim;
288
0
  int64_t *shape = ctx->shape;
289
0
  int32_t *chunkshape = ctx->chunkshape;
290
0
  int32_t *blockshape = ctx->blockshape;
291
0
  BLOSC_ERROR(update_shape(*array, ctx->ndim, shape, chunkshape, blockshape));
292
293
0
  if (ctx->dtype != NULL) {
294
0
    (*array)->dtype = malloc(strlen(ctx->dtype) + 1);
295
0
    strcpy((*array)->dtype, ctx->dtype);
296
0
  } else {
297
0
    (*array)->dtype = NULL;
298
0
  }
299
300
0
  (*array)->dtype_format = ctx->dtype_format;
301
302
  // The partition cache (empty initially)
303
0
  (*array)->chunk_cache.data = NULL;
304
0
  (*array)->chunk_cache.nchunk = -1;  // means no valid cache yet
305
306
0
  return BLOSC2_ERROR_SUCCESS;
307
0
}
308
309
310
0
int array_new(b2nd_context_t *ctx, int special_value, b2nd_array_t **array) {
311
0
  BLOSC_ERROR(array_without_schunk(ctx, array));
312
313
0
  blosc2_schunk *sc = blosc2_schunk_new(ctx->b2_storage);
314
0
  if (sc == NULL) {
315
0
    BLOSC_TRACE_ERROR("Pointer is NULL");
316
0
    return BLOSC2_ERROR_FAILURE;
317
0
  }
318
  // Set the chunksize for the schunk, as it cannot be derived from storage
319
0
  int32_t chunksize = (int32_t) (*array)->extchunknitems * sc->typesize;
320
0
  sc->chunksize = chunksize;
321
322
  // Serialize the dimension info
323
0
  if (sc->nmetalayers >= BLOSC2_MAX_METALAYERS) {
324
0
    BLOSC_TRACE_ERROR("the number of metalayers for this schunk has been exceeded");
325
0
    return BLOSC2_ERROR_FAILURE;
326
0
  }
327
0
  uint8_t *smeta = NULL;
328
0
  int32_t smeta_len = b2nd_serialize_meta(ctx->ndim,
329
0
                                          (*array)->shape,
330
0
                                          (*array)->chunkshape,
331
0
                                          (*array)->blockshape,
332
0
                                          (*array)->dtype,
333
0
                                          (*array)->dtype_format,
334
0
                                          &smeta);
335
0
  if (smeta_len < 0) {
336
0
    BLOSC_TRACE_ERROR("error during serializing dims info for Blosc2 NDim");
337
0
    return BLOSC2_ERROR_FAILURE;
338
0
  }
339
340
  // And store it in b2nd metalayer
341
0
  if (blosc2_meta_add(sc, "b2nd", smeta, smeta_len) < 0) {
342
0
    return BLOSC2_ERROR_FAILURE;
343
0
  }
344
345
0
  free(smeta);
346
347
0
  for (int i = 0; i < ctx->nmetalayers; ++i) {
348
0
    char *name = ctx->metalayers[i].name;
349
0
    uint8_t *data = ctx->metalayers[i].content;
350
0
    int32_t size = ctx->metalayers[i].content_len;
351
0
    if (blosc2_meta_add(sc, name, data, size) < 0) {
352
0
      BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
353
0
    }
354
0
  }
355
356
0
  if ((*array)->extchunknitems * sc->typesize > BLOSC2_MAX_BUFFERSIZE){
357
0
    BLOSC_TRACE_ERROR("Chunksize exceeds maximum of %d", BLOSC2_MAX_BUFFERSIZE);
358
0
    return BLOSC2_ERROR_MAX_BUFSIZE_EXCEEDED;
359
0
  }
360
  // Fill schunk with uninit values
361
0
  if ((*array)->nitems != 0) {
362
0
    int64_t nchunks = (*array)->extnitems / (*array)->chunknitems;
363
0
    int64_t nitems = nchunks * (*array)->extchunknitems;
364
0
    BLOSC_ERROR(blosc2_schunk_fill_special(sc, nitems, special_value, chunksize));
365
0
  }
366
0
  (*array)->sc = sc;
367
368
0
  return BLOSC2_ERROR_SUCCESS;
369
0
}
370
371
372
0
int b2nd_uninit(b2nd_context_t *ctx, b2nd_array_t **array) {
373
0
  BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER);
374
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
375
376
0
  BLOSC_ERROR(array_new(ctx, BLOSC2_SPECIAL_UNINIT, array));
377
378
0
  return BLOSC2_ERROR_SUCCESS;
379
0
}
380
381
382
0
int b2nd_empty(b2nd_context_t *ctx, b2nd_array_t **array) {
383
0
  BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER);
384
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
385
386
  // Fill with zeros to avoid variable cratios
387
0
  BLOSC_ERROR(array_new(ctx, BLOSC2_SPECIAL_ZERO, array));
388
389
0
  return BLOSC2_ERROR_SUCCESS;
390
0
}
391
392
393
0
int b2nd_zeros(b2nd_context_t *ctx, b2nd_array_t **array) {
394
0
  BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER);
395
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
396
397
0
  BLOSC_ERROR(array_new(ctx, BLOSC2_SPECIAL_ZERO, array));
398
399
0
  return BLOSC2_ERROR_SUCCESS;
400
0
}
401
402
403
0
int b2nd_nans(b2nd_context_t *ctx, b2nd_array_t **array) {
404
0
  BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER);
405
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
406
407
0
  BLOSC_ERROR(array_new(ctx, BLOSC2_SPECIAL_NAN, array));
408
409
0
  const int32_t typesize = (*array)->sc->typesize;
410
0
  if (typesize != 4 && typesize != 8)
411
0
  {
412
0
    BLOSC_TRACE_ERROR("Unsupported typesize for NaN");
413
0
    return BLOSC2_ERROR_DATA;
414
0
  }
415
416
0
  return BLOSC2_ERROR_SUCCESS;
417
0
}
418
419
420
0
int b2nd_full(b2nd_context_t *ctx, b2nd_array_t **array, const void *fill_value) {
421
0
  BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER);
422
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
423
424
0
  BLOSC_ERROR(b2nd_empty(ctx, array));
425
426
0
  int32_t chunkbytes = (int32_t) (*array)->extchunknitems * (*array)->sc->typesize;
427
428
0
  blosc2_cparams *cparams;
429
0
  if (blosc2_schunk_get_cparams((*array)->sc, &cparams) != 0) {
430
0
    BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
431
0
  }
432
433
0
  int32_t chunksize = BLOSC_EXTENDED_HEADER_LENGTH + (*array)->sc->typesize;
434
0
  uint8_t *chunk = malloc(chunksize);
435
0
  BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC);
436
0
  if (blosc2_chunk_repeatval(*cparams, chunkbytes, chunk, chunksize, fill_value) < 0) {
437
0
    BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
438
0
  }
439
0
  free(cparams);
440
441
0
  for (int i = 0; i < (*array)->sc->nchunks; ++i) {
442
0
    if (blosc2_schunk_update_chunk((*array)->sc, i, chunk, true) < 0) {
443
0
      BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
444
0
    }
445
0
  }
446
0
  free(chunk);
447
448
0
  return BLOSC2_ERROR_SUCCESS;
449
0
}
450
451
452
0
int b2nd_from_schunk(blosc2_schunk *schunk, b2nd_array_t **array) {
453
0
  BLOSC_ERROR_NULL(schunk, BLOSC2_ERROR_NULL_POINTER);
454
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
455
456
0
  if (schunk == NULL) {
457
0
    BLOSC_TRACE_ERROR("Schunk is null");
458
0
    return BLOSC2_ERROR_NULL_POINTER;
459
0
  }
460
461
0
  blosc2_cparams *cparams;
462
0
  if (blosc2_schunk_get_cparams(schunk, &cparams) < 0) {
463
0
    BLOSC_TRACE_ERROR("Blosc error");
464
0
    return BLOSC2_ERROR_NULL_POINTER;
465
0
  }
466
0
  free(cparams);
467
468
0
  b2nd_context_t params = {0};
469
0
  params.b2_storage = schunk->storage;
470
471
  // Deserialize the b2nd metalayer
472
0
  uint8_t *smeta;
473
0
  int32_t smeta_len;
474
0
  if (blosc2_meta_get(schunk, "b2nd", &smeta, &smeta_len) < 0) {
475
    // Try with a caterva metalayer; we are meant to be backward compatible with it
476
0
    if (blosc2_meta_get(schunk, "caterva", &smeta, &smeta_len) < 0) {
477
0
      BLOSC_ERROR(BLOSC2_ERROR_METALAYER_NOT_FOUND);
478
0
    }
479
0
  }
480
0
  BLOSC_ERROR(b2nd_deserialize_meta(smeta, smeta_len, &params.ndim, params.shape,
481
0
                                    params.chunkshape, params.blockshape, &params.dtype,
482
0
                                    &params.dtype_format));
483
0
  free(smeta);
484
485
0
  BLOSC_ERROR(array_without_schunk(&params, array));
486
0
  free(params.dtype);
487
488
0
  (*array)->sc = schunk;
489
490
0
  if ((*array) == NULL) {
491
0
    BLOSC_TRACE_ERROR("Error creating a b2nd container from a frame");
492
0
    return BLOSC2_ERROR_NULL_POINTER;
493
0
  }
494
495
0
  return BLOSC2_ERROR_SUCCESS;
496
0
}
497
498
499
int b2nd_to_cframe(const b2nd_array_t *array, uint8_t **cframe, int64_t *cframe_len,
500
0
                   bool *needs_free) {
501
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
502
0
  BLOSC_ERROR_NULL(cframe, BLOSC2_ERROR_NULL_POINTER);
503
0
  BLOSC_ERROR_NULL(cframe_len, BLOSC2_ERROR_NULL_POINTER);
504
0
  BLOSC_ERROR_NULL(needs_free, BLOSC2_ERROR_NULL_POINTER);
505
506
0
  *cframe_len = blosc2_schunk_to_buffer(array->sc, cframe, needs_free);
507
0
  if (*cframe_len <= 0) {
508
0
    BLOSC_TRACE_ERROR("Error serializing the b2nd array");
509
0
    return BLOSC2_ERROR_FAILURE;
510
0
  }
511
0
  return BLOSC2_ERROR_SUCCESS;
512
0
}
513
514
515
0
int b2nd_from_cframe(uint8_t *cframe, int64_t cframe_len, bool copy, b2nd_array_t **array) {
516
0
  BLOSC_ERROR_NULL(cframe, BLOSC2_ERROR_NULL_POINTER);
517
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
518
519
0
  blosc2_schunk *sc = blosc2_schunk_from_buffer(cframe, cframe_len, copy);
520
0
  if (sc == NULL) {
521
0
    BLOSC_TRACE_ERROR("Blosc error");
522
0
    return BLOSC2_ERROR_FAILURE;
523
0
  }
524
  // ...and create a b2nd array out of it
525
0
  BLOSC_ERROR(b2nd_from_schunk(sc, array));
526
527
0
  return BLOSC2_ERROR_SUCCESS;
528
0
}
529
530
531
0
int b2nd_open(const char *urlpath, b2nd_array_t **array) {
532
0
  BLOSC_ERROR_NULL(urlpath, BLOSC2_ERROR_NULL_POINTER);
533
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
534
535
0
  blosc2_schunk *sc = blosc2_schunk_open(urlpath);
536
537
  // ...and create a b2nd array out of it
538
0
  BLOSC_ERROR(b2nd_from_schunk(sc, array));
539
540
0
  return BLOSC2_ERROR_SUCCESS;
541
0
}
542
543
544
0
int b2nd_open_offset(const char *urlpath, b2nd_array_t **array, int64_t offset) {
545
0
  BLOSC_ERROR_NULL(urlpath, BLOSC2_ERROR_NULL_POINTER);
546
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
547
548
0
  blosc2_schunk *sc = blosc2_schunk_open_offset(urlpath, offset);
549
550
  // ...and create a b2nd array out of it
551
0
  BLOSC_ERROR(b2nd_from_schunk(sc, array));
552
553
0
  return BLOSC2_ERROR_SUCCESS;
554
0
}
555
556
557
0
int b2nd_free(b2nd_array_t *array) {
558
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
559
560
0
  if (array) {
561
0
    if (array->sc != NULL) {
562
0
      blosc2_schunk_free(array->sc);
563
0
    }
564
0
    free(array->dtype);
565
0
    free(array);
566
0
  }
567
0
  return BLOSC2_ERROR_SUCCESS;
568
0
}
569
570
571
0
int b2nd_from_cbuffer(b2nd_context_t *ctx, b2nd_array_t **array, const void *buffer, int64_t buffersize) {
572
0
  BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER);
573
0
  BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER);
574
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
575
576
0
  BLOSC_ERROR(b2nd_empty(ctx, array));
577
578
0
  if (buffersize < (int64_t) (*array)->nitems * (*array)->sc->typesize) {
579
0
    BLOSC_TRACE_ERROR("The buffersize (%lld) is smaller than the array size (%lld)",
580
0
                        (long long) buffersize, (long long) (*array)->nitems * (*array)->sc->typesize);
581
0
    BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
582
0
  }
583
584
0
  if ((*array)->nitems == 0) {
585
0
    return BLOSC2_ERROR_SUCCESS;
586
0
  }
587
588
0
  int64_t start[B2ND_MAX_DIM] = {0};
589
0
  int64_t *stop = (*array)->shape;
590
0
  int64_t *shape = (*array)->shape;
591
0
  BLOSC_ERROR(b2nd_set_slice_cbuffer(buffer, shape, buffersize, start, stop, *array));
592
593
0
  return BLOSC2_ERROR_SUCCESS;
594
0
}
595
596
597
int b2nd_to_cbuffer(const b2nd_array_t *array, void *buffer,
598
0
                    int64_t buffersize) {
599
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
600
0
  BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER);
601
602
0
  if (buffersize < (int64_t) array->nitems * array->sc->typesize) {
603
0
    BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
604
0
  }
605
606
0
  if (array->nitems == 0) {
607
0
    return BLOSC2_ERROR_SUCCESS;
608
0
  }
609
610
0
  int64_t start[B2ND_MAX_DIM] = {0};
611
0
  const int64_t *stop = array->shape;
612
0
  BLOSC_ERROR(b2nd_get_slice_cbuffer(array, start, stop, buffer, array->shape, buffersize));
613
0
  return BLOSC2_ERROR_SUCCESS;
614
0
}
615
616
0
int b2nd_get_slice_nchunks(const b2nd_array_t *array, const int64_t *start, const int64_t *stop, int64_t **chunks_idx) {
617
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
618
0
  BLOSC_ERROR_NULL(start, BLOSC2_ERROR_NULL_POINTER);
619
0
  BLOSC_ERROR_NULL(stop, BLOSC2_ERROR_NULL_POINTER);
620
621
0
  int8_t ndim = array->ndim;
622
623
0
  if (array->nitems == 0){  
624
0
    *chunks_idx = NULL;
625
0
    return 0;
626
0
  }
627
628
  // 0-dim case
629
0
  if (ndim == 0) {
630
0
    *chunks_idx = malloc(1 * sizeof(int64_t));
631
0
    *chunks_idx[0] = 0;
632
0
    return 1;
633
0
  }
634
635
0
  int64_t chunks_in_array[B2ND_MAX_DIM] = {0};
636
0
  for (int i = 0; i < ndim; ++i) {
637
0
    chunks_in_array[i] = array->extshape[i] / array->chunkshape[i];
638
0
  }
639
640
0
  int64_t chunks_in_array_strides[B2ND_MAX_DIM];
641
0
  chunks_in_array_strides[ndim - 1] = 1;
642
0
  for (int i = ndim - 2; i >= 0; --i) {
643
0
    chunks_in_array_strides[i] = chunks_in_array_strides[i + 1] * chunks_in_array[i + 1];
644
0
  }
645
646
  // Compute the number of chunks to update
647
0
  int64_t update_start[B2ND_MAX_DIM];
648
0
  int64_t update_shape[B2ND_MAX_DIM];
649
650
0
  int64_t update_nchunks = 1;
651
0
  for (int i = 0; i < ndim; ++i) {
652
0
    int64_t pos = 0;
653
0
    while (pos <= start[i]) {
654
0
      pos += array->chunkshape[i];
655
0
    }
656
0
    update_start[i] = pos / array->chunkshape[i] - 1;
657
0
    while (pos < stop[i]) {
658
0
      pos += array->chunkshape[i];
659
0
    }
660
0
    update_shape[i] = pos / array->chunkshape[i] - update_start[i];
661
0
    update_nchunks *= update_shape[i];
662
0
  }
663
664
0
  int nchunks = 0;
665
  // Initially we do not know the number of chunks that will be affected
666
0
  *chunks_idx = malloc(array->sc->nchunks * sizeof(int64_t));
667
0
  int64_t *ptr = *chunks_idx;
668
0
  for (int update_nchunk = 0; update_nchunk < update_nchunks; ++update_nchunk) {
669
0
    int64_t nchunk_ndim[B2ND_MAX_DIM] = {0};
670
0
    blosc2_unidim_to_multidim(ndim, update_shape, update_nchunk, nchunk_ndim);
671
0
    for (int i = 0; i < ndim; ++i) {
672
0
      nchunk_ndim[i] += update_start[i];
673
0
    }
674
0
    int64_t nchunk;
675
0
    blosc2_multidim_to_unidim(nchunk_ndim, ndim, chunks_in_array_strides, &nchunk);
676
677
    // Check if the chunk is inside the slice domain
678
0
    int64_t chunk_start[B2ND_MAX_DIM] = {0};
679
0
    int64_t chunk_stop[B2ND_MAX_DIM] = {0};
680
0
    for (int i = 0; i < ndim; ++i) {
681
0
      chunk_start[i] = nchunk_ndim[i] * array->chunkshape[i];
682
0
      chunk_stop[i] = chunk_start[i] + array->chunkshape[i];
683
0
      if (chunk_stop[i] > array->shape[i]) {
684
0
        chunk_stop[i] = array->shape[i];
685
0
      }
686
0
    }
687
0
    bool chunk_empty = false;
688
0
    for (int i = 0; i < ndim; ++i) {
689
0
      chunk_empty |= (chunk_stop[i] <= start[i] || chunk_start[i] >= stop[i]);
690
0
    }
691
0
    if (chunk_empty) {
692
0
      continue;
693
0
    }
694
695
0
    ptr[nchunks] = nchunk;
696
0
    nchunks++;
697
0
  }
698
699
0
  if (nchunks < array->sc->nchunks) {
700
0
    *chunks_idx = realloc(ptr, nchunks * sizeof(int64_t));
701
0
  }
702
703
0
  return nchunks;
704
0
}
705
706
707
// Check whether the slice defined by start and stop is a single chunk and contiguous
708
// in the C order. This is a fast path for the get_slice and set_slice functions.
709
int64_t nchunk_fastpath(const b2nd_array_t *array, const int64_t *start,
710
0
                        const int64_t *stop, const int64_t slice_size) {
711
0
  if (slice_size != array->chunknitems) {
712
0
    return -1;
713
0
  }
714
715
0
  int ndim = (int) array->ndim;
716
717
0
  int k = 0;
718
0
  for (int i = 0; i < ndim; ++i) {
719
    // The slice needs to correspond to a whole chunk (without padding)
720
0
    if (start[i] % array->chunkshape[i] != 0) {
721
0
      return -1;
722
0
    }
723
0
    if (stop[i] - start[i] != array->chunkshape[i]) {
724
0
      return -1;
725
0
    }
726
727
    // There needs to exist 0 <= k <= ndim such that:
728
    // - for i < k, blockshape[i] == 1
729
    // - for i == k, blockshape[i] divides chunkshape[i]
730
    // - for i > k, blockshape[i] == chunkshape[i]
731
0
    if (array->chunkshape[i] % array->blockshape[i] != 0) {
732
0
      return -1;
733
0
    }
734
0
    if (i > k && array->chunkshape[i] != array->blockshape[i]) {
735
0
      return -1;
736
0
    }
737
0
    if (i == k && array->blockshape[i] == 1) {
738
0
      k++;
739
0
    }
740
0
  }
741
  // Compute the chunk number
742
0
  int64_t *chunks_idx;
743
0
  int nchunks = b2nd_get_slice_nchunks(array, start, stop, &chunks_idx);
744
0
  if (nchunks != 1) {
745
0
    free(chunks_idx);
746
0
    BLOSC_TRACE_ERROR("The number of chunks to read is not 1; go fix the code");
747
0
    BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
748
0
  }
749
0
  int64_t nchunk = chunks_idx[0];
750
0
  free(chunks_idx);
751
752
0
  return nchunk;
753
0
}
754
755
756
// Setting and getting slices
757
int get_set_slice(void *buffer, int64_t buffersize, const int64_t *start, const int64_t *stop,
758
0
                  const int64_t *shape, b2nd_array_t *array, bool set_slice) {
759
0
  BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER);
760
0
  BLOSC_ERROR_NULL(start, BLOSC2_ERROR_NULL_POINTER);
761
0
  BLOSC_ERROR_NULL(stop, BLOSC2_ERROR_NULL_POINTER);
762
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
763
0
  if (buffersize < 0) {
764
0
    BLOSC_TRACE_ERROR("buffersize is < 0");
765
0
    BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
766
0
  }
767
768
0
  uint8_t *buffer_b = buffer;
769
0
  int8_t ndim = array->ndim;
770
0
  if (!set_slice) {
771
    // get_slice paths may touch only a subset of the destination buffer.
772
    // Pre-initialize so unread regions are defined and deterministic.
773
0
    memset(buffer_b, 0, (size_t)buffersize);
774
0
  }
775
776
  // 0-dim case
777
0
  if (ndim == 0) {
778
0
    if (set_slice) {
779
0
      int32_t chunk_size = array->sc->typesize + BLOSC2_MAX_OVERHEAD;
780
0
      uint8_t *chunk = malloc(chunk_size);
781
0
      BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC);
782
0
      if (blosc2_compress_ctx(array->sc->cctx, buffer_b, array->sc->typesize, chunk, chunk_size) < 0) {
783
0
        BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
784
0
      }
785
0
      if (blosc2_schunk_update_chunk(array->sc, 0, chunk, false) < 0) {
786
0
        BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
787
0
      }
788
789
0
    } else {
790
0
      if (blosc2_schunk_decompress_chunk(array->sc, 0, buffer_b, array->sc->typesize) < 0) {
791
0
        BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
792
0
      }
793
0
    }
794
0
    return BLOSC2_ERROR_SUCCESS;
795
0
  }
796
797
0
  if (array->nitems == 0) {
798
0
    return BLOSC2_ERROR_SUCCESS;
799
0
  }
800
801
0
  int64_t nelems_slice = 1;
802
0
  for (int i = 0; i < array->ndim; ++i) {
803
0
    if (stop[i] - start[i] > shape[i]) {
804
0
      BLOSC_TRACE_ERROR("The buffer shape can not be smaller than the slice shape");
805
0
      return BLOSC2_ERROR_INVALID_PARAM;
806
0
    }
807
0
    nelems_slice *= stop[i] - start[i];
808
0
  }
809
0
  int64_t slice_nbytes = nelems_slice * array->sc->typesize;
810
0
  int32_t data_nbytes = (int32_t) array->extchunknitems * array->sc->typesize;
811
812
0
  if (buffersize < slice_nbytes) {
813
0
    BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
814
0
  }
815
816
  // Check for fast path for aligned slices with chunks and blocks (only 1 chunk is supported)
817
0
  int64_t nchunk = nchunk_fastpath(array, start, stop, nelems_slice);
818
0
  if (nchunk >= 0) {
819
0
    if (set_slice) {
820
      // Fast path for set. Let's set the chunk buffer straight into the array.
821
      // Compress the chunk
822
0
      int32_t chunk_nbytes = data_nbytes + BLOSC2_MAX_OVERHEAD;
823
0
      uint8_t *chunk = malloc(chunk_nbytes);
824
0
      BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC);
825
0
      int brc;
826
      // Update current_chunk in case a prefilter is applied
827
0
      array->sc->current_nchunk = nchunk;
828
0
      brc = blosc2_compress_ctx(array->sc->cctx, buffer, data_nbytes, chunk, chunk_nbytes);
829
0
      if (brc < 0) {
830
0
        BLOSC_TRACE_ERROR("Blosc can not compress the data");
831
0
        BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
832
0
      }
833
0
      int64_t brc_ = blosc2_schunk_update_chunk(array->sc, nchunk, chunk, false);
834
0
      if (brc_ < 0) {
835
0
        BLOSC_TRACE_ERROR("Blosc can not update the chunk");
836
0
        BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
837
0
      }
838
      // We are done
839
0
      return BLOSC2_ERROR_SUCCESS;
840
0
    }
841
0
    else {
842
      // Fast path for get. Let's read the chunk straight into the buffer.
843
0
      if (blosc2_schunk_decompress_chunk(array->sc, nchunk, buffer, (int32_t) slice_nbytes) < 0) {
844
0
        BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
845
0
      }
846
0
      return BLOSC2_ERROR_SUCCESS;
847
0
    }
848
0
  }
849
850
  // Slow path for set and get
851
852
0
  uint8_t *data = malloc(data_nbytes);
853
0
  BLOSC_ERROR_NULL(data, BLOSC2_ERROR_MEMORY_ALLOC);
854
855
0
  int64_t chunks_in_array[B2ND_MAX_DIM] = {0};
856
0
  for (int i = 0; i < ndim; ++i) {
857
0
    chunks_in_array[i] = array->extshape[i] / array->chunkshape[i];
858
0
  }
859
860
0
  int64_t chunks_in_array_strides[B2ND_MAX_DIM];
861
0
  chunks_in_array_strides[ndim - 1] = 1;
862
0
  for (int i = ndim - 2; i >= 0; --i) {
863
0
    chunks_in_array_strides[i] = chunks_in_array_strides[i + 1] * chunks_in_array[i + 1];
864
0
  }
865
866
0
  int64_t blocks_in_chunk[B2ND_MAX_DIM] = {0};
867
0
  for (int i = 0; i < ndim; ++i) {
868
0
    blocks_in_chunk[i] = array->extchunkshape[i] / array->blockshape[i];
869
0
  }
870
871
  // Compute the number of chunks to update
872
0
  int64_t update_start[B2ND_MAX_DIM];
873
0
  int64_t update_shape[B2ND_MAX_DIM];
874
875
0
  int64_t update_nchunks = 1;
876
0
  for (int i = 0; i < ndim; ++i) {
877
0
    int64_t pos = 0;
878
0
    while (pos <= start[i]) {
879
0
      pos += array->chunkshape[i];
880
0
    }
881
0
    update_start[i] = pos / array->chunkshape[i] - 1;
882
0
    while (pos < stop[i]) {
883
0
      pos += array->chunkshape[i];
884
0
    }
885
0
    update_shape[i] = pos / array->chunkshape[i] - update_start[i];
886
0
    update_nchunks *= update_shape[i];
887
0
  }
888
889
0
  for (int update_nchunk = 0; update_nchunk < update_nchunks; ++update_nchunk) {
890
0
    int64_t nchunk_ndim[B2ND_MAX_DIM] = {0};
891
0
    blosc2_unidim_to_multidim(ndim, update_shape, update_nchunk, nchunk_ndim);
892
0
    for (int i = 0; i < ndim; ++i) {
893
0
      nchunk_ndim[i] += update_start[i];
894
0
    }
895
0
    int64_t nchunk;
896
0
    blosc2_multidim_to_unidim(nchunk_ndim, ndim, chunks_in_array_strides, &nchunk);
897
898
    // Check if the chunk needs to be updated
899
0
    int64_t chunk_start[B2ND_MAX_DIM] = {0};
900
0
    int64_t chunk_stop[B2ND_MAX_DIM] = {0};
901
0
    for (int i = 0; i < ndim; ++i) {
902
0
      chunk_start[i] = nchunk_ndim[i] * array->chunkshape[i];
903
0
      chunk_stop[i] = chunk_start[i] + array->chunkshape[i];
904
0
      if (chunk_stop[i] > array->shape[i]) {
905
0
        chunk_stop[i] = array->shape[i];
906
0
      }
907
0
    }
908
0
    bool chunk_empty = false;
909
0
    for (int i = 0; i < ndim; ++i) {
910
0
      chunk_empty |= (chunk_stop[i] <= start[i] || chunk_start[i] >= stop[i]);
911
0
    }
912
0
    if (chunk_empty) {
913
0
      continue;
914
0
    }
915
916
0
    int32_t nblocks = (int32_t) array->extchunknitems / array->blocknitems;
917
0
    if (set_slice) {
918
      // Check if all the chunk is going to be updated and avoid the decompression
919
0
      bool decompress_chunk = false;
920
0
      for (int i = 0; i < ndim; ++i) {
921
0
        decompress_chunk |= (chunk_start[i] < start[i] || chunk_stop[i] > stop[i]);
922
0
      }
923
924
0
      if (decompress_chunk) {
925
0
        int err = blosc2_schunk_decompress_chunk(array->sc, nchunk, data, data_nbytes);
926
0
        if (err < 0) {
927
0
          BLOSC_TRACE_ERROR("Error decompressing chunk");
928
0
          BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
929
0
        }
930
0
      } else {
931
        // Avoid writing non zero padding from previous chunk
932
0
        memset(data, 0, data_nbytes);
933
0
      }
934
0
    } else {
935
0
      bool *block_maskout = malloc(nblocks);
936
0
      BLOSC_ERROR_NULL(block_maskout, BLOSC2_ERROR_MEMORY_ALLOC);
937
0
      for (int nblock = 0; nblock < nblocks; ++nblock) {
938
0
        int64_t nblock_ndim[B2ND_MAX_DIM] = {0};
939
0
        blosc2_unidim_to_multidim(ndim, blocks_in_chunk, nblock, nblock_ndim);
940
941
        // Check if the block needs to be updated
942
0
        int64_t block_start[B2ND_MAX_DIM] = {0};
943
0
        int64_t block_stop[B2ND_MAX_DIM] = {0};
944
0
        for (int i = 0; i < ndim; ++i) {
945
0
          block_start[i] = nblock_ndim[i] * array->blockshape[i];
946
0
          block_stop[i] = block_start[i] + array->blockshape[i];
947
0
          block_start[i] += chunk_start[i];
948
0
          block_stop[i] += chunk_start[i];
949
950
0
          if (block_start[i] > chunk_stop[i]) {
951
0
            block_start[i] = chunk_stop[i];
952
0
          }
953
0
          if (block_stop[i] > chunk_stop[i]) {
954
0
            block_stop[i] = chunk_stop[i];
955
0
          }
956
0
        }
957
958
0
        bool block_empty = false;
959
0
        for (int i = 0; i < ndim; ++i) {
960
0
          block_empty |= (block_stop[i] <= start[i] || block_start[i] >= stop[i]);
961
0
        }
962
0
        block_maskout[nblock] = block_empty ? true : false;
963
0
      }
964
965
0
      if (blosc2_set_maskout(array->sc->dctx, block_maskout, nblocks) != BLOSC2_ERROR_SUCCESS) {
966
0
        BLOSC_TRACE_ERROR("Error setting the maskout");
967
0
        BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
968
0
      }
969
970
0
      int err = blosc2_schunk_decompress_chunk(array->sc, nchunk, data, data_nbytes);
971
0
      if (err < 0) {
972
0
        BLOSC_TRACE_ERROR("Error decompressing chunk");
973
0
        BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
974
0
      }
975
976
0
      free(block_maskout);
977
0
    }
978
979
    // Iterate over blocks
980
981
0
    for (int nblock = 0; nblock < nblocks; ++nblock) {
982
0
      int64_t nblock_ndim[B2ND_MAX_DIM] = {0};
983
0
      blosc2_unidim_to_multidim(ndim, blocks_in_chunk, nblock, nblock_ndim);
984
985
      // Check if the block needs to be updated
986
0
      int64_t block_start[B2ND_MAX_DIM] = {0};
987
0
      int64_t block_stop[B2ND_MAX_DIM] = {0};
988
0
      for (int i = 0; i < ndim; ++i) {
989
0
        block_start[i] = nblock_ndim[i] * array->blockshape[i];
990
0
        block_stop[i] = block_start[i] + array->blockshape[i];
991
0
        block_start[i] += chunk_start[i];
992
0
        block_stop[i] += chunk_start[i];
993
994
0
        if (block_start[i] > chunk_stop[i]) {
995
0
          block_start[i] = chunk_stop[i];
996
0
        }
997
0
        if (block_stop[i] > chunk_stop[i]) {
998
0
          block_stop[i] = chunk_stop[i];
999
0
        }
1000
0
      }
1001
0
      int64_t block_shape[B2ND_MAX_DIM] = {0};
1002
0
      for (int i = 0; i < ndim; ++i) {
1003
0
        block_shape[i] = block_stop[i] - block_start[i];
1004
0
      }
1005
0
      bool block_empty = false;
1006
0
      for (int i = 0; i < ndim; ++i) {
1007
0
        block_empty |= (block_stop[i] <= start[i] || block_start[i] >= stop[i]);
1008
0
      }
1009
0
      if (block_empty) {
1010
0
        continue;
1011
0
      }
1012
1013
      // compute the start of the slice inside the block
1014
0
      int64_t slice_start[B2ND_MAX_DIM] = {0};
1015
0
      for (int i = 0; i < ndim; ++i) {
1016
0
        if (block_start[i] < start[i]) {
1017
0
          slice_start[i] = start[i] - block_start[i];
1018
0
        } else {
1019
0
          slice_start[i] = 0;
1020
0
        }
1021
0
        slice_start[i] += block_start[i];
1022
0
      }
1023
1024
0
      int64_t slice_stop[B2ND_MAX_DIM] = {0};
1025
0
      for (int i = 0; i < ndim; ++i) {
1026
0
        if (block_stop[i] > stop[i]) {
1027
0
          slice_stop[i] = block_shape[i] - (block_stop[i] - stop[i]);
1028
0
        } else {
1029
0
          slice_stop[i] = block_stop[i] - block_start[i];
1030
0
        }
1031
0
        slice_stop[i] += block_start[i];
1032
0
      }
1033
1034
0
      int64_t slice_shape[B2ND_MAX_DIM] = {0};
1035
0
      for (int i = 0; i < ndim; ++i) {
1036
0
        slice_shape[i] = slice_stop[i] - slice_start[i];
1037
0
      }
1038
1039
0
      uint8_t *src = &buffer_b[0];
1040
1041
0
      int64_t src_start[B2ND_MAX_DIM] = {0};
1042
0
      int64_t src_stop[B2ND_MAX_DIM] = {0};
1043
0
      for (int i = 0; i < ndim; ++i) {
1044
0
        src_start[i] = slice_start[i] - start[i];
1045
0
        src_stop[i] = slice_stop[i] - start[i];
1046
0
      }
1047
1048
0
      uint8_t *dst = &data[nblock * array->blocknitems * array->sc->typesize];
1049
0
      int64_t dst_pad_shape[B2ND_MAX_DIM];
1050
0
      for (int i = 0; i < ndim; ++i) {
1051
0
        dst_pad_shape[i] = array->blockshape[i];
1052
0
      }
1053
1054
0
      int64_t dst_start[B2ND_MAX_DIM] = {0};
1055
0
      int64_t dst_stop[B2ND_MAX_DIM] = {0};
1056
0
      for (int i = 0; i < ndim; ++i) {
1057
0
        dst_start[i] = slice_start[i] - block_start[i];
1058
0
        dst_stop[i] = dst_start[i] + slice_shape[i];
1059
0
      }
1060
1061
0
      if (set_slice) {
1062
0
        b2nd_copy_buffer2(ndim, array->sc->typesize,
1063
0
                          src, shape, src_start, src_stop,
1064
0
                          dst, dst_pad_shape, dst_start);
1065
0
      } else {
1066
0
        b2nd_copy_buffer2(ndim, array->sc->typesize,
1067
0
                          dst, dst_pad_shape, dst_start, dst_stop,
1068
0
                          src, shape, src_start);
1069
0
      }
1070
0
    }
1071
1072
0
    if (set_slice) {
1073
      // Recompress the data
1074
0
      int32_t chunk_nbytes = data_nbytes + BLOSC2_MAX_OVERHEAD;
1075
0
      uint8_t *chunk = malloc(chunk_nbytes);
1076
0
      BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC);
1077
0
      int brc;
1078
      // Update current_chunk in case a prefilter is applied
1079
0
      array->sc->current_nchunk = nchunk;
1080
0
      brc = blosc2_compress_ctx(array->sc->cctx, data, data_nbytes, chunk, chunk_nbytes);
1081
0
      if (brc < 0) {
1082
0
        BLOSC_TRACE_ERROR("Blosc can not compress the data");
1083
0
        BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
1084
0
      }
1085
0
      int64_t brc_ = blosc2_schunk_update_chunk(array->sc, nchunk, chunk, false);
1086
0
      if (brc_ < 0) {
1087
0
        BLOSC_TRACE_ERROR("Blosc can not update the chunk");
1088
0
        BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
1089
0
      }
1090
0
    }
1091
0
  }
1092
1093
0
  free(data);
1094
1095
0
  return BLOSC2_ERROR_SUCCESS;
1096
0
}
1097
1098
1099
int b2nd_get_slice_cbuffer(const b2nd_array_t *array, const int64_t *start, const int64_t *stop,
1100
0
                           void *buffer, const int64_t *buffershape, int64_t buffersize) {
1101
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1102
0
  BLOSC_ERROR_NULL(start, BLOSC2_ERROR_NULL_POINTER);
1103
0
  BLOSC_ERROR_NULL(stop, BLOSC2_ERROR_NULL_POINTER);
1104
0
  BLOSC_ERROR_NULL(buffershape, BLOSC2_ERROR_NULL_POINTER);
1105
0
  BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER);
1106
1107
0
  BLOSC_ERROR(get_set_slice(buffer, buffersize, start, stop, buffershape, (b2nd_array_t *)array, false));
1108
1109
0
  return BLOSC2_ERROR_SUCCESS;
1110
0
}
1111
1112
1113
int b2nd_set_slice_cbuffer(const void *buffer, const int64_t *buffershape, int64_t buffersize,
1114
                           const int64_t *start, const int64_t *stop,
1115
0
                           b2nd_array_t *array) {
1116
0
  BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER);
1117
0
  BLOSC_ERROR_NULL(start, BLOSC2_ERROR_NULL_POINTER);
1118
0
  BLOSC_ERROR_NULL(stop, BLOSC2_ERROR_NULL_POINTER);
1119
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1120
1121
0
  BLOSC_ERROR(get_set_slice((void*)buffer, buffersize, start, stop, (int64_t *)buffershape, array, true));
1122
1123
0
  return BLOSC2_ERROR_SUCCESS;
1124
0
}
1125
1126
1127
int b2nd_get_slice(b2nd_context_t *ctx, b2nd_array_t **array, const b2nd_array_t *src, const int64_t *start,
1128
0
                   const int64_t *stop) {
1129
0
  BLOSC_ERROR_NULL(src, BLOSC2_ERROR_NULL_POINTER);
1130
0
  BLOSC_ERROR_NULL(start, BLOSC2_ERROR_NULL_POINTER);
1131
0
  BLOSC_ERROR_NULL(stop, BLOSC2_ERROR_NULL_POINTER);
1132
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1133
1134
0
  ctx->ndim = src->ndim;
1135
0
  for (int i = 0; i < src->ndim; ++i) {
1136
0
    ctx->shape[i] = stop[i] - start[i];
1137
0
  }
1138
1139
  // Add data
1140
0
  BLOSC_ERROR(b2nd_empty(ctx, array));
1141
1142
0
  if ((*array)->nitems == 0) {
1143
0
    return BLOSC2_ERROR_SUCCESS;
1144
0
  }
1145
1146
0
  int8_t ndim = (*array)->ndim;
1147
0
  int64_t chunks_in_array[B2ND_MAX_DIM] = {0};
1148
0
  for (int i = 0; i < ndim; ++i) {
1149
0
    chunks_in_array[i] = (*array)->extshape[i] / (*array)->chunkshape[i];
1150
0
  }
1151
0
  int64_t nchunks = (*array)->sc->nchunks;
1152
0
  for (int nchunk = 0; nchunk < nchunks; ++nchunk) {
1153
0
    int64_t nchunk_ndim[B2ND_MAX_DIM] = {0};
1154
0
    blosc2_unidim_to_multidim(ndim, chunks_in_array, nchunk, nchunk_ndim);
1155
1156
    // Check if the chunk needs to be updated
1157
0
    int64_t chunk_start[B2ND_MAX_DIM] = {0};
1158
0
    int64_t chunk_stop[B2ND_MAX_DIM] = {0};
1159
0
    int64_t chunk_shape[B2ND_MAX_DIM] = {0};
1160
0
    for (int i = 0; i < ndim; ++i) {
1161
0
      chunk_start[i] = nchunk_ndim[i] * (*array)->chunkshape[i];
1162
0
      chunk_stop[i] = chunk_start[i] + (*array)->chunkshape[i];
1163
0
      if (chunk_stop[i] > (*array)->shape[i]) {
1164
0
        chunk_stop[i] = (*array)->shape[i];
1165
0
      }
1166
0
      chunk_shape[i] = chunk_stop[i] - chunk_start[i];
1167
0
    }
1168
1169
0
    int64_t src_start[B2ND_MAX_DIM] = {0};
1170
0
    int64_t src_stop[B2ND_MAX_DIM] = {0};
1171
0
    for (int i = 0; i < ndim; ++i) {
1172
0
      src_start[i] = chunk_start[i] + start[i];
1173
0
      src_stop[i] = chunk_stop[i] + start[i];
1174
0
    }
1175
0
    int64_t buffersize = ctx->b2_storage->cparams->typesize;
1176
0
    for (int i = 0; i < ndim; ++i) {
1177
0
      buffersize *= chunk_shape[i];
1178
0
    }
1179
0
    uint8_t *buffer = malloc(buffersize);
1180
0
    BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_MEMORY_ALLOC);
1181
0
    BLOSC_ERROR(b2nd_get_slice_cbuffer(src, src_start, src_stop, buffer, chunk_shape,
1182
0
                                       buffersize));
1183
0
    BLOSC_ERROR(b2nd_set_slice_cbuffer(buffer, chunk_shape, buffersize, chunk_start,
1184
0
                                       chunk_stop, *array));
1185
0
    free(buffer);
1186
0
  }
1187
1188
0
  return BLOSC2_ERROR_SUCCESS;
1189
0
}
1190
1191
/**
1192
 * @brief Return a view of a b2nd array.
1193
 *
1194
 * @param array The memory pointer of the array which will be viewed.
1195
 * @param view The memory pointer where the view will be created.
1196
 * @param ctx1 The b2nd context for the new array, containing new shape and other metadata.
1197
 *
1198
 * @return An error code.
1199
 *
1200
 * @note This doesn't support slices of arrays and is only useful for adding (or removing) dimensions.
1201
 *
1202
 */
1203
0
int view_new(const b2nd_array_t *array, b2nd_array_t **view, b2nd_context_t *ctx1) {
1204
1205
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1206
0
  BLOSC_ERROR_NULL(view, BLOSC2_ERROR_NULL_POINTER);
1207
1208
  // The view is not contiguous (points to the original contiguous cframe which has different shape)
1209
  // so we set contiguous to false, which forces a copy when calling to_cframe
1210
0
  ctx1->b2_storage->contiguous = false;
1211
1212
1213
  /* Fill view with zeros */
1214
0
  BLOSC_ERROR(b2nd_zeros(ctx1, view));
1215
  // Free the chunks in base array
1216
0
  for (int i = 0; i < (*view)->sc->nchunks; i++) {
1217
0
    free((*view)->sc->data[i]);
1218
0
  }
1219
0
  free((*view)->sc->data);
1220
0
  (*view)->sc->view = true;
1221
0
  (*view)->sc->data = array->sc->data; // point view to the same data
1222
0
  (*view)->sc->frame = array->sc->frame; // if original array is contiguous, point to frame
1223
0
  (*view)->sc->nvlmetalayers = array->sc->nvlmetalayers; //
1224
0
  for (int i = 0; i< array->sc->nvlmetalayers; i++) {
1225
0
    (*view)->sc->vlmetalayers[i] = array->sc->vlmetalayers[i]; // add ptrs to vlmetalayers
1226
0
  }
1227
1228
0
  return BLOSC2_ERROR_SUCCESS;
1229
0
}
1230
1231
0
int b2nd_expand_dims(const b2nd_array_t *array, b2nd_array_t **view, const bool *axis, const uint8_t final_dims) {
1232
0
  for (int i = 0; i < array->sc->nmetalayers; ++i) {
1233
0
    if (strcmp(array->sc->metalayers[i]->name, "b2nd") != 0) {
1234
0
      BLOSC_TRACE_ERROR("Cannot expand dimensions of an array with non-b2nd metalayers");
1235
0
      return BLOSC2_ERROR_INVALID_PARAM;
1236
0
    }
1237
0
  }
1238
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1239
0
  BLOSC_ERROR_NULL(view, BLOSC2_ERROR_NULL_POINTER);
1240
1241
0
  uint8_t old_idx = 0;
1242
0
  int64_t newshape[B2ND_MAX_DIM];
1243
0
  int32_t newchunkshape[B2ND_MAX_DIM];
1244
0
  int32_t newblockshape[B2ND_MAX_DIM];
1245
1246
0
  for (int i = 0; i < final_dims; ++i) {
1247
0
    if (axis[i] == true) {
1248
0
      newshape[i] = 1;
1249
0
      newchunkshape[i] = 1;
1250
0
      newblockshape[i] = 1;
1251
0
    }
1252
0
    else {
1253
0
      if (old_idx == array->ndim) {
1254
0
        BLOSC_TRACE_ERROR("Error in axis list: original array has fewer dimensions than the axis list implies!");
1255
0
        return BLOSC2_ERROR_INVALID_PARAM;
1256
0
      }
1257
0
      newshape[i] = array->shape[old_idx];
1258
0
      newchunkshape[i] = array->chunkshape[old_idx];
1259
0
      newblockshape[i] = array->blockshape[old_idx];
1260
0
      old_idx++;
1261
0
    }
1262
0
  }
1263
1264
  //views only deal with cparams/dparams; storage is always in-memory (ephemeral).
1265
0
  blosc2_cparams cparams = *(array->sc->storage->cparams);
1266
0
  blosc2_dparams dparams = *(array->sc->storage->dparams);
1267
0
  blosc2_storage b2_storage1 = {.cparams=&cparams, .dparams=&dparams};
1268
1269
0
  b2nd_context_t *ctx1 = b2nd_create_ctx(&b2_storage1, final_dims, newshape,
1270
0
                                        newchunkshape, newblockshape, array->dtype,
1271
0
                                        array->dtype_format, NULL, 0);
1272
1273
0
  view_new(array, view, ctx1);
1274
0
  b2nd_free_ctx(ctx1);
1275
1276
0
  return BLOSC2_ERROR_SUCCESS;
1277
0
}
1278
1279
1280
0
int b2nd_squeeze(b2nd_array_t *array, b2nd_array_t **view) {
1281
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1282
0
  BLOSC_ERROR_NULL(view, BLOSC2_ERROR_NULL_POINTER);
1283
1284
0
  bool index[B2ND_MAX_DIM];
1285
1286
0
  for (int i = 0; i < array->ndim; ++i) {
1287
0
    if (array->shape[i] != 1) {
1288
0
      index[i] = false;
1289
0
    } else {
1290
0
      index[i] = true;
1291
0
    }
1292
0
  }
1293
0
  BLOSC_ERROR(b2nd_squeeze_index(array, view, index));
1294
1295
0
  return BLOSC2_ERROR_SUCCESS;
1296
0
}
1297
1298
1299
0
int b2nd_squeeze_index(b2nd_array_t *array, b2nd_array_t **view, const bool *index) {
1300
0
  for (int i = 0; i < array->sc->nmetalayers; ++i) {
1301
0
    if (strcmp(array->sc->metalayers[i]->name, "b2nd") != 0) {
1302
0
      BLOSC_TRACE_ERROR("Cannot squeeze dimensions of an array with non-b2nd metalayers");
1303
0
      return BLOSC2_ERROR_INVALID_PARAM;
1304
0
    }
1305
0
  }
1306
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1307
0
  BLOSC_ERROR_NULL(view, BLOSC2_ERROR_NULL_POINTER);
1308
1309
0
  uint8_t nones = 0;
1310
0
  int64_t newshape[B2ND_MAX_DIM];
1311
0
  int32_t newchunkshape[B2ND_MAX_DIM];
1312
0
  int32_t newblockshape[B2ND_MAX_DIM];
1313
1314
0
  for (int i = 0; i < array->ndim; ++i) {
1315
0
    if (index[i] == true) {
1316
0
      if (array->shape[i] != 1) {
1317
0
        BLOSC_ERROR(BLOSC2_ERROR_INVALID_INDEX);
1318
0
      }
1319
0
    } else {
1320
0
      newshape[nones] = array->shape[i];
1321
0
      newchunkshape[nones] = array->chunkshape[i];
1322
0
      newblockshape[nones] = array->blockshape[i];
1323
0
      nones += 1;
1324
0
    }
1325
0
  }
1326
1327
  //views only deal with cparams/dparams; storage is always in-memory (ephemeral).
1328
0
  blosc2_cparams cparams = *(array->sc->storage->cparams);
1329
0
  blosc2_dparams dparams = *(array->sc->storage->dparams);
1330
0
  blosc2_storage b2_storage1 = {.cparams=&cparams, .dparams=&dparams};
1331
1332
0
  b2nd_context_t *ctx1 = b2nd_create_ctx(&b2_storage1, nones, newshape,
1333
0
                                        newchunkshape, newblockshape, array->dtype,
1334
0
                                        array->dtype_format, NULL, 0);
1335
1336
0
  view_new(array, view, ctx1);
1337
0
  b2nd_free_ctx(ctx1);
1338
1339
0
  return BLOSC2_ERROR_SUCCESS;
1340
0
}
1341
1342
1343
0
int b2nd_copy(b2nd_context_t *ctx, const b2nd_array_t *src, b2nd_array_t **array) {
1344
0
  BLOSC_ERROR_NULL(src, BLOSC2_ERROR_NULL_POINTER);
1345
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1346
1347
0
  ctx->ndim = src->ndim;
1348
1349
0
  for (int i = 0; i < src->ndim; ++i) {
1350
0
    ctx->shape[i] = src->shape[i];
1351
0
  }
1352
1353
0
  bool equals = true;
1354
0
  for (int i = 0; i < src->ndim; ++i) {
1355
0
    if (src->chunkshape[i] != ctx->chunkshape[i]) {
1356
0
      equals = false;
1357
0
      break;
1358
0
    }
1359
0
    if (src->blockshape[i] != ctx->blockshape[i]) {
1360
0
      equals = false;
1361
0
      break;
1362
0
    }
1363
0
  }
1364
1365
0
  if (equals) {
1366
0
    BLOSC_ERROR(array_without_schunk(ctx, array));
1367
1368
0
    blosc2_schunk *new_sc = blosc2_schunk_copy(src->sc, ctx->b2_storage);
1369
1370
0
    if (new_sc == NULL) {
1371
0
      return BLOSC2_ERROR_FAILURE;
1372
0
    }
1373
0
    (*array)->sc = new_sc;
1374
1375
0
  } else {
1376
0
    int64_t start[B2ND_MAX_DIM] = {0};
1377
1378
0
    int64_t stop[B2ND_MAX_DIM];
1379
0
    for (int i = 0; i < src->ndim; ++i) {
1380
0
      stop[i] = src->shape[i];
1381
0
    }
1382
    // Copy metalayers
1383
0
    b2nd_context_t params_meta;
1384
0
    memcpy(&params_meta, ctx, sizeof(params_meta));
1385
0
    int j = 0;
1386
1387
0
    for (int i = 0; i < src->sc->nmetalayers; ++i) {
1388
0
      if (strcmp(src->sc->metalayers[i]->name, "b2nd") == 0) {
1389
0
        continue;
1390
0
      }
1391
0
      blosc2_metalayer *meta = &params_meta.metalayers[j];
1392
0
      meta->name = src->sc->metalayers[i]->name;
1393
0
      meta->content = src->sc->metalayers[i]->content;
1394
0
      meta->content_len = src->sc->metalayers[i]->content_len;
1395
0
      j++;
1396
0
    }
1397
0
    params_meta.nmetalayers = j;
1398
1399
    // Copy data
1400
0
    BLOSC_ERROR(b2nd_get_slice(&params_meta, array, src, start, stop));
1401
1402
    // Copy vlmetayers
1403
0
    for (int i = 0; i < src->sc->nvlmetalayers; ++i) {
1404
0
      uint8_t *content;
1405
0
      int32_t content_len;
1406
0
      if (blosc2_vlmeta_get(src->sc, src->sc->vlmetalayers[i]->name, &content,
1407
0
                            &content_len) < 0) {
1408
0
        BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
1409
0
      }
1410
0
      BLOSC_ERROR(blosc2_vlmeta_add((*array)->sc, src->sc->vlmetalayers[i]->name, content, content_len,
1411
0
                                      (*array)->sc->storage->cparams));
1412
0
      free(content);
1413
0
    }
1414
0
  }
1415
0
  return BLOSC2_ERROR_SUCCESS;
1416
0
}
1417
1418
1419
int b2nd_concatenate(b2nd_context_t *ctx, const b2nd_array_t *src1, const b2nd_array_t *src2,
1420
0
                     int8_t axis, bool copy, b2nd_array_t **array) {
1421
0
  BLOSC_ERROR_NULL(src1, BLOSC2_ERROR_NULL_POINTER);
1422
0
  BLOSC_ERROR_NULL(src2, BLOSC2_ERROR_NULL_POINTER);
1423
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1424
1425
  // Validate the axis parameter
1426
0
  if (axis < 0 || axis >= src1->ndim) {
1427
0
    BLOSC_TRACE_ERROR("axis parameter is out of bounds: axis=%d, expected range=[0, %d)", axis, src1->ndim - 1);
1428
0
    BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
1429
0
  }
1430
1431
  // typesize must be the same for both arrays
1432
0
  if (src1->sc->typesize != src2->sc->typesize) {
1433
0
    BLOSC_TRACE_ERROR("The two arrays must have the same typesize");
1434
0
    BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
1435
0
  }
1436
1437
  // Keep the src1 shape for later use
1438
0
  int64_t src1_shape[B2ND_MAX_DIM];
1439
0
  for (int i = 0; i < src1->ndim; ++i) {
1440
0
    src1_shape[i] = src1->shape[i];
1441
0
  }
1442
1443
  // Support for 0-dim arrays is not implemented
1444
0
  if (src1->ndim == 0 || src2->ndim == 0) {
1445
0
    BLOSC_TRACE_ERROR("Concatenation of 0-dim arrays is not supported");
1446
0
    BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
1447
0
  }
1448
1449
  // Check that the shapes are compatible for concatenation
1450
0
  if (src1->ndim != src2->ndim) {
1451
0
    BLOSC_TRACE_ERROR("The two arrays must have the same number of dimensions");
1452
0
    BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
1453
0
  }
1454
  // Compute the new shape
1455
0
  int64_t newshape[B2ND_MAX_DIM];
1456
0
  for (int8_t i = 0; i < src1->ndim; ++i) {
1457
0
    if (i == axis) {
1458
0
      newshape[i] = src1->shape[i] + src2->shape[i];
1459
0
    } else {
1460
0
      if (src1->shape[i] != src2->shape[i]) {
1461
0
        BLOSC_TRACE_ERROR("The two arrays must have the same shape in all dimensions except the concatenation axis");
1462
0
        BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
1463
0
      }
1464
0
      newshape[i] = src1->shape[i];
1465
0
    }
1466
0
  }
1467
1468
0
  if (copy) {
1469
0
    BLOSC_ERROR(b2nd_copy(ctx, src1, array));
1470
0
  }
1471
0
  else {
1472
0
    *array = (b2nd_array_t *)src1;
1473
0
  }
1474
1475
  // Extend the array, we don't need to specify the start in resize, as we are extending the shape from the end
1476
0
  BLOSC_ERROR(b2nd_resize(*array, newshape, NULL));
1477
1478
  // Copy the data from the second array
1479
0
  int64_t start[B2ND_MAX_DIM];
1480
0
  int64_t stop[B2ND_MAX_DIM];
1481
1482
  // Check if the chunk is aligned with dest chunks, and has the same blockshape
1483
0
  bool aligned = true;
1484
0
  for (int8_t i = 0; i < src2->ndim; ++i) {
1485
0
    if (src1->chunkshape[i] != src2->chunkshape[i] ||
1486
0
        src2->blockshape[i] != (*array)->blockshape[i] ||
1487
0
        (i == axis && (src1_shape[i]) % (*array)->chunkshape[i] != 0)
1488
0
        ) {
1489
0
      aligned = false;
1490
0
      break;
1491
0
        }
1492
0
  }
1493
  // ...and get the chunk index in the dest array if aligned
1494
0
  int64_t chunks_in_array_strides[B2ND_MAX_DIM];
1495
  // Calculate strides for destination array
1496
0
  chunks_in_array_strides[(*array)->ndim - 1] = 1;
1497
0
  for (int i = (*array)->ndim - 2; i >= 0; --i) {
1498
0
    chunks_in_array_strides[i] = chunks_in_array_strides[i + 1] *
1499
0
                                ((*array)->extshape[i + 1] / (*array)->chunkshape[i + 1]);
1500
0
  }
1501
1502
  // Copy chunk by chunk
1503
0
  void *buffer = malloc(src2->sc->typesize * src2->extchunknitems);
1504
0
  BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_MEMORY_ALLOC);
1505
0
  for (int64_t nchunk = 0; nchunk < src2->sc->nchunks; ++nchunk) {
1506
    // Get multidimensional chunk position
1507
0
    int64_t nchunk_ndim[B2ND_MAX_DIM] = {0};
1508
0
    int64_t chunkshape[B2ND_MAX_DIM] = {0};
1509
0
    for (int8_t i = 0; i < src2->ndim; ++i) {
1510
0
      chunkshape[i] = src2->chunkshape[i];
1511
0
    }
1512
0
    int64_t chunks_in_dim[B2ND_MAX_DIM] = {0};
1513
0
    for (int8_t i = 0; i < src2->ndim; ++i) {
1514
0
      chunks_in_dim[i] = src2->extshape[i] / src2->chunkshape[i];
1515
0
    }
1516
0
    blosc2_unidim_to_multidim(src2->ndim, chunks_in_dim, nchunk, nchunk_ndim);
1517
1518
0
    if (aligned) {
1519
      // Get the uncompressed chunk buffer from the source array
1520
0
      bool needs_free = false;
1521
0
      uint8_t *chunk;
1522
0
      int32_t cbytes = blosc2_schunk_get_chunk(src2->sc, nchunk, &chunk, &needs_free);
1523
0
      if (cbytes < 0) {
1524
0
        BLOSC_TRACE_ERROR("Error getting chunk from source array");
1525
0
        BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
1526
0
      }
1527
      // Update the chunk in the destination array
1528
      // We need to free only if needs_free is true or copy is false
1529
      // bool needs_copy = !needs_free || copy;
1530
      // BLOSC_ERROR(blosc2_schunk_update_chunk((*array)->sc, nchunk_dest, chunk, needs_copy));
1531
      // if (needs_free && !copy) {
1532
      //   free(chunk);
1533
      // }
1534
      // TODO: the above makes some tests to crash, so always force a copy; try to optimize this later
1535
0
      int64_t nchunk_dest = 0;
1536
0
      nchunk_ndim[axis] += src1_shape[axis] / (*array)->chunkshape[axis];
1537
0
      for ( int i =0; i< src2->ndim; i++) {
1538
0
        nchunk_dest += nchunk_ndim[i] * chunks_in_array_strides[i];
1539
0
      }
1540
0
      BLOSC_ERROR(blosc2_schunk_update_chunk((*array)->sc, nchunk_dest, chunk, true));
1541
0
      if (needs_free) {
1542
0
        free(chunk);
1543
0
      }
1544
0
    }
1545
0
    else {
1546
1547
      // Set positions for each dimension
1548
0
      for (int8_t i = 0; i < src2->ndim; ++i) {
1549
0
        start[i] = nchunk_ndim[i] * src2->chunkshape[i];
1550
0
        stop[i] = start[i] + src2->chunkshape[i];
1551
0
        if (stop[i] > src2->shape[i]) {
1552
0
          stop[i] = src2->shape[i];  // Handle boundary chunks
1553
0
        }
1554
0
      }
1555
      // Load chunk into buffer
1556
0
      BLOSC_ERROR(b2nd_get_slice_cbuffer(src2, start, stop, buffer, chunkshape, src2->sc->chunksize));
1557
1558
      // Apply chunk offset only for concatenation axis
1559
0
      start[axis] += src1_shape[axis];
1560
0
      stop[axis] += src1_shape[axis];
1561
1562
      // Copy the chunk to the correct position
1563
0
      BLOSC_ERROR(b2nd_set_slice_cbuffer(buffer, chunkshape,
1564
0
                                         src2->sc->typesize * src2->extchunknitems,
1565
0
                                         start, stop, *array));
1566
0
    }
1567
0
  }
1568
1569
0
  free(buffer);
1570
1571
0
  return BLOSC2_ERROR_SUCCESS;
1572
0
}
1573
1574
0
int b2nd_save(const b2nd_array_t *array, char *urlpath) {
1575
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1576
0
  BLOSC_ERROR_NULL(urlpath, BLOSC2_ERROR_NULL_POINTER);
1577
1578
0
  b2nd_array_t *tmp;
1579
0
  blosc2_storage b2_storage = BLOSC2_STORAGE_DEFAULTS;
1580
0
  b2nd_context_t params = {.b2_storage=&b2_storage};
1581
0
  b2_storage.urlpath = urlpath;
1582
0
  b2_storage.contiguous = array->sc->storage->contiguous;
1583
1584
0
  for (int i = 0; i < array->ndim; ++i) {
1585
0
    params.chunkshape[i] = array->chunkshape[i];
1586
0
    params.blockshape[i] = array->blockshape[i];
1587
0
  }
1588
1589
0
  BLOSC_ERROR(b2nd_copy(&params, array, &tmp));
1590
0
  BLOSC_ERROR(b2nd_free(tmp));
1591
1592
0
  return BLOSC2_ERROR_SUCCESS;
1593
0
}
1594
1595
0
int64_t b2nd_save_append(const b2nd_array_t *array, const char *urlpath) {
1596
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1597
0
  return blosc2_schunk_append_file(array->sc, urlpath);
1598
0
}
1599
1600
0
int b2nd_print_meta(const b2nd_array_t *array) {
1601
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1602
0
  int8_t ndim;
1603
0
  int64_t shape[B2ND_MAX_DIM];
1604
0
  int32_t chunkshape[B2ND_MAX_DIM];
1605
0
  int32_t blockshape[B2ND_MAX_DIM];
1606
0
  char *dtype;
1607
0
  int8_t dtype_format;
1608
0
  uint8_t *smeta;
1609
0
  int32_t smeta_len;
1610
0
  if (blosc2_meta_get(array->sc, "b2nd", &smeta, &smeta_len) < 0) {
1611
    // Try with a caterva metalayer; we are meant to be backward compatible with it
1612
0
    if (blosc2_meta_get(array->sc, "caterva", &smeta, &smeta_len) < 0) {
1613
0
      BLOSC_ERROR(BLOSC2_ERROR_METALAYER_NOT_FOUND);
1614
0
    }
1615
0
  }
1616
0
  BLOSC_ERROR(b2nd_deserialize_meta(smeta, smeta_len, &ndim, shape, chunkshape, blockshape,
1617
0
                                    &dtype, &dtype_format));
1618
0
  free(smeta);
1619
1620
0
  printf("b2nd metalayer parameters:\n Ndim:       %d", ndim);
1621
0
  printf("\n shape:      %" PRId64 "", shape[0]);
1622
0
  for (int i = 1; i < ndim; ++i) {
1623
0
    printf(", %" PRId64 "", shape[i]);
1624
0
  }
1625
0
  printf("\n chunkshape: %d", chunkshape[0]);
1626
0
  for (int i = 1; i < ndim; ++i) {
1627
0
    printf(", %d", chunkshape[i]);
1628
0
  }
1629
0
  if (dtype != NULL) {
1630
0
    printf("\n dtype: %s", dtype);
1631
0
    free(dtype);
1632
0
  }
1633
1634
0
  printf("\n blockshape: %d", blockshape[0]);
1635
0
  for (int i = 1; i < ndim; ++i) {
1636
0
    printf(", %d", blockshape[i]);
1637
0
  }
1638
0
  printf("\n");
1639
1640
0
  return BLOSC2_ERROR_SUCCESS;
1641
0
}
1642
1643
1644
0
int extend_shape(b2nd_array_t *array, const int64_t *new_shape, const int64_t *start) {
1645
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1646
0
  BLOSC_ERROR_NULL(new_shape, BLOSC2_ERROR_NULL_POINTER);
1647
1648
0
  int8_t ndim = array->ndim;
1649
0
  int64_t diffs_shape[B2ND_MAX_DIM];
1650
0
  int64_t diffs_sum = 0;
1651
0
  for (int i = 0; i < ndim; i++) {
1652
0
    diffs_shape[i] = new_shape[i] - array->shape[i];
1653
0
    diffs_sum += diffs_shape[i];
1654
0
    if (diffs_shape[i] < 0) {
1655
0
      BLOSC_TRACE_ERROR("The new shape must be greater than the old one");
1656
0
      BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
1657
0
    }
1658
0
    if (array->shape[i] == INT64_MAX) {
1659
0
      BLOSC_TRACE_ERROR("Cannot extend array with shape[%d] = %" PRId64 "d", i, INT64_MAX);
1660
0
      BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
1661
0
    }
1662
0
  }
1663
0
  if (diffs_sum == 0) {
1664
    // Shapes are equal. Do nothing.
1665
0
    return BLOSC2_ERROR_SUCCESS;
1666
0
  }
1667
1668
0
  int64_t old_nchunks = array->sc->nchunks;
1669
  // aux array to keep old shapes
1670
0
  b2nd_array_t *aux = malloc(sizeof(b2nd_array_t));
1671
0
  BLOSC_ERROR_NULL(aux, BLOSC2_ERROR_MEMORY_ALLOC);
1672
0
  aux->sc = NULL;
1673
0
  BLOSC_ERROR(update_shape(aux, ndim, array->shape, array->chunkshape, array->blockshape));
1674
1675
0
  BLOSC_ERROR(update_shape(array, ndim, new_shape, array->chunkshape, array->blockshape));
1676
1677
0
  int64_t nchunks = array->extnitems / array->chunknitems;
1678
0
  int64_t nchunks_;
1679
0
  int64_t nchunk_ndim[B2ND_MAX_DIM];
1680
0
  blosc2_cparams *cparams;
1681
0
  BLOSC_ERROR(blosc2_schunk_get_cparams(array->sc, &cparams));
1682
0
  void *chunk;
1683
0
  int64_t csize;
1684
0
  if (nchunks != old_nchunks) {
1685
0
    if (start == NULL) {
1686
0
      start = aux->shape;
1687
0
    }
1688
0
    int64_t chunks_in_array[B2ND_MAX_DIM] = {0};
1689
0
    for (int i = 0; i < ndim; ++i) {
1690
0
      chunks_in_array[i] = array->extshape[i] / array->chunkshape[i];
1691
0
    }
1692
0
    for (int i = 0; i < nchunks; ++i) {
1693
0
      blosc2_unidim_to_multidim(ndim, chunks_in_array, i, nchunk_ndim);
1694
0
      for (int j = 0; j < ndim; ++j) {
1695
0
        if (start[j] <= (array->chunkshape[j] * nchunk_ndim[j])
1696
0
            && (array->chunkshape[j] * nchunk_ndim[j]) < (start[j] + new_shape[j] - aux->shape[j])) {
1697
0
          chunk = malloc(BLOSC_EXTENDED_HEADER_LENGTH);
1698
0
          BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC);
1699
0
          csize = blosc2_chunk_zeros(*cparams, array->sc->chunksize, chunk, BLOSC_EXTENDED_HEADER_LENGTH);
1700
0
          if (csize < 0) {
1701
0
            free(aux);
1702
0
            free(cparams);
1703
0
            BLOSC_TRACE_ERROR("Blosc error when creating a chunk");
1704
0
            return BLOSC2_ERROR_FAILURE;
1705
0
          }
1706
0
          nchunks_ = blosc2_schunk_insert_chunk(array->sc, i, chunk, false);
1707
0
          if (nchunks_ < 0) {
1708
0
            free(aux);
1709
0
            free(cparams);
1710
0
            BLOSC_TRACE_ERROR("Blosc error when inserting a chunk");
1711
0
            return BLOSC2_ERROR_FAILURE;
1712
0
          }
1713
0
          break;
1714
0
        }
1715
0
      }
1716
0
    }
1717
0
  }
1718
0
  free(aux);
1719
0
  free(cparams);
1720
1721
0
  return BLOSC2_ERROR_SUCCESS;
1722
0
}
1723
1724
1725
0
int shrink_shape(b2nd_array_t *array, const int64_t *new_shape, const int64_t *start) {
1726
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1727
0
  BLOSC_ERROR_NULL(new_shape, BLOSC2_ERROR_NULL_POINTER);
1728
1729
0
  int8_t ndim = array->ndim;
1730
0
  int64_t diffs_shape[B2ND_MAX_DIM];
1731
0
  int64_t diffs_sum = 0;
1732
0
  for (int i = 0; i < ndim; i++) {
1733
0
    diffs_shape[i] = new_shape[i] - array->shape[i];
1734
0
    diffs_sum += diffs_shape[i];
1735
0
    if (diffs_shape[i] > 0) {
1736
0
      BLOSC_TRACE_ERROR("The new shape must be smaller than the old one");
1737
0
      BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
1738
0
    }
1739
0
    if (array->shape[i] == 0) {
1740
0
      continue;
1741
0
    }
1742
0
  }
1743
0
  if (diffs_sum == 0) {
1744
    // Shapes are equal. Do nothing.
1745
0
    return BLOSC2_ERROR_SUCCESS;
1746
0
  }
1747
1748
0
  int64_t old_nchunks = array->sc->nchunks;
1749
  // aux array to keep old shapes
1750
0
  b2nd_array_t *aux = malloc(sizeof(b2nd_array_t));
1751
0
  BLOSC_ERROR_NULL(aux, BLOSC2_ERROR_MEMORY_ALLOC);
1752
0
  aux->sc = NULL;
1753
0
  BLOSC_ERROR(update_shape(aux, ndim, array->shape, array->chunkshape, array->blockshape));
1754
1755
0
  BLOSC_ERROR(update_shape(array, ndim, new_shape, array->chunkshape, array->blockshape));
1756
1757
  // Delete chunks if needed
1758
0
  int64_t chunks_in_array_old[B2ND_MAX_DIM] = {0};
1759
0
  for (int i = 0; i < ndim; ++i) {
1760
0
    chunks_in_array_old[i] = aux->extshape[i] / aux->chunkshape[i];
1761
0
  }
1762
0
  if (start == NULL) {
1763
0
    start = new_shape;
1764
0
  }
1765
1766
0
  int64_t nchunk_ndim[B2ND_MAX_DIM] = {0};
1767
0
  int64_t nchunks_;
1768
0
  for (int i = (int) old_nchunks - 1; i >= 0; --i) {
1769
0
    blosc2_unidim_to_multidim(ndim, chunks_in_array_old, i, nchunk_ndim);
1770
0
    for (int j = 0; j < ndim; ++j) {
1771
0
      if (start[j] <= (array->chunkshape[j] * nchunk_ndim[j])
1772
0
          && (array->chunkshape[j] * nchunk_ndim[j]) < (start[j] + aux->shape[j] - new_shape[j])) {
1773
0
        nchunks_ = blosc2_schunk_delete_chunk(array->sc, i);
1774
0
        if (nchunks_ < 0) {
1775
0
          free(aux);
1776
0
          BLOSC_TRACE_ERROR("Blosc error when deleting a chunk");
1777
0
          return BLOSC2_ERROR_FAILURE;
1778
0
        }
1779
0
        break;
1780
0
      }
1781
0
    }
1782
0
  }
1783
0
  free(aux);
1784
1785
0
  return BLOSC2_ERROR_SUCCESS;
1786
0
}
1787
1788
1789
int b2nd_resize(b2nd_array_t *array, const int64_t *new_shape,
1790
0
                const int64_t *start) {
1791
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1792
0
  BLOSC_ERROR_NULL(new_shape, BLOSC2_ERROR_NULL_POINTER);
1793
1794
0
  if (start != NULL) {
1795
0
    for (int i = 0; i < array->ndim; ++i) {
1796
0
      if (start[i] > array->shape[i]) {
1797
0
        BLOSC_TRACE_ERROR("`start` must be lower or equal than old array shape in all dims");
1798
0
        BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
1799
0
      }
1800
0
      if ((new_shape[i] > array->shape[i] && start[i] != array->shape[i])
1801
0
          || (new_shape[i] < array->shape[i]
1802
0
              && (start[i] + array->shape[i] - new_shape[i]) != array->shape[i])) {
1803
        // Chunks cannot be cut unless they are in the last position
1804
0
        if (start[i] % array->chunkshape[i] != 0) {
1805
0
          BLOSC_TRACE_ERROR("If array end is not being modified "
1806
0
                              "`start` must be a multiple of chunkshape in all dims");
1807
0
          BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
1808
0
        }
1809
0
        if ((new_shape[i] - array->shape[i]) % array->chunkshape[i] != 0) {
1810
0
          BLOSC_TRACE_ERROR("If array end is not being modified "
1811
0
                              "`(new_shape - shape)` must be multiple of chunkshape in all dims");
1812
0
          BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
1813
0
        }
1814
0
      }
1815
0
    }
1816
0
  }
1817
1818
  // Get shrunk shape
1819
0
  int64_t shrunk_shape[B2ND_MAX_DIM] = {0};
1820
0
  for (int i = 0; i < array->ndim; ++i) {
1821
0
    if (new_shape[i] <= array->shape[i]) {
1822
0
      shrunk_shape[i] = new_shape[i];
1823
0
    } else {
1824
0
      shrunk_shape[i] = array->shape[i];
1825
0
    }
1826
0
  }
1827
1828
0
  BLOSC_ERROR(shrink_shape(array, shrunk_shape, start));
1829
0
  BLOSC_ERROR(extend_shape(array, new_shape, start));
1830
1831
0
  return BLOSC2_ERROR_SUCCESS;
1832
0
}
1833
1834
1835
int b2nd_insert(b2nd_array_t *array, const void *buffer, int64_t buffersize,
1836
0
                int8_t axis, int64_t insert_start) {
1837
1838
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1839
0
  BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER);
1840
1841
0
  if (axis >= array->ndim) {
1842
0
    BLOSC_TRACE_ERROR("`axis` cannot be greater than the number of dimensions");
1843
0
    BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
1844
0
  }
1845
1846
0
  int64_t axis_size = array->sc->typesize;
1847
0
  int64_t buffershape[B2ND_MAX_DIM];
1848
0
  for (int i = 0; i < array->ndim; ++i) {
1849
0
    if (i != axis) {
1850
0
      axis_size *= array->shape[i];
1851
0
      buffershape[i] = array->shape[i];
1852
0
    }
1853
0
  }
1854
0
  if (buffersize % axis_size != 0) {
1855
0
    BLOSC_TRACE_ERROR("`buffersize` must be multiple of the array");
1856
0
    BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
1857
0
  }
1858
0
  int64_t newshape[B2ND_MAX_DIM];
1859
0
  memcpy(newshape, array->shape, array->ndim * sizeof(int64_t));
1860
0
  newshape[axis] += buffersize / axis_size;
1861
0
  buffershape[axis] = newshape[axis] - array->shape[axis];
1862
0
  int64_t start[B2ND_MAX_DIM] = {0};
1863
0
  start[axis] = insert_start;
1864
1865
0
  if (insert_start == array->shape[axis]) {
1866
0
    BLOSC_ERROR(b2nd_resize(array, newshape, NULL));
1867
0
  } else {
1868
0
    BLOSC_ERROR(b2nd_resize(array, newshape, start));
1869
0
  }
1870
1871
0
  int64_t stop[B2ND_MAX_DIM];
1872
0
  memcpy(stop, array->shape, sizeof(int64_t) * array->ndim);
1873
0
  stop[axis] = start[axis] + buffershape[axis];
1874
0
  BLOSC_ERROR(b2nd_set_slice_cbuffer(buffer, buffershape, buffersize, start, stop, array));
1875
1876
0
  return BLOSC2_ERROR_SUCCESS;
1877
0
}
1878
1879
1880
int b2nd_append(b2nd_array_t *array, const void *buffer, int64_t buffersize,
1881
0
                int8_t axis) {
1882
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1883
0
  BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER);
1884
1885
0
  int32_t chunksize = array->sc->chunksize;
1886
0
  int64_t nchunks_append = buffersize / chunksize;
1887
  // Check whether chunkshape and blockshape are compatible with accelerated path.
1888
  // Essentially, we are checking whether the buffer is a multiple of the chunksize
1889
  // and that the chunkshape and blockshape are the same, except for the first axis.
1890
  // Also, axis needs to be the first one.
1891
0
  bool compat_chunks_blocks = true;
1892
0
  for (int i = 1; i < array->ndim; ++i) {
1893
0
    if (array->chunkshape[i] != array->blockshape[i]) {
1894
0
      compat_chunks_blocks = false;
1895
0
      break;
1896
0
    }
1897
0
  }
1898
0
  if (axis > 0) {
1899
0
    compat_chunks_blocks = false;
1900
0
  }
1901
  // General case where a buffer has a different size than the chunksize
1902
0
  if (!compat_chunks_blocks || buffersize % chunksize != 0 || nchunks_append != 1) {
1903
0
    BLOSC_ERROR(b2nd_insert(array, buffer, buffersize, axis, array->shape[axis]));
1904
0
    return BLOSC2_ERROR_SUCCESS;
1905
0
  }
1906
1907
  // Accelerated path for buffers that are of the same size as the chunksize
1908
  // printf("accelerated path\n");
1909
1910
  // Append the buffer to the underlying schunk. This is very fast, as
1911
  // it doesn't need to do internal partitioning.
1912
0
  BLOSC_ERROR(blosc2_schunk_append_buffer(array->sc, (void*)buffer, buffersize));
1913
1914
  // Finally, resize the array
1915
0
  int64_t newshape[B2ND_MAX_DIM];
1916
0
  memcpy(newshape, array->shape, array->ndim * sizeof(int64_t));
1917
0
  newshape[axis] += nchunks_append * array->chunkshape[axis];
1918
0
  BLOSC_ERROR(b2nd_resize(array, newshape, NULL));
1919
1920
0
  return BLOSC2_ERROR_SUCCESS;
1921
0
}
1922
1923
1924
int b2nd_delete(b2nd_array_t *array, const int8_t axis,
1925
0
                int64_t delete_start, int64_t delete_len) {
1926
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
1927
1928
0
  if (axis >= array->ndim) {
1929
0
    BLOSC_TRACE_ERROR("axis cannot be greater than the number of dimensions");
1930
0
    BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
1931
0
  }
1932
1933
1934
0
  int64_t newshape[B2ND_MAX_DIM];
1935
0
  memcpy(newshape, array->shape, array->ndim * sizeof(int64_t));
1936
0
  newshape[axis] -= delete_len;
1937
0
  int64_t start[B2ND_MAX_DIM] = {0};
1938
0
  start[axis] = delete_start;
1939
1940
0
  if (delete_start == (array->shape[axis] - delete_len)) {
1941
0
    BLOSC_ERROR(b2nd_resize(array, newshape, NULL));
1942
0
  } else {
1943
0
    BLOSC_ERROR(b2nd_resize(array, newshape, start));
1944
0
  }
1945
1946
0
  return BLOSC2_ERROR_SUCCESS;
1947
0
}
1948
1949
// Indexing
1950
1951
typedef struct {
1952
    int64_t value;
1953
    int64_t index;
1954
} b2nd_selection_t;
1955
1956
1957
0
int compare_selection(const void *a, const void *b) {
1958
0
  int res = (int) (((b2nd_selection_t *) a)->value - ((b2nd_selection_t *) b)->value);
1959
  // In case values are equal, sort by index
1960
0
  if (res == 0) {
1961
0
    res = (int) (((b2nd_selection_t *) a)->index - ((b2nd_selection_t *) b)->index);
1962
0
  }
1963
0
  return res;
1964
0
}
1965
1966
1967
int copy_block_buffer_data(b2nd_array_t *array,
1968
                           int8_t ndim,
1969
                           int64_t *block_selection_size,
1970
                           b2nd_selection_t **chunk_selection,
1971
                           b2nd_selection_t **p_block_selection_0,
1972
                           b2nd_selection_t **p_block_selection_1,
1973
                           uint8_t *block,
1974
                           uint8_t *buffer,
1975
                           int64_t *buffershape,
1976
                           int64_t *bufferstrides,
1977
0
                           bool get) {
1978
0
  p_block_selection_0[ndim] = chunk_selection[ndim];
1979
0
  p_block_selection_1[ndim] = chunk_selection[ndim];
1980
0
  while (p_block_selection_1[ndim] - p_block_selection_0[ndim] < block_selection_size[ndim]) {
1981
0
    if (ndim == array->ndim - 1) {
1982
1983
0
      int64_t index_in_block_n[B2ND_MAX_DIM];
1984
0
      for (int i = 0; i < array->ndim; ++i) {
1985
0
        index_in_block_n[i] = p_block_selection_1[i]->value % array->chunkshape[i] % array->blockshape[i];
1986
0
      }
1987
0
      int64_t index_in_block = 0;
1988
0
      for (int i = 0; i < array->ndim; ++i) {
1989
0
        index_in_block += index_in_block_n[i] * array->item_block_strides[i];
1990
0
      }
1991
1992
0
      int64_t index_in_buffer_n[B2ND_MAX_DIM];
1993
0
      for (int i = 0; i < array->ndim; ++i) {
1994
0
        index_in_buffer_n[i] = p_block_selection_1[i]->index;
1995
0
      }
1996
0
      int64_t index_in_buffer = 0;
1997
0
      for (int i = 0; i < array->ndim; ++i) {
1998
0
        index_in_buffer += index_in_buffer_n[i] * bufferstrides[i];
1999
0
      }
2000
0
      if (get) {
2001
0
        memcpy(&buffer[index_in_buffer * array->sc->typesize],
2002
0
               &block[index_in_block * array->sc->typesize],
2003
0
               array->sc->typesize);
2004
0
      } else {
2005
0
        memcpy(&block[index_in_block * array->sc->typesize],
2006
0
               &buffer[index_in_buffer * array->sc->typesize],
2007
0
               array->sc->typesize);
2008
0
      }
2009
0
    } else {
2010
0
      BLOSC_ERROR(copy_block_buffer_data(array, (int8_t) (ndim + 1), block_selection_size,
2011
0
                                         chunk_selection,
2012
0
                                         p_block_selection_0, p_block_selection_1, block,
2013
0
                                         buffer, buffershape, bufferstrides, get)
2014
0
      );
2015
0
    }
2016
0
    p_block_selection_1[ndim]++;
2017
0
  }
2018
0
  return BLOSC2_ERROR_SUCCESS;
2019
0
}
2020
2021
2022
int iter_block_copy(b2nd_array_t *array, int8_t ndim,
2023
                    int64_t *chunk_selection_size,
2024
                    b2nd_selection_t **ordered_selection,
2025
                    b2nd_selection_t **chunk_selection_0,
2026
                    b2nd_selection_t **chunk_selection_1,
2027
                    uint8_t *data,
2028
                    uint8_t *buffer,
2029
                    int64_t *buffershape,
2030
                    int64_t *bufferstrides,
2031
0
                    bool get) {
2032
0
  chunk_selection_0[ndim] = ordered_selection[ndim];
2033
0
  chunk_selection_1[ndim] = ordered_selection[ndim];
2034
0
  while (chunk_selection_1[ndim] - ordered_selection[ndim] < chunk_selection_size[ndim]) {
2035
0
    int64_t block_index_ndim = ((*chunk_selection_1[ndim]).value % array->chunkshape[ndim]) / array->blockshape[ndim];
2036
0
    while (chunk_selection_1[ndim] - ordered_selection[ndim] < chunk_selection_size[ndim] &&
2037
0
           block_index_ndim == ((*chunk_selection_1[ndim]).value % array->chunkshape[ndim]) / array->blockshape[ndim]) {
2038
0
      chunk_selection_1[ndim]++;
2039
0
    }
2040
0
    if (ndim == array->ndim - 1) {
2041
0
      int64_t block_chunk_strides[B2ND_MAX_DIM];
2042
0
      block_chunk_strides[array->ndim - 1] = 1;
2043
0
      for (int i = array->ndim - 2; i >= 0; --i) {
2044
0
        block_chunk_strides[i] = block_chunk_strides[i + 1] * (array->extchunkshape[i + 1] / array->blockshape[i + 1]);
2045
0
      }
2046
0
      int64_t block_index[B2ND_MAX_DIM];
2047
0
      for (int i = 0; i < array->ndim; ++i) {
2048
0
        block_index[i] = ((*chunk_selection_0[i]).value % array->chunkshape[i]) / array->blockshape[i];
2049
0
      }
2050
0
      int64_t nblock = 0;
2051
0
      for (int i = 0; i < array->ndim; ++i) {
2052
0
        nblock += block_index[i] * block_chunk_strides[i];
2053
0
      }
2054
0
      b2nd_selection_t **p_block_selection_0 = malloc(array->ndim * sizeof(b2nd_selection_t *));
2055
0
      BLOSC_ERROR_NULL(p_block_selection_0, BLOSC2_ERROR_MEMORY_ALLOC);
2056
0
      b2nd_selection_t **p_block_selection_1 = malloc(array->ndim * sizeof(b2nd_selection_t *));
2057
0
      BLOSC_ERROR_NULL(p_block_selection_1, BLOSC2_ERROR_MEMORY_ALLOC);
2058
0
      int64_t *block_selection_size = malloc(array->ndim * sizeof(int64_t));
2059
0
      BLOSC_ERROR_NULL(block_selection_size, BLOSC2_ERROR_MEMORY_ALLOC);
2060
0
      for (int i = 0; i < array->ndim; ++i) {
2061
0
        block_selection_size[i] = chunk_selection_1[i] - chunk_selection_0[i];
2062
0
      }
2063
2064
0
      BLOSC_ERROR(copy_block_buffer_data(array,
2065
0
                                         (int8_t) 0,
2066
0
                                         block_selection_size,
2067
0
                                         chunk_selection_0,
2068
0
                                         p_block_selection_0,
2069
0
                                         p_block_selection_1,
2070
0
                                         &data[nblock * array->blocknitems * array->sc->typesize],
2071
0
                                         buffer,
2072
0
                                         buffershape,
2073
0
                                         bufferstrides,
2074
0
                                         get)
2075
0
      );
2076
0
      free(p_block_selection_0);
2077
0
      free(p_block_selection_1);
2078
0
      free(block_selection_size);
2079
0
    } else {
2080
0
      BLOSC_ERROR(iter_block_copy(array, (int8_t) (ndim + 1), chunk_selection_size,
2081
0
                                  ordered_selection, chunk_selection_0, chunk_selection_1,
2082
0
                                  data, buffer, buffershape, bufferstrides, get)
2083
0
      );
2084
0
    }
2085
0
    chunk_selection_0[ndim] = chunk_selection_1[ndim];
2086
2087
0
  }
2088
2089
0
  return BLOSC2_ERROR_SUCCESS;
2090
0
}
2091
2092
2093
int iter_block_maskout(b2nd_array_t *array, int8_t ndim,
2094
                       int64_t *sel_block_size,
2095
                       b2nd_selection_t **o_selection,
2096
                       b2nd_selection_t **p_o_sel_block_0,
2097
                       b2nd_selection_t **p_o_sel_block_1,
2098
0
                       bool *maskout) {
2099
0
  p_o_sel_block_0[ndim] = o_selection[ndim];
2100
0
  p_o_sel_block_1[ndim] = o_selection[ndim];
2101
0
  while (p_o_sel_block_1[ndim] - o_selection[ndim] < sel_block_size[ndim]) {
2102
0
    int64_t block_index_ndim = ((*p_o_sel_block_1[ndim]).value % array->chunkshape[ndim]) / array->blockshape[ndim];
2103
0
    while (p_o_sel_block_1[ndim] - o_selection[ndim] < sel_block_size[ndim] &&
2104
0
           block_index_ndim == ((*p_o_sel_block_1[ndim]).value % array->chunkshape[ndim]) / array->blockshape[ndim]) {
2105
0
      p_o_sel_block_1[ndim]++;
2106
0
    }
2107
0
    if (ndim == array->ndim - 1) {
2108
0
      int64_t block_chunk_strides[B2ND_MAX_DIM];
2109
0
      block_chunk_strides[array->ndim - 1] = 1;
2110
0
      for (int i = array->ndim - 2; i >= 0; --i) {
2111
0
        block_chunk_strides[i] = block_chunk_strides[i + 1] * (array->extchunkshape[i + 1] / array->blockshape[i + 1]);
2112
0
      }
2113
0
      int64_t block_index[B2ND_MAX_DIM];
2114
0
      for (int i = 0; i < array->ndim; ++i) {
2115
0
        block_index[i] = ((*p_o_sel_block_0[i]).value % array->chunkshape[i]) / array->blockshape[i];
2116
0
      }
2117
0
      int64_t nblock = 0;
2118
0
      for (int i = 0; i < array->ndim; ++i) {
2119
0
        nblock += block_index[i] * block_chunk_strides[i];
2120
0
      }
2121
0
      maskout[nblock] = false;
2122
0
    } else {
2123
0
      BLOSC_ERROR(iter_block_maskout(array, (int8_t) (ndim + 1), sel_block_size,
2124
0
                                     o_selection, p_o_sel_block_0, p_o_sel_block_1,
2125
0
                                     maskout)
2126
0
      );
2127
0
    }
2128
0
    p_o_sel_block_0[ndim] = p_o_sel_block_1[ndim];
2129
2130
0
  }
2131
2132
0
  return BLOSC2_ERROR_SUCCESS;
2133
0
}
2134
2135
2136
int iter_chunk(b2nd_array_t *array, int8_t ndim,
2137
               int64_t *selection_size,
2138
               b2nd_selection_t **ordered_selection,
2139
               b2nd_selection_t **p_ordered_selection_0,
2140
               b2nd_selection_t **p_ordered_selection_1,
2141
               uint8_t *buffer,
2142
               int64_t *buffershape,
2143
               int64_t *bufferstrides,
2144
0
               bool get) {
2145
0
  p_ordered_selection_0[ndim] = ordered_selection[ndim];
2146
0
  p_ordered_selection_1[ndim] = ordered_selection[ndim];
2147
0
  while (p_ordered_selection_1[ndim] - ordered_selection[ndim] < selection_size[ndim]) {
2148
0
    int64_t chunk_index_ndim = (*p_ordered_selection_1[ndim]).value / array->chunkshape[ndim];
2149
0
    while (p_ordered_selection_1[ndim] - ordered_selection[ndim] < selection_size[ndim] &&
2150
0
           chunk_index_ndim == (*p_ordered_selection_1[ndim]).value / array->chunkshape[ndim]) {
2151
0
      p_ordered_selection_1[ndim]++;
2152
0
    }
2153
0
    if (ndim == array->ndim - 1) {
2154
0
      int64_t chunk_array_strides[B2ND_MAX_DIM];
2155
0
      chunk_array_strides[array->ndim - 1] = 1;
2156
0
      for (int i = array->ndim - 2; i >= 0; --i) {
2157
0
        chunk_array_strides[i] = chunk_array_strides[i + 1] *
2158
0
                                 (array->extshape[i + 1] / array->chunkshape[i + 1]);
2159
0
      }
2160
0
      int64_t chunk_index[B2ND_MAX_DIM];
2161
0
      for (int i = 0; i < array->ndim; ++i) {
2162
0
        chunk_index[i] = (*p_ordered_selection_0[i]).value / array->chunkshape[i];
2163
0
      }
2164
0
      int64_t nchunk = 0;
2165
0
      for (int i = 0; i < array->ndim; ++i) {
2166
0
        nchunk += chunk_index[i] * chunk_array_strides[i];
2167
0
      }
2168
2169
0
      int64_t nblocks = array->extchunknitems / array->blocknitems;
2170
0
      b2nd_selection_t **p_chunk_selection_0 = malloc(array->ndim * sizeof(b2nd_selection_t *));
2171
0
      BLOSC_ERROR_NULL(p_chunk_selection_0, BLOSC2_ERROR_MEMORY_ALLOC);
2172
0
      b2nd_selection_t **p_chunk_selection_1 = malloc(array->ndim * sizeof(b2nd_selection_t *));
2173
0
      BLOSC_ERROR_NULL(p_chunk_selection_1, BLOSC2_ERROR_MEMORY_ALLOC);
2174
0
      int64_t *chunk_selection_size = malloc(array->ndim * sizeof(int64_t));
2175
0
      BLOSC_ERROR_NULL(chunk_selection_size, BLOSC2_ERROR_MEMORY_ALLOC);
2176
0
      for (int i = 0; i < array->ndim; ++i) {
2177
0
        chunk_selection_size[i] = p_ordered_selection_1[i] - p_ordered_selection_0[i];
2178
0
      }
2179
2180
0
      if (get) {
2181
0
        bool *maskout = calloc(nblocks, sizeof(bool));
2182
0
        for (int i = 0; i < nblocks; ++i) {
2183
0
          maskout[i] = true;
2184
0
        }
2185
2186
0
        BLOSC_ERROR(iter_block_maskout(array, (int8_t) 0,
2187
0
                                       chunk_selection_size,
2188
0
                                       p_ordered_selection_0,
2189
0
                                       p_chunk_selection_0,
2190
0
                                       p_chunk_selection_1,
2191
0
                                       maskout));
2192
2193
0
        if (blosc2_set_maskout(array->sc->dctx, maskout, (int) nblocks) !=
2194
0
            BLOSC2_ERROR_SUCCESS) {
2195
0
          BLOSC_TRACE_ERROR("Error setting the maskout");
2196
0
          BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
2197
0
        }
2198
0
        free(maskout);
2199
0
      }
2200
0
      int data_nitems = (int) array->extchunknitems;
2201
0
      int data_nbytes = data_nitems * array->sc->typesize;
2202
0
      uint8_t *data = malloc(data_nitems * array->sc->typesize);
2203
0
      BLOSC_ERROR_NULL(data, BLOSC2_ERROR_MEMORY_ALLOC);
2204
0
      int err = blosc2_schunk_decompress_chunk(array->sc, nchunk, data, data_nbytes);
2205
0
      if (err < 0) {
2206
0
        BLOSC_TRACE_ERROR("Error decompressing chunk");
2207
0
        BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
2208
0
      }
2209
0
      BLOSC_ERROR(iter_block_copy(array, 0, chunk_selection_size,
2210
0
                                  p_ordered_selection_0, p_chunk_selection_0, p_chunk_selection_1,
2211
0
                                  data, buffer, buffershape, bufferstrides, get));
2212
2213
0
      if (!get) {
2214
0
        int32_t chunk_size = data_nbytes + BLOSC_EXTENDED_HEADER_LENGTH;
2215
0
        uint8_t *chunk = malloc(chunk_size);
2216
0
        BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC);
2217
0
        err = blosc2_compress_ctx(array->sc->cctx, data, data_nbytes, chunk, chunk_size);
2218
0
        if (err < 0) {
2219
0
          BLOSC_TRACE_ERROR("Error compressing data");
2220
0
          BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
2221
0
        }
2222
0
        err = (int) blosc2_schunk_update_chunk(array->sc, nchunk, chunk, false);
2223
0
        if (err < 0) {
2224
0
          BLOSC_TRACE_ERROR("Error updating chunk");
2225
0
          BLOSC_ERROR(BLOSC2_ERROR_FAILURE);
2226
0
        }
2227
0
      }
2228
0
      free(data);
2229
0
      free(chunk_selection_size);
2230
0
      free(p_chunk_selection_0);
2231
0
      free(p_chunk_selection_1);
2232
0
    } else {
2233
0
      BLOSC_ERROR(iter_chunk(array, (int8_t) (ndim + 1), selection_size,
2234
0
                             ordered_selection, p_ordered_selection_0, p_ordered_selection_1,
2235
0
                             buffer, buffershape, bufferstrides, get));
2236
0
    }
2237
2238
0
    p_ordered_selection_0[ndim] = p_ordered_selection_1[ndim];
2239
0
  }
2240
0
  return BLOSC2_ERROR_SUCCESS;
2241
0
}
2242
2243
2244
int orthogonal_selection(b2nd_array_t *array, int64_t **selection, int64_t *selection_size, void *buffer,
2245
0
                         int64_t *buffershape, int64_t buffersize, bool get) {
2246
0
  BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER);
2247
0
  BLOSC_ERROR_NULL(selection, BLOSC2_ERROR_NULL_POINTER);
2248
0
  BLOSC_ERROR_NULL(selection_size, BLOSC2_ERROR_NULL_POINTER);
2249
2250
0
  int8_t ndim = array->ndim;
2251
2252
0
  for (int i = 0; i < ndim; ++i) {
2253
0
    BLOSC_ERROR_NULL(selection[i], BLOSC2_ERROR_NULL_POINTER);
2254
    // Check that indexes are not larger than array shape
2255
0
    for (int j = 0; j < selection_size[i]; ++j) {
2256
0
      if (selection[i][j] > array->shape[i]) {
2257
0
        BLOSC_ERROR(BLOSC2_ERROR_INVALID_INDEX);
2258
0
      }
2259
0
    }
2260
0
  }
2261
2262
  // Check buffer size
2263
0
  int64_t sel_size = array->sc->typesize;
2264
0
  for (int i = 0; i < ndim; ++i) {
2265
0
    sel_size *= selection_size[i];
2266
0
  }
2267
2268
0
  if (sel_size < buffersize) {
2269
0
    BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM);
2270
0
  }
2271
2272
  // Sort selections
2273
0
  b2nd_selection_t **ordered_selection = malloc(ndim * sizeof(b2nd_selection_t *));
2274
0
  BLOSC_ERROR_NULL(ordered_selection, BLOSC2_ERROR_MEMORY_ALLOC);
2275
0
  for (int i = 0; i < ndim; ++i) {
2276
0
    ordered_selection[i] = malloc(selection_size[i] * sizeof(b2nd_selection_t));
2277
0
    for (int j = 0; j < selection_size[i]; ++j) {
2278
0
      ordered_selection[i][j].index = j;
2279
0
      ordered_selection[i][j].value = selection[i][j];
2280
0
    }
2281
0
    qsort(ordered_selection[i], selection_size[i], sizeof(b2nd_selection_t), compare_selection);
2282
0
  }
2283
2284
  // Define pointers to iterate over ordered_selection data
2285
0
  b2nd_selection_t **p_ordered_selection_0 = malloc(ndim * sizeof(b2nd_selection_t *));
2286
0
  BLOSC_ERROR_NULL(p_ordered_selection_0, BLOSC2_ERROR_MEMORY_ALLOC);
2287
0
  b2nd_selection_t **p_ordered_selection_1 = malloc(ndim * sizeof(b2nd_selection_t *));
2288
0
  BLOSC_ERROR_NULL(p_ordered_selection_1, BLOSC2_ERROR_MEMORY_ALLOC);
2289
2290
0
  int64_t bufferstrides[B2ND_MAX_DIM];
2291
0
  bufferstrides[array->ndim - 1] = 1;
2292
0
  for (int i = array->ndim - 2; i >= 0; --i) {
2293
0
    bufferstrides[i] = bufferstrides[i + 1] * buffershape[i + 1];
2294
0
  }
2295
2296
0
  BLOSC_ERROR(iter_chunk(array, 0,
2297
0
                         selection_size, ordered_selection,
2298
0
                         p_ordered_selection_0,
2299
0
                         p_ordered_selection_1,
2300
0
                         buffer, buffershape, bufferstrides, get));
2301
2302
  // Free allocated memory
2303
0
  free(p_ordered_selection_0);
2304
0
  free(p_ordered_selection_1);
2305
0
  for (int i = 0; i < ndim; ++i) {
2306
0
    free(ordered_selection[i]);
2307
0
  }
2308
0
  free(ordered_selection);
2309
2310
0
  return BLOSC2_ERROR_SUCCESS;
2311
0
}
2312
2313
2314
int b2nd_get_orthogonal_selection(const b2nd_array_t *array, int64_t **selection, int64_t *selection_size, void *buffer,
2315
0
                                  int64_t *buffershape, int64_t buffersize) {
2316
0
  return orthogonal_selection((b2nd_array_t *)array, selection, selection_size, buffer, buffershape, buffersize, true);
2317
0
}
2318
2319
2320
int b2nd_set_orthogonal_selection(b2nd_array_t *array, int64_t **selection, int64_t *selection_size, const void *buffer,
2321
0
                                  int64_t *buffershape, int64_t buffersize) {
2322
0
  return orthogonal_selection(array, selection, selection_size, (void*)buffer, buffershape, buffersize, false);
2323
0
}
2324
2325
2326
b2nd_context_t *
2327
b2nd_create_ctx(const blosc2_storage *b2_storage, int8_t ndim, const int64_t *shape, const int32_t *chunkshape,
2328
                const int32_t *blockshape, const char *dtype, int8_t dtype_format, const blosc2_metalayer *metalayers,
2329
0
                int32_t nmetalayers) {
2330
0
  b2nd_context_t *ctx = malloc(sizeof(b2nd_context_t));
2331
0
  BLOSC_ERROR_NULL(ctx, NULL);
2332
0
  blosc2_storage *params_b2_storage = malloc(sizeof(blosc2_storage));
2333
0
  BLOSC_ERROR_NULL(params_b2_storage, NULL);
2334
0
  if (b2_storage == NULL) {
2335
0
    memcpy(params_b2_storage, &BLOSC2_STORAGE_DEFAULTS, sizeof(blosc2_storage));
2336
0
  }
2337
0
  else {
2338
0
    memcpy(params_b2_storage, b2_storage, sizeof(blosc2_storage));
2339
0
  }
2340
0
  blosc2_cparams *cparams = malloc(sizeof(blosc2_cparams));
2341
0
  BLOSC_ERROR_NULL(cparams, NULL);
2342
  // We need a copy of cparams mainly to be able to modify blocksize
2343
0
  if (params_b2_storage->cparams == NULL) {
2344
0
    memcpy(cparams, &BLOSC2_CPARAMS_DEFAULTS, sizeof(blosc2_cparams));
2345
0
  }
2346
0
  else {
2347
0
    memcpy(cparams, params_b2_storage->cparams, sizeof(blosc2_cparams));
2348
0
  }
2349
2350
0
  if (dtype == NULL) {
2351
    // ctx->dtype = strdup(B2ND_DEFAULT_DTYPE);
2352
0
    char buf[16] = {0};
2353
0
    snprintf(buf, sizeof(buf), "|S%d", cparams->typesize);
2354
0
    ctx->dtype = strdup(buf);
2355
0
  }
2356
0
  else {
2357
0
    ctx->dtype = strdup(dtype);
2358
0
  }
2359
0
  ctx->dtype_format = dtype_format;
2360
2361
0
  params_b2_storage->cparams = cparams;
2362
0
  ctx->b2_storage = params_b2_storage;
2363
0
  ctx->ndim = ndim;
2364
0
  int32_t blocknitems = 1;
2365
0
  for (int i = 0; i < ndim; i++) {
2366
0
    ctx->shape[i] = shape[i];
2367
0
    ctx->chunkshape[i] = chunkshape[i];
2368
0
    ctx->blockshape[i] = blockshape[i];
2369
0
    blocknitems *= ctx->blockshape[i];
2370
0
  }
2371
0
  cparams->blocksize = blocknitems * cparams->typesize;
2372
2373
0
  ctx->nmetalayers = nmetalayers;
2374
0
  for (int i = 0; i < nmetalayers; ++i) {
2375
0
    ctx->metalayers[i] = metalayers[i];
2376
0
  }
2377
2378
#if defined(HAVE_PLUGINS)
2379
  #include "blosc2/codecs-registry.h"
2380
  if ((ctx->b2_storage->cparams->compcode >= BLOSC_CODEC_ZFP_FIXED_ACCURACY) &&
2381
      (ctx->b2_storage->cparams->compcode <= BLOSC_CODEC_ZFP_FIXED_RATE)) {
2382
    for (int i = 0; i < BLOSC2_MAX_FILTERS; ++i) {
2383
      if ((ctx->b2_storage->cparams->filters[i] == BLOSC_SHUFFLE) ||
2384
          (ctx->b2_storage->cparams->filters[i] == BLOSC_BITSHUFFLE)) {
2385
        BLOSC_TRACE_ERROR("ZFP cannot be run in presence of SHUFFLE / BITSHUFFLE");
2386
        return NULL;
2387
      }
2388
    }
2389
  }
2390
#endif /* HAVE_PLUGINS */
2391
2392
0
  return ctx;
2393
0
}
2394
2395
2396
0
int b2nd_free_ctx(b2nd_context_t *ctx) {
2397
0
  ctx->b2_storage->cparams->schunk = NULL;
2398
0
  free(ctx->b2_storage->cparams);
2399
0
  free(ctx->b2_storage);
2400
0
  free(ctx->dtype);
2401
0
  free(ctx);
2402
2403
0
  return BLOSC2_ERROR_SUCCESS;
2404
0
}