1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.nio.file;
  27 
  28 import java.io.IOException;
  29 import java.io.InputStream;
  30 import java.nio.file.attribute.BasicFileAttributes;
  31 import java.util.Arrays;
  32 
  33 /**
  34  * Utility methods for Files.mismatch.
  35  */
  36 class FilesMismatch {
  37     // see Files.mismatch for the specification
  38     static long mismatch(Path path, Path path2) throws IOException {
  39         long result = mismatchByAttrs(path, path2);
  40         if (result != -2) {
  41             return result;
  42         }
  43 
  44         try (InputStream in1 = Files.newInputStream(path);
  45              InputStream in2 = Files.newInputStream(path2);) {
  46             result = mismatch(in1, in2);
  47             return result;
  48         }
  49     }
  50 
  51     // check mismatch by file attributes, return -1 for no mismatch, 0 for mismatch
  52     // at the beginning of the file, or -2 to indicate undetermined
  53     private static long mismatchByAttrs(Path path, Path path2) throws IOException {
  54         // initial values: -1 -- no mismatch
  55         long result = -1;
  56         try {
  57             // the following checks NPE as well
  58             path = path.toRealPath();
  59             path2 = path2.toRealPath();
  60             if (path.compareTo(path2) == 0) {
  61                 return result;
  62             }
  63             // readAttributes checks whether the file exists, throws IOE if not
  64             BasicFileAttributes attrs1 = Files.readAttributes(path, BasicFileAttributes.class);
  65             BasicFileAttributes attrs2 = Files.readAttributes(path2, BasicFileAttributes.class);
  66             long s1 = attrs1.size();
  67             long s2 = attrs2.size();
  68 
  69             if (s1 == 0 && s2 == 0) {
  70                 return result;
  71             }
  72             if (s1 == 0 && s2 > 0 || s1 > 0 && s2 == 0) {
  73                 return 0;
  74             }
  75             if (s1 == s2) {
  76                 Object k1 = attrs1.fileKey();
  77                 if (k1 != null && k1.equals(attrs2.fileKey())) {
  78                     return result;
  79                 }
  80             }
  81         } catch (ProviderMismatchException e) {
  82             throw new UnsupportedOperationException(e);
  83         }
  84         return -2;
  85     }
  86 
  87     /**
  88      * Finds and returns the index of the first mismatching byte in the content
  89      * of two files.
  90      *
  91      * @param in1 an inputstream
  92      * @param in2 another inputstream
  93      * @return the mismatch index if a mismatch is found, otherwise -1 to indicate
  94      *         no mismatch
  95      * @throws IOException
  96      */
  97     private static long mismatch(InputStream in1, InputStream in2)
  98         throws IOException {
  99         byte[] buffer1 = new byte[Files.BUFFER_SIZE];
 100         byte[] buffer2 = new byte[Files.BUFFER_SIZE];
 101         int totalRead = 0;
 102         while (true) {
 103             int nRead1 = in1.readNBytes(buffer1, 0, Files.BUFFER_SIZE);
 104             int nRead2 = in2.readNBytes(buffer2, 0, Files.BUFFER_SIZE);
 105 
 106             if (nRead1 == 0 || nRead2 == 0) {
 107                 if (nRead1 == 0 && nRead2 == 0) {
 108                     // both files reach EOF
 109                     return -1;
 110                 } else {
 111                     // one of the files reaches EOF
 112                     return totalRead;
 113                 }
 114             } else if (nRead1 != nRead2) {
 115                 int len = Math.min(nRead1, nRead2);
 116                 // there's always a mismatch when nRead1 != nRead2
 117                 return totalRead +
 118                     Arrays.mismatch(buffer1, 0, len, buffer2, 0, len);
 119 
 120             } else {
 121                 int i = Arrays.mismatch(buffer1, 0, nRead1, buffer2, 0, nRead1);
 122                 if (i > -1) {
 123                     return totalRead + i;
 124                 }
 125                 if (nRead1 < Files.BUFFER_SIZE) {
 126                     // we've reached the end of the files, but found no mismatch
 127                     return -1;
 128                 }
 129                 totalRead += nRead1;
 130             }
 131         }
 132     }
 133 }