Lindenii Project Forge
Login

server

Lindenii Forge’s main backend daemon
Commit info
ID
3d69c8607e8480d87bd0c89ccc3f97f79104d8fe
Author
Runxi Yu <me@runxiyu.org>
Author date
Sun, 17 Aug 2025 12:26:44 +0800
Committer
Runxi Yu <me@runxiyu.org>
Committer date
Sun, 17 Aug 2025 12:26:44 +0800
Actions
Fix some imports
version: "2"

linters:
  default: all
#  disable:
#    - depguard
#    - err113           # dynamically defined errors are fine for our purposes
#    - forcetypeassert  # type assertion failures are usually programming errors
#    - gochecknoinits   # we use inits sparingly for good reasons
#    - godox            # they're just used as markers for where needs improvements
#    - ireturn          # doesn't work well with how we use generics
#    - lll              # long lines are acceptable
#    - mnd              # it's a bit ridiculous to replace all of them
#    - nakedret         # patterns should be consistent
#    - nonamedreturns   # i like named returns
#    - wrapcheck        # wrapping all errors is just not necessary
#    - varnamelen       # "from" and "to" are very valid
#    - containedctx
#    - godot
#    - dogsled
#    - maintidx    # e
#    - nestif      # e
#    - gocognit    # e
#    - gocyclo     # e
#    - dupl        # e
#    - cyclop      # e
#    - goconst     # e
#    - funlen      # e
#    - wsl         # e
#    - nlreturn    # e
#    - unused      # e
#    - exhaustruct # e
# 
# linters-settings:
#   revive:
#     rules:
#       - name: error-strings
#         disabled: true
# 
# issues:
#   max-issues-per-linter: 0
#   max-same-issues: 0
  disable:
    - depguard
    - err113           # dynamically defined errors are fine for our purposes
    - forcetypeassert  # type assertion failures are usually programming errors
    - gochecknoinits   # we use inits sparingly for good reasons
    - godox            # they're just used as markers for where needs improvements
    - ireturn          # doesn't work well with how we use generics
    - lll              # long lines are acceptable
    - mnd              # it's a bit ridiculous to replace all of them
    - nakedret         # patterns should be consistent
    - nonamedreturns   # i like named returns
    - wrapcheck        # wrapping all errors is just not necessary
    - varnamelen       # "from" and "to" are very valid
    - containedctx
    - godot
    - dogsled
    - maintidx    # e
    - nestif      # e
    - gocognit    # e
    - gocyclo     # e
    - dupl        # e
    - cyclop      # e
    - goconst     # e
    - funlen      # e
    - wsl         # e
    - nlreturn    # e
    - unused      # e
    - exhaustruct # e

linters-settings:
  revive:
    rules:
      - name: error-strings
        disabled: true

issues:
  max-issues-per-linter: 0
  max-same-issues: 0
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright (c) 2025 Drew Devault <https://drewdevault.com>

package bare

import (
	"encoding/binary"
	"fmt"
	"io"
	"math"
	"unicode/utf8"

	"go.lindenii.runxiyu.org/forge/forged/internal/misc"
	"go.lindenii.runxiyu.org/forge/forged/internal/common/misc"
)

type byteReader interface {
	io.Reader
	io.ByteReader
}

// A Reader for BARE primitive types.
type Reader struct {
	base    byteReader
	scratch [8]byte
}

type simpleByteReader struct {
	io.Reader
	scratch [1]byte
}

func (r simpleByteReader) ReadByte() (byte, error) {
	// using reference type here saves us allocations
	_, err := r.Read(r.scratch[:])
	return r.scratch[0], err
}

// Returns a new BARE primitive reader wrapping the given io.Reader.
func NewReader(base io.Reader) *Reader {
	br, ok := base.(byteReader)
	if !ok {
		br = simpleByteReader{Reader: base}
	}
	return &Reader{base: br}
}

func (r *Reader) ReadUint() (uint64, error) {
	x, err := binary.ReadUvarint(r.base)
	if err != nil {
		return x, err
	}
	return x, nil
}

func (r *Reader) ReadU8() (uint8, error) {
	return r.base.ReadByte()
}

func (r *Reader) ReadU16() (uint16, error) {
	var i uint16
	if _, err := io.ReadAtLeast(r.base, r.scratch[:2], 2); err != nil {
		return i, err
	}
	return binary.LittleEndian.Uint16(r.scratch[:]), nil
}

func (r *Reader) ReadU32() (uint32, error) {
	var i uint32
	if _, err := io.ReadAtLeast(r.base, r.scratch[:4], 4); err != nil {
		return i, err
	}
	return binary.LittleEndian.Uint32(r.scratch[:]), nil
}

func (r *Reader) ReadU64() (uint64, error) {
	var i uint64
	if _, err := io.ReadAtLeast(r.base, r.scratch[:8], 8); err != nil {
		return i, err
	}
	return binary.LittleEndian.Uint64(r.scratch[:]), nil
}

func (r *Reader) ReadInt() (int64, error) {
	return binary.ReadVarint(r.base)
}

func (r *Reader) ReadI8() (int8, error) {
	b, err := r.base.ReadByte()
	return int8(b), err
}

func (r *Reader) ReadI16() (int16, error) {
	var i int16
	if _, err := io.ReadAtLeast(r.base, r.scratch[:2], 2); err != nil {
		return i, err
	}
	return int16(binary.LittleEndian.Uint16(r.scratch[:])), nil
}

func (r *Reader) ReadI32() (int32, error) {
	var i int32
	if _, err := io.ReadAtLeast(r.base, r.scratch[:4], 4); err != nil {
		return i, err
	}
	return int32(binary.LittleEndian.Uint32(r.scratch[:])), nil
}

func (r *Reader) ReadI64() (int64, error) {
	var i int64
	if _, err := io.ReadAtLeast(r.base, r.scratch[:], 8); err != nil {
		return i, err
	}
	return int64(binary.LittleEndian.Uint64(r.scratch[:])), nil
}

func (r *Reader) ReadF32() (float32, error) {
	u, err := r.ReadU32()
	f := math.Float32frombits(u)
	if math.IsNaN(float64(f)) {
		return 0.0, fmt.Errorf("NaN is not permitted in BARE floats")
	}
	return f, err
}

func (r *Reader) ReadF64() (float64, error) {
	u, err := r.ReadU64()
	f := math.Float64frombits(u)
	if math.IsNaN(f) {
		return 0.0, fmt.Errorf("NaN is not permitted in BARE floats")
	}
	return f, err
}

func (r *Reader) ReadBool() (bool, error) {
	b, err := r.ReadU8()
	if err != nil {
		return false, err
	}

	if b > 1 {
		return false, fmt.Errorf("Invalid bool value: %#x", b)
	}

	return b == 1, nil
}

func (r *Reader) ReadString() (string, error) {
	buf, err := r.ReadData()
	if err != nil {
		return "", err
	}
	if !utf8.Valid(buf) {
		return "", ErrInvalidStr
	}
	return misc.BytesToString(buf), nil
}

// Reads a fixed amount of arbitrary data, defined by the length of the slice.
func (r *Reader) ReadDataFixed(dest []byte) error {
	var amt int = 0
	for amt < len(dest) {
		n, err := r.base.Read(dest[amt:])
		if err != nil {
			return err
		}
		amt += n
	}
	return nil
}

// Reads arbitrary data whose length is read from the message.
func (r *Reader) ReadData() ([]byte, error) {
	l, err := r.ReadUint()
	if err != nil {
		return nil, err
	}
	if l >= maxUnmarshalBytes {
		return nil, ErrLimitExceeded
	}
	buf := make([]byte, l)
	var amt uint64 = 0
	for amt < l {
		n, err := r.base.Read(buf[amt:])
		if err != nil {
			return nil, err
		}
		amt += uint64(n)
	}
	return buf, nil
}
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright (c) 2025 Drew Devault <https://drewdevault.com>

package bare

import (
	"encoding/binary"
	"fmt"
	"io"
	"math"

	"go.lindenii.runxiyu.org/forge/forged/internal/misc"
	"go.lindenii.runxiyu.org/forge/forged/internal/common/misc"
)

