ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/aya/vendor/gopkg.in/yaml.v3/encode.go
Revision: 1.1
Committed: Mon Sep 30 00:42:06 2024 UTC (6 weeks, 4 days ago) by yakumo_izuru
Branch: MAIN
CVS Tags: HEAD
Log Message:
Mirrored from https://git.chaotic.ninja/git/yakumo_izuru/aya

File Contents

# User Rev Content
1 yakumo_izuru 1.1 //
2     // Copyright (c) 2011-2019 Canonical Ltd
3     //
4     // Licensed under the Apache License, Version 2.0 (the "License");
5     // you may not use this file except in compliance with the License.
6     // You may obtain a copy of the License at
7     //
8     // http://www.apache.org/licenses/LICENSE-2.0
9     //
10     // Unless required by applicable law or agreed to in writing, software
11     // distributed under the License is distributed on an "AS IS" BASIS,
12     // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     // See the License for the specific language governing permissions and
14     // limitations under the License.
15    
16     package yaml
17    
18     import (
19     "encoding"
20     "fmt"
21     "io"
22     "reflect"
23     "regexp"
24     "sort"
25     "strconv"
26     "strings"
27     "time"
28     "unicode/utf8"
29     )
30    
31     type encoder struct {
32     emitter yaml_emitter_t
33     event yaml_event_t
34     out []byte
35     flow bool
36     indent int
37     doneInit bool
38     }
39    
40     func newEncoder() *encoder {
41     e := &encoder{}
42     yaml_emitter_initialize(&e.emitter)
43     yaml_emitter_set_output_string(&e.emitter, &e.out)
44     yaml_emitter_set_unicode(&e.emitter, true)
45     return e
46     }
47    
48     func newEncoderWithWriter(w io.Writer) *encoder {
49     e := &encoder{}
50     yaml_emitter_initialize(&e.emitter)
51     yaml_emitter_set_output_writer(&e.emitter, w)
52     yaml_emitter_set_unicode(&e.emitter, true)
53     return e
54     }
55    
56     func (e *encoder) init() {
57     if e.doneInit {
58     return
59     }
60     if e.indent == 0 {
61     e.indent = 4
62     }
63     e.emitter.best_indent = e.indent
64     yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)
65     e.emit()
66     e.doneInit = true
67     }
68    
69     func (e *encoder) finish() {
70     e.emitter.open_ended = false
71     yaml_stream_end_event_initialize(&e.event)
72     e.emit()
73     }
74    
75     func (e *encoder) destroy() {
76     yaml_emitter_delete(&e.emitter)
77     }
78    
79     func (e *encoder) emit() {
80     // This will internally delete the e.event value.
81     e.must(yaml_emitter_emit(&e.emitter, &e.event))
82     }
83    
84     func (e *encoder) must(ok bool) {
85     if !ok {
86     msg := e.emitter.problem
87     if msg == "" {
88     msg = "unknown problem generating YAML content"
89     }
90     failf("%s", msg)
91     }
92     }
93    
94     func (e *encoder) marshalDoc(tag string, in reflect.Value) {
95     e.init()
96     var node *Node
97     if in.IsValid() {
98     node, _ = in.Interface().(*Node)
99     }
100     if node != nil && node.Kind == DocumentNode {
101     e.nodev(in)
102     } else {
103     yaml_document_start_event_initialize(&e.event, nil, nil, true)
104     e.emit()
105     e.marshal(tag, in)
106     yaml_document_end_event_initialize(&e.event, true)
107     e.emit()
108     }
109     }
110    
111     func (e *encoder) marshal(tag string, in reflect.Value) {
112     tag = shortTag(tag)
113     if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() {
114     e.nilv()
115     return
116     }
117     iface := in.Interface()
118     switch value := iface.(type) {
119     case *Node:
120     e.nodev(in)
121     return
122     case Node:
123     if !in.CanAddr() {
124     var n = reflect.New(in.Type()).Elem()
125     n.Set(in)
126     in = n
127     }
128     e.nodev(in.Addr())
129     return
130     case time.Time:
131     e.timev(tag, in)
132     return
133     case *time.Time:
134     e.timev(tag, in.Elem())
135     return
136     case time.Duration:
137     e.stringv(tag, reflect.ValueOf(value.String()))
138     return
139     case Marshaler:
140     v, err := value.MarshalYAML()
141     if err != nil {
142     fail(err)
143     }
144     if v == nil {
145     e.nilv()
146     return
147     }
148     e.marshal(tag, reflect.ValueOf(v))
149     return
150     case encoding.TextMarshaler:
151     text, err := value.MarshalText()
152     if err != nil {
153     fail(err)
154     }
155     in = reflect.ValueOf(string(text))
156     case nil:
157     e.nilv()
158     return
159     }
160     switch in.Kind() {
161     case reflect.Interface:
162     e.marshal(tag, in.Elem())
163     case reflect.Map:
164     e.mapv(tag, in)
165     case reflect.Ptr:
166     e.marshal(tag, in.Elem())
167     case reflect.Struct:
168     e.structv(tag, in)
169     case reflect.Slice, reflect.Array:
170     e.slicev(tag, in)
171     case reflect.String:
172     e.stringv(tag, in)
173     case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
174     e.intv(tag, in)
175     case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
176     e.uintv(tag, in)
177     case reflect.Float32, reflect.Float64:
178     e.floatv(tag, in)
179     case reflect.Bool:
180     e.boolv(tag, in)
181     default:
182     panic("cannot marshal type: " + in.Type().String())
183     }
184     }
185    
186     func (e *encoder) mapv(tag string, in reflect.Value) {
187     e.mappingv(tag, func() {
188     keys := keyList(in.MapKeys())
189     sort.Sort(keys)
190     for _, k := range keys {
191     e.marshal("", k)
192     e.marshal("", in.MapIndex(k))
193     }
194     })
195     }
196    
197     func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) {
198     for _, num := range index {
199     for {
200     if v.Kind() == reflect.Ptr {
201     if v.IsNil() {
202     return reflect.Value{}
203     }
204     v = v.Elem()
205     continue
206     }
207     break
208     }
209     v = v.Field(num)
210     }
211     return v
212     }
213    
214     func (e *encoder) structv(tag string, in reflect.Value) {
215     sinfo, err := getStructInfo(in.Type())
216     if err != nil {
217     panic(err)
218     }
219     e.mappingv(tag, func() {
220     for _, info := range sinfo.FieldsList {
221     var value reflect.Value
222     if info.Inline == nil {
223     value = in.Field(info.Num)
224     } else {
225     value = e.fieldByIndex(in, info.Inline)
226     if !value.IsValid() {
227     continue
228     }
229     }
230     if info.OmitEmpty && isZero(value) {
231     continue
232     }
233     e.marshal("", reflect.ValueOf(info.Key))
234     e.flow = info.Flow
235     e.marshal("", value)
236     }
237     if sinfo.InlineMap >= 0 {
238     m := in.Field(sinfo.InlineMap)
239     if m.Len() > 0 {
240     e.flow = false
241     keys := keyList(m.MapKeys())
242     sort.Sort(keys)
243     for _, k := range keys {
244     if _, found := sinfo.FieldsMap[k.String()]; found {
245     panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String()))
246     }
247     e.marshal("", k)
248     e.flow = false
249     e.marshal("", m.MapIndex(k))
250     }
251     }
252     }
253     })
254     }
255    
256     func (e *encoder) mappingv(tag string, f func()) {
257     implicit := tag == ""
258     style := yaml_BLOCK_MAPPING_STYLE
259     if e.flow {
260     e.flow = false
261     style = yaml_FLOW_MAPPING_STYLE
262     }
263     yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)
264     e.emit()
265     f()
266     yaml_mapping_end_event_initialize(&e.event)
267     e.emit()
268     }
269    
270     func (e *encoder) slicev(tag string, in reflect.Value) {
271     implicit := tag == ""
272     style := yaml_BLOCK_SEQUENCE_STYLE
273     if e.flow {
274     e.flow = false
275     style = yaml_FLOW_SEQUENCE_STYLE
276     }
277     e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
278     e.emit()
279     n := in.Len()
280     for i := 0; i < n; i++ {
281     e.marshal("", in.Index(i))
282     }
283     e.must(yaml_sequence_end_event_initialize(&e.event))
284     e.emit()
285     }
286    
287     // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
288     //
289     // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
290     // in YAML 1.2 and by this package, but these should be marshalled quoted for
291     // the time being for compatibility with other parsers.
292     func isBase60Float(s string) (result bool) {
293     // Fast path.
294     if s == "" {
295     return false
296     }
297     c := s[0]
298     if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
299     return false
300     }
301     // Do the full match.
302     return base60float.MatchString(s)
303     }
304    
305     // From http://yaml.org/type/float.html, except the regular expression there
306     // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
307     var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
308    
309     // isOldBool returns whether s is bool notation as defined in YAML 1.1.
310     //
311     // We continue to force strings that YAML 1.1 would interpret as booleans to be
312     // rendered as quotes strings so that the marshalled output valid for YAML 1.1
313     // parsing.
314     func isOldBool(s string) (result bool) {
315     switch s {
316     case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON",
317     "n", "N", "no", "No", "NO", "off", "Off", "OFF":
318     return true
319     default:
320     return false
321     }
322     }
323    
324     func (e *encoder) stringv(tag string, in reflect.Value) {
325     var style yaml_scalar_style_t
326     s := in.String()
327     canUsePlain := true
328     switch {
329     case !utf8.ValidString(s):
330     if tag == binaryTag {
331     failf("explicitly tagged !!binary data must be base64-encoded")
332     }
333     if tag != "" {
334     failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
335     }
336     // It can't be encoded directly as YAML so use a binary tag
337     // and encode it as base64.
338     tag = binaryTag
339     s = encodeBase64(s)
340     case tag == "":
341     // Check to see if it would resolve to a specific
342     // tag when encoded unquoted. If it doesn't,
343     // there's no need to quote it.
344     rtag, _ := resolve("", s)
345     canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s))
346     }
347     // Note: it's possible for user code to emit invalid YAML
348     // if they explicitly specify a tag and a string containing
349     // text that's incompatible with that tag.
350     switch {
351     case strings.Contains(s, "\n"):
352     if e.flow {
353     style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
354     } else {
355     style = yaml_LITERAL_SCALAR_STYLE
356     }
357     case canUsePlain:
358     style = yaml_PLAIN_SCALAR_STYLE
359     default:
360     style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
361     }
362     e.emitScalar(s, "", tag, style, nil, nil, nil, nil)
363     }
364    
365     func (e *encoder) boolv(tag string, in reflect.Value) {
366     var s string
367     if in.Bool() {
368     s = "true"
369     } else {
370     s = "false"
371     }
372     e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
373     }
374    
375     func (e *encoder) intv(tag string, in reflect.Value) {
376     s := strconv.FormatInt(in.Int(), 10)
377     e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
378     }
379    
380     func (e *encoder) uintv(tag string, in reflect.Value) {
381     s := strconv.FormatUint(in.Uint(), 10)
382     e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
383     }
384    
385     func (e *encoder) timev(tag string, in reflect.Value) {
386     t := in.Interface().(time.Time)
387     s := t.Format(time.RFC3339Nano)
388     e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
389     }
390    
391     func (e *encoder) floatv(tag string, in reflect.Value) {
392     // Issue #352: When formatting, use the precision of the underlying value
393     precision := 64
394     if in.Kind() == reflect.Float32 {
395     precision = 32
396     }
397    
398     s := strconv.FormatFloat(in.Float(), 'g', -1, precision)
399     switch s {
400     case "+Inf":
401     s = ".inf"
402     case "-Inf":
403     s = "-.inf"
404     case "NaN":
405     s = ".nan"
406     }
407     e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
408     }
409    
410     func (e *encoder) nilv() {
411     e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
412     }
413    
414     func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) {
415     // TODO Kill this function. Replace all initialize calls by their underlining Go literals.
416     implicit := tag == ""
417     if !implicit {
418     tag = longTag(tag)
419     }
420     e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
421     e.event.head_comment = head
422     e.event.line_comment = line
423     e.event.foot_comment = foot
424     e.event.tail_comment = tail
425     e.emit()
426     }
427    
428     func (e *encoder) nodev(in reflect.Value) {
429     e.node(in.Interface().(*Node), "")
430     }
431    
432     func (e *encoder) node(node *Node, tail string) {
433     // Zero nodes behave as nil.
434     if node.Kind == 0 && node.IsZero() {
435     e.nilv()
436     return
437     }
438    
439     // If the tag was not explicitly requested, and dropping it won't change the
440     // implicit tag of the value, don't include it in the presentation.
441     var tag = node.Tag
442     var stag = shortTag(tag)
443     var forceQuoting bool
444     if tag != "" && node.Style&TaggedStyle == 0 {
445     if node.Kind == ScalarNode {
446     if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 {
447     tag = ""
448     } else {
449     rtag, _ := resolve("", node.Value)
450     if rtag == stag {
451     tag = ""
452     } else if stag == strTag {
453     tag = ""
454     forceQuoting = true
455     }
456     }
457     } else {
458     var rtag string
459     switch node.Kind {
460     case MappingNode:
461     rtag = mapTag
462     case SequenceNode:
463     rtag = seqTag
464     }
465     if rtag == stag {
466     tag = ""
467     }
468     }
469     }
470    
471     switch node.Kind {
472     case DocumentNode:
473     yaml_document_start_event_initialize(&e.event, nil, nil, true)
474     e.event.head_comment = []byte(node.HeadComment)
475     e.emit()
476     for _, node := range node.Content {
477     e.node(node, "")
478     }
479     yaml_document_end_event_initialize(&e.event, true)
480     e.event.foot_comment = []byte(node.FootComment)
481     e.emit()
482    
483     case SequenceNode:
484     style := yaml_BLOCK_SEQUENCE_STYLE
485     if node.Style&FlowStyle != 0 {
486     style = yaml_FLOW_SEQUENCE_STYLE
487     }
488     e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style))
489     e.event.head_comment = []byte(node.HeadComment)
490     e.emit()
491     for _, node := range node.Content {
492     e.node(node, "")
493     }
494     e.must(yaml_sequence_end_event_initialize(&e.event))
495     e.event.line_comment = []byte(node.LineComment)
496     e.event.foot_comment = []byte(node.FootComment)
497     e.emit()
498    
499     case MappingNode:
500     style := yaml_BLOCK_MAPPING_STYLE
501     if node.Style&FlowStyle != 0 {
502     style = yaml_FLOW_MAPPING_STYLE
503     }
504     yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style)
505     e.event.tail_comment = []byte(tail)
506     e.event.head_comment = []byte(node.HeadComment)
507     e.emit()
508    
509     // The tail logic below moves the foot comment of prior keys to the following key,
510     // since the value for each key may be a nested structure and the foot needs to be
511     // processed only the entirety of the value is streamed. The last tail is processed
512     // with the mapping end event.
513     var tail string
514     for i := 0; i+1 < len(node.Content); i += 2 {
515     k := node.Content[i]
516     foot := k.FootComment
517     if foot != "" {
518     kopy := *k
519     kopy.FootComment = ""
520     k = &kopy
521     }
522     e.node(k, tail)
523     tail = foot
524    
525     v := node.Content[i+1]
526     e.node(v, "")
527     }
528    
529     yaml_mapping_end_event_initialize(&e.event)
530     e.event.tail_comment = []byte(tail)
531     e.event.line_comment = []byte(node.LineComment)
532     e.event.foot_comment = []byte(node.FootComment)
533     e.emit()
534    
535     case AliasNode:
536     yaml_alias_event_initialize(&e.event, []byte(node.Value))
537     e.event.head_comment = []byte(node.HeadComment)
538     e.event.line_comment = []byte(node.LineComment)
539     e.event.foot_comment = []byte(node.FootComment)
540     e.emit()
541    
542     case ScalarNode:
543     value := node.Value
544     if !utf8.ValidString(value) {
545     if stag == binaryTag {
546     failf("explicitly tagged !!binary data must be base64-encoded")
547     }
548     if stag != "" {
549     failf("cannot marshal invalid UTF-8 data as %s", stag)
550     }
551     // It can't be encoded directly as YAML so use a binary tag
552     // and encode it as base64.
553     tag = binaryTag
554     value = encodeBase64(value)
555     }
556    
557     style := yaml_PLAIN_SCALAR_STYLE
558     switch {
559     case node.Style&DoubleQuotedStyle != 0:
560     style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
561     case node.Style&SingleQuotedStyle != 0:
562     style = yaml_SINGLE_QUOTED_SCALAR_STYLE
563     case node.Style&LiteralStyle != 0:
564     style = yaml_LITERAL_SCALAR_STYLE
565     case node.Style&FoldedStyle != 0:
566     style = yaml_FOLDED_SCALAR_STYLE
567     case strings.Contains(value, "\n"):
568     style = yaml_LITERAL_SCALAR_STYLE
569     case forceQuoting:
570     style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
571     }
572    
573     e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail))
574     default:
575     failf("cannot encode node with unknown kind %d", node.Kind)
576     }
577     }