encode.go raw

   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  }
 578