feat(data): expose CandleReader (CSV) natively in all 10 languages (#311)
Add the data-layer CSV candle reader to every binding so loading OHLCV candles from a CSV no longer needs a per-language CSV/dataframe dependency. - C ABI: wickra_candle_reader_new(bytes, len) / _count / _read / _free over an opaque CandleReader handle (parse the whole buffer up front, then drain). - Native: Node/WASM CandleReader.read() -> Candle[], Python read() -> list[tuple]. - C-ABI languages: Go Read() []Candle, C# Candle[] Read(), Java Candle[] read(), R read() S3 generic (n x 6 matrix); C / C++ call the C ABI directly. - Cross-language golden testdata/golden/data_csv*.csv pins the parsed candles bit-for-bit across every binding. Verified locally across Rust (test+clippy+fmt), Node, WASM, Python, C#, Go, Java, R, and the C/C++ cmake parity suite.
This commit is contained in:
@@ -4981,6 +4981,60 @@ public sealed class Camarilla : IDisposable
|
||||
public void Dispose() => _handle.Dispose();
|
||||
}
|
||||
|
||||
public sealed class CandleReader : IDisposable
|
||||
{
|
||||
private readonly WickraHandle _handle;
|
||||
|
||||
/// <summary>Parse a timestamp,open,high,low,close,volume CSV string (a
|
||||
/// leading UTF-8 BOM and field whitespace are tolerated).</summary>
|
||||
public CandleReader(string csv)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(csv);
|
||||
var bytes = System.Text.Encoding.UTF8.GetBytes(csv);
|
||||
nint ptr;
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* data = bytes)
|
||||
{
|
||||
ptr = NativeMethods.wickra_candle_reader_new(data, (nuint)bytes.Length);
|
||||
}
|
||||
}
|
||||
if (ptr == nint.Zero)
|
||||
{
|
||||
throw new ArgumentException("invalid CandleReader CSV");
|
||||
}
|
||||
_handle = new WickraHandle(ptr, NativeMethods.wickra_candle_reader_free);
|
||||
}
|
||||
|
||||
/// <summary>Every candle parsed from the CSV, in file order.</summary>
|
||||
public Candle[] Read()
|
||||
{
|
||||
var count = (long)NativeMethods.wickra_candle_reader_count(_handle.DangerousGetHandle());
|
||||
GC.KeepAlive(_handle);
|
||||
if (count <= 0)
|
||||
{
|
||||
return Array.Empty<Candle>();
|
||||
}
|
||||
var buffer = new WickraCandle[count];
|
||||
unsafe
|
||||
{
|
||||
fixed (WickraCandle* ptr = buffer)
|
||||
{
|
||||
NativeMethods.wickra_candle_reader_read(_handle.DangerousGetHandle(), ptr, (nuint)count);
|
||||
}
|
||||
}
|
||||
GC.KeepAlive(_handle);
|
||||
var result = new Candle[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
result[i] = new Candle(buffer[i].open, buffer[i].high, buffer[i].low, buffer[i].close, buffer[i].volume, buffer[i].timestamp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Dispose() => _handle.Dispose();
|
||||
}
|
||||
|
||||
public sealed class CandleVolume : IDisposable
|
||||
{
|
||||
private readonly WickraHandle _handle;
|
||||
|
||||
@@ -12430,6 +12430,18 @@ internal static partial class NativeMethods
|
||||
[LibraryImport(WickraNative.LibraryName)]
|
||||
internal static partial void wickra_resampler_free(nint handle);
|
||||
|
||||
[LibraryImport(WickraNative.LibraryName)]
|
||||
internal static unsafe partial nint wickra_candle_reader_new(byte* data, nuint len);
|
||||
|
||||
[LibraryImport(WickraNative.LibraryName)]
|
||||
internal static partial nuint wickra_candle_reader_count(nint handle);
|
||||
|
||||
[LibraryImport(WickraNative.LibraryName)]
|
||||
internal static unsafe partial nuint wickra_candle_reader_read(nint handle, WickraCandle* @out, nuint cap);
|
||||
|
||||
[LibraryImport(WickraNative.LibraryName)]
|
||||
internal static partial void wickra_candle_reader_free(nint handle);
|
||||
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
|
||||
Reference in New Issue
Block a user