• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2016 Google Inc. All Rights Reserved.
2 
3    Distributed under MIT license.
4    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
5 */
6 
7 package org.brotli.integration;
8 
9 import org.brotli.dec.BrotliInputStream;
10 import java.io.FileInputStream;
11 import java.io.FileNotFoundException;
12 import java.io.FilterInputStream;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.util.concurrent.atomic.AtomicInteger;
16 import java.util.zip.ZipEntry;
17 import java.util.zip.ZipInputStream;
18 
19 /**
20  * Decompress files and (optionally) checks their checksums.
21  *
22  * <p> File are read from ZIP archive passed as an array of bytes. Multiple checkers negotiate about
23  * task distribution via shared AtomicInteger counter.
24  * <p> All entries are expected to be valid brotli compressed streams and output CRC64 checksum
25  * is expected to match the checksum hex-encoded in the first part of entry name.
26  */
27 public class BundleChecker implements Runnable {
28   private final AtomicInteger nextJob;
29   private final InputStream input;
30   private final boolean sanityCheck;
31 
32   /**
33    * @param sanityCheck do not calculate checksum and ignore {@link IOException}.
34    */
BundleChecker(InputStream input, AtomicInteger nextJob, boolean sanityCheck)35   public BundleChecker(InputStream input, AtomicInteger nextJob, boolean sanityCheck) {
36     this.input = input;
37     this.nextJob = nextJob;
38     this.sanityCheck = sanityCheck;
39   }
40 
decompressAndCalculateCrc(ZipInputStream input)41   private long decompressAndCalculateCrc(ZipInputStream input) throws IOException {
42     /* Do not allow entry readers to close the whole ZipInputStream. */
43     FilterInputStream entryStream = new FilterInputStream(input) {
44       @Override
45       public void close() {}
46     };
47 
48     BrotliInputStream decompressedStream = new BrotliInputStream(entryStream);
49     long crc;
50     try {
51       crc = BundleHelper.fingerprintStream(decompressedStream);
52     } finally {
53       decompressedStream.close();
54     }
55     return crc;
56   }
57 
58   @Override
run()59   public void run() {
60     String entryName = "";
61     ZipInputStream zis = new ZipInputStream(input);
62     try {
63       int entryIndex = 0;
64       ZipEntry entry;
65       int jobIndex = nextJob.getAndIncrement();
66       while ((entry = zis.getNextEntry()) != null) {
67         if (entry.isDirectory()) {
68           continue;
69         }
70         if (entryIndex++ != jobIndex) {
71           zis.closeEntry();
72           continue;
73         }
74         entryName = entry.getName();
75         long entryCrc = BundleHelper.getExpectedFingerprint(entryName);
76         try {
77           if (entryCrc != decompressAndCalculateCrc(zis) && !sanityCheck) {
78             throw new RuntimeException("CRC mismatch");
79           }
80         } catch (IOException iox) {
81           if (!sanityCheck) {
82             throw new RuntimeException("Decompression failed", iox);
83           }
84         }
85         zis.closeEntry();
86         entryName = "";
87         jobIndex = nextJob.getAndIncrement();
88       }
89       zis.close();
90       input.close();
91     } catch (Throwable ex) {
92       throw new RuntimeException(entryName, ex);
93     }
94   }
95 
main(String[] args)96   public static void main(String[] args) throws FileNotFoundException {
97     int argsOffset = 0;
98     boolean sanityCheck = false;
99     if (args.length != 0) {
100       if (args[0].equals("-s")) {
101         sanityCheck = true;
102         argsOffset = 1;
103       }
104     }
105     if (args.length == argsOffset) {
106       throw new RuntimeException("Usage: BundleChecker [-s] <fileX.zip> ...");
107     }
108     for (int i = argsOffset; i < args.length; ++i) {
109       new BundleChecker(new FileInputStream(args[i]), new AtomicInteger(0), sanityCheck).run();
110     }
111   }
112 }
113