EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.store.fs]

COVERAGE SUMMARY FOR SOURCE FILE [FilePathSplit.java]

nameclass, %method, %block, %line, %
FilePathSplit.java100% (2/2)100% (30/30)86%  (765/886)88%  (157.7/180)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class FileSplit100% (1/1)100% (12/12)82%  (339/412)82%  (68.4/83)
truncate (long): FileChannel 100% (1/1)53%  (52/99)47%  (9/19)
write (ByteBuffer): int 100% (1/1)79%  (99/125)79%  (17.4/22)
FileSplit (FilePathSplit, String, FileChannel [], long, long): void 100% (1/1)100% (18/18)100% (7/7)
force (boolean): void 100% (1/1)100% (21/21)100% (3/3)
getFileChannel (): FileChannel 100% (1/1)100% (49/49)100% (10/10)
implCloseChannel (): void 100% (1/1)100% (20/20)100% (3/3)
position (): long 100% (1/1)100% (3/3)100% (1/1)
position (long): FileChannel 100% (1/1)100% (5/5)100% (2/2)
read (ByteBuffer): int 100% (1/1)100% (56/56)100% (13/13)
size (): long 100% (1/1)100% (3/3)100% (1/1)
toString (): String 100% (1/1)100% (4/4)100% (1/1)
tryLock (long, long, boolean): FileLock 100% (1/1)100% (9/9)100% (1/1)
     
class FilePathSplit100% (1/1)100% (18/18)90%  (426/474)92%  (89.4/97)
newInputStream (): InputStream 100% (1/1)58%  (15/26)62%  (4.4/7)
parse (String): String [] 100% (1/1)79%  (52/66)83%  (10/12)
open (String): FileChannel 100% (1/1)83%  (111/134)90%  (26/29)
FilePathSplit (): void 100% (1/1)100% (3/3)100% (1/1)
closeAndThrow (int, FileChannel [], FileChannel, long): void 100% (1/1)100% (43/43)100% (4/4)
delete (): void 100% (1/1)100% (14/14)100% (5/5)
getBase (int): FilePath 100% (1/1)100% (5/5)100% (1/1)
getDefaultMaxLength (): long 100% (1/1)100% (11/11)100% (1/1)
getName (int): String 100% (1/1)100% (21/21)100% (1/1)
getPrefix (): String 100% (1/1)100% (19/19)100% (1/1)
getScheme (): String 100% (1/1)100% (2/2)100% (1/1)
lastModified (): long 100% (1/1)100% (22/22)100% (7/7)
moveTo (FilePath, boolean): void 100% (1/1)100% (21/21)100% (6/6)
newDirectoryStream (): ArrayList 100% (1/1)100% (34/34)100% (7/7)
newOutputStream (boolean): OutputStream 100% (1/1)100% (8/8)100% (1/1)
setReadOnly (): boolean 100% (1/1)100% (18/18)100% (6/6)
size (): long 100% (1/1)100% (20/20)100% (6/6)
unwrap (String): FilePath 100% (1/1)100% (7/7)100% (1/1)

