github.com/astrogo/cfitsio@v0.1.0/table.go (about)

     1  package cfitsio
     2  
     3  // #include "go-cfitsio.h"
     4  // #include "go-cfitsio-utils.h"
     5  import "C"
     6  import (
     7  	"fmt"
     8  	"reflect"
     9  	"strconv"
    10  	"strings"
    11  	"unsafe"
    12  )
    13  
    14  type TypeCode int
    15  
    16  const (
    17  	TBIT        TypeCode = C.TBIT /* codes for FITS table data types */
    18  	TBYTE       TypeCode = C.TBYTE
    19  	TSBYTE      TypeCode = C.TSBYTE
    20  	TLOGICAL    TypeCode = C.TLOGICAL
    21  	TSTRING     TypeCode = C.TSTRING
    22  	TUSHORT     TypeCode = C.TUSHORT
    23  	TSHORT      TypeCode = C.TSHORT
    24  	TUINT       TypeCode = C.TUINT
    25  	TINT        TypeCode = C.TINT
    26  	TULONG      TypeCode = C.TULONG
    27  	TLONG       TypeCode = C.TLONG
    28  	TINT32BIT   TypeCode = C.TINT32BIT /* used when returning datatype of a column */
    29  	TFLOAT      TypeCode = C.TFLOAT
    30  	TLONGLONG   TypeCode = C.TLONGLONG
    31  	TDOUBLE     TypeCode = C.TDOUBLE
    32  	TCOMPLEX    TypeCode = C.TCOMPLEX
    33  	TDBLCOMPLEX TypeCode = C.TDBLCOMPLEX
    34  
    35  	// variable length arrays
    36  	TVLABIT        TypeCode = -C.TBIT /* codes for FITS table data types */
    37  	TVLABYTE       TypeCode = -C.TBYTE
    38  	TVLASBYTE      TypeCode = -C.TSBYTE
    39  	TVLALOGICAL    TypeCode = -C.TLOGICAL
    40  	TVLASTRING     TypeCode = -C.TSTRING
    41  	TVLAUSHORT     TypeCode = -C.TUSHORT
    42  	TVLASHORT      TypeCode = -C.TSHORT
    43  	TVLAUINT       TypeCode = -C.TUINT
    44  	TVLAINT        TypeCode = -C.TINT
    45  	TVLAULONG      TypeCode = -C.TULONG
    46  	TVLALONG       TypeCode = -C.TLONG
    47  	TVLAINT32BIT   TypeCode = -C.TINT32BIT /* used when returning datatype of a column */
    48  	TVLAFLOAT      TypeCode = -C.TFLOAT
    49  	TVLALONGLONG   TypeCode = -C.TLONGLONG
    50  	TVLADOUBLE     TypeCode = -C.TDOUBLE
    51  	TVLACOMPLEX    TypeCode = -C.TCOMPLEX
    52  	TVLADBLCOMPLEX TypeCode = -C.TDBLCOMPLEX
    53  )
    54  
    55  func (tc TypeCode) String() string {
    56  	switch tc {
    57  	case TBIT:
    58  		return "TBIT"
    59  	case TBYTE:
    60  		return "TBYTE"
    61  	case TSBYTE:
    62  		return "TSBYTE"
    63  	case TLOGICAL:
    64  		return "TLOGICAL"
    65  	case TSTRING:
    66  		return "TSTRING"
    67  	case TUSHORT:
    68  		return "TUSHORT"
    69  	case TSHORT:
    70  		return "TSHORT"
    71  	case TUINT:
    72  		return "TUINT"
    73  	case TINT:
    74  		return "TINT"
    75  	case TULONG:
    76  		return "TULONG"
    77  	case TLONG:
    78  		return "TLONG"
    79  	case TFLOAT:
    80  		return "TFLOAT"
    81  	case TLONGLONG:
    82  		return "TLONGLONG"
    83  	case TDOUBLE:
    84  		return "TDOUBLE"
    85  	case TCOMPLEX:
    86  		return "TCOMPLEX"
    87  	case TDBLCOMPLEX:
    88  		return "TDBLCOMPLEX"
    89  
    90  	case TVLABIT:
    91  		return "TVLABIT"
    92  	case TVLABYTE:
    93  		return "TVLABYTE"
    94  	case TVLASBYTE:
    95  		return "TVLASBYTE"
    96  	case TVLALOGICAL:
    97  		return "TVLALOGICAL"
    98  	case TVLASTRING:
    99  		return "TVLASTRING"
   100  	case TVLAUSHORT:
   101  		return "TVLAUSHORT"
   102  	case TVLASHORT:
   103  		return "TVLASHORT"
   104  	case TVLAUINT:
   105  		return "TVLAUINT"
   106  	case TVLAINT:
   107  		return "TVLAINT"
   108  	case TVLAULONG:
   109  		return "TVLAULONG"
   110  	case TVLALONG:
   111  		return "TVLALONG"
   112  	case TVLAFLOAT:
   113  		return "TVLAFLOAT"
   114  	case TVLALONGLONG:
   115  		return "TVLALONGLONG"
   116  	case TVLADOUBLE:
   117  		return "TVLADOUBLE"
   118  	case TVLACOMPLEX:
   119  		return "TVLACOMPLEX"
   120  	case TVLADBLCOMPLEX:
   121  		return "TVLADBLCOMPLEX"
   122  	}
   123  	panic("unreachable")
   124  }
   125  
   126  var g_cfits2go map[TypeCode]reflect.Type
   127  var g_go2cfits map[reflect.Type]TypeCode
   128  
   129  func govalue_from_repeat(rt reflect.Type, n int) Value {
   130  	var v Value
   131  	switch n {
   132  	case 1:
   133  		rv := reflect.New(rt)
   134  		v = rv.Elem().Interface()
   135  	default:
   136  		// FIXME: distinguish b/w reflect.Slice and reflect.Array
   137  		// FIXME: use reflect.MakeArray + reflect.ArrayOf when available
   138  		rv := reflect.MakeSlice(reflect.SliceOf(rt), n, n)
   139  		v = rv.Interface()
   140  	}
   141  	return v
   142  }
   143  
   144  func govalue_from_typecode(t TypeCode, n int) Value {
   145  	var v Value
   146  	rt, ok := g_cfits2go[t]
   147  	if !ok {
   148  		panic(fmt.Errorf("cfitsio: invalid TypeCode value [%v]", int(t)))
   149  
   150  	}
   151  	// FIXME: distinguish b/w reflect.Slice and reflect.Array
   152  	v = govalue_from_repeat(rt, n)
   153  	return v
   154  }
   155  
   156  type Table struct {
   157  	f       *File
   158  	id      C.int
   159  	header  Header
   160  	nrows   int64
   161  	cols    []Column
   162  	col2idx map[string]int // associates a column name to its index
   163  	data    interface{}
   164  }
   165  
   166  func (hdu *Table) Close() error {
   167  	hdu.f = nil
   168  	return nil
   169  }
   170  
   171  func (hdu *Table) Header() Header {
   172  	return hdu.header
   173  }
   174  
   175  func (hdu *Table) Type() HDUType {
   176  	return hdu.header.htype
   177  }
   178  
   179  func (hdu *Table) Name() string {
   180  	card := hdu.header.Get("EXTNAME")
   181  	if card == nil {
   182  		return ""
   183  	}
   184  	return card.Value.(string)
   185  }
   186  
   187  func (hdu *Table) Version() int {
   188  	card := hdu.header.Get("EXTVER")
   189  	if card == nil {
   190  		return 1
   191  	}
   192  	return card.Value.(int)
   193  }
   194  
   195  func (hdu *Table) Data(interface{}) error {
   196  	var err error
   197  	if hdu.data == nil {
   198  		err = hdu.load()
   199  	}
   200  	return err
   201  }
   202  
   203  func (hdu *Table) load() error {
   204  	return nil
   205  }
   206  
   207  func (hdu *Table) NumRows() int64 {
   208  	return hdu.nrows
   209  }
   210  
   211  func (hdu *Table) NumCols() int {
   212  	return len(hdu.cols)
   213  }
   214  
   215  func (hdu *Table) Cols() []Column {
   216  	return hdu.cols
   217  }
   218  
   219  func (hdu *Table) Col(i int) *Column {
   220  	return &hdu.cols[i]
   221  }
   222  
   223  // Index returns the index of the first column with name `n` or -1
   224  func (hdu *Table) Index(n string) int {
   225  	idx, ok := hdu.col2idx[n]
   226  	if !ok {
   227  		return -1
   228  	}
   229  	return idx
   230  }
   231  
   232  func (hdu *Table) readRow(irow int64) error {
   233  	err := hdu.seekHDU()
   234  	if err != nil {
   235  		return err
   236  	}
   237  	for icol := range hdu.cols {
   238  		col := &hdu.cols[icol]
   239  		err = col.read(hdu.f, icol, irow, &col.Value)
   240  		if err != nil {
   241  			return err
   242  		}
   243  	}
   244  	return err
   245  }
   246  
   247  // ReadRange reads rows over the range [beg, end) and returns the corresponding iterator.
   248  // if end > maxrows, the iteration will stop at maxrows
   249  // ReadRange has the same semantics than a `for i=0; i < max; i+=inc {...}` loop
   250  func (hdu *Table) ReadRange(beg, end, inc int64) (*Rows, error) {
   251  	var rows *Rows
   252  	err := hdu.seekHDU()
   253  	if err != nil {
   254  		return rows, err
   255  	}
   256  
   257  	maxrows := hdu.NumRows()
   258  	if end > maxrows {
   259  		end = maxrows
   260  	}
   261  
   262  	if beg < 0 {
   263  		beg = 0
   264  	}
   265  
   266  	cols := make([]int, len(hdu.cols))
   267  	for i := range hdu.cols {
   268  		cols[i] = i
   269  	}
   270  
   271  	rows = &Rows{
   272  		table: hdu,
   273  		cols:  cols,
   274  		i:     beg,
   275  		n:     end,
   276  		inc:   inc,
   277  		cur:   beg - inc,
   278  		err:   nil,
   279  		icols: make(map[reflect.Type][][2]int),
   280  	}
   281  	return rows, err
   282  }
   283  
   284  // Read reads rows over the range [beg, end) and returns the corresponding iterator.
   285  // if end > maxrows, the iteration will stop at maxrows
   286  // ReadRange has the same semantics than a `for i=0; i < max; i++ {...}` loop
   287  func (hdu *Table) Read(beg, end int64) (*Rows, error) {
   288  	return hdu.ReadRange(beg, end, 1)
   289  }
   290  
   291  func (hdu *Table) seekHDU() error {
   292  	c_status := C.int(0)
   293  	c_htype := C.int(0)
   294  	C.fits_movabs_hdu(hdu.f.c, hdu.id, &c_htype, &c_status)
   295  	if c_status > 0 {
   296  		return to_err(c_status)
   297  	}
   298  	return nil
   299  }
   300  
   301  func newTable(f *File, hdr Header, i int) (hdu HDU, err error) {
   302  	c_status := C.int(0)
   303  	c_id := C.int(0)
   304  	C.fits_get_hdu_num(f.c, &c_id)
   305  	if c_status > 0 {
   306  		return nil, to_err(c_status)
   307  	}
   308  
   309  	c_nrows := C.long(0)
   310  	C.fits_get_num_rows(f.c, &c_nrows, &c_status)
   311  	if c_status > 0 {
   312  		return nil, to_err(c_status)
   313  	}
   314  
   315  	c_ncols := C.int(0)
   316  	C.fits_get_num_cols(f.c, &c_ncols, &c_status)
   317  	if c_status > 0 {
   318  		return nil, to_err(c_status)
   319  	}
   320  
   321  	ncols := int(c_ncols)
   322  	cols := make([]Column, ncols)
   323  	col2idx := make(map[string]int, ncols)
   324  
   325  	get := func(str string, ii int) *Card {
   326  		return hdr.Get(fmt.Sprintf(str+"%d", ii+1))
   327  	}
   328  	for ii := 0; ii < ncols; ii++ {
   329  		col := &cols[ii]
   330  		// column name
   331  		{
   332  			c_status := C.int(0)
   333  			c_tmpl := C.CString(fmt.Sprintf("%d", ii+1))
   334  			defer C.free(unsafe.Pointer(c_tmpl))
   335  			c_name := C.CStringN(C.FLEN_CARD)
   336  			defer C.free(unsafe.Pointer(c_name))
   337  			c_colnum := C.int(0)
   338  			C.fits_get_colname(f.c, C.CASESEN, c_tmpl, c_name, &c_colnum, &c_status)
   339  			if c_status > 0 {
   340  				return nil, to_err(c_status)
   341  			}
   342  			col.Name = C.GoString(c_name)
   343  			col2idx[col.Name] = ii
   344  		}
   345  
   346  		card := get("TFORM", ii)
   347  		if card != nil {
   348  			col.Format = card.Value.(string)
   349  		}
   350  
   351  		card = get("TUNIT", ii)
   352  		if card != nil {
   353  			col.Unit = card.Value.(string)
   354  		}
   355  
   356  		card = get("TNULL", ii)
   357  		if card != nil {
   358  			col.Null = card.Value.(string)
   359  		}
   360  
   361  		card = get("TSCAL", ii)
   362  		if card != nil {
   363  			switch vv := card.Value.(type) {
   364  			case float64:
   365  				col.Bscale = vv
   366  			case int64:
   367  				col.Bscale = float64(vv)
   368  			default:
   369  				panic(fmt.Errorf("unhandled type [%T]", vv))
   370  			}
   371  			//col.Bscale = card.Value.(float64)
   372  		} else {
   373  			col.Bscale = 1.0
   374  		}
   375  
   376  		card = get("TZERO", ii)
   377  		if card != nil {
   378  			switch vv := card.Value.(type) {
   379  			case float64:
   380  				col.Bzero = vv
   381  			case int64:
   382  				col.Bzero = float64(vv)
   383  			default:
   384  				panic(fmt.Errorf("unhandled type [%T]", vv))
   385  			}
   386  			//col.Bzero = card.Value.(float64)
   387  		} else {
   388  			col.Bzero = 0.0
   389  		}
   390  
   391  		card = get("TDISP", ii)
   392  		if card != nil {
   393  			col.Display = card.Value.(string)
   394  		}
   395  
   396  		{
   397  			// int fits_read_tdimll / ffgtdmll
   398  			//(fitsfile *fptr, int colnum, int maxdim, > int *naxis,
   399  			//LONGLONG *naxes, int *status)
   400  
   401  		}
   402  		card = get("TDIM", ii)
   403  		if card != nil {
   404  			dims := card.Value.(string)
   405  			dims = strings.Replace(dims, "(", "", -1)
   406  			dims = strings.Replace(dims, ")", "", -1)
   407  			toks := make([]string, 0)
   408  			for _, tok := range strings.Split(dims, ",") {
   409  				tok = strings.Trim(tok, " \t\n")
   410  				if tok == "" {
   411  					continue
   412  				}
   413  				toks = append(toks, tok)
   414  			}
   415  			col.Dim = make([]int64, 0, len(toks))
   416  			for _, tok := range toks {
   417  				dim, err := strconv.ParseInt(tok, 10, 64)
   418  				if err != nil {
   419  					return nil, err
   420  				}
   421  				col.Dim = append(col.Dim, dim)
   422  			}
   423  		}
   424  
   425  		card = get("TBCOL", ii)
   426  		if card != nil {
   427  			col.Start = card.Value.(int64)
   428  		}
   429  
   430  		{
   431  			c_type := C.int(0)
   432  			c_repeat := C.long(0)
   433  			c_width := C.long(0)
   434  			c_status := C.int(0)
   435  			c_col := C.int(ii + 1) // 1-based index
   436  			C.fits_get_coltype(f.c, c_col, &c_type, &c_repeat, &c_width, &c_status)
   437  			if c_status > 0 {
   438  				return nil, to_err(c_status)
   439  			}
   440  			col.Type = TypeCode(c_type)
   441  			col.Len = int(c_repeat)
   442  			col.Value = govalue_from_typecode(col.Type, col.Len)
   443  		}
   444  	}
   445  
   446  	hdu = &Table{
   447  		f:       f,
   448  		id:      c_id,
   449  		header:  hdr,
   450  		nrows:   int64(c_nrows),
   451  		cols:    cols,
   452  		col2idx: col2idx,
   453  		data:    nil,
   454  	}
   455  	return hdu, err
   456  }
   457  
   458  // NewTable creates a new table in the given FITS file
   459  func NewTable(f *File, name string, cols []Column, hdutype HDUType) (*Table, error) {
   460  	var err error
   461  	var table *Table
   462  	mode, err := f.Mode()
   463  	if err != nil {
   464  		return table, err
   465  	}
   466  	if mode == ReadOnly {
   467  		return table, READONLY_FILE
   468  	}
   469  
   470  	nhdus := len(f.hdus)
   471  
   472  	if len(cols) <= 0 {
   473  		return table, fmt.Errorf("cfitsio.NewTable: invalid number of columns (%v)", len(cols))
   474  	}
   475  
   476  	c_status := C.int(0)
   477  	c_sz := C.int(len(cols))
   478  	c_types := C.char_array_new(c_sz)
   479  	defer C.free(unsafe.Pointer(c_types))
   480  	c_forms := C.char_array_new(c_sz)
   481  	defer C.free(unsafe.Pointer(c_forms))
   482  	c_units := C.char_array_new(c_sz)
   483  	defer C.free(unsafe.Pointer(c_units))
   484  	c_hduname := C.CString(name)
   485  	defer C.free(unsafe.Pointer(c_hduname))
   486  
   487  	for i := 0; i < len(cols); i++ {
   488  		c_idx := C.int(i)
   489  		col := &cols[i]
   490  		c_name := C.CString(col.Name)
   491  		defer C.free(unsafe.Pointer(c_name))
   492  		C.char_array_set(c_types, c_idx, c_name)
   493  
   494  		err = col.inferFormat(hdutype)
   495  		if err != nil {
   496  			return table, err
   497  		}
   498  		c_form := C.CString(col.Format)
   499  		defer C.free(unsafe.Pointer(c_form))
   500  		C.char_array_set(c_forms, c_idx, c_form)
   501  
   502  		c_unit := C.CString(col.Unit)
   503  		defer C.free(unsafe.Pointer(c_unit))
   504  		C.char_array_set(c_units, c_idx, c_unit)
   505  	}
   506  
   507  	C.fits_create_tbl(f.c, C.int(hdutype), 0, C.int(len(cols)), c_types, c_forms, c_units, c_hduname, &c_status)
   508  	if c_status > 0 {
   509  		return table, to_err(c_status)
   510  	}
   511  
   512  	hdu, err := f.readHDU(nhdus)
   513  	if err != nil {
   514  		return table, err
   515  	}
   516  	f.hdus = append(f.hdus, hdu)
   517  	table = hdu.(*Table)
   518  
   519  	return table, err
   520  }
   521  
   522  // NewTableFrom creates a new table in the given FITS file, using the struct v as schema
   523  func NewTableFrom(f *File, name string, v Value, hdutype HDUType) (*Table, error) {
   524  	rv := reflect.Indirect(reflect.ValueOf(v))
   525  	rt := rv.Type()
   526  	if rt.Kind() != reflect.Struct {
   527  		return nil, fmt.Errorf("cfitsio: NewTableFrom takes a struct value. got: %T", v)
   528  	}
   529  	cols := make([]Column, rt.NumField())
   530  	for i := range cols {
   531  		ft := rt.Field(i)
   532  		name := ft.Tag.Get("fits")
   533  		if name == "" {
   534  			name = ft.Name
   535  		}
   536  		n := 0
   537  		if ft.Type.Kind() == reflect.Array {
   538  			n = ft.Type.Len()
   539  		}
   540  		cols[i] = Column{
   541  			Name:  name,
   542  			Len:   n,
   543  			Value: rv.Field(i).Interface(),
   544  		}
   545  	}
   546  	return NewTable(f, name, cols, hdutype)
   547  }
   548  
   549  // Write writes a row to the table
   550  func (hdu *Table) Write(args ...interface{}) error {
   551  
   552  	err := hdu.seekHDU()
   553  	if err != nil {
   554  		return err
   555  	}
   556  
   557  	irow := hdu.NumRows()
   558  
   559  	defer func() {
   560  		// update nrows
   561  		c_nrows := C.long(0)
   562  		c_status := C.int(0)
   563  		C.fits_get_num_rows(hdu.f.c, &c_nrows, &c_status)
   564  		if c_status > 0 {
   565  			return
   566  		}
   567  		hdu.nrows = int64(c_nrows)
   568  	}()
   569  
   570  	switch len(args) {
   571  	case 0:
   572  		return fmt.Errorf("cfitsio: Table.Write needs at least one argument")
   573  	case 1:
   574  		// maybe special case: map? struct?
   575  		rt := reflect.TypeOf(args[0]).Elem()
   576  		switch rt.Kind() {
   577  		case reflect.Map:
   578  			return hdu.writeMap(irow, *args[0].(*map[string]interface{}))
   579  		case reflect.Struct:
   580  			return hdu.writeStruct(irow, args[0])
   581  		}
   582  	}
   583  
   584  	return hdu.write(irow, args...)
   585  }
   586  
   587  func (hdu *Table) writeMap(irow int64, data map[string]interface{}) error {
   588  	var err error
   589  
   590  	for k, v := range data {
   591  		icol := hdu.Index(k)
   592  		if icol < 0 {
   593  			continue
   594  		}
   595  		col := &hdu.cols[icol]
   596  		col.Value = v
   597  		err = col.write(hdu.f, icol, irow, col.Value)
   598  		if err != nil {
   599  			return err
   600  		}
   601  	}
   602  
   603  	return err
   604  }
   605  
   606  func (hdu *Table) writeStruct(irow int64, data interface{}) error {
   607  	var err error
   608  	rt := reflect.TypeOf(data).Elem()
   609  	rv := reflect.ValueOf(data).Elem()
   610  	icols := make([][2]int, 0, rt.NumField())
   611  	for i := 0; i < rt.NumField(); i++ {
   612  		f := rt.Field(i)
   613  		n := f.Tag.Get("fits")
   614  		if n == "" {
   615  			n = f.Name
   616  		}
   617  		icol := hdu.Index(n)
   618  		if icol >= 0 {
   619  			icols = append(icols, [2]int{i, icol})
   620  		}
   621  	}
   622  
   623  	for _, icol := range icols {
   624  		vv := rv.Field(icol[0])
   625  		col := &hdu.cols[icol[1]]
   626  		col.Value = vv.Interface()
   627  		err = col.write(hdu.f, icol[1], irow, col.Value)
   628  		if err != nil {
   629  			return err
   630  		}
   631  	}
   632  	return err
   633  }
   634  
   635  func (hdu *Table) write(irow int64, args ...interface{}) error {
   636  	var err error
   637  
   638  	nargs := len(args)
   639  	if nargs > len(hdu.cols) {
   640  		nargs = len(hdu.cols)
   641  	}
   642  
   643  	for i := 0; i < nargs; i++ {
   644  		col := &hdu.cols[i]
   645  		rv := reflect.ValueOf(args[i]).Elem()
   646  		vv := reflect.ValueOf(&col.Value).Elem()
   647  		vv.Set(rv)
   648  		err = col.write(hdu.f, i, irow, col.Value)
   649  		if err != nil {
   650  			return err
   651  		}
   652  	}
   653  	return err
   654  }
   655  
   656  // CopyTable copies all the rows from src into dst.
   657  func CopyTable(dst, src *Table) error {
   658  	return CopyTableRange(dst, src, 0, src.NumRows())
   659  }
   660  
   661  // CopyTableRange copies the rows interval [beg,end) from src into dst
   662  func CopyTableRange(dst, src *Table, beg, end int64) error {
   663  	var err error
   664  	if dst == nil {
   665  		return fmt.Errorf("cfitsio: dst pointer is nil")
   666  	}
   667  	if src == nil {
   668  		return fmt.Errorf("cfitsio: src pointer is nil")
   669  	}
   670  
   671  	defer func() {
   672  		// update nrows
   673  		c_nrows := C.long(0)
   674  		c_status := C.int(0)
   675  		C.fits_get_num_rows(dst.f.c, &c_nrows, &c_status)
   676  		if c_status > 0 {
   677  			return
   678  		}
   679  		dst.nrows = int64(c_nrows)
   680  	}()
   681  
   682  	err = dst.seekHDU()
   683  	if err != nil {
   684  		return err
   685  	}
   686  	err = src.seekHDU()
   687  	if err != nil {
   688  		return err
   689  	}
   690  
   691  	hdr := src.Header()
   692  	buf := make([]byte, int(hdr.Axes()[0]))
   693  	slice := (*reflect.SliceHeader)((unsafe.Pointer(&buf)))
   694  	c_ptr := (*C.uchar)(unsafe.Pointer(slice.Data))
   695  	c_len := C.LONGLONG(len(buf))
   696  	c_orow := C.LONGLONG(dst.nrows)
   697  	for irow := beg; irow < end; irow++ {
   698  		c_status := C.int(0)
   699  		c_row := C.LONGLONG(irow) + 1 // from 0-based to 1-based index
   700  		C.fits_read_tblbytes(src.f.c, c_row, 1, c_len, c_ptr, &c_status)
   701  		if c_status > 0 {
   702  			return to_err(c_status)
   703  		}
   704  
   705  		C.fits_write_tblbytes(dst.f.c, c_orow+c_row, 1, c_len, c_ptr, &c_status)
   706  		if c_status > 0 {
   707  			return to_err(c_status)
   708  		}
   709  	}
   710  
   711  	return err
   712  }
   713  
   714  func init() {
   715  	g_hdus[ASCII_TBL] = newTable
   716  	g_hdus[BINARY_TBL] = newTable
   717  	g_cfits2go = map[TypeCode]reflect.Type{
   718  		TBIT:        reflect.TypeOf((*byte)(nil)).Elem(),
   719  		TBYTE:       reflect.TypeOf((*byte)(nil)).Elem(),
   720  		TSBYTE:      reflect.TypeOf((*int8)(nil)).Elem(),
   721  		TLOGICAL:    reflect.TypeOf((*bool)(nil)).Elem(),
   722  		TSTRING:     reflect.TypeOf((*string)(nil)).Elem(),
   723  		TUSHORT:     reflect.TypeOf((*uint16)(nil)).Elem(),
   724  		TSHORT:      reflect.TypeOf((*int16)(nil)).Elem(),
   725  		TUINT:       reflect.TypeOf((*uint32)(nil)).Elem(),
   726  		TINT:        reflect.TypeOf((*int32)(nil)).Elem(),
   727  		TULONG:      reflect.TypeOf((*uint64)(nil)).Elem(),
   728  		TLONG:       reflect.TypeOf((*int64)(nil)).Elem(),
   729  		TFLOAT:      reflect.TypeOf((*float32)(nil)).Elem(),
   730  		TLONGLONG:   reflect.TypeOf((*int64)(nil)).Elem(),
   731  		TDOUBLE:     reflect.TypeOf((*float64)(nil)).Elem(),
   732  		TCOMPLEX:    reflect.TypeOf((*complex64)(nil)).Elem(),
   733  		TDBLCOMPLEX: reflect.TypeOf((*complex128)(nil)).Elem(),
   734  
   735  		TVLABIT:        reflect.TypeOf((*[]byte)(nil)).Elem(),
   736  		TVLABYTE:       reflect.TypeOf((*[]byte)(nil)).Elem(),
   737  		TVLASBYTE:      reflect.TypeOf((*[]int8)(nil)).Elem(),
   738  		TVLALOGICAL:    reflect.TypeOf((*[]bool)(nil)).Elem(),
   739  		TVLASTRING:     reflect.TypeOf((*[]string)(nil)).Elem(),
   740  		TVLAUSHORT:     reflect.TypeOf((*[]uint16)(nil)).Elem(),
   741  		TVLASHORT:      reflect.TypeOf((*[]int16)(nil)).Elem(),
   742  		TVLAUINT:       reflect.TypeOf((*[]uint32)(nil)).Elem(),
   743  		TVLAINT:        reflect.TypeOf((*[]int32)(nil)).Elem(),
   744  		TVLAULONG:      reflect.TypeOf((*[]uint64)(nil)).Elem(),
   745  		TVLALONG:       reflect.TypeOf((*[]int64)(nil)).Elem(),
   746  		TVLAFLOAT:      reflect.TypeOf((*[]float32)(nil)).Elem(),
   747  		TVLALONGLONG:   reflect.TypeOf((*[]int64)(nil)).Elem(),
   748  		TVLADOUBLE:     reflect.TypeOf((*[]float64)(nil)).Elem(),
   749  		TVLACOMPLEX:    reflect.TypeOf((*[]complex64)(nil)).Elem(),
   750  		TVLADBLCOMPLEX: reflect.TypeOf((*[]complex128)(nil)).Elem(),
   751  	}
   752  
   753  	g_go2cfits = make(map[reflect.Type]TypeCode, len(g_cfits2go))
   754  	for k, v := range g_cfits2go {
   755  		g_go2cfits[v] = k
   756  	}
   757  }
   758  
   759  // EOF