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
@@ -1,4 +1,4 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "bindata"
4
4
 
@@ -17,7 +17,7 @@ class BufferCursor
17
17
  attr_accessor :direction
18
18
  attr_accessor :name
19
19
 
20
- def initialize(cursor, position=0, direction=:forward, name=nil)
20
+ def initialize(cursor, position = 0, direction = :forward, name = nil)
21
21
  @cursor = cursor
22
22
  @position = position
23
23
  @direction = direction
@@ -37,21 +37,25 @@ class BufferCursor
37
37
  end
38
38
  end
39
39
 
40
- @@global_tracing = false
40
+ @global_tracing = false
41
41
 
42
42
  # Enable tracing for all BufferCursor objects globally.
43
- def self.trace!(arg=true)
44
- @@global_tracing = arg
43
+ def self.trace!(arg = true) # rubocop:disable Style/OptionalBooleanParameter
44
+ @global_tracing = arg
45
+ end
46
+
47
+ def self.global_tracing?
48
+ @global_tracing
45
49
  end
46
50
 
47
51
  # Initialize a cursor within a buffer at the given position.
48
52
  def initialize(buffer, position)
49
53
  @buffer = buffer
50
- @stack = [ StackEntry.new(self, position) ]
54
+ @stack = [StackEntry.new(self, position)]
51
55
 
52
56
  trace false
53
57
  trace_with :print_trace
54
- trace_to STDOUT
58
+ trace_to $stdout
55
59
  end
56
60
 
57
61
  def inspect
@@ -62,46 +66,49 @@ class BufferCursor
62
66
  ]
63
67
  end
64
68
 
65
- def trace(arg=true)
69
+ def trace(arg = true) # rubocop:disable Style/OptionalBooleanParameter
66
70
  @instance_tracing = arg
71
+
67
72
  self
68
73
  end
69
74
 
70
75
  # Print a trace output for this cursor. The method is passed a cursor object,
71
76
  # position, raw byte buffer, and array of names.
72
- def print_trace(cursor, position, bytes, name)
77
+ def print_trace(_cursor, position, bytes, name)
73
78
  slice_size = 16
74
79
  bytes.each_slice(slice_size).each_with_index do |slice_bytes, slice_count|
75
80
  @trace_io.puts "%06i %s %-32s %s" % [
76
81
  position + (slice_count * slice_size),
77
82
  direction == :backward ? "←" : "→",
78
83
  slice_bytes.map { |n| "%02x" % n }.join,
79
- slice_count == 0 ? name.join(".") : "↵",
84
+ slice_count.zero? ? name.join(".") : "↵",
80
85
  ]
81
86
  end
82
87
  end
83
88
 
84
89
  def trace_to(file)
85
90
  @trace_io = file
91
+
86
92
  self
87
93
  end
88
94
 
89
95
  # Set a Proc or method on self to trace with.
90
- def trace_with(arg=nil)
96
+ def trace_with(arg = nil)
91
97
  if arg.nil?
92
98
  @trace_proc = nil
93
- elsif arg.class == Proc
99
+ elsif arg.instance_of?(Proc)
94
100
  @trace_proc = arg
95
- elsif arg.class == Symbol
96
- @trace_proc = lambda { |cursor, position, bytes, name| self.send(arg, cursor, position, bytes, name) }
101
+ elsif arg.instance_of?(Symbol)
102
+ @trace_proc = ->(cursor, position, bytes, name) { send(arg, cursor, position, bytes, name) }
97
103
  else
98
104
  raise "Don't know how to trace with #{arg}"
99
105
  end
106
+
100
107
  self
101
108
  end
102
109
 
103
110
  def tracing_enabled?
104
- (@@global_tracing or @instance_tracing) && @trace_proc
111
+ (self.class.global_tracing? || @instance_tracing) && @trace_proc
105
112
  end
106
113
 
107
114
  # Generate a trace record from the current cursor.
@@ -123,14 +130,10 @@ class BufferCursor
123
130
  end
124
131
 
125
132
  # Set the field name.
126
- def name(name_arg=nil)
127
- if name_arg.nil?
128
- return current.name
129
- end
133
+ def name(name_arg = nil)
134
+ return current.name if name_arg.nil?
130
135
 
131
- unless block_given?
132
- raise "No block given"
133
- end
136
+ raise "No block given" unless block_given?
134
137
 
135
138
  current.name.push name_arg
136
139
  ret = yield(self)
@@ -139,12 +142,11 @@ class BufferCursor
139
142
  end
140
143
 
141
144
  # Return the direction of the current cursor.
142
- def direction(direction_arg=nil)
143
- if direction_arg.nil?
144
- return current.direction
145
- end
145
+ def direction(direction_arg = nil)
146
+ return current.direction if direction_arg.nil?
146
147
 
147
148
  current.direction = direction_arg
149
+
148
150
  self
149
151
  end
150
152
 
@@ -166,34 +168,40 @@ class BufferCursor
166
168
  # Move the current cursor to a new absolute position.
167
169
  def seek(position)
168
170
  current.position = position if position
171
+
169
172
  self
170
173
  end
171
174
 
172
175
  # Adjust the current cursor to a new relative position.
173
176
  def adjust(relative_position)
174
177
  current.position += relative_position
178
+
175
179
  self
176
180
  end
177
181
 
178
182
  # Save the current cursor position and start a new (nested, stacked) cursor.
179
- def push(position=nil)
183
+ def push(position = nil)
180
184
  @stack.push current.dup
181
185
  seek(position)
186
+
182
187
  self
183
188
  end
184
189
 
185
190
  # Restore the last cursor position.
186
191
  def pop
187
192
  raise "No cursors to pop" unless @stack.size > 1
193
+
188
194
  @stack.pop
195
+
189
196
  self
190
197
  end
191
198
 
192
199
  # Execute a block and restore the cursor to the previous position after
193
200
  # the block returns. Return the block's return value after restoring the
194
201
  # cursor. Optionally seek to provided position before executing block.
195
- def peek(position=nil)
202
+ def peek(position = nil)
196
203
  raise "No block given" unless block_given?
204
+
197
205
  push(position)
198
206
  result = yield(self)
199
207
  pop
@@ -219,105 +227,101 @@ class BufferCursor
219
227
  end
220
228
 
221
229
  # Return raw bytes.
222
- def get_bytes(length)
230
+ def read_bytes(length)
223
231
  read_and_advance(length)
224
232
  end
225
233
 
226
234
  # Return a null-terminated string.
227
- def get_string(length)
235
+ def read_string(length)
228
236
  BinData::Stringz.read(read_and_advance(length))
229
237
  end
230
238
 
231
239
  # Iterate through length bytes returning each as an unsigned 8-bit integer.
232
- def each_byte_as_uint8(length)
233
- unless block_given?
234
- return enum_for(:each_byte_as_uint8, length)
235
- end
240
+ def each_byte_as_uint8(length, &block)
241
+ return enum_for(:each_byte_as_uint8, length) unless block_given?
236
242
 
237
- read_and_advance(length).bytes.each do |byte|
238
- yield byte
239
- end
243
+ read_and_advance(length).bytes.each(&block)
240
244
 
241
245
  nil
242
246
  end
243
247
 
244
248
  # Return raw bytes as hex.
245
- def get_hex(length)
249
+ def read_hex(length)
246
250
  read_and_advance(length).bytes.map { |c| "%02x" % c }.join
247
251
  end
248
252
 
249
253
  # Read an unsigned 8-bit integer.
250
- def get_uint8(position=nil)
254
+ def read_uint8(position = nil)
251
255
  seek(position)
252
256
  data = read_and_advance(1)
253
257
  BinData::Uint8.read(data).to_i
254
258
  end
255
259
 
256
260
  # Read a big-endian unsigned 16-bit integer.
257
- def get_uint16(position=nil)
261
+ def read_uint16(position = nil)
258
262
  seek(position)
259
263
  data = read_and_advance(2)
260
264
  BinData::Uint16be.read(data).to_i
261
265
  end
262
266
 
263
267
  # Read a big-endian signed 16-bit integer.
264
- def get_sint16(position=nil)
268
+ def read_sint16(position = nil)
265
269
  seek(position)
266
270
  data = read_and_advance(2)
