crystalruby 0.2.3 → 0.3.1

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.
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