Lindenii Project Forge
Login

server

Lindenii Forge’s main backend daemon
Commit info
ID
1b2e3c0cd0ca1bdf4cad031a0132ae6e802c2c95
Author
Runxi Yu <me@runxiyu.org>
Author date
Sun, 06 Apr 2025 10:13:50 +0800
Committer
Runxi Yu <me@runxiyu.org>
Committer date
Sun, 06 Apr 2025 10:13:50 +0800
Actions
Import BARE
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright (c) 2025 Drew Devault <https://drewdevault.com>

package bare

import (
	"errors"
	"fmt"
	"reflect"
)

var ErrInvalidStr = errors.New("String contains invalid UTF-8 sequences")

type UnsupportedTypeError struct {
	Type reflect.Type
}

func (e *UnsupportedTypeError) Error() string {
	return fmt.Sprintf("Unsupported type for marshaling: %s\n", e.Type.String())
}
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright (c) 2025 Drew Devault <https://drewdevault.com>

package bare

import (
	"errors"
	"io"
)

var (
	maxUnmarshalBytes uint64 = 1024 * 1024 * 32 /* 32 MiB */
	maxArrayLength    uint64 = 1024 * 4         /* 4096 elements */
	maxMapSize        uint64 = 1024
)

// MaxUnmarshalBytes sets the maximum size of a message decoded by unmarshal.
// By default, this is set to 32 MiB.
func MaxUnmarshalBytes(bytes uint64) {
	maxUnmarshalBytes = bytes
}

// MaxArrayLength sets maximum number of elements in array. Defaults to 4096 elements
func MaxArrayLength(length uint64) {
	maxArrayLength = length
}

// MaxMapSize sets maximum size of map. Defaults to 1024 key/value pairs
func MaxMapSize(size uint64) {
	maxMapSize = size
}

// Use MaxUnmarshalBytes to prevent this error from occuring on messages which
// are large by design.
var ErrLimitExceeded = errors.New("Maximum message size exceeded")

// Identical to io.LimitedReader, except it returns our custom error instead of
// EOF if the limit is reached.
type limitedReader struct {
	R io.Reader
	N uint64
}

func (l *limitedReader) Read(p []byte) (n int, err error) {
	if l.N <= 0 {
		return 0, ErrLimitExceeded
	}
	if uint64(len(p)) > l.N {
		p = p[0:l.N]
	}
	n, err = l.R.Read(p)
	l.N -= uint64(n)
	return
}

func newLimitedReader(r io.Reader) *limitedReader {
	return &limitedReader{r, maxUnmarshalBytes}
}
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright (c) 2025 Drew Devault <https://drewdevault.com>

package bare

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"sync"
)

// A type which implements this interface will be responsible for marshaling
// itself when encountered.
type Marshalable interface {
	Marshal(w *Writer) error
}

var encoderBufferPool = sync.Pool{
	New: func() interface{} {
		buf := &bytes.Buffer{}
		buf.Grow(32)
		return buf
	},
}

// Marshals a value (val, which must be a pointer) into a BARE message.
//
// The encoding of each struct field can be customized by the format string
// stored under the "bare" key in the struct field's tag.
//
// As a special case, if the field tag is "-", the field is always omitted.
func Marshal(val interface{}) ([]byte, error) {
	// reuse buffers from previous serializations
	b := encoderBufferPool.Get().(*bytes.Buffer)
	defer func() {
		b.Reset()
		encoderBufferPool.Put(b)
	}()

	w := NewWriter(b)
	err := MarshalWriter(w, val)

	msg := make([]byte, b.Len())
	copy(msg, b.Bytes())

	return msg, err
}

// Marshals a value (val, which must be a pointer) into a BARE message and
// writes it to a Writer. See Marshal for details.
func MarshalWriter(w *Writer, val interface{}) error {
	t := reflect.TypeOf(val)
	v := reflect.ValueOf(val)
	if t.Kind() != reflect.Ptr {
		return errors.New("Expected val to be pointer type")
	}

	return getEncoder(t.Elem())(w, v.Elem())
}

type encodeFunc func(w *Writer, v reflect.Value) error

var encodeFuncCache sync.Map // map[reflect.Type]encodeFunc

