#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>

#include "../src/erofs_fs.h"

struct legacy_erofs_super_block {
	uint32_t magic;
	uint32_t checksum;
	uint32_t feature_compat;
	uint8_t blkszbits;
	uint8_t sb_extslots;
	union {
		uint16_t rootnid_2b;
		uint16_t blocks_hi;
	} __packed rb;
	uint64_t inos;
	uint64_t epoch;
	uint32_t fixed_nsec;
	uint32_t blocks_lo;
	uint32_t meta_blkaddr;
	uint32_t xattr_blkaddr;
	uint8_t uuid[16];
	uint8_t volume_name[16];
	uint32_t feature_incompat;
	union {
		uint16_t available_compr_algs;
		uint16_t lz4_max_distance;
	} __packed u1;
	uint16_t extra_devices;
	uint16_t devt_slotoff;
	uint8_t dirblkbits;
	uint8_t xattr_prefix_count;
	uint32_t xattr_prefix_start;
	uint64_t packed_nid;
	uint8_t xattr_filter_reserved;
	uint8_t ishare_xattr_prefix_id;
	uint8_t reserved[2];
	uint32_t build_time;
	uint64_t rootnid_8b;
	uint64_t reserved2;
	uint64_t metabox_nid;
	uint64_t reserved3;
} __packed;

union legacy_erofs_inode_i_nb {
	uint16_t nlink;
	uint16_t blocks_hi;
	uint16_t startblk_hi;
} __packed;

struct legacy_erofs_inode_compact {
	uint16_t i_format;
	uint16_t i_xattr_icount;
	uint16_t i_mode;
	union legacy_erofs_inode_i_nb i_nb;
	uint32_t i_size;
	uint32_t i_mtime;
	union erofs_inode_i_u i_u;
	uint32_t i_ino;
	uint16_t i_uid;
	uint16_t i_gid;
	uint32_t i_reserved;
} __packed;

struct legacy_erofs_inode_extended {
	uint16_t i_format;
	uint16_t i_xattr_icount;
	uint16_t i_mode;
	union legacy_erofs_inode_i_nb i_nb;
	uint64_t i_size;
	union erofs_inode_i_u i_u;
	uint32_t i_ino;
	uint32_t i_uid;
	uint32_t i_gid;
	uint64_t i_mtime;
	uint32_t i_mtime_nsec;
	uint32_t i_nlink;
	uint8_t i_reserved2[16];
} __packed;

struct legacy_erofs_dirent {
	uint64_t nid;
	uint16_t nameoff;
	uint8_t file_type;
	uint8_t reserved;
} __packed;

struct legacy_erofs_xattr_ibody_header {
	uint32_t h_name_filter;
	uint8_t h_shared_count;
	uint8_t h_reserved2[7];
	uint32_t h_shared_xattrs[0];
} __packed;

struct legacy_erofs_xattr_long_prefix {
	uint8_t base_index;
	char infix[0];
} __packed;

struct legacy_erofs_xattr_entry {
	uint8_t e_name_len;
	uint8_t e_name_index;
	uint16_t e_value_size;
	char e_name[];
} __packed;

#define ASSERT_LAYOUT(current, legacy) \
	_Static_assert(sizeof(current) == sizeof(legacy), "size " #current); \
	_Static_assert(_Alignof(current) == _Alignof(legacy), "align " #current)
#define ASSERT_OFFSET(current, legacy, field) \
	_Static_assert(offsetof(current, field) == offsetof(legacy, field), \
	    "offset " #current "." #field)

ASSERT_LAYOUT(struct erofs_super_block, struct legacy_erofs_super_block);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block, magic);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block, checksum);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    feature_compat);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    blkszbits);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    sb_extslots);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block, rb);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block, inos);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block, epoch);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    fixed_nsec);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    blocks_lo);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    meta_blkaddr);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    xattr_blkaddr);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block, uuid);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    volume_name);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    feature_incompat);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block, u1);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    extra_devices);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    devt_slotoff);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    dirblkbits);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    xattr_prefix_count);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    xattr_prefix_start);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    packed_nid);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    xattr_filter_reserved);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    ishare_xattr_prefix_id);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    reserved);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    build_time);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    rootnid_8b);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    reserved2);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    metabox_nid);
ASSERT_OFFSET(struct erofs_super_block, struct legacy_erofs_super_block,
    reserved3);

