summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rrdformat/format.go60
-rw-r--r--rrdformat/rrdbinary/errors.go2
-rw-r--r--rrdformat/rrdbinary/errors_test.go4
-rw-r--r--rrdformat/rrdbinary/types.go2
-rw-r--r--rrdformat/rrdbinary/unmarshal.go6
-rw-r--r--rrdformat/sniff.go141
6 files changed, 118 insertions, 97 deletions
diff --git a/rrdformat/format.go b/rrdformat/format.go
index 71bd966..8898ffd 100644
--- a/rrdformat/format.go
+++ b/rrdformat/format.go
@@ -1,11 +1,8 @@
package rrdformat
import (
- "bytes"
- "encoding"
- "encoding/binary"
+ //"encoding"
"encoding/xml"
- "math"
"git.lukeshu.com/go/librrd/rrdformat/rrdbinary"
)
@@ -48,12 +45,12 @@ const XMLNS = "https://oss.oetiker.ch/rrdtool/rrdtool-dump.xml"
type RRDValue = rrdbinary.Float
type Header struct {
- Cookie rrdbinary.String `rrdbinary:"size=3" xml:"-"`
- Version rrdbinary.String `rrdbinary:"size=4" xml:"version"`
+ Cookie rrdbinary.String `rrdbinary:"size=4" xml:"-"`
+ Version rrdbinary.String `rrdbinary:"size=5" xml:"version"`
FloatCookie rrdbinary.Float `xml:"-"`
DSCnt rrdbinary.Uint `xml:"-"`
RRACnt rrdbinary.Uint `xml:"-"`
- DPDStep rrdbinary.Uint `xml:"step"`
+ PDPStep rrdbinary.Uint `xml:"step"`
Parameters [10]rrdbinary.Unival `xml:"-"`
}
@@ -66,10 +63,10 @@ type DSDef struct {
type RRADef struct {
CFName rrdbinary.String `rrdbinary:"size=20"`
RowCnt rrdbinary.Uint
- PDPCnt rrdBinary.Uint
+ PDPCnt rrdbinary.Uint
}
-type Timestamp struct {
+type Time struct {
Sec rrdbinary.Time
Usec rrdbinary.Int // signed, but always >= 0
}
@@ -92,24 +89,41 @@ type RRDv0004 = RRDv0003
type RRDv0003 struct {
Header Header
- DSDefs []DSDef
- RRADefs []RRADef
- LastUpdated Timestamp
- PDPPrep TODO
- CPDPrep TODO
- RRAPtr TODO
- Values []RRDValue
+ DSDefs []DSDef // .Header.DSCnt
+ RRADefs []RRADef // .Header.RRACnt
+ LastUpdated Time
+ PDPPrep []PDPPrep // .Header.DSCnt
+ CDPPrep []CDPPrep // .Header.DSCnt * .Header.RRACnt
+ RRAPtr []RRAPtr // .Header.RRACnt
+ Values []RRDValue // Σ .RRADefs[i].RowCnt*.Header.DsCnt
}
type RRDv0002 = RRDv0001
type RRDv0001 struct {
Header Header
- DSDefs []DSDef
- RRADefs []RRADef
- LastUpdated rrdbinary.Timestamp
- PDPPrep TODO
- CPDPrep TODO
- RRAPtr TODO
- Values []RRDValue
+ DSDefs []DSDef // .Header.DSCnt
+ RRADefs []RRADef // .Header.RRACnt
+ LastUpdated rrdbinary.Time
+ PDPPrep []PDPPrep // .Header.DSCnt
+ CDPPrep []CDPPrep // .Header.DSCnt * .Header.RRACnt
+ RRAPtr []RRAPtr // .Header.RRACnt
+ Values []RRDValue // Σ .RRADefs[i].RowCnt*.Header.DsCnt
}
+
+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{}
+
+var _ xml.Marshaler = &Header{}
+
+//var _ xml.Unmarshaler = &Header{}
diff --git a/rrdformat/rrdbinary/errors.go b/rrdformat/rrdbinary/errors.go
index 984e0a5..1e61af0 100644
--- a/rrdformat/rrdbinary/errors.go
+++ b/rrdformat/rrdbinary/errors.go
@@ -13,7 +13,7 @@ type BinaryError struct {
ctxEOF bool
}
-func newBinError(msg string, ctxFile []byte, ctxStart, ctxLen int) error {
+func NewBinError(msg string, ctxFile []byte, ctxStart, ctxLen int) error {
if ctxStart+ctxLen > len(ctxFile) {
ctxLen = len(ctxFile) - ctxStart
}
diff --git a/rrdformat/rrdbinary/errors_test.go b/rrdformat/rrdbinary/errors_test.go
index f65929b..86ae809 100644
--- a/rrdformat/rrdbinary/errors_test.go
+++ b/rrdformat/rrdbinary/errors_test.go
@@ -11,7 +11,7 @@ func TestBinaryError(t *testing.T) {
assert := assert.New(t)
bad404 := []byte(`<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"`)
- err := newBinError("not an RRD file: wrong magic number", bad404, 0, 4)
+ err := NewBinError("not an RRD file: wrong magic number", bad404, 0, 4)
assert.Equal(err.Error(), `invalid RRD: not an RRD file: wrong magic number`)
assert.Equal(fmt.Sprintf("%v", err), `invalid RRD: not an RRD file: wrong magic number`)
assert.Equal(fmt.Sprintf("%q", err), `"invalid RRD: not an RRD file: wrong magic number"`)
@@ -22,7 +22,7 @@ func TestBinaryError(t *testing.T) {
`)
badShort := []byte{'R'}
- err = newBinError("not an RRD file: wrong magic number", badShort, 0, 4)
+ err = NewBinError("not an RRD file: wrong magic number", badShort, 0, 4)
assert.Equal(err.Error(), `invalid RRD: not an RRD file: wrong magic number`)
assert.Equal(fmt.Sprintf("%v", err), `invalid RRD: not an RRD file: wrong magic number`)
assert.Equal(fmt.Sprintf("%q", err), `"invalid RRD: not an RRD file: wrong magic number"`)
diff --git a/rrdformat/rrdbinary/types.go b/rrdformat/rrdbinary/types.go
index 36e89fa..a036d5d 100644
--- a/rrdformat/rrdbinary/types.go
+++ b/rrdformat/rrdbinary/types.go
@@ -24,7 +24,7 @@ type Architecture struct {
type String string // \0-terminated
type Float float64 // 8 bytes
type Uint uint64 // 4 or 8 bytes
-type _Int int64 // 4 or 8 bytes
+type Int int64 // 4 or 8 bytes
type Unival uint64 // 8 bytes
type Time int64 // 4 or 8 bytes, only has second-precision
diff --git a/rrdformat/rrdbinary/unmarshal.go b/rrdformat/rrdbinary/unmarshal.go
index e0f8988..1cfee34 100644
--- a/rrdformat/rrdbinary/unmarshal.go
+++ b/rrdformat/rrdbinary/unmarshal.go
@@ -29,7 +29,7 @@ type unmarshaler struct {
}
func (d *unmarshaler) binError(ctxLen int, msg string) error {
- return newBinError(msg, d.data, d.pos, ctxLen)
+ return NewBinError(msg, d.data, d.pos, ctxLen)
}
func (d *unmarshaler) binErrorf(ctxLen int, format string, a ...interface{}) error {
@@ -44,7 +44,7 @@ func (d *unmarshaler) unmarshal(v reflect.Value, tag string) error {
return d.unmarshalFloat(v, tag)
case reflect.TypeOf(Uint(0)):
return d.unmarshalUint(v, tag)
- case reflect.TypeOf(_Int(0)):
+ case reflect.TypeOf(Int(0)):
return d.unmarshalInt(v, tag)
case reflect.TypeOf(Unival(0)):
return d.unmarshalUnival(v, tag)
@@ -213,7 +213,7 @@ func (d *unmarshaler) unmarshalUint(v reflect.Value, tag string) error {
}
func (d *unmarshaler) unmarshalInt(v reflect.Value, tag string) error {
- panicUnless(v.Type() == reflect.TypeOf(_Int(0)))
+ panicUnless(v.Type() == reflect.TypeOf(Int(0)))
panicUnless(v.CanSet())
if d.arch.IntWidth != 4 && d.arch.IntWidth != 8 {
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
+}