Skip to content

Commit

Permalink
Merge pull request Mrs4s#215 from fumiama/master
Browse files Browse the repository at this point in the history
perf(jce): drop reflect in writer
  • Loading branch information
Mrs4s committed Dec 10, 2021
2 parents 8cd25e0 + bcde705 commit be7293b
Show file tree
Hide file tree
Showing 8 changed files with 1,213 additions and 305 deletions.
92 changes: 92 additions & 0 deletions binary/jce/gen/structs_parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package main

import (
"bufio"
"bytes"
"flag"
"fmt"
"os"
"os/exec"
"strings"

_ "embed"
)

//go:embed structs_parser_tmp.go
var tmpmain string

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)
fmt.Println("len of tmp main is", len(tmpmain))
tmp, err := os.Create("tmp.go")
if err != nil {
panic(err)
}
inp, err := os.Open(*i)
if err != nil {
panic(err)
}
var structs []string
var isinhead = true
var isinfill = false
var isnexttemplate = false
s := bufio.NewScanner(bytes.NewReader([]byte(tmpmain)))
for s.Scan() {
line := s.Text()
if isinhead {
if strings.Contains(line, "Main_") {
line = "func main() {"
isnexttemplate = true
isinhead = false
scanner := bufio.NewScanner(inp)
start := false
for scanner.Scan() {
t := scanner.Text()
if t == "type (" {
start = true
}
if start {
tmp.WriteString(t + "\n")
if t == ")" {
break
}
if strings.Contains(t, " struct {") {
structs = append(structs, strings.Trim(t[:len(t)-9], "\t"))
}
}
}
inp.Close()
}
tmp.WriteString(line + "\n")
} else if isinfill {
for _, s := range structs {
fmt.Fprintf(tmp, "\tWriteJceStruct(w, &%s{})\n", s)
}
isinfill = false
tmp.WriteString("// structs_parser: fill area\n")
tmp.WriteString(line + "\n")
} else {
if strings.Contains(line, "// structs_parser: fill area") {
isinfill = true
tmp.WriteString("// structs_parser: fill area\n")
} else {
if isnexttemplate {
fmt.Fprintf(tmp, line+"\n", *f)
isnexttemplate = false
} else {
tmp.WriteString(line + "\n")
}
}
}
}
tmp.Close()
c := exec.Command("go", "run", "tmp.go")
err = c.Run()
if err != nil {
panic(err)
}
os.Remove("tmp.go")
}
149 changes: 149 additions & 0 deletions binary/jce/gen/structs_parser_tmp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package main

import (
"fmt"
"io"
"os"
"reflect"
"strconv"
"strings"
"sync"
)

type IJceStruct interface{}

const head = "// Code generated by structs_parser; DO NOT EDIT.\npackage jce\n"

func Main_() {
w, err := os.Create("%s")
if err != nil {
panic(err)
}
w.WriteString(head)
// structs_parser: fill area
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)))
default:
writeJceStructSlice(w, tag, name)
}
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.Int, reflect.Uint:
w.Write([]byte(fmt.Sprintf("\tw.WriteInt64(int64(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 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)))
case IJceStruct:
w.Write([]byte(fmt.Sprintf("\tw.writeHead(10, %d)\n", tag)))
w.Write([]byte(fmt.Sprintf("\tw.buf.Write(pkt.%s.ToBytes())\n", name)))
w.Write([]byte("\tw.writeHead(11, 0)\n"))
}
}
}

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 writeJceStructSlice(w io.Writer, tag byte, name string) {
w.Write([]byte(fmt.Sprintf("\tw.writeHead(9, %d)\n", tag)))
w.Write([]byte(fmt.Sprintf("\tif len(pkt.%s) == 0 {\n", name)))
w.Write([]byte("\t\tw.writeHead(12, 0) // w.WriteInt32(0, 0)\n"))
w.Write([]byte("\t} else {\n"))
w.Write([]byte(fmt.Sprintf("\t\tw.WriteInt32(int32(len(pkt.%s)), 0)\n", name)))
w.Write([]byte(fmt.Sprintf("\t\tfor _, i := range pkt.%s {\n", name)))
w.Write([]byte("\t\t\tw.buf.Write(i.ToBytes())\n"))
w.Write([]byte("\t\t}\n"))
w.Write([]byte("\t}\n"))
}

func WriteJceStruct(w io.Writer, s IJceStruct) {
w.Write([]byte(fmt.Sprintf("\nfunc (pkt %s) ToBytes() []byte {\n", strings.ReplaceAll(reflect.TypeOf(s).String(), "main.", ""))))
w.Write([]byte("\tw := NewJceWriter()\n"))
writeJceStructRaw(w, s)
w.Write([]byte("\treturn w.Bytes()\n"))
w.Write([]byte("}\n"))
}
Loading

0 comments on commit be7293b

Please sign in to comment.