Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CodePoints.toChars functions #12

Merged
merged 1 commit into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/commonImplementation/kotlin/CodePoints.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
25 changes: 25 additions & 0 deletions src/commonImplementation/kotlin/CommonCodePoints.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
16 changes: 16 additions & 0 deletions src/commonMain/kotlin/CodePoints.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
73 changes: 73 additions & 0 deletions src/commonTest/kotlin/CodePointsTest.kt
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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<IndexOutOfBoundsException> {
CodePoints.toChars('a'.code, chars, 0)
}
assertFailsWith<IndexOutOfBoundsException> {
CodePoints.toChars("\uD83E\uDD95".codePointAt(0), chars, 0)
}
}

@Test
fun toCharsDestinationOffsetInvalid() {
val chars = charArrayOf('z', 'z')

assertFailsWith<IndexOutOfBoundsException> {
CodePoints.toChars('a'.code, chars, 2)
}
assertContentEquals(charArrayOf('z', 'z'), chars)

assertFailsWith<IndexOutOfBoundsException> {
CodePoints.toChars('a'.code, chars, -1)
}
assertContentEquals(charArrayOf('z', 'z'), chars)

assertFailsWith<IndexOutOfBoundsException> {
CodePoints.toChars("\uD83E\uDD95".codePointAt(0), chars, 2)
}
assertContentEquals(charArrayOf('z', 'z'), chars)

assertFailsWith<IndexOutOfBoundsException> {
CodePoints.toChars("\uD83E\uDD95".codePointAt(0), chars, -1)
}
assertContentEquals(charArrayOf('z', 'z'), chars)
}

@Test
fun toCharsDestinationOffsetTooSmall() {
val chars = charArrayOf('z', 'z')

assertFailsWith<IndexOutOfBoundsException> {
CodePoints.toChars("\uD83E\uDD95".codePointAt(0), chars, 1)
}
assertContentEquals(charArrayOf('z', 'z'), chars)
}
}
8 changes: 8 additions & 0 deletions src/jvmMain/kotlin/CodePoints.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}