encode.go (14789B)
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 }