Ctrl K
ComunidadGuías…

vlib/json2/decode.v

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
module json2

import strconv
import time
import math
import sync.stdatomic

const null_in_string = 'null'

const true_in_string = 'true'

const false_in_string = 'false'

const whitespace_chars = [` `, `\t`, `\n`, `\r`]!

// ValueInfo represents the position and length of a value, such as string, number, array, object key, and object value in a JSON string.
struct ValueInfo {
mut:
	position   int       // The position of the value in the JSON string.
	value_kind ValueKind // The kind of the value.
	has_escape bool      // Whether a string contains an escape sequence.
	length     int       // The length of the value in the JSON string.
}

struct DecoderFieldInfo {
	key_name string

	is_skip     bool
	is_required bool
	is_raw      bool
}

@[markused]
struct StructFieldInfo {
	name          string // name is the V name of the field, for error messages.
	json_name_ptr voidptr
	json_name_len int
	is_skip       bool
	is_required   bool
	is_raw        bool
}

// Keep runtime attribute parsing outside the compile-time field loop. This is called only while
// a struct type's field metadata cache is initialized. The cached pointers borrow attribute
// bytes, so autofree must not release the loop's attribute strings.
@[manualfree; noinline]
fn struct_field_info(field_name string, attrs []string) StructFieldInfo {
	mut json_name_str := field_name.str
	mut json_name_len := field_name.len
	mut is_json_skip := false
	for i in 0 .. attrs.len {
		attr := &attrs[i]
		if start, end := json_attr_value_range(attr) {
			if end <= start {
				continue
			}
			if end == start + 1 && (*attr)[start] == `-` {
				is_json_skip = true
				break
			}
			json_name_str = unsafe { attr.str + start }
			json_name_len = end - start
			break
		}
	}
	return StructFieldInfo{
		name:          field_name
		json_name_ptr: voidptr(json_name_str)
		json_name_len: json_name_len
		is_skip:       attrs.contains('skip') || is_json_skip
		is_required:   attrs.contains('required')
		is_raw:        attrs.contains('raw')
	}
}

struct StructKeyDecodeResult[T] {
	matched bool
	value   T
}

// DecoderOptions provides options for JSON decoding.
// By default, decoding is lenient. Use `strict: true` for strict type conversion
// and `json5: true` to accept JSON5 syntax.
@[markused; params]
pub struct DecoderOptions {
pub:
	// json5 accepts JSON5 comments, keys, strings, trailing commas and numbers.
	json5 bool
	// In strict mode, quoted strings are not accepted as numbers.
	// For example, '"123"' will fail to decode as int in strict mode,
	// but will succeed in default mode.
	// Strict mode also requires a fixed size array to get a JSON array with exactly
	// as many elements. By default, `null` or missing trailing elements keep their
	// default values, and extra elements are ignored.
	// Strict mode also rejects `null` for a value that is not an option. By default,
	// like in the removed `json` module, `null` is the zero value of a number, bool,
	// string, enum or time, an empty array or map, and a nested struct with its
	// default field values.
	strict bool
}

// DecodeBuffer retains token storage between decode_reuse calls. Give each
// concurrent or reentrant decoding operation its own buffer. It never retains
// the input or decoded values; its capacity grows with the largest token count.
// Create independent buffers with DecodeBuffer{}; do not copy a buffer after
// decoding has retained storage in it, including after a decoding error.
pub struct DecodeBuffer {
mut:
	values_info []ValueInfo
}

// Decoder is the internal decoding state.
@[markused]
struct Decoder {
	json               string // json is the JSON data to be decoded.
	json5              bool
	source_json        string // Original JSON5 input, used only for decoding diagnostics.
	source_values_info []Json5SourceSpan
	strict             bool // strict mode rejects quoted strings as numbers, fixed arrays of another length, and null values
mut:
	// Scratch result of check_string, copied to the corresponding ValueInfo.
	string_has_escape bool
	// values_info describes every value of the JSON string, in the order in which they
	// start: an array or an object is followed by its elements, or by its keys and their
	// values. It is one flat array, so nothing is allocated per value, and moving to
	// the next value is an index increment.
	values_info []ValueInfo
	checker_idx int // checker_idx is the current index of the decoder.
	current_idx int // current_idx is the index in values_info of the value that is decoded next.
	// checker_depth is the nesting depth reached by the current check_array/
	// check_object call chain; see max_nesting_depth in check.v.
	checker_depth int
}

// no_value_idx is the index of a value that is not there, such as the `_type` of an
// object without that key.
const no_value_idx = -1

// has_value reports whether `value_idx` is the index of a value in values_info. The
// index after the last value, and no_value_idx, are not.
@[inline; markused]
fn (decoder &Decoder) has_value(value_idx int) bool {
	return value_idx >= 0 && value_idx < decoder.values_info.len
}

// current_value returns the value that is decoded next.
@[inline; markused]
fn (decoder &Decoder) current_value() ValueInfo {
	return decoder.values_info[decoder.current_idx]
}

// skip_strings_min_json_len is the length of a JSON string from which
// value_count_bound() skips the strings in it. That takes longer, which a shorter
// JSON string is not worth: the room that it saves there is small.
const skip_strings_min_json_len = 16384

// value_count_bound returns an upper bound of the number of values in the `json`
// string, to allocate values_info once, with (nearly) no room to spare. Outside of
// strings, every value but the root follows a `,`, a `:`, a `[` or a `{` of its own,
// so the bound is only higher than the count for empty arrays and objects, and in a
// short JSON string, for those characters in strings.
@[direct_array_access; markused]
fn value_count_bound(json string) int {
	mut count := 1
	if json.len < skip_strings_min_json_len {
		for c in json {
			// `[` and `{` only differ in the bit that is set here.
			count += int(c == `,`) + int(c == `:`) + int((c | 0x20) == `{`)
		}
		return count
	}
	mut i := 0
	for i < json.len {
		c := json[i]
		i++
		if c == `"` {
			i = json_string_end(json, i) + 1
			continue
		}
		count += int(c == `,`) + int(c == `:`) + int((c | 0x20) == `{`)
	}
	return count
}

// json_string_end returns the index of the `"` that ends the JSON string whose content
// starts at `start` in `json`, or `json.len` when the string is not closed.
@[direct_array_access]
fn json_string_end(json string, start int) int {
	mut i := start
	for {
		// A long string is skipped 8 bytes at a time, as long as none is a `"` or a `\`.
		for i + 8 <= json.len {
			mut word := u64(0)
			unsafe { vmemcpy(&word, json.str + i, 8) }
			if word_has_byte(word, `"`) || word_has_byte(word, `\\`) {
				break
			}
			i += 8
		}
		for i < json.len && json[i] != `"` && json[i] != `\\` {
			i++
		}
		if i >= json.len {
			return json.len
		}
		if json[i] == `"` {
			return i
		}
		// The character after a `\` is escaped: a `"` there does not end the string.
		i += 2
	}
	return json.len
}

// word_has_byte reports whether one of the 8 bytes of `word` is `b`.
@[inline]
fn word_has_byte(word u64, b u8) bool {
	// A byte of `diff` is zero where `word` has `b`. The subtraction sets the high bit
	// of a zero byte, that `~diff` keeps, and without a zero byte no high bit is left.
	diff := word ^ (u64(b) * 0x0101010101010101)
	return ((diff - 0x0101010101010101) & ~diff & 0x8080808080808080) != 0
}

// add_value puts a value of `value_kind`, that starts at the checker's position, in
// values_info. Its length is set once its end is known.
@[direct_array_access; inline; markused]
fn (mut checker Decoder) add_value(value_kind ValueKind) {
	if checker.values_info.len == checker.values_info.cap
		&& !checker.values_info.flags.has(.managed) {
		capacity := value_count_bound(checker.json)
		unsafe { checker.values_info.grow_cap(capacity - checker.values_info.len) }
	}
	checker.values_info << ValueInfo{
		position:   checker.checker_idx
		value_kind: value_kind
		has_escape: false
	}
	if checker.values_info.flags.has(.managed) {
		unsafe { checker.values_info.flags |= .noslices }
	}
}

const max_context_length = 50
const max_extra_characters = 5
const tab_width = 8

pub struct JsonDecodeError {
	Error
	context string
pub:
	message string

