diff --git a/example/simple-chat/chat.go b/example/simple-chat/chat.go index 12ec715..bf1a639 100644 --- a/example/simple-chat/chat.go +++ b/example/simple-chat/chat.go @@ -21,7 +21,7 @@ const ( ProtoQuitRoom ProtoSendMessage - ProtoRecvMessage = iota + 1000 + ProtoRecvMessage = iota - ProtoSendMessage + 1000 ) type Proto struct { @@ -123,9 +123,15 @@ func (s *service) sendMessage(req *proto.Request[Proto]) { func main() { - engine := proto.New[Proto, ProtoType](func() proto.Proto[Proto, ProtoType] { + instancePool := proto.NewInstancePool[Proto, ProtoType](func() proto.Proto[Proto, ProtoType] { return new(Proto) }) + + engine := proto.New[Proto, ProtoType](instancePool.Alloc) + engine.RegisterDestroyProto(func(_ *qWebsocket.HandlerParams, proto proto.Proto[Proto, ProtoType]) { + instancePool.Free(proto) + }) + s := &service{} engine.Register(ProtoJoinRoom, s.joinRoom) diff --git a/proto/codec_xml.go b/proto/codec_xml.go new file mode 100644 index 0000000..95506a4 --- /dev/null +++ b/proto/codec_xml.go @@ -0,0 +1,16 @@ +package proto + +import ( + "encoding/xml" + "io" +) + +type CodecXML[T any, K comparable] struct{} + +func (k CodecXML[T, K]) Marshal(w io.Writer, val Proto[T, K]) error { + return xml.NewEncoder(w).Encode(val.Value()) +} + +func (k CodecXML[T, K]) Unmarshal(r io.Reader, ptr Proto[T, K]) error { + return xml.NewDecoder(r).Decode(ptr.Self()) +} diff --git a/proto/engine.go b/proto/engine.go index cb9fb84..6335f10 100644 --- a/proto/engine.go +++ b/proto/engine.go @@ -6,12 +6,17 @@ import ( "log" ) -type HandlerFunc[T any] func(*Request[T]) +type ( + HandlerFunc[T any] func(*Request[T]) + NewProtoFunc[T any, K comparable] func() Proto[T, K] + DestroyProtoFunc[T any, K comparable] func(params *qWebsocket.HandlerParams, proto Proto[T, K]) +) type Engine[T any, K comparable] struct { - codec Codec[T, K] - handlers map[K]HandlerFunc[T] - newProto func() Proto[T, K] + codec Codec[T, K] + handlers map[K]HandlerFunc[T] + newProto NewProtoFunc[T, K] + destroyProto DestroyProtoFunc[T, K] } // Handler @@ -20,6 +25,10 @@ type Engine[T any, K comparable] struct { func (e *Engine[T, K]) Handler(params *qWebsocket.HandlerParams) { proto := e.newProto() + if e.destroyProto != nil { + defer e.destroyProto(params, proto) + } + if err := e.codec.Unmarshal(bytes.NewReader(params.Request), proto); err != nil { log.Println("Proto engine codec error:", err) return @@ -46,3 +55,7 @@ func (e *Engine[T, K]) Register(key K, handler HandlerFunc[T]) { func (e *Engine[T, K]) RegisterCodec(codec Codec[T, K]) { e.codec = codec } + +func (e *Engine[T, K]) RegisterDestroyProto(handler DestroyProtoFunc[T, K]) { + e.destroyProto = handler +} diff --git a/proto/instance.go b/proto/instance.go new file mode 100644 index 0000000..0cfd674 --- /dev/null +++ b/proto/instance.go @@ -0,0 +1,27 @@ +package proto + +import "sync" + +type InstancePool[T any, K comparable] struct { + pool sync.Pool +} + +func (k *InstancePool[T, K]) Alloc() Proto[T, K] { + return k.pool.Get().(Proto[T, K]) +} + +func (k *InstancePool[T, K]) Free(proto Proto[T, K]) { + // if Proto impl Resetter interface + if free, ok := proto.(Resetter); ok { + free.Reset() + } + k.pool.Put(proto) +} + +func NewInstancePool[T any, K comparable](newProto func() Proto[T, K]) *InstancePool[T, K] { + return &InstancePool[T, K]{ + pool: sync.Pool{ + New: func() any { return newProto() }, + }, + } +} diff --git a/proto/proto.go b/proto/proto.go index d850e56..6f88ece 100644 --- a/proto/proto.go +++ b/proto/proto.go @@ -1,12 +1,38 @@ package proto type Proto[T any, K comparable] interface { + // Key realizes the value that returns Proto can be comparable + // + // case: + // type Proto struct { + // Type uint32 + // Message string + // } + // + // func (p *Proto) Key() uint32 { return p.Type } Key() K + // Value return a copy of itself + // + // case: + // func (p *Proto) Value() Proto { return *p } Value() T + // Self return a pointer of itself + // + // case: + // func (p *Proto) Self() *Proto { return p } Self() *T } -func New[T any, K comparable](newProto func() Proto[T, K]) *Engine[T, K] { +type Resetter interface { + Reset() +} + +// New a proto engine instance +// +// args +// +// - newProto should be return a Proto instance +func New[T any, K comparable](newProto NewProtoFunc[T, K]) *Engine[T, K] { return &Engine[T, K]{ codec: &CodecJSON[T, K]{}, handlers: make(map[K]HandlerFunc[T]),