<commit_before>package femebe

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"reflect"
)

type Message struct {
	// Constant-width header
	msgType byte
	sz      uint32

	// Tracks the state of the Payload stream's progression
	payloadReader io.Reader

	// Message contents buffered in memory
	buffered bytes.Buffer
}

func (m *Message) MsgType() byte {
	return m.msgType
}

func (m *Message) Payload() io.Reader {
	return m.payloadReader
}

func (m *Message) Size() uint32 {
	return m.sz
}

func (m *Message) WriteTo(w io.Writer) (_ int64, err error) {
	var totalN int64

	// Write message type byte, with a special exception for
	// differently-formatted messages (currently only
	// StartupMessage and CancelRequest; these don't have a
	// proper message type on the wire, but are noted with
	// a fake pseudo-type byte with the high-bit set)
	if mt := m.MsgType(); (mt & 0x80) == 0 {
		n, err := w.Write([]byte{mt})
		totalN += int64(n)
		if err != nil {
			return totalN, err
		}
	}

	// Write message size integer to the stream
	var bufBack [4]byte
	buf := bufBack[:]
	binary.BigEndian.PutUint32(buf, m.Size())
	nMsgSz, err := w.Write(buf)
	totalN += int64(nMsgSz)
	if err != nil {
		return totalN, err
	}

	// Write the actual payload
	nPayload, err := io.Copy(w, m.Payload())
	totalN += nPayload
	return totalN, err
}

func (m *Message) InitFullyBufferedMsg(msgType byte, size uint32) {
	m.msgType = msgType
	m.sz = size
	m.buffered.Reset()
	m.payloadReader = &m.buffered
}

func (m *Message) InitMsgFromBytes(msgType byte, payload []byte) {
	m.InitFullyBufferedMsg(msgType, uint32(len(payload))+4)
	m.buffered.Write(payload)
}

func (m *Message) InitPromiseMsg(msgType byte, size uint32, r io.Reader) {
	m.msgType = msgType
	m.sz = size
	m.payloadReader = r
	m.buffered = bytes.Buffer{}
}

func IsReadyForQuery(msg *Message) bool {
	return msg.MsgType() == MSG_READY_FOR_QUERY_Z
}

func (m *Message) InitReadyForQuery(connState ConnStatus) {
	if connState != RFQ_IDLE &&
		connState != RFQ_INTRANS &&
		connState != RFQ_ERROR {
		panic(fmt.Errorf("Invalid message type %v", connState))
	}

	m.InitMsgFromBytes(MSG_READY_FOR_QUERY_Z, []byte{byte(connState)})
}

func NewField(name string, dataType PGType) *FieldDescription {
	switch dataType {
	case INT16:
		return &FieldDescription{name, 0, 0, 21, 2, -1, ENC_FMT_TEXT}
	case INT32:
		return &FieldDescription{name, 0, 0, 23, 4, -1, ENC_FMT_TEXT}
	case INT64:
		return &FieldDescription{name, 0, 0, 20, 8, -1, ENC_FMT_TEXT}
	case FLOAT32:
		return &FieldDescription{name, 0, 0, 700, 4, -1, ENC_FMT_TEXT}
	case FLOAT64:
		return &FieldDescription{name, 0, 0, 701, 8, -1, ENC_FMT_TEXT}
	case STRING:
		return &FieldDescription{name, 0, 0, 25, -1, -1, ENC_FMT_TEXT}
	case BOOL:
		return &FieldDescription{name, 0, 0, 16, 1, -1, ENC_FMT_TEXT}
	}
	panic("Oh snap")
}

func (m *Message) InitRowDescription(fields []FieldDescription) {
	msgBytes := make([]byte, 0, len(fields)*(10+4+2+4+2+4+2))
	buf := bytes.NewBuffer(msgBytes)
	WriteInt16(buf, int16(len(fields)))
	for _, field := range fields {
		WriteCString(buf, field.name)
		WriteInt32(buf, field.tableOid)
		WriteInt16(buf, field.tableAttNo)
		WriteInt32(buf, field.typeOid)
		WriteInt16(buf, field.typLen)
		WriteInt32(buf, field.atttypmod)
		WriteInt16(buf, int16(field.format))
	}

	m.InitMsgFromBytes(MSG_ROW_DESCRIPTION_T, buf.Bytes())
}

func (m *Message) InitDataRow(cols []interface{}) {
	msgBytes := make([]byte, 0, 2+len(cols)*4)
	buf := bytes.NewBuffer(msgBytes)
	colCount := int16(len(cols))
	WriteInt16(buf, colCount)
	fmt.Printf("making data message with %v columns\n", colCount)
	for _, val := range cols {
		// TODO: allow format specification
		encodeValue(buf, val, ENC_FMT_TEXT)
	}

	m.InitMsgFromBytes(MSG_DATA_ROW_D, buf.Bytes())
}

func (m *Message) InitCommandComplete(cmdTag string) {
	msgBytes := make([]byte, 0, len([]byte(cmdTag)))
	buf := bytes.NewBuffer(msgBytes)
	WriteCString(buf, cmdTag)

	m.InitMsgFromBytes(MSG_COMMAND_COMPLETE_C, buf.Bytes())
}

func (m *Message) InitQuery(query string) {
	msgBytes := make([]byte, 0, len([]byte(query))+1)
	buf := bytes.NewBuffer(msgBytes)
	WriteCString(buf, query)
	m.InitMsgFromBytes(MSG_QUERY_Q, buf.Bytes())
}

type Query struct {
	Query string
}

func IsQuery(msg *Message) bool {
	return msg.MsgType() == 'Q'
}

func ReadQuery(msg *Message) (*Query, error) {
	qs, err := ReadCString(msg.Payload())
	if err != nil {
		return nil, err
	}

	return &Query{Query: qs}, err
}

type FieldDescription struct {
	name       string
	tableOid   int32
	tableAttNo int16
	typeOid    int32
	typLen     int16
	atttypmod  int32
	format     EncFmt
}

type PGType int16

const (
	INT16 PGType = iota
	INT32
	INT64
	FLOAT32
	FLOAT64
	STRING
	BOOL
)

func encodeValue(buff *bytes.Buffer, val interface{},
	format EncFmt) {
	switch val.(type) {
	case int16:
		EncodeInt16(buff, val.(int16), format)
	case int32:
		EncodeInt32(buff, val.(int32), format)
	case int64:
		EncodeInt64(buff, val.(int64), format)
	case float32:
		EncodeFloat32(buff, val.(float32), format)
	case float64:
		EncodeFloat64(buff, val.(float64), format)
	case string:
		EncodeString(buff, val.(string), format)
	case bool:
		EncodeBool(buff, val.(bool), format)
	default:
		panic(fmt.Errorf("Can't encode value: %#q:%#q\n",
			reflect.TypeOf(val), val))
	}
}

type RowDescription struct {
	fields []FieldDescription
}

func ReadRowDescription(msg *Message) (
	rd *RowDescription, err error) {
	if msg.MsgType() != MSG_ROW_DESCRIPTION_T {
		panic("Oh snap")
	}
	b := msg.Payload()
	fieldCount, err := ReadUint16(b)
	if err != nil {
		return nil, err
	}

	fields := make([]FieldDescription, fieldCount)
	for i, _ := range fields {
		name, err := ReadCString(b)
		if err != nil {
			return nil, err
		}

		tableOid, err := ReadInt32(b)
		if err != nil {
			return nil, err
		}
		tableAttNo, err := ReadInt16(b)
		if err != nil {
			return nil, err
		}
		typeOid, err := ReadInt32(b)
		if err != nil {
			return nil, err
		}
		typLen, err := ReadInt16(b)
		if err != nil {
			return nil, err
		}
		atttypmod, err := ReadInt32(b)
		if err != nil {
			return nil, err
		}
		format, err := ReadInt16(b)
		if err != nil {
			return nil, err
		}

		fields[i] = FieldDescription{name, tableOid, tableAttNo,
			typeOid, typLen, atttypmod, EncFmt(format)}
	}

	return &RowDescription{fields}, nil
}

type StartupMessage struct {
	Params map[string]string
}

func IsStartupMessage(msg *Message) bool {
	return msg.MsgType() == MSG_STARTUP_MESSAGE
}

func (msg *Message) ReadStartupMessage() (*StartupMessage, error) {
	if msg.MsgType() != MSG_STARTUP_MESSAGE {
		panic("Oh snap")
	}
	msgLen := msg.Size()
	b := msg.Payload()

	protoVer, err := ReadInt32(b)
	if err != nil {
		fmt.Println("protover")
		return nil, err
	}

	if protoVer != 0x00030000 {
		panic("Oh snap! Unrecognized protocol version number")
	}

	params := make(map[string]string)
	for remaining := msgLen - 8; remaining > 1; {
		fmt.Printf("remaining: %v\n", remaining)
		key, err := ReadCString(b)
		fmt.Printf("keycstring, %v %v\n", key, err)
		if err != nil {
			return nil, err
		}

		val, err := ReadCString(b)
		fmt.Printf("valcstring, %v %v\n", val, err)

		if err != nil {
			return nil, err
		}

		remaining -= uint32(len(key) + len(val) + 2) /* null bytes */
		params[key] = val
	}

	// Fidelity check on the startup packet, whereby the last byte
	// must be a NUL.
	chrBuf := make([]byte, 1)
	_, err = io.ReadAtLeast(b, chrBuf, 1)

	if chrBuf[0] != '\000' {
		panic("Oh snap! WTF byte is this?")
	}

	return &StartupMessage{params}, nil
}

func (m *Message) InitAuthenticationOk() {
	m.InitMsgFromBytes(MSG_AUTHENTICATION_OK_R, []byte{0, 0, 0, 0})
}

// FEBE Message type constants shamelessly stolen from the pq library.
//
// All the constants in this file have a special naming convention:
// "msg(NameInManual)(characterCode)".  This results in long and
// awkward constant names, but also makes it easy to determine what
// the author's intent is quickly in code (consider that both
// msgDescribeD and msgDataRowD appear on the wire as 'D') as well as
// debugging against captured wire protocol traffic (where one will
// only see 'D', but has a sense what state the protocol is in).

type EncFmt int16

const (
	ENC_FMT_TEXT    EncFmt = 0
	ENC_FMT_BINARY         = 1
	ENC_FMT_UNKNOWN        = 0
)

// Special sub-message coding for Close and Describe
const (
	IS_PORTAL = 'P'
	IS_STMT   = 'S'
)

// Sub-message character coding that is part of ReadyForQuery
type ConnStatus byte

const (
	RFQ_IDLE    ConnStatus = 'I'
	RFQ_INTRANS            = 'T'
	RFQ_ERROR              = 'E'
)

