crystalruby 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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