I want to implement my own static array class in ruby. It will be an array with a fixed capacity and all elements in the array will be of a single type. In order to have direct access to memory, I am using the FFI gem https://github.com/ffi/ffi which enables to create your own C functions and use them in your ruby program. I created a very simple C function that allocates memory for an array of integer and returns a pointer to the memory space :
int * create_static_array(int size) {
int *arr = malloc(size * sizeof(int));
return arr;
}
This is my ruby static_array class that uses create_static_array :
require 'ffi'
class StaticArray
attr_accessor :pointer, :capacity, :next_index
extend FFI::Library
ffi_lib './create_array/create_array.so'
attach_function :create_static_array, [:int], :pointer
def initialize(capacity)
@capacity = capacity
@pointer = create_static_array(capacity)
@next_index = 0
end
# adds value to the next_index in array
def push(val)
@pointer[@next_index].write(:int, val)
@next_index += 1
end
# reads value at index
def [](index)
raise IndexOutOfBoundException if index >= @capacity
self.pointer[index].read(:int)
end
# print every value in index
def print
i = 0
while (i < @capacity)
puts @pointer[i].read(:int)
i += 1
end
end
end
I added a couple methods to interact with my array, push elements, read elements at index... However my static_array instances are not exactly working as expected...
Let's say I write :
// creates a static array in memory which can store 4 ints
arr = StaticArray.new(4)
now let's push an int in our arr :
arr.push(20)
arr.print
will output
20
0
0
0
which makes sense. Now let's push another int into arr :
arr.push(16)
and arr.print
again :
4116
16
0
0
20 has been replaced by 4116 ... I cant really get what is going on here ?
The FFI interface doesn't know about the type of your pointer, so it is just treating it as a byte array (see initialize on the pointer type). Note that while you do pass :int
, this is to the specific write
and read
, not where you are doing the indexing. Thus you are writing and printing at byte offsets 0,1,2,3 rather than the integer elements at 0,4,8,12.
On a little endian system, with a 32bit, 4 byte int, the binary value of 20 is 14 00 00 00
and 16 is 10 00 00 00
.
So you allocate 4*4 bytes, so 32 bytes, the first 8 being.
00 00 00 00 00 00 00 00
And write 20 at offset 0
14 00 00 00 00 00 00 00
And then write 16 at offset 1
14 10 00 00 00 00 00 00
14 10 00 00
is 0x00001014
or 4116, and then at the next offset you print it is 10 00 00 00
which is 16.