Skip to content

Commit a953991

Browse files
authored
Use openarray of bytes in md5 (#19307)
* Use openarray of bytes in md5 * fix CI * cleanup * use noSideEffect for bootstrapping * fix CI again * actually fix CI by checking if it works * this is getting ridiculous * put old md5 version in compiler, remove vmop
1 parent 342b74e commit a953991

File tree

9 files changed

+413
-51
lines changed

9 files changed

+413
-51
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
filename argument for more informative errors.
2424
- Module `colors` expanded with missing colors from the CSS color standard.
2525
- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)).
26+
- `md5` now works at compile time and in JavaScript.
2627

2728
## `std/smtp`
2829

compiler/md5_old.nim

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
#
2+
#
3+
# Nim's Runtime Library
4+
# (c) Copyright 2010 Andreas Rumpf
5+
#
6+
# See the file "copying.txt", included in this
7+
# distribution, for details about the copyright.
8+
#
9+
10+
# `std/md5` without VM and JavaScript support, to circumvent a bug with
11+
# openarrays on Nim < 1.4.
12+
13+
when defined(nimHasStyleChecks):
14+
{.push styleChecks: off.}
15+
16+
type
17+
MD5State = array[0..3, uint32]
18+
MD5Block = array[0..15, uint32]
19+
MD5CBits = array[0..7, uint8]
20+
MD5Digest* = array[0..15, uint8]
21+
## MD5 checksum of a string, obtained with the `toMD5 proc <#toMD5,string>`_.
22+
MD5Buffer = array[0..63, uint8]
23+
MD5Context* {.final.} = object
24+
state: MD5State
25+
count: array[0..1, uint32]
26+
buffer: MD5Buffer
27+
28+
const
29+
padding: array[0..63, uint8] = [
30+
0x80'u8, 0, 0, 0, 0, 0, 0, 0,
31+
0, 0, 0, 0, 0, 0, 0, 0,
32+
0, 0, 0, 0, 0, 0, 0, 0,
33+
0, 0, 0, 0, 0, 0, 0, 0,
34+
0, 0, 0, 0, 0, 0, 0, 0,
35+
0, 0, 0, 0, 0, 0, 0, 0,
36+
0, 0, 0, 0, 0, 0, 0, 0,
37+
0, 0, 0, 0, 0, 0, 0, 0
38+
]
39+
40+
proc F(x, y, z: uint32): uint32 {.inline.} =
41+
result = (x and y) or ((not x) and z)
42+
43+
proc G(x, y, z: uint32): uint32 {.inline.} =
44+
result = (x and z) or (y and (not z))
45+
46+
proc H(x, y, z: uint32): uint32 {.inline.} =
47+
result = x xor y xor z
48+
49+
proc I(x, y, z: uint32): uint32 {.inline.} =
50+
result = y xor (x or (not z))
51+
52+
proc rot(x: var uint32, n: uint8) {.inline.} =
53+
x = (x shl n) or (x shr (32'u32 - n))
54+
55+
proc FF(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
56+
a = a + F(b, c, d) + x + ac
57+
rot(a, s)
58+
a = a + b
59+
60+
proc GG(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
61+
a = a + G(b, c, d) + x + ac
62+
rot(a, s)
63+
a = a + b
64+
65+
proc HH(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
66+
a = a + H(b, c, d) + x + ac
67+
rot(a, s)
68+
a = a + b
69+
70+
proc II(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
71+
a = a + I(b, c, d) + x + ac
72+
rot(a, s)
73+
a = a + b
74+
75+
proc encode(dest: var MD5Block, src: openArray[uint8]) =
76+
var j = 0
77+
for i in 0..high(dest):
78+
dest[i] = uint32(ord(src[j])) or
79+
uint32(ord(src[j+1])) shl 8 or
80+
uint32(ord(src[j+2])) shl 16 or
81+
uint32(ord(src[j+3])) shl 24
82+
inc(j, 4)
83+
84+
proc decode(dest: var openArray[uint8], src: openArray[uint32]) =
85+
var i = 0
86+
for j in 0..high(src):
87+
dest[i] = uint8(src[j] and 0xff'u32)
88+
dest[i+1] = uint8(src[j] shr 8 and 0xff'u32)
89+
dest[i+2] = uint8(src[j] shr 16 and 0xff'u32)
90+
dest[i+3] = uint8(src[j] shr 24 and 0xff'u32)
91+
inc(i, 4)
92+
93+
template slice(s: string | cstring, a, b): openArray[uint8] =
94+
s.toOpenArrayByte(a, b)
95+
96+
template slice(s: openArray[uint8], a, b): openArray[uint8] =
97+
s.toOpenArray(a, b)
98+
99+
proc transform(buffer: openArray[uint8], state: var MD5State) =
100+
var
101+
myBlock: MD5Block
102+
encode(myBlock, buffer)
103+
var a = state[0]
104+
var b = state[1]
105+
var c = state[2]
106+
var d = state[3]
107+
FF(a, b, c, d, myBlock[0], 7'u8, 0xD76AA478'u32)
108+
FF(d, a, b, c, myBlock[1], 12'u8, 0xE8C7B756'u32)
109+
FF(c, d, a, b, myBlock[2], 17'u8, 0x242070DB'u32)
110+
FF(b, c, d, a, myBlock[3], 22'u8, 0xC1BDCEEE'u32)
111+
FF(a, b, c, d, myBlock[4], 7'u8, 0xF57C0FAF'u32)
112+
FF(d, a, b, c, myBlock[5], 12'u8, 0x4787C62A'u32)
113+
FF(c, d, a, b, myBlock[6], 17'u8, 0xA8304613'u32)
114+
FF(b, c, d, a, myBlock[7], 22'u8, 0xFD469501'u32)
115+
FF(a, b, c, d, myBlock[8], 7'u8, 0x698098D8'u32)
116+
FF(d, a, b, c, myBlock[9], 12'u8, 0x8B44F7AF'u32)
117+
FF(c, d, a, b, myBlock[10], 17'u8, 0xFFFF5BB1'u32)
118+
FF(b, c, d, a, myBlock[11], 22'u8, 0x895CD7BE'u32)
119+
FF(a, b, c, d, myBlock[12], 7'u8, 0x6B901122'u32)
120+
FF(d, a, b, c, myBlock[13], 12'u8, 0xFD987193'u32)
121+
FF(c, d, a, b, myBlock[14], 17'u8, 0xA679438E'u32)
122+
FF(b, c, d, a, myBlock[15], 22'u8, 0x49B40821'u32)
123+
GG(a, b, c, d, myBlock[1], 5'u8, 0xF61E2562'u32)
124+
GG(d, a, b, c, myBlock[6], 9'u8, 0xC040B340'u32)
125+
GG(c, d, a, b, myBlock[11], 14'u8, 0x265E5A51'u32)
126+
GG(b, c, d, a, myBlock[0], 20'u8, 0xE9B6C7AA'u32)
127+
GG(a, b, c, d, myBlock[5], 5'u8, 0xD62F105D'u32)
128+
GG(d, a, b, c, myBlock[10], 9'u8, 0x02441453'u32)
129+
GG(c, d, a, b, myBlock[15], 14'u8, 0xD8A1E681'u32)
130+
GG(b, c, d, a, myBlock[4], 20'u8, 0xE7D3FBC8'u32)
131+
GG(a, b, c, d, myBlock[9], 5'u8, 0x21E1CDE6'u32)
132+
GG(d, a, b, c, myBlock[14], 9'u8, 0xC33707D6'u32)
133+
GG(c, d, a, b, myBlock[3], 14'u8, 0xF4D50D87'u32)
134+
GG(b, c, d, a, myBlock[8], 20'u8, 0x455A14ED'u32)
135+
GG(a, b, c, d, myBlock[13], 5'u8, 0xA9E3E905'u32)
136+
GG(d, a, b, c, myBlock[2], 9'u8, 0xFCEFA3F8'u32)
137+
GG(c, d, a, b, myBlock[7], 14'u8, 0x676F02D9'u32)
138+
GG(b, c, d, a, myBlock[12], 20'u8, 0x8D2A4C8A'u32)
139+
HH(a, b, c, d, myBlock[5], 4'u8, 0xFFFA3942'u32)
140+
HH(d, a, b, c, myBlock[8], 11'u8, 0x8771F681'u32)
141+
HH(c, d, a, b, myBlock[11], 16'u8, 0x6D9D6122'u32)
142+
HH(b, c, d, a, myBlock[14], 23'u8, 0xFDE5380C'u32)
143+
HH(a, b, c, d, myBlock[1], 4'u8, 0xA4BEEA44'u32)
144+
HH(d, a, b, c, myBlock[4], 11'u8, 0x4BDECFA9'u32)
145+
HH(c, d, a, b, myBlock[7], 16'u8, 0xF6BB4B60'u32)
146+
HH(b, c, d, a, myBlock[10], 23'u8, 0xBEBFBC70'u32)
147+
HH(a, b, c, d, myBlock[13], 4'u8, 0x289B7EC6'u32)
148+
HH(d, a, b, c, myBlock[0], 11'u8, 0xEAA127FA'u32)
149+
HH(c, d, a, b, myBlock[3], 16'u8, 0xD4EF3085'u32)
150+
HH(b, c, d, a, myBlock[6], 23'u8, 0x04881D05'u32)
151+
HH(a, b, c, d, myBlock[9], 4'u8, 0xD9D4D039'u32)
152+
HH(d, a, b, c, myBlock[12], 11'u8, 0xE6DB99E5'u32)
153+
HH(c, d, a, b, myBlock[15], 16'u8, 0x1FA27CF8'u32)
154+
HH(b, c, d, a, myBlock[2], 23'u8, 0xC4AC5665'u32)
155+
II(a, b, c, d, myBlock[0], 6'u8, 0xF4292244'u32)
156+
II(d, a, b, c, myBlock[7], 10'u8, 0x432AFF97'u32)
157+
II(c, d, a, b, myBlock[14], 15'u8, 0xAB9423A7'u32)
158+
II(b, c, d, a, myBlock[5], 21'u8, 0xFC93A039'u32)
159+
II(a, b, c, d, myBlock[12], 6'u8, 0x655B59C3'u32)
160+
II(d, a, b, c, myBlock[3], 10'u8, 0x8F0CCC92'u32)
161+
II(c, d, a, b, myBlock[10], 15'u8, 0xFFEFF47D'u32)
162+
II(b, c, d, a, myBlock[1], 21'u8, 0x85845DD1'u32)
163+
II(a, b, c, d, myBlock[8], 6'u8, 0x6FA87E4F'u32)
164+
II(d, a, b, c, myBlock[15], 10'u8, 0xFE2CE6E0'u32)
165+
II(c, d, a, b, myBlock[6], 15'u8, 0xA3014314'u32)
166+
II(b, c, d, a, myBlock[13], 21'u8, 0x4E0811A1'u32)
167+
II(a, b, c, d, myBlock[4], 6'u8, 0xF7537E82'u32)
168+
II(d, a, b, c, myBlock[11], 10'u8, 0xBD3AF235'u32)
169+
II(c, d, a, b, myBlock[2], 15'u8, 0x2AD7D2BB'u32)
170+
II(b, c, d, a, myBlock[9], 21'u8, 0xEB86D391'u32)
171+
state[0] = state[0] + a
172+
state[1] = state[1] + b
173+
state[2] = state[2] + c
174+
state[3] = state[3] + d
175+
176+
proc md5Init*(c: var MD5Context) {.raises: [], tags: [], gcsafe.}
177+
proc md5Update*(c: var MD5Context, input: openArray[uint8]) {.raises: [],
178+
tags: [], gcsafe.}
179+
proc md5Final*(c: var MD5Context, digest: var MD5Digest) {.raises: [], tags: [], gcsafe.}
180+
181+
proc md5Update*(c: var MD5Context, input: cstring, len: int) {.raises: [],
182+
tags: [], gcsafe.} =
183+
## Updates the `MD5Context` with the `input` data of length `len`.
184+
##
185+
## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this
186+
## function explicitly.
187+
md5Update(c, input.slice(0, len - 1))
188+
189+
190+
proc toMD5*(s: string): MD5Digest =
191+
## Computes the `MD5Digest` value for a string `s`.
192+
##
193+
## **See also:**
194+
## * `getMD5 proc <#getMD5,string>`_ which returns a string representation
195+
## of the `MD5Digest`
196+
## * `$ proc <#$,MD5Digest>`_ for converting MD5Digest to string
197+
runnableExamples:
198+
assert $toMD5("abc") == "900150983cd24fb0d6963f7d28e17f72"
199+
200+
var c: MD5Context
201+
md5Init(c)
202+
md5Update(c, s.slice(0, s.len - 1))
203+
md5Final(c, result)
204+
205+
proc `$`*(d: MD5Digest): string =
206+
## Converts a `MD5Digest` value into its string representation.
207+
const digits = "0123456789abcdef"
208+
result = ""
209+
for i in 0..15:
210+
add(result, digits[(d[i].int shr 4) and 0xF])
211+
add(result, digits[d[i].int and 0xF])
212+
213+
proc getMD5*(s: string): string =
214+
## Computes an MD5 value of `s` and returns its string representation.
215+
##
216+
## **See also:**
217+
## * `toMD5 proc <#toMD5,string>`_ which returns the `MD5Digest` of a string
218+
runnableExamples:
219+
assert getMD5("abc") == "900150983cd24fb0d6963f7d28e17f72"
220+
221+
var
222+
c: MD5Context
223+
d: MD5Digest
224+
md5Init(c)
225+
md5Update(c, s.slice(0, s.len - 1))
226+
md5Final(c, d)
227+
result = $d
228+
229+
proc `==`*(D1, D2: MD5Digest): bool =
230+
## Checks if two `MD5Digest` values are identical.
231+
for i in 0..15:
232+
if D1[i] != D2[i]: return false
233+
return true
234+
235+
236+
proc clearBuffer(c: var MD5Context) {.inline.} =
237+
zeroMem(addr(c.buffer), sizeof(MD5Buffer))
238+
239+
proc md5Init*(c: var MD5Context) =
240+
## Initializes an `MD5Context`.
241+
##
242+
## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this
243+
## function explicitly.
244+
c.state[0] = 0x67452301'u32
245+
c.state[1] = 0xEFCDAB89'u32
246+
c.state[2] = 0x98BADCFE'u32
247+
c.state[3] = 0x10325476'u32
248+
c.count[0] = 0'u32
249+
c.count[1] = 0'u32
250+
clearBuffer(c)
251+
252+
proc writeBuffer(c: var MD5Context, index: int,
253+
input: openArray[uint8], inputIndex, len: int) {.inline.} =
254+
copyMem(addr(c.buffer[index]), unsafeAddr(input[inputIndex]), len)
255+
256+
proc md5Update*(c: var MD5Context, input: openArray[uint8]) =
257+
## Updates the `MD5Context` with the `input` data.
258+
##
259+
## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this
260+
## function explicitly.
261+
var Index = int((c.count[0] shr 3) and 0x3F)
262+
c.count[0] = c.count[0] + (uint32(input.len) shl 3)
263+
if c.count[0] < (uint32(input.len) shl 3): c.count[1] = c.count[1] + 1'u32
264+
c.count[1] = c.count[1] + (uint32(input.len) shr 29)
265+
var PartLen = 64 - Index
266+
if input.len >= PartLen:
267+
writeBuffer(c, Index, input, 0, PartLen)
268+
transform(c.buffer, c.state)
269+
var i = PartLen
270+
while i + 63 < input.len:
271+
transform(input.slice(i, i + 63), c.state)
272+
inc(i, 64)
273+
if i < input.len:
274+
writeBuffer(c, 0, input, i, input.len - i)
275+
elif input.len > 0:
276+
writeBuffer(c, Index, input, 0, input.len)
277+
278+
proc md5Final*(c: var MD5Context, digest: var MD5Digest) =
279+
## Finishes the `MD5Context` and stores the result in `digest`.
280+
##
281+
## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this
282+
## function explicitly.
283+
var
284+
Bits: MD5CBits
285+
PadLen: int
286+
decode(Bits, c.count)
287+
var Index = int((c.count[0] shr 3) and 0x3F)
288+
if Index < 56: PadLen = 56 - Index
289+
else: PadLen = 120 - Index
290+
md5Update(c, padding.slice(0, PadLen - 1))
291+
md5Update(c, Bits)
292+
decode(digest, c.state)
293+
clearBuffer(c)
294+
295+
296+
when defined(nimHasStyleChecks):
297+
{.pop.} #{.push styleChecks: off.}

compiler/modulegraphs.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
## represents a complete Nim project. Single modules can either be kept in RAM
1212
## or stored in a rod-file.
1313

14-
import intsets, tables, hashes, md5
14+
import intsets, tables, hashes, md5_old
1515
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils
1616
import ic / [packed_ast, ic]
1717

compiler/sighashes.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
## Computes hash values for routine (proc, method etc) signatures.
1111

12-
import ast, tables, ropes, md5, modulegraphs
12+
import ast, tables, ropes, md5_old, modulegraphs
1313
from hashes import Hash
1414
import types
1515

compiler/vmops.nim

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ when declared(math.signbit):
2525
from std/os import getEnv, existsEnv, delEnv, putEnv, envPairs,
2626
dirExists, fileExists, walkDir, getAppFilename, raiseOSError, osLastError
2727

28-
from std/md5 import getMD5
2928
from std/times import cpuTime
3029
from std/hashes import hash
3130
from std/osproc import nil
@@ -53,9 +52,6 @@ template ioop(op) {.dirty.} =
5352
template macrosop(op) {.dirty.} =
5453
registerCallback(c, "stdlib.macros." & astToStr(op), `op Wrapper`)
5554

56-
template md5op(op) {.dirty.} =
57-
registerCallback(c, "stdlib.md5." & astToStr(op), `op Wrapper`)
58-
5955
template wrap1f_math(op) {.dirty.} =
6056
proc `op Wrapper`(a: VmArgs) {.nimcall.} =
6157
doAssert a.numArgs == 1
@@ -212,8 +208,6 @@ proc registerAdditionalOps*(c: PCtx) =
212208
of 2: setResult(a, round(getFloat(a, 0), getInt(a, 1).int))
213209
else: doAssert false, $n
214210

215-
wrap1s(getMD5, md5op)
216-
217211
proc `mod Wrapper`(a: VmArgs) {.nimcall.} =
218212
setResult(a, `mod`(getFloat(a, 0), getFloat(a, 1)))
219213
registerCallback(c, "stdlib.math.mod", `mod Wrapper`)

0 commit comments

Comments
 (0)