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