#include #include #include #include #include namespace { void wr32(std::vector &bytes, size_t off, uint32_t value) { bytes[off + 0] = uint8_t(value); bytes[off + 1] = uint8_t(value >> 8); bytes[off + 2] = uint8_t(value >> 16); bytes[off + 3] = uint8_t(value >> 24); } std::vector granny_header(size_t size, uint32_t section_count) { std::vector bytes(size, 0); const uint32_t magic[] = { 0xCAB067B8, 0x0FB16DF8, 0x7E8C7284, 0x1E00195E }; for (int i = 0; i < 4; ++i) { wr32(bytes, size_t(i) * 4, magic[i]); } constexpr size_t h = 32; wr32(bytes, h + 0, 6); // Version wr32(bytes, h + 4, uint32_t(size)); wr32(bytes, h + 12, 40); // section table starts at byte 72 wr32(bytes, h + 16, section_count); // Root type/object remain section 0, offset 0. With no section this must be // rejected by the FileInfo stage rather than returned as an empty File. return bytes; } bool expect_failure(const std::vector &bytes, const char *stage) { gr2::LoadError err; auto file = gr2::File::load(bytes.data(), bytes.size(), &err); if (file || err.stage != stage) { std::fprintf(stderr, "expected failure stage '%s', got file=%d stage='%s' message='%s'\n", stage, file.has_value(), err.stage.c_str(), err.message.c_str()); return false; } return true; } } // namespace int main() { bool ok = true; ok &= expect_failure(std::vector(80, 0), "header"); ok &= expect_failure(granny_header(80, 0), "fileinfo"); auto unsupported = granny_header(160, 1); constexpr size_t section = 72; wr32(unsupported, section + 0, 4); // unsupported BitKnit wr32(unsupported, section + 4, uint32_t(unsupported.size())); wr32(unsupported, section + 12, 1); // non-empty expanded section ok &= expect_failure(unsupported, "section"); auto bad_offset = granny_header(160, 1); wr32(bad_offset, section + 4, UINT32_MAX); ok &= expect_failure(bad_offset, "section"); return ok ? 0 : 1; }