summaryrefslogtreecommitdiff
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 @@
+/// Bitflags module.
+// The bitflag object makes it easy to manipulate flags in a bitmask.
+//
+// It has metamethods that do the hard work, adding flags sets them, substracting
+// unsets them. Comparing flags checks if all flags in the second set are also set
+// in the first set. The `has` method checks if all flags in the second set are
+// also set in the first set, but behaves slightly different.
+//
+// Indexing allows checking values or setting them by bit index (eg. 0-7 for flags
+// in the first byte).
+//
+// _NOTE_: unavailable flags (eg. Windows flags on a Posix system) should not be
+// omitted, but be assigned a value of 0. This is because the `has` method will
+// return `false` if the flags are checked and the value is 0.
+//
+// See `system.bitflag` (the constructor) for extensive examples on usage.
+// @classmod bitflags
+#include "bitflags.h"
+
+#define BITFLAGS_MT_NAME "LuaSystem.BitFlags"
+
+typedef struct {
+ LSBF_BITFLAG flags;
+} LS_BitFlags;
+
+/// Bit flags.
+// Bitflag objects can be used to easily manipulate and compare bit flags.
+// These are primarily for use with the terminal functions, but can be used
+// in other places as well.
+// @section bitflags
+
+
+// pushes a new LS_BitFlags object with the given value onto the stack
+void lsbf_pushbitflags(lua_State *L, LSBF_BITFLAG value) {
+ LS_BitFlags *obj = (LS_BitFlags *)lua_newuserdata(L, sizeof(LS_BitFlags));
+ if (!obj) luaL_error(L, "Memory allocation failed");
+ luaL_getmetatable(L, BITFLAGS_MT_NAME);
+ lua_setmetatable(L, -2);
+ obj->flags = value;
+}
+
+// gets the LS_BitFlags value at the given index. Returns a Lua error if it is not
+// a LS_BitFlags object.
+LSBF_BITFLAG lsbf_checkbitflags(lua_State *L, int index) {
+ LS_BitFlags *obj = (LS_BitFlags *)luaL_checkudata(L, index, BITFLAGS_MT_NAME);
+ return obj->flags;
+}
+
+// Validates that the given index is a table containing a field 'fieldname'
+// which is a bitflag object and returns its value.
+// If the index is not a table or the field is not a bitflag object, a Lua
+// error is raised. If the bitflag is not present, the default value is returned.
+// The stack remains unchanged.
+LSBF_BITFLAG lsbf_checkbitflagsfield(lua_State *L, int index, const char *fieldname, LSBF_BITFLAG default_value) {
+ luaL_checktype(L, index, LUA_TTABLE);
+ lua_getfield(L, index, fieldname);
+
+ // if null, return default value
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1);
+ return default_value;
+ }
+
+ // check to bitflags
+ LS_BitFlags *obj = luaL_testudata(L, -1, BITFLAGS_MT_NAME);
+ if (obj == NULL) {
+ lua_pop(L, 1);
+ return luaL_error(L, "bad argument #%d, field '%s' must be a bitflag object", index, fieldname);
+ }
+ LSBF_BITFLAG value = obj->flags;
+ lua_pop(L, 1);
+ return value;
+}
+
+/***
+Creates a new bitflag object from the given value.
+@function system.bitflag
+@tparam[opt=0] number value the value to create the bitflag object from.
+@treturn bitflag bitflag object with the given values set.
+@usage
+local sys = require 'system'
+local flags = sys.bitflag(2) -- b0010
+
+-- get state of individual bits
+print(flags[0]) -- false
+print(flags[1]) -- true
+
+-- set individual bits
+flags[0] = true -- b0011
+print(flags:value()) -- 3
+print(flags) -- "bitflags: 3"
+
+-- adding flags (bitwise OR)
+local flags1 = sys.bitflag(1) -- b0001
+local flags2 = sys.bitflag(2) -- b0010
+local flags3 = flags1 + flags2 -- b0011
+
+-- substracting flags (bitwise AND NOT)
+print(flags3:value()) -- 3
+flag3 = flag3 - flag3 -- b0000
+print(flags3:value()) -- 0
+
+-- comparing flags
+local flags4 = sys.bitflag(7) -- b0111
+local flags5 = sys.bitflag(255) -- b11111111
+print(flags5 ~= flags4) -- true, not the same flags
+local flags6 = sys.bitflag(7) -- b0111
+print(flags6 == flags4) -- true, same flags
+
+-- comparison of subsets
+local flags7 = sys.bitflag(0) -- b0000
+local flags8 = sys.bitflag(3) -- b0011
+local flags9 = sys.bitflag(7) -- b0111
+print(flags9:has_all_of(flags8)) -- true, flags8 bits are all set in flags9
+print(flags8:has_any_of(flags9)) -- true, some of flags9 bits are set in flags8
+print(flags8:has_all_of(flags7)) -- false, flags7 (== 0) is not set in flags8
+*/
+static int lsbf_new(lua_State *L) {
+ LSBF_BITFLAG flags = 0;
+ if (lua_gettop(L) > 0) {
+ flags = luaL_checkinteger(L, 1);
+ }
+ lsbf_pushbitflags(L, flags);
+ return 1;
+}
+
+/***
+Retrieves the numeric value of the bitflag object.
+@function bitflag:value
+@treturn number the numeric value of the bitflags.
+@usage
+local sys = require 'system'
+local flags = sys.bitflag() -- b0000
+flags[0] = true -- b0001
+flags[2] = true -- b0101
+print(flags:value()) -- 5
+*/
+static int lsbf_value(lua_State *L) {
+ lua_pushinteger(L, lsbf_checkbitflags(L, 1));
+ return 1;
+}
+
+static int lsbf_tostring(lua_State *L) {
+ lua_pushfstring(L, "bitflags: %d", lsbf_checkbitflags(L, 1));
+ return 1;
+}
+
+static int lsbf_add(lua_State *L) {
+ lsbf_pushbitflags(L, lsbf_checkbitflags(L, 1) | lsbf_checkbitflags(L, 2));
+ return 1;
+}
+
+static int lsbf_sub(lua_State *L) {
+ lsbf_pushbitflags(L, lsbf_checkbitflags(L, 1) & ~lsbf_checkbitflags(L, 2));
+ return 1;
+}
+
+static int lsbf_eq(lua_State *L) {
+ lua_pushboolean(L, lsbf_checkbitflags(L, 1) == lsbf_checkbitflags(L, 2));
+ return 1;
+}
+
+/***
+Checks if all the flags in the given subset are set.
+If the flags to check has a value `0`, it will always return `false`. So if there are flags that are
+unsupported on a platform, they can be set to 0 and the `has_all_of` function will
+return `false` if the flags are checked.
+@function bitflag:has_all_of
+@tparam bitflag subset the flags to check for.
+@treturn boolean true if all the flags are set, false otherwise.
+@usage
+local sys = require 'system'
+local flags = sys.bitflag(12) -- b1100
+local myflags = sys.bitflag(15) -- b1111
+print(flags:has_all_of(myflags)) -- false, not all bits in myflags are set in flags
+print(myflags:has_all_of(flags)) -- true, all bits in flags are set in myflags
+*/
+static int lsbf_has_all_of(lua_State *L) {
+ LSBF_BITFLAG a = lsbf_checkbitflags(L, 1);
+ LSBF_BITFLAG b = lsbf_checkbitflags(L, 2);
+ // Check if all bits in b are also set in a, and b is not 0
+ lua_pushboolean(L, (a & b) == b && b != 0);
+ return 1;
+}
+
+/***
+Checks if any of the flags in the given subset are set.
+If the flags to check has a value `0`, it will always return `false`. So if there are flags that are
+unsupported on a platform, they can be set to 0 and the `has_any_of` function will
+return `false` if the flags are checked.
+@function bitflag:has_any_of
+@tparam bitflag subset the flags to check for.
+@treturn boolean true if any of the flags are set, false otherwise.
+@usage
+local sys = require 'system'
+local flags = sys.bitflag(12) -- b1100
+local myflags = sys.bitflag(7) -- b0111
+print(flags:has_any_of(myflags)) -- true, some bits in myflags are set in flags
+print(myflags:has_any_of(flags)) -- true, some bits in flags are set in myflags
+*/
+static int lsbf_has_any_of(lua_State *L) {
+ LSBF_BITFLAG a = lsbf_checkbitflags(L, 1);
+ LSBF_BITFLAG b = lsbf_checkbitflags(L, 2);
+ // Check if any bits in b are set in a
+ lua_pushboolean(L, (a & b) != 0);
+ return 1;
+}
+
+static int lsbf_index(lua_State *L) {
+ if (!lua_isnumber(L, 2)) {
+ // the parameter isn't a number, just lookup the key in the metatable
+ lua_getmetatable(L, 1);
+ lua_pushvalue(L, 2);
+ lua_gettable(L, -2);
+ return 1;
+ }
+
+ int index = luaL_checkinteger(L, 2);
+ if (index < 0 || index >= sizeof(LSBF_BITFLAG) * 8) {
+ return luaL_error(L, "index out of range");
+ }
+ lua_pushboolean(L, (lsbf_checkbitflags(L, 1) & (1 << index)) != 0);
+ return 1;
+}
+
+static int lsbf_newindex(lua_State *L) {
+ LS_BitFlags *obj = (LS_BitFlags *)luaL_checkudata(L, 1, BITFLAGS_MT_NAME);
+
+ if (!lua_isnumber(L, 2)) {
+ return luaL_error(L, "index must be a number");
+ }
+ int index = luaL_checkinteger(L, 2);
+ if (index < 0 || index >= sizeof(LSBF_BITFLAG) * 8) {
+ return luaL_error(L, "index out of range");
+ }
+
+ luaL_checkany(L, 3);
+ if (lua_toboolean(L, 3)) {
+ obj->flags |= (1 << index);
+ } else {
+ obj->flags &= ~(1 << index);
+ }
+ return 0;
+}
+
+static const struct luaL_Reg lsbf_funcs[] = {
+ {"bitflag", lsbf_new},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lsbf_methods[] = {
+ {"value", lsbf_value},
+ {"has_all_of", lsbf_has_all_of},
+ {"has_any_of", lsbf_has_any_of},
+ {"__tostring", lsbf_tostring},
+ {"__add", lsbf_add},
+ {"__sub", lsbf_sub},
+ {"__eq", lsbf_eq},
+ {"__index", lsbf_index},
+ {"__newindex", lsbf_newindex},
+ {NULL, NULL}
+};
+
+void bitflags_open(lua_State *L) {
+ luaL_newmetatable(L, BITFLAGS_MT_NAME);
+ luaL_setfuncs(L, lsbf_methods, 0);
+ lua_pop(L, 1);
+
+ luaL_setfuncs(L, lsbf_funcs, 0);
+}