v

Зеркало из https://github.com/vlang/v
Форк
0
/x
/
encoder.v 
583 строки · 15.8 Кб
1
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2
// Use of this source code is governed by an MIT license
3
// that can be found in the LICENSE file.
4
module json2
5

6
import time
7

8
// Encoder encodes the an `Any` type into JSON representation.
9
// It provides parameters in order to change the end result.
10
pub struct Encoder {
11
pub:
12
	newline              u8
13
	newline_spaces_count int
14
	escape_unicode       bool = true
15
}
16

17
// byte array versions of the most common tokens/chars to avoid reallocations
18
const null_in_bytes = 'null'
19

20
const true_in_string = 'true'
21

22
const false_in_string = 'false'
23

24
const empty_array = [u8(`[`), `]`]!
25

26
const comma_rune = `,`
27

28
const colon_rune = `:`
29

30
const quote_rune = `"`
31

32
const back_slash = [u8(`\\`), `\\`]!
33

34
const quote = [u8(`\\`), `"`]!
35

36
const slash = [u8(`\\`), `/`]!
37

38
const null_unicode = [u8(`\\`), `u`, `0`, `0`, `0`, `0`]!
39

40
const ascii_control_characters = ['\\u0000', '\\t', '\\n', '\\r', '\\u0004', '\\u0005', '\\u0006',
41
	'\\u0007', '\\b', '\\t', '\\n', '\\u000b', '\\f', '\\r', '\\u000e', '\\u000f', '\\u0010',
42
	'\\u0011', '\\u0012', '\\u0013', '\\u0014', '\\u0015', '\\u0016', '\\u0017', '\\u0018', '\\u0019',
43
	'\\u001a', '\\u001b', '\\u001c', '\\u001d', '\\u001e', '\\u001f']!
44

45
const curly_open_rune = `{`
46

47
const curly_close_rune = `}`
48

49
const ascii_especial_characters = [u8(`\\`), `"`, `/`]!
50

51
// encode is a generic function that encodes a type into a JSON string.
52
@[manualfree]
53
pub fn encode[T](val T) string {
54
	$if T is $array {
55
		return encode_array(val)
56
	} $else {
57
		mut count := Count{0}
58
		count.count_chars(val)
59

60
		mut buf := []u8{cap: count.total}
61

62
		defer {
63
			unsafe { buf.free() }
64
		}
65
		encoder := Encoder{}
66

67
		encoder.encode_value(val, mut buf) or {
68
			println(err)
69
			encoder.encode_value[string]('null', mut buf) or {}
70
		}
71

72
		return buf.bytestr()
73
	}
74
}
75

76
// encode_array is a generic function that encodes a array into a JSON string.
77
@[manualfree]
78
fn encode_array[T](val []T) string {
79
	if val.len == 0 {
80
		return '[]'
81
	}
82

83
	mut buf := []u8{}
84

85
	defer {
86
		unsafe { buf.free() }
87
	}
88

89
	encoder := Encoder{}
90
	encoder.encode_array(val, 1, mut buf) or {
91
		println(err)
92
		encoder.encode_value[string]('null', mut buf) or {}
93
	}
94

95
	return buf.bytestr()
96
}
97

98
// encode_pretty ...
99
pub fn encode_pretty[T](typed_data T) string {
100
	encoded := encode(typed_data)
101
	raw_decoded := raw_decode(encoded) or { 0 }
102
	return raw_decoded.prettify_json_str()
103
}
104

105
// encode_value encodes a value to the specific buffer.
106
pub fn (e &Encoder) encode_value[T](val T, mut buf []u8) ! {
107
	e.encode_value_with_level[T](val, 1, mut buf)!
108
}
109

110
fn (e &Encoder) encode_newline(level int, mut buf []u8) ! {
111
	if e.newline != 0 {
112
		buf << e.newline
113
		for j := 0; j < level * e.newline_spaces_count; j++ {
114
			buf << ` `
115
		}
116
	}
117
}
118

