mirror of
https://github.com/idrunk/dce-go.git
synced 2026-04-22 23:17:04 +08:00
feat: 升级路由中间件机制,优化 FlexNum 和 Package 打包/解析
This commit is contained in:
+11
-8
@@ -28,15 +28,18 @@ DCE 致力于打造一个高效、开放、安全的通用路由库,欢迎社
|
||||
---
|
||||
|
||||
**TODO**:
|
||||
- [ ] 优化 JS 版 WebSocket 可路由协议客户端。
|
||||
- [ ] 升级控制器前后置事件接口,支持与程序接口绑定。
|
||||
- [x] 优化 JS 版 WebSocket 可路由协议客户端。(暂时搁置,可参见`easy-tools`库的实现)
|
||||
- [x] 升级控制器前后置事件接口,支持与程序接口绑定。
|
||||
- [ ] 完善数字路径支持。
|
||||
- [ ] 调整弹性数字函数为结构方法式。
|
||||
- [ ] 研究可路由协议中支持自定义业务属性的可能性。
|
||||
- [ ] 支持文件上传等超大数据包的可路由协议。
|
||||
- [x] 调整弹性数字函数为结构方法式。(取消)
|
||||
- [x] 研究可路由协议中支持自定义业务属性的可能性。(已实现)
|
||||
- [x] 支持文件上传等超大数据包的可路由协议。(暂时搁置,可参见`easy-tools`库的实现)
|
||||
- [ ] 升级 DCE-RUST 功能版本。
|
||||
- [ ] 完善各协议的 Golang 客户端实现。
|
||||
- [x] 完善各协议的 Golang 客户端实现。(取消)
|
||||
- [ ] 逐步替换 AI 生成的文档为人工编写文档。
|
||||
- [ ] Api扩展属性支持继承。
|
||||
|
||||
*由于工作原因,加上不知此框架有任何实际应用者,作者将不计划新功能开发,包括上述全部TODO。后续若作者自身有需求,或者达到一定数量的用户需求,作者将继续开发,BUG修复将优先于新功能开发。若你认同此项目,想推动其发展,欢迎协作开发,欢迎对外推广。*
|
||||
|
||||
---
|
||||
|
||||
@@ -146,7 +149,7 @@ func bindServer() {
|
||||
jc.Response(member)
|
||||
})
|
||||
|
||||
flex.TcpRouter.SetEventHandler(func(c *flex.Tcp) error {
|
||||
flex.TcpRouter.SetBefore("*", func(c *flex.Tcp) error {
|
||||
shadow, _ := c.Rp.CtxData("$shadowSession")
|
||||
rs := shadow.(*session.ShmSession[*Member])
|
||||
cloned, err := rs.CloneForRequest(c.Rp.Sid())
|
||||
@@ -171,7 +174,7 @@ func bindServer() {
|
||||
}
|
||||
c.Rp.SetSession(se)
|
||||
return nil
|
||||
}, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func bindClient() {
|
||||
|
||||
@@ -28,15 +28,18 @@ DCE is committed to building an efficient, open, and secure universal routing li
|
||||
---
|
||||
|
||||
**TODO**:
|
||||
- [ ] Optimize the JS version of the WebSocket routable protocol client.
|
||||
- [ ] Upgrade the controller pre- and post-event interfaces to support binding with program interfaces.
|
||||
- [x] Optimize the JS version of the WebSocket routable protocol client. (Temporarily shelved, refer to the implementation in the `easy-tools` repository)
|
||||
- [x] Upgrade the controller pre- and post-event interfaces to support binding with program interfaces.
|
||||
- [ ] Enhance support for digital paths.
|
||||
- [ ] Refactor elastic numeric functions into structural method styles.
|
||||
- [ ] Investigate the possibility of supporting custom business attributes in routable protocols.
|
||||
- [ ] Routable protocol support very large data packets such as file uploads.
|
||||
- [x] Refactor elastic numeric functions into structural method styles. (Canceled)
|
||||
- [x] Investigate the possibility of supporting custom business attributes in routable protocols. (Implemented)
|
||||
- [x] Routable protocol support very large data packets such as file uploads. (Temporarily shelved, refer to the implementation in the `easy-tools` repository)
|
||||
- [ ] Upgrade the feature version of DCE-RUST.
|
||||
- [ ] Improve the Golang client implementations for various protocols.
|
||||
- [x] Improve the Golang client implementations for various protocols. (Canceled)
|
||||
- [ ] Gradually replace AI-generated documentation with manually written documentation.
|
||||
- [ ] Api extension attributes support inheritance.
|
||||
|
||||
*Due to work reasons and not knowing of any actual users of this framework, the author does not plan to develop new features, including all the TODOs listed above. Development will resume later if the author has personal needs or if there is a certain number of user demands. Bug fixes will be prioritized over new feature development. If you recognize this project and want to promote its development, collaboration and external promotion are welcome.*
|
||||
|
||||
---
|
||||
|
||||
@@ -146,7 +149,7 @@ func bindServer() {
|
||||
jc.Response(member)
|
||||
})
|
||||
|
||||
flex.TcpRouter.SetEventHandler(func(c *flex.Tcp) error {
|
||||
flex.TcpRouter.SetBefore("*", func(c *flex.Tcp) error {
|
||||
shadow, _ := c.Rp.CtxData("$shadowSession")
|
||||
rs := shadow.(*session.ShmSession[*Member])
|
||||
cloned, err := rs.CloneForRequest(c.Rp.Sid())
|
||||
@@ -171,7 +174,7 @@ func bindServer() {
|
||||
}
|
||||
c.Rp.SetSession(se)
|
||||
return nil
|
||||
}, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func bindClient() {
|
||||
|
||||
@@ -109,7 +109,7 @@ func flexWebsocketBind(port string) {
|
||||
_, _ = w.Write([]byte{'1'})
|
||||
})
|
||||
|
||||
flex.WebsocketRouter.SetEventHandler(func(ctx *flex.Websocket) error {
|
||||
flex.WebsocketRouter.SetBefore("*", func(ctx *flex.Websocket) error {
|
||||
shadowSession, _ := ctx.Rp.CtxData("$shadowSession")
|
||||
rs := shadowSession.(*session.ShmSession[*session.SimpleUser])
|
||||
cloned, _ := rs.CloneForRequest(ctx.Rp.Sid())
|
||||
@@ -123,7 +123,7 @@ func flexWebsocketBind(port string) {
|
||||
ctx.Rp.SetRespSid(sess.Id())
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func syncUserList(sess *session.ShmSession[*session.SimpleUser], h *proto.Http, user *session.SimpleUser) {
|
||||
|
||||
+118
-122
@@ -95,15 +95,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
class FlexPackage {
|
||||
static #flagId = 128
|
||||
static #flagPath = 64
|
||||
static #flagSid = 32
|
||||
static #flagCode = 16
|
||||
static #flagMsg = 8
|
||||
static #flagBody = 4
|
||||
static #flagNumPath = 2
|
||||
class PackageField {
|
||||
constructor(field, kind) {
|
||||
this.field = field
|
||||
this.kind = kind
|
||||
}
|
||||
}
|
||||
|
||||
const baseFields = [
|
||||
new PackageField("id", "uint"),
|
||||
new PackageField("path", "string"),
|
||||
new PackageField("numPath", "uint"),
|
||||
new PackageField("sid", "string"),
|
||||
new PackageField("code", "int"),
|
||||
new PackageField("message", "string"),
|
||||
new PackageField("body", "bytes"),
|
||||
];
|
||||
|
||||
class FlexPackage {
|
||||
static #reqId = 0
|
||||
|
||||
id
|
||||
@@ -128,120 +137,107 @@
|
||||
}
|
||||
|
||||
serialize() {
|
||||
const buffer = [0]
|
||||
const lenSeqInfo = []
|
||||
const textBuffer = []
|
||||
let seq
|
||||
if ((this.path || "").length > 0) {
|
||||
buffer[0] |= FlexPackage.#flagPath
|
||||
textBuffer.push(seq = new TextEncoder().encode(this.path))
|
||||
lenSeqInfo.push(FlexNum.non0LenPackHead(seq.length))
|
||||
let flag = 0;
|
||||
const totalFields = baseFields.length;
|
||||
const numHeadVec = [];
|
||||
const textBuffer = [];
|
||||
const bodyBuffer = [];
|
||||
for (const [i, fc] of baseFields.entries()) {
|
||||
let nh;
|
||||
let textSeq;
|
||||
const prop = this[fc.field];
|
||||
if (prop === undefined) {
|
||||
continue;
|
||||
} else if (fc.kind === "int") {
|
||||
nh = FlexNum.intPackHead(prop);
|
||||
} else if (fc.kind === "uint") {
|
||||
nh = FlexNum.uintPackHead(prop);
|
||||
} else if (fc.kind === "string") {
|
||||
textSeq = new TextEncoder().encode(prop);
|
||||
nh = FlexNum.non0LenPackHead(textSeq.length);
|
||||
} else if (fc.kind === "bytes") {
|
||||
textSeq = new TextEncoder().encode(prop);
|
||||
nh = FlexNum.non0LenPackHead(textSeq.length);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
flag |= 1 << i;
|
||||
numHeadVec.push(nh);
|
||||
if (fc.kind === "bytes") {
|
||||
textSeq && bodyBuffer.push(textSeq);
|
||||
} else if (textSeq) {
|
||||
textBuffer.push(textSeq);
|
||||
}
|
||||
}
|
||||
if ((this.sid || "").length > 0) {
|
||||
buffer[0] |= FlexPackage.#flagSid
|
||||
textBuffer.push(seq = new TextEncoder().encode(this.sid))
|
||||
lenSeqInfo.push(FlexNum.non0LenPackHead(seq.length))
|
||||
|
||||
const flagSeq = FlexNum.uintSerialize(flag);
|
||||
const flagLen = flagSeq.length;
|
||||
const buffer = [...flagSeq];
|
||||
buffer[buffer.length + flagLen - 1] = undefined;
|
||||
for (const [i, nh] of numHeadVec.entries()) {
|
||||
buffer[flagLen+i] = nh[0];
|
||||
buffer.push(...FlexNum.packBody(nh));
|
||||
}
|
||||
if ((this.message || "").length > 0) {
|
||||
buffer[0] |= FlexPackage.#flagMsg
|
||||
textBuffer.push(seq = new TextEncoder().encode(this.message))
|
||||
lenSeqInfo.push(FlexNum.non0LenPackHead(seq.length))
|
||||
for (const part of textBuffer) {
|
||||
buffer.push(...part);
|
||||
}
|
||||
if ((this.body || "").length > 0) {
|
||||
buffer[0] |= FlexPackage.#flagBody
|
||||
textBuffer.push(seq = new TextEncoder().encode(this.body))
|
||||
lenSeqInfo.push(FlexNum.non0LenPackHead(seq.length))
|
||||
for (const part of bodyBuffer) {
|
||||
buffer.push(...part);
|
||||
}
|
||||
if (this.id > 0) {
|
||||
buffer[0] |= FlexPackage.#flagId
|
||||
lenSeqInfo.push(FlexNum.non0LenPackHead(this.id))
|
||||
}
|
||||
if (this.code) {
|
||||
buffer[0] |= FlexPackage.#flagCode
|
||||
lenSeqInfo.push(FlexNum.intPackHead(this.code))
|
||||
}
|
||||
if (this.numPath > 0) {
|
||||
buffer[0] |= FlexPackage.#flagNumPath
|
||||
lenSeqInfo.push(FlexNum.non0LenPackHead(this.numPath))
|
||||
}
|
||||
buffer.push(...Array(lenSeqInfo.length).fill(0))
|
||||
for (let i=0; i<lenSeqInfo.length; i++) {
|
||||
buffer[1+i] = lenSeqInfo[i][0]
|
||||
buffer.push(...FlexNum.packBody(lenSeqInfo[i][3], lenSeqInfo[i][2], lenSeqInfo[i][1]))
|
||||
}
|
||||
for (let i=0; i<textBuffer.length; i++) {
|
||||
buffer.push(...textBuffer[i])
|
||||
}
|
||||
return Uint8Array.from(buffer)
|
||||
return Uint8Array.from(buffer);
|
||||
}
|
||||
|
||||
static deserialize(seq) {
|
||||
const fp = new FlexPackage
|
||||
const flag = seq.splice(0, 1)?.[0]
|
||||
if (! flag) {
|
||||
return fp
|
||||
const fp = new FlexPackage;
|
||||
const head = seq.splice(0, 1)?.[0];
|
||||
if (!head) {
|
||||
return fp;
|
||||
}
|
||||
const onesCount = FlexNum.onesCount(flag)
|
||||
const numHeadSeq = seq.splice(0, onesCount)
|
||||
if (numHeadSeq.length < onesCount) {
|
||||
return fp
|
||||
const nh = FlexNum.parseHead(head, false);
|
||||
let flag = nh[0];
|
||||
if (nh[1] > 0) {
|
||||
const flagBodySeq = seq.splice(0, nh[1]);
|
||||
flagBodySeq.unshift(nh[3]);
|
||||
flag = FlexNum.parse(flagBodySeq);
|
||||
}
|
||||
const numInfoSeq = new Array(onesCount).fill(0)
|
||||
for (let i=0; i<onesCount; i++) {
|
||||
const tuple = FlexNum.parseHead(numHeadSeq[i], true)
|
||||
const numBodySeq = tuple[1] > 0 ? seq.splice(0, tuple[1]) : []
|
||||
if (numBodySeq.length < tuple[1]) {
|
||||
return fp
|
||||
|
||||
const onesCount = FlexNum.onesCount(flag);
|
||||
const numHeadList = seq.splice(0, onesCount)
|
||||
if (numHeadList.length < onesCount) {
|
||||
return fp;
|
||||
}
|
||||
const numInfoList = new Array(onesCount).fill(0)
|
||||
let nhi = 0;
|
||||
let bitsLen = FlexNum.bitsLen(flag);
|
||||
for (let i=0; i<bitsLen; i++) {
|
||||
if (((1 << i) & flag) === 0) {
|
||||
continue;
|
||||
}
|
||||
numInfoSeq[i] = [tuple, numBodySeq]
|
||||
const nh = FlexNum.parseHead(numHeadList[nhi], true);
|
||||
const numBodySeq = seq.splice(0, nh[1]);
|
||||
numInfoList[nhi] = [i, nh, numBodySeq];
|
||||
nhi ++;
|
||||
}
|
||||
if ((flag & this.#flagPath) > 0) {
|
||||
const [numInfo, numBodySeq] = numInfoSeq.shift()
|
||||
const len = FlexNum.non0LenParse([numInfo[3], ...numBodySeq])
|
||||
const sq = seq.splice(0, len)
|
||||
if (sq.length < len) {
|
||||
return fp
|
||||
|
||||
if (bitsLen > baseFields.length) {
|
||||
return fp;
|
||||
}
|
||||
for (const ni of numInfoList) {
|
||||
const fc = baseFields[ni[0]];
|
||||
if (fc.kind === "string" || fc.kind === "bytes") {
|
||||
const len = FlexNum.non0LenParse([ni[1][3], ...ni[2]]);
|
||||
const sq = seq.splice(0, len);
|
||||
if (sq.length < len) {
|
||||
return fp;
|
||||
}
|
||||
fp[fc.field] = new TextDecoder().decode(Uint8Array.from(sq));
|
||||
} else if (fc.kind === "int") {
|
||||
const val = FlexNum.intParse([ni[1][0], ...ni[2]], ni[1][2]);
|
||||
fp[fc.field] = val;
|
||||
} else if (fc.kind === "uint") {
|
||||
const val = FlexNum.parse([ni[1][3], ...ni[2]]);
|
||||
fp[fc.field] = val;
|
||||
}
|
||||
fp.path = new TextDecoder().decode(Uint8Array.from(sq))
|
||||
}
|
||||
if ((flag & this.#flagSid) > 0) {
|
||||
const [numInfo, numBodySeq] = numInfoSeq.shift()
|
||||
const len = FlexNum.non0LenParse([numInfo[3], ...numBodySeq])
|
||||
const sq = seq.splice(0, len)
|
||||
if (sq.length < len) {
|
||||
return fp
|
||||
}
|
||||
fp.sid = new TextDecoder().decode(Uint8Array.from(sq))
|
||||
}
|
||||
if ((flag & this.#flagMsg) > 0) {
|
||||
const [numInfo, numBodySeq] = numInfoSeq.shift()
|
||||
const len = FlexNum.non0LenParse([numInfo[3], ...numBodySeq])
|
||||
const sq = seq.splice(0, len)
|
||||
if (sq.length < len) {
|
||||
return fp
|
||||
}
|
||||
fp.message = new TextDecoder().decode(Uint8Array.from(sq))
|
||||
}
|
||||
if ((flag & this.#flagBody) > 0) {
|
||||
const [numInfo, numBodySeq] = numInfoSeq.shift()
|
||||
const len = FlexNum.non0LenParse([numInfo[3], ...numBodySeq])
|
||||
const sq = seq.splice(0, len)
|
||||
if (sq.length < len) {
|
||||
return fp
|
||||
}
|
||||
fp.body = new TextDecoder().decode(Uint8Array.from(sq))
|
||||
}
|
||||
if ((flag & this.#flagId) > 0) {
|
||||
const [numInfo, numBodySeq] = numInfoSeq.shift()
|
||||
fp.id = FlexNum.non0LenParse([numInfo[3], ...numBodySeq])
|
||||
}
|
||||
if ((flag & this.#flagCode) > 0) {
|
||||
const [numInfo, numBodySeq] = numInfoSeq.shift()
|
||||
fp.code = FlexNum.intParse([numInfo[0], ...numBodySeq], numInfo[2])
|
||||
}
|
||||
if ((flag & this.#flagNumPath) > 0) {
|
||||
const [numInfo, numBodySeq] = numInfoSeq.shift()
|
||||
fp.numPath = FlexNum.non0LenParse([numInfo[3], ...numBodySeq])
|
||||
}
|
||||
return fp
|
||||
}
|
||||
@@ -249,22 +245,22 @@
|
||||
|
||||
class FlexNum {
|
||||
static uintSerialize(unsigned) {
|
||||
return this.#serialize(...this.#uintPackHead(unsigned))
|
||||
return this.#serialize(...this.uintPackHead(unsigned));
|
||||
}
|
||||
|
||||
static intSerialize(integer) {
|
||||
return this.#serialize(...this.intPackHead(integer))
|
||||
return this.#serialize(...this.intPackHead(integer));
|
||||
}
|
||||
|
||||
static non0LenPackHead(unsigned) {
|
||||
return this.#uintPackHead(unsigned - 1)
|
||||
return this.uintPackHead(unsigned - 1);
|
||||
}
|
||||
|
||||
static #uintPackHead(unsigned) {
|
||||
const usize = Math.abs(unsigned)
|
||||
const bitsLen = this.bitsLen(usize)
|
||||
const [head, bytesLen] = this.#packHead(usize, bitsLen)
|
||||
return [head, bytesLen, bitsLen, usize]
|
||||
static uintPackHead(unsigned) {
|
||||
const usize = Math.abs(unsigned);
|
||||
const bitsLen = this.bitsLen(usize);
|
||||
const [head, bytesLen] = this.#packHead(usize, bitsLen);
|
||||
return [head, bytesLen, bitsLen, usize];
|
||||
}
|
||||
|
||||
static intPackHead(integer) {
|
||||
@@ -317,7 +313,7 @@
|
||||
|
||||
static uintDeserialize(seq) {
|
||||
[seq[0]] = this.parseHead(seq[0], false)
|
||||
return this.#parse(seq)
|
||||
return this.parse(seq)
|
||||
}
|
||||
|
||||
static intDeserialize(seq) {
|
||||
@@ -358,7 +354,7 @@
|
||||
}
|
||||
|
||||
static intParse(seq, negative) {
|
||||
const u64 = this.#parse(seq)
|
||||
const u64 = this.parse(seq)
|
||||
if (negative) {
|
||||
return -u64
|
||||
}
|
||||
@@ -366,10 +362,10 @@
|
||||
}
|
||||
|
||||
static non0LenParse(seq) {
|
||||
return this.#parse(seq) + 1
|
||||
return this.parse(seq) + 1
|
||||
}
|
||||
|
||||
static #parse(seq) {
|
||||
static parse(seq) {
|
||||
let u64 = 0
|
||||
for (let i = 0; i < seq.length; i++) {
|
||||
if (seq[i] > 0) {
|
||||
|
||||
@@ -30,16 +30,15 @@ func HttpStart(c *proto.Cli) {
|
||||
Method: proto.HttpGet,
|
||||
Omission: true,
|
||||
}, home).
|
||||
Raw().SetEventHandler(func(ctx *router.Context[*proto.HttpProtocol]) error {
|
||||
if ctx.Api.Path == "session/{username?}" {
|
||||
Raw().
|
||||
SetBefore("session/{username?}", func(ctx *router.Context[*proto.HttpProtocol]) error {
|
||||
if username := ctx.Param("username"); len(username) > 0 {
|
||||
ctx.Rp.SetCtxData("hello", "Inject from [BeforeController]")
|
||||
} else {
|
||||
return util.Openly(401, "Need to login")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
port := c.Rp.ArgOr("-p", "2046")
|
||||
fmt.Printf("Http server is starting on port %s\n", port)
|
||||
|
||||
@@ -128,7 +128,8 @@ func bind() {
|
||||
},
|
||||
)
|
||||
|
||||
proto.HttpRouter.Raw().SetEventHandler(func(ctx *router.Context[*proto.HttpProtocol]) error {
|
||||
proto.HttpRouter.Raw().
|
||||
SetBefore("*", func(ctx *router.Context[*proto.HttpProtocol]) error {
|
||||
sess, err := redises.NewSession[*Member](Rdb, []string{ctx.Rp.Sid()}, session.DefaultTtlMinutes)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -148,7 +149,8 @@ func bind() {
|
||||
}
|
||||
ctx.Rp.SetSession(sess)
|
||||
return nil
|
||||
}, func(ctx *router.Context[*proto.HttpProtocol]) error {
|
||||
}).
|
||||
SetAfter("*", func(ctx *router.Context[*proto.HttpProtocol]) error {
|
||||
if newSid := ctx.Rp.RespSid(); newSid != "" {
|
||||
_, _ = ctx.Rp.WriteString(fmt.Sprintf("\n\nGot new sid, you can use it to access private page:\n%s", newSid))
|
||||
}
|
||||
|
||||
+187
-260
@@ -3,11 +3,14 @@ package flex
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"math/bits"
|
||||
"net"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"go.drunkce.com/dce/router"
|
||||
"go.drunkce.com/dce/util"
|
||||
@@ -54,15 +57,78 @@ func NewPackageProtocolWithMeta[Req any](reader *bufio.Reader, meta router.Meta[
|
||||
return &PackageProtocol[Req]{meta, pkg}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
flagId uint8 = 128 >> iota
|
||||
flagPath
|
||||
flagSid
|
||||
flagCode
|
||||
flagMsg
|
||||
flagBody
|
||||
flagNumPath
|
||||
)
|
||||
type PackageField struct {
|
||||
Field string
|
||||
Kind reflect.Kind
|
||||
Get func(fc *PackageField, pkg *reflect.Value) (numHead *NumHead, textSeq []byte)
|
||||
Set func(fc *PackageField, pkg *reflect.Value, nh *NumHead, nbSeq []byte, reader io.Reader) (err error)
|
||||
}
|
||||
|
||||
func DefaultPropertyGetter(fc *PackageField, pkg *reflect.Value) (numHead *NumHead, textSeq []byte) {
|
||||
val := pkg.FieldByName(fc.Field)
|
||||
if val.IsZero() {
|
||||
// if (field.Required) {
|
||||
// log.Panicf("%s is required in Package.", field.Field)
|
||||
// }
|
||||
return
|
||||
}
|
||||
|
||||
if fc.Kind >= reflect.Int && fc.Kind < reflect.Uint { // int
|
||||
numHead = IntPackHead(val.Int())
|
||||
} else if fc.Kind >= reflect.Uint && fc.Kind <= reflect.Uint64 { // uint
|
||||
numHead = UintPackHead(val.Uint())
|
||||
} else if fc.Kind == reflect.String { // string
|
||||
str := val.String()
|
||||
numHead = Non0LenPackHead(uint(len(str)))
|
||||
textSeq = []byte(str)
|
||||
} else if bts, ok := val.Interface().([]byte); ok { // []byte
|
||||
numHead = Non0LenPackHead(uint(len(bts)))
|
||||
textSeq = bts
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func DefaultPropertySetter(fc *PackageField, pkg *reflect.Value, nh *NumHead, nbSeq []byte, reader io.Reader) (err error) {
|
||||
tyRef := pkg.Type()
|
||||
f, ok := tyRef.FieldByName(fc.Field)
|
||||
if !ok {
|
||||
log.Fatalf(`Field "%s" not defined in Package`, fc.Field)
|
||||
}
|
||||
field := pkg.FieldByIndex(f.Index)
|
||||
switch fc.Kind {
|
||||
case reflect.String, reflect.Slice:
|
||||
len := Non0LenParse(nh.Original, nbSeq)
|
||||
if fc.Kind == reflect.String {
|
||||
seq := make([]byte, len)
|
||||
if _, err = io.ReadFull(reader, seq); err != nil {
|
||||
return err
|
||||
}
|
||||
field.SetString(string(seq))
|
||||
} else if fc.Field == "Body" { // Hard-coding for `Body`
|
||||
blf := pkg.FieldByName("bodyLen")
|
||||
ptr := unsafe.Pointer(blf.UnsafeAddr())
|
||||
realPtr := (*uint64)(ptr)
|
||||
*realPtr = len
|
||||
}
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
val := IntParse(nh.Negative, nh.Unsigned, nbSeq)
|
||||
field.SetInt(val)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
val := UintParse(nh.Original, nbSeq)
|
||||
field.SetUint(val)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var baseFields = []*PackageField{
|
||||
{"Id", reflect.Uint32, DefaultPropertyGetter, DefaultPropertySetter},
|
||||
{"Path", reflect.String, DefaultPropertyGetter, DefaultPropertySetter},
|
||||
{"NumPath", reflect.Uint32, DefaultPropertyGetter, DefaultPropertySetter},
|
||||
{"Sid", reflect.String, DefaultPropertyGetter, DefaultPropertySetter},
|
||||
{"Code", reflect.Int32, DefaultPropertyGetter, DefaultPropertySetter},
|
||||
{"Message", reflect.String, DefaultPropertyGetter, DefaultPropertySetter},
|
||||
{"Body", reflect.Slice, DefaultPropertyGetter, DefaultPropertySetter},
|
||||
}
|
||||
|
||||
type Package struct {
|
||||
Id uint32
|
||||
@@ -77,53 +143,61 @@ type Package struct {
|
||||
}
|
||||
|
||||
func (p *Package) Serialize() []byte {
|
||||
// Init a seq buffer and set flag byte to 0
|
||||
buffer := make([]byte, 1, 512)
|
||||
textBuffer := make([][]byte, 0, 4)
|
||||
lenSeqInfoVec := make([]util.Tuple4[byte, int, int, uint], 0, 8)
|
||||
// Fill protocol headFlags and pack FlexNum head, cache text contents
|
||||
if length := len(p.Path); length > 0 {
|
||||
buffer[0] |= flagPath
|
||||
lenSeqInfoVec = append(lenSeqInfoVec, util.NewTuple4(Non0LenPackHead(uint16(length))))
|
||||
textBuffer = append(textBuffer, []byte(p.Path))
|
||||
return p.SerializeWith(nil, nil)
|
||||
}
|
||||
|
||||
func (p *Package) mergeFields(fields []*PackageField, pkg *reflect.Value) []*util.Tuple2[*PackageField, *reflect.Value] {
|
||||
pe := reflect.ValueOf(p).Elem()
|
||||
fullFields := make([]*util.Tuple2[*PackageField, *reflect.Value], 0, len(baseFields))
|
||||
for _, f := range baseFields {
|
||||
fullFields = append(fullFields, util.NewTuple2(f, &pe))
|
||||
}
|
||||
if length := len(p.Sid); length > 0 {
|
||||
buffer[0] |= flagSid
|
||||
lenSeqInfoVec = append(lenSeqInfoVec, util.NewTuple4(Non0LenPackHead(uint16(length))))
|
||||
textBuffer = append(textBuffer, []byte(p.Sid))
|
||||
if fields != nil && pkg != nil {
|
||||
for _, f := range fields {
|
||||
fullFields = append(fullFields, util.NewTuple2(f, pkg))
|
||||
}
|
||||
}
|
||||
if length := len(p.Message); length > 0 {
|
||||
buffer[0] |= flagMsg
|
||||
lenSeqInfoVec = append(lenSeqInfoVec, util.NewTuple4(Non0LenPackHead(uint16(length))))
|
||||
textBuffer = append(textBuffer, []byte(p.Message))
|
||||
}
|
||||
if length := len(p.Body); length > 0 {
|
||||
buffer[0] |= flagBody
|
||||
lenSeqInfoVec = append(lenSeqInfoVec, util.NewTuple4(Non0LenPackHead(uint(length))))
|
||||
textBuffer = append(textBuffer, p.Body)
|
||||
}
|
||||
if p.Id > 0 {
|
||||
buffer[0] |= flagId
|
||||
lenSeqInfoVec = append(lenSeqInfoVec, util.NewTuple4(Non0LenPackHead(p.Id)))
|
||||
}
|
||||
if p.Code != 0 {
|
||||
buffer[0] |= flagCode
|
||||
lenSeqInfoVec = append(lenSeqInfoVec, util.NewTuple4(IntPackHead(p.Code)))
|
||||
}
|
||||
if p.NumPath > 0 {
|
||||
buffer[0] |= flagNumPath
|
||||
lenSeqInfoVec = append(lenSeqInfoVec, util.NewTuple4(Non0LenPackHead(p.NumPath)))
|
||||
return fullFields
|
||||
}
|
||||
|
||||
func (p *Package) SerializeWith(fields []*PackageField, pkg *reflect.Value) []byte {
|
||||
fullFields := p.mergeFields(fields, pkg)
|
||||
flag := 0
|
||||
totalFields := len(fullFields)
|
||||
numHeadVec := make([]*NumHead, 0, totalFields + 1)
|
||||
textBuffer := make([][]byte, 0, totalFields - 4) // 容量为减掉至少4个非文本的字段数
|
||||
bodyBuffer := make([][]byte, 0, 1)
|
||||
for i, t2 := range fullFields {
|
||||
numHead, textSeq := t2.A.Get(t2.A, t2.B);
|
||||
if numHead == nil {
|
||||
continue
|
||||
}
|
||||
flag |= 1 << i
|
||||
numHeadVec = append(numHeadVec, numHead)
|
||||
if t2.A.Kind == reflect.Slice {
|
||||
bodyBuffer = append(bodyBuffer, textSeq)
|
||||
} else if len(textSeq) > 0 {
|
||||
textBuffer = append(textBuffer, textSeq)
|
||||
}
|
||||
}
|
||||
|
||||
flagSeq := UintSerialize(uint(flag))
|
||||
flagLen := len(flagSeq)
|
||||
buffer := make([]byte, flagLen, 512)
|
||||
copy(buffer, flagSeq)
|
||||
// Fill FlexNum heads and pack and fill FlexNum body
|
||||
buffer = buffer[:1+len(lenSeqInfoVec)]
|
||||
for i, lenSeqInfo := range lenSeqInfoVec {
|
||||
buffer[1+i] = lenSeqInfo.A
|
||||
buffer = append(buffer, NumPackBody(lenSeqInfo.D, lenSeqInfo.C, lenSeqInfo.B)...)
|
||||
buffer = buffer[:flagLen+len(numHeadVec)]
|
||||
for i, nh := range numHeadVec {
|
||||
buffer[flagLen+i] = nh.Head
|
||||
buffer = append(buffer, NumPackBody(nh)...)
|
||||
}
|
||||
// Append the text contents
|
||||
for _, part := range textBuffer {
|
||||
buffer = append(buffer, part...)
|
||||
}
|
||||
for _, part := range bodyBuffer {
|
||||
buffer = append(buffer, part...)
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
@@ -135,84 +209,6 @@ func (p *Package) parseBody() ([]byte, error) {
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func PackageDeserializeHead(reader *bufio.Reader) (*Package, error) {
|
||||
// Try read flag
|
||||
flag, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Count the FlexNum and init a numHead bytes container
|
||||
var numHeadSeq = make([]byte, bits.OnesCount8(flag))
|
||||
if _, err = reader.Read(numHeadSeq); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var numInfoSeq = make([]util.Tuple5[byte, uint8, bool, byte, []byte], len(numHeadSeq))
|
||||
// Parse the FlexNum head and total the FlexNum body len
|
||||
for i, numHead := range numHeadSeq {
|
||||
unsignedBits, bytesLen, negative, originalBits := NumParseHead(numHead, true)
|
||||
// Read FlexNum bodies
|
||||
var numBodySeq = make([]byte, bytesLen)
|
||||
if _, err = reader.Read(numBodySeq); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
numInfoSeq[i] = util.NewTuple5(unsignedBits, bytesLen, negative, originalBits, numBodySeq)
|
||||
}
|
||||
pkg := Package{reader: reader}
|
||||
// Finally number parse and read text seq
|
||||
if flag&flagPath > 0 {
|
||||
numInfo, _ := util.SliceDeleteGet(&numInfoSeq, 0, 1)
|
||||
seq := make([]byte, Non0LenParse(numInfo[0].D, numInfo[0].E))
|
||||
if _, err = io.ReadFull(reader, seq); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pkg.Path = string(seq)
|
||||
}
|
||||
if flag&flagSid > 0 {
|
||||
numInfo, _ := util.SliceDeleteGet(&numInfoSeq, 0, 1)
|
||||
seq := make([]byte, Non0LenParse(numInfo[0].D, numInfo[0].E))
|
||||
if _, err = io.ReadFull(reader, seq); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pkg.Sid = string(seq)
|
||||
}
|
||||
if flag&flagMsg > 0 {
|
||||
numInfo, _ := util.SliceDeleteGet(&numInfoSeq, 0, 1)
|
||||
seq := make([]byte, Non0LenParse(numInfo[0].D, numInfo[0].E))
|
||||
if _, err = io.ReadFull(reader, seq); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pkg.Message = string(seq)
|
||||
}
|
||||
if flag&flagBody > 0 {
|
||||
numInfo, _ := util.SliceDeleteGet(&numInfoSeq, 0, 1)
|
||||
pkg.bodyLen = Non0LenParse(numInfo[0].D, numInfo[0].E)
|
||||
}
|
||||
if flag&flagId > 0 {
|
||||
numInfo, _ := util.SliceDeleteGet(&numInfoSeq, 0, 1)
|
||||
pkg.Id = uint32(Non0LenParse(numInfo[0].D, numInfo[0].E))
|
||||
}
|
||||
if flag&flagCode > 0 {
|
||||
numInfo, _ := util.SliceDeleteGet(&numInfoSeq, 0, 1)
|
||||
pkg.Code = IntParse[int32](numInfo[0].C, numInfo[0].A, numInfo[0].E)
|
||||
}
|
||||
if flag&flagNumPath > 0 {
|
||||
numInfo, _ := util.SliceDeleteGet(&numInfoSeq, 0, 1)
|
||||
pkg.NumPath = uint32(Non0LenParse(numInfo[0].D, numInfo[0].E))
|
||||
}
|
||||
return &pkg, nil
|
||||
}
|
||||
|
||||
func PackageDeserialize(reader *bufio.Reader) (*Package, error) {
|
||||
sp, err := PackageDeserializeHead(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if b, e := sp.parseBody(); e != nil {
|
||||
return nil, e
|
||||
} else {
|
||||
sp.Body = b
|
||||
return sp, nil
|
||||
}
|
||||
}
|
||||
|
||||
var reqId atomic.Uint32
|
||||
|
||||
@@ -230,142 +226,73 @@ func NewNumPackage(numPath uint32, body []byte, sid string, id int, path string)
|
||||
return &Package{Id: uint32(id), Path: path, NumPath: numPath, Sid: sid, Body: body}
|
||||
}
|
||||
|
||||
func UintSerialize[U uint | uint64 | uint32 | uint16 | uint8](unsigned U) []byte {
|
||||
return numSerialize(UintPackHead(unsigned))
|
||||
func PackageDeserializeHead(reader *bufio.Reader) (*Package, error) {
|
||||
return PackageDeserializeHeadWith(reader, nil, nil)
|
||||
}
|
||||
|
||||
func IntSerialize[I int | int64 | int32 | int16 | int8](integer I) []byte {
|
||||
return numSerialize(IntPackHead(integer))
|
||||
func PackageDeserializeHeadWith(reader *bufio.Reader, fields []*PackageField, pkg *reflect.Value) (*Package, error) {
|
||||
// Try read flagHead
|
||||
head, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nh := NumParseHead(head, false)
|
||||
flag := uint64(nh.Original)
|
||||
if nh.BytesLen > 0 {
|
||||
var flagBodySeq = make([]byte, nh.BytesLen)
|
||||
if _, err = reader.Read(flagBodySeq); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
flagBodySeq = slices.Insert(flagBodySeq, 0, nh.Original)
|
||||
flag = NumParse(flagBodySeq)
|
||||
}
|
||||
|
||||
// Count the FlexNum and init a numHead bytes container
|
||||
headOnesCount := bits.OnesCount64(flag)
|
||||
numHeadList := make([]byte, headOnesCount)
|
||||
if _, err = reader.Read(numHeadList); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
numInfoList := make([]*util.Tuple3[int, *NumHead, []byte], headOnesCount)
|
||||
nhi := 0
|
||||
bitsLen := bits.Len64(flag)
|
||||
for i := range bitsLen {
|
||||
if 1 << i & flag == 0 {
|
||||
continue // Directly continue if current bit is zero
|
||||
}
|
||||
nh := NumParseHead(numHeadList[nhi], true)
|
||||
// Read FlexNum bodies
|
||||
var numBodySeq = make([]byte, nh.BytesLen)
|
||||
if _, err = reader.Read(numBodySeq); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
numInfoList[nhi] = util.NewTuple3(i, nh, numBodySeq)
|
||||
nhi ++
|
||||
}
|
||||
|
||||
p := &Package{reader: reader}
|
||||
pkgRef := reflect.ValueOf(p).Elem()
|
||||
fullFields := p.mergeFields(fields, &pkgRef)
|
||||
if bitsLen > len(fullFields) {
|
||||
return nil, util.Closed0(`Packet exception, flag overflow`)
|
||||
}
|
||||
for _, ni := range numInfoList {
|
||||
fc := fullFields[ni.A]
|
||||
fc.A.Set(fc.A, fc.B, ni.B, ni.C, reader)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Non0LenPackHead can package 128 into uint7 to represent the length of sha512 in hexadecimal
|
||||
func Non0LenPackHead[U uint | uint64 | uint32 | uint16 | uint8](unsigned U) (head byte, bytesLen int, bitsLen int, usize uint) {
|
||||
return UintPackHead(unsigned - 1)
|
||||
}
|
||||
|
||||
func UintPackHead[U uint | uint64 | uint32 | uint16 | uint8](unsigned U) (head byte, bytesLen int, bitsLen int, usize uint) {
|
||||
usize = uint(unsigned)
|
||||
bitsLen = bits.Len(usize)
|
||||
head, bytesLen = numPackHead(usize, bitsLen)
|
||||
return head, bytesLen, bitsLen, usize
|
||||
}
|
||||
|
||||
func IntPackHead[I int | int64 | int32 | int16 | int8](integer I) (byte, int, int, uint) {
|
||||
var unsigned uint
|
||||
if integer < 0 {
|
||||
// use -int to resolve the edge case, eg. int8(-128) to int8(128) is illegal, but to int(128) is legal.
|
||||
// but the int(math.MinInt) to -int(math.MinInt) may still illegal, but it is unlikely to be used.
|
||||
unsigned = uint(-int(integer))
|
||||
func PackageDeserialize(reader *bufio.Reader) (*Package, error) {
|
||||
sp, err := PackageDeserializeHead(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if b, e := sp.parseBody(); e != nil {
|
||||
return nil, e
|
||||
} else {
|
||||
unsigned = uint(integer)
|
||||
sp.Body = b
|
||||
return sp, nil
|
||||
}
|
||||
// add the width of the sign
|
||||
bitsLen := bits.Len(unsigned) + 1
|
||||
head, bytesLen := numPackHead(unsigned, bitsLen)
|
||||
if integer < 0 {
|
||||
var negative uint8 = 1
|
||||
if bytesLen < 7 {
|
||||
negative = 1 << (6 - bytesLen)
|
||||
}
|
||||
// handle the negative situation to mark the sign bit
|
||||
head |= negative
|
||||
}
|
||||
return head, bytesLen, bitsLen, unsigned
|
||||
}
|
||||
|
||||
func numPackHead[U uint | uint64 | uint32 | uint16 | uint8](u64 U, bitsLen int) (byte, int) {
|
||||
bytesLen := int(math.Floor(float64(bitsLen) / 8))
|
||||
headMaskShift := 8 - bytesLen
|
||||
var headBits uint8
|
||||
if bytesLen > 5 {
|
||||
// Directly alloc 8bytes if the requirement greater than 5
|
||||
bytesLen = 8
|
||||
headMaskShift = 2
|
||||
} else if bitsLen%8 > 7-bytesLen {
|
||||
// If the remains bits width than the headBits, then need to increase the bytes length,
|
||||
// and decrease the maskShift to match the body byte length.
|
||||
// When bytesLen updated, the head no longer needs to be stored any bits, just need to keep the default
|
||||
bytesLen++
|
||||
headMaskShift--
|
||||
} else {
|
||||
// Otherwise, right shift the bodyBytesLen to calc out the headBits
|
||||
headBits |= uint8(u64 >> (bytesLen * 8))
|
||||
}
|
||||
return 255<<headMaskShift&255 | headBits, bytesLen
|
||||
}
|
||||
|
||||
func numSerialize[U uint | uint64 | uint32 | uint16 | uint8](head uint8, bytesLen int, bitsLen int, u64 U) []byte {
|
||||
units := make([]byte, bytesLen+1)
|
||||
units[0] = head
|
||||
copy(units[1:], NumPackBody(u64, bitsLen, bytesLen))
|
||||
return units
|
||||
}
|
||||
|
||||
func NumPackBody[U uint | uint64 | uint32 | uint16 | uint8](u64 U, bitsLen int, bytesLen int) []byte {
|
||||
units := make([]byte, bytesLen)
|
||||
for i := 0; i < bytesLen && i*8 < bitsLen; i++ {
|
||||
units[bytesLen-i-1] = uint8(u64 >> (i * 8) & 255)
|
||||
}
|
||||
return units
|
||||
}
|
||||
|
||||
func UintDeserialize[U uint | uint64 | uint32 | uint16 | uint8](seq []byte) U {
|
||||
seq[0], _, _, _ = NumParseHead(seq[0], false)
|
||||
return U(NumParse(seq))
|
||||
}
|
||||
|
||||
func IntDeserialize[I int | int64 | int32 | int16 | int8](seq []byte) I {
|
||||
headBits, _, negative, _ := NumParseHead(seq[0], true)
|
||||
return IntParse[I](negative, headBits, seq[1:])
|
||||
}
|
||||
|
||||
func NumParseHead(head byte, sign bool) (unsignedBits byte, bytesLen uint8, negative bool, originalBits byte) {
|
||||
for i := 0; i < 8; i++ {
|
||||
if 128>>i&head == 0 {
|
||||
if bytesLen = uint8(i); bytesLen > 5 {
|
||||
bytesLen = 8
|
||||
originalBits = 1 & head
|
||||
} else {
|
||||
originalBits = 127 >> bytesLen & head
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
unsignedBits = originalBits
|
||||
if sign {
|
||||
if bytesLen == 8 {
|
||||
negative = 1&head == 1
|
||||
} else {
|
||||
signShift := 0
|
||||
if negative = 64>>bytesLen&head > 0; negative {
|
||||
signShift = 1
|
||||
}
|
||||
unsignedBits = 127 >> bytesLen >> signShift & head
|
||||
}
|
||||
}
|
||||
return unsignedBits, bytesLen, negative, originalBits
|
||||
}
|
||||
|
||||
func IntParse[I int | int64 | int32 | int16 | int8](negative bool, head uint8, seq []byte) I {
|
||||
u64 := NumParse(slices.Insert(seq, 0, head))
|
||||
if negative {
|
||||
return I(-int64(u64))
|
||||
}
|
||||
return I(u64)
|
||||
}
|
||||
|
||||
func Non0LenParse(head uint8, seq []byte) uint64 {
|
||||
return NumParse(slices.Insert(seq, 0, head)) + 1
|
||||
}
|
||||
|
||||
func NumParse(seq []byte) uint64 {
|
||||
var u64 uint64 = 0
|
||||
for i, b := range seq {
|
||||
if b > 0 {
|
||||
u64 |= uint64(b) << ((len(seq) - i - 1) * 8)
|
||||
}
|
||||
}
|
||||
return u64
|
||||
}
|
||||
|
||||
func StreamRead(conn net.Conn) ([]byte, error) {
|
||||
@@ -374,12 +301,12 @@ func StreamRead(conn net.Conn) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, bytesLen, _, headBits := NumParseHead(numHead, false)
|
||||
numBody := make([]byte, bytesLen)
|
||||
nh := NumParseHead(numHead, false)
|
||||
numBody := make([]byte, nh.BytesLen)
|
||||
if _, err = reader.Read(numBody); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data = make([]byte, Non0LenParse(headBits, numBody))
|
||||
var data = make([]byte, Non0LenParse(nh.Head, numBody))
|
||||
if _, err = io.ReadFull(reader, data); err != nil {
|
||||
return data, err
|
||||
}
|
||||
@@ -387,6 +314,6 @@ func StreamRead(conn net.Conn) ([]byte, error) {
|
||||
}
|
||||
|
||||
func StreamPack(bytes []byte) []byte {
|
||||
head, bytesLen, bitsLen, usize := Non0LenPackHead(uint(len(bytes)))
|
||||
return slices.Insert(bytes, 0, slices.Insert(NumPackBody(usize, bitsLen, bytesLen), 0, head)...)
|
||||
nh := Non0LenPackHead(uint(len(bytes)))
|
||||
return slices.Insert(bytes, 0, slices.Insert(NumPackBody(nh), 0, nh.Head)...)
|
||||
}
|
||||
|
||||
@@ -13,12 +13,12 @@ import (
|
||||
)
|
||||
|
||||
func TestPackage_Serialize(t *testing.T) {
|
||||
Body := []byte(strings.Repeat("Hello world!你好,世界!", rand.IntN(1000)))
|
||||
body := []byte(strings.Repeat("Hello world!你好,世界!", rand.IntN(1000)))
|
||||
hash := md5.New()
|
||||
hash.Write(Body)
|
||||
hash.Write(body)
|
||||
srcHash := hex.EncodeToString(hash.Sum(nil))
|
||||
t.Logf("Source content hash: %s", srcHash)
|
||||
pkg := NewPackage("home", Body, srcHash, -1)
|
||||
pkg := NewPackage("home", body, srcHash, -1)
|
||||
seq := pkg.Serialize()
|
||||
dePkg, _ := PackageDeserialize(bufio.NewReader(bytes.NewReader(seq)))
|
||||
hash2 := md5.New()
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
package flex
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/bits"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type NumHead struct {
|
||||
Unsigned byte // 整数除开符号位的头部
|
||||
BytesLen uint8 // 数字躯体部分字节长度
|
||||
Negative bool // 是否负数
|
||||
Original byte // 整数头部(若为有符号整数,将符号位也视为数字一部分)
|
||||
Head byte // 弹性数字序列头
|
||||
BitsLen uint8 // 数字比特长度
|
||||
Positive uint64 // 正整数
|
||||
}
|
||||
|
||||
func UintSerialize[U uint | uint64 | uint32 | uint16 | uint8](unsigned U) []byte {
|
||||
return numSerialize(UintPackHead(unsigned))
|
||||
}
|
||||
|
||||
func IntSerialize[I int | int64 | int32 | int16 | int8](integer I) []byte {
|
||||
return numSerialize(IntPackHead(integer))
|
||||
}
|
||||
|
||||
func numSerialize(nh *NumHead) []byte {
|
||||
units := make([]byte, nh.BytesLen+1)
|
||||
units[0] = nh.Head
|
||||
copy(units[1:], NumPackBody(nh))
|
||||
return units
|
||||
}
|
||||
|
||||
func NumPackBody(nh *NumHead) []byte {
|
||||
units := make([]byte, nh.BytesLen)
|
||||
for i := uint8(0); i < nh.BytesLen && i*8 < nh.BitsLen; i++ {
|
||||
units[nh.BytesLen-i-1] = uint8(int(nh.Positive) >> (i * 8) & 255)
|
||||
}
|
||||
return units
|
||||
}
|
||||
|
||||
// Non0LenPackHead can package 128 into uint7 to represent the length of sha512 in hexadecimal
|
||||
func Non0LenPackHead[U uint | uint64 | uint32 | uint16 | uint8](unsigned U) *NumHead {
|
||||
return UintPackHead(unsigned - 1)
|
||||
}
|
||||
|
||||
func UintPackHead[U uint | uint64 | uint32 | uint16 | uint8](unsigned U) *NumHead {
|
||||
nh := &NumHead{}
|
||||
nh.Positive = uint64(unsigned)
|
||||
nh.BitsLen = uint8(bits.Len64(nh.Positive))
|
||||
nh.Head, nh.BytesLen = numPackHead(nh.Positive, nh.BitsLen)
|
||||
return nh
|
||||
}
|
||||
|
||||
func IntPackHead[I int | int64 | int32 | int16 | int8](integer I) *NumHead {
|
||||
nh := &NumHead{}
|
||||
if integer < 0 {
|
||||
// use -int to resolve the edge case, eg. int8(-128) to int8(128) is illegal, but to int(128) is legal.
|
||||
// but the int(math.MinInt) to -int(math.MinInt) may still illegal, but it is unlikely to be used.
|
||||
nh.Positive = uint64(-int64(integer))
|
||||
} else {
|
||||
nh.Positive = uint64(integer)
|
||||
}
|
||||
// add the width of the sign
|
||||
nh.BitsLen = uint8(bits.Len64(nh.Positive)) + 1
|
||||
nh.Head, nh.BytesLen = numPackHead(nh.Positive, nh.BitsLen)
|
||||
if integer < 0 {
|
||||
var negative uint8 = 1
|
||||
if nh.BytesLen < 7 {
|
||||
negative = 1 << (6 - nh.BytesLen)
|
||||
}
|
||||
// handle the negative situation to mark the sign bit
|
||||
nh.Head |= negative
|
||||
}
|
||||
return nh
|
||||
}
|
||||
|
||||
func numPackHead(u64 uint64, bitsLen uint8) (byte, uint8) {
|
||||
bytesLen := uint8(math.Floor(float64(bitsLen) / 8))
|
||||
headMaskShift := 8 - bytesLen
|
||||
var headBits uint8
|
||||
if bytesLen > 5 {
|
||||
// Directly alloc 8bytes if the requirement greater than 5
|
||||
bytesLen = 8
|
||||
headMaskShift = 2
|
||||
} else if bitsLen%8 > 7-bytesLen {
|
||||
// If the remains bits width than the headBits, then need to increase the bytes length,
|
||||
// and decrease the maskShift to match the body byte length.
|
||||
// When bytesLen updated, the head no longer needs to be stored any bits, just need to keep the default
|
||||
bytesLen++
|
||||
headMaskShift--
|
||||
} else {
|
||||
// Otherwise, right shift the bodyBytesLen to calc out the headBits
|
||||
headBits |= uint8(u64 >> (bytesLen * 8))
|
||||
}
|
||||
return 255<<headMaskShift&255 | headBits, bytesLen
|
||||
}
|
||||
|
||||
func UintDeserialize[U uint | uint64 | uint32 | uint16 | uint8](seq []byte) U {
|
||||
nh := NumParseHead(seq[0], false)
|
||||
seq[0] = nh.Unsigned
|
||||
return U(NumParse(seq))
|
||||
}
|
||||
|
||||
func IntDeserialize[I int | int64 | int32 | int16 | int8](seq []byte) I {
|
||||
nh := NumParseHead(seq[0], true)
|
||||
return I(IntParse(nh.Negative, nh.Unsigned, seq[1:]))
|
||||
}
|
||||
|
||||
func NumParseHead(head byte, sign bool) *NumHead {
|
||||
nh := &NumHead{}
|
||||
for i := range 8 {
|
||||
if 128>>i&head == 0 {
|
||||
if nh.BytesLen = uint8(i); nh.BytesLen > 5 {
|
||||
nh.BytesLen = 8
|
||||
nh.Original = 1 & head
|
||||
} else {
|
||||
nh.Original = 127 >> nh.BytesLen & head
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
nh.Unsigned = nh.Original
|
||||
if sign {
|
||||
if nh.BytesLen == 8 {
|
||||
nh.Negative = 1&head == 1
|
||||
} else {
|
||||
signShift := 0
|
||||
if nh.Negative = 64>>nh.BytesLen&head > 0; nh.Negative {
|
||||
signShift = 1
|
||||
}
|
||||
nh.Unsigned = 127 >> nh.BytesLen >> signShift & head
|
||||
}
|
||||
}
|
||||
return nh
|
||||
}
|
||||
|
||||
func IntParse(negative bool, head uint8, seq []byte) int64 {
|
||||
i64 := int64(NumParse(slices.Insert(seq, 0, head)))
|
||||
if negative {
|
||||
i64 = -i64
|
||||
}
|
||||
return i64
|
||||
}
|
||||
|
||||
func UintParse(head uint8, seq []byte) uint64 {
|
||||
return NumParse(slices.Insert(seq, 0, head))
|
||||
}
|
||||
|
||||
func Non0LenParse(head uint8, seq []byte) uint64 {
|
||||
return UintParse(head, seq) + 1
|
||||
}
|
||||
|
||||
func NumParse(seq []byte) uint64 {
|
||||
var u64 uint64 = 0
|
||||
for i, b := range seq {
|
||||
if b > 0 {
|
||||
u64 |= uint64(b) << ((len(seq) - i - 1) * 8)
|
||||
}
|
||||
}
|
||||
return u64
|
||||
}
|
||||
+3
-3
@@ -48,10 +48,10 @@ func (w *ConnectorMappingManager[Rp, C]) ConnMapping() map[string]C {
|
||||
return cm
|
||||
}
|
||||
|
||||
func (w *ConnectorMappingManager[Rp, C]) ListBy(filter func(s string) bool) []util.Tuple2[string, C] {
|
||||
return util.MapSeq2From[string, C, util.Tuple2[string, C]](w.ConnMapping()).Filter2(func(s string, _ C) bool {
|
||||
func (w *ConnectorMappingManager[Rp, C]) ListBy(filter func(s string) bool) []*util.Tuple2[string, C] {
|
||||
return util.MapSeq2From[string, C, *util.Tuple2[string, C]](w.ConnMapping()).Filter2(func(s string, _ C) bool {
|
||||
return filter(s)
|
||||
}).Map2(func(addr string, conn C) util.Tuple2[string, C] {
|
||||
}).Map2(func(addr string, conn C) *util.Tuple2[string, C] {
|
||||
return util.NewTuple2(addr, conn)
|
||||
}).Collect()
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ type Context[Rp RoutableProtocol] struct {
|
||||
Rp Rp
|
||||
Api *RpApi[Rp]
|
||||
router *Router[Rp]
|
||||
suffix util.Tuple2[*Suffix, bool]
|
||||
suffix *util.Tuple2[*Suffix, bool]
|
||||
params map[string]Param
|
||||
}
|
||||
|
||||
|
||||
+101
-18
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"maps"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
@@ -34,8 +35,10 @@ type Router[Rp RoutableProtocol] struct {
|
||||
apisMapping map[string][]*RpApi[Rp]
|
||||
apisTree util.Tree[ApiBranch[Rp], string]
|
||||
apiMatcher func(rp Rp, apis []*Api) (index int)
|
||||
beforeController func(ctx *Context[Rp]) error
|
||||
afterController func(ctx *Context[Rp]) error
|
||||
beforeMapping map[string] func(ctx *Context[Rp]) error
|
||||
afterMapping map[string] func(ctx *Context[Rp]) error
|
||||
pathBeforeMapping map[string]string
|
||||
pathAfterMapping map[string]string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
@@ -45,6 +48,10 @@ func NewRouter[Rp RoutableProtocol]() *Router[Rp] {
|
||||
suffixBoundary: MarkSuffixBoundary,
|
||||
idApiMapping: make(map[string]*RpApi[Rp]),
|
||||
apisMapping: make(map[string][]*RpApi[Rp]),
|
||||
beforeMapping: make(map[string]func(ctx *Context[Rp]) error),
|
||||
afterMapping: make(map[string]func(ctx *Context[Rp]) error),
|
||||
pathBeforeMapping: make(map[string]string),
|
||||
pathAfterMapping: make(map[string]string),
|
||||
apisTree: util.NewTree(newApiBranch("", make([]*RpApi[Rp], 0))),
|
||||
}
|
||||
}
|
||||
@@ -55,12 +62,51 @@ func (r *Router[Rp]) SetSeparator(pps string, sb string) *Router[Rp] {
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Router[Rp]) SetEventHandler(before func(ctx *Context[Rp]) error, after func(ctx *Context[Rp]) error) *Router[Rp] {
|
||||
r.beforeController = before
|
||||
r.afterController = after
|
||||
// SetBefore sets the pre-controller middleware, which can be used for tasks such as authentication,
|
||||
// validation, etc. The path parameter works similarly to that in the Push method,
|
||||
// with the following differences: a trailing + indicates matching child APIs excluding the current one,
|
||||
// while a trailing * includes the current API as well.
|
||||
// Path omission is not supported—if the path was omitted in the Push configuration, it must be fully specified here.
|
||||
//
|
||||
// SetBefore("member", func(ctx *Context[Rp]) error {}) // Intercept "/member"
|
||||
// SetBefore("member*", func(ctx *Context[Rp]) error {}) // Intercept "/member" and its sub-APIs
|
||||
// SetBefore("member+", func(ctx *Context[Rp]) error {}) // Intercept sub-APIs of "/member"
|
||||
// SetBefore("member/{mid}+", func(ctx *Context[Rp]) error {}) // Intercept sub-APIs of "/member/{mid}"
|
||||
//
|
||||
// Returning an error can interrupt and clear the processing flow.
|
||||
func (r *Router[Rp]) SetBefore(path string, handler func(ctx *Context[Rp]) error) *Router[Rp] {
|
||||
hookOverrideWarn(path, r.beforeMapping, true)
|
||||
r.beforeMapping[path] = handler
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Router[Rp]) SetBeforePaths(paths []string, handler func(ctx *Context[Rp]) error) *Router[Rp] {
|
||||
for _, path := range paths { r.SetBefore(path, handler) }
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Router[Rp]) SetAfter(path string, handler func(ctx *Context[Rp]) error) *Router[Rp] {
|
||||
hookOverrideWarn(path, r.beforeMapping, false)
|
||||
r.afterMapping[path] = handler
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Router[Rp]) SetAfterPaths(paths []string, handler func(ctx *Context[Rp]) error) *Router[Rp] {
|
||||
for _, path := range paths { r.SetAfter(path, handler) }
|
||||
return r
|
||||
}
|
||||
|
||||
func hookOverrideWarn[T any](path string, mapping map[string]T, pre bool) {
|
||||
if _, ok := mapping[path]; !ok {
|
||||
return
|
||||
}
|
||||
hook := "preprocessor"
|
||||
if !pre {
|
||||
hook = "postprocesser"
|
||||
}
|
||||
slog.Warn(fmt.Sprintf(`Path "%s" already has a %s; reassigning it will overwrite the current one.`, path, hook))
|
||||
}
|
||||
|
||||
func (r *Router[Rp]) SetApiMatcher(apiMatcher func(rp Rp, apis []*Api) (index int)) *Router[Rp] {
|
||||
r.apiMatcher = apiMatcher
|
||||
return r
|
||||
@@ -145,12 +191,8 @@ func (r *Router[Rp]) ready() {
|
||||
return rp.MatchApi(apis)
|
||||
}
|
||||
}
|
||||
if r.beforeController == nil {
|
||||
r.beforeController = func(ctx *Context[Rp]) error { return nil }
|
||||
}
|
||||
if r.afterController == nil {
|
||||
r.afterController = func(ctx *Context[Rp]) error { return nil }
|
||||
}
|
||||
r.mapMiddleware(r.beforeMapping, r.pathBeforeMapping, true)
|
||||
r.mapMiddleware(r.afterMapping, r.pathAfterMapping, true)
|
||||
}
|
||||
|
||||
func (r *Router[Rp]) omittedPath(path string) string {
|
||||
@@ -191,19 +233,19 @@ func (r *Router[Rp]) buildTree() {
|
||||
}).Collect()
|
||||
// 2. init the apisTree
|
||||
r.apisTree.Build(apiBranches, func(tree *util.Tree[ApiBranch[Rp], string], remains []ApiBranch[Rp]) {
|
||||
var fills []util.Tuple2[string, ApiBranch[Rp]]
|
||||
var fills []*util.Tuple2[string, ApiBranch[Rp]]
|
||||
for _, remain := range remains {
|
||||
paths := strings.Split(remain.Path, MarkPathPartSeparator)
|
||||
for i := 0; i < len(paths)-1; i++ {
|
||||
path := strings.Join(paths[:i+1], MarkPathPartSeparator)
|
||||
if _, ok := tree.ChildByPath(paths[:i+1]); !ok && !util.MapSeqFrom[util.Tuple2[string, ApiBranch[Rp]], string](fills).Map(func(f util.Tuple2[string, ApiBranch[Rp]]) string {
|
||||
if _, ok := tree.ChildByPath(paths[:i+1]); !ok && !util.MapSeqFrom[*util.Tuple2[string, ApiBranch[Rp]], string](fills).Map(func(f *util.Tuple2[string, ApiBranch[Rp]]) string {
|
||||
return f.A
|
||||
}).Contains(path, util.Equal) {
|
||||
fills = append(fills, util.NewTuple2(path, newApiBranch(path, []*RpApi[Rp]{})))
|
||||
}
|
||||
}
|
||||
// If the API already exists in `fills` and `.Apis` is empty, then need to replace with the valid API.
|
||||
if index := slices.IndexFunc(fills, func(tuple util.Tuple2[string, ApiBranch[Rp]]) bool {
|
||||
if index := slices.IndexFunc(fills, func(tuple *util.Tuple2[string, ApiBranch[Rp]]) bool {
|
||||
return tuple.A == remain.Path && len(tuple.B.Apis) == 0
|
||||
}); index > -1 {
|
||||
fills[index] = util.NewTuple2(remain.Path, remain)
|
||||
@@ -242,6 +284,37 @@ func (r *Router[Rp]) buildTree() {
|
||||
})
|
||||
}
|
||||
|
||||
var middlewarePathSuffixes = []string{"+", "*"}
|
||||
|
||||
func (r *Router[Rp]) mapMiddleware(handlerMapping map[string]func(ctx *Context[Rp]) error, pathMapping map[string]string, pre bool) {
|
||||
apisPaths := slices.Collect(maps.Keys(r.apisMapping))
|
||||
for k := range handlerMapping {
|
||||
path := k
|
||||
suffix := ""
|
||||
matchChildren := len(k) > 0 && slices.Contains(middlewarePathSuffixes, k[len(k) - 1:])
|
||||
if matchChildren {
|
||||
suffix = k[len(k) - 1:]
|
||||
path = k[:len(k)-1]
|
||||
}
|
||||
for i := len(apisPaths) - 1; i >= 0; i -- {
|
||||
apiPath := apisPaths[i]
|
||||
matched := false
|
||||
if suffix != middlewarePathSuffixes[0] && path == apiPath {
|
||||
matched = true
|
||||
} else if matchChildren {
|
||||
// If child matching is enabled and the path is empty, the middleware is treated as a global wildcard;
|
||||
// otherwise, it applies to sub-paths only.
|
||||
matched = path == "" || strings.Contains(apiPath, path + MarkPathPartSeparator)
|
||||
}
|
||||
if matched {
|
||||
hookOverrideWarn(apiPath, pathMapping, pre)
|
||||
pathMapping[apiPath] = k
|
||||
apisPaths = slices.Delete(apisPaths, i, i + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router[Rp]) locate(path string, apiFinder func([]*RpApi[Rp]) (*RpApi[Rp], bool)) (*RpApi[Rp], map[string]Param, *Suffix, error) {
|
||||
var api *RpApi[Rp]
|
||||
var suffix *Suffix
|
||||
@@ -281,7 +354,7 @@ func (r *Router[Rp]) locate(path string, apiFinder func([]*RpApi[Rp]) (*RpApi[Rp
|
||||
|
||||
func (r *Router[Rp]) matchVarPath(path string) (string, map[string]Param, *Suffix, bool) {
|
||||
pathParts := strings.Split(path, r.pathPartSeparator)
|
||||
loopItems := []util.Tuple2[*util.Tree[ApiBranch[Rp], string], int]{util.NewTuple2(&r.apisTree, 0)}
|
||||
loopItems := []*util.Tuple2[*util.Tree[ApiBranch[Rp], string], int]{util.NewTuple2(&r.apisTree, 0)}
|
||||
pathParams := map[string]Param{}
|
||||
var targetApiBranch *util.Tree[ApiBranch[Rp], string]
|
||||
var suffix *Suffix
|
||||
@@ -421,11 +494,21 @@ func (r *Router[Rp]) Route(context *Context[Rp]) {
|
||||
|
||||
func (r *Router[Rp]) routedHandle(api *RpApi[Rp], pathParams map[string]Param, suffix *Suffix, context *Context[Rp]) error {
|
||||
context.SetRoutes(r, api, pathParams, suffix)
|
||||
if err := r.beforeController(context); err != nil {
|
||||
return err
|
||||
if bk, ok := r.pathBeforeMapping[api.Path]; ok {
|
||||
if before, ok := r.beforeMapping[bk]; ok {
|
||||
if err := before(context); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
api.Controller(context)
|
||||
return r.afterController(context)
|
||||
var err error = nil
|
||||
if ak, ok := r.pathAfterMapping[api.Path]; ok {
|
||||
if after, ok := r.afterMapping[ak]; ok {
|
||||
err = after(context)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Router[Rp]) idLocate(id string) (*RpApi[Rp], error) {
|
||||
|
||||
+12
-12
@@ -5,12 +5,12 @@ type Tuple2[A, B any] struct {
|
||||
B B
|
||||
}
|
||||
|
||||
func (t Tuple2[A, B]) Values() (A, B) {
|
||||
func (t *Tuple2[A, B]) Values() (A, B) {
|
||||
return t.A, t.B
|
||||
}
|
||||
|
||||
func NewTuple2[A, B any](a A, b B) Tuple2[A, B] {
|
||||
return Tuple2[A, B]{a, b}
|
||||
func NewTuple2[A, B any](a A, b B) *Tuple2[A, B] {
|
||||
return &Tuple2[A, B]{a, b}
|
||||
}
|
||||
|
||||
type Tuple3[A, B, C any] struct {
|
||||
@@ -19,12 +19,12 @@ type Tuple3[A, B, C any] struct {
|
||||
C C
|
||||
}
|
||||
|
||||
func (t Tuple3[A, B, C]) Values() (A, B, C) {
|
||||
func (t *Tuple3[A, B, C]) Values() (A, B, C) {
|
||||
return t.A, t.B, t.C
|
||||
}
|
||||
|
||||
func NewTuple3[A, B, C any](a A, b B, c C) Tuple3[A, B, C] {
|
||||
return Tuple3[A, B, C]{a, b, c}
|
||||
func NewTuple3[A, B, C any](a A, b B, c C) *Tuple3[A, B, C] {
|
||||
return &Tuple3[A, B, C]{a, b, c}
|
||||
}
|
||||
|
||||
type Tuple4[A, B, C, D any] struct {
|
||||
@@ -34,12 +34,12 @@ type Tuple4[A, B, C, D any] struct {
|
||||
D D
|
||||
}
|
||||
|
||||
func (t Tuple4[A, B, C, D]) Values() (A, B, C, D) {
|
||||
func (t *Tuple4[A, B, C, D]) Values() (A, B, C, D) {
|
||||
return t.A, t.B, t.C, t.D
|
||||
}
|
||||
|
||||
func NewTuple4[A, B, C, D any](a A, b B, c C, d D) Tuple4[A, B, C, D] {
|
||||
return Tuple4[A, B, C, D]{a, b, c, d}
|
||||
func NewTuple4[A, B, C, D any](a A, b B, c C, d D) *Tuple4[A, B, C, D] {
|
||||
return &Tuple4[A, B, C, D]{a, b, c, d}
|
||||
}
|
||||
|
||||
type Tuple5[A, B, C, D, E any] struct {
|
||||
@@ -50,10 +50,10 @@ type Tuple5[A, B, C, D, E any] struct {
|
||||
E E
|
||||
}
|
||||
|
||||
func (t Tuple5[A, B, C, D, E]) Values() (A, B, C, D, E) {
|
||||
func (t *Tuple5[A, B, C, D, E]) Values() (A, B, C, D, E) {
|
||||
return t.A, t.B, t.C, t.D, t.E
|
||||
}
|
||||
|
||||
func NewTuple5[A, B, C, D, E any](a A, b B, c C, d D, e E) Tuple5[A, B, C, D, E] {
|
||||
return Tuple5[A, B, C, D, E]{a, b, c, d, e}
|
||||
func NewTuple5[A, B, C, D, E any](a A, b B, c C, d D, e E) *Tuple5[A, B, C, D, E] {
|
||||
return &Tuple5[A, B, C, D, E]{a, b, c, d, e}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user