-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
registry.go
154 lines (123 loc) · 3.97 KB
/
registry.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright (c) 2022-2024 Xelaj Software
//
// This file is a part of tl package.
// See https://github.com/xelaj/tl/blob/master/LICENSE_README.md for details.
package tl
import (
"cmp"
"fmt"
"reflect"
"github.com/quenbyako/ext/slices"
)
// key is crc code, value is name of constructor.
type (
enumNames = map[crc32]string
typeName = string
)
type Registry interface {
ConstructObject(code crc32) (Object, bool)
}
var _defaultRegistry = val(NewRegistry())
func DefaultRegistry() *ObjectRegistry { return &_defaultRegistry }
func RegisterObjectDefault[T Object]() { RegisterObject[T](&_defaultRegistry) }
func RegisterEnumDefault[T Object](enums ...T) { RegisterEnum[T](&_defaultRegistry, enums...) }
func RegisterCustomDefault[T Object](constructor func(uint32) T, crcs ...uint32) {
RegisterCustom[T](&_defaultRegistry, constructor, crcs...)
}
// ObjectRegistry is a type, which handles code generated schema, and could be
// useful for spawning TL objects. Unlike RawSchemaRegistry, it can work only
// with predefined go types.
//
// If you are not able to use codegen, use RawSchemaRegistry, it could be
// slower, but more flexible.
type ObjectRegistry struct {
// in objects it's allowed to store ONLY structs and uint32.
objects map[crc32]func(uint32) Object
}
var _ Registry = (*ObjectRegistry)(nil)
func NewRegistry() *ObjectRegistry {
return &ObjectRegistry{
objects: make(map[crc32]func(uint32) Object),
}
}
// ConstructObject spawns new object by crc code from registry.
func (r *ObjectRegistry) ConstructObject(crc crc32) (Object, bool) {
if obj, ok := r.objects[crc]; ok {
return obj(crc), true
}
return nil, false
}
type field struct {
fType fieldType
name string
flagTrigger uint8 // index of field in list of fields in orphanType
bitTrigger uint8
noEncode bool // don't encode value, if value is true. works ONLY for boolean types
optional bool
}
type structFields struct {
// key is an index of optional field in list of fields in object
// value is bit, which you need to trigger
bitflags map[int]BitflagBit
tags []StructTag
}
func (s *structFields) isFieldOptional(fieldIndex int) bool {
_, ok := s.bitflags[fieldIndex]
return ok
}
type BitflagBit struct {
FieldIndex int // в какое поле пихать бит что поле существует
BitIndex int // собственно в какой бит пихать флаг что все ок
}
func RegisterObject[T Object](r *ObjectRegistry) {
r.registerObject(asObject(new[T]))
}
func RegisterEnum[T Object](r *ObjectRegistry, enums ...T) {
if t := new[T](); reflect.TypeOf(t).Kind() != reflect.Uint32 {
panic("enums must be uint32")
}
if len(enums) == 0 {
panic("no enums provided")
}
enums = slices.SortFunc(enums, func(a, b T) int { return cmp.Compare(a.CRC(), b.CRC()) })
f := func(crc uint32) (res T, ok bool) {
if i, ok := slices.BinarySearchFunc(enums, crc, func(enum T, crc uint32) int {
return cmp.Compare(enum.CRC(), crc)
}); ok {
return enums[i], true
}
return res, false
}
r.registerCustom(slices.Remap(enums, func(enum T) uint32 { return enum.CRC() }), asEnum(f))
}
func RegisterCustom[T Object](r *ObjectRegistry, constructor func(uint32) T, crcs ...uint32) {
if len(crcs) == 0 {
panic("no enums provided")
}
r.registerCustom(crcs, func(u uint32) Object { return constructor(u) })
}
func (r *ObjectRegistry) registerObject(c func(uint32) Object) {
typ := reflect.TypeOf(c(0))
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
if typ.Kind() != reflect.Struct {
panic("accepted only structs")
}
if r.objects == nil {
r.objects = make(map[crc32]func(uint32) Object)
}
crc := c(0).CRC()
if _, ok := r.objects[crc]; ok {
panic(fmt.Sprintf("object with code %x already registered", crc))
}
r.objects[crc] = c
}
func (r *ObjectRegistry) registerCustom(valid []uint32, c func(uint32) Object) {
if r.objects == nil {
r.objects = make(map[crc32]func(uint32) Object)
}
for _, enum := range valid {
r.objects[enum] = c
}
}