innodb_ruby 0.9.16 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +5 -6
  3. data/bin/innodb_log +13 -18
  4. data/bin/innodb_space +377 -757
  5. data/lib/innodb.rb +4 -5
  6. data/lib/innodb/checksum.rb +26 -24
  7. data/lib/innodb/data_dictionary.rb +490 -550
  8. data/lib/innodb/data_type.rb +362 -326
  9. data/lib/innodb/field.rb +102 -89
  10. data/lib/innodb/fseg_entry.rb +22 -26
  11. data/lib/innodb/history.rb +21 -21
  12. data/lib/innodb/history_list.rb +72 -76
  13. data/lib/innodb/ibuf_bitmap.rb +36 -36
  14. data/lib/innodb/ibuf_index.rb +6 -2
  15. data/lib/innodb/index.rb +245 -276
  16. data/lib/innodb/inode.rb +154 -155
  17. data/lib/innodb/list.rb +191 -183
  18. data/lib/innodb/log.rb +139 -110
  19. data/lib/innodb/log_block.rb +100 -91
  20. data/lib/innodb/log_group.rb +53 -64
  21. data/lib/innodb/log_reader.rb +97 -96
  22. data/lib/innodb/log_record.rb +328 -279
  23. data/lib/innodb/lsn.rb +86 -81
  24. data/lib/innodb/page.rb +417 -414
  25. data/lib/innodb/page/blob.rb +82 -83
  26. data/lib/innodb/page/fsp_hdr_xdes.rb +174 -165
  27. data/lib/innodb/page/ibuf_bitmap.rb +34 -34
  28. data/lib/innodb/page/index.rb +964 -943
  29. data/lib/innodb/page/index_compressed.rb +34 -34
  30. data/lib/innodb/page/inode.rb +103 -112
  31. data/lib/innodb/page/sys.rb +13 -15
  32. data/lib/innodb/page/sys_data_dictionary_header.rb +81 -59
  33. data/lib/innodb/page/sys_ibuf_header.rb +45 -42
  34. data/lib/innodb/page/sys_rseg_header.rb +88 -82
  35. data/lib/innodb/page/trx_sys.rb +204 -182
  36. data/lib/innodb/page/undo_log.rb +106 -92
  37. data/lib/innodb/record.rb +121 -160
  38. data/lib/innodb/record_describer.rb +66 -68
  39. data/lib/innodb/space.rb +380 -418
  40. data/lib/innodb/stats.rb +33 -35
  41. data/lib/innodb/system.rb +149 -171
  42. data/lib/innodb/undo_log.rb +129 -107
  43. data/lib/innodb/undo_record.rb +255 -247
  44. data/lib/innodb/util/buffer_cursor.rb +81 -79
  45. data/lib/innodb/util/read_bits_at_offset.rb +2 -1
  46. data/lib/innodb/version.rb +2 -2
  47. data/lib/innodb/xdes.rb +144 -142
  48. metadata +80 -11
