diff --git a/docs/stdlib-base64.md b/docs/stdlib-base64.md new file mode 100644 index 0000000..864cfa7 --- /dev/null +++ b/docs/stdlib-base64.md @@ -0,0 +1,16 @@ +# Module - "base64" + +```golang +fmt := import("base64") +``` + +## Functions + +- `encode(src)`: returns the base64 encoding of src. +- `decode(s)`: returns the bytes represented by the base64 string s. +- `raw_encode(src)`: returns the base64 encoding of src but omits the padding. +- `raw_decode(s)`: returns the bytes represented by the base64 string s which omits the padding. +- `url_encode(src)`: returns the url-base64 encoding of src. +- `url_decode(s)`: returns the bytes represented by the url-base64 string s. +- `raw_url_encode(src)`: returns the url-base64 encoding of src but omits the padding. +- `raw_url_decode(s)`: returns the bytes represented by the url-base64 string s which omits the padding. \ No newline at end of file diff --git a/docs/stdlib-hex.md b/docs/stdlib-hex.md new file mode 100644 index 0000000..9d219ba --- /dev/null +++ b/docs/stdlib-hex.md @@ -0,0 +1,10 @@ +# Module - "hex" + +```golang +fmt := import("hex") +``` + +## Functions + +- `encode(src)`: returns the hexadecimal encoding of src. +- `decode(s)`: returns the bytes represented by the hexadecimal string s. \ No newline at end of file diff --git a/docs/stdlib.md b/docs/stdlib.md index ccb4f1c..6383060 100644 --- a/docs/stdlib.md +++ b/docs/stdlib.md @@ -7,4 +7,6 @@ - [rand](https://github.com/d5/tengo/blob/master/docs/stdlib-rand.md): random functions - [fmt](https://github.com/d5/tengo/blob/master/docs/stdlib-fmt.md): formatting functions - [json](https://github.com/d5/tengo/blob/master/docs/stdlib-json.md): JSON functions -- [enum](https://github.com/d5/tengo/blob/master/docs/stdlib-enum.md): Enumeration functions \ No newline at end of file +- [enum](https://github.com/d5/tengo/blob/master/docs/stdlib-enum.md): Enumeration functions +- [hex](https://github.com/d5/tengo/blob/master/docs/stdlib-hex.md): hex encoding and decoding functions +- [base64](https://github.com/d5/tengo/blob/master/docs/stdlib-base64.md): base64 encoding and decoding functions \ No newline at end of file diff --git a/stdlib/base64.go b/stdlib/base64.go new file mode 100644 index 0000000..40a746c --- /dev/null +++ b/stdlib/base64.go @@ -0,0 +1,20 @@ +package stdlib + +import ( + "encoding/base64" + "github.com/d5/tengo/objects" +) + +var base64Module = map[string]objects.Object{ + "encode": &objects.UserFunction{Value: FuncAYRS(base64.StdEncoding.EncodeToString)}, + "decode": &objects.UserFunction{Value: FuncASRYE(base64.StdEncoding.DecodeString)}, + + "raw_encode": &objects.UserFunction{Value: FuncAYRS(base64.RawStdEncoding.EncodeToString)}, + "raw_decode": &objects.UserFunction{Value: FuncASRYE(base64.RawStdEncoding.DecodeString)}, + + "url_encode": &objects.UserFunction{Value: FuncAYRS(base64.URLEncoding.EncodeToString)}, + "url_decode": &objects.UserFunction{Value: FuncASRYE(base64.URLEncoding.DecodeString)}, + + "raw_url_encode": &objects.UserFunction{Value: FuncAYRS(base64.RawURLEncoding.EncodeToString)}, + "raw_url_decode": &objects.UserFunction{Value: FuncASRYE(base64.RawURLEncoding.DecodeString)}, +} diff --git a/stdlib/base64_test.go b/stdlib/base64_test.go new file mode 100644 index 0000000..b1157d6 --- /dev/null +++ b/stdlib/base64_test.go @@ -0,0 +1,20 @@ +package stdlib_test + +import "testing" + +var base64Bytes1 = []byte{0x06, 0xAC, 0x76, 0x1B, 0x1D, 0x6A, 0xFA, 0x9D, 0xB1, 0xA0} +const base64Std = "Bqx2Gx1q+p2xoA==" +const base64URL = "Bqx2Gx1q-p2xoA==" +const base64RawStd = "Bqx2Gx1q+p2xoA" +const base64RawURL = "Bqx2Gx1q-p2xoA" + +func TestBase64(t *testing.T) { + module(t, `base64`).call("encode", base64Bytes1).expect(base64Std) + module(t, `base64`).call("decode", base64Std).expect(base64Bytes1) + module(t, `base64`).call("url_encode", base64Bytes1).expect(base64URL) + module(t, `base64`).call("url_decode", base64URL).expect(base64Bytes1) + module(t, `base64`).call("raw_encode", base64Bytes1).expect(base64RawStd) + module(t, `base64`).call("raw_decode", base64RawStd).expect(base64Bytes1) + module(t, `base64`).call("raw_url_encode", base64Bytes1).expect(base64RawURL) + module(t, `base64`).call("raw_url_decode", base64RawURL).expect(base64Bytes1) +} diff --git a/stdlib/builtin_modules.go b/stdlib/builtin_modules.go index cc2796f..722461b 100644 --- a/stdlib/builtin_modules.go +++ b/stdlib/builtin_modules.go @@ -11,4 +11,6 @@ var BuiltinModules = map[string]map[string]objects.Object{ "rand": randModule, "fmt": fmtModule, "json": jsonModule, + "base64": base64Module, + "hex": hexModule, } diff --git a/stdlib/func_typedefs.go b/stdlib/func_typedefs.go index 26c7ddd..c7bd11f 100644 --- a/stdlib/func_typedefs.go +++ b/stdlib/func_typedefs.go @@ -1036,6 +1036,29 @@ func FuncAYRIE(fn func([]byte) (int, error)) objects.CallableFunc { } } +// FuncAYRS transform a function of 'func([]byte) string' signature +// into CallableFunc type. +func FuncAYRS(fn func([]byte) string) objects.CallableFunc { + return func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + return nil, objects.ErrWrongNumArguments + } + + y1, ok := objects.ToByteSlice(args[0]) + if !ok { + return nil, objects.ErrInvalidArgumentType{ + Name: "first", + Expected: "bytes(compatible)", + Found: args[0].TypeName(), + } + } + + res := fn(y1) + + return &objects.String{Value: res}, nil + } +} + // FuncASRIE transform a function of 'func(string) (int, error)' signature // into CallableFunc type. func FuncASRIE(fn func(string) (int, error)) objects.CallableFunc { @@ -1062,6 +1085,36 @@ func FuncASRIE(fn func(string) (int, error)) objects.CallableFunc { } } +// FuncASRYE transform a function of 'func(string) ([]byte, error)' signature +// into CallableFunc type. +func FuncASRYE(fn func(string) ([]byte, error)) objects.CallableFunc { + return func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + return nil, objects.ErrWrongNumArguments + } + + s1, ok := objects.ToString(args[0]) + if !ok { + return nil, objects.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + + res, err := fn(s1) + if err != nil { + return wrapError(err), nil + } + + if len(res) > tengo.MaxBytesLen { + return nil, objects.ErrBytesLimit + } + + return &objects.Bytes{Value: res}, nil + } +} + // FuncAIRSsE transform a function of 'func(int) ([]string, error)' signature // into CallableFunc type. func FuncAIRSsE(fn func(int) ([]string, error)) objects.CallableFunc { diff --git a/stdlib/hex.go b/stdlib/hex.go new file mode 100644 index 0000000..acc29e6 --- /dev/null +++ b/stdlib/hex.go @@ -0,0 +1,11 @@ +package stdlib + +import ( + "encoding/hex" + "github.com/d5/tengo/objects" +) + +var hexModule = map[string]objects.Object{ + "encode": &objects.UserFunction{Value: FuncAYRS(hex.EncodeToString)}, + "decode": &objects.UserFunction{Value: FuncASRYE(hex.DecodeString)}, +} diff --git a/stdlib/hex_test.go b/stdlib/hex_test.go new file mode 100644 index 0000000..f545a51 --- /dev/null +++ b/stdlib/hex_test.go @@ -0,0 +1,11 @@ +package stdlib_test + +import "testing" + +var hexBytes1 = []byte{0x06, 0xAC, 0x76, 0x1B, 0x1D, 0x6A, 0xFA, 0x9D, 0xB1, 0xA0} +const hex1 = "06ac761b1d6afa9db1a0" + +func TestHex(t *testing.T) { + module(t, `hex`).call("encode", hexBytes1).expect(hex1) + module(t, `hex`).call("decode", hex1).expect(hexBytes1) +}