boltffi_bindgen 0.24.0

Code generation library for BoltFFI - generates Swift, Kotlin, and TypeScript bindings
Documentation
{%- if module.needs_ffi_buf() %}
    [StructLayout(LayoutKind.Sequential)]
    internal struct FfiBuf
    {
        public IntPtr ptr;
        public UIntPtr len;
        public UIntPtr cap;
        public UIntPtr align;
    }

{% endif %}
{%- if module.needs_wire_reader() %}
    internal sealed class WireReader
    {
        // Reads directly from the unmanaged FfiBuf pointer — no eager copy into
        // a managed byte[]. Safe APIs only: Marshal.Read* for primitives,
        // Marshal.PtrToStringUTF8 for length-prefixed UTF-8, Marshal.Copy for
        // bytes. BitConverter.IsLittleEndian is a JIT intrinsic that folds the
        // byte-swap branch away on little-endian hosts (all our supported
        // targets).
        private readonly IntPtr _ptr;
        private readonly int _length;
        private int _pos;

        internal WireReader(FfiBuf buf)
        {
            _ptr = buf.ptr;
            _length = buf.ptr == IntPtr.Zero ? 0 : checked((int)(nuint)buf.len);
            _pos = 0;
        }

        internal bool ReadBool() => ReadU8() != 0;

        internal sbyte ReadI8()
        {
            Require(1, "i8");
            sbyte v = (sbyte)Marshal.ReadByte(_ptr, _pos);
            _pos += 1;
            return v;
        }

        internal byte ReadU8()
        {
            Require(1, "u8");
            byte v = Marshal.ReadByte(_ptr, _pos);
            _pos += 1;
            return v;
        }

        internal short ReadI16()
        {
            Require(2, "i16");
            short v = Marshal.ReadInt16(_ptr, _pos);
            _pos += 2;
            return BitConverter.IsLittleEndian ? v : BinaryPrimitives.ReverseEndianness(v);
        }

        internal ushort ReadU16()
        {
            Require(2, "u16");
            ushort v = (ushort)Marshal.ReadInt16(_ptr, _pos);
            _pos += 2;
            return BitConverter.IsLittleEndian ? v : BinaryPrimitives.ReverseEndianness(v);
        }

        internal int ReadI32()
        {
            Require(4, "i32");
            int v = Marshal.ReadInt32(_ptr, _pos);
            _pos += 4;
            return BitConverter.IsLittleEndian ? v : BinaryPrimitives.ReverseEndianness(v);
        }

        internal uint ReadU32()
        {
            Require(4, "u32");
            uint v = (uint)Marshal.ReadInt32(_ptr, _pos);
            _pos += 4;
            return BitConverter.IsLittleEndian ? v : BinaryPrimitives.ReverseEndianness(v);
        }

        internal long ReadI64()
        {
            Require(8, "i64");
            long v = Marshal.ReadInt64(_ptr, _pos);
            _pos += 8;
            return BitConverter.IsLittleEndian ? v : BinaryPrimitives.ReverseEndianness(v);
        }

        internal ulong ReadU64()
        {
            Require(8, "u64");
            ulong v = (ulong)Marshal.ReadInt64(_ptr, _pos);
            _pos += 8;
            return BitConverter.IsLittleEndian ? v : BinaryPrimitives.ReverseEndianness(v);
        }

        internal float ReadF32()
        {
            Require(4, "f32");
            int bits = Marshal.ReadInt32(_ptr, _pos);
            _pos += 4;
            return BitConverter.Int32BitsToSingle(BitConverter.IsLittleEndian ? bits : BinaryPrimitives.ReverseEndianness(bits));
        }

        internal double ReadF64()
        {
            Require(8, "f64");
            long bits = Marshal.ReadInt64(_ptr, _pos);
            _pos += 8;
            return BitConverter.Int64BitsToDouble(BitConverter.IsLittleEndian ? bits : BinaryPrimitives.ReverseEndianness(bits));
        }

        // usize/isize travel as 64-bit integers on the wire so the wire
        // layout stays stable across pointer widths.
        internal nint ReadNInt() => (nint)ReadI64();
        internal nuint ReadNUInt() => (nuint)ReadU64();

        internal string ReadString()
        {
            int len = ReadI32();
            if (len == 0) return "";
            if (len < 0) throw new InvalidOperationException("corrupt wire: negative string length");
            Require(len, "string payload");
            string v = Marshal.PtrToStringUTF8(_ptr + _pos, len)
                ?? throw new InvalidOperationException("PtrToStringUTF8 returned null");
            _pos += len;
            return v;
        }

        internal byte[] ReadBytes()
        {
            int len = ReadI32();
            if (len == 0) return Array.Empty<byte>();
            if (len < 0) throw new InvalidOperationException("corrupt wire: negative bytes length");
            Require(len, "bytes payload");
            byte[] v = new byte[len];
            Marshal.Copy(_ptr + _pos, v, 0, len);
            _pos += len;
            return v;
        }

        private void Require(int n, string kind)
        {
            if (n < 0 || n > _length - _pos) throw new InvalidOperationException("corrupt wire: truncated " + kind);
        }
    }

