mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 16:32:26 -05:00 
			
		
		
		
	
		
			
	
	
		
			816 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			816 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | package yaml | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"encoding" | ||
|  | 	"encoding/base64" | ||
|  | 	"fmt" | ||
|  | 	"io" | ||
|  | 	"math" | ||
|  | 	"reflect" | ||
|  | 	"strconv" | ||
|  | 	"time" | ||
|  | ) | ||
|  | 
 | ||
|  | const ( | ||
|  | 	documentNode = 1 << iota | ||
|  | 	mappingNode | ||
|  | 	sequenceNode | ||
|  | 	scalarNode | ||
|  | 	aliasNode | ||
|  | ) | ||
|  | 
 | ||
|  | type node struct { | ||
|  | 	kind         int | ||
|  | 	line, column int | ||
|  | 	tag          string | ||
|  | 	// For an alias node, alias holds the resolved alias. | ||
|  | 	alias    *node | ||
|  | 	value    string | ||
|  | 	implicit bool | ||
|  | 	children []*node | ||
|  | 	anchors  map[string]*node | ||
|  | } | ||
|  | 
 | ||
|  | // ---------------------------------------------------------------------------- | ||
|  | // Parser, produces a node tree out of a libyaml event stream. | ||
|  | 
 | ||
|  | type parser struct { | ||
|  | 	parser   yaml_parser_t | ||
|  | 	event    yaml_event_t | ||
|  | 	doc      *node | ||
|  | 	doneInit bool | ||
|  | } | ||
|  | 
 | ||
|  | func newParser(b []byte) *parser { | ||
|  | 	p := parser{} | ||
|  | 	if !yaml_parser_initialize(&p.parser) { | ||
|  | 		panic("failed to initialize YAML emitter") | ||
|  | 	} | ||
|  | 	if len(b) == 0 { | ||
|  | 		b = []byte{'\n'} | ||
|  | 	} | ||
|  | 	yaml_parser_set_input_string(&p.parser, b) | ||
|  | 	return &p | ||
|  | } | ||
|  | 
 | ||
|  | func newParserFromReader(r io.Reader) *parser { | ||
|  | 	p := parser{} | ||
|  | 	if !yaml_parser_initialize(&p.parser) { | ||
|  | 		panic("failed to initialize YAML emitter") | ||
|  | 	} | ||
|  | 	yaml_parser_set_input_reader(&p.parser, r) | ||
|  | 	return &p | ||
|  | } | ||
|  | 
 | ||
|  | func (p *parser) init() { | ||
|  | 	if p.doneInit { | ||
|  | 		return | ||
|  | 	} | ||
|  | 	p.expect(yaml_STREAM_START_EVENT) | ||
|  | 	p.doneInit = true | ||
|  | } | ||
|  | 
 | ||
|  | func (p *parser) destroy() { | ||
|  | 	if p.event.typ != yaml_NO_EVENT { | ||
|  | 		yaml_event_delete(&p.event) | ||
|  | 	} | ||
|  | 	yaml_parser_delete(&p.parser) | ||
|  | } | ||
|  | 
 | ||
|  | // expect consumes an event from the event stream and | ||
|  | // checks that it's of the expected type. | ||
|  | func (p *parser) expect(e yaml_event_type_t) { | ||
|  | 	if p.event.typ == yaml_NO_EVENT { | ||
|  | 		if !yaml_parser_parse(&p.parser, &p.event) { | ||
|  | 			p.fail() | ||
|  | 		} | ||
|  | 	} | ||
|  | 	if p.event.typ == yaml_STREAM_END_EVENT { | ||
|  | 		failf("attempted to go past the end of stream; corrupted value?") | ||
|  | 	} | ||
|  | 	if p.event.typ != e { | ||
|  | 		p.parser.problem = fmt.Sprintf("expected %s event but got %s", e, p.event.typ) | ||
|  | 		p.fail() | ||
|  | 	} | ||
|  | 	yaml_event_delete(&p.event) | ||
|  | 	p.event.typ = yaml_NO_EVENT | ||
|  | } | ||
|  | 
 | ||
|  | // peek peeks at the next event in the event stream, | ||
|  | // puts the results into p.event and returns the event type. | ||
|  | func (p *parser) peek() yaml_event_type_t { | ||
|  | 	if p.event.typ != yaml_NO_EVENT { | ||
|  | 		return p.event.typ | ||
|  | 	} | ||
|  | 	if !yaml_parser_parse(&p.parser, &p.event) { | ||
|  | 		p.fail() | ||
|  | 	} | ||
|  | 	return p.event.typ | ||
|  | } | ||
|  | 
 | ||
