<commit_before>/*--------------------------------------------------------*\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: https://hprose.com                     |
|                                                          |
| encoding/array_decoder.go                                |
|                                                          |
| LastModified: Jun 13, 2020                               |
| Author: Ma Bingyao <andot@hprose.com>                    |
|                                                          |
\*________________________________________________________*/

package encoding

import (
	"reflect"
	"unsafe"

	"github.com/modern-go/reflect2"
)

// arrayDecoder is the implementation of ValueDecoder for [N]T.
type arrayDecoder struct {
	at        *reflect2.UnsafeArrayType
	et        reflect.Type
	empty     unsafe.Pointer
	st        *reflect2.UnsafeSliceType
	emptyElem unsafe.Pointer
	tempElem  unsafe.Pointer
	readElem  func(dec *Decoder, et reflect.Type, ep unsafe.Pointer)
}

func (valdec arrayDecoder) Decode(dec *Decoder, p interface{}, tag byte) {
	switch tag {
	case TagNull, TagEmpty:
		valdec.at.UnsafeSet(reflect2.PtrOf(p), valdec.empty)
	case TagList:
		length := valdec.at.Len()
		count := dec.ReadInt()
		slice := reflect2.PtrOf(sliceHeader{reflect2.PtrOf(p), length, length})
		dec.AddReference(p)
		n := length
		if n > count {
			n = count
		}
		for i := 0; i < n; i++ {
			valdec.readElem(dec, valdec.et, valdec.st.UnsafeGetIndex(slice, i))
		}
		switch {
		case n < length:
			for i := n; i < length; i++ {
				valdec.st.UnsafeSetIndex(slice, i, valdec.emptyElem)
			}
		case n < count:
			for i := n; i < count; i++ {
				valdec.readElem(dec, valdec.et, valdec.tempElem)
			}
		}
		dec.Skip()
	default:
		dec.decodeError(valdec.at.Type1(), tag)
	}
}

func (valdec arrayDecoder) Type() reflect.Type {
	return valdec.at.Type1()
}

// ArrayDecoder returns a ValueDecoder for [N]T.
func ArrayDecoder(t reflect.Type, readElem func(dec *Decoder, et reflect.Type, ep unsafe.Pointer)) ValueDecoder {
	at := reflect2.Type2(t).(*reflect2.UnsafeArrayType)
	et := t.Elem()
	return arrayDecoder{
		at,
		et,
		at.UnsafeNew(),
		reflect2.Type2(reflect.SliceOf(et)).(*reflect2.UnsafeSliceType),
		reflect2.Type2(et).UnsafeNew(),
		reflect2.Type2(et).UnsafeNew(),
		readElem,
	}
}

func boolArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*bool)(ep) = dec.decodeBool(et, dec.NextByte())
	})
}

func intArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*int)(ep) = dec.decodeInt(et, dec.NextByte())
	})
}

func int8ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*int8)(ep) = dec.decodeInt8(et, dec.NextByte())
	})
}

func int16ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*int16)(ep) = dec.decodeInt16(et, dec.NextByte())
	})
}

func int32ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*int32)(ep) = dec.decodeInt32(et, dec.NextByte())
	})
}

func int64ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*int64)(ep) = dec.decodeInt64(et, dec.NextByte())
	})
}

func uintArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*uint)(ep) = dec.decodeUint(et, dec.NextByte())
	})
}

func uint8ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*uint8)(ep) = dec.decodeUint8(et, dec.NextByte())
	})
}

func uint16ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*uint16)(ep) = dec.decodeUint16(et, dec.NextByte())
	})
}

func uint32ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*uint32)(ep) = dec.decodeUint32(et, dec.NextByte())
	})
}

func uint64ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*uint64)(ep) = dec.decodeUint64(et, dec.NextByte())
	})
}

func uintptrArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*uintptr)(ep) = dec.decodeUintptr(et, dec.NextByte())
	})
}

func float32ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*float32)(ep) = dec.decodeFloat32(et, dec.NextByte())
	})
}

func float64ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*float64)(ep) = dec.decodeFloat64(et, dec.NextByte())
	})
}

func complex64ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*complex64)(ep) = dec.decodeComplex64(et, dec.NextByte())
	})
}

func complex128ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*complex128)(ep) = dec.decodeComplex128(et, dec.NextByte())
	})
}

func interfaceArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*interface{})(ep) = dec.decodeInterface(dec.NextByte())
	})
}

func stringArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*string)(ep) = dec.decodeString(et, dec.NextByte())
	})
}

func otherArrayDecoder(t reflect.Type) ValueDecoder {
	valdec := getValueDecoder(t.Elem())
	et2 := reflect2.Type2(t.Elem())
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		valdec.Decode(dec, et2.UnsafeIndirect(ep), dec.NextByte())
	})
}

type byteArrayDecoder struct {
	arrayDecoder
}

func (valdec byteArrayDecoder) copy(p interface{}, data []byte) {
	count := len(data)
	length := valdec.at.Len()
	slice := *(*[]byte)(unsafe.Pointer(&sliceHeader{reflect2.PtrOf(p), length, length}))
	copy(slice, data)
	if length > count {
		for i := count; i < length; i++ {
			slice[i] = 0
		}
	}
}

func (valdec byteArrayDecoder) Decode(dec *Decoder, p interface{}, tag byte) {
	switch tag {
	case TagBytes:
		data := dec.UnsafeNext(dec.ReadInt())
		dec.Skip()
		valdec.copy(p, data)
		dec.AddReference(p)
	case TagUTF8Char:
		data, _ := dec.readStringAsBytes(1)
		valdec.copy(p, data)
	case TagString:
		if dec.IsSimple() {
			data, _ := dec.readStringAsBytes(dec.ReadInt())
			dec.Skip()
			valdec.copy(p, data)
		} else {
			valdec.copy(p, reflect2.UnsafeCastString(dec.ReadString()))
		}
	default:
		valdec.arrayDecoder.Decode(dec, p, tag)
	}
}

// ByteArrayDecoder returns a ValueDecoder for [N]byte.
func ByteArrayDecoder(t reflect.Type) ValueDecoder {
	return byteArrayDecoder{uint8ArrayDecoder(t).(arrayDecoder)}
}
<commit_msg>thread safe<commit_after>/*--------------------------------------------------------*\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: https://hprose.com                     |
|                                                          |
| encoding/array_decoder.go                                |
|                                                          |
| LastModified: Jun 13, 2020                               |
| Author: Ma Bingyao <andot@hprose.com>                    |
|                                                          |
\*________________________________________________________*/

package encoding

import (
	"reflect"
	"unsafe"

	"github.com/modern-go/reflect2"
)

// arrayDecoder is the implementation of ValueDecoder for [N]T.
type arrayDecoder struct {
	at        *reflect2.UnsafeArrayType
	et        reflect.Type
	empty     unsafe.Pointer
	st        *reflect2.UnsafeSliceType
	emptyElem unsafe.Pointer
	readElem  func(dec *Decoder, et reflect.Type, ep unsafe.Pointer)
}

func (valdec arrayDecoder) Decode(dec *Decoder, p interface{}, tag byte) {
	switch tag {
	case TagNull, TagEmpty:
		valdec.at.UnsafeSet(reflect2.PtrOf(p), valdec.empty)
	case TagList:
		length := valdec.at.Len()
		count := dec.ReadInt()
		slice := reflect2.PtrOf(sliceHeader{reflect2.PtrOf(p), length, length})
		dec.AddReference(p)
		n := length
		if n > count {
			n = count
		}
		for i := 0; i < n; i++ {
			valdec.readElem(dec, valdec.et, valdec.st.UnsafeGetIndex(slice, i))
		}
		switch {
		case n < length:
			for i := n; i < length; i++ {
				valdec.st.UnsafeSetIndex(slice, i, valdec.emptyElem)
			}
		case n < count:
			temp := reflect2.Type2(valdec.et).UnsafeNew()
			for i := n; i < count; i++ {
				valdec.readElem(dec, valdec.et, temp)
			}
		}
		dec.Skip()
	default:
		dec.decodeError(valdec.at.Type1(), tag)
	}
}

func (valdec arrayDecoder) Type() reflect.Type {
	return valdec.at.Type1()
}

// ArrayDecoder returns a ValueDecoder for [N]T.
func ArrayDecoder(t reflect.Type, readElem func(dec *Decoder, et reflect.Type, ep unsafe.Pointer)) ValueDecoder {
	at := reflect2.Type2(t).(*reflect2.UnsafeArrayType)
	et := t.Elem()
	return arrayDecoder{
		at,
		et,
		at.UnsafeNew(),
		reflect2.Type2(reflect.SliceOf(et)).(*reflect2.UnsafeSliceType),
		reflect2.Type2(et).UnsafeNew(),
		readElem,
	}
}

func boolArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*bool)(ep) = dec.decodeBool(et, dec.NextByte())
	})
}

func intArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*int)(ep) = dec.decodeInt(et, dec.NextByte())
	})
}

func int8ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*int8)(ep) = dec.decodeInt8(et, dec.NextByte())
	})
}

func int16ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*int16)(ep) = dec.decodeInt16(et, dec.NextByte())
	})
}

func int32ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*int32)(ep) = dec.decodeInt32(et, dec.NextByte())
	})
}

func int64ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*int64)(ep) = dec.decodeInt64(et, dec.NextByte())
	})
}

func uintArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*uint)(ep) = dec.decodeUint(et, dec.NextByte())
	})
}

func uint8ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*uint8)(ep) = dec.decodeUint8(et, dec.NextByte())
	})
}

func uint16ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*uint16)(ep) = dec.decodeUint16(et, dec.NextByte())
	})
}

func uint32ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*uint32)(ep) = dec.decodeUint32(et, dec.NextByte())
	})
}

func uint64ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*uint64)(ep) = dec.decodeUint64(et, dec.NextByte())
	})
}

func uintptrArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*uintptr)(ep) = dec.decodeUintptr(et, dec.NextByte())
	})
}

func float32ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*float32)(ep) = dec.decodeFloat32(et, dec.NextByte())
	})
}

func float64ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*float64)(ep) = dec.decodeFloat64(et, dec.NextByte())
	})
}

func complex64ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*complex64)(ep) = dec.decodeComplex64(et, dec.NextByte())
	})
}

func complex128ArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*complex128)(ep) = dec.decodeComplex128(et, dec.NextByte())
	})
}

func interfaceArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*interface{})(ep) = dec.decodeInterface(dec.NextByte())
	})
}

func stringArrayDecoder(t reflect.Type) ValueDecoder {
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		*(*string)(ep) = dec.decodeString(et, dec.NextByte())
	})
}

func otherArrayDecoder(t reflect.Type) ValueDecoder {
	valdec := getValueDecoder(t.Elem())
	et2 := reflect2.Type2(t.Elem())
	return ArrayDecoder(t, func(dec *Decoder, et reflect.Type, ep unsafe.Pointer) {
		valdec.Decode(dec, et2.UnsafeIndirect(ep), dec.NextByte())
	})
}

type byteArrayDecoder struct {
	arrayDecoder
}

func (valdec byteArrayDecoder) copy(p interface{}, data []byte) {
	count := len(data)
	length := valdec.at.Len()
	slice := *(*[]byte)(unsafe.Pointer(&sliceHeader{reflect2.PtrOf(p), length, length}))
	copy(slice, data)
	if length > count {
		for i := count; i < length; i++ {
			slice[i] = 0
		}
	}
}

