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 }