| /* |
| * ***************************************************************************** |
| * |
| * SPDX-License-Identifier: BSD-2-Clause |
| * |
| * Copyright (c) 2018-2023 Gavin D. Howard and contributors. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, this |
| * list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| * |
| * ***************************************************************************** |
| * |
| * The private header for the bc library. |
| * |
| */ |
| |
| #ifndef LIBBC_PRIVATE_H |
| #define LIBBC_PRIVATE_H |
| |
| #ifndef _WIN32 |
| |
| #include <pthread.h> |
| |
| #endif // _WIN32 |
| |
| #include <bcl.h> |
| |
| #include <num.h> |
| #include <vm.h> |
| |
| #if BC_ENABLE_MEMCHECK |
| |
| /** |
| * A typedef for Valgrind builds. This is to add a generation index for error |
| * checking. |
| */ |
| typedef struct BclNum |
| { |
| /// The number. |
| BcNum n; |
| |
| /// The generation index. |
| size_t gen_idx; |
| |
| } BclNum; |
| |
| /** |
| * Clears the generation byte in a BclNumber and returns the value. |
| * @param n The BclNumber. |
| * @return The value of the index. |
| */ |
| #define BCL_NO_GEN(n) \ |
| ((n).i & ~(((size_t) UCHAR_MAX) << ((sizeof(size_t) - 1) * CHAR_BIT))) |
| |
| /** |
| * Gets the generation index in a BclNumber. |
| * @param n The BclNumber. |
| * @return The generation index. |
| */ |
| #define BCL_GET_GEN(n) ((n).i >> ((sizeof(size_t) - 1) * CHAR_BIT)) |
| |
| /** |
| * Turns a BclNumber into a BcNum. |
| * @param c The context. |
| * @param n The BclNumber. |
| */ |
| #define BCL_NUM(c, n) ((BclNum*) bc_vec_item(&(c)->nums, BCL_NO_GEN(n))) |
| |
| /** |
| * Clears the generation index top byte in the BclNumber. |
| * @param n The BclNumber. |
| */ |
| #define BCL_CLEAR_GEN(n) \ |
| do \ |
| { \ |
| (n).i &= ~(((size_t) UCHAR_MAX) << ((sizeof(size_t) - 1) * CHAR_BIT)); \ |
| } \ |
| while (0) |
| |
| #define BCL_CHECK_NUM_GEN(c, bn) \ |
| do \ |
| { \ |
| size_t gen_ = BCL_GET_GEN(bn); \ |
| BclNum* ptr_ = BCL_NUM(c, bn); \ |
| if (BCL_NUM_ARRAY(ptr_) == NULL) \ |
| { \ |
| bcl_nonexistentNum(); \ |
| } \ |
| if (gen_ != ptr_->gen_idx) \ |
| { \ |
| bcl_invalidGeneration(); \ |
| } \ |
| } \ |
| while (0) |
| |
| |
| #define BCL_CHECK_NUM_VALID(c, bn) \ |
| do \ |
| { \ |
| size_t idx_ = BCL_NO_GEN(bn); \ |
| if ((c)->nums.len <= idx_) \ |
| { \ |
| bcl_numIdxOutOfRange(); \ |
| } \ |
| BCL_CHECK_NUM_GEN(c, bn); \ |
| } \ |
| while (0) |
| |
| /** |
| * Returns the limb array of the number. |
| * @param bn The number. |
| * @return The limb array. |
| */ |
| #define BCL_NUM_ARRAY(bn) ((bn)->n.num) |
| |
| /** |
| * Returns the limb array of the number for a non-pointer. |
| * @param bn The number. |
| * @return The limb array. |
| */ |
| #define BCL_NUM_ARRAY_NP(bn) ((bn).n.num) |
| |
| /** |
| * Returns the BcNum pointer. |
| * @param bn The number. |
| * @return The BcNum pointer. |
| */ |
| #define BCL_NUM_NUM(bn) (&(bn)->n) |
| |
| /** |
| * Returns the BcNum pointer for a non-pointer. |
| * @param bn The number. |
| * @return The BcNum pointer. |
| */ |
| #define BCL_NUM_NUM_NP(bn) (&(bn).n) |
| |
| // These functions only abort. They exist to give developers some idea of what |
| // went wrong when bugs are found, if they look at the Valgrind stack trace. |
| |
| BC_NORETURN void |
| bcl_invalidGeneration(void); |
| |
| BC_NORETURN void |
| bcl_nonexistentNum(void); |
| |
| BC_NORETURN void |
| bcl_numIdxOutOfRange(void); |
| |
| #else // BC_ENABLE_MEMCHECK |
| |
| /** |
| * A typedef for non-Valgrind builds. |
| */ |
| typedef BcNum BclNum; |
| |
| #define BCL_NO_GEN(n) (n) |
| #define BCL_GET_GEN(n) (n) |
| #define BCL_NUM(c, n) ((BclNum*) bc_vec_item(&(c)->nums, (n).i)) |
| #define BCL_CLEAR_GEN(n) ((void) (n)) |
| |
| #define BCL_CHECK_NUM_GEN(c, bn) |
| #define BCL_CHECK_NUM_VALID(c, n) |
| |
| #define BCL_NUM_ARRAY(n) ((bn)->num) |
| #define BCL_NUM_ARRAY_NP(bn) ((bn).num) |
| |
| #define BCL_NUM_NUM(bn) (bn) |
| #define BCL_NUM_NUM_NP(bn) (&(bn)) |
| |
| #endif // BC_ENABLE_MEMCHECK |
| |
| /** |
| * A header that sets a jump. |
| * @param vm The thread data. |
| * @param l The label to jump to on error. |
| */ |
| #define BC_FUNC_HEADER(vm, l) \ |
| do \ |
| { \ |
| BC_SETJMP(vm, l); \ |
| vm->err = BCL_ERROR_NONE; \ |
| } \ |
| while (0) |
| |
| /** |
| * A footer for functions that do not return an error code. |
| */ |
| #define BC_FUNC_FOOTER_NO_ERR(vm) \ |
| do \ |
| { \ |
| BC_UNSETJMP(vm); \ |
| } \ |
| while (0) |
| |
| /** |
| * A footer for functions that *do* return an error code. |
| * @param vm The thread data. |
| * @param e The error variable to set. |
| */ |
| #define BC_FUNC_FOOTER(vm, e) \ |
| do \ |
| { \ |
| e = vm->err; \ |
| BC_FUNC_FOOTER_NO_ERR(vm); \ |
| } \ |
| while (0) |
| |
| /** |
| * A footer that sets up n based the value of e and sets up the return value in |
| * idx. |
| * @param c The context. |
| * @param e The error. |
| * @param bn The number. |
| * @param idx The idx to set as the return value. |
| */ |
| #define BC_MAYBE_SETUP(c, e, bn, idx) \ |
| do \ |
| { \ |
| if (BC_ERR((e) != BCL_ERROR_NONE)) \ |
| { \ |
| if (BCL_NUM_ARRAY_NP(bn) != NULL) bc_num_free(BCL_NUM_NUM_NP(bn)); \ |
| idx.i = 0 - (size_t) (e); \ |
| } \ |
| else idx = bcl_num_insert(c, &(bn)); \ |
| } \ |
| while (0) |
| |
| /** |
| * A header to check the context and return an error encoded in a number if it |
| * is bad. |
| * @param c The context. |
| */ |
| #define BC_CHECK_CTXT(vm, c) \ |
| do \ |
| { \ |
| c = bcl_contextHelper(vm); \ |
| if (BC_ERR(c == NULL)) \ |
| { \ |
| BclNumber n_num; \ |
| n_num.i = 0 - (size_t) BCL_ERROR_INVALID_CONTEXT; \ |
| return n_num; \ |
| } \ |
| } \ |
| while (0) |
| |
| /** |
| * A header to check the context and return an error directly if it is bad. |
| * @param c The context. |
| */ |
| #define BC_CHECK_CTXT_ERR(vm, c) \ |
| do \ |
| { \ |
| c = bcl_contextHelper(vm); \ |
| if (BC_ERR(c == NULL)) \ |
| { \ |
| return BCL_ERROR_INVALID_CONTEXT; \ |
| } \ |
| } \ |
| while (0) |
| |
| /** |
| * A header to check the context and abort if it is bad. |
| * @param c The context. |
| */ |
| #define BC_CHECK_CTXT_ASSERT(vm, c) \ |
| do \ |
| { \ |
| c = bcl_contextHelper(vm); \ |
| assert(c != NULL); \ |
| } \ |
| while (0) |
| |
| /** |
| * A header to check the number in the context and return an error encoded as a |
| * @param c The context. |
| * number if it is bad. |
| * @param n The BclNumber. |
| */ |
| #define BC_CHECK_NUM(c, n) \ |
| do \ |
| { \ |
| if (BC_ERR((n).i >= (c)->nums.len)) \ |
| { \ |
| if ((n).i > 0 - (size_t) BCL_ERROR_NELEMS) return (n); \ |
| else \ |
| { \ |
| BclNumber n_num_; \ |
| n_num_.i = 0 - (size_t) BCL_ERROR_INVALID_NUM; \ |
| return n_num_; \ |
| } \ |
| } \ |
| BCL_CHECK_NUM_GEN(c, n); \ |
| } \ |
| while (0) |
| |
| //clang-format off |
| |
| /** |
| * A header to check the number in the context and return an error directly if |
| * it is bad. |
| * @param c The context. |
| * @param n The BclNumber. |
| */ |
| #define BC_CHECK_NUM_ERR(c, n) \ |
| do \ |
| { \ |
| if (BC_ERR((n).i >= (c)->nums.len)) \ |
| { \ |
| if ((n).i > 0 - (size_t) BCL_ERROR_NELEMS) \ |
| { \ |
| return (BclError) (0 - (n).i); \ |
| } \ |
| else return BCL_ERROR_INVALID_NUM; \ |
| } \ |
| BCL_CHECK_NUM_GEN(c, n); \ |
| } \ |
| while (0) |
| |
| //clang-format on |
| |
| /** |
| * Grows the context's nums array if necessary. |
| * @param c The context. |
| */ |
| #define BCL_GROW_NUMS(c) \ |
| do \ |
| { \ |
| if ((c)->free_nums.len == 0) \ |
| { \ |
| bc_vec_grow(&((c)->nums), 1); \ |
| } \ |
| } \ |
| while (0) |
| |
| /** |
| * Frees a BcNum for bcl. This is a destructor. |
| * @param num The BcNum to free, as a void pointer. |
| */ |
| void |
| bcl_num_destruct(void* num); |
| |
| /// The actual context struct. |
| typedef struct BclCtxt |
| { |
| /// The context's scale. |
| size_t scale; |
| |
| /// The context's ibase. |
| size_t ibase; |
| |
| /// The context's obase. |
| size_t obase; |
| |
| /// A vector of BcNum numbers. |
| BcVec nums; |
| |
| /// A vector of BclNumbers. These are the indices in nums that are currently |
| /// not used (because they were freed). |
| BcVec free_nums; |
| |
| } BclCtxt; |
| |
| /** |
| * Returns the @a BcVm for the current thread. |
| * @return The vm for the current thread. |
| */ |
| BcVm* |
| bcl_getspecific(void); |
| |
| #ifndef _WIN32 |
| |
| typedef pthread_key_t BclTls; |
| |
| #else // _WIN32 |
| |
| typedef DWORD BclTls; |
| |
| #endif // _WIN32 |
| |
| #endif // LIBBC_PRIVATE_H |