File tree Expand file tree Collapse file tree 6 files changed +80
-28
lines changed Expand file tree Collapse file tree 6 files changed +80
-28
lines changed Original file line number Diff line number Diff line change @@ -16,7 +16,7 @@ type Error struct {
16
16
underlying []error
17
17
stackTrace * stackTrace
18
18
transparent bool
19
- properties map [ int64 ] interface {}
19
+ properties * propertyMap
20
20
}
21
21
22
22
var _ fmt.Formatter = (* Error )(nil )
@@ -27,17 +27,7 @@ var _ fmt.Formatter = (*Error)(nil)
27
27
// Dynamic properties is a brittle mechanism and should therefore be used with care and in a simple and robust manner.
28
28
func (e * Error ) WithProperty (key Property , value interface {}) * Error {
29
29
errorCopy := * e
30
-
31
- if errorCopy .properties == nil {
32
- errorCopy .properties = make (map [int64 ]interface {}, 1 )
33
- } else {
34
- errorCopy .properties = make (map [int64 ]interface {}, len (e .properties )+ 1 )
35
- for k , v := range e .properties {
36
- errorCopy .properties [k ] = v
37
- }
38
- }
39
-
40
- errorCopy .properties [key .id ] = value
30
+ errorCopy .properties = errorCopy .properties .with (key , value )
41
31
return & errorCopy
42
32
}
43
33
@@ -69,7 +59,7 @@ func (e *Error) WithUnderlyingErrors(errs ...error) *Error {
69
59
func (e * Error ) Property (key Property ) (interface {}, bool ) {
70
60
cause := e
71
61
for cause != nil {
72
- value , ok := cause .properties [ key . id ]
62
+ value , ok := cause .properties . get ( key )
73
63
if ok {
74
64
return value , true
75
65
}
Original file line number Diff line number Diff line change 1
1
package errorx
2
2
3
- import "sync/atomic"
3
+ import (
4
+ "sync"
5
+ )
4
6
5
- var internalID int64
7
+ var internalID uint64 = 0x123456789abcdef0
8
+ var idMtx sync.Mutex
6
9
7
10
// nextInternalID creates next unique id for errorx entities.
8
11
// All equality comparison should take id into account, lest there be some false positive matches.
9
- func nextInternalID () int64 {
10
- return atomic .AddInt64 (& internalID , 1 )
12
+ func nextInternalID () uint64 {
13
+ idMtx .Lock ()
14
+ // xorshift
15
+ x := internalID
16
+ x ^= x << 13
17
+ x ^= x >> 7
18
+ x ^= x << 17
19
+ id := x + internalID
20
+ internalID = x
21
+ idMtx .Unlock ()
22
+ return id
11
23
}
Original file line number Diff line number Diff line change @@ -11,16 +11,15 @@ import "fmt"
11
11
//
12
12
type Namespace struct {
13
13
parent * Namespace
14
- id int64
14
+ id uint64
15
15
name string
16
16
traits []Trait
17
17
modifiers modifiers
18
18
}
19
19
20
20
// NamespaceKey is a comparable descriptor of a Namespace.
21
21
type NamespaceKey struct {
22
- id int64
23
- name string
22
+ id uint64
24
23
}
25
24
26
25
// NewNamespace defines a namespace with a name and, optionally, a number of inheritable traits.
@@ -51,8 +50,7 @@ func (n Namespace) NewType(typeName string, traits ...Trait) *Type {
51
50
// Key returns a comparison key for namespace.
52
51
func (n Namespace ) Key () NamespaceKey {
53
52
return NamespaceKey {
54
- id : n .id ,
55
- name : n .name ,
53
+ id : n .id ,
56
54
}
57
55
}
58
56
Original file line number Diff line number Diff line change @@ -8,7 +8,11 @@ import (
8
8
// Property value belongs to an error instance only, never inherited from a type.
9
9
// Property visibility is hindered by Wrap, preserved by Decorate.
10
10
type Property struct {
11
- id int64
11
+ * property
12
+ }
13
+
14
+ type property struct {
15
+ id uint64
12
16
label string
13
17
}
14
18
70
74
)
71
75
72
76
func newProperty (label string ) Property {
73
- return Property {
74
- id : nextInternalID (),
75
- label : label ,
77
+ p := Property {
78
+ & property {
79
+ id : nextInternalID (),
80
+ label : label ,
81
+ },
82
+ }
83
+ return p
84
+ }
85
+
86
+ // propertyMap represents map of properties.
87
+ // Compared to builtin type, it uses less allocations and reallocations on copy.
88
+ // It is simple binary search tree that relies on property id randomness for balancing.
89
+ type propertyMap struct {
90
+ p Property
91
+ value interface {}
92
+ left * propertyMap
93
+ right * propertyMap
94
+ }
95
+
96
+ // withProperty creates new map with property added or overwritten
97
+ func (pm * propertyMap ) with (p Property , value interface {}) * propertyMap {
98
+ if pm == nil {
99
+ return & propertyMap {p : p , value : value }
100
+ } else if pm .p == p {
101
+ return & propertyMap {
102
+ p : p ,
103
+ value : value ,
104
+ left : pm .left ,
105
+ right : pm .right ,
106
+ }
107
+ } else {
108
+ copy := * pm
109
+ if copy .p .id < p .id {
110
+ copy .right = copy .right .with (p , value )
111
+ } else {
112
+ copy .left = copy .left .with (p , value )
113
+ }
114
+ return & copy
115
+ }
116
+ }
117
+
118
+ func (pm * propertyMap ) get (p Property ) (value interface {}, ok bool ) {
119
+ switch {
120
+ case pm == nil :
121
+ return nil , false
122
+ case pm .p == p :
123
+ return pm .value , true
124
+ case pm .p .id < p .id :
125
+ return pm .right .get (p )
126
+ default :
127
+ return pm .left .get (p )
76
128
}
77
129
}
Original file line number Diff line number Diff line change @@ -4,7 +4,7 @@ package errorx
4
4
// All errors of a specific type possess exactly the same traits.
5
5
// Traits are both defined along with an error and inherited from a supertype and a namespace.
6
6
type Trait struct {
7
- id int64
7
+ id uint64
8
8
label string
9
9
}
10
10
Original file line number Diff line number Diff line change @@ -11,7 +11,7 @@ import (
11
11
type Type struct {
12
12
namespace Namespace
13
13
parent * Type
14
- id int64
14
+ id uint64
15
15
fullName string
16
16
traits map [Trait ]bool
17
17
modifiers modifiers
You can’t perform that action at this time.
0 commit comments