func (valdec byteArrayDecoder) Decode(dec *Decoder, p interface{}, tag byte) {
	switch tag {
	case TagBytes:
		data := dec.UnsafeNext(dec.ReadInt())
		dec.Skip()
		valdec.copy(p, data)
		dec.AddReference(p)
	case TagUTF8Char:
		data, _ := dec.readStringAsBytes(1)
		valdec.copy(p, data)
	case TagString:
		if dec.IsSimple() {
			data, _ := dec.readStringAsBytes(dec.ReadInt())
			dec.Skip()
			valdec.copy(p, data)
		} else {
			valdec.copy(p, reflect2.UnsafeCastString(dec.ReadString()))
		}
	default:
		valdec.arrayDecoder.Decode(dec, p, tag)
	}
}

// ByteArrayDecoder returns a ValueDecoder for [N]byte.
func ByteArrayDecoder(t reflect.Type) ValueDecoder {
	return byteArrayDecoder{uint8ArrayDecoder(t).(arrayDecoder)}
}
<|endoftext|>


<commit_before>// Package frame contains data structures and
// related functions for parsing and searching
// through Dwarf .debug_frame data.
package frame

import (
	"bytes"
	"encoding/binary"
	"unicode/utf8"
)

type parsefunc func(*bytes.Buffer, CommonEntries, uint32) (parsefunc, CommonEntries, uint32)

type CommonEntries []*CommonInformationEntry

// Represents a Common Information Entry in
// the Dwarf .debug_frame section.
type CommonInformationEntry struct {
	Length                 uint32
	CIE_id                 uint32
	Version                uint8
	Augmentation           string
	CodeAlignmentFactor    uint64
	DataAlignmentFactor    uint64
	ReturnAddressRegister  byte
	InitialInstructions    []byte
	FrameDescriptorEntries []*FrameDescriptorEntry
}

// Represents a Frame Descriptor Entry in the
// Dwarf .debug_frame section.
type FrameDescriptorEntry struct {
	Length          uint32
	CIE_pointer     *CommonInformationEntry
	InitialLocation uint64
	AddressRange    uint64
	Instructions    []byte
}

const (
	DW_CFA_advance_loc        = (0x1 << 6) // High 2 bits: 0x1, low 6: delta
	DW_CFA_offset             = (0x2 << 6) // High 2 bits: 0x2, low 6: register
	DW_CFA_restore            = (0x3 << 6) // High 2 bits: 0x3, low 6: register
	DW_CFA_nop                = 0x0        // No ops
	DW_CFA_set_loc            = 0x1        // op1: address
	DW_CFA_advance_loc1       = iota       // op1: 1-bytes delta
	DW_CFA_advance_loc2                    // op1: 2-byte delta
	DW_CFA_advance_loc4                    // op1: 4-byte delta
	DW_CFA_offset_extended                 // op1: ULEB128 register, op2: ULEB128 offset
	DW_CFA_restore_extended                // op1: ULEB128 register
	DW_CFA_undefined                       // op1: ULEB128 register
	DW_CFA_same_value                      // op1: ULEB128 register
	DW_CFA_register                        // op1: ULEB128 register, op2: ULEB128 register
	DW_CFA_remember_state                  // No ops
	DW_CFA_restore_state                   // No ops
	DW_CFA_def_cfa                         // op1: ULEB128 register, op2: ULEB128 offset
	DW_CFA_def_cfa_register                // op1: ULEB128 register
	DW_CFA_def_cfa_offset                  // op1: ULEB128 offset
	DW_CFA_def_cfa_expression              // op1: BLOCK
	DW_CFA_expression                      // op1: ULEB128 register, op2: BLOCK
	DW_CFA_offset_extended_sf              // op1: ULEB128 register, op2: SLEB128 offset
	DW_CFA_def_cfa_sf                      // op1: ULEB128 register, op2: SLEB128 offset
	DW_CFA_def_cfa_offset_sf               // op1: SLEB128 offset
	DW_CFA_val_offset                      // op1: ULEB128, op2: ULEB128
	DW_CFA_val_offset_sf                   // op1: ULEB128, op2: SLEB128
	DW_CFA_val_expression                  // op1: ULEB128, op2: BLOCK
	DW_CFA_lo_user            = 0x1c       // op1: BLOCK
	DW_CFA_hi_user            = 0x3f       // op1: ULEB128 register, op2: BLOCK
)

// Parse take in data (a byte slice) and returns a slice of
// CommonInformationEntry structures. Each CommonInformationEntry
// has a slice of FrameDescriptorEntry structures.
func Parse(data []byte) CommonEntries {
	var (
		length  uint32
		entries CommonEntries
		reader  = bytes.NewBuffer(data)
	)

	for fn := parseLength; reader.Len() != 0; {
		fn, entries, length = fn(reader, entries, length)
	}

	return entries
}

// DecodeLEB128 decodes a Little Endian Base 128
// represented number.
func DecodeLEB128(reader *bytes.Buffer) (uint64, uint32) {
	var (
		result uint64
		shift  uint64
		length uint32
	)

	for {
		b, err := reader.ReadByte()
		if err != nil {
			panic("Could not parse LEB128 value")
		}
		length++

		result |= uint64((uint(b) & 0x7f) << shift)

		// If high order bit is 1.
		if b&0x80 == 0 {
			break
		}

		shift += 7
	}

	return result, length
}

func cieEntry(data []byte) bool {
	return bytes.Equal(data, []byte{0xff, 0xff, 0xff, 0xff})
}

func parseLength(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	binary.Read(reader, binary.LittleEndian, &length)

	if cieEntry(reader.Bytes()[0:4]) {
		return parseCIEID, append(entries, &CommonInformationEntry{Length: length}), length
	}

	entry := entries[len(entries)-1]
	entry.FrameDescriptorEntries = append(entry.FrameDescriptorEntries, &FrameDescriptorEntry{Length: length, CIE_pointer: entry})

	return parseInitialLocation, entries, length
}

func parseInitialLocation(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var (
		frameEntries = entries[len(entries)-1].FrameDescriptorEntries
		frame        = frameEntries[len(frameEntries)-1]
	)

	binary.Read(reader, binary.LittleEndian, &frame.InitialLocation)

	return parseAddressRange, entries, length - 4
}

func parseAddressRange(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var (
		frameEntries = entries[len(entries)-1].FrameDescriptorEntries
		frame        = frameEntries[len(frameEntries)-1]
	)

	binary.Read(reader, binary.LittleEndian, &frame.AddressRange)

	return parseFrameInstructions, entries, length - 4
}

func parseFrameInstructions(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var (
		// The rest of this entry consists of the instructions
		// so we can just grab all of the data from the buffer
		// cursor to length.
		buf          = make([]byte, length)
		frameEntries = entries[len(entries)-1].FrameDescriptorEntries
		frame        = frameEntries[len(frameEntries)-1]
	)

	binary.Read(reader, binary.LittleEndian, &buf)
	frame.Instructions = buf

	return parseLength, entries, 0
}

func parseCIEID(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var entry = entries[len(entries)-1]

	binary.Read(reader, binary.LittleEndian, &entry.CIE_id)

	return parseVersion, entries, length - 4
}

func parseVersion(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	entry := entries[len(entries)-1]

	binary.Read(reader, binary.LittleEndian, &entry.Version)

	return parseAugmentation, entries, length - 1
}

func parseAugmentation(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var (
		entry  = entries[len(entries)-1]
		str, c = parseString(reader)
	)

	entry.Augmentation = str
	return parseCodeAlignmentFactor, entries, length - c
}

func parseCodeAlignmentFactor(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var (
		entry  = entries[len(entries)-1]
		caf, c = DecodeLEB128(reader)
	)

	entry.CodeAlignmentFactor = caf

	return parseDataAlignmentFactor, entries, length - c
}

func parseDataAlignmentFactor(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var (
		entry  = entries[len(entries)-1]
		daf, c = DecodeLEB128(reader)
	)

	entry.DataAlignmentFactor = daf

	return parseReturnAddressRegister, entries, length - c
}

func parseReturnAddressRegister(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	entry := entries[len(entries)-1]

	binary.Read(reader, binary.LittleEndian, &entry.ReturnAddressRegister)

	return parseInitialInstructions, entries, length - 1
}

func parseInitialInstructions(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var (
		// The rest of this entry consists of the instructions
		// so we can just grab all of the data from the buffer
		// cursor to length.
		buf   = make([]byte, length)
		entry = entries[len(entries)-1]
	)

	binary.Read(reader, binary.LittleEndian, &buf)
	entry.InitialInstructions = buf

	return parseLength, entries, 0
}

func parseString(data *bytes.Buffer) (string, uint32) {
	var (
		size uint32
		str  []rune
		strb []byte
	)

	for {
		b, err := data.ReadByte()
		if err != nil {
			panic("parseString(): Could not read byte")
		}
		size++

		if b == 0x0 {
			if size == 1 {
				return "", size
			}

			break
		}

		strb = append(strb, b)

		if utf8.FullRune(strb) {
			r, _ := utf8.DecodeRune(strb)
			str = append(str, r)
			size++
			strb = strb[0:0]
		}
	}

	return string(str), size
}
<commit_msg>Fix type<commit_after>// Package frame contains data structures and
// related functions for parsing and searching
// through Dwarf .debug_frame data.
package frame

import (
	"bytes"
	"encoding/binary"
	"unicode/utf8"
)

type parsefunc func(*bytes.Buffer, CommonEntries, uint32) (parsefunc, CommonEntries, uint32)

type CommonEntries []*CommonInformationEntry

// Represents a Common Information Entry in
// the Dwarf .debug_frame section.
type CommonInformationEntry struct {
	Length                 uint32
	CIE_id                 uint32
	Version                uint8
	Augmentation           string
	CodeAlignmentFactor    uint64
	DataAlignmentFactor    uint64
	ReturnAddressRegister  byte
	InitialInstructions    []byte
	FrameDescriptorEntries []*FrameDescriptorEntry
}

// Represents a Frame Descriptor Entry in the
// Dwarf .debug_frame section.
type FrameDescriptorEntry struct {
	Length          uint32
	CIE_pointer     *CommonInformationEntry
	InitialLocation uint64
	AddressRange    uint64
	Instructions    []byte
}

const (
	DW_CFA_advance_loc        = (0x1 << 6) // High 2 bits: 0x1, low 6: delta
	DW_CFA_offset             = (0x2 << 6) // High 2 bits: 0x2, low 6: register
	DW_CFA_restore            = (0x3 << 6) // High 2 bits: 0x3, low 6: register
	DW_CFA_nop                = 0x0        // No ops
	DW_CFA_set_loc            = 0x1        // op1: address
	DW_CFA_advance_loc1       = iota       // op1: 1-bytes delta
	DW_CFA_advance_loc2                    // op1: 2-byte delta
	DW_CFA_advance_loc4                    // op1: 4-byte delta
	DW_CFA_offset_extended                 // op1: ULEB128 register, op2: ULEB128 offset
	DW_CFA_restore_extended                // op1: ULEB128 register
	DW_CFA_undefined                       // op1: ULEB128 register
	DW_CFA_same_value                      // op1: ULEB128 register
	DW_CFA_register                        // op1: ULEB128 register, op2: ULEB128 register
	DW_CFA_remember_state                  // No ops
	DW_CFA_restore_state                   // No ops
	DW_CFA_def_cfa                         // op1: ULEB128 register, op2: ULEB128 offset
	DW_CFA_def_cfa_register                // op1: ULEB128 register
	DW_CFA_def_cfa_offset                  // op1: ULEB128 offset
	DW_CFA_def_cfa_expression              // op1: BLOCK
	DW_CFA_expression                      // op1: ULEB128 register, op2: BLOCK
	DW_CFA_offset_extended_sf              // op1: ULEB128 register, op2: SLEB128 offset
	DW_CFA_def_cfa_sf                      // op1: ULEB128 register, op2: SLEB128 offset
	DW_CFA_def_cfa_offset_sf               // op1: SLEB128 offset
	DW_CFA_val_offset                      // op1: ULEB128, op2: ULEB128
	DW_CFA_val_offset_sf                   // op1: ULEB128, op2: SLEB128
	DW_CFA_val_expression                  // op1: ULEB128, op2: BLOCK
	DW_CFA_lo_user            = 0x1c       // op1: BLOCK
	DW_CFA_hi_user            = 0x3f       // op1: ULEB128 register, op2: BLOCK
)

