crystalruby 0.2.3 → 0.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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2 -0
  3. data/README.md +389 -193
  4. data/Rakefile +4 -3
  5. data/crystalruby.gemspec +2 -2
  6. data/exe/crystalruby +1 -0
  7. data/lib/crystalruby/adapter.rb +131 -65
  8. data/lib/crystalruby/arc_mutex.rb +47 -0
  9. data/lib/crystalruby/compilation.rb +32 -3
  10. data/lib/crystalruby/config.rb +41 -37
  11. data/lib/crystalruby/function.rb +211 -68
  12. data/lib/crystalruby/library.rb +153 -48
  13. data/lib/crystalruby/reactor.rb +40 -23
  14. data/lib/crystalruby/source_reader.rb +86 -0
  15. data/lib/crystalruby/template.rb +16 -5
  16. data/lib/crystalruby/templates/function.cr +11 -10
  17. data/lib/crystalruby/templates/index.cr +53 -66
  18. data/lib/crystalruby/templates/inline_chunk.cr +1 -1
  19. data/lib/crystalruby/templates/ruby_interface.cr +34 -0
  20. data/lib/crystalruby/templates/top_level_function.cr +62 -0
  21. data/lib/crystalruby/templates/top_level_ruby_interface.cr +33 -0
  22. data/lib/crystalruby/typebuilder.rb +11 -55
  23. data/lib/crystalruby/typemaps.rb +92 -67
  24. data/lib/crystalruby/types/concerns/allocator.rb +80 -0
  25. data/lib/crystalruby/types/fixed_width/named_tuple.cr +80 -0
  26. data/lib/crystalruby/types/fixed_width/named_tuple.rb +86 -0
  27. data/lib/crystalruby/types/fixed_width/proc.cr +45 -0
  28. data/lib/crystalruby/types/fixed_width/proc.rb +79 -0
  29. data/lib/crystalruby/types/fixed_width/tagged_union.cr +53 -0
  30. data/lib/crystalruby/types/fixed_width/tagged_union.rb +109 -0
  31. data/lib/crystalruby/types/fixed_width/tuple.cr +82 -0
  32. data/lib/crystalruby/types/fixed_width/tuple.rb +92 -0
  33. data/lib/crystalruby/types/fixed_width.cr +138 -0
  34. data/lib/crystalruby/types/fixed_width.rb +205 -0
  35. data/lib/crystalruby/types/primitive.cr +21 -0
  36. data/lib/crystalruby/types/primitive.rb +117 -0
  37. data/lib/crystalruby/types/primitive_types/bool.cr +34 -0
  38. data/lib/crystalruby/types/primitive_types/bool.rb +11 -0
  39. data/lib/crystalruby/types/primitive_types/nil.cr +35 -0
  40. data/lib/crystalruby/types/primitive_types/nil.rb +16 -0
  41. data/lib/crystalruby/types/primitive_types/numbers.cr +37 -0
  42. data/lib/crystalruby/types/primitive_types/numbers.rb +28 -0
  43. data/lib/crystalruby/types/primitive_types/symbol.cr +55 -0
  44. data/lib/crystalruby/types/primitive_types/symbol.rb +35 -0
  45. data/lib/crystalruby/types/primitive_types/time.cr +35 -0
  46. data/lib/crystalruby/types/primitive_types/time.rb +25 -0
  47. data/lib/crystalruby/types/type.cr +64 -0
  48. data/lib/crystalruby/types/type.rb +239 -30
  49. data/lib/crystalruby/types/variable_width/array.cr +74 -0
  50. data/lib/crystalruby/types/variable_width/array.rb +88 -0
  51. data/lib/crystalruby/types/variable_width/hash.cr +146 -0
  52. data/lib/crystalruby/types/variable_width/hash.rb +117 -0
  53. data/lib/crystalruby/types/variable_width/string.cr +36 -0
  54. data/lib/crystalruby/types/variable_width/string.rb +18 -0
  55. data/lib/crystalruby/types/variable_width.cr +23 -0
  56. data/lib/crystalruby/types/variable_width.rb +46 -0
  57. data/lib/crystalruby/types.rb +32 -13
  58. data/lib/crystalruby/version.rb +2 -2
  59. data/lib/crystalruby.rb +13 -6
  60. metadata +41 -19
  61. data/lib/crystalruby/types/array.rb +0 -15
  62. data/lib/crystalruby/types/bool.rb +0 -3
  63. data/lib/crystalruby/types/hash.rb +0 -17
  64. data/lib/crystalruby/types/named_tuple.rb +0 -28
  65. data/lib/crystalruby/types/nil.rb +0 -3
  66. data/lib/crystalruby/types/numbers.rb +0 -5
  67. data/lib/crystalruby/types/string.rb +0 -3
  68. data/lib/crystalruby/types/symbol.rb +0 -3
  69. data/lib/crystalruby/types/time.rb +0 -8
  70. data/lib/crystalruby/types/tuple.rb +0 -17
  71. data/lib/crystalruby/types/type_serializer/json.rb +0 -41
  72. data/lib/crystalruby/types/type_serializer.rb +0 -37
  73. data/lib/crystalruby/types/typedef.rb +0 -57
  74. data/lib/crystalruby/types/union_type.rb +0 -43
  75. data/lib/module.rb +0 -3