data/lib/innodb/inode.rb CHANGED
@@ -1,187 +1,186 @@
1
- # -*- encoding : utf-8 -*-
2
-
3
- class Innodb::Inode
4
- # The number of "slots" (each representing one page) in the fragment array
5
- # within each Inode entry.
6
- FRAG_ARRAY_N_SLOTS = 32 # FSP_EXTENT_SIZE / 2
7
-
8
- # The size (in bytes) of each slot in the fragment array.
9
- FRAG_SLOT_SIZE = 4
10
-
11
- # A magic number which helps determine if an Inode structure is in use
12
- # and populated with valid data.
13
- MAGIC_N_VALUE = 97937874
14
-
15
- # The size (in bytes) of an Inode entry.
16
- SIZE = (16 + (3 * Innodb::List::BASE_NODE_SIZE) +
17
- (FRAG_ARRAY_N_SLOTS * FRAG_SLOT_SIZE))
18
-
19
- # Read an array of page numbers (32-bit integers, which may be nil) from
20
- # the provided cursor.
21
- def self.page_number_array(size, cursor)
22
- size.times.map do |n|
23
- cursor.name("page[#{n}]") do |c|
24
- Innodb::Page.maybe_undefined(c.get_uint32)
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module Innodb
6
+ class Inode
7
+ extend Forwardable
8
+
9
+ Header = Struct.new(
10
+ :offset,
11
+ :fseg_id,
12
+ :not_full_n_used,
13
+ :free,
14
+ :not_full,
15
+ :full,
16
+ :magic_n,
17
+ :frag_array,
18
+ keyword_init: true
19
+ )
20
+
21
+ # The number of "slots" (each representing one page) in the fragment array
22
+ # within each Inode entry.
23
+ FRAG_ARRAY_N_SLOTS = 32 # FSP_EXTENT_SIZE / 2
24
+
25
+ # The size (in bytes) of each slot in the fragment array.
26
+ FRAG_SLOT_SIZE = 4
27
+
28
+ # A magic number which helps determine if an Inode structure is in use
29
+ # and populated with valid data.
30
+ MAGIC_N_VALUE = 97_937_874
31
+
32
+ # The size (in bytes) of an Inode entry.
33
+ SIZE = (16 + (3 * Innodb::List::BASE_NODE_SIZE) +
34
+ (FRAG_ARRAY_N_SLOTS * FRAG_SLOT_SIZE))
35
+
36
+ LISTS = %i[
37
+ free
38
+ not_full
39
+ full
40
+ ].freeze
41
+
42
+ # Read an array of page numbers (32-bit integers, which may be nil) from
43
+ # the provided cursor.
44
+ def self.page_number_array(size, cursor)
45
+ size.times.map do |n|
46
+ cursor.name("page[#{n}]") do |c|
47
+ Innodb::Page.maybe_undefined(c.read_uint32)
48
+ end
25
49
  end
26
50
  end
27
- end
28
51
 
29
- # Construct a new Inode by reading an FSEG header from a cursor.
30
- def self.new_from_cursor(space, cursor)
31
- data = {
32
- :offset => cursor.position,
33
- :fseg_id => cursor.name("fseg_id") {
34
- cursor.get_uint64
35
- },
36
- :not_full_n_used => cursor.name("not_full_n_used") {
37
- cursor.get_uint32
38
- },
39
- :free => cursor.name("list[free]") {
40
- Innodb::List::Xdes.new(space, Innodb::List.get_base_node(cursor))
41
- },
42
- :not_full => cursor.name("list[not_full]") {
43
- Innodb::List::Xdes.new(space, Innodb::List.get_base_node(cursor))
44
- },
45
- :full => cursor.name("list[full]") {
46
- Innodb::List::Xdes.new(space, Innodb::List.get_base_node(cursor))
47
- },
48
- :magic_n => cursor.name("magic_n") {
49
- cursor.get_uint32
50
- },
51
- :frag_array => cursor.name("frag_array") {
52
- page_number_array(FRAG_ARRAY_N_SLOTS, cursor)
53
- },
54
- }
55
-
56
- Innodb::Inode.new(space, data)
57
- end
58
-
59
- attr_accessor :space
52
+ # Construct a new Inode by reading an FSEG header from a cursor.
53
+ def self.new_from_cursor(space, cursor)
54
+ Innodb::Inode.new(
55
+ space,
56
+ Header.new(
57
+ offset: cursor.position,
58
+ fseg_id: cursor.name("fseg_id") { cursor.read_uint64 },
59
+ not_full_n_used: cursor.name("not_full_n_used") { cursor.read_uint32 },
60
+ free: cursor.name("list[free]") { Innodb::List::Xdes.new(space, Innodb::List.get_base_node(cursor)) },
61
+ not_full: cursor.name("list[not_full]") { Innodb::List::Xdes.new(space, Innodb::List.get_base_node(cursor)) },
62
+ full: cursor.name("list[full]") { Innodb::List::Xdes.new(space, Innodb::List.get_base_node(cursor)) },
63
+ magic_n: cursor.name("magic_n") { cursor.read_uint32 },
64
+ frag_array: cursor.name("frag_array") { page_number_array(FRAG_ARRAY_N_SLOTS, cursor) }
65
+ )
66
+ )
67
+ end
60
68
 
61
- def initialize(space, data)
62
- @space = space
63
- @data = data
64
- end
69
+ attr_accessor :space
70
+ attr_accessor :header
65
71
 
66
- def offset; @data[:offset]; end
67
- def fseg_id; @data[:fseg_id]; end
68
- def not_full_n_used; @data[:not_full_n_used]; end
69
- def free; @data[:free]; end
70
- def not_full; @data[:not_full]; end
71
- def full; @data[:full]; end
72
- def magic_n; @data[:magic_n]; end
73
- def frag_array; @data[:frag_array]; end
74
-
75
- def inspect
76
- "<%s space=%s, fseg=%i>" % [
77
- self.class.name,
78
- space.inspect,
79
- fseg_id,
80
- ]
81
- end
72
+ def initialize(space, header)
73
+ @space = space
74
+ @header = header
75
+ end
82
76
 
83
- # Helper method to determine if an Inode is in use. Inodes that are not in
84
- # use have an fseg_id of 0.
85
- def allocated?
86
- fseg_id != 0
87
- end
77
+ def_delegator :header, :offset
78
+ def_delegator :header, :fseg_id
79
+ def_delegator :header, :not_full_n_used
80
+ def_delegator :header, :free
81
+ def_delegator :header, :not_full
82
+ def_delegator :header, :full
83
+ def_delegator :header, :magic_n
84
+ def_delegator :header, :frag_array
85
+
86
+ def inspect
87
+ "<%s space=%s, fseg=%i>" % [
88
+ self.class.name,
89
+ space.inspect,
90
+ fseg_id,
91
+ ]
92
+ end
88
93
 
89
- # Helper method to return an array of only non-nil fragment pages.
90
- def frag_array_pages
91
- frag_array.select { |n| ! n.nil? }
92
- end
94
+ # Helper method to determine if an Inode is in use. Inodes that are not in
95
+ # use have an fseg_id of 0.
96
+ def allocated?
97
+ fseg_id != 0
98
+ end
93
99
 
94
- # Helper method to count non-nil fragment pages.
95
- def frag_array_n_used
96
- frag_array.inject(0) { |n, i| n += 1 if i; n }
97
- end
100
+ # Helper method to return an array of only non-nil fragment pages.
101
+ def frag_array_pages
102
+ frag_array.reject(&:nil?)
103
+ end
98
104
 
99
- # Calculate the total number of pages in use (not free) within this fseg.
100
- def used_pages
101
- frag_array_n_used + not_full_n_used +
102
- (full.length * @space.pages_per_extent)
103
- end
105
+ # Helper method to count non-nil fragment pages.
106
+ def frag_array_n_used
107
+ frag_array_pages.count
108
+ end
104
109
 
105
- # Calculate the total number of pages within this fseg.
106
- def total_pages
107
- frag_array_n_used +
108
- (free.length * @space.pages_per_extent) +
109
- (not_full.length * @space.pages_per_extent) +
110
- (full.length * @space.pages_per_extent)
111
- end
110
+ # Calculate the total number of pages in use (not free) within this fseg.
111
+ def used_pages
112
+ frag_array_n_used + not_full_n_used +
113
+ (full.length * @space.pages_per_extent)
114
+ end
112
115
 
113
- # Calculate the fill factor of this fseg, in percent.
114
- def fill_factor
115
- total_pages > 0 ? 100.0 * (used_pages.to_f / total_pages.to_f) : 0.0
116
- end
116
+ # Calculate the total number of pages within this fseg.
117
+ def total_pages
118
+ frag_array_n_used +
119
+ (free.length * @space.pages_per_extent) +
120
+ (not_full.length * @space.pages_per_extent) +
121
+ (full.length * @space.pages_per_extent)
122
+ end
117
123
 
118
- # Return an array of lists within an fseg.
119
- def lists
120
- [:free, :not_full, :full]
121
- end
124
+ # Calculate the fill factor of this fseg, in percent.
125
+ def fill_factor
126
+ total_pages.positive? ? 100.0 * (used_pages.to_f / total_pages) : 0.0
127
+ end
122
128
 
123
- # Return a list from the fseg, given its name as a symbol.
124
- def list(name)
125
- @data[name] if lists.include? name
126
- end
129
+ # Return a list from the fseg, given its name as a symbol.
130
+ def list(name)
131
+ return unless LISTS.include?(name)
127
132
 
128
- # Iterate through all lists, yielding the list name and the list itself.
129
- def each_list
130
- unless block_given?
131
- return enum_for(:each_list)
133
+ header[name]
132
134
  end
133
135
 
134
- lists.each do |name|
135
- yield name, list(name)
136
- end
136
+ # Iterate through all lists, yielding the list name and the list itself.
137
+ def each_list
138
+ return enum_for(:each_list) unless block_given?
137
139
 
