-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support ByteString to/from NSData conversions (#384)
* Support ByteString to/from NSData conversions Closes #266
- Loading branch information
Showing
4 changed files
with
156 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. | ||
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. | ||
*/ | ||
|
||
package kotlinx.io.bytestring | ||
|
||
import kotlinx.cinterop.* | ||
import kotlinx.io.bytestring.unsafe.UnsafeByteStringApi | ||
import kotlinx.io.bytestring.unsafe.UnsafeByteStringOperations | ||
import platform.Foundation.NSData | ||
import platform.Foundation.create | ||
|
||
/** | ||
* Returns a new [NSData] instance initialized with bytes copied from [this] ByteString. | ||
* | ||
* @sample kotlinx.io.bytestring.samples.ByteStringSamplesApple.nsDataConversion | ||
*/ | ||
@OptIn(UnsafeNumber::class, BetaInteropApi::class, ExperimentalForeignApi::class) | ||
public fun ByteString.toNSData(): NSData { | ||
if (isEmpty()) { | ||
return NSData() | ||
} | ||
val data = getBackingArrayReference() | ||
return data.usePinned { | ||
NSData.create(bytes = it.addressOf(0), length = data.size.convert()) | ||
} | ||
} | ||
|
||
/** | ||
* Returns a new [ByteString] holding data copied from [this] NSData. | ||
* | ||
* @sample kotlinx.io.bytestring.samples.ByteStringSamplesApple.nsDataConversion | ||
*/ | ||
@OptIn(ExperimentalForeignApi::class, UnsafeNumber::class, UnsafeByteStringApi::class) | ||
public fun NSData.toByteString(): ByteString { | ||
val l = length.toLong() | ||
if (l == 0L) { | ||
return ByteString.EMPTY | ||
} | ||
if (l > Int.MAX_VALUE) { | ||
throw IllegalArgumentException("NSData content is to long to read as byte array: $l") | ||
} | ||
return UnsafeByteStringOperations.wrapUnsafe( | ||
bytes!!.readBytes(l.toInt()) | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* | ||
* Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. | ||
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. | ||
*/ | ||
|
||
package kotlinx.io.bytestring | ||
|
||
import kotlinx.cinterop.* | ||
import kotlinx.io.bytestring.unsafe.UnsafeByteStringApi | ||
import kotlinx.io.bytestring.unsafe.UnsafeByteStringOperations | ||
import platform.Foundation.NSData | ||
import platform.Foundation.create | ||
import platform.posix.memset | ||
import kotlin.io.encoding.Base64 | ||
import kotlin.io.encoding.ExperimentalEncodingApi | ||
import kotlin.test.* | ||
|
||
@OptIn(UnsafeNumber::class) | ||
class ByteStringAppleTest { | ||
@OptIn(ExperimentalForeignApi::class) | ||
@Test | ||
fun toNSData() { | ||
val emptyData = ByteString().toNSData() | ||
assertEquals(0u, emptyData.length) | ||
|
||
val copy = ByteString(0, 1, 2, 3, 4, 5).toNSData() | ||
assertContentEquals(byteArrayOf(0, 1, 2, 3, 4, 5), copy.bytes!!.readBytes(copy.length.convert())) | ||
} | ||
|
||
@OptIn(BetaInteropApi::class, ExperimentalEncodingApi::class) | ||
@Test | ||
fun fromNSData() { | ||
assertTrue(NSData().toByteString().isEmpty()) | ||
val src = NSData.create( | ||
base64EncodedString = Base64.Default.encode(byteArrayOf(0, 1, 2, 3, 4, 5)), | ||
options = 0u | ||
)!! | ||
val copy = src.toByteString() | ||
assertContentEquals(byteArrayOf(0, 1, 2, 3, 4, 5), copy.toByteArray()) | ||
} | ||
|
||
@OptIn(UnsafeByteStringApi::class, ExperimentalForeignApi::class) | ||
@Test | ||
fun toNSDataDataIntegrity() { | ||
val mutableArray = byteArrayOf(0, 0, 0, 0, 0, 0) | ||
// Don't try that at home, kids! | ||
val cursedString = UnsafeByteStringOperations.wrapUnsafe(mutableArray) | ||
val nsData = cursedString.toNSData() | ||
|
||
mutableArray.fill(42) | ||
// NSData should hold a copy | ||
assertContentEquals(ByteArray(6), nsData.bytes!!.readBytes(6)) | ||
} | ||
|
||
@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class) | ||
@Test | ||
fun fromNSDataIntegrity() = memScoped { | ||
val length = 6 | ||
val data = allocArray<ByteVar>(length) | ||
memset(data, 0, length.convert()) | ||
|
||
val cursedData = NSData.create( | ||
bytesNoCopy = data, length = length.convert(), | ||
freeWhenDone = false | ||
) | ||
|
||
val byteString = cursedData.toByteString() | ||
memset(data, 42, length.convert()) | ||
|
||
assertContentEquals(ByteArray(length), byteString.toByteArray()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* | ||
* Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. | ||
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. | ||
*/ | ||
|
||
package kotlinx.io.bytestring.samples | ||
|
||
import kotlinx.cinterop.ExperimentalForeignApi | ||
import kotlinx.cinterop.UnsafeNumber | ||
import kotlinx.io.bytestring.* | ||
import platform.Foundation.* | ||
import kotlin.test.* | ||
|
||
class ByteStringSamplesApple { | ||
@OptIn(UnsafeNumber::class, ExperimentalForeignApi::class, ExperimentalStdlibApi::class) | ||
@Test | ||
fun nsDataConversion() { | ||
val originalByteString: ByteString = "Compress me, please!".encodeToByteString() | ||
|
||
val compressedNSData: NSData = originalByteString.toNSData().compressedDataUsingAlgorithm( | ||
algorithm = NSDataCompressionAlgorithmZlib, | ||
error = null | ||
)!! | ||
|
||
val compressedByteString: ByteString = compressedNSData.toByteString() | ||
assertEquals("73cecf2d284a2d2e56c84dd55128c8494d2c4e550400", compressedByteString.toHexString()) | ||
// If there's no zlib-flate on your path, you can test it using: | ||
// zlib.decompress(binascii.unhexlify("73cecf2d284a2d2e56c84dd55128c8494d2c4e550400"), -15) | ||
} | ||
} |