github.com/livekit/protocol@v1.39.3/utils/timed_version.go (about) 1 // Copyright 2023 LiveKit, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package utils 16 17 import ( 18 "database/sql/driver" 19 "encoding/binary" 20 "errors" 21 "fmt" 22 "sync" 23 "time" 24 25 "github.com/livekit/protocol/livekit" 26 ) 27 28 const tickBits uint64 = 13 29 const tickMask uint64 = (1 << tickBits) - 1 30 31 var epoch = time.Date(2000, 0, 0, 0, 0, 0, 0, time.UTC).UnixMicro() 32 33 type TimedVersionGenerator interface { 34 Next() TimedVersion 35 } 36 37 func timedVersionComponents(v TimedVersion) (ts int64, ticks int32) { 38 return int64(v>>tickBits) + epoch, int32(uint64(v) & tickMask) 39 } 40 41 func timedVersionFromComponents(ts int64, ticks int32) TimedVersion { 42 if ts < epoch { 43 ts = epoch 44 } 45 return TimedVersion((uint64(ts-epoch) << tickBits) | uint64(ticks)) 46 } 47 48 type timedVersionGenerator struct { 49 mu sync.Mutex 50 ts int64 51 ticks uint64 52 } 53 54 func NewDefaultTimedVersionGenerator() TimedVersionGenerator { 55 return new(timedVersionGenerator) 56 } 57 58 func (g *timedVersionGenerator) New() *TimedVersion { 59 v := g.Next() 60 return &v 61 } 62 63 func (g *timedVersionGenerator) Next() TimedVersion { 64 ts := time.Now().UnixMicro() 65 66 g.mu.Lock() 67 defer g.mu.Unlock() 68 69 for { 70 if ts < g.ts { 71 ts = g.ts 72 } 73 if g.ts == ts { 74 // if incrementing the ticks would overflow the version sleep for a 75 // microsecond then try again. 76 if g.ticks == tickMask { 77 time.Sleep(time.Microsecond) 78 ts = time.Now().UnixMicro() 79 continue 80 } 81 g.ticks++ 82 } else { 83 g.ts = ts 84 g.ticks = 0 85 } 86 return timedVersionFromComponents(g.ts, int32(g.ticks)) 87 } 88 } 89 90 type TimedVersion uint64 91 92 func TimedVersionFromProto(proto *livekit.TimedVersion) TimedVersion { 93 return timedVersionFromComponents(proto.GetUnixMicro(), proto.GetTicks()) 94 } 95 96 func TimedVersionFromTime(t time.Time) TimedVersion { 97 return timedVersionFromComponents(t.UnixMicro(), 0) 98 } 99 100 func (t *TimedVersion) Update(other TimedVersion) bool { 101 return t.Upgrade(other) 102 } 103 104 func (t *TimedVersion) Upgrade(other TimedVersion) bool { 105 if *t < other { 106 *t = other 107 return true 108 } 109 return false 110 } 111 112 func (t *TimedVersion) Downgrade(other TimedVersion) bool { 113 if *t > other { 114 *t = other 115 return true 116 } 117 return false 118 } 119 120 func (t *TimedVersion) Store(other TimedVersion) { 121 *t = other 122 } 123 124 func (t TimedVersion) Load() TimedVersion { 125 return t 126 } 127 128 func (t TimedVersion) Next() TimedVersion { 129 return t + 1 130 } 131 132 func (t TimedVersion) Prev() TimedVersion { 133 if t == 0 { 134 return t 135 } 136 return t - 1 137 } 138 139 func (t TimedVersion) After(other TimedVersion) bool { 140 return t > other 141 } 142 143 func (t TimedVersion) Compare(other TimedVersion) int { 144 if t < other { 145 return -1 146 } 147 if t == other { 148 return 0 149 } 150 return 1 151 } 152 153 func (t TimedVersion) IsZero() bool { 154 return t == 0 155 } 156 157 func (t TimedVersion) ToProto() *livekit.TimedVersion { 158 ts, ticks := timedVersionComponents(t) 159 return &livekit.TimedVersion{ 160 UnixMicro: ts, 161 Ticks: ticks, 162 } 163 } 164 165 func (t TimedVersion) Time() time.Time { 166 ts, _ := timedVersionComponents(t) 167 return time.UnixMicro(ts) 168 } 169 170 func (t TimedVersion) String() string { 171 ts, ticks := timedVersionComponents(t) 172 return fmt.Sprintf("%d.%d", ts, ticks) 173 } 174 175 func (t TimedVersion) Value() (driver.Value, error) { 176 if t.IsZero() { 177 return nil, nil 178 } 179 180 return t.MarshalBinary() 181 } 182 183 func (t *TimedVersion) Scan(src interface{}) (err error) { 184 switch b := src.(type) { 185 case []byte: 186 switch len(b) { 187 case 0: 188 *t = 0 189 case 12: 190 t.UnmarshalBinary(b) 191 default: 192 return errors.New("(*TimedVersion).Scan: unsupported format") 193 } 194 case nil: 195 *t = 0 196 default: 197 return errors.New("(*TimedVersion).Scan: unsupported data type") 198 } 199 return nil 200 } 201 202 func (t TimedVersion) MarshalBinary() ([]byte, error) { 203 ts, ticks := timedVersionComponents(t) 204 b := make([]byte, 0, 12) 205 b = binary.BigEndian.AppendUint64(b, uint64(ts)) 206 b = binary.BigEndian.AppendUint32(b, uint32(ticks)) 207 return b, nil 208 } 209 210 func (t *TimedVersion) UnmarshalBinary(b []byte) error { 211 ts := int64(binary.BigEndian.Uint64(b)) 212 ticks := int32(binary.BigEndian.Uint32(b[8:])) 213 *t = timedVersionFromComponents(ts, ticks) 214 return nil 215 } 216 217 func (t TimedVersion) GormDataType() string { 218 return "bytes" 219 }