	line      int
	character int
}

fn (e JsonDecodeError) msg() string {
	return '\n${e.line}:${e.character}: Invalid json: ${e.message}\n${e.context}'
}

// checker_error generates a checker error message showing the position in the json string
@[markused]
fn (mut checker Decoder) checker_error(message string) ! {
	position := checker.checker_idx

	mut line_number := 0
	mut character_number := 0
	mut last_newline := 0

	for i := position - 1; i >= 0; i-- {
		if last_newline == 0 {
			if checker.json[i] == `\n` {
				last_newline = i + 1
			} else if checker.json[i] == `\t` {
				character_number += tab_width
			} else {
				character_number++
			}
		}
		if checker.json[i] == `\n` {
			line_number++
		}
	}

	cutoff := character_number > max_context_length

	// either start of string, last newline or a limited amount of characters
	// Tabs increase display columns without adding bytes; never slice before the current line.
	context_start := if cutoff {
		int_max(last_newline, position - max_context_length)
	} else {
		last_newline
	}

	// print some extra characters
	mut context_end := int_min(checker.json.len, position + max_extra_characters)
	context_end_newline := checker.json[position..context_end].index_u8(`\n`)

	if context_end_newline > 0 {
		context_end = position + context_end_newline
	}

	mut context := ''

	if cutoff {
		context += '...'
	}
	context += checker.json[context_start..position]
	context += '\\e[31m${checker.json[position].ascii_str()}\\e[0m'
	context += checker.json[position + 1..context_end]
	context += '\n'

	if cutoff {
		context += ' '.repeat(max_context_length + 3)
	} else {
		context += ' '.repeat(character_number)
	}
	context += '\e[31m^\e[0m'

	return JsonDecodeError{
		context:   context
		message:   'Syntax: ${message}'
		line:      line_number + 1
		character: character_number + 1
	}
}

// decode_error generates a decoding error from the decoding stage
@[markused]
fn (mut decoder Decoder) decode_error(message string) ! {
	mut error_info := ValueInfo{}
	mut error_idx := -1
	if decoder.current_idx < decoder.values_info.len {
		error_idx = decoder.current_idx
		error_info = decoder.current_value()
	} else if decoder.values_info.len > 0 {
		error_idx = decoder.values_info.len - 1
		error_info = decoder.values_info.last()
	}
	json := if decoder.json5 { decoder.source_json } else { decoder.json }
	if decoder.json5 && error_idx >= 0 {
		source_info := decoder.source_values_info[error_idx]
		error_info.position = source_info.position
		error_info.length = source_info.length
	}

	start := error_info.position
	if decoder.json5 {
		line, column, line_start := json5_source_location(json, start)
		return JsonDecodeError{
			context:   json5_decode_context(json, start, error_info.length, line_start, column)
			message:   'Data: ${message}'
			line:      line + 1
			character: column + 1
		}
	}
	end := start + int_min(error_info.length, max_context_length)
	mut line_number := 0
	mut character_number := 0
	mut last_newline := 0
	for i := start - 1; i >= 0; i-- {
		if last_newline == 0 {
			if json[i] == `\n` {
				last_newline = i + 1
			} else if json[i] == `\t` {
				character_number += tab_width
			} else {
				character_number++
			}
		}
		if json[i] == `\n` {
			line_number++
		}
	}

	cutoff := character_number > max_context_length

	// either start of string, last newline or a limited amount of characters
	// Tabs increase display columns without adding bytes; never slice before the current line.
	context_start := if cutoff {
		int_max(last_newline, start - max_context_length)
	} else {
		last_newline
	}

	// print some extra characters
	mut context_end := int_min(json.len, end + max_extra_characters)
	context_end_newline := json[end..context_end].index_u8(`\n`)
	if context_end_newline != -1 {
		context_end = end + context_end_newline
	}

	mut context := ''

	if cutoff {
		context += '...'
	}
	context += json[context_start..start]
	context += '\\e[31m${json[start..end]}\\e[0m'
	context += json[end..context_end]
	context += '\n'

	if cutoff {
		context += ' '.repeat(max_context_length + 3)
	} else {
		context += ' '.repeat(character_number)
	}
	context += '\\e[31m${'~'.repeat(error_info.length)}\\e[0m'

	return JsonDecodeError{
		context:   context
		message:   'Data: ${message}'
		line:      line_number + 1
		character: character_number + 1
	}
}

// decoder_for_text performs input validation and token allocation once in shared
// code rather than specializing the same setup for every target type.
@[manualfree]
fn decoder_for_text(val string, params DecoderOptions) !Decoder {
	if val == '' {
		return JsonDecodeError{
			message:   'empty string'
			line:      1
			character: 1
		}
	}
	mut decoder := Decoder{
		json:        val
		strict:      params.strict
		values_info: []ValueInfo{cap: value_count_bound(val)}
	}
	decoder.check_json_format() or {
		unsafe { decoder.values_info.free() }
		return err
	}
	return decoder
}

// decode decodes a JSON string into a specified type.
// By default, decoding is lenient. Use `strict: true` for strict type conversion
// and `json5: true` to accept JSON5 syntax.
// Use decode_standard to avoid generating JSON5 decoding code for the target type.
// Input nested deeper than 512 array/object levels errors; the limit applies to
// standard JSON only, the separate JSON5 parser is not covered by it.
@[manualfree]
pub fn decode[T](val string, params DecoderOptions) !T {
	if params.json5 {
		mut buffer := DecodeBuffer{}
		defer {
			unsafe { buffer.values_info.free() }
		}
		return decode_json5[T](val, mut buffer, params)
	}
	return decode_standard[T](val, params)
}

// decode_standard decodes standard JSON into a specified type without generating
// JSON5 decoding code. It is lenient by default; use `strict: true` for strict
// type conversion. The `json5` option is ignored; JSON5 syntax is always rejected.
// Input nested deeper than 512 array/object levels errors.
@[manualfree]
pub fn decode_standard[T](val string, params DecoderOptions) !T {
	mut decoder := decoder_for_text(val, params)!
	// Nothing that is decoded refers to values_info, so it is released right away.
	defer {
		unsafe {
			decoder.values_info.free()
			decoder.source_values_info.free()
		}
	}
	return decoder.decode_root[T]()
}

// decode_reuse decodes with the same options and validation as decode, reusing
// buffer's token allocation and growing it during validation instead of counting
// values in a separate pass. Returned values remain valid when buffer is reused,
// including after an error. The caller must provide exclusive access to buffer.
// Input nested deeper than 512 array/object levels errors; the limit applies to
// standard JSON only, the separate JSON5 parser is not covered by it.
@[manualfree]
pub fn decode_reuse[T](val string, mut buffer DecodeBuffer, params DecoderOptions) !T {
	if params.json5 {
		return decode_json5[T](val, mut buffer, params)
	}
	if val == '' {
		return JsonDecodeError{
			message:   'empty string'
			line:      1
			character: 1
		}
	}
	mut decoder := Decoder{
		json:        val
		strict:      params.strict
		// The decoder has exclusive access until the defer returns its storage.
		values_info: unsafe { buffer.values_info }
	}
	defer {
		buffer.values_info = unsafe { decoder.values_info }
		unsafe { decoder.source_values_info.free() }
	}
	decoder.values_info.clear()
	decoder.check_json_format()!
	return decoder.decode_root[T]()
}

fn (mut decoder Decoder) decode_root[T]() !T {
	mut result := T{}
	$if T.unaliased_typ is $array_dynamic {
		result.clear()
		decoder.decode_array(mut result)!
	} $else $if T.unaliased_typ is $map {
		decoder.decode_map(mut result)!
	} $else $if T is $pointer {
		// `&T`, `&&T` and `&&&T` point to a newly decoded value; `null` keeps `result`.
		decoder.decode_element[T](mut result)!
	} $else {
		decoder.decode_value(mut result)!
	}
	return result
}

// decode_new_pointer decodes the current value into newly allocated memory, behind
// as many pointers as `P` has (`&T`, `&&T`, `&&&&T`, ...).
fn (mut decoder Decoder) decode_new_pointer[P](_ P) !P {
	$if P.indirections == 1 {
		mut decoded_ptr := $new(P.pointee_type)
		decoder.decode_value(mut decoded_ptr)!
		return decoded_ptr
	} $else {
		return new_pointer_to(decoder.decode_new_pointer($zero(P.pointee_type))!)
	}
}

