func AppendMessage(dst []byte, msg ttnpb.Message) ([]byte, error) {
	dst, err := AppendMHDR(dst, msg.MHDR)
	if err != nil {
		return nil, errFailedEncoding("MHDR").WithCause(err)
	}
	switch msg.MType {
	case ttnpb.MType_CONFIRMED_DOWN, ttnpb.MType_UNCONFIRMED_DOWN:
		pld := msg.GetMacPayload()
		if pld == nil {
			return nil, errMissing("MACPayload")
		}
		dst, err = AppendMACPayload(dst, *pld, false)
		if err != nil {
			return nil, errFailedEncoding("downlink MACPayload").WithCause(err)
		}
	case ttnpb.MType_CONFIRMED_UP, ttnpb.MType_UNCONFIRMED_UP:
		pld := msg.GetMacPayload()
		if pld == nil {
			return nil, errMissing("MACPayload")
		}
		dst, err = AppendMACPayload(dst, *pld, true)
		if err != nil {
			return nil, errFailedEncoding("uplink MACPayload").WithCause(err)
		}
	case ttnpb.MType_JOIN_REQUEST:
		pld := msg.GetJoinRequestPayload()
		if pld == nil {
			return nil, errMissing("JoinRequestPayload")
		}
		dst, err = AppendJoinRequestPayload(dst, *pld)
		if err != nil {
			return nil, errFailedEncoding("JoinRequestPayload").WithCause(err)
		}
	case ttnpb.MType_REJOIN_REQUEST:
		pld := msg.GetRejoinRequestPayload()
		if pld == nil {
			return nil, errMissing("RejoinRequestPayload")
		}
		dst, err = AppendRejoinRequestPayload(dst, *pld)
		if err != nil {
			return nil, errFailedEncoding("RejoinRequestPayload").WithCause(err)
		}
	case ttnpb.MType_JOIN_ACCEPT:
		pld := msg.GetJoinAcceptPayload()
		if pld == nil {
			return nil, errMissing("JoinAcceptPayload")
		}
		n := len(pld.Encrypted)
		if n != 16 && n != 32 {
			return nil, errEncryptedJoinAcceptPayloadLength(n)
		}
		dst = append(dst, pld.Encrypted...)
	default:
		return nil, errUnknown("MType")(msg.MType.String())
	}
	dst = append(dst, msg.Mic...)
	return dst, nil
}
AppendMessage appends encoded msg to dst.
package lorawan
import (
	"bytes"
	"math"
	"go.thethings.network/lorawan-stack/v3/pkg/errors"
	"go.thethings.network/lorawan-stack/v3/pkg/ttnpb"
	"go.thethings.network/lorawan-stack/v3/pkg/types"
	"go.thethings.network/lorawan-stack/v3/pkg/util/byteutil"
)
func AppendMHDR(dst []byte, msg ttnpb.MHDR) ([]byte, error) {
	if msg.MType > 7 {
		return nil, errExpectedLowerOrEqual("MType", 7)(msg.MType)
	}
	if msg.Major > 4 {
		return nil, errExpectedLowerOrEqual("Major", 4)(msg.Major)
	}
	return append(dst, byte(msg.MType)<<5|byte(msg.Major)), nil
}
func MarshalMHDR(msg ttnpb.MHDR) ([]byte, error) {
	return AppendMHDR(make([]byte, 0, 1), msg)
}
func UnmarshalMHDR(b []byte, msg *ttnpb.MHDR) error {
	if len(b) != 1 {
		return errExpectedLengthEncodedEqual("MHDR", 1)(len(b))
	}
	v := b[0]
	msg.MType = ttnpb.MType(v >> 5)
	msg.Major = ttnpb.Major(v & 3)
	return nil
}
func AppendFCtrl(dst []byte, msg ttnpb.FCtrl, isUplink bool, fOptsLen uint8) ([]byte, error) {
	if fOptsLen > 15 {
		return nil, errExpectedLowerOrEqual("FOptsLen", 15)(fOptsLen)
	}
	b := fOptsLen
	if msg.Adr {
		b |= 1 << 7
	}
	if msg.Ack {
		b |= 1 << 5
	}
	if isUplink {
		if msg.AdrAckReq {
			b |= 1 << 6
		}
		if msg.ClassB {
			b |= 1 << 4
		}
	} else {
		if msg.FPending {
			b |= 1 << 4
		}
	}
	return append(dst, b), nil
}
func UnmarshalFCtrl(b []byte, msg *ttnpb.FCtrl, isUplink bool) error {
	if len(b) != 1 {
		return errExpectedLengthEncodedEqual("FCtrl", 1)(len(b))
	}
	v := b[0]
	msg.Adr = v&(1<<7) > 0
	msg.Ack = v&(1<<5) > 0
	if isUplink {
		msg.AdrAckReq = v&(1<<6) > 0
		msg.ClassB = v&(1<<4) > 0
	} else {
		msg.FPending = v&(1<<4) > 0
	}
	return nil
}
func AppendFHDR(dst []byte, msg ttnpb.FHDR, isUplink bool) ([]byte, error) {
	dst = appendReverse(dst, msg.DevAddr[:]...)
	fOptsLen := uint8(len(msg.FOpts))
	if fOptsLen > 15 {
		return nil, errExpectedLowerOrEqual("FOptsLen", 15)(fOptsLen)
	}
	dst, err := AppendFCtrl(dst, msg.FCtrl, isUplink, fOptsLen)
	if err != nil {
		return nil, errFailedEncoding("FCtrl").WithCause(err)
	}
	dst = byteutil.AppendUint32(dst, msg.FCnt&0xffff, 2)
	dst = append(dst, msg.FOpts...)
	return dst, nil
}
func UnmarshalFHDR(b []byte, msg *ttnpb.FHDR, isUplink bool) error {
	n := len(b)
	if n < 7 || n > 23 {
		return errExpectedLengthEncodedBound("FHDR", 7, 23)(n)
	}
	copyReverse(msg.DevAddr[:], b[0:4])
	if err := UnmarshalFCtrl(b[4:5], &msg.FCtrl, isUplink); err != nil {
		return errFailedDecoding("FCtrl").WithCause(err)
	}
	msg.FCnt = byteutil.ParseUint32(b[5:7])
	if n == 7 {
		return nil
	}
	msg.FOpts = make([]byte, 0, n-7)
	msg.FOpts = append(msg.FOpts, b[7:n]...)
	return nil
}
func AppendMACPayload(dst []byte, msg ttnpb.MACPayload, isUplink bool) ([]byte, error) {
	dst, err := AppendFHDR(dst, msg.FHDR, isUplink)
	if err != nil {
		return nil, errFailedEncoding("FHDR").WithCause(err)
	}
	if msg.FPort > math.MaxUint8 {
		return nil, errExpectedLowerOrEqual("FPort", math.MaxUint8)(msg.FPort)
	}
	if len(msg.FrmPayload) > 0 || msg.FPort != 0 {
		dst = append(dst, byte(msg.FPort))
	}
	dst = append(dst, msg.FrmPayload...)
	return dst, nil
}
func MarshalMACPayload(msg ttnpb.MACPayload, isUplink bool) ([]byte, error) {
	return AppendMACPayload(make([]byte, 0, 1), msg, isUplink)
}
func UnmarshalMACPayload(b []byte, msg *ttnpb.MACPayload, isUplink bool) error {
	n := uint8(len(b))
	if n < 7 {
		return errExpectedLengthHigherOrEqual("FHDR", 7)(n)
	}
	fOptsLen := b[4] & 0xf
	fhdrLen := fOptsLen + 7
	if n < fhdrLen {
		return errExpectedLengthHigherOrEqual("MACPayload", fhdrLen)(n)
	}
	if err := UnmarshalFHDR(b[0:fhdrLen], &msg.FHDR, isUplink); err != nil {
		return errFailedDecoding("FHDR").WithCause(err)
	}
	fPortIdx := fhdrLen
	if n > fPortIdx {
		msg.FPort = uint32(b[fPortIdx])
		frmPayloadIdx := fPortIdx + 1
		if n > frmPayloadIdx {
			msg.FrmPayload = make([]byte, 0, n-frmPayloadIdx)
			msg.FrmPayload = append(msg.FrmPayload, b[frmPayloadIdx:]...)
		}
	}
	return nil
}
func AppendDLSettings(dst []byte, msg ttnpb.DLSettings) ([]byte, error) {
	if msg.Rx1DrOffset > 7 {
		return nil, errExpectedLowerOrEqual("Rx1DROffset", 7)(msg.Rx1DrOffset)
	}
	if msg.Rx2Dr > 15 {
		return nil, errExpectedLowerOrEqual("Rx2DR", 15)(msg.Rx2Dr)
	}
	b := byte(msg.Rx2Dr)
	b |= byte(msg.Rx1DrOffset << 4)
	if msg.OptNeg {
		b |= (1 << 7)
	}
	return append(dst, b), nil
}
func MarshalDLSettings(msg ttnpb.DLSettings) ([]byte, error) {
	return AppendDLSettings(make([]byte, 0, 1), msg)
}
func UnmarshalDLSettings(b []byte, msg *ttnpb.DLSettings) error {
	if len(b) != 1 {
		return errExpectedLengthEncodedEqual("DLSettings", 1)(len(b))
	}
	v := uint32(b[0])
	msg.OptNeg = (v >> 7) != 0
	msg.Rx1DrOffset = ttnpb.DataRateOffset((v >> 4) & 0x7)
	msg.Rx2Dr = ttnpb.DataRateIndex(v & 0xf)
	return nil
}
var errMaxCFListFrequency = unexpectedValue(
	errors.DefineInvalidArgument("max_cflist_frequency", "expected CFList frequency to be less or equal to 0xFFFFFF", valueKey),
)
func AppendCFList(dst []byte, msg ttnpb.CFList) ([]byte, error) {
	switch msg.Type {
	case 0:
		n := len(msg.Freq)
		if n > 5 {
			return nil, errExpectedLengthLowerOrEqual("CFListFreq", 5)(n)
		}
		for _, freq := range msg.Freq {
			if freq > byteutil.MaxUint24 {
				return nil, errMaxCFListFrequency(freq)
			}
			dst = byteutil.AppendUint32(dst, freq, 3)
		}
		dst = append(dst, bytes.Repeat([]byte{0x0}, 15-n*3)...)
	case 1:
		n := uint(len(msg.ChMasks))
		if n > 96 {
			return nil, errExpectedLengthLowerOrEqual("CFListChMasks", 96)(n)
		}
		for i := uint(0); i < n; i += 8 {
			var b byte
			for j := uint(0); j < 8 && i+j < n; j++ {
				if msg.ChMasks[i+j] {
					b |= (1 << j)
				}
			}
			dst = append(dst, b)
		}
		dst = append(dst, bytes.Repeat([]byte{0x0}, int(15-(n+7)/8))...)
	}
	dst = append(dst, byte(msg.Type))
	return dst, nil
}
func MarshalCFList(msg ttnpb.CFList) ([]byte, error) {
	return AppendCFList(make([]byte, 0, 16), msg)
}
func UnmarshalCFList(b []byte, msg *ttnpb.CFList) error {
	n := len(b)
	if n != 16 {
		return errExpectedLengthEncodedEqual("CFList", 16)(n)
	}
	msg.Type = ttnpb.CFListType(b[15])
	switch msg.Type {
	case 0:
		msg.Freq = make([]uint32, 0, 5)
		for i := 0; i < 15; i += 3 {
			freq := byteutil.ParseUint32(b[i : i+3])
			if freq != 0 {
				msg.Freq = append(msg.Freq, freq)
			}
		}
	case 1:
		msg.ChMasks = make([]bool, 0, 96)
		for _, m := range b[:12] {
			msg.ChMasks = append(msg.ChMasks,
				m&1 > 0,
				m&(1<<1) > 0,
				m&(1<<2) > 0,
				m&(1<<3) > 0,
				m&(1<<4) > 0,
				m&(1<<5) > 0,
				m&(1<<6) > 0,
				m&(1<<7) > 0,
			)
		}
	default:
		return errUnknown("CFListType")(msg.Type.String())
	}
	return nil
}
func AppendJoinAcceptPayload(dst []byte, msg ttnpb.JoinAcceptPayload) ([]byte, error) {
	dst = appendReverse(dst, msg.JoinNonce[:]...)
	dst = appendReverse(dst, msg.NetId[:]...)
	dst = appendReverse(dst, msg.DevAddr[:]...)
	dst, err := AppendDLSettings(dst, msg.DLSettings)
	if err != nil {
		return nil, errFailedEncoding("DLSettings").WithCause(err)
	}
	if msg.RxDelay > math.MaxUint8 {
		return nil, errExpectedLowerOrEqual("RxDelay", math.MaxUint8)(msg.RxDelay)
	}
	dst = append(dst, byte(msg.RxDelay))
	if msg.GetCfList() != nil {
		dst, err = AppendCFList(dst, *msg.CfList)
		if err != nil {
			return nil, errFailedEncoding("CFList").WithCause(err)
		}
	}
	return dst, nil
}
func MarshalJoinAcceptPayload(msg ttnpb.JoinAcceptPayload) ([]byte, error) {
	if msg.GetCfList() != nil {
		return AppendJoinAcceptPayload(make([]byte, 0, 28), msg)
	}
	return AppendJoinAcceptPayload(make([]byte, 0, 12), msg)
}
func UnmarshalJoinAcceptPayload(b []byte, msg *ttnpb.JoinAcceptPayload) error {
	n := len(b)
	if n != 12 && n != 28 {
		return errExpectedLengthEncodedTwoChoices("JoinAcceptPayload", 12, 28)(n)
	}
	copyReverse(msg.JoinNonce[:], b[0:3])
	copyReverse(msg.NetId[:], b[3:6])
	copyReverse(msg.DevAddr[:], b[6:10])
	if err := UnmarshalDLSettings(b[10:11], &msg.DLSettings); err != nil {
		return errFailedDecoding("DLSettings").WithCause(err)
	}
	msg.RxDelay = ttnpb.RxDelay(b[11])
	if n == 12 {
		return nil
	}
	msg.CfList = &ttnpb.CFList{}
	if err := UnmarshalCFList(b[12:], msg.CfList); err != nil {
		return errFailedDecoding("CFList").WithCause(err)
	}
	return nil
}
func AppendJoinRequestPayload(dst []byte, msg ttnpb.JoinRequestPayload) ([]byte, error) {
	dst = appendReverse(dst, msg.JoinEui[:]...)
	dst = appendReverse(dst, msg.DevEui[:]...)
	dst = appendReverse(dst, msg.DevNonce[:]...)
	return dst, nil
}
func MarshalJoinRequestPayload(msg ttnpb.JoinRequestPayload) ([]byte, error) {
	return AppendJoinRequestPayload(make([]byte, 0, 18), msg)
}
func UnmarshalJoinRequestPayload(b []byte, msg *ttnpb.JoinRequestPayload) error {
	if len(b) != 18 {
		return errExpectedLengthEncodedEqual("JoinRequestPayload", 18)(len(b))
	}
	copyReverse(msg.JoinEui[:], b[0:8])
	copyReverse(msg.DevEui[:], b[8:16])
	copyReverse(msg.DevNonce[:], b[16:18])
	return nil
}
func AppendRejoinRequestPayload(dst []byte, msg ttnpb.RejoinRequestPayload) ([]byte, error) {
	dst = append(dst, byte(msg.RejoinType))
	switch msg.RejoinType {
	case 0, 2:
		dst = appendReverse(dst, msg.NetId[:]...)
		dst = appendReverse(dst, msg.DevEui[:]...)
	case 1:
		dst = appendReverse(dst, msg.JoinEui[:]...)
		dst = appendReverse(dst, msg.DevEui[:]...)
	default:
		return nil, errUnknown("RejoinType")(msg.RejoinType)
	}
	if msg.RejoinCnt > math.MaxUint16 {
		return nil, errExpectedLowerOrEqual("RJcount1", math.MaxUint16)(msg.RejoinCnt)
	}
	dst = byteutil.AppendUint32(dst, msg.RejoinCnt, 2)
	return dst, nil
}
func MarshalRejoinRequestPayload(msg ttnpb.RejoinRequestPayload) ([]byte, error) {
	if msg.RejoinType == 1 {
		return AppendRejoinRequestPayload(make([]byte, 0, 19), msg)
	}
	return AppendRejoinRequestPayload(make([]byte, 0, 14), msg)
}
var errEncryptedJoinAcceptPayloadLength = unexpectedValue(
	errors.DefineInvalidArgument("encrypted_joinacceptpayload_length", "encrypted JoinAcceptPayload should have length of 16 or 32", valueKey),
)
func UnmarshalRejoinRequestPayload(b []byte, msg *ttnpb.RejoinRequestPayload) error {
	msg.RejoinType = ttnpb.RejoinRequestType(b[0])
	switch msg.RejoinType {
	case 0, 2:
		if len(b) != 14 {
			return errExpectedLengthEqual("RejoinRequestPayload", 14)(len(b))
		}
		copyReverse(msg.NetId[:], b[1:4])
		copyReverse(msg.DevEui[:], b[4:12])
		msg.RejoinCnt = byteutil.ParseUint32(b[12:14])
	case 1:
		if len(b) != 19 {
			return errExpectedLengthEqual("RejoinRequestPayload", 19)(len(b))
		}
		copyReverse(msg.JoinEui[:], b[1:9])
		copyReverse(msg.DevEui[:], b[9:17])
		msg.RejoinCnt = byteutil.ParseUint32(b[17:19])
	default:
		return errUnknown("RejoinType")(msg.RejoinType.String())
	}
	return nil
}