Didier Villevalois
09/11/2023, 10:47 AMkotlinx-io reincarnation.
I would like to write a kotlinx-io adapter for `flatbuffers-kotlin`' ReadWriteBuffer. To do that I would need random-access read and write to buffers. Is this something that is on the roadmap?
I stumbled upon this issue but it was created for the previous incarnation of kotlinx-io.
To clarify my use-case:
• flatbuffers-kotlin 's ReadBuffer interface has a limit property indicating the read limit and only offers random-access reads (up to limit).
• flatbuffers-kotlin 's ReadWriteBuffer interface extends ReadBuffer, has a writePosition (equals limit) property, a writeLimit property and a capacity property, and offers both sequential writes (updating writePosition adequately) and random-access writes (up to capacity ).
• All read and write operations are in Little Endian byte order.Filipp Zhinkin
09/11/2023, 11:27 AMkotlinx.io.Buffer provides API for random-access reads. There's a plan to augment that API with something similar to Okio's UnsafeCursor (but a bit safer) as a more performance alternative to Buffer::get.
There were no plans to provide random-access writes, but it could be reconsidered if there's a demand for it.
Could you please elaborate a bit on what kotlinx-io adapters for FlatBuffers would look like? I see a few other potential issues there besides missing random-access writes.Didier Villevalois
09/11/2023, 11:45 AMBuffer::get as I was looking for getXXX versions for all XXX primitive types (or rather the Le-suffixed versions). So I will have to implement those first. Do you think I could contribute those as extension functions directly in kotlinx-io? (If I understand correctly, they would have to be rewritten to use unsafe cursor later to only seek once...)
As for the what the adapters would look like, I was naively thinking of something like
fun Source.asFlatbuffersReadBuffer(): ReadBuffer
fun Sink.asFlatbuffersReadWriteBuffer(): ReadWriteBuffer
where ReadBuffer and ReadWriteBuffer are the two interfaces defined at the top of https://github.com/google/flatbuffers/blob/master/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/Buffers.kt.
Do you think I am on the right track? What potential issues do you see?
Thanks for your help.Filipp Zhinkin
09/11/2023, 1:08 PMDo you think I am on the right track?
Sink and Source are more like Input- and OutputStream in Java, so Buffer would be a better counterpart for Read- and ReadWriteBuffer. Although, the extensions you've mentioned could simply wrap Sink's and Source's internal buffer.
What potential issues do you see?One of the main issues with
Buffer::get is its poor performance: every get call requires segment lookup. And it's hard to get rid of that lookup as indices are relative to the beginning of a buffer and it advances on every read.
The latter may also be an issue for a Sink/Source wrapped into a ReadBuffer/ReadWriteBuffer: I don't think that someone will actually use a Source and ReadBuffer-view simultaneously, but every read from the Source will invalidate corresponding ReadBuffer (and the same is true for Sinks and ReadWriteBuffers).
For Sinks it could also be problematic to implement requestCapacity as the only way to extend Sink's buffer is to write data into it.
And yet another possible issue from the top of my head - a Source could be infinite, or more realistically, it may not have a predefined size (for example, when a source reads data from a network). It's unclear how ReadBuffer::limit should work in that case.
I don't have a lot of experience with flatbuffers and don't see the full picture, so what I'd propose may sound silly, but instead of representing Sink/Source/Buffer as Read/ReadWriteBuffers and dealing with their semantic differences, it may be better to support reading ReadBuffer from Source and writing it and ReadWriteBuffer to Sink. The main issue here would be array copying, but it is something that could be addressed (relatively easy) in the near future.
Indeed, I missedOne of the reasons there are no such functions is the poor performance of random-access reads fromas I was looking forBuffer::getversions for allgetXXXprimitive types (or rather theXXX-suffixed versions). So I will have to implement those first. Do you think I could contribute those as extension functions directly inLe? (If I understand correctly, they would have to be rewritten to use unsafe cursor later to onlykotlinx-ioonce...)seek
Buffer, which makes Buffer::get almost impractical.altavir
09/11/2023, 6:20 PMDidier Villevalois
09/22/2023, 8:42 AMOne of the reasons there are no such functions is the poor performance of random-access reads fromI understand and looking at the implementation details, this makes total sense., which makesBufferalmost impractical.Buffer::get
[...] it may be better to support readingI see. So let me focus on readingfromReadBufferand writing it andSourcetoReadWriteBuffer.Sink
ReadBuffer from Source. I tried to experiment adding read-only views with random-access read from Source. Would this make sense to add the following to kotlinx-io?
• Defining a BufferView (bad tentative name) with random-access.
• In order, to support views larger than a buffer's segment size, we would make a segment index array and use a binary search in that index. This would give something like O(log n) index indirection (where n is the number of segments).
• "Reading" a BufferView from a Source, by creating a private buffer and wrapping it in a BufferView.
I toyed with this idea in the following branch: https://github.com/ptitjes/kotlinx-io/tree/read-random-access. Do you think it makes sense? Could a similar thing be added to kotlinx-io?
(Btw, this could use the API you experimented in the segments-iter branch.)Filipp Zhinkin
09/22/2023, 3:42 PM(Btw, this could use the API you experimented in theIt's for https://github.com/Kotlin/kotlinx-io/issues/135 which aimed to provide a public API not only for iterating over segments, but also for accessing individual bytes within the segment.branch.)segments-iter
I toyed with this idea in the following branch: https://github.com/ptitjes/kotlinx-io/tree/read-random-access. Do you think it makes sense? Could a similar thing be added toDo you mind opening an issue on github to loop in other potentially interested folks into a discussion? I think such an API might be useful, but it might make sense to wait until Buffers/Segments API update I mentioned above is complete.?kotlinx-io
Didier Villevalois
09/23/2023, 9:31 AMWhat concerns me a bit is that there will be no way to invalidate a view once it was created, a user read data from the buffer (for some reason) and attempted to use the same view again. Unlikely someone will intentionally write a code like that, but for a library, we have to anticipate such cases.I am not sure that I follow your reasoning, as the buffer instance is fully private. But it is true that I did not include a way to release the segments as I was focusing on the random-access. I expanded upon this on my branch: • made
BufferView implement AutoCloseableAlias, by clearing the buffer,
• added a BufferView.slice(startIndex: Long, endIndex: Long): BufferView that uses Buffer.copyTo(...) (and hence sharedCopies the segments),
• added a Buffer.copyToView(startIndex: Long, endIndex: Long): BufferView convenience method that also uses Buffer.copyTo(...)
• factored out byte reinterpretation operations (to avoid duplicated logic between Buffer and BufferView)
• added `TODO`s referencing https://github.com/Kotlin/kotlinx-io/issues/135, where appropriate
Do you mind opening an issue on github to loop in other potentially interested folks into a discussion?I created this issue: https://github.com/Kotlin/kotlinx-io/issues/225
I think such an API might be useful, but it might make sense to wait until Buffers/Segments API update I mentioned above is complete.Indeed. Would you need some help on #135? I would be glad to contribute to this too. (But I don't want to put any more pressure on you! :P)
Filipp Zhinkin
09/25/2023, 3:23 PMI created this issue: https://github.com/Kotlin/kotlinx-io/issues/225Thanks!
Indeed. Would you need some help on #135? I would be glad to contribute to this too. (But I don't want to put any more pressure on you! :P)The task is currently in a design phase, I'll publish an update as soon as it is more or less ready for public review along with a prototype implementation. Will appreciate any suggestions as well as reviews a lot then!