// Parse takes in data (a byte slice) and returns a slice of
// CommonInformationEntry structures. Each CommonInformationEntry
// has a slice of FrameDescriptorEntry structures.
func Parse(data []byte) CommonEntries {
	var (
		length  uint32
		entries CommonEntries
		reader  = bytes.NewBuffer(data)
	)

	for fn := parseLength; reader.Len() != 0; {
		fn, entries, length = fn(reader, entries, length)
	}

	return entries
}

// DecodeLEB128 decodes a Little Endian Base 128
// represented number.
func DecodeLEB128(reader *bytes.Buffer) (uint64, uint32) {
	var (
		result uint64
		shift  uint64
		length uint32
	)

	for {
		b, err := reader.ReadByte()
		if err != nil {
			panic("Could not parse LEB128 value")
		}
		length++

		result |= uint64((uint(b) & 0x7f) << shift)

		// If high order bit is 1.
		if b&0x80 == 0 {
			break
		}

		shift += 7
	}

	return result, length
}

func cieEntry(data []byte) bool {
	return bytes.Equal(data, []byte{0xff, 0xff, 0xff, 0xff})
}

func parseLength(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	binary.Read(reader, binary.LittleEndian, &length)

	if cieEntry(reader.Bytes()[0:4]) {
		return parseCIEID, append(entries, &CommonInformationEntry{Length: length}), length
	}

	entry := entries[len(entries)-1]
	entry.FrameDescriptorEntries = append(entry.FrameDescriptorEntries, &FrameDescriptorEntry{Length: length, CIE_pointer: entry})

	return parseInitialLocation, entries, length
}

func parseInitialLocation(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var (
		frameEntries = entries[len(entries)-1].FrameDescriptorEntries
		frame        = frameEntries[len(frameEntries)-1]
	)

	binary.Read(reader, binary.LittleEndian, &frame.InitialLocation)

	return parseAddressRange, entries, length - 4
}

func parseAddressRange(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var (
		frameEntries = entries[len(entries)-1].FrameDescriptorEntries
		frame        = frameEntries[len(frameEntries)-1]
	)

	binary.Read(reader, binary.LittleEndian, &frame.AddressRange)

	return parseFrameInstructions, entries, length - 4
}

func parseFrameInstructions(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var (
		// The rest of this entry consists of the instructions
		// so we can just grab all of the data from the buffer
		// cursor to length.
		buf          = make([]byte, length)
		frameEntries = entries[len(entries)-1].FrameDescriptorEntries
		frame        = frameEntries[len(frameEntries)-1]
	)

	binary.Read(reader, binary.LittleEndian, &buf)
	frame.Instructions = buf

	return parseLength, entries, 0
}

func parseCIEID(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var entry = entries[len(entries)-1]

	binary.Read(reader, binary.LittleEndian, &entry.CIE_id)

	return parseVersion, entries, length - 4
}

func parseVersion(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	entry := entries[len(entries)-1]

	binary.Read(reader, binary.LittleEndian, &entry.Version)

	return parseAugmentation, entries, length - 1
}

func parseAugmentation(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var (
		entry  = entries[len(entries)-1]
		str, c = parseString(reader)
	)

	entry.Augmentation = str
	return parseCodeAlignmentFactor, entries, length - c
}

func parseCodeAlignmentFactor(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var (
		entry  = entries[len(entries)-1]
		caf, c = DecodeLEB128(reader)
	)

	entry.CodeAlignmentFactor = caf

	return parseDataAlignmentFactor, entries, length - c
}

func parseDataAlignmentFactor(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var (
		entry  = entries[len(entries)-1]
		daf, c = DecodeLEB128(reader)
	)

	entry.DataAlignmentFactor = daf

	return parseReturnAddressRegister, entries, length - c
}

func parseReturnAddressRegister(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	entry := entries[len(entries)-1]

	binary.Read(reader, binary.LittleEndian, &entry.ReturnAddressRegister)

	return parseInitialInstructions, entries, length - 1
}

func parseInitialInstructions(reader *bytes.Buffer, entries CommonEntries, length uint32) (parsefunc, CommonEntries, uint32) {
	var (
		// The rest of this entry consists of the instructions
		// so we can just grab all of the data from the buffer
		// cursor to length.
		buf   = make([]byte, length)
		entry = entries[len(entries)-1]
	)

	binary.Read(reader, binary.LittleEndian, &buf)
	entry.InitialInstructions = buf

	return parseLength, entries, 0
}

func parseString(data *bytes.Buffer) (string, uint32) {
	var (
		size uint32
		str  []rune
		strb []byte
	)

	for {
		b, err := data.ReadByte()
		if err != nil {
			panic("parseString(): Could not read byte")
		}
		size++

		if b == 0x0 {
			if size == 1 {
				return "", size
			}

			break
		}

		strb = append(strb, b)

		if utf8.FullRune(strb) {
			r, _ := utf8.DecodeRune(strb)
			str = append(str, r)
			size++
			strb = strb[0:0]
		}
	}

	return string(str), size
}
<|endoftext|>


<commit_before>package encoding

import (
	"encoding/base64"
	"reflect"
	"time"
)

// newTypeEncoder constructs an encoderFunc for a type.
// The returned encoder only checks CanAddr when allowAddr is true.
func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
	if t.Implements(marshalerType) {
		return marshalerEncoder
	}
	if t.Kind() != reflect.Ptr && allowAddr {
		if reflect.PtrTo(t).Implements(marshalerType) {
			return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false))
		}
	}

	// Check for psuedo-types first
	switch t {
	case timeType:
		return timePseudoTypeEncoder
	}

	switch t.Kind() {
	case reflect.Bool:
		return boolEncoder
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return intEncoder
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return uintEncoder
	case reflect.Float32, reflect.Float64:
		return floatEncoder
	case reflect.String:
		return stringEncoder
	case reflect.Interface:
		return interfaceEncoder
	case reflect.Struct:
		return newStructEncoder(t)
	case reflect.Map:
		return newMapEncoder(t)
	case reflect.Slice:
		return newSliceEncoder(t)
	case reflect.Array:
		return newArrayEncoder(t)
	case reflect.Ptr:
		return newPtrEncoder(t)
	default:
		return unsupportedTypeEncoder
	}
}

func invalidValueEncoder(v reflect.Value) interface{} {
	return nil
}

func doNothingEncoder(v reflect.Value) interface{} {
	return v.Interface()
}

func marshalerEncoder(v reflect.Value) interface{} {
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return nil
	}
	m := v.Interface().(Marshaler)
	ev, err := m.MarshalRQL()
	if err != nil {
		panic(&MarshalerError{v.Type(), err})
	}

	return ev
}

func addrMarshalerEncoder(v reflect.Value) interface{} {
	va := v.Addr()
	if va.IsNil() {
		return nil
	}
	m := va.Interface().(Marshaler)
	ev, err := m.MarshalRQL()
	if err != nil {
		panic(&MarshalerError{v.Type(), err})
	}

	return ev
}

func boolEncoder(v reflect.Value) interface{} {
	if v.Bool() {
		return true
	} else {
		return false
	}
}

func intEncoder(v reflect.Value) interface{} {
	return v.Int()
}

func uintEncoder(v reflect.Value) interface{} {
	return v.Uint()
}

func floatEncoder(v reflect.Value) interface{} {
	return v.Float()
}

func stringEncoder(v reflect.Value) interface{} {
	return v.String()
}

func interfaceEncoder(v reflect.Value) interface{} {
	if v.IsNil() {
		return nil
	}
	return encode(v.Elem())
}

func unsupportedTypeEncoder(v reflect.Value) interface{} {
	panic(&UnsupportedTypeError{v.Type()})
}

type structEncoder struct {
	fields    []field
	fieldEncs []encoderFunc
}

func (se *structEncoder) encode(v reflect.Value) interface{} {
	m := make(map[string]interface{})

	for i, f := range se.fields {
		fv := fieldByIndex(v, f.index)
		if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
			continue
		}

		m[f.name] = se.fieldEncs[i](fv)
	}

	return m
}

func newStructEncoder(t reflect.Type) encoderFunc {
	fields := cachedTypeFields(t)
	se := &structEncoder{
		fields:    fields,
		fieldEncs: make([]encoderFunc, len(fields)),
	}
	for i, f := range fields {
		se.fieldEncs[i] = typeEncoder(typeByIndex(t, f.index))
	}
	return se.encode
}

type mapEncoder struct {
	elemEnc encoderFunc
}

func (me *mapEncoder) encode(v reflect.Value) interface{} {
	if v.IsNil() {
		return nil
	}

	m := make(map[string]interface{})

	for _, k := range v.MapKeys() {
		m[k.String()] = me.elemEnc(v.MapIndex(k))
	}

	return m
}

func newMapEncoder(t reflect.Type) encoderFunc {
	if t.Key().Kind() != reflect.String {
		return unsupportedTypeEncoder
	}
	me := &mapEncoder{typeEncoder(t.Elem())}
	return me.encode
}

// sliceEncoder just wraps an arrayEncoder, checking to make sure the value isn't nil.
type sliceEncoder struct {
	arrayEnc encoderFunc
}

func (se *sliceEncoder) encode(v reflect.Value) interface{} {
	if v.IsNil() {
		return []interface{}{}
	}
	return se.arrayEnc(v)
}

func newSliceEncoder(t reflect.Type) encoderFunc {
	// Byte slices get special treatment; arrays don't.
	if t.Elem().Kind() == reflect.Uint8 {
		return encodeByteSlice
	}
	enc := &sliceEncoder{newArrayEncoder(t)}
	return enc.encode
}

type arrayEncoder struct {
	elemEnc encoderFunc
}

func (ae *arrayEncoder) encode(v reflect.Value) interface{} {
	n := v.Len()

	a := make([]interface{}, n)
	for i := 0; i < n; i++ {
		a[i] = ae.elemEnc(v.Index(i))
	}

	return a
}

func newArrayEncoder(t reflect.Type) encoderFunc {
	enc := &arrayEncoder{typeEncoder(t.Elem())}
	return enc.encode
}

type ptrEncoder struct {
	elemEnc encoderFunc
}

func (pe *ptrEncoder) encode(v reflect.Value) interface{} {
	if v.IsNil() {
		return nil
	}
	return pe.elemEnc(v.Elem())
}

func newPtrEncoder(t reflect.Type) encoderFunc {
	enc := &ptrEncoder{typeEncoder(t.Elem())}
	return enc.encode
}

type condAddrEncoder struct {
	canAddrEnc, elseEnc encoderFunc
}

func (ce *condAddrEncoder) encode(v reflect.Value) interface{} {
	if v.CanAddr() {
		return ce.canAddrEnc(v)
	} else {
		return ce.elseEnc(v)
	}
}

// newCondAddrEncoder returns an encoder that checks whether its value
// CanAddr and delegates to canAddrEnc if so, else to elseEnc.
func newCondAddrEncoder(canAddrEnc, elseEnc encoderFunc) encoderFunc {
	enc := &condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc}
	return enc.encode
}

// Pseudo-type encoders

// Encode a time.Time value to the TIME RQL type
func timePseudoTypeEncoder(v reflect.Value) interface{} {
	t := v.Interface().(time.Time)

	return map[string]interface{}{
		"$reql_type$": "TIME",
		"epoch_time":  t.Unix(),
		"timezone":    "+00:00",
	}
}

