Class: Innodb::Cursor

Inherits:
Object
  • Object
show all
Defined in:
lib/innodb/cursor.rb

Overview

A cursor to walk through InnoDB data structures to read fields.

Defined Under Namespace

Classes: StackEntry

Constant Summary collapse

@@tracing =
false

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(buffer, position) ⇒ Cursor

Initialize a cursor within a buffer at the given position.



36
37
38
39
40
41
# File 'lib/innodb/cursor.rb', line 36

def initialize(buffer, position)
  @buffer = buffer
  @stack = [ StackEntry.new(self, position) ]

  trace_with :print_trace
end

Class Method Details

.trace!(arg = true) ⇒ Object

Enable tracing for all Innodb::Cursor objects.



31
32
33
# File 'lib/innodb/cursor.rb', line 31

def self.trace!(arg=true)
  @@tracing = arg
end

Instance Method Details

#adjust(relative_position) ⇒ Object

Adjust the current cursor to a new relative position.



128
129
130
131
# File 'lib/innodb/cursor.rb', line 128

def adjust(relative_position)
  current.position += relative_position
  self
end

#backwardObject

Set the direction of the cursor to "backward".



112
113
114
# File 'lib/innodb/cursor.rb', line 112

def backward
  direction(:backward)
end

#currentObject

The current cursor object; the top of the stack.



76
77
78
# File 'lib/innodb/cursor.rb', line 76

def current
  @stack.last
end

#direction(direction_arg = nil) ⇒ Object

Return the direction of the current cursor.



97
98
99
100
101
102
103
104
# File 'lib/innodb/cursor.rb', line 97

def direction(direction_arg=nil)
  if direction_arg.nil?
    return current.direction
  end

  current.direction = direction_arg
  self
end

#each_byte_as_uint8(length) ⇒ Object



181
182
183
184
185
186
187
188
189
190
191
# File 'lib/innodb/cursor.rb', line 181

def each_byte_as_uint8(length)
  unless block_given?
    return enum_for(:each_byte_as_uint8, length)
  end

  read_and_advance(length).bytes.each do |byte|
    yield byte
  end

  nil
end

#forwardObject

Set the direction of the cursor to "forward".



107
108
109
# File 'lib/innodb/cursor.rb', line 107

def forward
  direction(:forward)
end

#get_bit_array(num_bits) ⇒ Object

Read an array of 1-bit integers.



349
350
351
352
353
354
# File 'lib/innodb/cursor.rb', line 349

def get_bit_array(num_bits)
  size = (num_bits + 7) / 8
  data = read_and_advance(size)
  bit_array = BinData::Array.new(:type => :bit1, :initial_length => size * 8)
  bit_array.read(data).to_ary
end

#get_bytes(length) ⇒ Object

Return raw bytes.



177
178
179
# File 'lib/innodb/cursor.rb', line 177

def get_bytes(length)
  read_and_advance(length)
end

#get_hex(length) ⇒ Object

Return raw bytes as hex.



194
195
196
# File 'lib/innodb/cursor.rb', line 194

def get_hex(length)
  read_and_advance(length).bytes.map { |c| "%02x" % c }.join
end

#get_i_sint16Object

Read an InnoDB-munged signed 16-bit integer.



299
300
301
302
# File 'lib/innodb/cursor.rb', line 299

def get_i_sint16
  data = read_and_advance(2)
  BinData::Int16be.read(data) ^ (-1 << 15)
end

#get_i_sint24Object

Read an InnoDB-munged signed 24-bit integer.



305
306
307
308
# File 'lib/innodb/cursor.rb', line 305

def get_i_sint24
  data = read_and_advance(3)
  BinData::Int24be.read(data) ^ (-1 << 23)
end

#get_i_sint32Object

Read an InnoDB-munged signed 32-bit integer.



311
312
313
314
# File 'lib/innodb/cursor.rb', line 311

def get_i_sint32
  data = read_and_advance(4)
  BinData::Int32be.read(data) ^ (-1 << 31)
end

#get_i_sint48Object

Read an InnoDB-munged signed 48-bit integer.



