forked from Mrs4s/MiraiGo
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request Mrs4s#215 from fumiama/master
perf(jce): drop reflect in writer
- Loading branch information
Showing
8 changed files
with
1,213 additions
and
305 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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")) | ||
} |
Oops, something went wrong.