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

COVERAGE SUMMARY FOR SOURCE FILE [FilePathMem.java]

nameclass, %method, %block, %line, %
FilePathMem.java100% (6/6)92%  (57/62)80%  (889/1105)83%  (207.8/249)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class FileMemData$Cache100% (1/1)50%  (1/2)33%  (9/27)38%  (3/8)
removeEldestEntry (Map$Entry): boolean 0%   (0/1)0%   (0/18)0%   (0/5)
FileMemData$Cache (int): void 100% (1/1)100% (9/9)100% (3/3)
     
class FilePathMemLZF100% (1/1)67%  (2/3)71%  (5/7)67%  (2/3)
compressed (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
FilePathMemLZF (): void 100% (1/1)100% (3/3)100% (1/1)
getScheme (): String 100% (1/1)100% (2/2)100% (1/1)
     
class FileMemData100% (1/1)89%  (16/18)76%  (324/425)78%  (83/106)
compress (byte [][], int): void 0%   (0/1)0%   (0/40)0%   (0/9)
compressLater (byte [][], int): void 0%   (0/1)0%   (0/28)0%   (0/7)
expand (byte [][], int): void 100% (1/1)49%  (20/41)70%  (7/10)
lockExclusive (): boolean 100% (1/1)85%  (11/13)75%  (3/4)
lockShared (): boolean 100% (1/1)85%  (11/13)75%  (3/4)
truncate (long): void 100% (1/1)91%  (43/47)91%  (10/11)
readWrite (long, byte [], int, int, boolean): long 100% (1/1)96%  (87/91)95%  (20/21)
<static initializer> 100% (1/1)100% (32/32)100% (8/8)
FileMemData (String, boolean): void 100% (1/1)100% (16/16)100% (6/6)
canWrite (): boolean 100% (1/1)100% (7/7)100% (1/1)
changeLength (long): void 100% (1/1)100% (49/49)100% (10/10)
getLastModified (): long 100% (1/1)100% (3/3)100% (1/1)
getName (): String 100% (1/1)100% (3/3)100% (1/1)
length (): long 100% (1/1)100% (3/3)100% (1/1)
setName (String): void 100% (1/1)100% (4/4)100% (2/2)
setReadOnly (): boolean 100% (1/1)100% (5/5)100% (2/2)
touch (boolean): void 100% (1/1)100% (14/14)100% (4/4)
unlock (): void 100% (1/1)100% (16/16)100% (4/4)
     
class FilePathMem100% (1/1)100% (25/25)81%  (372/461)89%  (76.8/86)
createDirectory (): void 100% (1/1)50%  (17/34)79%  (4.7/6)
moveTo (FilePath, boolean): void 100% (1/1)60%  (39/65)87%  (7.8/9)
createFile (): boolean 100% (1/1)62%  (15/24)76%  (4.5/6)
getMemoryFile (): FileMemData 100% (1/1)66%  (33/50)78%  (7/9)
delete (): void 100% (1/1)77%  (17/22)95%  (5.7/6)
exists (): boolean 100% (1/1)80%  (20/25)80%  (4/5)
isDirectory (): boolean 100% (1/1)83%  (24/29)83%  (5/6)
newDirectoryStream (): List 100% (1/1)91%  (50/55)89%  (8/9)
<static initializer> 100% (1/1)100% (11/11)100% (2/2)
FilePathMem (): void 100% (1/1)100% (3/3)100% (1/1)
canWrite (): boolean 100% (1/1)100% (4/4)100% (1/1)
compressed (): boolean 100% (1/1)100% (2/2)100% (1/1)
getCanonicalPath (String): String 100% (1/1)100% (38/38)100% (5/5)
getParent (): FilePathMem 100% (1/1)100% (17/17)100% (2/2)
getPath (String): FilePathMem 100% (1/1)100% (10/10)100% (3/3)
getScheme (): String 100% (1/1)100% (2/2)100% (1/1)
isAbsolute (): boolean 100% (1/1)100% (2/2)100% (1/1)
isRoot (): boolean 100% (1/1)100% (13/13)100% (1/1)
lastModified (): long 100% (1/1)100% (4/4)100% (1/1)
newInputStream (): InputStream 100% (1/1)100% (15/15)100% (3/3)
newOutputStream (boolean): OutputStream 100% (1/1)100% (15/15)100% (3/3)
open (String): FileChannel 100% (1/1)100% (11/11)100% (2/2)
setReadOnly (): boolean 100% (1/1)100% (4/4)100% (1/1)
size (): long 100% (1/1)100% (4/4)100% (1/1)
toRealPath (): FilePathMem 100% (1/1)100% (2/2)100% (1/1)
     
class FileMem$1100% (1/1)67%  (2/3)88%  (15/17)75%  (3/4)
isValid (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
FileMem$1 (FileMem, FileChannel, long, long, boolean): void 100% (1/1)100% (10/10)100% (1/1)
release (): void 100% (1/1)100% (5/5)100% (2/2)
     
class FileMem100% (1/1)100% (11/11)98%  (164/168)95%  (41/43)
tryLock (long, long, boolean): FileLock 100% (1/1)85%  (22/26)71%  (5/7)
FileMem (FileMemData, boolean): void 100% (1/1)100% (9/9)100% (4/4)
force (boolean): void 100% (1/1)100% (1/1)100% (1/1)
implCloseChannel (): void 100% (1/1)100% (4/4)100% (2/2)
position (): long 100% (1/1)100% (3/3)100% (1/1)
position (long): FileChannel 100% (1/1)100% (7/7)100% (2/2)
read (ByteBuffer): int 100% (1/1)100% (44/44)100% (10/10)
size (): long 100% (1/1)100% (4/4)100% (1/1)
toString (): String 100% (1/1)100% (4/4)100% (1/1)
truncate (long): FileChannel 100% (1/1)100% (29/29)100% (7/7)
write (ByteBuffer): int 100% (1/1)100% (37/37)100% (7/7)

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.nio.ByteBuffer;
12import java.nio.channels.FileChannel;
13import java.nio.channels.FileLock;
14import java.nio.channels.NonWritableChannelException;
15import java.util.ArrayList;
16import java.util.LinkedHashMap;
17import java.util.List;
18import java.util.Map;
19import java.util.TreeMap;
20 
21import org.h2.api.ErrorCode;
22import org.h2.compress.CompressLZF;
23import org.h2.message.DbException;
24import org.h2.util.MathUtils;
25import org.h2.util.New;
26 
27/**
28 * This file system keeps files fully in memory. There is an option to compress
29 * file blocks to safe memory.
30 */
31public class FilePathMem extends FilePath {
32 
33    private static final TreeMap<String, FileMemData> MEMORY_FILES =
34            new TreeMap<String, FileMemData>();
35    private static final FileMemData DIRECTORY = new FileMemData("", false);
36 
37    @Override
38    public FilePathMem getPath(String path) {
39        FilePathMem p = new FilePathMem();
40        p.name = getCanonicalPath(path);
41        return p;
42    }
43 
44    @Override
45    public long size() {
46        return getMemoryFile().length();
47    }
48 
49    @Override
50    public void moveTo(FilePath newName, boolean atomicReplace) {
51        synchronized (MEMORY_FILES) {
52            if (!atomicReplace && !newName.name.equals(name) &&
53                    MEMORY_FILES.containsKey(newName.name)) {
54                throw DbException.get(ErrorCode.FILE_RENAME_FAILED_2,
55                        new String[] { name, newName + " (exists)" });
56            }
57            FileMemData f = getMemoryFile();
58            f.setName(newName.name);
59            MEMORY_FILES.remove(name);
60            MEMORY_FILES.put(newName.name, f);
61        }
62    }
63 
64    @Override
65    public boolean createFile() {
66        synchronized (MEMORY_FILES) {
67            if (exists()) {
68                return false;
69            }
70            getMemoryFile();
71        }
72        return true;
73    }
74 
75    @Override
76    public boolean exists() {
77        if (isRoot()) {
78            return true;
79        }
80        synchronized (MEMORY_FILES) {
81            return MEMORY_FILES.get(name) != null;
82        }
83    }
84 
85    @Override
86    public void delete() {
87        if (isRoot()) {
88            return;
89        }
90        synchronized (MEMORY_FILES) {
91            MEMORY_FILES.remove(name);
92        }
93    }
94 
95    @Override
96    public List<FilePath> newDirectoryStream() {
97        ArrayList<FilePath> list = New.arrayList();
98        synchronized (MEMORY_FILES) {
99            for (String n : MEMORY_FILES.tailMap(name).keySet()) {
100                if (n.startsWith(name)) {
101                    if (!n.equals(name) && n.indexOf('/', name.length() + 1) < 0) {
102                        list.add(getPath(n));
103                    }
104                } else {
105                    break;
106                }
107            }
108            return list;
109        }
110    }
111 
112    @Override
113    public boolean setReadOnly() {
114        return getMemoryFile().setReadOnly();
115    }
116 
117    @Override
118    public boolean canWrite() {
119        return getMemoryFile().canWrite();
120    }
121 
122    @Override
123    public FilePathMem getParent() {
124        int idx = name.lastIndexOf('/');
125        return idx < 0 ? null : getPath(name.substring(0, idx));
126    }
127 
128    @Override
129    public boolean isDirectory() {
130        if (isRoot()) {
131            return true;
132        }
133        synchronized (MEMORY_FILES) {
134            FileMemData d = MEMORY_FILES.get(name);
135            return d == DIRECTORY;
136        }
137    }
138 
139    @Override
140    public boolean isAbsolute() {
141        // TODO relative files are not supported
142        return true;
143    }
144 
145    @Override
146    public FilePathMem toRealPath() {
147        return this;
148    }
149 
150    @Override
151    public long lastModified() {
152        return getMemoryFile().getLastModified();
153    }
154 
155    @Override
156    public void createDirectory() {
157        if (exists()) {
158            throw DbException.get(ErrorCode.FILE_CREATION_FAILED_1,
159                    name + " (a file with this name already exists)");
160        }
161        synchronized (MEMORY_FILES) {
162            MEMORY_FILES.put(name, DIRECTORY);
163        }
164    }
165 
166    @Override
167    public OutputStream newOutputStream(boolean append) throws IOException {
168        FileMemData obj = getMemoryFile();
169        FileMem m = new FileMem(obj, false);
170        return new FileChannelOutputStream(m, append);
171    }
172 
173    @Override
174    public InputStream newInputStream() {
175        FileMemData obj = getMemoryFile();
176        FileMem m = new FileMem(obj, true);
177        return new FileChannelInputStream(m, true);
178    }
179 
180    @Override
181    public FileChannel open(String mode) {
182        FileMemData obj = getMemoryFile();
183        return new FileMem(obj, "r".equals(mode));
184    }
185 
186    private FileMemData getMemoryFile() {
187        synchronized (MEMORY_FILES) {
188            FileMemData m = MEMORY_FILES.get(name);
189            if (m == DIRECTORY) {
190                throw DbException.get(ErrorCode.FILE_CREATION_FAILED_1,
191                        name + " (a directory with this name already exists)");
192            }
193            if (m == null) {
194                m = new FileMemData(name, compressed());
195                MEMORY_FILES.put(name, m);
196            }
197            return m;
198        }
199    }
200 
201    private boolean isRoot() {
202        return name.equals(getScheme() + ":");
203    }
204 
205    private static String getCanonicalPath(String fileName) {
206        fileName = fileName.replace('\\', '/');
207        int idx = fileName.indexOf(':') + 1;
208        if (fileName.length() > idx && fileName.charAt(idx) != '/') {
209            fileName = fileName.substring(0, idx) + "/" + fileName.substring(idx);
210        }
211        return fileName;
212    }
213 
214    @Override
215    public String getScheme() {
216        return "memFS";
217    }
218 
219    /**
220     * Whether the file should be compressed.
221     *
222     * @return if it should be compressed.
223     */
224    boolean compressed() {
225        return false;
226    }
227 
228}
229 
230/**
231 * A memory file system that compresses blocks to conserve memory.
232 */
233class FilePathMemLZF extends FilePathMem {
234 
235    @Override
236    boolean compressed() {
237        return true;
238    }
239 
240    @Override
241    public String getScheme() {
242        return "memLZF";
243    }
244 
245}
246 
247/**
248 * This class represents an in-memory file.
249 */
250class FileMem extends FileBase {
251 
252    /**
253     * The file data.
254     */
255    final FileMemData data;
256 
257    private final boolean readOnly;
258    private long pos;
259 
260    FileMem(FileMemData data, boolean readOnly) {
261        this.data = data;
262        this.readOnly = readOnly;
263    }
264 
265    @Override
266    public long size() {
267        return data.length();
268    }
269 
270    @Override
271    public FileChannel truncate(long newLength) throws IOException {
272        // compatibility with JDK FileChannel#truncate
273        if (readOnly) {
274            throw new NonWritableChannelException();
275        }
276        if (newLength < size()) {
277            data.touch(readOnly);
278            pos = Math.min(pos, newLength);
279            data.truncate(newLength);
280        }
281        return this;
282    }
283 
284    @Override
285    public FileChannel position(long newPos) {
286        this.pos = (int) newPos;
287        return this;
288    }
289 
290    @Override
291    public int write(ByteBuffer src) throws IOException {
292        int len = src.remaining();
293        if (len == 0) {
294            return 0;
295        }
296        data.touch(readOnly);
297        pos = data.readWrite(pos, src.array(),
298                src.arrayOffset() + src.position(), len, true);
299        src.position(src.position() + len);
300        return len;
301    }
302 
303    @Override
304    public int read(ByteBuffer dst) throws IOException {
305        int len = dst.remaining();
306        if (len == 0) {
307            return 0;
308        }
309        long newPos = data.readWrite(pos, dst.array(),
310                dst.arrayOffset() + dst.position(), len, false);
311        len = (int) (newPos - pos);
312        if (len <= 0) {
313            return -1;
314        }
315        dst.position(dst.position() + len);
316        pos = newPos;
317        return len;
318    }
319 
320    @Override
321    public long position() {
322        return pos;
323    }
324 
325    @Override
326    public void implCloseChannel() throws IOException {
327        pos = 0;
328    }
329 
330    @Override
331    public void force(boolean metaData) throws IOException {
332        // do nothing
333    }
334 
335    @Override
336    public synchronized FileLock tryLock(long position, long size,
337            boolean shared) throws IOException {
338        if (shared) {
339            if (!data.lockShared()) {
340                return null;
341            }
342        } else {
343            if (!data.lockExclusive()) {
344                return null;
345            }
346        }
347 
348        // cast to FileChannel to avoid JDK 1.7 ambiguity
349        FileLock lock = new FileLock((FileChannel) null, position, size, shared) {
350 
351            @Override
352            public boolean isValid() {
353                return true;
354            }
355 
356            @Override
357            public void release() throws IOException {
358                data.unlock();
359            }
360        };
361        return lock;
362    }
363 
364    @Override
365    public String toString() {
366        return data.getName();
367    }
368 
369}
370 
371/**
372 * This class contains the data of an in-memory random access file.
373 * Data compression using the LZF algorithm is supported as well.
374 */
375class FileMemData {
376 
377    private static final int CACHE_SIZE = 8;
378    private static final int BLOCK_SIZE_SHIFT = 10;
379    private static final int BLOCK_SIZE = 1 << BLOCK_SIZE_SHIFT;
380    private static final int BLOCK_SIZE_MASK = BLOCK_SIZE - 1;
381    private static final CompressLZF LZF = new CompressLZF();
382    private static final byte[] BUFFER = new byte[BLOCK_SIZE * 2];
383    private static final byte[] COMPRESSED_EMPTY_BLOCK;
384 
385    private static final Cache<CompressItem, CompressItem> COMPRESS_LATER =
386        new Cache<CompressItem, CompressItem>(CACHE_SIZE);
387 
388    private String name;
389    private final boolean compress;
390    private long length;
391    private byte[][] data;
392    private long lastModified;
393    private boolean isReadOnly;
394    private boolean isLockedExclusive;
395    private int sharedLockCount;
396 
397    static {
398        byte[] n = new byte[BLOCK_SIZE];
399        int len = LZF.compress(n, BLOCK_SIZE, BUFFER, 0);
400        COMPRESSED_EMPTY_BLOCK = new byte[len];
401        System.arraycopy(BUFFER, 0, COMPRESSED_EMPTY_BLOCK, 0, len);
402    }
403 
404    FileMemData(String name, boolean compress) {
405        this.name = name;
406        this.compress = compress;
407        data = new byte[0][];
408        lastModified = System.currentTimeMillis();
409    }
410 
411    /**
412     * Lock the file in exclusive mode if possible.
413     *
414     * @return if locking was successful
415     */
416    synchronized boolean lockExclusive() {
417        if (sharedLockCount > 0 || isLockedExclusive) {
418            return false;
419        }
420        isLockedExclusive = true;
421        return true;
422    }
423 
424    /**
425     * Lock the file in shared mode if possible.
426     *
427     * @return if locking was successful
428     */
429    synchronized boolean lockShared() {
430        if (isLockedExclusive) {
431            return false;
432        }
433        sharedLockCount++;
434        return true;
435    }
436 
437    /**
438     * Unlock the file.
439     */
440    synchronized void unlock() {
441        if (isLockedExclusive) {
442            isLockedExclusive = false;
443        } else {
444            sharedLockCount = Math.max(0, sharedLockCount - 1);
445        }
446    }
447 
448    /**
449     * This small cache compresses the data if an element leaves the cache.
450     */
451    static class Cache<K, V> extends LinkedHashMap<K, V> {
452 
453        private static final long serialVersionUID = 1L;
454        private final int size;
455 
456        Cache(int size) {
457            super(size, (float) 0.75, true);
458            this.size = size;
459        }
460 
461        @Override
462        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
463            if (size() < size) {
464                return false;
465            }
466            CompressItem c = (CompressItem) eldest.getKey();
467            compress(c.data, c.page);
468            return true;
469        }
470    }
471 
472    /**
473     * Represents a compressed item.
474     */
475    static class CompressItem {
476 
477        /**
478         * The file data.
479         */
480        byte[][] data;
481 
482        /**
483         * The page to compress.
484         */
485        int page;
486 
487        @Override
488        public int hashCode() {
489            return page;
490        }
491 
492        @Override
493        public boolean equals(Object o) {
494            if (o instanceof CompressItem) {
495                CompressItem c = (CompressItem) o;
496                return c.data == data && c.page == page;
497            }
498            return false;
499        }
500 
501    }
502 
503    private static void compressLater(byte[][] data, int page) {
504        CompressItem c = new CompressItem();
505        c.data = data;
506        c.page = page;
507        synchronized (LZF) {
508            COMPRESS_LATER.put(c, c);
509        }
510    }
511 
512    private static void expand(byte[][] data, int page) {
513        byte[] d = data[page];
514        if (d.length == BLOCK_SIZE) {
515            return;
516        }
517        byte[] out = new byte[BLOCK_SIZE];
518        if (d != COMPRESSED_EMPTY_BLOCK) {
519            synchronized (LZF) {
520                LZF.expand(d, 0, d.length, out, 0, BLOCK_SIZE);
521            }
522        }
523        data[page] = out;
524    }
525 
526    /**
527     * Compress the data in a byte array.
528     *
529     * @param data the page array
530     * @param page which page to compress
531     */
532    static void compress(byte[][] data, int page) {
533        byte[] d = data[page];
534        synchronized (LZF) {
535            int len = LZF.compress(d, BLOCK_SIZE, BUFFER, 0);
536            if (len <= BLOCK_SIZE) {
537                d = new byte[len];
538                System.arraycopy(BUFFER, 0, d, 0, len);
539                data[page] = d;
540            }
541        }
542    }
543 
544    /**
545     * Update the last modified time.
546     *
547     * @param openReadOnly if the file was opened in read-only mode
548     */
549    void touch(boolean openReadOnly) throws IOException {
550        if (isReadOnly || openReadOnly) {
551            throw new IOException("Read only");
552        }
553        lastModified = System.currentTimeMillis();
554    }
555 
556    /**
557     * Get the file length.
558     *
559     * @return the length
560     */
561    long length() {
562        return length;
563    }
564 
565    /**
566     * Truncate the file.
567     *
568     * @param newLength the new length
569     */
570    void truncate(long newLength) {
571        changeLength(newLength);
572        long end = MathUtils.roundUpLong(newLength, BLOCK_SIZE);
573        if (end != newLength) {
574            int lastPage = (int) (newLength >>> BLOCK_SIZE_SHIFT);
575            expand(data, lastPage);
576            byte[] d = data[lastPage];
577            for (int i = (int) (newLength & BLOCK_SIZE_MASK); i < BLOCK_SIZE; i++) {
578                d[i] = 0;
579            }
580            if (compress) {
581                compressLater(data, lastPage);
582            }
583        }
584    }
585 
586    private void changeLength(long len) {
587        length = len;
588        len = MathUtils.roundUpLong(len, BLOCK_SIZE);
589        int blocks = (int) (len >>> BLOCK_SIZE_SHIFT);
590        if (blocks != data.length) {
591            byte[][] n = new byte[blocks][];
592            System.arraycopy(data, 0, n, 0, Math.min(data.length, n.length));
593            for (int i = data.length; i < blocks; i++) {
594                n[i] = COMPRESSED_EMPTY_BLOCK;
595            }
596            data = n;
597        }
598    }
599 
600    /**
601     * Read or write.
602     *
603     * @param pos the position
604     * @param b the byte array
605     * @param off the offset within the byte array
606     * @param len the number of bytes
607     * @param write true for writing
608     * @return the new position
609     */
610    long readWrite(long pos, byte[] b, int off, int len, boolean write) {
611        long end = pos + len;
612        if (end > length) {
613            if (write) {
614                changeLength(end);
615            } else {
616                len = (int) (length - pos);
617            }
618        }
619        while (len > 0) {
620            int l = (int) Math.min(len, BLOCK_SIZE - (pos & BLOCK_SIZE_MASK));
621            int page = (int) (pos >>> BLOCK_SIZE_SHIFT);
622            expand(data, page);
623            byte[] block = data[page];
624            int blockOffset = (int) (pos & BLOCK_SIZE_MASK);
625            if (write) {
626                System.arraycopy(b, off, block, blockOffset, l);
627            } else {
628                System.arraycopy(block, blockOffset, b, off, l);
629            }
630            if (compress) {
631                compressLater(data, page);
632            }
633            off += l;
634            pos += l;
635            len -= l;
636        }
637        return pos;
638    }
639 
640    /**
641     * Set the file name.
642     *
643     * @param name the name
644     */
645    void setName(String name) {
646        this.name = name;
647    }
648 
649    /**
650     * Get the file name
651     *
652     * @return the name
653     */
654    String getName() {
655        return name;
656    }
657 
658    /**
659     * Get the last modified time.
660     *
661     * @return the time
662     */
663    long getLastModified() {
664        return lastModified;
665    }
666 
667    /**
668     * Check whether writing is allowed.
669     *
670     * @return true if it is
671     */
672    boolean canWrite() {
673        return !isReadOnly;
674    }
675 
676    /**
677     * Set the read-only flag.
678     *
679     * @return true
680     */
681    boolean setReadOnly() {
682        isReadOnly = true;
683        return true;
684    }
685 
686}
687 
688 

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