119
fn (e &Encoder) encode_map[T](value T, level int, mut buf []u8) ! {
120
	buf << curly_open_rune
121
	mut idx := 0
122
	for k, v in value {
123
		e.encode_newline(level, mut buf)!
124
		// e.encode_string(k.str(), mut buf)!
125
		e.encode_string(k, mut buf)!
126
		buf << colon_rune
127
		if e.newline != 0 {
128
			buf << ` `
129
		}
130

131
		// workaround to avoid `cannot convert 'struct x__json2__Any' to 'struct string'`
132
		$if v is $sumtype {
133
			$for variant_value in v.variants {
134
				if v is variant_value {
135
					e.encode_value_with_level(v, level + 1, mut buf)!
136
				}
137
			}
138
		} $else {
139
			e.encode_value_with_level(v, level + 1, mut buf)!
140
		}
141

142
		if idx < value.len - 1 {
143
			buf << comma_rune
144
		}
145
		idx++
146
	}
147
	// e.encode_newline(level, mut buf)!
148
	e.encode_newline(level - 1, mut buf)!
149
	buf << curly_close_rune
150
}
151

152
fn (e &Encoder) encode_value_with_level[T](val T, level int, mut buf []u8) ! {
153
	$if val is $option {
154
		workaround := val
155
		if workaround != none {
156
			e.encode_value_with_level(val, level, mut buf)!
157
		}
158
	} $else $if T is string {
159
		e.encode_string(val, mut buf)!
160
	} $else $if T is $sumtype {
161
		$for v in val.variants {
162
			if val is v {
163
				e.encode_value_with_level(val, level, mut buf)!
164
			}
165
		}
166
	} $else $if T is $alias {
167
		// TODO
168
	} $else $if T is time.Time {
169
		str_value := val.format_rfc3339()
170
		buf << quote_rune
171
		unsafe { buf.push_many(str_value.str, str_value.len) }
172
		buf << quote_rune
173
	} $else $if T is $map {
174
		e.encode_map(val, level, mut buf)!
175
	} $else $if T is $array {
176
		e.encode_array(val, level, mut buf)!
177
	} $else $if T is Encodable {
178
		str_value := val.json_str()
179
		unsafe { buf.push_many(str_value.str, str_value.len) }
180
	} $else $if T is Null {
181
		unsafe { buf.push_many(null_in_bytes.str, null_in_bytes.len) }
182
	} $else $if T is $struct {
183
		e.encode_struct(val, level, mut buf)!
184
	} $else $if T is $enum {
185
		str_int := int(val).str()
186
		unsafe { buf.push_many(str_int.str, str_int.len) }
187
	} $else $if T is $int || T is $float || T is bool {
188
		str_int := val.str()
189
		unsafe { buf.push_many(str_int.str, str_int.len) }
190
	} $else {
191
		return error('cannot encode value with ${typeof(val).name} type')
192
	}
193
}
194

