Get Involved

InStream

InStream performs buffered input from an arbitrary input source.

InStream is similar to FILE in C or std::istream in C++. It maintains an internal input buffer on the heap, and provides functions to quickly read small amounts of data incrementally, making it suitable for application-level parsing of text and binary formats. InPipe, on the other hand, is a lower-level class more akin to a Unix file descriptor.

The parse and readString member functions are intended for reading text in an 8-bit format compatible with ASCII, such as UTF-8, ISO 8859-1 or Windows-1252. For more information, see Parsing Text.

Some InStream objects contain adapters that perform conversions. For example, the InStream returned by FileSystem::openTextForRead can normalize endings and convert from UTF-16 to UTF-8. For more information, see Unicode Support.

If the InStream's underlying input comes from an InPipe, the internal input buffer is managed by a ChunkListNode, and you can call getCursor() at any time to create a ChunkCursor. You can then rewind the InStream to an earlier point using rewind(), or copy some data between two ChunkCursors into a single contiguous memory buffer.

To create an InStream that reads from a contiguous memory block, use ViewInStream.

Header File

#include <ply-runtime/io/InStream.h>

Also included from <ply-runtime/Base.h>.

Data Members

 const char* curByte [code]

A pointer to the next byte in the input buffer. If this pointer is equal to endByte, it is not safe to read from the pointer. Use member functions such as tryMakeBytesAvailable() to ensure that this pointer can be read safely.

 const char* endByte [code]

A pointer to the last byte in the input buffer. This pointer does not necessarily represent the end of the input stream; for example, it still might be possible to read more data from the underlying InPipe, or to advance to the next chunk in a ChunkList, by calling tryMakeBytesAvailable().

Member Functions

  InStream::InStream() [code]

Constructs an empty InStream. You can replace it with a valid InStream later using move assignment.

  InStream::InStream(InStream&& other) [code]

Move constructor.

  InStream::InStream(OptionallyOwned<InPipe>&& inPipe, u32 chunkSizeExp = DefaultChunkSizeExp) [code]

Constructs an InStream from an InPipe. If inPipe is an owned pointer, the InStream takes ownership of the InPipe and will automatically destroy it in its destructor. If inPipe is a borrowed pointer, the InStream does not take ownership of the InPipe.

void InStream::operator=(InStream&& other) [code]

Move assignment operator.

 bool InStream::isView() const [code]

Return true if the InStream is a ViewInStream.

 bool InStream::atEOF() const [code]

Returns true if no further data can be read from the stream, such as when the end of-file is encountered. This function also returns true if there's an error in the underlying InPipe that prevents further reading, such as when a network socket is closed prematurely.

 s32 InStream::numBytesAvailable() const [code]

Returns the number of bytes between curByte and endByte. Equivalent to viewAvailable().numBytes.

 StringView InStream::viewAvailable() const [code]

Returns the memory region between curByte and endByte as a StringView.

 u64 InStream::getSeekPos() const [code]

Returns a number that increases each time a byte is read from the InStream. If the underlying InPipe is a file, this number typically corresponds to the file offset.

 ChunkCursor InStream::getCursor() const [code]

Returns a ChunkCursor at the current input position. The ChunkCursor increments the reference count of the InStream's internal ChunkListNode, preventing it from being destroyed when reading past the end of the chunk. This function is used internally by StringReader::readString in order to copy some region of the input to a String. In particular, StringReader::readString<fmt::Line> returns a single line of input as a String even if it originally spanned multiple ChunkListNodes.

 void InStream::rewind(ChunkCursor cursor) [code]

Rewinds the InStream back to a previously saved position. This also clears the InStream's end-of-file status.

 u32 InStream::tryMakeBytesAvailable(u32 numBytes = 1) [code]

Attempts to make at least numBytes available to read contiguously at curByte. Returns the number of bytes actually made available. If the underlying InPipe is waiting for data, this function will block until at least numBytes bytes arrive. If EOF/error is encountered, the return value will be less than numBytes; otherwise, it will be greater than or equal to numBytes.

 char InStream::peekByte(u32 index = 0) const [code]

Equivalent to curByte[index], with bounds checking performed on index at runtime. The caller is responsible for ensuring that it's safe to read from the input buffer at this index by calling tryMakeBytesAvailable() beforehand.

if (ins->tryMakeBytesAvailable() && ins->peekByte() == '.') {
    // Handle this input character...
    ins->advanceByte();
}
 void InStream::advanceByte(u32 numBytes = 1) [code]

Equivalent to curByte += numBytes, with bounds checking performed on numBytes at runtime. The caller is responsible for ensuring that there are actually numBytes available in the input buffer by calling tryMakeBytesAvailable() beforehand.

 u8 InStream::readByte() [code]

Reads and returns the next byte from the input stream, or 0 if no more bytes are available. Use atEOF() to determine whether the read was actually successful.

 bool InStream::read(MutableStringView dst) [code]

Attempts to fill dst with data from the input stream. If the underlying InPipe is waiting for data, this function will block until dst is filled. Returns true if the buffer is filled successfully. If EOF/error is encountered before dst can be filled, the remainder of dst is filled with zeros and false is returned.

 bool InStream::skip(u32 numBytes) [code]

Attempts to skip numBytes bytes the input stream. If the underlying InPipe is waiting for data, this function will block until the specified number of bytes arrive. Returns true if successful. Returns false if EOF/error is encountered before the specified number of bytes are skipped.

 String InStream::readRemainingContents() [code]

Reads all the remaining data from the input stream and returns the contents as a String.

 ViewInStream* InStream::asViewInStream() [code]
 const ViewInStream* InStream::asViewInStream() const [code]

Casts the InStream to various other types. It's always legal to call asStringReader(), but it's only legal to call the other functions if isView() returns true.

 template <typename Type>
Type InStream::parse(const decltype(fmt::TypeParser<Type>::defaultFormat())& format = fmt::TypeParser<Type>::defaultFormat()) [code]

A template function to parse the data type given by Type. It currently supports s8, s16, s32, s64, u8, u16, u32, u64, float and double. You can extend it to support additional types by specializing the fmt::TypeParser class template.

u32 a = ins.parse<u32>();         // parse an integer such as "123"
double b = ins.parse<double>();   // parse a floating-point number such as "-123.456"

This function accepts an optional argument format whose type depends on the data type being parsed. For s8, s16, s32, s64, u8, u16, u32, u64, float and double, the expected type of this argument is fmt::Radix.

u32 a = ins.parse<u32>(fmt::Radix{16});   // parse hex integer such as "badf00d"
u32 b = ins.parse<u32>(fmt::Radix{2});    // parse binary integer such as "1101101"

For more information, see Parsing Text.

 template <typename Format, typename = void_t<decltype(fmt::FormatParser<Format>::parse)>>
auto InStream::parse(const Format& format = {}) [code]

A template function to parse text in the format specified by Format. The return type depends on the format being parsed. It currently supports the following built-in formats:

  • fmt::QuotedString
  • fmt::Identifier
  • fmt::Line
  • fmt::WhiteSpace
  • fmt::NonWhiteSpace

Example:

ins.parse<fmt::WhiteSpace>();     // returns nothing; whitespace is skipped

This function accepts an optional argument format of type Format. When this argument is passed, you can leave out the function template argument and let the compiler deduce it from the argument type:

bool success = ins.parse(fmt::QuotedString{fmt::AllowSingleQuote});

You can extend this function to support additional formats by specializing the fmt::FormatParser class template.

For more information, see Parsing Text.

 template <typename Type>
String InStream::readString(const decltype(fmt::TypeParser<Type>::defaultFormat())& format = fmt::TypeParser<Type>::defaultFormat()) [code]
 template <typename Format, typename = void_t<decltype(fmt::FormatParser<Format>::parse)>>
String InStream::readString(const Format& format = {}) [code]

These functions are similar to the family of parse() functions, except that they return a String containing the input that was consumed by the parse operation. For example, readString<fmt::Line>() returns a single line of text terminated by '\n'.

String line = ins.readString<fmt::Line>();

Internally, readString() uses getCursor() to return a new String. If you would like both the String and the value returned by the parse function, you can use getCursor() yourself:

ChunkCursor startCursor = ins.getCursor();
u32 value = ins.parse<u32>();
String str = ChunkCursor::toString(std::move(startCursor), ins.getCursor());
 bool InStream::anyParseError() const [code]

Returns true if an error occurred in a previously called parse function.