1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-08 19:46:02 +01:00

Merge pull request #14291 from NixOS/skip-source

Add skip() method to Source interface to allow efficient seeks
This commit is contained in:
Eelco Dolstra 2025-10-20 15:04:36 +00:00 committed by GitHub
commit ddf7de0a76
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 66 additions and 4 deletions

View file

@ -132,6 +132,11 @@ static void parseContents(CreateRegularFileSink & sink, Source & source)
sink.preallocateContents(size); sink.preallocateContents(size);
if (sink.skipContents) {
source.skip(size + (size % 8 ? 8 - (size % 8) : 0));
return;
}
uint64_t left = size; uint64_t left = size;
std::array<char, 65536> buf; std::array<char, 65536> buf;
@ -166,7 +171,7 @@ static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath
auto expectTag = [&](std::string_view expected) { auto expectTag = [&](std::string_view expected) {
auto tag = getString(); auto tag = getString();
if (tag != expected) if (tag != expected)
throw badArchive("expected tag '%s', got '%s'", expected, tag); throw badArchive("expected tag '%s', got '%s'", expected, tag.substr(0, 1024));
}; };
expectTag("("); expectTag("(");

View file

@ -196,6 +196,8 @@ void NullFileSystemObjectSink::createRegularFile(
void isExecutable() override {} void isExecutable() override {}
} crf; } crf;
crf.skipContents = true;
// Even though `NullFileSystemObjectSink` doesn't do anything, it's important // Even though `NullFileSystemObjectSink` doesn't do anything, it's important
// that we call the function, to e.g. advance the parser using this // that we call the function, to e.g. advance the parser using this
// sink. // sink.

View file

@ -14,6 +14,14 @@ namespace nix {
*/ */
struct CreateRegularFileSink : Sink struct CreateRegularFileSink : Sink
{ {
/**
* If set to true, the sink will not be called with the contents
* of the file. `preallocateContents()` will still be called to
* convey the file size. Useful for sinks that want to efficiently
* discard the contents of the file.
*/
bool skipContents = false;
virtual void isExecutable() = 0; virtual void isExecutable() = 0;
/** /**

View file

@ -97,6 +97,8 @@ struct Source
void drainInto(Sink & sink); void drainInto(Sink & sink);
std::string drain(); std::string drain();
virtual void skip(size_t len);
}; };
/** /**
@ -177,6 +179,7 @@ struct FdSource : BufferedSource
Descriptor fd; Descriptor fd;
size_t read = 0; size_t read = 0;
BackedStringView endOfFileError{"unexpected end-of-file"}; BackedStringView endOfFileError{"unexpected end-of-file"};
bool isSeekable = true;
FdSource() FdSource()
: fd(INVALID_DESCRIPTOR) : fd(INVALID_DESCRIPTOR)
@ -200,6 +203,8 @@ struct FdSource : BufferedSource
*/ */
bool hasData(); bool hasData();
void skip(size_t len) override;
protected: protected:
size_t readUnbuffered(char * data, size_t len) override; size_t readUnbuffered(char * data, size_t len) override;
private: private:

View file

@ -94,9 +94,8 @@ void Source::drainInto(Sink & sink)
{ {
std::array<char, 8192> buf; std::array<char, 8192> buf;
while (true) { while (true) {
size_t n;
try { try {
n = read(buf.data(), buf.size()); auto n = read(buf.data(), buf.size());
sink({buf.data(), n}); sink({buf.data(), n});
} catch (EndOfFile &) { } catch (EndOfFile &) {
break; break;
@ -111,6 +110,16 @@ std::string Source::drain()
return std::move(s.s); return std::move(s.s);
} }
void Source::skip(size_t len)
{
std::array<char, 8192> buf;
while (len) {
auto n = read(buf.data(), std::min(len, buf.size()));
assert(n <= len);
len -= n;
}
}
size_t BufferedSource::read(char * data, size_t len) size_t BufferedSource::read(char * data, size_t len)
{ {
if (!buffer) if (!buffer)
@ -120,7 +129,7 @@ size_t BufferedSource::read(char * data, size_t len)
bufPosIn = readUnbuffered(buffer.get(), bufSize); bufPosIn = readUnbuffered(buffer.get(), bufSize);
/* Copy out the data in the buffer. */ /* Copy out the data in the buffer. */
size_t n = len > bufPosIn - bufPosOut ? bufPosIn - bufPosOut : len; auto n = std::min(len, bufPosIn - bufPosOut);
memcpy(data, buffer.get() + bufPosOut, n); memcpy(data, buffer.get() + bufPosOut, n);
bufPosOut += n; bufPosOut += n;
if (bufPosIn == bufPosOut) if (bufPosIn == bufPosOut)
@ -191,6 +200,39 @@ bool FdSource::hasData()
} }
} }
void FdSource::skip(size_t len)
{
/* Discard data in the buffer. */
if (len && buffer && bufPosIn - bufPosOut) {
if (len >= bufPosIn - bufPosOut) {
len -= bufPosIn - bufPosOut;
bufPosIn = bufPosOut = 0;
} else {
bufPosOut += len;
len = 0;
}
}
#ifndef _WIN32
/* If we can, seek forward in the file to skip the rest. */
if (isSeekable && len) {
if (lseek(fd, len, SEEK_CUR) == -1) {
if (errno == ESPIPE)
isSeekable = false;
else
throw SysError("seeking forward in file");
} else {
read += len;
return;
}
}
#endif
/* Otherwise, skip by reading. */
if (len)
BufferedSource::skip(len);
}
size_t StringSource::read(char * data, size_t len) size_t StringSource::read(char * data, size_t len)
{ {
if (pos == s.size()) if (pos == s.size())