@@ -1,66 +1,275 @@
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(", ")})"
80
+ end
81
+ end
82
+
83
+ def self.valid_cast?(raw)
84
+ raw.is_a?(self) || convert_if.any? { |type| raw.is_a?(type) }
85
+ end
86
+
87
+ def self.[](*value)
88
+ is_list_type = ancestors.any? { |a| a < CrystalRuby::Types::Array || a < CrystalRuby::Types::Tuple }
89
+ new(is_list_type ? value : value.first)
90
+ end
91
+
92
+ def self.anonymous?
93
+ name.nil? || name.start_with?("CrystalRuby::Types::")
94
+ end
95
+
96
+ def self.crystal_class_name
97
+ name || native_type_expr.split(",").join("_and_")
98
+ .split("|").join("_or_")
99
+ .split("(").join("_of_")
100
+ .gsub(/[^a-zA-Z0-9_]/, "")
101
+ .split("_")
102
+ .map(&:capitalize).join << "_#{type_digest[0..6]}"
103
+ end
104
+
105
+ def self.base_crystal_class_name
106
+ crystal_class_name.split("::").last
107
+ end
108
+
109
+ def value(native: false)
110
+ @value
111
+ end
112
+
113
+ def native
114
+ value(native: true)
115
+ end
116
+
117
+ def self.type_digest
118
+ Digest::MD5.hexdigest(native_type_expr.to_s)
119
+ end
120
+
121
+ def self.nested_types
122
+ [self, *(inner_types || []).map(&:nested_types)].flatten.uniq
123
+ end
124
+
125
+ def self.pointer_to_crystal_type_conversion(expr)
126
+ anonymous? ? "#{crystal_class_name}.new(#{expr}).native" : "#{crystal_class_name}.new(#{expr})"
127
+ end
128
+
129
+ def self.crystal_type_to_pointer_type_conversion(expr)
130
+ anonymous? ? "#{crystal_class_name}.new(#{expr}).return_value" : "#{expr}.return_value"
131
+ end
132
+
133
+ def self.template_name
134
+ typename || superclass.template_name
135
+ end
136
+
137
+ def self.type_defn
138
+ unless Template.const_defined?(template_name) && Template.const_get(template_name).is_a?(Template::Renderer)
139
+ raise "Template not found for #{template_name}"
42
140
  end
141
+
142
+ Template.const_get(template_name).render(binding)
143
+ end
144
+
145
+ def self.numeric?
146
+ false
147
+ end
148
+
149
+ def self.primitive?
150
+ false
43
151
  end
44
152
 
45
- def interprets?(raw)
46
- accept_if.any? { |type| raw.is_a?(type) }
153
+ def self.variable_width?
154
+ false
47
155
  end
48
156
 
