using System.Buffers;
using System.IO;
using System.Runtime.InteropServices;
namespace NetVips;
///
/// An source you can connect delegates to implement behaviour.
///
public class SourceCustom : Source
{
///
/// A read delegate.
///
///
/// The interface is exactly as .
/// The handler is given a number of bytes to fetch, and should return a
/// bytes-like object containing up to that number of bytes. If there is
/// no more data available, it should return .
///
/// An array of bytes.
/// The maximum number of bytes to be read.
/// The total number of bytes read into the buffer.
public delegate int ReadDelegate(byte[] buffer, int length);
///
/// A seek delegate.
///
///
/// The interface is exactly as . The handler is given
/// parameters for offset and whence with the same meanings. It also returns the
/// new position within the current source.
///
/// Seek handlers are optional. If you do not set one, your source will be
/// treated as unseekable and libvips will do extra caching.
///
/// A byte offset relative to the
/// parameter.
/// A value of type indicating the
/// reference point used to obtain the new position.
/// The new position within the current source.
public delegate long SeekDelegate(long offset, SeekOrigin origin);
///
/// Attach a read delegate.
///
public event ReadDelegate OnRead;
///
/// Attach a seek delegate.
///
public event SeekDelegate OnSeek;
///
public SourceCustom() : base(Internal.VipsSourceCustom.New())
{
SignalConnect("read", (Internal.VipsSourceCustom.ReadSignal)ReadHandler);
SignalConnect("seek", (Internal.VipsSourceCustom.SeekSignal)SeekHandler);
}
///
/// The internal read handler.
///
/// The underlying pointer to the source.
/// A pointer to an array of bytes.
/// The maximum number of bytes to be read.
/// User data associated with the source.
/// The total number of bytes read into the buffer.
internal long ReadHandler(nint sourcePtr, nint buffer, long length, nint userDataPtr)
{
if (length <= 0)
{
return 0;
}
var tempArray = ArrayPool.Shared.Rent((int)length);
try
{
var readLength = OnRead?.Invoke(tempArray, (int)length);
if (!readLength.HasValue)
{
return -1;
}
if (readLength.Value > 0)
{
Marshal.Copy(tempArray, 0, buffer, readLength.Value);
}
return readLength.Value;
}
catch
{
return -1;
}
finally
{
ArrayPool.Shared.Return(tempArray);
}
}
///
/// The internal seek handler.
///
/// The underlying pointer to the source.
/// A byte offset relative to the
/// parameter.
/// A value of type indicating the
/// reference point used to obtain the new position.
/// User data associated with the source.
/// The new position within the current source.
internal long SeekHandler(nint sourcePtr, long offset, int whence, nint userDataPtr)
{
var newPosition = OnSeek?.Invoke(offset, (SeekOrigin)whence);
return newPosition ?? -1;
}
}