github.com/kayoticsully/syncthing@v0.8.9-0.20140724133906-c45a2fdc03f8/cmd/genxdr/main.go (about)

     1  // Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
     2  // All rights reserved. Use of this source code is governed by an MIT-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"bytes"
     9  	"flag"
    10  	"fmt"
    11  	"go/ast"
    12  	"go/format"
    13  	"go/parser"
    14  	"go/token"
    15  	"os"
    16  	"regexp"
    17  	"strconv"
    18  	"strings"
    19  	"text/template"
    20  )
    21  
    22  type fieldInfo struct {
    23  	Name      string
    24  	IsBasic   bool   // handled by one the native Read/WriteUint64 etc functions
    25  	IsSlice   bool   // field is a slice of FieldType
    26  	FieldType string // original type of field, i.e. "int"
    27  	Encoder   string // the encoder name, i.e. "Uint64" for Read/WriteUint64
    28  	Convert   string // what to convert to when encoding, i.e. "uint64"
    29  	Max       int    // max size for slices and strings
    30  }
    31  
    32  type structInfo struct {
    33  	Name   string
    34  	Fields []fieldInfo
    35  }
    36  
    37  var headerTpl = template.Must(template.New("header").Parse(`// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
    38  // All rights reserved. Use of this source code is governed by an MIT-style
    39  // license that can be found in the LICENSE file.
    40  
    41  // ************************************************************
    42  // This file is automatically generated by genxdr. Do not edit.
    43  // ************************************************************
    44  
    45  package {{.Package}}
    46  
    47  import (
    48  	"bytes"
    49  	"io"
    50  
    51  	"github.com/calmh/syncthing/xdr"
    52  )
    53  `))
    54  
    55  var encodeTpl = template.Must(template.New("encoder").Parse(`
    56  func (o {{.TypeName}}) EncodeXDR(w io.Writer) (int, error) {
    57  	var xw = xdr.NewWriter(w)
    58  	return o.encodeXDR(xw)
    59  }//+n
    60  
    61  func (o {{.TypeName}}) MarshalXDR() []byte {
    62  	return o.AppendXDR(make([]byte, 0, 128))
    63  }//+n
    64  
    65  func (o {{.TypeName}}) AppendXDR(bs []byte) []byte {
    66  	var aw = xdr.AppendWriter(bs)
    67  	var xw = xdr.NewWriter(&aw)
    68  	o.encodeXDR(xw)
    69  	return []byte(aw)
    70  }//+n
    71  
    72  func (o {{.TypeName}}) encodeXDR(xw *xdr.Writer) (int, error) {
    73  	{{range $fieldInfo := .Fields}}
    74  		{{if not $fieldInfo.IsSlice}}
    75  			{{if ne $fieldInfo.Convert ""}}
    76  				xw.Write{{$fieldInfo.Encoder}}({{$fieldInfo.Convert}}(o.{{$fieldInfo.Name}}))
    77  			{{else if $fieldInfo.IsBasic}}
    78  				{{if ge $fieldInfo.Max 1}}
    79  					if len(o.{{$fieldInfo.Name}}) > {{$fieldInfo.Max}} {
    80  						return xw.Tot(), xdr.ErrElementSizeExceeded
    81  					}
    82  				{{end}}
    83  				xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}})
    84  			{{else}}
    85  				o.{{$fieldInfo.Name}}.encodeXDR(xw)
    86  			{{end}}
    87  		{{else}}
    88  			{{if ge $fieldInfo.Max 1}}
    89  				if len(o.{{$fieldInfo.Name}}) > {{$fieldInfo.Max}} {
    90  					return xw.Tot(), xdr.ErrElementSizeExceeded
    91  				}
    92  			{{end}}
    93  			xw.WriteUint32(uint32(len(o.{{$fieldInfo.Name}})))
    94  			for i := range o.{{$fieldInfo.Name}} {
    95  			{{if ne $fieldInfo.Convert ""}}
    96  				xw.Write{{$fieldInfo.Encoder}}({{$fieldInfo.Convert}}(o.{{$fieldInfo.Name}}[i]))
    97  			{{else if $fieldInfo.IsBasic}}
    98  				xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}}[i])
    99  			{{else}}
   100  				o.{{$fieldInfo.Name}}[i].encodeXDR(xw)
   101  			{{end}}
   102  			}
   103  		{{end}}
   104  	{{end}}
   105  	return xw.Tot(), xw.Error()
   106  }//+n
   107  
   108  func (o *{{.TypeName}}) DecodeXDR(r io.Reader) error {
   109  	xr := xdr.NewReader(r)
   110  	return o.decodeXDR(xr)
   111  }//+n
   112  
   113  func (o *{{.TypeName}}) UnmarshalXDR(bs []byte) error {
   114  	var br = bytes.NewReader(bs)
   115  	var xr = xdr.NewReader(br)
   116  	return o.decodeXDR(xr)
   117  }//+n
   118  
   119  func (o *{{.TypeName}}) decodeXDR(xr *xdr.Reader) error {
   120  	{{range $fieldInfo := .Fields}}
   121  		{{if not $fieldInfo.IsSlice}}
   122  			{{if ne $fieldInfo.Convert ""}}
   123  				o.{{$fieldInfo.Name}} = {{$fieldInfo.FieldType}}(xr.Read{{$fieldInfo.Encoder}}())
   124  			{{else if $fieldInfo.IsBasic}}
   125  				{{if ge $fieldInfo.Max 1}}
   126  					o.{{$fieldInfo.Name}} = xr.Read{{$fieldInfo.Encoder}}Max({{$fieldInfo.Max}})
   127  				{{else}}
   128  					o.{{$fieldInfo.Name}} = xr.Read{{$fieldInfo.Encoder}}()
   129  				{{end}}
   130  			{{else}}
   131  				(&o.{{$fieldInfo.Name}}).decodeXDR(xr)
   132  			{{end}}
   133  		{{else}}
   134  			_{{$fieldInfo.Name}}Size := int(xr.ReadUint32())
   135  			{{if ge $fieldInfo.Max 1}}
   136  				if _{{$fieldInfo.Name}}Size > {{$fieldInfo.Max}} {
   137  					return xdr.ErrElementSizeExceeded
   138  				}
   139  			{{end}}
   140  			o.{{$fieldInfo.Name}} = make([]{{$fieldInfo.FieldType}}, _{{$fieldInfo.Name}}Size)
   141  			for i := range o.{{$fieldInfo.Name}} {
   142  				{{if ne $fieldInfo.Convert ""}}
   143  					o.{{$fieldInfo.Name}}[i] = {{$fieldInfo.FieldType}}(xr.Read{{$fieldInfo.Encoder}}())
   144  				{{else if $fieldInfo.IsBasic}}
   145  					o.{{$fieldInfo.Name}}[i] = xr.Read{{$fieldInfo.Encoder}}()
   146  				{{else}}
   147  					(&o.{{$fieldInfo.Name}}[i]).decodeXDR(xr)
   148  				{{end}}
   149  			}
   150  		{{end}}
   151  	{{end}}
   152  	return xr.Error()
   153  }`))
   154  
   155  var maxRe = regexp.MustCompile(`\Wmax:(\d+)`)
   156  
   157  type typeSet struct {
   158  	Type    string
   159  	Encoder string
   160  }
   161  
   162  var xdrEncoders = map[string]typeSet{
   163  	"int16":  typeSet{"uint16", "Uint16"},
   164  	"uint16": typeSet{"", "Uint16"},
   165  	"int32":  typeSet{"uint32", "Uint32"},
   166  	"uint32": typeSet{"", "Uint32"},
   167  	"int64":  typeSet{"uint64", "Uint64"},
   168  	"uint64": typeSet{"", "Uint64"},
   169  	"int":    typeSet{"uint64", "Uint64"},
   170  	"string": typeSet{"", "String"},
   171  	"[]byte": typeSet{"", "Bytes"},
   172  	"bool":   typeSet{"", "Bool"},
   173  }
   174  
   175  func handleStruct(t *ast.StructType) []fieldInfo {
   176  	var fs []fieldInfo
   177  
   178  	for _, sf := range t.Fields.List {
   179  		if len(sf.Names) == 0 {
   180  			// We don't handle anonymous fields
   181  			continue
   182  		}
   183  
   184  		fn := sf.Names[0].Name
   185  		var max = 0
   186  		if sf.Comment != nil {
   187  			c := sf.Comment.List[0].Text
   188  			if m := maxRe.FindStringSubmatch(c); m != nil {
   189  				max, _ = strconv.Atoi(m[1])
   190  			}
   191  			if strings.Contains(c, "noencode") {
   192  				continue
   193  			}
   194  		}
   195  
   196  		var f fieldInfo
   197  		switch ft := sf.Type.(type) {
   198  		case *ast.Ident:
   199  			tn := ft.Name
   200  			if enc, ok := xdrEncoders[tn]; ok {
   201  				f = fieldInfo{
   202  					Name:      fn,
   203  					IsBasic:   true,
   204  					FieldType: tn,
   205  					Encoder:   enc.Encoder,
   206  					Convert:   enc.Type,
   207  					Max:       max,
   208  				}
   209  			} else {
   210  				f = fieldInfo{
   211  					Name:      fn,
   212  					IsBasic:   false,
   213  					FieldType: tn,
   214  					Max:       max,
   215  				}
   216  			}
   217  
   218  		case *ast.ArrayType:
   219  			if ft.Len != nil {
   220  				// We don't handle arrays
   221  				continue
   222  			}
   223  
   224  			tn := ft.Elt.(*ast.Ident).Name
   225  			if enc, ok := xdrEncoders["[]"+tn]; ok {
   226  				f = fieldInfo{
   227  					Name:      fn,
   228  					IsBasic:   true,
   229  					FieldType: tn,
   230  					Encoder:   enc.Encoder,
   231  					Convert:   enc.Type,
   232  					Max:       max,
   233  				}
   234  			} else if enc, ok := xdrEncoders[tn]; ok {
   235  				f = fieldInfo{
   236  					Name:      fn,
   237  					IsBasic:   true,
   238  					IsSlice:   true,
   239  					FieldType: tn,
   240  					Encoder:   enc.Encoder,
   241  					Convert:   enc.Type,
   242  					Max:       max,
   243  				}
   244  			} else {
   245  				f = fieldInfo{
   246  					Name:      fn,
   247  					IsBasic:   false,
   248  					IsSlice:   true,
   249  					FieldType: tn,
   250  					Max:       max,
   251  				}
   252  			}
   253  		}
   254  
   255  		fs = append(fs, f)
   256  	}
   257  
   258  	return fs
   259  }
   260  
   261  func generateCode(s structInfo) {
   262  	name := s.Name
   263  	fs := s.Fields
   264  
   265  	var buf bytes.Buffer
   266  	err := encodeTpl.Execute(&buf, map[string]interface{}{"TypeName": name, "Fields": fs})
   267  	if err != nil {
   268  		panic(err)
   269  	}
   270  
   271  	bs := regexp.MustCompile(`(\s*\n)+`).ReplaceAll(buf.Bytes(), []byte("\n"))
   272  	bs = bytes.Replace(bs, []byte("//+n"), []byte("\n"), -1)
   273  
   274  	bs, err = format.Source(bs)
   275  	if err != nil {
   276  		panic(err)
   277  	}
   278  	fmt.Println(string(bs))
   279  }
   280  
   281  func uncamelize(s string) string {
   282  	return regexp.MustCompile("[a-z][A-Z]").ReplaceAllStringFunc(s, func(camel string) string {
   283  		return camel[:1] + " " + camel[1:]
   284  	})
   285  }
   286  
   287  func generateDiagram(s structInfo) {
   288  	sn := s.Name
   289  	fs := s.Fields
   290  
   291  	fmt.Println(sn + " Structure:")
   292  	fmt.Println()
   293  	fmt.Println(" 0                   1                   2                   3")
   294  	fmt.Println(" 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1")
   295  	line := "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"
   296  	fmt.Println(line)
   297  
   298  	for _, f := range fs {
   299  		tn := f.FieldType
   300  		sl := f.IsSlice
   301  		name := uncamelize(f.Name)
   302  
   303  		if sl {
   304  			fmt.Printf("| %s |\n", center("Number of "+name, 61))
   305  			fmt.Println(line)
   306  		}
   307  		switch tn {
   308  		case "bool":
   309  			fmt.Printf("| %s |V|\n", center(name+" (V=0 or 1)", 59))
   310  			fmt.Println(line)
   311  		case "uint16":
   312  			fmt.Printf("| %s | %s |\n", center("0x0000", 29), center(name, 29))
   313  			fmt.Println(line)
   314  		case "uint32":
   315  			fmt.Printf("| %s |\n", center(name, 61))
   316  			fmt.Println(line)
   317  		case "int64", "uint64":
   318  			fmt.Printf("| %-61s |\n", "")
   319  			fmt.Printf("+ %s +\n", center(name+" (64 bits)", 61))
   320  			fmt.Printf("| %-61s |\n", "")
   321  			fmt.Println(line)
   322  		case "string", "byte": // XXX We assume slice of byte!
   323  			fmt.Printf("| %s |\n", center("Length of "+name, 61))
   324  			fmt.Println(line)
   325  			fmt.Printf("/ %61s /\n", "")
   326  			fmt.Printf("\\ %s \\\n", center(name+" (variable length)", 61))
   327  			fmt.Printf("/ %61s /\n", "")
   328  			fmt.Println(line)
   329  		default:
   330  			if sl {
   331  				tn = "Zero or more " + tn + " Structures"
   332  				fmt.Printf("/ %s /\n", center("", 61))
   333  				fmt.Printf("\\ %s \\\n", center(tn, 61))
   334  				fmt.Printf("/ %s /\n", center("", 61))
   335  			} else {
   336  				fmt.Printf("| %s |\n", center(tn, 61))
   337  			}
   338  			fmt.Println(line)
   339  		}
   340  	}
   341  	fmt.Println()
   342  	fmt.Println()
   343  }
   344  
   345  func generateXdr(s structInfo) {
   346  	sn := s.Name
   347  	fs := s.Fields
   348  
   349  	fmt.Printf("struct %s {\n", sn)
   350  
   351  	for _, f := range fs {
   352  		tn := f.FieldType
   353  		fn := f.Name
   354  		suf := ""
   355  		l := ""
   356  		if f.Max > 0 {
   357  			l = strconv.Itoa(f.Max)
   358  		}
   359  		if f.IsSlice {
   360  			suf = "<" + l + ">"
   361  		}
   362  
   363  		switch tn {
   364  		case "uint16", "uint32":
   365  			fmt.Printf("\tunsigned int %s%s;\n", fn, suf)
   366  		case "int64":
   367  			fmt.Printf("\thyper %s%s;\n", fn, suf)
   368  		case "uint64":
   369  			fmt.Printf("\tunsigned hyper %s%s;\n", fn, suf)
   370  		case "string":
   371  			fmt.Printf("\tstring %s<%s>;\n", fn, l)
   372  		case "byte":
   373  			fmt.Printf("\topaque %s<%s>;\n", fn, l)
   374  		default:
   375  			fmt.Printf("\t%s %s%s;\n", tn, fn, suf)
   376  		}
   377  	}
   378  	fmt.Println("}")
   379  	fmt.Println()
   380  }
   381  
   382  func center(s string, w int) string {
   383  	w -= len(s)
   384  	l := w / 2
   385  	r := l
   386  	if l+r < w {
   387  		r++
   388  	}
   389  	return strings.Repeat(" ", l) + s + strings.Repeat(" ", r)
   390  }
   391  
   392  func inspector(structs *[]structInfo) func(ast.Node) bool {
   393  	return func(n ast.Node) bool {
   394  		switch n := n.(type) {
   395  		case *ast.TypeSpec:
   396  			switch t := n.Type.(type) {
   397  			case *ast.StructType:
   398  				name := n.Name.Name
   399  				fs := handleStruct(t)
   400  				*structs = append(*structs, structInfo{name, fs})
   401  			}
   402  			return false
   403  		default:
   404  			return true
   405  		}
   406  	}
   407  }
   408  
   409  func main() {
   410  	flag.Parse()
   411  	fname := flag.Arg(0)
   412  
   413  	fset := token.NewFileSet()
   414  	f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments)
   415  	if err != nil {
   416  		panic(err)
   417  	}
   418  
   419  	var structs []structInfo
   420  	i := inspector(&structs)
   421  	ast.Inspect(f, i)
   422  
   423  	headerTpl.Execute(os.Stdout, map[string]string{"Package": f.Name.Name})
   424  	for _, s := range structs {
   425  		fmt.Printf("\n/*\n\n")
   426  		generateDiagram(s)
   427  		generateXdr(s)
   428  		fmt.Printf("*/\n")
   429  		generateCode(s)
   430  	}
   431  }