mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-27 05:42:56 +00:00
native/std: add stringSplit
method
This commit is contained in:
parent
dadfe2b9ab
commit
40d1dd0e0d
4 changed files with 108 additions and 0 deletions
|
@ -232,6 +232,8 @@ func TestNativeHelpersCompile(t *testing.T) {
|
|||
{"memorySearch", []string{"[]byte{1}", "[]byte{2}"}},
|
||||
{"memorySearchIndex", []string{"[]byte{1}", "[]byte{2}", "3"}},
|
||||
{"memorySearchLastIndex", []string{"[]byte{1}", "[]byte{2}", "3"}},
|
||||
{"stringSplit", []string{`"a,b"`, `","`}},
|
||||
{"stringSplitNonEmpty", []string{`"a,b"`, `","`}},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -256,6 +258,11 @@ func getMethod(t *testing.T, ctr interop.ContractMD, name string, params []strin
|
|||
paramLen += 1 // true should be appended inside of an interop
|
||||
}
|
||||
name = "memorySearch"
|
||||
case strings.HasPrefix(name, "stringSplit"):
|
||||
if strings.HasSuffix(name, "NonEmpty") {
|
||||
paramLen += 1 // true should be appended inside of an interop
|
||||
}
|
||||
name = "stringSplit"
|
||||
default:
|
||||
name = strings.TrimSuffix(name, "WithData")
|
||||
}
|
||||
|
|
|
@ -132,6 +132,19 @@ func newStd() *Std {
|
|||
md = newMethodAndPrice(s.memorySearch4, 1<<6, callflag.NoneFlag)
|
||||
s.AddMethod(md, desc)
|
||||
|
||||
desc = newDescriptor("stringSplit", smartcontract.ArrayType,
|
||||
manifest.NewParameter("str", smartcontract.StringType),
|
||||
manifest.NewParameter("separator", smartcontract.StringType))
|
||||
md = newMethodAndPrice(s.stringSplit2, 1<<8, callflag.NoneFlag)
|
||||
s.AddMethod(md, desc)
|
||||
|
||||
desc = newDescriptor("stringSplit", smartcontract.ArrayType,
|
||||
manifest.NewParameter("str", smartcontract.StringType),
|
||||
manifest.NewParameter("separator", smartcontract.StringType),
|
||||
manifest.NewParameter("removeEmptyEntries", smartcontract.BoolType))
|
||||
md = newMethodAndPrice(s.stringSplit3, 1<<8, callflag.NoneFlag)
|
||||
s.AddMethod(md, desc)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
|
@ -355,6 +368,36 @@ func (s *Std) memorySearchAux(mem, val []byte, start int, backward bool) int {
|
|||
return index + start
|
||||
}
|
||||
|
||||
func (s *Std) stringSplit2(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||
str := s.toLimitedString(args[0])
|
||||
sep := toString(args[1])
|
||||
return stackitem.NewArray(s.stringSplitAux(str, sep, false))
|
||||
}
|
||||
|
||||
func (s *Std) stringSplit3(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||
str := s.toLimitedString(args[0])
|
||||
sep := toString(args[1])
|
||||
removeEmpty, err := args[2].TryBool()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return stackitem.NewArray(s.stringSplitAux(str, sep, removeEmpty))
|
||||
}
|
||||
|
||||
func (s *Std) stringSplitAux(str, sep string, removeEmpty bool) []stackitem.Item {
|
||||
var result []stackitem.Item
|
||||
|
||||
arr := strings.Split(str, sep)
|
||||
for i := range arr {
|
||||
if !removeEmpty || len(arr[i]) != 0 {
|
||||
result = append(result, stackitem.Make(arr[i]))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Metadata implements Contract interface.
|
||||
func (s *Std) Metadata() *interop.ContractMD {
|
||||
return &s.ContractMD
|
||||
|
|
|
@ -481,3 +481,47 @@ func TestMemorySearch(t *testing.T) {
|
|||
func() { s.memorySearch4(ic, []stackitem.Item{s2, s1, start, b}) })
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringSplit(t *testing.T) {
|
||||
s := newStd()
|
||||
ic := &interop.Context{VM: vm.New()}
|
||||
|
||||
check := func(t *testing.T, result []string, str, sep string, remove interface{}) {
|
||||
args := []stackitem.Item{stackitem.Make(str), stackitem.Make(sep)}
|
||||
var actual stackitem.Item
|
||||
if remove == nil {
|
||||
actual = s.stringSplit2(ic, args)
|
||||
} else {
|
||||
args = append(args, stackitem.NewBool(remove.(bool)))
|
||||
actual = s.stringSplit3(ic, args)
|
||||
}
|
||||
|
||||
arr, ok := actual.Value().([]stackitem.Item)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, len(result), len(arr))
|
||||
for i := range result {
|
||||
require.Equal(t, stackitem.Make(result[i]), arr[i])
|
||||
}
|
||||
}
|
||||
|
||||
check(t, []string{"a", "b", "c"}, "abc", "", nil)
|
||||
check(t, []string{"a", "b", "c"}, "abc", "", true)
|
||||
check(t, []string{"a", "c", "", "", "d"}, "abcbbbd", "b", nil)
|
||||
check(t, []string{"a", "c", "", "", "d"}, "abcbbbd", "b", false)
|
||||
check(t, []string{"a", "c", "d"}, "abcbbbd", "b", true)
|
||||
check(t, []string{""}, "", "abc", nil)
|
||||
check(t, []string{}, "", "abc", true)
|
||||
|
||||
t.Run("C# compatibility", func(t *testing.T) {
|
||||
// These tests are taken from C# node.
|
||||
check(t, []string{"a", "b"}, "a,b", ",", nil)
|
||||
})
|
||||
|
||||
t.Run("big arguments", func(t *testing.T) {
|
||||
s1 := stackitem.Make(strings.Repeat("x", stdMaxInputLength+1))
|
||||
s2 := stackitem.Make("xxx")
|
||||
|
||||
require.PanicsWithError(t, ErrTooBigInput.Error(),
|
||||
func() { s.stringSplit2(ic, []stackitem.Item{s1, s2}) })
|
||||
})
|
||||
}
|
||||
|
|
|
@ -142,3 +142,17 @@ func MemorySearchLastIndex(mem, pattern []byte, start int) int {
|
|||
return contract.Call(interop.Hash160(Hash), "memorySearch", contract.NoneFlag,
|
||||
mem, pattern, start, true).(int)
|
||||
}
|
||||
|
||||
// StringSplit splits s by occurrences of sep.
|
||||
// It uses `stringSplit` method of StdLib native contract.
|
||||
func StringSplit(s, sep string) []string {
|
||||
return contract.Call(interop.Hash160(Hash), "stringSplit", contract.NoneFlag,
|
||||
s, sep).([]string)
|
||||
}
|
||||
|
||||
// StringSplitNonEmpty splits s by occurrences of sep and returns a list of non-empty items.
|
||||
// It uses `stringSplit` method of StdLib native contract.
|
||||
func StringSplitNonEmpty(s, sep string) []string {
|
||||
return contract.Call(interop.Hash160(Hash), "stringSplit", contract.NoneFlag,
|
||||
s, sep, true).([]string)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue