github.com/iDigitalFlame/xmt@v0.5.4/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  	return i.string(0, MachineIDSize)
    85  }
    86  
    87  // Load will attempt to load the Session UUID from the specified file. This
    88  // function will return an error if the file cannot be read or not found.
    89  func (i *ID) Load(s string) error {
    90  	// 0 - READONLY
    91  	r, err := os.OpenFile(s, 0, 0)
    92  	if err != nil {
    93  		return err
    94  	}
    95  	err = i.Read(r)
    96  	if r.Close(); err != nil {
    97  		return err
    98  	}
    99  	return nil
   100  }
   101  
   102  // Save will attempt to save the Session UUID to the specified file. This
   103  // function will return an error if the file cannot be written to or created.
   104  func (i ID) Save(s string) error {
   105  	// 0x242 - CREATE | TRUNCATE | RDWR
   106  	w, err := os.OpenFile(s, 0x242, 0644)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	_, err = w.Write(i[:])
   111  	if w.Close(); err != nil {
   112  		return err
   113  	}
   114  	return nil
   115  }
   116  
   117  // Read will attempt to read up to 'IDSize' bytes from the reader into this ID.
   118  func (i *ID) Read(r io.Reader) error {
   119  	if n, err := io.ReadFull(r, i[:]); n != IDSize || i[0] == 0 {
   120  		// NOTE(dij): This line causes empty ID values to be considered read
   121  		//            errors. Technically this is incorrect, but we should never
   122  		//            be using empty ID values to communicate so it's sorta ok.
   123  		if err != nil {
   124  			return err
   125  		}
   126  		return io.ErrUnexpectedEOF
   127  	}
   128  	return nil
   129  }
   130  
   131  // Write will attempt to write the ID bytes into the supplied writer.
   132  func (i ID) Write(w io.Writer) error {
   133  	n, err := w.Write(i[:])
   134  	if err == nil && n != IDSize {
   135  		return io.ErrShortWrite
   136  	}
   137  	return err
   138  }
   139  func (i ID) string(start, end int) string {
   140  	var (
   141  		b [64]byte
   142  		n int
   143  	)
   144  	for x := start; x < end; x++ {
   145  		b[n] = util.HexTable[i[x]>>4]
   146  		b[n+1] = util.HexTable[i[x]&0x0F]
   147  		n += 2
   148  	}
   149  	return string(b[:n])
   150  }
   151  
   152  // MarshalStream transform this struct into a binary format and writes to the
   153  // supplied data.Writer.
   154  func (i ID) MarshalStream(w data.Writer) error {
   155  	_, err := w.Write(i[:])
   156  	return err
   157  }
   158  
   159  // UnmarshalStream transforms this struct from a binary format that is read from
   160  // the supplied data.Reader.
   161  func (i *ID) UnmarshalStream(r data.Reader) error {
   162  	return i.Read(r)
   163  }