// Copyright (c) 2023-2025 Christoffer Lerno and contributors. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::bitorder;
import std::bits;
// This module contains types of different endianness.
// *BE types represent big-endian types
// *LE types represent little-endian types.

bitstruct ShortBE : short @bigendian
{
	short val : 0..15;
}

bitstruct UShortBE : ushort @bigendian
{
	ushort val : 0..15;
}

bitstruct IntBE : int @bigendian
{
	int val : 0..31;
}

bitstruct UIntBE : int @bigendian
{
	uint val : 0..31;
}

bitstruct LongBE : long @bigendian
{
	long val : 0..63;
}

bitstruct ULongBE : ulong @bigendian
{
	ulong val : 0..63;
}

bitstruct Int128BE : int128 @bigendian
{
	int128 val : 0..127;
}

bitstruct UInt128BE : uint128 @bigendian
{
	uint128 val : 0..127;
}

bitstruct ShortLE : short @littleendian
{
	short val : 0..15;
}

bitstruct UShortLE : ushort @littleendian
{
	ushort val : 0..15;
}

bitstruct IntLE : int @littleendian
{
	int val : 0..31;
}

bitstruct UIntLE : int @littleendian
{
	uint val : 0..31;
}

bitstruct LongLE : long @littleendian
{
	long val : 0..63;
}

bitstruct ULongLE : ulong @littleendian
{
	ulong val : 0..63;
}

bitstruct Int128LE : int128 @littleendian
{
	int128 val : 0..127;
}

bitstruct UInt128LE : uint128 @littleendian
{
	uint128 val : 0..127;
}

<*
 @require $defined(*bytes) : "Pointer must be possible to dereference"
 @require types::is_intlike($typeof(*bytes)) : "Type must be an integer or int vector"
*>
macro load_be(bytes)
{
	$if env::BIG_ENDIAN:
		return mem::load(bytes, $align: 1);
	$else
		return bswap(mem::load(bytes, $align: 1));
	$endif
}

<*
 @require $defined(*bytes) : "Pointer must be possible to dereference"
 @require types::is_intlike($typeof(*bytes)) : "Type must be an integer or int vector"
*>
macro load_le(bytes)
{
	$if env::BIG_ENDIAN:
		return bswap(mem::load(bytes, $align: 1));
	$else
		return mem::load(bytes, $align: 1);
	$endif
}

<*
 @require types::is_intlike($typeof(value)) : "Type must be an integer or int vector"
*>
macro void store_be(void* dst, value)
{
	$if env::BIG_ENDIAN:
		mem::store(($typeof(value)*)dst, value, $align: 1);
	$else
		mem::store(($typeof(value)*)dst, bswap(value), $align: 1);
	$endif
}

<*
 @require types::is_intlike($typeof(value)) : "Type must be an integer or int vector"
*>
macro void store_le(void* dst, value)
{
	$if env::BIG_ENDIAN:
		mem::store(($typeof(value)*)dst, bswap(value), $align: 1);
	$else
		mem::store(($typeof(value)*)dst, value, $align: 1);
	$endif
}

<*
 @require @is_array_or_slice_of_char(bytes) : "argument must be an array, a pointer to an array or a slice of char"
 @require is_bitorder($Type) : "type must be a bitorder integer"
 @require $defined(*bytes) ||| $defined(bytes[:$Type.sizeof]) : "Data is too short to contain value"
*>
macro read(bytes, $Type)
{
	char *ptr;
	$switch $kindof(bytes):
		$case POINTER:
			ptr = bytes;
		$default:
			ptr = bytes[..].ptr;
	$endswitch
	return bitcast(mem::load((char[$Type.sizeof]*)ptr, $align: 1), $Type).val;
}

<*
 @require @is_arrayptr_or_slice_of_char(bytes) : "argument must be a pointer to an array or a slice of char"
 @require is_bitorder($Type) : "type must be a bitorder integer"
 @require $defined(*bytes) ||| $defined(bytes[:$Type.sizeof]) : "Data is not sufficent to hold value"
*>
macro write(x, bytes, $Type)
{
	char *ptr;
	$switch $kindof(bytes):
		$case POINTER:
			ptr = bytes;
		$default:
			ptr = bytes[..].ptr;
	$endswitch
	mem::store(($typeof(x)*)ptr, bitcast(x, $Type).val, 1);
}

macro bool is_bitorder($Type)
{
	$switch $Type:
		$case UShortLE:
		$case ShortLE:
		$case UIntLE:
		$case IntLE:
		$case ULongLE:
		$case LongLE:
		$case UInt128LE:
		$case Int128LE:
		$case UShortBE:
		$case ShortBE:
		$case UIntBE:
		$case IntBE:
		$case ULongBE:
		$case LongBE:
		$case UInt128BE:
		$case Int128BE:
			return true;
		$default:
			return false;
	$endswitch
}

macro bool is_array_or_slice_of_char(bytes) @deprecated("Use @is_array_or_slice_of_char")
{
	return @is_array_or_slice_of_char(bytes);
}

macro bool @is_array_or_slice_of_char(#bytes) @const
{
    var $Type = $typeof(#bytes);
	$switch $Type.kindof:
		$case POINTER:
			typeid $inner = $Type.inner;
			return $inner.kindof == ARRAY &&& $inner.inner == char.typeid;
		$case ARRAY:
		$case SLICE:
			return $Type.inner == char.typeid;
		$default:
			return false;
	$endswitch
}

macro bool is_arrayptr_or_slice_of_char(bytes) @deprecated("Use @is_arrayptr_or_slice_of_char")
{
	return @is_arrayptr_or_slice_of_char(bytes);
}

macro bool @is_arrayptr_or_slice_of_char(#bytes) @const
{
	var $Type = $typeof(#bytes);
	$switch $Type.kindof:
		$case POINTER:
			typeid $inner = $Type.inner;
			return $inner.kindof == ARRAY &&& $inner.inner == char.typeid;
		$case SLICE:
			return $Type.inner == char.typeid;
		$default:
			return false;
	$endswitch
}
