summaryrefslogtreecommitdiff
path: root/src/bitflags.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bitflags.c')
-rw-r--r--src/bitflags.c235
1 files changed, 235 insertions, 0 deletions
diff --git a/src/bitflags.c b/src/bitflags.c
new file mode 100644
index 0000000..89a88b7
--- /dev/null
+++ b/src/bitflags.c
@@ -0,0 +1,235 @@
+/// 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;
+}
+
+/***
+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, all bits in flags4 are set in flags5
+
+-- comparing with 0 flags: comparison and `has` behave differently
+local flags6 = sys.bitflag(0) -- b0000
+local flags7 = sys.bitflag(1) -- b0001
+print(flags6 < flags7) -- true, flags6 is a subset of flags7
+print(flags7:has(flags6)) -- false, flags6 is not set in flags7
+*/
+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;
+}
+
+static int lsbf_le(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
+ lua_pushboolean(L, (a & b) == a);
+ return 1;
+}
+
+/***
+Checks if the given flags are set.
+This is different from the `>=` and `<=` operators because if the flag 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` function will
+return `false` if the flags are checked.
+@function bitflag:has
+@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(myflags)) -- false, not all bits in myflags are set in flags
+print(myflags:has(flags)) -- true, all bits in flags are set in myflags
+*/
+static int lsbf_has(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) == a && b != 0);
+ return 1;
+}
+
+static int lsbf_lt(lua_State *L) {
+ LSBF_BITFLAG a = lsbf_checkbitflags(L, 1);
+ LSBF_BITFLAG b = lsbf_checkbitflags(L, 2);
+ // Check if a is strictly less than b, meaning a != b and a is a subset of b
+ lua_pushboolean(L, (a != b) && ((a & b) == a));
+ 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", lsbf_has},
+ {"__tostring", lsbf_tostring},
+ {"__add", lsbf_add},
+ {"__sub", lsbf_sub},
+ {"__eq", lsbf_eq},
+ {"__le", lsbf_le},
+ {"__lt", lsbf_lt},
+ {"__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);
+}