snow-data 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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