// A Writer for BARE primitive types.
type Writer struct {
	base    io.Writer
	scratch [binary.MaxVarintLen64]byte
}

// Returns a new BARE primitive writer wrapping the given io.Writer.
func NewWriter(base io.Writer) *Writer {
	return &Writer{base: base}
}

func (w *Writer) WriteUint(i uint64) error {
	n := binary.PutUvarint(w.scratch[:], i)
	_, err := w.base.Write(w.scratch[:n])
	return err
}

func (w *Writer) WriteU8(i uint8) error {
	return binary.Write(w.base, binary.LittleEndian, i)
}

func (w *Writer) WriteU16(i uint16) error {
	return binary.Write(w.base, binary.LittleEndian, i)
}

func (w *Writer) WriteU32(i uint32) error {
	return binary.Write(w.base, binary.LittleEndian, i)
}

func (w *Writer) WriteU64(i uint64) error {
	return binary.Write(w.base, binary.LittleEndian, i)
}

func (w *Writer) WriteInt(i int64) error {
	var buf [binary.MaxVarintLen64]byte
	n := binary.PutVarint(buf[:], i)
	_, err := w.base.Write(buf[:n])
	return err
}

func (w *Writer) WriteI8(i int8) error {
	return binary.Write(w.base, binary.LittleEndian, i)
}

func (w *Writer) WriteI16(i int16) error {
	return binary.Write(w.base, binary.LittleEndian, i)
}

func (w *Writer) WriteI32(i int32) error {
	return binary.Write(w.base, binary.LittleEndian, i)
}

func (w *Writer) WriteI64(i int64) error {
	return binary.Write(w.base, binary.LittleEndian, i)
}

func (w *Writer) WriteF32(f float32) error {
	if math.IsNaN(float64(f)) {
		return fmt.Errorf("NaN is not permitted in BARE floats")
	}
	return binary.Write(w.base, binary.LittleEndian, f)
}

func (w *Writer) WriteF64(f float64) error {
	if math.IsNaN(f) {
		return fmt.Errorf("NaN is not permitted in BARE floats")
	}
	return binary.Write(w.base, binary.LittleEndian, f)
}

func (w *Writer) WriteBool(b bool) error {
	return binary.Write(w.base, binary.LittleEndian, b)
}

func (w *Writer) WriteString(str string) error {
	return w.WriteData(misc.StringToBytes(str))
}

// Writes a fixed amount of arbitrary data, defined by the length of the slice.
func (w *Writer) WriteDataFixed(data []byte) error {
	var amt int = 0
	for amt < len(data) {
		n, err := w.base.Write(data[amt:])
		if err != nil {
			return err
		}
		amt += n
	}
	return nil
}

// Writes arbitrary data whose length is encoded into the message.
func (w *Writer) WriteData(data []byte) error {
	err := w.WriteUint(uint64(len(data)))
	if err != nil {
		return err
	}
	var amt int = 0
	for amt < len(data) {
		n, err := w.base.Write(data[amt:])
		if err != nil {
			return err
		}
		amt += n
	}
	return nil
}
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>

package git2c

import (
	"fmt"
	"net"

	"go.lindenii.runxiyu.org/forge/forged/internal/bare"
	"go.lindenii.runxiyu.org/forge/forged/internal/common/bare"
)

// Client represents a connection to the git2d backend daemon.
type Client struct {
	socketPath string
	conn       net.Conn
	writer     *bare.Writer
	reader     *bare.Reader
}

// NewClient establishes a connection to a git2d socket and returns a new Client.
func NewClient(socketPath string) (*Client, error) {
	conn, err := net.Dial("unix", socketPath)
	if err != nil {
		return nil, fmt.Errorf("git2d connection failed: %w", err)
	}

	writer := bare.NewWriter(conn)
	reader := bare.NewReader(conn)

	return &Client{
		socketPath: socketPath,
		conn:       conn,
		writer:     writer,
		reader:     reader,
	}, nil
}

// Close terminates the underlying socket connection.
func (c *Client) Close() error {
	if c.conn != nil {
		return c.conn.Close()
	}
	return nil
}