lib/ffi-glib/ptr_array.rb
# frozen_string_literal: true
require "ffi-glib/container_class_methods"
require "gir_ffi/array_element_convertor"
GLib.load_class :PtrArray
module GLib
# Overrides for GPtrArray, GLib's automatically growing array of
# pointers.
class PtrArray
include Enumerable
extend ContainerClassMethods
attr_reader :element_type
POINTER_SIZE = FFI.type_size(:pointer)
class << self
# Remove stub generated by builder.
remove_method :add if method_defined? :add
end
def initialize(type)
@element_type = type
store_pointer Lib.g_ptr_array_new
end
def self.from_enumerable(type, arr)
new(type).tap { |it| it.add_array arr }
end
def self.add(array, data)
array.add data
end
def reset_typespec(typespec)
@element_type = typespec
self
end
def add(data)
ptr = GirFFI::InPointer.from element_type, data
Lib.g_ptr_array_add self, ptr
end
def add_array(ary)
ary.each { |item| add item }
end
# Re-implementation of the g_ptrarray_index macro
def index(idx)
item_ptr = item_pointer(idx)
convertor = GirFFI::ArrayElementConvertor.new convert_element_type, item_ptr
convertor.to_ruby_value
end
def each
length.times do |idx|
yield index(idx)
end
end
def length
struct[:len]
end
def ==(other)
to_a == other.to_a
end
private
def item_pointer(idx)
check_bounds(idx)
data_ptr + idx * element_size
end
def check_bounds(idx)
unless (0...length).cover? idx
raise IndexError, "Index #{idx} outside of bounds 0..#{length - 1}"
end
end
def convert_element_type
case element_type
when :utf8
:utf8
when GirFFI::ObjectBase
element_type
else
[:pointer, element_type]
end
end
def element_size
POINTER_SIZE
end
def data_ptr
struct[:pdata]
end
end
end