aboutsummaryrefslogtreecommitdiff
path: root/gcc/d/dmd/file_manager.d
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/d/dmd/file_manager.d')
-rw-r--r--gcc/d/dmd/file_manager.d268
1 files changed, 96 insertions, 172 deletions
diff --git a/gcc/d/dmd/file_manager.d b/gcc/d/dmd/file_manager.d
index c696a5c..6ad3d62 100644
--- a/gcc/d/dmd/file_manager.d
+++ b/gcc/d/dmd/file_manager.d
@@ -11,10 +11,12 @@
module dmd.file_manager;
import core.stdc.stdio;
+import dmd.common.outbuffer;
import dmd.root.stringtable : StringTable;
import dmd.root.file : File, Buffer;
import dmd.root.filename : FileName, isDirSeparator;
import dmd.root.string : toDString;
+import dmd.errors;
import dmd.globals;
import dmd.identifier;
import dmd.location;
@@ -70,44 +72,84 @@ private struct PathStack
}
}
-final class FileManager
+/***************************
+ * Cache path lookups so the operating system
+ * is only consulted once for each path.
+ */
+private struct PathCache
{
- private StringTable!(const(ubyte)[]) files; // contents of files indexed by file name
- private StringTable!(bool) packageStatus;
+ /* for filespec "a/b/c/d.ext"
+ * a b and c are directories, a, a/b, a/b/c are paths.
+ */
- // check if the package path of the given path exists. The input path is
- // expected to contain the full path to the module, so the parent
- // directories of that path are checked.
- private bool packageExists(const(char)[] p) nothrow
+ StringTable!(bool) pathStatus; // cached value of does a path exist or not
+
+ nothrow:
+
+ /**
+ * Determine if the path part of path/filename exists.
+ * Cache the results for the path and each left-justified subpath of the path.
+ * Params:
+ * filespec = path/filename
+ * Returns:
+ * true if path exists, false if it does not
+ */
+ bool pathExists(const(char)[] filespec) nothrow
{
- // step 1, look for the closest parent path that is cached
+ /* look for the longest leftmost parent path that is cached
+ * by starting at the right and working to the left
+ */
bool exists = true;
- auto st = PathStack(p);
+ auto st = PathStack(filespec);
while (st.up) {
- if (auto cached = packageStatus.lookup(st.cur)) {
+ if (auto cached = pathStatus.lookup(st.cur)) {
exists = cached.value;
break;
}
}
- // found a parent that is cached (or reached the end of the stack).
- // step 2, traverse back up the stack storing either false if the
- // parent doesn't exist, or the result of the `exists` call if it does.
+ /* found a parent path that is cached (or reached the left end of the path).
+ * Now move right caching the results of those directories.
+ * Once a directory is found to not exist, all the directories
+ * to the right of it do not exist
+ */
while (st.down) {
if (!exists)
- packageStatus.insert(st.cur, false);
+ pathStatus.insert(st.cur, false);
else
- exists = packageStatus.insert(st.cur, FileName.exists(st.cur) == 2).value;
+ exists = pathStatus.insert(st.cur, FileName.exists(st.cur) == 2).value;
}
- // at this point, exists should be the answer.
return exists;
}
+ /**
+ * Ask if path ends in a directory.
+ * Cache result for speed.
+ * Params:
+ * path = a path
+ * Returns:
+ * true if it's a path, false if not
+ */
+ bool isExistingPath(const char[] path)
+ {
+ auto cached = pathStatus.lookup(path);
+ if (!cached)
+ cached = pathStatus.insert(path, FileName.exists(path) == 2);
+ return cached.value;
+ }
+}
+
+final class FileManager
+{
+ private StringTable!(const(ubyte)[]) files; // contents of files indexed by file name
+
+ private PathCache pathCache;
+
///
public this () nothrow
{
this.files._init();
- this.packageStatus._init();
+ this.pathCache.pathStatus._init();
}
nothrow:
@@ -117,18 +159,18 @@ nothrow:
* Does not open the file.
* Params:
* filename = as supplied by the user
- * path = path to look for filename
+ * paths = paths to look for filename
* Returns:
* the found file name or
* `null` if it is not different from filename.
*/
- const(char)[] lookForSourceFile(const char[] filename, const char*[] path)
+ const(char)[] lookForSourceFile(const char[] filename, const char*[] paths)
{
//printf("lookForSourceFile(`%.*s`)\n", cast(int)filename.length, filename.ptr);
- /* Search along path[] for .di file, then .d file.
+ /* Search along paths[] for .di file, then .d file.
*/
// see if we should check for the module locally.
- bool checkLocal = packageExists(filename);
+ bool checkLocal = pathCache.pathExists(filename);
const sdi = FileName.forceExt(filename, hdr_ext);
if (checkLocal && FileName.exists(sdi) == 1)
return sdi;
@@ -144,12 +186,9 @@ nothrow:
if (checkLocal)
{
- auto cached = packageStatus.lookup(filename);
- if (!cached)
- cached = packageStatus.insert(filename, FileName.exists(filename) == 2);
- if (cached.value)
+ if (pathCache.isExistingPath(filename))
{
- /* The filename exists and it's a directory.
+ /* The filename exists but it's a directory.
* Therefore, the result should be: filename/package.d
* iff filename/package.d is a file
*/
@@ -167,15 +206,15 @@ nothrow:
if (FileName.absolute(filename))
return null;
- if (!path.length)
+ if (!paths.length)
return null;
- foreach (entry; path)
+ foreach (entry; paths)
{
const p = entry.toDString();
const(char)[] n = FileName.combine(p, sdi);
- if (!packageExists(n)) {
+ if (!pathCache.pathExists(n)) {
FileName.free(n.ptr);
continue; // no need to check for anything else.
}
@@ -190,19 +229,11 @@ nothrow:
}
FileName.free(n.ptr);
- const b = FileName.removeExt(filename);
- n = FileName.combine(p, b);
- FileName.free(b.ptr);
-
+ n = FileName.combine(p, FileName.sansExt(filename));
scope(exit) FileName.free(n.ptr);
// also cache this if we are looking for package.d[i]
- auto cached = packageStatus.lookup(n);
- if (!cached) {
- cached = packageStatus.insert(n, FileName.exists(n) == 2);
- }
-
- if (cached.value)
+ if (pathCache.isExistingPath(n))
{
const n2i = FileName.combine(n, package_di);
if (FileName.exists(n2i) == 1)
@@ -216,7 +247,7 @@ nothrow:
}
}
- /* ImportC: No D modules found, now search along path[] for .i file, then .c file.
+ /* ImportC: No D modules found, now search along paths[] for .i file, then .c file.
*/
const si = FileName.forceExt(filename, i_ext);
if (FileName.exists(si) == 1)
@@ -227,7 +258,7 @@ nothrow:
if (FileName.exists(sc) == 1)
return sc;
scope(exit) FileName.free(sc.ptr);
- foreach (entry; path)
+ foreach (entry; paths)
{
const p = entry.toDString();
@@ -261,128 +292,31 @@ nothrow:
if (auto val = files.lookup(name)) // if `name` is cached
return val.value; // return its contents
+ OutBuffer buf;
if (name == "__stdin.d") // special name for reading from stdin
{
- const ubyte[] buffer = readFromStdin().extractSlice();
- if (this.files.insert(name, buffer) is null)
- // this.files already contains the name
- assert(0, "stdin: Insert after lookup failure should never return `null`");
- return buffer;
+ if (readFromStdin(buf))
+ fatal();
}
+ else
+ {
+ if (FileName.exists(name) != 1) // if not an ordinary file
+ return null;
- if (FileName.exists(name) != 1) // if not an ordinary file
- return null;
+ if (File.read(name, buf))
+ return null; // failed
+ }
- auto readResult = File.read(name);
- if (!readResult.success)
- return null;
+ buf.write32(0); // terminating dchar 0
- const ubyte[] fb = readResult.extractSlice();
+ const length = buf.length;
+ const ubyte[] fb = cast(ubyte[])(buf.extractSlice()[0 .. length - 4]);
if (files.insert(name, fb) is null)
assert(0, "Insert after lookup failure should never return `null`");
return fb;
}
- /**********************************
- * Take `text` and turn it into an InputRange that emits
- * slices into `text` for each line.
- * Params:
- * text = array of characters
- * Returns:
- * InputRange accessing `text` as a sequence of lines
- * Reference:
- * `std.string.splitLines()`
- */
- auto splitLines(const char[] text)
- {
- struct Range
- {
- @safe:
- @nogc:
- nothrow:
- pure:
- private:
-
- const char[] text;
- size_t index; // index of start of line
- size_t eolIndex; // index of end of line before newline characters
- size_t nextIndex; // index past end of line
-
- public this(const char[] text)
- {
- this.text = text;
- }
-
- public bool empty() { return index == text.length; }
-
- public void popFront() { advance(); index = nextIndex; }
-
- public const(char)[] front() { advance(); return text[index .. eolIndex]; }
-
- private void advance()
- {
- if (index != nextIndex) // if already advanced
- return;
-
- for (size_t i = index; i < text.length; ++i)
- {
- switch (text[i])
- {
- case '\v', '\f', '\n':
- eolIndex = i;
- nextIndex = i + 1;
- return;
-
- case '\r':
- if (i + 1 < text.length && text[i + 1] == '\n') // decode "\r\n"
- {
- eolIndex = i;
- nextIndex = i + 2;
- return;
- }
- eolIndex = i;
- nextIndex = i + 1;
- return;
-
- /* Manually decode:
- * NEL is C2 85
- */
- case 0xC2:
- if (i + 1 < text.length && text[i + 1] == 0x85)
- {
- eolIndex = i;
- nextIndex = i + 2;
- return;
- }
- break;
-
- /* Manually decode:
- * lineSep is E2 80 A8
- * paraSep is E2 80 A9
- */
- case 0xE2:
- if (i + 2 < text.length &&
- text[i + 1] == 0x80 &&
- (text[i + 2] == 0xA8 || text[i + 2] == 0xA9)
- )
- {
- eolIndex = i;
- nextIndex = i + 3;
- return;
- }
- break;
-
- default:
- break;
- }
- }
- }
- }
-
- return Range(text);
- }
-
/**
* Adds the contents of a file to the table.
* Params:
@@ -398,44 +332,34 @@ nothrow:
}
}
-private Buffer readFromStdin() nothrow
+private bool readFromStdin(ref OutBuffer sink) nothrow
{
import core.stdc.stdio;
import dmd.errors;
- import dmd.root.rmem;
- enum bufIncrement = 128 * 1024;
- size_t pos = 0;
- size_t sz = bufIncrement;
+ enum BufIncrement = 128 * 1024;
- ubyte* buffer = null;
- for (;;)
+ for (size_t j; 1; ++j)
{
- buffer = cast(ubyte*)mem.xrealloc(buffer, sz + 4); // +2 for sentinel and +2 for lexer
+ char[] buffer = sink.allocate(BufIncrement);
// Fill up buffer
+ size_t filled = 0;
do
{
- assert(sz > pos);
- size_t rlen = fread(buffer + pos, 1, sz - pos, stdin);
- pos += rlen;
+ filled += fread(buffer.ptr + filled, 1, buffer.length - filled, stdin);
if (ferror(stdin))
{
import core.stdc.errno;
error(Loc.initial, "cannot read from stdin, errno = %d", errno);
- fatal();
+ return true;
}
- if (feof(stdin))
+ if (feof(stdin)) // successful completion
{
- // We're done
- assert(pos < sz + 2);
- buffer[pos .. pos + 4] = '\0';
- return Buffer(buffer[0 .. pos]);
+ sink.setsize(j * BufIncrement + filled);
+ return false;
}
- } while (pos < sz);
-
- // Buffer full, expand
- sz += bufIncrement;
+ } while (filled < BufIncrement);
}
assert(0);