aboutsummaryrefslogtreecommitdiff
path: root/loose.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-02-20 19:06:13 +0800
committerGravatar Runxi Yu2026-02-20 19:07:14 +0800
commitaa513c069c1418734aea894dc944e27c6a78a3bb (patch)
tree687f0a11bb550fa088fd82a98ceb8979bbc35f69 /loose.go
parentComment 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.go209
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
-}