github.com/sashka/siva@v1.6.0/writer.go (about)

     1  package siva
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  )
     7  
     8  var (
     9  	ErrMissingHeader = errors.New("WriteHeader was not called, or already flushed")
    10  	ErrClosedWriter  = errors.New("Writer is closed")
    11  )
    12  
    13  // A Writer provides sequential writing of a siva archive
    14  type Writer interface {
    15  	io.Writer
    16  	io.Closer
    17  	WriteHeader(h *Header) error
    18  	Flush() error
    19  }
    20  
    21  type writer struct {
    22  	w        *hashedWriter
    23  	index    Index
    24  	oIndex   OrderedIndex
    25  	current  *IndexEntry
    26  	position uint64
    27  	closed   bool
    28  }
    29  
    30  // NewWriter creates a new Writer writing to w.
    31  func NewWriter(w io.Writer) Writer {
    32  	return newWriter(w)
    33  }
    34  
    35  func newWriter(w io.Writer) *writer {
    36  	return &writer{
    37  		w: newHashedWriter(w),
    38  	}
    39  }
    40  
    41  // WriteHeader writes hdr and prepares to accept the file's contents.
    42  func (w *writer) WriteHeader(h *Header) error {
    43  	if err := w.flushIfPending(); err != nil {
    44  		return err
    45  	}
    46  
    47  	w.current = &IndexEntry{
    48  		Header: (*h),
    49  		Start:  w.position,
    50  	}
    51  
    52  	w.current.Name = ToSafePath(h.Name)
    53  
    54  	w.index = append(w.index, w.current)
    55  	w.oIndex = w.oIndex.Update(w.current)
    56  
    57  	return nil
    58  }
    59  
    60  // Write writes to the current entry in the siva archive, WriteHeader should
    61  // called before, if not returns ErrMissingHeader
    62  func (w *writer) Write(b []byte) (int, error) {
    63  	if w.current == nil {
    64  		return 0, ErrMissingHeader
    65  	}
    66  
    67  	n, err := w.w.Write(b)
    68  	w.position += uint64(n)
    69  
    70  	return n, err
    71  }
    72  
    73  // Flush finishes writing the current file (optional)
    74  func (w *writer) Flush() error {
    75  	if w.closed {
    76  		return ErrClosedWriter
    77  	} else if w.current == nil {
    78  		return ErrMissingHeader
    79  	}
    80  
    81  	w.current.Size = w.position - w.current.Start
    82  	w.current.CRC32 = w.w.Checksum()
    83  	w.current = nil
    84  	w.w.Reset()
    85  	return nil
    86  }
    87  
    88  func (w *writer) flushIfPending() error {
    89  	if w.closed {
    90  		return ErrClosedWriter
    91  	} else if w.current == nil {
    92  		return nil
    93  	}
    94  	return w.Flush()
    95  }
    96  
    97  // Close closes the siva archive, writing the Index footer to the current writer.
    98  func (w *writer) Close() error {
    99  	defer func() { w.closed = true }()
   100  
   101  	if err := w.flushIfPending(); err != nil {
   102  		return err
   103  	}
   104  
   105  	err := w.index.WriteTo(w.w)
   106  	if err == ErrEmptyIndex {
   107  		return nil
   108  	}
   109  
   110  	return err
   111  }