crystalruby 0.2.3 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2 -0
  3. data/Dockerfile +23 -2
  4. data/README.md +395 -198
  5. data/Rakefile +4 -3
  6. data/crystalruby.gemspec +2 -2
  7. data/examples/adder/adder.rb +1 -1
  8. data/exe/crystalruby +1 -0
  9. data/lib/crystalruby/adapter.rb +143 -73
  10. data/lib/crystalruby/arc_mutex.rb +47 -0
  11. data/lib/crystalruby/compilation.rb +32 -3
  12. data/lib/crystalruby/config.rb +41 -37
  13. data/lib/crystalruby/function.rb +216 -73
  14. data/lib/crystalruby/library.rb +157 -51
  15. data/lib/crystalruby/reactor.rb +63 -44
  16. data/lib/crystalruby/source_reader.rb +92 -0
  17. data/lib/crystalruby/template.rb +16 -5
  18. data/lib/crystalruby/templates/function.cr +11 -10
  19. data/lib/crystalruby/templates/index.cr +53 -66
  20. data/lib/crystalruby/templates/inline_chunk.cr +1 -1
  21. data/lib/crystalruby/templates/ruby_interface.cr +34 -0
  22. data/lib/crystalruby/templates/top_level_function.cr +62 -0
  23. data/lib/crystalruby/templates/top_level_ruby_interface.cr +33 -0
  24. data/lib/crystalruby/typebuilder.rb +11 -55
  25. data/lib/crystalruby/typemaps.rb +92 -67
  26. data/lib/crystalruby/types/concerns/allocator.rb +80 -0
  27. data/lib/crystalruby/types/fixed_width/named_tuple.cr +80 -0
  28. data/lib/crystalruby/types/fixed_width/named_tuple.rb +86 -0
  29. data/lib/crystalruby/types/fixed_width/proc.cr +45 -0
  30. data/lib/crystalruby/types/fixed_width/proc.rb +79 -0
  31. data/lib/crystalruby/types/fixed_width/tagged_union.cr +53 -0
  32. data/lib/crystalruby/types/fixed_width/tagged_union.rb +113 -0
  33. data/lib/crystalruby/types/fixed_width/tuple.cr +82 -0
  34. data/lib/crystalruby/types/fixed_width/tuple.rb +92 -0
  35. data/lib/crystalruby/types/fixed_width.cr +138 -0
  36. data/lib/crystalruby/types/fixed_width.rb +205 -0
  37. data/lib/crystalruby/types/primitive.cr +21 -0
  38. data/lib/crystalruby/types/primitive.rb +117 -0
  39. data/lib/crystalruby/types/primitive_types/bool.cr +34 -0
  40. data/lib/crystalruby/types/primitive_types/bool.rb +11 -0
  41. data/lib/crystalruby/types/primitive_types/nil.cr +35 -0
  42. data/lib/crystalruby/types/primitive_types/nil.rb +16 -0
  43. data/lib/crystalruby/types/primitive_types/numbers.cr +37 -0
  44. data/lib/crystalruby/types/primitive_types/numbers.rb +28 -0
  45. data/lib/crystalruby/types/primitive_types/symbol.cr +55 -0
  46. data/lib/crystalruby/types/primitive_types/symbol.rb +35 -0
  47. data/lib/crystalruby/types/primitive_types/time.cr +35 -0
  48. data/lib/crystalruby/types/primitive_types/time.rb +25 -0
  49. data/lib/crystalruby/types/type.cr +64 -0
  50. data/lib/crystalruby/types/type.rb +249 -30
  51. data/lib/crystalruby/types/variable_width/array.cr +74 -0
  52. data/lib/crystalruby/types/variable_width/array.rb +88 -0
  53. data/lib/crystalruby/types/variable_width/hash.cr +146 -0
  54. data/lib/crystalruby/types/variable_width/hash.rb +117 -0
  55. data/lib/crystalruby/types/variable_width/string.cr +36 -0
  56. data/lib/crystalruby/types/variable_width/string.rb +18 -0
  57. data/lib/crystalruby/types/variable_width.cr +23 -0
  58. data/lib/crystalruby/types/variable_width.rb +46 -0
  59. data/lib/crystalruby/types.rb +32 -13
  60. data/lib/crystalruby/version.rb +2 -2
  61. data/lib/crystalruby.rb +13 -6
  62. metadata +42 -22
  63. data/lib/crystalruby/types/array.rb +0 -15
  64. data/lib/crystalruby/types/bool.rb +0 -3
  65. data/lib/crystalruby/types/hash.rb +0 -17
  66. data/lib/crystalruby/types/named_tuple.rb +0 -28
  67. data/lib/crystalruby/types/nil.rb +0 -3
  68. data/lib/crystalruby/types/numbers.rb +0 -5
  69. data/lib/crystalruby/types/string.rb +0 -3
  70. data/lib/crystalruby/types/symbol.rb +0 -3
  71. data/lib/crystalruby/types/time.rb +0 -8
  72. data/lib/crystalruby/types/tuple.rb +0 -17
  73. data/lib/crystalruby/types/type_serializer/json.rb +0 -41
  74. data/lib/crystalruby/types/type_serializer.rb +0 -37
  75. data/lib/crystalruby/types/typedef.rb +0 -57
  76. data/lib/crystalruby/types/union_type.rb +0 -43
  77. data/lib/module.rb +0 -3
