github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/git/blob_nogogit.go (about)

     1  // Copyright 2023 The GitBundle Inc. All rights reserved.
     2  // Copyright 2017 The Gitea Authors. All rights reserved.
     3  // Use of this source code is governed by a MIT-style
     4  // license that can be found in the LICENSE file.
     5  
     6  //go:build !gogit
     7  
     8  package git
     9  
    10  import (
    11  	"bufio"
    12  	"bytes"
    13  	"io"
    14  	"math"
    15  
    16  	"github.com/gitbundle/modules/log"
    17  )
    18  
    19  // Blob represents a Git object.
    20  type Blob struct {
    21  	ID SHA1
    22  
    23  	gotSize bool
    24  	size    int64
    25  	name    string
    26  	repo    *Repository
    27  }
    28  
    29  // DataAsync gets a ReadCloser for the contents of a blob without reading it all.
    30  // Calling the Close function on the result will discard all unread output.
    31  func (b *Blob) DataAsync() (io.ReadCloser, error) {
    32  	wr, rd, cancel := b.repo.CatFileBatch(b.repo.Ctx)
    33  
    34  	_, err := wr.Write([]byte(b.ID.String() + "\n"))
    35  	if err != nil {
    36  		cancel()
    37  		return nil, err
    38  	}
    39  	_, _, size, err := ReadBatchLine(rd)
    40  	if err != nil {
    41  		cancel()
    42  		return nil, err
    43  	}
    44  	b.gotSize = true
    45  	b.size = size
    46  
    47  	if size < 4096 {
    48  		bs, err := io.ReadAll(io.LimitReader(rd, size))
    49  		defer cancel()
    50  		if err != nil {
    51  			return nil, err
    52  		}
    53  		_, err = rd.Discard(1)
    54  		return io.NopCloser(bytes.NewReader(bs)), err
    55  	}
    56  
    57  	return &blobReader{
    58  		rd:     rd,
    59  		n:      size,
    60  		cancel: cancel,
    61  	}, nil
    62  }
    63  
    64  // Size returns the uncompressed size of the blob
    65  func (b *Blob) Size() int64 {
    66  	if b.gotSize {
    67  		return b.size
    68  	}
    69  
    70  	wr, rd, cancel := b.repo.CatFileBatchCheck(b.repo.Ctx)
    71  	defer cancel()
    72  	_, err := wr.Write([]byte(b.ID.String() + "\n"))
    73  	if err != nil {
    74  		log.Debug("error whilst reading size for %s in %s. Error: %v", b.ID.String(), b.repo.Path, err)
    75  		return 0
    76  	}
    77  	_, _, b.size, err = ReadBatchLine(rd)
    78  	if err != nil {
    79  		log.Debug("error whilst reading size for %s in %s. Error: %v", b.ID.String(), b.repo.Path, err)
    80  		return 0
    81  	}
    82  
    83  	b.gotSize = true
    84  
    85  	return b.size
    86  }
    87  
    88  type blobReader struct {
    89  	rd     *bufio.Reader
    90  	n      int64
    91  	cancel func()
    92  }
    93  
    94  func (b *blobReader) Read(p []byte) (n int, err error) {
    95  	if b.n <= 0 {
    96  		return 0, io.EOF
    97  	}
    98  	if int64(len(p)) > b.n {
    99  		p = p[0:b.n]
   100  	}
   101  	n, err = b.rd.Read(p)
   102  	b.n -= int64(n)
   103  	return
   104  }
   105  
   106  // Close implements io.Closer
   107  func (b *blobReader) Close() error {
   108  	defer b.cancel()
   109  	if b.n > 0 {
   110  		for b.n > math.MaxInt32 {
   111  			n, err := b.rd.Discard(math.MaxInt32)
   112  			b.n -= int64(n)
   113  			if err != nil {
   114  				return err
   115  			}
   116  			b.n -= math.MaxInt32
   117  		}
   118  		n, err := b.rd.Discard(int(b.n))
   119  		b.n -= int64(n)
   120  		if err != nil {
   121  			return err
   122  		}
   123  	}
   124  	if b.n == 0 {
   125  		_, err := b.rd.Discard(1)
   126  		b.n--
   127  		return err
   128  	}
   129  	return nil
   130  }