From 4d8aefde470de630a1f6f6fc2c481fedb4a293c8 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 1 Jul 2013 20:32:53 -0400 Subject: [PATCH] reflect: add Value.Slice3 and Value.SetCap methods, to match x[i:j:k] Design doc at golang.org/s/go12slice. R=golang-dev, r, nightlyone CC=golang-dev https://golang.org/cl/10761045 --- src/pkg/reflect/all_test.go | 68 +++++++++++++++++++++++++++++ src/pkg/reflect/value.go | 85 +++++++++++++++++++++++++++++++------ 2 files changed, 141 insertions(+), 12 deletions(-) diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go index a61f663084..93df4d1365 100644 --- a/src/pkg/reflect/all_test.go +++ b/src/pkg/reflect/all_test.go @@ -2368,6 +2368,74 @@ func TestSlice(t *testing.T) { } } +func TestSlice3(t *testing.T) { + xs := []int{1, 2, 3, 4, 5, 6, 7, 8} + v := ValueOf(xs).Slice3(3, 5, 7).Interface().([]int) + if len(v) != 2 { + t.Errorf("len(xs.Slice3(3, 5, 7)) = %d", len(v)) + } + if cap(v) != 4 { + t.Errorf("cap(xs.Slice3(3, 5, 7)) = %d", cap(v)) + } + if !DeepEqual(v[0:4], xs[3:7:7]) { + t.Errorf("xs.Slice3(3, 5, 7)[0:4] = %v", v[0:4]) + } + rv := ValueOf(&xs).Elem() + shouldPanic(func() { rv.Slice3(1, 2, 1) }) + shouldPanic(func() { rv.Slice3(1, 1, 11) }) + shouldPanic(func() { rv.Slice3(2, 2, 1) }) + + xa := [8]int{10, 20, 30, 40, 50, 60, 70, 80} + v = ValueOf(&xa).Elem().Slice3(2, 5, 6).Interface().([]int) + if len(v) != 3 { + t.Errorf("len(xa.Slice(2, 5, 6)) = %d", len(v)) + } + if cap(v) != 4 { + t.Errorf("cap(xa.Slice(2, 5, 6)) = %d", cap(v)) + } + if !DeepEqual(v[0:4], xa[2:6:6]) { + t.Errorf("xs.Slice(2, 5, 6)[0:4] = %v", v[0:4]) + } + rv = ValueOf(&xa).Elem() + shouldPanic(func() { rv.Slice3(1, 2, 1) }) + shouldPanic(func() { rv.Slice3(1, 1, 11) }) + shouldPanic(func() { rv.Slice3(2, 2, 1) }) + + s := "hello world" + rv = ValueOf(&s).Elem() + shouldPanic(func() { rv.Slice3(1, 2, 3) }) +} + +func TestSetLenCap(t *testing.T) { + xs := []int{1, 2, 3, 4, 5, 6, 7, 8} + xa := [8]int{10, 20, 30, 40, 50, 60, 70, 80} + + vs := ValueOf(&xs).Elem() + shouldPanic(func() { vs.SetLen(10) }) + shouldPanic(func() { vs.SetCap(10) }) + shouldPanic(func() { vs.SetLen(-1) }) + shouldPanic(func() { vs.SetCap(-1) }) + shouldPanic(func() { vs.SetCap(6) }) // smaller than len + vs.SetLen(5) + if len(xs) != 5 || cap(xs) != 8 { + t.Errorf("after SetLen(5), len, cap = %d, %d, want 5, 8", len(xs), cap(xs)) + } + vs.SetCap(6) + if len(xs) != 5 || cap(xs) != 6 { + t.Errorf("after SetCap(6), len, cap = %d, %d, want 5, 6", len(xs), cap(xs)) + } + vs.SetCap(5) + if len(xs) != 5 || cap(xs) != 5 { + t.Errorf("after SetCap(5), len, cap = %d, %d, want 5, 5", len(xs), cap(xs)) + } + shouldPanic(func() { vs.SetCap(4) }) // smaller than len + shouldPanic(func() { vs.SetLen(6) }) // bigger than cap + + va := ValueOf(&xa).Elem() + shouldPanic(func() { va.SetLen(8) }) + shouldPanic(func() { va.SetCap(8) }) +} + func TestVariadic(t *testing.T) { var b bytes.Buffer V := ValueOf diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go index 80aa85723c..9b2630290a 100644 --- a/src/pkg/reflect/value.go +++ b/src/pkg/reflect/value.go @@ -1475,6 +1475,19 @@ func (v Value) SetLen(n int) { s.Len = n } +// SetCap sets v's capacity to n. +// It panics if v's Kind is not Slice or if n is smaller than the length or +// greater than the capacity of the slice. +func (v Value) SetCap(n int) { + v.mustBeAssignable() + v.mustBe(Slice) + s := (*SliceHeader)(v.val) + if n < int(s.Len) || n > int(s.Cap) { + panic("reflect: slice capacity out of range in SetCap") + } + s.Cap = n +} + // SetMapIndex sets the value associated with key in the map v to val. // It panics if v's Kind is not Map. // If val is the zero Value, SetMapIndex deletes the key from the map. @@ -1531,17 +1544,18 @@ func (v Value) SetString(x string) { *(*string)(v.val) = x } -// Slice returns a slice of v. -// It panics if v's Kind is not Array, Slice or String, or if v is an unaddressable array. -func (v Value) Slice(beg, end int) Value { +// Slice returns v[i:j]. +// It panics if v's Kind is not Array, Slice or String, or if v is an unaddressable array, +// or if the indexes are out of bounds. +func (v Value) Slice(i, j int) Value { var ( cap int typ *sliceType base unsafe.Pointer ) - switch k := v.kind(); k { + switch kind := v.kind(); kind { default: - panic(&ValueError{"reflect.Value.Slice", k}) + panic(&ValueError{"reflect.Value.Slice", kind}) case Array: if v.flag&flagAddr == 0 { @@ -1560,17 +1574,17 @@ func (v Value) Slice(beg, end int) Value { case String: s := (*StringHeader)(v.val) - if beg < 0 || end < beg || end > s.Len { + if i < 0 || j < i || j > s.Len { panic("reflect.Value.Slice: string slice index out of bounds") } var x string val := (*StringHeader)(unsafe.Pointer(&x)) - val.Data = s.Data + uintptr(beg) - val.Len = end - beg + val.Data = s.Data + uintptr(i) + val.Len = j - i return Value{v.typ, unsafe.Pointer(&x), v.flag} } - if beg < 0 || end < beg || end > cap { + if i < 0 || j < i || j > cap { panic("reflect.Value.Slice: slice index out of bounds") } @@ -1579,9 +1593,56 @@ func (v Value) Slice(beg, end int) Value { // Reinterpret as *SliceHeader to edit. s := (*SliceHeader)(unsafe.Pointer(&x)) - s.Data = uintptr(base) + uintptr(beg)*typ.elem.Size() - s.Len = end - beg - s.Cap = cap - beg + s.Data = uintptr(base) + uintptr(i)*typ.elem.Size() + s.Len = j - i + s.Cap = cap - i + + fl := v.flag&flagRO | flagIndir | flag(Slice)< cap { + panic("reflect.Value.Slice3: slice index out of bounds") + } + + // Declare slice so that the garbage collector + // can see the base pointer in it. + var x []unsafe.Pointer + + // Reinterpret as *SliceHeader to edit. + s := (*SliceHeader)(unsafe.Pointer(&x)) + s.Data = uintptr(base) + uintptr(i)*typ.elem.Size() + s.Len = j - i + s.Cap = k - i fl := v.flag&flagRO | flagIndir | flag(Slice)<