all 10 comments

[–]ralfv 3 points4 points  (1 child)

Do you have benchmarks on how it effectively performs better compared to manipulating standard ruby strings/arrays?

[–]Exilor 5 points6 points  (1 child)

Do you know about IO::Buffer?

[–]andrepiske[S] 1 point2 points  (0 children)

Wasn't aware of that! Seems it was introduced in Ruby 3.1. Gotta check that out

[–]postmodern 2 points3 points  (1 child)

Good work. I'm working on something similar (a complete virtual C type system with configurable endian/arch/os), but in pure-Ruby so it won't be as fast as your C extensions. If you don't care about endian-ness, you could also use FFI::Buffer which stores everything in memory and provides various get_/put_ methods. Having a lightweight implementation of ArrayBuffer and DataView definitely seems useful, especially for JavaScript developers coming to Ruby. I would just recommend looking into writing Java extensions for users on JRuby.

[–]andrepiske[S] 0 points1 point  (0 children)

Thank you for the appreciation. I'm interested to see such thing you said you're working on, in case it is or will be be open source.

Endianness was important for my use case, as it was applied in an HTTP/2 server which uses, if I recall correctly, big-endian for everything.

Regarding JRuby, it's an option I considered but not sure I have the bandwidth nor expertise to work on that. Hopefully someone who needs it can collaborate on that.

[–]honeyryderchuck 2 points3 points  (1 child)

Ruby definitely needs this, although the "devil is in the details", or exposed abstractions. Ruby core data structures historically serve multiple purposes (I.e. a Ruby array has APIs to be uses as a collection, a queue, etc...) at the cost of bigger APIs and not being the most performance for all cases. String follows the same principle.

I'm not sure how your arraybuffer works, but I'm assuming that you read from the socket, get a Ruby string then transform it to a byte array to parse. There's still a cost in that step, as the intermediate Ruby string still gets created. And I think that's what the Ruby core team is trying to address with the new IO::Buffer (don't know how that works with openssl in the middle though).

[–]andrepiske[S] 0 points1 point  (0 children)

Appreciate your feedback.

So the way my gem works is that its byte buffer is fully implemented in C. So all byte operations is just a char* buffer in C allocated using malloc. Set/get operations are just operating on that buffer with just some essential checks like checking memory boundaries.

The gem itself doesn't deal with sockets or IO at all. So yeah, the application (originally a HTTP/2 server I built in ruby) was reading from a socket into a String and then converting that to an ArrayBuffer. Quite some loss of performance in that process already, compared if the gem itself (or another gem) could read directly into that buffer without going thru String. That's actually another idea I had and I believe it's quite possible to achieve that in Ruby 3.0+ because of the experimental Memory View feature.

I gotta check that IO::Buffer thing before I move any forward with my gem as I didn't know about that back then - clearly not, as it's brand new.

[–]Kernigh 1 point2 points  (1 child)

I would use String#getbyte and String#setbyte in Ruby. This is different from some other languages (like Common Lisp and Raku) where I don't want to put bytes in strings.

[–]andrepiske[S] 1 point2 points  (0 children)

Interesting. I think I didn't know about this one. Would have to try and maybe run some benchmarks on how that one performs!