Module: CrystalRuby::Typemaps

Included in:
Function, Library, CrystalRuby::Types::Type
Defined in:
lib/crystalruby/typemaps.rb

Constant Summary collapse

CRYSTAL_TYPE_MAP =
{
  char: "Int8",             # In Crystal, :char is typically represented as Int8
  uchar: "UInt8",           # Unsigned char
  int8: "Int8",             # Same as :char
  uint8: "UInt8",           # Same as :uchar
  short: "Int16",           # Short integer
  ushort: "UInt16",         # Unsigned short integer
  int16: "Int16",           # Same as :short
  uint16: "UInt16",         # Same as :ushort
  int: "Int32",             # Integer, Crystal defaults to 32 bits
  uint: "UInt32",           # Unsigned integer
  int32: "Int32",           # 32-bit integer
  uint32: "UInt32",         # 32-bit unsigned integer
  long: "Int32 | Int64",    # Long integer, size depends on the platform (32 or 64 bits)
  ulong: "UInt32 | UInt64", # Unsigned long integer, size depends on the platform
  int64: "Int64",           # 64-bit integer
  uint64: "UInt64",         # 64-bit unsigned integer
  long_long: "Int64",       # Same as :int64
  ulong_long: "UInt64",     # Same as :uint64
  float: "Float32",         # Floating point number (single precision)
  double: "Float64",        # Double precision floating point number
  bool: "Bool",             # Boolean type
  void: "Void",             # Void type
  string: "String",         # String type
  pointer: "Pointer(Void)"  # Pointer type

}
FFI_TYPE_MAP =
CRYSTAL_TYPE_MAP.invert
ERROR_VALUE =
{
  char: "0i8", # In Crystal, :char is typically represented as Int8
  uchar: "0u8", # Unsigned char
  int8: "0i8", # Same as :char
  uint8: "0u8", # Same as :uchar
  short: "0i16", # Short integer
  ushort: "0u16", # Unsigned short integer
  int16: "0i16", # Same as :short
  uint16: "0u16", # Same as :ushort
  int: "0i32", # Integer, Crystal defaults to 32 bits
  uint: "0u32", # Unsigned integer
  int32: "0i32", # 32-bit integer
  uint32: "0u32", # 32-bit unsigned integer
  long: "0i64", # Long integer, size depends on the platform (32 or 64 bits)
  ulong: "0u64", # Unsigned long integer, size depends on the platform
  int64: "0_i64", # 64-bit integer
  uint64: "0_u64", # 64-bit unsigned integer
  long_long: "0_i64",  # Same as :int64
  ulong_long: "0_u64", # Same as :uint64
  float: "0.0f32",    # Floating point number (single precision)
  double: "0.0f64",   # Double precision floating point number
  bool: "false", # Boolean type
  void: "Void", # Void type
  string: '"".to_unsafe', # String type
  pointer: "Pointer(Void).null" # Pointer type
}
C_TYPE_MAP =
CRYSTAL_TYPE_MAP.merge(
  {
    string: "Pointer(UInt8)"
  }
)
C_TYPE_CONVERSIONS =
{
  string: {
    from: "String.new(%s)",
    to: "%s.to_unsafe"
  },
  void: {
    to: "nil"
  }
}.tap do |hash|
  hash.define_singleton_method(:convert) do |type, dir, expr|
    if hash.key?(type)
      conversion_string = hash[type][dir]
      conversion_string =~ /%/ ? conversion_string % expr : conversion_string
    else
      expr
    end
  end
end

Instance Method Summary collapse

Instance Method Details

#build_type_map(crystalruby_type) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/crystalruby/typemaps.rb', line 87

