1/* 2 * ***************************************************************************** 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 * 6 * Copyright (c) 2018-2023 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 */ 56typedef 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 158BC_NORETURN void 159bcl_invalidGeneration(void); 160 161BC_NORETURN void 162bcl_nonexistentNum(void); 163 164BC_NORETURN void 165bcl_numIdxOutOfRange(void); 166 167#else // BC_ENABLE_MEMCHECK 168 169/** 170 * A typedef for non-Valgrind builds. 171 */ 172typedef 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 */ 358void 359bcl_num_destruct(void* num); 360 361/// The actual context struct. 362typedef 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 */ 386BcVm* 387bcl_getspecific(void); 388 389#ifndef _WIN32 390 391typedef pthread_key_t BclTls; 392 393#else // _WIN32 394 395typedef DWORD BclTls; 396 397#endif // _WIN32 398 399#endif // LIBBC_PRIVATE_H 400