crystalruby 0.2.3 → 0.3.1
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/Dockerfile +23 -2
- data/README.md +395 -198
- data/Rakefile +4 -3
- data/crystalruby.gemspec +2 -2
- data/examples/adder/adder.rb +1 -1
- data/exe/crystalruby +1 -0
- data/lib/crystalruby/adapter.rb +143 -73
- 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 +216 -73
- data/lib/crystalruby/library.rb +157 -51
- data/lib/crystalruby/reactor.rb +63 -44
- data/lib/crystalruby/source_reader.rb +92 -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 +113 -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 +249 -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 +42 -22
- 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,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
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module CrystalRuby
|
2
|
+
module Types
|
3
|
+
class Primitive < Type
|
4
|
+
# Primitives just store the Ruby value directly
|
5
|
+
# (Or read it from memory if passed a pointer)
|
6
|
+
def initialize(rbval)
|
7
|
+
super(rbval)
|
8
|
+
self.value = rbval.is_a?(FFI::Pointer) ? rbval.send("read_#{ffi_type}") : rbval
|
9
|
+
end
|
10
|
+
|
11
|
+
# Read a value from a pointer at a given index
|
12
|
+
# (Type can be a byte-array, pointer or numeric type)
|
13
|
+
def self.fetch_single(pointer, native: false)
|
14
|
+
# Nothing to fetch for Nils
|
15
|
+
return if memsize.zero?
|
16
|
+
|
17
|
+
if numeric?
|
18
|
+
pointer.send("read_#{ffi_type}")
|
19
|
+
elsif primitive?
|
20
|
+
single = new(pointer.send("read_#{ffi_type}"))
|
21
|
+
if native
|
22
|
+
single.value
|
23
|
+
else
|
24
|
+
single
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Write a data type into a pointer at a given index
|
30
|
+
# (Type can be a byte-array, pointer or numeric type)
|
31
|
+
def self.write_single(pointer, value)
|
32
|
+
# Dont need to write nils
|
33
|
+
return if memsize.zero?
|
34
|
+
|
35
|
+
pointer.send("write_#{ffi_type}", to_ffi_repr(value))
|
36
|
+
end
|
37
|
+
|
38
|
+
# Fetch an array of a given data type from a list pointer
|
39
|
+
# (Type can be a byte-array, pointer or numeric type)
|
40
|
+
def self.fetch_multi(pointer, size, native: false)
|
41
|
+
if numeric?
|
42
|
+
pointer.send("get_array_of_#{ffi_type}", 0, size)
|
43
|
+
elsif primitive?
|
44
|
+
pointer.send("get_array_of_#{ffi_type}", 0, size).map(&method(:from_ffi_array_repr))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.decrement_ref_count!(pointer)
|
49
|
+
# Do nothing
|
50
|
+
end
|
51
|
+
|
52
|
+
# Define a new primitive type
|
53
|
+
# Primitive types are stored by value
|
54
|
+
# and efficiently copied using native FFI types
|
55
|
+
# They are written directly into the memory of a container type
|
56
|
+
# (No indirection)
|
57
|
+
def self.build(
|
58
|
+
typename = nil,
|
59
|
+
ffi_type: :uint8,
|
60
|
+
memsize: FFI.type_size(ffi_type),
|
61
|
+
convert_if: [],
|
62
|
+
error: nil,
|
63
|
+
superclass: Primitive,
|
64
|
+
&block
|
65
|
+
)
|
66
|
+
Class.new(superclass) do
|
67
|
+
%w[typename ffi_type memsize convert_if error].each do |name|
|
68
|
+
define_singleton_method(name) { binding.local_variable_get("#{name}") }
|
69
|
+
define_method(name) { binding.local_variable_get("#{name}") }
|
70
|
+
end
|
71
|
+
|
72
|
+
class_eval(&block) if block_given?
|
73
|
+
|
74
|
+
# Primitives are stored directly in memory as a raw numeric value
|
75
|
+
def self.to_ffi_repr(value)
|
76
|
+
new(value).inner_value
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.refsize
|
80
|
+
memsize
|
81
|
+
end
|
82
|
+
|
83
|
+
# Primiives are anonymous (Shouldn't be subclassed)
|
84
|
+
def self.anonymous?
|
85
|
+
true
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.copy_to!(rbval, memory:)
|
89
|
+
memory.send("write_#{self.ffi_type}", to_ffi_repr(rbval))
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.primitive?
|
93
|
+
true
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.inspect
|
97
|
+
inspect_name
|
98
|
+
end
|
99
|
+
|
100
|
+
def memory
|
101
|
+
@value
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.crystal_supertype
|
105
|
+
"CrystalRuby::Types::Primitive"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
require_relative "primitive_types/time"
|
114
|
+
require_relative "primitive_types/symbol"
|
115
|
+
require_relative "primitive_types/numbers"
|
116
|
+
require_relative "primitive_types/nil"
|
117
|
+
require_relative "primitive_types/bool"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class <%= base_crystal_class_name %> < CrystalRuby::Types::Primitive
|
2
|
+
|
3
|
+
def initialize(value : ::Bool)
|
4
|
+
@value = value ? 1_u8 : 0_u8
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(ptr : Pointer(::UInt8))
|
8
|
+
@value = ptr[0]
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(value : UInt8)
|
12
|
+
@value = value
|
13
|
+
end
|
14
|
+
|
15
|
+
def value=(value : ::Bool)
|
16
|
+
@value = value ? 1_u8 : 0_u8
|
17
|
+
end
|
18
|
+
|
19
|
+
def value : <%= native_type_expr %>
|
20
|
+
@value == 1_u8
|
21
|
+
end
|
22
|
+
|
23
|
+
def ==(other : ::Bool)
|
24
|
+
value == other
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.memsize
|
28
|
+
<%= memsize %>
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.write_single(pointer : Pointer(::UInt8), value)
|
32
|
+
pointer.as(Pointer(::UInt8)).value = value ? 1_u8 : 0_u8
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module CrystalRuby::Types
|
2
|
+
Bool = Primitive.build(:Bool, convert_if: [::TrueClass, ::FalseClass], ffi_type: :uint8, memsize: 1) do
|
3
|
+
def value(native: false)
|
4
|
+
super == 1
|
5
|
+
end
|
6
|
+
|
7
|
+
def value=(val)
|
8
|
+
!!val && val != 0 ? super(1) : super(0)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class <%= base_crystal_class_name %> < CrystalRuby::Types::Primitive
|
2
|
+
|
3
|
+
property value : ::Nil = nil
|
4
|
+
|
5
|
+
def initialize(nilval : ::Nil)
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(ptr : Pointer(::UInt8))
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(raw : UInt8)
|
12
|
+
end
|
13
|
+
|
14
|
+
def value : ::Nil
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other : ::Nil)
|
19
|
+
value.nil?
|
20
|
+
end
|
21
|
+
|
22
|
+
def value=(val : ::Nil)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.memsize
|
26
|
+
0
|
27
|
+
end
|
28
|
+
|
29
|
+
def return_value
|
30
|
+
0_u8
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.write_single(pointer : Pointer(::UInt8), value)
|
34
|
+
end
|
35
|
+
end
|