@@ -0,0 +1,37 @@
1
+ class <%= base_crystal_class_name %> < CrystalRuby::Types::Primitive
2
+
3
+ def initialize(value : <%= native_type_expr %>)
4
+ @value = value
5
+ end
6
+
7
+ def initialize(ptr : Pointer(::UInt8))
8
+ @value = ptr.as(Pointer( <%= native_type_expr %>))[0]
9
+ end
10
+
11
+ def value
12
+ @value
13
+ end
14
+
15
+ def ==(other : <%= native_type_expr %>)
16
+ value == other
17
+ end
18
+
19
+ def self.memsize
20
+ <%= memsize %>
21
+ end
22
+
23
+ def value=(value : <%= native_type_expr %>)
24
+ @value = value
25
+ end
26
+
27
+ def self.copy_to!(value : <%= native_type_expr %>, ptr : Pointer(::UInt8))
28
+ ptr.as(Pointer( <%= native_type_expr %>))[0] = value
29
+ end
30
+
31
+ # Write a data type into a pointer at a given index
32
+ # (Type can be a byte-array, pointer or numeric type)
33
+ def self.write_single(pointer : Pointer(::UInt8), value)
34
+ pointer.as(Pointer( <%= native_type_expr %>)).value = value
35
+ end
36
+
37
+ end
@@ -0,0 +1,28 @@
1
+ module CrystalRuby::Types
2
+ %i[UInt8 UInt16 UInt32 UInt64 Int8 Int16 Int32 Int64 Float32 Float64].each do |type_name|
3
+ ffi_type = CrystalRuby::Typemaps::FFI_TYPE_MAP.fetch(type_name.to_s)
4
+ const_set(type_name, Primitive.build(type_name, convert_if: [::Numeric], ffi_type: ffi_type) do
5
+ def value=(val)
6
+ raise "Expected a numeric value, got #{val}" unless val.is_a?(::Numeric)
7
+
8
+ super(typename.to_s.start_with?("Float") ? val.to_f : val.to_i)
9
+ end
10
+
11
+ def value(native: false)
12
+ @value
13
+ end
14
+
15
+ def self.from_ffi_array_repr(value)
16
+ value
17
+ end
18
+
19
+ def self.numeric?
20
+ true
21
+ end
22
+
23
+ def self.template_name
24
+ "Numbers"
25
+ end
26
+ end)
27
+ end
28
+ end
@@ -0,0 +1,55 @@
1
+ class <%= base_crystal_class_name %> < CrystalRuby::Types::Primitive
2
+
3
+ def initialize(value : ::Symbol)
4
+ @value = 0.to_u32
5
+ self.value = value
6
+ end
7
+
8
+ def initialize(value : UInt32)
9
+ @value = value
10
+ end
11
+
12
+ def initialize(ptr : Pointer(::UInt8))
13
+ initialize(ptr.as(Pointer(::UInt32))[0])
14
+ end
15
+
16
+ def ==(other : ::Symbol)
17
+ value == other
18
+ end
19
+
20
+ def value=(value : ::Symbol)
21
+ case value
22
+ <% allowed_values.each_with_index do |v, i| %>
23
+ when :<%= v %> then @value = <%= i %>.to_u32
24
+ <% end %>
25
+ else raise "Symbol must be one of <%= allowed_values %>. Got #{value}"
26
+ end
27
+ end
28
+
29
+ def value : ::Symbol
30
+ case @value
31
+ <% allowed_values.each_with_index do |v, i| %>
32
+ when <%= i %> then :<%= v %>
33
+ <% end %>
34
+ else raise "Symbol must be one of <%= allowed_values %>. Got #{value}"
35
+ end
36
+ end
37
+
38
+ def self.copy_to!(value : ::Symbol, ptr : Pointer(::UInt8))
39
+ ptr.as(Pointer(::UInt32))[0] = new(value).return_value
40
+ end
41
+
42
+ def self.memsize
43
+ <%= memsize %>
44
+ end
45
+
46
+ def self.write_single(pointer : Pointer(::UInt8), value)
47
+ as_uint32 = case value
48
+ <% allowed_values.each_with_index do |v, i| %>
49
+ when :<%= v %> then <%= i %>.to_u32
50
+ <% end %>
51
+ else raise "Symbol must be one of <%= allowed_values %>. Got #{value}"
52
+ end
53
+ pointer.as(Pointer(::UInt32)).value = as_uint32
54
+ end
55
+ end
@@ -0,0 +1,35 @@
1
+ module CrystalRuby::Types
2
+ Symbol = Primitive.build(
3
+ error: "Symbol CrystalRuby types should indicate a list of possible values shared between Crystal and Ruby. "\
4
+ "E.g. Symbol(:green, :blue, :orange). If this list is not known at compile time, you should use a String instead."
5
+ )
6
+
7
+ def self.Symbol(*allowed_values)
8
+ raise "Symbol must have at least one value" if allowed_values.empty?
9
+
10
+ allowed_values.flatten!
11
+ raise "Symbol allowed values must all be symbols" unless allowed_values.all? { |v| v.is_a?(::Symbol) }
12
+
13
+ Primitive.build(:Symbol, ffi_type: :uint32, convert_if: [Root::String, Root::Symbol], memsize: 4) do
14
+ bind_local_vars!(%i[allowed_values], binding)
15
+ define_method(:value=) do |val|
16
+ val = allowed_values[val] if val.is_a?(::Integer) && val >= 0 && val < allowed_values.size
17
+ raise "Symbol must be one of #{allowed_values}" unless allowed_values.include?(val)
18
+
19
+ super(allowed_values.index(val))
20
+ end
21
+
22
+ define_singleton_method(:valid_cast?) do |raw|
23
+ super(raw) && allowed_values.include?(raw)
24
+ end
25
+
26
+ define_method(:value) do |native: false|
27
+ allowed_values[super()]
28
+ end
29
+
30
+ define_singleton_method(:type_digest) do
31
+ Digest::MD5.hexdigest(native_type_expr.to_s + allowed_values.map(&:to_s).join(","))
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ class <%= base_crystal_class_name %> < CrystalRuby::Types::Primitive
2
+
3
+ def initialize(time : ::Time)
4
+ @value = time.to_unix_ns / 1000_000_000.0
5
+ end
6
+
7
+ def initialize(ptr : Pointer(::UInt8))
8
+ @value = ptr.as(Pointer(::Float64)).value
9
+ end
10
+
11
+ def initialize(@value : ::Float64)
12
+ end
13
+
14
+ def ==(other : ::Time)
15
+ value == other
16
+ end
17
+
18
+ def value=(time : ::Time)
19
+ @value = time.to_unix_ns / 1000_000_000.0
20
+ end
21
+
22
+ def value : ::Time
23
+ ::Time.unix_ns((@value * 1000_000_000).to_i128)
24
+ end
25
+
26
+ def self.memsize
27
+ <%= memsize %>
28
+ end
29
+
30
+
31
+ def self.write_single(pointer : Pointer(::UInt8), time)
32
+ pointer.as(Pointer(::Float64)).value = time.to_unix_ns / 1000_000_000.0
33
+ end
34
+
35
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+
5
+ module CrystalRuby::Types
6
+ Time = Primitive.build(:Time, convert_if: [Root::Time, Root::String, DateTime], ffi_type: :double) do
7
+ def initialize(val = Root::Time.now)
8
+ super
9
+ end
10
+
11
+ def value=(val)
12
+ super(
13
+ if val.respond_to?(:to_time)
14
+ val.to_time.to_f
15
+ else
16
+ val.respond_to?(:to_f) ? val.to_f : 0
17
+ end
18
+ )
19
+ end
20
+
21
+ def value(native: false)
22
+ ::Time.at(super)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,64 @@
1
+ module CrystalRuby
2
+ module Types
3
+ class Type
4
+ property memory : Pointer(::UInt8) = Pointer(::UInt8).null
5
+
6
+ macro method_missing(call)
7
+ current_value = self.native
8
+ current_hash = current_value.hash
9
+ return_value = current_value.{{ call }}
10
+
11
+ if current_hash != current_value.hash
12
+ self.value = current_value
13
+ end
14
+ return_value
15
+ end
16
+
17
+ def to_s
18
+ native.to_s
19
+ end
20
+
21
+ def synchronize
22
+ CrystalRuby.synchronize do
23
+ yield
24
+ end
25
+ end
26
+
27
+ def self.synchronize
28
+ yield
29
+ end
30
+
31
+ def variable_width?
32
+ false
33
+ end
34
+
35
+ def return_value
36
+ memory
37
+ end
38
+
39
+ def self.free(memory : Pointer(::UInt8))
40
+ LibC.free(memory)
41
+ end
42
+
43
+ def self.malloc(size : Int) : Pointer(::UInt8)
44
+ LibC.calloc(size, 1).as(Pointer(::UInt8))
45
+ end
46
+
47
+ def malloc(memsize)
48
+ self.class.malloc(memsize)
49
+ end
50
+
51
+ def self.each_child_address(pointer : Pointer(::UInt8), &block : Pointer(::UInt8) -> Nil)
52
+ # Do nothing
53
+ end
54
+
55
+ def self.fetch_multi!(pointer : Pointer(::UInt8), size)
56
+ size.times.map { |i| fetch_single(pointer + i * refsize) }.to_a
57
+ end
58
+
59
+ def self.fetch_multi_native!(pointer : Pointer(::UInt8), size)
60
+ size.times.map { |i| fetch_single(pointer + i * refsize).native }.to_a
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,66 +1,285 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Can create complex structs in Ruby
4
+ # E.g.
5
+ # Types::MyType.new({a: 1, b: [2,3, {four: "five"}})
6
+ #
7
+ # Can read complex structs in Crystal.
8
+ # Expose Ruby setters to Crystal.
9
+ # - These accepts pointers and *copy* the values from them (so that they are governed by Ruby GC)
10
+ # - Safe to GC on Crystal side after call to setter
11
+ #
12
+ # All Structs are memory managed in Ruby.
13
+ # Parameters are temporarily stored in memory *before* being passed, then nilled (might be GC'd if we don't hold on to it elsewhere)
14
+ # Internal pointers are stored in complex types.
15
+ # Cannot create new structs in Crystal.
16
+ # All information passed from Crystal back to Ruby is either:
17
+ # - Direct memory access (for primitives)
18
+ # - Using setters + copy
19
+ #
20
+ # What if:
21
+ # - We ask Crystal to implement allocation og memory, and implement all of the setters
22
+ # - It stores the pointers to the memory in an address indexed hash
23
+ # - Ruby will reference count using AutoPointer
24
+ # - If reference count is 0, clear out hash in Crystal (Trigger GC)
25
+ # - Don't like this because, types can't stand alone in Ruby AND crosses FFI bridge many times to allocate complex struct.
26
+
27
+ require "forwardable"
28
+
1
29
  module CrystalRuby
