-
-
Notifications
You must be signed in to change notification settings - Fork 127
/
actor_id.go
154 lines (128 loc) · 4.18 KB
/
actor_id.go
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
* Copyright 2020 The Yorkie Authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package time
import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"math"
)
const actorIDSize = 12
var (
// InitialActorID represents the initial value of ActorID.
InitialActorID = &ActorID{}
// MaxActorID represents the maximum value of ActorID.
MaxActorID = &ActorID{
bytes: [actorIDSize]byte{
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
},
}
// ErrInvalidHexString is returned when the given string is not valid hex.
ErrInvalidHexString = errors.New("invalid hex string")
// ErrInvalidActorID is returned when the given ID is not valid.
ErrInvalidActorID = errors.New("invalid actor id")
)
// ActorID represents the unique ID of the client. It is composed of 12 bytes.
// It caches the string representation of ActorID to reduce the number of calls
// to hex.EncodeToString. This causes a multi-routine problem, so it is
// recommended to use it in a single routine or to use it after locking.
type ActorID struct {
bytes [actorIDSize]byte
cachedString string
}
// ActorIDFromHex returns the bytes represented by the hexadecimal string str.
func ActorIDFromHex(str string) (*ActorID, error) {
actorID := &ActorID{}
if str == "" {
return actorID, fmt.Errorf("%s: %w", str, ErrInvalidHexString)
}
decoded, err := hex.DecodeString(str)
if err != nil {
return actorID, fmt.Errorf("%s: %w", str, ErrInvalidHexString)
}
if len(decoded) != actorIDSize {
return actorID, fmt.Errorf("decoded length %d: %w", len(decoded), ErrInvalidHexString)
}
copy(actorID.bytes[:], decoded[:actorIDSize])
return actorID, nil
}
// ActorIDFromBytes returns the bytes represented by the bytes of decoded hexadecimal string itself.
func ActorIDFromBytes(bytes []byte) (*ActorID, error) {
actorID := &ActorID{}
if len(bytes) == 0 {
return actorID, fmt.Errorf("bytes length %d: %w", len(bytes), ErrInvalidActorID)
}
if len(bytes) != actorIDSize {
return actorID, fmt.Errorf("bytes length %d: %w", len(bytes), ErrInvalidActorID)
}
copy(actorID.bytes[:], bytes)
return actorID, nil
}
// String returns the hexadecimal encoding of ActorID.
// If the receiver is nil, it would return empty string.
func (id *ActorID) String() string {
if id.cachedString == "" {
id.cachedString = hex.EncodeToString(id.bytes[:])
}
return id.cachedString
}
// Bytes returns the bytes of ActorID itself.
// If the receiver is nil, it would return empty array of byte.
func (id *ActorID) Bytes() []byte {
return id.bytes[:]
}
// Compare returns an integer comparing two ActorID lexicographically.
// The result will be 0 if id==other, -1 if id < other, and +1 if id > other.
// If the receiver or argument is nil, it would panic at runtime.
func (id *ActorID) Compare(other *ActorID) int {
return bytes.Compare(id.bytes[:], other.bytes[:])
}
// MarshalJSON ensures that when calling json.Marshal(),
// it is marshaled including private field.
func (id *ActorID) MarshalJSON() ([]byte, error) {
result, err := json.Marshal(&struct{ Bytes [actorIDSize]byte }{
Bytes: id.bytes,
})
if err != nil {
return nil, fmt.Errorf("marshal JSON: %w", err)
}
return result, nil
}
// UnmarshalJSON ensures that when calling json.Unmarshal(),
// it is unmarshalled including private field.
func (id *ActorID) UnmarshalJSON(bytes []byte) error {
temp := &(struct{ Bytes [actorIDSize]byte }{
Bytes: id.bytes,
})
if err := json.Unmarshal(bytes, temp); err != nil {
return fmt.Errorf("unmarshal JSON: %w", err)
}
id.bytes = temp.Bytes
return nil
}