From f7802403826b8d749616b20b7bd2c82e9361e0f0 Mon Sep 17 00:00:00 2001 From: Jake Wharton Date: Mon, 23 Jan 2023 13:27:21 -0500 Subject: [PATCH] Add CodePoints.toChars functions --- src/commonImplementation/kotlin/CodePoints.kt | 8 ++ .../kotlin/CommonCodePoints.kt | 25 +++++++ src/commonMain/kotlin/CodePoints.kt | 16 ++++ src/commonTest/kotlin/CodePointsTest.kt | 73 +++++++++++++++++++ src/jvmMain/kotlin/CodePoints.kt | 8 ++ 5 files changed, 130 insertions(+) diff --git a/src/commonImplementation/kotlin/CodePoints.kt b/src/commonImplementation/kotlin/CodePoints.kt index dfff6a2..1fd8b5a 100644 --- a/src/commonImplementation/kotlin/CodePoints.kt +++ b/src/commonImplementation/kotlin/CodePoints.kt @@ -46,4 +46,12 @@ actual object CodePoints { actual inline fun toCodePoint(highSurrogate: Char, lowSurrogate: Char): Int { return CommonCodePoints.toCodePoint(highSurrogate, lowSurrogate) } + + actual inline fun toChars(codePoint: Int): CharArray { + return CommonCodePoints.toChars(codePoint) + } + + actual inline fun toChars(codePoint: Int, destination: CharArray, offset: Int): Int { + return CommonCodePoints.toChars(codePoint, destination, offset) + } } diff --git a/src/commonImplementation/kotlin/CommonCodePoints.kt b/src/commonImplementation/kotlin/CommonCodePoints.kt index 2546bd1..c491a5e 100644 --- a/src/commonImplementation/kotlin/CommonCodePoints.kt +++ b/src/commonImplementation/kotlin/CommonCodePoints.kt @@ -58,4 +58,29 @@ object CommonCodePoints { fun toCodePoint(highSurrogate: Char, lowSurrogate: Char): Int { return (highSurrogate.code shl 10) + lowSurrogate.code + SURROGATE_DECODE_OFFSET } + + fun toChars(codePoint: Int): CharArray { + return if (isBmpCodePoint(codePoint)) { + charArrayOf(codePoint.toChar()) + } else { + charArrayOf(highSurrogate(codePoint), lowSurrogate(codePoint)) + } + } + + fun toChars(codePoint: Int, destination: CharArray, offset: Int): Int { + val size = destination.size + if (offset >= 0) { + if (isBmpCodePoint(codePoint)) { + if (offset < size) { + destination[offset] = codePoint.toChar() + return 1 + } + } else if (offset < size - 1) { + destination[offset] = highSurrogate(codePoint) + destination[offset + 1] = lowSurrogate(codePoint) + return 2 + } + } + throw IndexOutOfBoundsException("Size: $size, offset: $offset") + } } diff --git a/src/commonMain/kotlin/CodePoints.kt b/src/commonMain/kotlin/CodePoints.kt index 9a6142a..cd59628 100644 --- a/src/commonMain/kotlin/CodePoints.kt +++ b/src/commonMain/kotlin/CodePoints.kt @@ -101,4 +101,20 @@ expect object CodePoints { * if necessary. */ fun toCodePoint(highSurrogate: Char, lowSurrogate: Char): Int + + /** + * Converts the specified character (Unicode code point) to its UTF-16 representation stored in a char array. + * If the specified code point is a BMP (Basic Multilingual Plane or Plane 0) value, the resulting char array has + * the same value as [codePoint]. If the specified code point is a supplementary code point, the resulting char + * array has the corresponding surrogate pair. + */ + fun toChars(codePoint: Int): CharArray + + /** + * Converts the specified character (Unicode code point) to its UTF-16 representation. If the specified code point + * is a BMP (Basic Multilingual Plane or Plane 0) value, the same value is stored in `destination[offset]`, + * and 1 is returned. If the specified code point is a supplementary character, its surrogate values are stored in + * `destination[offset]` (high-surrogate) and `destination[offset+1]` (low-surrogate), and 2 is returned. + */ + fun toChars(codePoint: Int, destination: CharArray, offset: Int): Int } diff --git a/src/commonTest/kotlin/CodePointsTest.kt b/src/commonTest/kotlin/CodePointsTest.kt index 0a4836e..6e208c1 100644 --- a/src/commonTest/kotlin/CodePointsTest.kt +++ b/src/commonTest/kotlin/CodePointsTest.kt @@ -1,7 +1,9 @@ package de.cketti.codepoints import kotlin.test.Test +import kotlin.test.assertContentEquals import kotlin.test.assertEquals +import kotlin.test.assertFailsWith import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -120,4 +122,75 @@ class CodePointsTest { fun toCodePoint() { assertEquals(0x1F995, CodePoints.toCodePoint('\uD83E', '\uDD95')) } + + @Test + fun toChars() { + assertContentEquals(charArrayOf('a'), CodePoints.toChars('a'.code)) + assertContentEquals(charArrayOf('\uFFFF'), CodePoints.toChars(0xFFFF)) + assertContentEquals(charArrayOf('\uD83E', '\uDD95'), CodePoints.toChars("\uD83E\uDD95".codePointAt(0))) + } + + @Test + fun toCharsDestination() { + val chars = charArrayOf('z', 'z', 'z') + + CodePoints.toChars('a'.code, chars, 0) + assertContentEquals(charArrayOf('a', 'z', 'z'), chars) + + CodePoints.toChars('a'.code, chars, 2) + assertContentEquals(charArrayOf('a', 'z', 'a'), chars) + + CodePoints.toChars("\uD83E\uDD95".codePointAt(0), chars, 0) + assertContentEquals(charArrayOf('\uD83E', '\uDD95', 'a'), chars) + + CodePoints.toChars("\uD83E\uDD95".codePointAt(0), chars, 1) + assertContentEquals(charArrayOf('\uD83E', '\uD83E', '\uDD95'), chars) + } + + @Test + fun toCharsDestinationTooSmall() { + val chars = charArrayOf() + + assertFailsWith { + CodePoints.toChars('a'.code, chars, 0) + } + assertFailsWith { + CodePoints.toChars("\uD83E\uDD95".codePointAt(0), chars, 0) + } + } + + @Test + fun toCharsDestinationOffsetInvalid() { + val chars = charArrayOf('z', 'z') + + assertFailsWith { + CodePoints.toChars('a'.code, chars, 2) + } + assertContentEquals(charArrayOf('z', 'z'), chars) + + assertFailsWith { + CodePoints.toChars('a'.code, chars, -1) + } + assertContentEquals(charArrayOf('z', 'z'), chars) + + assertFailsWith { + CodePoints.toChars("\uD83E\uDD95".codePointAt(0), chars, 2) + } + assertContentEquals(charArrayOf('z', 'z'), chars) + + assertFailsWith { + CodePoints.toChars("\uD83E\uDD95".codePointAt(0), chars, -1) + } + assertContentEquals(charArrayOf('z', 'z'), chars) + } + + @Test + fun toCharsDestinationOffsetTooSmall() { + val chars = charArrayOf('z', 'z') + + assertFailsWith { + CodePoints.toChars("\uD83E\uDD95".codePointAt(0), chars, 1) + } + assertContentEquals(charArrayOf('z', 'z'), chars) + } } diff --git a/src/jvmMain/kotlin/CodePoints.kt b/src/jvmMain/kotlin/CodePoints.kt index 3519ca3..2bdf5dd 100644 --- a/src/jvmMain/kotlin/CodePoints.kt +++ b/src/jvmMain/kotlin/CodePoints.kt @@ -46,4 +46,12 @@ actual object CodePoints { actual inline fun toCodePoint(highSurrogate: Char, lowSurrogate: Char): Int { return Character.toCodePoint(highSurrogate, lowSurrogate) } + + actual inline fun toChars(codePoint: Int): CharArray { + return Character.toChars(codePoint) + } + + actual inline fun toChars(codePoint: Int, destination: CharArray, offset: Int): Int { + return Character.toChars(codePoint, destination, offset) + } }