30
+ InvalidCastError = Class.new(StandardError)
31
+
2
32
  module Types
3
33
  class Type
4
- attr_accessor :name, :error, :inner_types, :inner_keys, :accept_if, :convert
34
+ # TODO: Replace with pthread primitives and share
35
+ # with Crystal
36
+ ARC_MUTEX = CrystalRuby::ArcMutex.new
37
+
38
+ include Allocator
39
+ extend Typemaps
5
40
 
6
- def initialize(name, error: nil, inner_types: nil, inner_keys: nil, accept_if: [], &convert)
7
- self.name = name
8
- self.error = error
9
- self.inner_types = inner_types
10
- self.inner_keys = inner_keys
11
- self.accept_if = accept_if
12
- self.convert = convert
41
+ class << self
42
+ attr_accessor :typename, :ffi_type, :memsize, :convert_if, :inner_types
13
43
  end
14
44
 
15
- def union_types
16
- [self]
45
+ def_delegators :@class, :primitive?, :cast!, :type, :typename, :memsize, :refsize,
46
+ :ffi_type, :error, :inner_type, :inner_keys, :inner_types,
47
+ :write_mixed_byte_slices_to_uint8_array, :data_offset, :size_offset,
48
+ :union_types
49
+
50
+ attr_accessor :value, :memory
51
+
52
+ def initialize(_rbval)
53
+ @class = self.class
54
+ raise error if error
17
55
  end