267
271
  BinData::Int16be.read(data).to_i
268
272
  end
269
273
 
270
274
  # Read a big-endian unsigned 24-bit integer.
271
- def get_uint24(position=nil)
275
+ def read_uint24(position = nil)
272
276
  seek(position)
273
277
  data = read_and_advance(3)
274
278
  BinData::Uint24be.read(data).to_i
275
279
  end
276
280
 
277
281
  # Read a big-endian unsigned 32-bit integer.
278
- def get_uint32(position=nil)
282
+ def read_uint32(position = nil)
279
283
  seek(position)
280
284
  data = read_and_advance(4)
281
285
  BinData::Uint32be.read(data).to_i
282
286
  end
283
287
 
284
288
  # Read a big-endian unsigned 48-bit integer.
285
- def get_uint48(position=nil)
289
+ def read_uint48(position = nil)
286
290
  seek(position)
287
291
  data = read_and_advance(6)
288
292
  BinData::Uint48be.read(data).to_i
289
293
  end
290
294
 
291
295
  # Read a big-endian unsigned 64-bit integer.
292
- def get_uint64(position=nil)
296
+ def read_uint64(position = nil)
293
297
  seek(position)
294
298
  data = read_and_advance(8)
295
299
  BinData::Uint64be.read(data).to_i
296
300
  end
297
301
 
298
302
  # Read a big-endian unsigned integer given its size in bytes.
299
- def get_uint_by_size(size)
303
+ def read_uint_by_size(size)
300
304
  case size
301
305
  when 1
302
- get_uint8
306
+ read_uint8
303
307
  when 2
304
- get_uint16
308
+ read_uint16
305
309
  when 3
306
- get_uint24
310
+ read_uint24
307
311
  when 4
308
- get_uint32
312
+ read_uint32
309
313
  when 6
310
- get_uint48
314
+ read_uint48
311
315
  when 8
312
- get_uint64
316
+ read_uint64
313
317
  else
314
318
  raise "Integer size #{size} not implemented"
315
319
  end
316
320
  end
317
321
 
318
322
  # Read an array of count unsigned integers given their size in bytes.
319
- def get_uint_array_by_size(size, count)
320
- (0...count).to_a.inject([]) { |a, n| a << get_uint_by_size(size); a }
323
+ def read_uint_array_by_size(size, count)
324
+ count.times.map { read_uint_by_size(size) }
321
325
  end
322
326
 
323
327
  # Read an InnoDB-compressed unsigned 32-bit integer (1-5 bytes).
@@ -327,30 +331,28 @@ class BufferCursor
327
331
  # flag for integers >= 0xf0000000.
328
332
  #
329
333
  # Optionally accept a flag (first byte) if it has already been read (as is
330
- # the case in get_imc_uint64).
331
- def get_ic_uint32(flag=nil)
332
- name("ic_uint32") {
333
- if !flag
334
- flag = peek { name("uint8_or_flag") { get_uint8 } }
335
- end
334
+ # the case in read_imc_uint64).
335
+ def read_ic_uint32(flag = nil)
336
+ name("ic_uint32") do
337
+ flag ||= peek { name("uint8_or_flag") { read_uint8 } }
336
338
 
337
339
  case
338
340
  when flag < 0x80
339
341
  adjust(+1)
340
342
  flag
341
343
  when flag < 0xc0
342
- name("uint16") { get_uint16 } & 0x7fff
344
+ name("uint16") { read_uint16 } & 0x7fff
343
345
  when flag < 0xe0
344
- name("uint24") { get_uint24 } & 0x3fffff
346
+ name("uint24") { read_uint24 } & 0x3fffff
345
347
  when flag < 0xf0
346
- name("uint32") { get_uint32 } & 0x1fffffff
348
+ name("uint32") { read_uint32 } & 0x1fffffff
347
349
  when flag == 0xf0
348
350
  adjust(+1) # Skip the flag byte.
349
- name("uint32+1") { get_uint32 }
351
+ name("uint32+1") { read_uint32 }
350
352
  else
351
- raise "Invalid flag #{flag.to_s} seen"
353
+ raise "Invalid flag #{flag} seen"
352
354
  end
353
- }
355
+ end
354
356
  end
355
357
 
356
358
  # Read an InnoDB-compressed unsigned 64-bit integer (5-9 bytes).
@@ -359,13 +361,13 @@ class BufferCursor
359
361
  # integer (1-5 bytes) while the low 32 bits are stored as a standard
360
362
  # big-endian 32-bit integer (4 bytes). This makes a combined size of
361
363
  # between 5 and 9 bytes.
362
- def get_ic_uint64
363
- name("ic_uint64") {
364
- high = name("high") { get_ic_uint32 }
365
- low = name("low") { name("uint32") { get_uint32 } }
364
+ def read_ic_uint64
365
+ name("ic_uint64") do
366
+ high = name("high") { read_ic_uint32 }
367
+ low = name("low") { name("uint32") { read_uint32 } }
366
368
 
367
369
  (high << 32) | low
368
- }
370
+ end
369
371
  end
370
372
 
371
373
  # Read an InnoDB-"much compressed" unsigned 64-bit integer (1-11 bytes).
@@ -376,31 +378,31 @@ class BufferCursor
376
378
  # is also a flag) of the low 32 bits of the value, also as an InnoDB-
377
379
  # compressed 32-bit unsigned integer. This makes for a combined size
378
380
  # of between 1 and 11 bytes.
379
- def get_imc_uint64
380
- name("imc_uint64") {
381
+ def read_imc_uint64
382
+ name("imc_uint64") do
381
383
  high = 0
382
- flag = peek { name("uint8_or_flag") { get_uint8 } }
384
+ flag = peek { name("uint8_or_flag") { read_uint8 } }
383
385
 
384
386
  if flag == 0xff
385
387
  # The high 32-bits are stored first as an ic_uint32.
386
388
  adjust(+1) # Skip the flag byte.
387
- high = name("high") { get_ic_uint32 }
389
+ high = name("high") { read_ic_uint32 }
388
390
  flag = nil
389
391
  end
390
392
 
391
393
  # The low 32-bits are stored as an ic_uint32; pass the flag we already
392
394
  # read, so we don't have to read it again.
393
- low = name("low") { get_ic_uint32(flag) }
395
+ low = name("low") { read_ic_uint32(flag) }
394
396
 
395
397
  (high << 32) | low
396
- }
398
+ end
397
399
  end
398
400
 
399
401
  # Read an array of 1-bit integers.
400
- def get_bit_array(num_bits)
402
+ def read_bit_array(num_bits)
401
403
  size = (num_bits + 7) / 8
402
404
  data = read_and_advance(size)
403
- bit_array = BinData::Array.new(:type => :bit1, :initial_length => size * 8)
405
+ bit_array = BinData::Array.new(type: :bit1, initial_length: size * 8)
404
406
  bit_array.read(data).to_ary
405
407
  end
406
408
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ReadBitsAtOffset
2
4
  # Read a given number of bits from an integer at a specific bit offset. The
3
5
  # value returned is 0-based so does not need further shifting or adjustment.
@@ -5,4 +7,3 @@ module ReadBitsAtOffset
5
7
  ((data & (((1 << bits) - 1) << offset)) >> offset)
6
8
  end
7
9
  end
8
-
@@ -1,5 +1,5 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Innodb
4
- VERSION = "0.9.16"
4
+ VERSION = "0.11.0"
5
5
  end
data/lib/innodb/xdes.rb CHANGED
@@ -1,166 +1,168 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
2
4
 
3
5
  # An InnoDB "extent descriptor entry" or "+XDES+". These structures are used
4
6
  # in the +XDES+ entry array contained in +FSP_HDR+ and +XDES+ pages.
5
7
  #
6
8
  # Note the distinction between +XDES+ _entries_ and +XDES+ _pages_.