138
- nil
139
- end
140
+ LISTS.each do |name|
141
+ yield name, list(name)
142
+ end
140
143
 
141
- # Iterate through the fragment array followed by all lists, yielding the
142
- # page number. This allows a convenient way to identify all pages that are
143
- # part of this inode.
144
- def each_page_number
145
- unless block_given?
146
- return enum_for(:each_page_number)
144
+ nil
147
145
  end
148
146
 
149
- frag_array_pages.each do |page_number|
150
- yield page_number
151
- end
147
+ # Iterate through the fragment array followed by all lists, yielding the
148
+ # page number. This allows a convenient way to identify all pages that are
149
+ # part of this inode.
150
+ def each_page_number(&block)
151
+ return enum_for(:each_page_number) unless block_given?
152
+
153
+ frag_array_pages.each(&block)
152
154
 
153
- each_list do |fseg_name, fseg_list|
154
- fseg_list.each do |xdes|
155
- xdes.each_page_status do |page_number|
156
- yield page_number
155
+ each_list do |_fseg_name, fseg_list|
156
+ fseg_list.each do |xdes|
157
+ xdes.each_page_status(&block)
157
158
  end
158
159
  end
160
+
161
+ nil
159
162
  end
160
163
 
161
- nil
162
- end
164
+ # Iterate through the page as associated with this inode using the
165
+ # each_page_number method, and yield the page number and page.
166
+ def each_page
167
+ return enum_for(:each_page) unless block_given?
163
168
 
164
- # Iterate through the page as associated with this inode using the
165
- # each_page_number method, and yield the page number and page.
166
- def each_page
167
- unless block_given?
168
- return enum_for(:each_page)
169
- end
169
+ each_page_number do |page_number|
170
+ yield page_number, space.page(page_number)
171
+ end
170
172
 
171
- each_page_number do |page_number|
172
- yield page_number, space.page(page_number)
173
+ nil
173
174
  end
174
175
 
175
- nil
176
- end
177
-
178
- # Compare one Innodb::Inode to another.
179
- def ==(other)
180
- fseg_id == other.fseg_id if other
181
- end
176
+ # Compare one Innodb::Inode to another.
177
+ def ==(other)
178
+ fseg_id == other.fseg_id if other
179
+ end
182
180
 
183
- # Dump a summary of this object for debugging purposes.
184
- def dump
185
- pp @data
181
+ # Dump a summary of this object for debugging purposes.
182
+ def dump
183
+ pp header
184
+ end
186
185
  end
187
186
  end
data/lib/innodb/list.rb CHANGED
@@ -1,233 +1,241 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  # An abstract InnoDB "free list" or FLST (renamed to just "list" here as it
4
4
  # frequently is used for structures that aren't free lists). This class must
5
5
  # be sub-classed to provide an appropriate #object_from_address method.
6
6
 
7
- class Innodb::List
8
- # An "address", which consists of a page number and byte offset within the
9
- # page. This points to the list "node" pointers (prev and next) of the
10
- # node, not necessarily the node object.
11
- ADDRESS_SIZE = 4 + 2
12
-
13
- # Read a node address from a cursor. Return nil if the address is an end
14
- # or "NULL" pointer (the page number is UINT32_MAX), or the address if
15
- # valid.
16
- def self.get_address(cursor)
17
- page = cursor.name("page") {
18
- Innodb::Page.maybe_undefined(cursor.get_uint32)
19
- }
20
- offset = cursor.name("offset") { cursor.get_uint16 }
21
- if page
22
- {
23
- :page => page,
24
- :offset => offset,
25
- }
7
+ module Innodb
8
+ class List
9
+ BaseNode = Struct.new(
10
+ :length, # rubocop:disable Lint/StructNewOverride
11
+ :first, # rubocop:disable Lint/StructNewOverride
12
+ :last,
13
+ keyword_init: true
14
+ )
15
+
16
+ Node = Struct.new(
17
+ :prev,
18
+ :next,
19
+ keyword_init: true
20
+ )
21
+
22
+ # An "address", which consists of a page number and byte offset within the
23
+ # page. This points to the list "node" pointers (prev and next) of the
24
+ # node, not necessarily the node object.
25
+ ADDRESS_SIZE = 4 + 2
26
+
27
+ # Read a node address from a cursor. Return nil if the address is an end
28
+ # or "NULL" pointer (the page number is UINT32_MAX), or the address if
29
+ # valid.
30
+ def self.get_address(cursor)
31
+ page = cursor.name("page") { Innodb::Page.maybe_undefined(cursor.read_uint32) }
32
+ offset = cursor.name("offset") { cursor.read_uint16 }
33
+
34
+ Innodb::Page::Address.new(page: page, offset: offset) if page
26
35
  end
27
- end
28
-
29
- # A list node consists of two addresses: the "previous" node address, and
30
- # the "next" node address.
31
- NODE_SIZE = 2 * ADDRESS_SIZE
32
-
33
- # Read a node, consisting of two consecutive addresses (:prev and :next)
34
- # from a cursor. Either address may be nil, indicating the end of the
35
- # linked list.
36
- def self.get_node(cursor)
37
- {
38
- :prev => cursor.name("prev") { get_address(cursor) },
39
- :next => cursor.name("next") { get_address(cursor) },
40
- }
41
- end
42
36
 
43
- # A list base node consists of a list length followed by two addresses:
44
- # the "first" node address, and the "last" node address.
45
- BASE_NODE_SIZE = 4 + (2 * ADDRESS_SIZE)
46
-
47
- # Read a base node, consisting of a list length followed by two addresses
48
- # (:first and :last) from a cursor. Either address may be nil. An empty list
49
- # has a :length of 0 and :first and :last are nil. A list with only a single
50
- # item will have a :length of 1 and :first and :last will point to the same
51
- # address.
52
- def self.get_base_node(cursor)
53
- {
54
- :length => cursor.name("length") { cursor.get_uint32 },
55
- :first => cursor.name("first") { get_address(cursor) },
56
- :last => cursor.name("last") { get_address(cursor) },
57
- }
58
- end
37
+ # A list node consists of two addresses: the "previous" node address, and
38
+ # the "next" node address.
39
+ NODE_SIZE = 2 * ADDRESS_SIZE
40
+
41
+ # Read a node, consisting of two consecutive addresses (:prev and :next)
42
+ # from a cursor. Either address may be nil, indicating the end of the
43
+ # linked list.
44
+ def self.get_node(cursor)
45
+ Node.new(
46
+ prev: cursor.name("prev") { get_address(cursor) },
47
+ next: cursor.name("next") { get_address(cursor) }
48
+ )
49
+ end
59
50
 
60
- def initialize(space, base)
61
- @space = space
62
- @base = base
63
- end
51
+ # A list base node consists of a list length followed by two addresses:
52
+ # the "first" node address, and the "last" node address.
53
+ BASE_NODE_SIZE = 4 + (2 * ADDRESS_SIZE)
54
+
55
+ # Read a base node, consisting of a list length followed by two addresses
56
+ # (:first and :last) from a cursor. Either address may be nil. An empty list
57
+ # has a :length of 0 and :first and :last are nil. A list with only a single
58
+ # item will have a :length of 1 and :first and :last will point to the same
59
+ # address.
60
+ def self.get_base_node(cursor)
61
+ BaseNode.new(
62
+ length: cursor.name("length") { cursor.read_uint32 },
63
+ first: cursor.name("first") { get_address(cursor) },
64
+ last: cursor.name("last") { get_address(cursor) }
65
+ )
66
+ end
64
67
 
65
- attr_reader :space
66
- attr_reader :base
68
+ attr_reader :space
69
+ attr_reader :base
67
70
 
68
- # Abstract #object_from_address method which must be implemented by
69
- # sub-classes in order to return a useful object given an object address.
70
- def object_from_address(address)
71
- raise "#{self.class} must implement object_from_address"
72
- end
71
+ def initialize(space, base)
72
+ @space = space
73
+ @base = base
74
+ end
73
75
 
74
- # Return the object pointed to by the "previous" address pointer of the
75
- # provided object.
76
- def prev(object)
77
- unless object.respond_to? :prev_address
78
- raise "Class #{object.class} does not respond to prev_address"
76
+ # Abstract #object_from_address method which must be implemented by
77
+ # sub-classes in order to return a useful object given an object address.
78
+ def object_from_address(_address)
79
+ raise "#{self.class} must implement object_from_address"
79
80
  end
80
81
 
81
- object_from_address(object.prev_address)
82
- end
82
+ # Return the object pointed to by the "previous" address pointer of the
83
+ # provided object.
84
+ def prev(object)
85
+ raise "Class #{object.class} does not respond to prev_address" unless object.respond_to?(:prev_address)
83
86
 
84
- # Return the object pointed to by the "next" address pointer of the
85
- # provided object.
86
- def next(object)
87
- unless object.respond_to? :next_address
88
- raise "Class #{object.class} does not respond to next_address"
87
+ object_from_address(object.prev_address)
89
88
  end
90
89
 
91
- object_from_address(object.next_address)
92
- end
90
+ # Return the object pointed to by the "next" address pointer of the
91
+ # provided object.
92
+ def next(object)
93
+ raise "Class #{object.class} does not respond to next_address" unless object.respond_to?(:next_address)
93
94
 
94
- # Return the number of items in the list.
95
- def length
96
- @base[:length]
97
- end
95
+ object_from_address(object.next_address)
96
+ end
98
97
 
99
- # Return the first object in the list using the list base node "first"
100
- # address pointer.
101
- def first
102
- object_from_address(@base[:first])
103
- end
98
+ # Return the number of items in the list.
99
+ def length
100
+ @base.length
101
+ end
104
102
 
105
- # Return the first object in the list using the list base node "last"
106
- # address pointer.
107
- def last
108
- object_from_address(@base[:last])
109
- end
103
+ # Return the first object in the list using the list base node "first"
104
+ # address pointer.
105
+ def first
106
+ object_from_address(@base.first)
107
+ end
110
108
 
111
- # Return a list cursor for the list.
112
- def list_cursor(node=:min, direction=:forward)
113
- ListCursor.new(self, node, direction)
114
- end
109
+ # Return the first object in the list using the list base node "last"
110
+ # address pointer.
111
+ def last
112
+ object_from_address(@base.last)
113
+ end
115
114
 
116
- # Return whether the given item is present in the list. This depends on the
117
- # item and the items in the list implementing some sufficient == method.
118
- # This is implemented rather inefficiently by constructing an array and
119
- # leaning on Array#include? to do the real work.
120
- def include?(item)
121
- each.to_a.include? item
122
- end
115
+ # Return a list cursor for the list.
116
+ def list_cursor(node = :min, direction = :forward)
117
+ ListCursor.new(self, node, direction)
118
+ end
123
119
 
124
- # Iterate through all nodes in the list.
125
- def each
126
- unless block_given?
127
- return enum_for(:each)
120
+ # Return whether the given item is present in the list. This depends on the
121
+ # item and the items in the list implementing some sufficient == method.
122
+ # This is implemented rather inefficiently by constructing an array and
123
+ # leaning on Array#include? to do the real work.
124
+ def include?(item)
125
+ each.to_a.include?(item)
128
126
  end
129
127
 
130
- list_cursor.each_node do |node|
131
- yield node
128
+ # Iterate through all nodes in the list.
129
+ def each(&block)
130
+ return enum_for(:each) unless block_given?
131
+
132
+ list_cursor.each_node(&block)
132
133
  end
133
- end
134
134
 
135
- # A list iteration cursor used primarily by the Innodb::List #cursor method
136
- # implicitly. Keeps its own state for iterating through lists efficiently.
137
- class ListCursor
138
- def initialize(list, node=:min, direction=:forward)
139
- @initial = true
140
- @list = list
141
- @direction = direction
142
-
143
- case node
144
- when :min
145
- @node = @list.first
146
- when :max
147
- @node = @list.last
148
- else
149
- @node = node
135
+ # A list iteration cursor used primarily by the Innodb::List #cursor method
136
+ # implicitly. Keeps its own state for iterating through lists efficiently.
137
+ class ListCursor
138
+ def initialize(list, node = :min, direction = :forward)
139
+ @initial = true
140
+ @list = list
141
+ @direction = direction
142
+ @node = initial_node(node)
150
143
  end
151
- end
152
144
 
153
- def node
154
- if @initial
155
- @initial = false
156
- return @node
145
+ def initial_node(node)
146
+ case node
147
+ when :min
148
+ @list.first
149
+ when :max
150
+ @list.last
151
+ else
152
+ node
153
+ end
157
154
  end
158
155
 
