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

org.alephium.util.Base58.scala Maven / Gradle / Ivy

There is a newer version: 3.8.8
Show newest version
// Copyright 2018 The Alephium Authors
// This file is part of the alephium project.
//
// The library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the library. If not, see .

package org.alephium.util

import scala.annotation.tailrec

import akka.util.ByteString

// scalastyle:off magic.number return
object Base58 {
  val alphabet: String = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
  private val toBase58 = Array(
    0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, 9, 10, 11, 12, 13, 14, 15, 16, -1, 17,
    18, 19, 20, 21, -1, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1, -1, 33, 34,
    35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57
  )

  private def toBase58(c: Char): Int = {
    val x = c.toInt
    if (x < 49) -1 else if (x <= 122) toBase58(x - 49) else -1
  }

  private val zero = BigInt(0)
  private val base = BigInt(58)

  @inline private def count(f: => Int, length: Int): Int =
    f match {
      case -1 => length
      case n  => n
    }

  def encode(bs: ByteString): String = {
    if (bs.isEmpty) {
      ""
    } else {
      val array  = bs.toArray
      val nZeros = count(array.indexWhere(_ != 0), array.length)
      val prefix = Array.fill(nZeros)(alphabet(0))

      val stringBuilder = new StringBuilder()
      @tailrec
      def iter(value: BigInt): Unit = {
        if (value != zero) {
          val (div, rem) = value /% base
          stringBuilder.append(alphabet(rem.intValue))
          iter(div)
        } else {
          stringBuilder.reverseInPlace()
        }
      }
      iter(BigInt(1, array))

      stringBuilder.insertAll(0, prefix).toString
    }
  }

  def decode(input: String): Option[ByteString] = {
    val zeroLength = count(input.indexWhere(_ != '1'), input.length)
    val zeros      = ByteString.fromArrayUnsafe(Array.fill(zeroLength)(0))
    val trim       = input.drop(zeroLength)

    import org.alephium.macros.HPC.cfor
    var decodedBi = zero
    cfor(0)(_ < trim.length, _ + 1) { i =>
      val c = trim.charAt(i)
      val n = toBase58(c)
      if (n == -1) return None else decodedBi = decodedBi * base + n
    }

    if (decodedBi == zero) {
      Some(zeros)
    } else {
      Some(zeros ++ ByteString.fromArrayUnsafe(decodedBi.toByteArray).dropWhile(_ == 0))
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy