88package riff // import "golang.org/x/image/riff"
99
1010import (
11- "bytes"
1211 "encoding/binary"
1312 "errors"
1413 "io"
2423 errShortChunk = errors .New ("riff: short chunk" )
2524 errStaleReader = errors .New ("riff: stale reader" )
2625 errStaleWriter = errors .New ("riff: stale writer" )
26+ errInvalidOffset = errors .New ("riff: invalid offset" )
2727)
2828
2929// FourCC is a four character code.
@@ -126,67 +126,94 @@ func (r *Reader) Next() (FourCC, uint32, *ChunkReader, error) {
126126 return id , l , & cr , nil
127127}
128128
129- func NewWriter (w io.Writer , fileType FourCC ) (data * Writer , err error ) {
130- if _ , err := w .Write ([]byte {'R' , 'I' , 'F' , 'F' }); err != nil {
129+ func NewWriter (w io.WriterAt , fileType FourCC ) (data * Writer , err error ) {
130+ if _ , err := w .WriteAt ([]byte {'R' , 'I' , 'F' , 'F' }, 0 ); err != nil {
131131 return nil , err
132132 }
133- ww , err := NewListWriter (w , fileType )
133+ ww , err := NewListWriter (io . NewOffsetWriter ( w , 8 ) , fileType )
134134 if err != nil {
135135 return ww , err
136136 }
137- ww .root = true
137+ ww .root = w
138138 return ww , err
139139}
140140
141- func NewListWriter (chunkData io.Writer , listType FourCC ) (listw * Writer , err error ) {
142- w := & Writer {w : chunkData }
143- _ , w .err = w . buf . Write (listType [:])
141+ func NewListWriter (chunkData io.WriterAt , listType FourCC ) (listw * Writer , err error ) {
142+ w := & Writer {w : chunkData , len : 4 }
143+ _ , w .err = chunkData . WriteAt (listType [:], 0 )
144144 return w , w .err
145145}
146146
147147type Writer struct {
148- w io.Writer
149- err error
148+ w io.WriterAt
149+ root io.WriterAt
150+ err error
150151
151- chunk * ChunkWriter
152- buf bytes. Buffer
153- root bool
152+ chunk * ChunkWriter
153+ closed bool
154+ len int64
154155}
155156
156157type ChunkWriter struct {
157158 listw * Writer
158- buf bytes.Buffer
159+ len int64
160+
161+ base int64 // the original offset
162+ off int64 // the current offset
159163}
160164
161165func (w * ChunkWriter ) Write (p []byte ) (n int , err error ) {
162166 if w .listw .chunk != w {
163167 return 0 , errStaleWriter
164168 }
165- if (w .buf .Len () + len (p )) > math .MaxUint32 {
169+ n , err = w .WriteAt (p , w .off )
170+ w .off += int64 (n )
171+ return
172+ }
173+
174+ func (w * ChunkWriter ) WriteAt (p []byte , off int64 ) (n int , err error ) {
175+ if w .listw .chunk != w {
176+ return 0 , errStaleWriter
177+ }
178+ if off < 0 {
179+ return 0 , errInvalidOffset
180+ }
181+ if (w .base + w .len + int64 (len (p ))) > math .MaxUint32 {
166182 return 0 , ErrDataTooLong
167183 }
168- return w .buf .Write (p )
184+
185+ n , err = w .listw .w .WriteAt (p , off + w .base )
186+ l := off + int64 (n )
187+ if l > w .len {
188+ w .len = l
189+ w .listw .len = w .base + w .len
190+ }
191+ return n , err
169192}
170193
171194func (w * ChunkWriter ) close () error {
172195 var sizebuf [4 ]byte
173- l := uint32 (w .buf . Len () )
196+ l := uint32 (w .len )
174197 binary .LittleEndian .PutUint32 (sizebuf [:], l )
175- if _ , err := w .listw .buf . Write (sizebuf [:]); err != nil {
198+ if _ , err := w .listw .w . WriteAt (sizebuf [:], w . base - 4 ); err != nil {
176199 return err
177200 }
178201 if l & 1 == 1 {
179- if err := w .buf .WriteByte (0 ); err != nil {
202+ n , err := w .listw .w .WriteAt ([]byte {0 }, w .base + w .len )
203+ w .len += int64 (n )
204+ w .listw .len += int64 (n )
205+ if err != nil {
180206 return err
181207 }
182208 }
183- _ , err := w .buf .WriteTo (& w .listw .buf )
184- if err != nil {
185- return err
186- }
187- return err
209+ return nil
188210}
189211
212+ var sizePlaceholder = []byte {0 , 0 , 0 , 0 }
213+
214+ // Next returns a chunk writer which behaves as an io.OffsetWriter.
215+ //
216+ // Calling Next closes the returned writer.
190217func (w * Writer ) Next (id FourCC ) (* ChunkWriter , error ) {
191218 if w .err != nil {
192219 return nil , w .err
@@ -199,44 +226,55 @@ func (w *Writer) Next(id FourCC) (*ChunkWriter, error) {
199226 w .chunk = nil
200227 }
201228
202- if l := w .buf .Len (); l & 1 == 1 {
203- if _ , w .err = w .buf .Write ([]byte {0 }); w .err != nil {
204- return nil , w .err
205- }
229+ n , err := w .w .WriteAt (id [:], w .len )
230+ w .len += int64 (n )
231+ if err != nil {
232+ w .err = err
233+ return nil , w .err
206234 }
207- if _ , w .err = w .buf .Write (id [:]); w .err != nil {
235+ n , err = w .w .WriteAt (sizePlaceholder , w .len )
236+ w .len += int64 (n )
237+ if err != nil {
238+ w .err = err
208239 return nil , w .err
209240 }
210- w .chunk = & ChunkWriter {listw : w }
241+ w .chunk = & ChunkWriter {listw : w , base : w . len }
211242 return w .chunk , nil
212243}
213244
245+ // Close closes the list by closing the last writer returned by Next.
214246func (w * Writer ) Close () error {
247+ if w .err != nil {
248+ return w .err
249+ }
250+ if w .closed {
251+ return nil
252+ }
253+
215254 if w .chunk != nil {
216255 if w .err = w .chunk .close (); w .err != nil {
217256 return w .err
218257 }
219258 w .chunk = nil
220259 }
221260
222- if w .root {
261+ if w .root != nil {
223262 // TODO: think of a better way of writing the size of the root element,
224263 // but not write it for sublists because that occurs in ChunkWriter.
225264 var sizebuf [4 ]byte
226- l := uint32 (w .buf . Len () )
265+ l := uint32 (w .len )
227266 binary .LittleEndian .PutUint32 (sizebuf [:], l )
228- if _ , err : = w .w . Write (sizebuf [:]); err != nil {
229- return err
267+ if _ , w . err = w .root . WriteAt (sizebuf [:], 4 ); w . err != nil {
268+ return w . err
230269 }
231270 if l & 1 == 1 {
232- if err := w .buf .WriteByte (0 ); err != nil {
271+ n , err := w .w .WriteAt ([]byte {0 }, w .len )
272+ w .len += int64 (n )
273+ if err != nil {
233274 return err
234275 }
235276 }
236277 }
237- _ , err := w .buf .WriteTo (w .w )
238- if err != nil {
239- return err
240- }
241- return err
278+ w .closed = true
279+ return nil
242280}
0 commit comments