// Message tags
const (
	MSG_AUTHENTICATION_OK_R                 byte = 'R'
	MSG_AUTHENTICATION_CLEARTEXT_PASSWORD_R      = 'R'
	MSG_AUTHENTICATION_M_D5_PASSWORD_R           = 'R'
	MSG_AUTHENTICATION_S_C_M_CREDENTIAL_R        = 'R'
	MSG_AUTHENTICATION_G_S_S_R                   = 'R'
	MSG_AUTHENTICATION_S_S_P_I_R                 = 'R'
	MSG_AUTHENTICATION_G_S_S_CONTINUE_R          = 'R'
	MSG_BACKEND_KEY_DATA_K                       = 'K'
	MSG_BIND_B                                   = 'B'
	MSG_BIND_COMPLETE2                           = '2'
	MSG_CANCEL_REQUEST                           = 129 // see below
	MSG_CLOSE_C                                  = 'C'
	MSG_CLOSE_COMPLETE3                          = '3'
	MSG_COMMAND_COMPLETE_C                       = 'C'
	MSG_COPY_DATAD                               = 'd'
	MSG_COPY_DONEC                               = 'c'
	MSG_COPY_FAILF                               = 'f'
	MSG_COPY_IN_RESPONSE_G                       = 'G'
	MSG_COPY_OUT_RESPONSE_H                      = 'H'
	MSG_COPY_BOTH_RESPONSE_W                     = 'W'
	MSG_DATA_ROW_D                               = 'D'
	MSG_DESCRIBE_D                               = 'D'
	MSG_EMPTY_QUERY_RESPONSE_I                   = 'I'
	MSG_ERROR_RESPONSE_E                         = 'E'
	MSG_EXECUTE_E                                = 'E'
	MSG_FLUSH_H                                  = 'H'
	MSG_FUNCTION_CALL_F                          = 'F'
	MSG_FUNCTION_CALL_RESPONSE_V                 = 'V'
	MSG_NO_DATAN                                 = 'n'
	MSG_NOTICE_RESPONSE_N                        = 'N'
	MSG_NOTIFICATION_RESPONSE_A                  = 'A'
	MSG_PARAMETER_DESCRIPTIONT                   = 't'
	MSG_PARAMETER_STATUS_S                       = 'S'
	MSG_PARSE_P                                  = 'P'
	MSG_PARSE_COMPLETE1                          = '1'
	MSG_PASSWORD_MESSAGEP                        = 'p'
	MSG_PORTAL_SUSPENDEDS                        = 's'
	MSG_QUERY_Q                                  = 'Q'
	MSG_READY_FOR_QUERY_Z                        = 'Z'
	MSG_ROW_DESCRIPTION_T                        = 'T'
	// We treat SSLRequest as a protocol negotiation mechanic
	// rather than a first-class message, so it does not appear
	// here

	// StartupMessage and CancelRequest formatted differently:
	// on the wire, they do not have a formal message type, so
	// we use the top bit of these 8-bit bytes to flag these
	// with distinct message types. This is a pretty ugly hack,
	// but allows us to treat the messages uniformly throughout
	// most of the system
	MSG_STARTUP_MESSAGE = 128
	MSG_SYNC_S          = 'S'
	MSG_TERMINATE_X     = 'X'
)
<commit_msg>Remove excess debugging prints from message formation<commit_after>package femebe

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"reflect"
)

type Message struct {
	// Constant-width header
	msgType byte
	sz      uint32

	// Tracks the state of the Payload stream's progression
	payloadReader io.Reader

	// Message contents buffered in memory
	buffered bytes.Buffer
}

func (m *Message) MsgType() byte {
	return m.msgType
}

func (m *Message) Payload() io.Reader {
	return m.payloadReader
}

func (m *Message) Size() uint32 {
	return m.sz
}

func (m *Message) WriteTo(w io.Writer) (_ int64, err error) {
	var totalN int64

	// Write message type byte, with a special exception for
	// differently-formatted messages (currently only
	// StartupMessage and CancelRequest; these don't have a
	// proper message type on the wire, but are noted with
	// a fake pseudo-type byte with the high-bit set)
	if mt := m.MsgType(); (mt & 0x80) == 0 {
		n, err := w.Write([]byte{mt})
		totalN += int64(n)
		if err != nil {
			return totalN, err
		}
	}

	// Write message size integer to the stream
	var bufBack [4]byte
	buf := bufBack[:]
	binary.BigEndian.PutUint32(buf, m.Size())
	nMsgSz, err := w.Write(buf)
	totalN += int64(nMsgSz)
	if err != nil {
		return totalN, err
	}

	// Write the actual payload
	nPayload, err := io.Copy(w, m.Payload())
	totalN += nPayload
	return totalN, err
}

func (m *Message) InitFullyBufferedMsg(msgType byte, size uint32) {
	m.msgType = msgType
	m.sz = size
	m.buffered.Reset()
	m.payloadReader = &m.buffered
}

func (m *Message) InitMsgFromBytes(msgType byte, payload []byte) {
	m.InitFullyBufferedMsg(msgType, uint32(len(payload))+4)
	m.buffered.Write(payload)
}

func (m *Message) InitPromiseMsg(msgType byte, size uint32, r io.Reader) {
	m.msgType = msgType
	m.sz = size
	m.payloadReader = r
	m.buffered = bytes.Buffer{}
}

func IsReadyForQuery(msg *Message) bool {
	return msg.MsgType() == MSG_READY_FOR_QUERY_Z
}

func (m *Message) InitReadyForQuery(connState ConnStatus) {
	if connState != RFQ_IDLE &&
		connState != RFQ_INTRANS &&
		connState != RFQ_ERROR {
		panic(fmt.Errorf("Invalid message type %v", connState))
	}

	m.InitMsgFromBytes(MSG_READY_FOR_QUERY_Z, []byte{byte(connState)})
}

func NewField(name string, dataType PGType) *FieldDescription {
	switch dataType {
	case INT16:
		return &FieldDescription{name, 0, 0, 21, 2, -1, ENC_FMT_TEXT}
	case INT32:
		return &FieldDescription{name, 0, 0, 23, 4, -1, ENC_FMT_TEXT}
	case INT64:
		return &FieldDescription{name, 0, 0, 20, 8, -1, ENC_FMT_TEXT}
	case FLOAT32:
		return &FieldDescription{name, 0, 0, 700, 4, -1, ENC_FMT_TEXT}
	case FLOAT64:
		return &FieldDescription{name, 0, 0, 701, 8, -1, ENC_FMT_TEXT}
	case STRING:
		return &FieldDescription{name, 0, 0, 25, -1, -1, ENC_FMT_TEXT}
	case BOOL:
		return &FieldDescription{name, 0, 0, 16, 1, -1, ENC_FMT_TEXT}
	}
	panic("Oh snap")
}

func (m *Message) InitRowDescription(fields []FieldDescription) {
	msgBytes := make([]byte, 0, len(fields)*(10+4+2+4+2+4+2))
	buf := bytes.NewBuffer(msgBytes)
	WriteInt16(buf, int16(len(fields)))
	for _, field := range fields {
		WriteCString(buf, field.name)
		WriteInt32(buf, field.tableOid)
		WriteInt16(buf, field.tableAttNo)
		WriteInt32(buf, field.typeOid)
		WriteInt16(buf, field.typLen)
		WriteInt32(buf, field.atttypmod)
		WriteInt16(buf, int16(field.format))
	}

	m.InitMsgFromBytes(MSG_ROW_DESCRIPTION_T, buf.Bytes())
}

func (m *Message) InitDataRow(cols []interface{}) {
	msgBytes := make([]byte, 0, 2+len(cols)*4)
	buf := bytes.NewBuffer(msgBytes)
	colCount := int16(len(cols))
	WriteInt16(buf, colCount)
	for _, val := range cols {
		// TODO: allow format specification
		encodeValue(buf, val, ENC_FMT_TEXT)
	}

	m.InitMsgFromBytes(MSG_DATA_ROW_D, buf.Bytes())
}

func (m *Message) InitCommandComplete(cmdTag string) {
	msgBytes := make([]byte, 0, len([]byte(cmdTag)))
	buf := bytes.NewBuffer(msgBytes)
	WriteCString(buf, cmdTag)

	m.InitMsgFromBytes(MSG_COMMAND_COMPLETE_C, buf.Bytes())
}

func (m *Message) InitQuery(query string) {
	msgBytes := make([]byte, 0, len([]byte(query))+1)
	buf := bytes.NewBuffer(msgBytes)
	WriteCString(buf, query)
	m.InitMsgFromBytes(MSG_QUERY_Q, buf.Bytes())
}

type Query struct {
	Query string
}

func IsQuery(msg *Message) bool {
	return msg.MsgType() == 'Q'
}

func ReadQuery(msg *Message) (*Query, error) {
	qs, err := ReadCString(msg.Payload())
	if err != nil {
		return nil, err
	}

	return &Query{Query: qs}, err
}

type FieldDescription struct {
	name       string
	tableOid   int32
	tableAttNo int16
	typeOid    int32
	typLen     int16
	atttypmod  int32
	format     EncFmt
}

type PGType int16

const (
	INT16 PGType = iota
	INT32
	INT64
	FLOAT32
	FLOAT64
	STRING
	BOOL
)

func encodeValue(buff *bytes.Buffer, val interface{},
	format EncFmt) {
	switch val.(type) {
	case int16:
		EncodeInt16(buff, val.(int16), format)
	case int32:
		EncodeInt32(buff, val.(int32), format)
	case int64:
		EncodeInt64(buff, val.(int64), format)
	case float32:
		EncodeFloat32(buff, val.(float32), format)
	case float64:
		EncodeFloat64(buff, val.(float64), format)
	case string:
		EncodeString(buff, val.(string), format)
	case bool:
		EncodeBool(buff, val.(bool), format)
	default:
		panic(fmt.Errorf("Can't encode value: %#q:%#q\n",
			reflect.TypeOf(val), val))
	}
}

type RowDescription struct {
	fields []FieldDescription
}

func ReadRowDescription(msg *Message) (
	rd *RowDescription, err error) {
	if msg.MsgType() != MSG_ROW_DESCRIPTION_T {
		panic("Oh snap")
	}
	b := msg.Payload()
	fieldCount, err := ReadUint16(b)
	if err != nil {
		return nil, err
	}

	fields := make([]FieldDescription, fieldCount)
	for i, _ := range fields {
		name, err := ReadCString(b)
		if err != nil {
			return nil, err
		}

		tableOid, err := ReadInt32(b)
		if err != nil {
			return nil, err
		}
		tableAttNo, err := ReadInt16(b)
		if err != nil {
			return nil, err
		}
		typeOid, err := ReadInt32(b)
		if err != nil {
			return nil, err
		}
		typLen, err := ReadInt16(b)
		if err != nil {
			return nil, err
		}
		atttypmod, err := ReadInt32(b)
		if err != nil {
			return nil, err
		}
		format, err := ReadInt16(b)
		if err != nil {
			return nil, err
		}

		fields[i] = FieldDescription{name, tableOid, tableAttNo,
			typeOid, typLen, atttypmod, EncFmt(format)}
	}

	return &RowDescription{fields}, nil
}

type StartupMessage struct {
	Params map[string]string
}

func IsStartupMessage(msg *Message) bool {
	return msg.MsgType() == MSG_STARTUP_MESSAGE
}

func (msg *Message) ReadStartupMessage() (*StartupMessage, error) {
	if msg.MsgType() != MSG_STARTUP_MESSAGE {
		panic("Oh snap")
	}
	msgLen := msg.Size()
	b := msg.Payload()

	protoVer, err := ReadInt32(b)
	if err != nil {
		return nil, err
	}

	if protoVer != 0x00030000 {
		panic("Oh snap! Unrecognized protocol version number")
	}

	params := make(map[string]string)
	for remaining := msgLen - 8; remaining > 1; {
		key, err := ReadCString(b)
		if err != nil {
			return nil, err
		}

		val, err := ReadCString(b)

		if err != nil {
			return nil, err
		}

		remaining -= uint32(len(key) + len(val) + 2) /* null bytes */
		params[key] = val
	}

	// Fidelity check on the startup packet, whereby the last byte
	// must be a NUL.
	chrBuf := make([]byte, 1)
	_, err = io.ReadAtLeast(b, chrBuf, 1)

	if chrBuf[0] != '\000' {
		panic("Oh snap! WTF byte is this?")
	}

	return &StartupMessage{params}, nil
}

func (m *Message) InitAuthenticationOk() {
	m.InitMsgFromBytes(MSG_AUTHENTICATION_OK_R, []byte{0, 0, 0, 0})
}

// FEBE Message type constants shamelessly stolen from the pq library.
//
// All the constants in this file have a special naming convention:
// "msg(NameInManual)(characterCode)".  This results in long and
// awkward constant names, but also makes it easy to determine what
// the author's intent is quickly in code (consider that both
// msgDescribeD and msgDataRowD appear on the wire as 'D') as well as
// debugging against captured wire protocol traffic (where one will
// only see 'D', but has a sense what state the protocol is in).

type EncFmt int16

const (
	ENC_FMT_TEXT    EncFmt = 0
	ENC_FMT_BINARY         = 1
	ENC_FMT_UNKNOWN        = 0
)

// Special sub-message coding for Close and Describe
const (
	IS_PORTAL = 'P'
	IS_STMT   = 'S'
)

// Sub-message character coding that is part of ReadyForQuery
type ConnStatus byte

const (
	RFQ_IDLE    ConnStatus = 'I'
	RFQ_INTRANS            = 'T'
	RFQ_ERROR              = 'E'
)

// Message tags
const (
	MSG_AUTHENTICATION_OK_R                 byte = 'R'
	MSG_AUTHENTICATION_CLEARTEXT_PASSWORD_R      = 'R'
	MSG_AUTHENTICATION_M_D5_PASSWORD_R           = 'R'
	MSG_AUTHENTICATION_S_C_M_CREDENTIAL_R        = 'R'
	MSG_AUTHENTICATION_G_S_S_R                   = 'R'
	MSG_AUTHENTICATION_S_S_P_I_R                 = 'R'
	MSG_AUTHENTICATION_G_S_S_CONTINUE_R          = 'R'
	MSG_BACKEND_KEY_DATA_K                       = 'K'
	MSG_BIND_B                                   = 'B'
	MSG_BIND_COMPLETE2                           = '2'
	MSG_CANCEL_REQUEST                           = 129 // see below
	MSG_CLOSE_C                                  = 'C'
	MSG_CLOSE_COMPLETE3                          = '3'
	MSG_COMMAND_COMPLETE_C                       = 'C'
	MSG_COPY_DATAD                               = 'd'
	MSG_COPY_DONEC                               = 'c'
	MSG_COPY_FAILF                               = 'f'
	MSG_COPY_IN_RESPONSE_G                       = 'G'
	MSG_COPY_OUT_RESPONSE_H                      = 'H'
	MSG_COPY_BOTH_RESPONSE_W                     = 'W'
	MSG_DATA_ROW_D                               = 'D'
	MSG_DESCRIBE_D                               = 'D'
	MSG_EMPTY_QUERY_RESPONSE_I                   = 'I'
	MSG_ERROR_RESPONSE_E                         = 'E'
	MSG_EXECUTE_E                                = 'E'
	MSG_FLUSH_H                                  = 'H'
	MSG_FUNCTION_CALL_F                          = 'F'
	MSG_FUNCTION_CALL_RESPONSE_V                 = 'V'
	MSG_NO_DATAN                                 = 'n'
	MSG_NOTICE_RESPONSE_N                        = 'N'
	MSG_NOTIFICATION_RESPONSE_A                  = 'A'
	MSG_PARAMETER_DESCRIPTIONT                   = 't'
	MSG_PARAMETER_STATUS_S                       = 'S'
	MSG_PARSE_P                                  = 'P'
	MSG_PARSE_COMPLETE1                          = '1'
	MSG_PASSWORD_MESSAGEP                        = 'p'
	MSG_PORTAL_SUSPENDEDS                        = 's'
	MSG_QUERY_Q                                  = 'Q'
	MSG_READY_FOR_QUERY_Z                        = 'Z'
	MSG_ROW_DESCRIPTION_T                        = 'T'
	// We treat SSLRequest as a protocol negotiation mechanic
	// rather than a first-class message, so it does not appear
	// here

	// StartupMessage and CancelRequest formatted differently:
	// on the wire, they do not have a formal message type, so
	// we use the top bit of these 8-bit bytes to flag these
	// with distinct message types. This is a pretty ugly hack,
	// but allows us to treat the messages uniformly throughout
	// most of the system
	MSG_STARTUP_MESSAGE = 128
	MSG_SYNC_S          = 'S'
	MSG_TERMINATE_X     = 'X'
)
<|endoftext|>


<commit_before>package osc

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"net"
	"strings"
)

var (
	ErrIndexOutOfBounds = errors.New("index out of bounds")
	ErrInvalidTypeTag   = errors.New("invalid type tag")
	ErrNilWriter        = errors.New("writer must not be nil")
	ErrParse            = errors.New("error parsing message")
)

// Message is an OSC message.
// An OSC message consists of an OSC address pattern and zero or more arguments.
type Message struct {
	address       []byte
	typetag       []byte
	argbuf        *bytes.Buffer
	ttReadIndex   int
	senderAddress net.Addr
}

// NewMessage creates a new OSC message.
func NewMessage(addr string) (*Message, error) {
	return &Message{
		address:     []byte(addr),
		argbuf:      &bytes.Buffer{},
		typetag:     []byte{typetagPrefix},
		ttReadIndex: 1, // skip the leading ','
	}, nil
}

// CountArguments returns the number of arguments in the OSC message.
func (msg *Message) CountArguments() int {
	return len(msg.typetag) - 1
}

// ReadInt32 reads an int32 value from an OSC message.
func (msg *Message) ReadInt32() (int32, error) {
	tt := msg.typetag[msg.ttReadIndex]
	if tt != typetagInt {
		return 0, fmt.Errorf("Unexpected type %c", tt)
	}
	var val int32
	if err := binary.Read(msg.argbuf, byteOrder, &val); err != nil {
		return 0, err
	}
	msg.ttReadIndex++
	return val, nil
}

// ReadFloat32 reads a float32 value from an OSC message.
func (msg *Message) ReadFloat32() (float32, error) {
	tt := msg.typetag[msg.ttReadIndex]
	if tt != typetagFloat {
		return 0, fmt.Errorf("Unexpected type %c", tt)
	}
	var val float32
	if err := binary.Read(msg.argbuf, byteOrder, &val); err != nil {
		return 0, err
	}
	msg.ttReadIndex++
	return val, nil
}

// ReadBool reads a boolean value from an OSC message.
func (msg *Message) ReadBool() (bool, error) {
	tt := msg.typetag[msg.ttReadIndex]
	if tt != typetagTrue && tt != typetagFalse {
		return false, fmt.Errorf("Unexpected type %c", tt)
	}
	msg.ttReadIndex++
	return tt == typetagTrue, nil
}

// ReadString reads a string value from an OSC message.
func (msg *Message) ReadString() (string, error) {
	tt := msg.typetag[msg.ttReadIndex]
	if tt != typetagString {
		return "", fmt.Errorf("Unexpected type %c", tt)
	}

	val := []byte{}
	for i := 0; i < msg.argbuf.Len(); i++ {
		c, err := msg.argbuf.ReadByte()
		if err == io.EOF {
			break
		}
		if err != nil {
			return "", err
		}
		if c == 0 {
			// string values are padded to 32 bits by null bytes
			for j := i; j%4 != 0; j++ {
				if _, err := msg.argbuf.ReadByte(); err != nil && err != io.EOF {
					return "", err
				}
			}
			break
		}
		val = append(val, c)
	}

	msg.ttReadIndex++
	return string(val), nil
}

// ReadBlob reads a binary blob from an OSC message.
func (msg *Message) ReadBlob() ([]byte, error) {
	tt := msg.typetag[msg.ttReadIndex]
	if tt != typetagBlob {
		return nil, fmt.Errorf("Unexpected type %c", tt)
	}
	// read length
	var bl int32
	if err := binary.Read(msg.argbuf, byteOrder, &bl); err != nil {
		return nil, err
	}
	// read blob
	blob := make([]byte, bl)
	if _, err := msg.argbuf.Read(blob); err != nil {
		return nil, err
	}
	msg.ttReadIndex++
	return blob, nil
}

// WriteInt32 writes an int32 value to an OSC message.
func (msg *Message) WriteInt32(val int32) error {
	msg.typetag = append(msg.typetag, typetagInt)
	return binary.Write(msg.argbuf, byteOrder, val)
}

// WriteFloat32 writes a float32 value to an OSC message.
func (msg *Message) WriteFloat32(val float32) error {
	msg.typetag = append(msg.typetag, typetagFloat)
	return binary.Write(msg.argbuf, byteOrder, val)
}

// WriteBool writes a boolean value to an OSC message.
func (msg *Message) WriteBool(val bool) error {
	if val {
		msg.typetag = append(msg.typetag, typetagTrue)
	} else {
		msg.typetag = append(msg.typetag, typetagFalse)
	}
	return nil
}

// WriteString writes a string value to an OSC message.
func (msg *Message) WriteString(val string) error {
	msg.typetag = append(msg.typetag, typetagString)
	i := 0
	for _, c := range []byte(val) {
		if err := msg.argbuf.WriteByte(c); err != nil {
			return err
		}
		i++
	}
	for j := i; j%4 != 0; j++ {
		if err := msg.argbuf.WriteByte(0); err != nil {
			return err
		}
	}
	return nil
}

// WriteBlob writes a binary blob to an OSC message.
func (msg *Message) WriteBlob(blob []byte) error {
	bl := len(blob)
	if bl == 0 {
		return nil
	}

	// add typetag
	msg.typetag = append(msg.typetag, typetagBlob)

	// write length
	if err := binary.Write(msg.argbuf, byteOrder, int32(bl)); err != nil {
		return err
	}
	// write blob
	if _, err := msg.argbuf.Write(blob); err != nil {
		return err
	}
	// make it 32-bit aligned
	for i := bl; i%4 != 0; i++ {
		if err := msg.argbuf.WriteByte(0); err != nil {
			return err
		}
	}
	return nil
}

// TypeTags returns the message's typetags as a string.
func (msg *Message) TypeTags() string {
	return string(msg.typetag)
}

// Sender returns the address from which a message was sent.
func (msg *Message) Sender() net.Addr {
	return msg.senderAddress
}

// Returns true, if the address of the OSC Message matches the given address.
// Case sensitive!
func (msg *Message) Match(address string) (bool, error) {
	addr := string(msg.address)

	// verify same number of parts
	if !verifyParts(address, addr) {
		return false, nil
	}

	exp, err := getRegEx(addr)
	if err != nil {
		return false, err
	}
	return exp.MatchString(address), nil
}

// bytes returns the message as a slice of bytes.
func (msg *Message) bytes() ([]byte, error) {
	var (
		w            = &bytes.Buffer{}
		bytesWritten int64
	)

	// Write address
	if bw, err := w.Write(msg.address); err != nil {
		return nil, err
	} else {
		bytesWritten += int64(bw)
	}

	// Write typetag
	if bw, err := w.Write(msg.typetag); err != nil {
		return nil, err
	} else {
		bytesWritten += int64(bw)
	}

	// Write padding
	for i := bytesWritten; i%4 != 0; i++ {
		if err := w.WriteByte(0); err != nil {
			return nil, err
		}
	}

	// Write arguments
	args := msg.argbuf.Bytes()
	if _, err := w.Write(args); err != nil {
		return nil, err
	}

	return w.Bytes(), nil
}

// Print pretty prints a Message to an io.Writer.
func (msg *Message) Print(w io.Writer) error {
	if _, err := fmt.Fprintf(w, "%s%s", msg.address, msg.typetag); err != nil {
		return err
	}

	for _, tt := range msg.typetag[1:] {
		switch tt {
		case typetagInt:
			val, err := msg.ReadInt32()
			if err != nil {
				return err
			}
			if _, err := fmt.Fprintf(w, " %d", val); err != nil {
				return err
			}
		case typetagFloat:
			val, err := msg.ReadFloat32()
			if err != nil {
				return err
			}
			if _, err := fmt.Fprintf(w, " %f", val); err != nil {
				return err
			}
		case typetagString:
			val, err := msg.ReadString()
			if err != nil {
				return err
			}
			if _, err := fmt.Fprintf(w, " %s", val); err != nil {
				return err
			}
			// TODO: handle blobs
		case typetagTrue, typetagFalse:
			val, err := msg.ReadBool()
			if err != nil {
				return err
			}
			if _, err := fmt.Fprintf(w, " %t", val); err != nil {
				return err
			}
		}
	}

	return nil
}

// parseMessage parses an OSC message from a slice of bytes.
func parseMessage(data []byte, senderAddress net.Addr) (*Message, error) {
	var (
		address []byte
		i       = 0
		n       = len(data)
	)
	for i < n {
		if data[i] == ',' {
			address = data[0:i]
			break
		}
		i++
	}

	msg := &Message{
		address:       address,
		senderAddress: senderAddress,
		ttReadIndex:   1,
	}

	// Read all arguments
	if err := msg.parseArguments(data[i:]); err != nil {
		return nil, err
	}

	return msg, nil
}

// parseArguments reads all arguments from the reader and adds it to the OSC message.
func (msg *Message) parseArguments(data []byte) error {
	if len(data) == 0 || data[0] != typetagPrefix {
		return ErrInvalidTypeTag
	}

	var (
		i = 0
		n = len(data)
	)

	// read the typetag
	for i < n {
		if data[i] == 0 {
			msg.typetag = data[0:i]
			break
		}
		i++
	}

	// advance i to the next multiple of 4
	for i%4 != 0 {
		i++
	}

	msg.argbuf = bytes.NewBuffer(data[i:])
	return nil
}

// clone creates a clone of an OSC message.
func (msg *Message) clone() (*Message, error) {
	return &Message{
		address:       msg.address,
		typetag:       msg.typetag,
		argbuf:        bytes.NewBuffer(msg.argbuf.Bytes()),
		ttReadIndex:   1,
		senderAddress: msg.senderAddress,
	}, nil
}

// verifyParts verifies that m1 and m2 have the same number of parts,
// where a part is a nonempty string between pairs of '/' or a nonempty
// string at the end.
func verifyParts(m1, m2 string) bool {
	if m1 == m2 {
		return true
	}

	mc := string(messageChar)

	p1, p2 := strings.Split(m1, mc), strings.Split(m2, mc)
	if len(p1) != len(p2) || len(p1) == 0 {
		return false
	}
	for i, p := range p1[1:] {
		if len(p) == 0 || len(p2[i+1]) == 0 {
			return false
		}
	}

	return true
}
<commit_msg>add Address method<commit_after>package osc

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"net"
	"strings"
)

var (
	ErrIndexOutOfBounds = errors.New("index out of bounds")
	ErrInvalidTypeTag   = errors.New("invalid type tag")
	ErrNilWriter        = errors.New("writer must not be nil")
	ErrParse            = errors.New("error parsing message")
)

// Message is an OSC message.
// An OSC message consists of an OSC address pattern and zero or more arguments.
type Message struct {
	address       []byte
	typetag       []byte
	argbuf        *bytes.Buffer
	ttReadIndex   int
	senderAddress net.Addr
}

// NewMessage creates a new OSC message.
func NewMessage(addr string) (*Message, error) {
	return &Message{
		address:     []byte(addr),
		argbuf:      &bytes.Buffer{},
		typetag:     []byte{typetagPrefix},
		ttReadIndex: 1, // skip the leading ','
	}, nil
}

// Address returns the address of the message.
func (msg *Message) Address() string {
	return string(msg.address)
}

// CountArguments returns the number of arguments in the OSC message.
func (msg *Message) CountArguments() int {
	return len(msg.typetag) - 1
}

// ReadInt32 reads an int32 value from an OSC message.
func (msg *Message) ReadInt32() (int32, error) {
	tt := msg.typetag[msg.ttReadIndex]
	if tt != typetagInt {
		return 0, fmt.Errorf("Unexpected type %c", tt)
	}
	var val int32
	if err := binary.Read(msg.argbuf, byteOrder, &val); err != nil {
		return 0, err
	}
	msg.ttReadIndex++
	return val, nil
}

// ReadFloat32 reads a float32 value from an OSC message.
func (msg *Message) ReadFloat32() (float32, error) {
	tt := msg.typetag[msg.ttReadIndex]
	if tt != typetagFloat {
		return 0, fmt.Errorf("Unexpected type %c", tt)
	}
	var val float32
	if err := binary.Read(msg.argbuf, byteOrder, &val); err != nil {
		return 0, err
	}
	msg.ttReadIndex++
	return val, nil
}

// ReadBool reads a boolean value from an OSC message.
func (msg *Message) ReadBool() (bool, error) {
	tt := msg.typetag[msg.ttReadIndex]
	if tt != typetagTrue && tt != typetagFalse {
		return false, fmt.Errorf("Unexpected type %c", tt)
	}
	msg.ttReadIndex++
	return tt == typetagTrue, nil
}

// ReadString reads a string value from an OSC message.
func (msg *Message) ReadString() (string, error) {
	tt := msg.typetag[msg.ttReadIndex]
	if tt != typetagString {
		return "", fmt.Errorf("Unexpected type %c", tt)
	}

	val := []byte{}
	for i := 0; i < msg.argbuf.Len(); i++ {
		c, err := msg.argbuf.ReadByte()
		if err == io.EOF {
			break
		}
		if err != nil {
			return "", err
		}
		if c == 0 {
			// string values are padded to 32 bits by null bytes
			for j := i; j%4 != 0; j++ {
				if _, err := msg.argbuf.ReadByte(); err != nil && err != io.EOF {
					return "", err
				}
			}
			break
		}
		val = append(val, c)
	}

	msg.ttReadIndex++
	return string(val), nil
}

// ReadBlob reads a binary blob from an OSC message.
func (msg *Message) ReadBlob() ([]byte, error) {
	tt := msg.typetag[msg.ttReadIndex]
	if tt != typetagBlob {
		return nil, fmt.Errorf("Unexpected type %c", tt)
	}
	// read length
	var bl int32
	if err := binary.Read(msg.argbuf, byteOrder, &bl); err != nil {
		return nil, err
	}
	// read blob
	blob := make([]byte, bl)
	if _, err := msg.argbuf.Read(blob); err != nil {
		return nil, err
	}
	msg.ttReadIndex++
	return blob, nil
}

// WriteInt32 writes an int32 value to an OSC message.
func (msg *Message) WriteInt32(val int32) error {
	msg.typetag = append(msg.typetag, typetagInt)
	return binary.Write(msg.argbuf, byteOrder, val)
}

// WriteFloat32 writes a float32 value to an OSC message.
func (msg *Message) WriteFloat32(val float32) error {
	msg.typetag = append(msg.typetag, typetagFloat)
	return binary.Write(msg.argbuf, byteOrder, val)
}

// WriteBool writes a boolean value to an OSC message.
func (msg *Message) WriteBool(val bool) error {
	if val {
		msg.typetag = append(msg.typetag, typetagTrue)
	} else {
		msg.typetag = append(msg.typetag, typetagFalse)
	}
	return nil
}

// WriteString writes a string value to an OSC message.
func (msg *Message) WriteString(val string) error {
	msg.typetag = append(msg.typetag, typetagString)
	i := 0
	for _, c := range []byte(val) {
		if err := msg.argbuf.WriteByte(c); err != nil {
			return err
		}
		i++
	}
	for j := i; j%4 != 0; j++ {
		if err := msg.argbuf.WriteByte(0); err != nil {
			return err
		}
	}
	return nil
}

// WriteBlob writes a binary blob to an OSC message.
func (msg *Message) WriteBlob(blob []byte) error {
	bl := len(blob)
	if bl == 0 {
		return nil
	}

	// add typetag
	msg.typetag = append(msg.typetag, typetagBlob)

	// write length
	if err := binary.Write(msg.argbuf, byteOrder, int32(bl)); err != nil {
		return err
	}
	// write blob
	if _, err := msg.argbuf.Write(blob); err != nil {
		return err
	}
	// make it 32-bit aligned
	for i := bl; i%4 != 0; i++ {
		if err := msg.argbuf.WriteByte(0); err != nil {
			return err
		}
	}
	return nil
}

// TypeTags returns the message's typetags as a string.
func (msg *Message) TypeTags() string {
	return string(msg.typetag)
}

// Sender returns the address from which a message was sent.
func (msg *Message) Sender() net.Addr {
	return msg.senderAddress
}

// Returns true, if the address of the OSC Message matches the given address.
// Case sensitive!
func (msg *Message) Match(address string) (bool, error) {
	addr := string(msg.address)

	// verify same number of parts
	if !verifyParts(address, addr) {
		return false, nil
	}

	exp, err := getRegEx(addr)
	if err != nil {
		return false, err
	}
	return exp.MatchString(address), nil
}

// bytes returns the message as a slice of bytes.
func (msg *Message) bytes() ([]byte, error) {
	var (
		w            = &bytes.Buffer{}
		bytesWritten int64
	)

	// Write address
	if bw, err := w.Write(msg.address); err != nil {
		return nil, err
	} else {
		bytesWritten += int64(bw)
	}

	// Write typetag
	if bw, err := w.Write(msg.typetag); err != nil {
		return nil, err
	} else {
		bytesWritten += int64(bw)
	}

	// Write padding
	for i := bytesWritten; i%4 != 0; i++ {
		if err := w.WriteByte(0); err != nil {
			return nil, err
		}
	}

	// Write arguments
	args := msg.argbuf.Bytes()
	if _, err := w.Write(args); err != nil {
		return nil, err
	}

	return w.Bytes(), nil
}

// Print pretty prints a Message to an io.Writer.
func (msg *Message) Print(w io.Writer) error {
	if _, err := fmt.Fprintf(w, "%s%s", msg.address, msg.typetag); err != nil {
		return err
	}

	for _, tt := range msg.typetag[1:] {
		switch tt {
		case typetagInt:
			val, err := msg.ReadInt32()
			if err != nil {
				return err
			}
			if _, err := fmt.Fprintf(w, " %d", val); err != nil {
				return err
			}
		case typetagFloat:
			val, err := msg.ReadFloat32()
			if err != nil {
				return err
			}
			if _, err := fmt.Fprintf(w, " %f", val); err != nil {
				return err
			}
		case typetagString:
			val, err := msg.ReadString()
			if err != nil {
				return err
			}
			if _, err := fmt.Fprintf(w, " %s", val); err != nil {
				return err
			}
			// TODO: handle blobs
		case typetagTrue, typetagFalse:
			val, err := msg.ReadBool()
			if err != nil {
				return err
			}
			if _, err := fmt.Fprintf(w, " %t", val); err != nil {
				return err
			}
		}
	}

	return nil
}

// parseMessage parses an OSC message from a slice of bytes.
func parseMessage(data []byte, senderAddress net.Addr) (*Message, error) {
	var (
		address []byte
		i       = 0
		n       = len(data)
	)
	for i < n {
		if data[i] == ',' {
			address = data[0:i]
			break
		}
		i++
	}

	msg := &Message{
		address:       address,
		senderAddress: senderAddress,
		ttReadIndex:   1,
	}

	// Read all arguments
	if err := msg.parseArguments(data[i:]); err != nil {
		return nil, err
	}

	return msg, nil
}

// parseArguments reads all arguments from the reader and adds it to the OSC message.
func (msg *Message) parseArguments(data []byte) error {
	if len(data) == 0 || data[0] != typetagPrefix {
		return ErrInvalidTypeTag
	}

	var (
		i = 0
		n = len(data)
	)

	// read the typetag
	for i < n {
		if data[i] == 0 {
			msg.typetag = data[0:i]
			break
		}
		i++
	}

	// advance i to the next multiple of 4
	for i%4 != 0 {
		i++
	}

	msg.argbuf = bytes.NewBuffer(data[i:])
	return nil
}

