This commit is contained in:
qiulaidongfeng 2025-06-20 15:30:34 -04:00 committed by GitHub
commit e647daab4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 117 additions and 5 deletions

View File

@ -1056,6 +1056,30 @@ func init() {
cycleMap2["cycle"] = cycleMap2 cycleMap2["cycle"] = cycleMap2
cycleMap3 = map[string]any{} cycleMap3 = map[string]any{}
cycleMap3["different"] = cycleMap3 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{ var deepEqualTests = []DeepEqualTest{
@ -1075,6 +1099,10 @@ var deepEqualTests = []DeepEqualTest{
{[]byte{1, 2, 3}, []byte{1, 2, 3}, true}, {[]byte{1, 2, 3}, []byte{1, 2, 3}, true},
{[]MyByte{1, 2, 3}, []MyByte{1, 2, 3}, true}, {[]MyByte{1, 2, 3}, []MyByte{1, 2, 3}, true},
{MyBytes{1, 2, 3}, MyBytes{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 // Inequalities
{1, 2, false}, {1, 2, false},
@ -1096,6 +1124,9 @@ var deepEqualTests = []DeepEqualTest{
{fn3, fn3, false}, {fn3, fn3, false},
{[][]int{{1}}, [][]int{{2}}, false}, {[][]int{{1}}, [][]int{{2}}, false},
{&structWithSelfPtr{p: &structWithSelfPtr{s: "a"}}, &structWithSelfPtr{p: &structWithSelfPtr{s: "b"}}, 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. // Fun with floating point.
{math.NaN(), math.NaN(), false}, {math.NaN(), math.NaN(), false},
@ -1133,6 +1164,60 @@ var deepEqualTests = []DeepEqualTest{
{&loopy1, &loopy2, true}, {&loopy1, &loopy2, true},
{&cycleMap1, &cycleMap2, true}, {&cycleMap1, &cycleMap2, true},
{&cycleMap1, &cycleMap3, false}, {&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) { 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{'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: [][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) { func TestDeepEqualAllocs(t *testing.T) {

View File

@ -7,6 +7,7 @@
package reflect package reflect
import ( import (
"internal/abi"
"internal/bytealg" "internal/bytealg"
"unsafe" "unsafe"
) )
@ -99,15 +100,23 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool {
if v1.IsNil() != v2.IsNil() { if v1.IsNil() != v2.IsNil() {
return false return false
} }
if v1.Len() != v2.Len() { len := v1.Len()
if len != v2.Len() {
return false return false
} }
if v1.UnsafePointer() == v2.UnsafePointer() { v1ptr := v1.UnsafePointer()
v2ptr := v2.UnsafePointer()
if v1ptr == v2ptr {
return true return true
} }
// Special case for []byte, which is common. // Special case raw memory. Particularly, []byte is very common and is handled here.
if v1.Type().Elem().Kind() == Uint8 { elem := v1.typ().Elem()
return bytealg.Equal(v1.Bytes(), v2.Bytes()) 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++ { for i := 0; i < v1.Len(); i++ {
if !deepValueEqual(v1.Index(i), v2.Index(i), visited) { 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. // 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. // Two values of identical type are deeply equal if one of the following cases applies.
// Values of distinct types are never deeply equal. // Values of distinct types are never deeply equal.