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  }