summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rrdformat/format.go27
-rw-r--r--rrdformat/rrdbinary/types.go20
-rw-r--r--rrdformat/rrdbinary/unmarshal.go87
3 files changed, 111 insertions, 23 deletions
diff --git a/rrdformat/format.go b/rrdformat/format.go
index 7ea15b7..4e68b93 100644
--- a/rrdformat/format.go
+++ b/rrdformat/format.go
@@ -12,6 +12,8 @@ import (
const XMLNS = "https://oss.oetiker.ch/rrdtool/rrdtool-dump.xml"
+type RRDValue = rrdbinary.Float
+
// rrdtool:
// rrd_format.h: stat_head_t -- static header of the database
//
@@ -39,16 +41,30 @@ type RRADef struct {
PDPCnt rrdBinary.Uint
}
-type LiveHead struct {
-
+type PDPPrep struct {
+ LastDS rrdbinary.String `rrdbinary:"size=30"`
+ Scratch [10]rrdbinary.Unival
+}
+
+type CDPPrep struct {
+ Scratch [10]rrdbinary.Unival
+}
+
+type RRAPtr struct {
+ CurRow rrdbinary.Uint
}
// rrdtool:
// rrd_format.h: rrd_t One single struct to hold all the others.
type RRD struct {
- DSDefs []DSDef
- RRADefs []RRADef
- LiveHead LiveHead
+ Header Header
+ DSDefs []DSDef
+ RRADefs []RRADef
+ LastUpdated rrdbinary.Timesstamp
+ PDPPrep TODO
+ CPDPrep TODO
+ RRAPtr TODO
+ Values []RRDValue
}
type Header struct {
@@ -102,7 +118,6 @@ func (h *Header) UnmarshalBinary(data []byte) error {
// 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
magicFloat := float64(8.642135e130)
diff --git a/rrdformat/rrdbinary/types.go b/rrdformat/rrdbinary/types.go
index cbe3e2d..892bb25 100644
--- a/rrdformat/rrdbinary/types.go
+++ b/rrdformat/rrdbinary/types.go
@@ -8,20 +8,24 @@ import (
type Architecture struct {
ByteOrder binary.ByteOrder
// C `double`
- FloatWidth int // always 8
+ FloatWidth int // always 8 -- we assume IEEE 754 doubles
FloatAlign int
- // C `unsigned long`
- UintWidth int
- UintAlign int
+ // C `long` or `unsigned long`
+ IntWidth int
+ IntAlign int
// C `union { unsigned long; double; }`
UnivalWidth int // max(FloatWidth, IntWidth)
UnivalAlign int // max(FloatAlign, IntAlign)
+ // C `time_t`
+ TimeWidth int
+ TimeAlign int
}
-type String string // \0-terminatd
-type Float float64 // 8 bytes
-type Uint uint64 // 4 or 8 bytes
-type Unival uint64 // 8 bytes
+type String string // \0-terminatd
+type Float float64 // 8 bytes
+type Uint uint64 // 4 or 8 bytes
+type Unival uint64 // 8 bytes
+type Timestamp time.time // 8, 12, or 16 bytes
func (u Unival) AsUint64() uint64 { return uint64(u) }
func (u Unival) AsFloat64() float64 { return math.Float64frombits(uint64(u)) }
diff --git a/rrdformat/rrdbinary/unmarshal.go b/rrdformat/rrdbinary/unmarshal.go
index be0b492..34fc537 100644
--- a/rrdformat/rrdbinary/unmarshal.go
+++ b/rrdformat/rrdbinary/unmarshal.go
@@ -46,6 +46,8 @@ func (d *unmarshaler) unmarshal(v reflect.Value, tag string) error {
return d.unmarshalUint(v, tag)
case reflect.TypeOf(Unival(0)):
return d.unmarshalUnival(v, tag)
+ case reflect.TypeOf(Timestamp{}):
+ return d.unmarshalTimestamp(v, tag)
default:
switch v.Type().Kind() {
case reflect.Struct:
@@ -176,8 +178,8 @@ func (d *unmarshaler) unmarshalUint(v reflect.Value, tag string) error {
panicUnless(v.Type() == reflect.TypeOf(Uint(0)))
panicUnless(v.CanSet())
- if d.arch.UintWidth != 4 && d.arch.UintWidth != 8 {
- return archErrorf("rrdbinary does not support UintWidth=%d; only supports 4 or 8", d.arch.UintWidth)
+ if d.arch.IntWidth != 4 && d.arch.IntWidth != 8 {
+ return archErrorf("rrdbinary does not support IntWidth=%d; only supports 4 or 8", d.arch.IntWidth)
}
if tag != "" {
return typeErrorf("invalid rrdbinary struct tag for uint: %q", tag)
@@ -186,25 +188,25 @@ func (d *unmarshaler) unmarshalUint(v reflect.Value, tag string) error {
data := d.data[d.pos:]
padding := 0
- if d.pos%d.arch.UintAlign != 0 {
- padding = d.arch.UintAlign - (d.pos % d.arch.UintAlign)
+ if d.pos%d.arch.IntAlign != 0 {
+ padding = d.arch.IntAlign - (d.pos % d.arch.IntAlign)
}
if len(data) < padding {
- return d.binErrorf(padding+d.arch.UintWidth, "unexpected end-of-file in %d-byte padding-before-uint", padding)
+ return d.binErrorf(padding+d.arch.IntWidth, "unexpected end-of-file in %d-byte padding-before-uint", padding)
}
data = data[padding:]
- if len(data) < d.arch.UintWidth {
- return d.binErrorf(d.arch.UintWidth, "unexpected end-of-file in %d-byte uint", d.arch.UintWidth)
+ if len(data) < d.arch.IntWidth {
+ return d.binErrorf(d.arch.IntWidth, "unexpected end-of-file in %d-byte uint", d.arch.IntWidth)
}
- switch d.arch.UintWidth {
+ switch d.arch.IntWidth {
case 4:
v.SetUint(uint64(d.arch.ByteOrder.Uint32(data)))
case 8:
v.SetUint(d.arch.ByteOrder.Uint64(data))
}
- d.pos += padding + d.arch.UintWidth
+ d.pos += padding + d.arch.IntWidth
return nil
}
@@ -238,3 +240,70 @@ func (d *unmarshaler) unmarshalUnival(v reflect.Value, tag string) error {
d.pos += padding + d.arch.UnivalWidth
return nil
}
+
+func (d *unmarshaler) unmarshalTimestamp(v reflect.Value, tag string) error {
+ panicUnless(v.Type() == reflect.TypeOf(Timestamp{}))
+ panicUnless(v.CanSet())
+
+ if tag != "" {
+ return typeErrorf("invalid rrdbinary struct tag for timestamp: %q", tag)
+ }
+
+ data := d.data[d.pos:]
+ var sec, usec int64
+
+ // seconds -- time_t
+ if d.arch.TimeWidth != 4 && d.arch.TimeWidth != 8 {
+ return archErrorf("rrdbinary does not support TimeWidth=%d; only supports 4 or 8", d.arch.TimeWidth)
+ }
+ // padding
+ padding := 0
+ if d.pos%d.arch.TimeAlign != 0 {
+ padding = d.arch.TimeAlign - (d.pos % d.arch.TimeAlign)
+ }
+ if len(data) < padding {
+ return d.binErrorf(padding+d.arch.TimeWidth, "unexpected end-of-file in %d-byte padding-before-time_t", padding)
+ }
+ data = data[padding:]
+ // value
+ if len(data) < d.arch.TimeWidth {
+ return d.binErrorf(d.arch.TimeWidth, "unexpected end-of-file in %d-byte time_t", d.arch.TimeWidth)
+ }
+ switch d.arch.TimeWidth {
+ case 4:
+ sec = int64(d.arch.ByteOrder.Int32(data))
+ case 8:
+ sec = d.arch.ByteOrder.Int64(data)
+ }
+ data = data[d.arch.TimeWidth:]
+
+ // nanoseconds -- long
+ if d.arch.IntWidth != 4 && d.arch.IntWidth != 8 {
+ return archErrorf("rrdbinary does not support IntWidth=%d; only supports 4 or 8", d.arch.IntWidth)
+ }
+ // padding
+ padding = 0
+ if d.pos%d.arch.IntAlign != 0 {
+ padding = d.arch.IntAlign - (d.pos % d.arch.IntAlign)
+ }
+ if len(data) < padding {
+ return d.binErrorf(padding+d.arch.IntWidth, "unexpected end-of-file in %d-byte padding-before-int", padding)
+ }
+ data = data[padding:]
+ // value
+ if len(data) < d.arch.IntWidth {
+ return d.binErrorf(d.arch.IntWidth, "unexpected end-of-file in %d-byte int", d.arch.IntWidth)
+ }
+ switch d.arch.IntWidth {
+ case 4:
+ usec = int64(d.arch.ByteOrder.Int32(data))
+ case 8:
+ usec = d.arch.ByteOrder.Int64(data)
+ }
+ data = data[d.arch.IntWidth:]
+
+ // put it all together
+ v.Set(reflect.ValueOf(time.Unix(sec, usec*1000)))
+ d.pos = len(pos.data) - len(data)
+ return nil
+}