// clone creates a clone of an OSC message.
func (msg *Message) clone() (*Message, error) {
	return &Message{
		address:       msg.address,
		typetag:       msg.typetag,
		argbuf:        bytes.NewBuffer(msg.argbuf.Bytes()),
		ttReadIndex:   1,
		senderAddress: msg.senderAddress,
	}, nil
}

// verifyParts verifies that m1 and m2 have the same number of parts,
// where a part is a nonempty string between pairs of '/' or a nonempty
// string at the end.
func verifyParts(m1, m2 string) bool {
	if m1 == m2 {
		return true
	}

	mc := string(messageChar)

	p1, p2 := strings.Split(m1, mc), strings.Split(m2, mc)
	if len(p1) != len(p2) || len(p1) == 0 {
		return false
	}
	for i, p := range p1[1:] {
		if len(p) == 0 || len(p2[i+1]) == 0 {
			return false
		}
	}

	return true
}
<|endoftext|>


<commit_before>package netlink

import (
	"errors"
	"fmt"

	"github.com/mdlayher/netlink/nlenc"
)

// Various errors which may occur when attempting to marshal or unmarshal
// a Message to and from its binary form.
var (
	errIncorrectMessageLength = errors.New("netlink message header length incorrect")
	errShortMessage           = errors.New("not enough data to create a netlink message")
	errUnalignedMessage       = errors.New("input data is not properly aligned for netlink message")
)

// HeaderFlags specify flags which may be present in a Header.
type HeaderFlags uint16

const (
	// General netlink communication flags.

	// Request indicates a request to netlink.
	Request HeaderFlags = 1

	// Multi indicates a multi-part message, terminated
	// by Done on the last message.
	Multi HeaderFlags = 2

	// Acknowledge requests that netlink reply with
	// an acknowledgement using Error and, if needed,
	// an error code.
	Acknowledge HeaderFlags = 4

	// Echo requests that netlink echo this request
	// back to the sender.
	Echo HeaderFlags = 8

	// DumpInterrupted indicates that a dump was
	// inconsistent due to a sequence change.
	DumpInterrupted HeaderFlags = 16

	// DumpFiltered indicates that a dump was filtered
	// as requested.
	DumpFiltered HeaderFlags = 32

	// Flags used to retrieve data from netlink.

	// Root requests that netlink return a complete table instead
	// of a single entry.
	Root HeaderFlags = 0x100

	// Match requests that netlink return a list of all matching
	// entries.
	Match HeaderFlags = 0x200

	// Atomic requests that netlink send an atomic snapshot of
	// its entries.  Requires CAP_NET_ADMIN or an effective UID of 0.
	Atomic HeaderFlags = 0x400

	// Dump requests that netlink return a complete list of
	// all entries.
	Dump HeaderFlags = Root | Match

	// Flags used to create objects.

	// Replace indicates request replaces an existing matching object.
	Replace HeaderFlags = 0x100

	// Excl  indicates request does not replace the object if it already exists.
	Excl HeaderFlags = 0x200

	// Create indicates request creates an object if it doesn't already exist.
	Create HeaderFlags = 0x400

	// Append indicates request adds to the end of the object list.
	Append HeaderFlags = 0x800
)

// String returns the string representation of a HeaderFlags.
func (f HeaderFlags) String() string {
	names := []string{
		"request",
		"multi",
		"acknowledge",
		"echo",
		"dumpinterrupted",
		"dumpfiltered",
	}

	var s string

	left := uint(f)

	for i, name := range names {
		if f&(1<<uint(i)) != 0 {
			if s != "" {
				s += "|"
			}

			s += name

			left ^= (1 << uint(i))
		}
	}

	if s == "" && left == 0 {
		s = "0"
	}

	if left > 0 {
		if s != "" {
			s += "|"
		}
		s += fmt.Sprintf("%#x", left)
	}

	return s
}

// HeaderType specifies the type of a Header.
type HeaderType uint16

const (
	// Noop indicates that no action was taken.
	Noop HeaderType = 0x1

	// Error indicates an error code is present, which is also
	// used to indicate success when the code is 0.
	Error HeaderType = 0x2

	// Done indicates the end of a multi-part message.
	Done HeaderType = 0x3

	// Overrun indicates that data was lost from this message.
	Overrun HeaderType = 0x4
)

// String returns the string representation of a HeaderType.
func (t HeaderType) String() string {
	switch t {
	case Noop:
		return "noop"
	case Error:
		return "error"
	case Done:
		return "done"
	case Overrun:
		return "overrun"
	default:
		return fmt.Sprintf("unknown(%d)", t)
	}
}

// NB: the memory layout of Header and Linux's syscall.NlMsgHdr must be
// exactly the same.  Cannot reorder, change data type, add, or remove fields.
// Named types of the same size (e.g. HeaderFlags is a uint16) are okay.

// A Header is a netlink header.  A Header is sent and received with each
// Message to indicate metadata regarding a Message.
type Header struct {
	// Length of a Message, including this Header.
	Length uint32

	// Contents of a Message.
	Type HeaderType

	// Flags which may be used to modify a request or response.
	Flags HeaderFlags

	// The sequence number of a Message.
	Sequence uint32

	// The process ID of the sending process.
	PID uint32
}

// A Message is a netlink message.  It contains a Header and an arbitrary
// byte payload, which may be decoded using information from the Header.
//
// Data is encoded in the native endianness of the host system.  For easier
// of encoding and decoding of integers, use package nlenc.
type Message struct {
	Header Header
	Data   []byte
}

// MarshalBinary marshals a Message into a byte slice.
func (m Message) MarshalBinary() ([]byte, error) {
	ml := nlmsgAlign(int(m.Header.Length))
	if ml < nlmsgHeaderLen || ml != int(m.Header.Length) {
		return nil, errIncorrectMessageLength
	}

	b := make([]byte, ml)

	nlenc.PutUint32(b[0:4], m.Header.Length)
	nlenc.PutUint16(b[4:6], uint16(m.Header.Type))
	nlenc.PutUint16(b[6:8], uint16(m.Header.Flags))
	nlenc.PutUint32(b[8:12], m.Header.Sequence)
	nlenc.PutUint32(b[12:16], m.Header.PID)
	copy(b[16:], m.Data)

	return b, nil
}

// UnmarshalBinary unmarshals the contents of a byte slice into a Message.
func (m *Message) UnmarshalBinary(b []byte) error {
	if len(b) < nlmsgHeaderLen {
		return errShortMessage
	}
	if len(b) != nlmsgAlign(len(b)) {
		return errUnalignedMessage
	}

	// Don't allow misleading length
	m.Header.Length = nlenc.Uint32(b[0:4])
	if int(m.Header.Length) != len(b) {
		return errShortMessage
	}

	m.Header.Type = HeaderType(nlenc.Uint16(b[4:6]))
	m.Header.Flags = HeaderFlags(nlenc.Uint16(b[6:8]))
	m.Header.Sequence = nlenc.Uint32(b[8:12])
	m.Header.PID = nlenc.Uint32(b[12:16])
	m.Data = b[16:]

	return nil
}

// checkMessage checks a single Message for netlink errors.
func checkMessage(m Message) error {
	const success = 0

	// Per libnl documentation, only messages that indicate type error can
	// contain error codes:
	// https://www.infradead.org/~tgr/libnl/doc/core.html#core_errmsg.
	//
	// However, at one point, this package checked both done and error for
	// error codes.  Because there was no issue associated with the change,
	// it is unknown whether this change was correct or not.  If you run into
	// a problem with your application because of this change, please file
	// an issue.
	if m.Header.Type != Error {
		return nil
	}

	if len(m.Data) < 4 {
		return errShortErrorMessage
	}

	if c := nlenc.Int32(m.Data[0:4]); c != success {
		// Error code is a negative integer, convert it into
		// an OS-specific system call error
		return newError(-1 * int(c))
	}

	return nil
}
<commit_msg>netlink: reflow comments to use 80 character width<commit_after>package netlink

import (
	"errors"
	"fmt"

	"github.com/mdlayher/netlink/nlenc"
)

// Various errors which may occur when attempting to marshal or unmarshal
// a Message to and from its binary form.
var (
	errIncorrectMessageLength = errors.New("netlink message header length incorrect")
	errShortMessage           = errors.New("not enough data to create a netlink message")
	errUnalignedMessage       = errors.New("input data is not properly aligned for netlink message")
)

// HeaderFlags specify flags which may be present in a Header.
type HeaderFlags uint16

const (
	// General netlink communication flags.

	// Request indicates a request to netlink.
	Request HeaderFlags = 1

	// Multi indicates a multi-part message, terminated by Done on the
	// last message.
	Multi HeaderFlags = 2

	// Acknowledge requests that netlink reply with an acknowledgement
	// using Error and, if needed, an error code.
	Acknowledge HeaderFlags = 4

	// Echo requests that netlink echo this request back to the sender.
	Echo HeaderFlags = 8

	// DumpInterrupted indicates that a dump was inconsistent due to a
	// sequence change.
	DumpInterrupted HeaderFlags = 16

	// DumpFiltered indicates that a dump was filtered as requested.
	DumpFiltered HeaderFlags = 32

	// Flags used to retrieve data from netlink.

	// Root requests that netlink return a complete table instead of a
	// single entry.
	Root HeaderFlags = 0x100

	// Match requests that netlink return a list of all matching entries.
	Match HeaderFlags = 0x200

	// Atomic requests that netlink send an atomic snapshot of its entries.
	// Requires CAP_NET_ADMIN or an effective UID of 0.
	Atomic HeaderFlags = 0x400

	// Dump requests that netlink return a complete list of all entries.
	Dump HeaderFlags = Root | Match

	// Flags used to create objects.

	// Replace indicates request replaces an existing matching object.
	Replace HeaderFlags = 0x100

	// Excl indicates request does not replace the object if it already exists.
	Excl HeaderFlags = 0x200

	// Create indicates request creates an object if it doesn't already exist.
	Create HeaderFlags = 0x400

	// Append indicates request adds to the end of the object list.
	Append HeaderFlags = 0x800
)

// String returns the string representation of a HeaderFlags.
func (f HeaderFlags) String() string {
	names := []string{
		"request",
		"multi",
		"acknowledge",
		"echo",
		"dumpinterrupted",
		"dumpfiltered",
	}

	var s string

	left := uint(f)

	for i, name := range names {
		if f&(1<<uint(i)) != 0 {
			if s != "" {
				s += "|"
			}

			s += name

			left ^= (1 << uint(i))
		}
	}

	if s == "" && left == 0 {
		s = "0"
	}

	if left > 0 {
		if s != "" {
			s += "|"
		}
		s += fmt.Sprintf("%#x", left)
	}

	return s
}

// HeaderType specifies the type of a Header.
type HeaderType uint16

const (
	// Noop indicates that no action was taken.
	Noop HeaderType = 0x1

	// Error indicates an error code is present, which is also used to indicate
	// success when the code is 0.
	Error HeaderType = 0x2

	// Done indicates the end of a multi-part message.
	Done HeaderType = 0x3

	// Overrun indicates that data was lost from this message.
	Overrun HeaderType = 0x4
)

// String returns the string representation of a HeaderType.
func (t HeaderType) String() string {
	switch t {
	case Noop:
		return "noop"
	case Error:
		return "error"
	case Done:
		return "done"
	case Overrun:
		return "overrun"
	default:
		return fmt.Sprintf("unknown(%d)", t)
	}
}

// NB: the memory layout of Header and Linux's syscall.NlMsgHdr must be
// exactly the same.  Cannot reorder, change data type, add, or remove fields.
// Named types of the same size (e.g. HeaderFlags is a uint16) are okay.

