sbuf.c (8241B)
1 /* Copyright (C) 2022, 2023 |Méso|Star> (contact@meso-star.com) 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #define _POSIX_C_SOURCE 200809L /* mmap support */ 17 #define _DEFAULT_SOURCE 1 /* MAP_POPULATE support */ 18 #define _BSD_SOURCE 1 /* MAP_POPULATE for glibc < 2.19 */ 19 20 #include "sbuf.h" 21 #include "sbuf_c.h" 22 #include "sbuf_log.h" 23 24 #include <rsys/mem_allocator.h> 25 26 #include <errno.h> 27 #include <unistd.h> 28 #include <sys/mman.h> /* mmap */ 29 30 /******************************************************************************* 31 * Helper functions 32 ******************************************************************************/ 33 static INLINE res_T 34 check_sbuf_create_args(const struct sbuf_create_args* args) 35 { 36 /* Nothing to check. Only return RES_BAD_ARG if args is NULL */ 37 return args ? RES_OK : RES_BAD_ARG; 38 } 39 40 static void 41 reset_sbuf(struct sbuf* sbuf) 42 { 43 ASSERT(sbuf); 44 sbuf->pagesize = 0; 45 sbuf->size = 0; 46 sbuf->szitem = 0; 47 sbuf->alitem = 0; 48 if(sbuf->buffer && sbuf->buffer != MAP_FAILED) 49 munmap(sbuf->buffer, sbuf->map_len); 50 sbuf->buffer = NULL; 51 sbuf->map_len= 0; 52 } 53 54 static res_T 55 map_data 56 (struct sbuf* sbuf, 57 const char* stream_name, 58 const int fd, /* File descriptor */ 59 const size_t filesz, /* Overall filesize */ 60 const off_t offset, /* Offset of the data into file */ 61 const size_t map_len, 62 void** out_map) /* Lenght of the data to map */ 63 { 64 void* map = NULL; 65 res_T res = RES_OK; 66 ASSERT(sbuf && stream_name && filesz && map_len && out_map); 67 ASSERT(IS_ALIGNED((size_t)offset, (size_t)sbuf->pagesize)); 68 69 if((size_t)offset + map_len > filesz) { 70 log_err(sbuf, "%s: the amount of data to load exceed the file size.\n", 71 stream_name); 72 res = RES_IO_ERR; 73 goto error; 74 } 75 76 map = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE|MAP_POPULATE, fd, offset); 77 if(map == MAP_FAILED) { 78 log_err(sbuf, "%s: could not map the data -- %s.\n", 79 stream_name, strerror(errno)); 80 res = RES_IO_ERR; 81 goto error; 82 } 83 84 exit: 85 *out_map = map; 86 return res; 87 error: 88 if(map == MAP_FAILED) map = NULL; 89 goto exit; 90 } 91 92 static res_T 93 load_stream(struct sbuf* sbuf, FILE* stream, const char* stream_name) 94 { 95 off_t offset; 96 size_t filesz; 97 res_T res = RES_OK; 98 ASSERT(sbuf && stream && stream_name); 99 100 reset_sbuf(sbuf); 101 102 /* Read file header */ 103 #define READ(Var, N, Name) { \ 104 if(fread((Var), sizeof(*(Var)), (N), stream) != (N)) { \ 105 log_err(sbuf, "%s: could not read the %s.\n", stream_name, (Name)); \ 106 res = RES_IO_ERR; \ 107 goto error; \ 108 } \ 109 } (void)0 110 READ(&sbuf->pagesize, 1, "page size"); 111 READ(&sbuf->size, 1, "number of items"); 112 READ(&sbuf->szitem, 1, "size of an item"); 113 READ(&sbuf->alitem, 1, "alignment of an item"); 114 #undef READ 115 116 if(!IS_ALIGNED(sbuf->pagesize, sbuf->pagesize_os)) { 117 log_err(sbuf, 118 "%s: invalid page size %li. The page size attribute must be aligned on " 119 "the page size of the operating system (%lu).\n", 120 stream_name, sbuf->pagesize, (unsigned long)sbuf->pagesize_os); 121 res = RES_BAD_ARG; 122 goto error; 123 } 124 if(!sbuf->size) { 125 log_err(sbuf, 126 "%s: invalid buffer size %lu. " 127 "The number of items stored cannot be zero.\n", 128 stream_name, sbuf->size); 129 res = RES_BAD_ARG; 130 goto error; 131 } 132 if(!sbuf->szitem) { 133 log_err(sbuf, 134 "%s: invalid item size `%lu'.\n", 135 stream_name, (unsigned long)sbuf->szitem); 136 res = RES_BAD_ARG; 137 goto error; 138 } 139 if(!IS_POW2(sbuf->alitem)) { 140 log_err(sbuf, 141 "%s: invalid item alignment `%lu'. It must be a power of 2.\n", 142 stream_name, (unsigned long)sbuf->alitem); 143 res = RES_BAD_ARG; 144 goto error; 145 } 146 if(sbuf->alitem > sbuf->pagesize) { 147 log_err(sbuf, 148 "%s: invalid item alignment `%lu'. " 149 "It must be less than the provided pagesize `%lu'.\n", 150 stream_name, (unsigned long)sbuf->alitem, (unsigned long)sbuf->pagesize); 151 res = RES_BAD_ARG; 152 goto error; 153 154 } 155 156 sbuf->pitch = ALIGN_SIZE(sbuf->szitem, sbuf->alitem); 157 158 /* Compute the length in bytes of the data to map */ 159 sbuf->map_len = sbuf->size * sbuf->pitch; 160 sbuf->map_len = ALIGN_SIZE(sbuf->map_len, (size_t)sbuf->pagesize); 161 162 /* Find the offsets of the data into the stream */ 163 offset = (off_t)ALIGN_SIZE((uint64_t)ftell(stream), sbuf->pagesize); 164 165 /* Retrieve the overall filesize */ 166 fseek(stream, 0, SEEK_END); 167 filesz = (size_t)ftell(stream); 168 169 /* Map the data */ 170 res = map_data(sbuf, stream_name, fileno(stream), filesz, offset, 171 sbuf->map_len, &sbuf->buffer); 172 if(res != RES_OK) goto error; 173 174 exit: 175 return res; 176 error: 177 reset_sbuf(sbuf); 178 goto exit; 179 } 180 181 182 static void 183 release_sbuf(ref_T* ref) 184 { 185 struct sbuf* sbuf; 186 ASSERT(ref); 187 sbuf = CONTAINER_OF(ref, struct sbuf, ref); 188 reset_sbuf(sbuf); 189 if(sbuf->logger == &sbuf->logger__) logger_release(&sbuf->logger__); 190 MEM_RM(sbuf->allocator, sbuf); 191 } 192 193 /******************************************************************************* 194 * Exported functions 195 ******************************************************************************/ 196 res_T 197 sbuf_create 198 (const struct sbuf_create_args* args, 199 struct sbuf** out_sbuf) 200 { 201 struct sbuf* sbuf = NULL; 202 struct mem_allocator* allocator = NULL; 203 res_T res = RES_OK; 204 205 if(!out_sbuf) { res = RES_BAD_ARG; goto error; } 206 res = check_sbuf_create_args(args); 207 if(res != RES_OK) goto error; 208 209 allocator = args->allocator ? args->allocator : &mem_default_allocator; 210 sbuf = MEM_CALLOC(allocator, 1, sizeof(*sbuf)); 211 if(!sbuf) { 212 if(args->verbose) { 213 #define ERR_STR "Could not allocate the Star-Buffer device.\n" 214 if(args->logger) { 215 logger_print(args->logger, LOG_ERROR, ERR_STR); 216 } else { 217 fprintf(stderr, MSG_ERROR_PREFIX ERR_STR); 218 } 219 #undef ERR_STR 220 } 221 res = RES_MEM_ERR; 222 goto error; 223 } 224 ref_init(&sbuf->ref); 225 sbuf->allocator = allocator; 226 sbuf->verbose = args->verbose; 227 sbuf->pagesize_os = (size_t)sysconf(_SC_PAGESIZE); 228 if(args->logger) { 229 sbuf->logger = args->logger; 230 } else { 231 setup_log_default(sbuf); 232 } 233 234 exit: 235 if(out_sbuf) *out_sbuf = sbuf; 236 return res; 237 error: 238 if(sbuf) { SBUF(ref_put(sbuf)); sbuf = NULL; } 239 goto exit; 240 } 241 242 res_T 243 sbuf_ref_get(struct sbuf* sbuf) 244 { 245 if(!sbuf) return RES_BAD_ARG; 246 ref_get(&sbuf->ref); 247 return RES_OK; 248 } 249 250 res_T 251 sbuf_ref_put(struct sbuf* sbuf) 252 { 253 if(!sbuf) return RES_BAD_ARG; 254 ref_put(&sbuf->ref, release_sbuf); 255 return RES_OK; 256 } 257 258 259 res_T 260 sbuf_load(struct sbuf* sbuf, const char* path) 261 { 262 FILE* file = NULL; 263 res_T res = RES_OK; 264 265 if(!sbuf || !path) { 266 res = RES_BAD_ARG; 267 goto error; 268 } 269 270 file = fopen(path, "r"); 271 if(!file) { 272 log_err(sbuf, "%s: error opening file `%s'.\n", FUNC_NAME, path); 273 res = RES_IO_ERR; 274 goto error; 275 } 276 277 res = load_stream(sbuf, file, path); 278 if(res != RES_OK) goto error; 279 280 exit: 281 if(file) fclose(file); 282 return res; 283 error: 284 goto exit; 285 } 286 287 res_T 288 sbuf_load_stream 289 (struct sbuf* sbuf, 290 FILE* stream, 291 const char* stream_name) 292 { 293 if(!sbuf || !stream) return RES_BAD_ARG; 294 return load_stream(sbuf, stream, stream_name ? stream_name : "<stream>"); 295 } 296 297 res_T 298 sbuf_get_desc(const struct sbuf* sbuf, struct sbuf_desc* desc) 299 { 300 if(!sbuf || !desc) return RES_BAD_ARG; 301 desc->buffer = sbuf->buffer; 302 desc->size = sbuf->size; 303 desc->szitem = (size_t)sbuf->szitem; 304 desc->alitem = (size_t)sbuf->alitem; 305 desc->pitch = (size_t)sbuf->pitch; 306 return RES_OK; 307 }