You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
6.0 KiB
241 lines
6.0 KiB
package headers
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
)
|
|
|
|
const (
|
|
// MaxInOutStreams is the maximum allowed stream inputs/outputs into/out
|
|
// of a coder.
|
|
MaxInOutStreams = 4
|
|
|
|
// MaxPropertyDataSize is the size in bytes supported for coder property data.
|
|
MaxPropertyDataSize = 128
|
|
|
|
// MaxCodersInFolder is the maximum number of coders allowed to be
|
|
// specified in a folder.
|
|
MaxCodersInFolder = 4
|
|
|
|
// MaxPackedStreamsInFolder is the maximum number of packed streams allowed
|
|
// to be in a folder.
|
|
MaxPackedStreamsInFolder = 4
|
|
)
|
|
|
|
var (
|
|
// ErrInvalidStreamCount is the error returned when the input/output stream
|
|
// count for a coder is <= 0 || > MaxInOutStreams.
|
|
ErrInvalidStreamCount = errors.New("invalid in/out stream count")
|
|
|
|
// ErrInvalidPropertyDataSize is the error returned when the property data
|
|
// size is <= 0 || > MaxInOutStreams.
|
|
ErrInvalidPropertyDataSize = errors.New("invalid property data size")
|
|
|
|
// ErrInvalidCoderInFolderCount is the error returned when the number of
|
|
// coders in a folder is <= 0 || > MaxCodersInFolder.
|
|
ErrInvalidCoderInFolderCount = errors.New("invalid coder in folder count")
|
|
|
|
// ErrInvalidPackedStreamsCount is the error returned when the number of
|
|
// packed streams exceeds MaxPackedStreamsInFolder
|
|
ErrInvalidPackedStreamsCount = errors.New("invalid packed streams count")
|
|
)
|
|
|
|
// Folder is a structure containing information on how a solid block was
|
|
// constructed.
|
|
type Folder struct {
|
|
CoderInfo []*CoderInfo
|
|
BindPairsInfo []*BindPairsInfo
|
|
PackedIndices []int
|
|
UnpackSizes []uint64
|
|
UnpackCRC uint32
|
|
}
|
|
|
|
// NumInStreamsTotal is the sum of inputs required by all codecs.
|
|
func (f *Folder) NumInStreamsTotal() int {
|
|
var count int
|
|
for i := range f.CoderInfo {
|
|
count += f.CoderInfo[i].NumInStreams
|
|
}
|
|
return count
|
|
}
|
|
|
|
// NumOutStreamsTotal is the sum of outputs required by all codecs.
|
|
func (f *Folder) NumOutStreamsTotal() int {
|
|
var count int
|
|
for i := range f.CoderInfo {
|
|
count += f.CoderInfo[i].NumOutStreams
|
|
}
|
|
return count
|
|
}
|
|
|
|
// FindBindPairForInStream returns the index of a bindpair by an in index.
|
|
func (f *Folder) FindBindPairForInStream(inStreamIndex int) int {
|
|
for i := range f.BindPairsInfo {
|
|
if f.BindPairsInfo[i].InIndex == inStreamIndex {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// FindBindPairForOutStream returns the index of a bindpair by an out index.
|
|
func (f *Folder) FindBindPairForOutStream(outStreamIndex int) int {
|
|
for i := range f.BindPairsInfo {
|
|
if f.BindPairsInfo[i].OutIndex == outStreamIndex {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// UnpackSize returns the final unpacked size of the folder.
|
|
func (f *Folder) UnpackSize() uint64 {
|
|
for i := range f.UnpackSizes {
|
|
if f.FindBindPairForOutStream(i) < 0 {
|
|
return f.UnpackSizes[i]
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// ReadFolder reads a folder structure.
|
|
func ReadFolder(r io.Reader) (*Folder, error) {
|
|
var err error
|
|
|
|
folder := &Folder{}
|
|
|
|
numCoders, err := ReadNumberInt(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if numCoders == 0 || numCoders > MaxCodersInFolder {
|
|
return nil, ErrInvalidCoderInFolderCount
|
|
}
|
|
|
|
folder.CoderInfo = make([]*CoderInfo, numCoders)
|
|
for i := range folder.CoderInfo {
|
|
if folder.CoderInfo[i], err = ReadCoderInfo(r); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
folder.BindPairsInfo = make([]*BindPairsInfo, numCoders-1)
|
|
for i := range folder.BindPairsInfo {
|
|
if folder.BindPairsInfo[i], err = ReadBindPairsInfo(r); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
numInStreamsTotal := folder.NumInStreamsTotal()
|
|
numPackedStreams := numInStreamsTotal - len(folder.BindPairsInfo)
|
|
if numPackedStreams > 1 {
|
|
if numPackedStreams > MaxPackedStreamsInFolder {
|
|
return nil, ErrInvalidPackedStreamsCount
|
|
}
|
|
|
|
folder.PackedIndices = make([]int, numPackedStreams)
|
|
for i := range folder.PackedIndices {
|
|
if folder.PackedIndices[i], err = ReadNumberInt(r); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
} else if numPackedStreams == 1 {
|
|
for i := 0; i < numInStreamsTotal; i++ {
|
|
if folder.FindBindPairForInStream(i) < 0 {
|
|
folder.PackedIndices = []int{i}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return folder, nil
|
|
}
|
|
|
|
// CoderInfo is a structure holding information about a codec.
|
|
type CoderInfo struct {
|
|
CodecID uint32
|
|
Properties []byte
|
|
NumInStreams int
|
|
NumOutStreams int
|
|
}
|
|
|
|
// ReadCoderInfo reads a coder info structure.
|
|
func ReadCoderInfo(r io.Reader) (*CoderInfo, error) {
|
|
attributes, err := ReadByte(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
coderInfo := &CoderInfo{}
|
|
|
|
codecIDSize := attributes & 0x0f
|
|
isComplexCoder := attributes&0x10 > 0
|
|
hasAttributes := attributes&0x20 > 0
|
|
|
|
if codecIDSize > 0 {
|
|
b := make([]byte, codecIDSize)
|
|
if _, err = r.Read(b); err != nil {
|
|
return nil, err
|
|
}
|
|
for i := codecIDSize; i > 0; i-- {
|
|
coderInfo.CodecID |= uint32(b[i-1]) << ((codecIDSize - i) * 8)
|
|
}
|
|
}
|
|
|
|
coderInfo.NumInStreams = 1
|
|
coderInfo.NumOutStreams = 1
|
|
if isComplexCoder {
|
|
if coderInfo.NumInStreams, err = ReadNumberInt(r); err != nil {
|
|
return nil, err
|
|
}
|
|
if coderInfo.NumInStreams == 0 || coderInfo.NumInStreams > MaxInOutStreams {
|
|
return nil, ErrInvalidStreamCount
|
|
}
|
|
|
|
if coderInfo.NumOutStreams, err = ReadNumberInt(r); err != nil {
|
|
return nil, err
|
|
}
|
|
if coderInfo.NumOutStreams == 0 || coderInfo.NumOutStreams > MaxInOutStreams {
|
|
return nil, ErrInvalidStreamCount
|
|
}
|
|
}
|
|
|
|
if hasAttributes {
|
|
size, err := ReadNumberInt(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if size <= 0 || size > MaxPropertyDataSize {
|
|
return nil, ErrInvalidPropertyDataSize
|
|
}
|
|
|
|
coderInfo.Properties = make([]byte, size)
|
|
if _, err = r.Read(coderInfo.Properties); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return coderInfo, nil
|
|
}
|
|
|
|
// BindPairsInfo is a structure that binds the in and out indexes of a codec.
|
|
type BindPairsInfo struct {
|
|
InIndex int
|
|
OutIndex int
|
|
}
|
|
|
|
// ReadBindPairsInfo reads a bindpairs info structure.
|
|
func ReadBindPairsInfo(r io.Reader) (*BindPairsInfo, error) {
|
|
bindPairsInfo := &BindPairsInfo{}
|
|
|
|
var err error
|
|
if bindPairsInfo.InIndex, err = ReadNumberInt(r); err != nil {
|
|
return nil, err
|
|
}
|
|
if bindPairsInfo.OutIndex, err = ReadNumberInt(r); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return bindPairsInfo, nil
|
|
}
|