// Encode a byte slice to the BINARY RQL type
func encodeByteSlice(v reflect.Value) interface{} {
	var b []byte
	if !v.IsNil() {
		b = v.Bytes()
	}

	dst := make([]byte, base64.StdEncoding.EncodedLen(len(b)))
	base64.StdEncoding.Encode(dst, b)

	return map[string]interface{}{
		"$reql_type$": "BINARY",
		"data":        string(dst),
	}
}
<commit_msg>Encode Time with milliseconds. From Rethinkdb docs: Times are stored on the server as seconds since epoch (UTC) with millisecond precision plus a time zone.<commit_after>package encoding

import (
	"encoding/base64"
	"reflect"
	"time"
)

// newTypeEncoder constructs an encoderFunc for a type.
// The returned encoder only checks CanAddr when allowAddr is true.
func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
	if t.Implements(marshalerType) {
		return marshalerEncoder
	}
	if t.Kind() != reflect.Ptr && allowAddr {
		if reflect.PtrTo(t).Implements(marshalerType) {
			return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false))
		}
	}

	// Check for psuedo-types first
	switch t {
	case timeType:
		return timePseudoTypeEncoder
	}

	switch t.Kind() {
	case reflect.Bool:
		return boolEncoder
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return intEncoder
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return uintEncoder
	case reflect.Float32, reflect.Float64:
		return floatEncoder
	case reflect.String:
		return stringEncoder
	case reflect.Interface:
		return interfaceEncoder
	case reflect.Struct:
		return newStructEncoder(t)
	case reflect.Map:
		return newMapEncoder(t)
	case reflect.Slice:
		return newSliceEncoder(t)
	case reflect.Array:
		return newArrayEncoder(t)
	case reflect.Ptr:
		return newPtrEncoder(t)
	default:
		return unsupportedTypeEncoder
	}
}

func invalidValueEncoder(v reflect.Value) interface{} {
	return nil
}

func doNothingEncoder(v reflect.Value) interface{} {
	return v.Interface()
}

func marshalerEncoder(v reflect.Value) interface{} {
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return nil
	}
	m := v.Interface().(Marshaler)
	ev, err := m.MarshalRQL()
	if err != nil {
		panic(&MarshalerError{v.Type(), err})
	}

	return ev
}

func addrMarshalerEncoder(v reflect.Value) interface{} {
	va := v.Addr()
	if va.IsNil() {
		return nil
	}
	m := va.Interface().(Marshaler)
	ev, err := m.MarshalRQL()
	if err != nil {
		panic(&MarshalerError{v.Type(), err})
	}

	return ev
}

func boolEncoder(v reflect.Value) interface{} {
	if v.Bool() {
		return true
	} else {
		return false
	}
}

func intEncoder(v reflect.Value) interface{} {
	return v.Int()
}

func uintEncoder(v reflect.Value) interface{} {
	return v.Uint()
}

func floatEncoder(v reflect.Value) interface{} {
	return v.Float()
}

func stringEncoder(v reflect.Value) interface{} {
	return v.String()
}

func interfaceEncoder(v reflect.Value) interface{} {
	if v.IsNil() {
		return nil
	}
	return encode(v.Elem())
}

func unsupportedTypeEncoder(v reflect.Value) interface{} {
	panic(&UnsupportedTypeError{v.Type()})
}

type structEncoder struct {
	fields    []field
	fieldEncs []encoderFunc
}

func (se *structEncoder) encode(v reflect.Value) interface{} {
	m := make(map[string]interface{})

	for i, f := range se.fields {
		fv := fieldByIndex(v, f.index)
		if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
			continue
		}

		m[f.name] = se.fieldEncs[i](fv)
	}

	return m
}

func newStructEncoder(t reflect.Type) encoderFunc {
	fields := cachedTypeFields(t)
	se := &structEncoder{
		fields:    fields,
		fieldEncs: make([]encoderFunc, len(fields)),
	}
	for i, f := range fields {
		se.fieldEncs[i] = typeEncoder(typeByIndex(t, f.index))
	}
	return se.encode
}

type mapEncoder struct {
	elemEnc encoderFunc
}

func (me *mapEncoder) encode(v reflect.Value) interface{} {
	if v.IsNil() {
		return nil
	}

	m := make(map[string]interface{})

	for _, k := range v.MapKeys() {
		m[k.String()] = me.elemEnc(v.MapIndex(k))
	}

	return m
}

func newMapEncoder(t reflect.Type) encoderFunc {
	if t.Key().Kind() != reflect.String {
		return unsupportedTypeEncoder
	}
	me := &mapEncoder{typeEncoder(t.Elem())}
	return me.encode
}

// sliceEncoder just wraps an arrayEncoder, checking to make sure the value isn't nil.
type sliceEncoder struct {
	arrayEnc encoderFunc
}

func (se *sliceEncoder) encode(v reflect.Value) interface{} {
	if v.IsNil() {
		return []interface{}{}
	}
	return se.arrayEnc(v)
}

func newSliceEncoder(t reflect.Type) encoderFunc {
	// Byte slices get special treatment; arrays don't.
	if t.Elem().Kind() == reflect.Uint8 {
		return encodeByteSlice
	}
	enc := &sliceEncoder{newArrayEncoder(t)}
	return enc.encode
}

type arrayEncoder struct {
	elemEnc encoderFunc
}

func (ae *arrayEncoder) encode(v reflect.Value) interface{} {
	n := v.Len()

	a := make([]interface{}, n)
	for i := 0; i < n; i++ {
		a[i] = ae.elemEnc(v.Index(i))
	}

	return a
}

func newArrayEncoder(t reflect.Type) encoderFunc {
	enc := &arrayEncoder{typeEncoder(t.Elem())}
	return enc.encode
}

type ptrEncoder struct {
	elemEnc encoderFunc
}

func (pe *ptrEncoder) encode(v reflect.Value) interface{} {
	if v.IsNil() {
		return nil
	}
	return pe.elemEnc(v.Elem())
}

func newPtrEncoder(t reflect.Type) encoderFunc {
	enc := &ptrEncoder{typeEncoder(t.Elem())}
	return enc.encode
}

type condAddrEncoder struct {
	canAddrEnc, elseEnc encoderFunc
}

func (ce *condAddrEncoder) encode(v reflect.Value) interface{} {
	if v.CanAddr() {
		return ce.canAddrEnc(v)
	} else {
		return ce.elseEnc(v)
	}
}

// newCondAddrEncoder returns an encoder that checks whether its value
// CanAddr and delegates to canAddrEnc if so, else to elseEnc.
func newCondAddrEncoder(canAddrEnc, elseEnc encoderFunc) encoderFunc {
	enc := &condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc}
	return enc.encode
}

// Pseudo-type encoders

// Encode a time.Time value to the TIME RQL type
func timePseudoTypeEncoder(v reflect.Value) interface{} {
	t := v.Interface().(time.Time)

	return map[string]interface{}{
		"$reql_type$": "TIME",
		"epoch_time":  float64(t.UnixNano())/1000/1000/1000, //milliseconds
		"timezone":    "+00:00",
	}
}

// Encode a byte slice to the BINARY RQL type
func encodeByteSlice(v reflect.Value) interface{} {
	var b []byte
	if !v.IsNil() {
		b = v.Bytes()
	}

	dst := make([]byte, base64.StdEncoding.EncodedLen(len(b)))
	base64.StdEncoding.Encode(dst, b)

	return map[string]interface{}{
		"$reql_type$": "BINARY",
		"data":        string(dst),
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

// ClusterAutoscalerVersion contains version of CA.
const ClusterAutoscalerVersion = "1.2.0-rc1"
<commit_msg>CA 1.2.0-rc2<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

// ClusterAutoscalerVersion contains version of CA.
const ClusterAutoscalerVersion = "1.2.0-rc2"
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"time"

	"github.com/mesos/mesos-go"
	"github.com/mesos/mesos-go/encoding"
	"github.com/mesos/mesos-go/httpcli"
	"github.com/mesos/mesos-go/scheduler"
)

func main() {
	cfg := config{
		user:       "foobar",
		name:       "example",
		url:        "http://:5050/api/v1/scheduler",
		codec:      codec{Codec: &encoding.ProtobufCodec},
		timeout:    time.Second,
		checkpoint: true,
	}

	fs := flag.NewFlagSet("example-scheduler", flag.ExitOnError)
	fs.StringVar(&cfg.user, "user", cfg.user, "Framework user to register with the Mesos master")
	fs.StringVar(&cfg.name, "name", cfg.name, "Framework name to register with the Mesos master")
	fs.Var(&cfg.codec, "codec", "Codec to encode/decode scheduler API communications [protobuf, json]")
	fs.StringVar(&cfg.url, "url", cfg.url, "Mesos scheduler API URL")
	fs.DurationVar(&cfg.timeout, "timeout", cfg.timeout, "Mesos scheduler API connection timeout")
	fs.BoolVar(&cfg.checkpoint, "checkpoint", cfg.checkpoint, "Enable/disable framework checkpointing")
	fs.StringVar(&cfg.principal, "principal", cfg.principal, "Framework principal with which to authenticate")
	fs.StringVar(&cfg.hostname, "hostname", cfg.hostname, "Framework hostname that is advertised to the master")
	fs.Var(&cfg.labels, "label", "Framework label, may be specified multiple times")
	fs.Parse(os.Args[1:])

	if err := run(&cfg); err != nil {
		log.Fatal(err)
	}
}

func run(cfg *config) error {
	cli := httpcli.New(
		httpcli.URL(cfg.url),
		httpcli.Codec(cfg.codec.Codec),
		httpcli.Do(httpcli.With(httpcli.Timeout(cfg.timeout))),
	)

	frameworkInfo := &mesos.FrameworkInfo{
		User:       cfg.user,
		Name:       cfg.name,
		Checkpoint: &cfg.checkpoint,
	}
	if cfg.principal != "" {
		frameworkInfo.Principal = &cfg.principal
	}
	if cfg.hostname != "" {
		frameworkInfo.Hostname = &cfg.hostname
	}
	if len(cfg.labels) > 0 {
		log.Println("using labels:", cfg.labels)
		frameworkInfo.Labels = &mesos.Labels{Labels: cfg.labels}
	}
	subscribe := scheduler.SubscribeCall(true, frameworkInfo)
	registrationTokens := backoffBucket(1*time.Second, 15*time.Second, nil)
	for {
		frameworkID, err := eventLoop(cli.Do(subscribe, httpcli.Close(true)))
		if err != nil {
			log.Println(err)
		} else {
			log.Println("disconnected")
		}
		if frameworkID != "" {
			subscribe.Subscribe.FrameworkInfo.ID = &mesos.FrameworkID{Value: frameworkID}
		}
		<-registrationTokens
		log.Println("reconnecting..")
	}
}

// returns the framework ID received by mesos (if any); callers should check for a
// framework ID regardless of whether error != nil.
func eventLoop(events encoding.Decoder, conn io.Closer, err error) (string, error) {
	defer func() {
		if conn != nil {
			conn.Close()
		}
	}()
	frameworkID := ""
	callOptions := []scheduler.CallOpt{} // should be applied to every outgoing call
	for err == nil {
		var e scheduler.Event
		if err = events.Decode(&e); err != nil {
			if err == io.EOF {
				err = nil
			}
			continue
		}

		switch e.GetType().Enum() {
		case scheduler.Event_OFFERS.Enum():
		// ...

		case scheduler.Event_ERROR.Enum():
			// it's recommended that we abort and re-try subscribing; setting
			// err here will cause the event loop to terminate and the connection
			// will be reset.
			err = fmt.Errorf("ERROR: " + e.GetError().GetMessage())

		case scheduler.Event_SUBSCRIBED.Enum():
			if frameworkID == "" {
				frameworkID = e.GetSubscribed().GetFrameworkID().GetValue()
				callOptions = append(callOptions, scheduler.Framework(frameworkID))
			}
			// else, ignore subsequently received events like this on the same connection
		default:
			// handle unknown event
		}

		log.Printf("%+v\n", e)
	}
	return frameworkID, err
}

type config struct {
	id         string
	user       string
	name       string
	url        string
	codec      codec
	timeout    time.Duration
	checkpoint bool
	principal  string
	hostname   string
	labels     Labels
}
<commit_msg>sanity check for SUBSCRIBED framework-id<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"time"

	"github.com/mesos/mesos-go"
	"github.com/mesos/mesos-go/encoding"
	"github.com/mesos/mesos-go/httpcli"
	"github.com/mesos/mesos-go/scheduler"
)

