using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using Kalk.Core.Helpers;
using Scriban.Runtime;
namespace Kalk.Core.Modules
{
///
/// Memory functions.
///
public sealed partial class MemoryModule : KalkModuleWithFunctions
{
public const string CategoryMiscMemory = "Misc Memory Functions";
public MemoryModule()
{
IsBuiltin = true;
RegisterFunctionsAuto();
}
///
/// Allocates a `bytebuffer` of the specified size.
///
/// Size of the bytebuffer.
/// A bytebuffer of the specified size.
///
/// ```kalk
/// >>> buffer = malloc(16)
/// # buffer = malloc(16)
/// buffer = bytebuffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
/// >>> buffer[0] = 5
/// >>> buffer
/// # buffer
/// out = bytebuffer([5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
/// ```
///
[KalkExport("malloc", CategoryMiscMemory)]
public KalkNativeBuffer Malloc(int size)
{
if (size < 0) throw new ArgumentOutOfRangeException(nameof(size), "Size must be >= 0");
return new KalkNativeBuffer(size);
}
///
/// Binary cast of a value to a target type.
///
/// The type to cast to.
/// The value to cast.
/// The binary cast of the input value.
/// The supported types are `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `float`, `double`, `rgb`, `rgba` and all vector and matrix types.
///
/// ```kalk
/// >>> bitcast(int, 1.5f)
/// # bitcast(int, 1.5f)
/// out = 1_069_547_520
/// >>> bitcast(float, out)
/// # bitcast(float, out)
/// out = 1.5
/// >>> bitcast(long, 2.5)
/// # bitcast(long, 2.5)
/// out = 4_612_811_918_334_230_528
/// >>> bitcast(double, out)
/// # bitcast(double, out)
/// out = 2.5
/// >>> asbytes(float4(1..4))
/// # asbytes(float4(1..4))
/// out = bytebuffer([0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 128, 64])
/// >>> bitcast(float4, out)
/// # bitcast(float4, out)
/// out = float4(1, 2, 3, 4)
/// ```
///
[KalkExport("bitcast", CategoryMiscMemory)]
public object Bitcast(object type, object value)
{
if (type == null) throw new ArgumentNullException(nameof(type));
var bytes = AsBytes(value);
if (bytes == null) return null;
switch (type)
{
case byte:
return UnsafeHelpers.BitCast(bytes);
case sbyte:
return UnsafeHelpers.BitCast(bytes);
case short:
return UnsafeHelpers.BitCast(bytes);
case ushort:
return UnsafeHelpers.BitCast(bytes);
case int:
return UnsafeHelpers.BitCast(bytes);
case uint:
return UnsafeHelpers.BitCast(bytes);
case ulong:
return UnsafeHelpers.BitCast(bytes);
case long:
return UnsafeHelpers.BitCast(bytes);
case float:
return UnsafeHelpers.BitCast(bytes);
case KalkHalf:
return UnsafeHelpers.BitCast(bytes);
case double:
return UnsafeHelpers.BitCast(bytes);
case KalkValue kalkValue:
var dest = (KalkValue)kalkValue.Clone(true);
var span = kalkValue.AsSpan();
var elementCount = bytes.Count / span.Length;
if (elementCount == 0) return dest;
if (elementCount == 1)
{
dest.BitCastFrom(bytes.AsSpan());
return dest;
}
var scriptArray = new ScriptArray();
var nextBytes = bytes.AsSpan();
for (int i = 0; i < elementCount; i++)
{
dest.BitCastFrom(nextBytes);
scriptArray.Add(dest);
nextBytes = nextBytes.Slice(span.Length);
dest = (KalkValue)kalkValue.Clone(true);
}
return scriptArray;
default:
throw new ArgumentException($"The destination type `{Engine.GetTypeName(type)}` is not supported.", nameof(value));
}
}
///
/// Binary cast the specified value to a bytebuffer.
///
/// An input value.
/// A binary bytebuffer representing the value in binary form. The size of the buffer equals the binary size in memory of the input value.
///
/// ```kalk
/// >>> asbytes(float4(1..4))
/// # asbytes(float4(1..4))
/// out = bytebuffer([0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 128, 64])
/// >>> asbytes(int(0x01020304))
/// # asbytes(int(16909060))
/// out = bytebuffer([4, 3, 2, 1])
/// >>> asbytes(1.5)
/// # asbytes(1.5)
/// out = bytebuffer([0, 0, 0, 0, 0, 0, 248, 63])
/// >>> asbytes(2.5f)
/// # asbytes(2.5f)
/// out = bytebuffer([0, 0, 32, 64])
/// ```
///
[KalkExport("asbytes", CategoryMiscMemory)]
public KalkNativeBuffer AsBytes(object value)
{
if (value == null) return null;
switch (value)
{
case string str:
{
var buffer = Encoding.UTF8.GetBytes(str);
return KalkNativeBuffer.AsBytes(buffer.Length, in buffer[0]);
}
case byte vbyte:
return KalkNativeBuffer.AsBytes(1, vbyte);
case sbyte vsbyte:
return KalkNativeBuffer.AsBytes(1, vsbyte);
case short vshort:
return KalkNativeBuffer.AsBytes(2, vshort);
case ushort vushort:
return KalkNativeBuffer.AsBytes(2, vushort);
case int vint:
return KalkNativeBuffer.AsBytes(4, vint);
case uint vuint:
return KalkNativeBuffer.AsBytes(4, vuint);
case long vlong:
return KalkNativeBuffer.AsBytes(8, vlong);
case ulong vulong:
return KalkNativeBuffer.AsBytes(8, vulong);
case float vfloat:
{
var floatAsInt = BitConverter.SingleToInt32Bits(vfloat);
return KalkNativeBuffer.AsBytes(4, floatAsInt);
}
case double vdouble:
{
var doubleAsLong = BitConverter.DoubleToInt64Bits(vdouble);
return KalkNativeBuffer.AsBytes(8, doubleAsLong);
}
case BigInteger bigInt:
{
var array = bigInt.ToByteArray();
return KalkNativeBuffer.AsBytes(array.Length, in array[0]);
}
case IKalkSpannable spannable:
{
var span = spannable.AsSpan();
return KalkNativeBuffer.AsBytes(span.Length, in span[0]);
}
default:
throw new ArgumentException($"The type {Engine.GetTypeName(value)} is not supported ", nameof(value));
}
}
///
/// Counts the number of bits (per component) of the input value.
///
/// The input value.
/// The number of bits (per component if the input is an int vector).
///
/// ```kalk
/// >>> for val in 0..7; countbits(val); end;
/// # for val in 0..7; countbits(val); end;
/// out = 0
/// out = 1
/// out = 1
/// out = 2
/// out = 1
/// out = 2
/// out = 2
/// out = 3
/// >>> countbits(int4(1,2,3,4))
/// # countbits(int4(1, 2, 3, 4))
/// out = int4(1, 1, 2, 1)
/// >>> countbits(bytebuffer(1..16))
/// # countbits(bytebuffer(1..16))
/// out = 33
/// ```
///
[KalkExport("countbits", CategoryMiscMemory)]
public object CountBits(object value)
{
if (value == null) return 0;
switch (value)
{
case byte vbyte:
return BitOperations.PopCount(vbyte);
case sbyte vsbyte:
return BitOperations.PopCount((uint)vsbyte);
case short vshort:
return BitOperations.PopCount((uint)vshort);
case ushort vushort:
return BitOperations.PopCount((uint)vushort);
case int vint:
return BitOperations.PopCount((uint)vint);
case uint vuint:
return BitOperations.PopCount((uint)vuint);
case long vlong:
return BitOperations.PopCount((ulong)vlong);
case ulong vulong:
return BitOperations.PopCount((ulong)vulong);
case BigInteger bigInt:
{
// TODO: not optimized, should use 64 bits
var array = bigInt.ToByteArray();
int count = 0;
for (int i = 0; i < array.Length; i++)
{
count += BitOperations.PopCount(array[i]);
}
return count;
}
case KalkVector vector:
var uintVector = new KalkVector(vector.Length);
for (int i = 0; i < vector.Length; i++)
{
var result = CountBits(vector.GetComponent(i));
if (result is int intv)
{
uintVector[i] = (int)intv;
}
else
{
uintVector[i] = (int)(long)result;
}
}
return uintVector;
case KalkNativeBuffer nativeBuffer:
{
var span = nativeBuffer.AsSpan();
int count = 0;
for (int i = 0; i < span.Length; i++)
{
count += BitOperations.PopCount(span[i]);
}
return count;
}
case KalkMatrix matrix:
var uintMatrix = new KalkMatrix(matrix.RowCount, matrix.ColumnCount);
for (int y = 0; y < matrix.RowCount; y++)
{
uintMatrix.SetRow(y, (KalkVector)CountBits(matrix.GetRow(y)));
}
return uintMatrix;
default:
throw new ArgumentException($"The type {Engine.GetTypeName(value)} is not supported.", nameof(value));
}
}
///
/// Gets the location of the first set bit starting from the highest order bit and working downward, per component.
///
/// The input value.
/// The location of the first set bit.
/// If no bits are sets, this function will return -1.
///
/// ```kalk
/// >>> firstbithigh 128
/// # firstbithigh(128)
/// out = 24
/// >>> firstbithigh byte(128)
/// # firstbithigh(byte(128))
/// out = 0
/// >>> firstbithigh 0
/// # firstbithigh(0)
/// out = -1
/// >>> firstbithigh(int4(1, -1, 65536, 1 << 20))
/// # firstbithigh(int4(1, -1, 65536, 1 << 20))
/// out = int4(31, 0, 15, 11)
/// ```
///
///
/// ```kalk
/// >>> firstbithigh ulong(1 << 63)
/// # firstbithigh(ulong(1 << 63))
/// out = 0
/// >>> firstbithigh long(1)
/// # firstbithigh(long(1))
/// out = 63
/// >>> firstbithigh long(0)
/// # firstbithigh(long(0))
/// out = -1
/// ```
///
[KalkExport("firstbithigh", CategoryMiscMemory)]
public object FirstBitHigh(object value)
{
if (value == null) return 0;
switch (value)
{
case sbyte vsbyte:
{
var result = BitOperations.LeadingZeroCount((byte)(uint)vsbyte);
if (result >= 32) return -1;
return result - 24;
}
case byte vbyte:
{
var result = BitOperations.LeadingZeroCount(vbyte);
if (result >= 32) return -1;
return result - 24;
}
case short vshort:
{
var result = BitOperations.LeadingZeroCount((ushort)(uint)vshort);
if (result >= 32) return -1;
return result - 16;
}
case ushort vushort:
{
var result = BitOperations.LeadingZeroCount((uint)vushort);
if (result >= 32) return -1;
return result - 16;
}
case int vint:
{
var result = BitOperations.LeadingZeroCount((uint)vint);
if (result >= 32) return -1;
return result;
}
case uint vuint:
{
var result = BitOperations.LeadingZeroCount((uint)vuint);
if (result >= 32) return -1;
return result;
}
case long vlong:
{
var result = BitOperations.LeadingZeroCount((ulong)vlong);
if (result >= 64) return -1;
return result;
}
case ulong vulong:
{
var result = BitOperations.LeadingZeroCount((ulong)vulong);
if (result >= 64) return -1;
return result;
}
case KalkVector vector:
var uintVector = new KalkVector(vector.Length);
for (int i = 0; i < vector.Length; i++)
{
var result = FirstBitHigh(vector.GetComponent(i));
if (result is int intv)
{
uintVector[i] = (int)intv;
}
else
{
uintVector[i] = (int)(long)result;
}
}
return uintVector;
case KalkMatrix matrix:
var uintMatrix = new KalkMatrix(matrix.RowCount, matrix.ColumnCount);
for (int y = 0; y < matrix.RowCount; y++)
{
uintMatrix.SetRow(y, (KalkVector)FirstBitHigh(matrix.GetRow(y)));
}
return uintMatrix;
default:
throw new ArgumentException($"The type {Engine.GetTypeName(value)} is not supported.", nameof(value));
}
}
///
/// Returns the location of the first set bit starting from the lowest order bit and working upward, per component.
///
/// The input value.
/// The location of the first set bit.
/// If no bits are sets, this function will return -1.
///
/// ```kalk
/// >>> firstbitlow 128
/// # firstbitlow(128)
/// out = 7
/// >>> firstbitlow byte(128)
/// # firstbitlow(byte(128))
/// out = 7
/// >>> firstbitlow 0
/// # firstbitlow(0)
/// out = -1
/// >>> firstbitlow(int4(1, -1, 65536, 1 << 20))
/// # firstbitlow(int4(1, -1, 65536, 1 << 20))
/// out = int4(0, 0, 16, 20)
/// ```
///
///
/// ```kalk
/// >>> firstbitlow ulong(1 << 63)
/// # firstbitlow(ulong(1 << 63))
/// out = 63
/// >>> firstbitlow long(1)
/// # firstbitlow(long(1))
/// out = 0
/// >>> firstbitlow long(0)
/// # firstbitlow(long(0))
/// out = -1
/// ```
///
[KalkExport("firstbitlow", CategoryMiscMemory)]
public object FirstBitLow(object value)
{
if (value == null) return 0;
switch (value)
{
case sbyte vsbyte:
{
var result = BitOperations.TrailingZeroCount(vsbyte);
if (result >= 32) return -1;
return result;
}
case byte vbyte:
{
var result = BitOperations.TrailingZeroCount(vbyte);
if (result >= 32) return -1;
return result;
}
case short vshort:
{
var result = BitOperations.TrailingZeroCount(vshort);
if (result >= 32) return -1;
return result;
}
case ushort vushort:
{
var result = BitOperations.TrailingZeroCount(vushort);
if (result >= 32) return -1;
return result;
}
case int vint:
{
var result = BitOperations.TrailingZeroCount(vint);
if (result >= 32) return -1;
return result;
}
case uint vuint:
{
var result = BitOperations.TrailingZeroCount(vuint);
if (result >= 32) return -1;
return result;
}
case long vlong:
{
var result = BitOperations.TrailingZeroCount(vlong);
if (result >= 64) return -1;
return result;
}
case ulong vulong:
{
var result = BitOperations.TrailingZeroCount(vulong);
if (result >= 64) return -1;
return result;
}
case BigInteger bigint:
if (bigint >= 0 && bigint <= ulong.MaxValue)
{
var result = BitOperations.TrailingZeroCount((ulong)bigint);
if (result >= 64) return -1;
return result;
}
else if (bigint < 0 && bigint >= long.MinValue)
{
var result = BitOperations.TrailingZeroCount((long)bigint);
if (result >= 64) return -1;
return result;
}
// TODO: implement this case for bigint
goto default;
case KalkVector vector:
var uintVector = new KalkVector(vector.Length);
for (int i = 0; i < vector.Length; i++)
{
var result = FirstBitLow(vector.GetComponent(i));
if (result is int intv)
{
uintVector[i] = (int)intv;
}
else
{
uintVector[i] = (int)(long)result;
}
}
return uintVector;
case KalkMatrix matrix:
var uintMatrix = new KalkMatrix(matrix.RowCount, matrix.ColumnCount);
for (int y = 0; y < matrix.RowCount; y++)
{
uintMatrix.SetRow(y, (KalkVector)FirstBitLow(matrix.GetRow(y)));
}
return uintMatrix;
default:
throw new ArgumentException($"The type {Engine.GetTypeName(value)} is not supported.", nameof(value));
}
}
///
/// Reverses the order of the bits, per component
///
/// The input value.
/// The input value, with the bit order reversed
///
/// ```kalk
/// >>> reversebits 128
/// # reversebits(128)
/// out = 16_777_216
/// >>> reversebits out
/// # reversebits(out)
/// out = 128
/// >>> reversebits byte(128)
/// # reversebits(byte(128))
/// out = 1
/// >>> reversebits(out)
/// # reversebits(out)
/// out = 128
/// >>> reversebits(int4(1,2,3,4))
/// # reversebits(int4(1, 2, 3, 4))
/// out = int4(-2_147_483_648, 1_073_741_824, -1_073_741_824, 536_870_912)
/// >>> reversebits out
/// # reversebits(out)
/// out = int4(1, 2, 3, 4)
/// ```
///
///
/// ```kalk
/// >>> reversebits long(1)
/// # reversebits(long(1))
/// out = -9_223_372_036_854_775_808
/// >>> reversebits out
/// # reversebits(out)
/// out = 1
/// >>> reversebits(bytebuffer([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]))
/// # reversebits(bytebuffer([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]))
/// out = bytebuffer([240, 112, 176, 48, 208, 80, 144, 16, 224, 96, 160, 32, 192, 64, 128])
/// >>> reversebits out
/// # reversebits(out)
/// out = bytebuffer([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
/// ```
///
[KalkExport("reversebits", CategoryMiscMemory)]
public object ReverseBits(object value)
{
if (value == null) return 0;
switch (value)
{
case byte vbyte:
return ReverseBytes[vbyte];
case sbyte vsbyte:
return ReverseBytes[(byte)vsbyte];
case short vshort:
return (short) ((ReverseBytes[(byte) vshort] << 8) | ReverseBytes[(byte) (vshort >> 8)]);
case ushort vushort:
return (short) ((ReverseBytes[(byte) vushort] << 8) | ReverseBytes[(byte) (vushort >> 8)]);
case int vint:
return (ReverseBytes[(byte) vint] << 24) |
(ReverseBytes[(byte) (vint >> 8)] << 16) |
(ReverseBytes[(byte) (vint >> 16)] << 8) |
ReverseBytes[(byte) (vint >> 24)];
case uint vuint:
return (ReverseBytes[(byte)vuint] << 24) |
(ReverseBytes[(byte)(vuint >> 8)] << 16) |
(ReverseBytes[(byte)(vuint >> 16)] << 8) |
ReverseBytes[(byte)(vuint >> 24)];
case long vlong:
return ((long) ReverseBytes[(byte) vlong] << 56) |
((long) ReverseBytes[(byte) (vlong >> 8)] << 48) |
((long) ReverseBytes[(byte) (vlong >> 16)] << 40) |
((long) ReverseBytes[(byte) (vlong >> 24)] << 32) |
((long) ReverseBytes[(byte) (vlong >> 32)] << 24) |
((long) ReverseBytes[(byte) (vlong >> 40)] << 16) |
((long) ReverseBytes[(byte) (vlong >> 48)] << 8) |
(long) ReverseBytes[(byte) (vlong >> 56)];
case ulong vulong:
return ((ulong)ReverseBytes[(byte)vulong] << 56) |
((ulong)ReverseBytes[(byte)(vulong >> 8)] << 48) |
((ulong)ReverseBytes[(byte)(vulong >> 16)] << 40) |
((ulong)ReverseBytes[(byte)(vulong >> 24)] << 32) |
((ulong)ReverseBytes[(byte)(vulong >> 32)] << 24) |
((ulong)ReverseBytes[(byte)(vulong >> 40)] << 16) |
((ulong)ReverseBytes[(byte)(vulong >> 48)] << 8) |
(ulong)ReverseBytes[(byte)(vulong >> 56)];
case BigInteger bigInt:
{
var array = bigInt.ToByteArray();
int mid = array.Length / 2;
for (int i = 0; i < mid; i++)
{
var highIndex = array.Length - i - 1;
var high = ReverseBytes[array[highIndex]];
var low = ReverseBytes[array[i]];
array[i] = high;
array[highIndex] = low;
}
if ((array.Length & 1) != 0)
{
array[mid] = ReverseBytes[array[mid]];
}
return new BigInteger(array);
}
case KalkVector vector:
var outVector = vector.Clone();
for (int i = 0; i < vector.Length; i++)
{
var result = ReverseBits(vector.GetComponent(i));
outVector.SetComponent(i, result);
}
return outVector;
case KalkNativeBuffer nativeBuffer:
{
var buffer = new KalkNativeBuffer(nativeBuffer.Count);
int mid = nativeBuffer.Count / 2;
for (int i = 0; i < mid; i++)
{
var highIndex = nativeBuffer.Count - i - 1;
var high = ReverseBytes[nativeBuffer[highIndex]];
var low = ReverseBytes[nativeBuffer[i]];
buffer[i] = high;
buffer[highIndex] = low;
}
if ((buffer.Count & 1) != 0)
{
buffer[mid] = ReverseBytes[nativeBuffer[mid]];
}
return buffer;
}
case KalkMatrix matrix:
var uintMatrix = new KalkMatrix(matrix.RowCount, matrix.ColumnCount);
for (int y = 0; y < matrix.RowCount; y++)
{
uintMatrix.SetRow(y, (KalkVector)ReverseBits(matrix.GetRow(y)));
}
return uintMatrix;
default:
throw new ArgumentException($"The type {Engine.GetTypeName(value)} is not supported.", nameof(value));
}
}
///
/// Reinterprets a 64-bit value into a double.
///
/// The input value.
/// The input recast as a double.
///
/// ```kalk
/// >>> asdouble(1.5)
/// # asdouble(1.5)
/// out = 1.5
/// >>> aslong(1.5)
/// # aslong(1.5)
/// out = 4_609_434_218_613_702_656
/// >>> asdouble(out)
/// # asdouble(out)
/// out = 1.5
/// ```
///
[KalkExport("asdouble", CategoryMiscMemory)]
public double AsDouble(object x)
{
switch (x)
{
case long longValue:
return BitConverter.Int64BitsToDouble(longValue);
case ulong ulongValue:
return BitConverter.Int64BitsToDouble((long)ulongValue);
case int intValue:
return BitConverter.Int32BitsToSingle(intValue);
case uint uintValue:
return BitConverter.Int32BitsToSingle((int)uintValue);
case double d:
return d;
case float f:
return f;
case KalkNativeBuffer buffer:
return BitCastTo(buffer);
}
var bigInteger = Engine.ToObject(0, x);
var value = (long) bigInteger;
return BitConverter.Int64BitsToDouble(value);
}
///
/// Reinterprets a 32-bit value into a float.
///
/// The input value.
/// The input recast as a float.
///
/// ```kalk
/// >>> asfloat(1.5f)
/// # asfloat(1.5f)
/// out = 1.5
/// >>> asint(1.5f)
/// # asint(1.5f)
/// out = 1_069_547_520
/// >>> asfloat(out)
/// # asfloat(out)
/// out = 1.5
/// ```
///
[KalkExport("asfloat", CategoryMiscMemory)]
public float AsFloat(object x)
{
switch (x)
{
case long longValue:
return (float)BitConverter.Int64BitsToDouble(longValue);
case ulong ulongValue:
return (float)BitConverter.Int64BitsToDouble((long)ulongValue);
case int intValue:
return BitConverter.Int32BitsToSingle(intValue);
case uint uintValue:
return BitConverter.Int32BitsToSingle((int)uintValue);
case double d:
return (float)d;
case float f:
return f;
case KalkNativeBuffer buffer:
return BitCastTo(buffer);
}
var bigInteger = Engine.ToObject(0, x);
var value = (long)bigInteger;
return (float)BitConverter.Int64BitsToDouble(value);
}
///
/// Reinterprets an input value to a 64-bit long.
///
/// The input value.
/// The input recast as a 64-bit long.
///
/// ```kalk
/// >>> aslong(1.5)
/// # aslong(1.5)
/// out = 4_609_434_218_613_702_656
/// >>> asdouble(out)
/// # asdouble(out)
/// out = 1.5
/// ```
///
[KalkExport("aslong", CategoryMiscMemory)]
public long AsLong(object x)
{
switch (x)
{
case long longValue:
return longValue;
case ulong ulongValue:
return (long)ulongValue;
case int intValue:
return intValue;
case uint uintValue:
return uintValue;
case double f64:
return BitConverter.DoubleToInt64Bits(f64);
case float f32:
return BitConverter.SingleToInt32Bits(f32);
case KalkNativeBuffer buffer:
return BitCastTo(buffer);
default:
return (long)Engine.ToObject(0, x);
}
}
///
/// Reinterprets an input value to a 64-bit ulong.
///
/// The input value.
/// The input recast as a 64-bit ulong.
///
/// ```kalk
/// >>> asulong(-1.5)
/// # asulong(-1.5)
/// out = 13_832_806_255_468_478_464
/// >>> asdouble(out)
/// # asdouble(out)
/// out = -1.5
/// ```
///
[KalkExport("asulong", CategoryMiscMemory)]
public ulong AsULong(object x)
{
switch (x)
{
case long longValue:
return (ulong)longValue;
case ulong ulongValue:
return ulongValue;
case int intValue:
return (ulong)intValue;
case uint uintValue:
return uintValue;
case double f64:
return (ulong)BitConverter.DoubleToInt64Bits(f64);
case float f32:
return (ulong)BitConverter.SingleToInt32Bits(f32);
case KalkNativeBuffer buffer:
return BitCastTo(buffer);
default:
return (ulong)(long)Engine.ToObject(0, x);
}
}
///
/// Reinterprets an input value into a 32-bit int.
///
/// The input value.
/// The input recast as a 32-bit int.
///
/// ```kalk
/// >>> asint(1.5f)
/// # asint(1.5f)
/// out = 1_069_547_520
/// >>> asfloat(out)
/// # asfloat(out)
/// out = 1.5
/// ```
///
[KalkExport("asint", CategoryMiscMemory)]
public int AsInt(object x)
{
switch (x)
{
case long longValue:
return (int)longValue;
case ulong ulongValue:
return (int)ulongValue;
case int intValue:
return intValue;
case uint uintValue:
return (int)uintValue;
case double f64:
return BitConverter.SingleToInt32Bits((float)f64);
case float f32:
return BitConverter.SingleToInt32Bits(f32);
case KalkNativeBuffer buffer:
return BitCastTo(buffer);
default:
return (int)Engine.ToObject(0, x);
}
}
///
/// Reinterprets an input value into a 32-bit uint.
///
/// The input value.
/// The input recast as a 32-bit uint.
///
/// ```kalk
/// >>> asuint(-1.5f)
/// # asuint(-1.5f)
/// out = 3_217_031_168
/// >>> asfloat(out)
/// # asfloat(out)
/// out = -1.5
/// ```
///
[KalkExport("asuint", CategoryMiscMemory)]
public uint AsUInt(object x)
{
switch (x)
{
case long longValue:
return (uint)longValue;
case ulong ulongValue:
return (uint)ulongValue;
case int intValue:
return (uint)intValue;
case uint uintValue:
return uintValue;
case double f64:
return (uint)BitConverter.SingleToInt32Bits((float)f64);
case float f32:
return (uint)BitConverter.SingleToInt32Bits(f32);
case KalkNativeBuffer buffer:
return BitCastTo(buffer);
default:
return (uint)(int)Engine.ToObject(0, x);
}
}
///
/// Creates a bytebuffer from the specified input.
///
/// The input values.
/// A bytebuffer from the specified input.
///
/// ```kalk
/// >>> bytebuffer
/// # bytebuffer
/// out = bytebuffer([])
/// >>> bytebuffer(0,1,2,3,4)
/// # bytebuffer(0, 1, 2, 3, 4)
/// out = bytebuffer([0, 1, 2, 3, 4])
/// >>> bytebuffer(float4(1))
/// # bytebuffer(float4(1))
/// out = bytebuffer([0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63])
/// >>> bytebuffer([1,2,3,4])
/// # bytebuffer([1,2,3,4])
/// out = bytebuffer([1, 2, 3, 4])
/// ```
///
[KalkExport("bytebuffer", CategoryMiscMemory)]
public KalkNativeBuffer ByteBuffer(params object[] values)
{
if (values == null || values.Length == 0) return new KalkNativeBuffer(0);
// If we have a single value, try to extract a buffer from it.
if (values.Length == 1)
{
var element = values[0];
if (element is KalkNativeBuffer nativeBuffer)
{
return nativeBuffer;
}
if (element is string || element is IKalkSpannable)
{
return AsBytes(element);
}
if (element is IEnumerable it)
{
var buffer = new List();
foreach (var item in it)
{
var b = Engine.ToObject(0, item);
buffer.Add(b);
}
var result = new KalkNativeBuffer(buffer.Count);
var span = result.AsSpan();
for (int i = 0; i < buffer.Count; i++)
{
span[i] = buffer[i];
}
return result;
}
}
var byteBuffer = new KalkNativeBuffer(values.Length);
for (int i = 0; i < values.Length; i++)
{
byteBuffer[i] = (byte) Engine.ToObject(i, values[i]);
}
return byteBuffer;
}
private static unsafe T BitCastTo(KalkNativeBuffer buffer) where T : unmanaged
{
var sizeOfT = Unsafe.SizeOf();
T value = default;
var maxLength = Math.Min(buffer.Count, sizeOfT);
for (int i = 0; i < maxLength; i++)
{
((byte*)(&value))[i] = buffer[i];
}
return value;
}
private static readonly byte[] ReverseBytes = new byte[256]
{
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};
}
}