github.com/jjjabc/fitsio@v0.0.0-20161215022839-d1807e9e818e/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 }