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