1/*
2 * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
3 * and the EPL 1.0 (http://h2database.com/html/license.html).
4 * Initial Developer: H2 Group
5 */
6package org.h2.store.fs;
7 
8import java.io.IOException;
9import java.io.InputStream;
10import java.io.OutputStream;
11import java.io.SequenceInputStream;
12import java.nio.ByteBuffer;
13import java.nio.channels.FileChannel;
14import java.nio.channels.FileLock;
15import java.util.ArrayList;
16import java.util.List;
17 
18import org.h2.engine.SysProperties;
19import org.h2.message.DbException;
20import org.h2.util.New;
21 
22/**
23 * A file system that may split files into multiple smaller files.
24 * (required for a FAT32 because it only support files up to 2 GB).
25 */
26public class FilePathSplit extends FilePathWrapper {
27 
28    private static final String PART_SUFFIX = ".part";
29 
30    @Override
31    protected String getPrefix() {
32        return getScheme() + ":" + parse(name)[0] + ":";
33    }
34 
35    @Override
36    public FilePath unwrap(String fileName) {
37        return FilePath.get(parse(fileName)[1]);
38    }
39 
40    @Override
41    public boolean setReadOnly() {
42        boolean result = false;
43        for (int i = 0;; i++) {
44            FilePath f = getBase(i);
45            if (f.exists()) {
46                result = f.setReadOnly();
47            } else {
48                break;
49            }
50        }
51        return result;
52    }
53 
54    @Override
55    public void delete() {
56        for (int i = 0;; i++) {
57            FilePath f = getBase(i);
58            if (f.exists()) {
59                f.delete();
60            } else {
61                break;
62            }
63        }
64    }
65 
66    @Override
67    public long lastModified() {
68        long lastModified = 0;
69        for (int i = 0;; i++) {
70            FilePath f = getBase(i);
71            if (f.exists()) {
72                long l = f.lastModified();
73                lastModified = Math.max(lastModified, l);
74            } else {
75                break;
76            }
77        }
78        return lastModified;
79    }
80 
81    @Override
82    public long size() {
83        long length = 0;
84        for (int i = 0;; i++) {
85            FilePath f = getBase(i);
86            if (f.exists()) {
87                length += f.size();
88            } else {
89                break;
90            }
91        }
92        return length;
93    }
94 
95    @Override
96    public ArrayList<FilePath> newDirectoryStream() {
97        List<FilePath> list = getBase().newDirectoryStream();
98        ArrayList<FilePath> newList = New.arrayList();
99        for (int i = 0, size = list.size(); i < size; i++) {
100            FilePath f = list.get(i);
101            if (!f.getName().endsWith(PART_SUFFIX)) {
102                newList.add(wrap(f));
103            }
104        }
105        return newList;
106    }
107 
108    @Override
109    public InputStream newInputStream() throws IOException {
110        InputStream input = getBase().newInputStream();
111        for (int i = 1;; i++) {
112            FilePath f = getBase(i);
113            if (f.exists()) {
114                InputStream i2 = f.newInputStream();
115                input = new SequenceInputStream(input, i2);
116            } else {
117                break;
118            }
119        }
120        return input;
121    }
122 
123    @Override
124    public FileChannel open(String mode) throws IOException {
125        ArrayList<FileChannel> list = New.arrayList();
126        list.add(getBase().open(mode));
127        for (int i = 1;; i++) {
128            FilePath f = getBase(i);
129            if (f.exists()) {
130                list.add(f.open(mode));
131            } else {
132                break;
133            }
134        }
135        FileChannel[] array = new FileChannel[list.size()];
136        list.toArray(array);
137        long maxLength = array[0].size();
138        long length = maxLength;
139        if (array.length == 1) {
140            long defaultMaxLength = getDefaultMaxLength();
141            if (maxLength < defaultMaxLength) {
142                maxLength = defaultMaxLength;
143            }
144        } else {
145            if (maxLength == 0) {
146                closeAndThrow(0, array, array[0], maxLength);
147            }
148            for (int i = 1; i < array.length - 1; i++) {
149                FileChannel c = array[i];
150                long l = c.size();
151                length += l;
152                if (l != maxLength) {
153                    closeAndThrow(i, array, c, maxLength);
154                }
155            }
156            FileChannel c = array[array.length - 1];
157            long l = c.size();
158            length += l;
159            if (l > maxLength) {
160                closeAndThrow(array.length - 1, array, c, maxLength);
161            }
162        }
163        return new FileSplit(this, mode, array, length, maxLength);
164    }
165 
166    private long getDefaultMaxLength() {
167        return 1L << Integer.decode(parse(name)[0]).intValue();
168    }
169 
170    private void closeAndThrow(int id, FileChannel[] array, FileChannel o,
171            long maxLength) throws IOException {
172        String message = "Expected file length: " + maxLength + " got: " +
173                o.size() + " for " + getName(id);
174        for (FileChannel f : array) {
175            f.close();
176        }
177        throw new IOException(message);
178    }
179 
180    @Override
181    public OutputStream newOutputStream(boolean append) throws IOException {
182        return new FileChannelOutputStream(open("rw"), append);
183    }
184 
185    @Override
186    public void moveTo(FilePath path, boolean atomicReplace) {
187        FilePathSplit newName = (FilePathSplit) path;
188        for (int i = 0;; i++) {
189            FilePath o = getBase(i);
190            if (o.exists()) {
191                o.moveTo(newName.getBase(i), atomicReplace);
192            } else {
193                break;
194            }
195        }
196    }
197 
198    /**
199     * Split the file name into size and base file name.
200     *
201     * @param fileName the file name
202     * @return an array with size and file name
203     */
204    private String[] parse(String fileName) {
205        if (!fileName.startsWith(getScheme())) {
206            DbException.throwInternalError(fileName + " doesn't start with " + getScheme());
207        }
208        fileName = fileName.substring(getScheme().length() + 1);
209        String size;
210        if (fileName.length() > 0 && Character.isDigit(fileName.charAt(0))) {
211            int idx = fileName.indexOf(':');
212            size = fileName.substring(0, idx);
213            try {
214                fileName = fileName.substring(idx + 1);
215            } catch (NumberFormatException e) {
216                // ignore
217            }
218        } else {
219            size = Long.toString(SysProperties.SPLIT_FILE_SIZE_SHIFT);
220        }
221        return new String[] { size, fileName };
222    }
223 
224    /**
225     * Get the file name of a part file.
226     *
227     * @param id the part id
228     * @return the file name including the part id
229     */
230    FilePath getBase(int id) {
231        return FilePath.get(getName(id));
232    }
233 
234    private String getName(int id) {
235        return id > 0 ? getBase().name + "." + id + PART_SUFFIX : getBase().name;
236    }
237 
238    @Override
239    public String getScheme() {
240        return "split";
241    }
242 
243}
244 
245/**
246 * A file that may be split into multiple smaller files.
247 */
248class FileSplit extends FileBase {
249 
250    private final FilePathSplit file;
251    private final String mode;
252    private final long maxLength;
253    private FileChannel[] list;
254    private long filePointer;
255    private long length;
256 
257    FileSplit(FilePathSplit file, String mode, FileChannel[] list, long length,
258            long maxLength) {
259        this.file = file;
260        this.mode = mode;
261        this.list = list;
262        this.length = length;
263        this.maxLength = maxLength;
264    }
265 
266    @Override
267    public void implCloseChannel() throws IOException {
268        for (FileChannel c : list) {
269            c.close();
270        }
271    }
272 
273    @Override
274    public long position() {
275        return filePointer;
276    }
277 
278    @Override
279    public long size() {
280        return length;
281    }
282 
283    @Override
284    public int read(ByteBuffer dst) throws IOException {
285        int len = dst.remaining();
286        if (len == 0) {
287            return 0;
288        }
289        len = (int) Math.min(len, length - filePointer);
290        if (len <= 0) {
291            return -1;
292        }
293        long offset = filePointer % maxLength;
294        len = (int) Math.min(len, maxLength - offset);
295        FileChannel channel = getFileChannel();
296        channel.position(offset);
297        len = channel.read(dst);
298        filePointer += len;
299        return len;
300    }
301 
302    @Override
303    public FileChannel position(long pos) {
304        filePointer = pos;
305        return this;
306    }
307 
308    private FileChannel getFileChannel() throws IOException {
309        int id = (int) (filePointer / maxLength);
310        while (id >= list.length) {
311            int i = list.length;
312            FileChannel[] newList = new FileChannel[i + 1];
313            System.arraycopy(list, 0, newList, 0, i);
314            FilePath f = file.getBase(i);
315            newList[i] = f.open(mode);
316            list = newList;
317        }
318        return list[id];
319    }
320 
321    @Override
322    public FileChannel truncate(long newLength) throws IOException {
323        if (newLength >= length) {
324            return this;
325        }
326        filePointer = Math.min(filePointer, newLength);
327        int newFileCount = 1 + (int) (newLength / maxLength);
328        if (newFileCount < list.length) {
329            // delete some of the files
330            FileChannel[] newList = new FileChannel[newFileCount];
331            // delete backwards, so that truncating is somewhat transactional
332            for (int i = list.length - 1; i >= newFileCount; i--) {
333                // verify the file is writable
334                list[i].truncate(0);
335                list[i].close();
336                try {
337                    file.getBase(i).delete();
338                } catch (DbException e) {
339                    throw DbException.convertToIOException(e);
340                }
341            }
342            System.arraycopy(list, 0, newList, 0, newList.length);
343            list = newList;
344        }
345        long size = newLength - maxLength * (newFileCount - 1);
346        list[list.length - 1].truncate(size);
347        this.length = newLength;
348        return this;
349    }
350 
351    @Override
352    public void force(boolean metaData) throws IOException {
353        for (FileChannel c : list) {
354            c.force(metaData);
355        }
356    }
357 
358    @Override
359    public int write(ByteBuffer src) throws IOException {
360        if (filePointer >= length && filePointer > maxLength) {
361            // may need to extend and create files
362            long oldFilePointer = filePointer;
363            long x = length - (length % maxLength) + maxLength;
364            for (; x < filePointer; x += maxLength) {
365                if (x > length) {
366                    // expand the file size
367                    position(x - 1);
368                    write(ByteBuffer.wrap(new byte[1]));
369                }
370                filePointer = oldFilePointer;
371            }
372        }
373        long offset = filePointer % maxLength;
374        int len = src.remaining();
375        FileChannel channel = getFileChannel();
376        channel.position(offset);
377        int l = (int) Math.min(len, maxLength - offset);
378        if (l == len) {
379            l = channel.write(src);
380        } else {
381            int oldLimit = src.limit();
382            src.limit(src.position() + l);
383            l = channel.write(src);
384            src.limit(oldLimit);
385        }
386        filePointer += l;
387        length = Math.max(length, filePointer);
388        return l;
389    }
390 
391    @Override
392    public synchronized FileLock tryLock(long position, long size,
393            boolean shared) throws IOException {
394        return list[0].tryLock(position, size, shared);
395    }
396 
397    @Override
398    public String toString() {
399        return file.toString();
400    }
401 
402}

[all classes][org.h2.store.fs]
EMMA 2.0.5312 (C) Vladimir Roubtsov