317
318
319
320
# File 'lib/innodb/cursor.rb', line 317

def get_i_sint48
  data = read_and_advance(6)
  BinData::Int48be.read(data) ^ (-1 << 47)
end

#get_i_sint64Object

Read an InnoDB-munged signed 64-bit integer.



323
324
325
326
# File 'lib/innodb/cursor.rb', line 323

def get_i_sint64
  data = read_and_advance(8)
  BinData::Int64be.read(data) ^ (-1 << 63)
end

#get_i_sint8Object

Read an InnoDB-munged signed 8-bit integer.



293
294
295
296
# File 'lib/innodb/cursor.rb', line 293

def get_i_sint8
  data = read_and_advance(1)
  BinData::Int8.read(data) ^ (-1 << 7)
end

#get_i_sint_by_size(size) ⇒ Object

Read an InnoDB-munged signed integer given its size in bytes.



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/innodb/cursor.rb', line 329

def get_i_sint_by_size(size)
  case size
  when 1
    get_i_sint8
  when 2
    get_i_sint16
  when 3
    get_i_sint24
  when 4
    get_i_sint32
  when 6
    get_i_sint48
  when 8
    get_i_sint64
  else
    raise "Not implemented"
  end
end

#get_ic_uint32Object

Read an InnoDB-compressed unsigned 32-bit integer.



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/innodb/cursor.rb', line 272

def get_ic_uint32
  flag = peek { get_uint8 }

  case
  when flag < 0x80
    get_uint8
  when flag < 0xc0
    get_uint16 & 0x7fff
  when flag < 0xe0
    get_uint24 & 0x3fffff
  when flag < 0xf0
    get_uint32 & 0x1fffffff
  when flag == 0xf0
    adjust(+1) # Skip the flag.
    get_uint32
  else
    raise "Invalid flag #{flag.to_s(16)} seen"
  end
end

#get_sint16(position = nil) ⇒ Object

Read a big-endian signed 16-bit integer.



213
214
215
216
217
# File 'lib/innodb/cursor.rb', line 213

def get_sint16(position=nil)
  seek(position)
  data = read_and_advance(2)
  BinData::Int16be.read(data)
end

#get_uint16(position = nil) ⇒ Object

Read a big-endian unsigned 16-bit integer.



206
207
208
209
210
# File 'lib/innodb/cursor.rb', line 206

def get_uint16(position=nil)
  seek(position)
  data = read_and_advance(2)
  BinData::Uint16be.read(data)
end

#get_uint24(position = nil) ⇒ Object

Read a big-endian unsigned 24-bit integer.



220
221
222
223
224
# File 'lib/innodb/cursor.rb', line 220

def get_uint24(position=nil)
  seek(position)
  data = read_and_advance(3)
  BinData::Uint24be.read(data)
end

#get_uint32(position = nil) ⇒ Object

Read a big-endian unsigned 32-bit integer.



227
228
229
230
231
# File 'lib/innodb/cursor.rb', line 227

def get_uint32(position=nil)
  seek(position)
  data = read_and_advance(4)
  BinData::Uint32be.read(data)
end

#get_uint48(position = nil) ⇒ Object

Read a big-endian unsigned 48-bit integer.



234
235
236
237
238
# File 'lib/innodb/cursor.rb', line 234

def get_uint48(position=nil)
  seek(position)
  data = read_and_advance(6)
  BinData::Uint48be.read(data)
end

#get_uint64(position = nil) ⇒ Object

Read a big-endian unsigned 64-bit integer.



241
242
243
244
245
# File 'lib/innodb/cursor.rb', line 241

def get_uint64(position=nil)
  seek(position)
  data = read_and_advance(8)
  BinData::Uint64be.read(data)
end

#get_uint8(position = nil) ⇒ Object

Read an unsigned 8-bit integer.



199
200
201
202
203
# File 'lib/innodb/cursor.rb', line 199

def get_uint8(position=nil)
  seek(position)
  data = read_and_advance(1)
  BinData::Uint8.read(data)
end

#get_uint_array_by_size(size, count) ⇒ Object



267
268
269
# File 'lib/innodb/cursor.rb', line 267

def get_uint_array_by_size(size, count)
  (0...count).to_a.inject([]) { |a, n| a << get_uint_by_size(size); a }
end

#get_uint_by_size(size) ⇒ Object

Read a big-endian unsigned integer given its size in bytes.



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/innodb/cursor.rb', line 248

def get_uint_by_size(size)
  case size
  when 1
    get_uint8
  when 2
    get_uint16
  when 3
    get_uint24
  when 4
    get_uint32
  when 6
    get_uint48
  when 8
    get_uint64
  else
    raise "Not implemented"
  end
end

#name(name_arg = nil) ⇒ Object

Set the field name.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/innodb/cursor.rb', line 81

def name(name_arg=nil)
  if name_arg.nil?
    return current.name
  end

  unless block_given?
    raise "No block given"
  end

  current.name.push name_arg
  ret = yield(self)
  current.name.pop
  ret
end

#peek(position = nil) ⇒ Object

Execute a block and restore the cursor to the previous position after the block returns. Return the block's return value after restoring the cursor. Optionally seek to provided position before executing block.



150
151
152
153
154
155
156
# File 'lib/innodb/cursor.rb', line 150

def peek(position=nil)
  raise "No block given" unless block_given?
  push(position)
  result = yield(self)
  pop
  result
end

#popObject

Restore the last cursor position.



141
142
143
144
145
# File 'lib/innodb/cursor.rb', line 141

def pop
  raise "No cursors to pop" unless @stack.size > 1
  @stack.pop
  self
end

#positionObject

Return the position of the current cursor.



117
118
119
# File 'lib/innodb/cursor.rb', line 117

def position
  current.position
end

Print a trace output for this cursor. The method is passed a cursor object, position, raw byte buffer, and array of names.



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/innodb/cursor.rb', line 45

def print_trace(cursor, position, bytes, name)
  slice_size = 16
  bytes.each_slice(slice_size).each_with_index do |slice_bytes, slice_count|
    puts "%06i %s %-32s  %s" % [
      position + (slice_count * slice_size),
      direction == :backward ? "" : "",
      slice_bytes.map { |n| "%02x" % n }.join,
      slice_count == 0 ? name.join(".") : "",
    ]
  end
end

#push(position = nil) ⇒ Object

Save the current cursor position and start a new (nested, stacked) cursor.



134
135
136
137
138
# File 'lib/innodb/cursor.rb', line 134

def push(position=nil)
  @stack.push current.dup
  seek(position)
  self
end

#read_and_advance(length) ⇒ Object

Read a number of bytes forwards or backwards from the current cursor position and adjust the cursor position by that amount.



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/innodb/cursor.rb', line 160

def read_and_advance(length)
  data = nil
  cursor_start = current.position
  case current.direction
  when :forward
    data = @buffer.data(current.position, length)
    adjust(length)
  when :backward
    adjust(-length)
    data = @buffer.data(current.position, length)
  end

  trace(cursor_start, data.bytes, current.name)
  data
end

#seek(position) ⇒ Object

Move the current cursor to a new absolute position.



122
123
124
125
# File 'lib/innodb/cursor.rb', line 122

def seek(position)
  current.position = position if position
  self
end

#trace(position, bytes, name) ⇒ Object

Generate a trace record from the current cursor.



71
72
73
# File 'lib/innodb/cursor.rb', line 71

def trace(position, bytes, name)
  @trace_proc.call(self, position, bytes, name) if @@tracing && @trace_proc
end

#trace_with(arg = nil) ⇒ Object

Set a Proc or method on self to trace with.



58
59
60
61
62
63
64
65
66
67
68
# File 'lib/innodb/cursor.rb', line 58

def trace_with(arg=nil)
  if arg.nil?
    @trace_proc = nil
  elsif arg.class == Proc
    @trace_proc = arg
  elsif arg.class == Symbol
    @trace_proc = lambda { |cursor, position, bytes, name| self.send(arg, cursor, position, bytes, name) }
  else
    raise "Don't know how to trace with #{arg}"
  end
end