/
FileHashing.chpl
171 lines (149 loc) · 4.3 KB
/
FileHashing.chpl
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
module FileHashing {
/* SHA256Hash is a record storing a SHA256 hash value.
It supports comparison and writeln.
*/
record SHA256Hash : hashable, writeSerializable {
/* The actual hash value */
var hashVal: 8*uint(32);
/* Help `writeln` and other calls output SHA256Hash value
in a good format.
*/
proc serialize(writer, ref serializer) throws {
for component in hashVal {
var s = try! "%08xu".format(component);
writer.write(s);
}
}
/* How to initialize an empty SHA256Hash */
proc init() {
// compiler adds initialization of hash to 0s
}
/* How to initialize a SHA256Hash from another SHA256Hash */
proc init=(from: SHA256Hash) {
this.hashVal = from.hashVal;
}
/* How to initialize a SHA256Hash from a hash tuple */
proc init(hashVal: 8*uint(32)) {
this.hashVal = hashVal;
}
/* How to compute a hash a SHA256Hash */
proc hash() {
return hashVal.hash();
}
}
/* Called when assigning between SHA256Hash values */
operator SHA256Hash.=(ref lhs: SHA256Hash, rhs: SHA256Hash) {
lhs.hashVal = rhs.hashVal;
}
/* Helps to implement comparisons between SHA256Hash values.
Returns -1 if a < b, 1 if a > b, or 0 if a == b.
*/
proc compare(a: SHA256Hash, b: SHA256Hash): int {
for i in 0..7 {
var aa = a.hashVal[i];
var bb = b.hashVal[i];
if aa < bb {
return -1;
}
if aa > bb {
return 1;
}
}
return 0;
}
operator SHA256Hash.<(a: SHA256Hash, b: SHA256Hash) {
return compare(a, b) < 0;
}
operator SHA256Hash.<=(a: SHA256Hash, b: SHA256Hash) {
return compare(a, b) <= 0;
}
operator SHA256Hash.==(a: SHA256Hash, b: SHA256Hash) {
return compare(a, b) == 0;
}
operator SHA256Hash.!=(a: SHA256Hash, b: SHA256Hash) {
return compare(a, b) != 0;
}
operator SHA256Hash.>=(a: SHA256Hash, b: SHA256Hash) {
return compare(a, b) >= 0;
}
operator SHA256Hash.>(a: SHA256Hash, b: SHA256Hash) {
return compare(a, b) > 0;
}
/*
Returns the SHA256Hash for the file stored at `path`.
May throw an error if the file could not be opened, for example.
*/
proc computeFileHash(path: string): SHA256Hash throws {
use IO;
use SHA256Implementation;
var f = open(path, ioMode.r);
var len = f.size;
var r = f.reader(deserializer=new binaryDeserializer(endianness.big), locking=false,
region=0..#len);
var msg:16*uint(32); // aka 64 bytes
var offset = 0;
var state:SHA256State;
// Read as many full blocks as we can
// but don't ever read the last block in this loop
// since we need to pass that to state.lastblock
while offset+64 < len {
r.read(msg);
state.fullblock(msg);
offset += 64;
}
// clear msg before last block, so unused data are zeros
for i in 0..15 {
msg[i] = 0;
}
var nbits:uint = 0;
var msgi = 0;
// Now handle the last 4-byte words
while offset+4 <= len {
r.read(msg[msgi]);
msgi += 1;
offset += 4;
nbits += 32;
}
if offset < len {
var lastword:uint(32);
var byte:uint(8);
var bytei = 0;
// Now handle the last 1-byte portions
while offset < len {
r.read(byte);
lastword <<= 8;
lastword |= byte;
bytei += 1;
offset += 1;
nbits += 8;
}
// Shift left so the data is in the high order bits of lastword.
while bytei != 4 {
lastword <<= 8;
bytei += 1;
}
msg[msgi] = lastword;
}
var hashVal:8*uint(32) = state.lastblock(msg, nbits);
return new SHA256Hash(hashVal);
}
/*
Given a path argument, computes the realPath (that
is the path after symbolic links are processed),
and then computes a relative version of that to the
current directory. Returns the result.
*/
proc relativeRealPath(path: string): string throws {
use Path only ;
var currentDirectory = Path.realPath(".");
var fullPath = Path.realPath(path);
if !currentDirectory.endsWith("/") {
currentDirectory += "/";
}
// If fullPath starts with currentDirectory, remove it
if fullPath.startsWith(currentDirectory) {
fullPath = fullPath[currentDirectory.size..];
}
return fullPath;
}
}