Создание класса статического массива в ruby ​​с помощью ruby ​​FFI - PullRequest
2 голосов
/ 08 апреля 2019

Я хочу реализовать свой собственный класс статического массива в ruby. Это будет массив с фиксированной емкостью, и все элементы в массиве будут одного типа. Чтобы иметь прямой доступ к памяти, я использую FFI gem https://github.com/ffi/ffi, который позволяет создавать свои собственные C-функции и использовать их в вашей программе ruby. Я создал очень простую функцию C, которая выделяет память для целочисленного массива и возвращает указатель на пространство памяти:

int * create_static_array(int size) {
  int *arr = malloc(size * sizeof(int));
  return arr;
}

Это мой класс ruby ​​static_array, который использует 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

Я добавил пару методов для взаимодействия с моим массивом, push-элементов, чтения элементов по индексу ... Однако мои экземпляры static_array работают не совсем так, как ожидалось ...

Допустим, я пишу:

// creates a static array in memory which can store 4 ints
arr = StaticArray.new(4)

Теперь давайте вставим int в нашем arr:

arr.push(20)

arr.print выдаст

20
0
0
0

что имеет смысл. Теперь давайте вставим еще один int в arr:

arr.push(16)

и arr.print снова:

4116
16
0
0

20 был заменен на 4116 ... Я не могу понять, что здесь происходит?

Вот ссылка на документ класса FFIPointer, если это поможет https://www.rubydoc.info/github/ffi/ffi/FFI/Pointer

1 Ответ

5 голосов
/ 11 апреля 2019

Интерфейс FFI не знает о типе вашего указателя, поэтому он просто обрабатывает его как байтовый массив (см. Initialize для типа указателя). Обратите внимание, что в то время как вы передаете :int, это относится к конкретным write и read, а не к тому месту, где вы выполняете индексацию. Таким образом, вы пишете и печатаете с байтовыми смещениями 0,1,2,3, а не с целочисленными элементами 0,4,8,12.

В системе с прямым порядком байтов, с 32-битным 4-байтовым int двоичное значение 20 равно 14 00 00 00, а 16 - 10 00 00 00.

Таким образом, вы выделяете 4 * 4 байта, то есть 32 байта, первые 8 -.

00 00 00 00 00 00 00 00

И написать 20 со смещением 0

14 00 00 00 00 00 00 00

А затем написать 16 со смещением 1

14 10 00 00 00 00 00 00

14 10 00 00 равен 0x00001014 или 4116, а затем при следующем смещении, которое вы печатаете, это 10 00 00 00, что равно 16.

...