package riff
import (
"errors"
"io"
"io/ioutil"
"math"
)
var (
errMissingPaddingByte = errors .New ("riff: missing padding byte" )
errMissingRIFFChunkHeader = errors .New ("riff: missing RIFF chunk header" )
errListSubchunkTooLong = errors .New ("riff: list subchunk too long" )
errShortChunkData = errors .New ("riff: short chunk data" )
errShortChunkHeader = errors .New ("riff: short chunk header" )
errStaleReader = errors .New ("riff: stale reader" )
)
func u32(b []byte ) uint32 {
return uint32 (b [0 ]) | uint32 (b [1 ])<<8 | uint32 (b [2 ])<<16 | uint32 (b [3 ])<<24
}
const chunkHeaderSize = 8
type FourCC [4 ]byte
var LIST = FourCC {'L' , 'I' , 'S' , 'T' }
func NewReader (r io .Reader ) (formType FourCC , data *Reader , err error ) {
var buf [chunkHeaderSize ]byte
if _ , err := io .ReadFull (r , buf [:]); err != nil {
if err == io .EOF || err == io .ErrUnexpectedEOF {
err = errMissingRIFFChunkHeader
}
return FourCC {}, nil , err
}
if buf [0 ] != 'R' || buf [1 ] != 'I' || buf [2 ] != 'F' || buf [3 ] != 'F' {
return FourCC {}, nil , errMissingRIFFChunkHeader
}
return NewListReader (u32 (buf [4 :]), r )
}
func NewListReader (chunkLen uint32 , chunkData io .Reader ) (listType FourCC , data *Reader , err error ) {
if chunkLen < 4 {
return FourCC {}, nil , errShortChunkData
}
z := &Reader {r : chunkData }
if _ , err := io .ReadFull (chunkData , z .buf [:4 ]); err != nil {
if err == io .EOF || err == io .ErrUnexpectedEOF {
err = errShortChunkData
}
return FourCC {}, nil , err
}
z .totalLen = chunkLen - 4
return FourCC {z .buf [0 ], z .buf [1 ], z .buf [2 ], z .buf [3 ]}, z , nil
}
type Reader struct {
r io .Reader
err error
totalLen uint32
chunkLen uint32
chunkReader *chunkReader
buf [chunkHeaderSize ]byte
padded bool
}
func (z *Reader ) Next () (chunkID FourCC , chunkLen uint32 , chunkData io .Reader , err error ) {
if z .err != nil {
return FourCC {}, 0 , nil , z .err
}
if z .chunkLen != 0 {
want := z .chunkLen
var got int64
got , z .err = io .Copy (ioutil .Discard , z .chunkReader )
if z .err == nil && uint32 (got ) != want {
z .err = errShortChunkData
}
if z .err != nil {
return FourCC {}, 0 , nil , z .err
}
}
z .chunkReader = nil
if z .padded {
if z .totalLen == 0 {
z .err = errListSubchunkTooLong
return FourCC {}, 0 , nil , z .err
}
z .totalLen --
_, z .err = io .ReadFull (z .r , z .buf [:1 ])
if z .err != nil {
if z .err == io .EOF {
z .err = errMissingPaddingByte
}
return FourCC {}, 0 , nil , z .err
}
}
if z .totalLen == 0 {
z .err = io .EOF
return FourCC {}, 0 , nil , z .err
}
if z .totalLen < chunkHeaderSize {
z .err = errShortChunkHeader
return FourCC {}, 0 , nil , z .err
}
z .totalLen -= chunkHeaderSize
if _, z .err = io .ReadFull (z .r , z .buf [:chunkHeaderSize ]); z .err != nil {
if z .err == io .EOF || z .err == io .ErrUnexpectedEOF {
z .err = errShortChunkHeader
}
return FourCC {}, 0 , nil , z .err
}
chunkID = FourCC {z .buf [0 ], z .buf [1 ], z .buf [2 ], z .buf [3 ]}
z .chunkLen = u32 (z .buf [4 :])
if z .chunkLen > z .totalLen {
z .err = errListSubchunkTooLong
return FourCC {}, 0 , nil , z .err
}
z .padded = z .chunkLen &1 == 1
z .chunkReader = &chunkReader {z }
return chunkID , z .chunkLen , z .chunkReader , nil
}
type chunkReader struct {
z *Reader
}
func (c *chunkReader ) Read (p []byte ) (int , error ) {
if c != c .z .chunkReader {
return 0 , errStaleReader
}
z := c .z
if z .err != nil {
if z .err == io .EOF {
return 0 , errStaleReader
}
return 0 , z .err
}
n := int (z .chunkLen )
if n == 0 {
return 0 , io .EOF
}
if n < 0 {
n = math .MaxInt32
}
if n > len (p ) {
n = len (p )
}
n , err := z .r .Read (p [:n ])
z .totalLen -= uint32 (n )
z .chunkLen -= uint32 (n )
if err != io .EOF {
z .err = err
}
return n , err
}
The pages are generated with Golds v0.8.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .