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
1=== added directory 'client/gethosts'
2=== added file 'client/gethosts/gethost.go'
3--- client/gethosts/gethost.go 1970-01-01 00:00:00 +0000
4+++ client/gethosts/gethost.go 2014-03-24 15:37:31 +0000
5@@ -0,0 +1,92 @@
6+/*
7+ Copyright 2013-2014 Canonical Ltd.
8+
9+ This program is free software: you can redistribute it and/or modify it
10+ under the terms of the GNU General Public License version 3, as published
11+ by the Free Software Foundation.
12+
13+ This program is distributed in the hope that it will be useful, but
14+ WITHOUT ANY WARRANTY; without even the implied warranties of
15+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
16+ PURPOSE. See the GNU General Public License for more details.
17+
18+ You should have received a copy of the GNU General Public License along
19+ with this program. If not, see <http://www.gnu.org/licenses/>.
20+*/
21+
22+// Package gethosts implements finding hosts to connect to for delivery of notifications.
23+package gethosts
24+
25+import (
26+ "encoding/json"
27+ "errors"
28+ "fmt"
29+ "io/ioutil"
30+ "net/http"
31+ "time"
32+
33+ "launchpad.net/ubuntu-push/external/murmur3"
34+ http13 "launchpad.net/ubuntu-push/http13client"
35+)
36+
37+// GetHost implements finding hosts to connect to consulting a remote endpoint providing a hash of the device identifier.
38+type GetHost struct {
39+ hash string
40+ endpointUrl string
41+ cli *http13.Client
42+}
43+
44+// New makes a GetHost.
45+func New(deviceId, endpointUrl string, timeout time.Duration) *GetHost {
46+ hash := murmur3.Sum64([]byte(deviceId))
47+ return &GetHost{
48+ hash: fmt.Sprintf("%x", hash),
49+ endpointUrl: endpointUrl,
50+ cli: &http13.Client{
51+ Transport: &http13.Transport{TLSHandshakeTimeout: timeout},
52+ Timeout: timeout,
53+ },
54+ }
55+}
56+
57+type expected struct {
58+ Hosts []string
59+}
60+
61+var (
62+ ErrRequest = errors.New("request was not accepted")
63+ ErrInternal = errors.New("remote had an internal error")
64+ ErrTemporary = errors.New("remote had a temporary error")
65+)
66+
67+// Get gets a list of hosts consulting the endpoint.
68+func (gh *GetHost) Get() ([]string, error) {
69+ resp, err := gh.cli.Get(gh.endpointUrl + "?h=" + gh.hash)
70+ if err != nil {
71+ return nil, err
72+ }
73+ defer resp.Body.Close()
74+ if resp.StatusCode != http.StatusOK {
75+ switch {
76+ case resp.StatusCode == http.StatusInternalServerError:
77+ return nil, ErrInternal
78+ case resp.StatusCode > http.StatusInternalServerError:
79+ return nil, ErrTemporary
80+ default:
81+ return nil, ErrRequest
82+ }
83+ }
84+ body, err := ioutil.ReadAll(resp.Body)
85+ if err != nil {
86+ return nil, err
87+ }
88+ var parsed expected
89+ err = json.Unmarshal(body, &parsed)
90+ if err != nil {
91+ return nil, ErrTemporary
92+ }
93+ if len(parsed.Hosts) == 0 {
94+ return nil, ErrTemporary
95+ }
96+ return parsed.Hosts, nil
97+}
98
99=== added file 'client/gethosts/gethost_test.go'
100--- client/gethosts/gethost_test.go 1970-01-01 00:00:00 +0000
101+++ client/gethosts/gethost_test.go 2014-03-24 15:37:31 +0000
102@@ -0,0 +1,102 @@
103+/*
104+ Copyright 2013-2014 Canonical Ltd.
105+
106+ This program is free software: you can redistribute it and/or modify it
107+ under the terms of the GNU General Public License version 3, as published
108+ by the Free Software Foundation.
109+
110+ This program is distributed in the hope that it will be useful, but
111+ WITHOUT ANY WARRANTY; without even the implied warranties of
112+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
113+ PURPOSE. See the GNU General Public License for more details.
114+
115+ You should have received a copy of the GNU General Public License along
116+ with this program. If not, see <http://www.gnu.org/licenses/>.
117+*/
118+
119+package gethosts
120+
121+import (
122+ "encoding/json"
123+ "fmt"
124+ "net/http"
125+ "net/http/httptest"
126+ "testing"
127+ "time"
128+
129+ . "launchpad.net/gocheck"
130+
131+ "launchpad.net/ubuntu-push/external/murmur3"
132+)
133+
134+func TestGetHosts(t *testing.T) { TestingT(t) }
135+
136+type getHostsSuite struct{}
137+
138+var _ = Suite(&getHostsSuite{})
139+
140+func (s *getHostsSuite) TestNew(c *C) {
141+ gh := New("foobar", "http://where/hosts", 10*time.Second)
142+ c.Check(gh.hash, Equals, fmt.Sprintf("%x", murmur3.Sum64([]byte("foobar"))))
143+ c.Check(gh.endpointUrl, Equals, "http://where/hosts")
144+}
145+
146+func (s *getHostsSuite) TestGet(c *C) {
147+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
148+ x := r.FormValue("h")
149+ b, err := json.Marshal(map[string]interface{}{
150+ "hosts": []string{"http://" + x},
151+ })
152+ if err != nil {
153+ panic(err)
154+ }
155+ w.Header().Set("Content-Type", "application/json")
156+ w.Write(b)
157+ }))
158+ defer ts.Close()
159+ gh := New("foobar", ts.URL, 1*time.Second)
160+ res, err := gh.Get()
161+ c.Assert(err, IsNil)
162+ c.Check(res, DeepEquals, []string{"http://c1130408a700afe0"})
163+}
164+
165+func (s *getHostsSuite) TestGetTimeout(c *C) {
166+ finish := make(chan bool, 1)
167+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
168+ <-finish
169+ }))
170+ defer func() {
171+ time.Sleep(100 * time.Millisecond) // work around -race issue
172+ ts.Close()
173+ }()
174+ defer func() {
175+ finish <- true
176+ }()
177+ gh := New("foobar", ts.URL, 1*time.Second)
178+ _, err := gh.Get()
179+ c.Check(err, ErrorMatches, ".*closed.*")
180+}
181+
182+func (s *getHostsSuite) TestGetErrorScenarios(c *C) {
183+ status := make(chan int, 1)
184+ body := make(chan string, 1)
185+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
186+ w.WriteHeader(<-status)
187+ fmt.Fprintf(w, "%s", <-body)
188+ }))
189+ defer ts.Close()
190+ gh := New("foobar", ts.URL, 1*time.Second)
191+ scenario := func(st int, bdy string, expectedErr error) {
192+ status <- st
193+ body <- bdy
194+ _, err := gh.Get()
195+ c.Check(err, Equals, expectedErr)
196+ }
197+
198+ scenario(http.StatusBadRequest, "", ErrRequest)
199+ scenario(http.StatusInternalServerError, "", ErrInternal)
200+ scenario(http.StatusGatewayTimeout, "", ErrTemporary)
201+
202+ scenario(http.StatusOK, "{", ErrTemporary)
203+ scenario(http.StatusOK, "{}", ErrTemporary)
204+}
205
206=== modified file 'debian/copyright'
207--- debian/copyright 2014-03-21 11:21:30 +0000
208+++ debian/copyright 2014-03-24 15:37:31 +0000
209@@ -10,6 +10,10 @@
210 Copyright: 2009-2013 The Go Authors
211 License: BSD-3-clause
212
213+Files: external/murmur3/*
214+Copyright: 2013 Sébastien Paolacci
215+License: BSD-3-clause
216+
217 License: GPL-3.0
218 This program is free software: you can redistribute it and/or modify
219 it under the terms of the GNU General Public License as published by
220
221=== added directory 'external'
222=== added file 'external/README'
223--- external/README 1970-01-01 00:00:00 +0000
224+++ external/README 2014-03-24 15:37:31 +0000
225@@ -0,0 +1,3 @@
226+Directly included vendorized small packages.
227+
228+* murmor3 comes from import at lp:~ubuntu-push-hackers/ubuntu-push/murmur at revno 10
229
230=== added directory 'external/murmur3'
231=== added file 'external/murmur3/LICENSE'
232--- external/murmur3/LICENSE 1970-01-01 00:00:00 +0000
233+++ external/murmur3/LICENSE 2014-03-24 15:37:31 +0000
234@@ -0,0 +1,24 @@
235+Copyright 2013, Sébastien Paolacci.
236+All rights reserved.
237+
238+Redistribution and use in source and binary forms, with or without
239+modification, are permitted provided that the following conditions are met:
240+ * Redistributions of source code must retain the above copyright
241+ notice, this list of conditions and the following disclaimer.
242+ * Redistributions in binary form must reproduce the above copyright
243+ notice, this list of conditions and the following disclaimer in the
244+ documentation and/or other materials provided with the distribution.
245+ * Neither the name of the library nor the
246+ names of its contributors may be used to endorse or promote products
247+ derived from this software without specific prior written permission.
248+
249+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
250+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
251+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
252+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
253+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
254+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
255+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
256+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
257+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
258+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
259
260=== added file 'external/murmur3/README.md'
261--- external/murmur3/README.md 1970-01-01 00:00:00 +0000
262+++ external/murmur3/README.md 2014-03-24 15:37:31 +0000
263@@ -0,0 +1,49 @@
264+murmur3
265+=======
266+
267+Native Go implementation of Austin Appleby's third MurmurHash revision (aka
268+MurmurHash3).
269+
270+Reference algorithm has been slightly hacked as to support the streaming mode
271+required by Go's standard [Hash interface](http://golang.org/pkg/hash/#Hash).
272+
273+
274+Benchmarks
275+----------
276+
277+Go tip as of 2013-03-11 (i.e almost go1.1), core i7 @ 3.4 Ghz. All runs
278+include hasher instanciation and sequence finalization.
279+
280+<pre>
281+
282+Benchmark32_1 200000000 8.4 ns/op 119.39 MB/s
283+Benchmark32_2 200000000 9.5 ns/op 211.69 MB/s
284+Benchmark32_4 500000000 7.9 ns/op 506.24 MB/s
285+Benchmark32_8 200000000 9.4 ns/op 853.40 MB/s
286+Benchmark32_16 100000000 12.1 ns/op 1324.19 MB/s
287+Benchmark32_32 100000000 18.2 ns/op 1760.81 MB/s
288+Benchmark32_64 50000000 31.2 ns/op 2051.59 MB/s
289+Benchmark32_128 50000000 58.7 ns/op 2180.34 MB/s
290+Benchmark32_256 20000000 116.0 ns/op 2194.85 MB/s
291+Benchmark32_512 10000000 227.0 ns/op 2247.43 MB/s
292+Benchmark32_1024 5000000 449.0 ns/op 2276.88 MB/s
293+Benchmark32_2048 2000000 894.0 ns/op 2289.87 MB/s
294+Benchmark32_4096 1000000 1792.0 ns/op 2284.64 MB/s
295+Benchmark32_8192 500000 3559.0 ns/op 2301.33 MB/s
296+
297+Benchmark128_1 50000000 33.2 ns/op 30.15 MB/s
298+Benchmark128_2 50000000 33.3 ns/op 59.99 MB/s
299+Benchmark128_4 50000000 35.4 ns/op 112.88 MB/s
300+Benchmark128_8 50000000 36.6 ns/op 218.30 MB/s
301+Benchmark128_16 50000000 35.5 ns/op 450.86 MB/s
302+Benchmark128_32 50000000 35.3 ns/op 905.84 MB/s
303+Benchmark128_64 50000000 44.3 ns/op 1443.76 MB/s
304+Benchmark128_128 50000000 58.2 ns/op 2201.02 MB/s
305+Benchmark128_256 20000000 85.3 ns/op 2999.88 MB/s
306+Benchmark128_512 10000000 142.0 ns/op 3592.97 MB/s
307+Benchmark128_1024 10000000 258.0 ns/op 3963.74 MB/s
308+Benchmark128_2048 5000000 494.0 ns/op 4144.65 MB/s
309+Benchmark128_4096 2000000 955.0 ns/op 4285.80 MB/s
310+Benchmark128_8192 1000000 1884.0 ns/op 4347.12 MB/s
311+
312+</pre>
313
314=== added file 'external/murmur3/murmur.go'
315--- external/murmur3/murmur.go 1970-01-01 00:00:00 +0000
316+++ external/murmur3/murmur.go 2014-03-24 15:37:31 +0000
317@@ -0,0 +1,65 @@
318+// Copyright 2013, Sébastien Paolacci. All rights reserved.
319+// Use of this source code is governed by a BSD-style
320+// license that can be found in the LICENSE file.
321+
322+/*
323+Native (and fast) implementation of Austin Appleby's MurmurHash3.
324+
325+Package murmur3 implements Austin Appleby's non-cryptographic MurmurHash3.
326+
327+ Reference implementation:
328+ http://code.google.com/p/smhasher/wiki/MurmurHash3
329+
330+ History, characteristics and (legacy) perfs:
331+ https://sites.google.com/site/murmurhash/
332+ https://sites.google.com/site/murmurhash/statistics
333+*/
334+package murmur3
335+
336+type bmixer interface {
337+ bmix(p []byte) (tail []byte)
338+ Size() (n int)
339+ reset()
340+}
341+
342+type digest struct {
343+ clen int // Digested input cumulative length.
344+ tail []byte // 0 to Size()-1 bytes view of `buf'.
345+ buf [16]byte // Expected (but not required) to be Size() large.
346+ bmixer
347+}
348+
349+func (d *digest) BlockSize() int { return 1 }
350+
351+func (d *digest) Write(p []byte) (n int, err error) {
352+ n = len(p)
353+ d.clen += n
354+
355+ if len(d.tail) > 0 {
356+ // Stick back pending bytes.
357+ nfree := d.Size() - len(d.tail) // nfree ∈ [1, d.Size()-1].
358+ if nfree < len(p) {
359+ // One full block can be formed.
360+ block := append(d.tail, p[:nfree]...)
361+ p = p[nfree:]
362+ _ = d.bmix(block) // No tail.
363+ } else {
364+ // Tail's buf is large enough to prevent reallocs.
365+ p = append(d.tail, p...)
366+ }
367+ }
368+
369+ d.tail = d.bmix(p)
370+
371+ // Keep own copy of the 0 to Size()-1 pending bytes.
372+ nn := copy(d.buf[:], d.tail)
373+ d.tail = d.buf[:nn]
374+
375+ return n, nil
376+}
377+
378+func (d *digest) Reset() {
379+ d.clen = 0
380+ d.tail = nil
381+ d.bmixer.reset()
382+}
383
384=== added file 'external/murmur3/murmur128.go'
385--- external/murmur3/murmur128.go 1970-01-01 00:00:00 +0000
386+++ external/murmur3/murmur128.go 2014-03-24 15:37:31 +0000
387@@ -0,0 +1,189 @@
388+package murmur3
389+
390+import (
391+ //"encoding/binary"
392+ "hash"
393+ "unsafe"
394+)
395+
396+const (
397+ c1_128 = 0x87c37b91114253d5
398+ c2_128 = 0x4cf5ad432745937f
399+)
400+
401+// Make sure interfaces are correctly implemented.
402+var (
403+ _ hash.Hash = new(digest128)
404+ _ Hash128 = new(digest128)
405+ _ bmixer = new(digest128)
406+)
407+
408+// Hack: the standard api doesn't define any Hash128 interface.
409+type Hash128 interface {
410+ hash.Hash
411+ Sum128() (uint64, uint64)
412+}
413+
414+// digest128 represents a partial evaluation of a 128 bites hash.
415+type digest128 struct {
416+ digest
417+ h1 uint64 // Unfinalized running hash part 1.
418+ h2 uint64 // Unfinalized running hash part 2.
419+}
420+
421+func New128() Hash128 {
422+ d := new(digest128)
423+ d.bmixer = d
424+ d.Reset()
425+ return d
426+}
427+
428+func (d *digest128) Size() int { return 16 }
429+
430+func (d *digest128) reset() { d.h1, d.h2 = 1, 1 }
431+
432+func (d *digest128) Sum(b []byte) []byte {
433+ h1, h2 := d.h1, d.h2
434+ return append(b,
435+ byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32),
436+ byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1),
437+
438+ byte(h2>>56), byte(h2>>48), byte(h2>>40), byte(h2>>32),
439+ byte(h2>>24), byte(h2>>16), byte(h2>>8), byte(h2),
440+ )
441+}
442+
443+func (d *digest128) bmix(p []byte) (tail []byte) {
444+ h1, h2 := d.h1, d.h2
445+
446+ nblocks := len(p) / 16
447+ for i := 0; i < nblocks; i++ {
448+ t := (*[2]uint64)(unsafe.Pointer(&p[i*16]))
449+ k1, k2 := t[0], t[1]
450+
451+ k1 *= c1_128
452+ k1 = (k1 << 31) | (k1 >> 33) // rotl64(k1, 31)
453+ k1 *= c2_128
454+ h1 ^= k1
455+
456+ h1 = (h1 << 27) | (h1 >> 37) // rotl64(h1, 27)
457+ h1 += h2
458+ h1 = h1*5 + 0x52dce729
459+
460+ k2 *= c2_128
461+ k2 = (k2 << 33) | (k2 >> 31) // rotl64(k2, 33)
462+ k2 *= c1_128
463+ h2 ^= k2
464+
465+ h2 = (h2 << 31) | (h2 >> 33) // rotl64(h2, 31)
466+ h2 += h1
467+ h2 = h2*5 + 0x38495ab5
468+ }
469+ d.h1, d.h2 = h1, h2
470+ return p[nblocks*d.Size():]
471+}
472+
473+func (d *digest128) Sum128() (h1, h2 uint64) {
474+
475+ h1, h2 = d.h1, d.h2
476+
477+ var k1, k2 uint64
478+ switch len(d.tail) & 15 {
479+ case 15:
480+ k2 ^= uint64(d.tail[14]) << 48
481+ fallthrough
482+ case 14:
483+ k2 ^= uint64(d.tail[13]) << 40
484+ fallthrough
485+ case 13:
486+ k2 ^= uint64(d.tail[12]) << 32
487+ fallthrough
488+ case 12:
489+ k2 ^= uint64(d.tail[11]) << 24
490+ fallthrough
491+ case 11:
492+ k2 ^= uint64(d.tail[10]) << 16
493+ fallthrough
494+ case 10:
495+ k2 ^= uint64(d.tail[9]) << 8
496+ fallthrough
497+ case 9:
498+ k2 ^= uint64(d.tail[8]) << 0
499+
500+ k2 *= c2_128
501+ k2 = (k2 << 33) | (k2 >> 31) // rotl64(k2, 33)
502+ k2 *= c1_128
503+ h2 ^= k2
504+
505+ fallthrough
506+
507+ case 8:
508+ k1 ^= uint64(d.tail[7]) << 56
509+ fallthrough
510+ case 7:
511+ k1 ^= uint64(d.tail[6]) << 48
512+ fallthrough
513+ case 6:
514+ k1 ^= uint64(d.tail[5]) << 40
515+ fallthrough
516+ case 5:
517+ k1 ^= uint64(d.tail[4]) << 32
518+ fallthrough
519+ case 4:
520+ k1 ^= uint64(d.tail[3]) << 24
521+ fallthrough
522+ case 3:
523+ k1 ^= uint64(d.tail[2]) << 16
524+ fallthrough
525+ case 2:
526+ k1 ^= uint64(d.tail[1]) << 8
527+ fallthrough
528+ case 1:
529+ k1 ^= uint64(d.tail[0]) << 0
530+ k1 *= c1_128
531+ k1 = (k1 << 31) | (k1 >> 33) // rotl64(k1, 31)
532+ k1 *= c2_128
533+ h1 ^= k1
534+ }
535+
536+ h1 ^= uint64(d.clen)
537+ h2 ^= uint64(d.clen)
538+
539+ h1 += h2
540+ h2 += h1
541+
542+ h1 = fmix64(h1)
543+ h2 = fmix64(h2)
544+
545+ h1 += h2
546+ h2 += h1
547+
548+ return h1, h2
549+}
550+
551+func fmix64(k uint64) uint64 {
552+ k ^= k >> 33
553+ k *= 0xff51afd7ed558ccd
554+ k ^= k >> 33
555+ k *= 0xc4ceb9fe1a85ec53
556+ k ^= k >> 33
557+ return k
558+}
559+
560+/*
561+func rotl64(x uint64, r byte) uint64 {
562+ return (x << r) | (x >> (64 - r))
563+}
564+*/
565+
566+// Sum128 returns the MurmurHash3 sum of data. It is equivalent to the
567+// following sequence (without the extra burden and the extra allocation):
568+// hasher := New128()
569+// hasher.Write(data)
570+// return hasher.Sum128()
571+func Sum128(data []byte) (h1 uint64, h2 uint64) {
572+ d := &digest128{h1: 1, h2: 1}
573+ d.tail = d.bmix(data)
574+ d.clen = len(data)
575+ return d.Sum128()
576+}
577
578=== added file 'external/murmur3/murmur32.go'
579--- external/murmur3/murmur32.go 1970-01-01 00:00:00 +0000
580+++ external/murmur3/murmur32.go 2014-03-24 15:37:31 +0000
581@@ -0,0 +1,154 @@
582+package murmur3
583+
584+// http://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/hash/Murmur3_32HashFunction.java
585+
586+import (
587+ "hash"
588+ "unsafe"
589+)
590+
591+// Make sure interfaces are correctly implemented.
592+var (
593+ _ hash.Hash = new(digest32)
594+ _ hash.Hash32 = new(digest32)
595+)
596+
597+const (
598+ c1_32 uint32 = 0xcc9e2d51
599+ c2_32 uint32 = 0x1b873593
600+)
601+
602+// digest32 represents a partial evaluation of a 32 bites hash.
603+type digest32 struct {
604+ digest
605+ h1 uint32 // Unfinalized running hash.
606+}
607+
608+func New32() hash.Hash32 {
609+ d := new(digest32)
610+ d.bmixer = d
611+ d.Reset()
612+ return d
613+}
614+
615+func (d *digest32) Size() int { return 4 }
616+
617+func (d *digest32) reset() { d.h1 = 1 }
618+
619+func (d *digest32) Sum(b []byte) []byte {
620+ h := d.h1
621+ return append(b, byte(h>>24), byte(h>>16), byte(h>>8), byte(h))
622+}
623+
624+// Digest as many blocks as possible.
625+func (d *digest32) bmix(p []byte) (tail []byte) {
626+ h1 := d.h1
627+
628+ nblocks := len(p) / 4
629+ for i := 0; i < nblocks; i++ {
630+ k1 := *(*uint32)(unsafe.Pointer(&p[i*4]))
631+
632+ k1 *= c1_32
633+ k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
634+ k1 *= c2_32
635+
636+ h1 ^= k1
637+ h1 = (h1 << 13) | (h1 >> 19) // rotl32(h1, 13)
638+ h1 = h1*5 + 0xe6546b64
639+ }
640+ d.h1 = h1
641+ return p[nblocks*d.Size():]
642+}
643+
644+func (d *digest32) Sum32() (h1 uint32) {
645+
646+ h1 = d.h1
647+
648+ var k1 uint32
649+ switch len(d.tail) & 3 {
650+ case 3:
651+ k1 ^= uint32(d.tail[2]) << 16
652+ fallthrough
653+ case 2:
654+ k1 ^= uint32(d.tail[1]) << 8
655+ fallthrough
656+ case 1:
657+ k1 ^= uint32(d.tail[0])
658+ k1 *= c1_32
659+ k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
660+ k1 *= c2_32
661+ h1 ^= k1
662+ }
663+
664+ h1 ^= uint32(d.clen)
665+
666+ h1 ^= h1 >> 16
667+ h1 *= 0x85ebca6b
668+ h1 ^= h1 >> 13
669+ h1 *= 0xc2b2ae35
670+ h1 ^= h1 >> 16
671+
672+ return h1
673+}
674+
675+/*
676+func rotl32(x uint32, r byte) uint32 {
677+ return (x << r) | (x >> (32 - r))
678+}
679+*/
680+
681+// Sum32 returns the MurmurHash3 sum of data. It is equivalent to the
682+// following sequence (without the extra burden and the extra allocation):
683+// hasher := New32()
684+// hasher.Write(data)
685+// return hasher.Sum32()
686+func Sum32(data []byte) uint32 {
687+
688+ var h1 uint32 = 1
689+
690+ nblocks := len(data) / 4
691+ var p uintptr
692+ if len(data) > 0 {
693+ p = uintptr(unsafe.Pointer(&data[0]))
694+ }
695+ p1 := p + uintptr(4*nblocks)
696+ for ; p < p1; p += 4 {
697+ k1 := *(*uint32)(unsafe.Pointer(p))
698+
699+ k1 *= c1_32
700+ k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
701+ k1 *= c2_32
702+
703+ h1 ^= k1
704+ h1 = (h1 << 13) | (h1 >> 19) // rotl32(h1, 13)
705+ h1 = h1*5 + 0xe6546b64
706+ }
707+
708+ tail := data[nblocks*4:]
709+
710+ var k1 uint32
711+ switch len(tail) & 3 {
712+ case 3:
713+ k1 ^= uint32(tail[2]) << 16
714+ fallthrough
715+ case 2:
716+ k1 ^= uint32(tail[1]) << 8
717+ fallthrough
718+ case 1:
719+ k1 ^= uint32(tail[0])
720+ k1 *= c1_32
721+ k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
722+ k1 *= c2_32
723+ h1 ^= k1
724+ }
725+
726+ h1 ^= uint32(len(data))
727+
728+ h1 ^= h1 >> 16
729+ h1 *= 0x85ebca6b
730+ h1 ^= h1 >> 13
731+ h1 *= 0xc2b2ae35
732+ h1 ^= h1 >> 16
733+
734+ return h1
735+}
736
737=== added file 'external/murmur3/murmur64.go'
738--- external/murmur3/murmur64.go 1970-01-01 00:00:00 +0000
739+++ external/murmur3/murmur64.go 2014-03-24 15:37:31 +0000
740@@ -0,0 +1,45 @@
741+package murmur3
742+
743+import (
744+ "hash"
745+)
746+
747+// Make sure interfaces are correctly implemented.
748+var (
749+ _ hash.Hash = new(digest64)
750+ _ hash.Hash64 = new(digest64)
751+ _ bmixer = new(digest64)
752+)
753+
754+// digest64 is half a digest128.
755+type digest64 digest128
756+
757+func New64() hash.Hash64 {
758+ d := (*digest64)(New128().(*digest128))
759+ return d
760+}
761+
762+func (d *digest64) Sum(b []byte) []byte {
763+ h1 := d.h1
764+ return append(b,
765+ byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32),
766+ byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1))
767+}
768+
769+func (d *digest64) Sum64() uint64 {
770+ h1, _ := (*digest128)(d).Sum128()
771+ return h1
772+}
773+
774+// Sum64 returns the MurmurHash3 sum of data. It is equivalent to the
775+// following sequence (without the extra burden and the extra allocation):
776+// hasher := New64()
777+// hasher.Write(data)
778+// return hasher.Sum64()
779+func Sum64(data []byte) uint64 {
780+ d := &digest128{h1: 1, h2: 1}
781+ d.tail = d.bmix(data)
782+ d.clen = len(data)
783+ h1, _ := d.Sum128()
784+ return h1
785+}
786
787=== added file 'external/murmur3/murmur_test.go'
788--- external/murmur3/murmur_test.go 1970-01-01 00:00:00 +0000
789+++ external/murmur3/murmur_test.go 2014-03-24 15:37:31 +0000
790@@ -0,0 +1,225 @@
791+package murmur3
792+
793+import (
794+ "hash"
795+ "testing"
796+)
797+
798+var data = []struct {
799+ h32 uint32
800+ h64_1 uint64
801+ h64_2 uint64
802+ s string
803+}{
804+ {0x514e28b7, 0x4610abe56eff5cb5, 0x51622daa78f83583, ""},
805+ {0xbb4abcad, 0xa78ddff5adae8d10, 0x128900ef20900135, "hello"},
806+ {0x6f5cb2e9, 0x8b95f808840725c6, 0x1597ed5422bd493b, "hello, world"},
807+ {0xf50e1f30, 0x2a929de9c8f97b2f, 0x56a41d99af43a2db, "19 Jan 2038 at 3:14:07 AM"},
808+ {0x846f6a36, 0xfb3325171f9744da, 0xaaf8b92a5f722952, "The quick brown fox jumps over the lazy dog."},
809+}
810+
811+func TestRef(t *testing.T) {
812+ for _, elem := range data {
813+
814+ var h32 hash.Hash32 = New32()
815+ h32.Write([]byte(elem.s))
816+ if v := h32.Sum32(); v != elem.h32 {
817+ t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h32)
818+ }
819+
820+ if v := Sum32([]byte(elem.s)); v != elem.h32 {
821+ t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h32)
822+ }
823+
824+ var h64 hash.Hash64 = New64()
825+ h64.Write([]byte(elem.s))
826+ if v := h64.Sum64(); v != elem.h64_1 {
827+ t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h64_1)
828+ }
829+
830+ var h128 Hash128 = New128()
831+ h128.Write([]byte(elem.s))
832+ if v1, v2 := h128.Sum128(); v1 != elem.h64_1 || v2 != elem.h64_2 {
833+ t.Errorf("'%s': 0x%x-0x%x (want 0x%x-0x%x)", elem.s, v1, v2, elem.h64_1, elem.h64_2)
834+ }
835+
836+ if v1, v2 := Sum128([]byte(elem.s)); v1 != elem.h64_1 || v2 != elem.h64_2 {
837+ t.Errorf("'%s': 0x%x-0x%x (want 0x%x-0x%x)", elem.s, v1, v2, elem.h64_1, elem.h64_2)
838+ }
839+ }
840+}
841+
842+func TestIncremental(t *testing.T) {
843+ for _, elem := range data {
844+ h32 := New32()
845+ h128 := New128()
846+ for i, j, k := 0, 0, len(elem.s); i < k; i = j {
847+ j = 2*i + 3
848+ if j > k {
849+ j = k
850+ }
851+ s := elem.s[i:j]
852+ print(s + "|")
853+ h32.Write([]byte(s))
854+ h128.Write([]byte(s))
855+ }
856+ println()
857+ if v := h32.Sum32(); v != elem.h32 {
858+ t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h32)
859+ }
860+ if v1, v2 := h128.Sum128(); v1 != elem.h64_1 || v2 != elem.h64_2 {
861+ t.Errorf("'%s': 0x%x-0x%x (want 0x%x-0x%x)", elem.s, v1, v2, elem.h64_1, elem.h64_2)
862+ }
863+ }
864+}
865+
866+//---
867+
868+func bench32(b *testing.B, length int) {
869+ buf := make([]byte, length)
870+ b.SetBytes(int64(length))
871+ b.ResetTimer()
872+ for i := 0; i < b.N; i++ {
873+ Sum32(buf)
874+ }
875+}
876+
877+func Benchmark32_1(b *testing.B) {
878+ bench32(b, 1)
879+}
880+func Benchmark32_2(b *testing.B) {
881+ bench32(b, 2)
882+}
883+func Benchmark32_4(b *testing.B) {
884+ bench32(b, 4)
885+}
886+func Benchmark32_8(b *testing.B) {
887+ bench32(b, 8)
888+}
889+func Benchmark32_16(b *testing.B) {
890+ bench32(b, 16)
891+}
892+func Benchmark32_32(b *testing.B) {
893+ bench32(b, 32)
894+}
895+func Benchmark32_64(b *testing.B) {
896+ bench32(b, 64)
897+}
898+func Benchmark32_128(b *testing.B) {
899+ bench32(b, 128)
900+}
901+func Benchmark32_256(b *testing.B) {
902+ bench32(b, 256)
903+}
904+func Benchmark32_512(b *testing.B) {
905+ bench32(b, 512)
906+}
907+func Benchmark32_1024(b *testing.B) {
908+ bench32(b, 1024)
909+}
910+func Benchmark32_2048(b *testing.B) {
911+ bench32(b, 2048)
912+}
913+func Benchmark32_4096(b *testing.B) {
914+ bench32(b, 4096)
915+}
916+func Benchmark32_8192(b *testing.B) {
917+ bench32(b, 8192)
918+}
919+
920+//---
921+
922+func benchPartial32(b *testing.B, length int) {
923+ buf := make([]byte, length)
924+ b.SetBytes(int64(length))
925+
926+ start := (32 / 8) / 2
927+ chunks := 7
928+ k := length / chunks
929+ tail := (length - start) % k
930+
931+ b.ResetTimer()
932+ for i := 0; i < b.N; i++ {
933+ hasher := New32()
934+ hasher.Write(buf[0:start])
935+
936+ for j := start; j+k <= length; j += k {
937+ hasher.Write(buf[j : j+k])
938+ }
939+
940+ hasher.Write(buf[length-tail:])
941+ hasher.Sum32()
942+ }
943+}
944+
945+func BenchmarkPartial32_8(b *testing.B) {
946+ benchPartial32(b, 8)
947+}
948+func BenchmarkPartial32_16(b *testing.B) {
949+ benchPartial32(b, 16)
950+}
951+func BenchmarkPartial32_32(b *testing.B) {
952+ benchPartial32(b, 32)
953+}
954+func BenchmarkPartial32_64(b *testing.B) {
955+ benchPartial32(b, 64)
956+}
957+func BenchmarkPartial32_128(b *testing.B) {
958+ benchPartial32(b, 128)
959+}
960+
961+//---
962+
963+func bench128(b *testing.B, length int) {
964+ buf := make([]byte, length)
965+ b.SetBytes(int64(length))
966+ b.ResetTimer()
967+ for i := 0; i < b.N; i++ {
968+ Sum128(buf)
969+ }
970+}
971+
972+func Benchmark128_1(b *testing.B) {
973+ bench128(b, 1)
974+}
975+func Benchmark128_2(b *testing.B) {
976+ bench128(b, 2)
977+}
978+func Benchmark128_4(b *testing.B) {
979+ bench128(b, 4)
980+}
981+func Benchmark128_8(b *testing.B) {
982+ bench128(b, 8)
983+}
984+func Benchmark128_16(b *testing.B) {
985+ bench128(b, 16)
986+}
987+func Benchmark128_32(b *testing.B) {
988+ bench128(b, 32)
989+}
990+func Benchmark128_64(b *testing.B) {
991+ bench128(b, 64)
992+}
993+func Benchmark128_128(b *testing.B) {
994+ bench128(b, 128)
995+}
996+func Benchmark128_256(b *testing.B) {
997+ bench128(b, 256)
998+}
999+func Benchmark128_512(b *testing.B) {
1000+ bench128(b, 512)
1001+}
1002+func Benchmark128_1024(b *testing.B) {
1003+ bench128(b, 1024)
1004+}
1005+func Benchmark128_2048(b *testing.B) {
1006+ bench128(b, 2048)
1007+}
1008+func Benchmark128_4096(b *testing.B) {
1009+ bench128(b, 4096)
1010+}
1011+func Benchmark128_8192(b *testing.B) {
1012+ bench128(b, 8192)
1013+}
1014+
1015+//---

Subscribers

People subscribed via source and target branches