snow-data 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/snow-data/c_struct.rb +103 -17
- data/lib/snow-data/c_struct/builder.rb +160 -10
- data/lib/snow-data/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4070dd82808151a7dcdd5e41c49fcd14e2dc7f14
|
4
|
+
data.tar.gz: dfaee4ffae49a3c3752f052a3089c6c0010afea2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 264998e906e61221c5f4ad786a32bcf647b895b02d2af123f244da4a488ced5c2fba8f145b55c877963fca7ff10c14a7022763e9b0a33ecc2a84dbaa6be4696d
|
7
|
+
data.tar.gz: 4b64d995bb9c8ad6c4f187cb919ed3e673b478bdb8d5fb95b93354a6887e2488e16488c3daca301a79545df4003b7e324bcda0599e4181c2fcdd18e162b16796
|
data/lib/snow-data/c_struct.rb
CHANGED
@@ -79,7 +79,7 @@ class CStruct
|
|
79
79
|
# CStruct.member_encoding(:foo, :float, 32, nil) # => "foo:float[32]:4"
|
80
80
|
#
|
81
81
|
def self.member_encoding(name, type, length: 1, alignment: nil)
|
82
|
-
type = type.
|
82
|
+
type = type.to_sym
|
83
83
|
alignment = alignment || ALIGNMENTS[type] || ALIGNMENTS[:*]
|
84
84
|
raise ArgumentError, "Invalid length: #{length}. Must be > 0." if length < 1
|
85
85
|
raise ArgumentError, "Invalid alignment: #{alignment}. Must be a power of two." if ! power_of_two?(alignment)
|
@@ -111,6 +111,9 @@ class CStruct
|
|
111
111
|
(?<type_alignment_decl> \s* \: \s* # 4
|
112
112
|
(?<type_alignment> \d+ ) # 5
|
113
113
|
)?
|
114
|
+
(?<offset_decl> \s* @ \s* # 6
|
115
|
+
(?<offset> \d+ ) # 7
|
116
|
+
)?
|
114
117
|
\s* (?: ; | $ | \n) # terminator
|
115
118
|
}mx
|
116
119
|
|
@@ -280,7 +283,7 @@ class CStruct
|
|
280
283
|
define_method(setter) do |offset, data|
|
281
284
|
raise "Invalid value type, must be Data, but got #{data.class}" if ! data.kind_of?(Data)
|
282
285
|
local_addr = self.address + offset
|
283
|
-
if !data.respond_to?(:address) || local_addr != data.address
|
286
|
+
if ! data.respond_to?(:address) || local_addr != data.address
|
284
287
|
copy!(data, offset, 0, klass::SIZE)
|
285
288
|
end
|
286
289
|
|
@@ -332,11 +335,12 @@ class CStruct
|
|
332
335
|
# Encodings are how you define C structs using Snow::CStruct. It's a fairly
|
333
336
|
# simple string format, defined as such:
|
334
337
|
#
|
338
|
+
# offset ::= '@' integer
|
335
339
|
# length ::= '[' integer ']'
|
336
340
|
# alignment ::= ':' integer
|
337
341
|
# typename ::= ':' Name
|
338
342
|
# member_name ::= Name
|
339
|
-
# member_decl ::= member_name typename [ length ] [ alignment ]
|
343
|
+
# member_decl ::= member_name typename [ length ] [ alignment ] [ offset ]
|
340
344
|
#
|
341
345
|
# So, for example, the encoding string "foo: float[4]:8" defines a C struct
|
342
346
|
# with a single member, `foo`, which is an array of 4 32-bit floats with an
|
@@ -344,6 +348,12 @@ class CStruct
|
|
344
348
|
# size (e.g., "foo: float" would be algiend to 4 bytes) and all members have a
|
345
349
|
# length of 1 unless specified otherwise.
|
346
350
|
#
|
351
|
+
# Offsets should only be specified if you absolutely know what you're doing,
|
352
|
+
# otherwise you may break certain things (for example, native sizing on ints).
|
353
|
+
# In addition, an offset can be provided to simulate union-like behavior for
|
354
|
+
# some members, though you are better off using the Builder methods to define
|
355
|
+
# a union than you are via an encoding string.
|
356
|
+
#
|
347
357
|
# A list of all types follows, including their short and long names, and their
|
348
358
|
# corresponding types in C. Short names are only provided for convenience and
|
349
359
|
# are generally not too useful except for reducing string length. They're
|
@@ -423,23 +433,98 @@ class CStruct
|
|
423
433
|
raise ArgumentError, "wrong number of arguments (#{args.length} for 0..2)"
|
424
434
|
end
|
425
435
|
|
426
|
-
klass_name = klass_name.intern if klass_name
|
427
|
-
|
428
436
|
members = if block_given?
|
429
437
|
Builder.new(&block).member_info
|
430
438
|
else
|
431
439
|
decode_member_info(encoding)
|
432
440
|
end
|
433
441
|
|
434
|
-
|
442
|
+
__define_struct__(klass_name, build_struct_type(members))
|
443
|
+
end
|
435
444
|
|
436
|
-
klass = build_struct_type(members)
|
437
445
|
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
446
|
+
#
|
447
|
+
# :nodoc:
|
448
|
+
#
|
449
|
+
# Passes a block through to a Builder and provides a flag for whether the root
|
450
|
+
# level of the builder is a struct or union.
|
451
|
+
#
|
452
|
+
def self.__build_struct__(name, is_union, &block)
|
453
|
+
members = Builder.new(is_union: is_union, &block).member_info
|
454
|
+
__define_struct__(name, build_struct_type(members))
|
455
|
+
end
|
456
|
+
|
457
|
+
|
458
|
+
#
|
459
|
+
# call-seq:
|
460
|
+
# union { ... } => Class
|
461
|
+
# union(name) { ... } => Class
|
462
|
+
#
|
463
|
+
# Defines a union with a block for declaring the members of the union using
|
464
|
+
# Builder methods. Unions are basically structs whose root members occupy the
|
465
|
+
# same or adjacent locations in memory. You may use Builder#struct to define
|
466
|
+
# multiple internal unnamed structs inside a union.
|
467
|
+
#
|
468
|
+
# If a name is provided, the resulting class will be added as a constant under
|
469
|
+
# CStruct and it will be added as a type recognized in other CStruct-defined
|
470
|
+
# structs and unions.
|
471
|
+
#
|
472
|
+
# ### Example
|
473
|
+
#
|
474
|
+
# CStruct.union {
|
475
|
+
# # size, name, and some_value all share the same memory in the union
|
476
|
+
# # so set_name will modify size and some_value, and any transposition
|
477
|
+
# # of those names is also true.
|
478
|
+
# size_t :size
|
479
|
+
# uint32_t :name
|
480
|
+
# double :some_value
|
481
|
+
# }
|
482
|
+
#
|
483
|
+
def self.union(name = nil, &block)
|
484
|
+
__build_struct__(name, true, &block)
|
485
|
+
end
|
486
|
+
|
442
487
|
|
488
|
+
#
|
489
|
+
# call-seq:
|
490
|
+
# struct { ... } => Class
|
491
|
+
# struct(name) { ... } => Class
|
492
|
+
#
|
493
|
+
# Defines a struct with a block for declaring the members of the struct using
|
494
|
+
# Builder methods. Members of structs do not share space, unlike unions.
|
495
|
+
#
|
496
|
+
# If a name is provided, the resulting class will be added as a constant under
|
497
|
+
# CStruct and it will be added as a type recognized in other CStruct-defined
|
498
|
+
# structs and unions.
|
499
|
+
#
|
500
|
+
# ### Example
|
501
|
+
#
|
502
|
+
# CStruct.struct {
|
503
|
+
# # size, red, green, and blue all get their own memory in the struct,
|
504
|
+
# # one after the other. Modifying one value will not modify another.
|
505
|
+
# size_t :size
|
506
|
+
# uint16_t :red[256]
|
507
|
+
# uint16_t :green[256]
|
508
|
+
# uint16_t :blue[256]
|
509
|
+
# }
|
510
|
+
#
|
511
|
+
def self.struct(name = nil, &block)
|
512
|
+
__build_struct__(name, false, &block)
|
513
|
+
end
|
514
|
+
|
515
|
+
|
516
|
+
#
|
517
|
+
# :nodoc:
|
518
|
+
#
|
519
|
+
# Used by ::__build_struct__ and ::new to handle defining a struct class's
|
520
|
+
# constant and adding its type to those recognized by CStruct.
|
521
|
+
#
|
522
|
+
def self.__define_struct__(name, klass)
|
523
|
+
if name
|
524
|
+
name = name.to_sym
|
525
|
+
const_set(name, klass)
|
526
|
+
add_type(name, klass)
|
527
|
+
end
|
443
528
|
klass
|
444
529
|
end
|
445
530
|
|
@@ -455,12 +540,13 @@ class CStruct
|
|
455
540
|
total_size = 0
|
456
541
|
encoding.scan(ENCODING_REGEX).map do
|
457
542
|
|match|
|
458
|
-
name = match[0].
|
459
|
-
type = real_type_of(match[1].
|
543
|
+
name = match[0].to_sym
|
544
|
+
type = real_type_of(match[1].to_sym)
|
460
545
|
length = (match[3] || 1).to_i
|
461
546
|
align = (match[5] || ALIGNMENTS[type] || 1).to_i
|
462
547
|
size = SIZES[type] * length
|
463
|
-
offset =
|
548
|
+
offset = (match[7] || 0).to_i
|
549
|
+
offset += Memory.align_size(total_size, align) if ! match[7]
|
464
550
|
total_size = offset + size
|
465
551
|
|
466
552
|
StructMemberInfo[name, type, size, length, align, offset]
|
@@ -476,7 +562,7 @@ class CStruct
|
|
476
562
|
#
|
477
563
|
def self.encode_member_info(members)
|
478
564
|
members.map { |member|
|
479
|
-
"#{member.name}:#{member.type}[#{member.length}]:#{member.alignment}"
|
565
|
+
"#{member.name}:#{member.type}[#{member.length}]:#{member.alignment}@#{member.offset}"
|
480
566
|
}.join(?;)
|
481
567
|
end
|
482
568
|
|
@@ -498,8 +584,8 @@ class CStruct
|
|
498
584
|
members.freeze
|
499
585
|
|
500
586
|
# Get the alignment, size, aligned size, and encoding of the struct.
|
501
|
-
alignment = members.map
|
502
|
-
type_size = members.
|
587
|
+
alignment = members.map(&:alignment).max
|
588
|
+
type_size = members.map { |m| m.offset + m.size }.max
|
503
589
|
aligned_size = Memory.align_size(type_size, alignment)
|
504
590
|
# Oddly enough, it would be easier to pass the encoding string into this
|
505
591
|
# function, but then it would ruin the nice little thing I have going where
|
@@ -1,12 +1,35 @@
|
|
1
|
+
# This file is part of ruby-snowdata.
|
2
|
+
# Copyright (c) 2013 Noel Raymond Cower. All rights reserved.
|
3
|
+
# See COPYING for license details.
|
4
|
+
|
1
5
|
require 'set'
|
2
6
|
|
3
7
|
module Snow ; end
|
4
8
|
class Snow::CStruct ; end
|
5
9
|
|
10
|
+
#
|
11
|
+
# Utility classes used by CStruct to create member info structs. Exposed to the
|
12
|
+
# user via instance_exec blocks for declaring struct/union members.
|
13
|
+
#
|
14
|
+
# See CStruct::new, CStruct::struct, or CStruct::union.
|
15
|
+
#
|
6
16
|
class Snow::CStruct::Builder
|
7
17
|
|
18
|
+
#
|
19
|
+
# Struct describing a level of a C struct. Contains a flag for whether the
|
20
|
+
# level is a union, its offset, alignment, size, and its members (which may
|
21
|
+
# include descendant levels).
|
22
|
+
#
|
23
|
+
MemberStackLevel = Struct.new(:is_union, :offset, :alignment, :size, :members)
|
24
|
+
|
25
|
+
|
26
|
+
#
|
27
|
+
# A Set of symbols for all types whose declaration methods have already been
|
28
|
+
# defined by ::flush_type_methods!.
|
29
|
+
#
|
8
30
|
@@defined_types = Set.new
|
9
31
|
|
32
|
+
|
10
33
|
#
|
11
34
|
# call-seq:
|
12
35
|
# new => Builder
|
@@ -16,23 +39,69 @@ class Snow::CStruct::Builder
|
|
16
39
|
# given, then it will be instance_exec'd, allowing you to easily call any
|
17
40
|
# declaration methods on the builder instance.
|
18
41
|
#
|
19
|
-
def initialize(&block)
|
20
|
-
@
|
21
|
-
@
|
42
|
+
def initialize(is_union: false, &block)
|
43
|
+
@member_names = Set.new
|
44
|
+
@level = MemberStackLevel[!!is_union, 0, 1, 0, []]
|
22
45
|
instance_exec(&block) if block_given?
|
46
|
+
@members = []
|
47
|
+
self.class.adjust_level(@level)
|
48
|
+
@level.members.each { |member| member.freeze }
|
49
|
+
@members = self.class.flatten_level(@level)
|
50
|
+
@members.each(&:freeze)
|
51
|
+
@members.freeze
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
#
|
56
|
+
# Flattens a MemberStackLevel's members into a single array and returns an
|
57
|
+
# array of StructMemberInfo objects.
|
58
|
+
#
|
59
|
+
def self.flatten_level(level, info_buffer = [])
|
60
|
+
level.members.each { |m|
|
61
|
+
if m.kind_of?(MemberStackLevel)
|
62
|
+
flatten_level(m, info_buffer)
|
63
|
+
else
|
64
|
+
info_buffer.push(m)
|
65
|
+
end
|
66
|
+
}
|
67
|
+
info_buffer
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
#
|
72
|
+
# Iterates over a level's members, including sub-levels, and adjusts their
|
73
|
+
# offsets and the level's size accordingly. This is essentially the processing
|
74
|
+
# phase done to give members their offsets and levels their sizes.
|
75
|
+
#
|
76
|
+
def self.adjust_level(level, start_at: 0)
|
77
|
+
base_offset = offset = ::Snow::Memory.align_size(start_at, level.alignment)
|
78
|
+
level.size = 0
|
79
|
+
level.members.each do |m|
|
80
|
+
m.offset = offset = ::Snow::Memory.align_size(offset, m.alignment)
|
81
|
+
if m.kind_of?(MemberStackLevel)
|
82
|
+
adjust_level(m, start_at: offset)
|
83
|
+
end
|
84
|
+
if level.is_union
|
85
|
+
level.size = [level.size, (m.offset + m.size) - base_offset].max
|
86
|
+
else
|
87
|
+
level.size = (m.offset + m.size) - base_offset
|
88
|
+
offset += m.size
|
89
|
+
end
|
90
|
+
end
|
23
91
|
end
|
24
92
|
|
93
|
+
|
25
94
|
#
|
26
95
|
# call-seq:
|
27
96
|
# member_info => [StructMemberInfo]
|
28
97
|
#
|
29
|
-
# Returns the StructMemberInfo array for the builder.
|
30
|
-
# you need to modify it, and the same goes for its elements.
|
98
|
+
# Returns the StructMemberInfo array for the builder.
|
31
99
|
#
|
32
100
|
def member_info
|
33
101
|
@members
|
34
102
|
end
|
35
103
|
|
104
|
+
|
36
105
|
#
|
37
106
|
# call-seq:
|
38
107
|
# flush_type_methods! => self
|
@@ -55,7 +124,13 @@ class Snow::CStruct::Builder
|
|
55
124
|
method_name = method_name.to_sym
|
56
125
|
|
57
126
|
__send__(:define_method, method_name) do | name, lengths = 1, align: nil |
|
127
|
+
level = instance_variable_get(:@level)
|
128
|
+
|
129
|
+
member_names = instance_variable_get(:@member_names)
|
130
|
+
|
58
131
|
name = name.to_sym
|
132
|
+
raise ArgumentError, "#{name} redefined in struct" if member_names.include?(name)
|
133
|
+
member_names.add(name)
|
59
134
|
|
60
135
|
length = case lengths
|
61
136
|
when Integer, Fixnum then lengths
|
@@ -69,15 +144,21 @@ class Snow::CStruct::Builder
|
|
69
144
|
align = (align || ::Snow::CStruct::ALIGNMENTS[type_name]).to_i
|
70
145
|
raise "Nil alignment for type #{type_name}" if align.nil?
|
71
146
|
|
72
|
-
|
73
|
-
offset = ::Snow::Memory.align_size(
|
74
|
-
|
147
|
+
base_offset = level.offset
|
148
|
+
offset = ::Snow::Memory.align_size(base_offset, align)
|
149
|
+
level.offset = offset + size unless level.is_union
|
75
150
|
|
76
151
|
member_info = ::Snow::CStruct::StructMemberInfo[
|
77
152
|
name, type_name, size, length, align, offset]
|
78
153
|
|
79
|
-
|
80
|
-
|
154
|
+
level.alignment = [level.alignment, align].max
|
155
|
+
if level.is_union
|
156
|
+
level.size = [level.size, size].max
|
157
|
+
else
|
158
|
+
level.size += (offset - base_offset) + size
|
159
|
+
end
|
160
|
+
|
161
|
+
level.members.push(member_info)
|
81
162
|
end # define_method(type_name)
|
82
163
|
|
83
164
|
@@defined_types.add(type_name)
|
@@ -91,4 +172,73 @@ class Snow::CStruct::Builder
|
|
91
172
|
self
|
92
173
|
end # flush_type_methods!
|
93
174
|
|
175
|
+
|
176
|
+
#
|
177
|
+
# Creates a new member level for the builder and instance_exec-s the block,
|
178
|
+
# then passes its alignment onto its ancestor level.
|
179
|
+
#
|
180
|
+
def __do_level__(align: nil, is_union: false, &block)
|
181
|
+
parent = @level
|
182
|
+
next_level = MemberStackLevel[!!is_union, 0, 1, 0, []]
|
183
|
+
@level = next_level
|
184
|
+
|
185
|
+
self.instance_exec(&block)
|
186
|
+
|
187
|
+
next_level.alignment = align || next_level.alignment
|
188
|
+
parent.alignment = [parent.alignment, next_level.alignment].max
|
189
|
+
|
190
|
+
@level = parent
|
191
|
+
parent.members.push(next_level)
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
#
|
196
|
+
# call-seq:
|
197
|
+
# union { ... }
|
198
|
+
# union(align: nil) { ... }
|
199
|
+
#
|
200
|
+
# For the scope of the block, any members declared are considered union
|
201
|
+
# members, as opposed to struct members. Each member of a union occupies the
|
202
|
+
# same space as other members of the union, though their offsets may differ
|
203
|
+
# if their alignments differ as well.
|
204
|
+
#
|
205
|
+
# If no alignment is specified for the union, its base offset will be aligned
|
206
|
+
# to that of the member with the largest alignment. Otherwise, if an alignment
|
207
|
+
# is specified, members may not occupy the same offsets relative to the
|
208
|
+
# beginning of the union.
|
209
|
+
#
|
210
|
+
# For example, if a union with an alignment of 4 has uint32_t and uint64_t
|
211
|
+
# members with default alignments with a starting offset of 4, the uint32_t
|
212
|
+
# member will be located at offset 4, while the uint64_t member will be at
|
213
|
+
# offset 8. As such, it's best to leave union alignments at their default
|
214
|
+
# unless absolutely necessary.
|
215
|
+
#
|
216
|
+
def union(align: nil, &block)
|
217
|
+
__do_level__(align: align, is_union: true, &block)
|
218
|
+
end
|
219
|
+
|
220
|
+
|
221
|
+
#
|
222
|
+
# call-seq:
|
223
|
+
# struct { ... }
|
224
|
+
# struct(align: nil) { ... }
|
225
|
+
#
|
226
|
+
# For the scope of the block, any members declared are considered struct
|
227
|
+
# members, as opposed to union members.
|
228
|
+
#
|
229
|
+
# Each member of a struct occupies its own space inside the struct, unlike a
|
230
|
+
# union (where each member occupies either the same or adjacent space in the
|
231
|
+
# union).
|
232
|
+
#
|
233
|
+
# Unless an alignment is specified, the default alignment of a struct is that
|
234
|
+
# of the largest alignment of all its members. If specifying an alignment,
|
235
|
+
# keep in mind that the members of the struct may need to also be manually
|
236
|
+
# aligned, otherwise the first member may be preceeded by padding bytes
|
237
|
+
# regardless of the start of the struct. See #union for more information on
|
238
|
+
# alignment oddities.
|
239
|
+
#
|
240
|
+
def struct(align: nil, &block)
|
241
|
+
__do_level__(align: align, is_union: false, &block)
|
242
|
+
end
|
243
|
+
|
94
244
|
end
|
data/lib/snow-data/version.rb
CHANGED