1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-16 07:22:43 +01:00

Source: Add skip() method

This allows FdSource to efficiently skip data we don't care about.
This commit is contained in:
Eelco Dolstra 2025-10-17 13:56:17 +02:00
parent ad2360c59f
commit daa7e0d2e9
2 changed files with 50 additions and 3 deletions

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, 64 * 1024> 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())