#pragma once

namespace fast_io
{

extern void *linux_kernel_kmalloc(::std::size_t, int unsigned) noexcept __asm__("__kmalloc");

extern void *linux_kernel_krealloc(void const *, ::std::size_t, int unsigned) noexcept __asm__("krealloc");

extern void linux_kernel_kfree(void const *) noexcept __asm__("kfree");

inline constexpr int unsigned linux_kernel_gfp_kernel{0x400u | 0x800u | 0x40u | 0x80u};

inline constexpr int unsigned linux_kernel_gfp_kernel_zero{linux_kernel_gfp_kernel | 0x100u};

class linux_kmalloc_allocator
{
public:
#if __has_cpp_attribute(__gnu__::__returns_nonnull__)
	[[__gnu__::__returns_nonnull__]]
#endif
	static inline void *allocate(::std::size_t n) noexcept
	{
		if (n == 0)
		{
			n = 1;
		}
		void *p = linux_kernel_kmalloc(n, linux_kernel_gfp_kernel);
		if (p == nullptr)
		{
			::fast_io::fast_terminate();
		}
		return p;
	}
	static inline void *reallocate(void *p, ::std::size_t n) noexcept
	{
		if (n == 0)
		{
			n = 1;
		}
		p = linux_kernel_krealloc(p, n, linux_kernel_gfp_kernel);
		if (p == nullptr)
		{
			::fast_io::fast_terminate();
		}
		return p;
	}
	static inline void *allocate_zero(::std::size_t n) noexcept
	{
		if (n == 0)
		{
			n = 1;
		}
		void *p = linux_kernel_kmalloc(n, linux_kernel_gfp_kernel_zero);
		if (p == nullptr)
		{
			::fast_io::fast_terminate();
		}
		return p;
	}
	static inline void deallocate(void *p) noexcept
	{
		if (p == nullptr)
		{
			return;
		}
		linux_kernel_kfree(p);
	}
};

} // namespace fast_io
