add slicing/indexing for bytes kind

This commit is contained in:
Fabio Bozzo
2024-10-24 12:27:01 +02:00
parent a8780f750c
commit 00ff88ef23
3 changed files with 72 additions and 56 deletions

View File

@@ -413,42 +413,6 @@ func TestParse(t *testing.T) {
fmt.Println(err) fmt.Println(err)
}) })
t.Run("index out of range", func(t *testing.T) {
sel, err := Parse(".[1000]")
require.NoError(t, err)
require.Equal(t, 2, len(sel))
require.True(t, sel[0].Identity())
require.False(t, sel[0].Optional())
require.False(t, sel[0].Iterator())
require.Empty(t, sel[0].Slice())
require.Empty(t, sel[0].Field())
require.Empty(t, sel[0].Index())
require.False(t, sel[1].Identity())
require.False(t, sel[1].Optional())
require.False(t, sel[1].Iterator())
require.Empty(t, sel[1].Slice())
require.Empty(t, sel[1].Field())
require.Equal(t, sel[1].Index(), 1000)
})
t.Run("negative index out of range", func(t *testing.T) {
sel, err := Parse(".[-1000]")
require.NoError(t, err)
require.Equal(t, 2, len(sel))
require.True(t, sel[0].Identity())
require.False(t, sel[0].Optional())
require.False(t, sel[0].Iterator())
require.Empty(t, sel[0].Slice())
require.Empty(t, sel[0].Field())
require.Empty(t, sel[0].Index())
require.False(t, sel[1].Identity())
require.False(t, sel[1].Optional())
require.False(t, sel[1].Iterator())
require.Empty(t, sel[1].Slice())
require.Empty(t, sel[1].Field())
require.Equal(t, sel[1].Index(), -1000)
})
t.Run("slice with negative start and positive end", func(t *testing.T) { t.Run("slice with negative start and positive end", func(t *testing.T) {
sel, err := Parse(".[0:-2]") sel, err := Parse(".[0:-2]")
require.NoError(t, err) require.NoError(t, err)

View File

@@ -3,7 +3,6 @@ package selector
import ( import (
"errors" "errors"
"fmt" "fmt"
"math"
"strconv" "strconv"
"strings" "strings"
@@ -229,7 +228,7 @@ func resolve(sel Selector, subject ipld.Node, at []string) (ipld.Node, error) {
idx = len(b) + idx idx = len(b) + idx
} }
if idx < 0 || idx >= len(b) { if idx < 0 || idx >= len(b) {
err := newResolutionError(fmt.Sprintf("index out of bounds: %d", seg.Index()), at) err := newResolutionError(fmt.Sprintf("index %d out of bounds for bytes of length %d", seg.Index(), len(b)), at)
return nil, errIfNotOptional(seg, err) return nil, errIfNotOptional(seg, err)
} }
cur = basicnode.NewInt(int64(b[idx])) cur = basicnode.NewInt(int64(b[idx]))
@@ -263,32 +262,33 @@ func resolveSliceIndices(slice []int64, length int64) (start int64, end int64) {
start, end = slice[0], slice[1] start, end = slice[0], slice[1]
// adjust boundaries // clamp start index
switch { if start < 0 {
case slice[0] == math.MinInt: start = length + start
start = 0 if start < 0 {
case slice[0] < 0: start = 0
start = length + slice[0] }
} }
switch { if start > length {
case slice[1] == math.MaxInt: start = length
end = length
case slice[1] < 0:
end = length + slice[1]
} }
// backward iteration is not allowed, shortcut to an empty result // clamp end index
if start >= end { if end < 0 {
start, end = 0, 0 end = length + end
} if end < 0 {
// clamp out of bound end = 0
if start < 0 { }
start = 0
} }
if end > length { if end > length {
end = length end = length
} }
// handle backward slicing
if start > end {
start, end = 0, 0
}
return start, end return start, end
} }

View File

@@ -246,6 +246,58 @@ func TestSelect(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, int(must.Int(val))) // Assert sliced value at index 1 require.Equal(t, 2, int(must.Int(val))) // Assert sliced value at index 1
}) })
t.Run("slice on bytes", func(t *testing.T) {
sel, err := Parse(`.[1:3]`)
require.NoError(t, err)
node := basicnode.NewBytes([]byte{0x01, 0x02, 0x03, 0x04, 0x05})
res, err := sel.Select(node)
require.NoError(t, err)
require.NotEmpty(t, res)
bytes, err := res.AsBytes()
require.NoError(t, err)
require.Equal(t, []byte{0x02, 0x03}, bytes) // assert sliced bytes
})
t.Run("index on bytes", func(t *testing.T) {
sel, err := Parse(`.[2]`)
require.NoError(t, err)
node := basicnode.NewBytes([]byte{0x01, 0x02, 0x03, 0x04, 0x05})
res, err := sel.Select(node)
require.NoError(t, err)
require.NotEmpty(t, res)
val, err := res.AsInt()
require.NoError(t, err)
require.Equal(t, int64(0x03), val) // assert indexed byte value
})
t.Run("out of bounds slicing on bytes", func(t *testing.T) {
sel, err := Parse(`.[10:20]`)
require.NoError(t, err)
node := basicnode.NewBytes([]byte{0x01, 0x02, 0x03})
res, err := sel.Select(node)
require.NoError(t, err)
require.NotNil(t, res)
bytes, err := res.AsBytes()
require.NoError(t, err)
require.Empty(t, bytes) // assert empty result for out of bounds slice
})
t.Run("out of bounds indexing on bytes", func(t *testing.T) {
sel, err := Parse(`.[10]`)
require.NoError(t, err)
node := basicnode.NewBytes([]byte{0x01, 0x02, 0x03})
_, err = sel.Select(node)
require.Error(t, err)
require.Contains(t, err.Error(), "can not resolve path: .10") // assert error for out of bounds index
})
} }
func FuzzParse(f *testing.F) { func FuzzParse(f *testing.F) {