// A Header is a netlink header.  A Header is sent and received with each
// Message to indicate metadata regarding a Message.
type Header struct {
	// Length of a Message, including this Header.
	Length uint32

	// Contents of a Message.
	Type HeaderType

	// Flags which may be used to modify a request or response.
	Flags HeaderFlags

	// The sequence number of a Message.
	Sequence uint32

	// The process ID of the sending process.
	PID uint32
}

// A Message is a netlink message.  It contains a Header and an arbitrary
// byte payload, which may be decoded using information from the Header.
//
// Data is encoded in the native endianness of the host system.  For easier
// of encoding and decoding of integers, use package nlenc.
type Message struct {
	Header Header
	Data   []byte
}

// MarshalBinary marshals a Message into a byte slice.
func (m Message) MarshalBinary() ([]byte, error) {
	ml := nlmsgAlign(int(m.Header.Length))
	if ml < nlmsgHeaderLen || ml != int(m.Header.Length) {
		return nil, errIncorrectMessageLength
	}

	b := make([]byte, ml)

	nlenc.PutUint32(b[0:4], m.Header.Length)
	nlenc.PutUint16(b[4:6], uint16(m.Header.Type))
	nlenc.PutUint16(b[6:8], uint16(m.Header.Flags))
	nlenc.PutUint32(b[8:12], m.Header.Sequence)
	nlenc.PutUint32(b[12:16], m.Header.PID)
	copy(b[16:], m.Data)

	return b, nil
}

// UnmarshalBinary unmarshals the contents of a byte slice into a Message.
func (m *Message) UnmarshalBinary(b []byte) error {
	if len(b) < nlmsgHeaderLen {
		return errShortMessage
	}
	if len(b) != nlmsgAlign(len(b)) {
		return errUnalignedMessage
	}

	// Don't allow misleading length
	m.Header.Length = nlenc.Uint32(b[0:4])
	if int(m.Header.Length) != len(b) {
		return errShortMessage
	}

	m.Header.Type = HeaderType(nlenc.Uint16(b[4:6]))
	m.Header.Flags = HeaderFlags(nlenc.Uint16(b[6:8]))
	m.Header.Sequence = nlenc.Uint32(b[8:12])
	m.Header.PID = nlenc.Uint32(b[12:16])
	m.Data = b[16:]

	return nil
}

// checkMessage checks a single Message for netlink errors.
func checkMessage(m Message) error {
	const success = 0

	// Per libnl documentation, only messages that indicate type error can
	// contain error codes:
	// https://www.infradead.org/~tgr/libnl/doc/core.html#core_errmsg.
	//
	// However, at one point, this package checked both done and error for
	// error codes.  Because there was no issue associated with the change,
	// it is unknown whether this change was correct or not.  If you run into
	// a problem with your application because of this change, please file
	// an issue.
	if m.Header.Type != Error {
		return nil
	}

	if len(m.Data) < 4 {
		return errShortErrorMessage
	}

	if c := nlenc.Int32(m.Data[0:4]); c != success {
		// Error code is a negative integer, convert it into
		// an OS-specific system call error
		return newError(-1 * int(c))
	}

	return nil
}
<|endoftext|>


<commit_before>package campfire

import (
    "fmt"
)

type Message struct {
    conn *Connection

    Id        int    `json:"id,omitempty"`
    Type      string `json:"type"`
    UserId    int    `json:"user_id,omitempty"`
    RoomId    int    `json:"room_id,omitempty"`
    Body      string `json:"body"`
    Starred   bool   `json:"starred,omitempty"`
    CreatedAt string `json:"created_at,omitempty"`
}

type MessageResult struct {
    Message *Message `json:"message"`
}

// Star favorites a message
func (m *Message) Star() error {
    return m.conn.Post(fmt.Sprintf("/messages/%d/star", m.Id), nil)
}

// Unstar unfavorites a message
func (m *Message) Unstar() error {
    return m.conn.Delete(fmt.Sprintf("/messages/%d/unstar", m.Id))
}
<commit_msg>Add an accessor for the connection<commit_after>package campfire

import (
    "fmt"
)

type Message struct {
    conn *Connection

    Id        int    `json:"id,omitempty"`
    Type      string `json:"type"`
    UserId    int    `json:"user_id,omitempty"`
    RoomId    int    `json:"room_id,omitempty"`
    Body      string `json:"body"`
    Starred   bool   `json:"starred,omitempty"`
    CreatedAt string `json:"created_at,omitempty"`
}

type MessageResult struct {
    Message *Message `json:"message"`
}

// Star favorites a message
func (m *Message) Star() error {
    return m.conn.Post(fmt.Sprintf("/messages/%d/star", m.Id), nil)
}

// Unstar unfavorites a message
func (m *Message) Unstar() error {
    return m.conn.Delete(fmt.Sprintf("/messages/%d/unstar", m.Id))
}

// Connection returns the connection bound to this message
func (m *Message) Connection() *Connection {
    return m.conn
}
<|endoftext|>


<commit_before>package pcp

import "hash/fnv"

// MetricType is an enumerated type representing all valid types for a metric
type MetricType int32

// Possible values for a MetricType
const (
	NoSupportType       MetricType = iota
	Int32Type           MetricType = iota
	Uint32Type          MetricType = iota
	Int64Type           MetricType = iota
	Uint64Type          MetricType = iota
	FloatType           MetricType = iota
	DoubleType          MetricType = iota
	StringType          MetricType = iota
	AggregateType       MetricType = iota
	AggregateStaticType MetricType = iota
	EventType           MetricType = iota
	HighresEventType    MetricType = iota
	UnknownType         MetricType = iota
)

func (mt MetricType) String() string {
	switch mt {
	case NoSupportType:
		return "Type: No Support"
	case Int32Type:
		return "Type: Int32"
	case Int64Type:
		return "Type: Int64"
	case Uint32Type:
		return "Type: Uint32"
	case Uint64Type:
		return "Type: Uint64"
	case FloatType:
		return "Type: Float"
	case DoubleType:
		return "Type: Double"
	case StringType:
		return "Type: String"
	case AggregateType:
		return "Type: Aggregate"
	case AggregateStaticType:
		return "Type: Aggregate Static"
	case EventType:
		return "Type: Event"
	case HighresEventType:
		return "Type: Highres Event"
	case UnknownType:
		return "Type: Unknown"
	default:
		return "Type: Invalid"
	}
}

// MetricUnit is an enumerated type representing all possible values for a valid PCP unit
type MetricUnit int32

// SpaceUnit is an enumerated type representing all units for space
type SpaceUnit MetricUnit

// Possible values for SpaceUnit
const (
	ByteUnit     SpaceUnit = iota
	KilobyteUnit SpaceUnit = iota
	MegabyteUnit SpaceUnit = iota
	GigabyteUnit SpaceUnit = iota
	TerabyteUnit SpaceUnit = iota
	PetabyteUnit SpaceUnit = iota
	ExabyteUnit  SpaceUnit = iota
)

func (su SpaceUnit) String() string {
	switch su {
	case ByteUnit:
		return "Unit: Byte"
	case KilobyteUnit:
		return "Unit: Kilobyte"
	case MegabyteUnit:
		return "Unit: Megabyte"
	case GigabyteUnit:
		return "Unit: Gigabyte"
	case TerabyteUnit:
		return "Unit: Terabyte"
	case PetabyteUnit:
		return "Unit: Petabyte"
	case ExabyteUnit:
		return "Unit: Exabyte"
	default:
		return "Unit: Invalid SpaceUnit"
	}
}

// TimeUnit is an enumerated type representing all possible units for representing time
type TimeUnit MetricUnit

// Possible Values for TimeUnit
const (
	NanosecondUnit  TimeUnit = iota
	MicrosecondUnit TimeUnit = iota
	MillisecondUnit TimeUnit = iota
	SecondUnit      TimeUnit = iota
	MinuteUnit      TimeUnit = iota
	HourUnit        TimeUnit = iota
)

func (tu TimeUnit) String() string {
	switch tu {
	case NanosecondUnit:
		return "Unit: Nanosecond"
	case MicrosecondUnit:
		return "Unit: Microsecond"
	case MillisecondUnit:
		return "Unit: Millisecond"
	case SecondUnit:
		return "Unit: Second"
	case MinuteUnit:
		return "Unit: Minute"
	case HourUnit:
		return "Unit: Hour"
	default:
		return "Unit: Invalid TimeUnit"
	}
}

// CountUnit is a type representing a counted quantity
type CountUnit MetricUnit

// OneUnit represents the only CountUnit
const OneUnit CountUnit = iota

func (cu CountUnit) String() string {
	switch cu {
	case OneUnit:
		return "Unit: One"
	default:
		return "Unit: Invalid CounterUnit"
	}
}

// MetricSemantics represents an enumerated type representing the possible
// values for the semantics of a metric
type MetricSemantics int32

// Possible values for MetricSemantics
const (
	NoSemantics       MetricSemantics = iota
	CounterSemantics  MetricSemantics = iota
	InstantSemantics  MetricSemantics = iota
	DiscreteSemantics MetricSemantics = iota
)

func (ms MetricSemantics) String() string {
	switch ms {
	case NoSemantics:
		return "Semantics: None"
	case CounterSemantics:
		return "Semantics: Counter"
	case InstantSemantics:
		return "Semantics: Instant"
	case DiscreteSemantics:
		return "Semantics: Discrete"
	default:
		return "Semantics: Invalid"
	}
}

// Metric defines the general interface a type needs to implement to qualify
// as a valid PCP metric
type Metric interface {
	Val() interface{}           // gets the value of the metric
	Set(interface{}) error      // sets the value of the metric to a value, optionally returns an error on failure
	Type() MetricType           // gets the type of a metric
	Unit() MetricUnit           // gets the unit of a metric
	Semantics() MetricSemantics // gets the semantics for a metric
	Description() string        // gets the description of a metric
}

type InstanceDomain struct {
	id                          uint32
	name                        string
	shortHelpText, longHelpText string
}

// NOTE: this declaration alone doesn't make this usable
// it needs to be 'made' at the beginning of monitoring
var instanceDomainCache map[uint32]*InstanceDomain

func getHash(s string) uint32 {
	h := fnv.New32a()
	h.Write([]byte(s))
	return h.Sum32()
}

// NOTE: this is different from parfait's idea of generating ids for InstanceDomains
// We simply generate a unique 32 bit hash for an instance domain name, and if it has not
// already been created, we create it, otherwise we return the already created version
func NewInstanceDomain(name string) *InstanceDomain {
	h := getHash(name)

	v, present := instanceDomainCache[h]
	if present {
		return v
	}

	instanceDomainCache[h] = &InstanceDomain{
		id:   h,
		name: name,
	}

	return instanceDomainCache[h]
}
<commit_msg>metrics: add instance definition<commit_after>package pcp

import (
	"errors"
	"hash/fnv"
)

// MetricType is an enumerated type representing all valid types for a metric
type MetricType int32

// Possible values for a MetricType
const (
	NoSupportType       MetricType = iota
	Int32Type           MetricType = iota
	Uint32Type          MetricType = iota
	Int64Type           MetricType = iota
	Uint64Type          MetricType = iota
	FloatType           MetricType = iota
	DoubleType          MetricType = iota
	StringType          MetricType = iota
	AggregateType       MetricType = iota
	AggregateStaticType MetricType = iota
	EventType           MetricType = iota
	HighresEventType    MetricType = iota
	UnknownType         MetricType = iota
)

