|
1 | | -@file:Suppress("NOTHING_TO_INLINE") |
2 | | - |
3 | 1 | package de.cketti.codepoints |
4 | 2 |
|
5 | | -import de.cketti.codepoints.internal.charCount as commonCharCount |
6 | | -import de.cketti.codepoints.internal.highSurrogate as commonHighSurrogate |
7 | | -import de.cketti.codepoints.internal.isBmpCodePoint as commonIsBmpCodePoint |
8 | | -import de.cketti.codepoints.internal.isSupplementaryCodePoint as commonIsSupplementaryCodePoint |
9 | | -import de.cketti.codepoints.internal.isSurrogatePair as commonIsSurrogatePair |
10 | | -import de.cketti.codepoints.internal.isValidCodePoint as commonIsValidCodePoint |
11 | | -import de.cketti.codepoints.internal.lowSurrogate as commonLowSurrogate |
12 | | -import de.cketti.codepoints.internal.toChars as commonToChars |
13 | | -import de.cketti.codepoints.internal.toCodePoint as commonToCodePoint |
14 | | - |
15 | 3 | actual object CodePoints { |
| 4 | + private const val MIN_SUPPLEMENTARY_CODE_POINT = 0x10000 |
| 5 | + private const val MAX_CODE_POINT = 0x10FFFF |
| 6 | + |
| 7 | + private const val MIN_HIGH_SURROGATE = 0xD800 |
| 8 | + private const val MIN_LOW_SURROGATE = 0xDC00 |
| 9 | + |
| 10 | + private const val SURROGATE_DECODE_OFFSET = |
| 11 | + MIN_SUPPLEMENTARY_CODE_POINT - (MIN_HIGH_SURROGATE shl 10) - MIN_LOW_SURROGATE |
| 12 | + |
| 13 | + private const val HIGH_SURROGATE_ENCODE_OFFSET = |
| 14 | + (MIN_HIGH_SURROGATE - (MIN_SUPPLEMENTARY_CODE_POINT ushr 10)) |
| 15 | + |
16 | 16 | actual fun isValidCodePoint(codePoint: Int): Boolean { |
17 | | - return commonIsValidCodePoint(codePoint) |
| 17 | + return codePoint in 0..MAX_CODE_POINT |
18 | 18 | } |
19 | 19 |
|
20 | 20 | actual fun isBmpCodePoint(codePoint: Int): Boolean { |
21 | | - return commonIsBmpCodePoint(codePoint) |
| 21 | + return codePoint ushr 16 == 0 |
22 | 22 | } |
23 | 23 |
|
24 | 24 | actual fun isSupplementaryCodePoint(codePoint: Int): Boolean { |
25 | | - return commonIsSupplementaryCodePoint(codePoint) |
| 25 | + return codePoint in MIN_SUPPLEMENTARY_CODE_POINT..MAX_CODE_POINT |
26 | 26 | } |
27 | 27 |
|
28 | 28 | actual fun charCount(codePoint: Int): Int { |
29 | | - return commonCharCount(codePoint) |
| 29 | + return if (codePoint < MIN_SUPPLEMENTARY_CODE_POINT) 1 else 2 |
30 | 30 | } |
31 | 31 |
|
32 | 32 | actual fun isSurrogatePair(highSurrogate: Char, lowSurrogate: Char): Boolean { |
33 | | - return commonIsSurrogatePair(highSurrogate, lowSurrogate) |
| 33 | + return highSurrogate.isHighSurrogate() && lowSurrogate.isLowSurrogate() |
34 | 34 | } |
35 | 35 |
|
36 | 36 | actual fun highSurrogate(codePoint: Int): Char { |
37 | | - return commonHighSurrogate(codePoint) |
| 37 | + return ((codePoint ushr 10) + HIGH_SURROGATE_ENCODE_OFFSET).toChar() |
38 | 38 | } |
39 | 39 |
|
40 | 40 | actual fun lowSurrogate(codePoint: Int): Char { |
41 | | - return commonLowSurrogate(codePoint) |
| 41 | + return ((codePoint and 0x3FF) + MIN_LOW_SURROGATE).toChar() |
42 | 42 | } |
43 | 43 |
|
44 | 44 | actual fun toCodePoint(highSurrogate: Char, lowSurrogate: Char): Int { |
45 | | - return commonToCodePoint(highSurrogate, lowSurrogate) |
| 45 | + return (highSurrogate.code shl 10) + lowSurrogate.code + SURROGATE_DECODE_OFFSET |
46 | 46 | } |
47 | 47 |
|
48 | 48 | actual fun toChars(codePoint: Int): CharArray { |
49 | | - return commonToChars(codePoint) |
| 49 | + return if (isBmpCodePoint(codePoint)) { |
| 50 | + charArrayOf(codePoint.toChar()) |
| 51 | + } else { |
| 52 | + charArrayOf(highSurrogate(codePoint), lowSurrogate(codePoint)) |
| 53 | + } |
50 | 54 | } |
51 | 55 |
|
52 | 56 | actual fun toChars(codePoint: Int, destination: CharArray, offset: Int): Int { |
53 | | - return commonToChars(codePoint, destination, offset) |
| 57 | + if (isBmpCodePoint(codePoint)) { |
| 58 | + destination.setSafe(offset, codePoint.toChar()) |
| 59 | + return 1 |
| 60 | + } else { |
| 61 | + // When writing the low surrogate succeeds but writing the high surrogate fails (offset = -1), the |
| 62 | + // destination will be modified even though the method throws. This feels wrong, but matches the behavior |
| 63 | + // of the Java stdlib implementation. |
| 64 | + destination.setSafe(offset + 1, lowSurrogate(codePoint)) |
| 65 | + destination.setSafe(offset, highSurrogate(codePoint)) |
| 66 | + return 2 |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + private fun CharArray.setSafe(index: Int, value: Char) { |
| 71 | + if (index !in this.indices) { |
| 72 | + throw IndexOutOfBoundsException("Size: $size, offset: $index") |
| 73 | + } |
| 74 | + |
| 75 | + this[index] = value |
54 | 76 | } |
55 | 77 | } |
0 commit comments