code.gitea.io/gitea@v1.19.3/modules/git/blob_nogogit.go (about)

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