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