Coverage Report

Created: 2026-07-15 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/c-blosc2/blosc/sframe.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 "frame.h"
12
#include "blosc2.h"
13
14
#include <inttypes.h>
15
#include <limits.h>
16
#include <stdio.h>
17
#include <stdint.h>
18
#include <stdlib.h>
19
#include <string.h>
20
21
22
/* If C11 is supported, use it's built-in aligned allocation. */
23
#if __STDC_VERSION__ >= 201112L
24
#include <stdalign.h>
25
#endif
26
27
28
0
static char* sframe_make_index_path(const char* urlpath) {
29
0
  size_t path_len = strlen(urlpath);
30
0
  size_t suffix_len = strlen("/chunks.b2frame");
31
0
  if (path_len > SIZE_MAX - suffix_len - 1) {
32
0
    BLOSC_TRACE_ERROR("Index path length overflows size limits");
33
0
    return NULL;
34
0
  }
35
36
0
  char* index_path = malloc(path_len + suffix_len + 1);
37
0
  if (index_path == NULL) {
38
0
    return NULL;
39
0
  }
40
41
0
  int written = snprintf(index_path, path_len + suffix_len + 1, "%s/chunks.b2frame", urlpath);
42
0
  if (written < 0 || (size_t)written >= path_len + suffix_len + 1) {
43
0
    BLOSC_TRACE_ERROR("Error building index path");
44
0
    free(index_path);
45
0
    return NULL;
46
0
  }
47
48
0
  return index_path;
49
0
}
50
51
52
0
static char* sframe_make_chunk_path(const char* urlpath, int64_t nchunk) {
53
0
  if (nchunk < 0 || (uint64_t)nchunk > UINT32_MAX) {
54
0
    BLOSC_TRACE_ERROR("Chunk index (%" PRId64 ") is out of range for sframe filenames", nchunk);
55
0
    return NULL;
56
0
  }
57
58
0
  size_t path_len = strlen(urlpath);
59
0
  size_t suffix_len = strlen("/.chunk");
60
0
  size_t chunk_hex_len = 8;
61
0
  if (path_len > SIZE_MAX - suffix_len - chunk_hex_len - 1) {
62
0
    BLOSC_TRACE_ERROR("Chunk path length overflows size limits");
63
0
    return NULL;
64
0
  }
65
66
0
  size_t total_len = path_len + suffix_len + chunk_hex_len + 1;
67
0
  char* chunk_path = malloc(total_len);
68
0
  if (chunk_path == NULL) {
69
0
    return NULL;
70
0
  }
71
72
0
  int written = snprintf(chunk_path, total_len, "%s/%08" PRIX32 ".chunk", urlpath, (uint32_t)nchunk);
73
0
  if (written < 0 || (size_t)written >= total_len) {
74
0
    BLOSC_TRACE_ERROR("Error building chunk path for chunk index (%" PRId64 ")", nchunk);
75
0
    free(chunk_path);
76
0
    return NULL;
77
0
  }
78
79
0
  return chunk_path;
80
0
}
81
82
83
/* Open sparse frame index chunk */
84
0
void* sframe_open_index(const char* urlpath, const char* mode, const blosc2_io *io) {
85
0
  void* fp = NULL;
86
0
  char* index_path = sframe_make_index_path(urlpath);
87
0
  if (index_path) {
88
0
    blosc2_io_cb *io_cb = blosc2_get_io_cb(io->id);
89
0
    if (io_cb == NULL) {
90
0
      BLOSC_TRACE_ERROR("Error getting the input/output API");
91
0
      free(index_path);
92
0
      return NULL;
93
0
    }
94
0
    fp = io_cb->open(index_path, mode, io->params);
95
0
    if (fp == NULL)
96
0
      BLOSC_TRACE_ERROR("Error creating index path in: %s", index_path);
97
0
    free(index_path);
98
0
  }
99
0
  return fp;
100
0
}
101
102
/* Open directory/nchunk.chunk with 8 zeros of padding */
103
0
void* sframe_open_chunk(const char* urlpath, int64_t nchunk, const char* mode, const blosc2_io *io) {
104
0
  void* fp = NULL;
105
0
  char* chunk_path = sframe_make_chunk_path(urlpath, nchunk);
106
0
  if (chunk_path) {
107
0
    blosc2_io_cb *io_cb = blosc2_get_io_cb(io->id);
108
0
    if (io_cb == NULL) {
109
0
      BLOSC_TRACE_ERROR("Error getting the input/output API");
110
0
      free(chunk_path);
111
0
      return NULL;
112
0
    }
113
0
    fp = io_cb->open(chunk_path, mode, io->params);
114
0
    if (fp == NULL)
115
0
      BLOSC_TRACE_ERROR("Error opening chunk path in: %s", chunk_path);
116
0
    free(chunk_path);
117
0
  }
118
0
  return fp;
119
0
}
120
121
/* Append an existing chunk into a sparse frame. */
122
0
void* sframe_create_chunk(blosc2_frame_s* frame, uint8_t* chunk, int64_t nchunk, int64_t cbytes) {
123
0
  void* fpc = sframe_open_chunk(frame->urlpath, nchunk, "wb", frame->schunk->storage->io);
124
0
  if (fpc == NULL) {
125
0
    BLOSC_TRACE_ERROR("Cannot open the chunkfile.");
126
0
    return NULL;
127
0
  }
128
0
  blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
129
0
  if (io_cb == NULL) {
130
0
    BLOSC_TRACE_ERROR("Error getting the input/output API");
131
0
    return NULL;
132
0
  }
133
0
  int64_t io_pos = 0;
134
0
  int64_t wbytes = io_cb->write(chunk, 1, cbytes, io_pos, fpc);
135
0
  io_cb->close(fpc);
136
0
  if (wbytes != cbytes) {
137
0
    BLOSC_TRACE_ERROR("Cannot write the full chunk.");
138
0
    return NULL;
139
0
  }
140
141
0
  return frame;
142
0
}
143
144
/* Append an existing chunk into a sparse frame. */
145
0
int sframe_delete_chunk(const char *urlpath, int64_t nchunk) {
146
0
  char* chunk_path = sframe_make_chunk_path(urlpath, nchunk);
147
0
  if (chunk_path) {
148
0
    int rc = remove(chunk_path);
149
0
    free(chunk_path);
150
0
    return rc;
151
0
  }
152
0
  return BLOSC2_ERROR_FILE_REMOVE;
153
0
}
154
155
/* Get chunk from sparse frame. */
156
0
int32_t sframe_get_chunk(blosc2_frame_s* frame, int64_t nchunk, uint8_t** chunk, bool* needs_free){
157
0
  void *fpc = sframe_open_chunk(frame->urlpath, nchunk, "rb", frame->schunk->storage->io);
158
0
  if(fpc == NULL){
159
0
    BLOSC_TRACE_ERROR("Cannot open the chunkfile.");
160
0
    return BLOSC2_ERROR_FILE_OPEN;
161
0
  }
162
163
0
  blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id);
