github.com/astrogo/fitsio@v0.3.0/encode.go (about)

     1  // Copyright 2015 The astrogo Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package fitsio
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io"
    11  )
    12  
    13  type Encoder interface {
    14  	EncodeHDU(hdu HDU) error
    15  }
    16  
    17  // NewEncoder creates a new Encoder according to the capabilities of the underlying io.Writer
    18  func NewEncoder(w io.Writer) Encoder {
    19  	// FIXME(sbinet)
    20  	// if ww, ok := w.(io.WriteSeeker); ok {
    21  	// 	return &seekWriter{w: ww}
    22  	// }
    23  	return &streamEncoder{w: w}
    24  }
    25  
    26  // streamEncoder is a encoder which can not perform random access
    27  // into the underlying Writer
    28  type streamEncoder struct {
    29  	w io.Writer
    30  }
    31  
    32  func (enc *streamEncoder) EncodeHDU(hdu HDU) error {
    33  	var err error
    34  	const (
    35  		valind  = "= "
    36  		nKEY    = 8
    37  		nVALIND = 2
    38  		nVAL    = 30
    39  		nCOM    = 40
    40  		nLINE   = 80
    41  	)
    42  
    43  	hdr := hdu.Header()
    44  	nkeys := len(hdr.cards)
    45  	buf := new(bytes.Buffer)
    46  
    47  	buf.Grow(nkeys * nLINE)
    48  
    49  	for i := range hdr.cards {
    50  		card := &hdr.cards[i]
    51  		bline, err := makeHeaderLine(card)
    52  		if err != nil {
    53  			return err
    54  		}
    55  		_, err = buf.Write(bline)
    56  		if err != nil {
    57  			return err
    58  		}
    59  	}
    60  
    61  	{ // END
    62  		bline, err := makeHeaderLine(&Card{Name: "END"})
    63  		if err != nil {
    64  			return err
    65  		}
    66  		_, err = buf.Write(bline)
    67  		if err != nil {
    68  			return err
    69  		}
    70  	}
    71  
    72  	padsz := padBlock(buf.Len())
    73  	if padsz > 0 {
    74  		n, err := buf.Write(bytes.Repeat([]byte(" "), padsz))
    75  		if err != nil {
    76  			return fmt.Errorf("fitsio: error while padding header block: %v", err)
    77  		}
    78  		if n != padsz {
    79  			return fmt.Errorf("fitsio: wrote %d bytes. expected %d. (padding)", n, padsz)
    80  		}
    81  	}
    82  
    83  	alignsz := alignBlock(buf.Len())
    84  	if alignsz != buf.Len() {
    85  		return fmt.Errorf("fitsio: header not aligned (%d). expected %d.", buf.Len(), alignsz)
    86  	}
    87  
    88  	n, err := io.Copy(enc.w, buf)
    89  	if err != nil {
    90  		return fmt.Errorf("fitsio: error writing header block: %v", err)
    91  	}
    92  	if n != int64(alignsz) {
    93  		return fmt.Errorf("fitsio: wrote %d bytes. expected %d", n, alignsz)
    94  	}
    95  
    96  	// write payload
    97  	switch hdr.Type() {
    98  	case IMAGE_HDU:
    99  		img := hdu.(Image)
   100  		err = enc.saveImage(img)
   101  		if err != nil {
   102  			return fmt.Errorf("fitsio: error encoding image: %v", err)
   103  		}
   104  
   105  	case BINARY_TBL:
   106  		tbl := hdu.(*Table)
   107  		err = enc.saveTable(tbl)
   108  		if err != nil {
   109  			return fmt.Errorf("fitsio: error encoding binary table: %v", err)
   110  		}
   111  
   112  	case ASCII_TBL:
   113  		tbl := hdu.(*Table)
   114  		err = enc.saveTable(tbl)
   115  		if err != nil {
   116  			return fmt.Errorf("fitsio: error encoding ascii table: %v", err)
   117  		}
   118  
   119  	case ANY_HDU:
   120  		fallthrough
   121  	default:
   122  		return fmt.Errorf("fitsio: encoding for HDU [%v] not implemented", hdr.Type())
   123  	}
   124  	return err
   125  }
   126  
   127  func (enc *streamEncoder) saveImage(img Image) error {
   128  	raw := img.Raw()
   129  	n, err := enc.w.Write(raw)
   130  	if err != nil {
   131  		return err
   132  	}
   133  	if n != len(raw) {
   134  		return fmt.Errorf("fitsio: wrote %d bytes. expected %d", n, len(raw))
   135  	}
   136  
   137  	padsz := padBlock(n)
   138  	if padsz > 0 {
   139  		n, err := enc.w.Write(make([]byte, padsz))
   140  		if err != nil {
   141  			return fmt.Errorf("fitsio: error while padding data-image block: %v", err)
   142  		}
   143  		if n != padsz {
   144  			return fmt.Errorf("fitsio: wrote %d bytes. expected %d. (padding)", n, padsz)
   145  		}
   146  	}
   147  
   148  	return err
   149  }
   150  
   151  func (enc *streamEncoder) saveTable(table *Table) error {
   152  	ndata, err := enc.w.Write(table.data)
   153  	if err != nil {
   154  		return fmt.Errorf("fitsio: error writing table-data: %v", err)
   155  	}
   156  	if ndata != len(table.data) {
   157  		return fmt.Errorf("fitsio: wrote %d bytes. expected %d", ndata, len(table.data))
   158  	}
   159  
   160  	nheap, err := enc.w.Write(table.heap)
   161  	if err != nil {
   162  		return fmt.Errorf("fitsio: error writing table-heap: %v", err)
   163  	}
   164  	if nheap != len(table.heap) {
   165  		return fmt.Errorf("fitsio: wrote %d bytes. expected %d", nheap, len(table.heap))
   166  	}
   167  
   168  	// align to FITS block
   169  	padsz := padBlock(ndata + nheap)
   170  	if padsz > 0 {
   171  		n := 0
   172  
   173  		if table.binary {
   174  			n, err = enc.w.Write(make([]byte, padsz))
   175  		} else {
   176  			n, err = enc.w.Write(bytes.Repeat([]byte(" "), padsz))
   177  		}
   178  		if err != nil {
   179  			return fmt.Errorf("fitsio: error while padding table-data block: %v", err)
   180  		}
   181  		if n != padsz {
   182  			return fmt.Errorf("fitsio: wrote %d bytes. expected %d. (padding)", n, padsz)
   183  		}
   184  	}
   185  
   186  	return err
   187  }