slicereader.mx raw

   1  // Copyright 2021 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package slicereader
   6  
   7  import (
   8  	"encoding/binary"
   9  	"fmt"
  10  	"io"
  11  	"unsafe"
  12  )
  13  
  14  // This file contains the helper "SliceReader", a utility for
  15  // reading values from a byte slice that may or may not be backed
  16  // by a read-only mmap'd region.
  17  
  18  type Reader struct {
  19  	b        []byte
  20  	readonly bool
  21  	off      int64
  22  }
  23  
  24  func NewReader(b []byte, readonly bool) *Reader {
  25  	r := Reader{
  26  		b:        b,
  27  		readonly: readonly,
  28  	}
  29  	return &r
  30  }
  31  
  32  func (r *Reader) Read(b []byte) (int, error) {
  33  	amt := len(b)
  34  	toread := r.b[r.off:]
  35  	if len(toread) < amt {
  36  		amt = len(toread)
  37  	}
  38  	copy(b, toread)
  39  	r.off += int64(amt)
  40  	return amt, nil
  41  }
  42  
  43  func (r *Reader) Seek(offset int64, whence int) (ret int64, err error) {
  44  	switch whence {
  45  	case io.SeekStart:
  46  		if offset < 0 || offset > int64(len(r.b)) {
  47  			return 0, fmt.Errorf("invalid seek: new offset %d (out of range [0 %d]", offset, len(r.b))
  48  		}
  49  		r.off = offset
  50  		return offset, nil
  51  	case io.SeekCurrent:
  52  		newoff := r.off + offset
  53  		if newoff < 0 || newoff > int64(len(r.b)) {
  54  			return 0, fmt.Errorf("invalid seek: new offset %d (out of range [0 %d]", newoff, len(r.b))
  55  		}
  56  		r.off = newoff
  57  		return r.off, nil
  58  	case io.SeekEnd:
  59  		newoff := int64(len(r.b)) + offset
  60  		if newoff < 0 || newoff > int64(len(r.b)) {
  61  			return 0, fmt.Errorf("invalid seek: new offset %d (out of range [0 %d]", newoff, len(r.b))
  62  		}
  63  		r.off = newoff
  64  		return r.off, nil
  65  	}
  66  	// other modes are not supported
  67  	return 0, fmt.Errorf("unsupported seek mode %d", whence)
  68  }
  69  
  70  func (r *Reader) Offset() int64 {
  71  	return r.off
  72  }
  73  
  74  func (r *Reader) ReadUint8() uint8 {
  75  	rv := uint8(r.b[int(r.off)])
  76  	r.off += 1
  77  	return rv
  78  }
  79  
  80  func (r *Reader) ReadUint32() uint32 {
  81  	end := int(r.off) + 4
  82  	rv := binary.LittleEndian.Uint32(r.b[int(r.off):end:end])
  83  	r.off += 4
  84  	return rv
  85  }
  86  
  87  func (r *Reader) ReadUint64() uint64 {
  88  	end := int(r.off) + 8
  89  	rv := binary.LittleEndian.Uint64(r.b[int(r.off):end:end])
  90  	r.off += 8
  91  	return rv
  92  }
  93  
  94  func (r *Reader) ReadULEB128() (value uint64) {
  95  	var shift uint
  96  
  97  	for {
  98  		b := r.b[r.off]
  99  		r.off++
 100  		value |= (uint64(b&0x7F) << shift)
 101  		if b&0x80 == 0 {
 102  			break
 103  		}
 104  		shift += 7
 105  	}
 106  	return
 107  }
 108  
 109  func (r *Reader) ReadString(len int64) []byte {
 110  	b := r.b[r.off : r.off+len]
 111  	r.off += len
 112  	if r.readonly {
 113  		return toString(b) // backed by RO memory, ok to make unsafe string
 114  	}
 115  	return []byte(b)
 116  }
 117  
 118  func toString(b []byte) []byte {
 119  	if len(b) == 0 {
 120  		return ""
 121  	}
 122  	return unsafe.String(&b[0], len(b))
 123  }
 124