Skip to content

Commit

Permalink
Make alphabet a char vector internally
Browse files Browse the repository at this point in the history
  • Loading branch information
jesperoman committed Sep 16, 2023
1 parent e709be8 commit ccc0a7a
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/main/scala/sqid/Sqid.scala
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ object Sqid {
if (value.length < minLength) {
val shuffled = alphabet.shuffle
copy(
value = value + shuffled.value.take(math.min(minLength - length, alphabet.length)),
value = shuffled.fillToMinLength(value, minLength),
alphabet = shuffled
).fillToMinLength(minLength)
} else this
Expand Down
31 changes: 19 additions & 12 deletions src/main/scala/sqid/options/Alphabet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ package sqids.options
import scala.annotation.tailrec
import sqids.SqidsError

sealed abstract case class Alphabet(value: String) {
sealed abstract case class Alphabet(value: Vector[Char]) {
def length = value.length
def indexOf(c: Char) = value.indexOf(c.toInt)
def prefix = value.head
def partition = value(1)
def removeSeparator: Alphabet = new Alphabet(value.tail) {}
def separator: Char = value.head
def removeSeparator: Alphabet = new Alphabet(value.tail) {}
def splitAtSeparator(id: String): Either[String, (String, String)] =
(id.takeWhile(_ != separator), id.dropWhile(_ != separator).tail) match {
case (first, _) if first.exists(!removeSeparator.value.contains(_)) =>
Expand All @@ -35,17 +34,25 @@ sealed abstract case class Alphabet(value: String) {

go(num / length, List(value((num % length).toInt)))
}
def fillToMinLength(id: String, minLength: Int): String =
id + value.take(math.min(minLength - id.length, length)).mkString

def toNumber(id: String): Long =
id.foldLeft(0L)((acc, c) => acc * length + indexOf(c).toLong)

def shuffle: Alphabet =
new Alphabet(value.indices.take((length - 1).toInt).foldLeft(value) { (str, i) =>
val j: Int = length - 1 - i
val r: Int = (i * j + str(i) + str(j.toInt)) % length
val iChar = str(i)
str.updated(i, str(r)).updated(r, iChar)
}) {}
new Alphabet(
value.indices
.take((length - 1).toInt)
.foldLeft(value) { (vec, i) =>
val j: Int = length - 1 - i
val r: Int = (i * j + vec(i) + vec(j.toInt)) % length
val iChar = vec(i)
vec
.updated(i, vec(r))
.updated(r, iChar)
}
) {}

def offsetFromPrefix(prefix: Char) = value.indexOf(prefix.toInt)

Expand All @@ -55,7 +62,7 @@ sealed abstract case class Alphabet(value: String) {
} % length) + increment

def rearrange(offset: Int): Alphabet =
new Alphabet(value.drop(offset) + value.take(offset)) {}
new Alphabet(value.drop(offset) ++ value.take(offset)) {}

def rearrange(numbers: List[Long], increment: Int): Alphabet =
rearrange(getOffset(numbers, increment))
Expand All @@ -73,9 +80,9 @@ object Alphabet {
case v if v.getBytes.length != v.length =>
Left(SqidsError.AlphabetMultibyteChars)
case v =>
Right(new Alphabet(v) {})
Right(new Alphabet(v.toVector) {})
}

def default: Alphabet =
new Alphabet((('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9')).mkString) {}
new Alphabet((('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9')).toVector) {}
}

0 comments on commit ccc0a7a

Please sign in to comment.