Commit 227a4911 authored by Kenton Varda's avatar Kenton Varda

Refactor compiler binary into a multi-tool with sub-commands like 'compile' and…

Refactor compiler binary into a multi-tool with sub-commands like 'compile' and 'decode'.  The latter dynamically decodes binary input to text based on a provided schema file.
parent 7c9309be
...@@ -214,6 +214,7 @@ public: ...@@ -214,6 +214,7 @@ public:
virtual ~Impl(); virtual ~Impl();
uint64_t add(const Module& module, Mode mode) const; uint64_t add(const Module& module, Mode mode) const;
kj::Maybe<uint64_t> lookup(uint64_t parent, kj::StringPtr childName) const;
const CompiledModule& add(const Module& parsedModule) const; const CompiledModule& add(const Module& parsedModule) const;
struct Workspace { struct Workspace {
...@@ -785,6 +786,15 @@ uint64_t Compiler::Impl::add(const Module& module, Mode mode) const { ...@@ -785,6 +786,15 @@ uint64_t Compiler::Impl::add(const Module& module, Mode mode) const {
return node.getId(); return node.getId();
} }
kj::Maybe<uint64_t> Compiler::Impl::lookup(uint64_t parent, kj::StringPtr childName) const {
// We know this won't use the workspace, so we need not lock it.
KJ_IF_MAYBE(parentNode, findNode(parent)) {
return parentNode->lookupMember(childName).map([](const Node& n) { return n.getId(); });
} else {
KJ_FAIL_REQUIRE("lookup()s parameter 'parent' must be a known ID.", parent);
}
}
void Compiler::Impl::load(const SchemaLoader& loader, uint64_t id) const { void Compiler::Impl::load(const SchemaLoader& loader, uint64_t id) const {
KJ_IF_MAYBE(node, findNode(id)) { KJ_IF_MAYBE(node, findNode(id)) {
if (&loader == &finalLoader) { if (&loader == &finalLoader) {
...@@ -806,6 +816,10 @@ uint64_t Compiler::add(const Module& module, Mode mode) const { ...@@ -806,6 +816,10 @@ uint64_t Compiler::add(const Module& module, Mode mode) const {
return impl->add(module, mode); return impl->add(module, mode);
} }
kj::Maybe<uint64_t> Compiler::lookup(uint64_t parent, kj::StringPtr childName) const {
return impl->lookup(parent, childName);
}
const SchemaLoader& Compiler::getLoader() const { const SchemaLoader& Compiler::getLoader() const {
return impl->getFinalLoader(); return impl->getFinalLoader();
} }
......
...@@ -82,6 +82,11 @@ public: ...@@ -82,6 +82,11 @@ public:
// errors while compiling (reported via `module.addError()`), then the SchemaLoader may behave as // errors while compiling (reported via `module.addError()`), then the SchemaLoader may behave as
// if the node doesn't exist, or may return an invalid partial Schema. // if the node doesn't exist, or may return an invalid partial Schema.
kj::Maybe<uint64_t> lookup(uint64_t parent, kj::StringPtr childName) const;
// Given the type ID of a schema node, find the ID of a node nested within it, without actually
// building either node. Throws an exception if the parent ID is not recognized; returns null
// if the parent has no child of the given name.
const SchemaLoader& getLoader() const; const SchemaLoader& getLoader() const;
// Get a SchemaLoader backed by this compiler. Schema nodes will be lazily constructed as you // Get a SchemaLoader backed by this compiler. Schema nodes will be lazily constructed as you
// traverse them using this loader. // traverse them using this loader.
......
...@@ -59,7 +59,7 @@ public: ...@@ -59,7 +59,7 @@ public:
data.append(reinterpret_cast<const char*>(buffer), size); data.append(reinterpret_cast<const char*>(buffer), size);
} }
size_t read(void* buffer, size_t minBytes, size_t maxBytes) override { size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override {
KJ_ASSERT(maxBytes <= data.size() - readPos, "Overran end of stream."); KJ_ASSERT(maxBytes <= data.size() - readPos, "Overran end of stream.");
size_t amount = std::min(maxBytes, std::max(minBytes, preferredReadSize)); size_t amount = std::min(maxBytes, std::max(minBytes, preferredReadSize));
memcpy(buffer, data.data() + readPos, amount); memcpy(buffer, data.data() + readPos, amount);
...@@ -72,7 +72,7 @@ public: ...@@ -72,7 +72,7 @@ public:
readPos += bytes; readPos += bytes;
} }
kj::ArrayPtr<const byte> getReadBuffer() override { kj::ArrayPtr<const byte> tryGetReadBuffer() override {
size_t amount = std::min(data.size() - readPos, preferredReadSize); size_t amount = std::min(data.size() - readPos, preferredReadSize);
return kj::arrayPtr(reinterpret_cast<const byte*>(data.data() + readPos), amount); return kj::arrayPtr(reinterpret_cast<const byte*>(data.data() + readPos), amount);
} }
......
...@@ -33,7 +33,7 @@ namespace _ { // private ...@@ -33,7 +33,7 @@ namespace _ { // private
PackedInputStream::PackedInputStream(kj::BufferedInputStream& inner): inner(inner) {} PackedInputStream::PackedInputStream(kj::BufferedInputStream& inner): inner(inner) {}
PackedInputStream::~PackedInputStream() noexcept(false) {} PackedInputStream::~PackedInputStream() noexcept(false) {}
size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) { size_t PackedInputStream::tryRead(void* dst, size_t minBytes, size_t maxBytes) {
if (maxBytes == 0) { if (maxBytes == 0) {
return 0; return 0;
} }
...@@ -46,8 +46,8 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) { ...@@ -46,8 +46,8 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
uint8_t* const outMin = reinterpret_cast<uint8_t*>(dst) + minBytes; uint8_t* const outMin = reinterpret_cast<uint8_t*>(dst) + minBytes;
kj::ArrayPtr<const byte> buffer = inner.getReadBuffer(); kj::ArrayPtr<const byte> buffer = inner.getReadBuffer();
KJ_REQUIRE(buffer.size() > 0, "Premature end of packed input.") { if (buffer.size() == 0) {
return minBytes; // garbage return 0;
} }
const uint8_t* __restrict__ in = reinterpret_cast<const uint8_t*>(buffer.begin()); const uint8_t* __restrict__ in = reinterpret_cast<const uint8_t*>(buffer.begin());
...@@ -55,7 +55,7 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) { ...@@ -55,7 +55,7 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
inner.skip(buffer.size()); \ inner.skip(buffer.size()); \
buffer = inner.getReadBuffer(); \ buffer = inner.getReadBuffer(); \
KJ_REQUIRE(buffer.size() > 0, "Premature end of packed input.") { \ KJ_REQUIRE(buffer.size() > 0, "Premature end of packed input.") { \
return minBytes; /* garbage */ \ return out - reinterpret_cast<uint8_t*>(dst); \
} \ } \
in = reinterpret_cast<const uint8_t*>(buffer.begin()) in = reinterpret_cast<const uint8_t*>(buffer.begin())
...@@ -127,7 +127,7 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) { ...@@ -127,7 +127,7 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
KJ_REQUIRE(runLength <= outEnd - out, KJ_REQUIRE(runLength <= outEnd - out,
"Packed input did not end cleanly on a segment boundary.") { "Packed input did not end cleanly on a segment boundary.") {
return std::max<size_t>(minBytes, out - reinterpret_cast<uint8_t*>(dst)); // garbage return out - reinterpret_cast<uint8_t*>(dst);
} }
memset(out, 0, runLength); memset(out, 0, runLength);
out += runLength; out += runLength;
...@@ -139,7 +139,7 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) { ...@@ -139,7 +139,7 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
KJ_REQUIRE(runLength <= outEnd - out, KJ_REQUIRE(runLength <= outEnd - out,
"Packed input did not end cleanly on a segment boundary.") { "Packed input did not end cleanly on a segment boundary.") {
return std::max<size_t>(minBytes, out - reinterpret_cast<uint8_t*>(dst)); // garbage return out - reinterpret_cast<uint8_t*>(dst);
} }
uint inRemaining = BUFFER_REMAINING; uint inRemaining = BUFFER_REMAINING;
......
...@@ -40,7 +40,7 @@ public: ...@@ -40,7 +40,7 @@ public:
~PackedInputStream() noexcept(false); ~PackedInputStream() noexcept(false);
// implements InputStream ------------------------------------------ // implements InputStream ------------------------------------------
size_t read(void* buffer, size_t minBytes, size_t maxBytes) override; size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override;
void skip(size_t bytes) override; void skip(size_t bytes) override;
private: private:
......
...@@ -94,7 +94,7 @@ public: ...@@ -94,7 +94,7 @@ public:
data.append(reinterpret_cast<const char*>(buffer), size); data.append(reinterpret_cast<const char*>(buffer), size);
} }
size_t read(void* buffer, size_t minBytes, size_t maxBytes) override { size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override {
KJ_ASSERT(maxBytes <= data.size() - readPos, "Overran end of stream."); KJ_ASSERT(maxBytes <= data.size() - readPos, "Overran end of stream.");
size_t amount = std::min(maxBytes, std::max(minBytes, preferredReadSize)); size_t amount = std::min(maxBytes, std::max(minBytes, preferredReadSize));
memcpy(buffer, data.data() + readPos, amount); memcpy(buffer, data.data() + readPos, amount);
...@@ -107,7 +107,7 @@ public: ...@@ -107,7 +107,7 @@ public:
readPos += bytes; readPos += bytes;
} }
kj::ArrayPtr<const byte> getReadBuffer() override { kj::ArrayPtr<const byte> tryGetReadBuffer() override {
size_t amount = std::min(data.size() - readPos, preferredReadSize); size_t amount = std::min(data.size() - readPos, preferredReadSize);
return kj::arrayPtr(reinterpret_cast<const byte*>(data.data() + readPos), amount); return kj::arrayPtr(reinterpret_cast<const byte*>(data.data() + readPos), amount);
} }
......
...@@ -36,6 +36,10 @@ public: ...@@ -36,6 +36,10 @@ public:
: inputStream(inputStream) {} : inputStream(inputStream) {}
inline ~InputStreamSnappySource() noexcept {} inline ~InputStreamSnappySource() noexcept {}
bool atEnd() {
return inputStream.getReadBuffer().size() == 0;
}
// implements snappy::Source --------------------------------------- // implements snappy::Source ---------------------------------------
size_t Available() const override { size_t Available() const override {
...@@ -68,7 +72,7 @@ SnappyInputStream::SnappyInputStream(BufferedInputStream& inner, kj::ArrayPtr<by ...@@ -68,7 +72,7 @@ SnappyInputStream::SnappyInputStream(BufferedInputStream& inner, kj::ArrayPtr<by
SnappyInputStream::~SnappyInputStream() noexcept(false) {} SnappyInputStream::~SnappyInputStream() noexcept(false) {}
kj::ArrayPtr<const byte> SnappyInputStream::getReadBuffer() { kj::ArrayPtr<const byte> SnappyInputStream::tryGetReadBuffer() {
if (bufferAvailable.size() == 0) { if (bufferAvailable.size() == 0) {
refill(); refill();
} }
...@@ -76,44 +80,53 @@ kj::ArrayPtr<const byte> SnappyInputStream::getReadBuffer() { ...@@ -76,44 +80,53 @@ kj::ArrayPtr<const byte> SnappyInputStream::getReadBuffer() {
return bufferAvailable; return bufferAvailable;
} }
size_t SnappyInputStream::read(void* dst, size_t minBytes, size_t maxBytes) { size_t SnappyInputStream::tryRead(void* dst, size_t minBytes, size_t maxBytes) {
size_t total = 0;
while (minBytes > bufferAvailable.size()) { while (minBytes > bufferAvailable.size()) {
memcpy(dst, bufferAvailable.begin(), bufferAvailable.size()); memcpy(dst, bufferAvailable.begin(), bufferAvailable.size());
dst = reinterpret_cast<byte*>(dst) + bufferAvailable.size(); dst = reinterpret_cast<byte*>(dst) + bufferAvailable.size();
total += bufferAvailable.size();
minBytes -= bufferAvailable.size(); minBytes -= bufferAvailable.size();
maxBytes -= bufferAvailable.size(); maxBytes -= bufferAvailable.size();
refill(); if (!refill()) {
return total;
}
} }
// Serve from current buffer. // Serve from current buffer.
size_t n = std::min(bufferAvailable.size(), maxBytes); size_t n = std::min(bufferAvailable.size(), maxBytes);
memcpy(dst, bufferAvailable.begin(), n); memcpy(dst, bufferAvailable.begin(), n);
bufferAvailable = bufferAvailable.slice(n, bufferAvailable.size()); bufferAvailable = bufferAvailable.slice(n, bufferAvailable.size());
return n; return total + n;
} }
void SnappyInputStream::skip(size_t bytes) { void SnappyInputStream::skip(size_t bytes) {
while (bytes > bufferAvailable.size()) { while (bytes > bufferAvailable.size()) {
bytes -= bufferAvailable.size(); bytes -= bufferAvailable.size();
refill(); KJ_REQUIRE(refill(), "Premature EOF");
} }
bufferAvailable = bufferAvailable.slice(bytes, bufferAvailable.size()); bufferAvailable = bufferAvailable.slice(bytes, bufferAvailable.size());
} }
void SnappyInputStream::refill() { bool SnappyInputStream::refill() {
uint32_t length = 0; uint32_t length = 0;
InputStreamSnappySource snappySource(inner); InputStreamSnappySource snappySource(inner);
if (snappySource.atEnd()) {
return false;
}
KJ_REQUIRE( KJ_REQUIRE(
snappy::RawUncompress( snappy::RawUncompress(
&snappySource, reinterpret_cast<char*>(buffer.begin()), buffer.size(), &length), &snappySource, reinterpret_cast<char*>(buffer.begin()), buffer.size(), &length),
"Snappy decompression failed.") { "Snappy decompression failed.") {
length = 1; // garbage return false;
break;
} }
bufferAvailable = buffer.slice(0, length); bufferAvailable = buffer.slice(0, length);
return true;
} }
// ======================================================================================= // =======================================================================================
......
...@@ -39,8 +39,8 @@ public: ...@@ -39,8 +39,8 @@ public:
~SnappyInputStream() noexcept(false); ~SnappyInputStream() noexcept(false);
// implements BufferedInputStream ---------------------------------- // implements BufferedInputStream ----------------------------------
kj::ArrayPtr<const byte> getReadBuffer() override; kj::ArrayPtr<const byte> tryGetReadBuffer() override;
size_t read(void* buffer, size_t minBytes, size_t maxBytes) override; size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override;
void skip(size_t bytes) override; void skip(size_t bytes) override;
private: private:
...@@ -51,7 +51,7 @@ private: ...@@ -51,7 +51,7 @@ private:
kj::ArrayPtr<byte> buffer; kj::ArrayPtr<byte> buffer;
kj::ArrayPtr<byte> bufferAvailable; kj::ArrayPtr<byte> bufferAvailable;
void refill(); bool refill();
}; };
class SnappyOutputStream: public kj::BufferedOutputStream { class SnappyOutputStream: public kj::BufferedOutputStream {
......
...@@ -101,7 +101,7 @@ public: ...@@ -101,7 +101,7 @@ public:
lazy(lazy) {} lazy(lazy) {}
~TestInputStream() {} ~TestInputStream() {}
size_t read(void* buffer, size_t minBytes, size_t maxBytes) override { size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override {
KJ_ASSERT(maxBytes <= size_t(end - pos), "Overran end of stream."); KJ_ASSERT(maxBytes <= size_t(end - pos), "Overran end of stream.");
size_t amount = lazy ? minBytes : maxBytes; size_t amount = lazy ? minBytes : maxBytes;
memcpy(buffer, pos, amount); memcpy(buffer, pos, amount);
......
...@@ -252,7 +252,7 @@ static void print(std::ostream& os, const DynamicValue::Reader& value, ...@@ -252,7 +252,7 @@ static void print(std::ostream& os, const DynamicValue::Reader& value,
} }
break; break;
case DynamicValue::OBJECT: case DynamicValue::OBJECT:
os << "(opaque object)"; os << "<opaque object>";
break; break;
} }
} }
......
...@@ -35,6 +35,16 @@ OutputStream::~OutputStream() noexcept(false) {} ...@@ -35,6 +35,16 @@ OutputStream::~OutputStream() noexcept(false) {}
BufferedInputStream::~BufferedInputStream() noexcept(false) {} BufferedInputStream::~BufferedInputStream() noexcept(false) {}
BufferedOutputStream::~BufferedOutputStream() noexcept(false) {} BufferedOutputStream::~BufferedOutputStream() noexcept(false) {}
size_t InputStream::read(void* buffer, size_t minBytes, size_t maxBytes) {
size_t n = tryRead(buffer, minBytes, maxBytes);
KJ_REQUIRE(n >= minBytes, "Premature EOF") {
// Pretend we read zeros from the input.
memset(reinterpret_cast<byte*>(buffer) + n, 0, minBytes - n);
return minBytes;
}
return n;
}
void InputStream::skip(size_t bytes) { void InputStream::skip(size_t bytes) {
char scratch[8192]; char scratch[8192];
while (bytes > 0) { while (bytes > 0) {
...@@ -50,6 +60,12 @@ void OutputStream::write(ArrayPtr<const ArrayPtr<const byte>> pieces) { ...@@ -50,6 +60,12 @@ void OutputStream::write(ArrayPtr<const ArrayPtr<const byte>> pieces) {
} }
} }
ArrayPtr<const byte> BufferedInputStream::getReadBuffer() {
auto result = tryGetReadBuffer();
KJ_REQUIRE(result.size() > 0, "Premature EOF");
return result;
}
// ======================================================================================= // =======================================================================================
BufferedInputStreamWrapper::BufferedInputStreamWrapper(InputStream& inner, ArrayPtr<byte> buffer) BufferedInputStreamWrapper::BufferedInputStreamWrapper(InputStream& inner, ArrayPtr<byte> buffer)
...@@ -58,16 +74,16 @@ BufferedInputStreamWrapper::BufferedInputStreamWrapper(InputStream& inner, Array ...@@ -58,16 +74,16 @@ BufferedInputStreamWrapper::BufferedInputStreamWrapper(InputStream& inner, Array
BufferedInputStreamWrapper::~BufferedInputStreamWrapper() noexcept(false) {} BufferedInputStreamWrapper::~BufferedInputStreamWrapper() noexcept(false) {}
ArrayPtr<const byte> BufferedInputStreamWrapper::getReadBuffer() { ArrayPtr<const byte> BufferedInputStreamWrapper::tryGetReadBuffer() {
if (bufferAvailable.size() == 0) { if (bufferAvailable.size() == 0) {
size_t n = inner.read(buffer.begin(), 1, buffer.size()); size_t n = inner.tryRead(buffer.begin(), 1, buffer.size());
bufferAvailable = buffer.slice(0, n); bufferAvailable = buffer.slice(0, n);
} }
return bufferAvailable; return bufferAvailable;
} }
size_t BufferedInputStreamWrapper::read(void* dst, size_t minBytes, size_t maxBytes) { size_t BufferedInputStreamWrapper::tryRead(void* dst, size_t minBytes, size_t maxBytes) {
if (minBytes <= bufferAvailable.size()) { if (minBytes <= bufferAvailable.size()) {
// Serve from current buffer. // Serve from current buffer.
size_t n = std::min(bufferAvailable.size(), maxBytes); size_t n = std::min(bufferAvailable.size(), maxBytes);
...@@ -174,20 +190,15 @@ void BufferedOutputStreamWrapper::write(const void* src, size_t size) { ...@@ -174,20 +190,15 @@ void BufferedOutputStreamWrapper::write(const void* src, size_t size) {
ArrayInputStream::ArrayInputStream(ArrayPtr<const byte> array): array(array) {} ArrayInputStream::ArrayInputStream(ArrayPtr<const byte> array): array(array) {}
ArrayInputStream::~ArrayInputStream() noexcept(false) {} ArrayInputStream::~ArrayInputStream() noexcept(false) {}
ArrayPtr<const byte> ArrayInputStream::getReadBuffer() { ArrayPtr<const byte> ArrayInputStream::tryGetReadBuffer() {
return array; return array;
} }
size_t ArrayInputStream::read(void* dst, size_t minBytes, size_t maxBytes) { size_t ArrayInputStream::tryRead(void* dst, size_t minBytes, size_t maxBytes) {
size_t n = std::min(maxBytes, array.size()); size_t n = std::min(maxBytes, array.size());
size_t result = n;
KJ_REQUIRE(n >= minBytes, "ArrayInputStream ended prematurely.") {
result = minBytes; // garbage
break;
}
memcpy(dst, array.begin(), n); memcpy(dst, array.begin(), n);
array = array.slice(n, array.size()); array = array.slice(n, array.size());
return result; return n;
} }
void ArrayInputStream::skip(size_t bytes) { void ArrayInputStream::skip(size_t bytes) {
...@@ -234,7 +245,7 @@ AutoCloseFd::~AutoCloseFd() noexcept(false) { ...@@ -234,7 +245,7 @@ AutoCloseFd::~AutoCloseFd() noexcept(false) {
FdInputStream::~FdInputStream() noexcept(false) {} FdInputStream::~FdInputStream() noexcept(false) {}
size_t FdInputStream::read(void* buffer, size_t minBytes, size_t maxBytes) { size_t FdInputStream::tryRead(void* buffer, size_t minBytes, size_t maxBytes) {
byte* pos = reinterpret_cast<byte*>(buffer); byte* pos = reinterpret_cast<byte*>(buffer);
byte* min = pos + minBytes; byte* min = pos + minBytes;
byte* max = pos + maxBytes; byte* max = pos + maxBytes;
...@@ -242,8 +253,8 @@ size_t FdInputStream::read(void* buffer, size_t minBytes, size_t maxBytes) { ...@@ -242,8 +253,8 @@ size_t FdInputStream::read(void* buffer, size_t minBytes, size_t maxBytes) {
while (pos < min) { while (pos < min) {
ssize_t n; ssize_t n;
KJ_SYSCALL(n = ::read(fd, pos, max - pos), fd); KJ_SYSCALL(n = ::read(fd, pos, max - pos), fd);
KJ_REQUIRE(n > 0, "Premature EOF") { if (n == 0) {
return minBytes; break;
} }
pos += n; pos += n;
} }
......
...@@ -38,9 +38,9 @@ class InputStream { ...@@ -38,9 +38,9 @@ class InputStream {
public: public:
virtual ~InputStream() noexcept(false); virtual ~InputStream() noexcept(false);
virtual size_t read(void* buffer, size_t minBytes, size_t maxBytes) = 0; size_t read(void* buffer, size_t minBytes, size_t maxBytes);
// Reads at least minBytes and at most maxBytes, copying them into the given buffer. Returns // Reads at least minBytes and at most maxBytes, copying them into the given buffer. Returns
// the size read. Throws an exception on errors. // the size read. Throws an exception on errors. Implemented in terms of tryRead().
// //
// maxBytes is the number of bytes the caller really wants, but minBytes is the minimum amount // maxBytes is the number of bytes the caller really wants, but minBytes is the minimum amount
// needed by the caller before it can start doing useful processing. If the stream returns less // needed by the caller before it can start doing useful processing. If the stream returns less
...@@ -48,12 +48,19 @@ public: ...@@ -48,12 +48,19 @@ public:
// less than maxBytes is useful when it makes sense for the caller to parallelize processing // less than maxBytes is useful when it makes sense for the caller to parallelize processing
// with I/O. // with I/O.
// //
// Never blocks if minBytes is zero. If minBytes is zero and maxBytes is non-zero, this may
// attempt a non-blocking read or may just return zero. To force a read, use a non-zero minBytes.
// To detect EOF without throwing an exception, use tryRead().
//
// Cap'n Proto never asks for more bytes than it knows are part of the message. Therefore, if // Cap'n Proto never asks for more bytes than it knows are part of the message. Therefore, if
// the InputStream happens to know that the stream will never reach maxBytes -- even if it has // the InputStream happens to know that the stream will never reach maxBytes -- even if it has
// reached minBytes -- it should throw an exception to avoid wasting time processing an incomplete // reached minBytes -- it should throw an exception to avoid wasting time processing an incomplete
// message. If it can't even reach minBytes, it MUST throw an exception, as the caller is not // message. If it can't even reach minBytes, it MUST throw an exception, as the caller is not
// expected to understand how to deal with partial reads. // expected to understand how to deal with partial reads.
virtual size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) = 0;
// Like read(), but may return fewer than minBytes on EOF.
inline void read(void* buffer, size_t bytes) { read(buffer, bytes, bytes); } inline void read(void* buffer, size_t bytes) { read(buffer, bytes, bytes); }
// Convenience method for reading an exact number of bytes. // Convenience method for reading an exact number of bytes.
...@@ -84,10 +91,13 @@ class BufferedInputStream: public InputStream { ...@@ -84,10 +91,13 @@ class BufferedInputStream: public InputStream {
public: public:
virtual ~BufferedInputStream() noexcept(false); virtual ~BufferedInputStream() noexcept(false);
virtual ArrayPtr<const byte> getReadBuffer() = 0; ArrayPtr<const byte> getReadBuffer();
// Get a direct pointer into the read buffer, which contains the next bytes in the input. If the // Get a direct pointer into the read buffer, which contains the next bytes in the input. If the
// caller consumes any bytes, it should then call skip() to indicate this. This always returns a // caller consumes any bytes, it should then call skip() to indicate this. This always returns a
// non-empty buffer unless at EOF. // non-empty buffer or throws an exception. Implemented in terms of tryGetReadBuffer().
virtual ArrayPtr<const byte> tryGetReadBuffer() = 0;
// Like getReadBuffer() but may return an empty buffer on EOF.
}; };
class BufferedOutputStream: public OutputStream { class BufferedOutputStream: public OutputStream {
...@@ -130,8 +140,8 @@ public: ...@@ -130,8 +140,8 @@ public:
~BufferedInputStreamWrapper() noexcept(false); ~BufferedInputStreamWrapper() noexcept(false);
// implements BufferedInputStream ---------------------------------- // implements BufferedInputStream ----------------------------------
ArrayPtr<const byte> getReadBuffer() override; ArrayPtr<const byte> tryGetReadBuffer() override;
size_t read(void* buffer, size_t minBytes, size_t maxBytes) override; size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override;
void skip(size_t bytes) override; void skip(size_t bytes) override;
private: private:
...@@ -182,8 +192,8 @@ public: ...@@ -182,8 +192,8 @@ public:
~ArrayInputStream() noexcept(false); ~ArrayInputStream() noexcept(false);
// implements BufferedInputStream ---------------------------------- // implements BufferedInputStream ----------------------------------
ArrayPtr<const byte> getReadBuffer() override; ArrayPtr<const byte> tryGetReadBuffer() override;
size_t read(void* buffer, size_t minBytes, size_t maxBytes) override; size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override;
void skip(size_t bytes) override; void skip(size_t bytes) override;
private: private:
...@@ -250,7 +260,7 @@ public: ...@@ -250,7 +260,7 @@ public:
KJ_DISALLOW_COPY(FdInputStream); KJ_DISALLOW_COPY(FdInputStream);
~FdInputStream() noexcept(false); ~FdInputStream() noexcept(false);
size_t read(void* buffer, size_t minBytes, size_t maxBytes) override; size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override;
private: private:
int fd; int fd;
......
...@@ -411,7 +411,7 @@ void MainBuilder::MainImpl::operator()(StringPtr programName, ArrayPtr<const Str ...@@ -411,7 +411,7 @@ void MainBuilder::MainImpl::operator()(StringPtr programName, ArrayPtr<const Str
const Impl::Option& option = *iter->second; const Impl::Option& option = *iter->second;
if (option.hasArg) { if (option.hasArg) {
// Argument expected. // Argument expected.
if (j + 1 < params.size()) { if (j + 1 < param.size()) {
// Rest of flag is argument. // Rest of flag is argument.
StringPtr arg = param.slice(j + 1); StringPtr arg = param.slice(j + 1);
KJ_IF_MAYBE(error, (*option.funcWithArg)(arg).releaseError()) { KJ_IF_MAYBE(error, (*option.funcWithArg)(arg).releaseError()) {
...@@ -439,21 +439,23 @@ void MainBuilder::MainImpl::operator()(StringPtr programName, ArrayPtr<const Str ...@@ -439,21 +439,23 @@ void MainBuilder::MainImpl::operator()(StringPtr programName, ArrayPtr<const Str
} else if (!impl->subCommands.empty()) { } else if (!impl->subCommands.empty()) {
// A sub-command name. // A sub-command name.
auto iter = impl->subCommands.find(param); auto iter = impl->subCommands.find(param);
if (iter == impl->subCommands.end()) { if (iter != impl->subCommands.end()) {
MainFunc subMain = iter->second.func(); MainFunc subMain = iter->second.func();
subMain(str(programName, ' ', param), params.slice(i + 1, params.size())); subMain(str(programName, ' ', param), params.slice(i + 1, params.size()));
return; return;
} else if (param == "help") { } else if (param == "help") {
if (i + 1 < params.size()) { if (i + 1 < params.size()) {
iter = impl->subCommands.find(params[i + 1]); iter = impl->subCommands.find(params[i + 1]);
if (iter == impl->subCommands.end()) { if (iter != impl->subCommands.end()) {
usageError(programName, str(params[i + 1], ": unknown command"));
} else {
// Run the sub-command with "--help" as the argument. // Run the sub-command with "--help" as the argument.
MainFunc subMain = iter->second.func(); MainFunc subMain = iter->second.func();
StringPtr dummyArg = "--help"; StringPtr dummyArg = "--help";
subMain(str(programName, ' ', params[i + 1]), arrayPtr(&dummyArg, 1)); subMain(str(programName, ' ', params[i + 1]), arrayPtr(&dummyArg, 1));
return; return;
} else if (params[i + 1] == "help") {
impl->context.exitInfo("Help, I'm trapped in a help text factory!");
} else {
usageError(programName, str(params[i + 1], ": unknown command"));
} }
} else { } else {
printHelp(programName); printHelp(programName);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment