All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pico.hashids.Hashids.scala Maven / Gradle / Ivy

The newest version!
package org.pico.hashids

import scala.annotation.tailrec
import org.pico.hashids.impl._

class Hashids(
    salt: String = "",
    minHashLength: Int = 0,
    alphabet: String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
    seps: String,
    guards: String,
    effectiveAlphabet: String) {
  def encode(numbers: Long*): String = {
    if (numbers.isEmpty) {
      ""
    } else {
      org.pico.hashids.impl.encode(numbers:_*)(effectiveAlphabet, minHashLength, salt, seps, guards)
    }
  }

  def encodeHex(in: String): String = {
    require(in.matches("^[0-9a-fA-F]+$"), "Not a HEX string")

    val matcher = "[\\w\\W]{1,12}".r.pattern.matcher(in)

    @tailrec
    def doSplit(result: List[Long]): List[Long] = {
      if (matcher.find())
        doSplit(java.lang.Long.parseLong("1" + matcher.group, 16) :: result)
      else
        result
    }

    org.pico.hashids.impl.encode(doSplit(Nil):_*)(effectiveAlphabet, minHashLength, salt, seps, guards)
  }

  def decode(hash: String): List[Long] = hash match {
    case "" => Nil
    case x =>
      val res = org.pico.hashids.impl.decode(x)(effectiveAlphabet, salt, seps, guards)

      if (res.exists(_ < 0)) {
        List.empty
      } else if (encode(res: _*) == hash) {
        res
      } else {
        Nil
      }
  }

  def decodeHex(hash: String): String = decode(hash).map(_.toHexString.substring(1).toUpperCase).mkString

  def version = "1.0.0"
}

object Hashids {
  @deprecated("Use `Hashids.legacyJiecao` or `Hashids.reference` instead.  See compatibility note in README.md", "1.1.1")
  def apply(
      salt: String = "",
      minHashLength: Int = 0,
      alphabet: String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"): Hashids = ???

  def legacyJiecao(
      salt: String = "",
      minHashLength: Int = 0,
      alphabet: String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") = {
    val (seps, guards, effectiveAlphabet) = {
      val distinctAlphabet = alphabet.distinct

      require(distinctAlphabet.length >= 16, "alphabet must contain at least 16 unique characters")
      require(distinctAlphabet.indexOf(" ") < 0, "alphabet cannot contains spaces")

      val sepDiv = 3.5
      val guardDiv = 12
      val filteredSeps = "cfhistuCFHISTU".filter(x => distinctAlphabet.contains(x))
      val filteredAlphabet = distinctAlphabet.filterNot(x => filteredSeps.contains(x))
      val shuffledSeps = consistentShuffle(filteredSeps, salt)

      val (tmpSeps, tmpAlpha) = {
        if (shuffledSeps.isEmpty || ((filteredAlphabet.length / shuffledSeps.length) > sepDiv)) {
          val sepsTmpLen = Math.ceil(filteredAlphabet.length / sepDiv).toInt
          val sepsLen = if (sepsTmpLen == 1) 2 else sepsTmpLen

          if (sepsLen > shuffledSeps.length) {
            val diff = sepsLen - shuffledSeps.length
            val seps = shuffledSeps + filteredAlphabet.substring(0, diff)
            val alpha = filteredAlphabet.substring(diff)
            (seps, alpha)
          } else {
            val seps = shuffledSeps.substring(0, sepsLen)
            val alpha = filteredAlphabet
            (seps, alpha)
          }
        } else (shuffledSeps, filteredAlphabet)
      }

      val guardCount = Math.ceil(tmpAlpha.length.toDouble / guardDiv).toInt
      val shuffledAlpha = consistentShuffle(tmpAlpha, salt)

      if (shuffledAlpha.length < 3) {
        val guards = tmpSeps.substring(0, guardCount)
        val seps = tmpSeps.substring(guardCount)
        (seps, guards, shuffledAlpha)
      } else {
        val guards = shuffledAlpha.substring(0, guardCount)
        val alpha = shuffledAlpha.substring(guardCount)
        (tmpSeps, guards, alpha)
      }
    }

    new Hashids(salt, minHashLength, alphabet, seps, guards, effectiveAlphabet)
  }

  def reference(
      salt: String = "",
      minHashLength: Int = 0,
      alphabet: String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") = {
    val (seps, guards, effectiveAlphabet) = {
      val distinctAlphabet = alphabet.distinct

      require(distinctAlphabet.length >= 16, "alphabet must contain at least 16 unique characters")
      require(distinctAlphabet.indexOf(" ") < 0, "alphabet cannot contains spaces")

      val sepDiv = 3.5
      val guardDiv = 12
      val filteredSeps = "cfhistuCFHISTU".filter(x => distinctAlphabet.contains(x))
      val filteredAlphabet = distinctAlphabet.filterNot(x => filteredSeps.contains(x))
      val shuffledSeps = consistentShuffle(filteredSeps, salt)

      val (tmpSeps, tmpAlpha) = {
        if (shuffledSeps.isEmpty || (Math.ceil(filteredAlphabet.length.toDouble / shuffledSeps.length) > sepDiv)) {
          val sepsTmpLen = Math.ceil(filteredAlphabet.length / sepDiv).toInt
          val sepsLen = if (sepsTmpLen == 1) 2 else sepsTmpLen

          if (sepsLen > shuffledSeps.length) {
            val diff = sepsLen - shuffledSeps.length
            val seps = shuffledSeps + filteredAlphabet.substring(0, diff)
            val alpha = filteredAlphabet.substring(diff)
            (seps, alpha)
          } else {
            val seps = shuffledSeps.substring(0, sepsLen)
            val alpha = filteredAlphabet
            (seps, alpha)
          }
        } else (shuffledSeps, filteredAlphabet)
      }

      val guardCount = Math.ceil(tmpAlpha.length.toDouble / guardDiv).toInt
      val shuffledAlpha = consistentShuffle(tmpAlpha, salt)

      if (shuffledAlpha.length < 3) {
        val guards = tmpSeps.substring(0, guardCount)
        val seps = tmpSeps.substring(guardCount)
        (seps, guards, shuffledAlpha)
      } else {
        val guards = shuffledAlpha.substring(0, guardCount)
        val alpha = shuffledAlpha.substring(guardCount)
        (tmpSeps, guards, alpha)
      }
    }

    new Hashids(salt, minHashLength, alphabet, seps, guards, effectiveAlphabet)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy