github.com/iDigitalFlame/xmt@v0.5.1/device/id.go (about) 1 // Copyright (C) 2020 - 2023 iDigitalFlame 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU General Public License for more details. 12 // 13 // You should have received a copy of the GNU General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 // 16 17 package device 18 19 import ( 20 "io" 21 "os" 22 23 "github.com/iDigitalFlame/xmt/data" 24 "github.com/iDigitalFlame/xmt/util" 25 ) 26 27 const ( 28 // IDSize is the amount of bytes used to store the Host ID and 29 // SessionID values. The ID is the (HostID + SessionID). 30 IDSize = 32 31 // MachineIDSize is the amount of bytes that is used as the Host 32 // specific ID value that does not change when on the same host. 33 MachineIDSize = 28 34 ) 35 36 // ID is an alias for a byte array that represents a 32 byte client 37 // identification number. This is used for tracking and detection purposes. 38 // 39 // The first byte and the machine ID byte should NEVER be zero, otherwise it 40 // signals an invalid ID value or missing a random identifier. 41 type ID [IDSize]byte 42 43 // Empty returns true if this ID is considered empty. 44 func (i ID) Empty() bool { 45 return i[0] == 0 46 } 47 48 // Hash returns the 32bit hash sum of this ID value. The hash mechanism used is 49 // similar to the hash/fnv mechanism. 50 func (i ID) Hash() uint32 { 51 h := uint32(2166136261) 52 for x := range i { 53 h *= 16777619 54 h ^= uint32(i[x]) 55 } 56 return h 57 } 58 59 // Full returns the full string representation of this ID instance. 60 func (i ID) Full() string { 61 return i.string(0, IDSize) 62 } 63 64 // String returns a representation of this ID instance. 65 func (i ID) String() string { 66 if i[MachineIDSize] == 0 { 67 return i.string(0, MachineIDSize) 68 } 69 return i.string(MachineIDSize, IDSize) 70 } 71 72 // Seed will set the random portion of the ID value to the specified byte array 73 // value. 74 func (i *ID) Seed(b []byte) { 75 if len(b) == 0 { 76 return 77 } 78 copy(i[MachineIDSize:], b) 79 } 80 81 // Signature returns the signature portion of the ID value. This value is 82 // constant and unique for each device. 83 func (i ID) Signature() string { 84 if i[MachineIDSize] == 0 { 85 return i.string(0, MachineIDSize) 86 } 87 return i.string(0, MachineIDSize) 88 } 89 90 // Load will attempt to load the Session UUID from the specified file. This 91 // function will return an error if the file cannot be read or not found. 92 func (i *ID) Load(s string) error { 93 // 0 - READONLY 94 r, err := os.OpenFile(s, 0, 0) 95 if err != nil { 96 return err 97 } 98 err = i.Read(r) 99 if r.Close(); err != nil { 100 return err 101 } 102 return nil 103 } 104 105 // Save will attempt to save the Session UUID to the specified file. This 106 // function will return an error if the file cannot be written to or created. 107 func (i ID) Save(s string) error { 108 // 0x242 - CREATE | TRUNCATE | RDWR 109 w, err := os.OpenFile(s, 0x242, 0644) 110 if err != nil { 111 return err 112 } 113 _, err = w.Write(i[:]) 114 if w.Close(); err != nil { 115 return err 116 } 117 return nil 118 } 119 120 // Read will attempt to read up to 'IDSize' bytes from the reader into this ID. 121 func (i *ID) Read(r io.Reader) error { 122 if n, err := io.ReadFull(r, i[:]); n != IDSize || i[0] == 0 { 123 // NOTE(dij): This line causes empty ID values to be considered read 124 // errors. Technically this is incorrect, but we should never 125 // be using empty ID values to communicate so it's sorta ok. 126 if err != nil { 127 return err 128 } 129 return io.ErrUnexpectedEOF 130 } 131 return nil 132 } 133 134 // Write will attempt to write the ID bytes into the supplied writer. 135 func (i ID) Write(w io.Writer) error { 136 n, err := w.Write(i[:]) 137 if err == nil && n != IDSize { 138 return io.ErrShortWrite 139 } 140 return err 141 } 142 func (i ID) string(start, end int) string { 143 var ( 144 b [64]byte 145 n int 146 ) 147 for x := start; x < end; x++ { 148 b[n] = util.HexTable[i[x]>>4] 149 b[n+1] = util.HexTable[i[x]&0x0F] 150 n += 2 151 } 152 return string(b[:n]) 153 } 154 155 // MarshalStream transform this struct into a binary format and writes to the 156 // supplied data.Writer. 157 func (i ID) MarshalStream(w data.Writer) error { 158 _, err := w.Write(i[:]) 159 return err 160 } 161 162 // UnmarshalStream transforms this struct from a binary format that is read from 163 // the supplied data.Reader. 164 func (i *ID) UnmarshalStream(r data.Reader) error { 165 return i.Read(r) 166 }