std.check.src.stream.cpp: src/stream.cpp
#include "error.hpp"
#include "formatter.hpp"
#include "stream.hpp"
// Disable annoying warning 4996: "'function' was declared deprecated"
#if CHECK_MSC_VERSION(14, 0)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
namespace Check
{
const Decision Stream::FormatErrorResult[3] =
{ // 0 = InputStream, 1 = OutputStream, 2 = AnswerStream
UnhandledError, PresentationError, UnhandledError
};
void Stream::initialize()
{
file = NULL;
type = InputStream;
closed = endFound = true;
boolFmt = IntegerBoolean;
trueStr.reserve(DefaultTrueFalseLength);
falseStr.reserve(DefaultTrueFalseLength);
endFileMode = FailAtEnd;
spaceMode = SkipWhite;
charsBeforeWord = &SpaceChars;
commonBuffer.reserve(DefaultTrueFalseLength);
}
Stream &Stream::read(char &symbol)
{
const int value = CHECK_GETC(file);
symbol = static_cast(value);
endFound = (value == EOF);
if (endFound && endFileMode == FailAtEnd)
incite(FormatErrorResult[type]);
return *this;
}
Stream &Stream::read(
char *word, const size_t maxLength, const CharSet &before,
const CharSet &after, const bool putbackLast)
{
// If it's impossible to read anything, do not touch the stream
if (maxLength == 0)
return *this;
skip(before);
if (CHECK_FEOF(file))
{
endFound = true;
if (endFileMode == FailAtEnd)
incite(FormatErrorResult[type]);
else
return *this;
}
for (size_t count = 0; count + 1 != maxLength; ++count)
{
const int symbol = CHECK_GETC(file);
if (symbol == EOF || after.contains(symbol))
{
if (putbackLast)
CHECK_UNGETC(symbol, file);
break;
}
*word = static_cast(symbol);
++word;
}
*word = '\0';
return *this;
}
Stream &Stream::read(
std::string &word, const CharSet &before,
const CharSet &after, const bool putbackLast)
{
skip(before);
if (CHECK_FEOF(file))
{
endFound = true;
if (endFileMode == FailAtEnd)
incite(FormatErrorResult[type]);
else
return *this;
}
word.clear();
size_t count = 0;
static char buffer[StringReadBufferSize];
int symbol = CHECK_GETC(file);
while (symbol != EOF && !after.contains(symbol))
{
if (count == StringReadBufferSize)
{
word.append(buffer, count);
count = 0;
}
buffer[count] = static_cast(symbol);
++count;
symbol = CHECK_GETC(file);
}
word.append(buffer, count);
if (putbackLast)
CHECK_UNGETC(symbol, file);
return *this;
}
void Stream::open(const char *fileName, const StreamMode openMode)
{
close();
name = fileName;
type = openMode;
endFound = false;
// File should be opened as text to handle new lines correctly (on Windows)
file = fopen(name.c_str(), "rt");
if (file == NULL)
raise(
FormatErrorResult[type], "File \'%s\' cannot be opened", name.c_str());
closed = false;
}
void Stream::close()
{
if (!closed)
{
// Mark the file as closed not to try to close it again after fail
closed = endFound = true;
if (fclose(file) != 0)
raise(
UnhandledError, "File \'%s\' cannot be closed", name.c_str());
file = NULL;
}
}
Stream &Stream::skip(const CharSet &charSet)
{
if (charSet.isEmpty())
return *this;
int symbol;
do
symbol = CHECK_GETC(file);
while (symbol != EOF && charSet.contains(symbol));
CHECK_UNGETC(symbol, file);
return *this;
}
Stream &Stream::skip(const CharSet &charSet, const size_t maxLength)
{
if (maxLength == 0 || charSet.isEmpty())
return *this;
size_t count = 0;
int symbol = CHECK_GETC(file);
while (symbol != EOF && charSet.contains(symbol))
{
++count;
if (count == maxLength)
return *this;
symbol = CHECK_GETC(file);
}
CHECK_UNGETC(symbol, file);
return *this;
}
int Stream::peek()
{
const int symbol = CHECK_GETC(file);
endFound = (symbol == EOF);
CHECK_UNGETC(symbol, file);
return symbol;
}
Stream &Stream::putback(const char symbol)
{
if (CHECK_UNGETC(symbol, file) == EOF)
incite(UnhandledError, "Symbol cannot be put back");
return *this;
}
Stream &Stream::get(char &symbol, const CharSet &charSet)
{
read(symbol);
if (!(endFound || charSet.contains(symbol)))
raise(
FormatErrorResult[type], "Unexpected symbol \'%c\'", symbol);
return *this;
}
Stream &Stream::operator >> (bool &value)
{
switch (boolFmt)
{
case IntegerBoolean:
{
int number;
(*this) >> number;
value = (number != 0);
} break;
case StringBoolean:
{
get(commonBuffer);
if (commonBuffer == trueStr)
value = true;
else if (commonBuffer == falseStr)
value = false;
else
raise(
FormatErrorResult[type], "Unexpected boolean value %s",
quote(commonBuffer).c_str());
} break;
default:
{
incite(UnhandledError, "Undefined boolean format");
} break;
}
return *this;
}
void Stream::setWhiteMode(const WhitespaceMode value)
{
spaceMode = value;
charsBeforeWord = (value == SkipWhite ? &SpaceChars : &NoneChars);
}
}
#if CHECK_MSC_VERSION(14, 0)
# pragma warning(pop)
#endif