7
- class Innodb::Xdes
8
- # Number of bits per page in the +XDES+ entry bitmap field. Currently
9
- # +XDES+ entries store two bits per page, with the following meanings:
10
- #
11
- # * 1 = free (the page is free, or not in use)
12
- # * 2 = clean (currently unused, always 1 when initialized)
13
- BITS_PER_PAGE = 2
14
-
15
- # The bit value for a free page.
16
- BITMAP_BV_FREE = 1
17
-
18
- # The bit value for a clean page (currently unused in InnoDB).
19
- BITMAP_BV_CLEAN = 2
20
-
21
- # The bitwise-OR of all bitmap bit values.
22
- BITMAP_BV_ALL = (BITMAP_BV_FREE | BITMAP_BV_CLEAN)
23
-
24
- # The values used in the +:state+ field indicating what the extent is
25
- # used for (or what list it is on).
26
- STATES = {
27
- 1 => :free, # The extent is completely empty and unused, and should
28
- # be present on the filespace's FREE list.
29
-
30
- 2 => :free_frag, # Some pages of the extent are used individually, and
31
- # the extent should be present on the filespace's
32
- # FREE_FRAG list.
33
-
34
- 3 => :full_frag, # All pages of the extent are used individually, and
35
- # the extent should be present on the filespace's
36
- # FULL_FRAG list.
37
-
38
- 4 => :fseg, # The extent is wholly allocated to a file segment.
39
- # Additional information about the state of this extent
40
- # can be derived from the its presence on particular
41
- # file segment lists (FULL, NOT_FULL, or FREE).
42
- }
43
-
44
- def initialize(page, cursor)
45
- @page = page
46
- @xdes = read_xdes_entry(page, cursor)
47
- end
9
+ module Innodb
10
+ class Xdes
11
+ # An XDES entry structure.
12
+ Entry = Struct.new(:offset, :start_page, :end_page, :fseg_id, :this, :list, :state, :bitmap, keyword_init: true)
13
+
14
+ PageStatus = Struct.new(:free, :clean, keyword_init: true)
15
+
16
+ # Number of bits per page in the +XDES+ entry bitmap field. Currently
17
+ # +XDES+ entries store two bits per page, with the following meanings:
18
+ #
19
+ # * 1 = free (the page is free, or not in use)
20
+ # * 2 = clean (currently unused, always 1 when initialized)
21
+ BITS_PER_PAGE = 2
22
+
23
+ # The bit value for a free page.
24
+ BITMAP_BV_FREE = 1
25
+
26
+ # The bit value for a clean page (currently unused in InnoDB).
27
+ BITMAP_BV_CLEAN = 2
28
+
29
+ # The bitwise-OR of all bitmap bit values.
30
+ BITMAP_BV_ALL = (BITMAP_BV_FREE | BITMAP_BV_CLEAN)
31
+
32
+ # The values used in the +:state+ field indicating what the extent is
33
+ # used for (or what list it is on).
34
+ STATES = {
35
+ # The extent is completely empty and unused, and should be present on the filespace's FREE list.
36
+ 1 => :free,
37
+
38
+ # Some pages of the extent are used individually, and the extent should be present on the filespace's
39
+ # FREE_FRAG list.
40
+ 2 => :free_frag,
41
+
42
+ # All pages of the extent are used individually, and the extent should be present on the filespace's
43
+ # FULL_FRAG list.
44
+ 3 => :full_frag,
45
+
46
+ # The extent is wholly allocated to a file segment. Additional information about the state of this extent can
47
+ # be derived from the its presence on particular file segment lists (FULL, NOT_FULL, or FREE).
48
+ 4 => :fseg,
49
+ }.freeze
50
+
51
+ attr_reader :xdes
52
+
53
+ extend Forwardable
54
+
55
+ def_delegator :xdes, :offset
56
+ def_delegator :xdes, :start_page
57
+ def_delegator :xdes, :end_page
58
+ def_delegator :xdes, :fseg_id
59
+ def_delegator :xdes, :this
60
+ def_delegator :xdes, :list
61
+ def_delegator :xdes, :state
62
+ def_delegator :xdes, :bitmap
63
+
64
+ def initialize(page, cursor)
65
+ @page = page
66
+ @xdes = read_xdes_entry(page, cursor)
67
+ end
48
68
 
49
- # Size (in bytes) of the bitmap field in the +XDES+ entry.
50
- def size_bitmap
51
- (@page.space.pages_per_extent * BITS_PER_PAGE) / 8
52
- end
69
+ # Size (in bytes) of the bitmap field in the +XDES+ entry.
70
+ def size_bitmap
71
+ (@page.space.pages_per_extent * BITS_PER_PAGE) / 8
72
+ end
53
73
 