18
56
 
19
- def valid?
20
- !error
57
+ def self.finalize(_memory)
58
+ ->(_) {}
21
59
  end
22
60
 
23
- def |(other)
24
- raise "Cannot union non-crystal type #{other}" unless other.is_a?(Type) || (
25
- other.is_a?(Class) && other.ancestors.include?(Typedef)
26
- )
61
+ def self.inspect_name
62
+ (name || "#{typename}").to_s.gsub(/^CrystalRuby::Types::[^::]+::/, "")
63
+ end
27
64
 
28
- UnionType.new(*union_types, *other.union_types)
65
+ def self.union_types
66
+ [self]
29
67
  end
30
68
 
31
- def type_expr
32
- inspect
69
+ def self.valid?
70
+ true
33
71
  end
34
72
 
35
- def inspect
73
+ def self.native_type_expr
36
74
  if !inner_types
37
- name
75
+ "::#{typename}"
38
76
  elsif !inner_keys
39
- "#{name}(#{inner_types.map(&:inspect).join(", ")})"
77
+ "::#{typename}(#{inner_types.map(&:native_type_expr).join(", ")})"
40
78
  else
41
- "#{name}(#{inner_keys.zip(inner_types).map { |k, v| "#{k}: #{v.inspect}" }.join(", ")})"
79
+ "::#{typename}(#{inner_keys.zip(inner_types).map { |k, v| "#{k}: #{v.native_type_expr}" }.join(", ")})"
42
80
  end
