1 /* 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 3 * All rights reserved. 4 * 5 * This source code is licensed under both the BSD-style license (found in the 6 * LICENSE file in the root directory of this source tree) and the GPLv2 (found 7 * in the COPYING file in the root directory of this source tree). 8 * You may select, at your option, one of the above-listed licenses. 9 */ 10 11 /* 12 * This header file has common utility functions used in examples. 13 */ 14 #ifndef COMMON_H 15 #define COMMON_H 16 17 #include <stdlib.h> // malloc, free, exit 18 #include <stdio.h> // fprintf, perror, fopen, etc. 19 #include <string.h> // strerror 20 #include <errno.h> // errno 21 #include <sys/stat.h> // stat 22 #include <zstd.h> 23 24 25 /* UNUSED_ATTR tells the compiler it is okay if the function is unused. */ 26 #if defined(__GNUC__) 27 # define UNUSED_ATTR __attribute__((unused)) 28 #else 29 # define UNUSED_ATTR 30 #endif 31 32 #define HEADER_FUNCTION static UNUSED_ATTR 33 34 35 /* 36 * Define the returned error code from utility functions. 37 */ 38 typedef enum { 39 ERROR_fsize = 1, 40 ERROR_fopen = 2, 41 ERROR_fclose = 3, 42 ERROR_fread = 4, 43 ERROR_fwrite = 5, 44 ERROR_loadFile = 6, 45 ERROR_saveFile = 7, 46 ERROR_malloc = 8, 47 ERROR_largeFile = 9, 48 } COMMON_ErrorCode; 49 50 /*! CHECK 51 * Check that the condition holds. If it doesn't print a message and die. 52 */ 53 #define CHECK(cond, ...) \ 54 do { \ 55 if (!(cond)) { \ 56 fprintf(stderr, \ 57 "%s:%d CHECK(%s) failed: ", \ 58 __FILE__, \ 59 __LINE__, \ 60 #cond); \ 61 fprintf(stderr, "" __VA_ARGS__); \ 62 fprintf(stderr, "\n"); \ 63 exit(1); \ 64 } \ 65 } while (0) 66 67 /*! CHECK_ZSTD 68 * Check the zstd error code and die if an error occurred after printing a 69 * message. 70 */ 71 #define CHECK_ZSTD(fn) \ 72 do { \ 73 size_t const err = (fn); \ 74 CHECK(!ZSTD_isError(err), "%s", ZSTD_getErrorName(err)); \ 75 } while (0) 76 77 /*! fsize_orDie() : 78 * Get the size of a given file path. 79 * 80 * @return The size of a given file path. 81 */ 82 HEADER_FUNCTION size_t fsize_orDie(const char *filename) 83 { 84 struct stat st; 85 if (stat(filename, &st) != 0) { 86 /* error */ 87 perror(filename); 88 exit(ERROR_fsize); 89 } 90 91 off_t const fileSize = st.st_size; 92 size_t const size = (size_t)fileSize; 93 /* 1. fileSize should be non-negative, 94 * 2. if off_t -> size_t type conversion results in discrepancy, 95 * the file size is too large for type size_t. 96 */ 97 if ((fileSize < 0) || (fileSize != (off_t)size)) { 98 fprintf(stderr, "%s : filesize too large \n", filename); 99 exit(ERROR_largeFile); 100 } 101 return size; 102 } 103 104 /*! fopen_orDie() : 105 * Open a file using given file path and open option. 106 * 107 * @return If successful this function will return a FILE pointer to an 108 * opened file otherwise it sends an error to stderr and exits. 109 */ 110 HEADER_FUNCTION FILE* fopen_orDie(const char *filename, const char *instruction) 111 { 112 FILE* const inFile = fopen(filename, instruction); 113 if (inFile) return inFile; 114 /* error */ 115 perror(filename); 116 exit(ERROR_fopen); 117 } 118 119 /*! fclose_orDie() : 120 * Close an opened file using given FILE pointer. 121 */ 122 HEADER_FUNCTION void fclose_orDie(FILE* file) 123 { 124 if (!fclose(file)) { return; }; 125 /* error */ 126 perror("fclose"); 127 exit(ERROR_fclose); 128 } 129 130 /*! fread_orDie() : 131 * 132 * Read sizeToRead bytes from a given file, storing them at the 133 * location given by buffer. 134 * 135 * @return The number of bytes read. 136 */ 137 HEADER_FUNCTION size_t fread_orDie(void* buffer, size_t sizeToRead, FILE* file) 138 { 139 size_t const readSize = fread(buffer, 1, sizeToRead, file); 140 if (readSize == sizeToRead) return readSize; /* good */ 141 if (feof(file)) return readSize; /* good, reached end of file */ 142 /* error */ 143 perror("fread"); 144 exit(ERROR_fread); 145 } 146 147 /*! fwrite_orDie() : 148 * 149 * Write sizeToWrite bytes to a file pointed to by file, obtaining 150 * them from a location given by buffer. 151 * 152 * Note: This function will send an error to stderr and exit if it 153 * cannot write data to the given file pointer. 154 * 155 * @return The number of bytes written. 156 */ 157 HEADER_FUNCTION size_t fwrite_orDie(const void* buffer, size_t sizeToWrite, FILE* file) 158 { 159 size_t const writtenSize = fwrite(buffer, 1, sizeToWrite, file); 160 if (writtenSize == sizeToWrite) return sizeToWrite; /* good */ 161 /* error */ 162 perror("fwrite"); 163 exit(ERROR_fwrite); 164 } 165 166 /*! malloc_orDie() : 167 * Allocate memory. 168 * 169 * @return If successful this function returns a pointer to allo- 170 * cated memory. If there is an error, this function will send that 171 * error to stderr and exit. 172 */ 173 HEADER_FUNCTION void* malloc_orDie(size_t size) 174 { 175 void* const buff = malloc(size); 176 if (buff) return buff; 177 /* error */ 178 perror("malloc"); 179 exit(ERROR_malloc); 180 } 181 182 /*! loadFile_orDie() : 183 * load file into buffer (memory). 184 * 185 * Note: This function will send an error to stderr and exit if it 186 * cannot read data from the given file path. 187 * 188 * @return If successful this function will load file into buffer and 189 * return file size, otherwise it will printout an error to stderr and exit. 190 */ 191 HEADER_FUNCTION size_t loadFile_orDie(const char* fileName, void* buffer, size_t bufferSize) 192 { 193 size_t const fileSize = fsize_orDie(fileName); 194 CHECK(fileSize <= bufferSize, "File too large!"); 195 196 FILE* const inFile = fopen_orDie(fileName, "rb"); 197 size_t const readSize = fread(buffer, 1, fileSize, inFile); 198 if (readSize != (size_t)fileSize) { 199 fprintf(stderr, "fread: %s : %s \n", fileName, strerror(errno)); 200 exit(ERROR_fread); 201 } 202 fclose(inFile); /* can't fail, read only */ 203 return fileSize; 204 } 205 206 /*! mallocAndLoadFile_orDie() : 207 * allocate memory buffer and then load file into it. 208 * 209 * Note: This function will send an error to stderr and exit if memory allocation 210 * fails or it cannot read data from the given file path. 211 * 212 * @return If successful this function will return buffer and bufferSize(=fileSize), 213 * otherwise it will printout an error to stderr and exit. 214 */ 215 HEADER_FUNCTION void* mallocAndLoadFile_orDie(const char* fileName, size_t* bufferSize) 216 { 217 size_t const fileSize = fsize_orDie(fileName); 218 *bufferSize = fileSize; 219 void* const buffer = malloc_orDie(*bufferSize); 220 loadFile_orDie(fileName, buffer, *bufferSize); 221 return buffer; 222 } 223 224 /*! saveFile_orDie() : 225 * 226 * Save buffSize bytes to a given file path, obtaining them from a location pointed 227 * to by buff. 228 * 229 * Note: This function will send an error to stderr and exit if it 230 * cannot write to a given file. 231 */ 232 HEADER_FUNCTION void saveFile_orDie(const char* fileName, const void* buff, size_t buffSize) 233 { 234 FILE* const oFile = fopen_orDie(fileName, "wb"); 235 size_t const wSize = fwrite(buff, 1, buffSize, oFile); 236 if (wSize != (size_t)buffSize) { 237 fprintf(stderr, "fwrite: %s : %s \n", fileName, strerror(errno)); 238 exit(ERROR_fwrite); 239 } 240 if (fclose(oFile)) { 241 perror(fileName); 242 exit(ERROR_fclose); 243 } 244 } 245 246 #endif 247