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

sbt.io.Hash.scala Maven / Gradle / Ivy

/*
 * sbt IO
 * Copyright Scala Center, Lightbend, and Mark Harrah
 *
 * Licensed under Apache License 2.0
 * SPDX-License-Identifier: Apache-2.0
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package sbt.io

import java.io._
import java.net.{ URI, URL }

object Hash {
  private val BufferSize = 8192

  /** Converts an array of `bytes` to a hexadecimal representation String. */
  def toHex(bytes: Array[Byte]): String = {
    val buffer = new StringBuilder(bytes.length * 2)
    for (i <- bytes.indices) {
      val b = bytes(i)
      val bi: Int = if (b < 0) b + 256 else b.toInt
      buffer append toHex((bi >>> 4).asInstanceOf[Byte])
      buffer append toHex((bi & 0x0f).asInstanceOf[Byte])
    }
    buffer.toString
  }

  /**
   * Converts the provided hexadecimal representation `hex` to an array of bytes.
   * The hexadecimal representation must have an even number of characters in the range 0-9, a-f, or A-F.
   */
  def fromHex(hex: String): Array[Byte] = {
    require((hex.length & 1) == 0, "Hex string must have length 2n.")
    val array = new Array[Byte](hex.length >> 1)
    for (i <- 0 until hex.length by 2) {
      val c1 = hex.charAt(i)
      val c2 = hex.charAt(i + 1)
      array(i >> 1) = ((fromHex(c1) << 4) | fromHex(c2)).asInstanceOf[Byte]
    }
    array
  }

  /** Truncates the last half of `s` if the string has at least four characters.  Otherwise, the original string is returned. */
  def halve(s: String): String = if (s.length > 3) s.substring(0, s.length / 2) else s

  /** Computes the SHA-1 hash of `s` and returns the first `i` characters of the hexadecimal representation of the hash. */
  def trimHashString(s: String, i: Int): String = toHex(apply(s)).take(i)

  /** Computes the SHA-1 hash of `s` and truncates the hexadecimal representation of the hash via [[halve]]. */
  def halfHashString(s: String): String = halve(toHex(apply(s)))

  /** Calculates the SHA-1 hash of the given String. */
  def apply(s: String): Array[Byte] = apply(s.getBytes("UTF-8"))

  /** Calculates the SHA-1 hash of the given Array[Byte]. */
  def apply(as: Array[Byte]): Array[Byte] = apply(new ByteArrayInputStream(as))

  /** Calculates the SHA-1 hash of the given file. */
  def apply(file: File): Array[Byte] =
    try apply(new BufferedInputStream(new FileInputStream(file))) // apply closes the stream
    catch { case _: FileNotFoundException => apply("") }

  /** Calculates the SHA-1 hash of the given resource. */
  def apply(url: URL): Array[Byte] = Using.urlInputStream(url)(apply)

  /**
   * If the URI represents a local file (the scheme is "file"),
   *  this method calculates the SHA-1 hash of the contents of that file.
   * Otherwise, this methods calculates the SHA-1 hash of the normalized string representation of the URI.
   */
  def contentsIfLocal(uri: URI): Array[Byte] =
    if (uri.getScheme == "file") apply(uri.toURL) else apply(uri.normalize.toString)

  /** Calculates the SHA-1 hash of the given stream, closing it when finished. */
  def apply(stream: InputStream): Array[Byte] = {
    import java.security.{ DigestInputStream, MessageDigest }
    val digest = MessageDigest.getInstance("SHA")
    try {
      val dis = new DigestInputStream(stream, digest)
      val buffer = new Array[Byte](BufferSize)
      while (dis.read(buffer) >= 0) {}
      dis.close()
      digest.digest
    } finally {
      stream.close()
    }
  }

  private def toHex(b: Byte): Char = {
    require(b >= 0 && b <= 15, "Byte " + b + " was not between 0 and 15")
    if (b < 10)
      ('0'.asInstanceOf[Int] + b).asInstanceOf[Char]
    else
      ('a'.asInstanceOf[Int] + (b - 10)).asInstanceOf[Char]
  }

  private def fromHex(c: Char): Int = {
    val b =
      if (c >= '0' && c <= '9')
        (c - '0')
      else if (c >= 'a' && c <= 'f')
        (c - 'a') + 10
      else if (c >= 'A' && c <= 'F')
        (c - 'A') + 10
      else
        throw new RuntimeException("Invalid hex character: '" + c + "'.")
    b
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy