module Gdsii::Read
This module will be extended into all classes descended of Group (i.e. the high-level GDSII access classes). The extension brings a “read” class method (i.e. singleton) into these classes which enables a GDSII file to be read into these data structures using the BNF specification.
Public Instance Methods
read(file, parent_records=nil, parent_bnf_item=nil, yield_at=nil) { |group| ... }
click to toggle source
Accepts a file handle and reads data from that file handle into an object descended of Group. The parent_records and parent_bnf_item arguments are for internal use only.
# File lib/gdsii/mixins.rb, line 288 def read(file, parent_records=nil, parent_bnf_item=nil, yield_at=nil) # Set BNF index at 0, get the BNF items, create an empty grouping object i = 0 bnf_items = bnf_spec.bnf_items group = self.new # puts "#{self}" # Loop through the BNF spec for this grouping... while i < bnf_items.length do bnf_item = bnf_items[i] # see what kind of a BNF item this is - a Class or a record type (Fixnum) if bnf_item.key.class == Class # return if stop_at_class is set to true (used internally for # Library#read_header and Cell#read_header). yield group if yield_at == :before_group # Determine the class to use klass = bnf_item.key # Read from the class if bnf_item.multiple? if (rec = klass.read(file, group.records, bnf_item)) # if a record was returned, then add it group.records.add(bnf_item.key, rec) else # if nil was returned, then we're done with the record; next i += 1 end else # If the record is singular, then get it from the class and # increment the counter rec = klass.read(file, group.records, bnf_item) group.records.set(bnf_item.key, rec) i += 1 end else # ELSE, a record type is expected (Fixnum) rec = Record.read(file) # puts " --> expect #{Gdsii::grt_name(bnf_item.key)}; rec == #{rec.name}" if rec.type == bnf_item.key # This record matches the grouping BNF item that was expected, so # store the data if bnf_item.multiple? group.records.add(bnf_item.key, rec) else group.records.set(bnf_item.key, rec) i += 1 end else # Record does not match expected record as per BNF. Check that we # have data already set in this record or that the record is # optional. if group.records.has_data?(bnf_item.key) or bnf_item.optional? # Already has data - just move to the next record and reset file # pointer i += 1 file.seek(-rec.byte_size, IO::SEEK_CUR) elsif (parent_bnf_item and parent_bnf_item.key.class == Class and (parent_records.has_data?(parent_bnf_item.key) or parent_bnf_item.optional?)) # OK, in this case, we are descended into a Class and did not # match the BNF expected. Furthermore, the parent calling this # grouping either already got the data needed or this was an # optional class in the first place. In either case, we're OK # and just need to get out - which is what we do by returning # nil. file.seek(-rec.byte_size, IO::SEEK_CUR) return nil else # Does not match the expected BNF... fail raise "Unexpected record while reading GDSII file starting at file position #{file.pos-rec.byte_size}\n" + "This record is in the wrong place according to the GDSII specification (from BNF)\n" + "Expected record was #{Gdsii::grt_name(bnf_item.key)}; instead, received:\n" + "Record type = #{Gdsii::grt_name(rec.type)}\n" + "Data type = #{Gdsii::gdt_name(rec.data.type)}\n" + "Record length = #{rec.byte_size}\n" end end end end # Return this record grouping yield group if yield_at == :group group end