// new_pointer_to returns a new heap pointer to `value`: one more level of
// indirection, for decoding `&&T`, `&&&T`, ... values.
fn new_pointer_to[T](value T) &T {
	mut ptr := unsafe { &T(vcalloc(int(sizeof(T)))) }
	unsafe {
		*ptr = value
	}
	return ptr
}

// option_element_is_none reports whether the current array element is `none` for an
// option element type: `null`, or `{}` (how the removed module wrote a `none` element
// of a sum type's array) when the payload does not take an object, as for `?int`.
fn (mut decoder Decoder) option_element_is_none[P](element ?P) bool {
	return decoder.current_value().value_kind == .null
		|| (decoder.is_empty_object(decoder.current_idx)
			&& option_payload_fit(element, .object) == 0)
}

// decode_option_payload decodes the current value as the payload of an option.
fn (mut decoder Decoder) decode_option_payload[P](_ ?P) !P {
	$if P is $pointer {
		return decoder.decode_array_element(P{})!
	} $else {
		mut payload := P{}
		decoder.decode_value(mut payload)!
		return payload
	}
}

fn create_decoded_ptr[T](_ &T) &T {
	$if T is $interface {
		return unsafe { nil }
	} $else $if T.unaliased_typ is voidptr {
		return unsafe { nil }
	} $else {
		// Typed initialization retains struct defaults and container element metadata.
		return $new(T)
	}
}

fn decoder_field_infos[T]() []DecoderFieldInfo {
	mut field_infos := []DecoderFieldInfo{}
	$for field in T.fields {
		mut key_name := field.name
		mut is_json_skip := false
		for attr in field.attrs {
			if start, end := json_attr_value_range(attr) {
				if end <= start {
					continue
				}
				if end == start + 1 && (*attr)[start] == `-` {
					is_json_skip = true
					break
				}
				key_name = attr[start..end]
				break
			}
		}
		field_infos << DecoderFieldInfo{
			key_name:    key_name
			is_skip:     field.attrs.contains('skip') || is_json_skip
			is_required: field.attrs.contains('required')
			is_raw:      field.attrs.contains('raw')
		}
	}
	return field_infos
}

struct StructFieldInfoCache {
mut:
	field_infos []StructFieldInfo
}

@[manualfree; unsafe]
fn (mut decoder Decoder) cached_struct_field_infos[T]() &StructFieldInfoCache {
	static cache := &StructFieldInfoCache(nil)
	static initializing := u64(0)
	static initialized := u64(0)
	// Publish immutable field metadata only after its initialization completes.
	if stdatomic.load_u64(&initialized) == 0 {
		if stdatomic.fetch_add_u64(&initializing, 1) == 0 {
			cache = &StructFieldInfoCache{}
			$for field in T.fields {
				cache.field_infos << struct_field_info(field.name, field.attrs)
			}
			stdatomic.store_u64(&initialized, 1)
		} else {
			for stdatomic.load_u64(&initialized) == 0 {
				time.sleep(time.microsecond)
			}
		}
	}
	return cache
}

@[inline; markused]
fn struct_field_is_decoded(decoded_mask u64, decoded_fields []bool, field_idx int) bool {
	if field_idx < 64 {
		return (decoded_mask & (u64(1) << u64(field_idx))) != 0
	}
	return decoded_fields[field_idx]
}

@[inline; markused]
fn mark_struct_field_decoded(decoded_mask u64, mut decoded_fields []bool, field_idx int) u64 {
	if field_idx < 64 {
		return decoded_mask | (u64(1) << u64(field_idx))
	}
	decoded_fields[field_idx] = true
	return decoded_mask
}

// find_struct_field centralizes the runtime part of struct key matching. Keeping
// this loop outside the comptime field loop avoids emitting the same skip,
// length, and memory-comparison checks once for every struct field.
@[noinline]
fn (decoder &Decoder) find_struct_field(field_infos []StructFieldInfo, key_ptr voidptr, key_len int) int {
	for field_idx in 0 .. field_infos.len {
		field_info := &field_infos[field_idx]
		// `@[omitempty]` only affects encoding: an explicit empty value (`0`, `""`) is
		// decoded like any other, as in the removed `json` module.
		field_can_match := !field_info.is_skip || field_info.is_required
		field_name_matches := key_len == field_info.json_name_len && unsafe {
			vmemcmp(key_ptr, field_info.json_name_ptr, field_info.json_name_len) == 0
		}
		if field_can_match && field_name_matches {
			return field_idx
		}
	}
	return -1
}

// key_has_escape reports whether the JSON string key described by `key_info`
// contains a `\` escape sequence.
@[direct_array_access; inline]
fn (decoder &Decoder) key_has_escape(key_info ValueInfo) bool {
	for i in key_info.position + 1 .. key_info.position + key_info.length - 1 {
		if decoder.json[i] == `\\` {
			return true
		}
	}
	return false
}

@[inline]
fn (mut decoder Decoder) json_key_matches(key_info ValueInfo, key_name string) !bool {
	if decoder.key_has_escape(key_info) {
		return decoder.decode_string_value(key_info)! == key_name
	}
	if key_info.length - 2 != key_name.len {
		return false
	}
	return unsafe {
		vmemcmp(decoder.json.str + key_info.position + 1, key_name.str, key_name.len) == 0
	}
}

// skip_current_value moves past the current value, and past the values nested in it:
// those follow it in values_info, and start before its end.
@[direct_array_access; markused]
fn (decoder &Decoder) next_value(index int) int {
	if index >= decoder.values_info.len {
		return decoder.values_info.len
	}
	value := decoder.values_info[index]
	end := value.position + value.length
	mut next := index + 1
	mut limit := decoder.values_info.len
	if next == limit || decoder.values_info[next].position >= end {
		return next
	}
	for next < limit {
		middle := next + (limit - next) / 2
		if decoder.values_info[middle].position < end {
			next = middle + 1
		} else {
			limit = middle
		}
	}
	return next
}

fn (decoder &Decoder) container_len(index int) int {
	value := decoder.values_info[index]
	end := value.position + value.length
	mut next := index + 1
	mut count := 0
	for next < decoder.values_info.len {
		if decoder.values_info[next].position >= end {
			break
		}
		count++
		next = decoder.next_value(next)
	}
	return if value.value_kind == .object { count / 2 } else { count }
}

@[markused]
fn (mut decoder Decoder) skip_current_value() {
	decoder.current_idx = decoder.next_value(decoder.current_idx)
}

@[manualfree]
fn decode_struct_key[T](mut decoder Decoder, val T, key_info ValueInfo, prefix string, mut seen_required []string) !StructKeyDecodeResult[T] {
	field_infos := decoder_field_infos[T]()
	mut new_val := val
	mut i := 0
	$for field in T.fields {
		field_info := field_infos[i]
		$if !field.is_embed {
			if decoder.json_key_matches(key_info, field_info.key_name)!
				|| (prefix != ''
					&& decoder.json_key_matches(key_info, prefix + field_info.key_name)!) {
				decoder.current_idx++

				if field_info.is_skip {
					if field_info.is_required {
						seen_required << prefix + field.name
					}
					decoder.skip_current_value()
					return StructKeyDecodeResult[T]{
						matched: true
						value:   new_val
					}
				}

				if !field.attrs.contains('skip') {
					if field_info.is_required {
						seen_required << prefix + field.name
					}

					$if field.is_shared {
						decoder.reject_shared_struct_field(field_info.is_raw, field_info.is_required,
							field.name)!
					} $else $if field.typ is ?rune {
						decoder.decode_option_rune_struct_field(&new_val.$(field.name),
							field_info.is_raw)!
					} $else {
						decoder.decode_struct_field(&new_val.$(field.name), field_info.is_raw,
							field_info.is_required, field.name)!
					}
					return StructKeyDecodeResult[T]{
						matched: true
						value:   new_val
					}
				}
			}
		}
		i++
	}
	i = 0
	$for field in T.fields {
		field_info := field_infos[i]
		$if field.is_embed {
			if decoder.json_key_matches(key_info, field_info.key_name)!
				&& decoder.has_value(decoder.current_idx + 1)
				&& decoder.values_info[decoder.current_idx + 1].value_kind == .object {
				if field_info.is_required {
					seen_required << prefix + field.name
				}
				decoder.current_idx++
				decoder.decode_value(mut new_val.$(field.name))!
				return StructKeyDecodeResult[T]{
					matched: true
					value:   new_val
				}
			}
			{
				embed_result := decode_struct_key(mut decoder, new_val.$(field.name), key_info,

					prefix + field.name + '.', mut seen_required)!
				if embed_result.matched {
					new_val.$(field.name) = embed_result.value
					return StructKeyDecodeResult[T]{
						matched: true
						value:   new_val
					}
				}
			}
		}
		i++
	}
	return StructKeyDecodeResult[T]{
		matched: false
		value:   val
	}
}

