1 /*
2  * Copyright (c) 2017, 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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /* @test
25  * @bug 8164900
26  * @summary Test read method of FileChannel with DirectIO
27  * (use -Dseed=X to set PRNG seed)
28  * @library .. /test/lib
29  * @build jdk.test.lib.RandomFactory
30  *        DirectIOTest
31  * @run main/othervm ReadDirect
32  * @key randomness
33  */
34 
35 import java.io.*;
36 import java.nio.ByteBuffer;
37 import java.nio.CharBuffer;
38 import java.nio.channels.*;
39 import java.nio.file.Files;
40 import java.nio.file.FileStore;
41 import java.nio.file.Path;
42 import java.nio.file.Paths;
43 import java.nio.file.StandardOpenOption;
44 import java.util.Random;
45 import com.sun.nio.file.ExtendedOpenOption;
46 
47 import jdk.test.lib.RandomFactory;
48 
49 public class ReadDirect {
50 
51     private static PrintStream err = System.err;
52 
53     private static Random generator = RandomFactory.getRandom();
54 
55     private static int charsPerGroup = -1;
56 
57     private static int alignment = -1;
58 
initTests()59     private static boolean initTests() throws Exception {
60         Path p = DirectIOTest.createTempFile();
61         if (!DirectIOTest.isDirectIOSupportedByFS(p)) {
62             Files.delete(p);
63             return false;
64         }
65         try {
66             FileStore fs = Files.getFileStore(p);
67             alignment = (int)fs.getBlockSize();
68             charsPerGroup = alignment;
69         } finally {
70             Files.delete(p);
71         }
72         return true;
73     }
74 
testWithSingleBuffer()75     private static void testWithSingleBuffer() throws Exception {
76         StringBuffer sb = new StringBuffer();
77         sb.setLength(2);
78 
79         Path p = DirectIOTest.createTempFile();
80 
81         initTestFile(p);
82         try (FileChannel fc = FileChannel.open(p,
83             StandardOpenOption.READ, StandardOpenOption.DELETE_ON_CLOSE,
84             ExtendedOpenOption.DIRECT)) {
85             ByteBuffer block = ByteBuffer.allocateDirect(charsPerGroup
86                 + alignment - 1).alignedSlice(alignment);
87             for (int x = 0; x < 100; x++) {
88                 block.clear();
89                 long offset = x * charsPerGroup;
90                 long expectedResult = offset / charsPerGroup;
91                 fc.read(block);
92 
93                 for (int i = 0; i < 2; i++) {
94                     byte aByte = block.get(i);
95                     sb.setCharAt(i, (char)aByte);
96                 }
97                 int result = Integer.parseInt(sb.toString());
98                 if (result != expectedResult) {
99                     err.println("I expected " + expectedResult);
100                     err.println("I got " + result);
101                     throw new Exception("Read test failed");
102                 }
103             }
104         }
105     }
106 
testWithNotAlignedBufferSize()107     private static void testWithNotAlignedBufferSize() throws Exception {
108         int bufferSize = charsPerGroup - 1;
109         Path p = DirectIOTest.createTempFile();
110 
111         try (OutputStream fos = Files.newOutputStream(p)) {
112             fos.write(new byte[bufferSize]);
113         }
114 
115         try (FileChannel fc = FileChannel.open(p,
116             StandardOpenOption.READ, StandardOpenOption.DELETE_ON_CLOSE,
117             ExtendedOpenOption.DIRECT)) {
118             ByteBuffer block = ByteBuffer.allocate(bufferSize);
119             try {
120                 fc.read(block);
121                 throw new RuntimeException("Expected exception not thrown");
122             } catch (IOException e) {
123                 if (!e.getMessage().contains("Number of remaining bytes ("
124                     + bufferSize + ") is not a multiple of the block size ("
125                     + alignment + ")"))
126                     throw new Exception("Read test failed");
127             }
128         }
129     }
130 
testWithNotAlignedBufferOffset()131     private static void testWithNotAlignedBufferOffset() throws Exception {
132         int bufferSize = charsPerGroup * 2;
133         int pos = alignment - 1;
134 
135         Path p = DirectIOTest.createTempFile();
136 
137         try (OutputStream fos = Files.newOutputStream(p)) {
138             fos.write(new byte[bufferSize]);
139         }
140 
141         try (FileChannel fc = FileChannel.open(p,
142             StandardOpenOption.READ, StandardOpenOption.DELETE_ON_CLOSE,
143             ExtendedOpenOption.DIRECT)) {
144             ByteBuffer block = ByteBuffer.allocateDirect(bufferSize);
145             block.position(pos);
146             block.limit(bufferSize - 1);
147             try {
148                 fc.read(block);
149                 throw new RuntimeException("Expected exception not thrown");
150             } catch (IOException e) {
151                 if (!e.getMessage().contains("Current location of the bytebuffer "
152                     +  "(" + pos + ") is not a multiple of the block size ("
153                     + alignment + ")"))
154                     throw new Exception("Read test failed");
155             }
156         }
157     }
158 
testWithArrayOfBuffer()159     private static void testWithArrayOfBuffer() throws Exception {
160         StringBuffer sb = new StringBuffer();
161         sb.setLength(2);
162         ByteBuffer[] dests = new ByteBuffer[4];
163         Path p = DirectIOTest.createTempFile();
164 
165         initTestFile(p);
166 
167         try (FileChannel fc = FileChannel.open(p,
168             StandardOpenOption.READ, StandardOpenOption.DELETE_ON_CLOSE,
169             ExtendedOpenOption.DIRECT)) {
170             int randomNumber = -1;
171 
172             for (int i = 0; i < 4; i++) {
173                 dests[i] = ByteBuffer.allocateDirect
174                     (charsPerGroup + alignment - 1).alignedSlice(alignment);
175                 for (int j = 0; j < charsPerGroup; j++) {
176                     dests[i].put(j, (byte)'a');
177                 }
178             }
179 
180             // The size of the test FileChannel is 100*charsPerGroup.
181             // As the channel bytes will be scattered into two buffers
182             // each of size charsPerGroup, the offset cannot be greater
183             // than 98*charsPerGroup, so the value of randomNumber must
184             // be in the range [0,98], i.e., 0 <= randomNumber < 99.
185             randomNumber = generator.nextInt(99);
186             long offset =  randomNumber * charsPerGroup;
187             fc.position(offset);
188             fc.read(dests, 1, 2);
189 
190             for (int i = 0; i < 4; i++) {
191                 if (i == 1 || i == 2) {
192                     for (int j = 0; j < 2; j++) {
193                         byte aByte = dests[i].get(j);
194                         sb.setCharAt(j, (char)aByte);
195                     }
196                     int result = Integer.parseInt(sb.toString());
197                     int expectedResult = randomNumber + i - 1;
198                     if (result != expectedResult) {
199                         err.println("I expected " + expectedResult);
200                         err.println("I got " + result);
201                         throw new Exception("Read test failed");
202                     }
203                 } else {
204                     for (int k = 0; k < charsPerGroup; k++) {
205                         if (dests[i].get(k) != (byte)'a')
206                             throw new RuntimeException("Read test failed");
207                     }
208                 }
209             }
210         }
211     }
212 
testOnEOF()213     public static void testOnEOF() throws Exception {
214         int bufferSize = charsPerGroup / 2;
215         Path p = DirectIOTest.createTempFile();
216 
217         try (OutputStream fos = Files.newOutputStream(p)) {
218             byte[] writeBlock = new byte[bufferSize];
219             for (int i = 0; i < bufferSize; i++) {
220                 writeBlock[i] = ((byte)'a');
221             }
222             fos.write(writeBlock);
223         }
224 
225         try (FileChannel fc = FileChannel.open(p,
226             StandardOpenOption.READ, StandardOpenOption.DELETE_ON_CLOSE,
227             ExtendedOpenOption.DIRECT)) {
228             ByteBuffer block = ByteBuffer.allocateDirect(
229                     (bufferSize / alignment + 1) * alignment + alignment - 1)
230                     .alignedSlice(alignment);
231             int result = fc.read(block);
232             if (result != bufferSize) {
233                 err.println("Number of bytes to read " + bufferSize);
234                 err.println("I read " + result);
235                 throw new Exception("Read test failed");
236             }
237             for (int j = 0; j < bufferSize; j++) {
238                 if (block.get(j) != (byte)'a')
239                     throw new RuntimeException("Read test failed");
240             }
241         }
242     }
243 
main(String[] args)244     public static void main(String[] args) throws Exception {
245         if (initTests()) {
246             testWithSingleBuffer();
247             testWithNotAlignedBufferSize();
248             testWithNotAlignedBufferOffset();
249             testWithArrayOfBuffer();
250             testOnEOF();
251         }
252     }
253 
initTestFile(Path p)254     private static void initTestFile(Path p)
255             throws Exception {
256         try (OutputStream fos = Files.newOutputStream(p)) {
257             try (BufferedWriter awriter
258                  = new BufferedWriter(new OutputStreamWriter(fos, "8859_1"))) {
259                 for (int i = 0; i < 100; i++) {
260                     String number = new Integer(i).toString();
261                     for (int h = 0; h < 2 - number.length(); h++)
262                         awriter.write("0");
263                     awriter.write("" + i);
264                     for (int j = 0; j < (charsPerGroup - 2); j++)
265                         awriter.write("0");
266                 }
267                 awriter.flush();
268             }
269         }
270     }
271 }
272