49
- def interpret!(raw)
50
- if interprets?(raw)
51
- convert ? convert.call(raw) : raw
157
+ def self.fixed_width?
158
+ false
159
+ end
160
+
161
+ def self.cast!(value)
162
+ value.is_a?(Type) ? value.value : value
163
+ end
164
+
165
+ def ==(other)
166
+ value(native: true) == (other.is_a?(Type) ? other.value(native: true) : other)
167
+ end
168
+
169
+ def nil?
170
+ value.nil?
171
+ end
172
+
173
+ def coerce(other)
174
+ [other, value]
175
+ end
176
+
177
+ def inspect
178
+ value.inspect
179
+ end
180
+
181
+ def self.from_ffi_array_repr(value)
182
+ anonymous? ? new(value).value : new(value)
183
+ end
184
+
185
+ def inner_value
186
+ @value
187
+ end
188
+
189
+ # Create a brand new copy of this object
190
+ def deep_dup
191
+ self.class.new(native)
192
+ end
193
+
194
+ # Create a new reference to this object.
195
+ def dup
196
+ self.class.new(@memory)
197
+ end
198
+
199
+ def method_missing(method, *args, &block)
200
+ v = begin
201
+ native
202
+ rescue StandardError
203
+ super
204
+ end
205
+ if v.respond_to?(method)
206
+ hash_before = v.hash
207
+ result = v.send(method, *args, &block)
208
+ if v.hash != hash_before
209
+ self.value = v
210
+ v.equal?(result) ? self : result
211
+ else
212
+ result
213
+ end
52
214
  else
53
- raise "Invalid deserialized value #{raw} for type #{inspect}"
215
+ super
54
216
  end
55
217
  end
56
218
 
219
+ def self.bind_local_vars!(variable_names, binding)
220
+ variable_names.each do |name|
221
+ define_singleton_method(name) do
222
+ binding.local_variable_get("#{name}")
223
+ end
224
+ define_method(name) do
225
+ binding.local_variable_get("#{name}")
226
+ end
227
+ end
228
+ end
229
+
230
+ def self.each_child_address(pointer); end
231
+
232
+ def item_size
233
+ inner_types.map(&:memsize).sum
234
+ end
235
+
236
+ def self.crystal_type
237
+ lib_type(ffi_type)
238
+ end
239
+
240
+ def self.|(other)
241
+ raise "Cannot union non-crystal type #{other}" unless other.is_a?(Class) && other.ancestors.include?(Type)
242
+
243
+ CrystalRuby::Types::TaggedUnion(*union_types, *other.union_types)
244
+ end
245
+
57
246
  def self.validate!(type)
58
- unless type.is_a?(Types::Type) || (type.is_a?(Class) && type.ancestors.include?(Types::Typedef))
247
+ unless type.is_a?(Class) && type.ancestors.include?(Types::Type)
59
248
  raise "Result #{type} is not a valid CrystalRuby type"
60
249
  end
61
250
 
62
251
  raise "Invalid type: #{type.error}" unless type.valid?
63
252
  end
253
+
254
+ def self.inner_type
255
+ inner_types.first
256
+ end
257
+
258
+ def self.type_expr
259
+ if !inner_types
260
+ inspect_name
261
+ elsif !anonymous?
262
+ name
263
+ elsif inner_keys
264
+ "#{inspect_name}(#{inner_keys.zip(inner_types).map { |k, v| "#{k}: #{v.inspect}" }.join(", ")})"
265
+ else
266
+ "#{inspect_name}(#{inner_types.map(&:inspect).join(", ")})"
267
+ end
268
+ end
269
+
270
+ def self.inspect
271
+ type_expr
272
+ end
64
273
  end
65
274
  end
