1// Copyright 2022 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 cov
6
7import (
8	"cmd/internal/bio"
9	"io"
10	"os"
11)
12
13// This file contains the helper "MReader", a wrapper around bio plus
14// an "mmap'd read-only" view of the file obtained from bio.SliceRO().
15// MReader is designed to implement the io.ReaderSeeker interface.
16// Since bio.SliceOS() is not guaranteed to succeed, MReader falls back
17// on explicit reads + seeks provided by bio.Reader if needed.
18
19type MReader struct {
20	f        *os.File
21	rdr      *bio.Reader
22	fileView []byte
23	off      int64
24}
25
26func NewMreader(f *os.File) (*MReader, error) {
27	rdr := bio.NewReader(f)
28	fi, err := f.Stat()
29	if err != nil {
30		return nil, err
31	}
32	r := MReader{
33		f:        f,
34		rdr:      rdr,
35		fileView: rdr.SliceRO(uint64(fi.Size())),
36	}
37	return &r, nil
38}
39
40func (r *MReader) Read(p []byte) (int, error) {
41	if r.fileView != nil {
42		amt := len(p)
43		toread := r.fileView[r.off:]
44		if len(toread) < 1 {
45			return 0, io.EOF
46		}
47		if len(toread) < amt {
48			amt = len(toread)
49		}
50		copy(p, toread)
51		r.off += int64(amt)
52		return amt, nil
53	}
54	return io.ReadFull(r.rdr, p)
55}
56
57func (r *MReader) ReadByte() (byte, error) {
58	if r.fileView != nil {
59		toread := r.fileView[r.off:]
60		if len(toread) < 1 {
61			return 0, io.EOF
62		}
63		rv := toread[0]
64		r.off++
65		return rv, nil
66	}
67	return r.rdr.ReadByte()
68}
69
70func (r *MReader) Seek(offset int64, whence int) (int64, error) {
71	if r.fileView == nil {
72		return r.rdr.MustSeek(offset, whence), nil
73	}
74	switch whence {
75	case io.SeekStart:
76		r.off = offset
77		return offset, nil
78	case io.SeekCurrent:
79		return r.off, nil
80	case io.SeekEnd:
81		r.off = int64(len(r.fileView)) + offset
82		return r.off, nil
83	}
84	panic("other modes not implemented")
85}
86