From 72a1afdcb21465fa2a68d76df0d50ff4d0e8bf4b Mon Sep 17 00:00:00 2001 From: Thomas Pelletier Date: Mon, 29 Mar 2021 22:33:28 -0400 Subject: [PATCH] Add some unsafe helper to track errors --- internal/errors/unsafe.go | 37 ++++++++++++++++ internal/errors/unsafe_test.go | 79 ++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 internal/errors/unsafe.go create mode 100644 internal/errors/unsafe_test.go diff --git a/internal/errors/unsafe.go b/internal/errors/unsafe.go new file mode 100644 index 0000000..626c01b --- /dev/null +++ b/internal/errors/unsafe.go @@ -0,0 +1,37 @@ +package errors + +import ( + "fmt" + "reflect" + "unsafe" +) + +const maxInt = uintptr(int(^uint(0) >> 1)) + + +func UnsafeSubsliceOffset(data []byte, subslice []byte) int { + datap := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + hlp := (*reflect.SliceHeader)(unsafe.Pointer(&subslice)) + + + if hlp.Data < datap.Data { + panic(fmt.Errorf("subslice address (%d) is before data address (%d)", hlp.Data, datap.Data)) + } + offset := hlp.Data - datap.Data + + if offset > maxInt { + panic(fmt.Errorf("slice offset larger than int (%d)", offset)) + } + + intoffset := int(offset) + + if intoffset >= datap.Len { + panic(fmt.Errorf("slice offset (%d) is farther than data length (%d)", intoffset, datap.Len)) + } + + if intoffset + hlp.Len > datap.Len { + panic(fmt.Errorf("slice ends (%d+%d) is farther than data length (%d)", intoffset, hlp.Len, datap.Len)) + } + + return intoffset +} diff --git a/internal/errors/unsafe_test.go b/internal/errors/unsafe_test.go new file mode 100644 index 0000000..5c3b50b --- /dev/null +++ b/internal/errors/unsafe_test.go @@ -0,0 +1,79 @@ +package errors_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/pelletier/go-toml/v2/internal/errors" +) + +func TestUnsafeSubsliceOffsetValid(t *testing.T) { + examples := []struct{ + desc string + test func() ([]byte, []byte) + offset int + }{ + { + desc: "simple", + test: func() ([]byte, []byte) { + data := []byte("hello") + return data, data[1:] + }, + offset: 1, + }, + } + + for _, e := range examples { + t.Run(e.desc, func(t *testing.T) { + d, s := e.test() + offset := errors.UnsafeSubsliceOffset(d, s) + assert.Equal(t, e.offset, offset) + }) + } +} + +func TestUnsafeSubsliceOffsetInvalid(t *testing.T) { + examples := []struct{ + desc string + test func() ([]byte, []byte) + }{ + { + desc: "unrelated arrays", + test: func() ([]byte, []byte) { + return []byte("one"), []byte("two") + }, + }, + { + desc: "slice starts before data", + test: func() ([]byte, []byte) { + full := []byte("hello world") + return full[5:], full[1:] + }, + }, + { + desc: "slice starts after data", + test: func() ([]byte, []byte) { + full := []byte("hello world") + return full[:3], full[5:] + }, + }, + { + desc: "slice ends after data", + test: func() ([]byte, []byte) { + full := []byte("hello world") + return full[:5], full[3:8] + }, + }, + } + + for _, e := range examples { + t.Run(e.desc, func(t *testing.T) { + d, s := e.test() + require.Panics(t, func() { + errors.UnsafeSubsliceOffset(d, s) + }) + }) + } +}