Skip to content

Commit 04b1795

Browse files
committed
[feature] Facilities for working with Hashes
1 parent 1e8a2d1 commit 04b1795

File tree

7 files changed

+538
-0
lines changed

7 files changed

+538
-0
lines changed

src/org/exist/util/FileUtils.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,13 @@
2424

2525
import com.evolvedbinary.j8fu.Either;
2626
import com.evolvedbinary.j8fu.function.FunctionE;
27+
import org.exist.util.crypto.digest.DigestOutputStream;
28+
import org.exist.util.crypto.digest.StreamableDigest;
2729

2830
import java.io.File;
2931
import java.io.IOException;
32+
import java.io.InputStream;
33+
import java.io.OutputStream;
3034
import java.nio.file.*;
3135
import java.nio.file.attribute.BasicFileAttributes;
3236
import java.nio.file.attribute.FileTime;
@@ -35,6 +39,10 @@
3539
import java.util.stream.Collectors;
3640
import java.util.stream.Stream;
3741

42+
import static java.nio.file.StandardOpenOption.READ;
43+
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
44+
import static java.nio.file.StandardOpenOption.WRITE;
45+
3846
/**
3947
* @author Adam Retter <[email protected]>
4048
* @author alex
@@ -398,4 +406,77 @@ public static String addPaths(final String path1, final String path2) {
398406
}
399407
}
400408

409+
/**
410+
* Copy a file whilst generating a digest of the bytes that are being written.
411+
*
412+
* Destination file will be overwritten.
413+
*
414+
* @param src the source file.
415+
* @param dst the destination file
416+
* @param streamableDigest the digest
417+
*/
418+
public static void copyWithDigest(final Path src, final Path dst, final StreamableDigest streamableDigest) throws IOException {
419+
copyWithDigest(src, dst, streamableDigest, WRITE, TRUNCATE_EXISTING);
420+
}
421+
422+
/**
423+
* Copy a file whilst generating a digest of the bytes that are being written.
424+
*
425+
* @param src the source file.
426+
* @param dst the destination file
427+
* @param streamableDigest the digest
428+
*/
429+
public static void copyWithDigest(final Path src, final Path dst, final StreamableDigest streamableDigest, final OpenOption... dstOptions) throws IOException {
430+
try (final InputStream is = Files.newInputStream(src, READ)) {
431+
copyWithDigest(is, dst, streamableDigest);
432+
}
433+
}
434+
435+
/**
436+
* Copy data to a file whilst generating a digest of the bytes that are being written.
437+
*
438+
* Destination file will be overwritten.
439+
*
440+
* @param is the source data.
441+
* @param dst the destination file
442+
* @param streamableDigest the digest
443+
*/
444+
public static void copyWithDigest(final InputStream is, final Path dst, final StreamableDigest streamableDigest) throws IOException {
445+
copyWithDigest(is, dst, streamableDigest, WRITE, TRUNCATE_EXISTING);
446+
}
447+
448+
/**
449+
* Copy data to a file whilst generating a digest of the bytes that are being written.
450+
*
451+
* @param is the source data.
452+
* @param dst the destination file
453+
* @param streamableDigest the digest
454+
*/
455+
public static void copyWithDigest(final InputStream is, final Path dst, final StreamableDigest streamableDigest, final OpenOption... dstOptions) throws IOException {
456+
try (final OutputStream os = new DigestOutputStream(Files.newOutputStream(dst, dstOptions), streamableDigest)) {
457+
458+
final byte[] buf = new byte[8192];
459+
int read;
460+
while ((read = is.read(buf)) != -1) {
461+
os.write(buf, 0, read);
462+
}
463+
}
464+
}
465+
466+
/**
467+
* Calculates the digest for a file.
468+
*
469+
* @param path the file to calculate the digest for.
470+
* @param streamableDigest the digest.
471+
*/
472+
public static void digest(final Path path, final StreamableDigest streamableDigest) throws IOException {
473+
try (final InputStream is = Files.newInputStream(path, READ)) {
474+
final byte[] buf = new byte[8192];
475+
476+
int read;
477+
while ((read = is.read(buf)) != -1) {
478+
streamableDigest.update(buf, 0, read);
479+
}
480+
}
481+
}
401482
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* eXist Open Source Native XML Database
3+
* Copyright (C) 2001-2018 The eXist Project
4+
* http://exist-db.org
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public License
8+
* as published by the Free Software Foundation; either version 2
9+
* of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public
17+
* License along with this library; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
*/
20+
21+
package org.exist.util.crypto.digest;
22+
23+
import org.bouncycastle.crypto.ExtendedDigest;
24+
import org.bouncycastle.crypto.digests.Blake2bDigest;
25+
26+
import java.util.Arrays;
27+
28+
import static org.exist.util.crypto.digest.DigestType.BLAKE_256;
29+
30+
/**
31+
* Implementation of Blake2b 256 bit streamable digest.
32+
*
33+
* @author Adam Retter <[email protected]>
34+
*/
35+
public class Blake256StreamableDigest implements StreamableDigest {
36+
private final ExtendedDigest ed = new Blake2bDigest(BLAKE_256.getDigestLength());
37+
38+
@Override
39+
public void update(final byte b) {
40+
ed.update(b);
41+
}
42+
43+
@Override
44+
public void update(final byte[] buf, final int offset, final int len) {
45+
ed.update(buf, offset, len);
46+
}
47+
48+
@Override
49+
public DigestType getDigestType() {
50+
return BLAKE_256;
51+
}
52+
53+
@Override
54+
public byte[] getMessageDigest() {
55+
final byte[] digestBytes = new byte[BLAKE_256.getDigestLengthBytes()];
56+
ed.doFinal(digestBytes, 0);
57+
return digestBytes;
58+
}
59+
60+
@Override
61+
public MessageDigest copyMessageDigest() {
62+
return new MessageDigest(BLAKE_256,
63+
Arrays.copyOf(getMessageDigest(), BLAKE_256.getDigestLengthBytes())
64+
);
65+
}
66+
67+
@Override
68+
public void reset() {
69+
ed.reset();
70+
}
71+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* eXist Open Source Native XML Database
3+
* Copyright (C) 2001-2018 The eXist Project
4+
* http://exist-db.org
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public License
8+
* as published by the Free Software Foundation; either version 2
9+
* of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public
17+
* License along with this library; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
*/
20+
package org.exist.util.crypto.digest;
21+
22+
import java.io.FilterInputStream;
23+
import java.io.IOException;
24+
import java.io.InputStream;
25+
26+
/**
27+
* An input stream which calculates a digest of the
28+
* data that is read.
29+
*
30+
* @author Adam Retter <[email protected]>
31+
*/
32+
public class DigestInputStream extends FilterInputStream {
33+
34+
private final StreamableDigest streamableDigest;
35+
36+
/**
37+
* Creates an input stream filter which calculates a digest
38+
* as the underlying input stream is read.
39+
*
40+
* @param is the input stream
41+
* @param streamableDigest the streamable digest
42+
*/
43+
public DigestInputStream(final InputStream is, final StreamableDigest streamableDigest) {
44+
super(is);
45+
this.streamableDigest = streamableDigest;
46+
}
47+
48+
@Override
49+
public int read() throws IOException {
50+
int b = in.read();
51+
if (b != -1) {
52+
streamableDigest.update((byte)(b & 0xFF));
53+
}
54+
return b;
55+
}
56+
57+
@Override
58+
public int read(final byte[] buf, final int off, int len) throws IOException {
59+
len = in.read(buf, off, len);
60+
if (len != -1) {
61+
streamableDigest.update(buf, off, len);
62+
}
63+
return len;
64+
}
65+
66+
@Override
67+
public long skip(final long n) throws IOException {
68+
final byte[] buf = new byte[512];
69+
long total = 0;
70+
while (total < n) {
71+
long len = n - total;
72+
len = read(buf, 0, len < buf.length ? (int)len : buf.length);
73+
if (len == -1) {
74+
return total;
75+
}
76+
total += len;
77+
}
78+
return total;
79+
}
80+
81+
public StreamableDigest getStreamableDigest() {
82+
return streamableDigest;
83+
}
84+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* eXist Open Source Native XML Database
3+
* Copyright (C) 2001-2018 The eXist Project
4+
* http://exist-db.org
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public License
8+
* as published by the Free Software Foundation; either version 2
9+
* of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public
17+
* License along with this library; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
*/
20+
package org.exist.util.crypto.digest;
21+
22+
import java.io.FilterOutputStream;
23+
import java.io.IOException;
24+
import java.io.OutputStream;
25+
26+
/**
27+
* An input stream which calculates a digest of the
28+
* data that is written.
29+
*
30+
* @author Adam Retter <[email protected]>
31+
*/
32+
public class DigestOutputStream extends FilterOutputStream {
33+
34+
private final StreamableDigest streamableDigest;
35+
36+
/**
37+
* Creates an output stream filter which calculates a digest
38+
* as the underlying output stream is written.
39+
*
40+
* @param os the input stream
41+
* @param streamableDigest the streamable digest
42+
*/
43+
public DigestOutputStream(final OutputStream os, final StreamableDigest streamableDigest) {
44+
super(os);
45+
this.streamableDigest = streamableDigest;
46+
}
47+
48+
@Override
49+
public void write(final int b) throws IOException {
50+
out.write(b);
51+
streamableDigest.update((byte) (b & 0xFF));
52+
}
53+
54+
@Override
55+
public void write(final byte[] b, final int off, final int len) throws IOException {
56+
out.write(b, off, len);
57+
streamableDigest.update(b, off, len);
58+
}
59+
60+
public StreamableDigest getStreamableDigest() {
61+
return streamableDigest;
62+
}
63+
}

0 commit comments

Comments
 (0)