ASSERT_LAYOUT(union erofs_inode_i_nb, union legacy_erofs_inode_i_nb);
ASSERT_OFFSET(union erofs_inode_i_nb, union legacy_erofs_inode_i_nb, nlink);
ASSERT_OFFSET(union erofs_inode_i_nb, union legacy_erofs_inode_i_nb, blocks_hi);
ASSERT_OFFSET(union erofs_inode_i_nb, union legacy_erofs_inode_i_nb,
    startblk_hi);

#define ASSERT_INODE_LAYOUT(current, legacy) \
	ASSERT_LAYOUT(current, legacy); \
	ASSERT_OFFSET(current, legacy, i_format); \
	ASSERT_OFFSET(current, legacy, i_xattr_icount); \
	ASSERT_OFFSET(current, legacy, i_mode); \
	ASSERT_OFFSET(current, legacy, i_nb); \
	ASSERT_OFFSET(current, legacy, i_size); \
	ASSERT_OFFSET(current, legacy, i_u); \
	ASSERT_OFFSET(current, legacy, i_ino); \
	ASSERT_OFFSET(current, legacy, i_uid); \
	ASSERT_OFFSET(current, legacy, i_gid)

ASSERT_INODE_LAYOUT(struct erofs_inode_compact,
    struct legacy_erofs_inode_compact);
ASSERT_OFFSET(struct erofs_inode_compact, struct legacy_erofs_inode_compact,
    i_mtime);
ASSERT_OFFSET(struct erofs_inode_compact, struct legacy_erofs_inode_compact,
    i_reserved);

ASSERT_INODE_LAYOUT(struct erofs_inode_extended,
    struct legacy_erofs_inode_extended);
ASSERT_OFFSET(struct erofs_inode_extended, struct legacy_erofs_inode_extended,
    i_mtime);
ASSERT_OFFSET(struct erofs_inode_extended, struct legacy_erofs_inode_extended,
    i_mtime_nsec);
ASSERT_OFFSET(struct erofs_inode_extended, struct legacy_erofs_inode_extended,
    i_nlink);
ASSERT_OFFSET(struct erofs_inode_extended, struct legacy_erofs_inode_extended,
    i_reserved2);

ASSERT_LAYOUT(struct erofs_dirent, struct legacy_erofs_dirent);
ASSERT_OFFSET(struct erofs_dirent, struct legacy_erofs_dirent, nid);
ASSERT_OFFSET(struct erofs_dirent, struct legacy_erofs_dirent, nameoff);
ASSERT_OFFSET(struct erofs_dirent, struct legacy_erofs_dirent, file_type);
ASSERT_OFFSET(struct erofs_dirent, struct legacy_erofs_dirent, reserved);

ASSERT_LAYOUT(struct erofs_xattr_ibody_header,
    struct legacy_erofs_xattr_ibody_header);
ASSERT_OFFSET(struct erofs_xattr_ibody_header,
    struct legacy_erofs_xattr_ibody_header, h_name_filter);
ASSERT_OFFSET(struct erofs_xattr_ibody_header,
    struct legacy_erofs_xattr_ibody_header, h_shared_count);
ASSERT_OFFSET(struct erofs_xattr_ibody_header,
    struct legacy_erofs_xattr_ibody_header, h_reserved2);
ASSERT_OFFSET(struct erofs_xattr_ibody_header,
    struct legacy_erofs_xattr_ibody_header, h_shared_xattrs);

ASSERT_LAYOUT(struct erofs_xattr_long_prefix,
    struct legacy_erofs_xattr_long_prefix);
ASSERT_OFFSET(struct erofs_xattr_long_prefix,
    struct legacy_erofs_xattr_long_prefix, base_index);
ASSERT_OFFSET(struct erofs_xattr_long_prefix,
    struct legacy_erofs_xattr_long_prefix, infix);

ASSERT_LAYOUT(struct erofs_xattr_entry, struct legacy_erofs_xattr_entry);
ASSERT_OFFSET(struct erofs_xattr_entry, struct legacy_erofs_xattr_entry,
    e_name_len);
ASSERT_OFFSET(struct erofs_xattr_entry, struct legacy_erofs_xattr_entry,
    e_name_index);
ASSERT_OFFSET(struct erofs_xattr_entry, struct legacy_erofs_xattr_entry,
    e_value_size);
ASSERT_OFFSET(struct erofs_xattr_entry, struct legacy_erofs_xattr_entry,
    e_name);

int
main(void)
{
	return (0);
}
