swiftlang / swift-java

Apache License 2.0
729 stars 29 forks source link

JavaKit: inout primitive arrays #156

Open lhoward opened 2 weeks ago

lhoward commented 2 weeks ago

Presume this is on the TODO list :)

I'd like to use

@JavaClass("java.nio.ByteBuffer")
open class JavaNIOByteBuffer: JavaNIOBuffer {
...
  @JavaMethod
  open func get(_ arg0: [Int8], _ arg1: Int32, _ arg2: Int32) -> JavaNIOByteBuffer!
...
}

but the projection of byte[] is not inout.

lhoward commented 2 weeks ago

Feel free to close if known issue.

lhoward commented 2 weeks ago

I'd also be happy with a special case for byte buffers that lives within JavaKit (there is precedence, JNI has one).

ktoso commented 2 weeks ago

Issues are good, please keep em coming but I'd like to ask if you could qualify that they're all about "JavaKit" or "Java2Swift" because the same kinds of things need to be done in jextract -- it'll be easier to know which side of conversions the issue is about this way.

Anyway, lemme add tags :)

lhoward commented 2 weeks ago

Note to self: may be possible to use jniGetArrayRegion(). Essentially I'm just trying to bridge java.nio.ByteBuffer to/from Data.

lhoward commented 2 weeks ago

Ah, it's tricky to implement this in Swift without reimplementing a lot of JavaKit. The options are really:

To unblock progress (well, I was unblocked, but reading a byte at a time!), I've opted for the third option for now.

import FoundationEssentials
import JavaKit
import JavaRuntime

extension Data {
  func asJavaNIOByteBuffer() -> JavaNIOByteBuffer {
    let byteBufferClass = try! JavaClass<JavaNIOByteBuffer>()
    let byteBuffer = byteBufferClass.allocateDirect(Int32(count))!

    return byteBuffer.put(map { Int8(bitPattern: $0) }, 0, Int32(count))
  }
}

extension JavaNIOByteBuffer {
  func asData() throws -> Data {
    let array: [Int8]

    if hasArray() {
      let position = Int(position()), limit = Int(limit())
      let offset = Int(arrayOffset()) + position

      array = Array(self.array()[offset..<(offset + limit)])
    } else {
      array = try! JavaClass<ByteBufferHelper>(environment: javaEnvironment)
        .getByteBufferContents(self)
    }

    return Data(array.map { UInt8(bitPattern: $0) })
  }
}