54
- # Size (in bytes) of the an +XDES+ entry.
55
- def size_entry
56
- 8 + Innodb::List::NODE_SIZE + 4 + size_bitmap
57
- end
74
+ # Size (in bytes) of the an +XDES+ entry.
75
+ def size_entry
76
+ 8 + Innodb::List::NODE_SIZE + 4 + size_bitmap
77
+ end
58
78
 
59
- # Read an XDES entry from a cursor.
60
- def read_xdes_entry(page, cursor)
61
- extent_number = (cursor.position - page.pos_xdes_array) / size_entry
62
- start_page = page.offset + (extent_number * page.space.pages_per_extent)
63
- cursor.name("xdes[#{extent_number}]") do |c|
64
- {
65
- :offset => c.position,
66
- :start_page => start_page,
67
- :end_page => start_page + page.space.pages_per_extent - 1,
68
- :fseg_id => c.name("fseg_id") { c.get_uint64 },
69
- :this => {:page => page.offset, :offset => c.position},
70
- :list => c.name("list") { Innodb::List.get_node(c) },
71
- :state => c.name("state") { STATES[c.get_uint32] },
72
- :bitmap => c.name("bitmap") { c.get_bytes(size_bitmap) },
73
- }
79
+ # Read an XDES entry from a cursor.
80
+ def read_xdes_entry(page, cursor)
81
+ extent_number = (cursor.position - page.pos_xdes_array) / size_entry
82
+ start_page = page.offset + (extent_number * page.space.pages_per_extent)
83
+ cursor.name("xdes[#{extent_number}]") do |c|
84
+ Entry.new(
85
+ offset: c.position,
86
+ start_page: start_page,
87
+ end_page: start_page + page.space.pages_per_extent - 1,
88
+ fseg_id: c.name("fseg_id") { c.read_uint64 },
89
+ this: Innodb::Page::Address.new(page: page.offset, offset: c.position),
90
+ list: c.name("list") { Innodb::List.get_node(c) },
91
+ state: c.name("state") { STATES[c.read_uint32] },
92
+ bitmap: c.name("bitmap") { c.read_bytes(size_bitmap) }
93
+ )
94
+ end
74
95
  end
75
- end
76
96
 
77
- # Return the stored extent descriptor entry.
78
- def xdes
79
- @xdes
80
- end
97
+ # Return whether this XDES entry is allocated to an fseg (the whole extent
98
+ # then belongs to the fseg).
99
+ def allocated_to_fseg?
100
+ fseg_id != 0
101
+ end
81
102
 
82
- def offset; @xdes[:offset]; end
83
- def start_page; @xdes[:start_page]; end
84
- def end_page; @xdes[:end_page]; end
85
- def fseg_id; @xdes[:fseg_id]; end
86
- def this; @xdes[:this]; end
87
- def list; @xdes[:list]; end
88
- def state; @xdes[:state]; end
89
- def bitmap; @xdes[:bitmap]; end
90
-
91
- # Return whether this XDES entry is allocated to an fseg (the whole extent
92
- # then belongs to the fseg).
93
- def allocated_to_fseg?
94
- fseg_id != 0
95
- end
103
+ # Return the status for a given page. This is relatively inefficient as
104
+ # implemented and could be done better.
105
+ def page_status(page_number)
106
+ page_status_array = each_page_status.to_a
107
+ page_status_array[page_number - start_page][1]
108
+ end
96
109
 