// get decoder from cache
func getEncoder(t reflect.Type) encodeFunc {
	if f, ok := encodeFuncCache.Load(t); ok {
		return f.(encodeFunc)
	}

	f := encoderFunc(t)
	encodeFuncCache.Store(t, f)
	return f
}

var marshalableInterface = reflect.TypeOf((*Unmarshalable)(nil)).Elem()

func encoderFunc(t reflect.Type) encodeFunc {
	if reflect.PtrTo(t).Implements(marshalableInterface) {
		return func(w *Writer, v reflect.Value) error {
			uv := v.Addr().Interface().(Marshalable)
			return uv.Marshal(w)
		}
	}

	if t.Kind() == reflect.Interface && t.Implements(unionInterface) {
		return encodeUnion(t)
	}

	switch t.Kind() {
	case reflect.Ptr:
		return encodeOptional(t.Elem())
	case reflect.Struct:
		return encodeStruct(t)
	case reflect.Array:
		return encodeArray(t)
	case reflect.Slice:
		return encodeSlice(t)
	case reflect.Map:
		return encodeMap(t)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return encodeUint
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return encodeInt
	case reflect.Float32, reflect.Float64:
		return encodeFloat
	case reflect.Bool:
		return encodeBool
	case reflect.String:
		return encodeString
	}

	return func(w *Writer, v reflect.Value) error {
		return &UnsupportedTypeError{v.Type()}
	}
}

func encodeOptional(t reflect.Type) encodeFunc {
	return func(w *Writer, v reflect.Value) error {
		if v.IsNil() {
			return w.WriteBool(false)
		}

		if err := w.WriteBool(true); err != nil {
			return err
		}

		return getEncoder(t)(w, v.Elem())
	}
}

func encodeStruct(t reflect.Type) encodeFunc {
	n := t.NumField()
	encoders := make([]encodeFunc, n)
	for i := 0; i < n; i++ {
		field := t.Field(i)
		if field.Tag.Get("bare") == "-" {
			continue
		}
		encoders[i] = getEncoder(field.Type)
	}

	return func(w *Writer, v reflect.Value) error {
		for i := 0; i < n; i++ {
			if encoders[i] == nil {
				continue
			}
			err := encoders[i](w, v.Field(i))
			if err != nil {
				return err
			}
		}
		return nil
	}
}

func encodeArray(t reflect.Type) encodeFunc {
	f := getEncoder(t.Elem())
	len := t.Len()

	return func(w *Writer, v reflect.Value) error {
		for i := 0; i < len; i++ {
			if err := f(w, v.Index(i)); err != nil {
				return err
			}
		}
		return nil
	}
}

func encodeSlice(t reflect.Type) encodeFunc {
	elem := t.Elem()
	f := getEncoder(elem)

	return func(w *Writer, v reflect.Value) error {
		if err := w.WriteUint(uint64(v.Len())); err != nil {
			return err
		}

		for i := 0; i < v.Len(); i++ {
			if err := f(w, v.Index(i)); err != nil {
				return err
			}
		}
		return nil
	}
}

func encodeMap(t reflect.Type) encodeFunc {
	keyType := t.Key()
	keyf := getEncoder(keyType)

	valueType := t.Elem()
	valf := getEncoder(valueType)

	return func(w *Writer, v reflect.Value) error {
		if err := w.WriteUint(uint64(v.Len())); err != nil {
			return err
		}

		iter := v.MapRange()
		for iter.Next() {
			if err := keyf(w, iter.Key()); err != nil {
				return err
			}
			if err := valf(w, iter.Value()); err != nil {
				return err
			}
		}
		return nil
	}
}

func encodeUnion(t reflect.Type) encodeFunc {
	ut, ok := unionRegistry[t]
	if !ok {
		return func(w *Writer, v reflect.Value) error {
			return fmt.Errorf("Union type %s is not registered", t.Name())
		}
	}

	encoders := make(map[uint64]encodeFunc)
	for tag, t := range ut.types {
		encoders[tag] = getEncoder(t)
	}

	return func(w *Writer, v reflect.Value) error {
		t := v.Elem().Type()
		if t.Kind() == reflect.Ptr {
			// If T is a valid union value type, *T is valid too.
			t = t.Elem()
			v = v.Elem()
		}
		tag, ok := ut.tags[t]
		if !ok {
			return fmt.Errorf("Invalid union value: %s", v.Elem().String())
		}

		if err := w.WriteUint(tag); err != nil {
			return err
		}

		return encoders[tag](w, v.Elem())
	}
}

func encodeUint(w *Writer, v reflect.Value) error {
	switch getIntKind(v.Type()) {
	case reflect.Uint:
		return w.WriteUint(v.Uint())

	case reflect.Uint8:
		return w.WriteU8(uint8(v.Uint()))

	case reflect.Uint16:
		return w.WriteU16(uint16(v.Uint()))

	case reflect.Uint32:
		return w.WriteU32(uint32(v.Uint()))

	case reflect.Uint64:
		return w.WriteU64(uint64(v.Uint()))
	}

	panic("not uint")
}

func encodeInt(w *Writer, v reflect.Value) error {
	switch getIntKind(v.Type()) {
	case reflect.Int:
		return w.WriteInt(v.Int())

	case reflect.Int8:
		return w.WriteI8(int8(v.Int()))

	case reflect.Int16:
		return w.WriteI16(int16(v.Int()))

	case reflect.Int32:
		return w.WriteI32(int32(v.Int()))

	case reflect.Int64:
		return w.WriteI64(int64(v.Int()))
	}

	panic("not int")
}

func encodeFloat(w *Writer, v reflect.Value) error {
	switch v.Type().Kind() {
	case reflect.Float32:
		return w.WriteF32(float32(v.Float()))
	case reflect.Float64:
		return w.WriteF64(v.Float())
	}

	panic("not float")
}

func encodeBool(w *Writer, v reflect.Value) error {
	return w.WriteBool(v.Bool())
}

func encodeString(w *Writer, v reflect.Value) error {
	if v.Kind() != reflect.String {
		panic("not string")
	}
	return w.WriteString(v.String())
}
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>

// Package bare provides primitives to encode and decode BARE messages.
//
// There is no guarantee that this is compatible with the upstream
// implementation at https://git.sr.ht/~sircmpwn/go-bare.
package bare
// 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"
)

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 string(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 (
	"fmt"
	"reflect"
)

// Any type which is a union member must implement this interface. You must
// also call RegisterUnion for go-bare to marshal or unmarshal messages which
// utilize your union type.
type Union interface {
	IsUnion()
}

type UnionTags struct {
	iface reflect.Type
	tags  map[reflect.Type]uint64
	types map[uint64]reflect.Type
}

var unionInterface = reflect.TypeOf((*Union)(nil)).Elem()
var unionRegistry map[reflect.Type]*UnionTags

func init() {
	unionRegistry = make(map[reflect.Type]*UnionTags)
}

// Registers a union type in this context. Pass the union interface and the
// list of types associated with it, sorted ascending by their union tag.
func RegisterUnion(iface interface{}) *UnionTags {
	ity := reflect.TypeOf(iface).Elem()
	if _, ok := unionRegistry[ity]; ok {
		panic(fmt.Errorf("Type %s has already been registered", ity.Name()))
	}

	if !ity.Implements(reflect.TypeOf((*Union)(nil)).Elem()) {
		panic(fmt.Errorf("Type %s does not implement bare.Union", ity.Name()))
	}

	utypes := &UnionTags{
		iface: ity,
		tags:  make(map[reflect.Type]uint64),
		types: make(map[uint64]reflect.Type),
	}
	unionRegistry[ity] = utypes
	return utypes
}

func (ut *UnionTags) Member(t interface{}, tag uint64) *UnionTags {
	ty := reflect.TypeOf(t)
	if !ty.AssignableTo(ut.iface) {
		panic(fmt.Errorf("Type %s does not implement interface %s",
			ty.Name(), ut.iface.Name()))
	}
	if _, ok := ut.tags[ty]; ok {
		panic(fmt.Errorf("Type %s is already registered for union %s",
			ty.Name(), ut.iface.Name()))
	}
	if _, ok := ut.types[tag]; ok {
		panic(fmt.Errorf("Tag %d is already registered for union %s",
			tag, ut.iface.Name()))
	}
	ut.tags[ty] = tag
	ut.types[tag] = ty
	return ut
}

func (ut *UnionTags) TagFor(v interface{}) (uint64, bool) {
	tag, ok := ut.tags[reflect.TypeOf(v)]
	return tag, ok
}

func (ut *UnionTags) TypeFor(tag uint64) (reflect.Type, bool) {
	t, ok := ut.types[tag]
	return t, ok
}
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright (c) 2025 Drew Devault <https://drewdevault.com>

package bare

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"reflect"
	"sync"
)

// A type which implements this interface will be responsible for unmarshaling
// itself when encountered.
type Unmarshalable interface {
	Unmarshal(r *Reader) error
}

// Unmarshals a BARE message into val, which must be a pointer to a value of
// the message type.
func Unmarshal(data []byte, val interface{}) error {
	b := bytes.NewReader(data)
	r := NewReader(b)
	return UnmarshalBareReader(r, val)
}

// Unmarshals a BARE message into value (val, which must be a pointer), from a
// reader. See Unmarshal for details.
func UnmarshalReader(r io.Reader, val interface{}) error {
	r = newLimitedReader(r)
	return UnmarshalBareReader(NewReader(r), val)
}

type decodeFunc func(r *Reader, v reflect.Value) error

var decodeFuncCache sync.Map // map[reflect.Type]decodeFunc

func UnmarshalBareReader(r *Reader, val interface{}) error {
	t := reflect.TypeOf(val)
	v := reflect.ValueOf(val)
	if t.Kind() != reflect.Ptr {
		return errors.New("Expected val to be pointer type")
	}

	return getDecoder(t.Elem())(r, v.Elem())
}

// get decoder from cache
func getDecoder(t reflect.Type) decodeFunc {
	if f, ok := decodeFuncCache.Load(t); ok {
		return f.(decodeFunc)
	}

	f := decoderFunc(t)
	decodeFuncCache.Store(t, f)
	return f
}

var unmarshalableInterface = reflect.TypeOf((*Unmarshalable)(nil)).Elem()

func decoderFunc(t reflect.Type) decodeFunc {
	if reflect.PtrTo(t).Implements(unmarshalableInterface) {
		return func(r *Reader, v reflect.Value) error {
			uv := v.Addr().Interface().(Unmarshalable)
			return uv.Unmarshal(r)
		}
	}

	if t.Kind() == reflect.Interface && t.Implements(unionInterface) {
		return decodeUnion(t)
	}

	switch t.Kind() {
	case reflect.Ptr:
		return decodeOptional(t.Elem())
	case reflect.Struct:
		return decodeStruct(t)
	case reflect.Array:
		return decodeArray(t)
	case reflect.Slice:
		return decodeSlice(t)
	case reflect.Map:
		return decodeMap(t)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return decodeUint
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return decodeInt
	case reflect.Float32, reflect.Float64:
		return decodeFloat
	case reflect.Bool:
		return decodeBool
	case reflect.String:
		return decodeString
	}

	return func(r *Reader, v reflect.Value) error {
		return &UnsupportedTypeError{v.Type()}
	}
}

func decodeOptional(t reflect.Type) decodeFunc {
	return func(r *Reader, v reflect.Value) error {
		s, err := r.ReadU8()
		if err != nil {
			return err
		}

		if s > 1 {
			return fmt.Errorf("Invalid optional value: %#x", s)
		}

		if s == 0 {
			return nil
		}

		v.Set(reflect.New(t))
		return getDecoder(t)(r, v.Elem())
	}
}

func decodeStruct(t reflect.Type) decodeFunc {
	n := t.NumField()
	decoders := make([]decodeFunc, n)
	for i := 0; i < n; i++ {
		field := t.Field(i)
		if field.Tag.Get("bare") == "-" {
			continue
		}
		decoders[i] = getDecoder(field.Type)
	}

	return func(r *Reader, v reflect.Value) error {
		for i := 0; i < n; i++ {
			if decoders[i] == nil {
				continue
			}
			err := decoders[i](r, v.Field(i))
			if err != nil {
				return err
			}
		}
		return nil
	}
}

