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.
goip/vendor/github.com/saracen/go7z/headers/folder.go

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
}