github.com/livekit/protocol@v1.39.3/utils/guid/id.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 guid 16 17 import ( 18 "crypto/rand" 19 "crypto/sha1" 20 "fmt" 21 mrand "math/rand/v2" 22 "os" 23 "sync" 24 "unsafe" 25 26 "github.com/jxskiss/base62" 27 "github.com/lithammer/shortuuid/v4" 28 29 "github.com/livekit/protocol/livekit" 30 "github.com/livekit/protocol/utils/must" 31 ) 32 33 const Size = 12 34 35 const ( 36 RoomPrefix = "RM_" 37 NodePrefix = "ND_" 38 ParticipantPrefix = "PA_" 39 TrackPrefix = "TR_" 40 APIKeyPrefix = "API" 41 EgressPrefix = "EG_" 42 IngressPrefix = "IN_" 43 SIPTrunkPrefix = "ST_" 44 SIPDispatchRulePrefix = "SDR_" 45 SIPCallPrefix = "SCL_" 46 SIPTransferPrefix = "STR_" 47 RPCPrefix = "RPC_" 48 WHIPResourcePrefix = "WH_" 49 RTMPResourcePrefix = "RT_" 50 URLResourcePrefix = "UR_" 51 AgentWorkerPrefix = "AW_" 52 AgentJobPrefix = "AJ_" 53 AgentDispatchPrefix = "AD_" 54 CloudAgentPrefix = "CA_" 55 CloudAgentRegionPrefix = "CAR_" 56 CloudAgentVersionPrefix = "CAV_" 57 CloudAgentSecretPrefix = "CAS_" 58 CloudAgentWorkerPrefix = "CAW_" 59 ) 60 61 var guidGeneratorPool = sync.Pool{ 62 New: func() any { 63 return must.Get(newGenerator()) 64 }, 65 } 66 67 func New(prefix string) string { 68 g := guidGeneratorPool.Get().(*guidGenerator) 69 defer guidGeneratorPool.Put(g) 70 return g.New(prefix) 71 } 72 73 // HashedID creates a hashed ID from a unique string 74 func HashedID(id string) string { 75 h := sha1.New() 76 h.Write([]byte(id)) 77 val := h.Sum(nil) 78 79 return base62.EncodeToString(val) 80 } 81 82 func LocalNodeID() (string, error) { 83 hostname, err := os.Hostname() 84 if err != nil { 85 return "", err 86 } 87 return fmt.Sprintf("%s%s", NodePrefix, HashedID(hostname)[:8]), nil 88 } 89 90 var b57Index = newB57Index() 91 var b57Chars = []byte(shortuuid.DefaultAlphabet) 92 93 func newB57Index() [256]byte { 94 var index [256]byte 95 for i := 0; i < len(b57Chars); i++ { 96 index[b57Chars[i]] = byte(i) 97 } 98 return index 99 } 100 101 type guidGenerator struct { 102 rng *mrand.ChaCha8 103 } 104 105 func newGenerator() (*guidGenerator, error) { 106 var seed [32]byte 107 if _, err := rand.Read(seed[:]); err != nil { 108 return nil, err 109 } 110 111 return &guidGenerator{ 112 rng: mrand.NewChaCha8(seed), 113 }, nil 114 } 115 116 func (g *guidGenerator) readIDChars(b []byte) { 117 var n int 118 for { 119 r := g.rng.Uint64() 120 for i := 0; i < 10; i++ { 121 if int(r&0x3f) < len(b57Chars) { 122 b[n] = b57Chars[r&0x3f] 123 n++ 124 if n == len(b) { 125 return 126 } 127 } 128 r >>= 6 129 } 130 } 131 } 132 133 func (g *guidGenerator) New(prefix string) string { 134 b := make([]byte, len(prefix)+Size) 135 copy(b, prefix) 136 g.readIDChars(b[len(prefix):]) 137 return unsafe.String(unsafe.SliceData(b), len(b)) 138 } 139 140 func guidPrefix[T livekit.Guid]() string { 141 var id T 142 switch any(id).(type) { 143 case livekit.TrackID: 144 return TrackPrefix 145 case livekit.ParticipantID: 146 return ParticipantPrefix 147 case livekit.RoomID: 148 return RoomPrefix 149 default: 150 panic("unreachable") 151 } 152 } 153 154 func Marshal[T livekit.Guid](id T) livekit.GuidBlock { 155 return livekit.GuidBlock(MarshalAppend(nil, id)) 156 } 157 158 func MarshalAppend[T livekit.Guid](b []byte, id T) []byte { 159 off := len(b) 160 b = append(b, make([]byte, Size*3/4)...) 161 idb := []byte(id)[len(id)-Size:] 162 for i := 0; i < 3; i++ { 163 j := i*3 + off 164 k := i * 4 165 b[j] = b57Index[idb[k]]<<2 | b57Index[idb[k+1]]>>4 166 b[j+1] = b57Index[idb[k+1]]<<4 | b57Index[idb[k+2]]>>2 167 b[j+2] = b57Index[idb[k+2]]<<6 | b57Index[idb[k+3]] 168 } 169 return b 170 } 171 172 func Unmarshal[T livekit.Guid](b livekit.GuidBlock) T { 173 prefix := guidPrefix[T]() 174 id := make([]byte, len(prefix)+Size) 175 copy(id, []byte(prefix)) 176 idb := id[len(prefix):] 177 for i := 0; i < 3; i++ { 178 j := i * 3 179 k := i * 4 180 idb[k] = b57Chars[b[j]>>2] 181 idb[k+1] = b57Chars[(b[j]&3)<<4|b[j+1]>>4] 182 idb[k+2] = b57Chars[(b[j+1]&15)<<2|b[j+2]>>6] 183 idb[k+3] = b57Chars[b[j+2]&63] 184 } 185 return T(unsafe.String(unsafe.SliceData(id), len(id))) 186 }