diff options
| author | 2026-02-20 19:06:13 +0800 | |
|---|---|---|
| committer | 2026-02-20 19:07:14 +0800 | |
| commit | aa513c069c1418734aea894dc944e27c6a78a3bb (patch) | |
| tree | 687f0a11bb550fa088fd82a98ceb8979bbc35f69 /loose.go | |
| parent | Comment on prior reverts removing the pack writing API (diff) | |
Delete everything, I'm redesigning this.
I'll stop using a flat package and make things much more modular.
And also experiment with streaming APIs so large blobs don't OOM us.
Diffstat (limited to 'loose.go')
| -rw-r--r-- | loose.go | 209 |
1 files changed, 0 insertions, 209 deletions
diff --git a/loose.go b/loose.go deleted file mode 100644 index 89779a93..00000000 --- a/loose.go +++ /dev/null @@ -1,209 +0,0 @@ -package furgit - -import ( - "bytes" - "fmt" - "io" - "os" - "path/filepath" - "strconv" - - "codeberg.org/lindenii/furgit/internal/bufpool" - "codeberg.org/lindenii/furgit/internal/zlib" - "codeberg.org/lindenii/furgit/internal/zlibx" -) - -const looseHeaderLimit = 4096 - -// loosePath returns the path for a loose object, validating hash size. -func (repo *Repository) loosePath(id Hash) (string, error) { - if id.algo != repo.hashAlgo { - return "", fmt.Errorf("furgit: hash algorithm mismatch: got %s, expected %s", id.algo.String(), repo.hashAlgo.String()) - } - hex := id.String() - return filepath.Join("objects", hex[:2], hex[2:]), nil -} - -func (repo *Repository) looseRead(id Hash) (ObjectType, bufpool.Buffer, error) { - ty, body, err := repo.looseReadTyped(id) - if err != nil { - return ObjectTypeInvalid, bufpool.Buffer{}, err - } - return ty, body, nil -} - -func (repo *Repository) looseReadTyped(id Hash) (ObjectType, bufpool.Buffer, error) { - path, err := repo.loosePath(id) - if err != nil { - return ObjectTypeInvalid, bufpool.Buffer{}, err - } - path = repo.repoPath(path) - f, err := os.Open(path) - if err != nil { - if os.IsNotExist(err) { - return ObjectTypeInvalid, bufpool.Buffer{}, ErrNotFound - } - return ObjectTypeInvalid, bufpool.Buffer{}, err - } - defer func() { _ = f.Close() }() - - compressed, err := io.ReadAll(f) - if err != nil { - return ObjectTypeInvalid, bufpool.Buffer{}, err - } - - raw, err := zlibx.Decompress(compressed) - if err != nil { - return ObjectTypeInvalid, bufpool.Buffer{}, err - } - - rawBytes := raw.Bytes() - nul := bytes.IndexByte(rawBytes, 0) - if nul < 0 { - raw.Release() - return ObjectTypeInvalid, bufpool.Buffer{}, ErrInvalidObject - } - - header := rawBytes[:nul] - body := rawBytes[nul+1:] - - ty, declaredSize, err := parseLooseHeader(header) - if err != nil { - raw.Release() - return ObjectTypeInvalid, bufpool.Buffer{}, err - } - if declaredSize != int64(len(body)) { - raw.Release() - return ObjectTypeInvalid, bufpool.Buffer{}, ErrInvalidObject - } - - copy(rawBytes, body) - raw.Resize(len(body)) - - return ty, raw, nil -} - -func (repo *Repository) looseTypeSize(id Hash) (ObjectType, int64, error) { - path, err := repo.loosePath(id) - if err != nil { - return ObjectTypeInvalid, 0, err - } - path = repo.repoPath(path) - // #nosec G304 - f, err := os.Open(path) - if err != nil { - if os.IsNotExist(err) { - return ObjectTypeInvalid, 0, ErrNotFound - } - return ObjectTypeInvalid, 0, err - } - defer func() { _ = f.Close() }() - - zr, err := zlib.NewReader(f) - if err != nil { - return ObjectTypeInvalid, 0, err - } - defer func() { _ = zr.Close() }() - - header := make([]byte, 0, 64) - chunk := make([]byte, 128) - for { - n, readErr := zr.Read(chunk) - if n > 0 { - data := chunk[:n] - if nul := bytes.IndexByte(data, 0); nul >= 0 { - header = append(header, data[:nul]...) - if len(header) > looseHeaderLimit { - return ObjectTypeInvalid, 0, ErrInvalidObject - } - break - } - header = append(header, data...) - if len(header) > looseHeaderLimit { - return ObjectTypeInvalid, 0, ErrInvalidObject - } - } - if readErr != nil { - if readErr == io.EOF { - return ObjectTypeInvalid, 0, ErrInvalidObject - } - return ObjectTypeInvalid, 0, readErr - } - } - return parseLooseHeader(header) -} - -func parseLooseHeader(header []byte) (ObjectType, int64, error) { - space := bytes.IndexByte(header, ' ') - if space < 0 { - return ObjectTypeInvalid, 0, ErrInvalidObject - } - ty, err := objTypeFromName(string(header[:space])) - if err != nil { - return ObjectTypeInvalid, 0, err - } - expect := header[space+1:] - if len(expect) == 0 { - return ObjectTypeInvalid, 0, ErrInvalidObject - } - size, err := strconv.ParseInt(string(expect), 10, 64) - if err != nil { - return ObjectTypeInvalid, 0, fmt.Errorf("furgit: loose: size parse: %w", err) - } - if size < 0 { - return ObjectTypeInvalid, 0, ErrInvalidObject - } - return ty, size, nil -} - -func objTypeFromName(name string) (ObjectType, error) { - switch name { - case objectTypeNameBlob: - return ObjectTypeBlob, nil - case objectTypeNameTree: - return ObjectTypeTree, nil - case objectTypeNameCommit: - return ObjectTypeCommit, nil - case objectTypeNameTag: - return ObjectTypeTag, nil - default: - return ObjectTypeInvalid, ErrInvalidObject - } -} - -// WriteLooseObject writes an object to the repository as a loose object. -func (repo *Repository) WriteLooseObject(obj Object) (Hash, error) { - if obj == nil { - return Hash{}, ErrInvalidObject - } - raw, err := obj.Serialize() - if err != nil { - return Hash{}, err - } - - id := repo.computeRawHash(raw) - path, err := repo.loosePath(id) - if err != nil { - return Hash{}, err - } - path = repo.repoPath(path) - - if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { - return Hash{}, err - } - - var buf bytes.Buffer - zw := zlib.NewWriter(&buf) - if _, err := zw.Write(raw); err != nil { - return Hash{}, err - } - if err := zw.Close(); err != nil { - return Hash{}, err - } - - if err := os.WriteFile(path, buf.Bytes(), 0o644); err != nil { - return Hash{}, err - } - - return id, nil -} |