fn check_required_struct_fields[T](mut decoder Decoder, val T, seen_required []string, prefix string) ! {
	field_infos := decoder_field_infos[T]()
	mut i := 0
	$for field in T.fields {
		field_info := field_infos[i]
		if field_info.is_required && prefix + field.name !in seen_required {
			decoder.decode_error('missing required field `${field.name}`')!
		}
		$if field.is_embed {
			check_required_struct_fields(mut decoder, val.$(field.name), seen_required, prefix +
				field.name + '.')!
		}
		i++
	}
}

// decode_struct_with_embeds decodes a JSON object into a struct with embedded structs,
// whose fields can also be matched through those embeds.
@[manualfree]
fn (mut decoder Decoder) decode_struct_with_embeds[T](mut val T, struct_info ValueInfo) ! {
	struct_end := struct_info.position + struct_info.length
	decoder.current_idx++
	mut seen_required := []string{}

	// json object loop
	for {
		if decoder.current_idx >= decoder.values_info.len {
			break
		}

		key_info := decoder.current_value()

		if key_info.position >= struct_end {
			break
		}

		decode_result := decode_struct_key(mut decoder, val, key_info, '', mut seen_required)!
		if decode_result.matched {
			val = decode_result.value
		} else {
			// The key doesn't match any field in the struct, skip the entire value
			// including all nested objects/arrays.
			decoder.current_idx++
			decoder.skip_current_value()
		}
	}

	check_required_struct_fields(mut decoder, val, seen_required, '')!
}

// decode_struct_fields decodes a JSON object into a struct without embedded structs.
// Everything that does not depend on the field types is done by non-generic helpers, and
// the field values by decode_struct_field, which is specialized per field type and shared
// by all structs. That keeps the code generated for each struct small.
@[manualfree]
fn (mut decoder Decoder) decode_struct_fields[T](mut val T, struct_info ValueInfo) ! {
	field_info_cache := unsafe { decoder.cached_struct_field_infos[T]() }
	mut state := struct_decode_state(field_info_cache.field_infos, struct_info.position + struct_info.length)
	decoder.current_idx++

	// json object loop
	for {
		field_idx := decoder.next_decoded_struct_field(mut state)!
		if field_idx == struct_key_object_end {
			break
		}
		field_info := field_info_cache.field_infos[field_idx]
		mut i := 0
		$for field in T.fields {
			if field.attrs.contains('skip') {
				// Handled above, without decoding (or even compiling a decoder for) its type.
			} else if i == field_idx {
				$if field.is_shared {
					decoder.reject_shared_struct_field(field_info.is_raw, field_info.is_required,
						field.name)!
				} $else $if field.typ is ?rune {
					decoder.decode_option_rune_struct_field(&val.$(field.name), field_info.is_raw)!
				} $else {
					decoder.decode_struct_field(&val.$(field.name), field_info.is_raw,
						field_info.is_required, field.name)!
				}
			}
			i++
		}
	}

	decoder.check_required_struct_fields_decoded(field_info_cache.field_infos, state.decoded_mask,
		state.decoded_fields)!
}

struct StructDecodeState {
	field_infos []StructFieldInfo // Borrowed from the immutable per-type cache.
	struct_end  int
mut:
	decoded_mask   u64
	decoded_fields []bool
}

@[manualfree]
fn struct_decode_state(field_infos []StructFieldInfo, struct_end int) StructDecodeState {
	mut state := StructDecodeState{ field_infos: field_infos, struct_end: struct_end }
	if field_infos.len > 64 {
		state.decoded_fields = []bool{len: field_infos.len}
	}
	return state
}

// next_decoded_struct_field shares key skipping and required-field tracking among
// all struct types. It returns only fields that need a typed value decoder.
@[manualfree]
fn (mut decoder Decoder) next_decoded_struct_field(mut state StructDecodeState) !int {
	for {
		field_idx := decoder.next_struct_field_idx(state.field_infos, state.struct_end)!
		if field_idx == struct_key_object_end {
			return field_idx
		}
		if field_idx == struct_key_no_field {
			continue
		}
		state.decoded_mask = mark_struct_field_decoded(state.decoded_mask, mut state.decoded_fields, field_idx)
		if state.field_infos[field_idx].is_skip {
			// Preserve the existing decode behavior for `skip`+`required`.
			decoder.current_idx++
			continue
		}
		return field_idx
	}
	return struct_key_object_end
}

const struct_key_no_field = -1
const struct_key_object_end = -2

// next_struct_field_idx reads the next key of the JSON object that ends at `struct_end`,
// and returns the index of the struct field that it names, with the decoder at the field
// value. A key that names no field is skipped with its value, and gives
// `struct_key_no_field`. The end of the object gives `struct_key_object_end`.
@[manualfree]
fn (mut decoder Decoder) next_struct_field_idx(field_infos []StructFieldInfo, struct_end int) !int {
	if decoder.current_idx >= decoder.values_info.len {
		return struct_key_object_end
	}

	key_info := decoder.current_value()

	if key_info.position >= struct_end {
		return struct_key_object_end
	}

	mut key_ptr := unsafe { voidptr(decoder.json.str + key_info.position + 1) }
	mut key_len := key_info.length - 2
	mut unescaped_key := ''
	if decoder.key_has_escape(key_info) {
		unescaped_key = decoder.decode_string_value(key_info)!
		key_ptr = voidptr(unescaped_key.str)
		key_len = unescaped_key.len
	}

	field_idx := decoder.find_struct_field(field_infos, key_ptr, key_len)
	// value node
	decoder.current_idx++
	if field_idx < 0 {
		// The key doesn't match any field in the struct, skip the entire value
		// including all nested objects/arrays.
		decoder.skip_current_value()
		return struct_key_no_field
	}
	return field_idx
}

// check_required_struct_fields_decoded reports a required field that got no value.
fn (mut decoder Decoder) check_required_struct_fields_decoded(field_infos []StructFieldInfo, decoded_mask u64, decoded_fields []bool) ! {
	for field_idx in 0 .. field_infos.len {
		field_info := &field_infos[field_idx]
		if field_info.is_required
			&& !struct_field_is_decoded(decoded_mask, decoded_fields, field_idx) {
			decoder.decode_error('missing required field `${field_info.name}`')!
		}
	}
}

// decode_struct_field decodes the current JSON value into the struct field at `field`.
// `field_name` is the V name of the field, for error messages.
@[manualfree]
fn (mut decoder Decoder) decode_struct_field[F](field &F, is_raw bool, is_required bool, field_name string) ! {
	if is_raw {
		$if F.unaliased_typ is $enum {
			decoder.decode_error('`raw` attribute cannot be used with enum fields')!
		} $else $if F is ?string || F is string {
			unsafe {
				*field = decoder.decode_raw_value()
			}
		} $else {
			decoder.decode_error('`raw` attribute can only be used with string fields')!
		}
		return
	}
	value_kind := decoder.current_value().value_kind
	$if F is $option {
		if value_kind == .null {
			unsafe {
				*field = none
			}
			decoder.current_idx++
		} else {
			unsafe {
				*field = decoder.decode_option_payload(F(none))!
			}
		}
	} $else {
		if value_kind == .null {
			// A `@[required]` field needs a value: `null` is rejected like in the removed
			// `json` module, before the value decoders treat it leniently.
			if is_required {
				decoder.decode_error('required field `${field_name}` cannot be null')!
			}
			$if F.unaliased_typ is $array_dynamic || F.unaliased_typ is $map {
				mut target := unsafe { field }
				target.clear()
				decoder.skip_current_value()
				return
			} $else $if F.unaliased_typ is string {
				unsafe {
					*field = F('')
				}
				decoder.skip_current_value()
				return
			} $else $if F.indirections != 0 {
				unsafe {
					*field = nil
				}
				decoder.current_idx++
				return
			}
		}
		$if F.indirections == 1 {
			mut decoded_ptr := create_decoded_ptr(*field)
			decoder.decode_value(mut decoded_ptr)!
			unsafe {
				*field = decoded_ptr
			}
		} $else $if F.indirections > 1 {
			unsafe {
				*field = decoder.decode_new_pointer(*field)!
			}
		} $else {
			mut target := unsafe { field }
			decoder.decode_value(mut target)!
		}
	}
}

