From ec053573f170c46567eb7087639ec529c2dfe25c Mon Sep 17 00:00:00 2001 From: fumiama Date: Tue, 23 Nov 2021 13:18:58 +0800 Subject: [PATCH 1/4] =?UTF-8?q?perf(tea):=20use=20runtime.fastrand=20name?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20old=20time/op=20=20=20=20new=20ti?= =?UTF-8?q?me/op=20=20=20=20delta=20TEAen/16-8=20=20=20=20=20=20=20242ns?= =?UTF-8?q?=20=C2=B1=201%=20=20=20=20=20225ns=20=C2=B1=202%=20=20-6.98%=20?= =?UTF-8?q?=20(p=3D0.008=20n=3D5+5)=20TEAen/256-8=20=20=20=20=201.71=C2=B5?= =?UTF-8?q?s=20=C2=B1=200%=20=20=20=201.68=C2=B5s=20=C2=B1=200%=20=20-1.58?= =?UTF-8?q?%=20=20(p=3D0.008=20n=3D5+5)=20TEAen/4K-8=20=20=20=20=20=2025.0?= =?UTF-8?q?=C2=B5s=20=C2=B1=201%=20=20=20=2025.0=C2=B5s=20=C2=B1=200%=20?= =?UTF-8?q?=20=20=20~=20=20=20=20=20(p=3D1.000=20n=3D5+5)=20TEAen/32K-8=20?= =?UTF-8?q?=20=20=20=20=20202=C2=B5s=20=C2=B1=201%=20=20=20=20=20202=C2=B5?= =?UTF-8?q?s=20=C2=B1=200%=20=20=20=20~=20=20=20=20=20(p=3D0.548=20n=3D5+5?= =?UTF-8?q?)=20TEAde/16-8=20=20=20=20=20=20=20208ns=20=C2=B1=200%=20=20=20?= =?UTF-8?q?=20=20207ns=20=C2=B1=200%=20=20=20=20~=20=20=20=20=20(p=3D0.198?= =?UTF-8?q?=20n=3D5+5)=20TEAde/256-8=20=20=20=20=201.65=C2=B5s=20=C2=B1=20?= =?UTF-8?q?0%=20=20=20=201.64=C2=B5s=20=C2=B1=200%=20=20-0.39%=20=20(p=3D0?= =?UTF-8?q?.048=20n=3D5+5)=20TEAde/4K-8=20=20=20=20=20=2024.6=C2=B5s=20?= =?UTF-8?q?=C2=B1=200%=20=20=20=2024.6=C2=B5s=20=C2=B1=201%=20=20=20=20~?= =?UTF-8?q?=20=20=20=20=20(p=3D1.000=20n=3D5+5)=20TEAde/32K-8=20=20=20=20?= =?UTF-8?q?=20=20199=C2=B5s=20=C2=B1=200%=20=20=20=20=20199=C2=B5s=20?= =?UTF-8?q?=C2=B1=200%=20=20=20=20~=20=20=20=20=20(p=3D0.905=20n=3D4+5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit name old speed new speed delta TEAen/16-8 66.2MB/s ± 1% 71.2MB/s ± 2% +7.51% (p=0.008 n=5+5) TEAen/256-8 150MB/s ± 0% 152MB/s ± 0% +1.61% (p=0.008 n=5+5) TEAen/4K-8 164MB/s ± 1% 164MB/s ± 0% ~ (p=1.000 n=5+5) TEAen/32K-8 162MB/s ± 1% 162MB/s ± 0% ~ (p=0.548 n=5+5) TEAde/16-8 154MB/s ± 0% 154MB/s ± 0% ~ (p=0.222 n=5+5) TEAde/256-8 165MB/s ± 0% 165MB/s ± 0% ~ (p=0.056 n=5+5) TEAde/4K-8 167MB/s ± 0% 167MB/s ± 1% ~ (p=1.000 n=5+5) TEAde/32K-8 165MB/s ± 0% 165MB/s ± 0% ~ (p=0.825 n=4+5) --- binary/tea.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/binary/tea.go b/binary/tea.go index 371dac04..a0bdff4c 100644 --- a/binary/tea.go +++ b/binary/tea.go @@ -2,11 +2,15 @@ package binary import ( "encoding/binary" - "math/rand" + _ "unsafe" // required by go:linkname ) type TEA [4]uint32 +// Uint32 returns a lock free uint32 value. +//go:linkname Uint32 runtime.fastrand +func Uint32() uint32 + // Encrypt tea 加密 // http://bbs.chinaunix.net/thread-583468-1-1.html // 感谢xichen大佬对TEA的解释 @@ -14,7 +18,9 @@ func (t TEA) Encrypt(src []byte) (dst []byte) { lens := len(src) fill := 10 - (lens+1)%8 dst = make([]byte, fill+lens+7) - _, _ = rand.Read(dst[0:fill]) + binary.LittleEndian.PutUint32(dst, Uint32()) + binary.LittleEndian.PutUint32(dst[4:], Uint32()) + binary.LittleEndian.PutUint32(dst[8:], Uint32()) dst[0] = byte(fill-3) | 0xF8 // 存储pad长度 copy(dst[fill:], src) @@ -36,14 +42,13 @@ func (t TEA) Decrypt(data []byte) []byte { return nil } dst := make([]byte, len(data)) - var iv1, iv2, holder, tmp uint64 + var iv1, iv2, holder uint64 for i := 0; i < len(dst); i += 8 { - block := binary.BigEndian.Uint64(data[i:]) - tmp = t.decode(block ^ iv2) - iv2 = tmp - holder = tmp ^ iv1 - iv1 = block - binary.BigEndian.PutUint64(dst[i:], holder) + iv1 = binary.BigEndian.Uint64(data[i:]) + iv2 ^= iv1 + iv2 = t.decode(iv2) + binary.BigEndian.PutUint64(dst[i:], iv2^holder) + holder = iv1 } return dst[dst[0]&7+3 : len(data)-7] } From 26579583ccb8baf149ceeed5b7bc7f235f78e864 Mon Sep 17 00:00:00 2001 From: fumiama Date: Tue, 23 Nov 2021 15:35:52 +0800 Subject: [PATCH 2/4] drop(pool): unnecessay nil judge --- binary/pool.go | 15 +++++++-------- binary/writer.go | 4 +--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/binary/pool.go b/binary/pool.go index 9f6e3f05..02c8b2b9 100644 --- a/binary/pool.go +++ b/binary/pool.go @@ -16,11 +16,10 @@ var bufferPool = sync.Pool{ // SelectWriter 从池中取出一个 Writer func SelectWriter() *Writer { - w := bufferPool.Get().(*Writer) - if w == nil { - return new(Writer) - } - return w + // 因为 bufferPool 定义有 New 函数 + // 所以 bufferPool.Get() 永不为 nil + // 不用判空 + return bufferPool.Get().(*Writer) } // PutWriter 将 Writer 放回池中 @@ -103,9 +102,9 @@ var b256kPool = sync.Pool{ // Get256KBytes 获取一个128k大小 []byte func Get256KBytes() *[]byte { buf := b256kPool.Get().(*[]byte) - if buf == nil { - return make128kSlicePointer() - } + // 因为 b256kPool 定义有 New 函数 + // 所以 b256kPool.Get() 永不为 nil + // 不用判空 if cap(*buf) < size256k { return make128kSlicePointer() } diff --git a/binary/writer.go b/binary/writer.go index 37e99d2d..1441d95f 100644 --- a/binary/writer.go +++ b/binary/writer.go @@ -69,9 +69,7 @@ func (w *Writer) WriteBool(b bool) { } func (w *Writer) EncryptAndWrite(key []byte, data []byte) { - tea := NewTeaCipher(key) - ed := tea.Encrypt(data) - w.Write(ed) + w.Write(NewTeaCipher(key).Encrypt(data)) } func (w *Writer) WriteIntLvPacket(offset int, f func(writer *Writer)) { From a70e0f20de405cafd8df617180694ec6d7b8a9a0 Mon Sep 17 00:00:00 2001 From: fumiama Date: Tue, 23 Nov 2021 16:21:06 +0800 Subject: [PATCH 3/4] perf: add 0 alloc func OpenWriterF --- binary/writer.go | 11 +++++++++- binary/writer_test.go | 48 +++++++++++++++++++++++++++++++++++++++++++ client/builders.go | 42 ++++++++++++++++++++++++------------- client/global.go | 9 +++++--- 4 files changed, 92 insertions(+), 18 deletions(-) diff --git a/binary/writer.go b/binary/writer.go index 1441d95f..de7d55f5 100644 --- a/binary/writer.go +++ b/binary/writer.go @@ -19,6 +19,14 @@ func NewWriterF(f func(writer *Writer)) []byte { return b } +// OpenWriterF must call func close +func OpenWriterF(f func(writer *Writer)) (b []byte, close func()) { + w := SelectWriter() + w.WriteByte(0) + f(w) + return w.Bytes(), func() { PutWriter(w) } +} + func (w *Writer) Write(b []byte) { (*bytes.Buffer)(w).Write(b) } @@ -73,9 +81,10 @@ func (w *Writer) EncryptAndWrite(key []byte, data []byte) { } func (w *Writer) WriteIntLvPacket(offset int, f func(writer *Writer)) { - data := NewWriterF(f) + data, close := OpenWriterF(f) w.WriteUInt32(uint32(len(data) + offset)) w.Write(data) + close() } func (w *Writer) WriteUniPacket(commandName string, sessionId, extraData, body []byte) { diff --git a/binary/writer_test.go b/binary/writer_test.go index 578095c2..6128e223 100644 --- a/binary/writer_test.go +++ b/binary/writer_test.go @@ -49,3 +49,51 @@ func BenchmarkNewWriterF128_5(b *testing.B) { } }) } + +func BenchmarkOpenWriterF128(b *testing.B) { + test := make([]byte, 128) + rand.Read(test) + b.StartTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _, close := OpenWriterF(func(w *Writer) { + w.Write(test) + }) + close() + } + }) +} + +func BenchmarkOpenWriterF128_3(b *testing.B) { + test := make([]byte, 128) + rand.Read(test) + b.StartTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _, close := OpenWriterF(func(w *Writer) { + w.Write(test) + w.Write(test) + w.Write(test) + }) + close() + } + }) +} + +func BenchmarkOpenWriterF128_5(b *testing.B) { + test := make([]byte, 128) + rand.Read(test) + b.StartTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _, close := OpenWriterF(func(w *Writer) { + w.Write(test) + w.Write(test) + w.Write(test) + w.Write(test) + w.Write(test) + }) + close() + } + }) +} diff --git a/client/builders.go b/client/builders.go index a98df673..49e6ea90 100644 --- a/client/builders.go +++ b/client/builders.go @@ -169,10 +169,12 @@ func (c *QQClient) buildQRCodeLoginPacket(t106, t16a, t318 []byte) (uint16, []by w.Write(tlv.T18(16, uint32(c.Uin))) w.Write(tlv.T1(uint32(c.Uin), c.deviceInfo.IpAddress)) - w.Write(binary.NewWriterF(func(w *binary.Writer) { + wb, cl := binary.OpenWriterF(func(w *binary.Writer) { w.WriteUInt16(0x106) w.WriteBytesShort(t106) - })) + }) + w.Write(wb) + cl() // w.Write(tlv.T106(uint32(c.Uin), 0, c.version.AppId, c.version.SSOVersion, c.PasswordMd5, true, c.deviceInfo.Guid, c.deviceInfo.TgtgtKey, 0)) w.Write(tlv.T116(c.version.MiscBitmap, c.version.SubSigmap)) w.Write(tlv.T100(c.version.SSOVersion, c.version.SubAppId, c.version.MainSigMap)) @@ -194,10 +196,12 @@ func (c *QQClient) buildQRCodeLoginPacket(t106, t16a, t318 []byte) (uint16, []by w.Write(tlv.T145(c.deviceInfo.Guid)) w.Write(tlv.T147(16, []byte(c.version.SortVersionName), c.version.ApkSign)) - w.Write(binary.NewWriterF(func(w *binary.Writer) { + wb, cl = binary.OpenWriterF(func(w *binary.Writer) { w.WriteUInt16(0x16A) w.WriteBytesShort(t16a) - })) + }) + w.Write(wb) + cl() w.Write(tlv.T154(seq)) w.Write(tlv.T141(c.deviceInfo.SimInfo, c.deviceInfo.APN)) w.Write(tlv.T8(2052)) @@ -219,10 +223,12 @@ func (c *QQClient) buildQRCodeLoginPacket(t106, t16a, t318 []byte) (uint16, []by w.Write(tlv.T516()) w.Write(tlv.T521(8)) // w.Write(tlv.T525(tlv.T536([]byte{0x01, 0x00}))) - w.Write(binary.NewWriterF(func(w *binary.Writer) { + wb, cl = binary.OpenWriterF(func(w *binary.Writer) { w.WriteUInt16(0x318) w.WriteBytesShort(t318) - })) + }) + w.Write(wb) + cl() }) sso := packets.BuildSsoPacket(seq, c.version.AppId, c.version.SubAppId, "wtlogin.login", c.deviceInfo.IMEI, []byte{}, c.OutGoingPacketSessionId, req, c.ksid) packet := packets.BuildLoginPacket(c.Uin, 2, make([]byte, 16), sso, []byte{}) @@ -306,10 +312,12 @@ func (c *QQClient) buildRequestTgtgtNopicsigPacket() (uint16, []byte) { w.Write(tlv.T18(16, uint32(c.Uin))) w.Write(tlv.T1(uint32(c.Uin), c.deviceInfo.IpAddress)) - w.Write(binary.NewWriterF(func(w *binary.Writer) { + wb, cl := binary.OpenWriterF(func(w *binary.Writer) { w.WriteUInt16(0x106) w.WriteBytesShort(c.sigInfo.encryptedA1) - })) + }) + w.Write(wb) + cl() w.Write(tlv.T116(c.version.MiscBitmap, c.version.SubSigmap)) w.Write(tlv.T100(c.version.SSOVersion, 2, c.version.MainSigMap)) w.Write(tlv.T107(0)) @@ -989,13 +997,15 @@ func (c *QQClient) buildGroupKickPacket(groupCode, memberUin int64, kickMsg stri // OidbSvc.0x570_8 func (c *QQClient) buildGroupMutePacket(groupCode, memberUin int64, time uint32) (uint16, []byte) { seq := c.nextSeq() - payload := c.packOIDBPackage(1392, 8, binary.NewWriterF(func(w *binary.Writer) { + b, cl := binary.OpenWriterF(func(w *binary.Writer) { w.WriteUInt32(uint32(groupCode)) w.WriteByte(32) w.WriteUInt16(1) w.WriteUInt32(uint32(memberUin)) w.WriteUInt32(time) - })) + }) + payload := c.packOIDBPackage(1392, 8, b) + cl() packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0x570_8", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return seq, packet } @@ -1029,7 +1039,7 @@ func (c *QQClient) buildFriendPokePacket(target int64) (uint16, []byte) { // OidbSvc.0x55c_1 func (c *QQClient) buildGroupAdminSetPacket(groupCode, member int64, flag bool) (uint16, []byte) { seq := c.nextSeq() - payload := c.packOIDBPackage(1372, 1, binary.NewWriterF(func(w *binary.Writer) { + b, cl := binary.OpenWriterF(func(w *binary.Writer) { w.WriteUInt32(uint32(groupCode)) w.WriteUInt32(uint32(member)) w.WriteByte(func() byte { @@ -1038,7 +1048,9 @@ func (c *QQClient) buildGroupAdminSetPacket(groupCode, member int64, flag bool) } return 0 }()) - })) + }) + payload := c.packOIDBPackage(1372, 1, b) + cl() packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0x55c_1", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return seq, packet } @@ -1049,10 +1061,12 @@ func (c *QQClient) buildQuitGroupPacket(groupCode int64) (uint16, []byte) { jw := jce.NewJceWriter() jw.WriteInt32(2, 0) jw.WriteInt64(c.Uin, 1) - jw.WriteBytes(binary.NewWriterF(func(w *binary.Writer) { + b, cl := binary.OpenWriterF(func(w *binary.Writer) { w.WriteUInt32(uint32(c.Uin)) w.WriteUInt32(uint32(groupCode)) - }), 2) + }) + jw.WriteBytes(b, 2) + cl() buf := &jce.RequestDataVersion3{Map: map[string][]byte{"GroupMngReq": packUniRequestData(jw.Bytes())}} pkt := &jce.RequestPacket{ IVersion: 3, diff --git a/client/global.go b/client/global.go index 56d24df8..a239f61f 100644 --- a/client/global.go +++ b/client/global.go @@ -459,12 +459,15 @@ func getSSOAddress() ([]*net.TCPAddr, error) { SFuncName: "HttpServerListReq", SBuffer: buf.ToBytes(), } - tea := binary.NewTeaCipher(key) - rsp, err := utils.HttpPostBytes("https://configsvr.msf.3g.qq.com/configsvr/serverlist.jsp", tea.Encrypt(binary.NewWriterF(func(w *binary.Writer) { + b, cl := binary.OpenWriterF(func(w *binary.Writer) { w.WriteIntLvPacket(0, func(w *binary.Writer) { w.Write(pkt.ToBytes()) }) - }))) + }) + tea := binary.NewTeaCipher(key) + encpkt := tea.Encrypt(b) + cl() + rsp, err := utils.HttpPostBytes("https://configsvr.msf.3g.qq.com/configsvr/serverlist.jsp", encpkt) if err != nil { return nil, errors.Wrap(err, "unable to fetch server list") } From 13a9d087e732294d73a9da0a0a50efc73e6b8ed0 Mon Sep 17 00:00:00 2001 From: fumiama Date: Fri, 26 Nov 2021 15:02:44 +0800 Subject: [PATCH 4/4] =?UTF-8?q?perf(jce):=20drop=20reflect=20in=20reader?= =?UTF-8?q?=20name=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20old=20time/op=20=20=20=20new=20time/op=20=20=20=20delta=20Jc?= =?UTF-8?q?eReader=5FReadSlice-8=20=20=20=201.53ms=20=C2=B190%=20=20=20=20?= =?UTF-8?q?0.82ms=20=C2=B186%=20=20-46.30%=20=20(p=3D0.017=20n=3D16+16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit name old speed new speed delta JceReader_ReadSlice-8 117MB/s ± 3% 228MB/s ± 4% +94.43% (p=0.000 n=16+16) name old alloc/op new alloc/op delta JceReader_ReadSlice-8 516kB ±88% 536kB ±85% ~ (p=0.780 n=16+16) name old allocs/op new allocs/op delta JceReader_ReadSlice-8 25.6k ±88% 26.6k ±85% ~ (p=0.780 n=16+16) --- binary/jce/reader.go | 433 ++++++++++++++++++++++++++++++++------ binary/jce/reader_test.go | 12 +- binary/jce/structs.go | 53 ++--- client/decoders.go | 18 +- client/global.go | 3 +- client/group_info.go | 3 +- client/online_push.go | 3 +- client/sync.go | 7 +- 8 files changed, 406 insertions(+), 126 deletions(-) diff --git a/binary/jce/reader.go b/binary/jce/reader.go index e1f88fb3..eff8e1dc 100644 --- a/binary/jce/reader.go +++ b/binary/jce/reader.go @@ -3,7 +3,6 @@ package jce import ( goBinary "encoding/binary" "math" - "reflect" "github.com/Mrs4s/MiraiGo/utils" ) @@ -22,28 +21,36 @@ func NewJceReader(data []byte) *JceReader { return &JceReader{buf: data} } -func (r *JceReader) readHead() (hd HeadData, l int32) { - b := r.buf[r.off] - hd.Type = b & 0xF - hd.Tag = (int(b) & 0xF0) >> 4 - l = 1 - if hd.Tag == 15 { - b = r.buf[r.off+1] - hd.Tag = int(b) & 0xFF - l = 2 - } - r.off += int(l) +func (r *JceReader) readHead() (hd HeadData, l int) { + hd, l = r.peakHead() + r.off += l return } -func (r *JceReader) peakHead() (h HeadData, l int32) { - h, l = r.readHead() - r.off -= int(l) +func (r *JceReader) peakHead() (hd HeadData, l int) { + b := r.buf[r.off] + hd.Type = b & 0xF + hd.Tag = int(uint(b) >> 4) + l = 1 + if hd.Tag == 0xF { + b = r.buf[r.off+1] + hd.Tag = int(uint(b)) + l = 2 + } + return +} + +func (r *JceReader) skipHead() { + l := 1 + if int(uint(r.buf[r.off])>>4) == 0xF { + l = 2 + } + r.off += l return } func (r *JceReader) skip(l int) { - r.readBytes(l) + r.skipBytes(l) } func (r *JceReader) skipField(t byte) { @@ -57,10 +64,9 @@ func (r *JceReader) skipField(t byte) { case 3, 5: r.skip(8) case 6: - b := r.readByte() - r.skip(int(b)) + r.skip(int(r.readByte())) case 7: - r.skip(int(r.readInt32())) + r.skip(int(r.readUInt32())) case 8: s := r.ReadInt32(0) for i := 0; i < int(s)*2; i++ { @@ -72,7 +78,7 @@ func (r *JceReader) skipField(t byte) { r.skipNextField() } case 13: - r.readHead() + r.skipHead() s := r.ReadInt32(0) r.skip(int(s)) case 10: @@ -96,11 +102,21 @@ func (r *JceReader) readBytes(n int) []byte { panic("readBytes: EOF") } b := make([]byte, n) - n = copy(b, r.buf[r.off:]) - r.off += n + r.off += copy(b, r.buf[r.off:]) return b } +func (r *JceReader) skipBytes(n int) { + if r.off+n > len(r.buf) { + panic("skipBytes: EOF") + } + lremain := len(r.buf[r.off:]) + if lremain < n { + n = lremain + } + r.off += n +} + func (r *JceReader) readByte() byte { if r.off >= len(r.buf) { panic("readByte: EOF") @@ -111,48 +127,40 @@ func (r *JceReader) readByte() byte { } func (r *JceReader) readUInt16() uint16 { - b := r.readBytes(2) - return uint16((int32(b[0]) << 8) + int32(b[1])) + return goBinary.BigEndian.Uint16(r.readBytes(2)) } -func (r *JceReader) readInt32() int32 { - b := r.readBytes(4) - return int32(goBinary.BigEndian.Uint32(b)) +func (r *JceReader) readUInt32() uint32 { + return goBinary.BigEndian.Uint32(r.readBytes(4)) } -func (r *JceReader) readInt64() int64 { - b := r.readBytes(8) - return int64(goBinary.BigEndian.Uint64(b)) +func (r *JceReader) readUInt64() uint64 { + return goBinary.BigEndian.Uint64(r.readBytes(8)) } func (r *JceReader) readFloat32() float32 { - b := r.readInt32() - return math.Float32frombits(uint32(b)) + return math.Float32frombits(r.readUInt32()) } func (r *JceReader) readFloat64() float64 { - b := r.readInt64() - return math.Float64frombits(uint64(b)) + return math.Float64frombits(r.readUInt64()) } func (r *JceReader) skipToTag(tag int) bool { - for { - hd, l := r.peakHead() - if tag <= hd.Tag || hd.Type == 11 { - return tag == hd.Tag - } - r.skip(int(l)) + hd, l := r.peakHead() + for tag > hd.Tag && hd.Type != 11 { + r.skip(l) r.skipField(hd.Type) + hd, l = r.peakHead() } + return tag == hd.Tag } func (r *JceReader) skipToStructEnd() { - for { - hd, _ := r.readHead() + hd, _ := r.readHead() + for hd.Type != 11 { r.skipField(hd.Type) - if hd.Type == 11 { - return - } + hd, _ = r.readHead() } } @@ -205,7 +213,7 @@ func (r *JceReader) ReadInt32(tag int) int32 { case 1: return int32(r.readUInt16()) case 2: - return r.readInt32() + return int32(r.readUInt32()) default: return 0 } @@ -224,9 +232,9 @@ func (r *JceReader) ReadInt64(tag int) int64 { case 1: return int64(int16(r.readUInt16())) case 2: - return int64(r.readInt32()) + return int64(r.readUInt32()) case 3: - return r.readInt64() + return int64(r.readUInt64()) default: return 0 } @@ -273,7 +281,7 @@ func (r *JceReader) ReadString(tag int) string { case 6: return utils.B2S(r.readBytes(int(r.readByte()))) case 7: - return utils.B2S(r.readBytes(int(r.readInt32()))) + return utils.B2S(r.readBytes(int(r.readUInt32()))) default: return "" } @@ -293,13 +301,32 @@ func (r *JceReader) ReadBytes(tag int) []byte { } return b case 13: - r.readHead() + r.skipHead() return r.readBytes(int(r.ReadInt32(0))) default: return nil } } +func (r *JceReader) ReadByteArrArr(tag int) (baa [][]byte) { + if !r.skipToTag(tag) { + return nil + } + hd, _ := r.readHead() + switch hd.Type { + case 9: + s := r.ReadInt32(0) + baa = make([][]byte, s) + for i := 0; i < int(s); i++ { + baa[i] = r.ReadBytes(0) + } + return baa + default: + return nil + } +} + +/* // ReadAny Read any type via tag, unsupported JceStruct func (r *JceReader) ReadAny(tag int) interface{} { if !r.skipToTag(tag) { @@ -312,17 +339,17 @@ func (r *JceReader) ReadAny(tag int) interface{} { case 1: return r.readUInt16() case 2: - return r.readInt32() + return r.readUInt32() case 3: - return r.readInt64() + return r.readUInt64() case 4: return r.readFloat32() case 5: return r.readFloat64() case 6: - return string(r.readBytes(int(r.readByte()))) + return utils.B2S(r.readBytes(int(r.readByte()))) case 7: - return string(r.readBytes(int(r.readInt32()))) + return utils.B2S(r.readBytes(int(r.readUInt32()))) case 8: s := r.ReadInt32(0) m := make(map[interface{}]interface{}) @@ -340,12 +367,13 @@ func (r *JceReader) ReadAny(tag int) interface{} { case 12: return 0 case 13: - r.readHead() + r.skipHead() return r.readBytes(int(r.ReadInt32(0))) default: return nil } } +*/ func (r *JceReader) ReadJceStruct(obj IJceStruct, tag int) { if !r.skipToTag(tag) { @@ -359,9 +387,63 @@ func (r *JceReader) ReadJceStruct(obj IJceStruct, tag int) { r.skipToStructEnd() } +func (r *JceReader) ReadMapStrStr(tag int) map[string]string { + if !r.skipToTag(tag) { + return nil + } + hd, _ := r.readHead() + switch hd.Type { + case 8: + s := r.ReadInt32(0) + m := make(map[string]string, s) + for i := 0; i < int(s); i++ { + m[r.ReadString(0)] = r.ReadString(1) + } + return m + default: + return nil + } +} + +func (r *JceReader) ReadMapStrByte(tag int) map[string][]byte { + if !r.skipToTag(tag) { + return nil + } + hd, _ := r.readHead() + switch hd.Type { + case 8: + s := r.ReadInt32(0) + m := make(map[string][]byte, s) + for i := 0; i < int(s); i++ { + m[r.ReadString(0)] = r.ReadBytes(1) + } + return m + default: + return nil + } +} + +func (r *JceReader) ReadMapStrMapStrByte(tag int) map[string]map[string][]byte { + if !r.skipToTag(tag) { + return nil + } + hd, _ := r.readHead() + switch hd.Type { + case 8: + s := r.ReadInt32(0) + m := make(map[string]map[string][]byte, s) + for i := 0; i < int(s); i++ { + m[r.ReadString(0)] = r.ReadMapStrByte(1) + } + return m + default: + return nil + } +} + +/* func (r *JceReader) ReadMap(i interface{}, tag int) { - v := reflect.ValueOf(i) - r.readMap(v, tag) + r.readMap(reflect.ValueOf(i), tag) } func (r *JceReader) readMap(v reflect.Value, tag int) { @@ -372,7 +454,7 @@ func (r *JceReader) readMap(v reflect.Value, tag int) { kt := t.Key() vt := t.Elem() - r.readHead() + r.skipHead() s := r.ReadInt32(0) // map with string key or string value is very common. @@ -404,21 +486,240 @@ func (r *JceReader) readMap(v reflect.Value, tag int) { v.SetMapIndex(kv.Elem(), vv.Elem()) } } +*/ +func (r *JceReader) ReadFileStorageServerInfos(tag int) []FileStorageServerInfo { + if !r.skipToTag(tag) { + return nil + } + hd, _ := r.readHead() + switch hd.Type { + case 9: + s := r.ReadInt32(0) + sl := make([]FileStorageServerInfo, s) + for i := 0; i < int(s); i++ { + r.skipHead() + sl[i].ReadFrom(r) + r.skipToStructEnd() + } + return sl + default: + return nil + } +} + +func (r *JceReader) ReadBigDataIPLists(tag int) []BigDataIPList { + if !r.skipToTag(tag) { + return nil + } + hd, _ := r.readHead() + switch hd.Type { + case 9: + s := r.ReadInt32(0) + sl := make([]BigDataIPList, s) + for i := 0; i < int(s); i++ { + r.skipHead() + sl[i].ReadFrom(r) + r.skipToStructEnd() + } + return sl + default: + return nil + } +} + +func (r *JceReader) ReadBigDataIPInfos(tag int) []BigDataIPInfo { + if !r.skipToTag(tag) { + return nil + } + hd, _ := r.readHead() + switch hd.Type { + case 9: + s := r.ReadInt32(0) + sl := make([]BigDataIPInfo, s) + for i := 0; i < int(s); i++ { + r.skipHead() + sl[i].ReadFrom(r) + r.skipToStructEnd() + } + return sl + default: + return nil + } +} + +func (r *JceReader) ReadOnlineInfos(tag int) []OnlineInfo { + if !r.skipToTag(tag) { + return nil + } + hd, _ := r.readHead() + switch hd.Type { + case 9: + s := r.ReadInt32(0) + sl := make([]OnlineInfo, s) + for i := 0; i < int(s); i++ { + r.skipHead() + sl[i].ReadFrom(r) + r.skipToStructEnd() + } + return sl + default: + return nil + } +} + +func (r *JceReader) ReadInstanceInfos(tag int) []InstanceInfo { + if !r.skipToTag(tag) { + return nil + } + hd, _ := r.readHead() + switch hd.Type { + case 9: + s := r.ReadInt32(0) + sl := make([]InstanceInfo, s) + for i := 0; i < int(s); i++ { + r.skipHead() + sl[i].ReadFrom(r) + r.skipToStructEnd() + } + return sl + default: + return nil + } +} + +func (r *JceReader) ReadSsoServerInfos(tag int) []SsoServerInfo { + if !r.skipToTag(tag) { + return nil + } + hd, _ := r.readHead() + switch hd.Type { + case 9: + s := r.ReadInt32(0) + sl := make([]SsoServerInfo, s) + for i := 0; i < int(s); i++ { + r.skipHead() + sl[i].ReadFrom(r) + r.skipToStructEnd() + } + return sl + default: + return nil + } +} + +func (r *JceReader) ReadFriendInfos(tag int) []FriendInfo { + if !r.skipToTag(tag) { + return nil + } + hd, _ := r.readHead() + switch hd.Type { + case 9: + s := r.ReadInt32(0) + sl := make([]FriendInfo, s) + for i := 0; i < int(s); i++ { + r.skipHead() + sl[i].ReadFrom(r) + r.skipToStructEnd() + } + return sl + default: + return nil + } +} + +func (r *JceReader) ReadTroopNumbers(tag int) []TroopNumber { + if !r.skipToTag(tag) { + return nil + } + hd, _ := r.readHead() + switch hd.Type { + case 9: + s := r.ReadInt32(0) + sl := make([]TroopNumber, s) + for i := 0; i < int(s); i++ { + r.skipHead() + sl[i].ReadFrom(r) + r.skipToStructEnd() + } + return sl + default: + return nil + } +} + +func (r *JceReader) ReadTroopMemberInfos(tag int) []TroopMemberInfo { + if !r.skipToTag(tag) { + return nil + } + hd, _ := r.readHead() + switch hd.Type { + case 9: + s := r.ReadInt32(0) + sl := make([]TroopMemberInfo, s) + for i := 0; i < int(s); i++ { + r.skipHead() + sl[i].ReadFrom(r) + r.skipToStructEnd() + } + return sl + default: + return nil + } +} + +func (r *JceReader) ReadPushMessageInfos(tag int) []PushMessageInfo { + if !r.skipToTag(tag) { + return nil + } + hd, _ := r.readHead() + switch hd.Type { + case 9: + s := r.ReadInt32(0) + sl := make([]PushMessageInfo, s) + for i := 0; i < int(s); i++ { + r.skipHead() + sl[i].ReadFrom(r) + r.skipToStructEnd() + } + return sl + default: + return nil + } +} + +func (r *JceReader) ReadSvcDevLoginInfos(tag int) []SvcDevLoginInfo { + if !r.skipToTag(tag) { + return nil + } + hd, _ := r.readHead() + switch hd.Type { + case 9: + s := r.ReadInt32(0) + sl := make([]SvcDevLoginInfo, s) + for i := 0; i < int(s); i++ { + r.skipHead() + sl[i].ReadFrom(r) + r.skipToStructEnd() + } + return sl + default: + return nil + } +} + +/* func (r *JceReader) ReadSlice(i interface{}, tag int) { r.readSlice(reflect.ValueOf(i), tag) } func (r *JceReader) readSlice(v reflect.Value, tag int) { t := v.Type() - if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Slice { + if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Slice || !r.skipToTag(tag) { return } v = v.Elem() t = t.Elem() - if !r.skipToTag(tag) { - return - } hd, _ := r.readHead() if hd.Type == 9 { s := r.ReadInt32(0) @@ -432,15 +733,14 @@ func (r *JceReader) readSlice(v reflect.Value, tag int) { v.Set(sv) } if hd.Type == 13 && t.Elem().Kind() == reflect.Uint8 { - r.readHead() + r.skipHead() arr := r.readBytes(int(r.ReadInt32(0))) v.SetBytes(arr) } } func (r *JceReader) ReadObject(i interface{}, tag int) { - v := reflect.ValueOf(i) - r.readObject(v, tag) + r.readObject(reflect.ValueOf(i), tag) } func (r *JceReader) readObject(v reflect.Value, tag int) { @@ -478,7 +778,7 @@ func (r *JceReader) readObject(v reflect.Value, tag int) { // other cases switch o := v.Interface().(type) { case IJceStruct: - r.readHead() + r.skipHead() o.ReadFrom(r) r.skipToStructEnd() case *float32: @@ -488,3 +788,4 @@ func (r *JceReader) readObject(v reflect.Value, tag int) { } } } +*/ diff --git a/binary/jce/reader_test.go b/binary/jce/reader_test.go index cebad57b..7caec125 100644 --- a/binary/jce/reader_test.go +++ b/binary/jce/reader_test.go @@ -8,15 +8,16 @@ import ( ) func TestJceReader_ReadSlice(t *testing.T) { - s := make([]int64, 50) + s := make([][]byte, 50) for i := range s { - s[i] = rand.Int63() + b := make([]byte, 64) + _, _ = rand.Read(b) + s[i] = b } w := NewJceWriter() w.WriteObject(s, 1) r := NewJceReader(w.Bytes()) - var result []int64 - r.ReadSlice(&result, 1) + result := r.ReadByteArrArr(1) assert.Equal(t, s, result) } @@ -35,10 +36,9 @@ func BenchmarkJceReader_ReadSlice(b *testing.B) { src := w.Bytes() b.SetBytes(int64(len(src))) b.StartTimer() - result := make([]BigDataIPInfo, 0) for i := 0; i < b.N; i++ { r := NewJceReader(src) - r.ReadSlice(&result, 1) + _ = r.ReadBigDataIPInfos(1) } } diff --git a/binary/jce/structs.go b/binary/jce/structs.go index cab8bcd0..65ecc6e9 100644 --- a/binary/jce/structs.go +++ b/binary/jce/structs.go @@ -555,10 +555,12 @@ func (pkt *RequestPacket) ReadFrom(r *JceReader) { pkt.IRequestId = r.ReadInt32(4) pkt.SServantName = r.ReadString(5) pkt.SFuncName = r.ReadString(6) - r.ReadSlice(&pkt.SBuffer, 7) + pkt.SBuffer = r.ReadBytes(7) pkt.ITimeout = r.ReadInt32(8) - r.ReadMap(pkt.Context, 9) - r.ReadMap(pkt.Status, 10) + // r.ReadMap(pkt.Context, 9) + pkt.Context = r.ReadMapStrStr(9) + // r.ReadMap(pkt.Status, 10) + pkt.Status = r.ReadMapStrStr(10) } func (pkt *RequestDataVersion3) ToBytes() []byte { @@ -568,8 +570,7 @@ func (pkt *RequestDataVersion3) ToBytes() []byte { } func (pkt *RequestDataVersion3) ReadFrom(r *JceReader) { - pkt.Map = make(map[string][]byte) - r.ReadMap(pkt.Map, 0) + pkt.Map = r.ReadMapStrByte(0) } func (pkt *RequestDataVersion2) ToBytes() []byte { @@ -579,8 +580,7 @@ func (pkt *RequestDataVersion2) ToBytes() []byte { } func (pkt *RequestDataVersion2) ReadFrom(r *JceReader) { - pkt.Map = make(map[string]map[string][]byte) - r.ReadMap(pkt.Map, 0) + pkt.Map = r.ReadMapStrMapStrByte(0) } func (pkt *SsoServerInfo) ReadFrom(r *JceReader) { @@ -590,22 +590,15 @@ func (pkt *SsoServerInfo) ReadFrom(r *JceReader) { } func (pkt *FileStoragePushFSSvcList) ReadFrom(r *JceReader) { - pkt.UploadList = []FileStorageServerInfo{} - pkt.PicDownloadList = []FileStorageServerInfo{} - pkt.GPicDownloadList = []FileStorageServerInfo{} - pkt.QZoneProxyServiceList = []FileStorageServerInfo{} - pkt.UrlEncodeServiceList = []FileStorageServerInfo{} + pkt.UploadList = r.ReadFileStorageServerInfos(0) + pkt.PicDownloadList = r.ReadFileStorageServerInfos(1) + pkt.GPicDownloadList = r.ReadFileStorageServerInfos(2) + pkt.QZoneProxyServiceList = r.ReadFileStorageServerInfos(3) + pkt.UrlEncodeServiceList = r.ReadFileStorageServerInfos(4) pkt.BigDataChannel = &BigDataChannel{} - pkt.VipEmotionList = []FileStorageServerInfo{} - pkt.C2CPicDownList = []FileStorageServerInfo{} - r.ReadSlice(&pkt.UploadList, 0) - r.ReadSlice(&pkt.PicDownloadList, 1) - r.ReadSlice(&pkt.GPicDownloadList, 2) - r.ReadSlice(&pkt.QZoneProxyServiceList, 3) - r.ReadSlice(&pkt.UrlEncodeServiceList, 4) + pkt.VipEmotionList = r.ReadFileStorageServerInfos(5) + pkt.C2CPicDownList = r.ReadFileStorageServerInfos(7) r.ReadJceStruct(pkt.BigDataChannel, 5) - r.ReadSlice(&pkt.VipEmotionList, 6) - r.ReadSlice(&pkt.C2CPicDownList, 7) pkt.PttList = r.ReadBytes(10) } @@ -615,8 +608,7 @@ func (pkt *FileStorageServerInfo) ReadFrom(r *JceReader) { } func (pkt *BigDataChannel) ReadFrom(r *JceReader) { - pkt.IPLists = []BigDataIPList{} - r.ReadSlice(&pkt.IPLists, 0) + pkt.IPLists = r.ReadBigDataIPLists(0) pkt.SigSession = r.ReadBytes(1) pkt.KeySession = r.ReadBytes(2) pkt.SigUin = r.ReadInt64(3) @@ -625,9 +617,8 @@ func (pkt *BigDataChannel) ReadFrom(r *JceReader) { } func (pkt *BigDataIPList) ReadFrom(r *JceReader) { - pkt.IPList = []BigDataIPInfo{} pkt.ServiceType = r.ReadInt64(0) - r.ReadSlice(&pkt.IPList, 1) + pkt.IPList = r.ReadBigDataIPInfos(1) pkt.FragmentSize = r.ReadInt64(3) } @@ -692,8 +683,7 @@ func (pkt *FriendInfo) ReadFrom(r *JceReader) { pkt.Nick = r.ReadString(14) pkt.Network = r.ReadByte(20) pkt.NetworkType = r.ReadInt32(24) - pkt.CardID = []byte{} - r.ReadObject(&pkt.CardID, 41) + pkt.CardID = r.ReadBytes(41) } func (pkt *TroopListRequest) ToBytes() []byte { @@ -750,8 +740,7 @@ func (pkt *PushMessageInfo) ReadFrom(r *JceReader) { func (pkt *SvcDevLoginInfo) ReadFrom(r *JceReader) { pkt.AppId = r.ReadInt64(0) - pkt.Guid = []byte{} - r.ReadSlice(&pkt.Guid, 1) + pkt.Guid = r.ReadBytes(1) pkt.LoginTime = r.ReadInt64(2) pkt.LoginPlatform = r.ReadInt64(3) pkt.LoginLocation = r.ReadString(4) @@ -763,7 +752,6 @@ func (pkt *SvcDevLoginInfo) ReadFrom(r *JceReader) { } func (pkt *SvcRespParam) ReadFrom(r *JceReader) { - pkt.OnlineInfos = []OnlineInfo{} pkt.PCStat = r.ReadInt32(0) pkt.IsSupportC2CRoamMsg = r.ReadInt32(1) pkt.IsSupportDataLine = r.ReadInt32(2) @@ -771,7 +759,7 @@ func (pkt *SvcRespParam) ReadFrom(r *JceReader) { pkt.IsSupportViewPCFile = r.ReadInt32(4) pkt.PcVersion = r.ReadInt32(5) pkt.RoamFlag = r.ReadInt64(6) - r.ReadSlice(&pkt.OnlineInfos, 7) + pkt.OnlineInfos = r.ReadOnlineInfos(7) pkt.PCClientType = r.ReadInt32(8) } @@ -797,7 +785,6 @@ func (pkt *OnlineInfo) ReadFrom(r *JceReader) { } func (pkt *SvcReqMSFLoginNotify) ReadFrom(r *JceReader) { - pkt.InstanceList = []InstanceInfo{} pkt.AppId = r.ReadInt64(0) pkt.Status = r.ReadByte(1) pkt.Tablet = r.ReadByte(2) @@ -806,7 +793,7 @@ func (pkt *SvcReqMSFLoginNotify) ReadFrom(r *JceReader) { pkt.Info = r.ReadString(5) pkt.ProductType = r.ReadInt64(6) pkt.ClientType = r.ReadInt64(7) - r.ReadSlice(&pkt.InstanceList, 8) + pkt.InstanceList = r.ReadInstanceInfos(8) } func (pkt *InstanceInfo) ReadFrom(r *JceReader) { diff --git a/client/decoders.go b/client/decoders.go index a1f45a15..0bf9b8bb 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -312,8 +312,7 @@ func decodePushReqPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (in switch t { case 1: ssoPkt := jce.NewJceReader(jceBuf) - servers := []jce.SsoServerInfo{} - ssoPkt.ReadSlice(&servers, 1) + servers := ssoPkt.ReadSsoServerInfos(1) if len(servers) > 0 { var adds []*net.TCPAddr for _, s := range servers { @@ -433,8 +432,7 @@ func decodeSummaryCardResponse(_ *QQClient, _ *incomingPacketInfo, payload []byt Uin: rsp.ReadInt64(23), LoginDays: rsp.ReadInt64(36), } - services := [][]byte{} - rsp.ReadSlice(&services, 46) + services := rsp.ReadByteArrArr(46) readService := func(buf []byte) (*profilecard.BusiComm, []byte) { r := binary.NewReader(buf) r.ReadByte() @@ -467,8 +465,7 @@ func decodeFriendGroupListResponse(_ *QQClient, _ *incomingPacketInfo, payload [ data.ReadFrom(jce.NewJceReader(request.SBuffer)) r := jce.NewJceReader(data.Map["FLRESP"][1:]) totalFriendCount := r.ReadInt16(5) - friends := make([]jce.FriendInfo, 0) - r.ReadSlice(&friends, 7) + friends := r.ReadFriendInfos(7) l := make([]*FriendInfo, 0, len(friends)) for _, f := range friends { l = append(l, &FriendInfo{ @@ -505,10 +502,8 @@ func decodeGroupListResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) data := &jce.RequestDataVersion3{} data.ReadFrom(jce.NewJceReader(request.SBuffer)) r := jce.NewJceReader(data.Map["GetTroopListRespV2"][1:]) - vecCookie := []byte{} - groups := []jce.TroopNumber{} - r.ReadSlice(&vecCookie, 4) - r.ReadSlice(&groups, 5) + vecCookie := r.ReadBytes(4) + groups := r.ReadTroopNumbers(5) l := make([]*GroupInfo, 0, len(groups)) for _, g := range groups { l = append(l, &GroupInfo{ @@ -539,8 +534,7 @@ func decodeGroupMemberListResponse(_ *QQClient, _ *incomingPacketInfo, payload [ data := &jce.RequestDataVersion3{} data.ReadFrom(jce.NewJceReader(request.SBuffer)) r := jce.NewJceReader(data.Map["GTMLRESP"][1:]) - members := make([]jce.TroopMemberInfo, 0) - r.ReadSlice(&members, 3) + members := r.ReadTroopMemberInfos(3) next := r.ReadInt64(4) l := make([]*GroupMemberInfo, 0, len(members)) for _, m := range members { diff --git a/client/global.go b/client/global.go index a239f61f..62471e62 100644 --- a/client/global.go +++ b/client/global.go @@ -476,8 +476,7 @@ func getSSOAddress() ([]*net.TCPAddr, error) { rspPkt.ReadFrom(jce.NewJceReader(tea.Decrypt(rsp)[4:])) data.ReadFrom(jce.NewJceReader(rspPkt.SBuffer)) reader := jce.NewJceReader(data.Map["HttpServerListRes"][1:]) - servers := []jce.SsoServerInfo{} - reader.ReadSlice(&servers, 2) + servers := reader.ReadSsoServerInfos(2) adds := make([]*net.TCPAddr, 0, len(servers)) for _, s := range servers { if strings.Contains(s.Server, "com") { diff --git a/client/group_info.go b/client/group_info.go index c103c904..46add952 100644 --- a/client/group_info.go +++ b/client/group_info.go @@ -200,7 +200,8 @@ func decodeGroupSearchResponse(_ *QQClient, _ *incomingPacketInfo, payload []byt } rsp := data.Map["RespSearch"]["SummaryCard.RespSearch"][1:] r := jce.NewJceReader(rsp) - rspService := r.ReadAny(2).([]interface{})[0].([]byte) + // rspService := r.ReadAny(2).([]interface{})[0].([]byte) + rspService := r.ReadByteArrArr(2)[0] sr := binary.NewReader(rspService) sr.ReadByte() ld1 := sr.ReadInt32() diff --git a/client/online_push.go b/client/online_push.go index 2718a930..c94b0cf0 100644 --- a/client/online_push.go +++ b/client/online_push.go @@ -28,9 +28,8 @@ func decodeOnlinePushReqPacket(c *QQClient, info *incomingPacketInfo, payload [] data := &jce.RequestDataVersion2{} data.ReadFrom(jce.NewJceReader(request.SBuffer)) jr := jce.NewJceReader(data.Map["req"]["OnlinePushPack.SvcReqPushMsg"][1:]) - msgInfos := []jce.PushMessageInfo{} uin := jr.ReadInt64(0) - jr.ReadSlice(&msgInfos, 2) + msgInfos := jr.ReadPushMessageInfos(2) _ = c.sendPacket(c.buildDeleteOnlinePushPacket(uin, 0, nil, info.SequenceId, msgInfos)) for _, m := range msgInfos { k := fmt.Sprintf("%v%v%v", m.MsgSeq, m.MsgTime, m.MsgUid) diff --git a/client/sync.go b/client/sync.go index 7b9e05ee..1e073376 100644 --- a/client/sync.go +++ b/client/sync.go @@ -302,16 +302,15 @@ func decodeDevListResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) ( data := &jce.RequestDataVersion2{} data.ReadFrom(jce.NewJceReader(request.SBuffer)) rsp := jce.NewJceReader(data.Map["SvcRspGetDevLoginInfo"]["QQService.SvcRspGetDevLoginInfo"][1:]) - d := []jce.SvcDevLoginInfo{} - rsp.ReadSlice(&d, 4) + d := rsp.ReadSvcDevLoginInfos(4) if len(d) > 0 { return d, nil } - rsp.ReadSlice(&d, 5) + d = rsp.ReadSvcDevLoginInfos(5) if len(d) > 0 { return d, nil } - rsp.ReadSlice(&d, 6) + d = rsp.ReadSvcDevLoginInfos(6) if len(d) > 0 { return d, nil }