From 34cfe3a3f2bdf13994a2a166e970e6b255368afb Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sun, 26 Jan 2020 17:33:43 -0500 Subject: add timestamp type --- rrdformat/format.go | 27 ++++++++++--- rrdformat/rrdbinary/types.go | 20 +++++---- rrdformat/rrdbinary/unmarshal.go | 87 +++++++++++++++++++++++++++++++++++----- 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 +} -- cgit v1.2.3