| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- package goyaml
- // #include "helpers.h"
- import "C"
- import (
- "reflect"
- "strconv"
- "unsafe"
- )
- type encoder struct {
- emitter C.yaml_emitter_t
- event C.yaml_event_t
- out []byte
- tmp []byte
- tmph *reflect.SliceHeader
- flow bool
- }
- //export outputHandler
- func outputHandler(data unsafe.Pointer, buffer *C.uchar, size C.size_t) C.int {
- e := (*encoder)(data)
- e.tmph.Data = uintptr(unsafe.Pointer(buffer))
- e.tmph.Len = int(size)
- e.tmph.Cap = int(size)
- e.out = append(e.out, e.tmp...)
- return 1
- }
- func newEncoder() (e *encoder) {
- e = &encoder{}
- e.tmph = (*reflect.SliceHeader)(unsafe.Pointer(&e.tmp))
- if C.yaml_emitter_initialize(&e.emitter) == 0 {
- panic("Failed to initialize YAML emitter")
- }
- C.set_output_handler(&e.emitter)
- C.yaml_stream_start_event_initialize(&e.event, C.YAML_UTF8_ENCODING)
- e.emit()
- C.yaml_document_start_event_initialize(&e.event, nil, nil, nil, 1)
- e.emit()
- return e
- }
- func (e *encoder) finish() {
- C.yaml_document_end_event_initialize(&e.event, 1)
- e.emit()
- e.emitter.open_ended = 0
- C.yaml_stream_end_event_initialize(&e.event)
- e.emit()
- }
- func (e *encoder) destroy() {
- C.yaml_emitter_delete(&e.emitter)
- }
- func (e *encoder) emit() {
- // This will internally delete the e.event value.
- if C.yaml_emitter_emit(&e.emitter, &e.event) == 0 &&
- e.event._type != C.YAML_DOCUMENT_END_EVENT &&
- e.event._type != C.YAML_STREAM_END_EVENT {
- if e.emitter.error == C.YAML_EMITTER_ERROR {
- // XXX TESTME
- panic("YAML emitter error: " + C.GoString(e.emitter.problem))
- } else {
- // XXX TESTME
- panic("Unknown YAML emitter error")
- }
- }
- }
- func (e *encoder) fail(msg string) {
- if msg == "" {
- if e.emitter.problem != nil {
- msg = C.GoString(e.emitter.problem)
- } else {
- msg = "Unknown problem generating YAML content"
- }
- }
- panic(msg)
- }
- func (e *encoder) marshal(tag string, in reflect.Value) {
- var value interface{}
- if getter, ok := in.Interface().(Getter); ok {
- tag, value = getter.GetYAML()
- if value == nil {
- e.nilv()
- return
- }
- in = reflect.ValueOf(value)
- }
- switch in.Kind() {
- case reflect.Interface:
- if in.IsNil() {
- e.nilv()
- } else {
- e.marshal(tag, in.Elem())
- }
- case reflect.Map:
- e.mapv(tag, in)
- case reflect.Ptr:
- if in.IsNil() {
- e.nilv()
- } else {
- e.marshal(tag, in.Elem())
- }
- case reflect.Struct:
- e.structv(tag, in)
- case reflect.Slice:
- e.slicev(tag, in)
- case reflect.String:
- e.stringv(tag, in)
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- e.intv(tag, in)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- e.uintv(tag, in)
- case reflect.Float32, reflect.Float64:
- e.floatv(tag, in)
- case reflect.Bool:
- e.boolv(tag, in)
- default:
- panic("Can't marshal type yet: " + in.Type().String())
- }
- }
- func (e *encoder) mapv(tag string, in reflect.Value) {
- e.mappingv(tag, func() {
- for _, k := range in.MapKeys() {
- e.marshal("", k)
- e.marshal("", in.MapIndex(k))
- }
- })
- }
- func (e *encoder) structv(tag string, in reflect.Value) {
- fields, err := getStructFields(in.Type())
- if err != nil {
- panic(err)
- }
- e.mappingv(tag, func() {
- for i, info := range fields.List {
- value := in.Field(i)
- if info.OmitEmpty && isZero(value) {
- continue
- }
- e.marshal("", reflect.ValueOf(info.Key))
- e.flow = info.Flow
- e.marshal("", value)
- }
- })
- }
- func (e *encoder) mappingv(tag string, f func()) {
- var ctag *C.yaml_char_t
- var free func()
- cimplicit := C.int(1)
- if tag != "" {
- ctag, free = ystr(tag)
- defer free()
- cimplicit = 0
- }
- cstyle := C.yaml_mapping_style_t(C.YAML_BLOCK_MAPPING_STYLE)
- if e.flow {
- e.flow = false
- cstyle = C.YAML_FLOW_MAPPING_STYLE
- }
- C.yaml_mapping_start_event_initialize(&e.event, nil, ctag, cimplicit,
- cstyle)
- e.emit()
- f()
- C.yaml_mapping_end_event_initialize(&e.event)
- e.emit()
- }
- func (e *encoder) slicev(tag string, in reflect.Value) {
- var ctag *C.yaml_char_t
- var free func()
- var cimplicit C.int
- if tag != "" {
- ctag, free = ystr(tag)
- defer free()
- cimplicit = 0
- } else {
- cimplicit = 1
- }
- cstyle := C.yaml_sequence_style_t(C.YAML_BLOCK_SEQUENCE_STYLE)
- if e.flow {
- e.flow = false
- cstyle = C.YAML_FLOW_SEQUENCE_STYLE
- }
- C.yaml_sequence_start_event_initialize(&e.event, nil, ctag, cimplicit,
- cstyle)
- e.emit()
- n := in.Len()
- for i := 0; i < n; i++ {
- e.marshal("", in.Index(i))
- }
- C.yaml_sequence_end_event_initialize(&e.event)
- e.emit()
- }
- func (e *encoder) stringv(tag string, in reflect.Value) {
- var style C.yaml_scalar_style_t
- s := in.String()
- if rtag, _ := resolve("", s); rtag != "!!str" {
- style = C.YAML_DOUBLE_QUOTED_SCALAR_STYLE
- } else {
- style = C.YAML_PLAIN_SCALAR_STYLE
- }
- e.emitScalar(s, "", tag, style)
- }
- func (e *encoder) boolv(tag string, in reflect.Value) {
- var s string
- if in.Bool() {
- s = "true"
- } else {
- s = "false"
- }
- e.emitScalar(s, "", tag, C.YAML_PLAIN_SCALAR_STYLE)
- }
- func (e *encoder) intv(tag string, in reflect.Value) {
- s := strconv.FormatInt(in.Int(), 10)
- e.emitScalar(s, "", tag, C.YAML_PLAIN_SCALAR_STYLE)
- }
- func (e *encoder) uintv(tag string, in reflect.Value) {
- s := strconv.FormatUint(in.Uint(), 10)
- e.emitScalar(s, "", tag, C.YAML_PLAIN_SCALAR_STYLE)
- }
- func (e *encoder) floatv(tag string, in reflect.Value) {
- // FIXME: Handle 64 bits here.
- s := strconv.FormatFloat(float64(in.Float()), 'g', -1, 32)
- switch s {
- case "+Inf":
- s = ".inf"
- case "-Inf":
- s = "-.inf"
- case "NaN":
- s = ".nan"
- }
- e.emitScalar(s, "", tag, C.YAML_PLAIN_SCALAR_STYLE)
- }
- func (e *encoder) nilv() {
- e.emitScalar("null", "", "", C.YAML_PLAIN_SCALAR_STYLE)
- }
- func (e *encoder) emitScalar(value, anchor, tag string, style C.yaml_scalar_style_t) {
- var canchor, ctag, cvalue *C.yaml_char_t
- var cimplicit C.int
- var free func()
- if anchor != "" {
- canchor, free = ystr(anchor)
- defer free()
- }
- if tag != "" {
- ctag, free = ystr(tag)
- defer free()
- cimplicit = 0
- style = C.YAML_PLAIN_SCALAR_STYLE
- } else {
- cimplicit = 1
- }
- cvalue, free = ystr(value)
- defer free()
- size := C.int(len(value))
- if C.yaml_scalar_event_initialize(&e.event, canchor, ctag, cvalue, size,
- cimplicit, cimplicit, style) == 0 {
- e.fail("")
- }
- e.emit()
- }
- func ystr(s string) (ys *C.yaml_char_t, free func()) {
- up := unsafe.Pointer(C.CString(s))
- ys = (*C.yaml_char_t)(up)
- free = func() { C.free(up) }
- return ys, free
- }
|