// decode_option_rune_struct_field decodes a `?rune` struct field. The payload of a generic
// `?rune` does not keep its `rune` type, so decode_struct_field would decode a number.
fn (mut decoder Decoder) decode_option_rune_struct_field(field &?rune, is_raw bool) ! {
	if is_raw {
		decoder.decode_error('`raw` attribute can only be used with string fields')!
	}
	if decoder.current_value().value_kind == .null {
		unsafe {
			*field = none
		}
		decoder.current_idx++
		return
	}
	mut unwrapped_rune := rune(0)
	decoder.decode_value(mut unwrapped_rune)!
	unsafe {
		*field = unwrapped_rune
	}
}

// reject_shared_struct_field reports the error for a value of a `shared` struct field,
// which cannot be decoded.
fn (mut decoder Decoder) reject_shared_struct_field(is_raw bool, is_required bool, field_name string) ! {
	if is_raw {
		decoder.decode_error('`raw` attribute can only be used with string fields')!
	}
	if is_required && decoder.current_value().value_kind == .null {
		decoder.decode_error('required field `${field_name}` cannot be null')!
	}
	decoder.decode_error('shared fields cannot be decoded')!
}

// decode_raw_value returns the JSON text of the current value, for a `@[raw]` field, and
// moves past it.
fn (mut decoder Decoder) decode_raw_value() string {
	value_info := decoder.current_value()
	raw := decoder.retained_text(value_info.position, value_info.position + value_info.length)
	decoder.skip_current_value()
	return raw
}

// retained_text copies text that may outlive the decoder's input buffer.
@[manualfree]
fn (decoder &Decoder) retained_text(start int, end int) string {
	return decoder.json.substr(start, end)
}

// decode_value decodes a value from the JSON nodes.
@[manualfree]
fn (mut decoder Decoder) decode_value[T](mut val T) ! {
	$if T.unaliased_typ is voidptr {
		// skip voidptr fields - they cannot be decoded from JSON
		decoder.current_idx++
		return
	} $else $if T is $interface {
		// skip interface fields - they cannot be decoded from JSON
		decoder.current_idx++
		return
	} $else $if T is $option {
		// An option element of an array or map (`[]?int`): `null` is `none`, anything
		// else is the payload, like in the removed `json` module.
		if decoder.current_value().value_kind == .null {
			val = none
			decoder.current_idx++
			return
		}
		val = decoder.decode_option_payload(val)!
		return
	} $else {
		// Custom Decoders
		$if val is ValueDecoder {
			mut value := Any(null)
			decoder.decode_sumtype(mut value)!
			val.from_json(value)
			return
		}
		$if val is StringDecoder {
			struct_info := decoder.current_value()

			if struct_info.value_kind == .string {
				val.from_json_string(decoder.retained_text(struct_info.position + 1, struct_info.position +
					struct_info.length - 1)) or {
					decoder.decode_error('${typeof(val).name}: ${err.msg()}')!
				}
				decoder.current_idx++

				return
			}
		}
		$if val is NumberDecoder {
			struct_info := decoder.current_value()

			if struct_info.value_kind == .number {
				val.from_json_number(decoder.retained_text(struct_info.position, struct_info.position +
					struct_info.length)) or {
					decoder.decode_error('${typeof(val).name}: ${err.msg()}')!
				}
				decoder.current_idx++

				return
			}
		}
		$if val is BooleanDecoder {
			struct_info := decoder.current_value()

			if struct_info.value_kind == .boolean {
				val.from_json_boolean(decoder.json[struct_info.position] == `t`)
				decoder.current_idx++

				return
			}
		}
		$if val is NullDecoder {
			struct_info := decoder.current_value()

			if struct_info.value_kind == .null {
				val.from_json_null()
				decoder.current_idx++

				return
			}
		}
		$if T.unaliased_typ is $int || T.unaliased_typ is $float || T.unaliased_typ is bool || T.unaliased_typ is string || T.unaliased_typ is $enum {
			if decoder.current_value().value_kind == .null && !decoder.strict {
				// Outside of strict mode, `null` is the zero value, like in the removed
				// `json` module: `[1, null]` decodes into `[]int` as `[1, 0]`.
				val = $zero(T)
				decoder.current_idx++
				return
			}
		}
		$if T.unaliased_typ is voidptr {
			// skip voidptr fields - they cannot be decoded from JSON
			decoder.current_idx++
			return
		} $else $if T.unaliased_typ is string {
			value_info := decoder.current_value()
			if (value_info.value_kind == .object || value_info.value_kind == .array)
				&& decoder.current_idx != 0 {
				// Like a string field (and the removed `json` module), an object or array
				// decodes into a string as its JSON text, also as an element or map value.
				// The removed module had no string root, which stays an error.
				val = T(decoder.retained_text(value_info.position, value_info.position + value_info.length))
				decoder.skip_current_value()
				return
			}
			decoder.decode_string(mut val)!
		} $else $if T.unaliased_typ is time.Time {
			value_info := decoder.current_value()
			mut decoded_time := time.Time{}
			if value_info.value_kind == .string {
				decoded_time.from_json_string(decoder.json[value_info.position + 1..value_info.position + value_info.length - 1]) or {
					decoder.decode_error('${typeof(val).name}: ${err.msg()}')!
				}
			} else if value_info.value_kind == .number {
				decoded_time.from_json_number(decoder.json[value_info.position..value_info.position + value_info.length]) or {
					decoder.decode_error('${typeof(val).name}: ${err.msg()}')!
				}
			} else if value_info.value_kind == .null && !decoder.strict {
				// Outside of strict mode `null` is the zero time, like in the removed module.
			} else if value_info.value_kind == .object {
				// The removed module wrote a time in a sum type, also as an element of an
				// array variant or an option payload, as `{"_type":"Time","value":...}`.
				// Only that wrapper is a time: other objects are rejected, like before.
				type_field_idx := decoder.get_sumtype_type_field_idx(decoder.current_idx)
				if !decoder.sumtype_type_field_matches(type_field_idx, 'Time')
					&& !decoder.sumtype_type_field_matches(type_field_idx, sumtype_variant_name(T.name)) {
					decoder.decode_error('Expected string, number or `Time` object, but got another object')!
				}
				decoder.decode_sumtype_time(mut decoded_time)!
				val = T(decoded_time)
				return
			} else {
				decoder.decode_error('Expected string or number, but got ${value_info.value_kind}')!
			}
			val = T(decoded_time)
		} $else $if T.unaliased_typ is $sumtype {
			decoder.decode_sumtype(mut val)!
			return
		} $else $if T.unaliased_typ is $map {
			decoder.decode_map(mut val)!
			return
		} $else $if T.unaliased_typ is $array_dynamic {
			val.clear()
			decoder.decode_array(mut val)!
			// return to avoid the next increment of the current node
			// this is because the current node is already incremented in the decode_array function
			// remove this line will cause the current node to be incremented twice
			// and bug recursive array decoding like `[][]int{}`
			return
		} $else $if T.unaliased_typ is $array_fixed {
			decoder.decode_fixed_array(mut val)!
			return
		} $else $if T.unaliased_typ is $struct {
			struct_info := decoder.current_value()

			if struct_info.value_kind == .object {
				// Only a struct with an embedded struct needs its keys matched through its
				// embeds too. Deciding that at compile time keeps the larger embed path out
				// of the code generated for every other struct.
				$for field in T.fields {
					$if field.is_embed {
						decoder.decode_struct_with_embeds(mut val, struct_info)!
						return
					}
				}
				decoder.decode_struct_fields(mut val, struct_info)!
			} else if struct_info.value_kind == .null && !decoder.strict
				&& decoder.current_idx != 0 {
				// Outside of strict mode a nested `null` is the struct with its default
				// field values, like in the removed `json` module, which rejected a
				// `null` root.
				val = T{}
				decoder.current_idx++
			} else {
				decoder.decode_error('Expected object, but got ${struct_info.value_kind}')!
			}
			return
		} $else $if T.unaliased_typ is bool {
			value_info := decoder.current_value()

			if value_info.value_kind != .boolean {
				decoder.decode_error('Expected boolean, but got ${value_info.value_kind}')!
			}

			unsafe {
				val = vmemcmp(decoder.json.str + value_info.position, c'true', 'true'.len) == 0
			}
		} $else $if T.unaliased_typ is rune {
			// Like the removed `json` module, a rune is decoded from a JSON string (its
			// first character); a number is taken as the code point.
			value_info := decoder.current_value()
			if value_info.value_kind == .string {
				decoded := decoder.decode_string_value(value_info)!
				val = if decoded.len > 0 { T(decoded.runes()[0]) } else { T(0) }
			} else if value_info.value_kind == .number {
				mut code_point := u32(0)
				unsafe { decoder.decode_number(&code_point)! }
				val = T(code_point)
			} else {
				decoder.decode_error('Expected string, but got ${value_info.value_kind}')!
			}
		} $else $if T.unaliased_typ is $float || T.unaliased_typ is $int {
			value_info := decoder.current_value()

			if value_info.value_kind == .number {
				unsafe { decoder.decode_number(&val)! }
			} else if value_info.value_kind == .string && !decoder.strict {
				// In default mode, try to parse quoted strings as numbers
				val = decoder.decode_number_from_string[T]()!
			} else {
				decoder.decode_error('Expected number, but got ${value_info.value_kind}')!
			}
		} $else $if T.unaliased_typ is $enum {
			decoder.decode_enum(mut val)!
		} $else {
			decoder.decode_error('cannot decode value with ${typeof(val).name} type')!
		}

		decoder.current_idx++
	} // $else (not voidptr / interface)
}