func decodeArray(t reflect.Type) decodeFunc {
	f := getDecoder(t.Elem())
	len := t.Len()

	return func(r *Reader, v reflect.Value) error {
		for i := 0; i < len; i++ {
			err := f(r, v.Index(i))
			if err != nil {
				return err
			}
		}
		return nil
	}
}

func decodeSlice(t reflect.Type) decodeFunc {
	elem := t.Elem()
	f := getDecoder(elem)

	return func(r *Reader, v reflect.Value) error {
		len, err := r.ReadUint()
		if err != nil {
			return err
		}

		if len > maxArrayLength {
			return fmt.Errorf("Array length %d exceeds configured limit of %d", len, maxArrayLength)
		}

		v.Set(reflect.MakeSlice(t, int(len), int(len)))

		for i := 0; i < int(len); i++ {
			if err := f(r, v.Index(i)); err != nil {
				return err
			}
		}
		return nil
	}
}

func decodeMap(t reflect.Type) decodeFunc {
	keyType := t.Key()
	keyf := getDecoder(keyType)

	valueType := t.Elem()
	valf := getDecoder(valueType)

	return func(r *Reader, v reflect.Value) error {
		size, err := r.ReadUint()
		if err != nil {
			return err
		}

		if size > maxMapSize {
			return fmt.Errorf("Map size %d exceeds configured limit of %d", size, maxMapSize)
		}

		v.Set(reflect.MakeMapWithSize(t, int(size)))

		key := reflect.New(keyType).Elem()
		value := reflect.New(valueType).Elem()

		for i := uint64(0); i < size; i++ {
			if err := keyf(r, key); err != nil {
				return err
			}

			if v.MapIndex(key).Kind() > reflect.Invalid {
				return fmt.Errorf("Encountered duplicate map key: %v", key.Interface())
			}

			if err := valf(r, value); err != nil {
				return err
			}

			v.SetMapIndex(key, value)
		}
		return nil
	}
}

func decodeUnion(t reflect.Type) decodeFunc {
	ut, ok := unionRegistry[t]
	if !ok {
		return func(r *Reader, v reflect.Value) error {
			return fmt.Errorf("Union type %s is not registered", t.Name())
		}
	}

	decoders := make(map[uint64]decodeFunc)
	for tag, t := range ut.types {
		t := t
		f := getDecoder(t)

		decoders[tag] = func(r *Reader, v reflect.Value) error {
			nv := reflect.New(t)
			if err := f(r, nv.Elem()); err != nil {
				return err
			}

			v.Set(nv)
			return nil
		}
	}

	return func(r *Reader, v reflect.Value) error {
		tag, err := r.ReadUint()
		if err != nil {
			return err
		}

		if f, ok := decoders[tag]; ok {
			return f(r, v)
		}

		return fmt.Errorf("Invalid union tag %d for type %s", tag, t.Name())
	}
}

func decodeUint(r *Reader, v reflect.Value) error {
	var err error
	switch getIntKind(v.Type()) {
	case reflect.Uint:
		var u uint64
		u, err = r.ReadUint()
		v.SetUint(u)

	case reflect.Uint8:
		var u uint8
		u, err = r.ReadU8()
		v.SetUint(uint64(u))

	case reflect.Uint16:
		var u uint16
		u, err = r.ReadU16()
		v.SetUint(uint64(u))
	case reflect.Uint32:
		var u uint32
		u, err = r.ReadU32()
		v.SetUint(uint64(u))

	case reflect.Uint64:
		var u uint64
		u, err = r.ReadU64()
		v.SetUint(uint64(u))

	default:
		panic("not an uint")
	}

	return err
}

func decodeInt(r *Reader, v reflect.Value) error {
	var err error
	switch getIntKind(v.Type()) {
	case reflect.Int:
		var i int64
		i, err = r.ReadInt()
		v.SetInt(i)

	case reflect.Int8:
		var i int8
		i, err = r.ReadI8()
		v.SetInt(int64(i))

	case reflect.Int16:
		var i int16
		i, err = r.ReadI16()
		v.SetInt(int64(i))
	case reflect.Int32:
		var i int32
		i, err = r.ReadI32()
		v.SetInt(int64(i))

	case reflect.Int64:
		var i int64
		i, err = r.ReadI64()
		v.SetInt(int64(i))

	default:
		panic("not an int")
	}

	return err
}

