DataEnqueue Refactor (#33)

* Refactored DataEnqueue to use pointer arithmetic
pull/85/head
mullerj 2024-06-13 22:33:07 -04:00 committed by GitHub
parent 0662326da8
commit 662e1faf86
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 209 additions and 65 deletions

View File

@ -60,7 +60,6 @@ class NormApi
+ {static} NormSetTxRobustFactor(sessionHandle:long, robustFactor:int) : void + {static} NormSetTxRobustFactor(sessionHandle:long, robustFactor:int) : void
+ {static} NormFileEnqueue(sessionHandle:long, fileName:string, infoPtr:string, infoLen:int): long + {static} NormFileEnqueue(sessionHandle:long, fileName:string, infoPtr:string, infoLen:int): long
+ {static} NormDataEnqueue(sessionHandle:long, dataPtr:nint, dataLen:int, infoPtr:nint, infoLen:int) : long + {static} NormDataEnqueue(sessionHandle:long, dataPtr:nint, dataLen:int, infoPtr:nint, infoLen:int) : long
+ {static} NormDataEnqueue(sessionHandle:long, data:byte[], dataLen:int, info:byte[], infoLen:int) : long
+ {static} NormRequeueObject(sessionHandle:long, objectHandle:long) : bool + {static} NormRequeueObject(sessionHandle:long, objectHandle:long) : bool
+ {static} NormStreamOpen(sessionHandle:long, bufferSize:long, infoPtr:string, infoLen:int) : long + {static} NormStreamOpen(sessionHandle:long, bufferSize:long, infoPtr:string, infoLen:int) : long
+ {static} NormStreamClose(streamHandle:long, graceful:bool) : void + {static} NormStreamClose(streamHandle:long, graceful:bool) : void

View File

@ -544,41 +544,6 @@ namespace Mil.Navy.Nrl.Norm
[DllImport(NORM_LIBRARY)] [DllImport(NORM_LIBRARY)]
public static extern long NormDataEnqueue(long sessionHandle, nint dataPtr, int dataLen, nint infoPtr, int infoLen); public static extern long NormDataEnqueue(long sessionHandle, nint dataPtr, int dataLen, nint infoPtr, int infoLen);
/// <summary>
/// This function enqueues a segment of application memory space for transmission within the specified NORM sessionHandle.
/// </summary>
/// <param name="sessionHandle">Used to identify application in the NormSession.</param>
/// <param name="data">The data parameter must be a managed buffer to be transmitted.</param>
/// <param name="dataLen">The dataLen parameter indicates the quantity of data to transmit.</param>
/// <param name="info">The optional info and infoLen parameters
/// are used to associate NORM_INFO content with the sent transport object. The maximum allowed infoLen
/// corresponds to the segmentSize used in the prior call to NormStartSender(). The use and interpretation of the
/// NORM_INFO content is left to the application's discretion.</param>
/// <param name="infoLen">The optional info and infoLen parameters
/// are used to associate NORM_INFO content with the sent transport object. The maximum allowed infoLen
/// corresponds to the segmentSize used in the prior call to NormStartSender(). The use and interpretation of the
/// NORM_INFO content is left to the application's discretion</param>
/// <returns>A NormObjectHandle is returned which the application may use in other NORM API calls as needed.</returns>
public static long NormDataEnqueue(long sessionHandle, byte[] data, int dataLen, byte[]? info, int infoLen)
{
long objectHandle;
var dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
var infoHandle = GCHandle.Alloc(info, GCHandleType.Pinned);
try
{
var dataPtr = dataHandle.AddrOfPinnedObject();
var infoPtr = infoHandle.AddrOfPinnedObject();
objectHandle = NormDataEnqueue(sessionHandle, dataPtr, dataLen, infoPtr, infoLen);
}
finally
{
dataHandle.Free();
infoHandle.Free();
}
return objectHandle;
}
/// <summary> /// <summary>
/// This function allows the application to resend (or reset transmission of) a NORM_OBJECT_FILE or NORM_OBJECT_DATA /// This function allows the application to resend (or reset transmission of) a NORM_OBJECT_FILE or NORM_OBJECT_DATA
/// transmit object that was previously enqueued for the indicated sessionHandle. /// transmit object that was previously enqueued for the indicated sessionHandle.

View File

@ -1,4 +1,5 @@
using System.Text; using System.Runtime.InteropServices;
using System.Text;
namespace Mil.Navy.Nrl.Norm namespace Mil.Navy.Nrl.Norm
{ {
@ -497,6 +498,7 @@ namespace Mil.Navy.Nrl.Norm
/// <param name="dataLength">Size of the message.</param> /// <param name="dataLength">Size of the message.</param>
/// <returns>A NormData is returned which the application may use in other NORM API calls as needed.</returns> /// <returns>A NormData is returned which the application may use in other NORM API calls as needed.</returns>
/// <exception cref="IOException">Thrown when NormDataEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue data.</exception> /// <exception cref="IOException">Thrown when NormDataEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue data.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the data offset or data length are outside of the data buffer.</exception>
public NormData DataEnqueue(byte[] dataBuffer, int dataOffset, int dataLength) public NormData DataEnqueue(byte[] dataBuffer, int dataOffset, int dataLength)
{ {
return DataEnqueue(dataBuffer, dataOffset, dataLength, null, 0, 0); return DataEnqueue(dataBuffer, dataOffset, dataLength, null, 0, 0);
@ -514,24 +516,46 @@ namespace Mil.Navy.Nrl.Norm
/// <param name="infoLength">The optional info and infoLength parameters are used to associate NORM_INFO content with the sent transport object.</param> /// <param name="infoLength">The optional info and infoLength parameters are used to associate NORM_INFO content with the sent transport object.</param>
/// <returns>A NormData is returned which the application may use in other NORM API calls as needed.</returns> /// <returns>A NormData is returned which the application may use in other NORM API calls as needed.</returns>
/// <exception cref="IOException">Thrown when NormDataEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue data.</exception> /// <exception cref="IOException">Thrown when NormDataEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue data.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the data offset, data length, info offset or info length are outside of the associated buffer.</exception>
public NormData DataEnqueue(byte[] dataBuffer, int dataOffset, int dataLength, byte[]? info, int infoOffset, int infoLength) public NormData DataEnqueue(byte[] dataBuffer, int dataOffset, int dataLength, byte[]? info, int infoOffset, int infoLength)
{ {
var dataBytes = dataBuffer.Skip(dataOffset).Take(dataLength).ToArray(); if (dataOffset < 0 || dataOffset >= dataBuffer.Length)
byte[]? infoBytes;
if (info != null)
{ {
infoBytes = info.Skip(infoOffset).Take(infoLength).ToArray(); throw new ArgumentOutOfRangeException(nameof(dataOffset), "The data offset is out of range");
} }
else if (dataOffset + dataLength > dataBuffer.Length)
{ {
infoBytes = null; throw new ArgumentOutOfRangeException(nameof(dataLength), "The data length is out of range");
infoLength = 0;
} }
var objectHandle = NormDataEnqueue(_handle, dataBytes, dataLength, infoBytes, infoLength); if (infoOffset < 0 || infoOffset >= info?.Length)
{
throw new ArgumentOutOfRangeException(nameof(infoOffset), "The info offset is out of range");
}
if (infoOffset + infoLength > info?.Length)
{
throw new ArgumentOutOfRangeException(nameof(infoLength), "The info length is out of range");
}
long objectHandle;
var dataHandle = GCHandle.Alloc(dataBuffer, GCHandleType.Pinned);
var infoHandle = GCHandle.Alloc(info, GCHandleType.Pinned);
try
{
var dataPtr = dataHandle.AddrOfPinnedObject() + dataOffset;
var infoPtr = infoHandle.AddrOfPinnedObject() + infoOffset;
objectHandle = NormDataEnqueue(_handle, dataPtr, dataLength, infoPtr, infoLength);
if (objectHandle == NormObject.NORM_OBJECT_INVALID) if (objectHandle == NormObject.NORM_OBJECT_INVALID)
{ {
throw new IOException("Failed to enqueue data"); throw new IOException("Failed to enqueue data");
} }
}
finally
{
dataHandle.Free();
infoHandle.Free();
}
return new NormData(objectHandle); return new NormData(objectHandle);
} }

View File

@ -186,12 +186,22 @@ namespace Mil.Navy.Nrl.Norm.IntegrationTests
/// Generates text content /// Generates text content
/// </summary> /// </summary>
/// <returns>The generated text content</returns> /// <returns>The generated text content</returns>
private string GenerateTextContent() private static string GenerateTextContent()
{ {
var faker = new Faker(); var faker = new Faker();
return faker.Lorem.Paragraph(); return faker.Lorem.Paragraph();
} }
/// <summary>
/// Generates info content
/// </summary>
/// <returns>The generated info content</returns>
private static string GenerateInfoContent()
{
var faker = new Faker();
return faker.Lorem.Sentence();
}
private IEnumerable<NormEvent> GetEvents(TimeSpan delayTime) private IEnumerable<NormEvent> GetEvents(TimeSpan delayTime)
{ {
var normEvents = new List<NormEvent>(); var normEvents = new List<NormEvent>();
@ -398,24 +408,85 @@ namespace Mil.Navy.Nrl.Norm.IntegrationTests
} }
} }
[SkippableFact(typeof(IOException))] public static IEnumerable<object[]> GenerateData()
public void EnqueuesData() {
var data = new List<object[]>();
var dataContent = GenerateTextContent();
var expectedDataContent = dataContent;
var dataOffset = 0;
var dataLength = dataContent.Length;
data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength });
var infoContent = GenerateInfoContent();
var expectedInfoContent = infoContent;
var infoOffset = 0;
var infoLength = infoContent.Length;
data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength, infoContent, expectedInfoContent, infoOffset, infoLength });
var faker = new Faker();
infoLength = faker.Random.Int(infoContent.Length / 2, infoContent.Length - 1);
expectedInfoContent = infoContent.Substring(infoOffset, infoLength);
data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength, infoContent, expectedInfoContent, infoOffset, infoLength });
infoOffset = faker.Random.Int(1, infoContent.Length - 1 / 2);
infoLength = infoContent.Length - infoOffset;
expectedInfoContent = infoContent.Substring(infoOffset, infoLength);
data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength, infoContent, expectedInfoContent, infoOffset, infoLength });
infoOffset = faker.Random.Int(1, infoContent.Length - 1 / 2);
infoLength = faker.Random.Int(1, infoContent.Length - infoOffset);
expectedInfoContent = infoContent.Substring(infoOffset, infoLength);
data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength, infoContent, expectedInfoContent, infoOffset, infoLength });
dataLength = faker.Random.Int(dataContent.Length / 2, dataContent.Length - 1);
expectedDataContent = dataContent.Substring(dataOffset, dataLength);
data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength });
dataOffset = faker.Random.Int(1, dataContent.Length - 1 / 2);
dataLength = dataContent.Length - dataOffset;
expectedDataContent = dataContent.Substring(dataOffset, dataLength);
data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength });
dataOffset = faker.Random.Int(1, dataContent.Length - 1 / 2);
dataLength = faker.Random.Int(1, dataContent.Length - dataOffset);
expectedDataContent = dataContent.Substring(dataOffset, dataLength);
data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength });
return data;
}
[SkippableTheory(typeof(IOException))]
[MemberData(nameof(GenerateData))]
public void EnqueuesData(string dataContent, string expectedDataContent, int dataOffset, int dataLength, string? infoContent = null, string expectedInfoContent = "", int? infoOffset = null, int? infoLength = null)
{ {
StartSender(); StartSender();
//Create data to write to the stream //Create data to write to enqueue
var expectedContent = GenerateTextContent(); var data = Encoding.ASCII.GetBytes(dataContent);
byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); var expectedData = Encoding.ASCII.GetBytes(expectedDataContent);
//Create info to enqueue
var info = infoContent != null ? Encoding.ASCII.GetBytes(infoContent) : null;
var expectedInfo = Encoding.ASCII.GetBytes(expectedInfoContent);
try try
{ {
var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); var normData = infoOffset != null && infoLength != null ?
_normSession.DataEnqueue(data, dataOffset, dataLength, info, infoOffset.Value, infoLength.Value) :
_normSession.DataEnqueue(data, dataOffset, dataLength);
var expectedEventTypes = new List<NormEventType> { NormEventType.NORM_TX_OBJECT_SENT, NormEventType.NORM_TX_QUEUE_EMPTY }; var expectedEventTypes = new List<NormEventType> { NormEventType.NORM_TX_OBJECT_SENT, NormEventType.NORM_TX_QUEUE_EMPTY };
var actualEventTypes = GetEvents().Select(e => e.Type).ToList(); var actualEventTypes = GetEvents().Select(e => e.Type).ToList();
Assert.Equal(expectedEventTypes, actualEventTypes); Assert.Equal(expectedEventTypes, actualEventTypes);
var actualData = normData.GetData(); var actualData = normData.GetData();
Assert.Equal(expectedData, actualData); Assert.Equal(expectedData, actualData);
var actualContent = Encoding.ASCII.GetString(actualData); var actualDataContent = Encoding.ASCII.GetString(actualData);
Assert.Equal(expectedContent, actualContent); Assert.Equal(expectedDataContent, actualDataContent);
var actualInfo = normData.Info;
Assert.Equal(expectedInfo, actualInfo);
if (actualInfo != null)
{
var actualInfoContent = Encoding.ASCII.GetString(actualInfo);
Assert.Equal(expectedInfoContent, actualInfoContent);
}
} }
catch (Exception) catch (Exception)
{ {
@ -427,8 +498,81 @@ namespace Mil.Navy.Nrl.Norm.IntegrationTests
} }
} }
[SkippableFact(typeof(IOException))] public static IEnumerable<object[]> GenerateOutOfRangeData()
public void ReceivesData() {
var data = new List<object[]>();
var dataContent = GenerateTextContent();
var faker = new Faker();
var dataOffset = faker.Random.Int(-dataContent.Length, -1);
var dataLength = dataContent.Length;
data.Add(new object[] { dataContent, dataOffset, dataLength });
dataOffset = faker.Random.Int(dataContent.Length, dataContent.Length * 2);
dataLength = dataContent.Length;
data.Add(new object[] { dataContent, dataOffset, dataLength });
dataOffset = 0;
dataLength = faker.Random.Int(dataContent.Length + 1, dataContent.Length * 2);
data.Add(new object[] { dataContent, dataOffset, dataLength });
dataOffset = dataContent.Length - 1;
dataLength = dataContent.Length;
data.Add(new object[] { dataContent, dataOffset, dataLength });
dataOffset = 0;
dataLength = dataContent.Length;
var infoContent = GenerateInfoContent();
var infoOffset = faker.Random.Int(-infoContent.Length, -1);
var infoLength = infoContent.Length;
data.Add(new object[] { dataContent, dataOffset, dataLength, infoContent, infoOffset, infoLength });
infoOffset = faker.Random.Int(infoContent.Length, infoContent.Length * 2);
infoLength = infoContent.Length;
data.Add(new object[] { dataContent, dataOffset, dataLength, infoContent, infoOffset, infoLength });
infoOffset = 0;
infoLength = faker.Random.Int(infoContent.Length + 1, infoContent.Length * 2);
data.Add(new object[] { dataContent, dataOffset, dataLength, infoContent, infoOffset, infoLength });
infoOffset = infoContent.Length - 1;
infoLength = infoContent.Length;
data.Add(new object[] { dataContent, dataOffset, dataLength, infoContent, infoOffset, infoLength });
return data;
}
[SkippableTheory(typeof(IOException))]
[MemberData(nameof(GenerateOutOfRangeData))]
public void EnqueuesDataThrowsExceptionWhenOutOfRange(string dataContent, int dataOffset, int dataLength, string? infoContent = null, int? infoOffset = null, int? infoLength = null)
{
StartSender();
//Create data to enqueue
var data = Encoding.ASCII.GetBytes(dataContent);
//Create info to enqueue
var info = infoContent != null ? Encoding.ASCII.GetBytes(infoContent) : null;
try
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
infoOffset != null && infoLength != null ?
_normSession.DataEnqueue(data, dataOffset, dataLength, info, infoOffset.Value, infoLength.Value) :
_normSession.DataEnqueue(data, dataOffset, dataLength));
}
catch (Exception)
{
throw;
}
finally
{
StopSender();
}
}
[SkippableTheory(typeof(IOException))]
[MemberData(nameof(GenerateData))]
public void ReceivesData(string content, string expectedDataContent, int dataOffset, int dataLength, string? infoContent = null, string expectedInfoContent = "", int? infoOffset = null, int? infoLength = null)
{ {
_normSession.SetLoopback(true); _normSession.SetLoopback(true);
StartSender(); StartSender();
@ -441,12 +585,17 @@ namespace Mil.Navy.Nrl.Norm.IntegrationTests
_normInstance.SetCacheDirectory(cachePath); _normInstance.SetCacheDirectory(cachePath);
//Create data to be sent //Create data to be sent
var expectedContent = GenerateTextContent(); var data = Encoding.ASCII.GetBytes(content);
var expectedData = Encoding.ASCII.GetBytes(expectedContent); var expectedData = Encoding.ASCII.GetBytes(expectedDataContent);
//Create info to be sent
var info = infoContent != null ? Encoding.ASCII.GetBytes(infoContent) : null;
var expectedInfo = Encoding.ASCII.GetBytes(expectedInfoContent);
try try
{ {
var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); var normData = infoOffset != null && infoLength != null ?
_normSession.DataEnqueue(data, dataOffset, dataLength, info, infoOffset.Value, infoLength.Value) :
_normSession.DataEnqueue(data, dataOffset, dataLength);
var expectedEventTypes = new List<NormEventType> var expectedEventTypes = new List<NormEventType>
{ {
NormEventType.NORM_REMOTE_SENDER_NEW, NormEventType.NORM_REMOTE_SENDER_NEW,
@ -468,8 +617,15 @@ namespace Mil.Navy.Nrl.Norm.IntegrationTests
var actualData = actualNormData.GetData(); var actualData = actualNormData.GetData();
Assert.Equal(expectedData, actualData); Assert.Equal(expectedData, actualData);
var actualContent = Encoding.ASCII.GetString(actualData); var actualDataContent = Encoding.ASCII.GetString(actualData);
Assert.Equal(expectedContent, actualContent); Assert.Equal(expectedDataContent, actualDataContent);
var actualInfo = normData.Info;
Assert.Equal(expectedInfo, actualInfo);
if (actualInfo != null)
{
var actualInfoContent = Encoding.ASCII.GetString(actualInfo);
Assert.Equal(expectedInfoContent, actualInfoContent);
}
} }
catch (Exception) catch (Exception)
{ {