xref: /freebsd/contrib/bc/include/library.h (revision 2a58b312)
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  */
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