1// Copyright 2012 The Go Authors.  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// +build ignore
6
7// This program generates md5block.go
8// Invoke as
9//
10//	go run gen.go [-full] |gofmt >md5block.go
11//
12// The -full flag causes the generated code to do a full
13// (16x) unrolling instead of a 4x unrolling.
14
15package main
16
17import (
18	"flag"
19	"log"
20	"os"
21	"strings"
22	"text/template"
23)
24
25func main() {
26	flag.Parse()
27
28	t := template.Must(template.New("main").Funcs(funcs).Parse(program))
29	if err := t.Execute(os.Stdout, data); err != nil {
30		log.Fatal(err)
31	}
32}
33
34type Data struct {
35	a, b, c, d string
36	Shift1     []int
37	Shift2     []int
38	Shift3     []int
39	Shift4     []int
40	Table1     []uint32
41	Table2     []uint32
42	Table3     []uint32
43	Table4     []uint32
44	Full       bool
45}
46
47var funcs = template.FuncMap{
48	"dup":     dup,
49	"relabel": relabel,
50	"rotate":  rotate,
51}
52
53func dup(count int, x []int) []int {
54	var out []int
55	for i := 0; i < count; i++ {
56		out = append(out, x...)
57	}
58	return out
59}
60
61func relabel(s string) string {
62	return strings.NewReplacer("a", data.a, "b", data.b, "c", data.c, "d", data.d).Replace(s)
63}
64
65func rotate() string {
66	data.a, data.b, data.c, data.d = data.d, data.a, data.b, data.c
67	return "" // no output
68}
69
70func init() {
71	flag.BoolVar(&data.Full, "full", false, "complete unrolling")
72}
73
74var data = Data{
75	a:      "a",
76	b:      "b",
77	c:      "c",
78	d:      "d",
79	Shift1: []int{7, 12, 17, 22},
80	Shift2: []int{5, 9, 14, 20},
81	Shift3: []int{4, 11, 16, 23},
82	Shift4: []int{6, 10, 15, 21},
83
84	// table[i] = int((1<<32) * abs(sin(i+1 radians))).
85	Table1: []uint32{
86		// round 1
87		0xd76aa478,
88		0xe8c7b756,
89		0x242070db,
90		0xc1bdceee,
91		0xf57c0faf,
92		0x4787c62a,
93		0xa8304613,
94		0xfd469501,
95		0x698098d8,
96		0x8b44f7af,
97		0xffff5bb1,
98		0x895cd7be,
99		0x6b901122,
100		0xfd987193,
101		0xa679438e,
102		0x49b40821,
103	},
104	Table2: []uint32{
105		// round 2
106		0xf61e2562,
107		0xc040b340,
108		0x265e5a51,
109		0xe9b6c7aa,
110		0xd62f105d,
111		0x2441453,
112		0xd8a1e681,
113		0xe7d3fbc8,
114		0x21e1cde6,
115		0xc33707d6,
116		0xf4d50d87,
117		0x455a14ed,
118		0xa9e3e905,
119		0xfcefa3f8,
120		0x676f02d9,
121		0x8d2a4c8a,
122	},
123	Table3: []uint32{
124		// round3
125		0xfffa3942,
126		0x8771f681,
127		0x6d9d6122,
128		0xfde5380c,
129		0xa4beea44,
130		0x4bdecfa9,
131		0xf6bb4b60,
132		0xbebfbc70,
133		0x289b7ec6,
134		0xeaa127fa,
135		0xd4ef3085,
136		0x4881d05,
137		0xd9d4d039,
138		0xe6db99e5,
139		0x1fa27cf8,
140		0xc4ac5665,
141	},
142	Table4: []uint32{
143		// round 4
144		0xf4292244,
145		0x432aff97,
146		0xab9423a7,
147		0xfc93a039,
148		0x655b59c3,
149		0x8f0ccc92,
150		0xffeff47d,
151		0x85845dd1,
152		0x6fa87e4f,
153		0xfe2ce6e0,
154		0xa3014314,
155		0x4e0811a1,
156		0xf7537e82,
157		0xbd3af235,
158		0x2ad7d2bb,
159		0xeb86d391,
160	},
161}
162
163var program = `
164// DO NOT EDIT.
165// Generate with: go run gen.go{{if .Full}} -full{{end}} | gofmt >md5block.go
166
167// +build !amd64
168
169package md5
170
171import (
172	"unsafe"
173	"runtime"
174)
175
176{{if not .Full}}
177	var t1 = [...]uint32{
178	{{range .Table1}}{{printf "\t%#x,\n" .}}{{end}}
179	}
180
181	var t2 = [...]uint32{
182	{{range .Table2}}{{printf "\t%#x,\n" .}}{{end}}
183	}
184
185	var t3 = [...]uint32{
186	{{range .Table3}}{{printf "\t%#x,\n" .}}{{end}}
187	}
188
189	var t4 = [...]uint32{
190	{{range .Table4}}{{printf "\t%#x,\n" .}}{{end}}
191	}
192{{end}}
193
194const x86 = runtime.GOARCH == "amd64" || runtime.GOARCH == "386"
195
196var littleEndian bool
197
198func init() {
199	x := uint32(0x04030201)
200	y := [4]byte{0x1, 0x2, 0x3, 0x4}
201	littleEndian = *(*[4]byte)(unsafe.Pointer(&x)) == y
202}
203
204func block(dig *digest, p []byte) {
205	a := dig.s[0]
206	b := dig.s[1]
207	c := dig.s[2]
208	d := dig.s[3]
209	var X *[16]uint32
210	var xbuf [16]uint32
211	for len(p) >= chunk {
212		aa, bb, cc, dd := a, b, c, d
213
214		// This is a constant condition - it is not evaluated on each iteration.
215		if x86 {
216			// MD5 was designed so that x86 processors can just iterate
217			// over the block data directly as uint32s, and we generate
218			// less code and run 1.3x faster if we take advantage of that.
219			// My apologies.
220			X = (*[16]uint32)(unsafe.Pointer(&p[0]))
221		} else if littleEndian && uintptr(unsafe.Pointer(&p[0]))&(unsafe.Alignof(uint32(0))-1) == 0 {
222			X = (*[16]uint32)(unsafe.Pointer(&p[0]))
223		} else {
224			X = &xbuf
225			j := 0
226			for i := 0; i < 16; i++ {
227				X[i&15] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24
228				j += 4
229			}
230		}
231
232		{{if .Full}}
233			// Round 1.
234			{{range $i, $s := dup 4 .Shift1}}
235				{{index $.Table1 $i | printf "a += (((c^d)&b)^d) + X[%d] + %d" $i | relabel}}
236				{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
237				{{rotate}}
238			{{end}}
239
240			// Round 2.
241			{{range $i, $s := dup 4 .Shift2}}
242				{{index $.Table2 $i | printf "a += (((b^c)&d)^c) + X[(1+5*%d)&15] + %d" $i | relabel}}
243				{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
244				{{rotate}}
245			{{end}}
246
247			// Round 3.
248			{{range $i, $s := dup 4 .Shift3}}
249				{{index $.Table3 $i | printf "a += (b^c^d) + X[(5+3*%d)&15] + %d" $i | relabel}}
250				{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
251				{{rotate}}
252			{{end}}
253
254			// Round 4.
255			{{range $i, $s := dup 4 .Shift4}}
256				{{index $.Table4 $i | printf "a += (c^(b|^d)) + X[(7*%d)&15] + %d" $i | relabel}}
257				{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
258				{{rotate}}
259			{{end}}
260		{{else}}
261			// Round 1.
262			for i := uint(0); i < 16; {
263				{{range $s := .Shift1}}
264					{{printf "a += (((c^d)&b)^d) + X[i&15] + t1[i&15]" | relabel}}
265					{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
266					i++
267					{{rotate}}
268				{{end}}
269			}
270
271			// Round 2.
272			for i := uint(0); i < 16; {
273				{{range $s := .Shift2}}
274					{{printf "a += (((b^c)&d)^c) + X[(1+5*i)&15] + t2[i&15]" | relabel}}
275					{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
276					i++
277					{{rotate}}
278				{{end}}
279			}
280
281			// Round 3.
282			for i := uint(0); i < 16; {
283				{{range $s := .Shift3}}
284					{{printf "a += (b^c^d) + X[(5+3*i)&15] + t3[i&15]" | relabel}}
285					{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
286					i++
287					{{rotate}}
288				{{end}}
289			}
290
291			// Round 4.
292			for i := uint(0); i < 16; {
293				{{range $s := .Shift4}}
294					{{printf "a += (c^(b|^d)) + X[(7*i)&15] + t4[i&15]" | relabel}}
295					{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
296					i++
297					{{rotate}}
298				{{end}}
299			}
300		{{end}}
301
302		a += aa
303		b += bb
304		c += cc
305		d += dd
306
307		p = p[chunk:]
308	}
309
310	dig.s[0] = a
311	dig.s[1] = b
312	dig.s[2] = c
313	dig.s[3] = d
314}
315`
316