func main() {
	cfg := config{
		user:       "foobar",
		name:       "example",
		url:        "http://:5050/api/v1/scheduler",
		codec:      codec{Codec: &encoding.ProtobufCodec},
		timeout:    time.Second,
		checkpoint: true,
	}

	fs := flag.NewFlagSet("example-scheduler", flag.ExitOnError)
	fs.StringVar(&cfg.user, "user", cfg.user, "Framework user to register with the Mesos master")
	fs.StringVar(&cfg.name, "name", cfg.name, "Framework name to register with the Mesos master")
	fs.Var(&cfg.codec, "codec", "Codec to encode/decode scheduler API communications [protobuf, json]")
	fs.StringVar(&cfg.url, "url", cfg.url, "Mesos scheduler API URL")
	fs.DurationVar(&cfg.timeout, "timeout", cfg.timeout, "Mesos scheduler API connection timeout")
	fs.BoolVar(&cfg.checkpoint, "checkpoint", cfg.checkpoint, "Enable/disable framework checkpointing")
	fs.StringVar(&cfg.principal, "principal", cfg.principal, "Framework principal with which to authenticate")
	fs.StringVar(&cfg.hostname, "hostname", cfg.hostname, "Framework hostname that is advertised to the master")
	fs.Var(&cfg.labels, "label", "Framework label, may be specified multiple times")
	fs.Parse(os.Args[1:])

	if err := run(&cfg); err != nil {
		log.Fatal(err)
	}
}

func run(cfg *config) error {
	cli := httpcli.New(
		httpcli.URL(cfg.url),
		httpcli.Codec(cfg.codec.Codec),
		httpcli.Do(httpcli.With(httpcli.Timeout(cfg.timeout))),
	)

	frameworkInfo := &mesos.FrameworkInfo{
		User:       cfg.user,
		Name:       cfg.name,
		Checkpoint: &cfg.checkpoint,
	}
	if cfg.principal != "" {
		frameworkInfo.Principal = &cfg.principal
	}
	if cfg.hostname != "" {
		frameworkInfo.Hostname = &cfg.hostname
	}
	if len(cfg.labels) > 0 {
		log.Println("using labels:", cfg.labels)
		frameworkInfo.Labels = &mesos.Labels{Labels: cfg.labels}
	}
	subscribe := scheduler.SubscribeCall(true, frameworkInfo)
	registrationTokens := backoffBucket(1*time.Second, 15*time.Second, nil)
	for {
		frameworkID, err := eventLoop(cli.Do(subscribe, httpcli.Close(true)))
		if err != nil {
			log.Println(err)
		} else {
			log.Println("disconnected")
		}
		if frameworkID != "" {
			subscribe.Subscribe.FrameworkInfo.ID = &mesos.FrameworkID{Value: frameworkID}
		}
		<-registrationTokens
		log.Println("reconnecting..")
	}
}

// returns the framework ID received by mesos (if any); callers should check for a
// framework ID regardless of whether error != nil.
func eventLoop(events encoding.Decoder, conn io.Closer, err error) (string, error) {
	defer func() {
		if conn != nil {
			conn.Close()
		}
	}()
	frameworkID := ""
	callOptions := []scheduler.CallOpt{} // should be applied to every outgoing call
	for err == nil {
		var e scheduler.Event
		if err = events.Decode(&e); err != nil {
			if err == io.EOF {
				err = nil
			}
			continue
		}

		switch e.GetType().Enum() {
		case scheduler.Event_OFFERS.Enum():
		// ...

		case scheduler.Event_ERROR.Enum():
			// it's recommended that we abort and re-try subscribing; setting
			// err here will cause the event loop to terminate and the connection
			// will be reset.
			err = fmt.Errorf("ERROR: " + e.GetError().GetMessage())

		case scheduler.Event_SUBSCRIBED.Enum():
			if frameworkID == "" {
				frameworkID = e.GetSubscribed().GetFrameworkID().GetValue()
				if frameworkID == "" {
					// sanity check
					panic("mesos gave us an empty frameworkID")
				}
				callOptions = append(callOptions, scheduler.Framework(frameworkID))
			}
			// else, ignore subsequently received events like this on the same connection
		default:
			// handle unknown event
		}

		log.Printf("%+v\n", e)
	}
	return frameworkID, err
}

type config struct {
	id         string
	user       string
	name       string
	url        string
	codec      codec
	timeout    time.Duration
	checkpoint bool
	principal  string
	hostname   string
	labels     Labels
}
<|endoftext|>


<commit_before>package main_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"testing"
)

type test struct {
	offset, from, to string // specify the arguments
	fileSpecified    bool   // true if the offset or from args specify a specific file
	pkgs             map[string][]string
	wantErr          bool
	wantOut          string              // a substring expected to be in the output
	packages         map[string][]string // a map of the package name to the files contained within, which will be numbered by i.go where i is the index
}

// Test that renaming that would modify cgo files will produce an error and not modify the file.
func TestGeneratedFiles(t *testing.T) {
	tmp, bin, cleanup := buildGorename(t)
	defer cleanup()

	srcDir := filepath.Join(tmp, "src")
	err := os.Mkdir(srcDir, os.ModePerm)
	if err != nil {
		t.Fatal(err)
	}

	env := append(os.Environ(), fmt.Sprintf("GOPATH=%s", tmp))
	// Testing renaming in packages that include cgo files:
	for iter, renameTest := range []test{
		{
			// Test: variable not used in any cgo file -> no error
			from: `"mytest"::f`, to: "g",
			packages: map[string][]string{
				"mytest": []string{`package mytest; func f() {}`,
					`package mytest
// #include <stdio.h>
import "C"

func z() {C.puts(nil)}`},
			},
			wantErr: false,
			wantOut: "Renamed 1 occurrence in 1 file in 1 package.",
		}, {
			// Test: to name used in cgo file -> rename error
			from: `"mytest"::f`, to: "g",
			packages: map[string][]string{
				"mytest": []string{`package mytest; func f() {}`,
					`package mytest
// #include <stdio.h>
import "C"

func g() {C.puts(nil)}`},
			},
			wantErr: true,
			wantOut: "conflicts with func in same block",
		},
		{
			// Test: from name in package in cgo file -> error
			from: `"mytest"::f`, to: "g",
			packages: map[string][]string{
				"mytest": []string{`package mytest

// #include <stdio.h>
import "C"

func f() { C.puts(nil); }
`},
			},
			wantErr: true,
			wantOut: "gorename: refusing to modify generated file containing DO NOT EDIT marker:",
		}, {
			// Test: from name in cgo file -> error
			from: filepath.Join("mytest", "0.go") + `::f`, to: "g",
			fileSpecified: true,
			packages: map[string][]string{
				"mytest": []string{`package mytest

// #include <stdio.h>
import "C"

func f() { C.puts(nil); }
`},
			},
			wantErr: true,
			wantOut: "gorename: refusing to modify generated file containing DO NOT EDIT marker:",
		}, {
			// Test: offset in cgo file -> identifier in cgo error
			offset: filepath.Join("main", "0.go") + `:#78`, to: "bar",
			fileSpecified: true,
			wantErr:       true,
			packages: map[string][]string{
				"main": {`package main

// #include <unistd.h>
import "C"
import "fmt"

func main() {
	foo := 1
	C.close(2)
	fmt.Println(foo)
}
`},
			},
			wantOut: "cannot rename identifiers in generated file containing DO NOT EDIT marker:",
		}, {
			// Test: from identifier appears in cgo file in another package -> error
			from: `"test"::Foo`, to: "Bar",
			packages: map[string][]string{
				"test": []string{
					`package test

func Foo(x int) (int){
	return x * 2
}
`,
				},
				"main": []string{
					`package main

import "test"
import "fmt"
// #include <unistd.h>
import "C"

func fun() {
	x := test.Foo(3)
	C.close(3)
	fmt.Println(x)
}
`,
				},
			},
			wantErr: true,
			wantOut: "gorename: refusing to modify generated file containing DO NOT EDIT marker:",
		}, {
			// Test: from identifier doesn't appear in cgo file that includes modified package -> rename successful
			from: `"test".Foo::x`, to: "y",
			packages: map[string][]string{
				"test": []string{
					`package test

func Foo(x int) (int){
	return x * 2
}
`,
				},
				"main": []string{
					`package main
import "test"
import "fmt"
// #include <unistd.h>
import "C"

func fun() {
	x := test.Foo(3)
	C.close(3)
	fmt.Println(x)
}
`,
				},
			},
			wantErr: false,
			wantOut: "Renamed 2 occurrences in 1 file in 1 package.",
		}, {
			// Test: from name appears in cgo file in same package -> error
			from: `"mytest"::f`, to: "g",
			packages: map[string][]string{
				"mytest": []string{`package mytest; func f() {}`,
					`package mytest
// #include <stdio.h>
import "C"

func z() {C.puts(nil); f()}`,
					`package mytest
// #include <unistd.h>
import "C"

func foo() {C.close(3); f()}`,
				},
			},
			wantErr: true,
			wantOut: "gorename: refusing to modify generated files containing DO NOT EDIT marker:",
		}, {
			// Test: from name in file, identifier not used in cgo file -> rename successful
			from: filepath.Join("mytest", "0.go") + `::f`, to: "g",
			fileSpecified: true,
			packages: map[string][]string{
				"mytest": []string{`package mytest; func f() {}`,
					`package mytest
// #include <stdio.h>
import "C"

func z() {C.puts(nil)}`},
			},
			wantErr: false,
			wantOut: "Renamed 1 occurrence in 1 file in 1 package.",
		}, {
			// Test: from identifier imported to another package but does not modify cgo file -> rename successful
			from: `"test".Foo`, to: "Bar",
			packages: map[string][]string{
				"test": []string{
					`package test

func Foo(x int) (int){
	return x * 2
}
`,
				},
				"main": []string{
					`package main
// #include <unistd.h>
import "C"

func fun() {
	C.close(3)
}
`,
					`package main
import "test"
import "fmt"
func g() { fmt.Println(test.Foo(3)) }
`,
				},
			},
			wantErr: false,
			wantOut: "Renamed 2 occurrences in 2 files in 2 packages.",
		},
	} {
		// Write the test files
		testCleanup := setUpPackages(t, srcDir, renameTest.packages)

		// Set up arguments
		var args []string

		var arg, val string
		if renameTest.offset != "" {
			arg, val = "-offset", renameTest.offset
		} else {
			arg, val = "-from", renameTest.from
		}

		prefix := fmt.Sprintf("%d: %s %q -to %q", iter, arg, val, renameTest.to)

		if renameTest.fileSpecified {
			// add the src dir to the value of the argument
			val = filepath.Join(srcDir, val)
		}

		args = append(args, arg, val, "-to", renameTest.to)

		// Run command
		cmd := exec.Command(bin, args...)
		cmd.Args[0] = "gorename"
		cmd.Env = env

		// Check the output
		out, err := cmd.CombinedOutput()
		// errors should result in no changes to files
		if err != nil {
			if !renameTest.wantErr {
				t.Errorf("%s: received unexpected error %s", prefix, err)
			}
			// Compare output
			if ok := strings.Contains(string(out), renameTest.wantOut); !ok {
				t.Errorf("%s: unexpected command output: %s (want: %s)", prefix, out, renameTest.wantOut)
			}
			// Check that no files were modified
			if modified := modifiedFiles(t, srcDir, renameTest.packages); len(modified) != 0 {
				t.Errorf("%s: files unexpectedly modified: %s", prefix, modified)
			}

		} else {
			if !renameTest.wantErr {
				if ok := strings.Contains(string(out), renameTest.wantOut); !ok {
					t.Errorf("%s: unexpected command output: %s (want: %s)", prefix, out, renameTest.wantOut)
				}
			} else {
				t.Errorf("%s: command succeeded unexpectedly, output: %s", prefix, out)
			}
		}
		testCleanup()
	}
}