195
fn (e &Encoder) encode_struct[U](val U, level int, mut buf []u8) ! {
196
	buf << curly_open_rune
197
	mut i := 0
198
	mut fields_len := 0
199

200
	$for field in U.fields {
201
		mut @continue := false
202
		for attr in field.attrs {
203
			if attr.contains('skip') {
204
				@continue = true
205
			}
206
			if attr.contains('json: ') {
207
				if attr.replace('json: ', '') == '-' {
208
					@continue = true
209
				}
210
				break
211
			}
212
		}
213
		if !@continue {
214
			$if field.is_option {
215
				if val.$(field.name) != none {
216
					fields_len++
217
				}
218
			} $else {
219
				fields_len++
220
			}
221
		}
222
	}
223
	$for field in U.fields {
224
		mut ignore_field := false
225

226
		value := val.$(field.name)
227

228
		is_nil := val.$(field.name).str() == '&nil'
229

230
		mut json_name := ''
231

232
		for attr in field.attrs {
233
			if attr.contains('skip') {
234
				ignore_field = true
235
			}
236
			if attr.contains('json: ') {
237
				json_name = attr.replace('json: ', '')
238
				if json_name == '-' {
239
					ignore_field = true
240
				}
241
				break
242
			}
243
		}
244

245
		if !ignore_field {
246
			$if value is $option {
247
				workaround := val.$(field.name)
248
				if workaround != none { // smartcast
249
					e.encode_newline(level, mut buf)!
250
					if json_name != '' {
251
						e.encode_string(json_name, mut buf)!
252
					} else {
253
						e.encode_string(field.name, mut buf)!
254
					}
255
					buf << colon_rune
256

257
					if e.newline != 0 {
258
						buf << ` `
259
					}
260
					e.encode_value_with_level(value, level, mut buf)!
261
				} else {
262
					ignore_field = true
263
				}
264
			} $else {
265
				is_none := val.$(field.name).str() == 'unknown sum type value' // assert json.encode(StructType[SumTypes]{}) == '{}'
266
				if !is_none && !is_nil {
267
					e.encode_newline(level, mut buf)!
268
					if json_name != '' {
269
						e.encode_string(json_name, mut buf)!
270
					} else {
271
						e.encode_string(field.name, mut buf)!
272
					}
273
					buf << colon_rune
274

275
					if e.newline != 0 {
276
						buf << ` `
277
					}
278
				}
279

280
				$if field.indirections != 0 {
281
					if val.$(field.name) != unsafe { nil } {
282
						$if field.indirections == 1 {
283
							e.encode_value_with_level(*val.$(field.name), level + 1, mut
284
								buf)!
285
						}
286
						$if field.indirections == 2 {
287
							e.encode_value_with_level(**val.$(field.name), level + 1, mut
288
								buf)!
289
						}
290
						$if field.indirections == 3 {
291
							e.encode_value_with_level(***val.$(field.name), level + 1, mut
292
								buf)!
293
						}
294
					}
295
				} $else $if field.typ is string {
296
					e.encode_string(val.$(field.name).str(), mut buf)!
297
				} $else $if field.typ is time.Time {
298
					str_value := val.$(field.name).format_rfc3339()
299
					buf << quote_rune
300
					unsafe { buf.push_many(str_value.str, str_value.len) }
301
					buf << quote_rune
302
				} $else $if field.typ is bool {
303
					if value {
304
						unsafe { buf.push_many(true_in_string.str, true_in_string.len) }
305
					} else {
306
						unsafe { buf.push_many(false_in_string.str, false_in_string.len) }
307
					}
308
				} $else $if field.typ in [$float, $int] {
309
					str_value := val.$(field.name).str()
310
					unsafe { buf.push_many(str_value.str, str_value.len) }
311
				} $else $if field.is_array {
312
					// TODO: replace for `field.typ is $array`
313
					e.encode_array(value, level + 1, mut buf)!
314
				} $else $if field.typ is $array {
315
					// e.encode_array(value, level + 1, mut buf)! // FIXME: error: could not infer generic type `U` in call to `encode_array`
316
				} $else $if field.typ is $struct {
317
					e.encode_struct(value, level + 1, mut buf)!
318
				} $else $if field.is_map {
319
					e.encode_map(value, level + 1, mut buf)!
320
				} $else $if field.is_enum {
321
					// TODO: replace for `field.typ is $enum`
322
					// str_value := int(val.$(field.name)).str()
323
					// unsafe { buf.push_many(str_value.str, str_value.len) }
324
					e.encode_value_with_level(val.$(field.name), level + 1, mut buf)!
325
				} $else $if field.typ is $enum {
326
				} $else $if field.typ is $sumtype {
327
					field_value := val.$(field.name)
328
					if field_value.str() != 'unknown sum type value' {
329
						$for v in field_value.variants {
330
							if field_value is v {
331
								e.encode_value_with_level(field_value, level, mut buf)!
332
							}
333
						}
334
					}
335
				} $else $if field.typ is $alias {
336
					$if field.unaliased_typ is string {
337
						e.encode_string(val.$(field.name).str(), mut buf)!
338
					} $else $if field.unaliased_typ is time.Time {
339
						parsed_time := time.parse(val.$(field.name).str()) or { time.Time{} }
340
						e.encode_string(parsed_time.format_rfc3339(), mut buf)!
341
					} $else $if field.unaliased_typ is bool {
342
						if val.$(field.name) {
343
							unsafe { buf.push_many(true_in_string.str, true_in_string.len) }
344
						} else {
345
							unsafe { buf.push_many(false_in_string.str, false_in_string.len) }
346
						}
347
					} $else $if field.unaliased_typ in [$float, $int] {
348
						str_value := val.$(field.name).str()
349
						unsafe { buf.push_many(str_value.str, str_value.len) }
350
					} $else $if field.unaliased_typ is $array {
351
						// TODO
352
					} $else $if field.unaliased_typ is $struct {
353
						e.encode_struct(value, level + 1, mut buf)!
354
					} $else $if field.unaliased_typ is $enum {
355
						// TODO
356
					} $else $if field.unaliased_typ is $sumtype {
357
						// TODO
358
					} $else {
359
						return error('the alias ${typeof(val).name} cannot be encoded')
360
					}
361
				} $else {
362
					return error('type ${typeof(val).name} cannot be array encoded')
363
				}
364
			}
365
		}
366

367
		if i < fields_len - 1 && !ignore_field {
368
			if !is_nil {
369
				buf << comma_rune
370
			}
371
		}
372
		if !ignore_field {
373
			i++
374
		}
375
	}
376
	e.encode_newline(level - 1, mut buf)!
377
	buf << curly_close_rune
378
	// b.measure('encode_struct')
379
}
380

