mirror of
https://github.com/google/go-jsonnet.git
synced 2026-05-05 12:06:11 +02:00
feat: improve std.base64Decode performance 97%+ (#346)
feat: improve std.base64Decode performance 97%+ Provides a Go-native implementation of std.base64Decode and std.base64DecodeBytes benchmark old ns/op new ns/op delta Benchmark_Builtin_base64Decode-16 10946388307 25004135 -99.77% Benchmark_Builtin_base64DecodeBytes-16 6420742757 181513016 -97.17% related to #111
This commit is contained in:
parent
e8bd3f4ff8
commit
399a61ca31
5
builtin-benchmarks/base64DecodeBytes.jsonnet
Normal file
5
builtin-benchmarks/base64DecodeBytes.jsonnet
Normal file
File diff suppressed because one or more lines are too long
5
builtin-benchmarks/base64decode.jsonnet
Normal file
5
builtin-benchmarks/base64decode.jsonnet
Normal file
File diff suppressed because one or more lines are too long
52
builtins.go
52
builtins.go
@ -1032,6 +1032,56 @@ func builtinStrReplace(i *interpreter, trace traceElement, strv, fromv, tov valu
|
||||
return makeValueString(strings.Replace(sStr, sFrom, sTo, -1)), nil
|
||||
}
|
||||
|
||||
func base64DecodeGoBytes(i *interpreter, trace traceElement, str string) ([]byte, error) {
|
||||
strLen := len(str)
|
||||
if strLen%4 != 0 {
|
||||
msg := fmt.Sprintf("input string appears not to be a base64 encoded string. Wrong length found (%d)", strLen)
|
||||
return nil, makeRuntimeError(msg, i.getCurrentStackTrace(trace))
|
||||
}
|
||||
|
||||
decodedBytes, err := base64.StdEncoding.DecodeString(str)
|
||||
if err != nil {
|
||||
return nil, i.Error(fmt.Sprintf("failed to decode: %s", err), trace)
|
||||
}
|
||||
|
||||
return decodedBytes, nil
|
||||
}
|
||||
|
||||
func builtinBase64DecodeBytes(i *interpreter, trace traceElement, input value) (value, error) {
|
||||
vStr, err := i.getString(input, trace)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("base64DecodeBytes requires a string, got %s", input.getType().name)
|
||||
return nil, makeRuntimeError(msg, i.getCurrentStackTrace(trace))
|
||||
}
|
||||
|
||||
decodedBytes, err := base64DecodeGoBytes(i, trace, vStr.getGoString())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := make([]*cachedThunk, len(decodedBytes))
|
||||
for i := range decodedBytes {
|
||||
res[i] = readyThunk(makeValueNumber(float64(int(decodedBytes[i]))))
|
||||
}
|
||||
|
||||
return makeValueArray(res), nil
|
||||
}
|
||||
|
||||
func builtinBase64Decode(i *interpreter, trace traceElement, input value) (value, error) {
|
||||
vStr, err := i.getString(input, trace)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("base64DecodeBytes requires a string, got %s", input.getType().name)
|
||||
return nil, makeRuntimeError(msg, i.getCurrentStackTrace(trace))
|
||||
}
|
||||
|
||||
decodedBytes, err := base64DecodeGoBytes(i, trace, vStr.getGoString())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return makeValueString(string(decodedBytes)), nil
|
||||
}
|
||||
|
||||
func builtinUglyObjectFlatMerge(i *interpreter, trace traceElement, x value) (value, error) {
|
||||
// TODO(sbarzowski) consider keeping comprehensions in AST
|
||||
// It will probably be way less hacky, with better error messages and better performance
|
||||
@ -1381,6 +1431,8 @@ var funcBuiltins = buildBuiltinMap([]builtin{
|
||||
&ternaryBuiltin{name: "substr", function: builtinSubstr, parameters: ast.Identifiers{"str", "from", "len"}},
|
||||
&ternaryBuiltin{name: "splitLimit", function: builtinSplitLimit, parameters: ast.Identifiers{"str", "c", "maxsplits"}},
|
||||
&ternaryBuiltin{name: "strReplace", function: builtinStrReplace, parameters: ast.Identifiers{"str", "from", "to"}},
|
||||
&unaryBuiltin{name: "base64Decode", function: builtinBase64Decode, parameters: ast.Identifiers{"str"}},
|
||||
&unaryBuiltin{name: "base64DecodeBytes", function: builtinBase64DecodeBytes, parameters: ast.Identifiers{"str"}},
|
||||
&unaryBuiltin{name: "parseJson", function: builtinParseJSON, parameters: ast.Identifiers{"str"}},
|
||||
&unaryBuiltin{name: "base64", function: builtinBase64, parameters: ast.Identifiers{"input"}},
|
||||
&unaryBuiltin{name: "encodeUTF8", function: builtinEncodeUTF8, parameters: ast.Identifiers{"str"}},
|
||||
|
||||
@ -39,6 +39,14 @@ func Benchmark_Builtin_reverse(b *testing.B) {
|
||||
RunBenchmark(b, "reverse")
|
||||
}
|
||||
|
||||
func Benchmark_Builtin_base64Decode(b *testing.B) {
|
||||
RunBenchmark(b, "base64Decode")
|
||||
}
|
||||
|
||||
func Benchmark_Builtin_base64DecodeBytes(b *testing.B) {
|
||||
RunBenchmark(b, "base64DecodeBytes")
|
||||
}
|
||||
|
||||
func Benchmark_Builtin_base64(b *testing.B) {
|
||||
RunBenchmark(b, "base64")
|
||||
}
|
||||
|
||||
1
testdata/builtinBase64Decode.golden
vendored
Normal file
1
testdata/builtinBase64Decode.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
"a"
|
||||
1
testdata/builtinBase64Decode.jsonnet
vendored
Normal file
1
testdata/builtinBase64Decode.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
||||
std.base64Decode("YQ==")
|
||||
3
testdata/builtinBase64DecodeBytes.golden
vendored
Normal file
3
testdata/builtinBase64DecodeBytes.golden
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[
|
||||
97
|
||||
]
|
||||
1
testdata/builtinBase64DecodeBytes.jsonnet
vendored
Normal file
1
testdata/builtinBase64DecodeBytes.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
||||
std.base64DecodeBytes("YQ==")
|
||||
10
testdata/builtinBase64DecodeBytes_high_codepoint.golden
vendored
Normal file
10
testdata/builtinBase64DecodeBytes_high_codepoint.golden
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
RUNTIME ERROR: failed to decode: illegal base64 data at input byte 0
|
||||
-------------------------------------------------
|
||||
testdata/builtinBase64DecodeBytes_high_codepoint:1:1-30 builtin function <base64DecodeBytes>
|
||||
|
||||
std.base64DecodeBytes("ĀQ=")
|
||||
|
||||
-------------------------------------------------
|
||||
During evaluation
|
||||
|
||||
|
||||
1
testdata/builtinBase64DecodeBytes_high_codepoint.jsonnet
vendored
Normal file
1
testdata/builtinBase64DecodeBytes_high_codepoint.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
||||
std.base64DecodeBytes("ĀQ=")
|
||||
10
testdata/builtinBase64DecodeBytes_invalid_base64_data.golden
vendored
Normal file
10
testdata/builtinBase64DecodeBytes_invalid_base64_data.golden
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
RUNTIME ERROR: input string appears not to be a base64 encoded string. Wrong length found (5)
|
||||
-------------------------------------------------
|
||||
testdata/builtinBase64DecodeBytes_invalid_base64_data:1:1-31 builtin function <base64DecodeBytes>
|
||||
|
||||
std.base64DecodeBytes("wrong")
|
||||
|
||||
-------------------------------------------------
|
||||
During evaluation
|
||||
|
||||
|
||||
1
testdata/builtinBase64DecodeBytes_invalid_base64_data.jsonnet
vendored
Normal file
1
testdata/builtinBase64DecodeBytes_invalid_base64_data.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
||||
std.base64DecodeBytes("wrong")
|
||||
10
testdata/builtinBase64DecodeBytes_wrong_type.golden
vendored
Normal file
10
testdata/builtinBase64DecodeBytes_wrong_type.golden
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
RUNTIME ERROR: base64DecodeBytes requires a string, got number
|
||||
-------------------------------------------------
|
||||
testdata/builtinBase64DecodeBytes_wrong_type:1:1-25 builtin function <base64DecodeBytes>
|
||||
|
||||
std.base64DecodeBytes(1)
|
||||
|
||||
-------------------------------------------------
|
||||
During evaluation
|
||||
|
||||
|
||||
1
testdata/builtinBase64DecodeBytes_wrong_type.jsonnet
vendored
Normal file
1
testdata/builtinBase64DecodeBytes_wrong_type.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
||||
std.base64DecodeBytes(1)
|
||||
10
testdata/builtinBase64Decode_high_codepoint.golden
vendored
Normal file
10
testdata/builtinBase64Decode_high_codepoint.golden
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
RUNTIME ERROR: failed to decode: illegal base64 data at input byte 0
|
||||
-------------------------------------------------
|
||||
testdata/builtinBase64Decode_high_codepoint:1:1-25 builtin function <base64Decode>
|
||||
|
||||
std.base64Decode("ĀQ=")
|
||||
|
||||
-------------------------------------------------
|
||||
During evaluation
|
||||
|
||||
|
||||
1
testdata/builtinBase64Decode_high_codepoint.jsonnet
vendored
Normal file
1
testdata/builtinBase64Decode_high_codepoint.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
||||
std.base64Decode("ĀQ=")
|
||||
10
testdata/builtinBase64Decode_invalid_base64_data.golden
vendored
Normal file
10
testdata/builtinBase64Decode_invalid_base64_data.golden
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
RUNTIME ERROR: input string appears not to be a base64 encoded string. Wrong length found (5)
|
||||
-------------------------------------------------
|
||||
testdata/builtinBase64Decode_invalid_base64_data:1:1-26 builtin function <base64Decode>
|
||||
|
||||
std.base64Decode("wrong")
|
||||
|
||||
-------------------------------------------------
|
||||
During evaluation
|
||||
|
||||
|
||||
1
testdata/builtinBase64Decode_invalid_base64_data.jsonnet
vendored
Normal file
1
testdata/builtinBase64Decode_invalid_base64_data.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
||||
std.base64Decode("wrong")
|
||||
10
testdata/builtinBase64Decode_wrong_type.golden
vendored
Normal file
10
testdata/builtinBase64Decode_wrong_type.golden
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
RUNTIME ERROR: base64DecodeBytes requires a string, got number
|
||||
-------------------------------------------------
|
||||
testdata/builtinBase64Decode_wrong_type:1:1-20 builtin function <base64Decode>
|
||||
|
||||
std.base64Decode(1)
|
||||
|
||||
-------------------------------------------------
|
||||
During evaluation
|
||||
|
||||
|
||||
1
testdata/builtinBase64Decode_wrong_type.jsonnet
vendored
Normal file
1
testdata/builtinBase64Decode_wrong_type.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
||||
std.base64Decode(1)
|
||||
Loading…
x
Reference in New Issue
Block a user