97
- # Return the status for a given page. This is relatively inefficient as
98
- # implemented and could be done better.
99
- def page_status(page_number)
100
- page_status_array = each_page_status.to_a
101
- page_status_array[page_number - xdes[:start_page]][1]
102
- end
110
+ # Iterate through all pages represented by this extent descriptor,
111
+ # yielding a page status hash for each page, containing the following
112
+ # fields:
113
+ #
114
+ # :page The page number.
115
+ # :free Boolean indicating whether the page is free.
116
+ # :clean Boolean indicating whether the page is clean (currently
117
+ # this bit is unused by InnoDB, and always set true).
118
+ def each_page_status
119
+ return enum_for(:each_page_status) unless block_given?
120
+
121
+ bitmap.each_byte.each_with_index do |byte, byte_index|
122
+ (0..3).each do |page_offset|
123
+ page_number = start_page + (byte_index * 4) + page_offset
124
+ page_bits = ((byte >> (page_offset * BITS_PER_PAGE)) & BITMAP_BV_ALL)
125
+ page_status = PageStatus.new(
126
+ free: (page_bits & BITMAP_BV_FREE != 0),
127
+ clean: (page_bits & BITMAP_BV_CLEAN != 0)
128
+ )
129
+ yield page_number, page_status
130
+ end
131
+ end
103
132
 
104
- # Iterate through all pages represented by this extent descriptor,
105
- # yielding a page status hash for each page, containing the following
106
- # fields:
107
- #
108
- # :page The page number.
109
- # :free Boolean indicating whether the page is free.
110
- # :clean Boolean indicating whether the page is clean (currently
111
- # this bit is unused by InnoDB, and always set true).
112
- def each_page_status
113
- unless block_given?
114
- return enum_for(:each_page_status)
133
+ nil
115
134
  end
116
135
 
117
- bitmap = xdes[:bitmap].enum_for(:each_byte)
118
-
119
- bitmap.each_with_index do |byte, byte_index|
120
- (0..3).each do |page_offset|
121
- page_number = xdes[:start_page] + (byte_index * 4) + page_offset
122
- page_bits = ((byte >> (page_offset * BITS_PER_PAGE)) & BITMAP_BV_ALL)
123
- page_status = {
124
- :free => (page_bits & BITMAP_BV_FREE != 0),
125
- :clean => (page_bits & BITMAP_BV_CLEAN != 0),
126
- }
127
- yield page_number, page_status
136
+ # Return the count of free pages (free bit is true) on this extent.
137
+ def free_pages
138
+ each_page_status.inject(0) do |sum, (_page_number, page_status)|
139
+ sum += 1 if page_status.free
140
+ sum
128
141
  end
129
142
  end
130
143
 
131
- nil
132
- end
133
-
134
- # Return the count of free pages (free bit is true) on this extent.
135
- def free_pages
136
- each_page_status.inject(0) do |sum, (page_number, page_status)|
137
- sum += 1 if page_status[:free]
138
- sum
144
+ # Return the count of used pages (free bit is false) on this extent.
145
+ def used_pages
146
+ @page.space.pages_per_extent - free_pages
139
147
  end
140
- end
141
-
142
- # Return the count of used pages (free bit is false) on this extent.
143
- def used_pages
144
- @page.space.pages_per_extent - free_pages
145
- end
146
148
 
147
- # Return the address of the previous list pointer from the list node
148
- # contained within the XDES entry. This is used by +Innodb::List::Xdes+
149
- # to iterate through XDES entries in a list.
150
- def prev_address
151
- xdes[:list][:prev]
152
- end
149
+ # Return the address of the previous list pointer from the list node
150
+ # contained within the XDES entry. This is used by +Innodb::List::Xdes+
151
+ # to iterate through XDES entries in a list.
152
+ def prev_address
153
+ list.prev
154
+ end
153
155
 
154
- # Return the address of the next list pointer from the list node
155
- # contained within the XDES entry. This is used by +Innodb::List::Xdes+
156
- # to iterate through XDES entries in a list.
157
- def next_address
158
- xdes[:list][:next]
159
- end
156
+ # Return the address of the next list pointer from the list node
157
+ # contained within the XDES entry. This is used by +Innodb::List::Xdes+
158
+ # to iterate through XDES entries in a list.
159
+ def next_address
160
+ list.next
161
+ end
160
162
 
161
- # Compare one Innodb::Xdes to another.
162
- def ==(other)
163
- xdes[:this][:page] == other.xdes[:this][:page] &&
164
- xdes[:this][:offset] == other.xdes[:this][:offset]
163
+ # Compare one Innodb::Xdes to another.
164
+ def ==(other)
165
+ this.page == other.this.page && this.offset == other.this.offset
166
+ end
165
167
  end
166
168
  end