{% endif %}
{%- if module.needs_wire_writer() %}
    internal sealed class WireWriter : IDisposable
    {
        private const int MinCapacity = 16;

        private byte[] _buffer;
        private int _pos;
        private bool _disposed;

        internal WireWriter(int initialCapacity)
        {
            int cap = Math.Max(initialCapacity, MinCapacity);
            _buffer = ArrayPool<byte>.Shared.Rent(cap);
            _pos = 0;
            _disposed = false;
        }

        /// <summary>Copy the written bytes into a fresh managed array.</summary>
        internal byte[] ToArray()
        {
            if (_pos == 0) return Array.Empty<byte>();
            byte[] result = new byte[_pos];
            Buffer.BlockCopy(_buffer, 0, result, 0, _pos);
            return result;
        }

        internal int Position => _pos;

        internal void WriteBool(bool v) { EnsureCapacity(1); _buffer[_pos++] = (byte)(v ? 1 : 0); }
        internal void WriteI8(sbyte v) { EnsureCapacity(1); _buffer[_pos++] = (byte)v; }
        internal void WriteU8(byte v) { EnsureCapacity(1); _buffer[_pos++] = v; }
        internal void WriteI16(short v) { EnsureCapacity(2); BinaryPrimitives.WriteInt16LittleEndian(_buffer.AsSpan(_pos), v); _pos += 2; }
        internal void WriteU16(ushort v) { EnsureCapacity(2); BinaryPrimitives.WriteUInt16LittleEndian(_buffer.AsSpan(_pos), v); _pos += 2; }
        internal void WriteI32(int v) { EnsureCapacity(4); BinaryPrimitives.WriteInt32LittleEndian(_buffer.AsSpan(_pos), v); _pos += 4; }
        internal void WriteU32(uint v) { EnsureCapacity(4); BinaryPrimitives.WriteUInt32LittleEndian(_buffer.AsSpan(_pos), v); _pos += 4; }
        internal void WriteI64(long v) { EnsureCapacity(8); BinaryPrimitives.WriteInt64LittleEndian(_buffer.AsSpan(_pos), v); _pos += 8; }
        internal void WriteU64(ulong v) { EnsureCapacity(8); BinaryPrimitives.WriteUInt64LittleEndian(_buffer.AsSpan(_pos), v); _pos += 8; }
        internal void WriteF32(float v) { EnsureCapacity(4); BinaryPrimitives.WriteSingleLittleEndian(_buffer.AsSpan(_pos), v); _pos += 4; }
        internal void WriteF64(double v) { EnsureCapacity(8); BinaryPrimitives.WriteDoubleLittleEndian(_buffer.AsSpan(_pos), v); _pos += 8; }

        internal void WriteNInt(nint v) => WriteI64((long)v);
        internal void WriteNUInt(nuint v) => WriteU64((ulong)v);

        internal void WriteString(string v)
        {
            int byteCount = Encoding.UTF8.GetByteCount(v);
            WriteI32(byteCount);
            if (byteCount == 0) return;
            EnsureCapacity(byteCount);
            Encoding.UTF8.GetBytes(v, 0, v.Length, _buffer, _pos);
            _pos += byteCount;
        }

        internal void WriteBytes(byte[] v)
        {
            WriteI32(v.Length);
            if (v.Length == 0) return;
            EnsureCapacity(v.Length);
            Buffer.BlockCopy(v, 0, _buffer, _pos, v.Length);
            _pos += v.Length;
        }

        private void EnsureCapacity(int additional)
        {
            if (_pos + additional <= _buffer.Length) return;
            int next = Math.Max(_buffer.Length * 2, _pos + additional);
            byte[] grown = ArrayPool<byte>.Shared.Rent(next);
            Buffer.BlockCopy(_buffer, 0, grown, 0, _pos);
            ArrayPool<byte>.Shared.Return(_buffer);
            _buffer = grown;
        }

        public void Dispose()
        {
            if (_disposed) return;
            _disposed = true;
            ArrayPool<byte>.Shared.Return(_buffer);
            _buffer = Array.Empty<byte>();
        }
    }

{% endif %}
    internal static class NativeMethods
    {
        private const string LibName = "{{ module.lib_name }}";
{%- if module.needs_ffi_buf() %}

        [DllImport(LibName, EntryPoint = "{{ module.prefix }}_free_buf")]
        internal static extern void FreeBuf(FfiBuf buf);
{%- endif %}
{%- for func in module.functions %}

        [DllImport(LibName, EntryPoint = "{{ func.ffi_name }}")]
{%- if func.return_type.is_bool() %}
        [return: MarshalAs(UnmanagedType.I1)]
{%- endif %}
        internal static extern {{ func.native_return_type() }} {{ func.name }}({{ func.native_param_list() }});
{%- endfor %}
    }
}