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

#define B02_ALIGN(value, alignment) \
	(((value) + (alignment) - 1) & ~((alignment) - 1))
#ifndef roundup2
#define roundup2(value, alignment) B02_ALIGN(value, alignment)
#endif

#ifdef B02_LINUX_REFERENCE
typedef uint8_t u8;
typedef uint8_t __u8;
typedef uint32_t __u32;
typedef uint16_t __le16;
typedef uint32_t __le32;
typedef uint64_t __le64;
#define __packed __attribute__((__packed__))
#define BIT(nr) (1UL << (nr))
#define BIT_ULL(nr) (1ULL << (nr))
#define ALIGN(value, alignment) B02_ALIGN(value, alignment)
#define round_up(value, alignment) ALIGN(value, alignment)
#define le16_to_cpu(value) (value)
#define cpu_to_le64(value) (value)
#define BUILD_BUG_ON(condition) ((void)sizeof(condition))
#include "../../../../src-linux/erofs_fs.h"
#define EXPECTED_ALIGN(freebsd, linux) (linux)
#else
#include "../../../src/erofs_fs.h"
#define EXPECTED_ALIGN(freebsd, linux) (freebsd)
#endif

#define ASSERT_RECORD(type, size, freebsd_align, linux_align) \
	_Static_assert(sizeof(type) == (size), "size " #type); \
	_Static_assert(_Alignof(type) == \
	    EXPECTED_ALIGN(freebsd_align, linux_align), "alignment " #type)
#define ASSERT_OFFSET(type, field, offset) \
	_Static_assert(offsetof(type, field) == (offset), \
	    "offset " #type "." #field)
#define ASSERT_TYPE(type, field, expected) \
	_Static_assert(__builtin_types_compatible_p( \
	    __typeof__(((type *)0)->field), expected), \
	    "underlying type " #type "." #field)
#define ASSERT_FIELD(type, field, offset, expected) \
	ASSERT_OFFSET(type, field, offset); \
	ASSERT_TYPE(type, field, expected)

ASSERT_RECORD(struct erofs_deviceslot, 128, 1, 4);
ASSERT_FIELD(struct erofs_deviceslot, tag, 0, uint8_t[64]);
ASSERT_FIELD(struct erofs_deviceslot, blocks_lo, 64, __le32);
ASSERT_FIELD(struct erofs_deviceslot, uniaddr_lo, 68, __le32);
ASSERT_FIELD(struct erofs_deviceslot, blocks_hi, 72, __le16);
ASSERT_FIELD(struct erofs_deviceslot, uniaddr_hi, 74, __le16);
ASSERT_FIELD(struct erofs_deviceslot, reserved, 76, uint8_t[52]);

ASSERT_RECORD(struct erofs_super_block, 144, 1, 8);
ASSERT_FIELD(struct erofs_super_block, magic, 0, __le32);
ASSERT_FIELD(struct erofs_super_block, checksum, 4, __le32);
ASSERT_FIELD(struct erofs_super_block, feature_compat, 8, __le32);
ASSERT_FIELD(struct erofs_super_block, blkszbits, 12, uint8_t);
ASSERT_FIELD(struct erofs_super_block, sb_extslots, 13, uint8_t);
ASSERT_OFFSET(struct erofs_super_block, rb, 14);
ASSERT_FIELD(struct erofs_super_block, rb.rootnid_2b, 14, __le16);
ASSERT_FIELD(struct erofs_super_block, rb.blocks_hi, 14, __le16);
ASSERT_FIELD(struct erofs_super_block, inos, 16, __le64);
ASSERT_FIELD(struct erofs_super_block, epoch, 24, __le64);
ASSERT_FIELD(struct erofs_super_block, fixed_nsec, 32, __le32);
ASSERT_FIELD(struct erofs_super_block, blocks_lo, 36, __le32);
ASSERT_FIELD(struct erofs_super_block, meta_blkaddr, 40, __le32);
ASSERT_FIELD(struct erofs_super_block, xattr_blkaddr, 44, __le32);
ASSERT_FIELD(struct erofs_super_block, uuid, 48, uint8_t[16]);
ASSERT_FIELD(struct erofs_super_block, volume_name, 64, uint8_t[16]);
ASSERT_FIELD(struct erofs_super_block, feature_incompat, 80, __le32);
ASSERT_OFFSET(struct erofs_super_block, u1, 84);
ASSERT_FIELD(struct erofs_super_block, u1.available_compr_algs, 84, __le16);
ASSERT_FIELD(struct erofs_super_block, u1.lz4_max_distance, 84, __le16);
ASSERT_FIELD(struct erofs_super_block, extra_devices, 86, __le16);
ASSERT_FIELD(struct erofs_super_block, devt_slotoff, 88, __le16);
ASSERT_FIELD(struct erofs_super_block, dirblkbits, 90, uint8_t);
ASSERT_FIELD(struct erofs_super_block, xattr_prefix_count, 91, uint8_t);
ASSERT_FIELD(struct erofs_super_block, xattr_prefix_start, 92, __le32);
ASSERT_FIELD(struct erofs_super_block, packed_nid, 96, __le64);
ASSERT_FIELD(struct erofs_super_block, xattr_filter_reserved, 104, uint8_t);
ASSERT_FIELD(struct erofs_super_block, ishare_xattr_prefix_id, 105, uint8_t);
ASSERT_FIELD(struct erofs_super_block, reserved, 106, uint8_t[2]);
ASSERT_FIELD(struct erofs_super_block, build_time, 108, __le32);
ASSERT_FIELD(struct erofs_super_block, rootnid_8b, 112, __le64);
ASSERT_FIELD(struct erofs_super_block, reserved2, 120, __le64);
ASSERT_FIELD(struct erofs_super_block, metabox_nid, 128, __le64);
ASSERT_FIELD(struct erofs_super_block, reserved3, 136, __le64);

ASSERT_RECORD(struct erofs_inode_chunk_info, 4, 1, 2);
ASSERT_FIELD(struct erofs_inode_chunk_info, format, 0, __le16);
ASSERT_FIELD(struct erofs_inode_chunk_info, reserved, 2, __le16);

ASSERT_RECORD(union erofs_inode_i_u, 4, 4, 4);
ASSERT_FIELD(union erofs_inode_i_u, blocks_lo, 0, __le32);
ASSERT_FIELD(union erofs_inode_i_u, startblk_lo, 0, __le32);
ASSERT_FIELD(union erofs_inode_i_u, rdev, 0, __le32);
ASSERT_FIELD(union erofs_inode_i_u, c, 0, struct erofs_inode_chunk_info);

ASSERT_RECORD(union erofs_inode_i_nb, 2, 1, 1);
ASSERT_FIELD(union erofs_inode_i_nb, nlink, 0, __le16);
ASSERT_FIELD(union erofs_inode_i_nb, blocks_hi, 0, __le16);
ASSERT_FIELD(union erofs_inode_i_nb, startblk_hi, 0, __le16);

ASSERT_RECORD(struct erofs_inode_compact, 32, 1, 4);
ASSERT_FIELD(struct erofs_inode_compact, i_format, 0, __le16);
ASSERT_FIELD(struct erofs_inode_compact, i_xattr_icount, 2, __le16);
ASSERT_FIELD(struct erofs_inode_compact, i_mode, 4, __le16);
ASSERT_FIELD(struct erofs_inode_compact, i_nb, 6, union erofs_inode_i_nb);
ASSERT_FIELD(struct erofs_inode_compact, i_size, 8, __le32);
ASSERT_FIELD(struct erofs_inode_compact, i_mtime, 12, __le32);
ASSERT_FIELD(struct erofs_inode_compact, i_u, 16, union erofs_inode_i_u);
ASSERT_FIELD(struct erofs_inode_compact, i_ino, 20, __le32);
ASSERT_FIELD(struct erofs_inode_compact, i_uid, 24, __le16);
ASSERT_FIELD(struct erofs_inode_compact, i_gid, 26, __le16);
ASSERT_FIELD(struct erofs_inode_compact, i_reserved, 28, __le32);

ASSERT_RECORD(struct erofs_inode_extended, 64, 1, 8);
ASSERT_FIELD(struct erofs_inode_extended, i_format, 0, __le16);
ASSERT_FIELD(struct erofs_inode_extended, i_xattr_icount, 2, __le16);
ASSERT_FIELD(struct erofs_inode_extended, i_mode, 4, __le16);
ASSERT_FIELD(struct erofs_inode_extended, i_nb, 6, union erofs_inode_i_nb);
ASSERT_FIELD(struct erofs_inode_extended, i_size, 8, __le64);
ASSERT_FIELD(struct erofs_inode_extended, i_u, 16, union erofs_inode_i_u);
ASSERT_FIELD(struct erofs_inode_extended, i_ino, 20, __le32);
ASSERT_FIELD(struct erofs_inode_extended, i_uid, 24, __le32);
ASSERT_FIELD(struct erofs_inode_extended, i_gid, 28, __le32);
ASSERT_FIELD(struct erofs_inode_extended, i_mtime, 32, __le64);
ASSERT_FIELD(struct erofs_inode_extended, i_mtime_nsec, 40, __le32);
ASSERT_FIELD(struct erofs_inode_extended, i_nlink, 44, __le32);
ASSERT_FIELD(struct erofs_inode_extended, i_reserved2, 48, uint8_t[16]);

ASSERT_RECORD(struct erofs_xattr_ibody_header, 12, 1, 4);
ASSERT_FIELD(struct erofs_xattr_ibody_header, h_name_filter, 0, __le32);
ASSERT_FIELD(struct erofs_xattr_ibody_header, h_shared_count, 4, uint8_t);
ASSERT_FIELD(struct erofs_xattr_ibody_header, h_reserved2, 5, uint8_t[7]);
ASSERT_FIELD(struct erofs_xattr_ibody_header, h_shared_xattrs, 12, __le32[]);

ASSERT_RECORD(struct erofs_xattr_entry, 4, 1, 2);
ASSERT_FIELD(struct erofs_xattr_entry, e_name_len, 0, uint8_t);
ASSERT_FIELD(struct erofs_xattr_entry, e_name_index, 1, uint8_t);
ASSERT_FIELD(struct erofs_xattr_entry, e_value_size, 2, __le16);
ASSERT_FIELD(struct erofs_xattr_entry, e_name, 4, char[]);

ASSERT_RECORD(struct erofs_xattr_long_prefix, 1, 1, 1);
ASSERT_FIELD(struct erofs_xattr_long_prefix, base_index, 0, uint8_t);
ASSERT_FIELD(struct erofs_xattr_long_prefix, infix, 1, char[]);

ASSERT_RECORD(struct erofs_inode_chunk_index, 8, 1, 4);
ASSERT_FIELD(struct erofs_inode_chunk_index, startblk_hi, 0, __le16);
ASSERT_FIELD(struct erofs_inode_chunk_index, device_id, 2, __le16);
ASSERT_FIELD(struct erofs_inode_chunk_index, startblk_lo, 4, __le32);

ASSERT_RECORD(struct erofs_dirent, 12, 1, 1);
ASSERT_FIELD(struct erofs_dirent, nid, 0, __le64);
ASSERT_FIELD(struct erofs_dirent, nameoff, 8, __le16);
ASSERT_FIELD(struct erofs_dirent, file_type, 10, uint8_t);
ASSERT_FIELD(struct erofs_dirent, reserved, 11, uint8_t);

ASSERT_RECORD(struct z_erofs_lz4_cfgs, 14, 1, 1);
ASSERT_FIELD(struct z_erofs_lz4_cfgs, max_distance, 0, __le16);
ASSERT_FIELD(struct z_erofs_lz4_cfgs, max_pclusterblks, 2, __le16);
ASSERT_FIELD(struct z_erofs_lz4_cfgs, reserved, 4, uint8_t[10]);

ASSERT_RECORD(struct z_erofs_lzma_cfgs, 14, 1, 1);
ASSERT_FIELD(struct z_erofs_lzma_cfgs, dict_size, 0, __le32);
ASSERT_FIELD(struct z_erofs_lzma_cfgs, format, 4, __le16);
ASSERT_FIELD(struct z_erofs_lzma_cfgs, reserved, 6, uint8_t[8]);

ASSERT_RECORD(struct z_erofs_deflate_cfgs, 6, 1, 1);
ASSERT_FIELD(struct z_erofs_deflate_cfgs, windowbits, 0, uint8_t);
ASSERT_FIELD(struct z_erofs_deflate_cfgs, reserved, 1, uint8_t[5]);

ASSERT_RECORD(struct z_erofs_zstd_cfgs, 6, 1, 1);
ASSERT_FIELD(struct z_erofs_zstd_cfgs, format, 0, uint8_t);
ASSERT_FIELD(struct z_erofs_zstd_cfgs, windowlog, 1, uint8_t);
ASSERT_FIELD(struct z_erofs_zstd_cfgs, reserved, 2, uint8_t[4]);

ASSERT_RECORD(struct z_erofs_map_header, 8, 1, 4);
ASSERT_FIELD(struct z_erofs_map_header, h_fragmentoff, 0, __le32);
ASSERT_FIELD(struct z_erofs_map_header, h_reserved1, 0, __le16);
ASSERT_FIELD(struct z_erofs_map_header, h_idata_size, 2, __le16);
ASSERT_FIELD(struct z_erofs_map_header, h_extents_lo, 0, __le32);
ASSERT_FIELD(struct z_erofs_map_header, h_advise, 4, __le16);
ASSERT_FIELD(struct z_erofs_map_header, h_algorithmtype, 6, uint8_t);
ASSERT_FIELD(struct z_erofs_map_header, h_clusterbits, 7, uint8_t);
ASSERT_FIELD(struct z_erofs_map_header, h_extents_hi, 6, __le16);

ASSERT_RECORD(struct z_erofs_lcluster_index, 8, 1, 4);
ASSERT_FIELD(struct z_erofs_lcluster_index, di_advise, 0, __le16);
ASSERT_FIELD(struct z_erofs_lcluster_index, di_clusterofs, 2, __le16);
ASSERT_FIELD(struct z_erofs_lcluster_index, di_u.blkaddr, 4, __le32);
ASSERT_FIELD(struct z_erofs_lcluster_index, di_u.delta, 4, __le16[2]);

ASSERT_RECORD(struct z_erofs_extent, 32, 1, 4);
ASSERT_FIELD(struct z_erofs_extent, plen, 0, __le32);
ASSERT_FIELD(struct z_erofs_extent, pstart_lo, 4, __le32);
ASSERT_FIELD(struct z_erofs_extent, pstart_hi, 8, __le32);
ASSERT_FIELD(struct z_erofs_extent, lstart_lo, 12, __le32);
ASSERT_FIELD(struct z_erofs_extent, lstart_hi, 16, __le32);
ASSERT_FIELD(struct z_erofs_extent, reserved, 20, uint8_t[12]);

#define ASSERT_VALUE(expression, value) \
	_Static_assert((expression) == (value), "value " #expression)

ASSERT_VALUE(EROFS_SUPER_OFFSET, 1024);
ASSERT_VALUE(EROFS_SB_EXTSLOT_SIZE, 16);
ASSERT_VALUE(EROFS_DEVT_SLOT_SIZE, 128);
ASSERT_VALUE(EROFS_FEATURE_COMPAT_SB_CHKSUM, 0x00000001);
ASSERT_VALUE(EROFS_FEATURE_COMPAT_MTIME, 0x00000002);
ASSERT_VALUE(EROFS_FEATURE_COMPAT_XATTR_FILTER, 0x00000004);
ASSERT_VALUE(EROFS_FEATURE_COMPAT_SHARED_EA_IN_METABOX, 0x00000008);
ASSERT_VALUE(EROFS_FEATURE_COMPAT_PLAIN_XATTR_PFX, 0x00000010);
ASSERT_VALUE(EROFS_FEATURE_COMPAT_ISHARE_XATTRS, 0x00000020);
ASSERT_VALUE(EROFS_FEATURE_INCOMPAT_LZ4_0PADDING, 0x00000001);
ASSERT_VALUE(EROFS_FEATURE_INCOMPAT_COMPR_CFGS, 0x00000002);
ASSERT_VALUE(EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER, 0x00000002);
ASSERT_VALUE(EROFS_FEATURE_INCOMPAT_CHUNKED_FILE, 0x00000004);
ASSERT_VALUE(EROFS_FEATURE_INCOMPAT_DEVICE_TABLE, 0x00000008);
ASSERT_VALUE(EROFS_FEATURE_INCOMPAT_COMPR_HEAD2, 0x00000008);
ASSERT_VALUE(EROFS_FEATURE_INCOMPAT_ZTAILPACKING, 0x00000010);
ASSERT_VALUE(EROFS_FEATURE_INCOMPAT_FRAGMENTS, 0x00000020);
ASSERT_VALUE(EROFS_FEATURE_INCOMPAT_DEDUPE, 0x00000020);
ASSERT_VALUE(EROFS_FEATURE_INCOMPAT_XATTR_PREFIXES, 0x00000040);
ASSERT_VALUE(EROFS_FEATURE_INCOMPAT_48BIT, 0x00000080);
ASSERT_VALUE(EROFS_FEATURE_INCOMPAT_METABOX, 0x00000100);
ASSERT_VALUE(EROFS_ALL_FEATURE_INCOMPAT, 0x000001ff);
ASSERT_VALUE(EROFS_INODE_FLAT_PLAIN, 0);
ASSERT_VALUE(EROFS_INODE_COMPRESSED_FULL, 1);
ASSERT_VALUE(EROFS_INODE_FLAT_INLINE, 2);
ASSERT_VALUE(EROFS_INODE_COMPRESSED_COMPACT, 3);
ASSERT_VALUE(EROFS_INODE_CHUNK_BASED, 4);
ASSERT_VALUE(EROFS_INODE_DATALAYOUT_MAX, 5);
ASSERT_VALUE(EROFS_I_VERSION_MASK, 0x01);
ASSERT_VALUE(EROFS_I_DATALAYOUT_MASK, 0x07);
ASSERT_VALUE(EROFS_I_VERSION_BIT, 0);
ASSERT_VALUE(EROFS_I_DATALAYOUT_BIT, 1);
ASSERT_VALUE(EROFS_I_NLINK_1_BIT, 4);
ASSERT_VALUE(EROFS_I_DOT_OMITTED_BIT, 4);
ASSERT_VALUE(EROFS_I_ALL, 31);
ASSERT_VALUE(EROFS_CHUNK_FORMAT_BLKBITS_MASK, 0x001f);
ASSERT_VALUE(EROFS_CHUNK_FORMAT_INDEXES, 0x0020);
ASSERT_VALUE(EROFS_CHUNK_FORMAT_48BIT, 0x0040);
ASSERT_VALUE(EROFS_CHUNK_FORMAT_ALL, 0x007f);
ASSERT_VALUE(EROFS_INODE_LAYOUT_COMPACT, 0);
ASSERT_VALUE(EROFS_INODE_LAYOUT_EXTENDED, 1);
ASSERT_VALUE(EROFS_XATTR_INDEX_USER, 1);
ASSERT_VALUE(EROFS_XATTR_INDEX_POSIX_ACL_ACCESS, 2);
ASSERT_VALUE(EROFS_XATTR_INDEX_POSIX_ACL_DEFAULT, 3);
ASSERT_VALUE(EROFS_XATTR_INDEX_TRUSTED, 4);
ASSERT_VALUE(EROFS_XATTR_INDEX_LUSTRE, 5);
ASSERT_VALUE(EROFS_XATTR_INDEX_SECURITY, 6);
ASSERT_VALUE(EROFS_XATTR_LONG_PREFIX, 0x80);
ASSERT_VALUE(EROFS_XATTR_LONG_PREFIX_MASK, 0x7f);
ASSERT_VALUE(EROFS_XATTR_ALIGN(0), 0);
ASSERT_VALUE(EROFS_XATTR_ALIGN(1), 4);
ASSERT_VALUE(EROFS_XATTR_ALIGN(5), 8);
ASSERT_VALUE((uint64_t)EROFS_NULL_ADDR, UINT64_MAX);
ASSERT_VALUE(EROFS_BLOCK_MAP_ENTRY_SIZE, 4);
ASSERT_VALUE(EROFS_DIRENT_NID_METABOX_BIT, 63);
ASSERT_VALUE(EROFS_DIRENT_NID_MASK, 0x7fffffffffffffffULL);
ASSERT_VALUE(EROFS_NAME_LEN, 255);
ASSERT_VALUE(Z_EROFS_PCLUSTER_MAX_SIZE, 1048576);
ASSERT_VALUE(Z_EROFS_PCLUSTER_MAX_DSIZE, 12582912);
ASSERT_VALUE(Z_EROFS_COMPRESSION_LZ4, 0);
ASSERT_VALUE(Z_EROFS_COMPRESSION_LZMA, 1);
ASSERT_VALUE(Z_EROFS_COMPRESSION_DEFLATE, 2);
ASSERT_VALUE(Z_EROFS_COMPRESSION_ZSTD, 3);
ASSERT_VALUE(Z_EROFS_COMPRESSION_MAX, 4);
ASSERT_VALUE(Z_EROFS_ALL_COMPR_ALGS, 15);
ASSERT_VALUE(Z_EROFS_LZMA_MAX_DICT_SIZE, 8388608);
ASSERT_VALUE(Z_EROFS_ZSTD_MAX_DICT_SIZE, 1048576);
ASSERT_VALUE(Z_EROFS_ADVISE_COMPACTED_2B, 0x0001);
ASSERT_VALUE(Z_EROFS_ADVISE_EXTENTS, 0x0001);
ASSERT_VALUE(Z_EROFS_ADVISE_BIG_PCLUSTER_1, 0x0002);
ASSERT_VALUE(Z_EROFS_ADVISE_BIG_PCLUSTER_2, 0x0004);
ASSERT_VALUE(Z_EROFS_ADVISE_INLINE_PCLUSTER, 0x0008);
ASSERT_VALUE(Z_EROFS_ADVISE_INTERLACED_PCLUSTER, 0x0010);
ASSERT_VALUE(Z_EROFS_ADVISE_FRAGMENT_PCLUSTER, 0x0020);
ASSERT_VALUE(Z_EROFS_ADVISE_EXTRECSZ_BIT, 1);
ASSERT_VALUE(Z_EROFS_ADVISE_EXTRECSZ_MASK, 3);
ASSERT_VALUE(Z_EROFS_FRAGMENT_INODE_BIT, 7);
ASSERT_VALUE(Z_EROFS_LCLUSTER_TYPE_PLAIN, 0);
ASSERT_VALUE(Z_EROFS_LCLUSTER_TYPE_HEAD1, 1);
ASSERT_VALUE(Z_EROFS_LCLUSTER_TYPE_NONHEAD, 2);
ASSERT_VALUE(Z_EROFS_LCLUSTER_TYPE_HEAD2, 3);
ASSERT_VALUE(Z_EROFS_LCLUSTER_TYPE_MAX, 4);
ASSERT_VALUE(Z_EROFS_LI_LCLUSTER_TYPE_MASK, 3);
ASSERT_VALUE(Z_EROFS_LI_PARTIAL_REF, 0x8000);
ASSERT_VALUE(Z_EROFS_LI_D0_CBLKCNT, 0x0800);
ASSERT_VALUE(Z_EROFS_MAP_HEADER_END(0), 8);
ASSERT_VALUE(Z_EROFS_MAP_HEADER_END(1), 16);
ASSERT_VALUE(Z_EROFS_FULL_INDEX_START(1), 24);
ASSERT_VALUE(Z_EROFS_EXTENT_PLEN_PARTIAL, 0x08000000);
ASSERT_VALUE(Z_EROFS_EXTENT_PLEN_FMT_BIT, 28);
ASSERT_VALUE(Z_EROFS_EXTENT_PLEN_MASK, 0x001fffff);

#ifndef B02_LINUX_REFERENCE
ASSERT_VALUE(EROFS_SUPER_MAGIC_V1, 0xE0F5E1E2);
ASSERT_VALUE(EROFS_INODE_LAYOUT_PLAIN, EROFS_INODE_FLAT_PLAIN);
ASSERT_VALUE(EROFS_DIRENT_NID_METABOX, 0x8000000000000000ULL);
ASSERT_VALUE(EROFS_FT_UNKNOWN, 0);
ASSERT_VALUE(EROFS_FT_REG_FILE, 1);
ASSERT_VALUE(EROFS_FT_DIR, 2);
ASSERT_VALUE(EROFS_FT_CHRDEV, 3);
ASSERT_VALUE(EROFS_FT_BLKDEV, 4);
ASSERT_VALUE(EROFS_FT_FIFO, 5);
ASSERT_VALUE(EROFS_FT_SOCK, 6);
ASSERT_VALUE(EROFS_FT_SYMLINK, 7);
#endif

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