1 /* 2 * ***************************************************************************** 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 * 6 * Copyright (c) 2018-2024 Gavin D. Howard and contributors. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are met: 10 * 11 * * Redistributions of source code must retain the above copyright notice, this 12 * list of conditions and the following disclaimer. 13 * 14 * * Redistributions in binary form must reproduce the above copyright notice, 15 * this list of conditions and the following disclaimer in the documentation 16 * and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 * 30 * ***************************************************************************** 31 * 32 * The private header for the bc library. 33 * 34 */ 35 36 #ifndef LIBBC_PRIVATE_H 37 #define LIBBC_PRIVATE_H 38 39 #ifndef _WIN32 40 41 #include <pthread.h> 42 43 #endif // _WIN32 44 45 #include <bcl.h> 46 47 #include <num.h> 48 #include <vm.h> 49 50 #if BC_ENABLE_MEMCHECK 51 52 /** 53 * A typedef for Valgrind builds. This is to add a generation index for error 54 * checking. 55 */ 56 typedef struct BclNum 57 { 58 /// The number. 59 BcNum n; 60 61 /// The generation index. 62 size_t gen_idx; 63 64 } BclNum; 65 66 /** 67 * Clears the generation byte in a BclNumber and returns the value. 68 * @param n The BclNumber. 69 * @return The value of the index. 70 */ 71 #define BCL_NO_GEN(n) \ 72 ((n).i & ~(((size_t) UCHAR_MAX) << ((sizeof(size_t) - 1) * CHAR_BIT))) 73 74 /** 75 * Gets the generation index in a BclNumber. 76 * @param n The BclNumber. 77 * @return The generation index. 78 */ 79 #define BCL_GET_GEN(n) ((n).i >> ((sizeof(size_t) - 1) * CHAR_BIT)) 80 81 /** 82 * Turns a BclNumber into a BcNum. 83 * @param c The context. 84 * @param n The BclNumber. 85 */ 86 #define BCL_NUM(c, n) ((BclNum*) bc_vec_item(&(c)->nums, BCL_NO_GEN(n))) 87 88 /** 89 * Clears the generation index top byte in the BclNumber. 90 * @param n The BclNumber. 91 */ 92 #define BCL_CLEAR_GEN(n) \ 93 do \ 94 { \ 95 (n).i &= ~(((size_t) UCHAR_MAX) << ((sizeof(size_t) - 1) * CHAR_BIT)); \ 96 } \ 97 while (0) 98 99 #define BCL_CHECK_NUM_GEN(c, bn) \ 100 do \ 101 { \ 102 size_t gen_ = BCL_GET_GEN(bn); \ 103 BclNum* ptr_ = BCL_NUM(c, bn); \ 104 if (BCL_NUM_ARRAY(ptr_) == NULL) \ 105 { \ 106 bcl_nonexistentNum(); \ 107 } \ 108 if (gen_ != ptr_->gen_idx) \ 109 { \ 110 bcl_invalidGeneration(); \ 111 } \ 112 } \ 113 while (0) 114 115 #define BCL_CHECK_NUM_VALID(c, bn) \ 116 do \ 117 { \ 118 size_t idx_ = BCL_NO_GEN(bn); \ 119 if ((c)->nums.len <= idx_) \ 120 { \ 121 bcl_numIdxOutOfRange(); \ 122 } \ 123 BCL_CHECK_NUM_GEN(c, bn); \ 124 } \ 125 while (0) 126 127 /** 128 * Returns the limb array of the number. 129 * @param bn The number. 130 * @return The limb array. 131 */ 132 #define BCL_NUM_ARRAY(bn) ((bn)->n.num) 133 134 /** 135 * Returns the limb array of the number for a non-pointer. 136 * @param bn The number. 137 * @return The limb array. 138 */ 139 #define BCL_NUM_ARRAY_NP(bn) ((bn).n.num) 140 141 /** 142 * Returns the BcNum pointer. 143 * @param bn The number. 144 * @return The BcNum pointer. 145 */ 146 #define BCL_NUM_NUM(bn) (&(bn)->n) 147 148 /** 149 * Returns the BcNum pointer for a non-pointer. 150 * @param bn The number. 151 * @return The BcNum pointer. 152 */ 153 #define BCL_NUM_NUM_NP(bn) (&(bn).n) 154 155 // These functions only abort. They exist to give developers some idea of what 156 // went wrong when bugs are found, if they look at the Valgrind stack trace. 157 158 BC_NORETURN void 159 bcl_invalidGeneration(void); 160 161 BC_NORETURN void 162 bcl_nonexistentNum(void); 163 164 BC_NORETURN void 165 bcl_numIdxOutOfRange(void); 166 167 #else // BC_ENABLE_MEMCHECK 168 169 /** 170 * A typedef for non-Valgrind builds. 171 */ 172 typedef BcNum BclNum; 173 174 #define BCL_NO_GEN(n) ((n).i) 175 #define BCL_NUM(c, n) ((BclNum*) bc_vec_item(&(c)->nums, (n).i)) 176 #define BCL_CLEAR_GEN(n) ((void) (n)) 177 178 #define BCL_CHECK_NUM_GEN(c, bn) 179 #define BCL_CHECK_NUM_VALID(c, n) 180 181 #define BCL_NUM_ARRAY(bn) ((bn)->num) 182 #define BCL_NUM_ARRAY_NP(bn) ((bn).num) 183 184 #define BCL_NUM_NUM(bn) (bn) 185 #define BCL_NUM_NUM_NP(bn) (&(bn)) 186 187 #endif // BC_ENABLE_MEMCHECK 188 189 /** 190 * A header that sets a jump. 191 * @param vm The thread data. 192 * @param l The label to jump to on error. 193 */ 194 #define BC_FUNC_HEADER(vm, l) \ 195 do \ 196 { \ 197 BC_SETJMP(vm, l); \ 198 vm->err = BCL_ERROR_NONE; \ 199 } \ 200 while (0) 201 202 /** 203 * A footer for functions that do not return an error code. 204 */ 205 #define BC_FUNC_FOOTER_NO_ERR(vm) \ 206 do \ 207 { \ 208 BC_UNSETJMP(vm); \ 209 } \ 210 while (0) 211 212 /** 213 * A footer for functions that *do* return an error code. 214 * @param vm The thread data. 215 * @param e The error variable to set. 216 */ 217 #define BC_FUNC_FOOTER(vm, e) \ 218 do \ 219 { \ 220 e = vm->err; \ 221 BC_FUNC_FOOTER_NO_ERR(vm); \ 222 } \ 223 while (0) 224 225 /** 226 * A footer that sets up n based the value of e and sets up the return value in 227 * idx. 228 * @param c The context. 229 * @param e The error. 230 * @param bn The number. 231 * @param idx The idx to set as the return value. 232 */ 233 #define BC_MAYBE_SETUP(c, e, bn, idx) \ 234 do \ 235 { \ 236 if (BC_ERR((e) != BCL_ERROR_NONE)) \ 237 { \ 238 if (BCL_NUM_ARRAY_NP(bn) != NULL) bc_num_free(BCL_NUM_NUM_NP(bn)); \ 239 idx.i = 0 - (size_t) (e); \ 240 } \ 241 else idx = bcl_num_insert(c, &(bn)); \ 242 } \ 243 while (0) 244 245 /** 246 * A header to check the context and return an error encoded in a number if it 247 * is bad. 248 * @param c The context. 249 */ 250 #define BC_CHECK_CTXT(vm, c) \ 251 do \ 252 { \ 253 c = bcl_contextHelper(vm); \ 254 if (BC_ERR(c == NULL)) \ 255 { \ 256 BclNumber n_num_; \ 257 n_num_.i = 0 - (size_t) BCL_ERROR_INVALID_CONTEXT; \ 258 return n_num_; \ 259 } \ 260 } \ 261 while (0) 262 263 /** 264 * A header to check the context and return an error directly if it is bad. 265 * @param c The context. 266 */ 267 #define BC_CHECK_CTXT_ERR(vm, c) \ 268 do \ 269 { \ 270 c = bcl_contextHelper(vm); \ 271 if (BC_ERR(c == NULL)) \ 272 { \ 273 return BCL_ERROR_INVALID_CONTEXT; \ 274 } \ 275 } \ 276 while (0) 277 278 /** 279 * A header to check the context and abort if it is bad. 280 * @param c The context. 281 */ 282 #define BC_CHECK_CTXT_ASSERT(vm, c) \ 283 do \ 284 { \ 285 c = bcl_contextHelper(vm); \ 286 assert(c != NULL); \ 287 } \ 288 while (0) 289 290 /** 291 * A header to check the number in the context and return an error encoded as a 292 * @param c The context. 293 * number if it is bad. 294 * @param n The BclNumber. 295 */ 296 #define BC_CHECK_NUM(c, n) \ 297 do \ 298 { \ 299 size_t no_gen_ = BCL_NO_GEN(n); \ 300 if (BC_ERR(no_gen_ >= (c)->nums.len)) \ 301 { \ 302 if ((n).i > 0 - (size_t) BCL_ERROR_NELEMS) return (n); \ 303 else \ 304 { \ 305 BclNumber n_num_; \ 306 n_num_.i = 0 - (size_t) BCL_ERROR_INVALID_NUM; \ 307 return n_num_; \ 308 } \ 309 } \ 310 BCL_CHECK_NUM_GEN(c, n); \ 311 } \ 312 while (0) 313 314 //clang-format off 315 316 /** 317 * A header to check the number in the context and return an error directly if 318 * it is bad. 319 * @param c The context. 320 * @param n The BclNumber. 321 */ 322 #define BC_CHECK_NUM_ERR(c, n) \ 323 do \ 324 { \ 325 size_t no_gen_ = BCL_NO_GEN(n); \ 326 if (BC_ERR(no_gen_ >= (c)->nums.len)) \ 327 { \ 328 if ((n).i > 0 - (size_t) BCL_ERROR_NELEMS) \ 329 { \ 330 return (BclError) (0 - (n).i); \ 331 } \ 332 else return BCL_ERROR_INVALID_NUM; \ 333 } \ 334 BCL_CHECK_NUM_GEN(c, n); \ 335 } \ 336 while (0) 337 338 //clang-format on 339 340 /** 341 * Grows the context's nums array if necessary. 342 * @param c The context. 343 */ 344 #define BCL_GROW_NUMS(c) \ 345 do \ 346 { \ 347 if ((c)->free_nums.len == 0) \ 348 { \ 349 bc_vec_grow(&((c)->nums), 1); \ 350 } \ 351 } \ 352 while (0) 353 354 /** 355 * Frees a BcNum for bcl. This is a destructor. 356 * @param num The BcNum to free, as a void pointer. 357 */ 358 void 359 bcl_num_destruct(void* num); 360 361 /// The actual context struct. 362 typedef struct BclCtxt 363 { 364 /// The context's scale. 365 size_t scale; 366 367 /// The context's ibase. 368 size_t ibase; 369 370 /// The context's obase. 371 size_t obase; 372 373 /// A vector of BcNum numbers. 374 BcVec nums; 375 376 /// A vector of BclNumbers. These are the indices in nums that are currently 377 /// not used (because they were freed). 378 BcVec free_nums; 379 380 } BclCtxt; 381 382 /** 383 * Returns the @a BcVm for the current thread. 384 * @return The vm for the current thread. 385 */ 386 BcVm* 387 bcl_getspecific(void); 388 389 #ifndef _WIN32 390 391 typedef pthread_key_t BclTls; 392 393 #else // _WIN32 394 395 typedef DWORD BclTls; 396 397 #endif // _WIN32 398 399 #endif // LIBBC_PRIVATE_H 400