xref: /freebsd/contrib/bc/include/library.h (revision 1f1e2261e341e6ca6862f82261066ef1705f0a7a)
1 /*
2  * *****************************************************************************
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  *
6  * Copyright (c) 2018-2021 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 #include <bcl.h>
40 
41 #include <num.h>
42 
43 /**
44  * A header for functions that need to lock and setjmp(). It also sets the
45  * variable that tells bcl that it is running.
46  * @param l  The label to jump to on error.
47  */
48 #define BC_FUNC_HEADER_LOCK(l)   \
49 	do                           \
50 	{                            \
51 		BC_SIG_LOCK;             \
52 		BC_SETJMP_LOCKED(l);     \
53 		vm.err = BCL_ERROR_NONE; \
54 		vm.running = 1;          \
55 	}                            \
56 	while (0)
57 
58 /**
59  * A footer to unlock and stop the jumping if an error happened. It also sets
60  * the variable that tells bcl that it is running.
61  * @param e  The error variable to set.
62  */
63 #define BC_FUNC_FOOTER_UNLOCK(e) \
64 	do                           \
65 	{                            \
66 		BC_SIG_ASSERT_LOCKED;    \
67 		e = vm.err;              \
68 		vm.running = 0;          \
69 		BC_UNSETJMP;             \
70 		BC_LONGJMP_STOP;         \
71 		vm.sig_lock = 0;         \
72 	}                            \
73 	while (0)
74 
75 /**
76  * A header that sets a jump and sets running.
77  * @param l  The label to jump to on error.
78  */
79 #define BC_FUNC_HEADER(l)        \
80 	do                           \
81 	{                            \
82 		BC_SETJMP(l);            \
83 		vm.err = BCL_ERROR_NONE; \
84 		vm.running = 1;          \
85 	}                            \
86 	while (0)
87 
88 /**
89  * A header that assumes that signals are already locked. It sets a jump and
90  * running.
91  * @param l  The label to jump to on error.
92  */
93 #define BC_FUNC_HEADER_INIT(l)   \
94 	do                           \
95 	{                            \
96 		BC_SETJMP_LOCKED(l);     \
97 		vm.err = BCL_ERROR_NONE; \
98 		vm.running = 1;          \
99 	}                            \
100 	while (0)
101 
102 /**
103  * A footer for functions that do not return an error code. It clears running
104  * and unlocks the signals. It also stops the jumping.
105  */
106 #define BC_FUNC_FOOTER_NO_ERR \
107 	do                        \
108 	{                         \
109 		vm.running = 0;       \
110 		BC_UNSETJMP;          \
111 		BC_LONGJMP_STOP;      \
112 		vm.sig_lock = 0;      \
113 	}                         \
114 	while (0)
115 
116 /**
117  * A footer for functions that *do* return an error code. It clears running and
118  * unlocks the signals. It also stops the jumping.
119  * @param e  The error variable to set.
120  */
121 #define BC_FUNC_FOOTER(e)      \
122 	do                         \
123 	{                          \
124 		e = vm.err;            \
125 		BC_FUNC_FOOTER_NO_ERR; \
126 	}                          \
127 	while (0)
128 
129 /**
130  * A footer that sets up n based the value of e and sets up the return value in
131  * idx.
132  * @param c    The context.
133  * @param e    The error.
134  * @param n    The number.
135  * @param idx  The idx to set as the return value.
136  */
137 #define BC_MAYBE_SETUP(c, e, n, idx)                \
138 	do                                              \
139 	{                                               \
140 		if (BC_ERR((e) != BCL_ERROR_NONE))          \
141 		{                                           \
142 			if ((n).num != NULL) bc_num_free(&(n)); \
143 			idx.i = 0 - (size_t) (e);               \
144 		}                                           \
145 		else idx = bcl_num_insert(c, &(n));         \
146 	}                                               \
147 	while (0)
148 
149 /**
150  * A header to check the context and return an error encoded in a number if it
151  * is bad.
152  * @param c  The context.
153  */
154 #define BC_CHECK_CTXT(c)                                      \
155 	do                                                        \
156 	{                                                         \
157 		c = bcl_context();                                    \
158 		if (BC_ERR(c == NULL))                                \
159 		{                                                     \
160 			BclNumber n_num;                                  \
161 			n_num.i = 0 - (size_t) BCL_ERROR_INVALID_CONTEXT; \
162 			return n_num;                                     \
163 		}                                                     \
164 	}                                                         \
165 	while (0)
166 
167 /**
168  * A header to check the context and return an error directly if it is bad.
169  * @param c  The context.
170  */
171 #define BC_CHECK_CTXT_ERR(c)                  \
172 	do                                        \
173 	{                                         \
174 		c = bcl_context();                    \
175 		if (BC_ERR(c == NULL))                \
176 		{                                     \
177 			return BCL_ERROR_INVALID_CONTEXT; \
178 		}                                     \
179 	}                                         \
180 	while (0)
181 
182 /**
183  * A header to check the context and abort if it is bad.
184  * @param c  The context.
185  */
186 #define BC_CHECK_CTXT_ASSERT(c) \
187 	do                          \
188 	{                           \
189 		c = bcl_context();      \
190 		assert(c != NULL);      \
191 	}                           \
192 	while (0)
193 
194 /**
195  * A header to check the number in the context and return an error encoded as a
196  * @param c  The context.
197  * number if it is bad.
198  * @param n  The BclNumber.
199  */
200 #define BC_CHECK_NUM(c, n)                                         \
201 	do                                                             \
202 	{                                                              \
203 		if (BC_ERR((n).i >= (c)->nums.len))                        \
204 		{                                                          \
205 			if ((n).i > 0 - (size_t) BCL_ERROR_NELEMS) return (n); \
206 			else                                                   \
207 			{                                                      \
208 				BclNumber n_num;                                   \
209 				n_num.i = 0 - (size_t) BCL_ERROR_INVALID_NUM;      \
210 				return n_num;                                      \
211 			}                                                      \
212 		}                                                          \
213 	}                                                              \
214 	while (0)
215 
216 //clang-format off
217 
218 /**
219  * A header to check the number in the context and return an error directly if
220  * it is bad.
221  * @param c  The context.
222  * @param n  The BclNumber.
223  */
224 #define BC_CHECK_NUM_ERR(c, n)                         \
225 	do                                                 \
226 	{                                                  \
227 		if (BC_ERR((n).i >= (c)->nums.len))            \
228 		{                                              \
229 			if ((n).i > 0 - (size_t) BCL_ERROR_NELEMS) \
230 			{                                          \
231 				return (BclError) (0 - (n).i);         \
232 			}                                          \
233 			else return BCL_ERROR_INVALID_NUM;         \
234 		}                                              \
235 	}                                                  \
236 	while (0)
237 
238 //clang-format on
239 
240 /**
241  * Turns a BclNumber into a BcNum.
242  * @param c  The context.
243  * @param n  The BclNumber.
244  */
245 #define BC_NUM(c, n) ((BcNum*) bc_vec_item(&(c)->nums, (n).i))
246 
247 /**
248  * Frees a BcNum for bcl. This is a destructor.
249  * @param num  The BcNum to free, as a void pointer.
250  */
251 void
252 bcl_num_destruct(void* num);
253 
254 /// The actual context struct.
255 typedef struct BclCtxt
256 {
257 	/// The context's scale.
258 	size_t scale;
259 
260 	/// The context's ibase.
261 	size_t ibase;
262 
263 	/// The context's obase.
264 	size_t obase;
265 
266 	/// A vector of BcNum numbers.
267 	BcVec nums;
268 
269 	/// A vector of BclNumbers. These are the indices in nums that are currently
270 	/// not used (because they were freed).
271 	BcVec free_nums;
272 
273 } BclCtxt;
274 
275 #endif // LIBBC_PRIVATE_H
276