github.com/iDigitalFlame/xmt@v0.5.4/data/util.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 data
    18  
    19  import (
    20  	"context"
    21  	"io"
    22  	"os"
    23  	"unsafe"
    24  )
    25  
    26  type ctxReader struct {
    27  	ctx    context.Context
    28  	cancel context.CancelFunc
    29  	io.ReadCloser
    30  }
    31  type nopReadCloser struct {
    32  	_ [0]func()
    33  	io.Reader
    34  }
    35  type nopWriteCloser struct {
    36  	_ [0]func()
    37  	io.Writer
    38  }
    39  
    40  func (r *ctxReader) Close() error {
    41  	r.cancel()
    42  	return r.ReadCloser.Close()
    43  }
    44  func (nopReadCloser) Close() error {
    45  	return nil
    46  }
    47  func float32ToInt(f float32) uint32 {
    48  	return *(*uint32)(unsafe.Pointer(&f))
    49  }
    50  func float64ToInt(f float64) uint64 {
    51  	return *(*uint64)(unsafe.Pointer(&f))
    52  }
    53  func (nopWriteCloser) Close() error {
    54  	return nil
    55  }
    56  func float32FromInt(i uint32) float32 {
    57  	return *(*float32)(unsafe.Pointer(&i))
    58  }
    59  func float64FromInt(i uint64) float64 {
    60  	return *(*float64)(unsafe.Pointer(&i))
    61  }
    62  
    63  // ReadFile reads the named file and returns the contents. A successful call
    64  // returns err == nil, not err == EOF.
    65  //
    66  // Because ReadFile reads the whole file, it does not treat an EOF from Read as
    67  // an error to be reported.
    68  //
    69  // This is a pre go1.16 compatibility helper.
    70  func ReadFile(n string) ([]byte, error) {
    71  	f, err := os.OpenFile(n, 0, 0)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	var s int
    76  	if i, err := f.Stat(); err == nil {
    77  		if x := i.Size(); int64(int(x)) == x {
    78  			s = int(x)
    79  		}
    80  	}
    81  	if s++; s < 512 {
    82  		s = 512
    83  	}
    84  	var (
    85  		d = make([]byte, 0, s)
    86  		c int
    87  	)
    88  	for {
    89  		if len(d) >= cap(d) {
    90  			q := append(d[:cap(d)], 0)
    91  			d = q[:len(d)]
    92  		}
    93  		c, err = f.Read(d[len(d):cap(d)])
    94  		if d = d[:len(d)+c]; err != nil {
    95  			if err == io.EOF {
    96  				err = nil
    97  			}
    98  			break
    99  		}
   100  	}
   101  	f.Close()
   102  	return d, err
   103  }
   104  
   105  // ReadAll reads from r until an error or EOF and returns the data it read. A
   106  // successful call returns err == nil, not err == EOF.
   107  //
   108  // Because ReadAll is defined to read from src until EOF, it does not treat an
   109  // EOF from Read as an error to be reported.
   110  //
   111  // This is a pre go1.16 compatibility helper.
   112  func ReadAll(r io.Reader) ([]byte, error) {
   113  	var (
   114  		b   = make([]byte, 0, 512)
   115  		n   int
   116  		err error
   117  	)
   118  	for {
   119  		if len(b) == cap(b) {
   120  			b = append(b, 0)[:len(b)]
   121  		}
   122  		n, err = r.Read(b[len(b):cap(b)])
   123  		if b = b[:len(b)+n]; err != nil {
   124  			if err == io.EOF {
   125  				return b, nil
   126  			}
   127  			return b, err
   128  		}
   129  	}
   130  }
   131  
   132  // ReadCloser is a function that will wrap the supplied Reader in a NopReadCloser.
   133  func ReadCloser(r io.Reader) io.ReadCloser {
   134  	if v, ok := r.(io.ReadCloser); ok {
   135  		return v
   136  	}
   137  	return &nopReadCloser{Reader: r}
   138  }
   139  
   140  // WriteCloser is a function that will wrap the supplied Writer in a NopWriteCloser.
   141  func WriteCloser(w io.Writer) io.WriteCloser {
   142  	if v, ok := w.(io.WriteCloser); ok {
   143  		return v
   144  	}
   145  	return &nopWriteCloser{Writer: w}
   146  }
   147  func (r *ctxReader) Read(b []byte) (int, error) {
   148  	select {
   149  	case <-r.ctx.Done():
   150  		if err := r.ReadCloser.Close(); err != nil {
   151  			return 0, err
   152  		}
   153  		return 0, r.ctx.Err()
   154  	default:
   155  		return r.ReadCloser.Read(b)
   156  	}
   157  }
   158  
   159  // ReadStringList attempts to read a string list written using the 'WriteStringList'
   160  // function from the supplied string into the string list pointer. If the provided
   161  // array is nil or not large enough, it will be resized.
   162  func ReadStringList(r Reader, s *[]string) error {
   163  	t, err := r.Uint8()
   164  	if err != nil {
   165  		return err
   166  	}
   167  	var l int
   168  	switch t {
   169  	case 0:
   170  		return nil
   171  	case 1, 2:
   172  		n, err := r.Uint8()
   173  		if err != nil {
   174  			return err
   175  		}
   176  		l = int(n)
   177  	case 3, 4:
   178  		n, err := r.Uint16()
   179  		if err != nil {
   180  			return err
   181  		}
   182  		l = int(n)
   183  	case 5, 6:
   184  		n, err := r.Uint32()
   185  		if err != nil {
   186  			return err
   187  		}
   188  		l = int(n)
   189  	case 7, 8:
   190  		n, err := r.Uint64()
   191  		if err != nil {
   192  			return err
   193  		}
   194  		l = int(n)
   195  	default:
   196  		return ErrInvalidType
   197  	}
   198  	if s == nil || len(*s) < l {
   199  		*s = make([]string, l)
   200  	}
   201  	for x := 0; x < l; x++ {
   202  		if err := r.ReadString(&(*s)[x]); err != nil {
   203  			return err
   204  		}
   205  	}
   206  	return nil
   207  }
   208  
   209  // WriteStringList will attempt to write the supplied string list to the writer.
   210  // If the string list is nil or empty, it will write a zero byte to the Writer.
   211  // The resulting data can be read using the 'ReadStringList' function.
   212  func WriteStringList(w Writer, s []string) error {
   213  	switch l := uint64(len(s)); {
   214  	case l == 0:
   215  		v, err := w.Write([]byte{0})
   216  		if err == nil && v != 1 {
   217  			return io.ErrShortWrite
   218  		}
   219  		return err
   220  	case l < LimitSmall:
   221  		if v, err := w.Write([]byte{1, byte(l)}); err != nil {
   222  			return err
   223  		} else if v != 2 {
   224  			return io.ErrShortWrite
   225  		}
   226  	case l < LimitMedium:
   227  		if v, err := w.Write([]byte{3, byte(l >> 8), byte(l)}); err != nil {
   228  			return err
   229  		} else if v != 3 {
   230  			return io.ErrShortWrite
   231  		}
   232  	case l < LimitLarge:
   233  		if v, err := w.Write([]byte{5, byte(l >> 24), byte(l >> 16), byte(l >> 8), byte(l)}); err != nil {
   234  			return err
   235  		} else if v != 5 {
   236  			return io.ErrShortWrite
   237  		}
   238  	default:
   239  		if v, err := w.Write([]byte{
   240  			7, byte(l >> 56), byte(l >> 48), byte(l >> 40), byte(l >> 32),
   241  			byte(l >> 24), byte(l >> 16), byte(l >> 8), byte(l),
   242  		}); err != nil {
   243  			return nil
   244  		} else if v != 9 {
   245  			return io.ErrShortWrite
   246  		}
   247  	}
   248  	for i := range s {
   249  		if err := w.WriteString(s[i]); err != nil {
   250  			return err
   251  		}
   252  	}
   253  	return nil
   254  }
   255  
   256  // NewCtxReader creates a reader backed by the supplied Reader and Context. This
   257  // reader will automatically close when the parent context is canceled. This is
   258  // useful in situations when direct copies using 'io.Copy' on threads or timed
   259  // operations are required.
   260  func NewCtxReader(x context.Context, r io.Reader) io.ReadCloser {
   261  	var i ctxReader
   262  	if c, ok := r.(io.ReadCloser); ok {
   263  		i.ReadCloser = c
   264  	} else {
   265  		i.ReadCloser = &nopReadCloser{Reader: r}
   266  	}
   267  	i.ctx, i.cancel = context.WithCancel(x)
   268  	return &i
   269  }