diff --git a/bgv/params.go b/bgv/params.go index ec9b3483..0fc0aa0e 100644 --- a/bgv/params.go +++ b/bgv/params.go @@ -214,6 +214,8 @@ func (p Parameters) Equal(other rlwe.ParametersInterface) bool { } // MarshalBinary returns a []byte representation of the parameter set. +// The representation corresponds to the JSON representation obtained +// from MarshalJSON. func (p Parameters) MarshalBinary() ([]byte, error) { return p.MarshalJSON() } diff --git a/ckks/bootstrapping/parameters.go b/ckks/bootstrapping/parameters.go index 1ea79aef..d7ca4624 100644 --- a/ckks/bootstrapping/parameters.go +++ b/ckks/bootstrapping/parameters.go @@ -211,7 +211,7 @@ func (p *Parameters) Depth() (depth int) { return p.DepthCoeffsToSlots() + p.DepthEvalMod() + p.DepthSlotsToCoeffs() } -// MarshalBinary returns a JSON representation of the the target Parameters struct on a slice of bytes. +// MarshalBinary returns a JSON representation of the bootstrapping Parameters struct. // See `Marshal` from the `encoding/json` package. func (p *Parameters) MarshalBinary() (data []byte, err error) { return json.Marshal(p) diff --git a/ckks/params.go b/ckks/params.go index 2869c833..5de0afd4 100644 --- a/ckks/params.go +++ b/ckks/params.go @@ -198,6 +198,7 @@ func (p Parameters) Equal(other rlwe.ParametersInterface) bool { } // MarshalBinary returns a []byte representation of the parameter set. +// This representation corresponds to the one returned by MarshalJSON. func (p Parameters) MarshalBinary() ([]byte, error) { return p.MarshalJSON() } diff --git a/drlwe/keygen_cpk.go b/drlwe/keygen_cpk.go index 79af408d..44630878 100644 --- a/drlwe/keygen_cpk.go +++ b/drlwe/keygen_cpk.go @@ -26,69 +26,6 @@ type PublicKeyGenCRP struct { Value ringqp.Poly } -// ShallowCopy creates a shallow copy of PublicKeyGenProtocol in which all the read-only data-structures are -// shared with the receiver and the temporary buffers are reallocated. The receiver and the returned -// PublicKeyGenProtocol can be used concurrently. -func (ckg *PublicKeyGenProtocol) ShallowCopy() *PublicKeyGenProtocol { - prng, err := sampling.NewPRNG() - if err != nil { - panic(err) - } - - return &PublicKeyGenProtocol{ckg.params, ring.NewSampler(prng, ckg.params.RingQ(), ckg.params.Xe(), false)} -} - -// BinarySize returns the size in bytes of the object -// when encoded using Encode. -func (share *PublicKeyGenShare) BinarySize() int { - return share.Value.BinarySize() -} - -// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (share *PublicKeyGenShare) MarshalBinary() (p []byte, err error) { - return share.Value.MarshalBinary() -} - -// Encode encodes the object into a binary form on a preallocated slice of bytes -// and returns the number of bytes written. -func (share *PublicKeyGenShare) Encode(p []byte) (ptr int, err error) { - return share.Value.Encode(p) -} - -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. -func (share *PublicKeyGenShare) WriteTo(w io.Writer) (n int64, err error) { - return share.Value.WriteTo(w) -} - -// UnmarshalBinary decodes a slice of bytes generated by -// MarshalBinary or WriteTo on the object. -func (share *PublicKeyGenShare) UnmarshalBinary(p []byte) (err error) { - return share.Value.UnmarshalBinary(p) -} - -// Decode decodes a slice of bytes generated by Encode -// on the object and returns the number of bytes read. -func (share *PublicKeyGenShare) Decode(p []byte) (n int, err error) { - return share.Value.Decode(p) -} - -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. -func (share *PublicKeyGenShare) ReadFrom(r io.Reader) (n int64, err error) { - return share.Value.ReadFrom(r) -} - // NewPublicKeyGenProtocol creates a new PublicKeyGenProtocol instance func NewPublicKeyGenProtocol(params rlwe.Parameters) *PublicKeyGenProtocol { ckg := new(PublicKeyGenProtocol) @@ -145,3 +82,74 @@ func (ckg *PublicKeyGenProtocol) GenPublicKey(roundShare *PublicKeyGenShare, crp pubkey.Value[0].Copy(&roundShare.Value) pubkey.Value[1].Copy(&crp.Value) } + +// ShallowCopy creates a shallow copy of PublicKeyGenProtocol in which all the read-only data-structures are +// shared with the receiver and the temporary buffers are reallocated. The receiver and the returned +// PublicKeyGenProtocol can be used concurrently. +func (ckg *PublicKeyGenProtocol) ShallowCopy() *PublicKeyGenProtocol { + prng, err := sampling.NewPRNG() + if err != nil { + panic(err) + } + + return &PublicKeyGenProtocol{ckg.params, ring.NewSampler(prng, ckg.params.RingQ(), ckg.params.Xe(), false)} +} + +// BinarySize returns the size in bytes of the object +// when encoded using Encode. +func (share *PublicKeyGenShare) BinarySize() int { + return share.Value.BinarySize() +} + +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (share *PublicKeyGenShare) WriteTo(w io.Writer) (n int64, err error) { + return share.Value.WriteTo(w) +} + +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (share *PublicKeyGenShare) ReadFrom(r io.Reader) (n int64, err error) { + return share.Value.ReadFrom(r) +} + +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (share *PublicKeyGenShare) MarshalBinary() (p []byte, err error) { + return share.Value.MarshalBinary() +} + +// UnmarshalBinary decodes a slice of bytes generated by +// MarshalBinary or WriteTo on the object. +func (share *PublicKeyGenShare) UnmarshalBinary(p []byte) (err error) { + return share.Value.UnmarshalBinary(p) +} + +// Encode encodes the object into a binary form on a preallocated slice of bytes +// and returns the number of bytes written. +func (share *PublicKeyGenShare) Encode(p []byte) (ptr int, err error) { + return share.Value.Encode(p) +} + +// Decode decodes a slice of bytes generated by Encode +// on the object and returns the number of bytes read. +func (share *PublicKeyGenShare) Decode(p []byte) (n int, err error) { + return share.Value.Decode(p) +} diff --git a/drlwe/keygen_gal.go b/drlwe/keygen_gal.go index f363f14d..c6410e15 100644 --- a/drlwe/keygen_gal.go +++ b/drlwe/keygen_gal.go @@ -2,7 +2,6 @@ package drlwe import ( "bufio" - "bytes" "encoding/binary" "fmt" "io" @@ -246,28 +245,17 @@ func (share *GaloisKeyGenShare) BinarySize() int { return 8 + share.Value.BinarySize() } -// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (share *GaloisKeyGenShare) MarshalBinary() (p []byte, err error) { - buf := bytes.NewBuffer([]byte{}) - _, err = share.WriteTo(buf) - return buf.Bytes(), err -} - -// Encode encodes the object into a binary form on a preallocated slice of bytes -// and returns the number of bytes written. -func (share *GaloisKeyGenShare) Encode(p []byte) (n int, err error) { - binary.LittleEndian.PutUint64(p, share.GaloisElement) - n, err = share.Value.Encode(p[8:]) - return n + 8, err -} - -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (share *GaloisKeyGenShare) WriteTo(w io.Writer) (n int64, err error) { switch w := w.(type) { case buffer.Writer: @@ -293,13 +281,60 @@ func (share *GaloisKeyGenShare) WriteTo(w io.Writer) (n int64, err error) { } } +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (share *GaloisKeyGenShare) ReadFrom(r io.Reader) (n int64, err error) { + switch r := r.(type) { + case buffer.Reader: + + var inc int + if inc, err = buffer.ReadUint64(r, &share.GaloisElement); err != nil { + return n + int64(inc), err + } + n += int64(inc) + + var inc64 int64 + if inc64, err = share.Value.ReadFrom(r); err != nil { + return n + inc64, err + } + + return n + inc64, nil + default: + return share.ReadFrom(bufio.NewReader(r)) + } +} + +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (share *GaloisKeyGenShare) MarshalBinary() (p []byte, err error) { + buf := buffer.NewBufferSize(share.BinarySize()) + _, err = share.WriteTo(buf) + return buf.Bytes(), err +} + // UnmarshalBinary decodes a slice of bytes generated by // MarshalBinary or WriteTo on the object. func (share *GaloisKeyGenShare) UnmarshalBinary(p []byte) (err error) { - _, err = share.ReadFrom(bytes.NewBuffer(p)) + _, err = share.ReadFrom(buffer.NewBuffer(p)) return } +// Encode encodes the object into a binary form on a preallocated slice of bytes +// and returns the number of bytes written. +func (share *GaloisKeyGenShare) Encode(p []byte) (n int, err error) { + binary.LittleEndian.PutUint64(p, share.GaloisElement) + n, err = share.Value.Encode(p[8:]) + return n + 8, err +} + // Decode decodes a slice of bytes generated by Encode // on the object and returns the number of bytes read. func (share *GaloisKeyGenShare) Decode(p []byte) (n int, err error) { @@ -307,35 +342,3 @@ func (share *GaloisKeyGenShare) Decode(p []byte) (n int, err error) { n, err = share.Value.Decode(p[8:]) return n + 8, err } - -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. -func (share *GaloisKeyGenShare) ReadFrom(r io.Reader) (n int64, err error) { - switch r := r.(type) { - case buffer.Reader: - - var inc int - - if inc, err = buffer.ReadUint64(r, &share.GaloisElement); err != nil { - return n + int64(inc), err - } - - n += int64(inc) - - var inc2 int64 - if inc2, err = share.Value.ReadFrom(r); err != nil { - return n + inc2, err - } - - n += inc2 - - return - default: - return share.ReadFrom(bufio.NewReader(r)) - } -} diff --git a/drlwe/keygen_relin.go b/drlwe/keygen_relin.go index 8b1b219c..e0b0f416 100644 --- a/drlwe/keygen_relin.go +++ b/drlwe/keygen_relin.go @@ -314,47 +314,55 @@ func (share *RelinKeyGenShare) BinarySize() int { return share.GadgetCiphertext.BinarySize() } +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (share *RelinKeyGenShare) WriteTo(w io.Writer) (n int64, err error) { + return share.GadgetCiphertext.WriteTo(w) +} + +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (share *RelinKeyGenShare) ReadFrom(r io.Reader) (n int64, err error) { + return share.GadgetCiphertext.ReadFrom(r) +} + // MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. func (share *RelinKeyGenShare) MarshalBinary() (data []byte, err error) { return share.GadgetCiphertext.MarshalBinary() } -// Encode encodes the object into a binary form on a preallocated slice of bytes -// and returns the number of bytes written. -func (share *RelinKeyGenShare) Encode(data []byte) (n int, err error) { - return share.GadgetCiphertext.Encode(data) -} - -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. -func (share *RelinKeyGenShare) WriteTo(w io.Writer) (n int64, err error) { - return share.GadgetCiphertext.WriteTo(w) -} - // UnmarshalBinary decodes a slice of bytes generated by // MarshalBinary or WriteTo on the object. func (share *RelinKeyGenShare) UnmarshalBinary(data []byte) (err error) { return share.GadgetCiphertext.UnmarshalBinary(data) } +// Encode encodes the object into a binary form on a preallocated slice of bytes +// and returns the number of bytes written. +func (share *RelinKeyGenShare) Encode(data []byte) (n int, err error) { + return share.GadgetCiphertext.Encode(data) +} + // Decode decodes a slice of bytes generated by Encode // on the object and returns the number of bytes read. func (share *RelinKeyGenShare) Decode(data []byte) (n int, err error) { return share.GadgetCiphertext.Decode(data) } - -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. -func (share *RelinKeyGenShare) ReadFrom(r io.Reader) (n int64, err error) { - return share.GadgetCiphertext.ReadFrom(r) -} diff --git a/drlwe/keyswitch_pk.go b/drlwe/keyswitch_pk.go index 6cd2c4ec..1523b06a 100644 --- a/drlwe/keyswitch_pk.go +++ b/drlwe/keyswitch_pk.go @@ -27,25 +27,6 @@ type PublicKeySwitchShare struct { rlwe.OperandQ } -// ShallowCopy creates a shallow copy of PublicKeySwitchProtocol in which all the read-only data-structures are -// shared with the receiver and the temporary bufers are reallocated. The receiver and the returned -// PublicKeySwitchProtocol can be used concurrently. -func (pcks *PublicKeySwitchProtocol) ShallowCopy() *PublicKeySwitchProtocol { - prng, err := sampling.NewPRNG() - if err != nil { - panic(err) - } - - params := pcks.params - return &PublicKeySwitchProtocol{ - noiseSampler: ring.NewSampler(prng, params.RingQ(), pcks.noise, false), - noise: pcks.noise, - EncryptorInterface: rlwe.NewEncryptor(params, nil), - params: params, - buf: params.RingQ().NewPoly(), - } -} - // NewPublicKeySwitchProtocol creates a new PublicKeySwitchProtocol object and will be used to re-encrypt a ciphertext ctx encrypted under a secret-shared key among j parties under a new // collective public-key. func NewPublicKeySwitchProtocol(params rlwe.Parameters, noise distribution.Distribution) (pcks *PublicKeySwitchProtocol) { @@ -143,53 +124,80 @@ func (pcks *PublicKeySwitchProtocol) KeySwitch(ctIn *rlwe.Ciphertext, combined * ring.CopyLvl(level, &combined.Value[1], &ctOut.Value[1]) } +// ShallowCopy creates a shallow copy of PublicKeySwitchProtocol in which all the read-only data-structures are +// shared with the receiver and the temporary bufers are reallocated. The receiver and the returned +// PublicKeySwitchProtocol can be used concurrently. +func (pcks *PublicKeySwitchProtocol) ShallowCopy() *PublicKeySwitchProtocol { + prng, err := sampling.NewPRNG() + if err != nil { + panic(err) + } + + params := pcks.params + return &PublicKeySwitchProtocol{ + noiseSampler: ring.NewSampler(prng, params.RingQ(), pcks.noise, false), + noise: pcks.noise, + EncryptorInterface: rlwe.NewEncryptor(params, nil), + params: params, + buf: params.RingQ().NewPoly(), + } +} + // BinarySize returns the size in bytes of the object // when encoded using Encode. func (share *PublicKeySwitchShare) BinarySize() int { return share.OperandQ.BinarySize() } +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (share *PublicKeySwitchShare) WriteTo(w io.Writer) (n int64, err error) { + return share.OperandQ.WriteTo(w) +} + +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (share *PublicKeySwitchShare) ReadFrom(r io.Reader) (n int64, err error) { + return share.OperandQ.ReadFrom(r) +} + // MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. func (share *PublicKeySwitchShare) MarshalBinary() (p []byte, err error) { return share.OperandQ.MarshalBinary() } -// Encode encodes the object into a binary form on a preallocated slice of bytes -// and returns the number of bytes written. -func (share *PublicKeySwitchShare) Encode(p []byte) (n int, err error) { - return share.OperandQ.Encode(p) -} - -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface bufer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the bufer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/bufer/writer.go. -func (share *PublicKeySwitchShare) WriteTo(w io.Writer) (n int64, err error) { - return share.OperandQ.WriteTo(w) -} - // UnmarshalBinary decodes a slice of bytes generated by // MarshalBinary or WriteTo on the object. func (share *PublicKeySwitchShare) UnmarshalBinary(p []byte) (err error) { return share.OperandQ.UnmarshalBinary(p) } +// Encode encodes the object into a binary form on a preallocated slice of bytes +// and returns the number of bytes written. +func (share *PublicKeySwitchShare) Encode(p []byte) (n int, err error) { + return share.OperandQ.Encode(p) +} + // Decode decodes a slice of bytes generated by Encode // on the object and returns the number of bytes read. func (share *PublicKeySwitchShare) Decode(p []byte) (n int, err error) { return share.OperandQ.Decode(p) } - -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface bufer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the bufer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/bufer/reader.go. -func (share *PublicKeySwitchShare) ReadFrom(r io.Reader) (n int64, err error) { - return share.OperandQ.ReadFrom(r) -} diff --git a/drlwe/keyswitch_sk.go b/drlwe/keyswitch_sk.go index ea8f872e..c5a3f295 100644 --- a/drlwe/keyswitch_sk.go +++ b/drlwe/keyswitch_sk.go @@ -1,7 +1,6 @@ package drlwe import ( - "bytes" "fmt" "io" "math" @@ -171,11 +170,51 @@ func (ckss *KeySwitchShare) BinarySize() int { return ckss.Value.BinarySize() } +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (ckss *KeySwitchShare) WriteTo(w io.Writer) (n int64, err error) { + return ckss.Value.WriteTo(w) +} + +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (ckss *KeySwitchShare) ReadFrom(r io.Reader) (n int64, err error) { + if ckss.Value == nil { + ckss.Value = new(ring.Poly) + } + return ckss.Value.ReadFrom(r) +} + // MarshalBinary encodes a KeySwitch share on a slice of bytes. func (ckss *KeySwitchShare) MarshalBinary() (p []byte, err error) { - buf := bytes.NewBuffer([]byte{}) - _, err = ckss.WriteTo(buf) - return buf.Bytes(), err + return ckss.Value.MarshalBinary() +} + +// UnmarshalBinary decodes a slice of bytes generated by +// MarshalBinary or WriteTo on the object. +func (ckss *KeySwitchShare) UnmarshalBinary(p []byte) (err error) { + if ckss.Value == nil { + ckss.Value = new(ring.Poly) + } + return ckss.Value.UnmarshalBinary(p) } // Encode encodes the object into a binary form on a preallocated slice of bytes @@ -184,24 +223,6 @@ func (ckss *KeySwitchShare) Encode(p []byte) (ptr int, err error) { return ckss.Value.Encode(p) } -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface bufer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the bufer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/bufer/writer.go. -func (ckss *KeySwitchShare) WriteTo(w io.Writer) (n int64, err error) { - return ckss.Value.WriteTo(w) -} - -// UnmarshalBinary decodes a slice of bytes generated by -// MarshalBinary or WriteTo on the object. -func (ckss *KeySwitchShare) UnmarshalBinary(p []byte) (err error) { - _, err = ckss.ReadFrom(bytes.NewBuffer(p)) - return -} - // Decode decodes a slice of bytes generated by Encode // on the object and returns the number of bytes read. func (ckss *KeySwitchShare) Decode(p []byte) (ptr int, err error) { @@ -211,18 +232,3 @@ func (ckss *KeySwitchShare) Decode(p []byte) (ptr int, err error) { return ckss.Value.Decode(p) } - -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface bufer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the bufer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/bufer/reader.go. -func (ckss *KeySwitchShare) ReadFrom(r io.Reader) (n int64, err error) { - if ckss.Value == nil { - ckss.Value = new(ring.Poly) - } - - return ckss.Value.ReadFrom(r) -} diff --git a/drlwe/refresh.go b/drlwe/refresh.go index 3e9b9c9b..7a3ead9e 100644 --- a/drlwe/refresh.go +++ b/drlwe/refresh.go @@ -2,7 +2,6 @@ package drlwe import ( "bufio" - "bytes" "io" "github.com/tuneinsight/lattigo/v4/utils/buffer" @@ -20,31 +19,17 @@ func (share *RefreshShare) BinarySize() int { return share.EncToShareShare.BinarySize() + share.ShareToEncShare.BinarySize() } -// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (share *RefreshShare) MarshalBinary() (p []byte, err error) { - buf := bytes.NewBuffer([]byte{}) - _, err = share.WriteTo(buf) - return buf.Bytes(), err -} - -// Encode encodes the object into a binary form on a preallocated slice of bytes -// and returns the number of bytes written. -func (share *RefreshShare) Encode(p []byte) (n int, err error) { - if n, err = share.EncToShareShare.Encode(p[n:]); err != nil { - return - } - var inc int - inc, err = share.ShareToEncShare.Encode(p[n:]) - return n + inc, err -} - -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (share *RefreshShare) WriteTo(w io.Writer) (n int64, err error) { switch w := w.(type) { case buffer.Writer: @@ -59,31 +44,17 @@ func (share *RefreshShare) WriteTo(w io.Writer) (n int64, err error) { } } -// UnmarshalBinary decodes a slice of bytes generated by -// MarshalBinary or WriteTo on the object. -func (share *RefreshShare) UnmarshalBinary(p []byte) (err error) { - _, err = share.ReadFrom(bytes.NewBuffer(p)) - return -} - -// Decode decodes a slice of bytes generated by Encode -// on the object and returns the number of bytes read. -func (share *RefreshShare) Decode(p []byte) (n int, err error) { - if n, err = share.EncToShareShare.Decode(p[n:]); err != nil { - return - } - var inc int - inc, err = share.ShareToEncShare.Decode(p[n:]) - return n + inc, err -} - -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (share *RefreshShare) ReadFrom(r io.Reader) (n int64, err error) { switch r := r.(type) { case buffer.Reader: @@ -97,3 +68,39 @@ func (share *RefreshShare) ReadFrom(r io.Reader) (n int64, err error) { return share.ReadFrom(bufio.NewReader(r)) } } + +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (share *RefreshShare) MarshalBinary() (p []byte, err error) { + buf := buffer.NewBufferSize(share.BinarySize()) + _, err = share.WriteTo(buf) + return buf.Bytes(), err +} + +// UnmarshalBinary decodes a slice of bytes generated by +// MarshalBinary or WriteTo on the object. +func (share *RefreshShare) UnmarshalBinary(p []byte) (err error) { + _, err = share.ReadFrom(buffer.NewBuffer(p)) + return +} + +// Encode encodes the object into a binary form on a preallocated slice of bytes +// and returns the number of bytes written. +func (share *RefreshShare) Encode(p []byte) (n int, err error) { + if n, err = share.EncToShareShare.Encode(p[n:]); err != nil { + return + } + var inc int + inc, err = share.ShareToEncShare.Encode(p[n:]) + return n + inc, err +} + +// Decode decodes a slice of bytes generated by Encode +// on the object and returns the number of bytes read. +func (share *RefreshShare) Decode(p []byte) (n int, err error) { + if n, err = share.EncToShareShare.Decode(p[n:]); err != nil { + return + } + var inc int + inc, err = share.ShareToEncShare.Decode(p[n:]) + return n + inc, err +} diff --git a/drlwe/threshold.go b/drlwe/threshold.go index d5a48416..d4c89715 100644 --- a/drlwe/threshold.go +++ b/drlwe/threshold.go @@ -180,47 +180,55 @@ func (s *ShamirSecretShare) BinarySize() int { return s.Poly.BinarySize() } +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (s *ShamirSecretShare) WriteTo(w io.Writer) (n int64, err error) { + return s.Poly.WriteTo(w) +} + +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (s *ShamirSecretShare) ReadFrom(r io.Reader) (n int64, err error) { + return s.Poly.ReadFrom(r) +} + // MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. func (s *ShamirSecretShare) MarshalBinary() (p []byte, err error) { return s.Poly.MarshalBinary() } -// Encode encodes the object into a binary form on a preallocated slice of bytes -// and returns the number of bytes written. -func (s *ShamirSecretShare) Encode(p []byte) (n int, err error) { - return s.Poly.Encode(p) -} - -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. -func (s *ShamirSecretShare) WriteTo(w io.Writer) (n int64, err error) { - return s.Poly.WriteTo(w) -} - // UnmarshalBinary decodes a slice of bytes generated by // MarshalBinary or WriteTo on the object. func (s *ShamirSecretShare) UnmarshalBinary(p []byte) (err error) { return s.Poly.UnmarshalBinary(p) } +// Encode encodes the object into a binary form on a preallocated slice of bytes +// and returns the number of bytes written. +func (s *ShamirSecretShare) Encode(p []byte) (n int, err error) { + return s.Poly.Encode(p) +} + // Decode decodes a slice of bytes generated by Encode // on the object and returns the number of bytes read. func (s *ShamirSecretShare) Decode(p []byte) (n int, err error) { return s.Poly.Decode(p) } - -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. -func (s *ShamirSecretShare) ReadFrom(r io.Reader) (n int64, err error) { - return s.Poly.ReadFrom(r) -} diff --git a/ring/poly.go b/ring/poly.go index 1bb5fc90..5847fa9e 100644 --- a/ring/poly.go +++ b/ring/poly.go @@ -2,7 +2,6 @@ package ring import ( "bufio" - "bytes" "encoding/binary" "fmt" "io" @@ -117,54 +116,33 @@ func (pol *Poly) Equal(other *Poly) bool { return false } -// BinarySize returns the size in bytes of the object +// polyBinarySize returns the size in bytes of the object // when encoded using Encode. -func BinarySize(N, Level int) (size int) { +func polyBinarySize(N, Level int) (size int) { return 16 + N*(Level+1)<<3 } // BinarySize returns the size in bytes of the object // when encoded using Encode. func (pol *Poly) BinarySize() (size int) { - return BinarySize(pol.N(), pol.Level()) + return polyBinarySize(pol.N(), pol.Level()) } -// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (pol *Poly) MarshalBinary() (p []byte, err error) { - buf := bytes.NewBuffer([]byte{}) - _, err = pol.WriteTo(buf) - return buf.Bytes(), err -} - -// UnmarshalBinary decodes a slice of bytes generated by -// MarshalBinary or WriteTo on the object. -func (pol *Poly) UnmarshalBinary(p []byte) (err error) { - - N := int(binary.LittleEndian.Uint64(p)) - Level := int(binary.LittleEndian.Uint64(p[8:])) - - if size := BinarySize(N, Level); len(p) != size { - return fmt.Errorf("cannot UnmarshalBinary: len(p)=%d != %d", len(p), size) - } - - if _, err = pol.ReadFrom(bytes.NewBuffer(p)); err != nil { - return - } - - return nil -} - -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (pol *Poly) WriteTo(w io.Writer) (int64, error) { switch w := w.(type) { - case *bufio.Writer: + case buffer.Writer: var err error @@ -191,17 +169,21 @@ func (pol *Poly) WriteTo(w io.Writer) (int64, error) { } } -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (pol *Poly) ReadFrom(r io.Reader) (int64, error) { switch r := r.(type) { - case *bufio.Reader: + case buffer.Reader: var err error var n, inc int @@ -254,6 +236,22 @@ func (pol *Poly) ReadFrom(r io.Reader) (int64, error) { } } +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (pol *Poly) MarshalBinary() (p []byte, err error) { + buf := buffer.NewBufferSize(pol.BinarySize()) + _, err = pol.WriteTo(buf) + return buf.Bytes(), err +} + +// UnmarshalBinary decodes a slice of bytes generated by +// MarshalBinary or WriteTo on the object. +func (pol *Poly) UnmarshalBinary(p []byte) (err error) { + if _, err = pol.ReadFrom(buffer.NewBuffer(p)); err != nil { + return + } + return +} + // Encode encodes the object into a binary form on a preallocated slice of bytes // and returns the number of bytes written. func (pol *Poly) Encode(p []byte) (n int, err error) { @@ -292,7 +290,7 @@ func (pol *Poly) Decode(p []byte) (n int, err error) { Level := int(binary.LittleEndian.Uint64(p[n:])) n += 8 - if size := BinarySize(N, Level); len(p) < size { + if size := polyBinarySize(N, Level); len(p) < size { return n, fmt.Errorf("cannot Decode: len(p)=%d < ", size) } diff --git a/rlwe/evaluationkeyset.go b/rlwe/evaluationkeyset.go index 0df41f7a..dace1294 100644 --- a/rlwe/evaluationkeyset.go +++ b/rlwe/evaluationkeyset.go @@ -2,7 +2,6 @@ package rlwe import ( "bufio" - "bytes" "fmt" "io" @@ -80,27 +79,32 @@ func (evk *MemEvaluationKeySet) GetRelinearizationKey() (rk *RelinearizationKey, return nil, fmt.Errorf("RelinearizationKey is nil") } -// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (evk *MemEvaluationKeySet) MarshalBinary() (p []byte, err error) { - buf := bytes.NewBuffer([]byte{}) - _, err = evk.WriteTo(buf) - return buf.Bytes(), err -} +func (evk *MemEvaluationKeySet) BinarySize() (size int) { + + size++ + if evk.Rlk != nil { + size += evk.Rlk.BinarySize() + } + + size++ + if evk.Gks != nil { + size += evk.Gks.BinarySize() + } -// UnmarshalBinary decodes a slice of bytes generated by -// MarshalBinary or WriteTo on the object. -func (evk *MemEvaluationKeySet) UnmarshalBinary(p []byte) (err error) { - _, err = evk.ReadFrom(bytes.NewBuffer(p)) return } -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (evk *MemEvaluationKeySet) WriteTo(w io.Writer) (int64, error) { switch w := w.(type) { case buffer.Writer: @@ -156,13 +160,17 @@ func (evk *MemEvaluationKeySet) WriteTo(w io.Writer) (int64, error) { } } -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (evk *MemEvaluationKeySet) ReadFrom(r io.Reader) (n int64, err error) { switch r := r.(type) { case buffer.Reader: @@ -217,18 +225,17 @@ func (evk *MemEvaluationKeySet) ReadFrom(r io.Reader) (n int64, err error) { } } -func (evk *MemEvaluationKeySet) BinarySize() (size int) { - - size++ - if evk.Rlk != nil { - size += evk.Rlk.BinarySize() - } - - size++ - if evk.Gks != nil { - size += evk.Gks.BinarySize() - } +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (evk *MemEvaluationKeySet) MarshalBinary() (p []byte, err error) { + buf := buffer.NewBufferSize(evk.BinarySize()) + _, err = evk.WriteTo(buf) + return buf.Bytes(), err +} +// UnmarshalBinary decodes a slice of bytes generated by +// MarshalBinary or WriteTo on the object. +func (evk *MemEvaluationKeySet) UnmarshalBinary(p []byte) (err error) { + _, err = evk.ReadFrom(buffer.NewBuffer(p)) return } diff --git a/rlwe/gadgetciphertext.go b/rlwe/gadgetciphertext.go index cefd5125..16a9ced8 100644 --- a/rlwe/gadgetciphertext.go +++ b/rlwe/gadgetciphertext.go @@ -1,7 +1,6 @@ package rlwe import ( - "bytes" "io" "github.com/google/go-cmp/cmp" @@ -66,46 +65,51 @@ func (ct *GadgetCiphertext) CopyNew() (ctCopy *GadgetCiphertext) { return &GadgetCiphertext{Value: v} } +// BinarySize returns the size in bytes of the object +// when encoded using Encode. +func (ct *GadgetCiphertext) BinarySize() (dataLen int) { + return ct.Value.BinarySize() +} + +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (ct *GadgetCiphertext) WriteTo(w io.Writer) (n int64, err error) { + return ct.Value.WriteTo(w) +} + +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (ct *GadgetCiphertext) ReadFrom(r io.Reader) (n int64, err error) { + return ct.Value.ReadFrom(r) +} + // MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. func (ct *GadgetCiphertext) MarshalBinary() (data []byte, err error) { - buf := bytes.NewBuffer([]byte{}) - _, err = ct.WriteTo(buf) - return buf.Bytes(), err + return ct.Value.MarshalBinary() } // UnmarshalBinary decodes a slice of bytes generated by // MarshalBinary or WriteTo on the object. func (ct *GadgetCiphertext) UnmarshalBinary(p []byte) (err error) { - _, err = ct.ReadFrom(bytes.NewBuffer(p)) - return -} - -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. -func (ct *GadgetCiphertext) WriteTo(w io.Writer) (n int64, err error) { - return ct.Value.WriteTo(w) -} - -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. -func (ct *GadgetCiphertext) ReadFrom(r io.Reader) (n int64, err error) { - return ct.Value.ReadFrom(r) -} - -// BinarySize returns the size in bytes of the object -// when encoded using Encode. -func (ct *GadgetCiphertext) BinarySize() (dataLen int) { - return ct.Value.BinarySize() + return ct.Value.UnmarshalBinary(p) } // Encode encodes the object into a binary form on a preallocated slice of bytes diff --git a/rlwe/galoiskey.go b/rlwe/galoiskey.go index 06841b31..0a2e466f 100644 --- a/rlwe/galoiskey.go +++ b/rlwe/galoiskey.go @@ -2,7 +2,6 @@ package rlwe import ( "bufio" - "bytes" "encoding/binary" "fmt" "io" @@ -47,20 +46,23 @@ func (gk *GaloisKey) CopyNew() *GaloisKey { } } -// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (gk *GaloisKey) MarshalBinary() (p []byte, err error) { - buf := bytes.NewBuffer([]byte{}) - _, err = gk.WriteTo(buf) - return buf.Bytes(), err +// BinarySize returns the size in bytes of the object +// when encoded using Encode. +func (gk *GaloisKey) BinarySize() (size int) { + return gk.EvaluationKey.BinarySize() + 16 } -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (gk *GaloisKey) WriteTo(w io.Writer) (n int64, err error) { switch w := w.(type) { case buffer.Writer: @@ -93,20 +95,17 @@ func (gk *GaloisKey) WriteTo(w io.Writer) (n int64, err error) { } } -// UnmarshalBinary decodes a slice of bytes generated by -// MarshalBinary or WriteTo on the object. -func (gk *GaloisKey) UnmarshalBinary(p []byte) (err error) { - _, err = gk.ReadFrom(bytes.NewBuffer(p)) - return -} - -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (gk *GaloisKey) ReadFrom(r io.Reader) (n int64, err error) { switch r := r.(type) { case buffer.Reader: @@ -138,10 +137,18 @@ func (gk *GaloisKey) ReadFrom(r io.Reader) (n int64, err error) { } } -// BinarySize returns the size in bytes of the object -// when encoded using Encode. -func (gk *GaloisKey) BinarySize() (size int) { - return gk.EvaluationKey.BinarySize() + 16 +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (gk *GaloisKey) MarshalBinary() (p []byte, err error) { + buf := buffer.NewBufferSize(gk.BinarySize()) + _, err = gk.WriteTo(buf) + return buf.Bytes(), err +} + +// UnmarshalBinary decodes a slice of bytes generated by +// MarshalBinary or WriteTo on the object. +func (gk *GaloisKey) UnmarshalBinary(p []byte) (err error) { + _, err = gk.ReadFrom(buffer.NewBuffer(p)) + return } // Encode encodes the object into a binary form on a preallocated slice of bytes diff --git a/rlwe/metadata.go b/rlwe/metadata.go index 0f7bf2f6..b860ea65 100644 --- a/rlwe/metadata.go +++ b/rlwe/metadata.go @@ -53,21 +53,8 @@ func (m MetaData) BinarySize() int { return 5 + m.PlaintextScale.BinarySize() } -// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (m MetaData) MarshalBinary() (p []byte, err error) { - p = make([]byte, m.BinarySize()) - _, err = m.Encode(p) - return -} - -// UnmarshalBinary decodes a slice of bytes generated by -// MarshalBinary or WriteTo on the object. -func (m *MetaData) UnmarshalBinary(p []byte) (err error) { - _, err = m.Decode(p) - return -} - -// WriteTo writes the object on an io.Writer. +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. func (m *MetaData) WriteTo(w io.Writer) (int64, error) { if p, err := m.MarshalBinary(); err != nil { return 0, err @@ -80,6 +67,8 @@ func (m *MetaData) WriteTo(w io.Writer) (int64, error) { } } +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. func (m *MetaData) ReadFrom(r io.Reader) (int64, error) { p := make([]byte, m.BinarySize()) if n, err := r.Read(p); err != nil { @@ -90,6 +79,20 @@ func (m *MetaData) ReadFrom(r io.Reader) (int64, error) { } } +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (m MetaData) MarshalBinary() (p []byte, err error) { + p = make([]byte, m.BinarySize()) + _, err = m.Encode(p) + return +} + +// UnmarshalBinary decodes a slice of bytes generated by +// MarshalBinary or WriteTo on the object. +func (m *MetaData) UnmarshalBinary(p []byte) (err error) { + _, err = m.Decode(p) + return +} + // Encode encodes the object into a binary form on a preallocated slice of bytes // and returns the number of bytes written. func (m MetaData) Encode(p []byte) (n int, err error) { diff --git a/rlwe/operand.go b/rlwe/operand.go index f887d87c..f1623d0d 100644 --- a/rlwe/operand.go +++ b/rlwe/operand.go @@ -1,13 +1,13 @@ package rlwe import ( - "bytes" "fmt" "io" "github.com/google/go-cmp/cmp" "github.com/tuneinsight/lattigo/v4/ring" "github.com/tuneinsight/lattigo/v4/rlwe/ringqp" + "github.com/tuneinsight/lattigo/v4/utils/buffer" "github.com/tuneinsight/lattigo/v4/utils/sampling" "github.com/tuneinsight/lattigo/v4/utils/structs" ) @@ -212,27 +212,23 @@ func SwitchCiphertextRingDegree(ctIn, ctOut *OperandQ) { ctOut.MetaData = ctIn.MetaData } -// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (op *OperandQ) MarshalBinary() (data []byte, err error) { - buf := bytes.NewBuffer([]byte{}) - _, err = op.WriteTo(buf) - return buf.Bytes(), err +// BinarySize returns the size in bytes of the object +// when encoded using Encode. +func (op *OperandQ) BinarySize() int { + return op.MetaData.BinarySize() + op.Value.BinarySize() } -// UnmarshalBinary decodes a slice of bytes generated by MarshalBinary -// or Read on the objeop. -func (op *OperandQ) UnmarshalBinary(p []byte) (err error) { - _, err = op.ReadFrom(bytes.NewBuffer(p)) - return -} - -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (op *OperandQ) WriteTo(w io.Writer) (n int64, err error) { if n, err = op.MetaData.WriteTo(w); err != nil { @@ -244,13 +240,17 @@ func (op *OperandQ) WriteTo(w io.Writer) (n int64, err error) { return n + inc, err } -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (op *OperandQ) ReadFrom(r io.Reader) (n int64, err error) { if op == nil { @@ -266,10 +266,18 @@ func (op *OperandQ) ReadFrom(r io.Reader) (n int64, err error) { return n + inc, err } -// BinarySize returns the size in bytes of the object -// when encoded using Encode. -func (op *OperandQ) BinarySize() int { - return op.MetaData.BinarySize() + op.Value.BinarySize() +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (op *OperandQ) MarshalBinary() (data []byte, err error) { + buf := buffer.NewBufferSize(op.BinarySize()) + _, err = op.WriteTo(buf) + return buf.Bytes(), err +} + +// UnmarshalBinary decodes a slice of bytes generated by MarshalBinary +// or Read on the objeop. +func (op *OperandQ) UnmarshalBinary(p []byte) (err error) { + _, err = op.ReadFrom(buffer.NewBuffer(p)) + return } // Encode encodes the object into a binary form on a preallocated slice of bytes @@ -280,17 +288,13 @@ func (op *OperandQ) Encode(p []byte) (n int, err error) { return 0, fmt.Errorf("cannot Encode: len(p) is too small") } - // if n, err = op.MetaData.Encode(p); err != nil { - // return - // } + if n, err = op.MetaData.Encode(p); err != nil { + return + } - // inc, err := op.Value.Encode(p[n:]) + inc, err := op.Value.Encode(p[n:]) - // return n + inc, err - - buf := bytes.NewBuffer(p[:0]) - nint64, err := op.WriteTo(buf) - return int(nint64), err + return n + inc, err } // Decode decodes a slice of bytes generated by Encode @@ -354,27 +358,23 @@ func (op *OperandQP) CopyNew() *OperandQP { return &OperandQP{Value: Value, MetaData: op.MetaData} } -// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (op *OperandQP) MarshalBinary() (data []byte, err error) { - buf := bytes.NewBuffer([]byte{}) - _, err = op.WriteTo(buf) - return buf.Bytes(), err +// BinarySize returns the size in bytes of the object +// when encoded using Encode. +func (op *OperandQP) BinarySize() int { + return op.MetaData.BinarySize() + op.Value.BinarySize() } -// UnmarshalBinary decodes a slice of bytes generated by MarshalBinary -// or Read on the objeop. -func (op *OperandQP) UnmarshalBinary(p []byte) (err error) { - _, err = op.ReadFrom(bytes.NewBuffer(p)) - return -} - -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (op *OperandQP) WriteTo(w io.Writer) (n int64, err error) { if n, err = op.MetaData.WriteTo(w); err != nil { @@ -386,13 +386,17 @@ func (op *OperandQP) WriteTo(w io.Writer) (n int64, err error) { return n + inc, err } -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (op *OperandQP) ReadFrom(r io.Reader) (n int64, err error) { if op == nil { @@ -408,10 +412,18 @@ func (op *OperandQP) ReadFrom(r io.Reader) (n int64, err error) { return n + inc, err } -// BinarySize returns the size in bytes of the object -// when encoded using Encode. -func (op *OperandQP) BinarySize() int { - return op.MetaData.BinarySize() + op.Value.BinarySize() +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (op *OperandQP) MarshalBinary() (data []byte, err error) { + buf := buffer.NewBufferSize(op.BinarySize()) + _, err = op.WriteTo(buf) + return buf.Bytes(), err +} + +// UnmarshalBinary decodes a slice of bytes generated by MarshalBinary +// or Read on the objeop. +func (op *OperandQP) UnmarshalBinary(p []byte) (err error) { + _, err = op.ReadFrom(buffer.NewBuffer(p)) + return } // Encode encodes the object into a binary form on a preallocated slice of bytes diff --git a/rlwe/params.go b/rlwe/params.go index f598fc84..70b66ef2 100644 --- a/rlwe/params.go +++ b/rlwe/params.go @@ -801,6 +801,7 @@ func (p Parameters) Equal(other ParametersInterface) (res bool) { } // MarshalBinary returns a []byte representation of the parameter set. +// This representation corresponds to the MarshalJSON representation. func (p Parameters) MarshalBinary() ([]byte, error) { return p.MarshalJSON() } diff --git a/rlwe/plaintext.go b/rlwe/plaintext.go index 2576cc85..5b68e9ac 100644 --- a/rlwe/plaintext.go +++ b/rlwe/plaintext.go @@ -48,6 +48,26 @@ func NewPlaintextRandom(prng sampling.PRNG, params ParametersInterface, level in return } +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (pt *Plaintext) ReadFrom(r io.Reader) (n int64, err error) { + if n, err = pt.OperandQ.ReadFrom(r); err != nil { + return + } + + pt.Value = &pt.OperandQ.Value[0] + return +} + // UnmarshalBinary decodes a slice of bytes generated by MarshalBinary // or Read on the objeop. func (pt *Plaintext) UnmarshalBinary(p []byte) (err error) { @@ -67,19 +87,3 @@ func (pt *Plaintext) Decode(p []byte) (n int, err error) { pt.Value = &pt.OperandQ.Value[0] return } - -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. -func (pt *Plaintext) ReadFrom(r io.Reader) (n int64, err error) { - if n, err = pt.OperandQ.ReadFrom(r); err != nil { - return - } - - pt.Value = &pt.OperandQ.Value[0] - return -} diff --git a/rlwe/power_basis.go b/rlwe/power_basis.go index f27b8ebc..bd329dcb 100644 --- a/rlwe/power_basis.go +++ b/rlwe/power_basis.go @@ -2,7 +2,6 @@ package rlwe import ( "bufio" - "bytes" "fmt" "io" "math/bits" @@ -164,27 +163,23 @@ func (p *PowerBasis) genPower(n int, lazy, rescale bool) (rescaltOut bool, err e return false, nil } -// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (p *PowerBasis) MarshalBinary() (data []byte, err error) { - buf := bytes.NewBuffer([]byte{}) - _, err = p.WriteTo(buf) - return buf.Bytes(), err +// BinarySize returns the size in bytes of the object +// when encoded using Encode. +func (p *PowerBasis) BinarySize() (size int) { + return 1 + p.Value.BinarySize() } -// UnmarshalBinary decodes a slice of bytes generated by -// MarshalBinary or WriteTo on the object. -func (p *PowerBasis) UnmarshalBinary(data []byte) (err error) { - _, err = p.ReadFrom(bytes.NewBuffer(data)) - return -} - -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (p *PowerBasis) WriteTo(w io.Writer) (n int64, err error) { switch w := w.(type) { @@ -207,13 +202,17 @@ func (p *PowerBasis) WriteTo(w io.Writer) (n int64, err error) { } } -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (p *PowerBasis) ReadFrom(r io.Reader) (n int64, err error) { switch r := r.(type) { case buffer.Reader: @@ -242,10 +241,18 @@ func (p *PowerBasis) ReadFrom(r io.Reader) (n int64, err error) { } } -// BinarySize returns the size in bytes of the object -// when encoded using Encode. -func (p *PowerBasis) BinarySize() (size int) { - return 1 + p.Value.BinarySize() +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (p *PowerBasis) MarshalBinary() (data []byte, err error) { + buf := buffer.NewBufferSize(p.BinarySize()) + _, err = p.WriteTo(buf) + return buf.Bytes(), err +} + +// UnmarshalBinary decodes a slice of bytes generated by +// MarshalBinary or WriteTo on the object. +func (p *PowerBasis) UnmarshalBinary(data []byte) (err error) { + _, err = p.ReadFrom(buffer.NewBuffer(data)) + return } // Encode encodes the object into a binary form on a preallocated slice of bytes diff --git a/rlwe/ringqp/poly.go b/rlwe/ringqp/poly.go index 4375b15a..b703cfa5 100644 --- a/rlwe/ringqp/poly.go +++ b/rlwe/ringqp/poly.go @@ -2,7 +2,6 @@ package ringqp import ( "bufio" - "bytes" "io" "github.com/google/go-cmp/cmp" @@ -121,7 +120,7 @@ func (p *Poly) Resize(levelQ, levelP int) { // Assumes that each coefficient takes 8 bytes. func (p *Poly) BinarySize() (dataLen int) { - dataLen = 2 + dataLen = 1 if p.Q != nil { dataLen += p.Q.BinarySize() @@ -133,52 +132,37 @@ func (p *Poly) BinarySize() (dataLen int) { return } -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (p *Poly) WriteTo(w io.Writer) (n int64, err error) { switch w := w.(type) { case buffer.Writer: + var hasQP byte if p.Q != nil { - - var inc int - if inc, err = buffer.WriteUint8(w, 1); err != nil { - return int64(n), err - } - - n += int64(inc) - - } else { - var inc int - if inc, err = buffer.WriteUint8(w, 0); err != nil { - return int64(n), err - } - - n += int64(inc) + hasQP = hasQP | 2 } - if p.P != nil { - var inc int - if inc, err = buffer.WriteUint8(w, 1); err != nil { - return int64(n), err - } - - n += int64(inc) - } else { - var inc int - if inc, err = buffer.WriteUint8(w, 0); err != nil { - return int64(n), err - } - - n += int64(inc) + hasQP = hasQP | 1 } + var inc int + if inc, err = buffer.WriteUint8(w, hasQP); err != nil { + return int64(n), err + } + + n += int64(inc) + if p.Q != nil { var inc int64 if inc, err = p.Q.WriteTo(w); err != nil { @@ -204,47 +188,44 @@ func (p *Poly) WriteTo(w io.Writer) (n int64, err error) { } } -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (p *Poly) ReadFrom(r io.Reader) (n int64, err error) { switch r := r.(type) { case buffer.Reader: - var hasQ, hasP uint8 - + var hasQP byte var inc int - if inc, err = buffer.ReadUint8(r, &hasQ); err != nil { + if inc, err = buffer.ReadUint8(r, &hasQP); err != nil { return n + int64(inc), err } n += int64(inc) - if inc, err = buffer.ReadUint8(r, &hasP); err != nil { - return n + int64(inc), err - } - - n += int64(inc) - - if hasQ == 1 { + if hasQP&2 == 2 { if p.Q == nil { p.Q = new(ring.Poly) } - var inc int64 - if inc, err = p.Q.ReadFrom(r); err != nil { - return n + inc, err + var inc64 int64 + if inc64, err = p.Q.ReadFrom(r); err != nil { + return n + inc64, err } - n += inc + n += inc64 } - if hasP == 1 { + if hasQP&1 == 1 { if p.P == nil { p.P = new(ring.Poly) @@ -265,6 +246,20 @@ func (p *Poly) ReadFrom(r io.Reader) (n int64, err error) { } } +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (p *Poly) MarshalBinary() (data []byte, err error) { + buf := buffer.NewBufferSize(p.BinarySize()) + _, err = p.WriteTo(buf) + return buf.Bytes(), err +} + +// UnmarshalBinary decodes a slice of bytes generated by +// MarshalBinary or WriteTo on the object. +func (p *Poly) UnmarshalBinary(data []byte) (err error) { + _, err = p.ReadFrom(buffer.NewBuffer(data)) + return err +} + // Encode encodes the object into a binary form on a preallocated slice of bytes // and returns the number of bytes written. func (p *Poly) Encode(data []byte) (n int, err error) { @@ -330,17 +325,3 @@ func (p *Poly) Decode(data []byte) (n int, err error) { return } - -// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (p *Poly) MarshalBinary() (data []byte, err error) { - buf := bytes.NewBuffer([]byte{}) - _, err = p.WriteTo(buf) - return buf.Bytes(), err -} - -// UnmarshalBinary decodes a slice of bytes generated by -// MarshalBinary or WriteTo on the object. -func (p *Poly) UnmarshalBinary(data []byte) (err error) { - _, err = p.ReadFrom(bytes.NewBuffer(data)) - return err -} diff --git a/rlwe/rlwe_benchmark_test.go b/rlwe/rlwe_benchmark_test.go index 32127ebf..c1ac373f 100644 --- a/rlwe/rlwe_benchmark_test.go +++ b/rlwe/rlwe_benchmark_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "github.com/tuneinsight/lattigo/v4/utils/buffer" ) func BenchmarkRLWE(b *testing.B) { @@ -136,46 +137,128 @@ func benchMarshalling(tc *TestContext, b *testing.B) { params := tc.params sk := tc.sk - ct := NewEncryptor(params, sk).EncryptZeroNew(params.MaxLevel()) - buf1 := make([]byte, ct.BinarySize()) - buf := bytes.NewBuffer(buf1) - b.Run(testString(params, params.MaxLevel(), "Marshalling/WriteTo"), func(b *testing.B) { + ctf := NewEncryptor(params, sk).EncryptZeroNew(params.MaxLevel()) + ct := ctf.Value + + badbuf := bytes.NewBuffer(make([]byte, ct.BinarySize())) + b.Run(testString(params, params.MaxLevel(), "Marshalling/WriteToBadBuf"), func(b *testing.B) { for i := 0; i < b.N; i++ { - buf.Reset() - ct.WriteTo(buf) + _, err := ct.WriteTo(badbuf) + + b.StopTimer() + if err != nil { + b.Fatal(err) + } + badbuf.Reset() + b.StartTimer() } }) - require.Equal(b, ct.BinarySize(), len(buf.Bytes())) + runtime.GC() - buf2 := make([]byte, ct.BinarySize()) + bytebuff := bytes.NewBuffer(make([]byte, ct.BinarySize())) + bufiobuf := bufio.NewWriter(bytebuff) + b.Run(testString(params, params.MaxLevel(), "Marshalling/WriteToIOBuf"), func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, err := ct.WriteTo(bufiobuf) + + b.StopTimer() + if err != nil { + b.Fatal(err) + } + bytebuff.Reset() + bufiobuf.Reset(bytebuff) + b.StartTimer() + } + }) + + runtime.GC() + + bsliceour := make([]byte, ct.BinarySize()) + ourbuf := buffer.NewBuffer(bsliceour) + b.Run(testString(params, params.MaxLevel(), "Marshalling/WriteToOurBuf"), func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, err := ct.WriteTo(ourbuf) + + b.StopTimer() + if err != nil { + b.Fatal(err) + } + ourbuf.Reset() + b.StartTimer() + } + }) + + runtime.GC() + require.Equal(b, ct.BinarySize(), len(ourbuf.Bytes())) + + encodeBuf := make([]byte, ct.BinarySize()) b.Run(testString(params, params.MaxLevel(), "Marshalling/Encode"), func(b *testing.B) { for i := 0; i < b.N; i++ { - ct.Encode(buf2) + _, err := ct.Encode(encodeBuf) + + b.StopTimer() + if err != nil { + b.Fatal(err) + } + b.StartTimer() } }) - rdr := bytes.NewReader(buf.Bytes()) - brdr := bufio.NewReader(rdr) - var ct2 Ciphertext - b.Run(testString(params, params.MaxLevel(), "Marshalling/ReadFrom"), func(b *testing.B) { + bufcmp := ourbuf.Bytes() + require.Equal(b, bufcmp, encodeBuf) + + rdr := bytes.NewReader(ourbuf.Bytes()) + //bufiordr := bufio.NewReaderSize(rdr, len(ourbuf.Bytes())) + bufiordr := bufio.NewReader(rdr) + ct2f := NewCiphertext(tc.params, 1, tc.params.MaxLevel()) + ct2 := ct2f.Value + b.Run(testString(params, params.MaxLevel(), "Marshalling/ReadFromIO"), func(b *testing.B) { for i := 0; i < b.N; i++ { + + _, err := ct2.ReadFrom(bufiordr) + + b.StopTimer() + if err != nil { + b.Fatal(err) + } rdr.Seek(0, 0) - brdr.Reset(rdr) - ct2.ReadFrom(brdr) - // if err != nil { - // b.Fatal(err) - // } + bufiordr.Reset(rdr) + b.StartTimer() } }) - require.True(b, ct.Equal(&ct2)) - var ct3 Ciphertext + // require.True(b, ct.Equal(ct2)) + + ct3f := NewCiphertext(tc.params, 1, tc.params.MaxLevel()) + ct3 := ct3f.Value + b.Run(testString(params, params.MaxLevel(), "Marshalling/ReadFromOur"), func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, err := ct3.ReadFrom(ourbuf) + + b.StopTimer() + if err != nil { + b.Fatal(err) + } + ourbuf.Reset() + b.StartTimer() + } + }) + require.True(b, ct.Equal(ct3)) + + ct4f := NewCiphertext(tc.params, 1, tc.params.MaxLevel()) + ct4 := ct4f.Value b.Run(testString(params, params.MaxLevel(), "Marshalling/Decode"), func(b *testing.B) { for i := 0; i < b.N; i++ { - ct3.Decode(buf2) + _, err := ct4.Decode(encodeBuf) + + b.StopTimer() + if err != nil { + b.Fatal(err) + } + b.StartTimer() } }) - require.True(b, ct.Equal(&ct3)) + require.True(b, ct.Equal(ct4)) } diff --git a/rlwe/secretkey.go b/rlwe/secretkey.go index 5994b882..2a599e3f 100644 --- a/rlwe/secretkey.go +++ b/rlwe/secretkey.go @@ -1,7 +1,6 @@ package rlwe import ( - "bytes" "io" "github.com/google/go-cmp/cmp" @@ -46,46 +45,51 @@ func (sk *SecretKey) CopyNew() *SecretKey { return &SecretKey{*sk.Value.CopyNew()} } -// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (sk *SecretKey) MarshalBinary() (p []byte, err error) { - buf := bytes.NewBuffer([]byte{}) - _, err = sk.WriteTo(buf) - return buf.Bytes(), err +// BinarySize returns the size in bytes of the object +// when encoded using Encode. +func (sk *SecretKey) BinarySize() (dataLen int) { + return sk.Value.BinarySize() } -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (sk *SecretKey) WriteTo(w io.Writer) (n int64, err error) { return sk.Value.WriteTo(w) } +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (sk *SecretKey) ReadFrom(r io.Reader) (n int64, err error) { + return sk.Value.ReadFrom(r) +} + +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (sk *SecretKey) MarshalBinary() (p []byte, err error) { + return sk.Value.MarshalBinary() +} + // UnmarshalBinary decodes a slice of bytes generated by // MarshalBinary or WriteTo on the object. func (sk *SecretKey) UnmarshalBinary(p []byte) (err error) { - _, err = sk.ReadFrom(bytes.NewBuffer(p)) - return -} - -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. -func (sk *SecretKey) ReadFrom(r io.Reader) (n int64, err error) { - return sk.Value.ReadFrom(r) -} - -// BinarySize returns the size in bytes of the object -// when encoded using Encode. -func (sk *SecretKey) BinarySize() (dataLen int) { - return sk.Value.BinarySize() + return sk.Value.UnmarshalBinary(p) } // Encode encodes the object into a binary form on a preallocated slice of bytes diff --git a/utils/buffer/buffer.go b/utils/buffer/buffer.go index 194dba1d..3f080a0b 100644 --- a/utils/buffer/buffer.go +++ b/utils/buffer/buffer.go @@ -1,2 +1,135 @@ -// Package buffer implement methods to write and read slices on bufio.Writer and bufio.Reader. +// Package buffer implement methods for efficiently writing and reading values +// to and from io.Writer and io.Reader that also expose their internal buffers. package buffer + +import ( + "fmt" + "io" +) + +// Writer is an interface for writers that expose their internal +// buffers. +// This interface is notably implemented by the bufio.Writer type +// (see https://pkg.go.dev/bufio#Writer) and by the Buffer type. +type Writer interface { + io.Writer + Flush() (err error) + AvailableBuffer() []byte + Available() int +} + +// Reader is an interface for readers that expose their internal +// buffers. +// This interface is notably implemented by the bufio.Reader type +// (see https://pkg.go.dev/bufio#Reader) and by the Buffer type. +type Reader interface { + io.Reader + Size() int + Peek(n int) ([]byte, error) + Discard(n int) (discarded int, err error) +} + +// Buffer is a simple []byte-based buffer that complies to the +// Writer and Reader interfaces. This type assumes that its +// backing slice has a fixed size and won't attempt to extend +// it. Instead, writes beyond capacity will result in an error. +type Buffer struct { + buf []byte + n int + off int +} + +// NewBuffer creates a new Buffer struct with buff as a backing +// []byte. The read and write offset are initialized at buff[0]. +// Hence, writing new data will overwrite the content of buff. +func NewBuffer(buff []byte) *Buffer { + b := new(Buffer) + b.buf = buff + return b +} + +// NewBufferSize creates a new Buffer with size capacity. +func NewBufferSize(size int) *Buffer { + b := new(Buffer) + b.buf = make([]byte, size) + return b +} + +// Write writes p into b. It returns the number of bytes written +// and an error if attempting to write passed the initial capacity +// of the buffer. Note that the case where p shares the same backing +// memory as b is optimized. +func (b *Buffer) Write(p []byte) (n int, err error) { + if len(p)+b.n > cap(b.buf) { + return 0, fmt.Errorf("buffer too small") + } + inc := copy(b.buf[b.n:], p) // This is optimized if &b.buf[b.n:][0] == &p[0] + b.n += inc + return inc, nil +} + +// Flush doesn't do anything on this slice-based buffer. +func (b *Buffer) Flush() (err error) { + return nil +} + +// AvailableBuffer returns an empty buffer with b.Available() capacity, to be +// directly appended to and passed to a Write call. The buffer is only valid +// until the next write operation on b. +func (b *Buffer) AvailableBuffer() []byte { + return b.buf[b.n:][:0] +} + +// Available returns the number of bytes available for writes on the buffer. +func (b *Buffer) Available() int { + return len(b.buf) - b.n +} + +// Bytes returns the backing slice. +func (b *Buffer) Bytes() []byte { + return b.buf +} + +// Reset re-initializes the read and write offsets of b. +func (b *Buffer) Reset() { + b.n = 0 + b.off = 0 +} + +// Read reads len(p) bytes from the read offset of b into p. It returns the +// number n of bytes read and an error if n < len(p). +func (b *Buffer) Read(p []byte) (n int, err error) { + n = copy(p, b.buf[b.off:]) + b.off += n + if n < len(p) { + return n, io.EOF + } + return n, nil +} + +// Size returns the size of the buffer available for read. +func (b *Buffer) Size() int { + return len(b.buf) - b.off +} + +// Peek returns the next n bytes without advancing the read offset, directly +// as a reslice of the internal buffer. It returns an error if the number of +// returned bytes is smaller than n. +func (b *Buffer) Peek(n int) ([]byte, error) { + if b.off+n > len(b.buf) { + return b.buf[b.off:], io.EOF + } + return b.buf[b.off : b.off+n], nil +} + +// Discard skips the next n bytes, returning the number of bytes discarded. If +// Discard skips fewer than n bytes, it also returns an error. +func (b *Buffer) Discard(n int) (discarded int, err error) { + remain := len(b.buf) - b.off + if n > remain { + b.off = len(b.buf) + return remain, io.EOF + } + b.off += n + return n, nil +} diff --git a/utils/buffer/reader.go b/utils/buffer/reader.go index 8d4957a2..84e90666 100644 --- a/utils/buffer/reader.go +++ b/utils/buffer/reader.go @@ -3,22 +3,11 @@ package buffer import ( "encoding/binary" "fmt" - "io" "github.com/tuneinsight/lattigo/v4/utils" ) -// Reader defines a interface comprising of the minimum subset -// of methods defined by the type bufio.Reader necessary to run -// the functions defined in this file. -// See the documentation of bufio.Reader: https://pkg.go.dev/bufio. -type Reader interface { - io.Reader - Size() int - Peek(n int) ([]byte, error) - Discard(n int) (discarded int, err error) -} - +// ReadInt reads an int values from r and stores the result into *c. func ReadInt(r Reader, c *int) (n int, err error) { if c == nil { @@ -28,46 +17,48 @@ func ReadInt(r Reader, c *int) (n int, err error) { return ReadUint64(r, utils.PointyIntToPointUint64(c)) } +// ReadUint8 reads a byte from r and stores the result into *c. func ReadUint8(r Reader, c *uint8) (n int, err error) { if c == nil { return 0, fmt.Errorf("cannot ReadUint8: c is nil") } - var bb = [1]byte{} - - if n, err = r.Read(bb[:]); err != nil { - return + slice, err := r.Peek(1) + if err != nil { + return len(slice), err } // Reads one byte - *c = uint8(bb[0]) + *c = uint8(slice[0]) - return n, nil + return r.Discard(1) } +// ReadUint8Slice reads a slice of byte from r and stores the result into c. func ReadUint8Slice(r Reader, c []uint8) (n int, err error) { return r.Read(c) } +// ReadUint16 reads a uint16 from r and stores the result into *c. func ReadUint16(r Reader, c *uint16) (n int, err error) { if c == nil { return 0, fmt.Errorf("cannot ReadUint16: c is nil") } - var bb = [2]byte{} - - if n, err = r.Read(bb[:]); err != nil { - return + slice, err := r.Peek(2) + if err != nil { + return len(slice), err } // Reads one byte - *c = binary.LittleEndian.Uint16(bb[:]) + *c = binary.LittleEndian.Uint16(slice) - return n, nil + return r.Discard(2) } +// ReadUint16Slice reads a slice of uint16 from r and stores the result into c. func ReadUint16Slice(r Reader, c []uint16) (n int, err error) { // c is empty, return @@ -84,8 +75,7 @@ func ReadUint16Slice(r Reader, c []uint16) (n int, err error) { // Then returns the writen bytes if slice, err = r.Peek(size); err != nil { - fmt.Println(err) - return + return len(slice), err } buffered := len(slice) >> 1 @@ -121,24 +111,25 @@ func ReadUint16Slice(r Reader, c []uint16) (n int, err error) { return n + inc, nil } +// ReadUint32 reads a uint32 from r and stores the result into *c. func ReadUint32(r Reader, c *uint32) (n int, err error) { if c == nil { return 0, fmt.Errorf("cannot ReadUint32: c is nil") } - var bb = [4]byte{} - - if n, err = r.Read(bb[:]); err != nil { - return + slice, err := r.Peek(4) + if err != nil { + return len(slice), err } // Reads one byte - *c = binary.LittleEndian.Uint32(bb[:]) + *c = binary.LittleEndian.Uint32(slice) - return n, nil + return r.Discard(4) } +// ReadUint32Slice reads a slice of uint32 from r and stores the result into c. func ReadUint32Slice(r Reader, c []uint32) (n int, err error) { // c is empty, return @@ -156,8 +147,7 @@ func ReadUint32Slice(r Reader, c []uint32) (n int, err error) { // Then returns the writen bytes if slice, err = r.Peek(size); err != nil { - fmt.Println(err) - return + return len(slice), err } buffered := len(slice) >> 2 @@ -193,24 +183,25 @@ func ReadUint32Slice(r Reader, c []uint32) (n int, err error) { return n + inc, nil } +// ReadUint64 reads a uint64 from r and stores the result into c. func ReadUint64(r Reader, c *uint64) (n int, err error) { if c == nil { return 0, fmt.Errorf("cannot ReadUint64: c is nil") } - var bb = [8]byte{} - - if n, err = r.Read(bb[:]); err != nil { - return + bytes, err := r.Peek(8) + if err != nil { + return len(bytes), err } // Reads one byte - *c = binary.LittleEndian.Uint64(bb[:]) + *c = binary.LittleEndian.Uint64(bytes) - return n, nil + return r.Discard(8) } +// ReadUint64Slice reads a slice of uint64 from r and stores the result into c. func ReadUint64Slice(r Reader, c []uint64) (n int, err error) { // c is empty, return diff --git a/utils/buffer/writer.go b/utils/buffer/writer.go index 0b024017..c11c7052 100644 --- a/utils/buffer/writer.go +++ b/utils/buffer/writer.go @@ -2,32 +2,24 @@ package buffer import ( "encoding/binary" - "io" ) -// Writer defines a interface comprising of the minimum subset -// of methods defined by the type bufio.Writer necessary to run -// the functions defined in this file. -// See the documentation of bufio.Writer: https://pkg.go.dev/bufio. -type Writer interface { - io.Writer - Flush() (err error) - AvailableBuffer() []byte - Available() int -} - +// WriteInt writes an int c to w. func WriteInt(w Writer, c int) (n int, err error) { return WriteUint64(w, uint64(c)) } +// WriteUint8 writes a byte c to w. func WriteUint8(w Writer, c uint8) (n int, err error) { return w.Write([]byte{c}) } +// WriteUint8Slice writes a slice of bytes c to w. func WriteUint8Slice(w Writer, c []uint8) (n int, err error) { return w.Write(c) } +// WriteUint16 writes a uint16 c to w. func WriteUint16(w Writer, c uint16) (n int, err error) { buf := w.AvailableBuffer() @@ -38,13 +30,12 @@ func WriteUint16(w Writer, c uint16) (n int, err error) { } } - var bb = [2]byte{} - binary.LittleEndian.PutUint16(bb[:], c) - buf = append(buf, bb[:]...) + binary.LittleEndian.PutUint16(buf[:2], c) - return w.Write(buf) + return w.Write(buf[:2]) } +// WriteUint16Slice writes a slice of uint16 c to w. func WriteUint16Slice(w Writer, c []uint16) (n int, err error) { if len(c) == 0 { @@ -64,13 +55,10 @@ func WriteUint16Slice(w Writer, c []uint16) (n int, err error) { available = w.Available() >> 1 } - var bb = [2]byte{} - if N := len(c); N <= available { // If there is enough space in the available buffer - + buf = buf[:N<<1] for i := 0; i < N; i++ { - binary.LittleEndian.PutUint16(bb[:], c[i]) - buf = append(buf, bb[:]...) + binary.LittleEndian.PutUint16(buf[i<<2:(i<<2)+2], c[i]) } return w.Write(buf) @@ -78,8 +66,8 @@ func WriteUint16Slice(w Writer, c []uint16) (n int, err error) { // First fills the space for i := 0; i < available; i++ { - binary.LittleEndian.PutUint16(bb[:], c[i]) - buf = append(buf, bb[:]...) + buf = buf[:available<<1] + binary.LittleEndian.PutUint16(buf[i<<1:(i<<1)+2], c[i]) } var inc int @@ -102,6 +90,7 @@ func WriteUint16Slice(w Writer, c []uint16) (n int, err error) { return n + inc, nil } +// WriteUint32 writes a uint32 c into w. func WriteUint32(w Writer, c uint32) (n int, err error) { buf := w.AvailableBuffer() @@ -112,13 +101,12 @@ func WriteUint32(w Writer, c uint32) (n int, err error) { } } - var bb = [4]byte{} - binary.LittleEndian.PutUint32(bb[:], c) - buf = append(buf, bb[:]...) - + buf = buf[:4] + binary.LittleEndian.PutUint32(buf, c) return w.Write(buf) } +// WriteUint32Slice writes a slice of uint32 c into w. func WriteUint32Slice(w Writer, c []uint32) (n int, err error) { if len(c) == 0 { @@ -138,22 +126,18 @@ func WriteUint32Slice(w Writer, c []uint32) (n int, err error) { available = w.Available() >> 2 } - var bb = [4]byte{} - if N := len(c); N <= available { // If there is enough space in the available buffer - + buf = buf[:N<<2] for i := 0; i < N; i++ { - binary.LittleEndian.PutUint32(bb[:], c[i]) - buf = append(buf, bb[:]...) + binary.LittleEndian.PutUint32(buf[i<<2:(i<<2)+4], c[i]) } - return w.Write(buf) } // First fills the space + buf = buf[:available<<2] for i := 0; i < available; i++ { - binary.LittleEndian.PutUint32(bb[:], c[i]) - buf = append(buf, bb[:]...) + binary.LittleEndian.PutUint32(buf[i<<2:(i<<2)+4], c[i]) } var inc int @@ -176,6 +160,7 @@ func WriteUint32Slice(w Writer, c []uint32) (n int, err error) { return n + inc, nil } +// WriteUint64 writes a uint64 c into w. func WriteUint64(w Writer, c uint64) (n int, err error) { buf := w.AvailableBuffer() @@ -186,13 +171,12 @@ func WriteUint64(w Writer, c uint64) (n int, err error) { } } - var bb = [8]byte{} - binary.LittleEndian.PutUint64(bb[:], c) - buf = append(buf, bb[:]...) + binary.LittleEndian.PutUint64(buf[:8], c) - return w.Write(buf) + return w.Write(buf[:8]) } +// WriteUint64Slice writes a slice of uint64 into w. func WriteUint64Slice(w Writer, c []uint64) (n int, err error) { if len(c) == 0 { @@ -212,22 +196,18 @@ func WriteUint64Slice(w Writer, c []uint64) (n int, err error) { available = w.Available() >> 3 } - var bb = [8]byte{} - if N := len(c); N <= available { // If there is enough space in the available buffer - + buf = buf[:N<<3] for i := 0; i < N; i++ { - binary.LittleEndian.PutUint64(bb[:], c[i]) - buf = append(buf, bb[:]...) + binary.LittleEndian.PutUint64(buf[i<<3:(i<<3)+8], c[i]) } - return w.Write(buf) } // First fills the space + buf = buf[:available<<3] for i := 0; i < available; i++ { - binary.LittleEndian.PutUint64(bb[:], c[i]) - buf = append(buf, bb[:]...) + binary.LittleEndian.PutUint64(buf[i<<3:(i<<3)+8], c[i]) } var inc int diff --git a/utils/structs/map.go b/utils/structs/map.go index 77275f5f..bd72aa75 100644 --- a/utils/structs/map.go +++ b/utils/structs/map.go @@ -2,7 +2,6 @@ package structs import ( "bufio" - "bytes" "encoding/binary" "fmt" "io" @@ -32,13 +31,17 @@ func (m Map[K, T]) CopyNew() *Map[K, T] { return &mcpy } -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// 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 (m *Map[K, T]) WriteTo(w io.Writer) (n int64, err error) { if w, isWritable := any(new(T)).(io.WriterTo); !isWritable { @@ -78,13 +81,17 @@ func (m *Map[K, T]) WriteTo(w io.Writer) (n int64, err error) { } } -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (m *Map[K, T]) ReadFrom(r io.Reader) (n int64, err error) { if r, isReadable := any(new(T)).(io.ReaderFrom); !isReadable { @@ -113,7 +120,7 @@ func (m *Map[K, T]) ReadFrom(r io.Reader) (n int64, err error) { } n += int64(inc1) - var val *T = new(T) + var val = new(T) var inc2 int64 if inc2, err = any(val).(io.ReaderFrom).ReadFrom(r); err != nil { return n + inc2, err @@ -148,6 +155,20 @@ func (m Map[K, T]) BinarySize() (size int) { return } +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (m *Map[K, T]) MarshalBinary() (p []byte, err error) { + buf := buffer.NewBufferSize(m.BinarySize()) + _, err = m.WriteTo(buf) + return buf.Bytes(), err +} + +// UnmarshalBinary decodes a slice of bytes generated by +// MarshalBinary or WriteTo on the object. +func (m *Map[K, T]) UnmarshalBinary(p []byte) (err error) { + _, err = m.ReadFrom(buffer.NewBuffer(p)) + return +} + // Encode encodes the object into a binary form on a preallocated slice of bytes // and returns the number of bytes written. func (m *Map[K, T]) Encode(p []byte) (n int, err error) { @@ -200,7 +221,7 @@ func (m *Map[K, T]) Decode(p []byte) (n int, err error) { n += 8 var inc int - var val *T = new(T) + var val = new(T) if inc, err = any(val).(Decoder).Decode(p[n:]); err != nil { return n + inc, err } @@ -210,17 +231,3 @@ func (m *Map[K, T]) Decode(p []byte) (n int, err error) { return } - -// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (m *Map[K, T]) MarshalBinary() (p []byte, err error) { - buf := bytes.NewBuffer([]byte{}) - _, err = m.WriteTo(buf) - return buf.Bytes(), err -} - -// UnmarshalBinary decodes a slice of bytes generated by -// MarshalBinary or WriteTo on the object. -func (m *Map[K, T]) UnmarshalBinary(p []byte) (err error) { - _, err = m.ReadFrom(bytes.NewBuffer(p)) - return -} diff --git a/utils/structs/matrix.go b/utils/structs/matrix.go index 2de13939..5d4eb23f 100644 --- a/utils/structs/matrix.go +++ b/utils/structs/matrix.go @@ -2,7 +2,6 @@ package structs import ( "bufio" - "bytes" "encoding/binary" "fmt" "io" @@ -33,13 +32,33 @@ func (m Matrix[T]) CopyNew() *Matrix[T] { return &mcpy } -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. +// BinarySize returns the size in bytes of the object +// when encoded using Encode. +func (m Matrix[T]) BinarySize() (size int) { + + if s, isSizable := any(new(T)).(BinarySizer); !isSizable { + panic(fmt.Errorf("vector component of type %T does not comply to %T", new(T), s)) + } + + size += 8 + + for _, v := range m { + size += (*Vector[T])(&v).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. +// +// 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 (m *Matrix[T]) WriteTo(w io.Writer) (n int64, err error) { if w, isWritable := any(new(T)).(io.WriterTo); !isWritable { @@ -71,13 +90,17 @@ func (m *Matrix[T]) WriteTo(w io.Writer) (n int64, err error) { } } -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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 (m *Matrix[T]) ReadFrom(r io.Reader) (n int64, err error) { if r, isReadable := any(new(T)).(io.ReaderFrom); !isReadable { @@ -113,19 +136,17 @@ func (m *Matrix[T]) ReadFrom(r io.Reader) (n int64, err error) { } } -// BinarySize returns the size in bytes of the object -// when encoded using Encode. -func (m Matrix[T]) BinarySize() (size int) { +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (m *Matrix[T]) MarshalBinary() (p []byte, err error) { + buf := buffer.NewBufferSize(m.BinarySize()) + _, err = m.WriteTo(buf) + return buf.Bytes(), err +} - if s, isSizable := any(new(T)).(BinarySizer); !isSizable { - panic(fmt.Errorf("vector component of type %T does not comply to %T", new(T), s)) - } - - size += 8 - - for _, v := range m { - size += (*Vector[T])(&v).BinarySize() - } +// UnmarshalBinary decodes a slice of bytes generated by +// MarshalBinary or WriteTo on the object. +func (m *Matrix[T]) UnmarshalBinary(p []byte) (err error) { + _, err = m.ReadFrom(buffer.NewBuffer(p)) return } @@ -177,17 +198,3 @@ func (m *Matrix[T]) Decode(p []byte) (n int, err error) { return n, nil } - -// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (m *Matrix[T]) MarshalBinary() (p []byte, err error) { - buf := bytes.NewBuffer([]byte{}) - _, err = m.WriteTo(buf) - return buf.Bytes(), err -} - -// UnmarshalBinary decodes a slice of bytes generated by -// MarshalBinary or WriteTo on the object. -func (m *Matrix[T]) UnmarshalBinary(p []byte) (err error) { - _, err = m.ReadFrom(bytes.NewBuffer(p)) - return -} diff --git a/utils/structs/vector.go b/utils/structs/vector.go index cd2d77da..a03e39ea 100644 --- a/utils/structs/vector.go +++ b/utils/structs/vector.go @@ -2,8 +2,6 @@ package structs import ( "bufio" - "bytes" - "encoding" "fmt" "io" @@ -12,21 +10,22 @@ import ( "github.com/tuneinsight/lattigo/v4/utils/buffer" ) -type binarySerializer interface { - encoding.BinaryMarshaler - encoding.BinaryUnmarshaler - io.WriterTo - io.ReaderFrom - // Encoder - // Decoder -} +// type binarySerializer interface { +// encoding.BinaryMarshaler +// encoding.BinaryUnmarshaler +// io.WriterTo +// io.ReaderFrom +// // Encoder +// // Decoder +// } type Vector[T any] []T // CopyNew creates a copy of the oject. func (v Vector[T]) CopyNew() *Vector[T] { - if c, isCopiable := any(new(T)).(CopyNewer[T]); !isCopiable { + var ct *T + if c, isCopiable := any(ct).(CopyNewer[T]); !isCopiable { panic(fmt.Errorf("vector component of type %T does not comply to %T", new(T), c)) } @@ -37,30 +36,50 @@ func (v Vector[T]) CopyNew() *Vector[T] { return &vcpy } -// WriteTo writes the object on an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Writer, which defines -// a subset of the method of the bufio.Writer. -// If w is not compliant to the buffer.Writer interface, it will be wrapped in -// a new bufio.Writer. -// For additional information, see lattigo/utils/buffer/writer.go. +// BinarySize returns the size in bytes of the object +// when encoded using Encode. +func (v Vector[T]) BinarySize() (size int) { + + var st *T + if s, isSizable := any(st).(BinarySizer); !isSizable { + panic(fmt.Errorf("vector component of type %T does not comply to %T", st, s)) + } + + size += 8 + for _, c := range v { + size += any(&c).(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. +// +// 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) { - if w, isWritable := any(new(T)).(io.WriterTo); !isWritable { - return 0, fmt.Errorf("vector component of type %T does not comply to %T", new(T), w) + var o *T + if wt, isWritable := any(o).(io.WriterTo); !isWritable { + return 0, fmt.Errorf("vector component of type %T does not comply to %T", o, wt) } switch w := w.(type) { case buffer.Writer: - vval := *v var inc int - if inc, err = buffer.WriteInt(w, len(vval)); err != nil { + if inc, err = buffer.WriteInt(w, len(*v)); err != nil { return int64(inc), err } n += int64(inc) - for _, c := range vval { + for _, c := range *v { inc, err := any(&c).(io.WriterTo).WriteTo(w) n += inc if err != nil { @@ -75,20 +94,24 @@ func (v *Vector[T]) WriteTo(w io.Writer) (n int64, err error) { } } -// ReadFrom reads on the object from an io.Writer. -// To ensure optimal efficiency and minimal allocations, the user is encouraged -// to provide a struct implementing the interface buffer.Reader, which defines -// a subset of the method of the bufio.Reader. -// If r is not compliant to the buffer.Reader interface, it will be wrapped in -// a new bufio.Reader. -// For additional information, see lattigo/utils/buffer/reader.go. +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// 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) { - if r, isReadable := any(new(T)).(io.ReaderFrom); !isReadable { - return 0, fmt.Errorf("vector component of type %T does not comply to %T", new(T), r) + var rt *T + if r, isReadable := any(rt).(io.ReaderFrom); !isReadable { + return 0, fmt.Errorf("vector component of type %T does not comply to %T", rt, r) } - // TODO: when has access to Reader's buffer, call Decode ? switch r := r.(type) { case buffer.Reader: var size int @@ -119,18 +142,17 @@ func (v *Vector[T]) ReadFrom(r io.Reader) (n int64, err error) { } } -// BinarySize returns the size in bytes of the object -// when encoded using Encode. -func (v Vector[T]) BinarySize() (size int) { +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (v *Vector[T]) MarshalBinary() (p []byte, err error) { + buf := buffer.NewBufferSize(v.BinarySize()) + _, err = v.WriteTo(buf) + return buf.Bytes(), err +} - if s, isSizable := any(new(T)).(BinarySizer); !isSizable { - panic(fmt.Errorf("vector component of type %T does not comply to %T", new(T), s)) - } - - size += 8 - for _, c := range v { - size += any(&c).(BinarySizer).BinarySize() - } +// UnmarshalBinary decodes a slice of bytes generated by +// MarshalBinary or WriteTo on the object. +func (v *Vector[T]) UnmarshalBinary(p []byte) (err error) { + _, err = v.ReadFrom(buffer.NewBuffer(p)) return } @@ -138,8 +160,9 @@ func (v Vector[T]) BinarySize() (size int) { // and returns the number of bytes written. func (v *Vector[T]) Encode(b []byte) (n int, err error) { - if e, isEncodable := any(new(T)).(Encoder); !isEncodable { - panic(fmt.Errorf("vector component of type %T does not comply to %T", new(T), e)) + var et *T + if e, isEncodable := any(et).(Encoder); !isEncodable { + panic(fmt.Errorf("vector component of type %T does not comply to %T", et, e)) } vval := *v @@ -149,7 +172,7 @@ func (v *Vector[T]) Encode(b []byte) (n int, err error) { var inc int for _, c := range vval { - if inc, err := any(&c).(Encoder).Encode(b[n:]); err != nil { + if inc, err = any(&c).(Encoder).Encode(b[n:]); err != nil { return n + inc, err } n += inc @@ -166,7 +189,7 @@ func (v *Vector[T]) Decode(p []byte) (n int, err error) { panic(fmt.Errorf("vector component of type %T does not comply to %T", new(T), d)) } - size := int(binary.LittleEndian.Uint64(p[n:])) // TODO: there is a bug here but it is not caught by the tests. + size := int(binary.LittleEndian.Uint64(p)) n += 8 if cap(*v) < size { @@ -185,16 +208,20 @@ func (v *Vector[T]) Decode(p []byte) (n int, err error) { return } -// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (v *Vector[T]) MarshalBinary() (p []byte, err error) { - buf := bytes.NewBuffer([]byte{}) - _, err = v.WriteTo(buf) - return buf.Bytes(), err +type Equatable[T any] interface { + Equal(*T) bool } -// UnmarshalBinary decodes a slice of bytes generated by -// MarshalBinary or WriteTo on the object. -func (v *Vector[T]) UnmarshalBinary(p []byte) (err error) { - _, err = v.ReadFrom(bytes.NewBuffer(p)) - return +func (v Vector[T]) Equal(other Vector[T]) bool { + + if d, isEquatable := any(new(T)).(Equatable[T]); !isEquatable { + panic(fmt.Errorf("vector component of type %T does not comply to %T", new(T), d)) + } + + isEqual := true + for i, v := range v { + isEqual = isEqual && any(&v).(Equatable[T]).Equal(&other[i]) + } + + return isEqual }