github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libgit/id.go (about) 1 // Copyright 2017 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package libgit 6 7 import ( 8 "encoding" 9 "encoding/hex" 10 "fmt" 11 12 "github.com/keybase/client/go/kbfs/kbfscrypto" 13 "github.com/pkg/errors" 14 ) 15 16 const ( 17 // idByteLen is the number of bytes in a git repo ID. 18 idByteLen = 16 19 20 idSuffix = 0x2c 21 ) 22 23 // InvalidIDError indicates that a repo ID string is not parseable or 24 // invalid. 25 type InvalidIDError struct { 26 id string 27 } 28 29 func (e InvalidIDError) Error() string { 30 return fmt.Sprintf("Invalid repo ID %q", e.id) 31 } 32 33 // ID encapsulates a repo ID. 34 type ID struct { 35 id [idByteLen]byte 36 } 37 38 var _ encoding.BinaryMarshaler = ID{} 39 var _ encoding.BinaryUnmarshaler = (*ID)(nil) 40 41 var _ encoding.TextMarshaler = ID{} 42 var _ encoding.TextUnmarshaler = (*ID)(nil) 43 44 // NullID is an empty ID 45 var NullID = ID{} 46 47 // Bytes returns the bytes of the ID. 48 func (id ID) Bytes() []byte { 49 return id.id[:] 50 } 51 52 // String implements the Stringer interface for ID. 53 func (id ID) String() string { 54 return hex.EncodeToString(id.id[:]) 55 } 56 57 // MarshalBinary implements the encoding.BinaryMarshaler interface for ID. 58 func (id ID) MarshalBinary() (data []byte, err error) { 59 suffix := id.id[idByteLen-1] 60 if suffix != idSuffix { 61 return nil, errors.WithStack(InvalidIDError{id.String()}) 62 } 63 return id.id[:], nil 64 } 65 66 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface 67 // for ID. 68 func (id *ID) UnmarshalBinary(data []byte) error { 69 if len(data) != idByteLen { 70 return errors.WithStack(InvalidIDError{hex.EncodeToString(data)}) 71 } 72 suffix := data[idByteLen-1] 73 if suffix != idSuffix { 74 return errors.WithStack(InvalidIDError{hex.EncodeToString(data)}) 75 } 76 copy(id.id[:], data) 77 return nil 78 } 79 80 // MarshalText implements the encoding.TextMarshaler interface for ID. 81 func (id ID) MarshalText() ([]byte, error) { 82 bytes, err := id.MarshalBinary() 83 if err != nil { 84 return nil, err 85 } 86 return []byte(hex.EncodeToString(bytes)), nil 87 } 88 89 // UnmarshalText implements the encoding.TextUnmarshaler interface for 90 // ID. 91 func (id *ID) UnmarshalText(buf []byte) error { 92 s := string(buf) 93 bytes, err := hex.DecodeString(s) 94 if err != nil { 95 return errors.WithStack(InvalidIDError{s}) 96 } 97 return id.UnmarshalBinary(bytes) 98 } 99 100 func makeRandomID() (id ID, err error) { 101 // Loop just in case we randomly pick the null ID. 102 for id == NullID { 103 err := kbfscrypto.RandRead(id.id[:idByteLen-1]) 104 if err != nil { 105 return ID{}, err 106 } 107 } 108 id.id[idByteLen-1] = idSuffix 109 return id, nil 110 }