Files
lattigo/utils/buffer/writer.go
2023-10-13 14:38:42 +02:00

391 lines
9.0 KiB
Go

package buffer
import (
"encoding/binary"
"fmt"
"unsafe"
)
// WriteAsUint64 casts &T to an *uint64 and writes it to w.
// User must ensure that T can be stored in an uint64.
func WriteAsUint64[T any](w Writer, c T) (n int64, err error) {
/* #nosec G103 -- behavior and consequences well understood, pointer type cast */
return WriteUint64(w, *(*uint64)(unsafe.Pointer(&c)))
}
// WriteAsUint32 casts &T to an *uint32 and writes it to w.
// User must ensure that T can be stored in an uint32.
func WriteAsUint32[T any](w Writer, c T) (n int64, err error) {
/* #nosec G103 -- behavior and consequences well understood, pointer type cast */
return WriteUint32(w, *(*uint32)(unsafe.Pointer(&c)))
}
// WriteAsUint16 casts &T to an *uint16 and writes it to w.
// User must ensure that T can be stored in an uint16.
func WriteAsUint16[T any](w Writer, c T) (n int64, err error) {
/* #nosec G103 -- behavior and consequences well understood, pointer type cast */
return WriteUint16(w, *(*uint16)(unsafe.Pointer(&c)))
}
// WriteAsUint8 casts &T to an *uint8 and writes it to w.
// User must ensure that T can be stored in an uint8.
func WriteAsUint8[T any](w Writer, c T) (n int64, err error) {
/* #nosec G103 -- behavior and consequences well understood, pointer type cast */
return WriteUint8(w, *(*uint8)(unsafe.Pointer(&c)))
}
// WriteAsUint64Slice casts &[]T into *[]uint64 and writes it to w.
// User must ensure that T can be stored in an uint64.
func WriteAsUint64Slice[T any](w Writer, c []T) (n int64, err error) {
/* #nosec G103 -- behavior and consequences well understood, pointer type cast */
return WriteUint64Slice(w, *(*[]uint64)(unsafe.Pointer(&c)))
}
// WriteAsUint32Slice casts &[]T into *[]uint32 and writes it to w.
// User must ensure that T can be stored in an uint32.
func WriteAsUint32Slice[T any](w Writer, c []T) (n int64, err error) {
/* #nosec G103 -- behavior and consequences well understood, pointer type cast */
return WriteUint32Slice(w, *(*[]uint32)(unsafe.Pointer(&c)))
}
// WriteAsUint16Slice casts &[]T into *[]uint16 and writes it to w.
// User must ensure that T can be stored in an uint16.
func WriteAsUint16Slice[T any](w Writer, c []T) (n int64, err error) {
/* #nosec G103 -- behavior and consequences well understood, pointer type cast */
return WriteUint16Slice(w, *(*[]uint16)(unsafe.Pointer(&c)))
}
// WriteAsUint8Slice casts &[]T into *[]uint8 and writes it to w.
// User must ensure that T can be stored in an uint8.
func WriteAsUint8Slice[T any](w Writer, c []T) (n int64, err error) {
/* #nosec G103 -- behavior and consequences well understood, pointer type cast */
return WriteUint8Slice(w, *(*[]uint8)(unsafe.Pointer(&c)))
}
// Write writes a slice of bytes to w.
func Write(w Writer, c []byte) (n int64, err error) {
nint, err := w.Write(c)
return int64(nint), err
}
// WriteUint8 writes a byte c to w.
func WriteUint8(w Writer, c uint8) (n int64, err error) {
if w.Available() == 0 {
if err = w.Flush(); err != nil {
return
}
if w.Available() == 0 {
return 0, fmt.Errorf("cannot WriteUint8: available buffer is zero even after flush")
}
}
nint, err := w.Write([]byte{c})
return int64(nint), err
}
// WriteUint8Slice writes a slice of bytes c to w.
func WriteUint8Slice(w Writer, c []uint8) (n int64, err error) {
if len(c) == 0 {
return
}
// Remaining available space in the internal buffer
available := w.Available()
if available == 0 {
if err = w.Flush(); err != nil {
return
}
available = w.Available()
if available == 0 {
return 0, fmt.Errorf("cannot WriteUint8Slice: available buffer/2 is zero even after flush")
}
}
buf := w.AvailableBuffer()
if N := len(c); N <= available { // If there is enough space in the available buffer
buf = buf[:N]
copy(buf, c)
nint, err := w.Write(buf)
return int64(nint), err
}
// First fills the space
buf = buf[:available]
copy(buf, c)
var inc int
if inc, err = w.Write(buf); err != nil {
return n + int64(inc), err
}
n += int64(inc)
// Flushes
if err = w.Flush(); err != nil {
return n, err
}
// Then recurses on itself with the remaining slice
var inc64 int64
inc64, err = WriteUint8Slice(w, c[available:])
return n + inc64, err
}
// WriteUint16 writes a uint16 c to w.
func WriteUint16(w Writer, c uint16) (n int64, err error) {
if w.Available()>>1 == 0 {
if err = w.Flush(); err != nil {
return
}
if w.Available()>>1 == 0 {
return 0, fmt.Errorf("cannot WriteUint16: available buffer/2 is zero even after flush")
}
}
buf := w.AvailableBuffer()[:2]
binary.LittleEndian.PutUint16(buf, c)
nint, err := w.Write(buf)
return int64(nint), err
}
// WriteUint16Slice writes a slice of uint16 c to w.
func WriteUint16Slice(w Writer, c []uint16) (n int64, err error) {
if len(c) == 0 {
return
}
// Remaining available space in the internal buffer
available := w.Available() >> 1
if available == 0 {
if err = w.Flush(); err != nil {
return
}
available = w.Available() >> 1
if available == 0 {
return 0, fmt.Errorf("cannot WriteUint16Slice: available buffer/2 is zero even after flush")
}
}
buf := w.AvailableBuffer()
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(buf[i<<1:], c[i])
}
nint, err := w.Write(buf)
return int64(nint), err
}
buf = buf[:available<<1]
// First fills the space
for i := 0; i < available; i++ {
binary.LittleEndian.PutUint16(buf[i<<1:], c[i])
}
var inc int
if inc, err = w.Write(buf); err != nil {
return n + int64(inc), err
}
n += int64(inc)
// Flushes
if err = w.Flush(); err != nil {
return n, err
}
// Then recurses on itself with the remaining slice
var inc64 int64
inc64, err = WriteUint16Slice(w, c[available:])
return n + inc64, err
}
// WriteUint32 writes a uint32 c into w.
func WriteUint32(w Writer, c uint32) (n int64, err error) {
if w.Available()>>2 == 0 {
if err = w.Flush(); err != nil {
return
}
if w.Available()>>2 == 0 {
return 0, fmt.Errorf("cannot WriteUint32: available buffer/4 is zero even after flush")
}
}
buf := w.AvailableBuffer()[:4]
binary.LittleEndian.PutUint32(buf, c)
nint, err := w.Write(buf)
return int64(nint), err
}
// WriteUint32Slice writes a slice of uint32 c into w.
func WriteUint32Slice(w Writer, c []uint32) (n int64, err error) {
if len(c) == 0 {
return
}
// Remaining available space in the internal buffer
available := w.Available() >> 2
if available == 0 {
if err = w.Flush(); err != nil {
return
}
available = w.Available() >> 2
if available == 0 {
return 0, fmt.Errorf("cannot WriteUint32Slice: available buffer/4 is zero even after flush")
}
}
buf := w.AvailableBuffer()
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(buf[i<<2:], c[i])
}
nint, err := w.Write(buf)
return int64(nint), err
}
// First fills the space
buf = buf[:available<<2]
for i := 0; i < available; i++ {
binary.LittleEndian.PutUint32(buf[i<<2:], c[i])
}
var inc int
if inc, err = w.Write(buf); err != nil {
return n + int64(inc), err
}
n += int64(inc)
// Flushes
if err = w.Flush(); err != nil {
return n, err
}
// Then recurses on itself with the remaining slice
var inc64 int64
inc64, err = WriteUint32Slice(w, c[available:])
return n + inc64, err
}
// WriteUint64 writes a uint64 c into w.
func WriteUint64(w Writer, c uint64) (n int64, err error) {
if w.Available()>>3 == 0 {
if err = w.Flush(); err != nil {
return
}
if w.Available()>>3 == 0 {
return 0, fmt.Errorf("cannot WriteUint64: available buffer/8 is zero even after flush")
}
}
buf := w.AvailableBuffer()[:8]
binary.LittleEndian.PutUint64(buf, c)
nint, err := w.Write(buf)
return int64(nint), err
}
// WriteUint64Slice writes a slice of uint64 into w.
func WriteUint64Slice(w Writer, c []uint64) (n int64, err error) {
if len(c) == 0 {
return
}
// Remaining available space in the internal buffer
available := w.Available() >> 3
if available == 0 {
if err = w.Flush(); err != nil {
return
}
available = w.Available() >> 3
if available == 0 {
return 0, fmt.Errorf("cannot WriteUint64Slice: available buffer/8 is zero even after flush")
}
}
buf := w.AvailableBuffer()
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(buf[i<<3:], c[i])
}
nint, err := w.Write(buf)
return int64(nint), err
}
// First fills the space
buf = buf[:available<<3]
for i := 0; i < available; i++ {
binary.LittleEndian.PutUint64(buf[i<<3:], c[i])
}
var inc int
if inc, err = w.Write(buf); err != nil {
return n + int64(inc), err
}
n += int64(inc)
// Flushes
if err = w.Flush(); err != nil {
return n, err
}
// Then recurses on itself with the remaining slice
var inc64 int64
inc64, err = WriteUint64Slice(w, c[available:])
return n + inc64, err
}