mirror of https://github.com/golang/go.git
Merge ff903bb099 into 49cdf0c42e
This commit is contained in:
commit
e647daab4a
|
|
@ -1056,6 +1056,30 @@ func init() {
|
|||
cycleMap2["cycle"] = cycleMap2
|
||||
cycleMap3 = map[string]any{}
|
||||
cycleMap3["different"] = cycleMap3
|
||||
|
||||
p1.b = append(p1.b, p2, p1, p1)
|
||||
p2.b = append(p2.b, p1, p2, p1)
|
||||
}
|
||||
|
||||
type slicePtr struct {
|
||||
b []*slicePtr
|
||||
}
|
||||
|
||||
var (
|
||||
p1 = new(slicePtr)
|
||||
p2 = new(slicePtr)
|
||||
)
|
||||
|
||||
type structFloat64 struct {
|
||||
D float64
|
||||
}
|
||||
|
||||
type structFloat32 struct {
|
||||
D float32
|
||||
}
|
||||
|
||||
type structChanInt32 struct {
|
||||
D chan int32
|
||||
}
|
||||
|
||||
var deepEqualTests = []DeepEqualTest{
|
||||
|
|
@ -1075,6 +1099,10 @@ var deepEqualTests = []DeepEqualTest{
|
|||
{[]byte{1, 2, 3}, []byte{1, 2, 3}, true},
|
||||
{[]MyByte{1, 2, 3}, []MyByte{1, 2, 3}, true},
|
||||
{MyBytes{1, 2, 3}, MyBytes{1, 2, 3}, true},
|
||||
{[]int32{1, 2, 3}, []int32{1, 2, 3}, true},
|
||||
{p1, p2, true},
|
||||
{[]*int{ptr(1), ptr(2)}, []*int{ptr(1), ptr(2)}, true},
|
||||
{[]int32{1, 2, 3, 4}[:3], []int32{1, 2, 3, 5}[:3], true},
|
||||
|
||||
// Inequalities
|
||||
{1, 2, false},
|
||||
|
|
@ -1096,6 +1124,9 @@ var deepEqualTests = []DeepEqualTest{
|
|||
{fn3, fn3, false},
|
||||
{[][]int{{1}}, [][]int{{2}}, false},
|
||||
{&structWithSelfPtr{p: &structWithSelfPtr{s: "a"}}, &structWithSelfPtr{p: &structWithSelfPtr{s: "b"}}, false},
|
||||
{[]int32{1, 2, 3}, []int32{2, 1, 3}, false},
|
||||
{[]*int{ptr(1), ptr(2)}, []*int{ptr(2), ptr(1)}, false},
|
||||
{[]int32{1, 2, 3, 4}, []int32{1, 2, 3, 5}, false},
|
||||
|
||||
// Fun with floating point.
|
||||
{math.NaN(), math.NaN(), false},
|
||||
|
|
@ -1133,6 +1164,60 @@ var deepEqualTests = []DeepEqualTest{
|
|||
{&loopy1, &loopy2, true},
|
||||
{&cycleMap1, &cycleMap2, true},
|
||||
{&cycleMap1, &cycleMap3, false},
|
||||
|
||||
// struct slice and StructOf slice
|
||||
{[]structFloat64{structFloat64{D: negativeZero[float64]()}}, []structFloat64{structFloat64{D: 0}}, true},
|
||||
{
|
||||
[]any{newStructOf(VisibleFields(TypeFor[structFloat64]()), setStructOfField("D", negativeZero[float64]()))},
|
||||
[]any{newStructOf(VisibleFields(TypeFor[structFloat64]()), setStructOfField("D", float64(0)))},
|
||||
true,
|
||||
},
|
||||
|
||||
{[]structFloat32{structFloat32{D: negativeZero[float32]()}}, []structFloat32{structFloat32{D: 0}}, true},
|
||||
{
|
||||
[]any{newStructOf(VisibleFields(TypeFor[structFloat32]()), setStructOfField("D", negativeZero[float32]()))},
|
||||
[]any{newStructOf(VisibleFields(TypeFor[structFloat32]()), setStructOfField("D", float32(0)))},
|
||||
true,
|
||||
},
|
||||
|
||||
{[]structChanInt32{structChanInt32{D: make(chan int32)}}, []structChanInt32{structChanInt32{D: make(chan int32)}}, false},
|
||||
{
|
||||
[]any{newStructOf(VisibleFields(TypeFor[structChanInt32]()), setStructOfField("D", make(chan int32)))},
|
||||
[]any{newStructOf(VisibleFields(TypeFor[structChanInt32]()), setStructOfField("D", make(chan int32)))},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
func ptr[T any](a T) *T {
|
||||
return &a
|
||||
}
|
||||
|
||||
type fieldSet func(Value)
|
||||
|
||||
// newStructOf call StructOf get typ
|
||||
// use typ new any value
|
||||
// use set change new any value
|
||||
func newStructOf(field []StructField, set ...fieldSet) (ret any) {
|
||||
typ := StructOf(field)
|
||||
rret := New(typ).Elem()
|
||||
for _, f := range set {
|
||||
f(rret)
|
||||
}
|
||||
return rret.Interface()
|
||||
}
|
||||
|
||||
// setStructOfField set a field of the struct represented by reflect.Value
|
||||
func setStructOfField(fieldName string, fieldValue any) func(Value) {
|
||||
return func(v Value) {
|
||||
field := v.FieldByName(fieldName)
|
||||
field.Set(ValueOf(fieldValue))
|
||||
}
|
||||
}
|
||||
|
||||
func negativeZero[T float32 | float64]() T {
|
||||
var f T
|
||||
f = -f
|
||||
return f
|
||||
}
|
||||
|
||||
func TestDeepEqual(t *testing.T) {
|
||||
|
|
@ -1274,6 +1359,8 @@ var deepEqualPerfTests = []struct {
|
|||
|
||||
{x: [6]byte{'a', 'b', 'c', 'a', 'b', 'c'}, y: [6]byte{'a', 'b', 'c', 'a', 'b', 'c'}},
|
||||
{x: [][6]byte{[6]byte{'a', 'b', 'c', 'a', 'b', 'c'}}, y: [][6]byte{[6]byte{'a', 'b', 'c', 'a', 'b', 'c'}}},
|
||||
|
||||
{x: make([]int16, 10240), y: make([]int16, 10240)},
|
||||
}
|
||||
|
||||
func TestDeepEqualAllocs(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
package reflect
|
||||
|
||||
import (
|
||||
"internal/abi"
|
||||
"internal/bytealg"
|
||||
"unsafe"
|
||||
)
|
||||
|
|
@ -99,15 +100,23 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool {
|
|||
if v1.IsNil() != v2.IsNil() {
|
||||
return false
|
||||
}
|
||||
if v1.Len() != v2.Len() {
|
||||
len := v1.Len()
|
||||
if len != v2.Len() {
|
||||
return false
|
||||
}
|
||||
if v1.UnsafePointer() == v2.UnsafePointer() {
|
||||
v1ptr := v1.UnsafePointer()
|
||||
v2ptr := v2.UnsafePointer()
|
||||
if v1ptr == v2ptr {
|
||||
return true
|
||||
}
|
||||
// Special case for []byte, which is common.
|
||||
if v1.Type().Elem().Kind() == Uint8 {
|
||||
return bytealg.Equal(v1.Bytes(), v2.Bytes())
|
||||
// Special case raw memory. Particularly, []byte is very common and is handled here.
|
||||
elem := v1.typ().Elem()
|
||||
if isDeepEqualRawMemory(elem) {
|
||||
size := elem.Size_ * uintptr(len)
|
||||
return bytealg.Equal(
|
||||
unsafe.Slice((*byte)(v1ptr), size),
|
||||
unsafe.Slice((*byte)(v2ptr), size),
|
||||
)
|
||||
}
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {
|
||||
|
|
@ -175,6 +184,22 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool {
|
|||
}
|
||||
}
|
||||
|
||||
// isDeepEqualRawMemory reports whether DeepEqual can compare
|
||||
// two instances of a type by just comparing their raw memory contents.
|
||||
func isDeepEqualRawMemory(typ *abi.Type) (ok bool) {
|
||||
// Note: Here is an incorrect implementation :
|
||||
//
|
||||
// return typ.TFlag&abi.TFlagRegularMemory != 0
|
||||
//
|
||||
// The reason is DeepEqual can't determine whether two pointer values are equal by comparing them byte by byte.
|
||||
// Doing so would report unequal when it shouldn't,
|
||||
// in the case where two pointers are different but point to the same contents
|
||||
// (e.g. two *int32s that point to different int32s,
|
||||
// both of which contain the same value).
|
||||
|
||||
return typ.PtrBytes == 0 && typ.TFlag&abi.TFlagRegularMemory != 0
|
||||
}
|
||||
|
||||
// DeepEqual reports whether x and y are “deeply equal,” defined as follows.
|
||||
// Two values of identical type are deeply equal if one of the following cases applies.
|
||||
// Values of distinct types are never deeply equal.
|
||||
|
|
|
|||
Loading…
Reference in New Issue