func (raw RouteAttrValue) toTCLASS() (uint8, bool) {
	return raw.toUint8()
}
toTCLASS marshals the TCP Traffic Class octet.  See https://tools.ietf.org/html/rfc3168
package snapshot
import (
	"errors"
	"fmt"
	"io"
	"log"
	"time"
	"unsafe"
	"github.com/m-lab/go/logx"
	"github.com/m-lab/tcp-info/inetdiag"
	"github.com/m-lab/tcp-info/metrics"
	"github.com/m-lab/tcp-info/netlink"
	"github.com/m-lab/tcp-info/tcp"
)
var ErrEmptyRecord = errors.New("Message should contain Metadata or RawIDM")
var missingDecodeLog = logx.NewLogEvery(nil, time.Second)
func Decode(ar *netlink.ArchivalRecord) (*netlink.Metadata, *Snapshot, error) {
	var err error
	result := Snapshot{}
	result.Timestamp = ar.Timestamp
	if ar.Metadata == nil && ar.RawIDM == nil {
		return nil, nil, ErrEmptyRecord
	}
	if ar.RawIDM != nil {
		result.InetDiagMsg, err = ar.RawIDM.Parse()
		if err != nil {
			log.Println("Error decoding RawIDM:", err)
			return nil, nil, err
		}
	}
	for t, raw := range ar.Attributes {
		if raw == nil {
			continue
		}
		rta := RouteAttrValue(raw)
		ok := false
		switch t {
		case inetdiag.INET_DIAG_MEMINFO:
			result.MemInfo, ok = rta.toMemInfo()
		case inetdiag.INET_DIAG_INFO:
			result.TCPInfo, ok = rta.toLinuxTCPInfo()
		case inetdiag.INET_DIAG_VEGASINFO:
			result.VegasInfo, ok = rta.toVegasInfo()
		case inetdiag.INET_DIAG_CONG:
			result.CongestionAlgorithm, ok = rta.CongestionAlgorithm()
		case inetdiag.INET_DIAG_TOS:
			result.TOS, ok = rta.toTOS()
		case inetdiag.INET_DIAG_TCLASS:
			result.TClass, ok = rta.toTCLASS()
		case inetdiag.INET_DIAG_SKMEMINFO:
			result.SocketMem, ok = rta.toSockMemInfo()
		case inetdiag.INET_DIAG_SHUTDOWN:
			result.Shutdown, ok = rta.toShutdown()
		case inetdiag.INET_DIAG_DCTCPINFO:
			result.DCTCPInfo, ok = rta.toDCTCPInfo()
		case inetdiag.INET_DIAG_PROTOCOL:
			result.Protocol, ok = rta.toProtocol()
		case inetdiag.INET_DIAG_SKV6ONLY:
			metrics.NetlinkNotDecoded.WithLabelValues("INET_DIAG_SKV6ONLY").Inc()
			missingDecodeLog.Println("SKV6ONLY not handled", len(rta))
		case inetdiag.INET_DIAG_LOCALS:
			metrics.NetlinkNotDecoded.WithLabelValues("INET_DIAG_LOCALS").Inc()
			missingDecodeLog.Println("LOCAL not handled", len(rta))
		case inetdiag.INET_DIAG_PEERS:
			metrics.NetlinkNotDecoded.WithLabelValues("INET_DIAG_PEERS").Inc()
			missingDecodeLog.Println("PEERS not handled", len(rta))
		case inetdiag.INET_DIAG_PAD:
			metrics.NetlinkNotDecoded.WithLabelValues("INET_DIAG_PAD").Inc()
			missingDecodeLog.Println("PAD not handled", len(rta))
		case inetdiag.INET_DIAG_MARK:
			result.Mark, ok = rta.toMark()
		case inetdiag.INET_DIAG_BBRINFO:
			result.BBRInfo, ok = rta.toBBRInfo()
		case inetdiag.INET_DIAG_CLASS_ID:
			result.ClassID, ok = rta.toClassID()
		case inetdiag.INET_DIAG_MD5SIG:
			missingDecodeLog.Println("MD5SIGnot handled", len(rta))
		default:
			metrics.NetlinkNotDecoded.WithLabelValues(fmt.Sprint(t)).Inc()
			missingDecodeLog.Println("unhandled attribute type:", t)
		}
		bit := uint32(1) << uint8(t-1)
		result.Observed |= bit
		if !ok {
			result.NotFullyParsed |= bit
		}
	}
	return ar.Metadata, &result, nil
}
var oneSecondLog = logx.NewLogEvery(nil, time.Second)
type RouteAttrValue []byte
func maybeCopy(src []byte, size int, msgType string) (unsafe.Pointer, bool) {
	if len(src) < size {
		data := make([]byte, size)
		copy(data, src)
		return unsafe.Pointer(&data[0]), true
	}
	if len(src) > size {
		metrics.LargeNetlinkMsgTotal.WithLabelValues(msgType).Inc()
	}
	return unsafe.Pointer(&src[0]), len(src) == size
}
func (raw RouteAttrValue) toMemInfo() (*inetdiag.MemInfo, bool) {
	structSize := (int)(unsafe.Sizeof(inetdiag.MemInfo{}))
	data, ok := maybeCopy(raw, structSize, "MemInfo")
	if !ok {
		oneSecondLog.Println("memInfo data is larger than struct")
	}
	return (*inetdiag.MemInfo)(data), ok
}
func (raw RouteAttrValue) toLinuxTCPInfo() (*tcp.LinuxTCPInfo, bool) {
	structSize := (int)(unsafe.Sizeof(tcp.LinuxTCPInfo{}))
	data, ok := maybeCopy(raw, structSize, "TCPInfo")
	if !ok {
		oneSecondLog.Println("tcpinfo data is larger than struct")
	}
	return (*tcp.LinuxTCPInfo)(data), ok
}
func (raw RouteAttrValue) toVegasInfo() (*inetdiag.VegasInfo, bool) {
	structSize := (int)(unsafe.Sizeof(inetdiag.VegasInfo{}))
	data, ok := maybeCopy(raw, structSize, "VegasInfo")
	return (*inetdiag.VegasInfo)(data), ok
}
func (raw RouteAttrValue) CongestionAlgorithm() (string, bool) {
	return string(raw[:len(raw)-1]), true
}
func (raw RouteAttrValue) toUint8() (uint8, bool) {
	if len(raw) < 1 {
		return 0, false
	}
	return uint8(raw[0]), true
}
func (raw RouteAttrValue) toTOS() (uint8, bool) {
	return raw.toUint8()
}