164
0
  if (io_cb == NULL) {
165
0
    BLOSC_TRACE_ERROR("Error getting the input/output API");
166
0
    return BLOSC2_ERROR_PLUGIN_IO;
167
0
  }
168
169
0
  int64_t chunk_cbytes = io_cb->size(fpc);
170
0
  if (chunk_cbytes < BLOSC_MIN_HEADER_LENGTH) {
171
    // A valid chunk file always starts with a Blosc header; a smaller (e.g. empty)
172
    // file means the on-disk state changed under us (another handle replaced this
173
    // chunk) or the file is corrupted.  Fail the same way as frame_get_lazychunk().
174
0
    BLOSC_TRACE_ERROR("Chunkfile is too small to contain a valid chunk.");
175
0
    io_cb->close(fpc);
176
0
    return BLOSC2_ERROR_FILE_READ;
177
0
  }
178
179
0
  if (io_cb->is_allocation_necessary) {
180
0
    *chunk = malloc((size_t)chunk_cbytes);
181
0
    *needs_free = true;
182
0
  }
183
0
  else {
184
0
    *needs_free = false;
185
0
  }
186
187
0
  int64_t io_pos = 0;
188
0
  int64_t rbytes = io_cb->read((void**)chunk, 1, chunk_cbytes, io_pos, fpc);
189
0
  io_cb->close(fpc);
190
0
  if (rbytes != chunk_cbytes) {
191
0
    BLOSC_TRACE_ERROR("Cannot read the chunk out of the chunkfile.");
192
0
    return BLOSC2_ERROR_FILE_READ;
193
0
  }
194
195
0
  return (int32_t)chunk_cbytes;
196
0
}