Merge lp:~pedronis/ubuntu-push/gethosts into lp:ubuntu-push

Proposed by Samuele Pedroni
Status: Merged
Approved by: Samuele Pedroni
Approved revision: 92
Merged at revision: 89
Proposed branch: lp:~pedronis/ubuntu-push/gethosts
Merge into: lp:ubuntu-push
Diff against target: 1015 lines (+952/-0)
11 files modified
client/gethosts/gethost.go (+92/-0)
client/gethosts/gethost_test.go (+102/-0)
debian/copyright (+4/-0)
external/README (+3/-0)
external/murmur3/LICENSE (+24/-0)
external/murmur3/README.md (+49/-0)
external/murmur3/murmur.go (+65/-0)
external/murmur3/murmur128.go (+189/-0)
external/murmur3/murmur32.go (+154/-0)
external/murmur3/murmur64.go (+45/-0)
external/murmur3/murmur_test.go (+225/-0)
To merge this branch: bzr merge lp:~pedronis/ubuntu-push/gethosts
Reviewer Review Type Date Requested Status
John Lenton (community) Approve
Review via email: mp+212463@code.launchpad.net

Commit message

introduce package gethosts implementing finding hosts to connect to for delivery of notifications

Description of the change

introduce package gethosts implementing finding hosts to connect to for delivery of notifications

vendorize an impl of murmur3

To post a comment you must log in.
lp:~pedronis/ubuntu-push/gethosts updated
91. By Samuele Pedroni

newline

92. By Samuele Pedroni

remove .gitignore

