mirror of
https://github.com/tuneinsight/lattigo.git
synced 2025-09-13 03:27:14 +00:00
283 lines
7.6 KiB
Go
283 lines
7.6 KiB
Go
package structs
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/tuneinsight/lattigo/v4/utils/buffer"
|
|
)
|
|
|
|
// Vector is a struct wrapping a slice of components of type T.
|
|
// T can be:
|
|
// - uint, uint64, uint32, uint16, uint8/byte, int, int64, int32, int16, int8, float64, float32.
|
|
// - Or any object that implements CopyNewer, CopyNewer, io.WriterTo or io.ReaderFrom depending on
|
|
// the method called.
|
|
type Vector[T any] []T
|
|
|
|
// CopyNew returns a deep copy of the object.
|
|
// If T is a struct, this method requires that T implements CopyNewer.
|
|
func (v Vector[T]) CopyNew() (vcpy Vector[T]) {
|
|
|
|
var t T
|
|
switch any(t).(type) {
|
|
case uint, uint64, uint32, uint16, uint8, int, int64, int32, int16, int8, float64, float32:
|
|
vcpy = Vector[T](make([]T, len(v)))
|
|
copy(vcpy, v)
|
|
default:
|
|
if _, isCopiable := any(t).(CopyNewer[T]); !isCopiable {
|
|
panic(fmt.Errorf("vector component of type %T does not comply to %T", t, new(CopyNewer[T])))
|
|
}
|
|
|
|
vcpy = Vector[T](make([]T, len(v)))
|
|
for i := range v {
|
|
vcpy[i] = *any(&v[i]).(CopyNewer[T]).CopyNew()
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// BinarySize returns the serialized size of the object in bytes.
|
|
// If T is a struct, this method requires that T implements BinarySizer.
|
|
func (v Vector[T]) BinarySize() (size int) {
|
|
|
|
var t T
|
|
switch any(t).(type) {
|
|
case uint, uint64, int, int64, float64:
|
|
return 8 + len(v)*8
|
|
case uint32, int32, float32:
|
|
return 8 + len(v)*4
|
|
case uint16, int16:
|
|
return 8 + len(v)*2
|
|
case uint8, int8:
|
|
return 8 + len(v)*1
|
|
default:
|
|
if _, isSizable := any(t).(BinarySizer); !isSizable {
|
|
panic(fmt.Errorf("vector component of type %T does not comply to %T", t, new(BinarySizer)))
|
|
}
|
|
|
|
size += 8
|
|
for i := range v {
|
|
size += any(&v[i]).(BinarySizer).BinarySize()
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// WriteTo writes the object on an io.Writer. It implements the io.WriterTo
|
|
// interface, and will write exactly object.BinarySize() bytes on w.
|
|
//
|
|
// If T is a struct, this method requires that T implements io.WriterTo.
|
|
//
|
|
// Unless w implements the buffer.Writer interface (see lattigo/utils/buffer/writer.go),
|
|
// it will be wrapped into a bufio.Writer. Since this requires allocations, it
|
|
// is preferable to pass a buffer.Writer directly:
|
|
//
|
|
// - When writing multiple times to a io.Writer, it is preferable to first wrap the
|
|
// io.Writer in a pre-allocated bufio.Writer.
|
|
// - When writing to a pre-allocated var b []byte, it is preferable to pass
|
|
// buffer.NewBuffer(b) as w (see lattigo/utils/buffer/buffer.go).
|
|
func (v Vector[T]) WriteTo(w io.Writer) (n int64, err error) {
|
|
|
|
switch w := w.(type) {
|
|
case buffer.Writer:
|
|
|
|
var inc int64
|
|
if inc, err = buffer.WriteAsUint64[int](w, len(v)); err != nil {
|
|
return inc, fmt.Errorf("buffer.WriteAsUint64[int]: %w", err)
|
|
}
|
|
|
|
n += inc
|
|
|
|
var t T
|
|
switch t := any(t).(type) {
|
|
case uint, uint64, int, int64, float64:
|
|
|
|
if inc, err = buffer.WriteAsUint64Slice[T](w, v); err != nil {
|
|
return n + inc, fmt.Errorf("buffer.WriteAsUint64Slice[%T]: %w", t, err)
|
|
}
|
|
|
|
n += inc
|
|
|
|
case uint32, int32, float32:
|
|
|
|
if inc, err = buffer.WriteAsUint32Slice[T](w, v); err != nil {
|
|
return n + inc, fmt.Errorf("buffer.WriteAsUint32Slice[%T]: %w", t, err)
|
|
}
|
|
|
|
n += inc
|
|
|
|
case uint16, int16:
|
|
|
|
if inc, err = buffer.WriteAsUint16Slice[T](w, v); err != nil {
|
|
return n + inc, fmt.Errorf("buffer.WriteAsUint16Slice[%T]: %w", t, err)
|
|
}
|
|
|
|
n += inc
|
|
|
|
case uint8, int8:
|
|
|
|
if inc, err = buffer.WriteAsUint8Slice[T](w, v); err != nil {
|
|
return n + inc, fmt.Errorf("buffer.WriteAsUint8Slice[%T]: %w", t, err)
|
|
}
|
|
|
|
n += inc
|
|
|
|
default:
|
|
|
|
if _, isWritable := any(new(T)).(io.WriterTo); !isWritable {
|
|
return 0, fmt.Errorf("vector component of type %T does not comply to %T", t, new(io.WriterTo))
|
|
}
|
|
|
|
for i := range v {
|
|
if inc, err = any(&v[i]).(io.WriterTo).WriteTo(w); err != nil {
|
|
return n + inc, fmt.Errorf("%T.WriteTo: %w", t, err)
|
|
}
|
|
n += inc
|
|
}
|
|
}
|
|
|
|
return n, w.Flush()
|
|
|
|
default:
|
|
return v.WriteTo(bufio.NewWriter(w))
|
|
}
|
|
}
|
|
|
|
// ReadFrom reads on the object from an io.Writer. It implements the
|
|
// io.ReaderFrom interface.
|
|
//
|
|
// If T is a struct, this method requires that T implements io.ReaderFrom.
|
|
//
|
|
// Unless r implements the buffer.Reader interface (see see lattigo/utils/buffer/reader.go),
|
|
// it will be wrapped into a bufio.Reader. Since this requires allocation, it
|
|
// is preferable to pass a buffer.Reader directly:
|
|
//
|
|
// - When reading multiple values from a io.Reader, it is preferable to first
|
|
// first wrap io.Reader in a pre-allocated bufio.Reader.
|
|
// - When reading from a var b []byte, it is preferable to pass a buffer.NewBuffer(b)
|
|
// as w (see lattigo/utils/buffer/buffer.go).
|
|
func (v *Vector[T]) ReadFrom(r io.Reader) (n int64, err error) {
|
|
|
|
switch r := r.(type) {
|
|
case buffer.Reader:
|
|
|
|
var inc int64
|
|
|
|
var size int
|
|
|
|
if inc, err = buffer.ReadAsUint64[int](r, &size); err != nil {
|
|
return inc, fmt.Errorf("buffer.ReadAsUint64[int]: %w", err)
|
|
}
|
|
|
|
n += inc
|
|
|
|
if cap(*v) < size {
|
|
*v = make([]T, size)
|
|
}
|
|
|
|
*v = (*v)[:size]
|
|
|
|
var t T
|
|
switch any(t).(type) {
|
|
case uint, uint64, int, int64, float64:
|
|
|
|
if inc, err = buffer.ReadAsUint64Slice[T](r, *v); err != nil {
|
|
return n + inc, fmt.Errorf("buffer.ReadAsUint64Slice[%T]: %w", t, err)
|
|
}
|
|
|
|
n += inc
|
|
|
|
case uint32, int32, float32:
|
|
|
|
if inc, err = buffer.ReadAsUint32Slice[T](r, *v); err != nil {
|
|
return n + inc, fmt.Errorf("buffer.ReadAsUint32Slice[%T]: %w", t, err)
|
|
}
|
|
|
|
n += inc
|
|
|
|
case uint16, int16:
|
|
|
|
if inc, err = buffer.ReadAsUint16Slice[T](r, *v); err != nil {
|
|
return n + inc, fmt.Errorf("buffer.ReadAsUint16Slice[%T]: %w", t, err)
|
|
}
|
|
|
|
n += inc
|
|
|
|
case uint8, int8:
|
|
|
|
if inc, err = buffer.ReadAsUint8Slice[T](r, *v); err != nil {
|
|
return n + inc, fmt.Errorf("buffer.ReadAsUint8Slice[%T]: %w", t, err)
|
|
}
|
|
|
|
n += inc
|
|
default:
|
|
|
|
if _, isReadable := any(new(T)).(io.ReaderFrom); !isReadable {
|
|
return 0, fmt.Errorf("vector component of type %T does not comply to %T", t, new(io.ReaderFrom))
|
|
}
|
|
|
|
for i := range *v {
|
|
if inc, err = any(&(*v)[i]).(io.ReaderFrom).ReadFrom(r); err != nil {
|
|
var t T
|
|
return n + inc, fmt.Errorf("%T.ReadFrom: %w", t, err)
|
|
}
|
|
n += inc
|
|
}
|
|
}
|
|
|
|
return n, nil
|
|
|
|
default:
|
|
return v.ReadFrom(bufio.NewReader(r))
|
|
}
|
|
}
|
|
|
|
// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes.
|
|
// If T is a struct, this method requires that T implements io.WriterTo.
|
|
func (v Vector[T]) MarshalBinary() (p []byte, err error) {
|
|
buf := buffer.NewBufferSize(v.BinarySize())
|
|
_, err = v.WriteTo(buf)
|
|
return buf.Bytes(), err
|
|
}
|
|
|
|
// UnmarshalBinary decodes a slice of bytes generated by
|
|
// MarshalBinary or WriteTo on the object.
|
|
// If T is a struct, this method requires that T implements io.ReaderFrom.
|
|
func (v *Vector[T]) UnmarshalBinary(p []byte) (err error) {
|
|
_, err = v.ReadFrom(buffer.NewBuffer(p))
|
|
return
|
|
}
|
|
|
|
// Equal performs a deep equal.
|
|
// If T is a struct, this method requires that T implements Equatable.
|
|
func (v Vector[T]) Equal(other Vector[T]) (isEqual bool) {
|
|
|
|
var t T
|
|
switch any(t).(type) {
|
|
case uint, uint64, int, int64, float64:
|
|
return buffer.EqualAsUint64Slice([]T(v), []T(other))
|
|
case uint32, int32, float32:
|
|
return buffer.EqualAsUint32Slice([]T(v), []T(other))
|
|
case uint16, int16:
|
|
return buffer.EqualAsUint16Slice([]T(v), []T(other))
|
|
case uint8, int8:
|
|
return buffer.EqualAsUint8Slice([]T(v), []T(other))
|
|
default:
|
|
|
|
if _, isEquatable := any(t).(Equatable[T]); !isEquatable {
|
|
panic(fmt.Errorf("vector component of type %T does not comply to %T", t, new(Equatable[T])))
|
|
}
|
|
|
|
isEqual := true
|
|
for i, v := range v {
|
|
/* #nosec G601 -- Implicit memory aliasing in for loop acknowledged */
|
|
isEqual = isEqual && any(&v).(Equatable[T]).Equal(&other[i])
|
|
}
|
|
|
|
return isEqual
|
|
}
|
|
}
|