aboutsummaryrefslogtreecommitdiff
path: root/src/bitflags.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bitflags.c')
-rw-r--r--src/bitflags.c270
1 files changed, 270 insertions, 0 deletions
diff --git a/src/bitflags.c b/src/bitflags.c
new file mode 100644
index 0000000..e397918
--- /dev/null
+++ b/src/bitflags.c
@@ -0,0 +1,270 @@
1/// Bitflags module.
2// The bitflag object makes it easy to manipulate flags in a bitmask.
3//
4// It has metamethods that do the hard work, adding flags sets them, substracting
5// unsets them. Comparing flags checks if all flags in the second set are also set
6// in the first set. The `has` method checks if all flags in the second set are
7// also set in the first set, but behaves slightly different.
8//
9// Indexing allows checking values or setting them by bit index (eg. 0-7 for flags
10// in the first byte).
11//
12// _NOTE_: unavailable flags (eg. Windows flags on a Posix system) should not be
13// omitted, but be assigned a value of 0. This is because the `has` method will
14// return `false` if the flags are checked and the value is 0.
15//
16// See `system.bitflag` (the constructor) for extensive examples on usage.
17// @classmod bitflags
18#include "bitflags.h"
19
20#define BITFLAGS_MT_NAME "LuaSystem.BitFlags"
21
22typedef struct {
23 LSBF_BITFLAG flags;
24} LS_BitFlags;
25
26/// Bit flags.
27// Bitflag objects can be used to easily manipulate and compare bit flags.
28// These are primarily for use with the terminal functions, but can be used
29// in other places as well.
30// @section bitflags
31
32
33// pushes a new LS_BitFlags object with the given value onto the stack
34void lsbf_pushbitflags(lua_State *L, LSBF_BITFLAG value) {
35 LS_BitFlags *obj = (LS_BitFlags *)lua_newuserdata(L, sizeof(LS_BitFlags));
36 if (!obj) luaL_error(L, "Memory allocation failed");
37 luaL_getmetatable(L, BITFLAGS_MT_NAME);
38 lua_setmetatable(L, -2);
39 obj->flags = value;
40}
41
42// gets the LS_BitFlags value at the given index. Returns a Lua error if it is not
43// a LS_BitFlags object.
44LSBF_BITFLAG lsbf_checkbitflags(lua_State *L, int index) {
45 LS_BitFlags *obj = (LS_BitFlags *)luaL_checkudata(L, index, BITFLAGS_MT_NAME);
46 return obj->flags;
47}
48
49// Validates that the given index is a table containing a field 'fieldname'
50// which is a bitflag object and returns its value.
51// If the index is not a table or the field is not a bitflag object, a Lua
52// error is raised. If the bitflag is not present, the default value is returned.
53// The stack remains unchanged.
54LSBF_BITFLAG lsbf_checkbitflagsfield(lua_State *L, int index, const char *fieldname, LSBF_BITFLAG default_value) {
55 luaL_checktype(L, index, LUA_TTABLE);
56 lua_getfield(L, index, fieldname);
57
58 // if null, return default value
59 if (lua_isnil(L, -1)) {
60 lua_pop(L, 1);
61 return default_value;
62 }
63
64 // check to bitflags
65 LS_BitFlags *obj = luaL_testudata(L, -1, BITFLAGS_MT_NAME);
66 if (obj == NULL) {
67 lua_pop(L, 1);
68 return luaL_error(L, "bad argument #%d, field '%s' must be a bitflag object", index, fieldname);
69 }
70 LSBF_BITFLAG value = obj->flags;
71 lua_pop(L, 1);
72 return value;
73}
74
75/***
76Creates a new bitflag object from the given value.
77@function system.bitflag
78@tparam[opt=0] number value the value to create the bitflag object from.
79@treturn bitflag bitflag object with the given values set.
80@usage
81local sys = require 'system'
82local flags = sys.bitflag(2) -- b0010
83
84-- get state of individual bits
85print(flags[0]) -- false
86print(flags[1]) -- true
87
88-- set individual bits
89flags[0] = true -- b0011
90print(flags:value()) -- 3
91print(flags) -- "bitflags: 3"
92
93-- adding flags (bitwise OR)
94local flags1 = sys.bitflag(1) -- b0001
95local flags2 = sys.bitflag(2) -- b0010
96local flags3 = flags1 + flags2 -- b0011
97
98-- substracting flags (bitwise AND NOT)
99print(flags3:value()) -- 3
100flag3 = flag3 - flag3 -- b0000
101print(flags3:value()) -- 0
102
103-- comparing flags
104local flags4 = sys.bitflag(7) -- b0111
105local flags5 = sys.bitflag(255) -- b11111111
106print(flags5 ~= flags4) -- true, not the same flags
107local flags6 = sys.bitflag(7) -- b0111
108print(flags6 == flags4) -- true, same flags
109
110-- comparison of subsets
111local flags7 = sys.bitflag(0) -- b0000
112local flags8 = sys.bitflag(3) -- b0011
113local flags9 = sys.bitflag(7) -- b0111
114print(flags9:has_all_of(flags8)) -- true, flags8 bits are all set in flags9
115print(flags8:has_any_of(flags9)) -- true, some of flags9 bits are set in flags8
116print(flags8:has_all_of(flags7)) -- false, flags7 (== 0) is not set in flags8
117*/
118static int lsbf_new(lua_State *L) {
119 LSBF_BITFLAG flags = 0;
120 if (lua_gettop(L) > 0) {
121 flags = luaL_checkinteger(L, 1);
122 }
123 lsbf_pushbitflags(L, flags);
124 return 1;
125}
126
127/***
128Retrieves the numeric value of the bitflag object.
129@function bitflag:value
130@treturn number the numeric value of the bitflags.
131@usage
132local sys = require 'system'
133local flags = sys.bitflag() -- b0000
134flags[0] = true -- b0001
135flags[2] = true -- b0101
136print(flags:value()) -- 5
137*/
138static int lsbf_value(lua_State *L) {
139 lua_pushinteger(L, lsbf_checkbitflags(L, 1));
140 return 1;
141}
142
143static int lsbf_tostring(lua_State *L) {
144 lua_pushfstring(L, "bitflags: %d", lsbf_checkbitflags(L, 1));
145 return 1;
146}
147
148static int lsbf_add(lua_State *L) {
149 lsbf_pushbitflags(L, lsbf_checkbitflags(L, 1) | lsbf_checkbitflags(L, 2));
150 return 1;
151}
152
153static int lsbf_sub(lua_State *L) {
154 lsbf_pushbitflags(L, lsbf_checkbitflags(L, 1) & ~lsbf_checkbitflags(L, 2));
155 return 1;
156}
157
158static int lsbf_eq(lua_State *L) {
159 lua_pushboolean(L, lsbf_checkbitflags(L, 1) == lsbf_checkbitflags(L, 2));
160 return 1;
161}
162
163/***
164Checks if all the flags in the given subset are set.
165If the flags to check has a value `0`, it will always return `false`. So if there are flags that are
166unsupported on a platform, they can be set to 0 and the `has_all_of` function will
167return `false` if the flags are checked.
168@function bitflag:has_all_of
169@tparam bitflag subset the flags to check for.
170@treturn boolean true if all the flags are set, false otherwise.
171@usage
172local sys = require 'system'
173local flags = sys.bitflag(12) -- b1100
174local myflags = sys.bitflag(15) -- b1111
175print(flags:has_all_of(myflags)) -- false, not all bits in myflags are set in flags
176print(myflags:has_all_of(flags)) -- true, all bits in flags are set in myflags
177*/
178static int lsbf_has_all_of(lua_State *L) {
179 LSBF_BITFLAG a = lsbf_checkbitflags(L, 1);
180 LSBF_BITFLAG b = lsbf_checkbitflags(L, 2);
181 // Check if all bits in b are also set in a, and b is not 0
182 lua_pushboolean(L, (a & b) == b && b != 0);
183 return 1;
184}
185
186/***
187Checks if any of the flags in the given subset are set.
188If the flags to check has a value `0`, it will always return `false`. So if there are flags that are
189unsupported on a platform, they can be set to 0 and the `has_any_of` function will
190return `false` if the flags are checked.
191@function bitflag:has_any_of
192@tparam bitflag subset the flags to check for.
193@treturn boolean true if any of the flags are set, false otherwise.
194@usage
195local sys = require 'system'
196local flags = sys.bitflag(12) -- b1100
197local myflags = sys.bitflag(7) -- b0111
198print(flags:has_any_of(myflags)) -- true, some bits in myflags are set in flags
199print(myflags:has_any_of(flags)) -- true, some bits in flags are set in myflags
200*/
201static int lsbf_has_any_of(lua_State *L) {
202 LSBF_BITFLAG a = lsbf_checkbitflags(L, 1);
203 LSBF_BITFLAG b = lsbf_checkbitflags(L, 2);
204 // Check if any bits in b are set in a
205 lua_pushboolean(L, (a & b) != 0);
206 return 1;
207}
208
209static int lsbf_index(lua_State *L) {
210 if (!lua_isnumber(L, 2)) {
211 // the parameter isn't a number, just lookup the key in the metatable
212 lua_getmetatable(L, 1);
213 lua_pushvalue(L, 2);
214 lua_gettable(L, -2);
215 return 1;
216 }
217
218 int index = luaL_checkinteger(L, 2);
219 if (index < 0 || index >= sizeof(LSBF_BITFLAG) * 8) {
220 return luaL_error(L, "index out of range");
221 }
222 lua_pushboolean(L, (lsbf_checkbitflags(L, 1) & (1 << index)) != 0);
223 return 1;
224}
225
226static int lsbf_newindex(lua_State *L) {
227 LS_BitFlags *obj = (LS_BitFlags *)luaL_checkudata(L, 1, BITFLAGS_MT_NAME);
228
229 if (!lua_isnumber(L, 2)) {
230 return luaL_error(L, "index must be a number");
231 }
232 int index = luaL_checkinteger(L, 2);
233 if (index < 0 || index >= sizeof(LSBF_BITFLAG) * 8) {
234 return luaL_error(L, "index out of range");
235 }
236
237 luaL_checkany(L, 3);
238 if (lua_toboolean(L, 3)) {
239 obj->flags |= (1 << index);
240 } else {
241 obj->flags &= ~(1 << index);
242 }
243 return 0;
244}
245
246static const struct luaL_Reg lsbf_funcs[] = {
247 {"bitflag", lsbf_new},
248 {NULL, NULL}
249};
250
251static const struct luaL_Reg lsbf_methods[] = {
252 {"value", lsbf_value},
253 {"has_all_of", lsbf_has_all_of},
254 {"has_any_of", lsbf_has_any_of},
255 {"__tostring", lsbf_tostring},
256 {"__add", lsbf_add},
257 {"__sub", lsbf_sub},
258 {"__eq", lsbf_eq},
259 {"__index", lsbf_index},
260 {"__newindex", lsbf_newindex},
261 {NULL, NULL}
262};
263
264void bitflags_open(lua_State *L) {
265 luaL_newmetatable(L, BITFLAGS_MT_NAME);
266 luaL_setfuncs(L, lsbf_methods, 0);
267 lua_pop(L, 1);
268
269 luaL_setfuncs(L, lsbf_funcs, 0);
270}