diff options
Diffstat (limited to 'third_party/nix/src/libutil/serialise.hh')
-rw-r--r-- | third_party/nix/src/libutil/serialise.hh | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/third_party/nix/src/libutil/serialise.hh b/third_party/nix/src/libutil/serialise.hh new file mode 100644 index 000000000000..c6d1d814dbdd --- /dev/null +++ b/third_party/nix/src/libutil/serialise.hh @@ -0,0 +1,289 @@ +#pragma once + +#include <memory> + +#include "libutil/types.hh" +#include "libutil/util.hh" + +namespace nix { + +/* Abstract destination of binary data. */ +struct Sink { + virtual ~Sink() {} + virtual void operator()(const unsigned char* data, size_t len) = 0; + virtual bool good() { return true; } + + void operator()(const std::string& s) { + (*this)((const unsigned char*)s.data(), s.size()); + } +}; + +/* A buffered abstract sink. */ +struct BufferedSink : Sink { + size_t bufSize, bufPos; + std::unique_ptr<unsigned char[]> buffer; + + explicit BufferedSink(size_t bufSize = 32 * 1024) + : bufSize(bufSize), bufPos(0), buffer(nullptr) {} + + void operator()(const unsigned char* data, size_t len) override; + + void operator()(const std::string& s) { Sink::operator()(s); } + + void flush(); + + virtual void write(const unsigned char* data, size_t len) = 0; +}; + +/* Abstract source of binary data. */ +struct Source { + virtual ~Source() {} + + /* Store exactly ‘len’ bytes in the buffer pointed to by ‘data’. + It blocks until all the requested data is available, or throws + an error if it is not going to be available. */ + void operator()(unsigned char* data, size_t len); + + /* Store up to ‘len’ in the buffer pointed to by ‘data’, and + return the number of bytes stored. It blocks until at least + one byte is available. */ + virtual size_t read(unsigned char* data, size_t len) = 0; + + virtual bool good() { return true; } + + std::string drain(); +}; + +/* A buffered abstract source. */ +struct BufferedSource : Source { + size_t bufSize, bufPosIn, bufPosOut; + std::unique_ptr<unsigned char[]> buffer; + + explicit BufferedSource(size_t bufSize = 32 * 1024) + : bufSize(bufSize), bufPosIn(0), bufPosOut(0), buffer(nullptr) {} + + size_t read(unsigned char* data, size_t len) override; + + bool hasData(); + + protected: + /* Underlying read call, to be overridden. */ + virtual size_t readUnbuffered(unsigned char* data, size_t len) = 0; +}; + +/* A sink that writes data to a file descriptor. */ +struct FdSink : BufferedSink { + int fd; + bool warn = false; + size_t written = 0; + + FdSink() : fd(-1) {} + explicit FdSink(int fd) : fd(fd) {} + FdSink(FdSink&&) = default; + + FdSink& operator=(FdSink&& s) { + flush(); + fd = s.fd; + s.fd = -1; + warn = s.warn; + written = s.written; + return *this; + } + + ~FdSink(); + + void write(const unsigned char* data, size_t len) override; + + bool good() override; + + private: + bool _good = true; +}; + +/* A source that reads data from a file descriptor. */ +struct FdSource : BufferedSource { + int fd; + size_t read = 0; + + FdSource() : fd(-1) {} + explicit FdSource(int fd) : fd(fd) {} + FdSource(FdSource&&) = default; + + FdSource& operator=(FdSource&& s) { + fd = s.fd; + s.fd = -1; + read = s.read; + return *this; + } + + bool good() override; + + protected: + size_t readUnbuffered(unsigned char* data, size_t len) override; + + private: + bool _good = true; +}; + +/* A sink that writes data to a string. */ +struct StringSink : Sink { + ref<std::string> s; + StringSink() : s(make_ref<std::string>()){}; + explicit StringSink(ref<std::string> s) : s(s){}; + void operator()(const unsigned char* data, size_t len) override; +}; + +/* A source that reads data from a string. */ +struct StringSource : Source { + const std::string& s; + size_t pos; + explicit StringSource(const std::string& _s) : s(_s), pos(0) {} + size_t read(unsigned char* data, size_t len) override; +}; + +/* Adapter class of a Source that saves all data read to `s'. */ +struct TeeSource : Source { + Source& orig; + ref<std::string> data; + explicit TeeSource(Source& orig) + : orig(orig), data(make_ref<std::string>()) {} + size_t read(unsigned char* data, size_t len) { + size_t n = orig.read(data, len); + this->data->append((const char*)data, n); + return n; + } +}; + +/* A reader that consumes the original Source until 'size'. */ +struct SizedSource : Source { + Source& orig; + size_t remain; + SizedSource(Source& orig, size_t size) : orig(orig), remain(size) {} + size_t read(unsigned char* data, size_t len) { + if (this->remain <= 0) { + throw EndOfFile("sized: unexpected end-of-file"); + } + len = std::min(len, this->remain); + size_t n = this->orig.read(data, len); + this->remain -= n; + return n; + } + + /* Consume the original source until no remain data is left to consume. */ + size_t drainAll() { + std::vector<unsigned char> buf(8192); + size_t sum = 0; + while (this->remain > 0) { + size_t n = read(buf.data(), buf.size()); + sum += n; + } + return sum; + } +}; + +/* Convert a function into a sink. */ +struct LambdaSink : Sink { + typedef std::function<void(const unsigned char*, size_t)> lambda_t; + + lambda_t lambda; + + explicit LambdaSink(const lambda_t& lambda) : lambda(lambda) {} + + virtual void operator()(const unsigned char* data, size_t len) { + lambda(data, len); + } +}; + +/* Convert a function into a source. */ +struct LambdaSource : Source { + using lambda_t = std::function<size_t(unsigned char*, size_t)>; + + lambda_t lambda; + + explicit LambdaSource(const lambda_t& lambda) : lambda(lambda) {} + + size_t read(unsigned char* data, size_t len) override { + return lambda(data, len); + } +}; + +/* Convert a function that feeds data into a Sink into a Source. The + Source executes the function as a coroutine. */ +std::unique_ptr<Source> sinkToSource( + const std::function<void(Sink&)>& fun, + const std::function<void()>& eof = []() { + throw EndOfFile("coroutine has finished"); + }); + +void writePadding(size_t len, Sink& sink); +void writeString(const unsigned char* buf, size_t len, Sink& sink); + +inline Sink& operator<<(Sink& sink, uint64_t n) { + unsigned char buf[8]; + buf[0] = n & 0xff; + buf[1] = (n >> 8) & 0xff; + buf[2] = (n >> 16) & 0xff; + buf[3] = (n >> 24) & 0xff; + buf[4] = (n >> 32) & 0xff; + buf[5] = (n >> 40) & 0xff; + buf[6] = (n >> 48) & 0xff; + buf[7] = (unsigned char)(n >> 56) & 0xff; + sink(buf, sizeof(buf)); + return sink; +} + +Sink& operator<<(Sink& sink, const std::string& s); +Sink& operator<<(Sink& sink, const Strings& s); +Sink& operator<<(Sink& sink, const StringSet& s); + +MakeError(SerialisationError, Error); + +template <typename T> +T readNum(Source& source) { + unsigned char buf[8]; + source(buf, sizeof(buf)); + + uint64_t n = + ((unsigned long long)buf[0]) | ((unsigned long long)buf[1] << 8) | + ((unsigned long long)buf[2] << 16) | ((unsigned long long)buf[3] << 24) | + ((unsigned long long)buf[4] << 32) | ((unsigned long long)buf[5] << 40) | + ((unsigned long long)buf[6] << 48) | ((unsigned long long)buf[7] << 56); + + if (n > std::numeric_limits<T>::max()) { + throw SerialisationError("serialised integer %d is too large for type '%s'", + n, typeid(T).name()); + } + + return (T)n; +} + +inline unsigned int readInt(Source& source) { + return readNum<unsigned int>(source); +} + +inline uint64_t readLongLong(Source& source) { + return readNum<uint64_t>(source); +} + +void readPadding(size_t len, Source& source); +size_t readString(unsigned char* buf, size_t max, Source& source); +std::string readString(Source& source, + size_t max = std::numeric_limits<size_t>::max()); +template <class T> +T readStrings(Source& source); + +Source& operator>>(Source& in, std::string& s); + +template <typename T> +Source& operator>>(Source& in, T& n) { + n = readNum<T>(in); + return in; +} + +template <typename T> +Source& operator>>(Source& in, bool& b) { + b = readNum<uint64_t>(in); + return in; +} + +} // namespace nix |