func (mt MetricType) String() string {
	switch mt {
	case NoSupportType:
		return "Type: No Support"
	case Int32Type:
		return "Type: Int32"
	case Int64Type:
		return "Type: Int64"
	case Uint32Type:
		return "Type: Uint32"
	case Uint64Type:
		return "Type: Uint64"
	case FloatType:
		return "Type: Float"
	case DoubleType:
		return "Type: Double"
	case StringType:
		return "Type: String"
	case AggregateType:
		return "Type: Aggregate"
	case AggregateStaticType:
		return "Type: Aggregate Static"
	case EventType:
		return "Type: Event"
	case HighresEventType:
		return "Type: Highres Event"
	case UnknownType:
		return "Type: Unknown"
	default:
		return "Type: Invalid"
	}
}

// MetricUnit is an enumerated type representing all possible values for a valid PCP unit
type MetricUnit int32

// SpaceUnit is an enumerated type representing all units for space
type SpaceUnit MetricUnit

// Possible values for SpaceUnit
const (
	ByteUnit     SpaceUnit = iota
	KilobyteUnit SpaceUnit = iota
	MegabyteUnit SpaceUnit = iota
	GigabyteUnit SpaceUnit = iota
	TerabyteUnit SpaceUnit = iota
	PetabyteUnit SpaceUnit = iota
	ExabyteUnit  SpaceUnit = iota
)

func (su SpaceUnit) String() string {
	switch su {
	case ByteUnit:
		return "Unit: Byte"
	case KilobyteUnit:
		return "Unit: Kilobyte"
	case MegabyteUnit:
		return "Unit: Megabyte"
	case GigabyteUnit:
		return "Unit: Gigabyte"
	case TerabyteUnit:
		return "Unit: Terabyte"
	case PetabyteUnit:
		return "Unit: Petabyte"
	case ExabyteUnit:
		return "Unit: Exabyte"
	default:
		return "Unit: Invalid SpaceUnit"
	}
}

// TimeUnit is an enumerated type representing all possible units for representing time
type TimeUnit MetricUnit

// Possible Values for TimeUnit
const (
	NanosecondUnit  TimeUnit = iota
	MicrosecondUnit TimeUnit = iota
	MillisecondUnit TimeUnit = iota
	SecondUnit      TimeUnit = iota
	MinuteUnit      TimeUnit = iota
	HourUnit        TimeUnit = iota
)

func (tu TimeUnit) String() string {
	switch tu {
	case NanosecondUnit:
		return "Unit: Nanosecond"
	case MicrosecondUnit:
		return "Unit: Microsecond"
	case MillisecondUnit:
		return "Unit: Millisecond"
	case SecondUnit:
		return "Unit: Second"
	case MinuteUnit:
		return "Unit: Minute"
	case HourUnit:
		return "Unit: Hour"
	default:
		return "Unit: Invalid TimeUnit"
	}
}

// CountUnit is a type representing a counted quantity
type CountUnit MetricUnit

// OneUnit represents the only CountUnit
const OneUnit CountUnit = iota

func (cu CountUnit) String() string {
	switch cu {
	case OneUnit:
		return "Unit: One"
	default:
		return "Unit: Invalid CounterUnit"
	}
}

// MetricSemantics represents an enumerated type representing the possible
// values for the semantics of a metric
type MetricSemantics int32

// Possible values for MetricSemantics
const (
	NoSemantics       MetricSemantics = iota
	CounterSemantics  MetricSemantics = iota
	InstantSemantics  MetricSemantics = iota
	DiscreteSemantics MetricSemantics = iota
)

func (ms MetricSemantics) String() string {
	switch ms {
	case NoSemantics:
		return "Semantics: None"
	case CounterSemantics:
		return "Semantics: Counter"
	case InstantSemantics:
		return "Semantics: Instant"
	case DiscreteSemantics:
		return "Semantics: Discrete"
	default:
		return "Semantics: Invalid"
	}
}

// Metric defines the general interface a type needs to implement to qualify
// as a valid PCP metric
type Metric interface {
	Val() interface{}           // gets the value of the metric
	Set(interface{}) error      // sets the value of the metric to a value, optionally returns an error on failure
	Type() MetricType           // gets the type of a metric
	Unit() MetricUnit           // gets the unit of a metric
	Semantics() MetricSemantics // gets the semantics for a metric
	Description() string        // gets the description of a metric
}

// generate a unique uint32 hash for a string
// NOTE: make sure this is as fast as possible
func getHash(s string) uint32 {
	h := fnv.New32a()
	h.Write([]byte(s))
	return h.Sum32()
}

// Instance wraps a PCP compatible Instance
type Instance struct {
	name  string
	id    uint32
	indom *InstanceDomain
}

// NewInstance generates a new Instance type based on the passed parameters
// the id is passed explicitly as it is assumed that this will be constructed
// after initializing the InstanceDomain
// this is not a part of the public API as this is not supposed to be used directly,
// but instead added using the AddInstance method of InstanceDomain
func newInstance(id uint32, name string, indom *InstanceDomain) *Instance {
	return &Instance{
		name, id, indom,
	}
}

// InstanceDomain wraps a PCP compatible instance domain
type InstanceDomain struct {
	id                          uint32
	name                        string
	instanceCache               map[uint32]*Instance // the instances for this InstanceDomain stored as a map
	shortHelpText, longHelpText string
}

// NOTE: this declaration alone doesn't make this usable
// it needs to be 'made' at the beginning of monitoring
var instanceDomainCache map[uint32]*InstanceDomain

// NOTE: this is different from parfait's idea of generating ids for InstanceDomains
// We simply generate a unique 32 bit hash for an instance domain name, and if it has not
// already been created, we create it, otherwise we return the already created version
func NewInstanceDomain(name string) *InstanceDomain {
	h := getHash(name)

	v, present := instanceDomainCache[h]
	if present {
		return v
	}

	instanceDomainCache[h] = &InstanceDomain{
		id:   h,
		name: name,
	}

	return instanceDomainCache[h]
}

// AddInstance adds a new instance to the current InstanceDomain
func (indom *InstanceDomain) AddInstance(name string) error {
	h := getHash(name)

	_, present := indom.instanceCache[h]
	if present {
		return errors.New("Instance with same name already created for the InstanceDomain")
	}

	ins := newInstance(h, name, indom)
	indom.instanceCache[h] = ins

	return nil
}
<|endoftext|>


<commit_before>package migrate

import (
	"bytes"
	"database/sql"
	"flag"
	"fmt"
	"io"
	"os"
	"path"
	"sort"
	"strings"
	"time"

	"github.com/coopernurse/gorp"
	"github.com/rubenv/sql-migrate/sqlparse"
)

type MigrationDirection int

const (
	Up MigrationDirection = iota
	Down
)

var tableName string

func init() {
	flag.StringVar(&tableName, "table", "gorp_migrations", "Migration table name. Defaults to gorp_migrations.")
	flag.Parse()
}

type Migration struct {
	Id   string
	Up   []string
	Down []string
}

type PlannedMigration struct {
	*Migration
	Queries []string
}

type byId []*Migration

func (b byId) Len() int           { return len(b) }
func (b byId) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b byId) Less(i, j int) bool { return b[i].Id < b[j].Id }

type MigrationRecord struct {
	Id        string    `db:"id"`
	AppliedAt time.Time `db:"applied_at"`
}

var MigrationDialects = map[string]gorp.Dialect{
	"sqlite3":  gorp.SqliteDialect{},
	"postgres": gorp.PostgresDialect{},
	"mysql":    gorp.MySQLDialect{"InnoDB", "UTF8"},
	"mssql":    gorp.SqlServerDialect{},
	"oci8":     gorp.OracleDialect{},
}

type MigrationSource interface {
	// Finds the migrations.
	//
	// The resulting slice of migrations should be sorted by Id.
	FindMigrations() ([]*Migration, error)
}

// A hardcoded set of migrations, in-memory.
type MemoryMigrationSource struct {
	Migrations []*Migration
}

var _ MigrationSource = (*MemoryMigrationSource)(nil)

func (m MemoryMigrationSource) FindMigrations() ([]*Migration, error) {
	// Make sure migrations are sorted
	sort.Sort(byId(m.Migrations))

	return m.Migrations, nil
}

// A set of migrations loaded from a directory.
type FileMigrationSource struct {
	Dir string
}

var _ MigrationSource = (*FileMigrationSource)(nil)

func (f FileMigrationSource) FindMigrations() ([]*Migration, error) {
	migrations := make([]*Migration, 0)

	file, err := os.Open(f.Dir)
	if err != nil {
		return nil, err
	}

	files, err := file.Readdir(0)
	if err != nil {
		return nil, err
	}

	for _, info := range files {
		if strings.HasSuffix(info.Name(), ".sql") {
			file, err := os.Open(path.Join(f.Dir, info.Name()))
			if err != nil {
				return nil, err
			}

			migration, err := ParseMigration(info.Name(), file)
			if err != nil {
				return nil, err
			}

			migrations = append(migrations, migration)
		}
	}

	// Make sure migrations are sorted
	sort.Sort(byId(migrations))

	return migrations, nil
}

// Migrations from a bindata asset set.
type AssetMigrationSource struct {
	// Asset should return content of file in path if exists
	Asset func(path string) ([]byte, error)

	// AssetDir should return list of files in the path
	AssetDir func(path string) ([]string, error)

	// Path in the bindata to use.
	Dir string
}

var _ MigrationSource = (*AssetMigrationSource)(nil)

func (a AssetMigrationSource) FindMigrations() ([]*Migration, error) {
	migrations := make([]*Migration, 0)

	files, err := a.AssetDir(a.Dir)
	if err != nil {
		return nil, err
	}

	for _, name := range files {
		if strings.HasSuffix(name, ".sql") {
			file, err := a.Asset(path.Join(a.Dir, name))
			if err != nil {
				return nil, err
			}

			migration, err := ParseMigration(name, bytes.NewReader(file))
			if err != nil {
				return nil, err
			}

			migrations = append(migrations, migration)
		}
	}

	// Make sure migrations are sorted
	sort.Sort(byId(migrations))

	return migrations, nil
}

// Migration parsing
func ParseMigration(id string, r io.ReadSeeker) (*Migration, error) {
	m := &Migration{
		Id: id,
	}

	up, err := sqlparse.SplitSQLStatements(r, true)
	if err != nil {
		return nil, err
	}

	down, err := sqlparse.SplitSQLStatements(r, false)
	if err != nil {
		return nil, err
	}

	m.Up = up
	m.Down = down

	return m, nil
}

// Execute a set of migrations
//
// Returns the number of applied migrations.
func Exec(db *sql.DB, dialect string, m MigrationSource, dir MigrationDirection) (int, error) {
	return ExecMax(db, dialect, m, dir, 0)
}

// Execute a set of migrations
//
// Will apply at most `max` migrations. Pass 0 for no limit (or use Exec).
//
// Returns the number of applied migrations.
func ExecMax(db *sql.DB, dialect string, m MigrationSource, dir MigrationDirection, max int) (int, error) {
	migrations, dbMap, err := PlanMigration(db, dialect, m, dir, max)
	if err != nil {
		return 0, err
	}

	// Apply migrations
	applied := 0
	for _, migration := range migrations {
		trans, err := dbMap.Begin()
		if err != nil {
			return applied, err
		}

		for _, stmt := range migration.Queries {
			_, err := trans.Exec(stmt)
			if err != nil {
				trans.Rollback()
				return applied, err
			}
		}

		if dir == Up {
			err = trans.Insert(&MigrationRecord{
				Id:        migration.Id,
				AppliedAt: time.Now(),
			})
			if err != nil {
				return applied, err
			}
		} else if dir == Down {
			_, err := trans.Delete(&MigrationRecord{
				Id: migration.Id,
			})
			if err != nil {
				return applied, err
			}
		} else {
			panic("Not possible")
		}

		err = trans.Commit()
		if err != nil {
			return applied, err
		}

		applied++
	}

	return applied, nil
}

