func parseNFSOperationStats(s *bufio.Scanner) ([]NFSOperationStats, error) {
	const (
		minFields = 9
	)
	var ops []NFSOperationStats
	for s.Scan() {
		ss := strings.Fields(string(s.Bytes()))
		if len(ss) == 0 {
			break
		}
		if len(ss) < minFields {
			return nil, fmt.Errorf("invalid NFS per-operations stats: %v", ss)
		}
		ns := make([]uint64, 0, minFields-1)
		for _, st := range ss[1:] {
			n, err := strconv.ParseUint(st, 10, 64)
			if err != nil {
				return nil, err
			}
			ns = append(ns, n)
		}
		opStats := NFSOperationStats{
			Operation:                           strings.TrimSuffix(ss[0], ":"),
			Requests:                            ns[0],
			Transmissions:                       ns[1],
			MajorTimeouts:                       ns[2],
			BytesSent:                           ns[3],
			BytesReceived:                       ns[4],
			CumulativeQueueMilliseconds:         ns[5],
			CumulativeTotalResponseMilliseconds: ns[6],
			CumulativeTotalRequestMilliseconds:  ns[7],
		}
		if len(ns) > 8 {
			opStats.Errors = ns[8]
		}
		ops = append(ops, opStats)
	}
	return ops, s.Err()
}
parseNFSOperationStats parses a slice of NFSOperationStats by scanning
additional information about per-operation statistics until an empty
line is reached.
package procfs
import (
	"bufio"
	"fmt"
	"io"
	"strconv"
	"strings"
	"time"
)
const (
	deviceEntryLen = 8
	fieldBytesLen  = 8
	fieldEventsLen = 27
	statVersion10 = "1.0"
	statVersion11 = "1.1"
	fieldTransport10TCPLen = 10
	fieldTransport10UDPLen = 7
	fieldTransport11TCPLen = 13
	fieldTransport11UDPLen = 10
)
type Mount struct {
	Device string
	Mount string
	Type string
	Stats MountStats
}
type MountStats interface {
	mountStats()
}
type MountStatsNFS struct {
	StatVersion string
	Opts map[string]string
	Age time.Duration
	Bytes NFSBytesStats
	Events NFSEventsStats
	Operations []NFSOperationStats
	Transport NFSTransportStats
}
func (m MountStatsNFS) mountStats() {}
type NFSBytesStats struct {
	Read uint64
	Write uint64
	DirectRead uint64
	DirectWrite uint64
	ReadTotal uint64
	WriteTotal uint64
	ReadPages uint64
	WritePages uint64
}
type NFSEventsStats struct {
	InodeRevalidate uint64
	DnodeRevalidate uint64
	DataInvalidate uint64
	AttributeInvalidate uint64
	VFSOpen uint64
	VFSLookup uint64
	VFSAccess uint64
	VFSUpdatePage uint64
	VFSReadPage uint64
	VFSReadPages uint64
	VFSWritePage uint64
	VFSWritePages uint64
	VFSGetdents uint64
	VFSSetattr uint64
	VFSFlush uint64
	VFSFsync uint64
	VFSLock uint64
	VFSFileRelease uint64
	CongestionWait uint64
	Truncation uint64
	WriteExtension uint64
	SillyRename uint64
	ShortRead uint64
	ShortWrite uint64
	JukeboxDelay uint64
	PNFSRead uint64
	PNFSWrite uint64
}
type NFSOperationStats struct {
	Operation string
	Requests uint64
	Transmissions uint64
	MajorTimeouts uint64
	BytesSent uint64
	BytesReceived uint64
	CumulativeQueueMilliseconds uint64
	CumulativeTotalResponseMilliseconds uint64
	CumulativeTotalRequestMilliseconds uint64
	Errors uint64
}
type NFSTransportStats struct {
	Protocol string
	Port uint64
	Bind uint64
	Connect uint64
	ConnectIdleTime uint64
	IdleTimeSeconds uint64
	Sends uint64
	Receives uint64
	BadTransactionIDs uint64
	CumulativeActiveRequests uint64
	CumulativeBacklog uint64
	MaximumRPCSlotsUsed uint64
	CumulativeSendingQueue uint64
	CumulativePendingQueue uint64
}
func parseMountStats(r io.Reader) ([]*Mount, error) {
	const (
		device            = "device"
		statVersionPrefix = "statvers="
		nfs3Type = "nfs"
		nfs4Type = "nfs4"
	)
	var mounts []*Mount
	s := bufio.NewScanner(r)
	for s.Scan() {
		ss := strings.Fields(string(s.Bytes()))
		if len(ss) == 0 || ss[0] != device {
			continue
		}
		m, err := parseMount(ss)
		if err != nil {
			return nil, err
		}
		if len(ss) > deviceEntryLen {
			if m.Type != nfs3Type && m.Type != nfs4Type {
				return nil, fmt.Errorf("cannot parse MountStats for fstype %q", m.Type)
			}
			statVersion := strings.TrimPrefix(ss[8], statVersionPrefix)
			stats, err := parseMountStatsNFS(s, statVersion)
			if err != nil {
				return nil, err
			}
			m.Stats = stats
		}
		mounts = append(mounts, m)
	}
	return mounts, s.Err()
}
func parseMount(ss []string) (*Mount, error) {
	if len(ss) < deviceEntryLen {
		return nil, fmt.Errorf("invalid device entry: %v", ss)
	}
	format := []struct {
		i int
		s string
	}{
		{i: 0, s: "device"},
		{i: 2, s: "mounted"},
		{i: 3, s: "on"},
		{i: 5, s: "with"},
		{i: 6, s: "fstype"},
	}
	for _, f := range format {
		if ss[f.i] != f.s {
			return nil, fmt.Errorf("invalid device entry: %v", ss)
		}
	}
	return &Mount{
		Device: ss[1],
		Mount:  ss[4],
		Type:   ss[7],
	}, nil
}
func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, error) {
	const (
		fieldOpts       = "opts:"
		fieldAge        = "age:"
		fieldBytes      = "bytes:"
		fieldEvents     = "events:"
		fieldPerOpStats = "per-op"
		fieldTransport  = "xprt:"
	)
	stats := &MountStatsNFS{
		StatVersion: statVersion,
	}
	for s.Scan() {
		ss := strings.Fields(string(s.Bytes()))
		if len(ss) == 0 {
			break
		}
		if len(ss) < 2 {
			return nil, fmt.Errorf("not enough information for NFS stats: %v", ss)
		}
		switch ss[0] {
		case fieldOpts:
			if stats.Opts == nil {
				stats.Opts = map[string]string{}
			}
			for _, opt := range strings.Split(ss[1], ",") {
				split := strings.Split(opt, "=")
				if len(split) == 2 {
					stats.Opts[split[0]] = split[1]
				} else {
					stats.Opts[opt] = ""
				}
			}
		case fieldAge:
			d, err := time.ParseDuration(ss[1] + "s")
			if err != nil {
				return nil, err
			}
			stats.Age = d
		case fieldBytes:
			bstats, err := parseNFSBytesStats(ss[1:])
			if err != nil {
				return nil, err
			}
			stats.Bytes = *bstats
		case fieldEvents:
			estats, err := parseNFSEventsStats(ss[1:])
			if err != nil {
				return nil, err
			}
			stats.Events = *estats
		case fieldTransport:
			if len(ss) < 3 {
				return nil, fmt.Errorf("not enough information for NFS transport stats: %v", ss)
			}
			tstats, err := parseNFSTransportStats(ss[1:], statVersion)
			if err != nil {
				return nil, err
			}
			stats.Transport = *tstats
		}
		if ss[0] == fieldPerOpStats {
			break
		}
	}
	if err := s.Err(); err != nil {
		return nil, err
	}
	perOpStats, err := parseNFSOperationStats(s)
	if err != nil {
		return nil, err
	}
	stats.Operations = perOpStats
	return stats, nil
}
func parseNFSBytesStats(ss []string) (*NFSBytesStats, error) {
	if len(ss) != fieldBytesLen {
		return nil, fmt.Errorf("invalid NFS bytes stats: %v", ss)
	}
	ns := make([]uint64, 0, fieldBytesLen)
	for _, s := range ss {
		n, err := strconv.ParseUint(s, 10, 64)
		if err != nil {
			return nil, err
		}
		ns = append(ns, n)
	}
	return &NFSBytesStats{
		Read:        ns[0],
		Write:       ns[1],
		DirectRead:  ns[2],
		DirectWrite: ns[3],
		ReadTotal:   ns[4],
		WriteTotal:  ns[5],
		ReadPages:   ns[6],
		WritePages:  ns[7],
	}, nil
}
func parseNFSEventsStats(ss []string) (*NFSEventsStats, error) {
	if len(ss) != fieldEventsLen {
		return nil, fmt.Errorf("invalid NFS events stats: %v", ss)
	}
	ns := make([]uint64, 0, fieldEventsLen)
	for _, s := range ss {
		n, err := strconv.ParseUint(s, 10, 64)
		if err != nil {
			return nil, err
		}
		ns = append(ns, n)
	}
	return &NFSEventsStats{
		InodeRevalidate:     ns[0],
		DnodeRevalidate:     ns[1],
		DataInvalidate:      ns[2],
		AttributeInvalidate: ns[3],
		VFSOpen:             ns[4],
		VFSLookup:           ns[5],
		VFSAccess:           ns[6],
		VFSUpdatePage:       ns[7],
		VFSReadPage:         ns[8],
		VFSReadPages:        ns[9],
		VFSWritePage:        ns[10],
		VFSWritePages:       ns[11],
		VFSGetdents:         ns[12],
		VFSSetattr:          ns[13],
		VFSFlush:            ns[14],
		VFSFsync:            ns[15],
		VFSLock:             ns[16],
		VFSFileRelease:      ns[17],
		CongestionWait:      ns[18],
		Truncation:          ns[19],
		WriteExtension:      ns[20],
		SillyRename:         ns[21],
		ShortRead:           ns[22],
		ShortWrite:          ns[23],
		JukeboxDelay:        ns[24],
		PNFSRead:            ns[25],
		PNFSWrite:           ns[26],
	}, nil
}