sparse-router-formal / pascal /ffi /tensor_abi.pas
SNAPKITTYWEST's picture
Add covenant SPDX headers to every remaining unmarked source file
f7ccd08 verified
Raw History Blame Contribute Delete
9.64 kB
{ SPDX-License-Identifier: LicenseRef-Sovereign-Leviathan-SL-AGPL3-001
Licensing: see LICENSE in the repository root. }
{ ============================================================================
NraayTensor FFI ABI Bindings for Free Pascal
Provides Pascal-idiomatic bindings to the Rust tensor FFI layer.
All operations are thin wrappers around C FFI calls into tensor_ffi.rs.
The Pascal implementation remains independent for validation.
These bindings allow Pascal to invoke Rust tensor operations for
conformance testing and oracle comparison.
============================================================================ }
unit TensorABI;
{$MODE OBJFPC}
{$H+}
{$MODESWITCH ADVANCEDRECORDS}
interface
uses
Classes, SysUtils, TypInfo;
type
{ ========================================================================
Tensor Descriptor — mirrors C ABI
======================================================================== }
TTensorDescriptorC = packed record
buffer_id: UInt64;
base_addr: UInt64;
offset: UInt64;
rank: UInt32;
element_count: UInt64;
shape_ptr: UInt64;
stride_ptr: UInt64;
governance: Byte;
ownership: Byte;
materialized: Byte;
state: Byte;
generation: UInt64;
end;
PTensorDescriptorC = ^TTensorDescriptorC;
{ ========================================================================
Result Structure
======================================================================== }
TTensorResultC = packed record
success: Int32;
error_code: UInt32;
error_msg: array[0..255] of AnsiChar;
end;
{ ========================================================================
Operation Result
======================================================================== }
TTensorOpResultC = packed record
descriptor: TTensorDescriptorC;
result: TTensorResultC;
end;
{ ========================================================================
Ownership State Enumeration
======================================================================== }
TOwnershipState = (
OWNERSHIP_OWNED = 0,
OWNERSHIP_SHARED = 1,
OWNERSHIP_EXCLUSIVE = 2
);
{ ========================================================================
Governance State Enumeration
======================================================================== }
TGovernanceState = (
GOVERNANCE_SHARED = 0, { B=0: shared/non-canonical }
GOVERNANCE_CANONICAL = 1 { B=1: canonical/contiguous }
);
{ ============================================================================
FFI Declarations — map to Rust tensor_ffi.rs
============================================================================ }
{ External C function declarations }
function tensor_allocate(
rank: UInt32;
shape: PUInt64;
element_size: UInt64
): TTensorOpResultC; cdecl; external;
function tensor_deallocate(
desc: PTensorDescriptorC
): TTensorResultC; cdecl; external;
function tensor_view(
source: PTensorDescriptorC;
start_indices: PUInt64;
end_indices: PUInt64
): TTensorOpResultC; cdecl; external;
function tensor_materialize(
source: PTensorDescriptorC
): TTensorOpResultC; cdecl; external;
function tensor_validate_generation(
desc: PTensorDescriptorC
): TTensorResultC; cdecl; external;
function tensor_load(
desc: PTensorDescriptorC;
indices: PUInt64;
out_value: PSingle
): TTensorResultC; cdecl; external;
function tensor_store(
desc: PTensorDescriptorC;
indices: PUInt64;
value: Single
): TTensorResultC; cdecl; external;
function tensor_info(
desc: PTensorDescriptorC;
out_buffer: PByte;
out_len: SizeInt
): TTensorResultC; cdecl; external;
{ ============================================================================
Pascal Wrapper Functions (idiomatic error handling)
============================================================================ }
type
EFTensorError = class(Exception);
{ Allocate tensor, raise exception on failure }
procedure FTensorAllocate(
out Desc: TTensorDescriptorC;
Rank: UInt32;
const Shape: array of UInt64;
ElementSize: UInt64 = 4
);
{ Deallocate tensor, raise exception on failure }
procedure FTensorDeallocate(const Desc: TTensorDescriptorC);
{ Create view, raise exception on failure }
procedure FTensorView(
out ViewDesc: TTensorDescriptorC;
const Source: TTensorDescriptorC;
const StartIndices, EndIndices: array of UInt64
);
{ Materialize view, raise exception on failure }
procedure FTensorMaterialize(
out MatDesc: TTensorDescriptorC;
const Source: TTensorDescriptorC
);
{ Validate generation (stale descriptor check) }
function FTensorValidateGeneration(const Desc: TTensorDescriptorC): Boolean;
{ Load element value }
function FTensorLoad(
const Desc: TTensorDescriptorC;
const Indices: array of UInt64
): Single;
{ Store element value }
procedure FTensorStore(
const Desc: TTensorDescriptorC;
const Indices: array of UInt64;
Value: Single
);
{ Get info string (for debugging) }
function FTensorInfo(const Desc: TTensorDescriptorC): String;
{ Helper: check result and raise on error }
procedure CheckFTensorResult(const Result: TTensorResultC; const Context: String);
implementation
procedure CheckFTensorResult(const Result: TTensorResultC; const Context: String);
var
ErrorMsg: String;
begin
if Result.success = 0 then
begin
SetLength(ErrorMsg, 256);
ErrorMsg := PAnsiChar(@Result.error_msg[0]);
raise EFTensorError.Create(
Format('[%s] Error %d: %s', [Context, Result.error_code, ErrorMsg])
);
end;
end;
procedure FTensorAllocate(
out Desc: TTensorDescriptorC;
Rank: UInt32;
const Shape: array of UInt64;
ElementSize: UInt64 = 4
);
var
OpResult: TTensorOpResultC;
ShapeArray: array of UInt64;
I: Integer;
begin
if Rank = 0 then
raise EFTensorError.Create('Rank must be > 0');
if Rank <> High(Shape) + 1 then
raise EFTensorError.Create('Shape array length mismatch');
{ Copy shape to heap }
SetLength(ShapeArray, Rank);
for I := Low(Shape) to High(Shape) do
ShapeArray[I] := Shape[I];
{ Call FFI }
OpResult := tensor_allocate(Rank, @ShapeArray[0], ElementSize);
CheckFTensorResult(OpResult.result, 'tensor_allocate');
Desc := OpResult.descriptor;
end;
procedure FTensorDeallocate(const Desc: TTensorDescriptorC);
var
LocalDesc: TTensorDescriptorC;
Result: TTensorResultC;
begin
LocalDesc := Desc;
Result := tensor_deallocate(@LocalDesc);
CheckFTensorResult(Result, 'tensor_deallocate');
end;
procedure FTensorView(
out ViewDesc: TTensorDescriptorC;
const Source: TTensorDescriptorC;
const StartIndices, EndIndices: array of UInt64
);
var
OpResult: TTensorOpResultC;
SrcCopy: TTensorDescriptorC;
StartArray, EndArray: array of UInt64;
I: Integer;
begin
if High(StartIndices) <> High(EndIndices) then
raise EFTensorError.Create('Index array length mismatch');
if High(StartIndices) + 1 <> Source.rank then
raise EFTensorError.Create('Index count mismatch with tensor rank');
SetLength(StartArray, Source.rank);
SetLength(EndArray, Source.rank);
for I := Low(StartIndices) to High(StartIndices) do
begin
StartArray[I] := StartIndices[I];
EndArray[I] := EndIndices[I];
end;
SrcCopy := Source;
OpResult := tensor_view(@SrcCopy, @StartArray[0], @EndArray[0]);
CheckFTensorResult(OpResult.result, 'tensor_view');
ViewDesc := OpResult.descriptor;
end;
procedure FTensorMaterialize(
out MatDesc: TTensorDescriptorC;
const Source: TTensorDescriptorC
);
var
OpResult: TTensorOpResultC;
SrcCopy: TTensorDescriptorC;
begin
SrcCopy := Source;
OpResult := tensor_materialize(@SrcCopy);
CheckFTensorResult(OpResult.result, 'tensor_materialize');
MatDesc := OpResult.descriptor;
end;
function FTensorValidateGeneration(const Desc: TTensorDescriptorC): Boolean;
var
LocalDesc: TTensorDescriptorC;
Result: TTensorResultC;
begin
LocalDesc := Desc;
Result := tensor_validate_generation(@LocalDesc);
Exit(Result.success <> 0);
end;
function FTensorLoad(
const Desc: TTensorDescriptorC;
const Indices: array of UInt64
): Single;
var
LocalDesc: TTensorDescriptorC;
IndicesArray: array of UInt64;
Value: Single;
Result: TTensorResultC;
I: Integer;
begin
if High(Indices) + 1 <> Desc.rank then
raise EFTensorError.Create('Index count mismatch');
SetLength(IndicesArray, Desc.rank);
for I := Low(Indices) to High(Indices) do
IndicesArray[I] := Indices[I];
LocalDesc := Desc;
Result := tensor_load(@LocalDesc, @IndicesArray[0], @Value);
CheckFTensorResult(Result, 'tensor_load');
Exit(Value);
end;
procedure FTensorStore(
const Desc: TTensorDescriptorC;
const Indices: array of UInt64;
Value: Single
);
var
LocalDesc: TTensorDescriptorC;
IndicesArray: array of UInt64;
Result: TTensorResultC;
I: Integer;
begin
if High(Indices) + 1 <> Desc.rank then
raise EFTensorError.Create('Index count mismatch');
SetLength(IndicesArray, Desc.rank);
for I := Low(Indices) to High(Indices) do
IndicesArray[I] := Indices[I];
LocalDesc := Desc;
Result := tensor_store(@LocalDesc, @IndicesArray[0], Value);
CheckFTensorResult(Result, 'tensor_store');
end;
function FTensorInfo(const Desc: TTensorDescriptorC): String;
var
LocalDesc: TTensorDescriptorC;
Buffer: array[0..511] of AnsiChar;
Result: TTensorResultC;
begin
FillChar(Buffer, SizeOf(Buffer), 0);
LocalDesc := Desc;
Result := tensor_info(@LocalDesc, PByte(@Buffer), SizeOf(Buffer));
CheckFTensorResult(Result, 'tensor_info');
Exit(String(PAnsiChar(@Buffer)));
end;
end.