File: visited.go

package info (click to toggle)
golang-github-maxatome-go-testdeep 1.14.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,416 kB
  • sloc: perl: 1,012; yacc: 130; makefile: 2
file content (80 lines) | stat: -rw-r--r-- 2,010 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// Copyright (c) 2019, Maxime Soulé
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree.

package visited

import (
	"reflect"
)

// visitKey is used by ctxerr.Context and its Visited map to handle
// cyclic references.
type visitedKey struct {
	a1  uintptr
	a2  uintptr
	typ reflect.Type
}

// Visited allows to remember couples of same type pointers, typically
// to not do the same action twice if the couple has already been seen.
type Visited map[visitedKey]bool

// NewVisited returns a new [Visited] instance.
func NewVisited() Visited {
	return Visited{}
}

// Record checks and, if needed, records a new entry for (got,
// expected) couple. It returns true if got & expected are pointers
// and have already been seen together. It returns false otherwise.
// It is the caller responsibility to check that got and expected
// types are the same.
func (v Visited) Record(got, expected reflect.Value) bool {
	var addr1, addr2 uintptr
	switch got.Kind() {
	// Pointer() can not be used for interfaces and for slices the
	// returned address is the array behind the slice, use UnsafeAddr()
	// instead
	case reflect.Slice, reflect.Interface:
		if got.IsNil() || expected.IsNil() ||
			!got.CanAddr() || !expected.CanAddr() {
			return false
		}
		addr1 = got.UnsafeAddr()
		addr2 = expected.UnsafeAddr()

		// For maps and pointers use Pointer() to automatically handle
		// indirect pointers
	case reflect.Map, reflect.Ptr:
		if got.IsNil() || expected.IsNil() {
			return false
		}
		addr1 = got.Pointer()
		addr2 = expected.Pointer()

	default:
		return false
	}

	if addr1 > addr2 {
		// Canonicalize order to reduce number of entries in v.
		// Assumes non-moving garbage collector.
		addr1, addr2 = addr2, addr1
	}

	k := visitedKey{
		a1:  addr1,
		a2:  addr2,
		typ: got.Type(),
	}
	if v[k] {
		return true // references already seen
	}

	// Remember for later.
	v[k] = true
	return false
}