taldir

Directory service to resolve wallet mailboxes by messenger addresses
Log | Files | Refs | Submodules | README | LICENSE

encode.go (19658B)


      1 package toml
      2 
      3 import (
      4 	"bufio"
      5 	"bytes"
      6 	"encoding"
      7 	"encoding/json"
      8 	"errors"
      9 	"fmt"
     10 	"io"
     11 	"math"
     12 	"reflect"
     13 	"sort"
     14 	"strconv"
     15 	"strings"
     16 	"time"
     17 
     18 	"github.com/BurntSushi/toml/internal"
     19 )
     20 
     21 type tomlEncodeError struct{ error }
     22 
     23 var (
     24 	errArrayNilElement = errors.New("toml: cannot encode array with nil element")
     25 	errNonString       = errors.New("toml: cannot encode a map with non-string key type")
     26 	errNoKey           = errors.New("toml: top-level values must be Go maps or structs")
     27 	errAnything        = errors.New("") // used in testing
     28 )
     29 
     30 var dblQuotedReplacer = strings.NewReplacer(
     31 	"\"", "\\\"",
     32 	"\\", "\\\\",
     33 	"\x00", `\u0000`,
     34 	"\x01", `\u0001`,
     35 	"\x02", `\u0002`,
     36 	"\x03", `\u0003`,
     37 	"\x04", `\u0004`,
     38 	"\x05", `\u0005`,
     39 	"\x06", `\u0006`,
     40 	"\x07", `\u0007`,
     41 	"\b", `\b`,
     42 	"\t", `\t`,
     43 	"\n", `\n`,
     44 	"\x0b", `\u000b`,
     45 	"\f", `\f`,
     46 	"\r", `\r`,
     47 	"\x0e", `\u000e`,
     48 	"\x0f", `\u000f`,
     49 	"\x10", `\u0010`,
     50 	"\x11", `\u0011`,
     51 	"\x12", `\u0012`,
     52 	"\x13", `\u0013`,
     53 	"\x14", `\u0014`,
     54 	"\x15", `\u0015`,
     55 	"\x16", `\u0016`,
     56 	"\x17", `\u0017`,
     57 	"\x18", `\u0018`,
     58 	"\x19", `\u0019`,
     59 	"\x1a", `\u001a`,
     60 	"\x1b", `\u001b`,
     61 	"\x1c", `\u001c`,
     62 	"\x1d", `\u001d`,
     63 	"\x1e", `\u001e`,
     64 	"\x1f", `\u001f`,
     65 	"\x7f", `\u007f`,
     66 )
     67 
     68 var (
     69 	marshalToml = reflect.TypeOf((*Marshaler)(nil)).Elem()
     70 	marshalText = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
     71 	timeType    = reflect.TypeOf((*time.Time)(nil)).Elem()
     72 )
     73 
     74 // Marshaler is the interface implemented by types that can marshal themselves
     75 // into valid TOML.
     76 type Marshaler interface {
     77 	MarshalTOML() ([]byte, error)
     78 }
     79 
     80 // Marshal returns a TOML representation of the Go value.
     81 //
     82 // See [Encoder] for a description of the encoding process.
     83 func Marshal(v any) ([]byte, error) {
     84 	buff := new(bytes.Buffer)
     85 	if err := NewEncoder(buff).Encode(v); err != nil {
     86 		return nil, err
     87 	}
     88 	return buff.Bytes(), nil
     89 }
     90 
     91 // Encoder encodes a Go to a TOML document.
     92 //
     93 // The mapping between Go values and TOML values should be precisely the same as
     94 // for [Decode].
     95 //
     96 // time.Time is encoded as a RFC 3339 string, and time.Duration as its string
     97 // representation.
     98 //
     99 // The [Marshaler] and [encoding.TextMarshaler] interfaces are supported to
    100 // encoding the value as custom TOML.
    101 //
    102 // If you want to write arbitrary binary data then you will need to use
    103 // something like base64 since TOML does not have any binary types.
    104 //
    105 // When encoding TOML hashes (Go maps or structs), keys without any sub-hashes
    106 // are encoded first.
    107 //
    108 // Go maps will be sorted alphabetically by key for deterministic output.
    109 //
    110 // The toml struct tag can be used to provide the key name; if omitted the
    111 // struct field name will be used. If the "omitempty" option is present the
    112 // following value will be skipped:
    113 //
    114 //   - arrays, slices, maps, and string with len of 0
    115 //   - struct with all zero values
    116 //   - bool false
    117 //
    118 // If omitzero is given all int and float types with a value of 0 will be
    119 // skipped.
    120 //
    121 // Encoding Go values without a corresponding TOML representation will return an
    122 // error. Examples of this includes maps with non-string keys, slices with nil
    123 // elements, embedded non-struct types, and nested slices containing maps or
    124 // structs. (e.g. [][]map[string]string is not allowed but []map[string]string
    125 // is okay, as is []map[string][]string).
    126 //
    127 // NOTE: only exported keys are encoded due to the use of reflection. Unexported
    128 // keys are silently discarded.
    129 type Encoder struct {
    130 	Indent     string // string for a single indentation level; default is two spaces.
    131 	hasWritten bool   // written any output to w yet?
    132 	w          *bufio.Writer
    133 }
    134 
    135 // NewEncoder create a new Encoder.
    136 func NewEncoder(w io.Writer) *Encoder {
    137 	return &Encoder{w: bufio.NewWriter(w), Indent: "  "}
    138 }
    139 
    140 // Encode writes a TOML representation of the Go value to the [Encoder]'s writer.
    141 //
    142 // An error is returned if the value given cannot be encoded to a valid TOML
    143 // document.
    144 func (enc *Encoder) Encode(v any) error {
    145 	rv := eindirect(reflect.ValueOf(v))
    146 	err := enc.safeEncode(Key([]string{}), rv)
    147 	if err != nil {
    148 		return err
    149 	}
    150 	return enc.w.Flush()
    151 }
    152 
    153 func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) {
    154 	defer func() {
    155 		if r := recover(); r != nil {
    156 			if terr, ok := r.(tomlEncodeError); ok {
    157 				err = terr.error
    158 				return
    159 			}
    160 			panic(r)
    161 		}
    162 	}()
    163 	enc.encode(key, rv)
    164 	return nil
    165 }
    166 
    167 func (enc *Encoder) encode(key Key, rv reflect.Value) {
    168 	// If we can marshal the type to text, then we use that. This prevents the
    169 	// encoder for handling these types as generic structs (or whatever the
    170 	// underlying type of a TextMarshaler is).
    171 	switch {
    172 	case isMarshaler(rv):
    173 		enc.writeKeyValue(key, rv, false)
    174 		return
    175 	case rv.Type() == primitiveType: // TODO: #76 would make this superfluous after implemented.
    176 		enc.encode(key, reflect.ValueOf(rv.Interface().(Primitive).undecoded))
    177 		return
    178 	}
    179 
    180 	k := rv.Kind()
    181 	switch k {
    182 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
    183 		reflect.Int64,
    184 		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
    185 		reflect.Uint64,
    186 		reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
    187 		enc.writeKeyValue(key, rv, false)
    188 	case reflect.Array, reflect.Slice:
    189 		if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) {
    190 			enc.eArrayOfTables(key, rv)
    191 		} else {
    192 			enc.writeKeyValue(key, rv, false)
    193 		}
    194 	case reflect.Interface:
    195 		if rv.IsNil() {
    196 			return
    197 		}
    198 		enc.encode(key, rv.Elem())
    199 	case reflect.Map:
    200 		if rv.IsNil() {
    201 			return
    202 		}
    203 		enc.eTable(key, rv)
    204 	case reflect.Ptr:
    205 		if rv.IsNil() {
    206 			return
    207 		}
    208 		enc.encode(key, rv.Elem())
    209 	case reflect.Struct:
    210 		enc.eTable(key, rv)
    211 	default:
    212 		encPanic(fmt.Errorf("unsupported type for key '%s': %s", key, k))
    213 	}
    214 }
    215 
    216 // eElement encodes any value that can be an array element.
    217 func (enc *Encoder) eElement(rv reflect.Value) {
    218 	switch v := rv.Interface().(type) {
    219 	case time.Time: // Using TextMarshaler adds extra quotes, which we don't want.
    220 		format := time.RFC3339Nano
    221 		switch v.Location() {
    222 		case internal.LocalDatetime:
    223 			format = "2006-01-02T15:04:05.999999999"
    224 		case internal.LocalDate:
    225 			format = "2006-01-02"
    226 		case internal.LocalTime:
    227 			format = "15:04:05.999999999"
    228 		}
    229 		switch v.Location() {
    230 		default:
    231 			enc.write(v.Format(format))
    232 		case internal.LocalDatetime, internal.LocalDate, internal.LocalTime:
    233 			enc.write(v.In(time.UTC).Format(format))
    234 		}
    235 		return
    236 	case Marshaler:
    237 		s, err := v.MarshalTOML()
    238 		if err != nil {
    239 			encPanic(err)
    240 		}
    241 		if s == nil {
    242 			encPanic(errors.New("MarshalTOML returned nil and no error"))
    243 		}
    244 		enc.w.Write(s)
    245 		return
    246 	case encoding.TextMarshaler:
    247 		s, err := v.MarshalText()
    248 		if err != nil {
    249 			encPanic(err)
    250 		}
    251 		if s == nil {
    252 			encPanic(errors.New("MarshalText returned nil and no error"))
    253 		}
    254 		enc.writeQuoted(string(s))
    255 		return
    256 	case time.Duration:
    257 		enc.writeQuoted(v.String())
    258 		return
    259 	case json.Number:
    260 		n, _ := rv.Interface().(json.Number)
    261 
    262 		if n == "" { /// Useful zero value.
    263 			enc.w.WriteByte('0')
    264 			return
    265 		} else if v, err := n.Int64(); err == nil {
    266 			enc.eElement(reflect.ValueOf(v))
    267 			return
    268 		} else if v, err := n.Float64(); err == nil {
    269 			enc.eElement(reflect.ValueOf(v))
    270 			return
    271 		}
    272 		encPanic(fmt.Errorf("unable to convert %q to int64 or float64", n))
    273 	}
    274 
    275 	switch rv.Kind() {
    276 	case reflect.Ptr:
    277 		enc.eElement(rv.Elem())
    278 		return
    279 	case reflect.String:
    280 		enc.writeQuoted(rv.String())
    281 	case reflect.Bool:
    282 		enc.write(strconv.FormatBool(rv.Bool()))
    283 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    284 		enc.write(strconv.FormatInt(rv.Int(), 10))
    285 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    286 		enc.write(strconv.FormatUint(rv.Uint(), 10))
    287 	case reflect.Float32:
    288 		f := rv.Float()
    289 		if math.IsNaN(f) {
    290 			if math.Signbit(f) {
    291 				enc.write("-")
    292 			}
    293 			enc.write("nan")
    294 		} else if math.IsInf(f, 0) {
    295 			if math.Signbit(f) {
    296 				enc.write("-")
    297 			}
    298 			enc.write("inf")
    299 		} else {
    300 			enc.write(floatAddDecimal(strconv.FormatFloat(f, 'g', -1, 32)))
    301 		}
    302 	case reflect.Float64:
    303 		f := rv.Float()
    304 		if math.IsNaN(f) {
    305 			if math.Signbit(f) {
    306 				enc.write("-")
    307 			}
    308 			enc.write("nan")
    309 		} else if math.IsInf(f, 0) {
    310 			if math.Signbit(f) {
    311 				enc.write("-")
    312 			}
    313 			enc.write("inf")
    314 		} else {
    315 			enc.write(floatAddDecimal(strconv.FormatFloat(f, 'g', -1, 64)))
    316 		}
    317 	case reflect.Array, reflect.Slice:
    318 		enc.eArrayOrSliceElement(rv)
    319 	case reflect.Struct:
    320 		enc.eStruct(nil, rv, true)
    321 	case reflect.Map:
    322 		enc.eMap(nil, rv, true)
    323 	case reflect.Interface:
    324 		enc.eElement(rv.Elem())
    325 	default:
    326 		encPanic(fmt.Errorf("unexpected type: %s", fmtType(rv.Interface())))
    327 	}
    328 }
    329 
    330 // By the TOML spec, all floats must have a decimal with at least one number on
    331 // either side.
    332 func floatAddDecimal(fstr string) string {
    333 	for _, c := range fstr {
    334 		if c == 'e' { // Exponent syntax
    335 			return fstr
    336 		}
    337 		if c == '.' {
    338 			return fstr
    339 		}
    340 	}
    341 	return fstr + ".0"
    342 }
    343 
    344 func (enc *Encoder) writeQuoted(s string) {
    345 	enc.write(`"` + dblQuotedReplacer.Replace(s) + `"`)
    346 }
    347 
    348 func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
    349 	length := rv.Len()
    350 	enc.write("[")
    351 	for i := 0; i < length; i++ {
    352 		elem := eindirect(rv.Index(i))
    353 		enc.eElement(elem)
    354 		if i != length-1 {
    355 			enc.write(", ")
    356 		}
    357 	}
    358 	enc.write("]")
    359 }
    360 
    361 func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
    362 	if len(key) == 0 {
    363 		encPanic(errNoKey)
    364 	}
    365 	for i := 0; i < rv.Len(); i++ {
    366 		trv := eindirect(rv.Index(i))
    367 		if isNil(trv) {
    368 			continue
    369 		}
    370 		enc.newline()
    371 		enc.writef("%s[[%s]]", enc.indentStr(key), key)
    372 		enc.newline()
    373 		enc.eMapOrStruct(key, trv, false)
    374 	}
    375 }
    376 
    377 func (enc *Encoder) eTable(key Key, rv reflect.Value) {
    378 	if len(key) == 1 {
    379 		// Output an extra newline between top-level tables.
    380 		// (The newline isn't written if nothing else has been written though.)
    381 		enc.newline()
    382 	}
    383 	if len(key) > 0 {
    384 		enc.writef("%s[%s]", enc.indentStr(key), key)
    385 		enc.newline()
    386 	}
    387 	enc.eMapOrStruct(key, rv, false)
    388 }
    389 
    390 func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value, inline bool) {
    391 	switch rv.Kind() {
    392 	case reflect.Map:
    393 		enc.eMap(key, rv, inline)
    394 	case reflect.Struct:
    395 		enc.eStruct(key, rv, inline)
    396 	default:
    397 		// Should never happen?
    398 		panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String())
    399 	}
    400 }
    401 
    402 func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
    403 	rt := rv.Type()
    404 	if rt.Key().Kind() != reflect.String {
    405 		encPanic(errNonString)
    406 	}
    407 
    408 	// Sort keys so that we have deterministic output. And write keys directly
    409 	// underneath this key first, before writing sub-structs or sub-maps.
    410 	var mapKeysDirect, mapKeysSub []reflect.Value
    411 	for _, mapKey := range rv.MapKeys() {
    412 		if typeIsTable(tomlTypeOfGo(eindirect(rv.MapIndex(mapKey)))) {
    413 			mapKeysSub = append(mapKeysSub, mapKey)
    414 		} else {
    415 			mapKeysDirect = append(mapKeysDirect, mapKey)
    416 		}
    417 	}
    418 
    419 	writeMapKeys := func(mapKeys []reflect.Value, trailC bool) {
    420 		sort.Slice(mapKeys, func(i, j int) bool { return mapKeys[i].String() < mapKeys[j].String() })
    421 		for i, mapKey := range mapKeys {
    422 			val := eindirect(rv.MapIndex(mapKey))
    423 			if isNil(val) {
    424 				continue
    425 			}
    426 
    427 			if inline {
    428 				enc.writeKeyValue(Key{mapKey.String()}, val, true)
    429 				if trailC || i != len(mapKeys)-1 {
    430 					enc.write(", ")
    431 				}
    432 			} else {
    433 				enc.encode(key.add(mapKey.String()), val)
    434 			}
    435 		}
    436 	}
    437 
    438 	if inline {
    439 		enc.write("{")
    440 	}
    441 	writeMapKeys(mapKeysDirect, len(mapKeysSub) > 0)
    442 	writeMapKeys(mapKeysSub, false)
    443 	if inline {
    444 		enc.write("}")
    445 	}
    446 }
    447 
    448 func pointerTo(t reflect.Type) reflect.Type {
    449 	if t.Kind() == reflect.Ptr {
    450 		return pointerTo(t.Elem())
    451 	}
    452 	return t
    453 }
    454 
    455 func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
    456 	// Write keys for fields directly under this key first, because if we write
    457 	// a field that creates a new table then all keys under it will be in that
    458 	// table (not the one we're writing here).
    459 	//
    460 	// Fields is a [][]int: for fieldsDirect this always has one entry (the
    461 	// struct index). For fieldsSub it contains two entries: the parent field
    462 	// index from tv, and the field indexes for the fields of the sub.
    463 	var (
    464 		rt                      = rv.Type()
    465 		fieldsDirect, fieldsSub [][]int
    466 		addFields               func(rt reflect.Type, rv reflect.Value, start []int)
    467 	)
    468 	addFields = func(rt reflect.Type, rv reflect.Value, start []int) {
    469 		for i := 0; i < rt.NumField(); i++ {
    470 			f := rt.Field(i)
    471 			isEmbed := f.Anonymous && pointerTo(f.Type).Kind() == reflect.Struct
    472 			if f.PkgPath != "" && !isEmbed { /// Skip unexported fields.
    473 				continue
    474 			}
    475 			opts := getOptions(f.Tag)
    476 			if opts.skip {
    477 				continue
    478 			}
    479 
    480 			frv := eindirect(rv.Field(i))
    481 
    482 			// Need to make a copy because ... ehm, I don't know why... I guess
    483 			// allocating a new array can cause it to fail(?)
    484 			//
    485 			// Done for: https://github.com/BurntSushi/toml/issues/430
    486 			// Previously only on 32bit for: https://github.com/BurntSushi/toml/issues/314
    487 			copyStart := make([]int, len(start))
    488 			copy(copyStart, start)
    489 			start = copyStart
    490 
    491 			// Treat anonymous struct fields with tag names as though they are
    492 			// not anonymous, like encoding/json does.
    493 			//
    494 			// Non-struct anonymous fields use the normal encoding logic.
    495 			if isEmbed {
    496 				if getOptions(f.Tag).name == "" && frv.Kind() == reflect.Struct {
    497 					addFields(frv.Type(), frv, append(start, f.Index...))
    498 					continue
    499 				}
    500 			}
    501 
    502 			if typeIsTable(tomlTypeOfGo(frv)) {
    503 				fieldsSub = append(fieldsSub, append(start, f.Index...))
    504 			} else {
    505 				fieldsDirect = append(fieldsDirect, append(start, f.Index...))
    506 			}
    507 		}
    508 	}
    509 	addFields(rt, rv, nil)
    510 
    511 	writeFields := func(fields [][]int, totalFields int) {
    512 		for _, fieldIndex := range fields {
    513 			fieldType := rt.FieldByIndex(fieldIndex)
    514 			fieldVal := rv.FieldByIndex(fieldIndex)
    515 
    516 			opts := getOptions(fieldType.Tag)
    517 			if opts.skip {
    518 				continue
    519 			}
    520 			if opts.omitempty && isEmpty(fieldVal) {
    521 				continue
    522 			}
    523 
    524 			fieldVal = eindirect(fieldVal)
    525 
    526 			if isNil(fieldVal) { /// Don't write anything for nil fields.
    527 				continue
    528 			}
    529 
    530 			keyName := fieldType.Name
    531 			if opts.name != "" {
    532 				keyName = opts.name
    533 			}
    534 
    535 			if opts.omitzero && isZero(fieldVal) {
    536 				continue
    537 			}
    538 
    539 			if inline {
    540 				enc.writeKeyValue(Key{keyName}, fieldVal, true)
    541 				if fieldIndex[0] != totalFields-1 {
    542 					enc.write(", ")
    543 				}
    544 			} else {
    545 				enc.encode(key.add(keyName), fieldVal)
    546 			}
    547 		}
    548 	}
    549 
    550 	if inline {
    551 		enc.write("{")
    552 	}
    553 
    554 	l := len(fieldsDirect) + len(fieldsSub)
    555 	writeFields(fieldsDirect, l)
    556 	writeFields(fieldsSub, l)
    557 	if inline {
    558 		enc.write("}")
    559 	}
    560 }
    561 
    562 // tomlTypeOfGo returns the TOML type name of the Go value's type.
    563 //
    564 // It is used to determine whether the types of array elements are mixed (which
    565 // is forbidden). If the Go value is nil, then it is illegal for it to be an
    566 // array element, and valueIsNil is returned as true.
    567 //
    568 // The type may be `nil`, which means no concrete TOML type could be found.
    569 func tomlTypeOfGo(rv reflect.Value) tomlType {
    570 	if isNil(rv) || !rv.IsValid() {
    571 		return nil
    572 	}
    573 
    574 	if rv.Kind() == reflect.Struct {
    575 		if rv.Type() == timeType {
    576 			return tomlDatetime
    577 		}
    578 		if isMarshaler(rv) {
    579 			return tomlString
    580 		}
    581 		return tomlHash
    582 	}
    583 
    584 	if isMarshaler(rv) {
    585 		return tomlString
    586 	}
    587 
    588 	switch rv.Kind() {
    589 	case reflect.Bool:
    590 		return tomlBool
    591 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
    592 		reflect.Int64,
    593 		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
    594 		reflect.Uint64:
    595 		return tomlInteger
    596 	case reflect.Float32, reflect.Float64:
    597 		return tomlFloat
    598 	case reflect.Array, reflect.Slice:
    599 		if isTableArray(rv) {
    600 			return tomlArrayHash
    601 		}
    602 		return tomlArray
    603 	case reflect.Ptr, reflect.Interface:
    604 		return tomlTypeOfGo(rv.Elem())
    605 	case reflect.String:
    606 		return tomlString
    607 	case reflect.Map:
    608 		return tomlHash
    609 	default:
    610 		encPanic(errors.New("unsupported type: " + rv.Kind().String()))
    611 		panic("unreachable")
    612 	}
    613 }
    614 
    615 func isMarshaler(rv reflect.Value) bool {
    616 	return rv.Type().Implements(marshalText) || rv.Type().Implements(marshalToml)
    617 }
    618 
    619 // isTableArray reports if all entries in the array or slice are a table.
    620 func isTableArray(arr reflect.Value) bool {
    621 	if isNil(arr) || !arr.IsValid() || arr.Len() == 0 {
    622 		return false
    623 	}
    624 
    625 	ret := true
    626 	for i := 0; i < arr.Len(); i++ {
    627 		tt := tomlTypeOfGo(eindirect(arr.Index(i)))
    628 		// Don't allow nil.
    629 		if tt == nil {
    630 			encPanic(errArrayNilElement)
    631 		}
    632 
    633 		if ret && !typeEqual(tomlHash, tt) {
    634 			ret = false
    635 		}
    636 	}
    637 	return ret
    638 }
    639 
    640 type tagOptions struct {
    641 	skip      bool // "-"
    642 	name      string
    643 	omitempty bool
    644 	omitzero  bool
    645 }
    646 
    647 func getOptions(tag reflect.StructTag) tagOptions {
    648 	t := tag.Get("toml")
    649 	if t == "-" {
    650 		return tagOptions{skip: true}
    651 	}
    652 	var opts tagOptions
    653 	parts := strings.Split(t, ",")
    654 	opts.name = parts[0]
    655 	for _, s := range parts[1:] {
    656 		switch s {
    657 		case "omitempty":
    658 			opts.omitempty = true
    659 		case "omitzero":
    660 			opts.omitzero = true
    661 		}
    662 	}
    663 	return opts
    664 }
    665 
    666 func isZero(rv reflect.Value) bool {
    667 	switch rv.Kind() {
    668 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    669 		return rv.Int() == 0
    670 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    671 		return rv.Uint() == 0
    672 	case reflect.Float32, reflect.Float64:
    673 		return rv.Float() == 0.0
    674 	}
    675 	return false
    676 }
    677 
    678 func isEmpty(rv reflect.Value) bool {
    679 	switch rv.Kind() {
    680 	case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
    681 		return rv.Len() == 0
    682 	case reflect.Struct:
    683 		if rv.Type().Comparable() {
    684 			return reflect.Zero(rv.Type()).Interface() == rv.Interface()
    685 		}
    686 		// Need to also check if all the fields are empty, otherwise something
    687 		// like this with uncomparable types will always return true:
    688 		//
    689 		//   type a struct{ field b }
    690 		//   type b struct{ s []string }
    691 		//   s := a{field: b{s: []string{"AAA"}}}
    692 		for i := 0; i < rv.NumField(); i++ {
    693 			if !isEmpty(rv.Field(i)) {
    694 				return false
    695 			}
    696 		}
    697 		return true
    698 	case reflect.Bool:
    699 		return !rv.Bool()
    700 	case reflect.Ptr:
    701 		return rv.IsNil()
    702 	}
    703 	return false
    704 }
    705 
    706 func (enc *Encoder) newline() {
    707 	if enc.hasWritten {
    708 		enc.write("\n")
    709 	}
    710 }
    711 
    712 // Write a key/value pair:
    713 //
    714 //	key = <any value>
    715 //
    716 // This is also used for "k = v" in inline tables; so something like this will
    717 // be written in three calls:
    718 //
    719 //	┌───────────────────┐
    720 //	│      ┌───┐  ┌────┐│
    721 //	v      v   v  v    vv
    722 //	key = {k = 1, k2 = 2}
    723 func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) {
    724 	/// Marshaler used on top-level document; call eElement() to just call
    725 	/// Marshal{TOML,Text}.
    726 	if len(key) == 0 {
    727 		enc.eElement(val)
    728 		return
    729 	}
    730 	enc.writef("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
    731 	enc.eElement(val)
    732 	if !inline {
    733 		enc.newline()
    734 	}
    735 }
    736 
    737 func (enc *Encoder) write(s string) {
    738 	_, err := enc.w.WriteString(s)
    739 	if err != nil {
    740 		encPanic(err)
    741 	}
    742 	enc.hasWritten = true
    743 }
    744 
    745 func (enc *Encoder) writef(format string, v ...any) {
    746 	_, err := fmt.Fprintf(enc.w, format, v...)
    747 	if err != nil {
    748 		encPanic(err)
    749 	}
    750 	enc.hasWritten = true
    751 }
    752 
    753 func (enc *Encoder) indentStr(key Key) string {
    754 	return strings.Repeat(enc.Indent, len(key)-1)
    755 }
    756 
    757 func encPanic(err error) {
    758 	panic(tomlEncodeError{err})
    759 }
    760 
    761 // Resolve any level of pointers to the actual value (e.g. **string → string).
    762 func eindirect(v reflect.Value) reflect.Value {
    763 	if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface {
    764 		if isMarshaler(v) {
    765 			return v
    766 		}
    767 		if v.CanAddr() { /// Special case for marshalers; see #358.
    768 			if pv := v.Addr(); isMarshaler(pv) {
    769 				return pv
    770 			}
    771 		}
    772 		return v
    773 	}
    774 
    775 	if v.IsNil() {
    776 		return v
    777 	}
    778 
    779 	return eindirect(v.Elem())
    780 }
    781 
    782 func isNil(rv reflect.Value) bool {
    783 	switch rv.Kind() {
    784 	case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
    785 		return rv.IsNil()
    786 	default:
    787 		return false
    788 	}
    789 }