using System.Buffers;
using System.IO;
using System.Runtime.InteropServices;
namespace NetVips;
///
/// An target you can connect delegates to implement behaviour.
///
public class TargetCustom : Target
{
///
/// A write delegate.
///
///
/// The interface is the same as .
/// The handler is given a bytes-like object to write. However, the handler MUST
/// return the number of bytes written.
///
/// An array of bytes.
/// The number of bytes to be written to the current target.
/// The total number of bytes written to the target.
public delegate long WriteDelegate(byte[] buffer, int length);
///
/// A read delegate.
///
///
/// libtiff needs to be able to read on targets, unfortunately.
///
/// 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.
///
///
/// libtiff needs to be able to seek on targets, unfortunately.
///
/// 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 target.
public delegate long SeekDelegate(long offset, SeekOrigin origin);
///
/// A end delegate.
///
///
/// This optional handler is called at the end of write. It should do any
/// cleaning up, if necessary.
///
/// 0 on success, -1 on error.
public delegate int EndDelegate();
///
/// Attach a write delegate.
///
public event WriteDelegate OnWrite;
///
/// Attach a read delegate.
///
///
/// This is not called prior libvips 8.13.
///
public event ReadDelegate OnRead;
///
/// Attach a seek delegate.
///
///
/// This is not called prior libvips 8.13.
///
public event SeekDelegate OnSeek;
///
/// Attach a end delegate.
///
public event EndDelegate OnEnd;
///
public TargetCustom() : base(Internal.VipsTargetCustom.New())
{
var vips813 = NetVips.AtLeastLibvips(8, 13);
SignalConnect("write", (Internal.VipsTargetCustom.WriteSignal)WriteHandler);
if (vips813)
{
SignalConnect("read", (Internal.VipsTargetCustom.ReadSignal)ReadHandler);
SignalConnect("seek", (Internal.VipsTargetCustom.SeekSignal)SeekHandler);
}
SignalConnect(vips813 ? "end" : "finish", (Internal.VipsTargetCustom.EndSignal)EndHandler);
}
///
/// The internal write handler.
///
/// The underlying pointer to the target.
/// An array of bytes.
/// The number of bytes to be written to the current target.
/// User data associated with the target.
/// The total number of bytes written to the target.
internal long WriteHandler(nint targetPtr, byte[] buffer, int length, nint userDataPtr)
{
var bytesWritten = OnWrite?.Invoke(buffer, length);
return bytesWritten ?? -1;
}
///
/// The internal read handler.
///
/// The underlying pointer to the target.
/// A pointer to an array of bytes.
/// The maximum number of bytes to be read.
/// User data associated with the target.
/// The total number of bytes read into the buffer.
internal long ReadHandler(nint targetPtr, 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 target.
/// 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 target.
/// The new position within the current target.
internal long SeekHandler(nint targetPtr, long offset, int whence, nint userDataPtr)
{
var newPosition = OnSeek?.Invoke(offset, (SeekOrigin)whence);
return newPosition ?? -1;
}
///
/// The internal end handler.
///
/// The underlying pointer to the target.
/// User data associated with the target.
/// 0 on success, -1 on error.
internal int EndHandler(nint targetPtr, nint userDataPtr)
{
return OnEnd?.Invoke() ?? 0;
}
}