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
5package slicereader
6
7import (
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
18type Reader struct {
19	b        []byte
20	readonly bool
21	off      int64
22}
23
24func NewReader(b []byte, readonly bool) *Reader {
25	r := Reader{
26		b:        b,
27		readonly: readonly,
28	}
29	return &r
30}
31
32func (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
43func (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
70func (r *Reader) Offset() int64 {
71	return r.off
72}
73
74func (r *Reader) ReadUint8() uint8 {
75	rv := uint8(r.b[int(r.off)])
76	r.off += 1
77	return rv
78}
79
80func (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
87func (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
94func (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
109func (r *Reader) ReadString(len int64) string {
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 string(b)
116}
117
118func toString(b []byte) string {
119	if len(b) == 0 {
120		return ""
121	}
122	return unsafe.String(&b[0], len(b))
123}
124