go-hep.org/x/hep@v0.38.1/hbook/yodacnv/yoda.go (about)

     1  // Copyright ©2017 The go-hep 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 yodacnv provides tools to read/write YODA archive files.
     6  package yodacnv
     7  
     8  import (
     9  	"bufio"
    10  	"bytes"
    11  	"errors"
    12  	"fmt"
    13  	"io"
    14  	"reflect"
    15  
    16  	"go-hep.org/x/hep/hbook"
    17  )
    18  
    19  var (
    20  	begYoda = []byte("BEGIN YODA_")
    21  	endYoda = []byte("END YODA_")
    22  )
    23  
    24  var (
    25  	errIgnore = errors.New("yodacnv: ignore value (not implemented)")
    26  )
    27  
    28  // Read reads a YODA stream and converts the YODA values into their
    29  // go-hep/hbook equivalents.
    30  func Read(r io.Reader) ([]hbook.Object, error) {
    31  	var (
    32  		err    error
    33  		o      []hbook.Object
    34  		block  = make([]byte, 0, 1024)
    35  		rt     reflect.Type
    36  		ignore bool
    37  	)
    38  	scan := bufio.NewScanner(r)
    39  	for scan.Scan() {
    40  		raw := scan.Bytes()
    41  		switch {
    42  		case bytes.HasPrefix(raw, begYoda):
    43  			rt, err = splitHeader(raw)
    44  			if err == errIgnore {
    45  				err = nil
    46  				ignore = true
    47  			}
    48  			if err != nil {
    49  				return nil, fmt.Errorf("yoda: error parsing YODA header: %w", err)
    50  			}
    51  			block = block[:0]
    52  			block = append(block, raw...)
    53  			block = append(block, '\n')
    54  
    55  		case bytes.HasPrefix(raw, endYoda):
    56  			block = append(block, raw...)
    57  			block = append(block, '\n')
    58  
    59  			if ignore {
    60  				ignore = false
    61  				continue
    62  			}
    63  
    64  			v := reflect.New(rt).Elem()
    65  			err = v.Addr().Interface().(Unmarshaler).UnmarshalYODA(block)
    66  			if err != nil {
    67  				return nil, err
    68  			}
    69  			o = append(o, v.Addr().Interface().(hbook.Object))
    70  
    71  		default:
    72  			block = append(block, raw...)
    73  			block = append(block, '\n')
    74  		}
    75  	}
    76  	err = scan.Err()
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	return o, nil
    81  }
    82  
    83  // Write writes values to a YODA stream.
    84  func Write(w io.Writer, args ...Marshaler) error {
    85  	for _, v := range args {
    86  		raw, err := v.MarshalYODA()
    87  		if err != nil {
    88  			return err
    89  		}
    90  		n, err := w.Write(raw)
    91  		if err != nil {
    92  			return err
    93  		}
    94  		if n < len(raw) {
    95  			return io.ErrShortWrite
    96  		}
    97  	}
    98  	return nil
    99  }
   100  
   101  func splitHeader(raw []byte) (reflect.Type, error) {
   102  	raw = raw[len(begYoda):]
   103  	i := bytes.Index(raw, []byte(" "))
   104  	if i == -1 || i >= len(raw) {
   105  		return nil, fmt.Errorf("invalid YODA header (missing space)")
   106  	}
   107  
   108  	var rt reflect.Type
   109  
   110  	switch string(raw[:i]) {
   111  	case "HISTO1D", "HISTO1D_V2":
   112  		rt = reflect.TypeOf((*hbook.H1D)(nil)).Elem()
   113  	case "HISTO2D", "HISTO2D_V2":
   114  		rt = reflect.TypeOf((*hbook.H2D)(nil)).Elem()
   115  	case "PROFILE1D", "PROFILE1D_V2":
   116  		rt = reflect.TypeOf((*hbook.P1D)(nil)).Elem()
   117  	case "PROFILE2D", "PROFILE2D_V2":
   118  		return nil, errIgnore
   119  	case "SCATTER1D", "SCATTER1D_V2":
   120  		return nil, errIgnore
   121  	case "SCATTER2D", "SCATTER2D_V2":
   122  		rt = reflect.TypeOf((*hbook.S2D)(nil)).Elem()
   123  	case "SCATTER3D", "SCATTER3D_V2":
   124  		return nil, errIgnore
   125  	case "COUNTER", "COUNTER_V2":
   126  		return nil, errIgnore
   127  	default:
   128  		return nil, fmt.Errorf("unhandled YODA object type %q", string(raw[:i]))
   129  	}
   130  
   131  	return rt, nil
   132  }
   133  
   134  // Unmarshaler is the interface implemented by an object that can
   135  // unmarshal a YODA representation of itself.
   136  type Unmarshaler interface {
   137  	UnmarshalYODA([]byte) error
   138  }
   139  
   140  // Marshaler is the interface implemented by an object that can
   141  // marshal itself into a YODA form.
   142  type Marshaler interface {
   143  	MarshalYODA() ([]byte, error)
   144  }