mirror of
https://github.com/elyby/chrly.git
synced 2024-12-24 22:19:51 +05:30
137 lines
3.5 KiB
Go
137 lines
3.5 KiB
Go
|
package db
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"compress/zlib"
|
||
|
"io"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/valyala/fastjson"
|
||
|
)
|
||
|
|
||
|
type ProfileSerializer interface {
|
||
|
Serialize(profile *Profile) ([]byte, error)
|
||
|
Deserialize(value []byte) (*Profile, error)
|
||
|
}
|
||
|
|
||
|
func NewJsonSerializer() *JsonSerializer {
|
||
|
return &JsonSerializer{
|
||
|
parserPool: &fastjson.ParserPool{},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type JsonSerializer struct {
|
||
|
parserPool *fastjson.ParserPool
|
||
|
}
|
||
|
|
||
|
// Reasons for manual JSON serialization:
|
||
|
// 1. The Profile must be pure and must not contain tags.
|
||
|
// 2. Without tags it's impossible to apply omitempty during serialization.
|
||
|
// 3. Without omitempty we significantly inflate the storage size, which is critical for large deployments.
|
||
|
// Since the JSON structure in this case is very simple, it's very easy to write a manual serialization,
|
||
|
// achieving all constraints above.
|
||
|
func (s *JsonSerializer) Serialize(profile *Profile) ([]byte, error) {
|
||
|
var builder strings.Builder
|
||
|
// Prepare for the worst case (e.g. long username, long textures links, long Mojang textures and signature)
|
||
|
// to prevent additional memory allocations during serialization
|
||
|
builder.Grow(1536)
|
||
|
builder.WriteString(`{"uuid":"`)
|
||
|
builder.WriteString(profile.Uuid)
|
||
|
builder.WriteString(`","username":"`)
|
||
|
builder.WriteString(profile.Username)
|
||
|
builder.WriteString(`"`)
|
||
|
if profile.SkinUrl != "" {
|
||
|
builder.WriteString(`,"skinUrl":"`)
|
||
|
builder.WriteString(profile.SkinUrl)
|
||
|
builder.WriteString(`"`)
|
||
|
if profile.SkinModel != "" {
|
||
|
builder.WriteString(`,"skinModel":"`)
|
||
|
builder.WriteString(profile.SkinModel)
|
||
|
builder.WriteString(`"`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if profile.CapeUrl != "" {
|
||
|
builder.WriteString(`,"capeUrl":"`)
|
||
|
builder.WriteString(profile.CapeUrl)
|
||
|
builder.WriteString(`"`)
|
||
|
}
|
||
|
|
||
|
if profile.MojangTextures != "" {
|
||
|
builder.WriteString(`,"mojangTextures":"`)
|
||
|
builder.WriteString(profile.MojangTextures)
|
||
|
builder.WriteString(`","mojangSignature":"`)
|
||
|
builder.WriteString(profile.MojangSignature)
|
||
|
builder.WriteString(`"`)
|
||
|
}
|
||
|
|
||
|
builder.WriteString("}")
|
||
|
|
||
|
return []byte(builder.String()), nil
|
||
|
}
|
||
|
|
||
|
func (s *JsonSerializer) Deserialize(value []byte) (*Profile, error) {
|
||
|
parser := s.parserPool.Get()
|
||
|
defer s.parserPool.Put(parser)
|
||
|
v, err := parser.ParseBytes(value)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
profile := &Profile{
|
||
|
Uuid: string(v.GetStringBytes("uuid")),
|
||
|
Username: string(v.GetStringBytes("username")),
|
||
|
SkinUrl: string(v.GetStringBytes("skinUrl")),
|
||
|
SkinModel: string(v.GetStringBytes("skinModel")),
|
||
|
CapeUrl: string(v.GetStringBytes("capeUrl")),
|
||
|
MojangTextures: string(v.GetStringBytes("mojangTextures")),
|
||
|
MojangSignature: string(v.GetStringBytes("mojangSignature")),
|
||
|
}
|
||
|
|
||
|
return profile, nil
|
||
|
}
|
||
|
|
||
|
func NewZlibEncoder(serializer ProfileSerializer) *ZlibEncoder {
|
||
|
return &ZlibEncoder{serializer}
|
||
|
}
|
||
|
|
||
|
type ZlibEncoder struct {
|
||
|
serializer ProfileSerializer
|
||
|
}
|
||
|
|
||
|
func (s *ZlibEncoder) Serialize(profile *Profile) ([]byte, error) {
|
||
|
serialized, err := s.serializer.Serialize(profile)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
var buff bytes.Buffer
|
||
|
writer := zlib.NewWriter(&buff)
|
||
|
_, err = writer.Write(serialized)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
_ = writer.Close()
|
||
|
|
||
|
return buff.Bytes(), nil
|
||
|
}
|
||
|
|
||
|
func (s *ZlibEncoder) Deserialize(value []byte) (*Profile, error) {
|
||
|
buff := bytes.NewReader(value)
|
||
|
reader, err := zlib.NewReader(buff)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
resultBuffer := new(bytes.Buffer)
|
||
|
_, err = io.Copy(resultBuffer, reader)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
_ = reader.Close()
|
||
|
|
||
|
return s.serializer.Deserialize(resultBuffer.Bytes())
|
||
|
}
|