381
fn (e &Encoder) encode_array[U](val []U, level int, mut buf []u8) ! {
382
	if val.len == 0 {
383
		unsafe { buf.push_many(&empty_array[0], empty_array.len) }
384
		return
385
	}
386
	buf << `[`
387
	for i in 0 .. val.len {
388
		e.encode_newline(level, mut buf)!
389

390
		$if U is string || U is bool || U is $int || U is $float {
391
			e.encode_value_with_level(val[i], level + 1, mut buf)!
392
		} $else $if U is $array {
393
			e.encode_array(val[i], level + 1, mut buf)!
394
		} $else $if U is $struct {
395
			e.encode_struct(val[i], level + 1, mut buf)!
396
		} $else $if U is $sumtype {
397
			e.encode_value_with_level(val[i], level + 1, mut buf)!
398
		} $else $if U is $enum {
399
			// TODO: test
400
			e.encode_value_with_level(val[i], level + 1, mut buf)!
401
		} $else {
402
			return error('type ${typeof(val).name} cannot be array encoded')
403
		}
404
		if i < val.len - 1 {
405
			buf << comma_rune
406
		}
407
	}
408

409
	e.encode_newline(level - 1, mut buf)!
410
	buf << `]`
411
}
412

413
// str returns the JSON string representation of the `map[string]Any` type.
414
pub fn (f map[string]Any) str() string {
415
	return Any(f).json_str()
416
}
417

418
// str returns the JSON string representation of the `[]Any` type.
419
pub fn (f []Any) str() string {
420
	return Any(f).json_str()
421
}
422

423
// str returns the string representation of the `Any` type. Use the `json_str` method
424
// if you want to use the escaped str() version of the `Any` type.
425
pub fn (f Any) str() string {
426
	if f is string {
427
		return f
428
	} else {
429
		return f.json_str()
430
	}
431
}
432

433
// json_str returns the JSON string representation of the `Any` type.
434
pub fn (f Any) json_str() string {
435
	return encode(f)
436
}
437

438
// prettify_json_str returns the pretty-formatted JSON string representation of the `Any` type.
439
@[manualfree]
440
pub fn (f Any) prettify_json_str() string {
441
	mut buf := []u8{}
442
	defer {
443
		unsafe { buf.free() }
444
	}
445
	mut enc := Encoder{
446
		newline:              `\n`
447
		newline_spaces_count: 2
448
	}
449
	enc.encode_value(f, mut buf) or {}
450
	return buf.bytestr()
451
}
452

