2 Star 1 Fork 0

张明理 / sfw

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
incr-packer.go 5.98 KB
一键复制 编辑 原始数据 按行查看 历史
张明理 提交于 2022-06-29 10:36 . 修复: incrpatch 包
package incrpatch
import (
"fmt"
"reflect"
"strings"
"gitee.com/terender/sfw/rlog"
)
/*
文档型数据结构的读取、修改和打包工具
对传入数据实体有如下要求:
1. 必须传入一个 Addressable 的变量作为数据实体。
1. 叶子节点字段只能是 {基本类型}(数字、string、bool),数字可以是浮点数和整数,
对应变量为 [u]intx -> x in [8,16,32,64], byte, float32, float64, string, bool
2. 非叶子节点字段只能是 {复合类型}(k-v、list),
对应变量类型为 struct、*struct、map、slice、array。
3. map 类型字段,其 map-key 必须是 string 类型,其 map-value 可以是上述 {基本类型} 和 {复合类型},
如果 map-value 需要 {复合类型},建议使用 struct 和 *struct,不推荐用 map、slice、array
4. 不能存在 *{基本类型}、*map、*slice、*array 类型的成员变量。
数据实体生成绑定的 Packer 后,可以通过 {字段全名} (有时简称 key) 来读取和修改字段的值,字段名转换默认规则为
1. 成员变量名转为小写
2. 数组元素用其索引数字
3. map-key 转为小写
多个层级用 '.' 连接
一旦对一个数据实体生成了绑定的 Packer,则有如下要求:
1. 对此数据实体内所有字段的改动操作必须通过 Packer 的 Set 方法来进行,不能直接修改数据实体的成员变量,否则会导 Packer 致无法记录数据改动。
2. 对此数据实体内字段的读取操作,可以通过 Packer 的 Get 方法,也可以通过直接读取数据实体的成员变量的方法。
3. Packer 的 Get Set Pack 三个方法是非原子性非线程安全的,因此这三个方法以及对数据实体的成员变量的直接读取操作,
需要由调用者自行维护其原子性(加锁或者只在一个线程中操作)。
*/
// Serializer 序列化器方法定义,用于将提取出来的数据字段序列化,传入参数分别为
// up: 发生更新的字段数据
// del: 删除的字段
type Serializer func(up map[string]interface{}, del map[string]bool) (interface{}, error)
// Packer 脏数据打包器,与一个数据实体是绑定关系
type Packer struct {
data interface{} // 传入的数据实体
refTable *ReflectTable // 传入数据实体的固定字段全反射表
records *IncrRecords // 传入数据结构的增量更新记录
packSequenceCounter int // 打包序列号计数器
serializer Serializer // 序列化器
}
// NewPacker Packer 构造函数,传入数据实体,生成与其绑定的打包器
func NewPacker(data interface{}, f Serializer) (*Packer, error) {
root := reflect.ValueOf(data)
if root.Kind() == reflect.Interface {
root = root.Elem()
}
for root.Kind() == reflect.Ptr {
root = root.Elem()
}
if !root.IsValid() {
return nil, fmt.Errorf(`传入的数据实体是无效值`)
}
if !root.CanAddr() {
return nil, fmt.Errorf(`不能对 Unaddressable 的值生成打包器,因为它不能通过反射来进行赋值操作`)
}
packer := &Packer{
data: data,
refTable: NewReflectTable(root),
records: NewIncrRecords(),
serializer: f,
}
return packer, nil
}
// Records 返回打包器中的改动记录
func (p *Packer) Records() *IncrRecords { return p.records }
// Get 通过全反射结构表查询指定 key 对应的值
func (p *Packer) Get(key string) (interface{}, error) {
key = strings.TrimPrefix(key, `.`)
key = strings.TrimSuffix(key, `.`)
key = strings.ToLower(key)
// 如果在固定字段反射表中找到,表示此字段为固定字段
v, ok := p.refTable.GetValue(key)
if ok {
return v.Interface(), nil
}
// 否则寻找其父节点的 key、在父节点中的 subkey,父节点的反射值
// 在父节点上用反射方法通过 subkey 来获取字段的值
parentValue, subkey := p.refTable.FindParentOf(key)
v, ok = FindChildValueReflect(parentValue, subkey)
if !ok {
return nil, fmt.Errorf("key[%s] don't find in reflectStruct ", key)
}
return v.Interface(), nil
}
// Set 通过全反射结构表设置指定 key 的值
func (p *Packer) Set(key string, value interface{}) error {
// 格式化 key
key = strings.TrimPrefix(key, `.`)
key = strings.TrimSuffix(key, `.`)
key = strings.ToLower(key)
// 如果在固定字段反射表中找到,表示此字段为固定字段
v, ok := p.refTable.GetValue(key)
if ok {
err := SetValueReflect(v, value)
if err != nil {
return err
}
} else {
// 否则寻找其父节点的 key、在父节点中的 subkey,父节点的反射值
// 在父节点上用反射方法通过 subkey 来对字段赋值
parentValue, subkey := p.refTable.FindParentOf(key)
trimKey, err := SetChildValueReflect(parentValue, subkey, value)
if err != nil {
return err
}
if len(trimKey) > 0 {
key = strings.TrimRight(key, strings.Join(trimKey, `.`))
key = strings.TrimRight(key, `.`)
}
}
if value != nil {
p.records.Update(key)
} else {
p.records.Delete(key)
}
return nil
}
// Pack 根据脏标记列表打增量补丁包并放入等待队列
// 为避免产生数据竞争,打包同时就用外部传入的序列化方法立刻将数据序列化
func (p *Packer) Pack() (interface{}, error) {
if p.serializer == nil {
return nil, fmt.Errorf(`打包器中没有传入序列化方法`)
}
if !p.records.Dirty() {
return nil, nil
}
updates := map[string]interface{}{}
deletes := map[string]bool{}
for i, ver := range p.records.records {
if ver == 0 {
rlog.Errorf(`字段[%v]在改动记录列表中存在, 但是版本号为0`, i)
continue
}
if ver < 0 {
deletes[i] = true
continue
}
v, err := p.Get(i)
if err != nil {
rlog.Errorf(`打包增量更新包时 字段[%v] 在更新标记列表中存在,但是找不到对应数据`, i)
//patch.Deletes[i] = nil
continue
}
updates[i] = v
}
serialized, err := p.serializer(updates, deletes)
if err != nil {
return nil, err
}
p.packSequenceCounter++
p.records.Reset()
return serialized, nil
}
Go
1
https://gitee.com/terender/sfw.git
git@gitee.com:terender/sfw.git
terender
sfw
sfw
v1.0.11

搜索帮助