fn (mut decoder Decoder) decode_string[T](mut val T) ! {
	_ = val
	string_info := decoder.current_value()

	if string_info.value_kind == .string {
		val = decoder.decode_string_value(string_info)!
	} else {
		decoder.decode_error('Expected string, but got ${string_info.value_kind}')!
	}
}

// decode_string_value returns the unescaped content of the JSON string described by `string_info`.
@[manualfree]
fn (mut decoder Decoder) decode_string_value(string_info ValueInfo) !string {
	string_start := string_info.position + 1
	string_end := string_info.position + string_info.length - 1
	if !string_info.has_escape {
		return decoder.retained_text(string_start, string_end)
	}

	mut string_buffer := []u8{cap: string_info.length} // might be too long but most json strings don't contain many escape characters anyways
	defer {
		unsafe { string_buffer.free() }
	}

	mut buffer_index := 1
	mut string_index := 1

	for string_index < string_info.length - 1 {
		current_byte := decoder.json[string_info.position + string_index]

		if current_byte == `\\` {
			// push all characters up to this point
			unsafe {
				string_buffer.push_many(decoder.json.str + string_info.position + buffer_index,
					string_index - buffer_index)
			}

			string_index++

			escaped_char := decoder.json[string_info.position + string_index]

			string_index++

			match escaped_char {
				`/`, `"`, `\\` {
					string_buffer << escaped_char
				}
				`b` {
					string_buffer << `\b`
				}
				`f` {
					string_buffer << `\f`
				}
				`n` {
					string_buffer << `\n`
				}
				`r` {
					string_buffer << `\r`
				}
				`t` {
					string_buffer << `\t`
				}
				`u` {
					unicode_point := rune(strconv.parse_uint(decoder.json[string_info.position +
						string_index..string_info.position + string_index + 4], 16, 32)!)

					string_index += 4

					if unicode_point < 0xD800 || unicode_point > 0xDFFF { // normal utf-8
						string_buffer << unicode_point.bytes()
					} else if unicode_point >= 0xDC00 { // trail surrogate -> invalid
						decoder.decode_error('Got trail surrogate: ${u32(unicode_point):04X} before head surrogate.')!
					} else { // head surrogate -> treat as utf-16
						if string_index > string_info.length - 6 {
							decoder.decode_error('Expected a trail surrogate after a head surrogate, but got no valid escape sequence.')!
						}
						if decoder.json[string_info.position + string_index..string_info.position +
							string_index + 2] != '\\u' {
							decoder.decode_error('Expected a trail surrogate after a head surrogate, but got no valid escape sequence.')!
						}

						string_index += 2

						unicode_point2 := rune(strconv.parse_uint(decoder.json[string_info.position + string_index..string_info.position +
							string_index + 4], 16, 32)!)

						string_index += 4

						if unicode_point2 < 0xDC00 {
							decoder.decode_error('Expected a trail surrogate after a head surrogate, but got ${u32(unicode_point):04X}.')!
						}

						final_unicode_point := (unicode_point2 & 0x3FF) +
							((unicode_point & 0x3FF) << 10) + 0x10000
						string_buffer << final_unicode_point.bytes()
					}
				}
				else {}
			}

			// has already been checked

			buffer_index = string_index
		} else {
			string_index++
		}
	}

	// push the rest
	unsafe {
		string_buffer.push_many(decoder.json.str + string_info.position + buffer_index,
			string_index - buffer_index)
	}

	return string_buffer.bytestr()
}

fn (mut decoder Decoder) decode_array[T](mut val []T) ! {
	$if T is $interface || T.unaliased_typ is voidptr {
		decoder.skip_current_value()
		return
	}
	array_info := decoder.values_info[decoder.current_idx]
	if array_info.value_kind == .null && !decoder.strict {
		decoder.current_idx++
		return
	}
	if array_info.value_kind != .array {
		return decoder.decode_error('Expected array, but got ${array_info.value_kind}')
	}
	capacity := decoder.container_len(decoder.current_idx)
	mut decoded := []T{cap: capacity}
	decoder.current_idx++

	array_end := array_info.position + array_info.length

	for {
		if decoder.current_idx >= decoder.values_info.len
			|| decoder.values_info[decoder.current_idx].position >= array_end {
			break
		}

		$if T is $option {
			// An option element (`[]?int`): `null` is `none`. Decoded directly,
			// since v3 cannot return `!?T` from the element helper.
			if decoder.option_element_is_none(T(none)) {
				decoded << T(none)
				decoder.skip_current_value()
			} else {
				decoded << decoder.decode_option_payload(T(none))!
			}
		} $else {
			mut element := T{}
			decoder.decode_element[T](mut element)!
			decoded << element
		}
	}
	val = decoded
}

fn (mut decoder Decoder) decode_element[E](mut element E) ! {
	$if E is $interface {
		decoder.skip_current_value()
	} $else $if E.unaliased_typ is voidptr {
		decoder.skip_current_value()
	} $else $if E.indirections == 1 {
		if decoder.values_info[decoder.current_idx].value_kind == .null {
			decoder.current_idx++
		} else {
			mut decoded_ptr := create_decoded_ptr(element)
			decoder.decode_value(mut decoded_ptr)!
			element = decoded_ptr
		}
	} $else $if E.indirections > 1 {
		// `&&T`, `&&&T`, ... elements point to a newly decoded value, like fields.
		if decoder.values_info[decoder.current_idx].value_kind == .null {
			decoder.current_idx++
		} else {
			element = decoder.decode_new_pointer(element)!
		}
	} $else {
		decoder.decode_value(mut element)!
	}
}

// decode_fixed_array decodes a JSON array into the elements of a fixed size array.
// Outside of strict mode it behaves like the removed `json` module: `null` keeps the
// default elements, a shorter array only replaces the leading elements, and extra
// elements are skipped.
fn (mut decoder Decoder) decode_fixed_array[T](mut val T) ! {
	array_info := decoder.current_value()
	if array_info.value_kind == .null && !decoder.strict {
		decoder.current_idx++
		return
	}
	if array_info.value_kind != .array {
		decoder.decode_error('Expected array, but got ${array_info.value_kind}')!
	}
	decoder.current_idx++
	array_end := array_info.position + array_info.length
	mut idx := 0
	for decoder.current_idx < decoder.values_info.len
		&& decoder.current_value().position < array_end {
		if idx < val.len {
			decoder.decode_fixed_array_element(&val[idx])!
		} else {
			decoder.skip_current_value()
		}
		idx++
	}
	if decoder.strict && idx != val.len {
		decoder.decode_error('Fixed size array expected ${val.len} elements but got ${idx} elements')!
	}
}

