Lindenii Project Forge
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 )