Add SeekableBuffer

Add a specialised bytes.Buffer variant with seeking capabilities. This allows writers without seeking support to serialize using NodeSerializerStreamed.
This commit is contained in:
zervo 2025-02-14 12:04:04 +01:00
parent b7f8103a94
commit 96f0b64a11
2 changed files with 95 additions and 4 deletions

View file

@ -1,15 +1,16 @@
package sbhpfv1
import (
"bytes"
"io"
"git.zervo.org/FLUX/GoSBHPF/pkg/seekablebuffer"
)
// SerializeNodeStream serializes a node object into a SBHPF node.
// It is a wrapper around SerializeNode that temporarily stores the serialized nodes in a memory buffer.
// This is less efficient, but also works with writers that lack seeking support.
// It is a wrapper around SerializeNode that temporarily stores the serialized nodes in a seekable memory buffer.
// This is less efficient, but provides compatibility with writers that lack seeking support.
func SerializeNodeStream(w io.Writer, node *Node) error {
buf := &bytes.Buffer{}
buf := &seekablebuffer.Buffer{}
if err := SerializeNode(buf, node); err != nil {
return err
}

View file

@ -0,0 +1,90 @@
/*
SeekableBuffer
Copyright (c) 2023 aler9
Copyright (c) 2025 Zervó Zadachin
Implementation based on code from "bluenviron/mediacommon".
See: https://github.com/bluenviron/mediacommon/blob/main/pkg/formats/fmp4/seekablebuffer/seekablebuffer.go
Original code was licensed under the MIT License.
See: https://github.com/bluenviron/mediacommon/blob/main/LICENSE
*/
package seekablebuffer
import (
"bytes"
"fmt"
"io"
)
// Buffer is a bytes.Buffer with an additional Seek() method.
type Buffer struct {
bytes.Buffer
pos int64
}
// Write implements io.Writer.
func (b *Buffer) Write(p []byte) (int, error) {
var n int
if b.pos < int64(b.Len()) {
n = copy(b.Bytes()[b.pos:], p)
p = p[n:]
}
if len(p) > 0 {
// Buffer.Write should never return an error here.
nn, _ := b.Buffer.Write(p)
n += nn
}
b.pos += int64(n)
return n, nil
}
// Read (almost) implements io.Reader.
func (b *Buffer) Read(_ []byte) (int, error) {
return 0, fmt.Errorf("read not implemented")
}
// Seek implements io.Seeker.
func (b *Buffer) Seek(offset int64, whence int) (int64, error) {
var pos2 int64
switch whence {
case io.SeekStart:
pos2 = offset
case io.SeekCurrent:
pos2 = b.pos + offset
case io.SeekEnd:
pos2 = int64(b.Len()) + offset
default:
return 0, fmt.Errorf("invalid seek whence")
}
if pos2 < 0 {
return 0, fmt.Errorf("negative position")
}
b.pos = pos2
// Expand buffer if needed
diff := b.pos - int64(b.Len())
if diff > 0 {
// Buffer.Write should never return an error here.
b.Buffer.Write(make([]byte, diff))
}
return pos2, nil
}
// Reset resets the buffer state.
func (b *Buffer) Reset() {
b.Buffer.Reset()
b.pos = 0
}