crystalruby 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2 -0
  3. data/README.md +389 -193
  4. data/Rakefile +4 -3
  5. data/crystalruby.gemspec +2 -2
  6. data/exe/crystalruby +1 -0
  7. data/lib/crystalruby/adapter.rb +131 -65
  8. data/lib/crystalruby/arc_mutex.rb +47 -0
  9. data/lib/crystalruby/compilation.rb +32 -3
  10. data/lib/crystalruby/config.rb +41 -37
  11. data/lib/crystalruby/function.rb +211 -68
  12. data/lib/crystalruby/library.rb +153 -48
  13. data/lib/crystalruby/reactor.rb +40 -23
  14. data/lib/crystalruby/source_reader.rb +86 -0
  15. data/lib/crystalruby/template.rb +16 -5
  16. data/lib/crystalruby/templates/function.cr +11 -10
  17. data/lib/crystalruby/templates/index.cr +53 -66
  18. data/lib/crystalruby/templates/inline_chunk.cr +1 -1
  19. data/lib/crystalruby/templates/ruby_interface.cr +34 -0
  20. data/lib/crystalruby/templates/top_level_function.cr +62 -0
  21. data/lib/crystalruby/templates/top_level_ruby_interface.cr +33 -0
  22. data/lib/crystalruby/typebuilder.rb +11 -55
  23. data/lib/crystalruby/typemaps.rb +92 -67
  24. data/lib/crystalruby/types/concerns/allocator.rb +80 -0
  25. data/lib/crystalruby/types/fixed_width/named_tuple.cr +80 -0
  26. data/lib/crystalruby/types/fixed_width/named_tuple.rb +86 -0
  27. data/lib/crystalruby/types/fixed_width/proc.cr +45 -0
  28. data/lib/crystalruby/types/fixed_width/proc.rb +79 -0
  29. data/lib/crystalruby/types/fixed_width/tagged_union.cr +53 -0
  30. data/lib/crystalruby/types/fixed_width/tagged_union.rb +109 -0
  31. data/lib/crystalruby/types/fixed_width/tuple.cr +82 -0
  32. data/lib/crystalruby/types/fixed_width/tuple.rb +92 -0
  33. data/lib/crystalruby/types/fixed_width.cr +138 -0
  34. data/lib/crystalruby/types/fixed_width.rb +205 -0
  35. data/lib/crystalruby/types/primitive.cr +21 -0
  36. data/lib/crystalruby/types/primitive.rb +117 -0
  37. data/lib/crystalruby/types/primitive_types/bool.cr +34 -0
  38. data/lib/crystalruby/types/primitive_types/bool.rb +11 -0
  39. data/lib/crystalruby/types/primitive_types/nil.cr +35 -0
  40. data/lib/crystalruby/types/primitive_types/nil.rb +16 -0
  41. data/lib/crystalruby/types/primitive_types/numbers.cr +37 -0
  42. data/lib/crystalruby/types/primitive_types/numbers.rb +28 -0
  43. data/lib/crystalruby/types/primitive_types/symbol.cr +55 -0
  44. data/lib/crystalruby/types/primitive_types/symbol.rb +35 -0
  45. data/lib/crystalruby/types/primitive_types/time.cr +35 -0
  46. data/lib/crystalruby/types/primitive_types/time.rb +25 -0
  47. data/lib/crystalruby/types/type.cr +64 -0
  48. data/lib/crystalruby/types/type.rb +239 -30
  49. data/lib/crystalruby/types/variable_width/array.cr +74 -0
  50. data/lib/crystalruby/types/variable_width/array.rb +88 -0
  51. data/lib/crystalruby/types/variable_width/hash.cr +146 -0
  52. data/lib/crystalruby/types/variable_width/hash.rb +117 -0
  53. data/lib/crystalruby/types/variable_width/string.cr +36 -0
  54. data/lib/crystalruby/types/variable_width/string.rb +18 -0
  55. data/lib/crystalruby/types/variable_width.cr +23 -0
  56. data/lib/crystalruby/types/variable_width.rb +46 -0
  57. data/lib/crystalruby/types.rb +32 -13
  58. data/lib/crystalruby/version.rb +2 -2
  59. data/lib/crystalruby.rb +13 -6
  60. metadata +41 -19
  61. data/lib/crystalruby/types/array.rb +0 -15
  62. data/lib/crystalruby/types/bool.rb +0 -3
  63. data/lib/crystalruby/types/hash.rb +0 -17
  64. data/lib/crystalruby/types/named_tuple.rb +0 -28
  65. data/lib/crystalruby/types/nil.rb +0 -3
  66. data/lib/crystalruby/types/numbers.rb +0 -5
  67. data/lib/crystalruby/types/string.rb +0 -3
  68. data/lib/crystalruby/types/symbol.rb +0 -3
  69. data/lib/crystalruby/types/time.rb +0 -8
  70. data/lib/crystalruby/types/tuple.rb +0 -17
  71. data/lib/crystalruby/types/type_serializer/json.rb +0 -41
  72. data/lib/crystalruby/types/type_serializer.rb +0 -37
  73. data/lib/crystalruby/types/typedef.rb +0 -57
  74. data/lib/crystalruby/types/union_type.rb +0 -43
  75. 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