|  | func (p *parser) fail() { | ||
|  | 	var where string | ||
|  | 	var line int | ||
|  | 	if p.parser.problem_mark.line != 0 { | ||
|  | 		line = p.parser.problem_mark.line | ||
|  | 		// Scanner errors don't iterate line before returning error | ||
|  | 		if p.parser.error == yaml_SCANNER_ERROR { | ||
|  | 			line++ | ||
|  | 		} | ||
|  | 	} else if p.parser.context_mark.line != 0 { | ||
|  | 		line = p.parser.context_mark.line | ||
|  | 	} | ||
|  | 	if line != 0 { | ||
|  | 		where = "line " + strconv.Itoa(line) + ": " | ||
|  | 	} | ||
|  | 	var msg string | ||
|  | 	if len(p.parser.problem) > 0 { | ||
|  | 		msg = p.parser.problem | ||
|  | 	} else { | ||
|  | 		msg = "unknown problem parsing YAML content" | ||
|  | 	} | ||
|  | 	failf("%s%s", where, msg) | ||
|  | } | ||
|  | 
 | ||
|  | func (p *parser) anchor(n *node, anchor []byte) { | ||
|  | 	if anchor != nil { | ||
|  | 		p.doc.anchors[string(anchor)] = n | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (p *parser) parse() *node { | ||
|  | 	p.init() | ||
|  | 	switch p.peek() { | ||
|  | 	case yaml_SCALAR_EVENT: | ||
|  | 		return p.scalar() | ||
|  | 	case yaml_ALIAS_EVENT: | ||
|  | 		return p.alias() | ||
|  | 	case yaml_MAPPING_START_EVENT: | ||
|  | 		return p.mapping() | ||
|  | 	case yaml_SEQUENCE_START_EVENT: | ||
|  | 		return p.sequence() | ||
|  | 	case yaml_DOCUMENT_START_EVENT: | ||
|  | 		return p.document() | ||
|  | 	case yaml_STREAM_END_EVENT: | ||
|  | 		// Happens when attempting to decode an empty buffer. | ||
|  | 		return nil | ||
|  | 	default: | ||
|  | 		panic("attempted to parse unknown event: " + p.event.typ.String()) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (p *parser) node(kind int) *node { | ||
|  | 	return &node{ | ||
|  | 		kind:   kind, | ||
|  | 		line:   p.event.start_mark.line, | ||
|  | 		column: p.event.start_mark.column, | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (p *parser) document() *node { | ||
|  | 	n := p.node(documentNode) | ||
|  | 	n.anchors = make(map[string]*node) | ||
|  | 	p.doc = n | ||
|  | 	p.expect(yaml_DOCUMENT_START_EVENT) | ||
|  | 	n.children = append(n.children, p.parse()) | ||
|  | 	p.expect(yaml_DOCUMENT_END_EVENT) | ||
|  | 	return n | ||
|  | } | ||
|  | 
 | ||
|  | func (p *parser) alias() *node { | ||
|  | 	n := p.node(aliasNode) | ||
|  | 	n.value = string(p.event.anchor) | ||
|  | 	n.alias = p.doc.anchors[n.value] | ||
|  | 	if n.alias == nil { | ||
|  | 		failf("unknown anchor '%s' referenced", n.value) | ||
|  | 	} | ||
|  | 	p.expect(yaml_ALIAS_EVENT) | ||
|  | 	return n | ||
|  | } | ||
|  | 
 | ||
|  | func (p *parser) scalar() *node { | ||
|  | 	n := p.node(scalarNode) | ||
|  | 	n.value = string(p.event.value) | ||
|  | 	n.tag = string(p.event.tag) | ||
|  | 	n.implicit = p.event.implicit | ||
|  | 	p.anchor(n, p.event.anchor) | ||
|  | 	p.expect(yaml_SCALAR_EVENT) | ||
|  | 	return n | ||
|  | } | ||
|  | 
 | ||
|  | func (p *parser) sequence() *node { | ||
|  | 	n := p.node(sequenceNode) | ||
|  | 	p.anchor(n, p.event.anchor) | ||
|  | 	p.expect(yaml_SEQUENCE_START_EVENT) | ||
|  | 	for p.peek() != yaml_SEQUENCE_END_EVENT { | ||
|  | 		n.children = append(n.children, p.parse()) | ||
|  | 	} | ||
|  | 	p.expect(yaml_SEQUENCE_END_EVENT) | ||
|  | 	return n | ||
|  | } | ||
|  | 
 | ||
|  | func (p *parser) mapping() *node { | ||
|  | 	n := p.node(mappingNode) | ||
|  | 	p.anchor(n, p.event.anchor) | ||
|  | 	p.expect(yaml_MAPPING_START_EVENT) | ||
|  | 	for p.peek() != yaml_MAPPING_END_EVENT { | ||
|  | 		n.children = append(n.children, p.parse(), p.parse()) | ||
|  | 	} | ||
|  | 	p.expect(yaml_MAPPING_END_EVENT) | ||
|  | 	return n | ||
|  | } | ||
|  | 
 | ||
|  | // ---------------------------------------------------------------------------- | ||
|  | // Decoder, unmarshals a node into a provided value. | ||
|  | 
 | ||
|  | type decoder struct { | ||
|  | 	doc     *node | ||
|  | 	aliases map[*node]bool | ||
|  | 	mapType reflect.Type | ||
|  | 	terrors []string | ||
|  | 	strict  bool | ||
|  | 
 | ||
|  | 	decodeCount int | ||
|  | 	aliasCount  int | ||
|  | 	aliasDepth  int | ||
|  | } | ||
|  | 
 | ||
|  | var ( | ||
|  | 	mapItemType    = reflect.TypeOf(MapItem{}) | ||
|  | 	durationType   = reflect.TypeOf(time.Duration(0)) | ||
|  | 	defaultMapType = reflect.TypeOf(map[interface{}]interface{}{}) | ||
|  | 	ifaceType      = defaultMapType.Elem() | ||
|  | 	timeType       = reflect.TypeOf(time.Time{}) | ||
|  | 	ptrTimeType    = reflect.TypeOf(&time.Time{}) | ||
|  | ) | ||
|  | 
 | ||
|  | func newDecoder(strict bool) *decoder { | ||
|  | 	d := &decoder{mapType: defaultMapType, strict: strict} | ||
|  | 	d.aliases = make(map[*node]bool) | ||
|  | 	return d | ||
|  | } | ||
|  | 
 | ||
|  | func (d *decoder) terror(n *node, tag string, out reflect.Value) { | ||
|  | 	if n.tag != "" { | ||
|  | 		tag = n.tag | ||
|  | 	} | ||
|  | 	value := n.value | ||
|  | 	if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG { | ||
|  | 		if len(value) > 10 { | ||
|  | 			value = " `" + value[:7] + "...`" | ||
|  | 		} else { | ||
|  | 			value = " `" + value + "`" | ||
|  | 		} | ||
|  | 	} | ||
|  | 	d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type())) | ||
|  | } | ||
|  | 
 | ||
|  | func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) { | ||
|  | 	terrlen := len(d.terrors) | ||
|  | 	err := u.UnmarshalYAML(func(v interface{}) (err error) { | ||
|  | 		defer handleErr(&err) | ||
|  | 		d.unmarshal(n, reflect.ValueOf(v)) | ||
|  | 		if len(d.terrors) > terrlen { | ||
|  | 			issues := d.terrors[terrlen:] | ||
|  | 			d.terrors = d.terrors[:terrlen] | ||
|  | 			return &TypeError{issues} | ||
|  | 		} | ||
|  | 		return nil | ||
|  | 	}) | ||
|  | 	if e, ok := err.(*TypeError); ok { | ||
|  | 		d.terrors = append(d.terrors, e.Errors...) | ||
|  | 		return false | ||
|  | 	} | ||
|  | 	if err != nil { | ||
|  | 		fail(err) | ||
|  | 	} | ||
|  | 	return true | ||
|  | } | ||
|  | 
 | ||
|  | // d.prepare initializes and dereferences pointers and calls UnmarshalYAML | ||
|  | // if a value is found to implement it. | ||
|  | // It returns the initialized and dereferenced out value, whether | ||
|  | // unmarshalling was already done by UnmarshalYAML, and if so whether | ||
|  | // its types unmarshalled appropriately. | ||
|  | // | ||
|  | // If n holds a null value, prepare returns before doing anything. | ||
|  | func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { | ||
|  | 	if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "~" || n.value == "" && n.implicit) { | ||
|  | 		return out, false, false | ||
|  | 	} | ||
|  | 	again := true | ||
|  | 	for again { | ||
|  | 		again = false | ||
|  | 		if out.Kind() == reflect.Ptr { | ||
|  | 			if out.IsNil() { | ||
|  | 				out.Set(reflect.New(out.Type().Elem())) | ||
|  | 			} | ||
|  | 			out = out.Elem() | ||
|  | 			again = true | ||
|  | 		} | ||
|  | 		if out.CanAddr() { | ||
|  | 			if u, ok := out.Addr().Interface().(Unmarshaler); ok { | ||
|  | 				good = d.callUnmarshaler(n, u) | ||
|  | 				return out, true, good | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return out, false, false | ||
|  | } | ||
|  | 
 | ||
|  | const ( | ||
|  | 	// 400,000 decode operations is ~500kb of dense object declarations, or | ||
|  | 	// ~5kb of dense object declarations with 10000% alias expansion | ||
|  | 	alias_ratio_range_low = 400000 | ||
|  | 
 | ||
|  | 	// 4,000,000 decode operations is ~5MB of dense object declarations, or | ||
|  | 	// ~4.5MB of dense object declarations with 10% alias expansion | ||
|  | 	alias_ratio_range_high = 4000000 | ||
|  | 
 | ||
|  | 	// alias_ratio_range is the range over which we scale allowed alias ratios | ||
|  | 	alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low) | ||
|  | ) | ||
|  | 
 | ||
|  | func allowedAliasRatio(decodeCount int) float64 { | ||
|  | 	switch { | ||
|  | 	case decodeCount <= alias_ratio_range_low: | ||
|  | 		// allow 99% to come from alias expansion for small-to-medium documents | ||
|  | 		return 0.99 | ||
|  | 	case decodeCount >= alias_ratio_range_high: | ||
|  | 		// allow 10% to come from alias expansion for very large documents | ||
|  | 		return 0.10 | ||
|  | 	default: | ||
|  | 		// scale smoothly from 99% down to 10% over the range. | ||
|  | 		// this maps to 396,000 - 400,000 allowed alias-driven decodes over the range. | ||
|  | 		// 400,000 decode operations is ~100MB of allocations in worst-case scenarios (single-item maps). | ||
|  | 		return 0.99 - 0.89*(float64(decodeCount-alias_ratio_range_low)/alias_ratio_range) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { | ||
|  | 	d.decodeCount++ | ||
|  | 	if d.aliasDepth > 0 { | ||
|  | 		d.aliasCount++ | ||
|  | 	} | ||
|  | 	if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) { | ||
|  | 		failf("document contains excessive aliasing") | ||
|  | 	} | ||
|  | 	switch n.kind { | ||
|  | 	case documentNode: | ||
|  | 		return d.document(n, out) | ||
|  | 	case aliasNode: | ||
|  | 		return d.alias(n, out) | ||
|  | 	} | ||
|  | 	out, unmarshaled, good := d.prepare(n, out) | ||
|  | 	if unmarshaled { | ||
|  | 		return good | ||
|  | 	} | ||
|  | 	switch n.kind { | ||
|  | 	case scalarNode: | ||
|  | 		good = d.scalar(n, out) | ||
|  | 	case mappingNode: | ||
|  | 		good = d.mapping(n, out) | ||
|  | 	case sequenceNode: | ||
|  | 		good = d.sequence(n, out) | ||
|  | 	default: | ||
|  | 		panic("internal error: unknown node kind: " + strconv.Itoa(n.kind)) | ||
|  | 	} | ||
|  | 	return good | ||
|  | } | ||
|  | 
 | ||
|  | func (d *decoder) document(n *node, out reflect.Value) (good bool) { | ||
|  | 	if len(n.children) == 1 { | ||
|  | 		d.doc = n | ||
|  | 		d.unmarshal(n.children[0], out) | ||
|  | 		return true | ||
|  | 	} | ||
|  | 	return false | ||
|  | } | ||
|  | 
 | ||
|  | func (d *decoder) alias(n *node, out reflect.Value) (good bool) { | ||
|  | 	if d.aliases[n] { | ||
|  | 		// TODO this could actually be allowed in some circumstances. | ||
|  | 		failf("anchor '%s' value contains itself", n.value) | ||
|  | 	} | ||
|  | 	d.aliases[n] = true | ||
|  | 	d.aliasDepth++ | ||
|  | 	good = d.unmarshal(n.alias, out) | ||
|  | 	d.aliasDepth-- | ||
|  | 	delete(d.aliases, n) | ||
|  | 	return good | ||
|  | } | ||
|  | 
 | ||
|  | var zeroValue reflect.Value | ||
|  | 
 | ||
|  | func resetMap(out reflect.Value) { | ||
|  | 	for _, k := range out.MapKeys() { | ||
|  | 		out.SetMapIndex(k, zeroValue) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (d *decoder) scalar(n *node, out reflect.Value) bool { | ||
|  | 	var tag string | ||
|  | 	var resolved interface{} | ||
|  | 	if n.tag == "" && !n.implicit { | ||
|  | 		tag = yaml_STR_TAG | ||
|  | 		resolved = n.value | ||
|  | 	} else { | ||
|  | 		tag, resolved = resolve(n.tag, n.value) | ||
|  | 		if tag == yaml_BINARY_TAG { | ||
|  | 			data, err := base64.StdEncoding.DecodeString(resolved.(string)) | ||
|  | 			if err != nil { | ||
|  | 				failf("!!binary value contains invalid base64 data") | ||
|  | 			} | ||
|  | 			resolved = string(data) | ||
|  | 		} | ||
|  | 	} | ||
|  | 	if resolved == nil { | ||
|  | 		if out.Kind() == reflect.Map && !out.CanAddr() { | ||
|  | 			resetMap(out) | ||
|  | 		} else { | ||
|  | 			out.Set(reflect.Zero(out.Type())) | ||
|  | 		} | ||
|  | 		return true | ||
|  | 	} | ||
|  | 	if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { | ||
|  | 		// We've resolved to exactly the type we want, so use that. | ||
|  | 		out.Set(resolvedv) | ||
|  | 		return true | ||
|  | 	} | ||
|  | 	// Perhaps we can use the value as a TextUnmarshaler to | ||
|  | 	// set its value. | ||
|  | 	if out.CanAddr() { | ||
|  | 		u, ok := out.Addr().Interface().(encoding.TextUnmarshaler) | ||
|  | 		if ok { | ||
|  | 			var text []byte | ||
|  | 			if tag == yaml_BINARY_TAG { | ||
|  | 				text = []byte(resolved.(string)) | ||
|  | 			} else { | ||
|  | 				// We let any value be unmarshaled into TextUnmarshaler. | ||
|  | 				// That might be more lax than we'd like, but the | ||
|  | 				// TextUnmarshaler itself should bowl out any dubious values. | ||
|  | 				text = []byte(n.value) | ||
|  | 			} | ||
|  | 			err := u.UnmarshalText(text) | ||
|  | 			if err != nil { | ||
|  | 				fail(err) | ||
|  | 			} | ||
|  | 			return true | ||
|  | 		} | ||
|  | 	} | ||
|  | 	switch out.Kind() { | ||
|  | 	case reflect.String: | ||
|  | 		if tag == yaml_BINARY_TAG { | ||
|  | 			out.SetString(resolved.(string)) | ||
|  | 			return true | ||
|  | 		} | ||
|  | 		if resolved != nil { | ||
|  | 			out.SetString(n.value) | ||
|  | 			return true | ||
|  | 		} | ||
|  | 	case reflect.Interface: | ||
|  | 		if resolved == nil { | ||
|  | 			out.Set(reflect.Zero(out.Type())) | ||
|  | 		} else if tag == yaml_TIMESTAMP_TAG { | ||
|  | 			// It looks like a timestamp but for backward compatibility | ||
|  | 			// reasons we set it as a string, so that code that unmarshals | ||
|  | 			// timestamp-like values into interface{} will continue to | ||
|  | 			// see a string and not a time.Time. | ||
|  | 			// TODO(v3) Drop this. | ||
|  | 			out.Set(reflect.ValueOf(n.value)) | ||
|  | 		} else { | ||
|  | 			out.Set(reflect.ValueOf(resolved)) | ||
|  | 		} | ||
|  | 		return true | ||
|  | 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
|  | 		switch resolved := resolved.(type) { | ||
|  | 		case int: | ||
|  | 			if !out.OverflowInt(int64(resolved)) { | ||
|  | 				out.SetInt(int64(resolved)) | ||
|  | 				return true | ||
|  | 			} | ||
|  | 		case int64: | ||
|  | 			if !out.OverflowInt(resolved) { | ||
|  | 				out.SetInt(resolved) | ||
|  | 				return true | ||
|  | 			} | ||
|  | 		case uint64: | ||
|  | 			if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { | ||
|  | 				out.SetInt(int64(resolved)) | ||
|  | 				return true | ||
|  | 			} | ||
|  | 		case float64: | ||
|  | 			if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { | ||
|  | 				out.SetInt(int64(resolved)) | ||
|  | 				return true | ||
|  | 			} | ||
|  | 		case string: | ||
|  | 			if out.Type() == durationType { | ||
|  | 				d, err := time.ParseDuration(resolved) | ||
|  | 				if err == nil { | ||
|  | 					out.SetInt(int64(d)) | ||
|  | 					return true | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||
|  | 		switch resolved := resolved.(type) { | ||
|  | 		case int: | ||
|  | 			if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { | ||
|  | 				out.SetUint(uint64(resolved)) | ||
|  | 				return true | ||
|  | 			} | ||
|  | 		case int64: | ||
|  | 			if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { | ||
|  | 				out.SetUint(uint64(resolved)) | ||
|  | 				return true | ||
|  | 			} | ||
|  | 		case uint64: | ||
|  | 			if !out.OverflowUint(uint64(resolved)) { | ||
|  | 				out.SetUint(uint64(resolved)) | ||
|  | 				return true | ||
|  | 			} | ||
|  | 		case float64: | ||
|  | 			if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) { | ||
|  | 				out.SetUint(uint64(resolved)) | ||
|  | 				return true | ||
|  | 			} | ||
|  | 		} | ||
|  | 	case reflect.Bool: | ||
|  | 		switch resolved := resolved.(type) { | ||
|  | 		case bool: | ||
|  | 			out.SetBool(resolved) | ||
|  | 			return true | ||
|  | 		} | ||
|  | 	case reflect.Float32, reflect.Float64: | ||
|  | 		switch resolved := resolved.(type) { | ||
|  | 		case int: | ||
|  | 			out.SetFloat(float64(resolved)) | ||
|  | 			return true | ||
|  | 		case int64: | ||
|  | 			out.SetFloat(float64(resolved)) | ||
|  | 			return true | ||
|  | 		case uint64: | ||
|  | 			out.SetFloat(float64(resolved)) | ||
|  | 			return true | ||
|  | 		case float64: | ||
|  | 			out.SetFloat(resolved) | ||
|  | 			return true | ||
|  | 		} | ||
|  | 	case reflect.Struct: | ||
|  | 		if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { | ||
|  | 			out.Set(resolvedv) | ||
|  | 			return true | ||
|  | 		} | ||
|  | 	case reflect.Ptr: | ||
|  | 		if out.Type().Elem() == reflect.TypeOf(resolved) { | ||
|  | 			// TODO DOes this make sense? When is out a Ptr except when decoding a nil value? | ||
|  | 			elem := reflect.New(out.Type().Elem()) | ||
|  | 			elem.Elem().Set(reflect.ValueOf(resolved)) | ||
|  | 			out.Set(elem) | ||
|  | 			return true | ||
|  | 		} | ||
|  | 	} | ||
|  | 	d.terror(n, tag, out) | ||
|  | 	return false | ||
|  | } | ||
|  | 
 | ||
|  | func settableValueOf(i interface{}) reflect.Value { | ||
|  | 	v := reflect.ValueOf(i) | ||
|  | 	sv := reflect.New(v.Type()).Elem() | ||
|  | 	sv.Set(v) | ||
|  | 	return sv | ||
|  | } | ||
|  | 
 | ||
|  | func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { | ||
|  | 	l := len(n.children) | ||
|  | 
 | ||
|  | 	var iface reflect.Value | ||
|  | 	switch out.Kind() { | ||
|  | 	case reflect.Slice: | ||
|  | 		out.Set(reflect.MakeSlice(out.Type(), l, l)) | ||
|  | 	case reflect.Array: | ||
|  | 		if l != out.Len() { | ||
|  | 			failf("invalid array: want %d elements but got %d", out.Len(), l) | ||
|  | 		} | ||
|  | 	case reflect.Interface: | ||
|  | 		// No type hints. Will have to use a generic sequence. | ||
|  | 		iface = out | ||
|  | 		out = settableValueOf(make([]interface{}, l)) | ||
|  | 	default: | ||
|  | 		d.terror(n, yaml_SEQ_TAG, out) | ||
|  | 		return false | ||
|  | 	} | ||
|  | 	et := out.Type().Elem() | ||
|  | 
 | ||
|  | 	j := 0 | ||
|  | 	for i := 0; i < l; i++ { | ||
|  | 		e := reflect.New(et).Elem() | ||
|  | 		if ok := d.unmarshal(n.children[i], e); ok { | ||
|  | 			out.Index(j).Set(e) | ||
|  | 			j++ | ||
|  | 		} | ||
|  | 	} | ||
|  | 	if out.Kind() != reflect.Array { | ||
|  | 		out.Set(out.Slice(0, j)) | ||
|  | 	} | ||
|  | 	if iface.IsValid() { | ||
|  | 		iface.Set(out) | ||
|  | 	} | ||
|  | 	return true | ||
|  | } | ||
|  | 
 | ||
|  | func (d *decoder) mapping(n *node, out reflect.Value) (good bool) { | ||
|  | 	switch out.Kind() { | ||
|  | 	case reflect.Struct: | ||
|  | 		return d.mappingStruct(n, out) | ||
|  | 	case reflect.Slice: | ||
|  | 		return d.mappingSlice(n, out) | ||
|  | 	case reflect.Map: | ||
|  | 		// okay | ||
|  | 	case reflect.Interface: | ||
|  | 		if d.mapType.Kind() == reflect.Map { | ||
|  | 			iface := out | ||
|  | 			out = reflect.MakeMap(d.mapType) | ||
|  | 			iface.Set(out) | ||
|  | 		} else { | ||
|  | 			slicev := reflect.New(d.mapType).Elem() | ||
|  | 			if !d.mappingSlice(n, slicev) { | ||
|  | 				return false | ||
|  | 			} | ||
|  | 			out.Set(slicev) | ||
|  | 			return true | ||
|  | 		} | ||
|  | 	default: | ||
|  | 		d.terror(n, yaml_MAP_TAG, out) | ||
|  | 		return false | ||
|  | 	} | ||
|  | 	outt := out.Type() | ||
|  | 	kt := outt.Key() | ||
|  | 	et := outt.Elem() | ||
|  | 
 | ||
|  | 	mapType := d.mapType | ||
|  | 	if outt.Key() == ifaceType && outt.Elem() == ifaceType { | ||
|  | 		d.mapType = outt | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if out.IsNil() { | ||
|  | 		out.Set(reflect.MakeMap(outt)) | ||
|  | 	} | ||
|  | 	l := len(n.children) | ||
|  | 	for i := 0; i < l; i += 2 { | ||
|  | 		if isMerge(n.children[i]) { | ||
|  | 			d.merge(n.children[i+1], out) | ||
|  | 			continue | ||
|  | 		} | ||
|  | 		k := reflect.New(kt).Elem() | ||
|  | 		if d.unmarshal(n.children[i], k) { | ||
|  | 			kkind := k.Kind() | ||
|  | 			if kkind == reflect.Interface { | ||
|  | 				kkind = k.Elem().Kind() | ||
|  | 			} | ||
|  | 			if kkind == reflect.Map || kkind == reflect.Slice { | ||
|  | 				failf("invalid map key: %#v", k.Interface()) | ||
|  | 			} | ||
|  | 			e := reflect.New(et).Elem() | ||
|  | 			if d.unmarshal(n.children[i+1], e) { | ||
|  | 				d.setMapIndex(n.children[i+1], out, k, e) | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	d.mapType = mapType | ||
|  | 	return true | ||
|  | } | ||
|  | 
 | ||
|  | func (d *decoder) setMapIndex(n *node, out, k, v reflect.Value) { | ||
|  | 	if d.strict && out.MapIndex(k) != zeroValue { | ||
|  | 		d.terrors = append(d.terrors, fmt.Sprintf("line %d: key %#v already set in map", n.line+1, k.Interface())) | ||
|  | 		return | ||
|  | 	} | ||
|  | 	out.SetMapIndex(k, v) | ||
|  | } | ||
|  | 
 | ||
|  | func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) { | ||
|  | 	outt := out.Type() | ||
|  | 	if outt.Elem() != mapItemType { | ||
|  | 		d.terror(n, yaml_MAP_TAG, out) | ||
|  | 		return false | ||
|  | 	} | ||
|  | 
 | ||
|  | 	mapType := d.mapType | ||
|  | 	d.mapType = outt | ||
|  | 
 | ||
|  | 	var slice []MapItem | ||
|  | 	var l = len(n.children) | ||
|  | 	for i := 0; i < l; i += 2 { | ||
|  | 		if isMerge(n.children[i]) { | ||
|  | 			d.merge(n.children[i+1], out) | ||
|  | 			continue | ||
|  | 		} | ||
|  | 		item := MapItem{} | ||
|  | 		k := reflect.ValueOf(&item.Key).Elem() | ||
|  | 		if d.unmarshal(n.children[i], k) { | ||
|  | 			v := reflect.ValueOf(&item.Value).Elem() | ||
|  | 			if d.unmarshal(n.children[i+1], v) { | ||
|  | 				slice = append(slice, item) | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	out.Set(reflect.ValueOf(slice)) | ||
|  | 	d.mapType = mapType | ||
|  | 	return true | ||
|  | } | ||
|  | 
 | ||
|  | func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { | ||
|  | 	sinfo, err := getStructInfo(out.Type()) | ||
|  | 	if err != nil { | ||
|  | 		panic(err) | ||
|  | 	} | ||
|  | 	name := settableValueOf("") | ||
|  | 	l := len(n.children) | ||
|  | 
 | ||
|  | 	var inlineMap reflect.Value | ||
|  | 	var elemType reflect.Type | ||
|  | 	if sinfo.InlineMap != -1 { | ||
|  | 		inlineMap = out.Field(sinfo.InlineMap) | ||
|  | 		inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) | ||
|  | 		elemType = inlineMap.Type().Elem() | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var doneFields []bool | ||
|  | 	if d.strict { | ||
|  | 		doneFields = make([]bool, len(sinfo.FieldsList)) | ||
|  | 	} | ||
|  | 	for i := 0; i < l; i += 2 { | ||
|  | 		ni := n.children[i] | ||
|  | 		if isMerge(ni) { | ||
|  | 			d.merge(n.children[i+1], out) | ||
|  | 			continue | ||
|  | 		} | ||
|  | 		if !d.unmarshal(ni, name) { | ||
|  | 			continue | ||
|  | 		} | ||
|  | 		if info, ok := sinfo.FieldsMap[name.String()]; ok { | ||
|  | 			if d.strict { | ||
|  | 				if doneFields[info.Id] { | ||
|  | 					d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.line+1, name.String(), out.Type())) | ||
|  | 					continue | ||
|  | 				} | ||
|  | 				doneFields[info.Id] = true | ||
|  | 			} | ||
|  | 			var field reflect.Value | ||
|  | 			if info.Inline == nil { | ||
|  | 				field = out.Field(info.Num) | ||
|  | 			} else { | ||
|  | 				field = out.FieldByIndex(info.Inline) | ||
|  | 			} | ||
|  | 			d.unmarshal(n.children[i+1], field) | ||
|  | 		} else if sinfo.InlineMap != -1 { | ||
|  | 			if inlineMap.IsNil() { | ||
|  | 				inlineMap.Set(reflect.MakeMap(inlineMap.Type())) | ||
|  | 			} | ||
|  | 			value := reflect.New(elemType).Elem() | ||
|  | 			d.unmarshal(n.children[i+1], value) | ||
|  | 			d.setMapIndex(n.children[i+1], inlineMap, name, value) | ||
|  | 		} else if d.strict { | ||
|  | 			d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.line+1, name.String(), out.Type())) | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return true | ||
|  | } | ||
|  | 
 | ||
|  | func failWantMap() { | ||
|  | 	failf("map merge requires map or sequence of maps as the value") | ||
|  | } | ||
|  | 
 | ||
|  | func (d *decoder) merge(n *node, out reflect.Value) { | ||
|  | 	switch n.kind { | ||
|  | 	case mappingNode: | ||
|  | 		d.unmarshal(n, out) | ||
|  | 	case aliasNode: | ||
|  | 		if n.alias != nil && n.alias.kind != mappingNode { | ||
|  | 			failWantMap() | ||
|  | 		} | ||
|  | 		d.unmarshal(n, out) | ||
|  | 	case sequenceNode: | ||
|  | 		// Step backwards as earlier nodes take precedence. | ||
|  | 		for i := len(n.children) - 1; i >= 0; i-- { | ||
|  | 			ni := n.children[i] | ||
|  | 			if ni.kind == aliasNode { | ||
|  | 				if ni.alias != nil && ni.alias.kind != mappingNode { | ||
|  | 					failWantMap() | ||
|  | 				} | ||
|  | 			} else if ni.kind != mappingNode { | ||
|  | 				failWantMap() | ||
|  | 			} | ||
|  | 			d.unmarshal(ni, out) | ||
|  | 		} | ||
|  | 	default: | ||
|  | 		failWantMap() | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func isMerge(n *node) bool { | ||
|  | 	return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG) | ||
|  | } |