1
0
mirror of https://github.com/Mrs4s/go-cqhttp.git synced 2025-05-07 20:45:53 +08:00

Merge pull request #659 from wdvxdr1123/patch/cqcode_fast

 Speed up cqcode parsing
This commit is contained in:
Mrs4s 2021-02-21 23:42:21 +08:00 committed by GitHub
commit 65112f8752
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 123 additions and 90 deletions

View File

@ -4,21 +4,23 @@ import (
"bytes" "bytes"
"crypto/md5" "crypto/md5"
"encoding/base64" "encoding/base64"
goBinary "encoding/binary"
"encoding/hex" "encoding/hex"
xml2 "encoding/xml" xml2 "encoding/xml"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"math"
"math/rand" "math/rand"
"net/url" "net/url"
"os" "os"
"path" "path"
"reflect"
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"unsafe"
"github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/message"
@ -40,6 +42,26 @@ var IgnoreInvalidCQCode = false
// SplitURL 是否分割URL // SplitURL 是否分割URL
var SplitURL = false var SplitURL = false
// magicCQ 代表 uint32([]byte("[CQ:"))
var magicCQ = uint32(0)
func init() {
const sizeInt = int(unsafe.Sizeof(0))
x := 0x1234
p := unsafe.Pointer(&x)
p2 := (*[sizeInt]byte)(p)
if p2[0] == 0 {
magicCQ = goBinary.BigEndian.Uint32([]byte("[CQ:"))
} else {
magicCQ = goBinary.LittleEndian.Uint32([]byte("[CQ:"))
}
}
// add 指针运算
func add(ptr unsafe.Pointer, offset uintptr) unsafe.Pointer {
return unsafe.Pointer(uintptr(ptr) + offset)
}
const maxImageSize = 1024 * 1024 * 30 // 30MB const maxImageSize = 1024 * 1024 * 30 // 30MB
const maxVideoSize = 1024 * 1024 * 100 // 100MB const maxVideoSize = 1024 * 1024 * 100 // 100MB
// PokeElement 拍一拍 // PokeElement 拍一拍
@ -335,66 +357,14 @@ func ToStringMessage(e []message.IMessageElement, id int64, isRaw ...bool) (r st
} }
// ConvertStringMessage 将消息字符串转为消息元素数组 // ConvertStringMessage 将消息字符串转为消息元素数组
func (bot *CQBot) ConvertStringMessage(msg string, isGroup bool) (r []message.IMessageElement) { func (bot *CQBot) ConvertStringMessage(s string, isGroup bool) (r []message.IMessageElement) {
index := 0 var t, key string
stat := 0 var d map[string]string
rMsg := []rune(msg) ptr := unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&s)).Data)
var tempText, cqCode []rune l := len(s)
hasNext := func() bool { i, j, CQBegin := 0, 0, 0
return index < len(rMsg)
}
next := func() rune {
r := rMsg[index]
index++
return r
}
move := func(steps int) {
index += steps
}
peekN := func(count int) string {
lastIdx := int(math.Min(float64(index+count), float64(len(rMsg))))
return string(rMsg[index:lastIdx])
}
isCQCodeBegin := func(r rune) bool {
return r == '[' && peekN(3) == "CQ:"
}
saveTempText := func() {
if len(tempText) != 0 {
if SplitURL {
for _, t := range global.SplitURL(CQCodeUnescapeText(string(tempText))) {
r = append(r, message.NewText(t))
}
} else {
r = append(r, message.NewText(CQCodeUnescapeText(string(tempText))))
}
}
tempText = []rune{}
cqCode = []rune{}
}
saveCQCode := func() { saveCQCode := func() {
defer func() {
cqCode = []rune{}
tempText = []rune{}
}()
s := strings.SplitN(string(cqCode), ",", -1)
if len(s) == 0 {
return
}
t := s[0]
params := make(map[string]string)
for i := 1; i < len(s); i++ {
p := s[i]
p = strings.TrimSpace(p)
if p == "" {
continue
}
data := strings.SplitN(p, "=", 2)
if len(data) == 2 {
params[data[0]] = CQCodeUnescapeValue(data[1])
} else {
params[p] = ""
}
}
if t == "reply" { // reply 特殊处理 if t == "reply" { // reply 特殊处理
if len(r) > 0 { if len(r) > 0 {
if _, ok := r[0].(*message.ReplyElement); ok { if _, ok := r[0].(*message.ReplyElement); ok {
@ -402,8 +372,8 @@ func (bot *CQBot) ConvertStringMessage(msg string, isGroup bool) (r []message.IM
return return
} }
} }
mid, err := strconv.Atoi(params["id"]) mid, err := strconv.Atoi(d["id"])
customText := params["text"] customText := d["text"]
if err == nil { if err == nil {
org := bot.GetMessage(int32(mid)) org := bot.GetMessage(int32(mid))
if org != nil { if org != nil {
@ -418,12 +388,12 @@ func (bot *CQBot) ConvertStringMessage(msg string, isGroup bool) (r []message.IM
return return
} }
} else if customText != "" { } else if customText != "" {
sender, err := strconv.ParseInt(params["qq"], 10, 64) sender, err := strconv.ParseInt(d["qq"], 10, 64)
if err != nil { if err != nil {
log.Warnf("警告:自定义 Reply 元素中必须包含Uin") log.Warnf("警告:自定义 Reply 元素中必须包含Uin")
return return
} }
msgTime, err := strconv.ParseInt(params["time"], 10, 64) msgTime, err := strconv.ParseInt(d["time"], 10, 64)
if err != nil { if err != nil {
msgTime = time.Now().Unix() msgTime = time.Now().Unix()
} }
@ -439,14 +409,14 @@ func (bot *CQBot) ConvertStringMessage(msg string, isGroup bool) (r []message.IM
} }
} }
if t == "forward" { // 单独处理转发 if t == "forward" { // 单独处理转发
if id, ok := params["id"]; ok { if id, ok := d["id"]; ok {
r = []message.IMessageElement{bot.Client.DownloadForwardMessage(id)} r = []message.IMessageElement{bot.Client.DownloadForwardMessage(id)}
return return
} }
} }
elem, err := bot.ToElement(t, params, isGroup) elem, err := bot.ToElement(t, d, isGroup)
if err != nil { if err != nil {
org := "[CQ:" + string(cqCode) + "]" org := s[CQBegin:i]
if !IgnoreInvalidCQCode { if !IgnoreInvalidCQCode {
log.Warnf("转换CQ码 %v 时出现错误: %v 将原样发送.", org, err) log.Warnf("转换CQ码 %v 时出现错误: %v 将原样发送.", org, err)
r = append(r, message.NewText(org)) r = append(r, message.NewText(org))
@ -462,32 +432,70 @@ func (bot *CQBot) ConvertStringMessage(msg string, isGroup bool) (r []message.IM
r = append(r, i...) r = append(r, i...)
} }
} }
for hasNext() {
ch := next() S1: // Plain Text
switch stat { for ; i < l; i++ {
case 0: if *(*byte)(add(ptr, uintptr(i))) == '[' && i+4 < l &&
if isCQCodeBegin(ch) { *(*uint32)(add(ptr, uintptr(i))) == magicCQ { // Magic :uint32([]byte("[CQ:"))
saveTempText() if i > j {
tempText = append(tempText, []rune("[CQ:")...) r = append(r, message.NewText(CQCodeUnescapeText(s[j:i])))
move(3)
stat = 1
} else {
tempText = append(tempText, ch)
}
case 1:
if isCQCodeBegin(ch) {
move(-1)
stat = 0
} else if ch == ']' {
saveCQCode()
stat = 0
} else {
cqCode = append(cqCode, ch)
tempText = append(tempText, ch)
} }
CQBegin = i
i += 4
j = i
goto S2
} }
} }
saveTempText() goto End
S2: // CQCode Type
d = make(map[string]string)
for ; i < l; i++ {
switch *(*byte)(add(ptr, uintptr(i))) {
case ',': // CQ Code with params
t = s[j:i]
i++
j = i
goto S3
case ']': // CQ Code without params
t = s[j:i]
i++
j = i
saveCQCode()
goto S1
}
}
goto End
S3: // CQCode param key
for ; i < l; i++ {
if *(*byte)(add(ptr, uintptr(i))) == '=' {
key = s[j:i]
i++
j = i
goto S4
}
}
goto End
S4: // CQCode param value
for ; i < l; i++ {
switch *(*byte)(add(ptr, uintptr(i))) {
case ',': // more param
d[key] = CQCodeUnescapeValue(s[j:i])
i++
j = i
goto S3
case ']':
d[key] = CQCodeUnescapeValue(s[j:i])
i++
j = i
saveCQCode()
goto S1
}
}
goto End
End:
if i > j {
r = append(r, message.NewText(CQCodeUnescapeText(s[j:i])))
}
return return
} }

25
coolq/cqcode_test.go Normal file
View File

@ -0,0 +1,25 @@
package coolq
import (
"fmt"
"testing"
"github.com/Mrs4s/MiraiGo/client"
"github.com/Mrs4s/go-cqhttp/global"
)
var bot = NewQQBot(client.NewClient(1, ""), global.DefaultConfig())
func TestCQBot_ConvertStringMessage(t *testing.T) {
for _, v := range bot.ConvertStringMessage(`[CQ:face,id=115,text=111][CQ:face,id=217]] [CQ:text,text=123] [`, false) {
fmt.Println(v)
}
}
var bench = `asdfqwerqwerqwer[CQ:face,id=115,text=111]asdfasdfasdfasdfasdfasdfasd[CQ:face,id=217]] [CQ:text,text=123] [`
func BenchmarkCQBot_ConvertStringMessage(b *testing.B) {
for i := 0; i < b.N; i++ {
bot.ConvertStringMessage(bench, false)
}
}