66
275
  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
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CrystalRuby::Types
4
+ Array = VariableWidth.build(error: "Array type must have a type parameter. E.g. Array(Float64)")
5
+
6
+ # An array-like, reference counted manually managed memory type.
7
+ # Shareable between Crystal and Crystal.
8
+ def self.Array(type)
9
+ VariableWidth.build(:Array, inner_types: [type], convert_if: [Array, Root::Array], superclass: Array) do
10
+ include Enumerable
11
+
12
+ # Implement the Enumerable interface
13
+ # Helps this object to act like an Array
14
+ def each
15
+ size.times { |i| yield self[i] }
16
+ end
17
+
18
+ # We only accept Array-like values, from which all elements
19
+ # can successfully be cast to our inner type
20
+ def self.cast!(value)
21
+ unless value.is_a?(Array) || value.is_a?(Root::Array) && value.all?(&inner_type.method(:valid_cast?))
22
+ raise CrystalRuby::InvalidCastError, "Cannot cast #{value} to #{inspect}"
23
+ end
24
+
25
+ if inner_type.primitive?
26
+ value.map(&inner_type.method(:to_ffi_repr))
27
+ else
28
+ value
29
+ end
30
+ end
31
+
32
+ def self.copy_to!(rbval, memory:)
33
+ data_pointer = malloc(rbval.size * inner_type.refsize)
34
+
35
+ memory[size_offset].write_uint32(rbval.size)
36
+ memory[data_offset].write_pointer(data_pointer)
37
+
38
+ if inner_type.primitive?
39
+ data_pointer.send("put_array_of_#{inner_type.ffi_type}", 0, rbval)
40
+ else
41
+ rbval.each_with_index do |val, i|
42
+ inner_type.write_single(data_pointer[i * refsize], val)
43
+ end
44
+ end
45
+ end
46
+
47
+ def self.each_child_address(pointer)
48
+ size = pointer[size_offset].get_int32(0)
49
+ pointer = pointer[data_offset].read_pointer
50
+ size.times do |i|
51
+ yield inner_type, pointer[i * inner_type.refsize]
52
+ end
53
+ end
54
+
55
+ def checked_offset!(index, size)
56
+ raise "Index out of bounds: #{index} >= #{size}" if index >= size
57
+
58
+ if index < 0
59
+ raise "Index out of bounds: #{index} < -#{size}" if index < -size
60
+
61
+ index += size
62
+ end
63
+ index
64
+ end
65
+
66
+ # Return the element at the given index.
67
+ # This will automatically increment
68
+ # the reference count if not a primitive type.
69
+ def [](index)
70
+ inner_type.fetch_single(data_pointer[checked_offset!(index, size) * inner_type.refsize])
71
+ end
72
+
73
+ # Overwrite the element at the given index
74
+ # The replaced element will have
75
+ # its reference count decremented.
76
+ def []=(index, value)
77
+ inner_type.write_single(data_pointer[checked_offset!(index, size) * inner_type.refsize], value)
78
+ end
79
+
80
+ # Load values stored inside array type.
81
+ # If it's a primitive type, we can quickly bulk load the values.
82
+ # Otherwise we need toinstantiate new ref-checked instances.
83
+ def value(native: false)
84
+ inner_type.fetch_multi(data_pointer, size, native: native)
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,146 @@
1
+ class <%= base_crystal_class_name %> < CrystalRuby::Types::VariableWidth
2
+
3
+ def initialize(hash : Hash(<%= key_type.native_type_expr %>, <%= value_type.native_type_expr %>))
4
+ @memory = malloc(data_offset + 8)
5
+ self.value = hash
6
+ increment_ref_count!
7
+ end
8
+
9
+ def value=(hash : Hash(<%= key_type.native_type_expr %>, <%= value_type.native_type_expr %>))
10
+ if self.ref_count > 0
11
+ self.class.decr_inner_ref_counts!(memory)
12
+ end
13
+ self.class.copy_to!(hash, self.memory)
14
+ end
15
+
16
+ def ==(other : <%= native_type_expr %>)
17
+ native == other
18
+ end
19
+
20
+ def self.copy_to!(hash : Hash(<%= key_type.native_type_expr %>, <%= value_type.native_type_expr %>), memory : Pointer(::UInt8))
21
+ data_pointer = malloc(hash.size * (keysize + valsize)).as(Pointer(::UInt8))
22
+
23
+ hash.keys.each_with_index do |key, i|
24
+ <%= key_type.crystal_class_name %>.write_single(data_pointer + i * keysize, key)
25
+ end
26
+
27
+ hash.values.each_with_index do |value, i|
28
+ <%= value_type.crystal_class_name %>.write_single(data_pointer + hash.size * keysize + i * valsize, value)
29
+ end
30
+
31
+ (memory+size_offset).as(Pointer(::UInt32)).value = hash.size.to_u32
32
+ (memory+data_offset).as(Pointer(::UInt64)).value = data_pointer.address
33
+ end
34
+
35
+ def value
36
+ ::Hash.zip(keys, values)
37
+ end
38
+
39
+ def native : ::Hash(<%= key_type.native_type_expr %>, <%= value_type.native_type_expr %>)
40
+ ::Hash.zip(keys_native, values_native)
41
+ end
42
+
43
+ include Enumerable(::Tuple(<%= key_type.native_type_expr %>, <%= value_type.native_type_expr %>))
44
+
45
+ def each
46
+ size.times do |i|
47
+ yield({keys_native[i], values_native[i]})
48
+ end
49
+ end
50
+
51
+ def size
52
+ (memory+size_offset).as(Pointer(::UInt32)).value.to_i
53
+ end
54
+
55
+ def self.keysize
56
+ <%= key_type.refsize %>
57
+ end
58
+
59
+ def keysize
60
+ <%= key_type.refsize %>
61
+ end
62
+
63
+ def self.valsize
64
+ <%= value_type.refsize %>
65
+ end
66
+
67
+ def valsize
68
+ <%= value_type.refsize %>
69
+ end
70
+
71
+ def []=(key : <%= key_type.native_type_expr %>, value : <%= value_type.native_type_expr %>)
72
+ index = index_of(key)
73
+ if index
74
+ <%= value_type.crystal_class_name %>.write_single(data_pointer + size * keysize + index * valsize, value)
75
+ else
76
+ self.value = self.native.merge({key => value})
77
+ end
78
+ end
79
+
80
+ def key_ptr : Pointer(<%= !key_type.numeric? ? "UInt64" : key_type.native_type_expr %>)
81
+ data_pointer.as(Pointer(<%= !key_type.numeric? ? "UInt8" : key_type.native_type_expr %>))
82
+ end
83
+
84
+ def value_ptr : Pointer(<%= !value_type.numeric? ? "UInt64" : value_type.native_type_expr %>)
85
+ (data_pointer + size * keysize).as(Pointer(<%= !value_type.numeric? ? "UInt8" : value_type.native_type_expr %>))
86
+ end
87
+
88
+ def []?(key : <%= key_type.native_type_expr %>)
89
+ index = index_of(key)
90
+ if index
91
+ values[index]
92
+ else
93
+ nil
94
+ end
95
+ end
96
+
97
+ def [](key : <%= key_type.native_type_expr %>)
98
+ index = index_of(key)
99
+ if index
100
+ values[index]
101
+ else
102
+ raise "Key not found"
103
+ end
104
+ end
105
+
106
+
107
+ def data_pointer
108
+ Pointer(::UInt8).new((@memory + data_offset).as(::Pointer(UInt64)).value)
109
+ end
110
+
111
+ def keys
112
+ keys = [] of <%= key_type.numeric? ? key_type.native_type_expr : key_type.crystal_class_name %>
113
+ size.times do |i|
114
+ keys << <%= !key_type.numeric? ? "#{key_type.crystal_class_name}.fetch_single(data_pointer + i * keysize)" : "key_ptr[i]" %>
115
+ end
116
+ keys
117
+ end
118
+
119
+ def values
120
+ values = [] of <%= value_type.numeric? ? value_type.native_type_expr : value_type.crystal_class_name %>
121
+ size.times do |i|
122
+ values << <%= !value_type.numeric? ? "#{value_type.crystal_class_name}.fetch_single(data_pointer + size * keysize + i * valsize)" : "value_ptr[i]" %>
123
+ end
124
+ values
125
+ end
126
+
127
+ def keys_native
128
+ keys = [] of <%= key_type.native_type_expr %>
129
+ size.times do |i|
130
+ keys << <%= !key_type.numeric? ? "#{key_type.crystal_class_name}.fetch_single(data_pointer + i * keysize).native" : "key_ptr[i]" %>.as(<%= key_type.native_type_expr %>)
131
+ end
132
+ keys
133
+ end
134
+
135
+ def values_native
136
+ values = [] of <%= value_type.native_type_expr %>
137
+ size.times do |i|
138
+ values << <%= !value_type.numeric? ? "#{value_type.crystal_class_name}.fetch_single(data_pointer + size * keysize + i * valsize).native" : "value_ptr[i]" %>.as(<%= value_type.native_type_expr %>)
139
+ end
140
+ values
141
+ end
142
+
143
+ def index_of(key : <%= key_type.native_type_expr %>)
144
+ keys.index(key)
145
+ end
146
+ end