star-buffer

Load 1D arrays in binary format
git clone git://git.meso-star.fr/star-buffer.git
Log | Files | Refs | README | LICENSE

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 }