forked from openrs2/openrs2
parent
0c2108d750
commit
0666df686c
@ -0,0 +1,89 @@ |
|||||||
|
package org.openrs2.util |
||||||
|
|
||||||
|
public object Base37 { |
||||||
|
private const val MAX_LENGTH: Int = 12 |
||||||
|
|
||||||
|
private val FIRST_VALID_NAME = encode("") |
||||||
|
private val LAST_VALID_NAME = encode("999999999999") |
||||||
|
|
||||||
|
private val DECODE_TABLE = CharArray(37) { i -> |
||||||
|
when (i) { |
||||||
|
0 -> '_' |
||||||
|
in 1..26 -> 'a' + (i - 1) |
||||||
|
else -> '0' + (i - 27) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public fun encode(s: String): Long { |
||||||
|
// casting to CharSequence avoids a copy |
||||||
|
val trimmed = (s as CharSequence).trim { it == ' ' || it == '_' } |
||||||
|
require(trimmed.length <= MAX_LENGTH) |
||||||
|
|
||||||
|
var n = 0L |
||||||
|
|
||||||
|
for (c in trimmed) { |
||||||
|
n *= 37 |
||||||
|
|
||||||
|
when (c) { |
||||||
|
in 'A'..'Z' -> n += 1 + (c - 'A') |
||||||
|
in 'a'..'z' -> n += 1 + (c - 'a') |
||||||
|
in '0'..'9' -> n += 27 + (c - '0') |
||||||
|
' ', '_' -> Unit |
||||||
|
else -> throw IllegalArgumentException() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return n |
||||||
|
} |
||||||
|
|
||||||
|
public fun decodeLowerCase(n: Long): String { |
||||||
|
require(n in FIRST_VALID_NAME..LAST_VALID_NAME) |
||||||
|
require(n == 0L || n % 37 != 0L) |
||||||
|
|
||||||
|
val chars = CharArray(MAX_LENGTH) |
||||||
|
var len = 0 |
||||||
|
var temp = n |
||||||
|
|
||||||
|
while (temp != 0L) { |
||||||
|
chars[len++] = DECODE_TABLE[(temp % 37).toInt()] |
||||||
|
temp /= 37 |
||||||
|
} |
||||||
|
|
||||||
|
chars.reverse(0, len) |
||||||
|
return String(chars, 0, len) |
||||||
|
} |
||||||
|
|
||||||
|
public fun decodeTitleCase(n: Long): String { |
||||||
|
require(n in FIRST_VALID_NAME..LAST_VALID_NAME) |
||||||
|
require(n == 0L || n % 37 != 0L) |
||||||
|
|
||||||
|
val chars = CharArray(MAX_LENGTH) |
||||||
|
var len = 0 |
||||||
|
var temp = n |
||||||
|
|
||||||
|
while (temp != 0L) { |
||||||
|
var c = DECODE_TABLE[(temp % 37).toInt()] |
||||||
|
temp /= 37 |
||||||
|
|
||||||
|
if (c == '_') { |
||||||
|
c = ' ' |
||||||
|
chars[len - 1] = chars[len - 1].uppercaseChar() |
||||||
|
} |
||||||
|
|
||||||
|
chars[len++] = c |
||||||
|
} |
||||||
|
|
||||||
|
chars.reverse(0, len) |
||||||
|
chars[0] = chars[0].uppercaseChar() |
||||||
|
|
||||||
|
return String(chars, 0, len) |
||||||
|
} |
||||||
|
|
||||||
|
public fun toLowerCase(s: String): String { |
||||||
|
return decodeLowerCase(encode(s)) |
||||||
|
} |
||||||
|
|
||||||
|
public fun toTitleCase(s: String): String { |
||||||
|
return decodeTitleCase(encode(s)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,120 @@ |
|||||||
|
package org.openrs2.util |
||||||
|
|
||||||
|
import kotlin.test.Test |
||||||
|
import kotlin.test.assertEquals |
||||||
|
import kotlin.test.assertFailsWith |
||||||
|
|
||||||
|
class Base37Test { |
||||||
|
@Test |
||||||
|
fun testEncodeBounds() { |
||||||
|
assertEquals(0, Base37.encode("")) |
||||||
|
assertEquals(1, Base37.encode("a")) |
||||||
|
assertEquals(1, Base37.encode("A")) |
||||||
|
assertEquals(6582952005840035279, Base37.encode("999999999998")) |
||||||
|
assertEquals(6582952005840035280, Base37.encode("999999999999")) |
||||||
|
|
||||||
|
assertFailsWith<IllegalArgumentException> { |
||||||
|
Base37.encode("aaaaaaaaaaaaa") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testEncodeBoundsWhitespace() { |
||||||
|
assertEquals(6582952005840035280, Base37.encode("999999999999 ")) |
||||||
|
assertEquals(6582952005840035280, Base37.encode(" 999999999999")) |
||||||
|
assertEquals(6582952005840035280, Base37.encode(" 999999999999 ")) |
||||||
|
|
||||||
|
assertFailsWith<IllegalArgumentException> { |
||||||
|
Base37.encode("aaaaaaaaaaaaa ") |
||||||
|
} |
||||||
|
|
||||||
|
assertFailsWith<IllegalArgumentException> { |
||||||
|
Base37.encode(" aaaaaaaaaaaaa") |
||||||
|
} |
||||||
|
|
||||||
|
assertFailsWith<IllegalArgumentException> { |
||||||
|
Base37.encode(" aaaaaaaaaaaaa ") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testDecodeLowerCaseBounds() { |
||||||
|
assertFailsWith<IllegalArgumentException> { |
||||||
|
Base37.decodeLowerCase(-1) |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals("", Base37.decodeLowerCase(0)) |
||||||
|
assertEquals("a", Base37.decodeLowerCase(1)) |
||||||
|
assertEquals("999999999998", Base37.decodeLowerCase(6582952005840035279)) |
||||||
|
assertEquals("999999999999", Base37.decodeLowerCase(6582952005840035280)) |
||||||
|
|
||||||
|
assertFailsWith<IllegalArgumentException> { |
||||||
|
Base37.decodeLowerCase(6582952005840035281) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testDecodeTitleCaseBounds() { |
||||||
|
assertFailsWith<IllegalArgumentException> { |
||||||
|
Base37.decodeTitleCase(-1) |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals("", Base37.decodeTitleCase(0)) |
||||||
|
assertEquals("A", Base37.decodeTitleCase(1)) |
||||||
|
assertEquals("999999999998", Base37.decodeTitleCase(6582952005840035279)) |
||||||
|
assertEquals("999999999999", Base37.decodeTitleCase(6582952005840035280)) |
||||||
|
|
||||||
|
assertFailsWith<IllegalArgumentException> { |
||||||
|
Base37.decodeTitleCase(6582952005840035281) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testEncodeInvalidChar() { |
||||||
|
assertFailsWith<IllegalArgumentException> { |
||||||
|
Base37.encode("!") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testEncodeWhitespace() { |
||||||
|
assertEquals(1465402762952, Base37.encode("Open Rs2")) |
||||||
|
assertEquals(1465402762952, Base37.encode("open_rs2")) |
||||||
|
assertEquals(1465402762952, Base37.encode(" Open Rs2 ")) |
||||||
|
assertEquals(1465402762952, Base37.encode("_Open Rs2_")) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testDecodeLowerCaseWhitespace() { |
||||||
|
assertEquals("open_rs2", Base37.decodeLowerCase(1465402762952)) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testDecodeTitleCaseWhitespace() { |
||||||
|
assertEquals("Open Rs2", Base37.decodeTitleCase(1465402762952)) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testDecodeLowerCaseTrailingWhitespace() { |
||||||
|
assertFailsWith<IllegalArgumentException> { |
||||||
|
Base37.decodeLowerCase(54219902229224) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testDecodeTitleCaseTrailingWhitespace() { |
||||||
|
assertFailsWith<IllegalArgumentException> { |
||||||
|
Base37.decodeTitleCase(54219902229224) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testToLowerCase() { |
||||||
|
assertEquals("open_rs2", Base37.toLowerCase(" OpEn rS2_")) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testToTitleCase() { |
||||||
|
assertEquals("Open Rs2", Base37.toTitleCase(" OpEn rS2_")) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue