summaryrefslogtreecommitdiff
path: root/rrdformat/sniff.go
diff options
context:
space:
mode:
Diffstat (limited to 'rrdformat/sniff.go')
-rw-r--r--rrdformat/sniff.go141
1 files changed, 74 insertions, 67 deletions
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
+}