rrd-ruby 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: daeb5b5caaff3a57391092455327295daa8c6210
4
+ data.tar.gz: 7e97ab2b34253dc454f1a05e943c1116a06f6d98
5
+ SHA512:
6
+ metadata.gz: 16614243ebdf97b3b029f3bfb90aa771a80c9229845a428225f103c87d641a705aa6197ea5f91dc74659e834b876ed48bfc97bc35b6730426f5de4ab1647d3cd
7
+ data.tar.gz: 48774a58542500e7e4dbd4101a682068c1810af9195abdf06a63f6997278d1dc7f6b7b19fa8da84a6c2254d227ea6d13110ff1f4906f7d697c9ec0e525daf884
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rrd-native.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,165 @@
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+ This version of the GNU Lesser General Public License incorporates
10
+ the terms and conditions of version 3 of the GNU General Public
11
+ License, supplemented by the additional permissions listed below.
12
+
13
+ 0. Additional Definitions.
14
+
15
+ As used herein, "this License" refers to version 3 of the GNU Lesser
16
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+ General Public License.
18
+
19
+ "The Library" refers to a covered work governed by this License,
20
+ other than an Application or a Combined Work as defined below.
21
+
22
+ An "Application" is any work that makes use of an interface provided
23
+ by the Library, but which is not otherwise based on the Library.
24
+ Defining a subclass of a class defined by the Library is deemed a mode
25
+ of using an interface provided by the Library.
26
+
27
+ A "Combined Work" is a work produced by combining or linking an
28
+ Application with the Library. The particular version of the Library
29
+ with which the Combined Work was made is also called the "Linked
30
+ Version".
31
+
32
+ The "Minimal Corresponding Source" for a Combined Work means the
33
+ Corresponding Source for the Combined Work, excluding any source code
34
+ for portions of the Combined Work that, considered in isolation, are
35
+ based on the Application, and not on the Linked Version.
36
+
37
+ The "Corresponding Application Code" for a Combined Work means the
38
+ object code and/or source code for the Application, including any data
39
+ and utility programs needed for reproducing the Combined Work from the
40
+ Application, but excluding the System Libraries of the Combined Work.
41
+
42
+ 1. Exception to Section 3 of the GNU GPL.
43
+
44
+ You may convey a covered work under sections 3 and 4 of this License
45
+ without being bound by section 3 of the GNU GPL.
46
+
47
+ 2. Conveying Modified Versions.
48
+
49
+ If you modify a copy of the Library, and, in your modifications, a
50
+ facility refers to a function or data to be supplied by an Application
51
+ that uses the facility (other than as an argument passed when the
52
+ facility is invoked), then you may convey a copy of the modified
53
+ version:
54
+
55
+ a) under this License, provided that you make a good faith effort to
56
+ ensure that, in the event an Application does not supply the
57
+ function or data, the facility still operates, and performs
58
+ whatever part of its purpose remains meaningful, or
59
+
60
+ b) under the GNU GPL, with none of the additional permissions of
61
+ this License applicable to that copy.
62
+
63
+ 3. Object Code Incorporating Material from Library Header Files.
64
+
65
+ The object code form of an Application may incorporate material from
66
+ a header file that is part of the Library. You may convey such object
67
+ code under terms of your choice, provided that, if the incorporated
68
+ material is not limited to numerical parameters, data structure
69
+ layouts and accessors, or small macros, inline functions and templates
70
+ (ten or fewer lines in length), you do both of the following:
71
+
72
+ a) Give prominent notice with each copy of the object code that the
73
+ Library is used in it and that the Library and its use are
74
+ covered by this License.
75
+
76
+ b) Accompany the object code with a copy of the GNU GPL and this license
77
+ document.
78
+
79
+ 4. Combined Works.
80
+
81
+ You may convey a Combined Work under terms of your choice that,
82
+ taken together, effectively do not restrict modification of the
83
+ portions of the Library contained in the Combined Work and reverse
84
+ engineering for debugging such modifications, if you also do each of
85
+ the following:
86
+
87
+ a) Give prominent notice with each copy of the Combined Work that
88
+ the Library is used in it and that the Library and its use are
89
+ covered by this License.
90
+
91
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+ document.
93
+
94
+ c) For a Combined Work that displays copyright notices during
95
+ execution, include the copyright notice for the Library among
96
+ these notices, as well as a reference directing the user to the
97
+ copies of the GNU GPL and this license document.
98
+
99
+ d) Do one of the following:
100
+
101
+ 0) Convey the Minimal Corresponding Source under the terms of this
102
+ License, and the Corresponding Application Code in a form
103
+ suitable for, and under terms that permit, the user to
104
+ recombine or relink the Application with a modified version of
105
+ the Linked Version to produce a modified Combined Work, in the
106
+ manner specified by section 6 of the GNU GPL for conveying
107
+ Corresponding Source.
108
+
109
+ 1) Use a suitable shared library mechanism for linking with the
110
+ Library. A suitable mechanism is one that (a) uses at run time
111
+ a copy of the Library already present on the user's computer
112
+ system, and (b) will operate properly with a modified version
113
+ of the Library that is interface-compatible with the Linked
114
+ Version.
115
+
116
+ e) Provide Installation Information, but only if you would otherwise
117
+ be required to provide such information under section 6 of the
118
+ GNU GPL, and only to the extent that such information is
119
+ necessary to install and execute a modified version of the
120
+ Combined Work produced by recombining or relinking the
121
+ Application with a modified version of the Linked Version. (If
122
+ you use option 4d0, the Installation Information must accompany
123
+ the Minimal Corresponding Source and Corresponding Application
124
+ Code. If you use option 4d1, you must provide the Installation
125
+ Information in the manner specified by section 6 of the GNU GPL
126
+ for conveying Corresponding Source.)
127
+
128
+ 5. Combined Libraries.
129
+
130
+ You may place library facilities that are a work based on the
131
+ Library side by side in a single library together with other library
132
+ facilities that are not Applications and are not covered by this
133
+ License, and convey such a combined library under terms of your
134
+ choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based
137
+ on the Library, uncombined with any other library facilities,
138
+ conveyed under the terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it
141
+ is a work based on the Library, and explaining where to find the
142
+ accompanying uncombined form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions
147
+ of the GNU Lesser General Public License from time to time. Such new
148
+ versions will be similar in spirit to the present version, but may
149
+ differ in detail to address new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the
152
+ Library as you received it specifies that a certain numbered version
153
+ of the GNU Lesser General Public License "or any later version"
154
+ applies to it, you have the option of following the terms and
155
+ conditions either of that published version or of any later version
156
+ published by the Free Software Foundation. If the Library as you
157
+ received it does not specify a version number of the GNU Lesser
158
+ General Public License, you may choose any version of the GNU Lesser
159
+ General Public License ever published by the Free Software Foundation.
160
+
161
+ If the Library as you received it specifies that a proxy can decide
162
+ whether future versions of the GNU Lesser General Public License shall
163
+ apply, that proxy's public statement of acceptance of any version is
164
+ permanent authorization for you to choose that version for the
165
+ Library.
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Igor Yamolov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # RRD::Ruby
2
+
3
+ Native ruby implementation of RRD format parsing.
4
+ Don't need all these pesky C libraries.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'rrd-ruby'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install rrd-ruby
19
+
20
+ ## Usage
21
+
22
+ ```ruby
23
+ require 'rrd'
24
+
25
+ file = RRD::File.new("path/to/my_awesome.rrd")
26
+
27
+ # Get all Round Robin Archive infos
28
+ rra = file.rra
29
+
30
+ # Get all DataSource infos
31
+ ds = file.datasources
32
+
33
+ # Get all data from RRA in specified time interval
34
+ data = file.data(rra.first).fetch(start_time: 20.minutes.ago, end_time: 5.minutes.ago)
35
+
36
+ # Format data right in reading loop (perfomance for perfomance god!)
37
+ data = file.data(rra.first).fetch do |row, columns|
38
+ columns # => [:time, :col1, :col2, :col3]
39
+ row # => [123345345, 123.0, 456.0, 789.0]
40
+
41
+ # turn em into hash
42
+ Hash[columns.zip(row)] # => { time: 123345345, col1: 123.0, col2: 456.0, col3: 789.0 }
43
+ end
44
+ # => [... , { time: 123345345, col1: 123.0, col2: 456.0, col3: 789.0 }, ...]
45
+
46
+ # All data read from file stream, right on spot
47
+ file.close
48
+ ```
49
+
50
+ ## Contributing
51
+
52
+ 1. Fork it
53
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
54
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
55
+ 4. Push to the branch (`git push origin my-new-feature`)
56
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,57 @@
1
+ module RRD
2
+ class Archive
3
+ RRA_cdp_xff_val = 0
4
+ RRA_hw_alpha = 1
5
+ RRA_hw_beta = 2
6
+ RRA_dependent_rra_idx = 3
7
+ RRA_period = 4
8
+ RRA_seasonal_gamma = 1
9
+ RRA_seasonal_smoothing_window = 2
10
+ RRA_seasonal_smooth_idx = 4
11
+ RRA_delta_pos = 1
12
+ RRA_delta_neg = 2
13
+ RRA_window_len = 4
14
+ RRA_failure_threshold = 5
15
+
16
+ attr_reader :cf, :rows, :pdpr, :options
17
+
18
+ attr_accessor :current_row, :data_pointer
19
+
20
+ def initialize(cf, rows, per_row, options)
21
+ @cf, @rows, @pdpr, @options = cf, rows, per_row, options
22
+ end
23
+
24
+ def cdp
25
+ @cdp ||= []
26
+ end
27
+
28
+ def self.parse(reader)
29
+ cf = reader.read_string(20, "A*").downcase.to_sym
30
+ rows, pdpr = reader.read(16, "QQ", align: true)
31
+
32
+ params = reader.read_uniparams(10)
33
+
34
+ options = {}
35
+
36
+ case cf
37
+ when :hwpredict, :mhwpredict
38
+ options[:alpha] = params[RRA_hw_alpha].to_f
39
+ options[:beta] = params[RRA_hw_beta].to_f
40
+ when :seasonal, :devseasonal
41
+ options[:gamma] = params[RRA_seasonal_gamma].to_f
42
+ options[:smoothing_window] = params[RRA_seasonal_smoothing_window].to_f
43
+ when :failures
44
+ options[:delta_pos] = params[RRA_delta_pos].to_f
45
+ options[:delta_neg] = params[RRA_delta_neg].to_f
46
+ options[:failure_threshold] = params[RRA_failure_threshold].to_i
47
+ options[:window_length] = params[RRA_window_len].to_i
48
+ when :devpredict
49
+ else
50
+ options[:xff] = params[RRA_cdp_xff_val].to_f
51
+ end
52
+
53
+ Archive.new(cf, rows, pdpr, options)
54
+ end
55
+
56
+ end
57
+ end
data/lib/rrd/cdp.rb ADDED
@@ -0,0 +1,55 @@
1
+ module RRD
2
+ class CDP
3
+ CDP_val = 0
4
+ CDP_unkn_pdp_cnt = 1
5
+ CDP_hw_intercept = 2
6
+ CDP_hw_seasonal = 2
7
+ CDP_seasonal_deviation = 2
8
+ CDP_hw_last_intercept = 3
9
+ CDP_hw_last_seasonal = 3
10
+ CDP_last_seasonal_deviation = 3
11
+ CDP_hw_slope = 4
12
+ CDP_hw_last_slope = 5
13
+ CDP_null_count = 6
14
+ CDP_init_seasonal = 6
15
+ CDP_last_null_count = 7
16
+ CDP_primary_val = 8
17
+ CDP_secondary_val = 9
18
+
19
+ def initialize(options)
20
+ @options = options
21
+ end
22
+
23
+ def self.parse(reader, rra)
24
+ params = reader.read_uniparams(10)
25
+
26
+ options = {
27
+ primary_value: params[CDP_primary_val].to_f,
28
+ secondary_value: params[CDP_secondary_val].to_f
29
+ }
30
+
31
+ case rra.cf
32
+ when :hwpredict, :mhwpredict
33
+ options[:intercept] = params[CDP_hw_intercept].to_f
34
+ options[:last_intercept] = params[CDP_hw_intercept].to_f
35
+ options[:slope] = params[CDP_hw_intercept].to_f
36
+ options[:last_slope] = params[CDP_hw_intercept].to_f
37
+ options[:null_count] = params[CDP_hw_intercept].to_i
38
+ options[:last_null_count] = params[CDP_hw_intercept].to_i
39
+ when :seasonal
40
+ options[:seasonal] = params[CDP_hw_seasonal].to_f
41
+ options[:last_seasonal] = params[CDP_hw_last_seasonal].to_f
42
+ options[:init_seasonal] = params[CDP_init_seasonal].to_i
43
+ when :devseasonal
44
+ options[:seasonal_deviation] = params[CDP_seasonal_deviation].to_f
45
+ options[:last_seasonal_deviation] = params[CDP_last_seasonal_deviation].to_f
46
+ options[:init_seasonal] = params[CDP_init_seasonal].to_i
47
+ else
48
+ options[:value] = params[CDP_val].to_f
49
+ options[:unknown_datapoints] = params[CDP_unkn_pdp_cnt].to_i
50
+ end
51
+
52
+ new(options)
53
+ end
54
+ end
55
+ end
data/lib/rrd/data.rb ADDED
@@ -0,0 +1,113 @@
1
+ module RRD
2
+ class Data
3
+
4
+ attr_reader :reader, :rra, :header, :data_start
5
+
6
+ def initialize(rra, reader, header, data_start)
7
+ @rra = rra
8
+ @reader = reader
9
+ @header = header
10
+ @data_start = data_start
11
+
12
+ header.setup_reader(reader) # update endianess and alignment
13
+ end
14
+
15
+ def last_update
16
+ @last_update ||= round_time(header.last_update.to_i)
17
+ end
18
+
19
+ def last_row
20
+ reader.seek(base_position + rra.current_row * row_size)
21
+
22
+ [last_update] + reader.read(row_size, unpack_formula)
23
+ end
24
+
25
+ def fetch(options = {}, &block)
26
+ row = options[:start_row] || 0
27
+ rows = [options[:rows] || rra.rows, rra.rows].min
28
+
29
+ head =
30
+
31
+ last_update = round_time(header.last_update.to_i)
32
+ current_time = last_update - (rra.rows * step_time)
33
+
34
+ columns = [:time] + header.datasources.map(&:name).map(&:to_sym)
35
+
36
+ if options[:start_time]
37
+ start_time = round_time(options[:start_time].to_i)
38
+
39
+ if current_time < start_time
40
+ row = ((start_time - current_time) / step_time).floor
41
+ current_time = start_time
42
+ end
43
+ end
44
+
45
+ if options[:end_time]
46
+ end_time = round_time(options[:end_time].to_i)
47
+
48
+ if end_time < last_update
49
+ rows = ((end_time - current_time) / step_time).ceil
50
+ end
51
+ end
52
+
53
+ if row < head
54
+ reader.seek(base_position + ((rra.current_row + 1 + row) * row_size))
55
+ end
56
+
57
+ data = []
58
+ while row < rows
59
+ if head && row >= head
60
+ reader.seek(base_position + (row - head) * row_size) # jump to beginning
61
+ head = nil
62
+ end
63
+
64
+ current_time += step_time
65
+
66
+ row_data = [current_time] + reader.read(row_size, unpack_formula)
67
+
68
+ row_data = block.call(row_data, columns, options) if block
69
+
70
+ data << row_data
71
+
72
+ row += 1
73
+ end
74
+
75
+ data
76
+ end
77
+
78
+ private
79
+
80
+ def head
81
+ @head ||= rra.rows - (rra.current_row + 1)
82
+ end
83
+
84
+ def tail
85
+ rra.current_row
86
+ end
87
+
88
+ def unpack_formula
89
+ @unpack_formula ||= "D" * ds_count
90
+ end
91
+
92
+ def base_position
93
+ @base_position ||= data_start + rra.data_pointer
94
+ end
95
+
96
+ def step_time
97
+ @step_time ||= header.step * rra.pdpr
98
+ end
99
+
100
+ def row_size
101
+ @row_size ||= ds_count * 8
102
+ end
103
+
104
+ def ds_count
105
+ @ds_count ||= header.datasources.size
106
+ end
107
+
108
+ def round_time(time)
109
+ time - time % step_time
110
+ end
111
+
112
+ end
113
+ end
@@ -0,0 +1,36 @@
1
+ module RRD
2
+ class DataSource
3
+ PARAMS = [
4
+ :min_heartbeat, :min_value, :max_value
5
+ ]
6
+
7
+ attr_accessor :name, :type, :last_value, :last_ds, :unknown_seconds, :options
8
+
9
+ def initialize(name, type, options = {})
10
+ @name, @type = name, type
11
+ @options = options
12
+ end
13
+
14
+ def self.parse(reader)
15
+ name = reader.read_string(20, "A*")
16
+ type = reader.read_string(20, "A*")
17
+ params = Hash[PARAMS.zip(reader.read_uniparams(10))]
18
+
19
+ options = {
20
+ min_heartbeat: params[:min_heartbeat].to_i,
21
+ min_value: params[:min_value].to_f,
22
+ max_value: params[:max_value].to_f
23
+ }
24
+
25
+ DataSource.new(name, type, options)
26
+ end
27
+
28
+ def cdp
29
+ @cdp ||= []
30
+ end
31
+
32
+ def inspect
33
+ "<RRD::DataSource name='#{name}' type='#{type}' options='#{options}'>"
34
+ end
35
+ end
36
+ end
data/lib/rrd/file.rb ADDED
@@ -0,0 +1,31 @@
1
+ module RRD
2
+ class File
3
+ attr_reader :reader, :header, :data_start
4
+
5
+ def initialize(file)
6
+ @reader = RRD::Reader.new(file)
7
+ end
8
+
9
+ def close
10
+ @reader.close if @reader
11
+ end
12
+
13
+ def header
14
+ @header ||= begin
15
+ header = RRD::Header.parse(reader)
16
+
17
+ @data_start = reader.pos
18
+
19
+ header
20
+ end
21
+ end
22
+
23
+ def data(rra_index)
24
+ RRD::Data.new(header.rra[rra_index], reader, header, @data_start)
25
+ end
26
+
27
+ def fetch(rra_index, options ={}, &block)
28
+ data(rra_index).fetch(options, &block)
29
+ end
30
+ end
31
+ end
data/lib/rrd/header.rb ADDED
@@ -0,0 +1,117 @@
1
+ module RRD
2
+ class Header
3
+ COOKIE_STRING = "RRD\0"
4
+ COOKIE_DOUBLE = 8.642135E130
5
+
6
+ attr_reader :version, :last_update, :step, :endianess, :alignment
7
+
8
+ attr_reader :rra, :datasources
9
+
10
+ def initialize(options)
11
+ @version = options[:version] || "0003"
12
+ @last_update = options[:last_update] # || Time.now
13
+ @step = options[:step] || 10
14
+ @endianess = options[:endianess] || :little
15
+ @alignment = options[:alignment] || 4
16
+
17
+ @rra = options[:rra] || []
18
+ @datasources = options[:datasources] || []
19
+ end
20
+
21
+ def setup_reader(reader)
22
+ reader.endianess = endianess
23
+ reader.alignment = alignment
24
+ end
25
+
26
+ def self.parse(reader)
27
+ reader.seek(0) # rewind to start
28
+
29
+ read_cookie(reader)
30
+
31
+ options = {}
32
+
33
+ options[:version] = reader.read(5, "a*").first
34
+
35
+ read_double_cookie(reader, options)
36
+
37
+ datasources_count, rra_count, @step = reader.read(24, "QQQ")
38
+
39
+ global_params = reader.read_uniparams(10)
40
+
41
+ options[:datasources] = []
42
+ datasources_count.times do |i|
43
+ options[:datasources] << RRD::DataSource.parse(reader)
44
+ end
45
+
46
+ options[:rra] = []
47
+ rra_pointer = 0
48
+ rra_count.times do |i|
49
+ rra = RRD::Archive.parse(reader)
50
+
51
+ rra.data_pointer = rra_pointer
52
+ rra_pointer += rra.rows * datasources_count * 8
53
+
54
+ options[:rra] << rra
55
+ end
56
+
57
+ last_update, last_update_msec = reader.read(16, "QQ")
58
+
59
+ options[:last_update] = Time.at(last_update.to_f + (last_update_msec.to_f / 1000000))
60
+
61
+ datasources_count.times do |i|
62
+ datasource = options[:datasources][i]
63
+
64
+ datasource.last_ds = reader.read_string(30, "A*")
65
+ reader.align
66
+ params = reader.read_uniparams(10)
67
+ datasource.unknown_seconds = params[0].to_i
68
+ datasource.last_value = params[1].to_f
69
+ end
70
+
71
+ rra_count.times do |rra_index|
72
+ datasources_count.times do |ds_index|
73
+ cdp = RRD::CDP.parse(reader, options[:rra][rra_index])
74
+ options[:rra][rra_index].cdp[ds_index] = cdp
75
+ options[:datasources][ds_index].cdp[rra_index] = cdp
76
+ end
77
+ end
78
+
79
+ rra_count.times do |rra_index|
80
+ options[:rra][rra_index].current_row = reader.read(8, "Q").first
81
+ end
82
+
83
+ Header.new(options)
84
+ end
85
+
86
+ private
87
+
88
+ def self.read_cookie(reader)
89
+ raise "Invalid RRD file" unless reader.read_string(4) == COOKIE_STRING
90
+ end
91
+
92
+ def self.read_double_cookie(reader, options)
93
+ reader.read(3) # skip 32-bit alignment
94
+ part = reader.read_string(4) # 64-bit alignment or first part of double-cookie
95
+
96
+ if part == "\0\0\0\0" # RRD made on 64-bit system
97
+ cookie = reader.read_string(8)
98
+ options[:alignment] = 8
99
+ else # made on 32-bit system
100
+ cookie = part + reader.read_string(4)
101
+ options[:alignment] = 4
102
+ end
103
+
104
+ case COOKIE_DOUBLE
105
+ when cookie.unpack("G").first
106
+ options[:endianess] = :big
107
+ when cookie.unpack("E").first
108
+ options[:endianess] = :little
109
+ else
110
+ raise "Unknown architecture!"
111
+ end
112
+
113
+ reader.endianess = options[:endianess]
114
+ reader.alignment = options[:alignment]
115
+ end
116
+ end
117
+ end