-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve the ParseSink
interface
#9653
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -133,7 +133,7 @@ static SerialisationError badArchive(const std::string & s) | |
} | ||
|
||
|
||
static void parseContents(ParseSink & sink, Source & source, const Path & path) | ||
static void parseContents(CreateRegularFileSink & sink, Source & source) | ||
{ | ||
uint64_t size = readLongLong(source); | ||
|
||
|
@@ -147,7 +147,7 @@ static void parseContents(ParseSink & sink, Source & source, const Path & path) | |
auto n = buf.size(); | ||
if ((uint64_t)n > left) n = left; | ||
source(buf.data(), n); | ||
sink.receiveContents({buf.data(), n}); | ||
sink({buf.data(), n}); | ||
left -= n; | ||
} | ||
|
||
|
@@ -164,100 +164,112 @@ struct CaseInsensitiveCompare | |
}; | ||
|
||
|
||
static void parse(ParseSink & sink, Source & source, const Path & path) | ||
static void parse(FileSystemObjectSink & sink, Source & source, const Path & path) | ||
{ | ||
std::string s; | ||
|
||
s = readString(source); | ||
if (s != "(") throw badArchive("expected open tag"); | ||
|
||
enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown; | ||
|
||
std::map<Path, int, CaseInsensitiveCompare> names; | ||
|
||
while (1) { | ||
auto getString = [&]() { | ||
checkInterrupt(); | ||
return readString(source); | ||
}; | ||
|
||
s = readString(source); | ||
// For first iteration | ||
s = getString(); | ||
|
||
while (1) { | ||
|
||
if (s == ")") { | ||
break; | ||
} | ||
|
||
else if (s == "type") { | ||
if (type != tpUnknown) | ||
throw badArchive("multiple type fields"); | ||
std::string t = readString(source); | ||
std::string t = getString(); | ||
|
||
if (t == "regular") { | ||
type = tpRegular; | ||
sink.createRegularFile(path); | ||
sink.createRegularFile(path, [&](auto & crf) { | ||
while (1) { | ||
s = getString(); | ||
|
||
if (s == "contents") { | ||
parseContents(crf, source); | ||
} | ||
|
||
else if (s == "executable") { | ||
auto s2 = getString(); | ||
if (s2 != "") throw badArchive("executable marker has non-empty value"); | ||
crf.isExecutable(); | ||
} | ||
|
||
else break; | ||
} | ||
}); | ||
} | ||
|
||
else if (t == "directory") { | ||
sink.createDirectory(path); | ||
type = tpDirectory; | ||
} | ||
|
||
else if (t == "symlink") { | ||
type = tpSymlink; | ||
} | ||
while (1) { | ||
s = getString(); | ||
|
||
if (s == "entry") { | ||
std::string name, prevName; | ||
|
||
s = getString(); | ||
if (s != "(") throw badArchive("expected open tag"); | ||
|
||
while (1) { | ||
s = getString(); | ||
|
||
if (s == ")") { | ||
break; | ||
} else if (s == "name") { | ||
name = getString(); | ||
if (name.empty() || name == "." || name == ".." || name.find('/') != std::string::npos || name.find((char) 0) != std::string::npos) | ||
throw Error("NAR contains invalid file name '%1%'", name); | ||
if (name <= prevName) | ||
throw Error("NAR directory is not sorted"); | ||
prevName = name; | ||
if (archiveSettings.useCaseHack) { | ||
auto i = names.find(name); | ||
if (i != names.end()) { | ||
debug("case collision between '%1%' and '%2%'", i->first, name); | ||
name += caseHackSuffix; | ||
name += std::to_string(++i->second); | ||
} else | ||
names[name] = 0; | ||
} | ||
Comment on lines
+237
to
+245
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thought: This could be factored out into a sink to sink transformation that performs the renaming. |
||
} else if (s == "node") { | ||
if (name.empty()) throw badArchive("entry name missing"); | ||
parse(sink, source, path + "/" + name); | ||
} else | ||
throw badArchive("unknown field " + s); | ||
} | ||
} | ||
|
||
else throw badArchive("unknown file type " + t); | ||
else break; | ||
} | ||
} | ||
|
||
} | ||
else if (t == "symlink") { | ||
s = getString(); | ||
|
||
else if (s == "contents" && type == tpRegular) { | ||
parseContents(sink, source, path); | ||
sink.closeRegularFile(); | ||
} | ||
if (s != "target") | ||
throw badArchive("expected 'target' got " + s); | ||
|
||
else if (s == "executable" && type == tpRegular) { | ||
auto s = readString(source); | ||
if (s != "") throw badArchive("executable marker has non-empty value"); | ||
sink.isExecutable(); | ||
} | ||
std::string target = getString(); | ||
sink.createSymlink(path, target); | ||
|
||
else if (s == "entry" && type == tpDirectory) { | ||
std::string name, prevName; | ||
|
||
s = readString(source); | ||
if (s != "(") throw badArchive("expected open tag"); | ||
|
||
while (1) { | ||
checkInterrupt(); | ||
|
||
s = readString(source); | ||
|
||
if (s == ")") { | ||
break; | ||
} else if (s == "name") { | ||
name = readString(source); | ||
if (name.empty() || name == "." || name == ".." || name.find('/') != std::string::npos || name.find((char) 0) != std::string::npos) | ||
throw Error("NAR contains invalid file name '%1%'", name); | ||
if (name <= prevName) | ||
throw Error("NAR directory is not sorted"); | ||
prevName = name; | ||
if (archiveSettings.useCaseHack) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (pre-existing) |
||
auto i = names.find(name); | ||
if (i != names.end()) { | ||
debug("case collision between '%1%' and '%2%'", i->first, name); | ||
name += caseHackSuffix; | ||
name += std::to_string(++i->second); | ||
} else | ||
names[name] = 0; | ||
} | ||
} else if (s == "node") { | ||
if (name.empty()) throw badArchive("entry name missing"); | ||
parse(sink, source, path + "/" + name); | ||
} else | ||
throw badArchive("unknown field " + s); | ||
// for the next iteration | ||
s = getString(); | ||
} | ||
} | ||
|
||
else if (s == "target" && type == tpSymlink) { | ||
std::string target = readString(source); | ||
sink.createSymlink(path, target); | ||
else throw badArchive("unknown file type " + t); | ||
|
||
} | ||
|
||
else | ||
|
@@ -266,7 +278,7 @@ static void parse(ParseSink & sink, Source & source, const Path & path) | |
} | ||
|
||
|
||
void parseDump(ParseSink & sink, Source & source) | ||
void parseDump(FileSystemObjectSink & sink, Source & source) | ||
{ | ||
std::string version; | ||
try { | ||
|
@@ -294,7 +306,7 @@ void copyNAR(Source & source, Sink & sink) | |
// FIXME: if 'source' is the output of dumpPath() followed by EOF, | ||
// we should just forward all data directly without parsing. | ||
|
||
NullParseSink parseSink; /* just parse the NAR */ | ||
NullFileSystemObjectSink parseSink; /* just parse the NAR */ | ||
|
||
TeeSource wrapper { source, sink }; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change I did to the parser is basically to replace this extra state variable with control flow. This is needed so that the "content" and "executable" parsing happens within the callback. I then changed the others to keep the code design consistent (and get rid of this enum entirely, as opposed to confusingly just getting rid of
tpRegular
).