// Plan a migration.
func PlanMigration(db *sql.DB, dialect string, m MigrationSource, dir MigrationDirection, max int) ([]*PlannedMigration, *gorp.DbMap, error) {
	dbMap, err := getMigrationDbMap(db, dialect)
	if err != nil {
		return nil, nil, err
	}

	// Make sure we have the migrations table
	err = dbMap.CreateTablesIfNotExists()
	if err != nil {
		return nil, nil, err
	}

	migrations, err := m.FindMigrations()
	if err != nil {
		return nil, nil, err
	}

	// Find the newest applied migration
	var record MigrationRecord
	query := fmt.Sprintf("SELECT * FROM %s ORDER BY id DESC LIMIT 1", tableName)
	err = dbMap.SelectOne(&record, query)
	if err != nil && err != sql.ErrNoRows {
		return nil, nil, err
	}

	// Figure out which of the supplied migrations has been applied.
	toApply := ToApply(migrations, record.Id, dir)
	toApplyCount := len(toApply)
	if max > 0 && max < toApplyCount {
		toApplyCount = max
	}

	result := make([]*PlannedMigration, toApplyCount)
	for k, v := range toApply[0:toApplyCount] {
		result[k] = &PlannedMigration{
			Migration: v,
		}

		if dir == Up {
			result[k].Queries = v.Up
		} else if dir == Down {
			result[k].Queries = v.Down
		}
	}

	return result, dbMap, nil
}

// Filter a slice of migrations into ones that should be applied.
func ToApply(migrations []*Migration, current string, direction MigrationDirection) []*Migration {
	var index = -1
	for index < len(migrations)-1 && migrations[index+1].Id <= current {
		index++
	}

	if direction == Up {
		return migrations[index+1:]
	} else if direction == Down {
		if index == -1 {
			return []*Migration{}
		}

		// Add in reverse order
		toApply := make([]*Migration, index+1)
		for i := 0; i < index+1; i++ {
			toApply[index-i] = migrations[i]
		}
		return toApply
	}

	panic("Not possible")
}

func GetMigrationRecords(db *sql.DB, dialect string) ([]*MigrationRecord, error) {
	dbMap, err := getMigrationDbMap(db, dialect)
	if err != nil {
		return nil, err
	}

	var records []*MigrationRecord
	query := fmt.Sprintf("SELECT * FROM %s ORDER BY id ASC", tableName)
	_, err = dbMap.Select(&records, query)
	if err != nil {
		return nil, err
	}

	return records, nil
}

func getMigrationDbMap(db *sql.DB, dialect string) (*gorp.DbMap, error) {
	d, ok := MigrationDialects[dialect]
	if !ok {
		return nil, fmt.Errorf("Unknown dialect: %s", dialect)
	}

	dbMap := &gorp.DbMap{Db: db, Dialect: d}
	dbMap.AddTableWithName(MigrationRecord{}, tableName).SetKeys(false, "Id")
	//dbMap.TraceOn("", log.New(os.Stdout, "migrate: ", log.Lmicroseconds))
	return dbMap, nil
}

// TODO: Run migration + record insert in transaction.
<commit_msg>Revert "Adds command-line flag for migration tablename."<commit_after>package migrate

import (
	"bytes"
	"database/sql"
	"fmt"
	"io"
	"os"
	"path"
	"sort"
	"strings"
	"time"

	"github.com/coopernurse/gorp"
	"github.com/rubenv/sql-migrate/sqlparse"
)

type MigrationDirection int

const (
	Up MigrationDirection = iota
	Down
)

const tableName = "gorp_migrations"

type Migration struct {
	Id   string
	Up   []string
	Down []string
}

type PlannedMigration struct {
	*Migration
	Queries []string
}

type byId []*Migration

func (b byId) Len() int           { return len(b) }
func (b byId) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b byId) Less(i, j int) bool { return b[i].Id < b[j].Id }

type MigrationRecord struct {
	Id        string    `db:"id"`
	AppliedAt time.Time `db:"applied_at"`
}

var MigrationDialects = map[string]gorp.Dialect{
	"sqlite3":  gorp.SqliteDialect{},
	"postgres": gorp.PostgresDialect{},
	"mysql":    gorp.MySQLDialect{"InnoDB", "UTF8"},
	"mssql":    gorp.SqlServerDialect{},
	"oci8":     gorp.OracleDialect{},
}

type MigrationSource interface {
	// Finds the migrations.
	//
	// The resulting slice of migrations should be sorted by Id.
	FindMigrations() ([]*Migration, error)
}

// A hardcoded set of migrations, in-memory.
type MemoryMigrationSource struct {
	Migrations []*Migration
}

var _ MigrationSource = (*MemoryMigrationSource)(nil)

func (m MemoryMigrationSource) FindMigrations() ([]*Migration, error) {
	// Make sure migrations are sorted
	sort.Sort(byId(m.Migrations))

	return m.Migrations, nil
}

// A set of migrations loaded from a directory.
type FileMigrationSource struct {
	Dir string
}

var _ MigrationSource = (*FileMigrationSource)(nil)

func (f FileMigrationSource) FindMigrations() ([]*Migration, error) {
	migrations := make([]*Migration, 0)

	file, err := os.Open(f.Dir)
	if err != nil {
		return nil, err
	}

	files, err := file.Readdir(0)
	if err != nil {
		return nil, err
	}

	for _, info := range files {
		if strings.HasSuffix(info.Name(), ".sql") {
			file, err := os.Open(path.Join(f.Dir, info.Name()))
			if err != nil {
				return nil, err
			}

			migration, err := ParseMigration(info.Name(), file)
			if err != nil {
				return nil, err
			}

			migrations = append(migrations, migration)
		}
	}

	// Make sure migrations are sorted
	sort.Sort(byId(migrations))

	return migrations, nil
}

// Migrations from a bindata asset set.
type AssetMigrationSource struct {
	// Asset should return content of file in path if exists
	Asset func(path string) ([]byte, error)

	// AssetDir should return list of files in the path
	AssetDir func(path string) ([]string, error)

	// Path in the bindata to use.
	Dir string
}

var _ MigrationSource = (*AssetMigrationSource)(nil)

func (a AssetMigrationSource) FindMigrations() ([]*Migration, error) {
	migrations := make([]*Migration, 0)

	files, err := a.AssetDir(a.Dir)
	if err != nil {
		return nil, err
	}

	for _, name := range files {
		if strings.HasSuffix(name, ".sql") {
			file, err := a.Asset(path.Join(a.Dir, name))
			if err != nil {
				return nil, err
			}

			migration, err := ParseMigration(name, bytes.NewReader(file))
			if err != nil {
				return nil, err
			}

			migrations = append(migrations, migration)
		}
	}

	// Make sure migrations are sorted
	sort.Sort(byId(migrations))

	return migrations, nil
}

// Migration parsing
func ParseMigration(id string, r io.ReadSeeker) (*Migration, error) {
	m := &Migration{
		Id: id,
	}

	up, err := sqlparse.SplitSQLStatements(r, true)
	if err != nil {
		return nil, err
	}

	down, err := sqlparse.SplitSQLStatements(r, false)
	if err != nil {
		return nil, err
	}

	m.Up = up
	m.Down = down

	return m, nil
}

// Execute a set of migrations
//
// Returns the number of applied migrations.
func Exec(db *sql.DB, dialect string, m MigrationSource, dir MigrationDirection) (int, error) {
	return ExecMax(db, dialect, m, dir, 0)
}

// Execute a set of migrations
//
// Will apply at most `max` migrations. Pass 0 for no limit (or use Exec).
//
// Returns the number of applied migrations.
func ExecMax(db *sql.DB, dialect string, m MigrationSource, dir MigrationDirection, max int) (int, error) {
	migrations, dbMap, err := PlanMigration(db, dialect, m, dir, max)
	if err != nil {
		return 0, err
	}

	// Apply migrations
	applied := 0
	for _, migration := range migrations {
		trans, err := dbMap.Begin()
		if err != nil {
			return applied, err
		}

		for _, stmt := range migration.Queries {
			_, err := trans.Exec(stmt)
			if err != nil {
				trans.Rollback()
				return applied, err
			}
		}

		if dir == Up {
			err = trans.Insert(&MigrationRecord{
				Id:        migration.Id,
				AppliedAt: time.Now(),
			})
			if err != nil {
				return applied, err
			}
		} else if dir == Down {
			_, err := trans.Delete(&MigrationRecord{
				Id: migration.Id,
			})
			if err != nil {
				return applied, err
			}
		} else {
			panic("Not possible")
		}

		err = trans.Commit()
		if err != nil {
			return applied, err
		}

		applied++
	}

	return applied, nil
}

// Plan a migration.
func PlanMigration(db *sql.DB, dialect string, m MigrationSource, dir MigrationDirection, max int) ([]*PlannedMigration, *gorp.DbMap, error) {
	dbMap, err := getMigrationDbMap(db, dialect)
	if err != nil {
		return nil, nil, err
	}

	// Make sure we have the migrations table
	err = dbMap.CreateTablesIfNotExists()
	if err != nil {
		return nil, nil, err
	}

	migrations, err := m.FindMigrations()
	if err != nil {
		return nil, nil, err
	}

	// Find the newest applied migration
	var record MigrationRecord
	query := fmt.Sprintf("SELECT * FROM %s ORDER BY id DESC LIMIT 1", tableName)
	err = dbMap.SelectOne(&record, query)
	if err != nil && err != sql.ErrNoRows {
		return nil, nil, err
	}

	// Figure out which of the supplied migrations has been applied.
	toApply := ToApply(migrations, record.Id, dir)
	toApplyCount := len(toApply)
	if max > 0 && max < toApplyCount {
		toApplyCount = max
	}

	result := make([]*PlannedMigration, toApplyCount)
	for k, v := range toApply[0:toApplyCount] {
		result[k] = &PlannedMigration{
			Migration: v,
		}

		if dir == Up {
			result[k].Queries = v.Up
		} else if dir == Down {
			result[k].Queries = v.Down
		}
	}

	return result, dbMap, nil
}

// Filter a slice of migrations into ones that should be applied.
func ToApply(migrations []*Migration, current string, direction MigrationDirection) []*Migration {
	var index = -1
	for index < len(migrations)-1 && migrations[index+1].Id <= current {
		index++
	}

	if direction == Up {
		return migrations[index+1:]
	} else if direction == Down {
		if index == -1 {
			return []*Migration{}
		}

		// Add in reverse order
		toApply := make([]*Migration, index+1)
		for i := 0; i < index+1; i++ {
			toApply[index-i] = migrations[i]
		}
		return toApply
	}

	panic("Not possible")
}

func GetMigrationRecords(db *sql.DB, dialect string) ([]*MigrationRecord, error) {
	dbMap, err := getMigrationDbMap(db, dialect)
	if err != nil {
		return nil, err
	}

	var records []*MigrationRecord
	query := fmt.Sprintf("SELECT * FROM %s ORDER BY id ASC", tableName)
	_, err = dbMap.Select(&records, query)
	if err != nil {
		return nil, err
	}

	return records, nil
}

func getMigrationDbMap(db *sql.DB, dialect string) (*gorp.DbMap, error) {
	d, ok := MigrationDialects[dialect]
	if !ok {
		return nil, fmt.Errorf("Unknown dialect: %s", dialect)
	}

	dbMap := &gorp.DbMap{Db: db, Dialect: d}
	dbMap.AddTableWithName(MigrationRecord{}, tableName).SetKeys(false, "Id")
	//dbMap.TraceOn("", log.New(os.Stdout, "migrate: ", log.Lmicroseconds))
	return dbMap, nil
}

// TODO: Run migration + record insert in transaction.
<|endoftext|>