func NewFromFile(path string) (Digest, error) {
	f, err := os.Open(path)
	if err != nil {
		return Empty, err
	}
	defer f.Close()
	return NewFromReader(f)
}
NewFromFile computes a file digest from a path.
It returns an error if there was a problem accessing the file.
package digest
import (
	"crypto"
	"encoding/hex"
	"fmt"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"github.com/golang/protobuf/proto"
	repb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
)
var (
	hexStringRegex = regexp.MustCompile("^[a-f0-9]+$")
	HashFn = crypto.SHA256
	Empty = NewFromBlob([]byte{})
	copyBufs = sync.Pool{
		New: func() interface{} {
			buf := make([]byte, 32*1024)
			return &buf
		},
	}
)
type Digest struct {
	Hash string
	Size int64
}
func GetDigestFunction() repb.DigestFunction_Value {
	name := "SHA256"
	if val, ok := repb.DigestFunction_Value_value[name]; ok {
		return repb.DigestFunction_Value(val)
	}
	return repb.DigestFunction_UNKNOWN
}
func (d Digest) ToProto() *repb.Digest {
	return &repb.Digest{Hash: d.Hash, SizeBytes: d.Size}
}
func (d Digest) String() string {
	return fmt.Sprintf("%s/%d", d.Hash, d.Size)
}
func (d Digest) IsEmpty() bool {
	return d.Size == 0 && d.Hash == Empty.Hash
}
func (d Digest) Validate() error {
	length := len(d.Hash)
	if length != HashFn.Size()*2 {
		return fmt.Errorf("valid hash length is %d, got length %d (%s)", HashFn.Size()*2, length, d.Hash)
	}
	if !hexStringRegex.MatchString(d.Hash) {
		return fmt.Errorf("hash is not a lowercase hex string (%s)", d.Hash)
	}
	if d.Size < 0 {
		return fmt.Errorf("expected non-negative size, got %d", d.Size)
	}
	return nil
}
func New(hash string, size int64) (Digest, error) {
	d := Digest{Hash: hash, Size: size}
	return d, d.Validate()
}
func NewFromBlob(blob []byte) Digest {
	h := HashFn.New()
	h.Write(blob)
	arr := h.Sum(nil)
	return Digest{Hash: hex.EncodeToString(arr[:]), Size: int64(len(blob))}
}
func NewFromMessage(msg proto.Message) (Digest, error) {
	blob, err := proto.Marshal(msg)
	if err != nil {
		return Empty, err
	}
	return NewFromBlob(blob), nil
}
func NewFromProto(dg *repb.Digest) (Digest, error) {
	d := NewFromProtoUnvalidated(dg)
	return d, d.Validate()
}
func NewFromProtoUnvalidated(dg *repb.Digest) Digest {
	return Digest{Hash: dg.Hash, Size: dg.SizeBytes}
}
func NewFromString(s string) (Digest, error) {
	pair := strings.Split(s, "/")
	if len(pair) != 2 {
		return Empty, fmt.Errorf("expected digest in the form hash/size, got %s", s)
	}
	size, err := strconv.ParseInt(pair[1], 10, 64)
	if err != nil {
		return Empty, fmt.Errorf("invalid size in digest %s: %s", s, err)
	}
	return New(pair[0], size)
}