Skip to content

Commit ccb2795

Browse files
authored
Merge pull request #68 from dborovcanin/add-bitwise
Add bitwise operations support
2 parents c2a4800 + 4a804b9 commit ccb2795

File tree

7 files changed

+347
-0
lines changed

7 files changed

+347
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ go get github.com/vadv/gopher-lua-libs
5050
* [xmlpath](/xmlpath) [gopkg.in/xmlpath.v2](https://gopkg.in/xmlpath.v2) port
5151
* [yaml](/yaml) [gopkg.in/yaml.v2](https://gopkg.in/yaml.v2) port
5252
* [zabbix](/zabbix) zabbix bot
53+
* [bit](/bit) bitwise operations
5354

5455

5556
## Usage

bit/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# bit [![GoDoc](https://godoc.org/github.com/vadv/gopher-lua-libs/bit?bit.svg)](https://godoc.org/github.com/vadv/gopher-lua-libs/bit)
2+
3+
## Usage
4+
5+
```lua
6+
local bit = require("bit")
7+
8+
local result, _ = bit.band(1, 0)
9+
print(result)
10+
-- Output: 0
11+
12+
local result, _ = bit.lshift(10, 5)
13+
print(result)
14+
-- Output: 320
15+
```
16+

bit/api.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Package bit implements Go bitwise operations functionality for Lua.
2+
package bit
3+
4+
import (
5+
"fmt"
6+
"math"
7+
8+
lua "github.com/yuin/gopher-lua"
9+
)
10+
11+
type op uint
12+
13+
const (
14+
and op = iota
15+
or
16+
not
17+
xor
18+
ls
19+
rs
20+
)
21+
22+
// Bitwise returns a Lua function used for bitwise operations.
23+
func Bitwise(kind op) lua.LGFunction {
24+
return func(l *lua.LState) int {
25+
if kind > rs {
26+
l.RaiseError("unsupported operation type")
27+
return 0
28+
}
29+
val1, val2, err := prepareParams(l)
30+
if err != nil {
31+
l.Push(lua.LNil)
32+
l.Push(lua.LString(err.Error()))
33+
return 2
34+
}
35+
var ret uint32
36+
switch kind {
37+
case and:
38+
ret = val1 & val2
39+
case or:
40+
ret = val1 | val2
41+
case xor:
42+
ret = val1 ^ val2
43+
case ls:
44+
ret = val1 << val2
45+
case rs:
46+
ret = val1 >> val2
47+
}
48+
l.Push(lua.LNumber(ret))
49+
return 1
50+
}
51+
}
52+
53+
// Not implements bitwise not.
54+
func Not(l *lua.LState) int {
55+
val, err := intToU32(l.CheckInt(1))
56+
if err != nil {
57+
l.Push(lua.LNil)
58+
l.Push(lua.LString(err.Error()))
59+
return 2
60+
}
61+
l.Push(lua.LNumber(^val))
62+
return 1
63+
}
64+
65+
func prepareParams(l *lua.LState) (val1, val2 uint32, err error) {
66+
val1, err = intToU32(l.CheckInt(1))
67+
if err != nil {
68+
return 0, 0, err
69+
}
70+
val2, err = intToU32(l.CheckInt(2))
71+
if err != nil {
72+
return 0, 0, err
73+
}
74+
return
75+
}
76+
77+
func intToU32(i int) (uint32, error) {
78+
if i < 0 {
79+
return 0, fmt.Errorf("cannot convert negative int %d to uint32", i)
80+
}
81+
if i > math.MaxUint32 {
82+
return 0, fmt.Errorf("int %d overflows uint32", i)
83+
}
84+
return uint32(i), nil
85+
}

bit/api_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package bit
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/vadv/gopher-lua-libs/tests"
8+
)
9+
10+
func TestApi(t *testing.T) {
11+
preload := tests.SeveralPreloadFuncs(Preload)
12+
assert.NotZero(t, tests.RunLuaTestFile(t, preload, "./test/test_api.lua"))
13+
}

bit/loader.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package bit
2+
3+
import lua "github.com/yuin/gopher-lua"
4+
5+
// Preload adds bit to the given Lua state's package.preload table. After it
6+
// has been preloaded, it can be loaded using require:
7+
//
8+
// local bit = require("bit")
9+
func Preload(l *lua.LState) {
10+
l.PreloadModule("bit", Loader)
11+
}
12+
13+
// Loader is the module loader function.
14+
func Loader(L *lua.LState) int {
15+
t := L.NewTable()
16+
L.SetFuncs(t, api)
17+
L.Push(t)
18+
return 1
19+
}
20+
21+
var api = map[string]lua.LGFunction{
22+
"band": Bitwise(and),
23+
"bor": Bitwise(or),
24+
"bxor": Bitwise(xor),
25+
"lshift": Bitwise(ls),
26+
"rshift": Bitwise(rs),
27+
"bnot": Not,
28+
}

bit/test/test_api.lua

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
local bit = require 'bit'
2+
local assert = require 'assert'
3+
4+
function TestAnd(t)
5+
local tests = {
6+
{
7+
input1 = -3,
8+
input2 = 23,
9+
expected = nil,
10+
err = "cannot convert negative int -3 to uint32",
11+
},
12+
{
13+
input1 = 4294967296,
14+
input2 = 23,
15+
expected = nil,
16+
err = "int 4294967296 overflows uint32",
17+
},
18+
{
19+
input1 = 1,
20+
input2 = 0,
21+
expected = 0,
22+
},
23+
{
24+
input1 = 111,
25+
input2 = 222,
26+
expected = 78,
27+
}
28+
}
29+
for _, tt in ipairs(tests) do
30+
t:Run(tostring(tt.input1) .. " and " .. tostring(tt.input2), function(t)
31+
local got, err = bit.band(tt.input1, tt.input2)
32+
assert:Equal(t, tt.expected, got)
33+
assert:Equal(t, tt.err, err)
34+
end)
35+
end
36+
end
37+
38+
function TestOr(t)
39+
local tests = {
40+
{
41+
input1 = 5,
42+
input2 = -423,
43+
expected = nil,
44+
err = "cannot convert negative int -423 to uint32",
45+
},
46+
{
47+
input1 = 123,
48+
input2 = 4294967296,
49+
expected = nil,
50+
err = "int 4294967296 overflows uint32",
51+
},
52+
{
53+
input1 = 1,
54+
input2 = 0,
55+
expected = 1,
56+
},
57+
{
58+
input1 = 111,
59+
input2 = 222,
60+
expected = 255,
61+
}
62+
}
63+
for _, tt in ipairs(tests) do
64+
t:Run(tostring(tt.input1) .. " or " .. tostring(tt.input2), function(t)
65+
local got, err = bit.bor(tt.input1, tt.input2)
66+
assert:Equal(t, tt.expected, got)
67+
assert:Equal(t, tt.err, err)
68+
end)
69+
end
70+
end
71+
72+
function TestXor(t)
73+
local tests = {
74+
{
75+
input1 = -1,
76+
input2 = -46,
77+
expected = nil,
78+
err = "cannot convert negative int -1 to uint32",
79+
},
80+
{
81+
input1 = 4294967300,
82+
input2 = 46,
83+
expected = nil,
84+
err = "int 4294967300 overflows uint32",
85+
},
86+
{
87+
input1 = 1,
88+
input2 = 0,
89+
expected = 1,
90+
},
91+
{
92+
input1 = 111,
93+
input2 = 222,
94+
expected = 177,
95+
}
96+
}
97+
for _, tt in ipairs(tests) do
98+
t:Run(tostring(tt.input1) .. " xor " .. tostring(tt.input2), function(t)
99+
local got, err = bit.bxor(tt.input1, tt.input2)
100+
assert:Equal(t, tt.expected, got)
101+
assert:Equal(t, tt.err, err)
102+
end)
103+
end
104+
end
105+
106+
function TestLShift(t)
107+
local tests = {
108+
{
109+
input1 = 0,
110+
input2 = -10,
111+
expected = nil,
112+
err = "cannot convert negative int -10 to uint32",
113+
},
114+
{
115+
input1 = 4294967297,
116+
input2 = 4294967298,
117+
expected = nil,
118+
err = "int 4294967297 overflows uint32",
119+
},
120+
{
121+
input1 = 123456,
122+
input2 = 8,
123+
expected = 31604736,
124+
},
125+
{
126+
input1 = 0XFF,
127+
input2 = 8,
128+
expected = 65280,
129+
}
130+
}
131+
for _, tt in ipairs(tests) do
132+
t:Run(tostring(tt.input1) .. " << " .. tostring(tt.input2), function(t)
133+
local got, err = bit.lshift(tt.input1, tt.input2)
134+
assert:Equal(t, tt.expected, got)
135+
assert:Equal(t, tt.err, err)
136+
end)
137+
end
138+
end
139+
140+
function TestRShift(t)
141+
local tests = {
142+
{
143+
input1 = -10,
144+
input2 = 0,
145+
expected = nil,
146+
err = "cannot convert negative int -10 to uint32",
147+
},
148+
{
149+
input1 = 4294967296,
150+
input2 = -3,
151+
expected = nil,
152+
err = "int 4294967296 overflows uint32",
153+
},
154+
{
155+
input1 = 123456,
156+
input2 = 8,
157+
expected = 482,
158+
},
159+
{
160+
input1 = 0XFF,
161+
input2 = 1,
162+
expected = 0x7F,
163+
}
164+
}
165+
for _, tt in ipairs(tests) do
166+
t:Run(tostring(tt.input1) .. " >> " .. tostring(tt.input2), function(t)
167+
local got, err = bit.rshift(tt.input1, tt.input2)
168+
assert:Equal(t, tt.expected, got)
169+
assert:Equal(t, tt.err, err)
170+
end)
171+
end
172+
end
173+
174+
function TestNot(t)
175+
local tests = {
176+
{
177+
input = -3,
178+
expected = nil,
179+
err = "cannot convert negative int -3 to uint32",
180+
},
181+
{
182+
input = 4294967297,
183+
expected = nil,
184+
err = "int 4294967297 overflows uint32",
185+
},
186+
{
187+
input = 65536,
188+
expected = 4294901759,
189+
},
190+
{
191+
input = 4294901759,
192+
expected = 65536,
193+
}
194+
}
195+
for _, tt in ipairs(tests) do
196+
t:Run("not " .. tostring(tt.input), function(t)
197+
local got, err = bit.bnot(tt.input)
198+
assert:Equal(t, tt.expected, got)
199+
assert:Equal(t, tt.err, err)
200+
end)
201+
end
202+
end

plugin/preload.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"github.com/vadv/gopher-lua-libs/argparse"
55
"github.com/vadv/gopher-lua-libs/aws/cloudwatch"
66
"github.com/vadv/gopher-lua-libs/base64"
7+
"github.com/vadv/gopher-lua-libs/bit"
78
"github.com/vadv/gopher-lua-libs/cert_util"
89
"github.com/vadv/gopher-lua-libs/chef"
910
"github.com/vadv/gopher-lua-libs/cmd"
@@ -74,4 +75,5 @@ func PreloadAll(L *lua.LState) {
7475
xmlpath.Preload(L)
7576
yaml.Preload(L)
7677
zabbix.Preload(L)
78+
bit.Preload(L)
7779
}

0 commit comments

Comments
 (0)