v / vlib / encoding / binary / serialize.v
524 lines · 489 sloc · 11.13 KB · 4e71138489dcda3860faa7b93f7af231a491a255
Raw
1module binary
2
3struct EncodeState {
4mut:
5 b []u8
6 // pre-allocated buffers
7 b2 []u8 = [u8(0), 0]
8 b4 []u8 = [u8(0), 0, 0, 0]
9 b8 []u8 = [u8(0), 0, 0, 0, 0, 0, 0, 0]
10 big_endian bool
11}
12
13@[params]
14pub struct EncodeConfig {
15pub mut:
16 buffer_len int = 1024
17 big_endian bool // use big endian encoding the data
18}
19
20// encode_binary encode a T type data into u8 array.
21// for encoding struct, you can use `@[serialize: '-']` to skip field.
22// Note: `shared` fields in struct will be skipped.
23pub fn encode_binary[T](obj T, config EncodeConfig) ![]u8 {
24 mut s := EncodeState{
25 b: []u8{cap: config.buffer_len}
26 big_endian: config.big_endian
27 }
28 $if T is $array {
29 encode_array(mut s, obj)!
30 } $else $if T is $string {
31 encode_string(mut s, obj)!
32 } $else $if T is $struct {
33 encode_struct(mut s, obj)!
34 } $else $if T is $map {
35 encode_map(mut s, obj)!
36 } $else {
37 encode_primitive(mut s, obj)!
38 }
39 return s.b
40}
41
42@[inline]
43fn normalize_attr_arg(value string) string {
44 mut normalized := value.trim_space()
45 if normalized.len > 1 {
46 if (normalized[0] == `'` && normalized[normalized.len - 1] == `'`)
47 || (normalized[0] == `"` && normalized[normalized.len - 1] == `"`) {
48 normalized = normalized[1..normalized.len - 1]
49 }
50 }
51 return normalized
52}
53
54fn encode_struct[T](mut s EncodeState, obj T) ! {
55 $for field in T.fields {
56 mut is_skip := false
57 for attr in field.attrs {
58 f := attr.split_any(':')
59 if f.len == 2 {
60 match f[0].trim_space() {
61 'serialize' {
62 // @[serialize:'-']
63 if normalize_attr_arg(f[1]) == '-' {
64 is_skip = true
65 }
66 }
67 else {}
68 }
69 }
70 }
71 if !is_skip {
72 // TODO: support shared field in struct, currently skip.
73 $if field.typ !is $shared {
74 value := obj.$(field.name)
75 $if field.typ is $array {
76 encode_array(mut s, value)!
77 } $else $if field.typ is $string {
78 encode_string(mut s, value)!
79 } $else $if field.typ is $struct {
80 encode_struct(mut s, value)!
81 } $else $if field.typ is $map {
82 encode_map(mut s, value)!
83 } $else {
84 encode_primitive(mut s, value)!
85 }
86 }
87 }
88 }
89}
90
91// help unions for bypass `-cstrict`/ `-Wstrict-aliasing` check.
92union U32_F32 {
93 u u32
94 f f32
95}
96
97union U64_F64 {
98 u u64
99 f f64
100}
101
102fn encode_primitive[T](mut s EncodeState, value T) ! {
103 $if T is int {
104 // NOTE: `int` always use 64bit
105 s.put_u64(u64(value))
106 } $else $if T is u8 {
107 s.put_u8(u8(value))
108 } $else $if T is u16 {
109 s.put_u16(u16(value))
110 } $else $if T is u32 {
111 s.put_u32(u32(value))
112 } $else $if T is u64 {
113 s.put_u64(u64(value))
114 } $else $if T is i8 {
115 s.put_u8(u8(value))
116 } $else $if T is i16 {
117 s.put_u16(u16(value))
118 } $else $if T is i32 {
119 s.put_u32(u32(value))
120 } $else $if T is i64 {
121 s.put_u64(u64(value))
122 } $else $if T is f32 {
123 unsafe { s.put_u32(U32_F32{ f: value }.u) }
124 } $else $if T is f64 {
125 unsafe { s.put_u64(U64_F64{ f: value }.u) }
126 } $else $if T is bool {
127 s.put_u8(u8(value))
128 } $else $if T is rune {
129 s.put_u32(u32(value))
130 } $else $if T is isize {
131 if sizeof(isize) == 4 {
132 s.put_u32(u32(value))
133 } else {
134 s.put_u64(u64(value))
135 }
136 } $else $if T is usize {
137 if sizeof(usize) == 4 {
138 s.put_u32(u32(value))
139 } else {
140 s.put_u64(u64(value))
141 }
142 } $else $if T is voidptr {
143 s.put_u64(u64(value))
144 } $else {
145 // TODO: `any` type support?
146 return error('${@FN}(): unsupported type ${typeof(value).name}')
147 }
148}
149
150fn encode_array[T](mut s EncodeState, arr []T) ! {
151 s.put_u64(u64(arr.len))
152
153 $if T is u8 {
154 // optimization for `[]u8`
155 s.b << arr
156 } $else {
157 for element in arr {
158 $if T is $string {
159 encode_string(mut s, element)!
160 } $else $if T is $struct {
161 encode_struct(mut s, element)!
162 } $else $if T is $map {
163 encode_map(mut s, element)!
164 } $else {
165 encode_primitive(mut s, element)!
166 }
167 }
168 }
169}
170
171fn encode_string(mut s EncodeState, str string) ! {
172 s.put_u64(u64(str.len))
173 s.b << str.bytes()
174}
175
176fn encode_map[K, V](mut s EncodeState, m map[K]V) ! {
177 s.put_u64(u64(m.len))
178
179 for k, v in m {
180 // encode key first
181 // Maps can have keys of type string, rune, integer, float or voidptr.
182 $if K is $string {
183 encode_string(mut s, k)!
184 } $else {
185 encode_primitive(mut s, k)!
186 }
187
188 // encode value
189 $if V is $string {
190 encode_string(mut s, v)!
191 } $else $if V is $struct {
192 encode_struct(mut s, v)!
193 } $else $if V is $map {
194 encode_map(mut s, v)!
195 } $else {
196 encode_primitive(mut s, v)!
197 }
198 }
199}
200
201struct DecodeState {
202mut:
203 b []u8
204 b2 []u8 = [u8(0), 0]
205 b4 []u8 = [u8(0), 0, 0, 0]
206 b8 []u8 = [u8(0), 0, 0, 0, 0, 0, 0, 0]
207 offset int
208 big_endian bool
209}
210
211@[params]
212pub struct DecodeConfig {
213pub mut:
214 buffer_len int = 1024
215 big_endian bool // use big endian decode the data
216}
217
218// decode_binary decode a u8 array into T type data.
219// for decoding struct, you can use `@[serialize: '-']` to skip field.
220// Note: `shared` fields in struct will be skipped.
221pub fn decode_binary[T](b []u8, config DecodeConfig) !T {
222 mut s := DecodeState{
223 b: b
224 big_endian: config.big_endian
225 }
226 $if T is $array {
227 return decode_array(mut s, T{})!
228 } $else $if T is $string {
229 return decode_string(mut s)!
230 } $else $if T is $struct {
231 return decode_struct(mut s, T{})!
232 } $else $if T is $map {
233 return decode_map(mut s, T{})!
234 } $else {
235 return decode_primitive(mut s, unsafe { T(0) })!
236 }
237}
238
239fn decode_struct[T](mut s DecodeState, _ T) !T {
240 mut obj := T{}
241
242 $for field in T.fields {
243 mut is_skip := false
244 for attr in field.attrs {
245 f := attr.split_any(':')
246 if f.len == 2 {
247 match f[0].trim_space() {
248 'serialize' {
249 // @[serialize:'-']
250 if normalize_attr_arg(f[1]) == '-' {
251 is_skip = true
252 }
253 }
254 else {}
255 }
256 }
257 }
258 if !is_skip {
259 // TODO: support shared field in struct, currently skip.
260 $if field.typ !is $shared {
261 $if field.typ is $array {
262 obj.$(field.name) = decode_array(mut s, obj.$(field.name))!
263 } $else $if field.typ is $string {
264 obj.$(field.name) = decode_string(mut s)!
265 } $else $if field.typ is $struct {
266 obj.$(field.name) = decode_struct(mut s, obj.$(field.name))!
267 } $else $if field.typ is $map {
268 obj.$(field.name) = decode_map(mut s, obj.$(field.name))!
269 } $else {
270 obj.$(field.name) = decode_primitive(mut s, obj.$(field.name))!
271 }
272 }
273 }
274 }
275 return obj
276}
277
278fn decode_primitive[T](mut s DecodeState, value T) !T {
279 $if T is int {
280 // NOTE: int always use 64bit
281 return T(s.get_u64()!)
282 } $else $if T is u8 {
283 return T(s.get_u8()!)
284 } $else $if T is u16 {
285 return T(s.get_u16()!)
286 } $else $if T is u32 {
287 return T(s.get_u32()!)
288 } $else $if T is u64 {
289 return T(s.get_u64()!)
290 } $else $if T is i8 {
291 return T(s.get_u8()!)
292 } $else $if T is i16 {
293 return T(s.get_u16()!)
294 } $else $if T is i32 {
295 return T(s.get_u32()!)
296 } $else $if T is i64 {
297 return T(s.get_u64()!)
298 } $else $if T is f32 {
299 v := s.get_u32()!
300 return unsafe {
301 U32_F32{
302 u: v
303 }.f
304 }
305 } $else $if T is f64 {
306 v := s.get_u64()!
307 return unsafe {
308 U64_F64{
309 u: v
310 }.f
311 }
312 } $else $if T is bool {
313 return s.get_u8()! != 0
314 } $else $if T is rune {
315 return T(s.get_u32()!)
316 } $else $if T is isize {
317 if sizeof(isize) == 4 {
318 return T(s.get_u32()!)
319 } else {
320 return T(s.get_u64()!)
321 }
322 } $else $if T is usize {
323 if sizeof(usize) == 4 {
324 return T(s.get_u32()!)
325 } else {
326 return T(s.get_u64()!)
327 }
328 } $else $if T is voidptr {
329 return T(s.get_u64()!)
330 } $else {
331 // TODO: `any` type support?
332 return error('${@FN}(): unsupported type ${typeof(value).name}')
333 }
334 return error('${@FN}(): impossible error')
335}
336
337fn decode_array[T](mut s DecodeState, _ []T) ![]T {
338 len := int(s.get_u64()!)
339 if len <= 0 || s.offset + len > s.b.len {
340 return error('${@FN}(): invalid array length decode from stream')
341 }
342 mut arr := []T{cap: len}
343 $if T is u8 {
344 // optimization for `[]u8`
345 arr << s.b[s.offset..s.offset + len]
346 s.offset += len
347 } $else {
348 for _ in 0 .. len {
349 if s.offset >= s.b.len {
350 return error('${@FN}(): unexpected end of data')
351 }
352 $if T is $array {
353 arr << decode_array(mut s, T{})!
354 } $else $if T is $string {
355 arr << decode_string(mut s)!
356 } $else $if T is $struct {
357 arr << decode_struct(mut s, T{})!
358 } $else $if T is $map {
359 arr << decode_map(mut s, T{})!
360 } $else {
361 arr << decode_primitive(mut s, unsafe { T(0) })!
362 }
363 }
364 }
365 return arr
366}
367
368fn decode_string(mut s DecodeState) !string {
369 len := int(s.get_u64()!)
370 if len <= 0 || s.offset + len > s.b.len {
371 return error('${@FN}(): invalid string length decode from stream')
372 }
373 str := unsafe { s.b[s.offset..s.offset + len].bytestr() }
374 s.offset += len
375 return str
376}
377
378// `Any` is a sum type that lists the possible types to be decoded and used.
379type Any = int
380 | bool
381 | f64
382 | f32
383 | i64
384 | i32
385 | i16
386 | i8
387 | map[string]Any
388 | map[int]Any
389 | string
390 | u64
391 | u32
392 | u16
393 | u8
394 | rune
395 | isize
396 | usize
397 | []Any
398
399fn decode_map[K, V](mut s DecodeState, _ map[K]V) !map[K]V {
400 len := int(s.get_u64()!)
401 if len <= 0 || s.offset + len > s.b.len {
402 return error('${@FN}(): invalid map length decode from stream')
403 }
404
405 mut m := map[K]V{}
406
407 for _ in 0 .. len {
408 // decode key first
409 // Maps can have keys of type string, rune, integer, float or voidptr.
410 mut k := Any(0)
411 $if K is $string {
412 k = decode_string(mut s)!
413 } $else {
414 k = decode_primitive(mut s, unsafe { K(0) })!
415 }
416
417 // decode value
418 $if V is $struct {
419 v := decode_struct(mut s, V{})!
420 m[k as K] = v
421 } $else $if V is $map {
422 v := decode_map(mut s, V{})!
423 m[k as K] = v
424 } $else $if V is $string {
425 v := decode_string(mut s)!
426 m[k as K] = v
427 } $else {
428 v := decode_primitive(mut s, unsafe { V(0) })!
429 m[k as K] = v
430 }
431 }
432 return m
433}
434
435@[inline]
436fn (mut s DecodeState) get_u64() !u64 {
437 if s.offset + 8 > s.b.len {
438 return error('${@FN}(): bytes length is not enough for u64')
439 }
440 defer {
441 s.offset += 8
442 }
443 if s.big_endian {
444 return big_endian_u64_at(s.b, s.offset)
445 } else {
446 return little_endian_u64_at(s.b, s.offset)
447 }
448}
449
450@[inline]
451fn (mut s DecodeState) get_u32() !u32 {
452 if s.offset + 4 > s.b.len {
453 return error('${@FN}(): bytes length is not enough for u32')
454 }
455 defer {
456 s.offset += 4
457 }
458 if s.big_endian {
459 return big_endian_u32_at(s.b, s.offset)
460 } else {
461 return little_endian_u32_at(s.b, s.offset)
462 }
463}
464
465@[inline]
466fn (mut s DecodeState) get_u16() !u16 {
467 if s.offset + 2 > s.b.len {
468 return error('${@FN}(): bytes length is not enough for u16')
469 }
470 defer {
471 s.offset += 2
472 }
473 if s.big_endian {
474 return big_endian_u16_at(s.b, s.offset)
475 } else {
476 return little_endian_u16_at(s.b, s.offset)
477 }
478}
479
480@[inline]
481fn (mut s DecodeState) get_u8() !u8 {
482 if s.offset + 1 > s.b.len {
483 return error('${@FN}(): bytes length is not enough for u8')
484 }
485 defer {
486 s.offset += 1
487 }
488 return s.b[s.offset]
489}
490
491@[inline]
492fn (mut s EncodeState) put_u64(value u64) {
493 if s.big_endian {
494 big_endian_put_u64(mut s.b8, value)
495 } else {
496 little_endian_put_u64(mut s.b8, value)
497 }
498 s.b << s.b8
499}
500
501@[inline]
502fn (mut s EncodeState) put_u32(value u32) {
503 if s.big_endian {
504 big_endian_put_u32(mut s.b4, value)
505 } else {
506 little_endian_put_u32(mut s.b4, value)
507 }
508 s.b << s.b4
509}
510
511@[inline]
512fn (mut s EncodeState) put_u16(value u16) {
513 if s.big_endian {
514 big_endian_put_u16(mut s.b2, value)
515 } else {
516 little_endian_put_u16(mut s.b2, value)
517 }
518 s.b << s.b2
519}
520
521@[inline]
522fn (mut s EncodeState) put_u8(value u8) {
523 s.b << value
524}
525