diff options
Diffstat (limited to 'src/bitflags.c')
-rw-r--r-- | src/bitflags.c | 270 |
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 | |||
22 | typedef 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 | ||
34 | void 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. | ||
44 | LSBF_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. | ||
54 | LSBF_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 | /*** | ||
76 | Creates 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 | ||
81 | local sys = require 'system' | ||
82 | local flags = sys.bitflag(2) -- b0010 | ||
83 | |||
84 | -- get state of individual bits | ||
85 | print(flags[0]) -- false | ||
86 | print(flags[1]) -- true | ||
87 | |||
88 | -- set individual bits | ||
89 | flags[0] = true -- b0011 | ||
90 | print(flags:value()) -- 3 | ||
91 | print(flags) -- "bitflags: 3" | ||
92 | |||
93 | -- adding flags (bitwise OR) | ||
94 | local flags1 = sys.bitflag(1) -- b0001 | ||
95 | local flags2 = sys.bitflag(2) -- b0010 | ||
96 | local flags3 = flags1 + flags2 -- b0011 | ||
97 | |||
98 | -- substracting flags (bitwise AND NOT) | ||
99 | print(flags3:value()) -- 3 | ||
100 | flag3 = flag3 - flag3 -- b0000 | ||
101 | print(flags3:value()) -- 0 | ||
102 | |||
103 | -- comparing flags | ||
104 | local flags4 = sys.bitflag(7) -- b0111 | ||
105 | local flags5 = sys.bitflag(255) -- b11111111 | ||
106 | print(flags5 ~= flags4) -- true, not the same flags | ||
107 | local flags6 = sys.bitflag(7) -- b0111 | ||
108 | print(flags6 == flags4) -- true, same flags | ||
109 | |||
110 | -- comparison of subsets | ||
111 | local flags7 = sys.bitflag(0) -- b0000 | ||
112 | local flags8 = sys.bitflag(3) -- b0011 | ||
113 | local flags9 = sys.bitflag(7) -- b0111 | ||
114 | print(flags9:has_all_of(flags8)) -- true, flags8 bits are all set in flags9 | ||
115 | print(flags8:has_any_of(flags9)) -- true, some of flags9 bits are set in flags8 | ||
116 | print(flags8:has_all_of(flags7)) -- false, flags7 (== 0) is not set in flags8 | ||
117 | */ | ||
118 | static 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 | /*** | ||
128 | Retrieves the numeric value of the bitflag object. | ||
129 | @function bitflag:value | ||
130 | @treturn number the numeric value of the bitflags. | ||
131 | @usage | ||
132 | local sys = require 'system' | ||
133 | local flags = sys.bitflag() -- b0000 | ||
134 | flags[0] = true -- b0001 | ||
135 | flags[2] = true -- b0101 | ||
136 | print(flags:value()) -- 5 | ||
137 | */ | ||
138 | static int lsbf_value(lua_State *L) { | ||
139 | lua_pushinteger(L, lsbf_checkbitflags(L, 1)); | ||
140 | return 1; | ||
141 | } | ||
142 | |||
143 | static int lsbf_tostring(lua_State *L) { | ||
144 | lua_pushfstring(L, "bitflags: %d", lsbf_checkbitflags(L, 1)); | ||
145 | return 1; | ||
146 | } | ||
147 | |||
148 | static int lsbf_add(lua_State *L) { | ||
149 | lsbf_pushbitflags(L, lsbf_checkbitflags(L, 1) | lsbf_checkbitflags(L, 2)); | ||
150 | return 1; | ||
151 | } | ||
152 | |||
153 | static int lsbf_sub(lua_State *L) { | ||
154 | lsbf_pushbitflags(L, lsbf_checkbitflags(L, 1) & ~lsbf_checkbitflags(L, 2)); | ||
155 | return 1; | ||
156 | } | ||
157 | |||
158 | static int lsbf_eq(lua_State *L) { | ||
159 | lua_pushboolean(L, lsbf_checkbitflags(L, 1) == lsbf_checkbitflags(L, 2)); | ||
160 | return 1; | ||
161 | } | ||
162 | |||
163 | /*** | ||
164 | Checks if all the flags in the given subset are set. | ||
165 | If the flags to check has a value `0`, it will always return `false`. So if there are flags that are | ||
166 | unsupported on a platform, they can be set to 0 and the `has_all_of` function will | ||
167 | return `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 | ||
172 | local sys = require 'system' | ||
173 | local flags = sys.bitflag(12) -- b1100 | ||
174 | local myflags = sys.bitflag(15) -- b1111 | ||
175 | print(flags:has_all_of(myflags)) -- false, not all bits in myflags are set in flags | ||
176 | print(myflags:has_all_of(flags)) -- true, all bits in flags are set in myflags | ||
177 | */ | ||
178 | static 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 | /*** | ||
187 | Checks if any of the flags in the given subset are set. | ||
188 | If the flags to check has a value `0`, it will always return `false`. So if there are flags that are | ||
189 | unsupported on a platform, they can be set to 0 and the `has_any_of` function will | ||
190 | return `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 | ||
195 | local sys = require 'system' | ||
196 | local flags = sys.bitflag(12) -- b1100 | ||
197 | local myflags = sys.bitflag(7) -- b0111 | ||
198 | print(flags:has_any_of(myflags)) -- true, some bits in myflags are set in flags | ||
199 | print(myflags:has_any_of(flags)) -- true, some bits in flags are set in myflags | ||
200 | */ | ||
201 | static 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 | |||
209 | static 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 | |||
226 | static 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 | |||
246 | static const struct luaL_Reg lsbf_funcs[] = { | ||
247 | {"bitflag", lsbf_new}, | ||
248 | {NULL, NULL} | ||
249 | }; | ||
250 | |||
251 | static 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 | |||
264 | void 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 | } | ||