// buildGorename builds the gorename executable.
// It returns its path, and a cleanup function.
func buildGorename(t *testing.T) (tmp, bin string, cleanup func()) {

	tmp, err := ioutil.TempDir("", "gorename-regtest-")
	if err != nil {
		t.Fatal(err)
	}

	defer func() {
		if cleanup == nil { // probably, go build failed.
			os.RemoveAll(tmp)
		}
	}()

	bin = filepath.Join(tmp, "gorename")
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}
	cmd := exec.Command("go", "build", "-o", bin)
	if err := cmd.Run(); err != nil {
		t.Fatalf("Building gorename: %v", err)
	}
	return tmp, bin, func() { os.RemoveAll(tmp) }
}

// setUpPackages sets up the files in a temporary directory provided by arguments.
func setUpPackages(t *testing.T, dir string, packages map[string][]string) (cleanup func()) {
	var pkgDirs []string

	for pkgName, files := range packages {
		// Create a directory for the package.
		pkgDir := filepath.Join(dir, pkgName)
		pkgDirs = append(pkgDirs, pkgDir)

		if err := os.Mkdir(pkgDir, os.ModePerm); err != nil {
			t.Fatal(err)
		}
		// Write the packages files
		for i, val := range files {
			file := filepath.Join(pkgDir, strconv.Itoa(i)+".go")
			if err := ioutil.WriteFile(file, []byte(val), os.ModePerm); err != nil {
				t.Fatal(err)
			}
		}
	}
	return func() {
		for _, dir := range pkgDirs {
			os.RemoveAll(dir)
		}
	}
}

// modifiedFiles returns a list of files that were renamed (without the prefix dir).
func modifiedFiles(t *testing.T, dir string, packages map[string][]string) (results []string) {

	for pkgName, files := range packages {
		pkgDir := filepath.Join(dir, pkgName)

		for i, val := range files {
			file := filepath.Join(pkgDir, strconv.Itoa(i)+".go")
			// read file contents and compare to val
			if contents, err := ioutil.ReadFile(file); err != nil {
				t.Fatal("File missing: %s", err)
			} else if string(contents) != val {
				results = append(results, strings.TrimPrefix(dir, file))
			}
		}
	}
	return results
}
<commit_msg>cmd/gorename: add tests that run successfully<commit_after>package main_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"testing"
)

type test struct {
	offset, from, to string // specify the arguments
	fileSpecified    bool   // true if the offset or from args specify a specific file
	pkgs             map[string][]string
	wantErr          bool
	wantOut          string              // a substring expected to be in the output
	packages         map[string][]string // a map of the package name to the files contained within, which will be numbered by i.go where i is the index
}

// Test that renaming that would modify cgo files will produce an error and not modify the file.
func TestGeneratedFiles(t *testing.T) {
	tmp, bin, cleanup := buildGorename(t)
	defer cleanup()

	srcDir := filepath.Join(tmp, "src")
	err := os.Mkdir(srcDir, os.ModePerm)
	if err != nil {
		t.Fatal(err)
	}

	var env = []string{fmt.Sprintf("GOPATH=%s", tmp)}
	for _, envVar := range os.Environ() {
		if !strings.HasPrefix(envVar, "GOPATH=") {
			env = append(env, envVar)
		}
	}

	// Testing renaming in packages that include cgo files:
	for iter, renameTest := range []test{
		{
			// Test: variable not used in any cgo file -> no error
			from: `"mytest"::f`, to: "g",
			packages: map[string][]string{
				"mytest": []string{`package mytest; func f() {}`,
					`package mytest
// #include <stdio.h>
import "C"

func z() {C.puts(nil)}`},
			},
			wantErr: false,
			wantOut: "Renamed 1 occurrence in 1 file in 1 package.",
		}, {
			// Test: to name used in cgo file -> rename error
			from: `"mytest"::f`, to: "g",
			packages: map[string][]string{
				"mytest": []string{`package mytest; func f() {}`,
					`package mytest
// #include <stdio.h>
import "C"

func g() {C.puts(nil)}`},
			},
			wantErr: true,
			wantOut: "conflicts with func in same block",
		},
		{
			// Test: from name in package in cgo file -> error
			from: `"mytest"::f`, to: "g",
			packages: map[string][]string{
				"mytest": []string{`package mytest

// #include <stdio.h>
import "C"

func f() { C.puts(nil); }
`},
			},
			wantErr: true,
			wantOut: "gorename: refusing to modify generated file containing DO NOT EDIT marker:",
		}, {
			// Test: from name in cgo file -> error
			from: filepath.Join("mytest", "0.go") + `::f`, to: "g",
			fileSpecified: true,
			packages: map[string][]string{
				"mytest": []string{`package mytest

// #include <stdio.h>
import "C"

func f() { C.puts(nil); }
`},
			},
			wantErr: true,
			wantOut: "gorename: refusing to modify generated file containing DO NOT EDIT marker:",
		}, {
			// Test: offset in cgo file -> identifier in cgo error
			offset: filepath.Join("main", "0.go") + `:#78`, to: "bar",
			fileSpecified: true,
			wantErr:       true,
			packages: map[string][]string{
				"main": {`package main

// #include <unistd.h>
import "C"
import "fmt"

func main() {
	foo := 1
	C.close(2)
	fmt.Println(foo)
}
`},
			},
			wantOut: "cannot rename identifiers in generated file containing DO NOT EDIT marker:",
		}, {
			// Test: from identifier appears in cgo file in another package -> error
			from: `"test"::Foo`, to: "Bar",
			packages: map[string][]string{
				"test": []string{
					`package test

func Foo(x int) (int){
	return x * 2
}
`,
				},
				"main": []string{
					`package main

import "test"
import "fmt"
// #include <unistd.h>
import "C"

func fun() {
	x := test.Foo(3)
	C.close(3)
	fmt.Println(x)
}
`,
				},
			},
			wantErr: true,
			wantOut: "gorename: refusing to modify generated file containing DO NOT EDIT marker:",
		}, {
			// Test: from identifier doesn't appear in cgo file that includes modified package -> rename successful
			from: `"test".Foo::x`, to: "y",
			packages: map[string][]string{
				"test": []string{
					`package test

func Foo(x int) (int){
	return x * 2
}
`,
				},
				"main": []string{
					`package main
import "test"
import "fmt"
// #include <unistd.h>
import "C"

func fun() {
	x := test.Foo(3)
	C.close(3)
	fmt.Println(x)
}
`,
				},
			},
			wantErr: false,
			wantOut: "Renamed 2 occurrences in 1 file in 1 package.",
		}, {
			// Test: from name appears in cgo file in same package -> error
			from: `"mytest"::f`, to: "g",
			packages: map[string][]string{
				"mytest": []string{`package mytest; func f() {}`,
					`package mytest
// #include <stdio.h>
import "C"

func z() {C.puts(nil); f()}`,
					`package mytest
// #include <unistd.h>
import "C"

func foo() {C.close(3); f()}`,
				},
			},
			wantErr: true,
			wantOut: "gorename: refusing to modify generated files containing DO NOT EDIT marker:",
		}, {
			// Test: from name in file, identifier not used in cgo file -> rename successful
			from: filepath.Join("mytest", "0.go") + `::f`, to: "g",
			fileSpecified: true,
			packages: map[string][]string{
				"mytest": []string{`package mytest; func f() {}`,
					`package mytest
// #include <stdio.h>
import "C"

func z() {C.puts(nil)}`},
			},
			wantErr: false,
			wantOut: "Renamed 1 occurrence in 1 file in 1 package.",
		}, {
			// Test: from identifier imported to another package but does not modify cgo file -> rename successful
			from: `"test".Foo`, to: "Bar",
			packages: map[string][]string{
				"test": []string{
					`package test

func Foo(x int) (int){
	return x * 2
}
`,
				},
				"main": []string{
					`package main
// #include <unistd.h>
import "C"

func fun() {
	C.close(3)
}
`,
					`package main
import "test"
import "fmt"
func g() { fmt.Println(test.Foo(3)) }
`,
				},
			},
			wantErr: false,
			wantOut: "Renamed 2 occurrences in 2 files in 2 packages.",
		},
	} {
		// Write the test files
		testCleanup := setUpPackages(t, srcDir, renameTest.packages)

		// Set up arguments
		var args []string

		var arg, val string
		if renameTest.offset != "" {
			arg, val = "-offset", renameTest.offset
		} else {
			arg, val = "-from", renameTest.from
		}

		prefix := fmt.Sprintf("%d: %s %q -to %q", iter, arg, val, renameTest.to)

		if renameTest.fileSpecified {
			// add the src dir to the value of the argument
			val = filepath.Join(srcDir, val)
		}

		args = append(args, arg, val, "-to", renameTest.to)

		// Run command
		cmd := exec.Command(bin, args...)
		cmd.Args[0] = "gorename"
		cmd.Env = env

		// Check the output
		out, err := cmd.CombinedOutput()
		// errors should result in no changes to files
		if err != nil {
			if !renameTest.wantErr {
				t.Errorf("%s: received unexpected error %s", prefix, err)
			}
			// Compare output
			if ok := strings.Contains(string(out), renameTest.wantOut); !ok {
				t.Errorf("%s: unexpected command output: %s (want: %s)", prefix, out, renameTest.wantOut)
			}
			// Check that no files were modified
			if modified := modifiedFiles(t, srcDir, renameTest.packages); len(modified) != 0 {
				t.Errorf("%s: files unexpectedly modified: %s", prefix, modified)
			}

		} else {
			if !renameTest.wantErr {
				if ok := strings.Contains(string(out), renameTest.wantOut); !ok {
					t.Errorf("%s: unexpected command output: %s (want: %s)", prefix, out, renameTest.wantOut)
				}
			} else {
				t.Errorf("%s: command succeeded unexpectedly, output: %s", prefix, out)
			}
		}
		testCleanup()
	}
}

// buildGorename builds the gorename executable.
// It returns its path, and a cleanup function.
func buildGorename(t *testing.T) (tmp, bin string, cleanup func()) {

	tmp, err := ioutil.TempDir("", "gorename-regtest-")
	if err != nil {
		t.Fatal(err)
	}

	defer func() {
		if cleanup == nil { // probably, go build failed.
			os.RemoveAll(tmp)
		}
	}()

	bin = filepath.Join(tmp, "gorename")
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}
	cmd := exec.Command("go", "build", "-o", bin)
	if err := cmd.Run(); err != nil {
		t.Fatalf("Building gorename: %v", err)
	}
	return tmp, bin, func() { os.RemoveAll(tmp) }
}

// setUpPackages sets up the files in a temporary directory provided by arguments.
func setUpPackages(t *testing.T, dir string, packages map[string][]string) (cleanup func()) {
	var pkgDirs []string

	for pkgName, files := range packages {
		// Create a directory for the package.
		pkgDir := filepath.Join(dir, pkgName)
		pkgDirs = append(pkgDirs, pkgDir)

		if err := os.Mkdir(pkgDir, os.ModePerm); err != nil {
			t.Fatal(err)
		}
		// Write the packages files
		for i, val := range files {
			file := filepath.Join(pkgDir, strconv.Itoa(i)+".go")
			if err := ioutil.WriteFile(file, []byte(val), os.ModePerm); err != nil {
				t.Fatal(err)
			}
		}
	}
	return func() {
		for _, dir := range pkgDirs {
			os.RemoveAll(dir)
		}
	}
}

// modifiedFiles returns a list of files that were renamed (without the prefix dir).
func modifiedFiles(t *testing.T, dir string, packages map[string][]string) (results []string) {

	for pkgName, files := range packages {
		pkgDir := filepath.Join(dir, pkgName)

		for i, val := range files {
			file := filepath.Join(pkgDir, strconv.Itoa(i)+".go")
			// read file contents and compare to val
			if contents, err := ioutil.ReadFile(file); err != nil {
				t.Fatal("File missing: %s", err)
			} else if string(contents) != val {
				results = append(results, strings.TrimPrefix(dir, file))
			}
		}
	}
	return results
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Paul Jolly <paul@myitcv.org.uk>, all rights reserved.
// Use of this document is governed by a license found in the LICENSE document.

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/printer"
	"go/token"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"

	"myitcv.io/gogenerate"
	"myitcv.io/immutable/util"
)

const (
	fieldHidingPrefix = "_"
)

func execute(dir string, envPkg string, licenseHeader string, cmds gogenCmds) {

	absDir, err := filepath.Abs(dir)
	if err != nil {
		fatalf("could not make absolute path from %v: %v", dir, err)
	}

	bpkg, err := build.ImportDir(absDir, 0)
	if err != nil {
		fatalf("could not resolve package from dir %v: %v", dir, err)
	}

	fset := token.NewFileSet()

	notGenByUs := func(fi os.FileInfo) bool {
		return !gogenerate.FileGeneratedBy(fi.Name(), immutableGenCmd)
	}

	pkgs, err := parser.ParseDir(fset, dir, notGenByUs, parser.AllErrors|parser.ParseComments)
	if err != nil {
		fatalf("could not parse dir %v: %v", dir, err)
	}

	pkg, ok := pkgs[envPkg]

	if !ok {
		pps := make([]string, 0, len(pkgs))
		for k := range pkgs {
			pps = append(pps, k)
		}
		fatalf("expected to have parsed %v, instead parsed %v", envPkg, pps)
	}

	out := &output{
		dir:       dir,
		fset:      fset,
		pkg:       envPkg,
		license:   licenseHeader,
		goGenCmds: cmds,
		files:     make(map[*ast.File]*fileTmpls),
		cms:       make(map[*ast.File]ast.CommentMap),
	}

	allTypes := make(map[string]util.ImmTypeAst)

	for fn, f := range pkg.Files {
		// skip files that we generated
		if gogenerate.FileGeneratedBy(fn, immutableGenCmd) {
			continue
		}

		cm := ast.NewCommentMap(fset, f, f.Comments)
		og := gatherImmTypes(bpkg.ImportPath, fset, f)
		out.files[f] = og

		for _, m := range og.maps {
			allTypes[m.name] = util.ImmTypeAstMap{
				Key:  m.keyTyp,
				Elem: m.valTyp,
			}
		}

		for _, s := range og.slices {
			allTypes[s.name] = util.ImmTypeAstSlice{
				Elem: s.valTyp,
			}
		}

		for _, s := range og.structs {
			allTypes[s.name] = util.ImmTypeAstStruct{}
		}

		out.cms[f] = cm
	}

	out.immTypes = allTypes

	out.genImmTypes()
}

type output struct {
	dir       string
	pkg       string
	fset      *token.FileSet
	license   string
	goGenCmds gogenCmds

	output *bytes.Buffer

	curFile *ast.File

	// a convenience map of all the imm types we will
	// be generating in this package
	immTypes map[string]util.ImmTypeAst

	files map[*ast.File]*fileTmpls
	cms   map[*ast.File]ast.CommentMap
}

type fileTmpls struct {
	imports map[*ast.ImportSpec]struct{}

	maps    []immMap
	slices  []immSlice
	structs []immStruct
}

func gatherImmTypes(pkg string, fset *token.FileSet, file *ast.File) *fileTmpls {
	g := &fileTmpls{
		imports: make(map[*ast.ImportSpec]struct{}),
	}

	impf := &importFinder{
		imports: file.Imports,
		matches: g.imports,
	}

	comm := commonImm{
		fset: fset,
		file: file,
		pkg:  pkg,
	}

	for _, d := range file.Decls {

		gd, ok := d.(*ast.GenDecl)
		if !ok || gd.Tok != token.TYPE {
			continue
		}

		for _, s := range gd.Specs {
			ts := s.(*ast.TypeSpec)

			name, ok := util.IsImmTmplAst(ts)
			if !ok {
				continue
			}

			infof("found immutable declaration at %v", fset.Position(gd.Pos()))

			switch typ := ts.Type.(type) {
			case *ast.MapType:
				g.maps = append(g.maps, immMap{
					commonImm: comm,
					name:      name,
					dec:       gd,
					typ:       typ,
					keyTyp:    typ.Key,
					valTyp:    typ.Value,
				})

				ast.Walk(impf, ts.Type)

			case *ast.ArrayType:
				// TODO support for arrays

				if typ.Len == nil {
					g.slices = append(g.slices, immSlice{
						commonImm: comm,
						name:      name,
						dec:       gd,
						typ:       typ,
						valTyp:    typ.Elt,
					})
				}

				ast.Walk(impf, ts.Type)

			case *ast.StructType:
				g.structs = append(g.structs, immStruct{
					commonImm: comm,
					name:      name,
					dec:       gd,
					st:        typ,
					special:   isSpecialStruct(name, typ),
				})

				ast.Walk(impf, ts.Type)
			}
		}
	}

	return g
}

func isSpecialStruct(name string, st *ast.StructType) bool {
	// work out whether this is a special struct with a Key field
	// pattern is:
	//
	// 1. struct field has a field called Key of type {{.StructName}}Key (non pointer)
	//
	// later checks will include:
	//
	// 2. said type has two fields, Uuid and Version, of type {{.StructName}}Uuid and uint64 respectively
	// 3. the underlying type of {{.StructName}}Uuid is uint64 (we might be able to relax these two
	// two underlying type restrictions)

	if st.Fields == nil {
		return false
	}

	for _, f := range st.Fields.List {
		idt, ok := f.Type.(*ast.Ident)
		if !ok {
			continue
		}

		if idt.Name != name+"Key" {
			continue
		}

		for _, fn := range f.Names {
			if fn.Name == "Key" {
				return true
			}
		}
	}

	return false
}

func (o *output) genImmTypes() {
	for f, v := range o.files {
		o.curFile = f

		if len(v.maps) == 0 && len(v.slices) == 0 && len(v.structs) == 0 {
			continue
		}

		o.output = bytes.NewBuffer(nil)

		o.pfln("// Code generated by %v. DO NOT EDIT.", immutableGenCmd)
		o.pln("")

		o.pf(o.license)

		o.pf("package %v\n", o.pkg)

		// is there a "standard" place for //go:generate comments?
		for _, v := range o.goGenCmds {
			o.pf("//go:generate %v\n", v)
		}

		o.pln("//immutableVet:skipFile")
		o.pln("")

		o.pln("import (")

		o.pln("\"myitcv.io/immutable\"")
		o.pln()

		for i := range v.imports {
			if i.Name != nil {
				o.pfln("%v %v", i.Name.Name, i.Path.Value)
			} else {
				o.pfln("%v", i.Path.Value)
			}
		}

		o.pln(")")

		o.pln("")

		o.genImmMaps(v.maps)
		o.genImmSlices(v.slices)
		o.genImmStructs(v.structs)

		source := o.output.Bytes()

		toWrite := source

		fn := o.fset.Position(f.Pos()).Filename

		// this is the file path
		offn, ok := gogenerate.NameFileFromFile(fn, immutableGenCmd)
		if !ok {
			fatalf("could not name file from %v", fn)
		}

		out := bytes.NewBuffer(nil)
		cmd := exec.Command("gofmt", "-s")
		cmd.Stdin = o.output
		cmd.Stdout = out

		err := cmd.Run()
		if err == nil {
			toWrite = out.Bytes()
		} else {
			infof("failed to format %v: %v", fn, err)
		}

		wrote, err := gogenerate.WriteIfDiff(toWrite, offn)
		if err != nil {
			fatalf("could not write %v: %v", offn, err)
		}

		if wrote {
			infof("writing %v", offn)
		} else {
			infof("skipping writing of %v; it's identical", offn)
		}
	}
}

func (o *output) exprString(e ast.Expr) string {
	var buf bytes.Buffer

	err := printer.Fprint(&buf, o.fset, e)
	if err != nil {
		panic(err)
	}

	return buf.String()
}

func (o *output) printCommentGroup(d *ast.CommentGroup) {
	if d != nil {
		for _, c := range d.List {
			o.pfln("%v", c.Text)
		}
	}
}

func (o *output) printImmPreamble(name string, node ast.Node) {
	fset := o.fset

	if st, ok := node.(*ast.StructType); ok {

		// we need to do some manipulation

		buf := bytes.NewBuffer(nil)

		fmt.Fprintf(buf, "struct {\n")

		if st.Fields != nil {
			line := o.fset.Position(st.Fields.List[0].Pos()).Line

			for _, f := range st.Fields.List {
				curLine := o.fset.Position(f.Pos()).Line

				if line != curLine {
					// catch up
					fmt.Fprintln(buf, "")
					line = curLine
				}

				ids := make([]string, 0, len(f.Names))
				for _, n := range f.Names {
					ids = append(ids, n.Name)
				}
				fmt.Fprintf(buf, "%v %v\n", strings.Join(ids, ","), o.exprString(f.Type))

				line++
			}
		}

		fmt.Fprintf(buf, "}")

		exprStr := buf.String()

		fset = token.NewFileSet()
		newnode, err := parser.ParseExprFrom(fset, "", exprStr, 0)
		if err != nil {
			fatalf("could not parse documentation struct from %v: %v", exprStr, err)
		}

		node = newnode
	}

	o.pln("//")
	o.pfln("// %v is an immutable type and has the following template:", name)
	o.pln("//")

	tmplBuf := bytes.NewBuffer(nil)

	err := printer.Fprint(tmplBuf, fset, node)
	if err != nil {
		fatalf("could not printer template declaration: %v", err)
	}

	sc := bufio.NewScanner(tmplBuf)
	for sc.Scan() {
		o.pfln("// \t%v", sc.Text())
	}
	if err := sc.Err(); err != nil {
		fatalf("could not scan printed template: %v", err)
	}

	o.pln("//")
}

func (o *output) pln(i ...interface{}) {
	fmt.Fprintln(o.output, i...)
}

func (o *output) pf(format string, i ...interface{}) {
	fmt.Fprintf(o.output, format, i...)
}

func (o *output) pfln(format string, i ...interface{}) {
	o.pf(format+"\n", i...)
}

func (o *output) pt(tmpl string, fm template.FuncMap, val interface{}) {

	// on the basis most templates are for convenience define inline
	// as raw string literals which start the ` on one line but then start
	// the template on the next (for readability) we strip the first leading
	// \n if one exists
	tmpl = strings.TrimPrefix(tmpl, "\n")

	t := template.New("tmp")
	t.Funcs(fm)

	_, err := t.Parse(tmpl)
	if err != nil {
		panic(err)
	}

	err = t.Execute(o.output, val)
	if err != nil {
		panic(err)
	}
}

func fatalf(format string, args ...interface{}) {
	panic(fmt.Errorf(format, args...))
}

func infoln(args ...interface{}) {
	if *fGoGenLog == string(gogenerate.LogInfo) {
		log.Println(args...)
	}
}

func infof(format string, args ...interface{}) {
	if *fGoGenLog == string(gogenerate.LogInfo) {
		log.Printf(format, args...)
	}
}
<commit_msg>Fix edge case of imm struct template not having any fields<commit_after>// Copyright (c) 2016 Paul Jolly <paul@myitcv.org.uk>, all rights reserved.
// Use of this document is governed by a license found in the LICENSE document.

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/printer"
	"go/token"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"

	"myitcv.io/gogenerate"
	"myitcv.io/immutable/util"
)

const (
	fieldHidingPrefix = "_"
)

func execute(dir string, envPkg string, licenseHeader string, cmds gogenCmds) {

	absDir, err := filepath.Abs(dir)
	if err != nil {
		fatalf("could not make absolute path from %v: %v", dir, err)
	}

	bpkg, err := build.ImportDir(absDir, 0)
	if err != nil {
		fatalf("could not resolve package from dir %v: %v", dir, err)
	}

	fset := token.NewFileSet()

	notGenByUs := func(fi os.FileInfo) bool {
		return !gogenerate.FileGeneratedBy(fi.Name(), immutableGenCmd)
	}

	pkgs, err := parser.ParseDir(fset, dir, notGenByUs, parser.AllErrors|parser.ParseComments)
	if err != nil {
		fatalf("could not parse dir %v: %v", dir, err)
	}

	pkg, ok := pkgs[envPkg]

	if !ok {
		pps := make([]string, 0, len(pkgs))
		for k := range pkgs {
			pps = append(pps, k)
		}
		fatalf("expected to have parsed %v, instead parsed %v", envPkg, pps)
	}

	out := &output{
		dir:       dir,
		fset:      fset,
		pkg:       envPkg,
		license:   licenseHeader,
		goGenCmds: cmds,
		files:     make(map[*ast.File]*fileTmpls),
		cms:       make(map[*ast.File]ast.CommentMap),
	}

	allTypes := make(map[string]util.ImmTypeAst)

	for fn, f := range pkg.Files {
		// skip files that we generated
		if gogenerate.FileGeneratedBy(fn, immutableGenCmd) {
			continue
		}

		cm := ast.NewCommentMap(fset, f, f.Comments)
		og := gatherImmTypes(bpkg.ImportPath, fset, f)
		out.files[f] = og

		for _, m := range og.maps {
			allTypes[m.name] = util.ImmTypeAstMap{
				Key:  m.keyTyp,
				Elem: m.valTyp,
			}
		}

		for _, s := range og.slices {
			allTypes[s.name] = util.ImmTypeAstSlice{
				Elem: s.valTyp,
			}
		}

		for _, s := range og.structs {
			allTypes[s.name] = util.ImmTypeAstStruct{}
		}

		out.cms[f] = cm
	}

	out.immTypes = allTypes

	out.genImmTypes()
}

type output struct {
	dir       string
	pkg       string
	fset      *token.FileSet
	license   string
	goGenCmds gogenCmds

	output *bytes.Buffer

	curFile *ast.File

	// a convenience map of all the imm types we will
	// be generating in this package
	immTypes map[string]util.ImmTypeAst

	files map[*ast.File]*fileTmpls
	cms   map[*ast.File]ast.CommentMap
}

type fileTmpls struct {
	imports map[*ast.ImportSpec]struct{}

	maps    []immMap
	slices  []immSlice
	structs []immStruct
}

func gatherImmTypes(pkg string, fset *token.FileSet, file *ast.File) *fileTmpls {
	g := &fileTmpls{
		imports: make(map[*ast.ImportSpec]struct{}),
	}

	impf := &importFinder{
		imports: file.Imports,
		matches: g.imports,
	}

	comm := commonImm{
		fset: fset,
		file: file,
		pkg:  pkg,
	}

	for _, d := range file.Decls {

		gd, ok := d.(*ast.GenDecl)
		if !ok || gd.Tok != token.TYPE {
			continue
		}

		for _, s := range gd.Specs {
			ts := s.(*ast.TypeSpec)

			name, ok := util.IsImmTmplAst(ts)
			if !ok {
				continue
			}

			infof("found immutable declaration at %v", fset.Position(gd.Pos()))

			switch typ := ts.Type.(type) {
			case *ast.MapType:
				g.maps = append(g.maps, immMap{
					commonImm: comm,
					name:      name,
					dec:       gd,
					typ:       typ,
					keyTyp:    typ.Key,
					valTyp:    typ.Value,
				})

				ast.Walk(impf, ts.Type)

			case *ast.ArrayType:
				// TODO support for arrays

				if typ.Len == nil {
					g.slices = append(g.slices, immSlice{
						commonImm: comm,
						name:      name,
						dec:       gd,
						typ:       typ,
						valTyp:    typ.Elt,
					})
				}

				ast.Walk(impf, ts.Type)

			case *ast.StructType:
				g.structs = append(g.structs, immStruct{
					commonImm: comm,
					name:      name,
					dec:       gd,
					st:        typ,
					special:   isSpecialStruct(name, typ),
				})

				ast.Walk(impf, ts.Type)
			}
		}
	}

	return g
}

func isSpecialStruct(name string, st *ast.StructType) bool {
	// work out whether this is a special struct with a Key field
	// pattern is:
	//
	// 1. struct field has a field called Key of type {{.StructName}}Key (non pointer)
	//
	// later checks will include:
	//
	// 2. said type has two fields, Uuid and Version, of type {{.StructName}}Uuid and uint64 respectively
	// 3. the underlying type of {{.StructName}}Uuid is uint64 (we might be able to relax these two
	// two underlying type restrictions)

	if st.Fields == nil {
		return false
	}

	for _, f := range st.Fields.List {
		idt, ok := f.Type.(*ast.Ident)
		if !ok {
			continue
		}

		if idt.Name != name+"Key" {
			continue
		}

		for _, fn := range f.Names {
			if fn.Name == "Key" {
				return true
			}
		}
	}

	return false
}

func (o *output) genImmTypes() {
	for f, v := range o.files {
		o.curFile = f

		if len(v.maps) == 0 && len(v.slices) == 0 && len(v.structs) == 0 {
			continue
		}

		o.output = bytes.NewBuffer(nil)

		o.pfln("// Code generated by %v. DO NOT EDIT.", immutableGenCmd)
		o.pln("")

		o.pf(o.license)

		o.pf("package %v\n", o.pkg)

		// is there a "standard" place for //go:generate comments?
		for _, v := range o.goGenCmds {
			o.pf("//go:generate %v\n", v)
		}

		o.pln("//immutableVet:skipFile")
		o.pln("")

		o.pln("import (")

		o.pln("\"myitcv.io/immutable\"")
		o.pln()

		for i := range v.imports {
			if i.Name != nil {
				o.pfln("%v %v", i.Name.Name, i.Path.Value)
			} else {
				o.pfln("%v", i.Path.Value)
			}
		}

		o.pln(")")

		o.pln("")

		o.genImmMaps(v.maps)
		o.genImmSlices(v.slices)
		o.genImmStructs(v.structs)

		source := o.output.Bytes()

		toWrite := source

		fn := o.fset.Position(f.Pos()).Filename

		// this is the file path
		offn, ok := gogenerate.NameFileFromFile(fn, immutableGenCmd)
		if !ok {
			fatalf("could not name file from %v", fn)
		}

		out := bytes.NewBuffer(nil)
		cmd := exec.Command("gofmt", "-s")
		cmd.Stdin = o.output
		cmd.Stdout = out

		err := cmd.Run()
		if err == nil {
			toWrite = out.Bytes()
		} else {
			infof("failed to format %v: %v", fn, err)
		}

		wrote, err := gogenerate.WriteIfDiff(toWrite, offn)
		if err != nil {
			fatalf("could not write %v: %v", offn, err)
		}

		if wrote {
			infof("writing %v", offn)
		} else {
			infof("skipping writing of %v; it's identical", offn)
		}
	}
}

func (o *output) exprString(e ast.Expr) string {
	var buf bytes.Buffer

	err := printer.Fprint(&buf, o.fset, e)
	if err != nil {
		panic(err)
	}

	return buf.String()
}

func (o *output) printCommentGroup(d *ast.CommentGroup) {
	if d != nil {
		for _, c := range d.List {
			o.pfln("%v", c.Text)
		}
	}
}

func (o *output) printImmPreamble(name string, node ast.Node) {
	fset := o.fset

	if st, ok := node.(*ast.StructType); ok {

		// we need to do some manipulation

		buf := bytes.NewBuffer(nil)

		fmt.Fprintf(buf, "struct {\n")

		if st.Fields != nil && st.Fields.NumFields() > 0 {
			line := o.fset.Position(st.Fields.List[0].Pos()).Line

			for _, f := range st.Fields.List {
				curLine := o.fset.Position(f.Pos()).Line

				if line != curLine {
					// catch up
					fmt.Fprintln(buf, "")
					line = curLine
				}

				ids := make([]string, 0, len(f.Names))
				for _, n := range f.Names {
					ids = append(ids, n.Name)
				}
				fmt.Fprintf(buf, "%v %v\n", strings.Join(ids, ","), o.exprString(f.Type))

				line++
			}
		}

		fmt.Fprintf(buf, "}")

		exprStr := buf.String()

		fset = token.NewFileSet()
		newnode, err := parser.ParseExprFrom(fset, "", exprStr, 0)
		if err != nil {
			fatalf("could not parse documentation struct from %v: %v", exprStr, err)
		}

		node = newnode
	}

	o.pln("//")
	o.pfln("// %v is an immutable type and has the following template:", name)
	o.pln("//")

	tmplBuf := bytes.NewBuffer(nil)

	err := printer.Fprint(tmplBuf, fset, node)
	if err != nil {
		fatalf("could not printer template declaration: %v", err)
	}

	sc := bufio.NewScanner(tmplBuf)
	for sc.Scan() {
		o.pfln("// \t%v", sc.Text())
	}
	if err := sc.Err(); err != nil {
		fatalf("could not scan printed template: %v", err)
	}

	o.pln("//")
}

func (o *output) pln(i ...interface{}) {
	fmt.Fprintln(o.output, i...)
}

func (o *output) pf(format string, i ...interface{}) {
	fmt.Fprintf(o.output, format, i...)
}

func (o *output) pfln(format string, i ...interface{}) {
	o.pf(format+"\n", i...)
}

func (o *output) pt(tmpl string, fm template.FuncMap, val interface{}) {

	// on the basis most templates are for convenience define inline
	// as raw string literals which start the ` on one line but then start
	// the template on the next (for readability) we strip the first leading
	// \n if one exists
	tmpl = strings.TrimPrefix(tmpl, "\n")

	t := template.New("tmp")
	t.Funcs(fm)

	_, err := t.Parse(tmpl)
	if err != nil {
		panic(err)
	}

	err = t.Execute(o.output, val)
	if err != nil {
		panic(err)
	}
}

func fatalf(format string, args ...interface{}) {
	panic(fmt.Errorf(format, args...))
}

func infoln(args ...interface{}) {
	if *fGoGenLog == string(gogenerate.LogInfo) {
		log.Println(args...)
	}
}

func infof(format string, args ...interface{}) {
	if *fGoGenLog == string(gogenerate.LogInfo) {
		log.Printf(format, args...)
	}
}
<|endoftext|>