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.



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

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.



161
162
163
164
165
166
167
168
169
170
# File 'lib/seafoam/bgv/bgv_parser.rb', line 161

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
  groups_names + [name]
end

#read_document_propsObject



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

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)


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

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.



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
141
142
143
144
# File 'lib/seafoam/bgv/bgv_parser.rb', line 100

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 do |id|
      graph.nodes[id] || raise(EncodingError, "BGV edge with unknown node #{id}")
    end
    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.



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

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.



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

def read_graph_preheader
  return 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



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

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.



147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/seafoam/bgv/bgv_parser.rb', line 147

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.



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

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