43
81
  end
44
82
 
45
- def interprets?(raw)
46
- accept_if.any? { |type| raw.is_a?(type) }
83
+ def self.named_type_expr
84
+ if !inner_types
85
+ "::#{name || typename}"
86
+ elsif !inner_keys
87
+ "::#{name || typename}(#{inner_types.map(&:named_type_expr).join(", ")})"
88
+ else
89
+ "::#{name || typename}(#{inner_keys.zip(inner_types).map { |k, v| "#{k}: #{v.named_type_expr}" }.join(", ")})"
90
+ end
91
+ end
92
+
93
+ def self.valid_cast?(raw)
94
+ raw.is_a?(self) || convert_if.any? { |type| raw.is_a?(type) }
95
+ end
96
+
97
+ def self.[](*value)
98
+ is_list_type = ancestors.any? { |a| a < CrystalRuby::Types::Array || a < CrystalRuby::Types::Tuple }
99
+ new(is_list_type ? value : value.first)
100
+ end
101
+
102
+ def self.anonymous?
103
+ name.nil? || name.start_with?("CrystalRuby::Types::")
104
+ end
105
+
106
+ def self.crystal_class_name
107
+ name || named_type_expr.split(",").join("_and_")
108
+ .split("|").join("_or_")
109
+ .split("(").join("_of_")
110
+ .gsub(/[^a-zA-Z0-9_]/, "")
111
+ .split("_")
112
+ .map(&:capitalize).join << "_#{type_digest[0..6]}"
47
113
  end
