Class: Seafoam::BGV::BGVParser

Inherits:
Object
  • Object
show all
Defined in:
lib/seafoam/bgv/bgv_parser.rb

Overview

A parser for BGV files. It’s a push-pull streaming interface that you need to drive with what you want next from the file. It’s slightly complicated and some code is duplicated in order to support skipping over parts of the file that you don’t need.

Direct Known Subclasses

Commands::BGVDebugParser

Instance Method Summary collapse

Constructor Details

#initialize(file) ⇒ BGVParser

Returns a new instance of BGVParser.



11
12
13
14
15
16
17
18
19
20
# File 'lib/seafoam/bgv/bgv_parser.rb', line 11

def initialize(file)
  data = File.read(file, encoding: Encoding::ASCII_8BIT)
  if data[0..1].bytes == [0x1f, 0x8b]
    data = Zlib.gunzip(data)
  end
  @reader = Binary::IOBinaryReader.new(StringIO.new(data))
  @group_stack = []
  @pool = {}
  @index = 0
end

Instance Method Details

#graph_name(graph_header) ⇒ Object

Produce a flat graph name from a header.



157
158
159
160
161
162
163
164
165
166
167
# File 'lib/seafoam/bgv/bgv_parser.rb', line 157

def graph_name(graph_header)
  groups_names = graph_header[:group].map { |g| g[:short_name] }
  count = 0
  name = graph_header[:format].sub(/%s/) do
    arg = graph_header[:args][count]
    count += 1
    arg
  end
  components = groups_names + [name]
  components.join('/')
end

#read_document_propsObject



36
37
38
39
40
41
42
43
44
45
# File 'lib/seafoam/bgv/bgv_parser.rb', line 36

def read_document_props
  if @major >= 7
    token = @reader.peek_sint8
    if token == BEGIN_DOCUMENT
      @reader.skip_int8
      document_props = read_props
    end
  end
  document_props
end

#read_file_header(version_check: true) ⇒ Object

Read the file header and return the version.

Raises:

  • (EncodingError)


23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/seafoam/bgv/bgv_parser.rb', line 23

def read_file_header(version_check: true)
  raise EncodingError, 'does not appear to be a BGV file - missing header' unless @reader.read_bytes(4) == MAGIC

  @major = @reader.read_sint8
  @minor = @reader.read_sint8
  version = [@major, @minor]
  if version_check && !SUPPORTED_VERSIONS.include?(version)
    raise NotImplementedError, "unsupported BGV version #{@major}.#{@minor}"
  end

  version
end

#read_graphObject

Read a graph having either read or skipped its headers, producing a Graph object.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/seafoam/bgv/bgv_parser.rb', line 98

def read_graph
  # Already read BEGIN_GRAPH, id, format, args, and props
  graph = Graph.new(@graph_props)
  edge_delay = []
  @reader.read_sint32.times do
    id = @reader.read_sint32
    node_class = read_pool_object
    has_predecessor = read_bool
    props = read_props
    props[:id] = id
    props[:node_class] = node_class
    props[:has_predecessor] = has_predecessor
    node = graph.create_node(id, props)
    edge_delay.push(*read_edges(node, node_class, true))
    edge_delay.push(*read_edges(node, node_class, false))
  end
  edge_delay.each do |edge|
    node = edge[:node]
    props = edge[:edge]
    inputs = edge[:inputs]
    others = edge[:ids].reject(&:nil?).map { |id| graph.nodes[id] || raise(EncodingError, "BGV edge with unknown node #{id}") }
    others.each_with_index do |other, index|
      # We need to give each edge their own property as they're annotated separately.
      props = props.dup
      props[:index] = index
      if inputs
        graph.create_edge other, node, props
      else
        graph.create_edge node, other, props
      end
    end
  end

  # Read block information.
  @reader.read_sint32.times do
    block_id = @reader.read_sint32
    block_nodes = @reader.read_sint32.times.map { @reader.read_sint32 }
    # Followers aren't used but could be.
    @reader.read_sint32.times.map { @reader.read_sint32 }
    graph.create_block block_id, block_nodes
  end
  graph
end

#read_graph_headerObject

Read a graph’s headers, having just read its ID. This gives you the graph’s properties.



83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/seafoam/bgv/bgv_parser.rb', line 83

def read_graph_header
  # Already read BEGIN_GRAPH and id
  format = read_string
  args = read_args
  props = read_props
  @graph_props = props
  {
    group: @group_stack.dup,
    format: format,
    args: args,
    props: props
  }
end

#read_graph_preheaderObject

Move to the next graph in the file, and return its index and ID, or nil if there are no more graphs.



59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/seafoam/bgv/bgv_parser.rb', line 59

def read_graph_preheader
  return nil unless read_groups

  # Already read BEGIN_GRAPH
  index = @index
  id = @reader.read_sint32
  if id
    @index += 1
    [index, id]
  else
    [nil, nil]
  end
end

#skip_document_propsObject



47
48
49
50
51
52
53
54
55
# File 'lib/seafoam/bgv/bgv_parser.rb', line 47

def skip_document_props
  if @major >= 7
    token = @reader.peek_sint8
    if token == BEGIN_DOCUMENT
      @reader.skip_int8
      skip_props
    end
  end
end

#skip_graphObject

Skip over a graph, having read or skipped its headers.



143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/seafoam/bgv/bgv_parser.rb', line 143

def skip_graph
  # Already read BEGIN_GRAPH, id, format, args, and props.
  @reader.read_sint32.times do
    @reader.skip_int32
    node_class = read_pool_object
    skip_bool
    skip_props
    skip_edges node_class, true
    skip_edges node_class, false
  end
  skip_blocks
end

#skip_graph_headerObject

Skip over a graph’s headers, having just read its ID.



74
75
76
77
78
79
# File 'lib/seafoam/bgv/bgv_parser.rb', line 74

def skip_graph_header
  # Already read BEGIN_GRAPH and id
  skip_string
  skip_args
  @graph_props = read_props
end