From 72c4b2518a77ee9952a0fa25ae671f06d8d85570 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sun, 26 Jan 2020 22:30:00 -0500 Subject: it compiles --- rrdformat/sniff.go | 141 ++++++++++++++++++++++++++++------------------------- 1 file changed, 74 insertions(+), 67 deletions(-) (limited to 'rrdformat/sniff.go') diff --git a/rrdformat/sniff.go b/rrdformat/sniff.go index f8f3397..db48959 100644 --- a/rrdformat/sniff.go +++ b/rrdformat/sniff.go @@ -1,71 +1,81 @@ -func (h *Header) UnmarshalBinary(data []byte) error { - // magic number cookie - if !bytes.HasPrefix(data, []byte("RRD\x00")) { - return newBinError("not an RRD file: wrong magic number", data, 0, 4) +package rrdformat + +import ( + "encoding/binary" + "fmt" + "math" + + "git.lukeshu.com/go/librrd/rrdformat/rrdbinary" +) + +func SniffArchitecture(data []byte) (rrdbinary.Architecture, error) { + var arch rrdbinary.Architecture + + var header struct { + Cookie rrdbinary.String `rrdbinary:"size=4" xml:"-"` + Version rrdbinary.String `rrdbinary:"size=5" xml:"version"` + } + if err := rrdbinary.Unmarshal(arch, data, &header); err != nil { + return rrdbinary.Architecture{}, err } - h.Cookie = data[0:4] - // version string - null := bytes.IndexByte(data[4:], 0) - if null < 0 { - return newBinError("no null-terminator on version string", data, 4, 5) + // 1. File header magic number + if header.Cookie != "RRD" { + return rrdbinary.Architecture{}, rrdbinary.NewBinError("not an RRD file: wrong magic number", data, 0, 4) } - null += 4 - h.Version = data[4:null] - switch string(h.Version) { - case "0003": - case "0004": - case "0005": + + // 1. File format version string + switch header.Version { + case "0001", "0002", "0003", "0004", "0005": + // do nothing default: + return rrdbinary.Architecture{}, rrdbinary.NewBinError(fmt.Sprintf("can't handle RRD file version %q", header.Version), data, 4, 5) } - // float cookie + // 3. float cookie // // Assume IEEE 754 doubles. C doesn't assume 754 doubles, but anything that doesn't use 754 doubles is exotic // enough that I'm OK saying "you're going to need to use `rrdtool dump`". This lets us assume that: // - a 'double' is 8 bytes wide // - the value will be exactly equal, and we don't need to worry about weird rounding. - h.FloatWidth = 8 + arch.FloatWidth = 8 magicFloat := float64(8.642135e130) - floatAddrPacked := null + 1 + floatAddrPacked := 9 floatAddr32 := ((floatAddrPacked + 3) / 4) * 4 floatAddr64 := ((floatAddrPacked + 7) / 8) * 8 var restOffset int switch { - case len(data) < floatAddr32+h.FloatWidth: - return newBinError("unexpected end of file", data, floatAddrPacked, floatAddr64+h.FloatWidth-floatAddrPacked) + case len(data) < floatAddr32+arch.FloatWidth: + return rrdbinary.Architecture{}, rrdbinary.NewBinError("unexpected end-of-file", data, floatAddrPacked, floatAddr64+arch.FloatWidth-floatAddrPacked) case math.Float64frombits(binary.LittleEndian.Uint64(data[floatAddr32:])) == magicFloat: - h.FloatCookie = data[floatAddr32 : floatAddr32+h.FloatWidth] - h.ByteOrder = binary.LittleEndian - h.FloatAlign = 4 - restOffset = floatAddr32 + h.FloatWidth + arch.ByteOrder = binary.LittleEndian + arch.FloatAlign = 4 + restOffset = floatAddr32 + arch.FloatWidth case math.Float64frombits(binary.BigEndian.Uint64(data[floatAddr32:])) == magicFloat: - h.FloatCookie = data[floatAddr32 : floatAddr32+h.FloatWidth] - h.ByteOrder = binary.BigEndian - h.FloatAlign = 4 - restOffset = floatAddr32 + h.FloatWidth - case len(data) < floatAddr64+h.FloatWidth: - return newBinError("unexpected end of file", data, floatAddrPacked, floatAddr64+h.FloatWidth-floatAddrPacked) + arch.ByteOrder = binary.BigEndian + arch.FloatAlign = 4 + restOffset = floatAddr32 + arch.FloatWidth + case len(data) < floatAddr64+arch.FloatWidth: + return rrdbinary.Architecture{}, rrdbinary.NewBinError("unexpected end-of-file", data, floatAddrPacked, floatAddr64+arch.FloatWidth-floatAddrPacked) case math.Float64frombits(binary.LittleEndian.Uint64(data[floatAddr64:])) == magicFloat: - h.FloatCookie = data[floatAddr64 : floatAddr64+h.FloatWidth] - h.ByteOrder = binary.LittleEndian - h.FloatAlign = 8 - restOffset = floatAddr64 + h.FloatWidth + arch.ByteOrder = binary.LittleEndian + arch.FloatAlign = 8 + restOffset = floatAddr64 + arch.FloatWidth case math.Float64frombits(binary.BigEndian.Uint64(data[floatAddr64:])) == magicFloat: - h.FloatCookie = data[floatAddr64 : floatAddr64+h.FloatWidth] - h.ByteOrder = binary.BigEndian - h.FloatAlign = 8 - restOffset = floatAddr64 + h.FloatWidth + arch.ByteOrder = binary.BigEndian + arch.FloatAlign = 8 + restOffset = floatAddr64 + arch.FloatWidth default: - return newBinError("failed to sniff byte-order and float-alignment", - data, floatAddrPacked, floatAddr64+h.FloatWidth-floatAddrPacked) + return rrdbinary.Architecture{}, rrdbinary.NewBinError("failed to sniff byte-order and float-alignment", + data, floatAddrPacked, floatAddr64+arch.FloatWidth-floatAddrPacked) } - switch h.FloatAlign { + // 5, 6. ds_cnt, rra_cnt + switch arch.FloatAlign { case 4: // Assume that if floats are only 32-bit aligned, then everything is 32-bit - h.IntWidth = 4 - h.IntAlign = 4 + arch.IntWidth = 4 + arch.IntAlign = 4 case 8: // If floats are 64-bit aligned, then this might be all-in on 64-bit, or it might 32-bit ints. @@ -89,37 +99,34 @@ func (h *Header) UnmarshalBinary(data []byte) error { // 64le | R| R| D|\0| 0| 0| 0| 3|\0| |<----doublecookie----->|<1111----ds_cnt---0000>| // 64be | R| R| D|\0| 0| 0| 0| 3|\0| |<----doublecookie----->|<0000----ds_cnt---1111>| if len(data) < restOffset+8 { - return newBinError("unexpected end of file", data, restOffset, 8) + return rrdbinary.Architecture{}, rrdbinary.NewBinError("unexpected end of file", data, restOffset, 8) } offset := map[binary.ByteOrder]int{ binary.BigEndian: restOffset, // 24 in the above diagram binary.LittleEndian: restOffset + 4, // 28 in the above diagram - }[h.ByteOrder] - if h.ByteOrder.Uint32(data[offset:]) == 0 { - h.IntWidth = 8 - h.IntAlign = 8 + }[arch.ByteOrder] + if arch.ByteOrder.Uint32(data[offset:]) == 0 { + arch.IntWidth = 8 + arch.IntAlign = 8 } else { - h.IntWidth = 4 - h.IntAlign = 4 + arch.IntWidth = 4 + arch.IntAlign = 4 } } - return nil -} - -func (h *Header) MarshalXML(e *xml.Encoder, start xml.StartElement) error { - if err := e.EncodeElement(h.Version, xml.StartElement{Name: xml.Name{Local: "version", Space: XMLNS}}); err != nil { - return err - } - if err := e.EncodeElement(h.PDPStep, xml.StartElement{Name: xml.Name{Local: "step", Space: XMLNS}}); err != nil { - return err - } - return nil -} - -//var _ encoding.BinaryMarshaler = &Header{} -var _ encoding.BinaryUnmarshaler = &Header{} + // The above just os happens that FloatXXX >= IntXXX, so we + // can just set the Unival stuff to the Float Stuff. + arch.UnivalWidth = arch.FloatWidth // max(FloatWidth, IntWidth) + arch.UnivalAlign = arch.FloatAlign // max(FloatAlign, IntAlign) -var _ xml.Marshaler = &Header{} + // FIXME: Figure out how to sniff the sizeof(time_t). + // + // javascriptRRD assumes that it's the same as sizeof(long), + // which his historically been true, but + // - isn't true of proprietary 32-bit Unixen that are 2038-safe + // - isn't true of the Linux kernel with the x32 ABI + arch.TimeWidth = arch.IntWidth + arch.TimeAlign = arch.IntAlign -//var _ xml.Unmarshaler = &Header{} + return arch, nil +} -- cgit v1.2.3