48
114
 
49
- def interpret!(raw)
50
- if interprets?(raw)
51
- convert ? convert.call(raw) : raw
115
+ def self.base_crystal_class_name
116
+ crystal_class_name.split("::").last
117
+ end
118
+
119
+ def value(native: false)
120
+ @value
121
+ end
122
+
123
+ def native
124
+ value(native: true)
125
+ end
126
+
127
+ def self.type_digest
128
+ Digest::MD5.hexdigest(native_type_expr.to_s)
129
+ end
130
+
131
+ def self.nested_types
132
+ [self, *(inner_types || []).map(&:nested_types)].flatten.uniq
133
+ end
134
+
135
+ def self.pointer_to_crystal_type_conversion(expr)
136
+ anonymous? ? "#{crystal_class_name}.new(#{expr}).native" : "#{crystal_class_name}.new(#{expr})"
137
+ end
138
+
139
+ def self.crystal_type_to_pointer_type_conversion(expr)
140
+ anonymous? ? "#{crystal_class_name}.new(#{expr}).return_value" : "#{expr}.return_value"
141
+ end
142
+
143
+ def self.template_name
144
+ typename || superclass.template_name
145
+ end
146
+
147
+ def self.type_defn
148
+ unless Template.const_defined?(template_name) && Template.const_get(template_name).is_a?(Template::Renderer)
149
+ raise "Template not found for #{template_name}"
150
+ end
151
+
152
+ Template.const_get(template_name).render(binding)
153
+ end
154
+
155
+ def self.numeric?
156
+ false
157
+ end
158
+
159
+ def self.primitive?
160
+ false
161
+ end
162
+
163
+ def self.variable_width?
164
+ false
165
+ end
166
+
167
+ def self.fixed_width?
168
+ false
169
+ end
170
+
171
+ def self.cast!(value)
172
+ value.is_a?(Type) ? value.value : value
173
+ end
174
+
175
+ def ==(other)
176
+ value(native: true) == (other.is_a?(Type) ? other.value(native: true) : other)
177
+ end
178
+
179
+ def nil?
180
+ value.nil?
181
+ end
182
+
183
+ def coerce(other)
184
+ [other, value]
185
+ end
186
+
187
+ def inspect
188
+ value.inspect
189
+ end
190
+
191
+ def self.from_ffi_array_repr(value)
192
+ anonymous? ? new(value).value : new(value)
193
+ end
194
+
195
+ def inner_value
196
+ @value
197
+ end
198
+
199
+ # Create a brand new copy of this object
200
+ def deep_dup
201
+ self.class.new(native)
202
+ end
203
+
204
+ # Create a new reference to this object.
205
+ def dup
206
+ self.class.new(@memory)
207
+ end
208
+
209
+ def method_missing(method, *args, &block)
210
+ v = begin
211
+ native
212
+ rescue StandardError
213
+ super
214
+ end
215
+ if v.respond_to?(method)
216
+ hash_before = v.hash
217
+ result = v.send(method, *args, &block)
218
+ if v.hash != hash_before
219
+ self.value = v
220
+ v.equal?(result) ? self : result
221
+ else
222
+ result
223
+ end
52
224
  else
53
- raise "Invalid deserialized value #{raw} for type #{inspect}"
225
+ super
226
+ end
227
+ end
228
+
229
+ def self.bind_local_vars!(variable_names, binding)
230
+ variable_names.each do |name|
231
+ define_singleton_method(name) do
232
+ binding.local_variable_get("#{name}")
233
+ end
234
+ define_method(name) do
235
+ binding.local_variable_get("#{name}")
236
+ end
54
237
  end
55
238
  end
56
239
 
240
+ def self.each_child_address(pointer); end
241
+
242
+ def item_size
243
+ inner_types.map(&:memsize).sum
244
+ end
245
+
246
+ def self.crystal_type
247
+ lib_type(ffi_type)
248
+ end
249
+
250
+ def self.|(other)
251
+ raise "Cannot union non-crystal type #{other}" unless other.is_a?(Class) && other.ancestors.include?(Type)
252
+
253
+ CrystalRuby::Types::TaggedUnion(*union_types, *other.union_types)
254
+ end
255
+
57
256
  def self.validate!(type)
