crystalruby 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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