Feature request : new classes `Uint64View`, `Int64View`, `Float32View`, and `Float64View`
kojix2 opened this issue · 1 comments
Hi!
Thank you for creating very useful library.
Motivation
I am building a tool as a hobby to call WebAssembly functions generated by Crystal language from Ruby to speed up. When I tried to pass a Ruby array to Wasm, I noticed that int64_view
, uint64_view
, float32_view
, and float64_view
were not yet provided.
Proposed solution
new methods: uint64_view
, int64_view
, float32_view
, and float64_view
new classes Uint64View
, Int64View
, Float32View
, and Float64View
Alternatives
The use of binary strings is a possible alternative. In that case, Ruby methods such as pack
and unpack
could be used.
Additional context
You can find similar functionality in Python bindings.
wasmerio/wasmer-python@b484654
The method mentioned in the "Alternative" section using a binary string with pack
and unpack
is the method used in wasmtime-rb.
These are Memory#read(offset, size)
and Memory#write(offset, value)
. I think this is a very easy-to-use and highly versatile method. Therefore, it might be better to adopt this method rather than adding a new View.
I am a little unsure if the code below is completely correct, but it should work like this.
Write:
memory.write(
addr,
case type
when 'int8' then arg.pack('c*')
when 'uint8' then arg.pack('C*')
when 'int16' then arg.pack('s*')
when 'uint16' then arg.pack('S*')
when 'int32' then arg.pack('l*')
when 'uint32' then arg.pack('L*')
when 'int64' then arg.pack('q*')
when 'uint64' then arg.pack('Q*')
when 'float32' then arg.pack('e*')
when 'float64' then arg.pack('E*')
else raise "unsupported type: #{type}"
end
)
Read:
case type
when 'int8' then memory.read(addr, len * 1).unpack('c*')
when 'uint8' then memory.read(addr, len * 1).unpack('C*')
when 'int16' then memory.read(addr, len * 2).unpack('s*')
when 'uint16' then memory.read(addr, len * 2).unpack('S*')
when 'int32' then memory.read(addr, len * 4).unpack('l*')
when 'uint32' then memory.read(addr, len * 4).unpack('L*')
when 'int64' then memory.read(addr, len * 8).unpack('q*')
when 'uint64' then memory.read(addr, len * 8).unpack('Q*')
when 'float32' then memory.read(addr, len * 4).unpack('e*')
when 'float64' then memory.read(addr, len * 8).unpack('E*')
else raise "unsupported type: #{type}"
end
Of course, the same thing can be done with the current version of wasmer-ruby using uint8_view
.
Write:
uint8_view = memory.uint8_view(addr)
arg_uint8 = case type
when 'int8' then arg.pack('c*')
when 'uint8' then arg.pack('C*')
when 'int16' then arg.pack('s*')
when 'uint16' then arg.pack('S*')
when 'int32' then arg.pack('l*')
when 'uint32' then arg.pack('L*')
when 'int64' then arg.pack('q*')
when 'uint64' then arg.pack('Q*')
when 'float32' then arg.pack('e*')
when 'float64' then arg.pack('E*')
else raise "unsupported type: #{type}"
end.unpack('C*')
arg_uint8.each_with_index do |a, i|
uint8_view[i] = a
end
Read:
uint8_view = memory.uint8_view(addr)
case type
when 'int8' then Array.new(len * 1) { |i| uint8_view[i] }.pack('C*').unpack('c*')
when 'uint8' then Array.new(len * 1) { |i| uint8_view[i] }.pack('C*').unpack('C*')
when 'int16' then Array.new(len * 2) { |i| uint8_view[i] }.pack('C*').unpack('s*')
when 'uint16' then Array.new(len * 2) { |i| uint8_view[i] }.pack('C*').unpack('S*')
when 'int32' then Array.new(len * 4) { |i| uint8_view[i] }.pack('C*').unpack('l*')
when 'uint32' then Array.new(len * 4) { |i| uint8_view[i] }.pack('C*').unpack('L*')
when 'int64' then Array.new(len * 8) { |i| uint8_view[i] }.pack('C*').unpack('q*')
when 'uint64' then Array.new(len * 8) { |i| uint8_view[i] }.pack('C*').unpack('Q*')
when 'float32' then Array.new(len * 4) { |i| uint8_view[i] }.pack('C*').unpack('e*')
when 'float64' then Array.new(len * 8) { |i| uint8_view[i] }.pack('C*').unpack('E*')
else raise "unsupported type: #{type}"
end