159
- case @direction
160
- when :forward
161
- next_node
162
- when :backward
163
- prev_node
156
+ def node
157
+ if @initial
158
+ @initial = false
159
+ return @node
160
+ end
161
+
162
+ case @direction
163
+ when :forward
164
+ next_node
165
+ when :backward
166
+ prev_node
167
+ end
164
168
  end
165
- end
166
169
 
167
- # Return the previous entry from the current position, and advance the
168
- # cursor position to the returned entry. If the cursor is currently nil,
169
- # return the last entry in the list and adjust the cursor position to
170
- # that entry.
171
- def prev_node
172
- if node = @list.prev(@node)
173
- @node = node
170
+ def goto_node(node)
171
+ @node = node if node
174
172
  end
175
- end
176
173
 
177
- # Return the next entry from the current position, and advance the
178
- # cursor position to the returned entry. If the cursor is currently nil,
179
- # return the first entry in the list and adjust the cursor position to
180
- # that entry.
181
- def next_node
182
- if node = @list.next(@node)
183
- @node = node
174
+ # Return the previous entry from the current position, and advance the
175
+ # cursor position to the returned entry. If the cursor is currently nil,
176
+ # return the last entry in the list and adjust the cursor position to
177
+ # that entry.
178
+ def prev_node
179
+ goto_node(@list.prev(@node))
184
180
  end
185
- end
186
181
 
187
- def each_node
188
- unless block_given?
189
- return enum_for(:each_node)
182
+ # Return the next entry from the current position, and advance the
183
+ # cursor position to the returned entry. If the cursor is currently nil,
184
+ # return the first entry in the list and adjust the cursor position to
185
+ # that entry.
186
+ def next_node
187
+ goto_node(@list.next(@node))
190
188
  end
191
189
 
192
- while n = node
193
- yield n
190
+ def each_node
191
+ return enum_for(:each_node) unless block_given?
192
+
193
+ while (n = node)
194
+ yield n
195
+ end
194
196
  end
195
197
  end
196
- end
197
- end
198
198
 
199
- # A list of extent descriptor entries. Objects returned by list methods
200
- # will be Innodb::Xdes objects.
201
- class Innodb::List::Xdes < Innodb::List
202
- def object_from_address(address)
203
- if address && page = @space.page(address[:page])
204
- Innodb::Xdes.new(page, page.cursor(address[:offset] - 8))
199
+ # A list of extent descriptor entries. Objects returned by list methods
200
+ # will be Innodb::Xdes objects.
201
+ class Xdes < Innodb::List
202
+ def object_from_address(address)
203
+ return unless address
204
+
205
+ page = @space.page(address.page)
206
+ return unless page
207
+
208
+ Innodb::Xdes.new(page, page.cursor(address.offset - 8))
209
+ end
205
210
  end
206
- end
207
- end
208
211
 
209
- # A list of Inode pages. Objects returned by list methods will be
210
- # Innodb::Page::Inode objects.
211
- class Innodb::List::Inode < Innodb::List
212
- def object_from_address(address)
213
- if address && page = @space.page(address[:page])
214
- page
212
+ # A list of Inode pages. Objects returned by list methods will be
213
+ # Innodb::Page::Inode objects.
214
+ class Inode < Innodb::List
215
+ def object_from_address(address)
216
+ return unless address
217
+
218
+ @space.page(address.page)
219
+ end
215
220
  end
216
- end
217
- end
218
221
 
219
- class Innodb::List::UndoPage < Innodb::List
220
- def object_from_address(address)
221
- if address && page = @space.page(address[:page])
222
- page
222
+ class UndoPage < Innodb::List
223
+ def object_from_address(address)
224
+ return unless address
225
+
226
+ @space.page(address.page)
227
+ end
223
228
  end
224
- end
225
- end
226
229
 
227
- class Innodb::List::History < Innodb::List
228
- def object_from_address(address)
229
- if address && page = @space.page(address[:page])
230
- Innodb::UndoLog.new(page, address[:offset] - 34)
230
+ class History < Innodb::List
231
+ def object_from_address(address)
232
+ return unless address
233
+
234
+ page = @space.page(address.page)
235
+ return unless page
236
+
237
+ Innodb::UndoLog.new(page, address.offset - 34)
238
+ end
231
239
  end
232
240
  end
233
241
  end