crystalruby 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -0
- data/README.md +389 -193
- data/Rakefile +4 -3
- data/crystalruby.gemspec +2 -2
- data/exe/crystalruby +1 -0
- data/lib/crystalruby/adapter.rb +131 -65
- data/lib/crystalruby/arc_mutex.rb +47 -0
- data/lib/crystalruby/compilation.rb +32 -3
- data/lib/crystalruby/config.rb +41 -37
- data/lib/crystalruby/function.rb +211 -68
- data/lib/crystalruby/library.rb +153 -48
- data/lib/crystalruby/reactor.rb +40 -23
- data/lib/crystalruby/source_reader.rb +86 -0
- data/lib/crystalruby/template.rb +16 -5
- data/lib/crystalruby/templates/function.cr +11 -10
- data/lib/crystalruby/templates/index.cr +53 -66
- data/lib/crystalruby/templates/inline_chunk.cr +1 -1
- data/lib/crystalruby/templates/ruby_interface.cr +34 -0
- data/lib/crystalruby/templates/top_level_function.cr +62 -0
- data/lib/crystalruby/templates/top_level_ruby_interface.cr +33 -0
- data/lib/crystalruby/typebuilder.rb +11 -55
- data/lib/crystalruby/typemaps.rb +92 -67
- data/lib/crystalruby/types/concerns/allocator.rb +80 -0
- data/lib/crystalruby/types/fixed_width/named_tuple.cr +80 -0
- data/lib/crystalruby/types/fixed_width/named_tuple.rb +86 -0
- data/lib/crystalruby/types/fixed_width/proc.cr +45 -0
- data/lib/crystalruby/types/fixed_width/proc.rb +79 -0
- data/lib/crystalruby/types/fixed_width/tagged_union.cr +53 -0
- data/lib/crystalruby/types/fixed_width/tagged_union.rb +109 -0
- data/lib/crystalruby/types/fixed_width/tuple.cr +82 -0
- data/lib/crystalruby/types/fixed_width/tuple.rb +92 -0
- data/lib/crystalruby/types/fixed_width.cr +138 -0
- data/lib/crystalruby/types/fixed_width.rb +205 -0
- data/lib/crystalruby/types/primitive.cr +21 -0
- data/lib/crystalruby/types/primitive.rb +117 -0
- data/lib/crystalruby/types/primitive_types/bool.cr +34 -0
- data/lib/crystalruby/types/primitive_types/bool.rb +11 -0
- data/lib/crystalruby/types/primitive_types/nil.cr +35 -0
- data/lib/crystalruby/types/primitive_types/nil.rb +16 -0
- data/lib/crystalruby/types/primitive_types/numbers.cr +37 -0
- data/lib/crystalruby/types/primitive_types/numbers.rb +28 -0
- data/lib/crystalruby/types/primitive_types/symbol.cr +55 -0
- data/lib/crystalruby/types/primitive_types/symbol.rb +35 -0
- data/lib/crystalruby/types/primitive_types/time.cr +35 -0
- data/lib/crystalruby/types/primitive_types/time.rb +25 -0
- data/lib/crystalruby/types/type.cr +64 -0
- data/lib/crystalruby/types/type.rb +239 -30
- data/lib/crystalruby/types/variable_width/array.cr +74 -0
- data/lib/crystalruby/types/variable_width/array.rb +88 -0
- data/lib/crystalruby/types/variable_width/hash.cr +146 -0
- data/lib/crystalruby/types/variable_width/hash.rb +117 -0
- data/lib/crystalruby/types/variable_width/string.cr +36 -0
- data/lib/crystalruby/types/variable_width/string.rb +18 -0
- data/lib/crystalruby/types/variable_width.cr +23 -0
- data/lib/crystalruby/types/variable_width.rb +46 -0
- data/lib/crystalruby/types.rb +32 -13
- data/lib/crystalruby/version.rb +2 -2
- data/lib/crystalruby.rb +13 -6
- metadata +41 -19
- data/lib/crystalruby/types/array.rb +0 -15
- data/lib/crystalruby/types/bool.rb +0 -3
- data/lib/crystalruby/types/hash.rb +0 -17
- data/lib/crystalruby/types/named_tuple.rb +0 -28
- data/lib/crystalruby/types/nil.rb +0 -3
- data/lib/crystalruby/types/numbers.rb +0 -5
- data/lib/crystalruby/types/string.rb +0 -3
- data/lib/crystalruby/types/symbol.rb +0 -3
- data/lib/crystalruby/types/time.rb +0 -8
- data/lib/crystalruby/types/tuple.rb +0 -17
- data/lib/crystalruby/types/type_serializer/json.rb +0 -41
- data/lib/crystalruby/types/type_serializer.rb +0 -37
- data/lib/crystalruby/types/typedef.rb +0 -57
- data/lib/crystalruby/types/union_type.rb +0 -43
- 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
|
-
|
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
|
-
|
7
|
-
|
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
|
-
|
16
|
-
|
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
|
20
|
-
|
57
|
+
def self.finalize(_memory)
|
58
|
+
->(_) {}
|
21
59
|
end
|
22
60
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
)
|
61
|
+
def self.inspect_name
|
62
|
+
(name || "#{typename}").to_s.gsub(/^CrystalRuby::Types::[^::]+::/, "")
|
63
|
+
end
|
27
64
|
|
28
|
-
|
65
|
+
def self.union_types
|
66
|
+
[self]
|
29
67
|
end
|
30
68
|
|
31
|
-
def
|
32
|
-
|
69
|
+
def self.valid?
|
70
|
+
true
|
33
71
|
end
|
34
72
|
|
35
|
-
def
|
73
|
+
def self.native_type_expr
|
36
74
|
if !inner_types
|
37
|
-
|
75
|
+
"::#{typename}"
|
38
76
|
elsif !inner_keys
|
39
|
-
"
|
77
|
+
"::#{typename}(#{inner_types.map(&:native_type_expr).join(", ")})"
|
40
78
|
else
|
41
|
-
"
|
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
|
46
|
-
|
153
|
+
def self.variable_width?
|
154
|
+
false
|
47
155
|
end
|
48
156
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
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
|
-
|
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?(
|
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
|