58
- unless type.is_a?(Types::Type) || (type.is_a?(Class) && type.ancestors.include?(Types::Typedef))
257
+ unless type.is_a?(Class) && type.ancestors.include?(Types::Type)
59
258
  raise "Result #{type} is not a valid CrystalRuby type"
60
259
  end
61
260
 
62
261
  raise "Invalid type: #{type.error}" unless type.valid?
63
262
  end
263
+
264
+ def self.inner_type
265
+ inner_types.first
266
+ end
267
+
268
+ def self.type_expr
269
+ if !inner_types
270
+ inspect_name
271
+ elsif !anonymous?
272
+ name
273
+ elsif inner_keys
274
+ "#{inspect_name}(#{inner_keys.zip(inner_types).map { |k, v| "#{k}: #{v.inspect}" }.join(", ")})"
275
+ else
276
+ "#{inspect_name}(#{inner_types.map(&:inspect).join(", ")})"
277
+ end
278
+ end
279
+
280
+ def self.inspect
281
+ type_expr
282
+ end
64
283
  end
65
284
  end
66
285
  end
@@ -0,0 +1,74 @@
1
+ class <%= base_crystal_class_name %> < CrystalRuby::Types::VariableWidth
2
+
3
+ include Enumerable(<%= inner_type.native_type_expr %>)
4
+
5
+ def initialize(array : Array(<%= inner_type.native_type_expr %>))
6
+ @memory = malloc(data_offset + 8)
7
+ self.value = array
8
+ increment_ref_count!
9
+ end
10
+
11
+ def each
12
+ size.times do |i|
13
+ yield self[i].native
14
+ end
15
+ end
16
+
17
+ def ==(other : Array(<%= inner_type.native_type_expr %>))
18
+ native == other
19
+ end
20
+
21
+ def data_pointer
22
+ Pointer(::UInt8).new((@memory + data_offset).as(Pointer(::UInt64)).value)
23
+ end
24
+
25
+ def value=(array : Array(<%= inner_type.native_type_expr %>))
26
+ if self.ref_count > 0
27
+ self.class.decr_inner_ref_counts!(memory)
28
+ end
29
+ self.class.copy_to!(array, self.memory)
30
+ end
31
+
32
+ def <<(value : <%= inner_type.native_type_expr %>)
33
+ self.value = self.native + [value]
34
+ end
35
+
36
+ def []=(index : Int, value : <%= inner_type.native_type_expr %>)
37
+ index += size if index < 0
38
+ <%= inner_type.crystal_class_name %>.write_single(data_pointer + index * <%= inner_type.refsize %>, value)
39
+ end
40
+
41
+ def [](index : Int)
42
+ index += size if index < 0
43
+ <%= inner_type.crystal_class_name %>.fetch_single(data_pointer + index * <%= inner_type.refsize %>)
44
+ end
45
+
46
+ def self.copy_to!(array : Array(<%= inner_type.native_type_expr %>), memory : Pointer(::UInt8))
47
+ data_pointer = malloc(array.size * <%= inner_type.refsize %>)
48
+ array.size.times do |i|
49
+ <%= inner_type.crystal_class_name %>.write_single(data_pointer + i * <%= inner_type.refsize %>, array[i])
50
+ end
51
+ (memory+size_offset).as(Pointer(::UInt32)).value = array.size.to_u32
52
+ (memory+data_offset).as(Pointer(::UInt64)).value = data_pointer.address
53
+ end
54
+
55
+ def ==(other : <%= native_type_expr %>)
56
+ native == other
57
+ end
58
+
59
+ def value
60
+ <%= inner_type.crystal_class_name %>.fetch_multi!(data_pointer, size)
61
+ end
62
+
63
+ def native : ::Array(<%= inner_type.native_type_expr %>)
64
+ <%= inner_type.crystal_class_name %>.fetch_multi_native!(data_pointer, size)
65
+ end
66
+
67
+ def size
68
+ (@memory + self.class.size_offset).as(Pointer(::UInt32))[0]
69
+ end
70
+
71
+ def self.memsize
72
+ <%= memsize %>
73
+ end
74
+ end