gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

encode.go (9812B)


      1 package yaml
      2 
      3 import (
      4 	"encoding"
      5 	"fmt"
      6 	"io"
      7 	"reflect"
      8 	"regexp"
      9 	"sort"
     10 	"strconv"
     11 	"strings"
     12 	"time"
     13 	"unicode/utf8"
     14 )
     15 
     16 // jsonNumber is the interface of the encoding/json.Number datatype.
     17 // Repeating the interface here avoids a dependency on encoding/json, and also
     18 // supports other libraries like jsoniter, which use a similar datatype with
     19 // the same interface. Detecting this interface is useful when dealing with
     20 // structures containing json.Number, which is a string under the hood. The
     21 // encoder should prefer the use of Int64(), Float64() and string(), in that
     22 // order, when encoding this type.
     23 type jsonNumber interface {
     24 	Float64() (float64, error)
     25 	Int64() (int64, error)
     26 	String() string
     27 }
     28 
     29 type encoder struct {
     30 	emitter yaml_emitter_t
     31 	event   yaml_event_t
     32 	out     []byte
     33 	flow    bool
     34 	// doneInit holds whether the initial stream_start_event has been
     35 	// emitted.
     36 	doneInit bool
     37 }
     38 
     39 func newEncoder() *encoder {
     40 	e := &encoder{}
     41 	yaml_emitter_initialize(&e.emitter)
     42 	yaml_emitter_set_output_string(&e.emitter, &e.out)
     43 	yaml_emitter_set_unicode(&e.emitter, true)
     44 	return e
     45 }
     46 
     47 func newEncoderWithWriter(w io.Writer) *encoder {
     48 	e := &encoder{}
     49 	yaml_emitter_initialize(&e.emitter)
     50 	yaml_emitter_set_output_writer(&e.emitter, w)
     51 	yaml_emitter_set_unicode(&e.emitter, true)
     52 	return e
     53 }
     54 
     55 func (e *encoder) init() {
     56 	if e.doneInit {
     57 		return
     58 	}
     59 	yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)
     60 	e.emit()
     61 	e.doneInit = true
     62 }
     63 
     64 func (e *encoder) finish() {
     65 	e.emitter.open_ended = false
     66 	yaml_stream_end_event_initialize(&e.event)
     67 	e.emit()
     68 }
     69 
     70 func (e *encoder) destroy() {
     71 	yaml_emitter_delete(&e.emitter)
     72 }
     73 
     74 func (e *encoder) emit() {
     75 	// This will internally delete the e.event value.
     76 	e.must(yaml_emitter_emit(&e.emitter, &e.event))
     77 }
     78 
     79 func (e *encoder) must(ok bool) {
     80 	if !ok {
     81 		msg := e.emitter.problem
     82 		if msg == "" {
     83 			msg = "unknown problem generating YAML content"
     84 		}
     85 		failf("%s", msg)
     86 	}
     87 }
     88 
     89 func (e *encoder) marshalDoc(tag string, in reflect.Value) {
     90 	e.init()
     91 	yaml_document_start_event_initialize(&e.event, nil, nil, true)
     92 	e.emit()
     93 	e.marshal(tag, in)
     94 	yaml_document_end_event_initialize(&e.event, true)
     95 	e.emit()
     96 }
     97 
     98 func (e *encoder) marshal(tag string, in reflect.Value) {
     99 	if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() {
    100 		e.nilv()
    101 		return
    102 	}
    103 	iface := in.Interface()
    104 	switch m := iface.(type) {
    105 	case jsonNumber:
    106 		integer, err := m.Int64()
    107 		if err == nil {
    108 			// In this case the json.Number is a valid int64
    109 			in = reflect.ValueOf(integer)
    110 			break
    111 		}
    112 		float, err := m.Float64()
    113 		if err == nil {
    114 			// In this case the json.Number is a valid float64
    115 			in = reflect.ValueOf(float)
    116 			break
    117 		}
    118 		// fallback case - no number could be obtained
    119 		in = reflect.ValueOf(m.String())
    120 	case time.Time, *time.Time:
    121 		// Although time.Time implements TextMarshaler,
    122 		// we don't want to treat it as a string for YAML
    123 		// purposes because YAML has special support for
    124 		// timestamps.
    125 	case Marshaler:
    126 		v, err := m.MarshalYAML()
    127 		if err != nil {
    128 			fail(err)
    129 		}
    130 		if v == nil {
    131 			e.nilv()
    132 			return
    133 		}
    134 		in = reflect.ValueOf(v)
    135 	case encoding.TextMarshaler:
    136 		text, err := m.MarshalText()
    137 		if err != nil {
    138 			fail(err)
    139 		}
    140 		in = reflect.ValueOf(string(text))
    141 	case nil:
    142 		e.nilv()
    143 		return
    144 	}
    145 	switch in.Kind() {
    146 	case reflect.Interface:
    147 		e.marshal(tag, in.Elem())
    148 	case reflect.Map:
    149 		e.mapv(tag, in)
    150 	case reflect.Ptr:
    151 		if in.Type() == ptrTimeType {
    152 			e.timev(tag, in.Elem())
    153 		} else {
    154 			e.marshal(tag, in.Elem())
    155 		}
    156 	case reflect.Struct:
    157 		if in.Type() == timeType {
    158 			e.timev(tag, in)
    159 		} else {
    160 			e.structv(tag, in)
    161 		}
    162 	case reflect.Slice, reflect.Array:
    163 		if in.Type().Elem() == mapItemType {
    164 			e.itemsv(tag, in)
    165 		} else {
    166 			e.slicev(tag, in)
    167 		}
    168 	case reflect.String:
    169 		e.stringv(tag, in)
    170 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    171 		if in.Type() == durationType {
    172 			e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String()))
    173 		} else {
    174 			e.intv(tag, in)
    175 		}
    176 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
    177 		e.uintv(tag, in)
    178 	case reflect.Float32, reflect.Float64:
    179 		e.floatv(tag, in)
    180 	case reflect.Bool:
    181 		e.boolv(tag, in)
    182 	default:
    183 		panic("cannot marshal type: " + in.Type().String())
    184 	}
    185 }
    186 
    187 func (e *encoder) mapv(tag string, in reflect.Value) {
    188 	e.mappingv(tag, func() {
    189 		keys := keyList(in.MapKeys())
    190 		sort.Sort(keys)
    191 		for _, k := range keys {
    192 			e.marshal("", k)
    193 			e.marshal("", in.MapIndex(k))
    194 		}
    195 	})
    196 }
    197 
    198 func (e *encoder) itemsv(tag string, in reflect.Value) {
    199 	e.mappingv(tag, func() {
    200 		slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem)
    201 		for _, item := range slice {
    202 			e.marshal("", reflect.ValueOf(item.Key))
    203 			e.marshal("", reflect.ValueOf(item.Value))
    204 		}
    205 	})
    206 }
    207 
    208 func (e *encoder) structv(tag string, in reflect.Value) {
    209 	sinfo, err := getStructInfo(in.Type())
    210 	if err != nil {
    211 		panic(err)
    212 	}
    213 	e.mappingv(tag, func() {
    214 		for _, info := range sinfo.FieldsList {
    215 			var value reflect.Value
    216 			if info.Inline == nil {
    217 				value = in.Field(info.Num)
    218 			} else {
    219 				value = in.FieldByIndex(info.Inline)
    220 			}
    221 			if info.OmitEmpty && isZero(value) {
    222 				continue
    223 			}
    224 			e.marshal("", reflect.ValueOf(info.Key))
    225 			e.flow = info.Flow
    226 			e.marshal("", value)
    227 		}
    228 		if sinfo.InlineMap >= 0 {
    229 			m := in.Field(sinfo.InlineMap)
    230 			if m.Len() > 0 {
    231 				e.flow = false
    232 				keys := keyList(m.MapKeys())
    233 				sort.Sort(keys)
    234 				for _, k := range keys {
    235 					if _, found := sinfo.FieldsMap[k.String()]; found {
    236 						panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String()))
    237 					}
    238 					e.marshal("", k)
    239 					e.flow = false
    240 					e.marshal("", m.MapIndex(k))
    241 				}
    242 			}
    243 		}
    244 	})
    245 }
    246 
    247 func (e *encoder) mappingv(tag string, f func()) {
    248 	implicit := tag == ""
    249 	style := yaml_BLOCK_MAPPING_STYLE
    250 	if e.flow {
    251 		e.flow = false
    252 		style = yaml_FLOW_MAPPING_STYLE
    253 	}
    254 	yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)
    255 	e.emit()
    256 	f()
    257 	yaml_mapping_end_event_initialize(&e.event)
    258 	e.emit()
    259 }
    260 
    261 func (e *encoder) slicev(tag string, in reflect.Value) {
    262 	implicit := tag == ""
    263 	style := yaml_BLOCK_SEQUENCE_STYLE
    264 	if e.flow {
    265 		e.flow = false
    266 		style = yaml_FLOW_SEQUENCE_STYLE
    267 	}
    268 	e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
    269 	e.emit()
    270 	n := in.Len()
    271 	for i := 0; i < n; i++ {
    272 		e.marshal("", in.Index(i))
    273 	}
    274 	e.must(yaml_sequence_end_event_initialize(&e.event))
    275 	e.emit()
    276 }
    277 
    278 // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
    279 //
    280 // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
    281 // in YAML 1.2 and by this package, but these should be marshalled quoted for
    282 // the time being for compatibility with other parsers.
    283 func isBase60Float(s string) (result bool) {
    284 	// Fast path.
    285 	if s == "" {
    286 		return false
    287 	}
    288 	c := s[0]
    289 	if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
    290 		return false
    291 	}
    292 	// Do the full match.
    293 	return base60float.MatchString(s)
    294 }
    295 
    296 // From http://yaml.org/type/float.html, except the regular expression there
    297 // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
    298 var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
    299 
    300 func (e *encoder) stringv(tag string, in reflect.Value) {
    301 	var style yaml_scalar_style_t
    302 	s := in.String()
    303 	canUsePlain := true
    304 	switch {
    305 	case !utf8.ValidString(s):
    306 		if tag == yaml_BINARY_TAG {
    307 			failf("explicitly tagged !!binary data must be base64-encoded")
    308 		}
    309 		if tag != "" {
    310 			failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
    311 		}
    312 		// It can't be encoded directly as YAML so use a binary tag
    313 		// and encode it as base64.
    314 		tag = yaml_BINARY_TAG
    315 		s = encodeBase64(s)
    316 	case tag == "":
    317 		// Check to see if it would resolve to a specific
    318 		// tag when encoded unquoted. If it doesn't,
    319 		// there's no need to quote it.
    320 		rtag, _ := resolve("", s)
    321 		canUsePlain = rtag == yaml_STR_TAG && !isBase60Float(s)
    322 	}
    323 	// Note: it's possible for user code to emit invalid YAML
    324 	// if they explicitly specify a tag and a string containing
    325 	// text that's incompatible with that tag.
    326 	switch {
    327 	case strings.Contains(s, "\n"):
    328 		style = yaml_LITERAL_SCALAR_STYLE
    329 	case canUsePlain:
    330 		style = yaml_PLAIN_SCALAR_STYLE
    331 	default:
    332 		style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
    333 	}
    334 	e.emitScalar(s, "", tag, style)
    335 }
    336 
    337 func (e *encoder) boolv(tag string, in reflect.Value) {
    338 	var s string
    339 	if in.Bool() {
    340 		s = "true"
    341 	} else {
    342 		s = "false"
    343 	}
    344 	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
    345 }
    346 
    347 func (e *encoder) intv(tag string, in reflect.Value) {
    348 	s := strconv.FormatInt(in.Int(), 10)
    349 	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
    350 }
    351 
    352 func (e *encoder) uintv(tag string, in reflect.Value) {
    353 	s := strconv.FormatUint(in.Uint(), 10)
    354 	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
    355 }
    356 
    357 func (e *encoder) timev(tag string, in reflect.Value) {
    358 	t := in.Interface().(time.Time)
    359 	s := t.Format(time.RFC3339Nano)
    360 	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
    361 }
    362 
    363 func (e *encoder) floatv(tag string, in reflect.Value) {
    364 	// Issue #352: When formatting, use the precision of the underlying value
    365 	precision := 64
    366 	if in.Kind() == reflect.Float32 {
    367 		precision = 32
    368 	}
    369 
    370 	s := strconv.FormatFloat(in.Float(), 'g', -1, precision)
    371 	switch s {
    372 	case "+Inf":
    373 		s = ".inf"
    374 	case "-Inf":
    375 		s = "-.inf"
    376 	case "NaN":
    377 		s = ".nan"
    378 	}
    379 	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
    380 }
    381 
    382 func (e *encoder) nilv() {
    383 	e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE)
    384 }
    385 
    386 func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
    387 	implicit := tag == ""
    388 	e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
    389 	e.emit()
    390 }