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 }