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
#ffi_primitive, #memory, #value
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, ffi_primitive: false, &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, type) ⇒ 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
- #total_memsize ⇒ 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, ffi_primitive_type, fixed_width?, from_ffi_array_repr, inner_type, #inner_value, #inspect, inspect, inspect_name, #item_size, #method_missing, named_type_expr, #native, native_type_expr, nested_types, #nil?, numeric?, pointer_to_crystal_type_conversion, primitive?, subclass?, 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
gc_bytes_seen, gc_hint!, gc_hint_reset!, included
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 17 |
# 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, self.class)) Allocator.gc_hint!(total_memsize) 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, ffi_primitive: false, &block) ⇒ Object
Build a new FixedWith subtype Layout varies according to the sizes of internal types
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/crystalruby/types/fixed_width.rb', line 176 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, ffi_primitive: false, &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 ffi_primitive], binding ) class_eval(&block) if block_given? def self.fixed_width? true end end end |
.crystal_supertype ⇒ Object
149 150 151 |
# File 'lib/crystalruby/types/fixed_width.rb', line 149 def self.crystal_supertype "CrystalRuby::Types::FixedWidth" end |
.crystal_type ⇒ Object
153 154 155 |
# File 'lib/crystalruby/types/fixed_width.rb', line 153 def self.crystal_type "Pointer(::UInt8)" end |
.decr_inner_ref_counts!(pointer) ⇒ Object
111 112 113 114 115 116 117 118 119 |
# File 'lib/crystalruby/types/fixed_width.rb', line 111 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
95 96 97 98 99 100 |
# File 'lib/crystalruby/types/fixed_width.rb', line 95 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)
87 88 89 |
# File 'lib/crystalruby/types/fixed_width.rb', line 87 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
62 63 64 65 66 67 68 |
# File 'lib/crystalruby/types/fixed_width.rb', line 62 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, type) ⇒ Object
19 20 21 22 23 |
# File 'lib/crystalruby/types/fixed_width.rb', line 19 def self.finalize(memory, type) lambda do |_| decrement_ref_count!(memory) end end |
.free!(memory) ⇒ Object
102 103 104 105 106 107 108 109 |
# File 'lib/crystalruby/types/fixed_width.rb', line 102 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
91 92 93 |
# File 'lib/crystalruby/types/fixed_width.rb', line 91 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.
54 55 56 57 58 |
# File 'lib/crystalruby/types/fixed_width.rb', line 54 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
)
74 75 76 77 78 79 80 81 82 83 |
# File 'lib/crystalruby/types/fixed_width.rb', line 74 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
145 146 147 |
# File 'lib/crystalruby/types/fixed_width.rb', line 145 def address @memory.address end |
#allocate_new_from_reference!(memory) ⇒ Object
42 43 44 45 46 47 |
# File 'lib/crystalruby/types/fixed_width.rb', line 42 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
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/crystalruby/types/fixed_width.rb', line 25 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.
133 134 135 |
# File 'lib/crystalruby/types/fixed_width.rb', line 133 def data_pointer memory[data_offset].read_pointer end |
#ref_count ⇒ Object
Ref count is always the first Int32 in the memory block
122 123 124 |
# File 'lib/crystalruby/types/fixed_width.rb', line 122 def ref_count memory.read_uint32 end |
#ref_count=(val) ⇒ Object
126 127 128 |
# File 'lib/crystalruby/types/fixed_width.rb', line 126 def ref_count=(val) memory.write_int32(val) end |
#size ⇒ Object
137 138 139 |
# File 'lib/crystalruby/types/fixed_width.rb', line 137 def size memory[size_offset].read_int32 end |
#total_memsize ⇒ Object
141 142 143 |
# File 'lib/crystalruby/types/fixed_width.rb', line 141 def total_memsize memsize + refsize + size 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.
165 166 167 168 169 170 171 172 |
# File 'lib/crystalruby/types/fixed_width.rb', line 165 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 |