github.com/linuxboot/fiano@v1.2.0/pkg/guid2english/transformer.go (about) 1 // Copyright 2019 the LinuxBoot 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 guid2english provides a transform.Transformer which replaces all 6 // GUIDs in the input with their known English representation. 7 package guid2english 8 9 import ( 10 "bytes" 11 "regexp" 12 "text/template" 13 14 "github.com/linuxboot/fiano/pkg/guid" 15 "github.com/linuxboot/fiano/pkg/knownguids" 16 "github.com/linuxboot/fiano/pkg/log" 17 "golang.org/x/text/transform" 18 ) 19 20 var guidRegex = regexp.MustCompile( 21 "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", 22 ) 23 24 var partialguidRegex = regexp.MustCompile( 25 "[-a-fA-F0-9]{1,36}$", 26 ) 27 28 // Mapper converts a GUID to a string. 29 type Mapper interface { 30 Map(guid.GUID) []byte 31 } 32 33 // TemplateMapper implements mapper using Go's text/template package. The 34 // template can refer to the following variables: 35 // * {{.Guid}}: The GUID being mapped 36 // * {{.Name}}: The English name of the GUID or "UNKNOWN" 37 // * {{.IsKnown}}: Set to true when the English name is not known 38 type TemplateMapper struct { 39 tmpl *template.Template 40 } 41 42 // NewTemplateMapper creates a new TemplateMapper given a Template. 43 func NewTemplateMapper(tmpl *template.Template) *TemplateMapper { 44 return &TemplateMapper{ 45 tmpl: tmpl, 46 } 47 } 48 49 // Map implements the Mapper.Map() function. 50 func (f *TemplateMapper) Map(g guid.GUID) []byte { 51 name, isKnown := knownguids.GUIDs[g] 52 if !isKnown { 53 name = "UNKNOWN" 54 } 55 56 b := &bytes.Buffer{} 57 err := f.tmpl.Execute(b, struct { 58 GUID guid.GUID 59 Name string 60 IsKnown bool 61 }{ 62 GUID: g, 63 Name: name, 64 IsKnown: isKnown, 65 }) 66 if err != nil { 67 // There is likely a bug in the template. We do not want to 68 // interrupt the byte stream, so just log the error. 69 log.Errorf("Error in template: %v", err) 70 } 71 return b.Bytes() 72 } 73 74 // Transformer replaces all the GUIDs using the Mapper interface. For example, 75 // this can replace all the GUIDs with their's English representation. 76 type Transformer struct { 77 mapper Mapper 78 } 79 80 // New creates a new Transformer with the given Mapper. 81 func New(m Mapper) *Transformer { 82 return &Transformer{ 83 mapper: m, 84 } 85 } 86 87 func (t *Transformer) bufferMap(match []byte) []byte { 88 // The regex only matches valid GUIDs, so this must parse. 89 g, err := guid.Parse(string(match)) 90 if err != nil { 91 return match 92 } 93 return t.mapper.Map(*g) 94 } 95 96 // Transform implements transform.Transformer.Transform(). 97 func (t *Transformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 98 if atEOF { 99 // we have the end of file, try to process all at once 100 transformed := guidRegex.ReplaceAllFunc(src, t.bufferMap) 101 if len(transformed) > len(dst) { 102 // we were too optimistic, dst is too short 103 d, s, e := t.Transform(dst, src, false) 104 if e != transform.ErrShortSrc { 105 return d, s, e 106 } 107 return d, s, transform.ErrShortDst 108 } 109 copy(dst, transformed) 110 return len(transformed), len(src), nil 111 } 112 loc := guidRegex.FindIndex(src) 113 if loc == nil { 114 // check if the end potentially contain the beginning of a GUID 115 loc := partialguidRegex.FindIndex(src) 116 if loc == nil { 117 copy(dst, src) 118 return len(src), len(src), nil 119 } 120 copy(dst, src[0:loc[0]]) 121 return loc[0], loc[0], transform.ErrShortSrc 122 } 123 copy(dst, src[0:loc[0]]) 124 mappedGUID := t.bufferMap(src[loc[0]:loc[1]]) 125 if loc[0]+len(mappedGUID) > len(dst) { 126 // mapped buffer does not fit, only send the plain part 127 return loc[0], loc[0], transform.ErrShortDst 128 } 129 130 copy(dst[loc[0]:], mappedGUID) 131 return loc[0] + len(mappedGUID), loc[1], transform.ErrShortSrc 132 } 133 134 // Reset implements transform.Transformer.Reset(). 135 func (t *Transformer) Reset() { 136 }