func decodeFloat(r *Reader, v reflect.Value) error {
	var err error
	switch v.Type().Kind() {
	case reflect.Float32:
		var f float32
		f, err = r.ReadF32()
		v.SetFloat(float64(f))
	case reflect.Float64:
		var f float64
		f, err = r.ReadF64()
		v.SetFloat(f)
	default:
		panic("not a float")
	}
	return err
}

func decodeBool(r *Reader, v reflect.Value) error {
	b, err := r.ReadBool()
	v.SetBool(b)
	return err
}

func decodeString(r *Reader, v reflect.Value) error {
	s, err := r.ReadString()
	v.SetString(s)
	return err
}
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright (c) 2025 Drew Devault <https://drewdevault.com>

package bare

import (
	"reflect"
)

// Int is a variable-length encoded signed integer.
type Int int64

// Uint is a variable-length encoded unsigned integer.
type Uint uint64

var (
	intType  = reflect.TypeOf(Int(0))
	uintType = reflect.TypeOf(Uint(0))
)

func getIntKind(t reflect.Type) reflect.Kind {
	switch t {
	case intType:
		return reflect.Int
	case uintType:
		return reflect.Uint
	default:
		return t.Kind()
	}
}
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright (c) 2025 Drew Devault <https://drewdevault.com>

package bare

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

// 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([]byte(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 provides routines to interact with the git2d backend daemon.
package git2c

import (
	"fmt"
	"net"

	"git.sr.ht/~sircmpwn/go-bare"
	"go.lindenii.runxiyu.org/forge/forged/internal/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
}
module go.lindenii.runxiyu.org/forge

go 1.24.1

require (
	git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9
	github.com/alecthomas/chroma/v2 v2.16.0
	github.com/alexedwards/argon2id v1.0.0
	github.com/bluekeyes/go-gitdiff v0.8.1
	github.com/emersion/go-message v0.18.2
	github.com/emersion/go-smtp v0.21.3
	github.com/gliderlabs/ssh v0.3.8
	github.com/go-git/go-git/v5 v5.14.0
	github.com/jackc/pgx/v5 v5.7.4
	github.com/microcosm-cc/bluemonday v1.0.27
	github.com/niklasfasching/go-org v1.7.0
	github.com/tdewolff/minify/v2 v2.22.4
	github.com/yuin/goldmark v1.7.8
	go.lindenii.runxiyu.org/lindenii-common v0.0.0-20250321131425-dda3538a9cd4
	golang.org/x/crypto v0.36.0
)

require (
	dario.cat/mergo v1.0.1 // indirect
	github.com/Microsoft/go-winio v0.6.2 // indirect
	github.com/ProtonMail/go-crypto v1.1.6 // indirect
	github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
	github.com/aymerick/douceur v0.2.0 // indirect
	github.com/cloudflare/circl v1.6.0 // indirect
	github.com/cyphar/filepath-securejoin v0.4.1 // indirect
	github.com/dlclark/regexp2 v1.11.5 // indirect
	github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
	github.com/emirpasic/gods v1.18.1 // indirect
	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
	github.com/go-git/go-billy/v5 v5.6.2 // indirect
	github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
	github.com/gorilla/css v1.0.1 // indirect
	github.com/jackc/pgpassfile v1.0.0 // indirect
	github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
	github.com/jackc/puddle/v2 v2.2.2 // indirect
	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
	github.com/kevinburke/ssh_config v1.2.0 // indirect
	github.com/pjbgf/sha1cd v0.3.2 // indirect
	github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
	github.com/skeema/knownhosts v1.3.1 // indirect
	github.com/tdewolff/parse/v2 v2.7.21 // indirect
	github.com/xanzy/ssh-agent v0.3.3 // indirect
	golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
	golang.org/x/net v0.38.0 // indirect
	golang.org/x/sync v0.12.0 // indirect
	golang.org/x/sys v0.31.0 // indirect
	golang.org/x/text v0.23.0 // indirect
	gopkg.in/warnings.v0 v0.1.2 // indirect
)