Revision history for this message
John Lenton (chipaca) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'client/gethosts'
=== added file 'client/gethosts/gethost.go'
--- client/gethosts/gethost.go 1970-01-01 00:00:00 +0000
+++ client/gethosts/gethost.go 2014-03-24 15:37:31 +0000
@@ -0,0 +1,92 @@
1/*
2 Copyright 2013-2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17// Package gethosts implements finding hosts to connect to for delivery of notifications.
18package gethosts
19
20import (
21 "encoding/json"
22 "errors"
23 "fmt"
24 "io/ioutil"
25 "net/http"
26 "time"
27
28 "launchpad.net/ubuntu-push/external/murmur3"
29 http13 "launchpad.net/ubuntu-push/http13client"
30)
31
32// GetHost implements finding hosts to connect to consulting a remote endpoint providing a hash of the device identifier.
33type GetHost struct {
34 hash string
35 endpointUrl string
36 cli *http13.Client
37}
38
39// New makes a GetHost.
40func New(deviceId, endpointUrl string, timeout time.Duration) *GetHost {
41 hash := murmur3.Sum64([]byte(deviceId))
42 return &GetHost{
43 hash: fmt.Sprintf("%x", hash),
44 endpointUrl: endpointUrl,
45 cli: &http13.Client{
46 Transport: &http13.Transport{TLSHandshakeTimeout: timeout},
47 Timeout: timeout,
48 },
49 }
50}
51
52type expected struct {
53 Hosts []string
54}
55
56var (
57 ErrRequest = errors.New("request was not accepted")
58 ErrInternal = errors.New("remote had an internal error")
59 ErrTemporary = errors.New("remote had a temporary error")
60)
61
62// Get gets a list of hosts consulting the endpoint.
63func (gh *GetHost) Get() ([]string, error) {
64 resp, err := gh.cli.Get(gh.endpointUrl + "?h=" + gh.hash)
65 if err != nil {
66 return nil, err
67 }
68 defer resp.Body.Close()
69 if resp.StatusCode != http.StatusOK {
70 switch {
71 case resp.StatusCode == http.StatusInternalServerError:
72 return nil, ErrInternal
73 case resp.StatusCode > http.StatusInternalServerError:
74 return nil, ErrTemporary
75 default:
76 return nil, ErrRequest
77 }
78 }
79 body, err := ioutil.ReadAll(resp.Body)
80 if err != nil {
81 return nil, err
82 }
83 var parsed expected
84 err = json.Unmarshal(body, &parsed)
85 if err != nil {
86 return nil, ErrTemporary
87 }
88 if len(parsed.Hosts) == 0 {
89 return nil, ErrTemporary
90 }
91 return parsed.Hosts, nil
92}
093
=== added file 'client/gethosts/gethost_test.go'
--- client/gethosts/gethost_test.go 1970-01-01 00:00:00 +0000
+++ client/gethosts/gethost_test.go 2014-03-24 15:37:31 +0000
@@ -0,0 +1,102 @@
1/*
2 Copyright 2013-2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17package gethosts
18
19import (
20 "encoding/json"
21 "fmt"
22 "net/http"
23 "net/http/httptest"
24 "testing"
25 "time"
26
27 . "launchpad.net/gocheck"
28
29 "launchpad.net/ubuntu-push/external/murmur3"
30)
31
32func TestGetHosts(t *testing.T) { TestingT(t) }
33
34type getHostsSuite struct{}
35
36var _ = Suite(&getHostsSuite{})
37
38func (s *getHostsSuite) TestNew(c *C) {
39 gh := New("foobar", "http://where/hosts", 10*time.Second)
40 c.Check(gh.hash, Equals, fmt.Sprintf("%x", murmur3.Sum64([]byte("foobar"))))
41 c.Check(gh.endpointUrl, Equals, "http://where/hosts")
42}
43
44func (s *getHostsSuite) TestGet(c *C) {
45 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
46 x := r.FormValue("h")
47 b, err := json.Marshal(map[string]interface{}{
48 "hosts": []string{"http://" + x},
49 })
50 if err != nil {
51 panic(err)
52 }
53 w.Header().Set("Content-Type", "application/json")
54 w.Write(b)
55 }))
56 defer ts.Close()
57 gh := New("foobar", ts.URL, 1*time.Second)
58 res, err := gh.Get()
59 c.Assert(err, IsNil)
60 c.Check(res, DeepEquals, []string{"http://c1130408a700afe0"})
61}
62
63func (s *getHostsSuite) TestGetTimeout(c *C) {
64 finish := make(chan bool, 1)
65 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
66 <-finish
67 }))
68 defer func() {
69 time.Sleep(100 * time.Millisecond) // work around -race issue
70 ts.Close()
71 }()
72 defer func() {
73 finish <- true
74 }()
75 gh := New("foobar", ts.URL, 1*time.Second)
76 _, err := gh.Get()
77 c.Check(err, ErrorMatches, ".*closed.*")
78}
79
80func (s *getHostsSuite) TestGetErrorScenarios(c *C) {
81 status := make(chan int, 1)
82 body := make(chan string, 1)
83 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
84 w.WriteHeader(<-status)
85 fmt.Fprintf(w, "%s", <-body)
86 }))
87 defer ts.Close()
88 gh := New("foobar", ts.URL, 1*time.Second)
89 scenario := func(st int, bdy string, expectedErr error) {
90 status <- st
91 body <- bdy
92 _, err := gh.Get()
93 c.Check(err, Equals, expectedErr)
94 }
95
96 scenario(http.StatusBadRequest, "", ErrRequest)
97 scenario(http.StatusInternalServerError, "", ErrInternal)
98 scenario(http.StatusGatewayTimeout, "", ErrTemporary)
99
100 scenario(http.StatusOK, "{", ErrTemporary)
101 scenario(http.StatusOK, "{}", ErrTemporary)
102}
0103
=== modified file 'debian/copyright'
--- debian/copyright 2014-03-21 11:21:30 +0000
+++ debian/copyright 2014-03-24 15:37:31 +0000
@@ -10,6 +10,10 @@
10Copyright: 2009-2013 The Go Authors10Copyright: 2009-2013 The Go Authors
11License: BSD-3-clause11License: BSD-3-clause
1212
13Files: external/murmur3/*
14Copyright: 2013 Sébastien Paolacci
15License: BSD-3-clause
16
13License: GPL-3.017License: GPL-3.0
14 This program is free software: you can redistribute it and/or modify18 This program is free software: you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by19 it under the terms of the GNU General Public License as published by
1620
=== added directory 'external'
=== added file 'external/README'
--- external/README 1970-01-01 00:00:00 +0000
+++ external/README 2014-03-24 15:37:31 +0000
@@ -0,0 +1,3 @@
1Directly included vendorized small packages.
2
3* murmor3 comes from import at lp:~ubuntu-push-hackers/ubuntu-push/murmur at revno 10
04
=== added directory 'external/murmur3'
=== added file 'external/murmur3/LICENSE'
--- external/murmur3/LICENSE 1970-01-01 00:00:00 +0000
+++ external/murmur3/LICENSE 2014-03-24 15:37:31 +0000
@@ -0,0 +1,24 @@
1Copyright 2013, Sébastien Paolacci.
2All rights reserved.
3
4Redistribution and use in source and binary forms, with or without
5modification, are permitted provided that the following conditions are met:
6 * Redistributions of source code must retain the above copyright
7 notice, this list of conditions and the following disclaimer.
8 * Redistributions in binary form must reproduce the above copyright
9 notice, this list of conditions and the following disclaimer in the
10 documentation and/or other materials provided with the distribution.
11 * Neither the name of the library nor the
12 names of its contributors may be used to endorse or promote products
13 derived from this software without specific prior written permission.
14
15THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
19DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
025
=== added file 'external/murmur3/README.md'
--- external/murmur3/README.md 1970-01-01 00:00:00 +0000
+++ external/murmur3/README.md 2014-03-24 15:37:31 +0000
@@ -0,0 +1,49 @@
1murmur3
2=======
3
4Native Go implementation of Austin Appleby's third MurmurHash revision (aka
5MurmurHash3).
6
7Reference algorithm has been slightly hacked as to support the streaming mode
8required by Go's standard [Hash interface](http://golang.org/pkg/hash/#Hash).
9
10
11Benchmarks
12----------
13
14Go tip as of 2013-03-11 (i.e almost go1.1), core i7 @ 3.4 Ghz. All runs
15include hasher instanciation and sequence finalization.
16
17<pre>
18
19Benchmark32_1 200000000 8.4 ns/op 119.39 MB/s
20Benchmark32_2 200000000 9.5 ns/op 211.69 MB/s
21Benchmark32_4 500000000 7.9 ns/op 506.24 MB/s
22Benchmark32_8 200000000 9.4 ns/op 853.40 MB/s
23Benchmark32_16 100000000 12.1 ns/op 1324.19 MB/s
24Benchmark32_32 100000000 18.2 ns/op 1760.81 MB/s
25Benchmark32_64 50000000 31.2 ns/op 2051.59 MB/s
26Benchmark32_128 50000000 58.7 ns/op 2180.34 MB/s
27Benchmark32_256 20000000 116.0 ns/op 2194.85 MB/s
28Benchmark32_512 10000000 227.0 ns/op 2247.43 MB/s
29Benchmark32_1024 5000000 449.0 ns/op 2276.88 MB/s
30Benchmark32_2048 2000000 894.0 ns/op 2289.87 MB/s
31Benchmark32_4096 1000000 1792.0 ns/op 2284.64 MB/s
32Benchmark32_8192 500000 3559.0 ns/op 2301.33 MB/s
33
34Benchmark128_1 50000000 33.2 ns/op 30.15 MB/s
35Benchmark128_2 50000000 33.3 ns/op 59.99 MB/s
36Benchmark128_4 50000000 35.4 ns/op 112.88 MB/s
37Benchmark128_8 50000000 36.6 ns/op 218.30 MB/s
38Benchmark128_16 50000000 35.5 ns/op 450.86 MB/s
39Benchmark128_32 50000000 35.3 ns/op 905.84 MB/s
40Benchmark128_64 50000000 44.3 ns/op 1443.76 MB/s
41Benchmark128_128 50000000 58.2 ns/op 2201.02 MB/s
42Benchmark128_256 20000000 85.3 ns/op 2999.88 MB/s
43Benchmark128_512 10000000 142.0 ns/op 3592.97 MB/s
44Benchmark128_1024 10000000 258.0 ns/op 3963.74 MB/s
45Benchmark128_2048 5000000 494.0 ns/op 4144.65 MB/s
46Benchmark128_4096 2000000 955.0 ns/op 4285.80 MB/s
47Benchmark128_8192 1000000 1884.0 ns/op 4347.12 MB/s
48
49</pre>
050
=== added file 'external/murmur3/murmur.go'
--- external/murmur3/murmur.go 1970-01-01 00:00:00 +0000
+++ external/murmur3/murmur.go 2014-03-24 15:37:31 +0000
@@ -0,0 +1,65 @@
1// Copyright 2013, Sébastien Paolacci. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5/*
6Native (and fast) implementation of Austin Appleby's MurmurHash3.
7
8Package murmur3 implements Austin Appleby's non-cryptographic MurmurHash3.
9
10 Reference implementation:
11 http://code.google.com/p/smhasher/wiki/MurmurHash3
12
13 History, characteristics and (legacy) perfs:
14 https://sites.google.com/site/murmurhash/
15 https://sites.google.com/site/murmurhash/statistics
16*/
17package murmur3
18
19type bmixer interface {
20 bmix(p []byte) (tail []byte)
21 Size() (n int)
22 reset()
23}
24
25type digest struct {
26 clen int // Digested input cumulative length.
27 tail []byte // 0 to Size()-1 bytes view of `buf'.
28 buf [16]byte // Expected (but not required) to be Size() large.
29 bmixer
30}
31
32func (d *digest) BlockSize() int { return 1 }
33
34func (d *digest) Write(p []byte) (n int, err error) {
35 n = len(p)
36 d.clen += n
37
38 if len(d.tail) > 0 {
39 // Stick back pending bytes.
40 nfree := d.Size() - len(d.tail) // nfree ∈ [1, d.Size()-1].
41 if nfree < len(p) {
42 // One full block can be formed.
43 block := append(d.tail, p[:nfree]...)
44 p = p[nfree:]
45 _ = d.bmix(block) // No tail.
46 } else {
47 // Tail's buf is large enough to prevent reallocs.
48 p = append(d.tail, p...)
49 }
50 }
51
52 d.tail = d.bmix(p)
53
54 // Keep own copy of the 0 to Size()-1 pending bytes.
55 nn := copy(d.buf[:], d.tail)
56 d.tail = d.buf[:nn]
57
58 return n, nil
59}
60
61func (d *digest) Reset() {
62 d.clen = 0
63 d.tail = nil
64 d.bmixer.reset()
65}
066
=== added file 'external/murmur3/murmur128.go'
--- external/murmur3/murmur128.go 1970-01-01 00:00:00 +0000
+++ external/murmur3/murmur128.go 2014-03-24 15:37:31 +0000
@@ -0,0 +1,189 @@
1package murmur3
2
3import (
4 //"encoding/binary"
5 "hash"
6 "unsafe"
7)
8
9const (
10 c1_128 = 0x87c37b91114253d5
11 c2_128 = 0x4cf5ad432745937f
12)
13
14// Make sure interfaces are correctly implemented.
15var (
16 _ hash.Hash = new(digest128)
17 _ Hash128 = new(digest128)
18 _ bmixer = new(digest128)
19)
20
21// Hack: the standard api doesn't define any Hash128 interface.
22type Hash128 interface {
23 hash.Hash
24 Sum128() (uint64, uint64)
25}
26
27// digest128 represents a partial evaluation of a 128 bites hash.
28type digest128 struct {
29 digest
30 h1 uint64 // Unfinalized running hash part 1.
31 h2 uint64 // Unfinalized running hash part 2.
32}
33
34func New128() Hash128 {
35 d := new(digest128)
36 d.bmixer = d
37 d.Reset()
38 return d
39}
40
41func (d *digest128) Size() int { return 16 }
42
43func (d *digest128) reset() { d.h1, d.h2 = 1, 1 }
44
45func (d *digest128) Sum(b []byte) []byte {
46 h1, h2 := d.h1, d.h2
47 return append(b,
48 byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32),
49 byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1),
50
51 byte(h2>>56), byte(h2>>48), byte(h2>>40), byte(h2>>32),
52 byte(h2>>24), byte(h2>>16), byte(h2>>8), byte(h2),
53 )
54}
55
56func (d *digest128) bmix(p []byte) (tail []byte) {
57 h1, h2 := d.h1, d.h2
58
59 nblocks := len(p) / 16
60 for i := 0; i < nblocks; i++ {
61 t := (*[2]uint64)(unsafe.Pointer(&p[i*16]))
62 k1, k2 := t[0], t[1]
63
64 k1 *= c1_128
65 k1 = (k1 << 31) | (k1 >> 33) // rotl64(k1, 31)
66 k1 *= c2_128
67 h1 ^= k1
68
69 h1 = (h1 << 27) | (h1 >> 37) // rotl64(h1, 27)
70 h1 += h2
71 h1 = h1*5 + 0x52dce729
72
73 k2 *= c2_128
74 k2 = (k2 << 33) | (k2 >> 31) // rotl64(k2, 33)
75 k2 *= c1_128
76 h2 ^= k2
77
78 h2 = (h2 << 31) | (h2 >> 33) // rotl64(h2, 31)
79 h2 += h1
80 h2 = h2*5 + 0x38495ab5
81 }
82 d.h1, d.h2 = h1, h2
83 return p[nblocks*d.Size():]
84}
85
86func (d *digest128) Sum128() (h1, h2 uint64) {
87
88 h1, h2 = d.h1, d.h2
89
90 var k1, k2 uint64
91 switch len(d.tail) & 15 {
92 case 15:
93 k2 ^= uint64(d.tail[14]) << 48
94 fallthrough
95 case 14:
96 k2 ^= uint64(d.tail[13]) << 40
97 fallthrough
98 case 13:
99 k2 ^= uint64(d.tail[12]) << 32
100 fallthrough
101 case 12:
102 k2 ^= uint64(d.tail[11]) << 24
103 fallthrough
104 case 11:
105 k2 ^= uint64(d.tail[10]) << 16
106 fallthrough
107 case 10:
108 k2 ^= uint64(d.tail[9]) << 8
109 fallthrough
110 case 9:
111 k2 ^= uint64(d.tail[8]) << 0
112
113 k2 *= c2_128
114 k2 = (k2 << 33) | (k2 >> 31) // rotl64(k2, 33)
115 k2 *= c1_128
116 h2 ^= k2
117
118 fallthrough
119
120 case 8:
121 k1 ^= uint64(d.tail[7]) << 56
122 fallthrough
123 case 7:
124 k1 ^= uint64(d.tail[6]) << 48
125 fallthrough
126 case 6:
127 k1 ^= uint64(d.tail[5]) << 40
128 fallthrough
129 case 5:
130 k1 ^= uint64(d.tail[4]) << 32
131 fallthrough
132 case 4:
133 k1 ^= uint64(d.tail[3]) << 24
134 fallthrough
135 case 3:
136 k1 ^= uint64(d.tail[2]) << 16
137 fallthrough
138 case 2:
139 k1 ^= uint64(d.tail[1]) << 8
140 fallthrough
141 case 1:
142 k1 ^= uint64(d.tail[0]) << 0
143 k1 *= c1_128
144 k1 = (k1 << 31) | (k1 >> 33) // rotl64(k1, 31)
145 k1 *= c2_128
146 h1 ^= k1
147 }
148
149 h1 ^= uint64(d.clen)
150 h2 ^= uint64(d.clen)
151
152 h1 += h2
153 h2 += h1
154
155 h1 = fmix64(h1)
156 h2 = fmix64(h2)
157
158 h1 += h2
159 h2 += h1
160
161 return h1, h2
162}
163
164func fmix64(k uint64) uint64 {
165 k ^= k >> 33
166 k *= 0xff51afd7ed558ccd
167 k ^= k >> 33
168 k *= 0xc4ceb9fe1a85ec53
169 k ^= k >> 33
170 return k
171}
172
173/*
174func rotl64(x uint64, r byte) uint64 {
175 return (x << r) | (x >> (64 - r))
176}
177*/
178
179// Sum128 returns the MurmurHash3 sum of data. It is equivalent to the
180// following sequence (without the extra burden and the extra allocation):
181// hasher := New128()
182// hasher.Write(data)
183// return hasher.Sum128()
184func Sum128(data []byte) (h1 uint64, h2 uint64) {
185 d := &digest128{h1: 1, h2: 1}
186 d.tail = d.bmix(data)
187 d.clen = len(data)
188 return d.Sum128()
189}
0190
=== added file 'external/murmur3/murmur32.go'
--- external/murmur3/murmur32.go 1970-01-01 00:00:00 +0000
+++ external/murmur3/murmur32.go 2014-03-24 15:37:31 +0000
@@ -0,0 +1,154 @@
1package murmur3
2
3// http://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/hash/Murmur3_32HashFunction.java
4
5import (
6 "hash"
7 "unsafe"
8)
9
10// Make sure interfaces are correctly implemented.
11var (
12 _ hash.Hash = new(digest32)
13 _ hash.Hash32 = new(digest32)
14)
15
16const (
17 c1_32 uint32 = 0xcc9e2d51
18 c2_32 uint32 = 0x1b873593
19)
20
21// digest32 represents a partial evaluation of a 32 bites hash.
22type digest32 struct {
23 digest
24 h1 uint32 // Unfinalized running hash.
25}
26
27func New32() hash.Hash32 {
28 d := new(digest32)
29 d.bmixer = d
30 d.Reset()
31 return d
32}
33
34func (d *digest32) Size() int { return 4 }
35
36func (d *digest32) reset() { d.h1 = 1 }
37
38func (d *digest32) Sum(b []byte) []byte {
39 h := d.h1
40 return append(b, byte(h>>24), byte(h>>16), byte(h>>8), byte(h))
41}
42
43// Digest as many blocks as possible.
44func (d *digest32) bmix(p []byte) (tail []byte) {
45 h1 := d.h1
46
47 nblocks := len(p) / 4
48 for i := 0; i < nblocks; i++ {
49 k1 := *(*uint32)(unsafe.Pointer(&p[i*4]))
50
51 k1 *= c1_32
52 k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
53 k1 *= c2_32
54
55 h1 ^= k1
56 h1 = (h1 << 13) | (h1 >> 19) // rotl32(h1, 13)
57 h1 = h1*5 + 0xe6546b64
58 }
59 d.h1 = h1
60 return p[nblocks*d.Size():]
61}
62
63func (d *digest32) Sum32() (h1 uint32) {
64
65 h1 = d.h1
66
67 var k1 uint32
68 switch len(d.tail) & 3 {
69 case 3:
70 k1 ^= uint32(d.tail[2]) << 16
71 fallthrough
72 case 2:
73 k1 ^= uint32(d.tail[1]) << 8
74 fallthrough
75 case 1:
76 k1 ^= uint32(d.tail[0])
77 k1 *= c1_32
78 k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
79 k1 *= c2_32
80 h1 ^= k1
81 }
82
83 h1 ^= uint32(d.clen)
84
85 h1 ^= h1 >> 16
86 h1 *= 0x85ebca6b
87 h1 ^= h1 >> 13
88 h1 *= 0xc2b2ae35
89 h1 ^= h1 >> 16
90
91 return h1
92}
93
94/*
95func rotl32(x uint32, r byte) uint32 {
96 return (x << r) | (x >> (32 - r))
97}
98*/
99
100// Sum32 returns the MurmurHash3 sum of data. It is equivalent to the
101// following sequence (without the extra burden and the extra allocation):
102// hasher := New32()
103// hasher.Write(data)
104// return hasher.Sum32()
105func Sum32(data []byte) uint32 {
106
107 var h1 uint32 = 1
108
109 nblocks := len(data) / 4
110 var p uintptr
111 if len(data) > 0 {
112 p = uintptr(unsafe.Pointer(&data[0]))
113 }
114 p1 := p + uintptr(4*nblocks)
115 for ; p < p1; p += 4 {
116 k1 := *(*uint32)(unsafe.Pointer(p))
117
118 k1 *= c1_32
119 k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
120 k1 *= c2_32
121
122 h1 ^= k1
123 h1 = (h1 << 13) | (h1 >> 19) // rotl32(h1, 13)
124 h1 = h1*5 + 0xe6546b64
125 }
126
127 tail := data[nblocks*4:]
128
129 var k1 uint32
130 switch len(tail) & 3 {
131 case 3:
132 k1 ^= uint32(tail[2]) << 16
133 fallthrough
134 case 2:
135 k1 ^= uint32(tail[1]) << 8
136 fallthrough
137 case 1:
138 k1 ^= uint32(tail[0])
139 k1 *= c1_32
140 k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
141 k1 *= c2_32
142 h1 ^= k1
143 }
144
145 h1 ^= uint32(len(data))
146
147 h1 ^= h1 >> 16
148 h1 *= 0x85ebca6b
149 h1 ^= h1 >> 13
150 h1 *= 0xc2b2ae35
151 h1 ^= h1 >> 16
152
153 return h1
154}
0155
=== added file 'external/murmur3/murmur64.go'
--- external/murmur3/murmur64.go 1970-01-01 00:00:00 +0000
+++ external/murmur3/murmur64.go 2014-03-24 15:37:31 +0000
@@ -0,0 +1,45 @@
1package murmur3
2
3import (
4 "hash"
5)
6
7// Make sure interfaces are correctly implemented.
8var (
9 _ hash.Hash = new(digest64)
10 _ hash.Hash64 = new(digest64)
11 _ bmixer = new(digest64)
12)
13
14// digest64 is half a digest128.
15type digest64 digest128
16
17func New64() hash.Hash64 {
18 d := (*digest64)(New128().(*digest128))
19 return d
20}
21
22func (d *digest64) Sum(b []byte) []byte {
23 h1 := d.h1
24 return append(b,
25 byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32),
26 byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1))
27}
28
29func (d *digest64) Sum64() uint64 {
30 h1, _ := (*digest128)(d).Sum128()
31 return h1
32}
33
34// Sum64 returns the MurmurHash3 sum of data. It is equivalent to the
35// following sequence (without the extra burden and the extra allocation):
36// hasher := New64()
37// hasher.Write(data)
38// return hasher.Sum64()
39func Sum64(data []byte) uint64 {
40 d := &digest128{h1: 1, h2: 1}
41 d.tail = d.bmix(data)
42 d.clen = len(data)
43 h1, _ := d.Sum128()
44 return h1
45}
046
=== added file 'external/murmur3/murmur_test.go'
--- external/murmur3/murmur_test.go 1970-01-01 00:00:00 +0000
+++ external/murmur3/murmur_test.go 2014-03-24 15:37:31 +0000
@@ -0,0 +1,225 @@
1package murmur3
2
3import (
4 "hash"
5 "testing"
6)
7
8var data = []struct {
9 h32 uint32
10 h64_1 uint64
11 h64_2 uint64
12 s string
13}{
14 {0x514e28b7, 0x4610abe56eff5cb5, 0x51622daa78f83583, ""},
15 {0xbb4abcad, 0xa78ddff5adae8d10, 0x128900ef20900135, "hello"},
16 {0x6f5cb2e9, 0x8b95f808840725c6, 0x1597ed5422bd493b, "hello, world"},
17 {0xf50e1f30, 0x2a929de9c8f97b2f, 0x56a41d99af43a2db, "19 Jan 2038 at 3:14:07 AM"},
18 {0x846f6a36, 0xfb3325171f9744da, 0xaaf8b92a5f722952, "The quick brown fox jumps over the lazy dog."},
19}
20
21func TestRef(t *testing.T) {
22 for _, elem := range data {
23
24 var h32 hash.Hash32 = New32()
25 h32.Write([]byte(elem.s))
26 if v := h32.Sum32(); v != elem.h32 {
27 t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h32)
28 }
29
30 if v := Sum32([]byte(elem.s)); v != elem.h32 {
31 t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h32)
32 }
33
34 var h64 hash.Hash64 = New64()
35 h64.Write([]byte(elem.s))
36 if v := h64.Sum64(); v != elem.h64_1 {
37 t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h64_1)
38 }
39
40 var h128 Hash128 = New128()
41 h128.Write([]byte(elem.s))
42 if v1, v2 := h128.Sum128(); v1 != elem.h64_1 || v2 != elem.h64_2 {
43 t.Errorf("'%s': 0x%x-0x%x (want 0x%x-0x%x)", elem.s, v1, v2, elem.h64_1, elem.h64_2)
44 }
45
46 if v1, v2 := Sum128([]byte(elem.s)); v1 != elem.h64_1 || v2 != elem.h64_2 {
47 t.Errorf("'%s': 0x%x-0x%x (want 0x%x-0x%x)", elem.s, v1, v2, elem.h64_1, elem.h64_2)
48 }
49 }
50}
51
52func TestIncremental(t *testing.T) {
53 for _, elem := range data {
54 h32 := New32()
55 h128 := New128()
56 for i, j, k := 0, 0, len(elem.s); i < k; i = j {
57 j = 2*i + 3
58 if j > k {
59 j = k
60 }
61 s := elem.s[i:j]
62 print(s + "|")
63 h32.Write([]byte(s))
64 h128.Write([]byte(s))
65 }
66 println()
67 if v := h32.Sum32(); v != elem.h32 {
68 t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h32)
69 }
70 if v1, v2 := h128.Sum128(); v1 != elem.h64_1 || v2 != elem.h64_2 {
71 t.Errorf("'%s': 0x%x-0x%x (want 0x%x-0x%x)", elem.s, v1, v2, elem.h64_1, elem.h64_2)
72 }
73 }
74}
75
76//---
77
78func bench32(b *testing.B, length int) {
79 buf := make([]byte, length)
80 b.SetBytes(int64(length))
81 b.ResetTimer()
82 for i := 0; i < b.N; i++ {
83 Sum32(buf)
84 }
85}
86
87func Benchmark32_1(b *testing.B) {
88 bench32(b, 1)
89}
90func Benchmark32_2(b *testing.B) {
91 bench32(b, 2)
92}
93func Benchmark32_4(b *testing.B) {
94 bench32(b, 4)
95}
96func Benchmark32_8(b *testing.B) {
97 bench32(b, 8)
98}
99func Benchmark32_16(b *testing.B) {
100 bench32(b, 16)
101}
102func Benchmark32_32(b *testing.B) {
103 bench32(b, 32)
104}
105func Benchmark32_64(b *testing.B) {
106 bench32(b, 64)
107}
108func Benchmark32_128(b *testing.B) {
109 bench32(b, 128)
110}
111func Benchmark32_256(b *testing.B) {
112 bench32(b, 256)
113}
114func Benchmark32_512(b *testing.B) {
115 bench32(b, 512)
116}
117func Benchmark32_1024(b *testing.B) {
118 bench32(b, 1024)
119}
120func Benchmark32_2048(b *testing.B) {
121 bench32(b, 2048)
122}
123func Benchmark32_4096(b *testing.B) {
124 bench32(b, 4096)
125}
126func Benchmark32_8192(b *testing.B) {
127 bench32(b, 8192)
128}
129
130//---
131
132func benchPartial32(b *testing.B, length int) {
133 buf := make([]byte, length)
134 b.SetBytes(int64(length))
135
136 start := (32 / 8) / 2
137 chunks := 7
138 k := length / chunks
139 tail := (length - start) % k
140
141 b.ResetTimer()
142 for i := 0; i < b.N; i++ {
143 hasher := New32()
144 hasher.Write(buf[0:start])
145
146 for j := start; j+k <= length; j += k {
147 hasher.Write(buf[j : j+k])
148 }
149
150 hasher.Write(buf[length-tail:])
151 hasher.Sum32()
152 }
153}
154
155func BenchmarkPartial32_8(b *testing.B) {
156 benchPartial32(b, 8)
157}
158func BenchmarkPartial32_16(b *testing.B) {
159 benchPartial32(b, 16)
160}
161func BenchmarkPartial32_32(b *testing.B) {
162 benchPartial32(b, 32)
163}
164func BenchmarkPartial32_64(b *testing.B) {
165 benchPartial32(b, 64)
166}
167func BenchmarkPartial32_128(b *testing.B) {
168 benchPartial32(b, 128)
169}
170
171//---
172
173func bench128(b *testing.B, length int) {
174 buf := make([]byte, length)
175 b.SetBytes(int64(length))
176 b.ResetTimer()
177 for i := 0; i < b.N; i++ {
178 Sum128(buf)
179 }
180}
181
182func Benchmark128_1(b *testing.B) {
183 bench128(b, 1)
184}
185func Benchmark128_2(b *testing.B) {
186 bench128(b, 2)
187}
188func Benchmark128_4(b *testing.B) {
189 bench128(b, 4)
190}
191func Benchmark128_8(b *testing.B) {
192 bench128(b, 8)
193}
194func Benchmark128_16(b *testing.B) {
195 bench128(b, 16)
196}
197func Benchmark128_32(b *testing.B) {
198 bench128(b, 32)
199}
200func Benchmark128_64(b *testing.B) {
201 bench128(b, 64)
202}
203func Benchmark128_128(b *testing.B) {
204 bench128(b, 128)
205}
206func Benchmark128_256(b *testing.B) {
207 bench128(b, 256)
208}
209func Benchmark128_512(b *testing.B) {
210 bench128(b, 512)
211}
212func Benchmark128_1024(b *testing.B) {
213 bench128(b, 1024)
214}
215func Benchmark128_2048(b *testing.B) {
216 bench128(b, 2048)
217}
218func Benchmark128_4096(b *testing.B) {
219 bench128(b, 4096)
220}
221func Benchmark128_8192(b *testing.B) {
222 bench128(b, 8192)
223}
224
225//---

Subscribers

People subscribed via source and target branches