package main import ( "bufio" "flag" "fmt" "os" "os/exec" "strings" ) const tmphead = `package main import ( "flag" "fmt" "io" "os" "reflect" "strconv" "strings" "sync" ) type IJceStruct interface {} const head = "// Code generated by structs_parser; DO NOT EDIT.\npackage jce\n" ` const tmpmain = `func main() { w, err := os.Create(%s) if err != nil { panic(err) } tmp.WriteString(head) w.WriteString(head) ` const tmptail = ` err = w.Close() if err != nil { panic(err) } } func writeObject(w io.Writer, v reflect.Value, tag byte, name string) { k := v.Kind() if k == reflect.Map { switch v.Interface().(type) { case map[string]string: w.Write([]byte(fmt.Sprintf("\tw.writeMapStrStr(pkt.%s, %d)\n", name, tag))) case map[string][]byte: w.Write([]byte(fmt.Sprintf("\tw.writeMapStrBytes(pkt.%s, %d)\n", name, tag))) case map[string]map[string][]byte: w.Write([]byte(fmt.Sprintf("\tw.writeMapStrMapStrBytes(pkt.%s, %d)\n", name, tag))) default: w.Write([]byte(fmt.Sprintf("\tw.writeMap(pkt.%s, %d)\n", name, tag))) } return } if k == reflect.Slice { switch v.Interface().(type) { case []byte: w.Write([]byte(fmt.Sprintf("\tw.WriteBytes(pkt.%s, %d)\n", name, tag))) case []int64: w.Write([]byte(fmt.Sprintf("\tw.WriteInt64Slice(pkt.%s, %d)\n", name, tag))) case [][]byte: w.Write([]byte(fmt.Sprintf("\tw.WriteBytesSlice(pkt.%s, %d)\n", name, tag))) case []IJceStruct: w.Write([]byte(fmt.Sprintf("\tw.WriteJceStructSlice(pkt.%s, %d)\n", name, tag))) default: w.Write([]byte(fmt.Sprintf("\tw.writeSlice(pkt.%s, %d)\n", name, tag))) } return } switch k { case reflect.Uint8, reflect.Int8: w.Write([]byte(fmt.Sprintf("\tw.WriteByte(pkt.%s, %d)\n", name, tag))) case reflect.Uint16, reflect.Int16: w.Write([]byte(fmt.Sprintf("\tw.WriteInt16(pkt.%s, %d)\n", name, tag))) case reflect.Uint32, reflect.Int32: w.Write([]byte(fmt.Sprintf("\tw.WriteInt32(pkt.%s, %d)\n", name, tag))) case reflect.Uint64, reflect.Int64: w.Write([]byte(fmt.Sprintf("\tw.WriteInt64(pkt.%s, %d)\n", name, tag))) case reflect.String: w.Write([]byte(fmt.Sprintf("\tw.WriteString(pkt.%s, %d)\n", name, tag))) default: switch v.Interface().(type) { case IJceStruct: w.Write([]byte(fmt.Sprintf("\tw.WriteJceStruct(pkt.%s, %d)\n", name, tag))) case float32: w.Write([]byte(fmt.Sprintf("\tw.WriteFloat32(pkt.%s, %d)\n", name, tag))) case float64: w.Write([]byte(fmt.Sprintf("\tw.WriteFloat64(pkt.%s, %d)\n", name, tag))) } } } type decoder struct { index int id int name string } var decoderCache = sync.Map{} // writeJceStructRaw 写入 Jce 结构体 func writeJceStructRaw(w io.Writer, s interface{}) { t := reflect.TypeOf(s) if t.Kind() != reflect.Ptr { return } t = t.Elem() v := reflect.ValueOf(s).Elem() var jceDec []decoder dec, ok := decoderCache.Load(t) if ok { // 从缓存中加载 jceDec = dec.([]decoder) } else { // 初次反射 jceDec = make([]decoder, 0, t.NumField()) for i := 0; i < t.NumField(); i++ { field := t.Field(i) strId := field.Tag.Get("jceId") if strId == "" { continue } id, err := strconv.Atoi(strId) if err != nil { continue } jceDec = append(jceDec, decoder{ index: i, id: id, name: field.Name, }) } decoderCache.Store(t, jceDec) // 存入缓存 } for _, dec := range jceDec { obj := v.Field(dec.index) writeObject(w, obj, byte(dec.id), dec.name) } } func WriteJceStruct(w io.Writer, s IJceStruct) { w.Write([]byte(fmt.Sprintf("\nfunc (pkt %s) ToBytes() []byte {\n", strings.ReplaceAll(reflect.TypeOf(s).String(), "", "")))) w.Write([]byte("\tw := NewJceWriter()\n")) writeJceStructRaw(w, s) w.Write([]byte("\treturn w.Bytes()\n")) w.Write([]byte("}\n")) } ` func main() { f := flag.String("f", "structs_tobytes.go", "output file.") i := flag.String("i", "structs.go", "input file.") flag.Parse() fmt.Println("gen runs on arg", *f, *i) tmp, err := os.Create("tmp.go") if err != nil { panic(err) } inp, err := os.Open(*i) if err != nil { panic(err) } var structs []string tmp.WriteString(tmphead) scanner := bufio.NewScanner(inp) start := false for scanner.Scan() { if scanner.Text() == "type (" { start = true tmp.WriteString("type (\n") } if start { t := scanner.Text() tmp.WriteString(t + "\n") if t == ")" { break } if strings.Contains(t, " struct {") { structs = append(structs, strings.Trim(t[:len(t)-9], "\t")) } } } inp.Close() fmt.Fprintf(tmp, tmpmain, *i) for _, s := range structs { fmt.Fprintf(tmp, "\tWriteJceStruct(w, &%s{})\n", s) } tmp.WriteString(tmptail) tmp.Close() exec.Command("go", "tmp.go", "-o tmp") exec.Command("./tmp") os.Remove("tmp.go") os.Remove("tmp") }