// decode_fixed_array_element decodes the current JSON value into a fixed size array
// element. Elements are decoded in place, since v3 cannot yet assign a fixed array
// element that is itself a fixed array through a `mut` parameter; only a pointer
// element is replaced.
fn (mut decoder Decoder) decode_fixed_array_element[E](element &E) ! {
	$if E is $option {
		if decoder.option_element_is_none(E(none)) {
			unsafe {
				*element = E(none)
			}
			decoder.skip_current_value()
		} else {
			payload := decoder.decode_option_payload(E(none))!
			unsafe {
				*element = payload
			}
		}
	} $else $if E is $pointer {
		unsafe {
			*element = decoder.decode_array_element(*element)!
		}
	} $else {
		mut target := unsafe { element }
		decoder.decode_value(mut target)!
	}
}

// decode_array_element decodes the current JSON value as an array element that
// starts out as `initial`. It takes and returns the element by value: for a `mut`
// parameter, a pointer element type would be inferred without its `&`.
fn (mut decoder Decoder) decode_array_element[E](initial E) !E {
	mut element := initial
	$if E is $interface {
		decoder.skip_current_value()
	} $else $if E.unaliased_typ is voidptr {
		decoder.skip_current_value()
	} $else $if E.indirections == 1 {
		if decoder.current_value().value_kind == .null {
			decoder.current_idx++
		} else {
			mut decoded_ptr := create_decoded_ptr(element)
			decoder.decode_value(mut decoded_ptr)!
			element = decoded_ptr
		}
	} $else $if E.indirections > 1 {
		// `&&T`, `&&&T`, ... elements point to a newly decoded value, like fields.
		if decoder.current_value().value_kind == .null {
			decoder.current_idx++
		} else {
			element = decoder.decode_new_pointer(element)!
		}
	} $else {
		decoder.decode_value(mut element)!
	}
	return element
}

// decode_enum_map_key accepts member names and the map encoder's flag-enum syntax.
// JSON attributes only rename scalar enum values, not map keys.
fn (mut decoder Decoder) decode_enum_map_key[K](key_str string) !K {
	$for member in K.values {
		if key_str == member.name {
			return K(member.value)
		}
	}
	zero := unsafe { K(0) }
	zero_str := '${zero}'
	// Flag enums, including aliases, stringify zero as `Enum{}`. Use the same
	// type prefix as the map encoder instead of assuming the alias's name.
	if zero_str.ends_with('{}') {
		prefix := zero_str[..zero_str.len - 1]
		if key_str.starts_with(prefix) && key_str.ends_with('}') {
			flags := key_str[prefix.len..key_str.len - 1].trim_space()
			if flags == '' {
				return zero
			}
			mut bits := u64(0)
			mut valid := true
			for flag in flags.split('|') {
				name := flag.trim_space()
				mut matched := false
				$for member in K.values {
					if name == '.${member.name}' {
						bits |= u64(member.value)
						matched = true
					}
				}
				if !matched {
					valid = false
					break
				}
			}
			if valid {
				return unsafe { K(bits) }
			}
		}
	}
	decoder.decode_error('String map key: `${key_str}` does not match any field in enum: ${K.name}')!
	return zero
}

fn (mut decoder Decoder) decode_map_key[K](info ValueInfo) !K {
	text := if decoder.key_has_escape(info) {
		decoder.decode_string_value(info)!
	} else {
		decoder.json.substr_unsafe(info.position + 1, info.position + info.length - 1)
	}
	$if K.unaliased_typ is string {
		return K(text)
	} $else $if K.unaliased_typ is $enum {
		return decoder.decode_enum_map_key[K](text)!
	} $else $if K.unaliased_typ is rune {
		mut runes := text.runes_iterator()
		first := runes.next() or { return error('expected one rune for JSON key') }
		if _ := runes.next() {
			return error('expected one rune for JSON key')
		}
		return K(first)
	} $else $if K.unaliased_typ is $int {
		return parse_integer_number[K](text)!
	} $else {
		return error('unsupported JSON map key type ${K.name}')
	}
}

fn (mut decoder Decoder) decode_map[K, V](mut val map[K]V) ! {
	$if V is $interface || V.unaliased_typ is voidptr {
		decoder.skip_current_value()
		return
	}
	map_info := decoder.values_info[decoder.current_idx]
	if map_info.value_kind == .null && !decoder.strict {
		val.clear()
		decoder.current_idx++
		return
	}
	if map_info.value_kind != .object {
		return decoder.decode_error('Expected object, but got ${map_info.value_kind}')
	}
	map_end := map_info.position + map_info.length
	capacity := val.len + decoder.container_len(decoder.current_idx)
	val.reserve(u32(capacity))
	decoder.current_idx++
	for decoder.current_idx < decoder.values_info.len {
		key_info := decoder.values_info[decoder.current_idx]
		if key_info.position >= map_end {
			break
		}
		key := decoder.decode_map_key[K](key_info)!
		decoder.current_idx++
		$if V is $option {
			if decoder.values_info[decoder.current_idx].value_kind == .null {
				val[key] = V(none)
				decoder.current_idx++
			} else {
				val[key] = decoder.decode_option_payload(V(none))!
			}
			continue
		}
		mut map_value := V{}
		decoder.decode_element[V](mut map_value)!
		$if V.unaliased_typ is $map {
			val[key] = map_value.move()
		} $else {
			val[key] = map_value
		}
	}
}

fn (mut decoder Decoder) decode_enum[T](mut val T) ! {
	enum_info := decoder.current_value()

	if enum_info.value_kind == .number {
		$if T.unaliased_typ is $enum {
			if enum_uses_json_as_number[T]() {
				// Like the removed `json` module, a `@[json_as_number]` enum takes the
				// number as its backing value, declared or not (`Status(99)`). A
				// non-negative number is read as `u64`, so a `u64` backing value above
				// `max_i64` fits; a negative one as `i64`.
				if decoder.json[enum_info.position] == `-` {
					mut backing := i64(0)
					unsafe { decoder.decode_number(&backing)! }
					val = unsafe { T(backing) }
				} else {
					mut backing := u64(0)
					unsafe { decoder.decode_number(&backing)! }
					val = unsafe { T(backing) }
				}
				return
			}
		}
		mut result := 0
		unsafe { decoder.decode_number(&result)! }

		$for value in T.values {
			if int(value.value) == result {
				val = value.value
				return
			}
		}
		decoder.decode_error('Number value: `${result}` does not match any field in enum: ${typeof(val).name}')!
	} else if enum_info.value_kind == .string {
		mut result := ''
		decoder.decode_string(mut result)!

		$for value in T.values {
			for attr in value.attrs {
				if json_attr := json_attr_value(attr) {
					if json_attr == result {
						val = value.value
						return
					}
				}
			}
			if value.name == result {
				val = value.value
				return
			}
		}
		decoder.decode_error('String value: `${result}` does not match any field in enum: ${typeof(val).name}')!
	}

	decoder.decode_error('Expected number or string value for enum, got: ${enum_info.value_kind}')!
}

const max_integer_number_digits = 20

@[markused]
fn has_exponent_number_syntax(str string) bool {
	for c in str {
		if c == `e` || c == `E` {
			return true
		}
	}
	return false
}

