crystalruby 0.2.3 → 0.3.1
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/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,37 @@
|
|
1
|
+
class <%= base_crystal_class_name %> < CrystalRuby::Types::Primitive
|
2
|
+
|
3
|
+
def initialize(value : <%= native_type_expr %>)
|
4
|
+
@value = value
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(ptr : Pointer(::UInt8))
|
8
|
+
@value = ptr.as(Pointer( <%= native_type_expr %>))[0]
|
9
|
+
end
|
10
|
+
|
11
|
+
def value
|
12
|
+
@value
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other : <%= native_type_expr %>)
|
16
|
+
value == other
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.memsize
|
20
|
+
<%= memsize %>
|
21
|
+
end
|
22
|
+
|
23
|
+
def value=(value : <%= native_type_expr %>)
|
24
|
+
@value = value
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.copy_to!(value : <%= native_type_expr %>, ptr : Pointer(::UInt8))
|
28
|
+
ptr.as(Pointer( <%= native_type_expr %>))[0] = value
|
29
|
+
end
|
30
|
+
|
31
|
+
# Write a data type into a pointer at a given index
|
32
|
+
# (Type can be a byte-array, pointer or numeric type)
|
33
|
+
def self.write_single(pointer : Pointer(::UInt8), value)
|
34
|
+
pointer.as(Pointer( <%= native_type_expr %>)).value = value
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module CrystalRuby::Types
|
2
|
+
%i[UInt8 UInt16 UInt32 UInt64 Int8 Int16 Int32 Int64 Float32 Float64].each do |type_name|
|
3
|
+
ffi_type = CrystalRuby::Typemaps::FFI_TYPE_MAP.fetch(type_name.to_s)
|
4
|
+
const_set(type_name, Primitive.build(type_name, convert_if: [::Numeric], ffi_type: ffi_type) do
|
5
|
+
def value=(val)
|
6
|
+
raise "Expected a numeric value, got #{val}" unless val.is_a?(::Numeric)
|
7
|
+
|
8
|
+
super(typename.to_s.start_with?("Float") ? val.to_f : val.to_i)
|
9
|
+
end
|
10
|
+
|
11
|
+
def value(native: false)
|
12
|
+
@value
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.from_ffi_array_repr(value)
|
16
|
+
value
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.numeric?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.template_name
|
24
|
+
"Numbers"
|
25
|
+
end
|
26
|
+
end)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class <%= base_crystal_class_name %> < CrystalRuby::Types::Primitive
|
2
|
+
|
3
|
+
def initialize(value : ::Symbol)
|
4
|
+
@value = 0.to_u32
|
5
|
+
self.value = value
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(value : UInt32)
|
9
|
+
@value = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(ptr : Pointer(::UInt8))
|
13
|
+
initialize(ptr.as(Pointer(::UInt32))[0])
|
14
|
+
end
|
15
|
+
|
16
|
+
def ==(other : ::Symbol)
|
17
|
+
value == other
|
18
|
+
end
|
19
|
+
|
20
|
+
def value=(value : ::Symbol)
|
21
|
+
case value
|
22
|
+
<% allowed_values.each_with_index do |v, i| %>
|
23
|
+
when :<%= v %> then @value = <%= i %>.to_u32
|
24
|
+
<% end %>
|
25
|
+
else raise "Symbol must be one of <%= allowed_values %>. Got #{value}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def value : ::Symbol
|
30
|
+
case @value
|
31
|
+
<% allowed_values.each_with_index do |v, i| %>
|
32
|
+
when <%= i %> then :<%= v %>
|
33
|
+
<% end %>
|
34
|
+
else raise "Symbol must be one of <%= allowed_values %>. Got #{value}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.copy_to!(value : ::Symbol, ptr : Pointer(::UInt8))
|
39
|
+
ptr.as(Pointer(::UInt32))[0] = new(value).return_value
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.memsize
|
43
|
+
<%= memsize %>
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.write_single(pointer : Pointer(::UInt8), value)
|
47
|
+
as_uint32 = case value
|
48
|
+
<% allowed_values.each_with_index do |v, i| %>
|
49
|
+
when :<%= v %> then <%= i %>.to_u32
|
50
|
+
<% end %>
|
51
|
+
else raise "Symbol must be one of <%= allowed_values %>. Got #{value}"
|
52
|
+
end
|
53
|
+
pointer.as(Pointer(::UInt32)).value = as_uint32
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module CrystalRuby::Types
|
2
|
+
Symbol = Primitive.build(
|
3
|
+
error: "Symbol CrystalRuby types should indicate a list of possible values shared between Crystal and Ruby. "\
|
4
|
+
"E.g. Symbol(:green, :blue, :orange). If this list is not known at compile time, you should use a String instead."
|
5
|
+
)
|
6
|
+
|
7
|
+
def self.Symbol(*allowed_values)
|
8
|
+
raise "Symbol must have at least one value" if allowed_values.empty?
|
9
|
+
|
10
|
+
allowed_values.flatten!
|
11
|
+
raise "Symbol allowed values must all be symbols" unless allowed_values.all? { |v| v.is_a?(::Symbol) }
|
12
|
+
|
13
|
+
Primitive.build(:Symbol, ffi_type: :uint32, convert_if: [Root::String, Root::Symbol], memsize: 4) do
|
14
|
+
bind_local_vars!(%i[allowed_values], binding)
|
15
|
+
define_method(:value=) do |val|
|
16
|
+
val = allowed_values[val] if val.is_a?(::Integer) && val >= 0 && val < allowed_values.size
|
17
|
+
raise "Symbol must be one of #{allowed_values}" unless allowed_values.include?(val)
|
18
|
+
|
19
|
+
super(allowed_values.index(val))
|
20
|
+
end
|
21
|
+
|
22
|
+
define_singleton_method(:valid_cast?) do |raw|
|
23
|
+
super(raw) && allowed_values.include?(raw)
|
24
|
+
end
|
25
|
+
|
26
|
+
define_method(:value) do |native: false|
|
27
|
+
allowed_values[super()]
|
28
|
+
end
|
29
|
+
|
30
|
+
define_singleton_method(:type_digest) do
|
31
|
+
Digest::MD5.hexdigest(native_type_expr.to_s + allowed_values.map(&:to_s).join(","))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class <%= base_crystal_class_name %> < CrystalRuby::Types::Primitive
|
2
|
+
|
3
|
+
def initialize(time : ::Time)
|
4
|
+
@value = time.to_unix_ns / 1000_000_000.0
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(ptr : Pointer(::UInt8))
|
8
|
+
@value = ptr.as(Pointer(::Float64)).value
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(@value : ::Float64)
|
12
|
+
end
|
13
|
+
|
14
|
+
def ==(other : ::Time)
|
15
|
+
value == other
|
16
|
+
end
|
17
|
+
|
18
|
+
def value=(time : ::Time)
|
19
|
+
@value = time.to_unix_ns / 1000_000_000.0
|
20
|
+
end
|
21
|
+
|
22
|
+
def value : ::Time
|
23
|
+
::Time.unix_ns((@value * 1000_000_000).to_i128)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.memsize
|
27
|
+
<%= memsize %>
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def self.write_single(pointer : Pointer(::UInt8), time)
|
32
|
+
pointer.as(Pointer(::Float64)).value = time.to_unix_ns / 1000_000_000.0
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "date"
|
4
|
+
|
5
|
+
module CrystalRuby::Types
|
6
|
+
Time = Primitive.build(:Time, convert_if: [Root::Time, Root::String, DateTime], ffi_type: :double) do
|
7
|
+
def initialize(val = Root::Time.now)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def value=(val)
|
12
|
+
super(
|
13
|
+
if val.respond_to?(:to_time)
|
14
|
+
val.to_time.to_f
|
15
|
+
else
|
16
|
+
val.respond_to?(:to_f) ? val.to_f : 0
|
17
|
+
end
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def value(native: false)
|
22
|
+
::Time.at(super)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module CrystalRuby
|
2
|
+
module Types
|
3
|
+
class Type
|
4
|
+
property memory : Pointer(::UInt8) = Pointer(::UInt8).null
|
5
|
+
|
6
|
+
macro method_missing(call)
|
7
|
+
current_value = self.native
|
8
|
+
current_hash = current_value.hash
|
9
|
+
return_value = current_value.{{ call }}
|
10
|
+
|
11
|
+
if current_hash != current_value.hash
|
12
|
+
self.value = current_value
|
13
|
+
end
|
14
|
+
return_value
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
native.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def synchronize
|
22
|
+
CrystalRuby.synchronize do
|
23
|
+
yield
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.synchronize
|
28
|
+
yield
|
29
|
+
end
|
30
|
+
|
31
|
+
def variable_width?
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
def return_value
|
36
|
+
memory
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.free(memory : Pointer(::UInt8))
|
40
|
+
LibC.free(memory)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.malloc(size : Int) : Pointer(::UInt8)
|
44
|
+
LibC.calloc(size, 1).as(Pointer(::UInt8))
|
45
|
+
end
|
46
|
+
|
47
|
+
def malloc(memsize)
|
48
|
+
self.class.malloc(memsize)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.each_child_address(pointer : Pointer(::UInt8), &block : Pointer(::UInt8) -> Nil)
|
52
|
+
# Do nothing
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.fetch_multi!(pointer : Pointer(::UInt8), size)
|
56
|
+
size.times.map { |i| fetch_single(pointer + i * refsize) }.to_a
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.fetch_multi_native!(pointer : Pointer(::UInt8), size)
|
60
|
+
size.times.map { |i| fetch_single(pointer + i * refsize).native }.to_a
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -1,66 +1,285 @@
|
|
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(", ")})"
|
42
80
|
end
|
43
81
|
end
|
44
82
|
|
45
|
-
def
|
46
|
-
|
83
|
+
def self.named_type_expr
|
84
|
+
if !inner_types
|
85
|
+
"::#{name || typename}"
|
86
|
+
elsif !inner_keys
|
87
|
+
"::#{name || typename}(#{inner_types.map(&:named_type_expr).join(", ")})"
|
88
|
+
else
|
89
|
+
"::#{name || typename}(#{inner_keys.zip(inner_types).map { |k, v| "#{k}: #{v.named_type_expr}" }.join(", ")})"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.valid_cast?(raw)
|
94
|
+
raw.is_a?(self) || convert_if.any? { |type| raw.is_a?(type) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.[](*value)
|
98
|
+
is_list_type = ancestors.any? { |a| a < CrystalRuby::Types::Array || a < CrystalRuby::Types::Tuple }
|
99
|
+
new(is_list_type ? value : value.first)
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.anonymous?
|
103
|
+
name.nil? || name.start_with?("CrystalRuby::Types::")
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.crystal_class_name
|
107
|
+
name || named_type_expr.split(",").join("_and_")
|
108
|
+
.split("|").join("_or_")
|
109
|
+
.split("(").join("_of_")
|
110
|
+
.gsub(/[^a-zA-Z0-9_]/, "")
|
111
|
+
.split("_")
|
112
|
+
.map(&:capitalize).join << "_#{type_digest[0..6]}"
|
47
113
|
end
|
48
114
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
115
|
+
def self.base_crystal_class_name
|
116
|
+
crystal_class_name.split("::").last
|
117
|
+
end
|
118
|
+
|
119
|
+
def value(native: false)
|
120
|
+
@value
|
121
|
+
end
|
122
|
+
|
123
|
+
def native
|
124
|
+
value(native: true)
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.type_digest
|
128
|
+
Digest::MD5.hexdigest(native_type_expr.to_s)
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.nested_types
|
132
|
+
[self, *(inner_types || []).map(&:nested_types)].flatten.uniq
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.pointer_to_crystal_type_conversion(expr)
|
136
|
+
anonymous? ? "#{crystal_class_name}.new(#{expr}).native" : "#{crystal_class_name}.new(#{expr})"
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.crystal_type_to_pointer_type_conversion(expr)
|
140
|
+
anonymous? ? "#{crystal_class_name}.new(#{expr}).return_value" : "#{expr}.return_value"
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.template_name
|
144
|
+
typename || superclass.template_name
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.type_defn
|
148
|
+
unless Template.const_defined?(template_name) && Template.const_get(template_name).is_a?(Template::Renderer)
|
149
|
+
raise "Template not found for #{template_name}"
|
150
|
+
end
|
151
|
+
|
152
|
+
Template.const_get(template_name).render(binding)
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.numeric?
|
156
|
+
false
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.primitive?
|
160
|
+
false
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.variable_width?
|
164
|
+
false
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.fixed_width?
|
168
|
+
false
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.cast!(value)
|
172
|
+
value.is_a?(Type) ? value.value : value
|
173
|
+
end
|
174
|
+
|
175
|
+
def ==(other)
|
176
|
+
value(native: true) == (other.is_a?(Type) ? other.value(native: true) : other)
|
177
|
+
end
|
178
|
+
|
179
|
+
def nil?
|
180
|
+
value.nil?
|
181
|
+
end
|
182
|
+
|
183
|
+
def coerce(other)
|
184
|
+
[other, value]
|
185
|
+
end
|
186
|
+
|
187
|
+
def inspect
|
188
|
+
value.inspect
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.from_ffi_array_repr(value)
|
192
|
+
anonymous? ? new(value).value : new(value)
|
193
|
+
end
|
194
|
+
|
195
|
+
def inner_value
|
196
|
+
@value
|
197
|
+
end
|
198
|
+
|
199
|
+
# Create a brand new copy of this object
|
200
|
+
def deep_dup
|
201
|
+
self.class.new(native)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Create a new reference to this object.
|
205
|
+
def dup
|
206
|
+
self.class.new(@memory)
|
207
|
+
end
|
208
|
+
|
209
|
+
def method_missing(method, *args, &block)
|
210
|
+
v = begin
|
211
|
+
native
|
212
|
+
rescue StandardError
|
213
|
+
super
|
214
|
+
end
|
215
|
+
if v.respond_to?(method)
|
216
|
+
hash_before = v.hash
|
217
|
+
result = v.send(method, *args, &block)
|
218
|
+
if v.hash != hash_before
|
219
|
+
self.value = v
|
220
|
+
v.equal?(result) ? self : result
|
221
|
+
else
|
222
|
+
result
|
223
|
+
end
|
52
224
|
else
|
53
|
-
|
225
|
+
super
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def self.bind_local_vars!(variable_names, binding)
|
230
|
+
variable_names.each do |name|
|
231
|
+
define_singleton_method(name) do
|
232
|
+
binding.local_variable_get("#{name}")
|
233
|
+
end
|
234
|
+
define_method(name) do
|
235
|
+
binding.local_variable_get("#{name}")
|
236
|
+
end
|
54
237
|
end
|
55
238
|
end
|
56
239
|
|
240
|
+
def self.each_child_address(pointer); end
|
241
|
+
|
242
|
+
def item_size
|
243
|
+
inner_types.map(&:memsize).sum
|
244
|
+
end
|
245
|
+
|
246
|
+
def self.crystal_type
|
247
|
+
lib_type(ffi_type)
|
248
|
+
end
|
249
|
+
|
250
|
+
def self.|(other)
|
251
|
+
raise "Cannot union non-crystal type #{other}" unless other.is_a?(Class) && other.ancestors.include?(Type)
|
252
|
+
|
253
|
+
CrystalRuby::Types::TaggedUnion(*union_types, *other.union_types)
|
254
|
+
end
|
255
|
+
|
57
256
|
def self.validate!(type)
|
58
|
-
unless type.is_a?(
|
257
|
+
unless type.is_a?(Class) && type.ancestors.include?(Types::Type)
|
59
258
|
raise "Result #{type} is not a valid CrystalRuby type"
|
60
259
|
end
|
61
260
|
|
62
261
|
raise "Invalid type: #{type.error}" unless type.valid?
|
63
262
|
end
|
263
|
+
|
264
|
+
def self.inner_type
|
265
|
+
inner_types.first
|
266
|
+
end
|
267
|
+
|
268
|
+
def self.type_expr
|
269
|
+
if !inner_types
|
270
|
+
inspect_name
|
271
|
+
elsif !anonymous?
|
272
|
+
name
|
273
|
+
elsif inner_keys
|
274
|
+
"#{inspect_name}(#{inner_keys.zip(inner_types).map { |k, v| "#{k}: #{v.inspect}" }.join(", ")})"
|
275
|
+
else
|
276
|
+
"#{inspect_name}(#{inner_types.map(&:inspect).join(", ")})"
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def self.inspect
|
281
|
+
type_expr
|
282
|
+
end
|
64
283
|
end
|
65
284
|
end
|
66
285
|
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
|