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 {
1616 underlying []error
1717 stackTrace * stackTrace
1818 transparent bool
19- properties map [ int64 ] interface {}
19+ properties * propertyMap
2020}
2121
2222var _ fmt.Formatter = (* Error )(nil )
@@ -27,17 +27,7 @@ var _ fmt.Formatter = (*Error)(nil)
2727// Dynamic properties is a brittle mechanism and should therefore be used with care and in a simple and robust manner.
2828func (e * Error ) WithProperty (key Property , value interface {}) * Error {
2929 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 )
4131 return & errorCopy
4232}
4333
@@ -69,7 +59,7 @@ func (e *Error) WithUnderlyingErrors(errs ...error) *Error {
6959func (e * Error ) Property (key Property ) (interface {}, bool ) {
7060 cause := e
7161 for cause != nil {
72- value , ok := cause .properties [ key . id ]
62+ value , ok := cause .properties . get ( key )
7363 if ok {
7464 return value , true
7565 }
Original file line number Diff line number Diff line change 11package errorx
22
3- import "sync/atomic"
3+ import (
4+ "sync"
5+ )
46
5- var internalID int64
7+ var internalID uint64 = 0x123456789abcdef0
8+ var idMtx sync.Mutex
69
710// nextInternalID creates next unique id for errorx entities.
811// 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
1123}
Original file line number Diff line number Diff line change @@ -11,16 +11,15 @@ import "fmt"
1111//
1212type Namespace struct {
1313 parent * Namespace
14- id int64
14+ id uint64
1515 name string
1616 traits []Trait
1717 modifiers modifiers
1818}
1919
2020// NamespaceKey is a comparable descriptor of a Namespace.
2121type NamespaceKey struct {
22- id int64
23- name string
22+ id uint64
2423}
2524
2625// 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 {
5150// Key returns a comparison key for namespace.
5251func (n Namespace ) Key () NamespaceKey {
5352 return NamespaceKey {
54- id : n .id ,
55- name : n .name ,
53+ id : n .id ,
5654 }
5755}
5856
Original file line number Diff line number Diff line change @@ -8,7 +8,11 @@ import (
88// Property value belongs to an error instance only, never inherited from a type.
99// Property visibility is hindered by Wrap, preserved by Decorate.
1010type Property struct {
11- id int64
11+ * property
12+ }
13+
14+ type property struct {
15+ id uint64
1216 label string
1317}
1418
7074)
7175
7276func 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 )
76128 }
77129}
Original file line number Diff line number Diff line change @@ -4,7 +4,7 @@ package errorx
44// All errors of a specific type possess exactly the same traits.
55// Traits are both defined along with an error and inherited from a supertype and a namespace.
66type Trait struct {
7- id int64
7+ id uint64
88 label string
99}
1010
Original file line number Diff line number Diff line change @@ -11,7 +11,7 @@ import (
1111type Type struct {
1212 namespace Namespace
1313 parent * Type
14- id int64
14+ id uint64
1515 fullName string
1616 traits map [Trait ]bool
1717 modifiers modifiers
You can’t perform that action at this time.
0 commit comments