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
@@ -0,0 +1,53 @@
|
|
1
|
+
class <%= base_crystal_class_name %> < CrystalRuby::Types::FixedWidth
|
2
|
+
|
3
|
+
def initialize(value : <%= native_type_expr %>)
|
4
|
+
@memory = malloc(data_offset + <%= memsize %>_u64)
|
5
|
+
self.value = value
|
6
|
+
increment_ref_count!
|
7
|
+
end
|
8
|
+
|
9
|
+
def value=(value : <%= native_type_expr %>)
|
10
|
+
self.class.copy_to!(value, @memory)
|
11
|
+
end
|
12
|
+
|
13
|
+
def ==(other : <%= native_type_expr %>)
|
14
|
+
native == other
|
15
|
+
end
|
16
|
+
|
17
|
+
def value
|
18
|
+
data_pointer = @memory + data_offset
|
19
|
+
case data_pointer.value
|
20
|
+
<% union_types.each_with_index do |type, index| %>
|
21
|
+
when <%= index %>
|
22
|
+
<%= type.crystal_class_name %>.fetch_single(data_pointer+1)
|
23
|
+
<% end %>
|
24
|
+
else raise "Invalid union type #{data_pointer.value}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def native
|
29
|
+
data_pointer = @memory + data_offset
|
30
|
+
case data_pointer.value
|
31
|
+
<% union_types.each_with_index do |type, index| %>
|
32
|
+
when <%= index %>
|
33
|
+
<%= type.crystal_class_name %>.fetch_single(data_pointer+1).native
|
34
|
+
<% end %>
|
35
|
+
else raise "Invalid union type #{data_pointer.value}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.copy_to!(value : <%= native_type_expr %>, ptr : Pointer(::UInt8))
|
40
|
+
data_pointer = ptr + data_offset
|
41
|
+
case value
|
42
|
+
<% union_types.each_with_index do |type, index| %>
|
43
|
+
when <%= type.native_type_expr %>
|
44
|
+
data_pointer[0] = <%= index %>
|
45
|
+
<%= type.crystal_class_name %>.write_single(data_pointer + 1, value)
|
46
|
+
<% end %>
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.memsize
|
51
|
+
<%= memsize %>
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CrystalRuby::Types
|
4
|
+
TaggedUnion = Class.new(Type) { @error = "Union type must be instantiated from one or more concrete types" }
|
5
|
+
|
6
|
+
def self.TaggedUnion(*union_types)
|
7
|
+
Class.new(FixedWidth) do
|
8
|
+
# We only accept List-like values, which have all of the required keys
|
9
|
+
# and values of the correct type
|
10
|
+
# can successfully be cast to our inner types
|
11
|
+
def self.cast!(value)
|
12
|
+
casteable_type_index = union_types.find_index do |type, _index|
|
13
|
+
next false unless type.valid_cast?(value)
|
14
|
+
|
15
|
+
type.cast!(value)
|
16
|
+
next true
|
17
|
+
rescue StandardError
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
unless casteable_type_index
|
21
|
+
raise CrystalRuby::InvalidCastError,
|
22
|
+
"Cannot cast #{value}:#{value.class} to #{inspect}"
|
23
|
+
end
|
24
|
+
|
25
|
+
[casteable_type_index, value]
|
26
|
+
end
|
27
|
+
|
28
|
+
def value(native: false)
|
29
|
+
type = self.class.union_types[data_pointer.read_uint8]
|
30
|
+
type.fetch_single(data_pointer[1], native: native)
|
31
|
+
end
|
32
|
+
|
33
|
+
def nil?
|
34
|
+
value.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def ==(other)
|
38
|
+
value == other
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.copy_to!((type_index, value), memory:)
|
42
|
+
memory[data_offset].write_int8(type_index)
|
43
|
+
union_types[type_index].write_single(memory[data_offset + 1], value)
|
44
|
+
end
|
45
|
+
|
46
|
+
def data_pointer
|
47
|
+
memory[data_offset]
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.each_child_address(pointer)
|
51
|
+
pointer += data_offset
|
52
|
+
type = self.union_types[pointer.read_uint8]
|
53
|
+
yield type, pointer[1]
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.inner_types
|
57
|
+
union_types
|
58
|
+
end
|
59
|
+
|
60
|
+
define_singleton_method(:memsize) do
|
61
|
+
union_types.map(&:refsize).max + 1
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.refsize
|
65
|
+
8
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.typename
|
69
|
+
"TaggedUnion"
|
70
|
+
end
|
71
|
+
|
72
|
+
define_singleton_method(:union_types) do
|
73
|
+
union_types
|
74
|
+
end
|
75
|
+
|
76
|
+
define_singleton_method(:valid?) do
|
77
|
+
union_types.all?(&:valid?)
|
78
|
+
end
|
79
|
+
|
80
|
+
define_singleton_method(:error) do
|
81
|
+
union_types.map(&:error).join(", ") if union_types.any?(&:error)
|
82
|
+
end
|
83
|
+
|
84
|
+
define_singleton_method(:inspect) do
|
85
|
+
if anonymous?
|
86
|
+
union_types.map(&:inspect).join(" | ")
|
87
|
+
else
|
88
|
+
crystal_class_name
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
define_singleton_method(:native_type_expr) do
|
93
|
+
union_types.map(&:native_type_expr).join(" | ")
|
94
|
+
end
|
95
|
+
|
96
|
+
define_singleton_method(:type_expr) do
|
97
|
+
anonymous? ? native_type_expr : name
|
98
|
+
end
|
99
|
+
|
100
|
+
define_singleton_method(:data_offset) do
|
101
|
+
4
|
102
|
+
end
|
103
|
+
|
104
|
+
define_singleton_method(:valid_cast?) do |raw|
|
105
|
+
union_types.any? { |type| type.valid_cast?(raw) }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
class <%= base_crystal_class_name %> < CrystalRuby::Types::FixedWidth
|
2
|
+
|
3
|
+
def initialize(tuple : ::Tuple(<%= inner_types.map(&:native_type_expr).join(',') %>))
|
4
|
+
@memory = malloc(data_offset + 8)
|
5
|
+
self.value = tuple
|
6
|
+
increment_ref_count!
|
7
|
+
end
|
8
|
+
|
9
|
+
def data_pointer
|
10
|
+
Pointer(::UInt8).new((@memory + data_offset).as(Pointer(::UInt64)).value)
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](index : Int)
|
14
|
+
index += size if index < 0
|
15
|
+
<% offset = 0 %>
|
16
|
+
<% inner_types.each_with_index do |type, i| %>
|
17
|
+
<% offset += type.refsize %>
|
18
|
+
return <%= type.crystal_class_name %>.fetch_single(data_pointer + <%= offset - type.refsize %>) if <%= i %> == index
|
19
|
+
<% end %>
|
20
|
+
end
|
21
|
+
|
22
|
+
<% inner_types.each_with_index.group_by{|t,i| t.native_type_expr }.each do |(native_type_expr, types_width_index)| %>
|
23
|
+
def []=(index : Int, value : <%= native_type_expr %>)
|
24
|
+
index += size if index < 0
|
25
|
+
<% types_width_index.each do |type, i| %>
|
26
|
+
return <%= type.crystal_class_name %>.write_single(data_pointer + <%= offset_for(i) %>, value) if <%= i %> == index
|
27
|
+
<% end %>
|
28
|
+
raise ArgumentError.new("Index out of bounds")
|
29
|
+
end
|
30
|
+
<% end %>
|
31
|
+
|
32
|
+
def ==(other : ::Tuple(<%= inner_types.map(&:native_type_expr).join(',') %>))
|
33
|
+
value == other
|
34
|
+
end
|
35
|
+
|
36
|
+
def value=(tuple : ::Tuple(<%= inner_types.map(&:native_type_expr).join(',') %>))
|
37
|
+
self.class.copy_to!(tuple, @memory)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.copy_to!(tuple : ::Tuple(<%= inner_types.map(&:native_type_expr).join(',') %>), memory : Pointer(::UInt8))
|
41
|
+
|
42
|
+
data_pointer = malloc(self.memsize)
|
43
|
+
address = data_pointer.address
|
44
|
+
|
45
|
+
<% inner_types.each_with_index do |type, i| %>
|
46
|
+
<%= type.crystal_class_name %>.write_single(data_pointer, tuple[<%= i %>])
|
47
|
+
data_pointer += <%= type.refsize %>
|
48
|
+
<% end %>
|
49
|
+
|
50
|
+
(memory+data_offset).as(Pointer(::UInt64)).value = address
|
51
|
+
end
|
52
|
+
|
53
|
+
def ==(other : <%= native_type_expr %>)
|
54
|
+
native == other
|
55
|
+
end
|
56
|
+
|
57
|
+
def value : ::Tuple(<%= inner_types.map(&:native_type_expr).join(',') %>)
|
58
|
+
address = data_pointer
|
59
|
+
<% inner_types.each_with_index do |type, i| %>
|
60
|
+
v<%= i %> = <%= type.crystal_class_name %>.fetch_single(address)
|
61
|
+
address += <%= type.refsize %>
|
62
|
+
<% end %>
|
63
|
+
::Tuple.new(<%= inner_types.map.with_index { |_, i| "v#{i}" }.join(", ") %>)
|
64
|
+
end
|
65
|
+
|
66
|
+
def native : ::Tuple(<%= inner_types.map(&:native_type_expr).join(',') %>)
|
67
|
+
address = data_pointer
|
68
|
+
<% inner_types.each_with_index do |type, i| %>
|
69
|
+
v<%= i %> = <%= type.crystal_class_name %>.fetch_single(address).native
|
70
|
+
address += <%= type.refsize %>
|
71
|
+
<% end %>
|
72
|
+
::Tuple.new(<%= inner_types.map.with_index { |_, i| "v#{i}" }.join(", ") %>)
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.memsize
|
76
|
+
<%= memsize %>
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.refsize
|
80
|
+
<%= refsize %>
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CrystalRuby::Types
|
4
|
+
Tuple = FixedWidth.build(
|
5
|
+
:Tuple,
|
6
|
+
error: "Tuple type must contain one or more types E.g. Tuple(Int32, String)"
|
7
|
+
)
|
8
|
+
|
9
|
+
def self.Tuple(*types)
|
10
|
+
FixedWidth.build(:Tuple, inner_types: types, convert_if: [Root::Array], superclass: Tuple) do
|
11
|
+
@data_offset = 4
|
12
|
+
|
13
|
+
# We only accept List-like values, which have all of the required keys
|
14
|
+
# and values of the correct type
|
15
|
+
# can successfully be cast to our inner types
|
16
|
+
def self.cast!(value)
|
17
|
+
unless (value.is_a?(Array) || value.is_a?(Tuple) || value.is_a?(Root::Array)) && value.zip(inner_types).each do |v, t|
|
18
|
+
t && t.valid_cast?(v)
|
19
|
+
end && value.length == inner_types.length
|
20
|
+
raise CrystalRuby::InvalidCastError, "Cannot cast #{value} to #{inspect}"
|
21
|
+
end
|
22
|
+
|
23
|
+
value
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.copy_to!(values, memory:)
|
27
|
+
data_pointer = malloc(memsize)
|
28
|
+
|
29
|
+
memory[data_offset].write_pointer(data_pointer)
|
30
|
+
|
31
|
+
inner_types.each.reduce(0) do |offset, type|
|
32
|
+
type.write_single(data_pointer[offset], values.shift)
|
33
|
+
offset + type.refsize
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.each_child_address(pointer)
|
38
|
+
data_pointer = pointer[data_offset].read_pointer
|
39
|
+
inner_types.each do |type|
|
40
|
+
yield type, data_pointer
|
41
|
+
data_pointer += type.refsize
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.memsize
|
46
|
+
inner_types.map(&:refsize).sum
|
47
|
+
end
|
48
|
+
|
49
|
+
def size
|
50
|
+
inner_types.size
|
51
|
+
end
|
52
|
+
|
53
|
+
def checked_offset!(index, size)
|
54
|
+
raise "Index out of bounds: #{index} >= #{size}" if index >= size
|
55
|
+
|
56
|
+
if index < 0
|
57
|
+
raise "Index out of bounds: #{index} < -#{size}" if index < -size
|
58
|
+
|
59
|
+
index += size
|
60
|
+
end
|
61
|
+
self.class.offset_for(index)
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.offset_for(index)
|
65
|
+
inner_types[0...index].map(&:refsize).sum
|
66
|
+
end
|
67
|
+
|
68
|
+
# Return the element at the given index.
|
69
|
+
# This will automatically increment
|
70
|
+
# the reference count if not a primitive type.
|
71
|
+
def [](index)
|
72
|
+
inner_types[index].fetch_single(data_pointer[checked_offset!(index, size)])
|
73
|
+
end
|
74
|
+
|
75
|
+
# Overwrite the element at the given index
|
76
|
+
# The replaced element will have
|
77
|
+
# its reference count decremented.
|
78
|
+
def []=(index, value)
|
79
|
+
inner_types[index].write_single(data_pointer[checked_offset!(index, size)], value)
|
80
|
+
end
|
81
|
+
|
82
|
+
def value(native: false)
|
83
|
+
ptr = data_pointer
|
84
|
+
inner_types.map do |type|
|
85
|
+
result = type.fetch_single(ptr, native: native)
|
86
|
+
ptr += type.refsize
|
87
|
+
result
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module CrystalRuby
|
2
|
+
module Types
|
3
|
+
# For a fixed width type, we allocate a single block block of memory of the form
|
4
|
+
# [ref_count (uint32), data(uint8*)]
|
5
|
+
class FixedWidth < Type
|
6
|
+
|
7
|
+
# We can instantiate it with a Value (for a new object)
|
8
|
+
# or a Pointer (for a copy of an existing object)
|
9
|
+
macro inherited
|
10
|
+
def initialize(@memory : Pointer(::UInt8))
|
11
|
+
increment_ref_count!
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def finalize
|
16
|
+
self.class.decrement_ref_count!(@memory)
|
17
|
+
end
|
18
|
+
|
19
|
+
def increment_ref_count!
|
20
|
+
self.class.increment_ref_count!(@memory)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.increment_ref_count!(memory, by=1)
|
24
|
+
as_int32_ptr = memory.as(Pointer(::UInt32))
|
25
|
+
synchronize{ as_int32_ptr[0] += by }
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.decrement_ref_count!(memory, by=1)
|
29
|
+
as_int32_ptr = memory.as(Pointer(::UInt32))
|
30
|
+
synchronize{ as_int32_ptr[0] -= by }
|
31
|
+
free!(memory) if as_int32_ptr[0] == 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.refsize
|
35
|
+
8
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def self.free!(memory)
|
40
|
+
# Decrease ref counts for any data we are pointing to
|
41
|
+
# Also responsible for freeing internal memory if ref count reaches zero
|
42
|
+
decr_inner_ref_counts!(memory)
|
43
|
+
# # Free slot memory
|
44
|
+
free(memory)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.decr_inner_ref_counts!(pointer)
|
48
|
+
self.each_child_address(pointer) do |child_type, child_address|
|
49
|
+
child_type.decrement_ref_count!(child_address.read_pointer)
|
50
|
+
end
|
51
|
+
# Free data block, if we're a variable with type.
|
52
|
+
as_pointer_ref = (pointer+data_offset).as(Pointer(Pointer(::UInt8)))
|
53
|
+
free(as_pointer_ref[0]) if variable_width?
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.variable_width?
|
57
|
+
false
|
58
|
+
end
|
59
|
+
|
60
|
+
# Ref count is always the first UInt32 in the memory block
|
61
|
+
def ref_count
|
62
|
+
memory.as(Pointer(::UInt32))[0]
|
63
|
+
end
|
64
|
+
|
65
|
+
def ref_count=(val)
|
66
|
+
memory.as(Pointer(::UInt32))[0] = value
|
67
|
+
end
|
68
|
+
|
69
|
+
# Data pointer follows the ref count (and size for variable width types)
|
70
|
+
# In the case of variable width types the data pointer points to the start of a separate data block
|
71
|
+
# So this method is overridden inside variable_width.rb to resolve this pointer.
|
72
|
+
def data_pointer : Pointer(UInt8)
|
73
|
+
(memory + data_offset)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Create a brand new copy of this object
|
77
|
+
def deep_dup
|
78
|
+
self.class.new(value)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Create a new reference to this object.
|
82
|
+
def dup
|
83
|
+
self.class.new(@memory)
|
84
|
+
end
|
85
|
+
|
86
|
+
def address
|
87
|
+
memory.address
|
88
|
+
end
|
89
|
+
|
90
|
+
def data_offset
|
91
|
+
self.class.data_offset
|
92
|
+
end
|
93
|
+
|
94
|
+
def memsize
|
95
|
+
self.class.memsize
|
96
|
+
end
|
97
|
+
|
98
|
+
def size_offset
|
99
|
+
self.class.size_offset
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.size_offset
|
103
|
+
4
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.data_offset
|
107
|
+
4
|
108
|
+
end
|
109
|
+
|
110
|
+
# Read a value of this type from the
|
111
|
+
# contained pointer at a given index
|
112
|
+
def self.fetch_single(pointer : Pointer(::UInt8))
|
113
|
+
value_pointer = pointer.as(Pointer(Pointer(::UInt8)))
|
114
|
+
new(value_pointer.value)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Write a data type into a pointer at a given index
|
118
|
+
# (Type can be a byte-array, pointer or numeric type
|
119
|
+
#
|
120
|
+
# )
|
121
|
+
def self.write_single(pointer, value)
|
122
|
+
value_pointer = pointer.as(Pointer(Pointer(::UInt8)))
|
123
|
+
if !value_pointer[0].null?
|
124
|
+
decrement_ref_count!(value_pointer[0])
|
125
|
+
end
|
126
|
+
memory = malloc(self.data_offset + self.memsize)
|
127
|
+
|
128
|
+
self.copy_to!(value, memory)
|
129
|
+
value_pointer.value = memory
|
130
|
+
increment_ref_count!(memory)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Fetch an array of a given data type from a list pointer
|
134
|
+
# (Type can be a byte-array, pointer or numeric type)
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
module CrystalRuby
|
2
|
+
module Types
|
3
|
+
# For a fixed width type, we allocate a single block block of memory of the form
|
4
|
+
# [ref_count (uint32), data(uint8*)]
|
5
|
+
class FixedWidth < Type
|
6
|
+
# We can instantiate it with a Value (for a new object)
|
7
|
+
# or a Pointer (for a copy of an existing object)
|
8
|
+
def initialize(rbval)
|
9
|
+
super
|
10
|
+
case rbval
|
11
|
+
when FFI::Pointer then allocate_new_from_reference!(rbval)
|
12
|
+
else allocate_new_from_value!(rbval)
|
13
|
+
end
|
14
|
+
self.class.increment_ref_count!(memory)
|
15
|
+
ObjectSpace.define_finalizer(self, self.class.finalize(memory))
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.finalize(memory)
|
19
|
+
lambda { |_|
|
20
|
+
decrement_ref_count!(memory)
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def allocate_new_from_value!(rbval)
|
25
|
+
# New block of memory, to hold our object.
|
26
|
+
# For variable with, this is 2x UInt32 for ref count and size, plus a data pointer (8 bytes)
|
27
|
+
# Layout:
|
28
|
+
# - ref_count (4 bytes)
|
29
|
+
# - size (4 bytes)
|
30
|
+
# - data (8 bytes)
|
31
|
+
#
|
32
|
+
# For fixed the data is inline
|
33
|
+
# Layout:
|
34
|
+
# - ref_count (4 bytes)
|
35
|
+
# - size (0 bytes) (No size for fixed width types)
|
36
|
+
# - data (memsize bytes)
|
37
|
+
self.memory = malloc(refsize + data_offset)
|
38
|
+
self.value = rbval
|
39
|
+
end
|
40
|
+
|
41
|
+
def allocate_new_from_reference!(memory)
|
42
|
+
# When we point to an existing block of memory, we don't need to allocate anything.
|
43
|
+
# This memory should be to a single, separately allocated block of the above size.
|
44
|
+
# When this type is contained within another type, it should be as a pointer to this block (not the contents of the block itself).
|
45
|
+
self.memory = memory
|
46
|
+
end
|
47
|
+
|
48
|
+
# Each type should be convertible to an FFI representation. (I.e. how is a value or reference to this value stored
|
49
|
+
# within e.g. an Array, Hash, Tuple or any other containing type).
|
50
|
+
# For both fixed and variable types these are simply stored within
|
51
|
+
# the containing type as a pointer to the memory block.
|
52
|
+
# We return the pointer to this memory here.
|
53
|
+
def self.to_ffi_repr(value)
|
54
|
+
to_store = new(value)
|
55
|
+
increment_ref_count!(to_store.memory)
|
56
|
+
to_store.memory
|
57
|
+
end
|
58
|
+
|
59
|
+
# Read a value of this type from the
|
60
|
+
# contained pointer at a given index
|
61
|
+
def self.fetch_single(pointer, native: false)
|
62
|
+
# Nothing to fetch for Nils
|
63
|
+
return if memsize.zero?
|
64
|
+
|
65
|
+
value_pointer = pointer.read_pointer
|
66
|
+
native ? new(value_pointer).native : new(value_pointer)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Write a data type into a pointer at a given index
|
70
|
+
# (Type can be a byte-array, pointer or numeric type
|
71
|
+
#
|
72
|
+
# )
|
73
|
+
def self.write_single(pointer, value)
|
74
|
+
# Dont need to write nils
|
75
|
+
return if memsize.zero?
|
76
|
+
|
77
|
+
decrement_ref_count!(pointer.read_pointer) unless pointer.read_pointer.null?
|
78
|
+
memory = malloc(refsize + data_offset)
|
79
|
+
copy_to!(cast!(value), memory: memory)
|
80
|
+
increment_ref_count!(memory)
|
81
|
+
pointer.write_pointer(memory)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Fetch an array of a given data type from a list pointer
|
85
|
+
# (Type can be a byte-array, pointer or numeric type)
|
86
|
+
def self.fetch_multi(pointer, size, native: false)
|
87
|
+
size.times.map { |i| fetch_single(pointer[i * refsize], native: native) }
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.increment_ref_count!(memory, by = 1)
|
91
|
+
synchronize { memory.write_int32(memory.read_int32 + by) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.decrement_ref_count!(memory, by = 1)
|
95
|
+
synchronize { memory.write_int32(memory.read_int32 - by) }
|
96
|
+
return unless memory.read_int32.zero?
|
97
|
+
|
98
|
+
free!(memory)
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.free!(memory)
|
102
|
+
# Decrease ref counts for any data we are pointing to
|
103
|
+
# Also responsible for freeing internal memory if ref count reaches zero
|
104
|
+
decr_inner_ref_counts!(memory)
|
105
|
+
|
106
|
+
# # Free slot memory
|
107
|
+
free(memory)
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.decr_inner_ref_counts!(pointer)
|
111
|
+
each_child_address(pointer) do |child_type, child_address|
|
112
|
+
child_type.decrement_ref_count!(child_address.read_pointer) if child_type.fixed_width?
|
113
|
+
end
|
114
|
+
# Free data block, if we're a variable width type.
|
115
|
+
return unless variable_width?
|
116
|
+
|
117
|
+
free(pointer[data_offset].read_pointer)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Ref count is always the first Int32 in the memory block
|
121
|
+
def ref_count
|
122
|
+
memory.read_uint32
|
123
|
+
end
|
124
|
+
|
125
|
+
def ref_count=(val)
|
126
|
+
memory.write_int32(val)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Data pointer follows the ref count (and size for variable width types)
|
130
|
+
# In the case of variable width types the data pointer points to the start of a separate data block
|
131
|
+
# So this method is overridden inside variable_width.rb to resolve this pointer.
|
132
|
+
def data_pointer
|
133
|
+
memory[data_offset].read_pointer
|
134
|
+
end
|
135
|
+
|
136
|
+
def size
|
137
|
+
memory[size_offset].read_int32
|
138
|
+
end
|
139
|
+
|
140
|
+
def address
|
141
|
+
@memory.address
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.crystal_supertype
|
145
|
+
"CrystalRuby::Types::FixedWidth"
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.crystal_type
|
149
|
+
"Pointer(::UInt8)"
|
150
|
+
end
|
151
|
+
|
152
|
+
# If we are fixed with,
|
153
|
+
# The memory we allocate a single block of memory, if not already given.
|
154
|
+
# Within this block of memory, we copy our contents directly.
|
155
|
+
#
|
156
|
+
# If we are variable width, we allocate a small block of memory for the pointer only
|
157
|
+
# and allocate a separate block of memory for the data.
|
158
|
+
# We store the pointer to the data in the memory block.
|
159
|
+
|
160
|
+
def value=(value)
|
161
|
+
# If we're already pointing at something
|
162
|
+
# Decrement the ref counts of anything we're pointing at
|
163
|
+
value = cast!(value)
|
164
|
+
|
165
|
+
self.class.decr_inner_ref_counts!(memory) if ref_count > 0
|
166
|
+
self.class.copy_to!(value, memory: memory)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Build a new FixedWith subtype
|
170
|
+
# Layout varies according to the sizes of internal types
|
171
|
+
def self.build(
|
172
|
+
typename = nil,
|
173
|
+
error: nil,
|
174
|
+
inner_types: nil,
|
175
|
+
inner_keys: nil,
|
176
|
+
ffi_type: :pointer,
|
177
|
+
memsize: FFI.type_size(ffi_type),
|
178
|
+
refsize: 8,
|
179
|
+
convert_if: [],
|
180
|
+
superclass: FixedWidth,
|
181
|
+
size_offset: 4,
|
182
|
+
data_offset: 4,
|
183
|
+
&block
|
184
|
+
)
|
185
|
+
inner_types&.each(&Type.method(:validate!))
|
186
|
+
|
187
|
+
Class.new(superclass) do
|
188
|
+
bind_local_vars!(
|
189
|
+
%i[typename error inner_types inner_keys ffi_type memsize convert_if size_offset data_offset
|
190
|
+
refsize], binding
|
191
|
+
)
|
192
|
+
class_eval(&block) if block_given?
|
193
|
+
|
194
|
+
def self.fixed_width?
|
195
|
+
true
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
require_relative "fixed_width/proc"
|
203
|
+
require_relative "fixed_width/named_tuple"
|
204
|
+
require_relative "fixed_width/tuple"
|
205
|
+
require_relative "fixed_width/tagged_union"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module CrystalRuby
|
2
|
+
module Types
|
3
|
+
class Primitive < Type
|
4
|
+
def return_value
|
5
|
+
@value
|
6
|
+
end
|
7
|
+
|
8
|
+
def native
|
9
|
+
value
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.fetch_single(pointer : Pointer(::UInt8))
|
13
|
+
new(pointer)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.refsize
|
17
|
+
self.memsize
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|