summaryrefslogtreecommitdiff
path: root/src/library/scala/util/MurmurHash.scala
blob: 8ed91796a11c345fd28d3ee2629c66d7263ec4be (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2003-2011, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */

package scala.util

/** An implementation of MurmurHash 2.0.
 *  http://sites.google.com/site/murmurhash/
 *
 *  It is here primarily for use by case classes.
 *
 *  @author  archontophoenix
 *  @version 2.9
 *  @since   2.9
 */

object MurmurHash {
  private def bytesProvided (v: Any) = v match {
    case x: Byte      => 1
    case x: Short     => 2
    case x: Int       => 4
    case x: Long      => 8
    case x: Float     => 4
    case x: Double    => 8
    case x: Boolean   => 1
    case x: Char      => 2
    case x: Unit      => 0
    case _            => 4
  }

  private def highInt (v: Any): Int = {
    val l = v match {
      case x: Long   => x
      case x: Double => java.lang.Double.doubleToRawLongBits(x)
      case _         => throw new IllegalArgumentException("No highInt: " + v)
    }

    l >>> 32 toInt
  }

  final val m = 0x5bd1e995
  final val r = 24

  private def step (hh: Int, kk: Int): Int = {
    var h = hh
    var k = kk
    k *= m
    k ^= k >>> r
    k *= m
    h *= m
    h ^ k
  }

  def product(p: Product): Int = product(p, 0)
  def product(p: Product, seed: Int): Int = {
    // Initialize the hash to a 'random' value
    var n = p.productArity
    var h = seed ^ n
    var partial = 0
    var nextPartialByte = 0
    while (n > 0) {
      n -= 1
      val el: Any = p.productElement(n)
      bytesProvided(el) match {
        case 0 =>
        case 1 =>
          partial |= el.## << (nextPartialByte * 8)
          if (nextPartialByte < 3)
            nextPartialByte += 1
          else {
            h = step(h,partial)
            partial = 0
            nextPartialByte = 0
          }
        case 2 =>
          val i = el.##
          partial |= i << (nextPartialByte * 8)
          if (nextPartialByte < 2)
            nextPartialByte += 2
          else {
            h = step(h,partial)
            nextPartialByte -= 2
            partial = if (nextPartialByte == 0) 0 else i >>> 8
          }
        case 4 =>
          h = step(h, el.##)
        case 8 =>
          h = step(h, el.##)
          h = step(h, highInt(el))
      }
    }
    // Handle the last few bytes of the input array
    if (nextPartialByte > 0) {
      h ^= partial
      h *= m
    }
    // Do a few final mixes of the hash to ensure the last few
    // bytes are well-incorporated.
    h ^= h >>> 13
    h *= m
    h ^ (h >>> 15)
  }

  def string(s: String): Int = {
    // Initialize the hash to a 'random' value
    var n = s.length
    var h = n
    var nn = n & ~ 1
    while (nn > 0) {
      nn -= 2
      h = step(h, (s(nn + 1) << 16) | s(nn))
    }
    // Handle the last few bytes of the input array
    if ((n & 1) != 0) {
      h ^= s(n - 1)
      h *= m
    }
    // Do a few final mixes of the hash to ensure the last few
    // bytes are well-incorporated.
    h ^= h >>> 13
    h *= m
    h ^ (h >>> 15)
  }
}