xref: /freebsd-src/contrib/bc/include/library.h (revision 78bc019d220e05abb5b12f678f9b4a847019bbcc)
150696a6eSStefan Eßer /*
250696a6eSStefan Eßer  * *****************************************************************************
350696a6eSStefan Eßer  *
450696a6eSStefan Eßer  * SPDX-License-Identifier: BSD-2-Clause
550696a6eSStefan Eßer  *
610328f8bSStefan Eßer  * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
750696a6eSStefan Eßer  *
850696a6eSStefan Eßer  * Redistribution and use in source and binary forms, with or without
950696a6eSStefan Eßer  * modification, are permitted provided that the following conditions are met:
1050696a6eSStefan Eßer  *
1150696a6eSStefan Eßer  * * Redistributions of source code must retain the above copyright notice, this
1250696a6eSStefan Eßer  *   list of conditions and the following disclaimer.
1350696a6eSStefan Eßer  *
1450696a6eSStefan Eßer  * * Redistributions in binary form must reproduce the above copyright notice,
1550696a6eSStefan Eßer  *   this list of conditions and the following disclaimer in the documentation
1650696a6eSStefan Eßer  *   and/or other materials provided with the distribution.
1750696a6eSStefan Eßer  *
1850696a6eSStefan Eßer  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1950696a6eSStefan Eßer  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2050696a6eSStefan Eßer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2150696a6eSStefan Eßer  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
2250696a6eSStefan Eßer  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2350696a6eSStefan Eßer  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2450696a6eSStefan Eßer  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2550696a6eSStefan Eßer  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2650696a6eSStefan Eßer  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2750696a6eSStefan Eßer  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2850696a6eSStefan Eßer  * POSSIBILITY OF SUCH DAMAGE.
2950696a6eSStefan Eßer  *
3050696a6eSStefan Eßer  * *****************************************************************************
3150696a6eSStefan Eßer  *
3250696a6eSStefan Eßer  * The private header for the bc library.
3350696a6eSStefan Eßer  *
3450696a6eSStefan Eßer  */
3550696a6eSStefan Eßer 
3650696a6eSStefan Eßer #ifndef LIBBC_PRIVATE_H
3750696a6eSStefan Eßer #define LIBBC_PRIVATE_H
3850696a6eSStefan Eßer 
3950696a6eSStefan Eßer #include <bcl.h>
4050696a6eSStefan Eßer 
4150696a6eSStefan Eßer #include <num.h>
4250696a6eSStefan Eßer 
4344d4804dSStefan Eßer /**
4444d4804dSStefan Eßer  * A header for functions that need to lock and setjmp(). It also sets the
4544d4804dSStefan Eßer  * variable that tells bcl that it is running.
4644d4804dSStefan Eßer  * @param l  The label to jump to on error.
4744d4804dSStefan Eßer  */
4850696a6eSStefan Eßer #define BC_FUNC_HEADER_LOCK(l)   \
49*78bc019dSStefan Eßer 	do                           \
50*78bc019dSStefan Eßer 	{                            \
5150696a6eSStefan Eßer 		BC_SIG_LOCK;             \
5250696a6eSStefan Eßer 		BC_SETJMP_LOCKED(l);     \
5350696a6eSStefan Eßer 		vm.err = BCL_ERROR_NONE; \
5450696a6eSStefan Eßer 		vm.running = 1;          \
55*78bc019dSStefan Eßer 	}                            \
56*78bc019dSStefan Eßer 	while (0)
5750696a6eSStefan Eßer 
5844d4804dSStefan Eßer /**
5944d4804dSStefan Eßer  * A footer to unlock and stop the jumping if an error happened. It also sets
6044d4804dSStefan Eßer  * the variable that tells bcl that it is running.
6144d4804dSStefan Eßer  * @param e  The error variable to set.
6244d4804dSStefan Eßer  */
6350696a6eSStefan Eßer #define BC_FUNC_FOOTER_UNLOCK(e) \
64*78bc019dSStefan Eßer 	do                           \
65*78bc019dSStefan Eßer 	{                            \
6650696a6eSStefan Eßer 		BC_SIG_ASSERT_LOCKED;    \
6750696a6eSStefan Eßer 		e = vm.err;              \
6850696a6eSStefan Eßer 		vm.running = 0;          \
6950696a6eSStefan Eßer 		BC_UNSETJMP;             \
7050696a6eSStefan Eßer 		BC_LONGJMP_STOP;         \
7150696a6eSStefan Eßer 		vm.sig_lock = 0;         \
72*78bc019dSStefan Eßer 	}                            \
73*78bc019dSStefan Eßer 	while (0)
7450696a6eSStefan Eßer 
7544d4804dSStefan Eßer /**
7644d4804dSStefan Eßer  * A header that sets a jump and sets running.
7744d4804dSStefan Eßer  * @param l  The label to jump to on error.
7844d4804dSStefan Eßer  */
7950696a6eSStefan Eßer #define BC_FUNC_HEADER(l)        \
80*78bc019dSStefan Eßer 	do                           \
81*78bc019dSStefan Eßer 	{                            \
8250696a6eSStefan Eßer 		BC_SETJMP(l);            \
8350696a6eSStefan Eßer 		vm.err = BCL_ERROR_NONE; \
8450696a6eSStefan Eßer 		vm.running = 1;          \
85*78bc019dSStefan Eßer 	}                            \
86*78bc019dSStefan Eßer 	while (0)
8750696a6eSStefan Eßer 
8844d4804dSStefan Eßer /**
8944d4804dSStefan Eßer  * A header that assumes that signals are already locked. It sets a jump and
9044d4804dSStefan Eßer  * running.
9144d4804dSStefan Eßer  * @param l  The label to jump to on error.
9244d4804dSStefan Eßer  */
9350696a6eSStefan Eßer #define BC_FUNC_HEADER_INIT(l)   \
94*78bc019dSStefan Eßer 	do                           \
95*78bc019dSStefan Eßer 	{                            \
9650696a6eSStefan Eßer 		BC_SETJMP_LOCKED(l);     \
9750696a6eSStefan Eßer 		vm.err = BCL_ERROR_NONE; \
9850696a6eSStefan Eßer 		vm.running = 1;          \
99*78bc019dSStefan Eßer 	}                            \
100*78bc019dSStefan Eßer 	while (0)
10150696a6eSStefan Eßer 
10244d4804dSStefan Eßer /**
10344d4804dSStefan Eßer  * A footer for functions that do not return an error code. It clears running
10444d4804dSStefan Eßer  * and unlocks the signals. It also stops the jumping.
10544d4804dSStefan Eßer  */
10650696a6eSStefan Eßer #define BC_FUNC_FOOTER_NO_ERR \
107*78bc019dSStefan Eßer 	do                        \
108*78bc019dSStefan Eßer 	{                         \
10950696a6eSStefan Eßer 		vm.running = 0;       \
11050696a6eSStefan Eßer 		BC_UNSETJMP;          \
11150696a6eSStefan Eßer 		BC_LONGJMP_STOP;      \
11250696a6eSStefan Eßer 		vm.sig_lock = 0;      \
113*78bc019dSStefan Eßer 	}                         \
114*78bc019dSStefan Eßer 	while (0)
11550696a6eSStefan Eßer 
11644d4804dSStefan Eßer /**
11744d4804dSStefan Eßer  * A footer for functions that *do* return an error code. It clears running and
11844d4804dSStefan Eßer  * unlocks the signals. It also stops the jumping.
11944d4804dSStefan Eßer  * @param e  The error variable to set.
12044d4804dSStefan Eßer  */
12150696a6eSStefan Eßer #define BC_FUNC_FOOTER(e)      \
122*78bc019dSStefan Eßer 	do                         \
123*78bc019dSStefan Eßer 	{                          \
12450696a6eSStefan Eßer 		e = vm.err;            \
12550696a6eSStefan Eßer 		BC_FUNC_FOOTER_NO_ERR; \
126*78bc019dSStefan Eßer 	}                          \
127*78bc019dSStefan Eßer 	while (0)
12850696a6eSStefan Eßer 
12944d4804dSStefan Eßer /**
13044d4804dSStefan Eßer  * A footer that sets up n based the value of e and sets up the return value in
13144d4804dSStefan Eßer  * idx.
13244d4804dSStefan Eßer  * @param c    The context.
13344d4804dSStefan Eßer  * @param e    The error.
13444d4804dSStefan Eßer  * @param n    The number.
13544d4804dSStefan Eßer  * @param idx  The idx to set as the return value.
13644d4804dSStefan Eßer  */
13750696a6eSStefan Eßer #define BC_MAYBE_SETUP(c, e, n, idx)                \
138*78bc019dSStefan Eßer 	do                                              \
139*78bc019dSStefan Eßer 	{                                               \
140*78bc019dSStefan Eßer 		if (BC_ERR((e) != BCL_ERROR_NONE))          \
141*78bc019dSStefan Eßer 		{                                           \
14250696a6eSStefan Eßer 			if ((n).num != NULL) bc_num_free(&(n)); \
14350696a6eSStefan Eßer 			idx.i = 0 - (size_t) (e);               \
14450696a6eSStefan Eßer 		}                                           \
14550696a6eSStefan Eßer 		else idx = bcl_num_insert(c, &(n));         \
146*78bc019dSStefan Eßer 	}                                               \
147*78bc019dSStefan Eßer 	while (0)
14850696a6eSStefan Eßer 
14944d4804dSStefan Eßer /**
15044d4804dSStefan Eßer  * A header to check the context and return an error encoded in a number if it
15144d4804dSStefan Eßer  * is bad.
15244d4804dSStefan Eßer  * @param c  The context.
15344d4804dSStefan Eßer  */
15450696a6eSStefan Eßer #define BC_CHECK_CTXT(c)                                      \
155*78bc019dSStefan Eßer 	do                                                        \
156*78bc019dSStefan Eßer 	{                                                         \
15750696a6eSStefan Eßer 		c = bcl_context();                                    \
158*78bc019dSStefan Eßer 		if (BC_ERR(c == NULL))                                \
159*78bc019dSStefan Eßer 		{                                                     \
16050696a6eSStefan Eßer 			BclNumber n_num;                                  \
16150696a6eSStefan Eßer 			n_num.i = 0 - (size_t) BCL_ERROR_INVALID_CONTEXT; \
16250696a6eSStefan Eßer 			return n_num;                                     \
16350696a6eSStefan Eßer 		}                                                     \
164*78bc019dSStefan Eßer 	}                                                         \
165*78bc019dSStefan Eßer 	while (0)
16644d4804dSStefan Eßer 
16744d4804dSStefan Eßer /**
16844d4804dSStefan Eßer  * A header to check the context and return an error directly if it is bad.
16944d4804dSStefan Eßer  * @param c  The context.
17044d4804dSStefan Eßer  */
17150696a6eSStefan Eßer #define BC_CHECK_CTXT_ERR(c)                  \
172*78bc019dSStefan Eßer 	do                                        \
173*78bc019dSStefan Eßer 	{                                         \
17450696a6eSStefan Eßer 		c = bcl_context();                    \
175*78bc019dSStefan Eßer 		if (BC_ERR(c == NULL))                \
176*78bc019dSStefan Eßer 		{                                     \
17750696a6eSStefan Eßer 			return BCL_ERROR_INVALID_CONTEXT; \
17850696a6eSStefan Eßer 		}                                     \
179*78bc019dSStefan Eßer 	}                                         \
180*78bc019dSStefan Eßer 	while (0)
18150696a6eSStefan Eßer 
18244d4804dSStefan Eßer /**
18344d4804dSStefan Eßer  * A header to check the context and abort if it is bad.
18444d4804dSStefan Eßer  * @param c  The context.
18544d4804dSStefan Eßer  */
18650696a6eSStefan Eßer #define BC_CHECK_CTXT_ASSERT(c) \
187*78bc019dSStefan Eßer 	do                          \
188*78bc019dSStefan Eßer 	{                           \
18950696a6eSStefan Eßer 		c = bcl_context();      \
19050696a6eSStefan Eßer 		assert(c != NULL);      \
191*78bc019dSStefan Eßer 	}                           \
192*78bc019dSStefan Eßer 	while (0)
19350696a6eSStefan Eßer 
19444d4804dSStefan Eßer /**
19544d4804dSStefan Eßer  * A header to check the number in the context and return an error encoded as a
19644d4804dSStefan Eßer  * @param c  The context.
19744d4804dSStefan Eßer  * number if it is bad.
19844d4804dSStefan Eßer  * @param n  The BclNumber.
19944d4804dSStefan Eßer  */
20050696a6eSStefan Eßer #define BC_CHECK_NUM(c, n)                                         \
201*78bc019dSStefan Eßer 	do                                                             \
202*78bc019dSStefan Eßer 	{                                                              \
203*78bc019dSStefan Eßer 		if (BC_ERR((n).i >= (c)->nums.len))                        \
204*78bc019dSStefan Eßer 		{                                                          \
20550696a6eSStefan Eßer 			if ((n).i > 0 - (size_t) BCL_ERROR_NELEMS) return (n); \
206*78bc019dSStefan Eßer 			else                                                   \
207*78bc019dSStefan Eßer 			{                                                      \
20850696a6eSStefan Eßer 				BclNumber n_num;                                   \
20950696a6eSStefan Eßer 				n_num.i = 0 - (size_t) BCL_ERROR_INVALID_NUM;      \
21050696a6eSStefan Eßer 				return n_num;                                      \
21150696a6eSStefan Eßer 			}                                                      \
21250696a6eSStefan Eßer 		}                                                          \
213*78bc019dSStefan Eßer 	}                                                              \
214*78bc019dSStefan Eßer 	while (0)
215*78bc019dSStefan Eßer 
216*78bc019dSStefan Eßer //clang-format off
21750696a6eSStefan Eßer 
21844d4804dSStefan Eßer /**
21944d4804dSStefan Eßer  * A header to check the number in the context and return an error directly if
22044d4804dSStefan Eßer  * it is bad.
22144d4804dSStefan Eßer  * @param c  The context.
22244d4804dSStefan Eßer  * @param n  The BclNumber.
22344d4804dSStefan Eßer  */
22450696a6eSStefan Eßer #define BC_CHECK_NUM_ERR(c, n)                         \
225*78bc019dSStefan Eßer 	do                                                 \
226*78bc019dSStefan Eßer 	{                                                  \
227*78bc019dSStefan Eßer 		if (BC_ERR((n).i >= (c)->nums.len))            \
228*78bc019dSStefan Eßer 		{                                              \
22950696a6eSStefan Eßer 			if ((n).i > 0 - (size_t) BCL_ERROR_NELEMS) \
230*78bc019dSStefan Eßer 			{                                          \
23150696a6eSStefan Eßer 				return (BclError) (0 - (n).i);         \
232*78bc019dSStefan Eßer 			}                                          \
23350696a6eSStefan Eßer 			else return BCL_ERROR_INVALID_NUM;         \
23450696a6eSStefan Eßer 		}                                              \
235*78bc019dSStefan Eßer 	}                                                  \
236*78bc019dSStefan Eßer 	while (0)
237*78bc019dSStefan Eßer 
238*78bc019dSStefan Eßer //clang-format on
23950696a6eSStefan Eßer 
24044d4804dSStefan Eßer /**
24144d4804dSStefan Eßer  * Turns a BclNumber into a BcNum.
24244d4804dSStefan Eßer  * @param c  The context.
24344d4804dSStefan Eßer  * @param n  The BclNumber.
24444d4804dSStefan Eßer  */
24550696a6eSStefan Eßer #define BC_NUM(c, n) ((BcNum*) bc_vec_item(&(c)->nums, (n).i))
24650696a6eSStefan Eßer 
24744d4804dSStefan Eßer /**
24844d4804dSStefan Eßer  * Frees a BcNum for bcl. This is a destructor.
24944d4804dSStefan Eßer  * @param num  The BcNum to free, as a void pointer.
25044d4804dSStefan Eßer  */
251*78bc019dSStefan Eßer void
252*78bc019dSStefan Eßer bcl_num_destruct(void* num);
25350696a6eSStefan Eßer 
25444d4804dSStefan Eßer /// The actual context struct.
255*78bc019dSStefan Eßer typedef struct BclCtxt
256*78bc019dSStefan Eßer {
25744d4804dSStefan Eßer 	/// The context's scale.
25850696a6eSStefan Eßer 	size_t scale;
25944d4804dSStefan Eßer 
26044d4804dSStefan Eßer 	/// The context's ibase.
26150696a6eSStefan Eßer 	size_t ibase;
26244d4804dSStefan Eßer 
26344d4804dSStefan Eßer 	/// The context's obase.
26450696a6eSStefan Eßer 	size_t obase;
26550696a6eSStefan Eßer 
26644d4804dSStefan Eßer 	/// A vector of BcNum numbers.
26750696a6eSStefan Eßer 	BcVec nums;
26844d4804dSStefan Eßer 
26944d4804dSStefan Eßer 	/// A vector of BclNumbers. These are the indices in nums that are currently
27044d4804dSStefan Eßer 	/// not used (because they were freed).
27150696a6eSStefan Eßer 	BcVec free_nums;
27250696a6eSStefan Eßer 
27350696a6eSStefan Eßer } BclCtxt;
27450696a6eSStefan Eßer 
27550696a6eSStefan Eßer #endif // LIBBC_PRIVATE_H
276