This repository has no description
1// Copyright 2021 The Gitea Authors. All rights reserved.
2// SPDX-License-Identifier: MIT
3
4package gitea
5
6import (
7 "bufio"
8 "bytes"
9 "context"
10 "io"
11
12 "github.com/go-git/go-git/v5/plumbing"
13 "github.com/go-git/go-git/v5/plumbing/filemode"
14 "github.com/go-git/go-git/v5/plumbing/object"
15 "github.com/samber/lo"
16)
17
18func GetBlobSize(ctx context.Context, repoPath string, hash plumbing.Hash) (int64, error) {
19 wr, rd, cancel := CatFileBatchCheck(ctx, repoPath)
20 defer cancel()
21 if _, err := wr.Write([]byte(hash.String() + "\n")); err != nil {
22 return 0, err
23 }
24 _, _, size, err := ReadBatchLine(rd)
25 return size, err
26}
27
28func EntrySizes(ctx context.Context, repoPath string, entries []object.TreeEntry) ([]int64, error) {
29 sizes := make([]int64, len(entries))
30 blobs := lo.Filter(lo.Range(len(entries)), func(i int, _ int) bool {
31 return isBlobMode(entries[i].Mode)
32 })
33 if len(blobs) == 0 {
34 return sizes, nil
35 }
36 wr, rd, cancel := CatFileBatchCheck(ctx, repoPath)
37 defer cancel()
38 for _, i := range blobs {
39 if _, err := wr.Write([]byte(entries[i].Hash.String() + "\n")); err != nil {
40 return sizes, err
41 }
42 _, typ, size, err := ReadBatchLine(rd)
43 if err != nil {
44 return sizes, err
45 }
46 if typ == "blob" {
47 sizes[i] = size
48 }
49 }
50 return sizes, nil
51}
52
53func isBlobMode(mode filemode.FileMode) bool {
54 return mode == filemode.Regular || mode == filemode.Executable || mode == filemode.Symlink
55}
56
57// ReadBlob returns blob size and [io.ReadCloser] of that blob.
58func ReadBlob(ctx context.Context, repoPath string, hash plumbing.Hash) (int64, io.ReadCloser, error) {
59 wr, rd, cancel := CatFileBatch(ctx, repoPath)
60
61 _, err := wr.Write([]byte(hash.String() + "\n"))
62 if err != nil {
63 cancel()
64 return 0, nil, err
65 }
66 _, _, size, err := ReadBatchLine(rd)
67 if err != nil {
68 cancel()
69 return 0, nil, err
70 }
71
72 if size < 4096 {
73 bs, err := io.ReadAll(io.LimitReader(rd, size))
74 defer cancel()
75 if err != nil {
76 return 0, nil, err
77 }
78 _, err = rd.Discard(1)
79 return size, io.NopCloser(bytes.NewReader(bs)), err
80 }
81
82 return size, &blobReader{
83 rd: rd,
84 n: size,
85 cancel: cancel,
86 }, nil
87}
88
89type blobReader struct {
90 rd *bufio.Reader
91 n int64
92 cancel func()
93}
94
95func (b *blobReader) Read(p []byte) (n int, err error) {
96 if b.n <= 0 {
97 return 0, io.EOF
98 }
99 if int64(len(p)) > b.n {
100 p = p[0:b.n]
101 }
102 n, err = b.rd.Read(p)
103 b.n -= int64(n)
104 return n, err
105}
106
107// Close implements io.Closer
108func (b *blobReader) Close() error {
109 if b.rd == nil {
110 return nil
111 }
112
113 defer b.cancel()
114
115 if err := DiscardFull(b.rd, b.n+1); err != nil {
116 return err
117 }
118
119 b.rd = nil
120
121 return nil
122}