Class: CrystalRuby::Types::FixedWidth
- Defined in:
- lib/crystalruby/types/fixed_width.rb
Overview
For a fixed width type, we allocate a single block block of memory of the form
- ref_count (uint32), data(uint8*)
Direct Known Subclasses
Constant Summary
Constants inherited from Type
Constants included from CrystalRuby::Typemaps
CrystalRuby::Typemaps::CRYSTAL_TYPE_MAP, CrystalRuby::Typemaps::C_TYPE_CONVERSIONS, CrystalRuby::Typemaps::C_TYPE_MAP, CrystalRuby::Typemaps::ERROR_VALUE, CrystalRuby::Typemaps::FFI_TYPE_MAP
Instance Attribute Summary
Attributes inherited from Type
Class Method Summary collapse
-
.build(typename = nil, error: nil, inner_types: nil, inner_keys: nil, ffi_type: :pointer, memsize: FFI.type_size(ffi_type), refsize: 8, convert_if: [], superclass: FixedWidth, size_offset: 4, data_offset: 4, &block) ⇒ Object
Build a new FixedWith subtype Layout varies according to the sizes of internal types.
- .crystal_supertype ⇒ Object
- .crystal_type ⇒ Object
- .decr_inner_ref_counts!(pointer) ⇒ Object
- .decrement_ref_count!(memory, by = 1) ⇒ Object
-
.fetch_multi(pointer, size, native: false) ⇒ Object
Fetch an array of a given data type from a list pointer (Type can be a byte-array, pointer or numeric type).
-
.fetch_single(pointer, native: false) ⇒ Object
Read a value of this type from the contained pointer at a given index.
- .finalize(memory) ⇒ Object
- .free!(memory) ⇒ Object
- .increment_ref_count!(memory, by = 1) ⇒ Object
-
.to_ffi_repr(value) ⇒ Object
Each type should be convertible to an FFI representation.
-
.write_single(pointer, value) ⇒ Object
Write a data type into a pointer at a given index (Type can be a byte-array, pointer or numeric type.
Instance Method Summary collapse
- #address ⇒ Object
- #allocate_new_from_reference!(memory) ⇒ Object
- #allocate_new_from_value!(rbval) ⇒ Object
-
#data_pointer ⇒ Object
Data pointer follows the ref count (and size for variable width types) In the case of variable width types the data pointer points to the start of a separate data block So this method is overridden inside variable_width.rb to resolve this pointer.
-
#initialize(rbval) ⇒ FixedWidth
constructor
We can instantiate it with a Value (for a new object) or a Pointer (for a copy of an existing object).
-
#ref_count ⇒ Object
Ref count is always the first Int32 in the memory block.
- #ref_count=(val) ⇒ Object
- #size ⇒ Object
-
#value=(value) ⇒ Object
If we are fixed with, The memory we allocate a single block of memory, if not already given.
Methods inherited from Type
#==, [], anonymous?, base_crystal_class_name, bind_local_vars!, cast!, #coerce, crystal_class_name, crystal_type_to_pointer_type_conversion, #deep_dup, #dup, each_child_address, fixed_width?, from_ffi_array_repr, inner_type, #inner_value, inspect, #inspect, inspect_name, #item_size, #method_missing, #native, native_type_expr, nested_types, #nil?, numeric?, pointer_to_crystal_type_conversion, primitive?, template_name, type_defn, type_digest, type_expr, union_types, valid?, valid_cast?, validate!, variable_width?, |
Methods included from CrystalRuby::Typemaps
#build_type_map, #convert_crystal_to_lib_type, #convert_lib_to_crystal_type, #crystal_type, #error_value, #ffi_type, #lib_type
Methods included from Allocator
Constructor Details
#initialize(rbval) ⇒ FixedWidth
We can instantiate it with a Value (for a new object) or a Pointer (for a copy of an existing object)
8 9 10 11 12 13 14 15 16 |
# File 'lib/crystalruby/types/fixed_width.rb', line 8 def initialize(rbval) super case rbval when FFI::Pointer then allocate_new_from_reference!(rbval) else allocate_new_from_value!(rbval) end self.class.increment_ref_count!(memory) ObjectSpace.define_finalizer(self, self.class.finalize(memory)) end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method in the class CrystalRuby::Types::Type
Class Method Details
.build(typename = nil, error: nil, inner_types: nil, inner_keys: nil, ffi_type: :pointer, memsize: FFI.type_size(ffi_type), refsize: 8, convert_if: [], superclass: FixedWidth, size_offset: 4, data_offset: 4, &block) ⇒ Object
Build a new FixedWith subtype Layout varies according to the sizes of internal types
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/crystalruby/types/fixed_width.rb', line 171 def self.build( typename = nil, error: nil, inner_types: nil, inner_keys: nil, ffi_type: :pointer, memsize: FFI.type_size(ffi_type), refsize: 8, convert_if: [], superclass: FixedWidth, size_offset: 4, data_offset: 4, &block ) inner_types&.each(&Type.method(:validate!)) Class.new(superclass) do bind_local_vars!( %i[typename error inner_types inner_keys ffi_type memsize convert_if size_offset data_offset refsize], binding ) class_eval(&block) if block_given? def self.fixed_width? true end end end |
.crystal_supertype ⇒ Object
144 145 146 |
# File 'lib/crystalruby/types/fixed_width.rb', line 144 def self.crystal_supertype "CrystalRuby::Types::FixedWidth" end |
.crystal_type ⇒ Object
148 149 150 |
# File 'lib/crystalruby/types/fixed_width.rb', line 148 def self.crystal_type "Pointer(::UInt8)" end |
.decr_inner_ref_counts!(pointer) ⇒ Object
110 111 112 113 114 115 116 117 118 |
# File 'lib/crystalruby/types/fixed_width.rb', line 110 def self.decr_inner_ref_counts!(pointer) each_child_address(pointer) do |child_type, child_address| child_type.decrement_ref_count!(child_address.read_pointer) if child_type.fixed_width? end # Free data block, if we're a variable width type. return unless variable_width? free(pointer[data_offset].read_pointer) end |
.decrement_ref_count!(memory, by = 1) ⇒ Object
94 95 96 97 98 99 |
# File 'lib/crystalruby/types/fixed_width.rb', line 94 def self.decrement_ref_count!(memory, by = 1) synchronize { memory.write_int32(memory.read_int32 - by) } return unless memory.read_int32.zero? free!(memory) end |
.fetch_multi(pointer, size, native: false) ⇒ Object
Fetch an array of a given data type from a list pointer (Type can be a byte-array, pointer or numeric type)
86 87 88 |
# File 'lib/crystalruby/types/fixed_width.rb', line 86 def self.fetch_multi(pointer, size, native: false) size.times.map { |i| fetch_single(pointer[i * refsize], native: native) } end |
.fetch_single(pointer, native: false) ⇒ Object
Read a value of this type from the contained pointer at a given index
61 62 63 64 65 66 67 |
# File 'lib/crystalruby/types/fixed_width.rb', line 61 def self.fetch_single(pointer, native: false) # Nothing to fetch for Nils return if memsize.zero? value_pointer = pointer.read_pointer native ? new(value_pointer).native : new(value_pointer) end |
.finalize(memory) ⇒ Object
18 19 20 21 22 |
# File 'lib/crystalruby/types/fixed_width.rb', line 18 def self.finalize(memory) lambda { |_| decrement_ref_count!(memory) } end |
.free!(memory) ⇒ Object
101 102 103 104 105 106 107 108 |
# File 'lib/crystalruby/types/fixed_width.rb', line 101 def self.free!(memory) # Decrease ref counts for any data we are pointing to # Also responsible for freeing internal memory if ref count reaches zero decr_inner_ref_counts!(memory) # # Free slot memory free(memory) end |
.increment_ref_count!(memory, by = 1) ⇒ Object
90 91 92 |
# File 'lib/crystalruby/types/fixed_width.rb', line 90 def self.increment_ref_count!(memory, by = 1) synchronize { memory.write_int32(memory.read_int32 + by) } end |
.to_ffi_repr(value) ⇒ Object
Each type should be convertible to an FFI representation. (I.e. how is a value or reference to this value stored within e.g. an Array, Hash, Tuple or any other containing type). For both fixed and variable types these are simply stored within the containing type as a pointer to the memory block. We return the pointer to this memory here.
53 54 55 56 57 |
# File 'lib/crystalruby/types/fixed_width.rb', line 53 def self.to_ffi_repr(value) to_store = new(value) increment_ref_count!(to_store.memory) to_store.memory end |
.write_single(pointer, value) ⇒ Object
Write a data type into a pointer at a given index (Type can be a byte-array, pointer or numeric type
)
73 74 75 76 77 78 79 80 81 82 |
# File 'lib/crystalruby/types/fixed_width.rb', line 73 def self.write_single(pointer, value) # Dont need to write nils return if memsize.zero? decrement_ref_count!(pointer.read_pointer) unless pointer.read_pointer.null? memory = malloc(refsize + data_offset) copy_to!(cast!(value), memory: memory) increment_ref_count!(memory) pointer.write_pointer(memory) end |
Instance Method Details
#address ⇒ Object
140 141 142 |
# File 'lib/crystalruby/types/fixed_width.rb', line 140 def address @memory.address end |
#allocate_new_from_reference!(memory) ⇒ Object
41 42 43 44 45 46 |
# File 'lib/crystalruby/types/fixed_width.rb', line 41 def allocate_new_from_reference!(memory) # When we point to an existing block of memory, we don't need to allocate anything. # This memory should be to a single, separately allocated block of the above size. # When this type is contained within another type, it should be as a pointer to this block (not the contents of the block itself). self.memory = memory end |
#allocate_new_from_value!(rbval) ⇒ Object
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/crystalruby/types/fixed_width.rb', line 24 def allocate_new_from_value!(rbval) # New block of memory, to hold our object. # For variable with, this is 2x UInt32 for ref count and size, plus a data pointer (8 bytes) # Layout: # - ref_count (4 bytes) # - size (4 bytes) # - data (8 bytes) # # For fixed the data is inline # Layout: # - ref_count (4 bytes) # - size (0 bytes) (No size for fixed width types) # - data (memsize bytes) self.memory = malloc(refsize + data_offset) self.value = rbval end |
#data_pointer ⇒ Object
Data pointer follows the ref count (and size for variable width types) In the case of variable width types the data pointer points to the start of a separate data block So this method is overridden inside variable_width.rb to resolve this pointer.
132 133 134 |
# File 'lib/crystalruby/types/fixed_width.rb', line 132 def data_pointer memory[data_offset].read_pointer end |
#ref_count ⇒ Object
Ref count is always the first Int32 in the memory block
121 122 123 |
# File 'lib/crystalruby/types/fixed_width.rb', line 121 def ref_count memory.read_uint32 end |
#ref_count=(val) ⇒ Object
125 126 127 |
# File 'lib/crystalruby/types/fixed_width.rb', line 125 def ref_count=(val) memory.write_int32(val) end |
#size ⇒ Object
136 137 138 |
# File 'lib/crystalruby/types/fixed_width.rb', line 136 def size memory[size_offset].read_int32 end |
#value=(value) ⇒ Object
If we are fixed with, The memory we allocate a single block of memory, if not already given. Within this block of memory, we copy our contents directly.
If we are variable width, we allocate a small block of memory for the pointer only and allocate a separate block of memory for the data. We store the pointer to the data in the memory block.
160 161 162 163 164 165 166 167 |
# File 'lib/crystalruby/types/fixed_width.rb', line 160 def value=(value) # If we're already pointing at something # Decrement the ref counts of anything we're pointing at value = cast!(value) self.class.decr_inner_ref_counts!(memory) if ref_count > 0 self.class.copy_to!(value, memory: memory) end |