def build_type_map(crystalruby_type)
  crystalruby_type = CRType(&crystalruby_type) if crystalruby_type.is_a?(Proc)
  {
    ffi_type: ffi_type(crystalruby_type),
    ffi_ret_type: ffi_type(crystalruby_type),
    crystal_type: crystal_type(crystalruby_type),
    crystalruby_type: crystalruby_type,
    lib_type: lib_type(crystalruby_type),
    error_value: error_value(crystalruby_type),
    arg_mapper: if crystalruby_type.is_a?(Class) && crystalruby_type < Types::Type
                  lambda { |arg|
                    arg = crystalruby_type.new(arg.memory) if arg.is_a?(Types::Type) && !arg.is_a?(crystalruby_type)
                    arg = crystalruby_type.new(arg) unless arg.is_a?(Types::Type)
                    arg
                  }
                end,
    retval_mapper: if crystalruby_type.is_a?(Class) && crystalruby_type < Types::Type
                     lambda { |arg|
                       if arg.is_a?(Types::Type) && !arg.is_a?(crystalruby_type)
                         arg = crystalruby_type.new(arg.memory)
                       end
                       arg = crystalruby_type.new(arg) unless arg.is_a?(Types::Type)
                       crystalruby_type.anonymous? ? arg.native : arg
                     }
                   end,
    convert_crystal_to_lib_type: ->(expr) { convert_crystal_to_lib_type(expr, crystalruby_type) },
    convert_lib_to_crystal_type: ->(expr) { convert_lib_to_crystal_type(expr, crystalruby_type) }
  }
end

#convert_crystal_to_lib_type(expr, type) ⇒ Object



174
175
176
177
178
179
180
# File 'lib/crystalruby/typemaps.rb', line 174

def convert_crystal_to_lib_type(expr, type)
  if type.is_a?(Class) && type < Types::Type
    type.crystal_type_to_pointer_type_conversion(expr)
  else
    C_TYPE_CONVERSIONS.convert(type, :to, expr)
  end
end

#convert_lib_to_crystal_type(expr, type) ⇒ Object



163
164
165
166
167
168
169
170
171
172
# File 'lib/crystalruby/typemaps.rb', line 163

def convert_lib_to_crystal_type(expr, type)
  if type.is_a?(Class) && type < Types::Type
    expr = "#{expr}.not_nil!" unless type.nil?
    type.pointer_to_crystal_type_conversion(expr)
  elsif type == :void
    "nil"
  else
    "#{C_TYPE_CONVERSIONS.convert(type, :from, expr)}.not_nil!"
  end
end

#crystal_type(type) ⇒ Object



153
154
155
156
157
158
159
160
161
# File 'lib/crystalruby/typemaps.rb', line 153

def crystal_type(type)
  if type.is_a?(Class) && type < Types::Type
    type.anonymous? ? type.native_type_expr : type.inspect
  else
    CRYSTAL_TYPE_MAP.fetch(type)
  end
rescue StandardError
  raise "Unsupported type #{type}"
end

#error_value(type) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
# File 'lib/crystalruby/typemaps.rb', line 141

def error_value(type)
  if type.is_a?(Class) && type < Types::FixedWidth
    "Pointer(::UInt8).null"
  elsif type.is_a?(Class) && type < Types::Type
    ERROR_VALUE.fetch(type.ffi_type)
  else
    ERROR_VALUE.fetch(type)
  end
rescue StandardError
  raise "Unsupported type #{type}"
end

#ffi_type(type) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
# File 'lib/crystalruby/typemaps.rb', line 117

def ffi_type(type)
  case type
  when Symbol then type
  when Class
    if type < Types::FixedWidth
      :pointer
    elsif type < Types::Primitive
      type.ffi_type
    end
  end
end

#lib_type(type) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
# File 'lib/crystalruby/typemaps.rb', line 129

def lib_type(type)
  if type.is_a?(Class) && type < Types::FixedWidth
    "Pointer(::UInt8)"
  elsif type.is_a?(Class) && type < Types::Type
    C_TYPE_MAP.fetch(type.ffi_type)
  else
    C_TYPE_MAP.fetch(type)
  end
rescue StandardError
  raise "Unsupported type #{type}"
end