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 }