453
// TODO: Need refactor. Is so slow. The longer the string, the lower the performance.
454
// encode_string returns the JSON spec-compliant version of the string.
455
@[direct_array_access]
456
fn (e &Encoder) encode_string(s string, mut buf []u8) ! {
457
	if s == '' {
458
		empty := [u8(quote_rune), quote_rune]!
459
		unsafe { buf.push_many(&empty[0], 2) }
460
		return
461
	}
462
	mut last_no_buffer_expansible_char_position_candidate := 0
463
	buf << quote_rune
464

465
	if !e.escape_unicode {
466
		unsafe {
467
			buf.push_many(s.str, s.len)
468
			buf << quote_rune
469
		}
470
		return
471
	}
472

473
	for idx := 0; idx < s.len; idx++ {
474
		current_byte := s[idx]
475

476
		mut current_utf8_len := ((0xe5000000 >> ((current_byte >> 3) & 0x1e)) & 3) + 1
477

478
		current_value_cause_buffer_expansion :=
479
			(current_utf8_len == 1 && ((current_byte < 32 || current_byte > 127)
480
			|| current_byte in ascii_especial_characters)) || current_utf8_len == 3
481

482
		if !current_value_cause_buffer_expansion {
483
			// while it is not the last one
484
			if idx < s.len - 1 {
485
				if s.len > (idx + current_utf8_len) {
486
					if current_utf8_len == 2 || current_utf8_len == 4 {
487
						// runes like: ã, ü, etc.
488
						// Emojis ranges
489
						// 	(0x1F300, 0x1F5FF),  # Miscellaneous Symbols and Pictographs
490
						// 	(0x1F600, 0x1F64F),  # Emoticons
491
						// 	(0x1F680, 0x1F6FF),  # Transport and Map Symbols
492
						idx += current_utf8_len - 1
493
						continue
494
					}
495
				} else {
496
					unsafe {
497
						buf.push_many(s.str + last_no_buffer_expansible_char_position_candidate,
498
							s.len - last_no_buffer_expansible_char_position_candidate)
499
					}
500
					break
501
				}
502
			} else if idx == s.len - 1 {
503
				unsafe {
504
					buf.push_many(s.str + last_no_buffer_expansible_char_position_candidate,
505
						s.len - last_no_buffer_expansible_char_position_candidate)
506
				}
507
			}
508
		} else {
509
			if idx > 0 {
510
				length := idx - last_no_buffer_expansible_char_position_candidate
511
				unsafe {
512
					buf.push_many(s.str + last_no_buffer_expansible_char_position_candidate,
513
						length)
514
				}
515
				last_no_buffer_expansible_char_position_candidate = idx + 1
516
			}
517
		}
518

519
		if current_utf8_len == 1 {
520
			if current_byte < 32 {
521
				// ASCII Control Characters
522
				unsafe {
523
					buf.push_many(ascii_control_characters[current_byte].str, ascii_control_characters[current_byte].len)
524
				}
525
				last_no_buffer_expansible_char_position_candidate = idx + 1
526
			} else if current_byte >= 32 && current_byte < 128 {
527
				// ASCII especial characters
528
				if current_byte == `\\` {
529
					unsafe { buf.push_many(&back_slash[0], back_slash.len) }
530
					last_no_buffer_expansible_char_position_candidate = idx + 1
531
					continue
532
				} else if current_byte == `"` {
533
					unsafe { buf.push_many(&quote[0], quote.len) }
534
					last_no_buffer_expansible_char_position_candidate = idx + 1
535
					continue
536
				} else if current_byte == `/` {
537
					unsafe { buf.push_many(&slash[0], slash.len) }
538
					last_no_buffer_expansible_char_position_candidate = idx + 1
539
					continue
540
				}
541
			}
542
			continue
543
		} else if current_utf8_len == 3 {
544
			// runes like: ✔, ひらがな ...
545

546
			// Handle multi-byte characters byte-by-byte
547
			mut codepoint := u32(current_byte & ((1 << (7 - current_utf8_len)) - 1))
548
			for j in 1 .. current_utf8_len {
549
				if idx + j >= s.len {
550
					// Incomplete UTF-8 sequence, TODO handle error
551
					idx++
552
					continue
553
				}
554

555
				mut b := s[idx + j]
556
				if (b & 0xC0) != 0x80 {
557
					// Invalid continuation byte, TODO handle error
558
					idx++
559
					continue
560
				}
561

562
				codepoint = u32((codepoint << 6) | (b & 0x3F))
563
			}
564
			// runes like: ✔, ひらがな ...
565
			unsafe { buf.push_many(&null_unicode[0], null_unicode.len) }
566
			buf[buf.len - 1] = hex_digit(codepoint & 0xF)
567
			buf[buf.len - 2] = hex_digit((codepoint >> 4) & 0xF)
568
			buf[buf.len - 3] = hex_digit((codepoint >> 8) & 0xF)
569
			buf[buf.len - 4] = hex_digit((codepoint >> 12) & 0xF)
570
			idx += current_utf8_len - 1
571
			last_no_buffer_expansible_char_position_candidate = idx + 1
572
		}
573
	}
574

575
	buf << quote_rune
576
}
577

578
fn hex_digit(n int) u8 {
579
	if n < 10 {
580
		return `0` + n
581
	}
582
	return `a` + (n - 10)
583
}
584

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.