@[markused]
fn scientific_number_to_integer_string(str string) !string {
	if !has_exponent_number_syntax(str) {
		// Handle plain decimal numbers with zero fractional part (e.g., "-123.0")
		dot_pos := str.index_u8(`.`)
		if dot_pos >= 0 {
			frac := str[dot_pos + 1..]
			mut all_zeros := frac.len > 0
			for c in frac {
				if c != `0` {
					all_zeros = false
					break
				}
			}
			if all_zeros {
				result := str[..dot_pos]
				if result.len == 0 || result == '-' || result == '+' {
					return '0'
				}
				return result
			}
		}
		return str
	}
	if str.len == 0 {
		return error('invalid scientific notation number')
	}
	mut i := 0
	mut is_negative := false
	if str[i] == `+` || str[i] == `-` {
		is_negative = str[i] == `-`
		i++
	}
	if i >= str.len {
		return error('invalid scientific notation number')
	}
	mut digits := []u8{cap: str.len}
	mut fractional_digits := 0
	mut seen_digit := false
	mut seen_dot := false
	for i < str.len {
		c := str[i]
		if c >= `0` && c <= `9` {
			digits << c
			seen_digit = true
			if seen_dot {
				fractional_digits++
			}
			i++
			continue
		}
		if c == `.` && !seen_dot {
			seen_dot = true
			i++
			continue
		}
		break
	}
	if !seen_digit || i >= str.len || (str[i] != `e` && str[i] != `E`) {
		return error('invalid scientific notation number')
	}
	i++
	mut exponent_sign := 1
	if i < str.len && (str[i] == `+` || str[i] == `-`) {
		if str[i] == `-` {
			exponent_sign = -1
		}
		i++
	}
	if i >= str.len || str[i] < `0` || str[i] > `9` {
		return error('invalid scientific notation number')
	}
	mut exponent := 0
	exponent_cap := max_integer_number_digits + fractional_digits + 1
	for i < str.len && str[i] >= `0` && str[i] <= `9` {
		if exponent < exponent_cap {
			exponent = (exponent * 10) + int(str[i] - `0`)
		}
		i++
	}
	if i != str.len {
		return error('invalid scientific notation number')
	}
	if exponent_sign == -1 {
		exponent = -exponent
	}
	mut first_non_zero := 0
	for first_non_zero < digits.len && digits[first_non_zero] == `0` {
		first_non_zero++
	}
	if first_non_zero == digits.len {
		return '0'
	}
	digits = digits[first_non_zero..].clone()
	scale := exponent - fractional_digits
	if scale < 0 {
		truncated_digits := -scale
		if truncated_digits >= digits.len {
			return '0'
		}
		digits = digits[..digits.len - truncated_digits].clone()
	} else if scale > 0 {
		final_len := digits.len + scale
		if final_len > max_integer_number_digits {
			return error('number `${str}` exceeds 64-bit integer range')
		}
		mut out := []u8{cap: final_len + if is_negative { 1 } else { 0 }}
		if is_negative {
			out << `-`
		}
		out << digits
		for _ in 0 .. scale {
			out << `0`
		}
		return out.bytestr()
	}
	if digits.len == 0 {
		return '0'
	}
	mut out := []u8{cap: digits.len + if is_negative { 1 } else { 0 }}
	if is_negative {
		out << `-`
	}
	out << digits
	return out.bytestr()
}

fn parse_integer_number[T](str string) !T {
	int_str := scientific_number_to_integer_string(str)!
	$if T.unaliased_typ is i8 {
		return T(strconv.atoi8(int_str)!)
	} $else $if T.unaliased_typ is i16 {
		return T(strconv.atoi16(int_str)!)
	} $else $if T.unaliased_typ is i32 {
		return T(strconv.atoi32(int_str)!)
	} $else $if T.unaliased_typ is i64 {
		return T(strconv.atoi64(int_str)!)
	} $else $if T.unaliased_typ is u8 {
		return T(strconv.atou8(int_str)!)
	} $else $if T.unaliased_typ is u16 {
		return T(strconv.atou16(int_str)!)
	} $else $if T.unaliased_typ is u32 {
		return T(strconv.atou32(int_str)!)
	} $else $if T.unaliased_typ is u64 {
		return T(strconv.atou64(int_str)!)
	} $else $if T is int {
		return int(strconv.atoi64(int_str)!)
	} $else $if T.unaliased_typ is int || T.unaliased_typ is isize {
		return T(int(strconv.atoi64(int_str)!))
	} $else $if T.unaliased_typ is uint {
		return T(uint(strconv.atou64(int_str)!))
	} $else {
		return error('`parse_integer_number` cannot decode ${T.name} type')
	}
}

@[markused]
fn parse_int_number(str string) !int {
	int_str := scientific_number_to_integer_string(str)!
	return int(strconv.atoi64(int_str)!)
}

fn parse_float_number[T](str string) !T {
	value := $if js {
		strconv.atof64(str)!
	} $else {
		strconv.atof64(str, allow_extra_chars: false)!
	}
	$if T.unaliased_typ is f32 {
		narrowed := f32(value)
		// Test the representation so finite-math optimizations cannot erase the guard.
		if math.f32_bits(narrowed) & u32(0x7fffffff) == u32(0x7f800000) {
			return error('number `${str}` exceeds ${T.name} range')
		}
		return T(narrowed)
	} $else $if T.unaliased_typ is f64 {
		if math.f64_bits(value) & u64(0x7fffffffffffffff) == u64(0x7ff0000000000000) {
			return error('number `${str}` exceeds ${T.name} range')
		}
		return T(value)
	} $else {
		return error('`parse_float_number` cannot decode ${T.name} type')
	}
}

// use pointer instead of mut so enum cast works
@[manualfree; unsafe]
fn (mut decoder Decoder) decode_number[T](val &T) ! {
	_ = val
	number_info := decoder.current_value()
	raw := decoder.json.substr_unsafe(number_info.position, number_info.position + number_info.length)
	$if T.unaliased_typ is $float {
		if decoder.json5 {
			*val = T(parse_json5_float(raw)!)
			return
		}
	}
	str := if decoder.json5 { normalize_json5_number(raw)! } else { raw }
	defer {
		if decoder.json5 {
			unsafe { str.free() }
		}
	}
	$match T.unaliased_typ {
		i8 { *val = parse_integer_number[T](str)! }
		i16 { *val = parse_integer_number[T](str)! }
		i32 { *val = parse_integer_number[T](str)! }
		i64 { *val = parse_integer_number[T](str)! }
		u8 { *val = parse_integer_number[T](str)! }
		u16 { *val = parse_integer_number[T](str)! }
		u32 { *val = parse_integer_number[T](str)! }
		u64 { *val = parse_integer_number[T](str)! }
		int { *val = parse_int_number(str)! }
		isize { *val = parse_integer_number[T](str)! }
		uint { *val = parse_integer_number[T](str)! }
		f32 { *val = parse_float_number[T](str)! }
		f64 { *val = parse_float_number[T](str)! }
		$else { return error('`decode_number` can not decode ${T.name} type') }
	}
}

// decode_number_from_string parses a number from a JSON string value (default mode).
// This extracts the content between quotes and parses it as a number.
@[manualfree]
fn (mut decoder Decoder) decode_number_from_string[T]() !T {
	string_info := decoder.current_value()
	// Extract string content without quotes (position+1 to skip opening quote, length-2 to exclude both quotes)
	if string_info.length < 2 {
		return error('invalid string for number conversion')
	}
	str := decoder.json.substr_unsafe(string_info.position + 1, string_info.position + string_info.length - 1)
	$if T.unaliased_typ is i8 {
		return parse_integer_number[T](str)!
	} $else $if T.unaliased_typ is i16 {
		return parse_integer_number[T](str)!
	} $else $if T.unaliased_typ is i32 {
		return parse_integer_number[T](str)!
	} $else $if T.unaliased_typ is i64 {
		return parse_integer_number[T](str)!
	} $else $if T.unaliased_typ is u8 {
		return parse_integer_number[T](str)!
	} $else $if T.unaliased_typ is u16 {
		return parse_integer_number[T](str)!
	} $else $if T.unaliased_typ is u32 {
		return parse_integer_number[T](str)!
	} $else $if T.unaliased_typ is u64 {
		return parse_integer_number[T](str)!
	} $else $if T is int {
		return parse_int_number(str)!
	} $else $if T.unaliased_typ is int || T.unaliased_typ is isize {
		return parse_integer_number[T](str)!
	} $else $if T.unaliased_typ is uint {
		return parse_integer_number[T](str)!
	} $else $if T.unaliased_typ is f32 {
		return parse_float_number[T](str)!
	} $else $if T.unaliased_typ is f64 {
		return parse_float_number[T](str)!
	} $else {
		return error('`decode_number_from_string` cannot decode ${T.name} type')
	}
}