ruby-gdsii 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +20 -0
- data/README.txt +113 -0
- data/bin/rgds-debug +43 -0
- data/bin/rgds-dump +38 -0
- data/bin/rgds-join +98 -0
- data/bin/rgds-layers +53 -0
- data/bin/rgds-sremove +135 -0
- data/bin/rgds-ssplit +113 -0
- data/bin/rgds-stats +134 -0
- data/bin/rgds-structs +41 -0
- data/bin/rgds-tree +166 -0
- data/bin/rgds2rb +99 -0
- data/lib/gdsii.rb +137 -0
- data/lib/gdsii/aref.rb +243 -0
- data/lib/gdsii/bnf.rb +309 -0
- data/lib/gdsii/boundary.rb +53 -0
- data/lib/gdsii/box.rb +65 -0
- data/lib/gdsii/byte_order.rb +36 -0
- data/lib/gdsii/element.rb +172 -0
- data/lib/gdsii/group.rb +98 -0
- data/lib/gdsii/library.rb +518 -0
- data/lib/gdsii/mixins.rb +378 -0
- data/lib/gdsii/node.rb +65 -0
- data/lib/gdsii/path.rb +169 -0
- data/lib/gdsii/property.rb +108 -0
- data/lib/gdsii/record.rb +606 -0
- data/lib/gdsii/record/consts.rb +384 -0
- data/lib/gdsii/record/datatypes/ascii.rb +145 -0
- data/lib/gdsii/record/datatypes/bitarray.rb +101 -0
- data/lib/gdsii/record/datatypes/data.rb +111 -0
- data/lib/gdsii/record/datatypes/int2.rb +67 -0
- data/lib/gdsii/record/datatypes/int4.rb +65 -0
- data/lib/gdsii/record/datatypes/nodata.rb +60 -0
- data/lib/gdsii/record/datatypes/real4.rb +51 -0
- data/lib/gdsii/record/datatypes/real8.rb +120 -0
- data/lib/gdsii/sref.rb +61 -0
- data/lib/gdsii/strans.rb +133 -0
- data/lib/gdsii/structure.rb +352 -0
- data/lib/gdsii/text.rb +203 -0
- data/pkg/ruby-gdsii.gem +23 -0
- data/samples/hello.gds +0 -0
- data/samples/hello.out.rb +84 -0
- data/samples/hello.rb +94 -0
- data/test/baseline/dcp1.gds +0 -0
- data/test/baseline/h_write.gds +0 -0
- data/test/h_pthru.rb +22 -0
- data/test/h_write.rb +117 -0
- data/test/hs_pthru.rb +31 -0
- data/test/l_pthru.rb +23 -0
- data/test/test_gds_group.rb +379 -0
- data/test/test_gds_record.rb +99 -0
- metadata +118 -0
@@ -0,0 +1,384 @@
|
|
1
|
+
module Gdsii
|
2
|
+
|
3
|
+
#
|
4
|
+
# Class to store information about each record type. The Gdsii::RECORD_INFO
|
5
|
+
# constant is an array with an index of the record type number and the value
|
6
|
+
# being instances of this class. For example:
|
7
|
+
#
|
8
|
+
# Gdsii::RECORD_INFO[Gdsii::GRT_HEADER].name #=> 'HEADER'
|
9
|
+
# Gdsii::RECORD_INFO[Gdsii::GRT_XY].min_len #=> 4
|
10
|
+
#
|
11
|
+
# See the Gdsii module for a complete listing of record type constants
|
12
|
+
# (Gdsii::GRT_*).
|
13
|
+
#
|
14
|
+
class RecInfo
|
15
|
+
|
16
|
+
# Name of this record (should be the same as the Gdsii::GRT_* name but
|
17
|
+
# without the "Gdsii::GRT_".
|
18
|
+
attr_reader :name
|
19
|
+
|
20
|
+
# Record type is an integer of the constant Gdsii::GRT_* for this record
|
21
|
+
# type.
|
22
|
+
attr_reader :type
|
23
|
+
|
24
|
+
# Boolean as to whether or not this record is a valid GDS record (some
|
25
|
+
# records are not valid to the GDS specification)
|
26
|
+
attr_reader :valid
|
27
|
+
|
28
|
+
# Data type is an integer of the constant Gdsii::GDT_* for this record.
|
29
|
+
# This represents the data type expected for this record.
|
30
|
+
attr_reader :data_type
|
31
|
+
|
32
|
+
# Size indicates how many bytes are necessary to store each element of
|
33
|
+
# this record. For example, the size of a Gdsii::GRT_XY record is 8 with a
|
34
|
+
# record size of 4 bytes for each record (since it's a Gdsii::GDT_INT4).
|
35
|
+
# This means that the minimum number of items needed (#min_items) is 2.
|
36
|
+
attr_reader :size
|
37
|
+
|
38
|
+
# Minimum length (in bytes) to store this record
|
39
|
+
attr_reader :min_len
|
40
|
+
|
41
|
+
# Maximum length (in bytes) to store this record
|
42
|
+
attr_reader :max_len
|
43
|
+
|
44
|
+
# Object constructor. Intended to be used internally only to add elements
|
45
|
+
# to the Gdsii::RECORD_INFO array.
|
46
|
+
def initialize(name, data_type, valid, size, min_len, max_len)
|
47
|
+
@name = name
|
48
|
+
@data_type = data_type
|
49
|
+
@valid = valid
|
50
|
+
@size = size
|
51
|
+
@min_len = min_len
|
52
|
+
@max_len = max_len
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the minimum number of items necessary for this record type.
|
56
|
+
def min_items
|
57
|
+
case @data_type
|
58
|
+
when GDT_NO_DATA : 0
|
59
|
+
when GDT_ASCII : (@size == 0) ? 1 : @min_len/@size
|
60
|
+
else
|
61
|
+
@min_len/@size
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the maximum number of items necessary for this record type.
|
66
|
+
def max_items
|
67
|
+
case @data_type
|
68
|
+
when GDT_NO_DATA : 0
|
69
|
+
when GDT_ASCII : (@size == 0) ? 1 : @max_len/@size
|
70
|
+
else
|
71
|
+
@max_len/@size
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns true if this object has only a single datum; false if it can
|
76
|
+
# have multiple data.
|
77
|
+
def single_data_value?
|
78
|
+
if @data_type == GDT_ASCII
|
79
|
+
@size == 0
|
80
|
+
else
|
81
|
+
@size == @min_len and @size == @max_len
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Converts this record to a string (returns the record's name)
|
86
|
+
def to_s(); @name; end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
############################################################################
|
91
|
+
|
92
|
+
#
|
93
|
+
# Class to store information about each record data type. The
|
94
|
+
# Gdsii::DATATYPE_INFO array has an index of GDT_* integer values with the
|
95
|
+
# value being an instance of this class. For example:
|
96
|
+
#
|
97
|
+
# Gdsii::RECORD_INFO[Gdsii::GDT_ASCII].name #=> 'ASCII'
|
98
|
+
# Gdsii::RECORD_INFO[Gdsii::GDT_REAL4].valid #=> false
|
99
|
+
#
|
100
|
+
class RecDataTypeInfo
|
101
|
+
|
102
|
+
# Name of this record data type (should be the same as the Gdsii::GDT_* name
|
103
|
+
# but without the "Gdsii::GDT_".
|
104
|
+
attr_reader :name
|
105
|
+
|
106
|
+
# Boolean value indicating whether or not this record data type is valid.
|
107
|
+
attr_reader :valid
|
108
|
+
|
109
|
+
# Integer value indicating the size (in bytes) required for this data
|
110
|
+
# type. The exception is Gdsii::GDT_ASCII which has a size of 0 but in
|
111
|
+
# actuality has a variable-length size.
|
112
|
+
attr_reader :size
|
113
|
+
|
114
|
+
# Object constructor. Intended to be used internally only to add elements
|
115
|
+
# to the Gdsii::DATATYPE_INFO array.
|
116
|
+
def initialize(name, valid, size)
|
117
|
+
@name = name
|
118
|
+
@valid = valid
|
119
|
+
@size = size
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns the data type's name
|
123
|
+
def to_s(); @name; end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
############################################################################
|
128
|
+
|
129
|
+
# These numbers correspond to GDSII format indicating the path types
|
130
|
+
if not defined?(PATHTYPE_FLUSH)
|
131
|
+
PATHTYPE_FLUSH = 0
|
132
|
+
PATHTYPE_ROUND = 1
|
133
|
+
PATHTYPE_EXTEND = 2
|
134
|
+
PATHTYPE_CUSTOM = 4
|
135
|
+
|
136
|
+
# These numbers correspond to GDSII format
|
137
|
+
FORMAT_GDSII_ARCHIVE = 0
|
138
|
+
FORMAT_GDSII_FILTERED = 1
|
139
|
+
FORMAT_EDSIII_ARCHIVE = 2
|
140
|
+
FORMAT_EDSIII_FILTERED = 3
|
141
|
+
|
142
|
+
# These are GDSII record numbers and correspond to the GDSII number in the
|
143
|
+
# GDSII file specification
|
144
|
+
GRT_HEADER = 0
|
145
|
+
GRT_BGNLIB = 1
|
146
|
+
GRT_LIBNAME = 2
|
147
|
+
GRT_UNITS = 3
|
148
|
+
GRT_ENDLIB = 4
|
149
|
+
GRT_BGNSTR = 5
|
150
|
+
GRT_STRNAME = 6
|
151
|
+
GRT_ENDSTR = 7
|
152
|
+
GRT_BOUNDARY = 8
|
153
|
+
GRT_PATH = 9
|
154
|
+
GRT_SREF = 10
|
155
|
+
|
156
|
+
GRT_AREF = 11
|
157
|
+
GRT_TEXT = 12
|
158
|
+
GRT_LAYER = 13
|
159
|
+
GRT_DATATYPE = 14
|
160
|
+
GRT_WIDTH = 15
|
161
|
+
GRT_XY = 16
|
162
|
+
GRT_ENDEL = 17
|
163
|
+
GRT_SNAME = 18
|
164
|
+
GRT_COLROW = 19
|
165
|
+
GRT_TEXTNODE = 20
|
166
|
+
|
167
|
+
GRT_NODE = 21
|
168
|
+
GRT_TEXTTYPE = 22
|
169
|
+
GRT_PRESENTATION = 23
|
170
|
+
GRT_SPACING = 24
|
171
|
+
GRT_STRING = 25
|
172
|
+
GRT_STRANS = 26
|
173
|
+
GRT_MAG = 27
|
174
|
+
GRT_ANGLE = 28
|
175
|
+
GRT_UINTEGER = 29
|
176
|
+
GRT_USTRING = 30
|
177
|
+
|
178
|
+
GRT_REFLIBS = 31
|
179
|
+
GRT_FONTS = 32
|
180
|
+
GRT_PATHTYPE = 33
|
181
|
+
GRT_GENERATIONS = 34
|
182
|
+
GRT_ATTRTABLE = 35
|
183
|
+
GRT_STYPTABLE = 36
|
184
|
+
GRT_STRTYPE = 37
|
185
|
+
GRT_ELFLAGS = 38
|
186
|
+
GRT_ELKEY = 39
|
187
|
+
GRT_LINKTYPE = 40
|
188
|
+
|
189
|
+
GRT_LINKKEYS = 41
|
190
|
+
GRT_NODETYPE = 42
|
191
|
+
GRT_PROPATTR = 43
|
192
|
+
GRT_PROPVALUE = 44
|
193
|
+
GRT_BOX = 45
|
194
|
+
GRT_BOXTYPE = 46
|
195
|
+
GRT_PLEX = 47
|
196
|
+
GRT_BGNEXTN = 48
|
197
|
+
GRT_ENDEXTN = 49
|
198
|
+
GRT_TAPENUM = 50
|
199
|
+
|
200
|
+
GRT_TAPECODE = 51
|
201
|
+
GRT_STRCLASS = 52
|
202
|
+
GRT_RESERVED = 53
|
203
|
+
GRT_FORMAT = 54
|
204
|
+
GRT_MASK = 55
|
205
|
+
GRT_ENDMASKS = 56
|
206
|
+
GRT_LIBDIRSIZE = 57
|
207
|
+
GRT_SRFNAME = 58
|
208
|
+
GRT_LIBSECUR = 59
|
209
|
+
GRT_BORDER = 60
|
210
|
+
|
211
|
+
GRT_SOFTFENCE = 61
|
212
|
+
GRT_HARDFENCE = 62
|
213
|
+
GRT_SOFTWIRE = 63
|
214
|
+
GRT_HARDWIRE = 64
|
215
|
+
GRT_PATHPORT = 65
|
216
|
+
GRT_NODEPORT = 66
|
217
|
+
GRT_USERCONSTRAINT = 67
|
218
|
+
GRT_SPACER_ERROR = 68
|
219
|
+
GRT_CONTACT = 69
|
220
|
+
|
221
|
+
# GDSII record data types
|
222
|
+
GDT_NO_DATA = 0
|
223
|
+
GDT_BITARRAY = 1
|
224
|
+
GDT_INT2 = 2
|
225
|
+
GDT_INT4 = 3
|
226
|
+
GDT_REAL4 = 4
|
227
|
+
GDT_REAL8 = 5
|
228
|
+
GDT_ASCII = 6
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
if not defined?(RECORD_INFO)
|
233
|
+
|
234
|
+
# Gdsii::RECORD_INFO is an array of Gdsii::RecInfo objects. The array order
|
235
|
+
# is significant in that the index of the array is the value of the record
|
236
|
+
# and corresponds to the Gdsii::GRT_* constant values. This allows easy
|
237
|
+
# validation lookup based upon the record type constants. Example:
|
238
|
+
#
|
239
|
+
# Gdsii::RECORD_INFO[Gdsii::GRT_HEADER].name # => "HEADER"
|
240
|
+
# Gdsii::RECORD_INFO[Gdsii::GRT_HEADER].valid # => true
|
241
|
+
# Gdsii::RECORD_INFO[Gdsii::GRT_HEADER].data_type # => 2
|
242
|
+
#
|
243
|
+
RECORD_INFO =
|
244
|
+
[
|
245
|
+
# name data_type valid size minlen maxlen num
|
246
|
+
RecInfo.new('HEADER', GDT_INT2, true, 2, 2, 2), # 0 - GDS version
|
247
|
+
RecInfo.new('BGNLIB', GDT_INT2, true, 2, 24, 24), # 1 - Modification & access time
|
248
|
+
RecInfo.new('LIBNAME', GDT_ASCII, true, 0, 0, 65530), # 2
|
249
|
+
RecInfo.new('UNITS', GDT_REAL8, true, 8, 16, 16), # 3
|
250
|
+
RecInfo.new('ENDLIB', GDT_NO_DATA, true, 0, 0, 0), # 4
|
251
|
+
RecInfo.new('BGNSTR', GDT_INT2, true, 2, 24, 24), # 5
|
252
|
+
|
253
|
+
RecInfo.new('STRNAME', GDT_ASCII, true, 0, 2, 512), # 6
|
254
|
+
RecInfo.new('ENDSTR', GDT_NO_DATA, true, 0, 0, 0), # 7
|
255
|
+
RecInfo.new('BOUNDARY', GDT_NO_DATA, true, 0, 0, 0), # 8
|
256
|
+
RecInfo.new('PATH', GDT_NO_DATA, true, 0, 0, 0), # 9
|
257
|
+
RecInfo.new('SREF', GDT_NO_DATA, true, 0, 0, 0), # 10
|
258
|
+
|
259
|
+
RecInfo.new('AREF', GDT_NO_DATA, true, 0, 0, 0), # 11
|
260
|
+
RecInfo.new('TEXT', GDT_NO_DATA, true, 0, 0, 0), # 12
|
261
|
+
RecInfo.new('LAYER', GDT_INT2, true, 2, 2, 2), # 13
|
262
|
+
RecInfo.new('DATATYPE', GDT_INT2, true, 2, 2, 2), # 14
|
263
|
+
RecInfo.new('WIDTH', GDT_INT4, true, 4, 4, 4), # 15
|
264
|
+
|
265
|
+
RecInfo.new('XY', GDT_INT4, true, 4, 8, 65528), # 16
|
266
|
+
RecInfo.new('ENDEL', GDT_NO_DATA, true, 0, 0, 0), # 17
|
267
|
+
RecInfo.new('SNAME', GDT_ASCII, true, 0, 2, 65530), # 18
|
268
|
+
RecInfo.new('COLROW', GDT_INT2, true, 2, 4, 4), # 19
|
269
|
+
RecInfo.new('TEXTNODE', GDT_NO_DATA, true, 0, 0, 0), # 20
|
270
|
+
|
271
|
+
RecInfo.new('NODE', GDT_NO_DATA, true, 0, 0, 0), # 21
|
272
|
+
RecInfo.new('TEXTTYPE', GDT_INT2, true, 2, 2, 2), # 22
|
273
|
+
RecInfo.new('PRESENTATION', GDT_BITARRAY, true, 2, 2, 2), # 23
|
274
|
+
RecInfo.new('SPACING', 0, false, 0, 0, 0), # 24
|
275
|
+
RecInfo.new('STRING', GDT_ASCII, true, 0, 2, 512), # 25
|
276
|
+
|
277
|
+
RecInfo.new('STRANS', GDT_BITARRAY, true, 2, 2, 2), # 26
|
278
|
+
RecInfo.new('MAG', GDT_REAL8, true, 8, 8, 8), # 27
|
279
|
+
RecInfo.new('ANGLE', GDT_REAL8, true, 8, 8, 8), # 28
|
280
|
+
RecInfo.new('UINTEGER', 0, false, 0, 0, 0), # 29
|
281
|
+
RecInfo.new('USTRING', 0, false, 0, 0, 0), # 30
|
282
|
+
|
283
|
+
RecInfo.new('REFLIBS', GDT_ASCII, true, 44, 88, 748), # 31
|
284
|
+
RecInfo.new('FONTS', GDT_ASCII, true, 44, 176, 176), # 32 - paths to text font def files
|
285
|
+
RecInfo.new('PATHTYPE', GDT_INT2, true, 2, 2, 2), # 33
|
286
|
+
RecInfo.new('GENERATIONS', GDT_INT2, true, 2, 2, 2), # 34
|
287
|
+
RecInfo.new('ATTRTABLE', GDT_ASCII, true, 0, 2, 44), # 35 - path of attr def file
|
288
|
+
|
289
|
+
RecInfo.new('STYPTABLE', 0, false, 0, 0, 0), # 36
|
290
|
+
RecInfo.new('STRTYPE', 0, false, 0, 0, 0), # 37
|
291
|
+
RecInfo.new('ELFLAGS', GDT_BITARRAY, true, 2, 2, 2), # 38
|
292
|
+
RecInfo.new('ELKEY', 0, false, 0, 0, 0), # 39
|
293
|
+
RecInfo.new('LINKTYPE', 0, false, 0, 0, 0), # 40
|
294
|
+
|
295
|
+
RecInfo.new('LINKKEYS', 0, false, 0, 0, 0), # 41
|
296
|
+
RecInfo.new('NODETYPE', GDT_INT2, true, 2, 2, 2), # 42
|
297
|
+
RecInfo.new('PROPATTR', GDT_INT2, true, 2, 2, 2), # 43
|
298
|
+
RecInfo.new('PROPVALUE', GDT_ASCII, true, 0, 2, 126), # 44
|
299
|
+
RecInfo.new('BOX', GDT_NO_DATA, true, 0, 0, 0), # 45
|
300
|
+
|
301
|
+
RecInfo.new('BOXTYPE', GDT_INT2, true, 2, 2, 2), # 46
|
302
|
+
RecInfo.new('PLEX', GDT_INT4, true, 4, 4, 4), # 47
|
303
|
+
RecInfo.new('BGNEXTN', GDT_INT4, true, 4, 4, 4), # 48
|
304
|
+
RecInfo.new('ENDEXTN', GDT_INT4, true, 4, 4, 4), # 49
|
305
|
+
RecInfo.new('TAPENUM', GDT_INT2, true, 2, 2, 2), # 50
|
306
|
+
|
307
|
+
RecInfo.new('TAPECODE', GDT_INT2, true, 2, 12, 12), # 51
|
308
|
+
RecInfo.new('STRCLASS', GDT_BITARRAY, true, 2, 2, 2), # 52
|
309
|
+
RecInfo.new('RESERVED', 0, false, 0, 0, 0), # 53
|
310
|
+
RecInfo.new('FORMAT', GDT_INT2, true, 2, 2, 2), # 54 - GdsiiFormat type
|
311
|
+
RecInfo.new('MASK', GDT_ASCII, true, 0, 2, 65530), # 55
|
312
|
+
|
313
|
+
RecInfo.new('ENDMASKS', GDT_NO_DATA, true, 0, 0, 0), # 56
|
314
|
+
RecInfo.new('LIBDIRSIZE', GDT_INT2, true, 2, 2, 2), # 57 - # of pages in lib dir
|
315
|
+
RecInfo.new('SRFNAME', GDT_ASCII, true, 0, 2, 65530), # 58 - name of spacing rules file
|
316
|
+
RecInfo.new('LIBSECUR', GDT_INT2, true, 2, 6, 192), # 59 - array of ACL data
|
317
|
+
RecInfo.new('BORDER', GDT_NO_DATA, true, 0, 0, 0), # 60
|
318
|
+
|
319
|
+
RecInfo.new('SOFTFENCE', GDT_NO_DATA, true, 0, 0, 0), # 61
|
320
|
+
RecInfo.new('HARDFENCE', GDT_NO_DATA, true, 0, 0, 0), # 62
|
321
|
+
RecInfo.new('SOFTWIRE', GDT_NO_DATA, true, 0, 0, 0), # 63
|
322
|
+
RecInfo.new('HARDWIRE', GDT_NO_DATA, true, 0, 0, 0), # 64
|
323
|
+
RecInfo.new('PATHPORT', GDT_NO_DATA, true, 0, 0, 0), # 65
|
324
|
+
|
325
|
+
RecInfo.new('NODEPORT', GDT_NO_DATA, true, 0, 0, 0), # 66
|
326
|
+
RecInfo.new('USERCONSTRAINT', GDT_NO_DATA, true, 0, 0, 0), # 67
|
327
|
+
RecInfo.new('SPACER_ERROR', GDT_NO_DATA, true, 0, 0, 0), # 68
|
328
|
+
RecInfo.new('CONTACT', GDT_NO_DATA, true, 0, 0, 0), # 69
|
329
|
+
]
|
330
|
+
|
331
|
+
#
|
332
|
+
# Gdsii::DATATYPE_INFO is an array of Gdsii::RecDataTypeInfo objects. The array
|
333
|
+
# order is significant in that the index of the array is the value of the
|
334
|
+
# record data type constant. This allows easy validation lookup based upon
|
335
|
+
# the record data type constants. Example:
|
336
|
+
#
|
337
|
+
# Gdsii::DATATYPE_INFO[Gdsii::GRT_REAL8].name # => "REAL8"
|
338
|
+
# Gdsii::DATATYPE_INFO[Gdsii::GRT_REAL8].valid # => true
|
339
|
+
# Gdsii::DATATYPE_INFO[Gdsii::GRT_REAL8].size # => 8
|
340
|
+
#
|
341
|
+
DATATYPE_INFO =
|
342
|
+
[
|
343
|
+
RecDataTypeInfo.new('NO_DATA', true, 0 ), # 0
|
344
|
+
RecDataTypeInfo.new('BITARRAY', true, 2 ), # 1
|
345
|
+
RecDataTypeInfo.new('INT2', true, 2 ), # 2
|
346
|
+
RecDataTypeInfo.new('INT4', true, 4 ), # 3
|
347
|
+
RecDataTypeInfo.new('REAL4', false, 4 ), # 4
|
348
|
+
RecDataTypeInfo.new('REAL8', true, 8 ), # 5
|
349
|
+
RecDataTypeInfo.new('ASCII', true, 0 ), # 6 ; string len is variable
|
350
|
+
]
|
351
|
+
end
|
352
|
+
|
353
|
+
# Returns the name for given record type if it is found; if not, then
|
354
|
+
# the record number formatted as a String is returned
|
355
|
+
def grt_name(grt_number)
|
356
|
+
if grt_number.class == Fixnum
|
357
|
+
if (0..RECORD_INFO.length-1).member?(grt_number)
|
358
|
+
RECORD_INFO[grt_number].name
|
359
|
+
else
|
360
|
+
grt_number.to_s
|
361
|
+
end
|
362
|
+
else
|
363
|
+
grt_number.inspect
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
# Returns the name for given record data type if it is found; if not, then
|
368
|
+
# the record data type number formatted as a String is returned
|
369
|
+
def gdt_name(gdt_number)
|
370
|
+
if gdt_number.class == Fixnum
|
371
|
+
if (0..DATATYPE_INFO.length-1).member?(gdt_number)
|
372
|
+
DATATYPE_INFO[gdt_number].name
|
373
|
+
else
|
374
|
+
gdt_number.to_s
|
375
|
+
end
|
376
|
+
else
|
377
|
+
gdt_number.inspect
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
module_function :grt_name
|
382
|
+
module_function :gdt_name
|
383
|
+
|
384
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'gdsii/record/datatypes/data'
|
2
|
+
|
3
|
+
module Gdsii
|
4
|
+
|
5
|
+
module RecData
|
6
|
+
|
7
|
+
#
|
8
|
+
# Class for ASCII data type
|
9
|
+
#
|
10
|
+
class Ascii < Data
|
11
|
+
|
12
|
+
# Value is an array of strings. Most Gdsii::RecData::Ascii records only
|
13
|
+
# have a single value.
|
14
|
+
attr_reader :value
|
15
|
+
|
16
|
+
# Construct an Gdsii::RecData::Ascii data object. The value is an array
|
17
|
+
# of strings (String).
|
18
|
+
def initialize(value, record=nil)
|
19
|
+
super(GDT_ASCII, value, record)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Set the value for this object; verify that the value items are of
|
23
|
+
# type String (or at least can be coerced using "to_s").
|
24
|
+
def value=(value)
|
25
|
+
@value = Data.coerce_value(value, String, :to_s)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the size of the record *data* in bytes. This will *always*
|
29
|
+
# return an even-length (multiple of 2) number since odd-length GDSII
|
30
|
+
# strings are padded with null characters when written to a file. If the
|
31
|
+
# record type has a specific size (i.e. Gdsii::GRT_FONTS and
|
32
|
+
# Gdsii::GRT_REFLIBS) then that size will automatically be used for each
|
33
|
+
# element in the value array.
|
34
|
+
def byte_size()
|
35
|
+
if @record and RECORD_INFO[@record.type].size > 0 then
|
36
|
+
RECORD_INFO[@record.type].size * @value.length
|
37
|
+
else
|
38
|
+
sum = 0
|
39
|
+
@value.each do |val|
|
40
|
+
sum += (val.length % 2 == 0) ? val.length : val.length + 1
|
41
|
+
end
|
42
|
+
sum
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return value with stripped off trailing null characters (which are
|
47
|
+
# present when reading this record from a file). For example, assuming
|
48
|
+
# that a record is read from a file and is a null padded string
|
49
|
+
# "hello\0", then:
|
50
|
+
#
|
51
|
+
# record.inspect #=> ["hello\0"]
|
52
|
+
# record.unpad.inspect #=> ["hello"]
|
53
|
+
#
|
54
|
+
def unpad()
|
55
|
+
new_arr = []
|
56
|
+
@value.each do |string|
|
57
|
+
string = string.dup # to avoid changing the original
|
58
|
+
while (string[-1] == 0)
|
59
|
+
string.chop!
|
60
|
+
end
|
61
|
+
new_arr.push string
|
62
|
+
end
|
63
|
+
new_arr
|
64
|
+
end
|
65
|
+
|
66
|
+
# Same as #unpad but modifies the value of this object.
|
67
|
+
def unpad!()
|
68
|
+
@value = unpad
|
69
|
+
end
|
70
|
+
|
71
|
+
# Pad the value with a null character if the string is odd (but do not
|
72
|
+
# change the value itself). If a desired string length is given, then
|
73
|
+
# the string will be padded by the length given. For example, if we
|
74
|
+
# created a record with the string "hello", then padding will add a
|
75
|
+
# null character since "hello" has an odd number of characters:
|
76
|
+
#
|
77
|
+
# record = Gdsii::RecType::Ascii.new(["hello"])
|
78
|
+
# record.pad.inspect #=> ["hello\0"]
|
79
|
+
#
|
80
|
+
def pad(str_length=nil)
|
81
|
+
new_arr = []
|
82
|
+
@value.each_with_index do |string, i|
|
83
|
+
string = string.dup # to avoid changing the original
|
84
|
+
if str_length.nil? then
|
85
|
+
# pad if the string is odd or use the size property if there is a
|
86
|
+
# predefined size
|
87
|
+
if @record and (size=RECORD_INFO[@record.type].size) > 0 then
|
88
|
+
new_arr.push [string].pack("a#{size}")
|
89
|
+
elsif (len=string.length)%2 == 1 then
|
90
|
+
new_arr.push [string].pack("a#{len+1}")
|
91
|
+
else
|
92
|
+
new_arr.push string
|
93
|
+
end
|
94
|
+
else
|
95
|
+
# A desired string length was given; ensure that the requested
|
96
|
+
# length is a multiple of 2 and is not less than the string given.
|
97
|
+
if str_length%2 == 1 then
|
98
|
+
raise ArgumentError,
|
99
|
+
"Desired string length must be a multiple of 2"
|
100
|
+
elsif str_length < string.length then
|
101
|
+
raise ArgumentError,
|
102
|
+
"Desired string length given #{str_length} is less than actual string length #{string.length}"
|
103
|
+
else
|
104
|
+
new_arr.push [string].pack("a#{str_length}")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
new_arr
|
109
|
+
end
|
110
|
+
|
111
|
+
# Same as #pad except the value of this object is modified.
|
112
|
+
def pad!(str_length=nil)
|
113
|
+
@value = pad(str_length)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Reads an ASCII record from the given file for the length of bytes
|
117
|
+
# given and returns a new Gdsii::RecData::Ascii object.
|
118
|
+
def Ascii.read(file, byte_count)
|
119
|
+
# Verify byte count is even
|
120
|
+
if byte_count%2 == 1 then
|
121
|
+
raise ArgumentError,
|
122
|
+
"GDT_ASCII records must have an even length; requested: #{byte_count}"
|
123
|
+
end
|
124
|
+
|
125
|
+
# read the string in; unpad; and return the new Gdsii::Ascii object
|
126
|
+
raw = file.read(byte_count)
|
127
|
+
string = raw.unpack("a#{byte_count}")
|
128
|
+
data = Ascii.new(string)
|
129
|
+
data.unpad!
|
130
|
+
end
|
131
|
+
|
132
|
+
# Writes the string values in this Gdsii::RecData::Ascii object to the
|
133
|
+
# given file as a GDSII ASCII record.
|
134
|
+
def write(file)
|
135
|
+
padded_str = self.pad
|
136
|
+
file.write padded_str
|
137
|
+
end
|
138
|
+
|
139
|
+
# Joins all strings in the array with spaces and returns the joined
|
140
|
+
# string.
|
141
|
+
def to_s(); self.unpad[0] end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|