blob: 3008961bf6dcde582567c194334aa62517062fd9 [file]
#include <catch.hpp>
#include "rive/core/binary_reader.hpp"
#include "rive/core/vector_binary_writer.hpp"
template <typename T> void checkFits()
{
int64_t min = std::numeric_limits<T>::min();
int64_t max = std::numeric_limits<T>::max();
REQUIRE(rive::fitsIn<T>(max + 0));
REQUIRE(rive::fitsIn<T>(min - 0));
REQUIRE(!rive::fitsIn<T>(max + 1));
REQUIRE(!rive::fitsIn<T>(min - 1));
}
TEST_CASE("fitsIn checks", "[type_conversions]")
{
checkFits<int8_t>();
checkFits<uint8_t>();
checkFits<int16_t>();
checkFits<uint16_t>();
checkFits<int32_t>();
checkFits<uint32_t>();
}
static uint8_t* packvarint(uint8_t array[], uint64_t value)
{
while (value > 127)
{
*array++ = static_cast<uint8_t>(0x80 | (value & 0x7F));
value >>= 7;
}
*array++ = static_cast<uint8_t>(value);
return array;
}
template <typename T> bool checkAs(uint64_t value)
{
uint8_t storage[16];
uint8_t* p = storage;
p = packvarint(storage, value);
rive::BinaryReader reader(rive::make_span(storage, p - storage));
auto newValue = reader.readVarUintAs<T>();
if (reader.hasError())
{
REQUIRE(newValue == 0);
}
return !reader.hasError() && value == newValue;
}
TEST_CASE("range checks", "[binary_reader]")
{
REQUIRE(checkAs<uint8_t>(100));
REQUIRE(checkAs<uint16_t>(100));
REQUIRE(checkAs<uint32_t>(100));
REQUIRE(!checkAs<uint8_t>(1000));
REQUIRE(checkAs<uint16_t>(1000));
REQUIRE(checkAs<uint32_t>(1000));
REQUIRE(!checkAs<uint8_t>(100000));
REQUIRE(!checkAs<uint16_t>(100000));
REQUIRE(checkAs<uint32_t>(100000));
}
TEST_CASE("all data types can be written & read", "[binary_reader]")
{
std::vector<uint8_t> buffer;
rive::VectorBinaryWriter writer(&buffer);
writer.writeVarUint((uint32_t)34);
writer.write((uint16_t)22);
writer.write(3.14f);
rive::BinaryReader reader(buffer);
REQUIRE(reader.readVarUintAs<uint32_t>() == 34);
REQUIRE(reader.readUint16() == 22);
REQUIRE(reader.readFloat32() == 3.14f);
}
// The byte-length prefix for a readBytes() blob is decoded straight from the
// file, so a corrupt or truncated file can ask for more bytes than the buffer
// holds. readBytes() must never hand back a Span that points past the end of
// the backing buffer: callers build a sub-reader over that Span (e.g. data
// bind source paths, mesh index/vertex buffers, blob assets) and would
// otherwise read out of bounds.
TEST_CASE("readBytes overflows on a length past the end of the buffer",
"[binary_reader]")
{
const uint8_t storage[] = {1, 2, 3, 4};
rive::BinaryReader reader(rive::make_span(storage, sizeof(storage)));
auto bytes = reader.readBytes(1000);
REQUIRE(reader.didOverflow());
REQUIRE(bytes.size() == 0);
// The span must stay within the source buffer.
REQUIRE(bytes.begin() >= storage);
REQUIRE(bytes.end() <= storage + sizeof(storage));
}
TEST_CASE("readBytes returns exactly the in-range bytes requested",
"[binary_reader]")
{
const uint8_t storage[] = {1, 2, 3, 4};
rive::BinaryReader reader(rive::make_span(storage, sizeof(storage)));
auto bytes = reader.readBytes(3);
REQUIRE(!reader.didOverflow());
REQUIRE(bytes.size() == 3);
REQUIRE(bytes[0] == 1);
REQUIRE(bytes[2] == 3);
// Only one byte remains; asking for more must overflow, not overrun.
auto rest = reader.readBytes(100);
REQUIRE(reader.didOverflow());
REQUIRE(rest.size() == 0);
REQUIRE(rest.end() <= storage + sizeof(storage));
}