Support UTF-32 conversion. (#2689)

This commit is contained in:
Nikolay Igotti
2019-02-20 12:39:15 +03:00
committed by GitHub
parent d6f6b626d0
commit e97ff3a1fc
2 changed files with 121 additions and 26 deletions
@@ -392,6 +392,12 @@ private class CString(val bytes: ByteArray): CValues<ByteVar>() {
public val String.cstr: CValues<ByteVar>
get() = CString(encodeToUtf8(this))
/**
* @return the value of zero-terminated UTF-8-encoded C string constructed from given [kotlin.String].
*/
public val String.utf8: CValues<ByteVar>
get() = CString(encodeToUtf8(this))
/**
* Convert this list of Kotlin strings to C array of C strings,
* allocating memory for the array and C strings with given [AutofreeScope].
@@ -407,7 +413,7 @@ public fun Array<String>.toCStringArray(autofreeScope: AutofreeScope): CPointer<
autofreeScope.allocArrayOf(this.map { it.cstr.getPointer(autofreeScope) })
private class WCString(val chars: CharArray): CValues<UShortVar>() {
private class U16CString(val chars: CharArray): CValues<UShortVar>() {
override val size get() = 2 * (chars.size + 1)
override val align get() = 2
@@ -425,16 +431,61 @@ private class WCString(val chars: CharArray): CValues<UShortVar>() {
}
}
/**
* @return the value of zero-terminated UTF-16-encoded C string constructed from given [kotlin.String].
*/
public val String.wcstr: CValues<UShortVar>
get() = WCString(this.toCharArray())
get() = U16CString(this.toCharArray())
/**
* TODO: should the name of the function reflect the encoding?
*
* @return the value of zero-terminated UTF-16-encoded C string constructed from given [kotlin.String].
*/
public val String.utf16: CValues<UShortVar>
get() = U16CString(this.toCharArray())
private class U32CString(val chars: CharArray): CValues<IntVar>() {
override val size get() = 4 * (chars.size + 1)
override val align get() = 4
// Optimization to avoid unneeded virtual calls in base class implementation.
override fun getPointer(scope: AutofreeScope): CPointer<IntVar> {
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
}
override fun place(placement: CPointer<IntVar>): CPointer<IntVar> {
var indexIn = 0
var indexOut = 0
while (indexIn < chars.size) {
var value = chars[indexIn++].toInt()
if (value >= 0xd800 && value < 0xdc00) {
// Surrogate pair.
if (indexIn >= chars.size - 1) throw IllegalArgumentException()
indexIn++
val next = chars[indexIn].toInt()
if (next < 0xdc00 || next >= 0xe000) throw IllegalArgumentException()
value = 0x10000 + ((value and 0x3ff) shl 10) + (next and 0x3ff)
}
nativeMemUtils.putInt((placement + indexOut)!!.pointed, value)
indexOut++
}
nativeMemUtils.putInt((placement + indexOut)!!.pointed, 0)
return placement
}
}
/**
* @return the value of zero-terminated UTF-32-encoded C string constructed from given [kotlin.String].
*/
public val String.utf32: CValues<IntVar>
get() = U32CString(this.toCharArray())
// TODO: optimize
/**
* @return the [kotlin.String] decoded from given zero-terminated UTF-8-encoded C string.
*/
// TODO: optimize
public fun CPointer<ByteVar>.toKString(): String {
public fun CPointer<ByteVar>.toKStringFromUtf8(): String {
val nativeBytes = this
var length = 0
@@ -447,6 +498,62 @@ public fun CPointer<ByteVar>.toKString(): String {
return decodeFromUtf8(bytes)
}
/**
* @return the [kotlin.String] decoded from given zero-terminated UTF-8-encoded C string.
*/
public fun CPointer<ByteVar>.toKString(): String = this.toKStringFromUtf8()
/**
* @return the [kotlin.String] decoded from given zero-terminated UTF-16-encoded C string.
*/
public fun CPointer<ShortVar>.toKStringFromUtf16(): String {
val nativeBytes = this
var length = 0
while (nativeBytes[length] != 0.toShort()) {
++length
}
val chars = CharArray(length)
var index = 0
while (index < length) {
chars[index] = nativeBytes[index].toChar()
++index
}
return String(chars)
}
/**
* @return the [kotlin.String] decoded from given zero-terminated UTF-32-encoded C string.
*/
public fun CPointer<IntVar>.toKStringFromUtf32(): String {
val nativeBytes = this
var fromIndex = 0
var toIndex = 0
while (true) {
val value = nativeBytes[fromIndex++]
toIndex++
if (value == 0) break
if (value >= 0x10000 && value <= 0x10ffff) {
toIndex++
}
}
val length = toIndex
val chars = CharArray(length)
fromIndex = 0
toIndex = 0
while (toIndex < length) {
var value = nativeBytes[fromIndex++]
if (value >= 0x10000 && value <= 0x10ffff) {
chars[toIndex++] = (((value - 0x10000) shr 10) or 0xd800).toChar()
chars[toIndex++] = (((value - 0x10000) and 0x3ff) or 0xdc00).toChar()
} else {
chars[toIndex++] = value.toChar()
}
}
return String(chars)
}
public class MemScope : ArenaBase() {
val memScope: MemScope
@@ -127,38 +127,26 @@ internal object nativeMemUtils {
}
}
fun CPointer<ShortVar>.toKString(): String {
val nativeBytes = this
var length = 0
while (nativeBytes[length] != 0.toShort()) {
++length
}
val bytes = CharArray(length)
var index = 0
while (index < length) {
bytes[index] = nativeBytes[index].toChar()
++index
}
return String(bytes)
}
fun CPointer<UShortVar>.toKString(): String {
public fun CPointer<UShortVar>.toKStringFromUtf16(): String {
val nativeBytes = this
var length = 0
while (nativeBytes[length] != 0.toUShort()) {
++length
}
val bytes = CharArray(length)
val chars = kotlin.CharArray(length)
var index = 0
while (index < length) {
bytes[index] = nativeBytes[index].toShort().toChar()
chars[index] = nativeBytes[index].toShort().toChar()
++index
}
return String(bytes)
return String(chars)
}
public fun CPointer<ShortVar>.toKString(): String = this.toKStringFromUtf16()
public fun CPointer<UShortVar>.toKString(): String = this.toKStringFromUtf16()
@SymbolName("Kotlin_interop_malloc")
private external fun malloc(size: Long, align: Int): NativePtr