1 /** 2 * Cache the contents from files read from disk into memory. 3 * 4 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/filecache.d, filecache.d) 8 * Documentation: https://dlang.org/phobos/dmd_filecache.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/filecache.d 10 */ 11 12 module dmd.filecache; 13 14 import dmd.root.stringtable; 15 import dmd.root.array; 16 import dmd.root.file; 17 import dmd.root.filename; 18 19 import core.stdc.stdio; 20 21 /** 22 A line-by-line representation of a $(REF File, dmd,root,file). 23 */ 24 class FileAndLines 25 { 26 FileName* file; 27 FileBuffer* buffer; 28 const(char[])[] lines; 29 30 nothrow: 31 32 /** 33 File to read and split into its lines. 34 */ 35 this(const(char)[] filename) 36 { 37 file = new FileName(filename); 38 readAndSplit(); 39 } 40 41 // Read a file and split the file buffer linewise 42 private void readAndSplit() 43 { 44 auto readResult = File.read(file.toChars()); 45 // FIXME: check success 46 // take ownership of buffer 47 buffer = new FileBuffer(readResult.extractSlice()); 48 ubyte* buf = buffer.data.ptr; 49 // slice into lines 50 while (*buf) 51 { 52 auto prevBuf = buf; 53 for (; *buf != '\n' && *buf != '\r'; buf++) 54 { 55 if (!*buf) 56 break; 57 } 58 // handle Windows line endings 59 if (*buf == '\r' && *(buf + 1) == '\n') 60 buf++; 61 lines ~= cast(const(char)[]) prevBuf[0 .. buf - prevBuf]; 62 buf++; 63 } 64 } 65 66 void destroy() 67 { 68 if (file) 69 { 70 file.destroy(); 71 file = null; 72 buffer.destroy(); 73 buffer = null; 74 lines.destroy(); 75 lines = null; 76 } 77 } 78 79 ~this() 80 { 81 destroy(); 82 } 83 } 84 85 /** 86 A simple file cache that can be used to avoid reading the same file multiple times. 87 It stores its cached files as $(LREF FileAndLines) 88 */ 89 struct FileCache 90 { 91 private StringTable!(FileAndLines) files; 92 93 nothrow: 94 95 /** 96 Add or get a file from the file cache. 97 If the file isn't part of the cache, it will be read from the filesystem. 98 If the file has been read before, the cached file object will be returned 99 100 Params: 101 file = file to load in (or get from) the cache 102 103 Returns: a $(LREF FileAndLines) object containing a line-by-line representation of the requested file 104 */ 105 FileAndLines addOrGetFile(const(char)[] file) 106 { 107 if (auto payload = files.lookup(file)) 108 { 109 if (payload !is null) 110 return payload.value; 111 } 112 113 auto lines = new FileAndLines(file); 114 files.insert(file, lines); 115 return lines; 116 } 117 118 __gshared fileCache = FileCache(); 119 120 // Initializes the global FileCache singleton 121 static __gshared void _init() 122 { 123 fileCache.initialize(); 124 } 125 126 void initialize() 127 { 128 files._init(); 129 } 130 131 void deinitialize() 132 { 133 foreach (sv; files) 134 sv.destroy(); 135 files.reset(); 136 } 137 }