Lua(JIT)のbit演算
この記事はLua advent calendar 2022の19日目です。
Luaとbit演算
現在使われているLuaの多くは、5.x系だと思います。
5.3以降でbit演算子が追加されました。
Lua supports the following bitwise operators:
- &: bitwise AND
- |: bitwise OR
- ~: bitwise exclusive OR
- >>: right shift
- <<: left shift
- ~: unary bitwise NOT
出典 : https://www.lua.org/manual/5.3/manual.html#3.4.2
ところで、Luaのruntimeには公式以外に有名なものが一つありますよね。
そう、LuaJIT (a Just-In-Time Compiler for Lua)です。
これは非常に高速ですが、Lua5.1互換です。
ですのでbit演算子はありませんし、他にも幾つかのlibraryが使えなかったりします。
LuaJITでもbit演算はできる
さて、早速矛盾するようですが、LuaJITでもbit演算はできます。
言い直すなら、「bit演算子はない」ですが「bit演算」はできます。
新しい演算子を追加してしまうと互換性を失ってしまいますからね。
つまり、この機能は関数として提供されます。
LuaJITはLua5.1互換ですが、全く同じなわけではありません。
本家にはない機能を持っていたりもします。
使用例
LuaJITはNeovimというテキストエディタに組み込まれており、設定の記述やプラグインの作成に使えます。
vim scriptよりもはるかに高速なことから人気で、多くのプラグインが作成されています。
しかし、日本人である私にとって困る点が一つありました。
それは、LuaJITでは標準でマルチバイト文字を扱う方法がないということです。
Lua5.3以降では標準でutf8 moduleが存在するのですが、LuaJITにはありません。
そこで、同様の機能を使うためにbit演算を用いて作ったのがこちらのプラグインです。
こんな感じでbit演算を使っています。
local utf8 = {}
local bit = require("bit") -- luajit
local band = bit.band
local bor = bit.bor
local rshift = bit.rshift
local lshift = bit.lshift
--------------------------------------------------------------
-- 中略
--------------------------------------------------------------
---Receives zero or more integers, converts each one to its corresponding UTF-8 byte sequence
---and returns a string with the concatenation of all these sequences.
---@vararg integer
---@return string
function utf8.char(...)
local buffer = {}
for i, v in ipairs({ ... }) do
if v < 0 or v > 0x10FFFF then
error(create_errmsg(i, "char", "value"), 2)
elseif v < 0x80 then
-- single-byte
buffer[i] = string.char(v)
elseif v < 0x800 then
-- two-byte
local b1 = bor(0xC0, band(rshift(v, 6), 0x1F)) -- 110x-xxxx
local b2 = bor(0x80, band(v, 0x3F)) -- 10xx-xxxx
buffer[i] = string.char(b1, b2)
elseif v < 0x10000 then
-- three-byte
local b1 = bor(0xE0, band(rshift(v, 12), 0x0F)) -- 1110-xxxx
local b2 = bor(0x80, band(rshift(v, 6), 0x3F)) -- 10xx-xxxx
local b3 = bor(0x80, band(v, 0x3F)) -- 10xx-xxxx
buffer[i] = string.char(b1, b2, b3)
else
-- four-byte
local b1 = bor(0xF0, band(rshift(v, 18), 0x07)) -- 1111-0xxx
local b2 = bor(0x80, band(rshift(v, 12), 0x3F)) -- 10xx-xxxx
local b3 = bor(0x80, band(rshift(v, 6), 0x3F)) -- 10xx-xxxx
local b4 = bor(0x80, band(v, 0x3F)) -- 10xx-xxxx
buffer[i] = string.char(b1, b2, b3, b4)
end
end
return table.concat(buffer, "")
end
ネストが増えやすく演算子ほど書きやすくはないですが、十分使えます。
中々使う機会はないかもしれませんが、覚えておくとどこかで助かるかもしれませんね。
Discussion