Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DataEnqueue Refactor #33

Merged
merged 9 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ class NormApi
+ {static} NormSetTxRobustFactor(sessionHandle:long, robustFactor:int) : void
+ {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, data:byte[], dataLen:int, info:byte[], infoLen:int) : long
+ {static} NormRequeueObject(sessionHandle:long, objectHandle:long) : bool
+ {static} NormStreamOpen(sessionHandle:long, bufferSize:long, infoPtr:string, infoLen:int) : long
+ {static} NormStreamClose(streamHandle:long, graceful:bool) : void
Expand Down
35 changes: 0 additions & 35 deletions src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -544,41 +544,6 @@ public struct NormEvent
[DllImport(NORM_LIBRARY)]
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>
/// 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.
Expand Down
48 changes: 36 additions & 12 deletions src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text;
using System.Runtime.InteropServices;
using System.Text;

namespace Mil.Navy.Nrl.Norm
{
Expand Down Expand Up @@ -497,6 +498,7 @@ public NormFile FileEnqueue(string filename, byte[] info, int infoOffset, int in
/// <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>
/// <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)
{
return DataEnqueue(dataBuffer, dataOffset, dataLength, null, 0, 0);
Expand All @@ -514,24 +516,46 @@ public NormData DataEnqueue(byte[] dataBuffer, int dataOffset, int dataLength)
/// <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>
/// <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)
{
var dataBytes = dataBuffer.Skip(dataOffset).Take(dataLength).ToArray();
byte[]? infoBytes;
if (info != null)
if (dataOffset < 0 || dataOffset >= dataBuffer.Length)
{
infoBytes = info.Skip(infoOffset).Take(infoLength).ToArray();
}
else
throw new ArgumentOutOfRangeException(nameof(dataOffset), "The data offset is out of range");
}
if (dataOffset + dataLength > dataBuffer.Length)
{
infoBytes = null;
infoLength = 0;
throw new ArgumentOutOfRangeException(nameof(dataLength), "The data length is out of range");
}
var objectHandle = NormDataEnqueue(_handle, dataBytes, dataLength, infoBytes, infoLength);
if (objectHandle == NormObject.NORM_OBJECT_INVALID)
if (infoOffset < 0 || infoOffset >= info?.Length)
{
throw new IOException("Failed to enqueue data");
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)
{
throw new IOException("Failed to enqueue data");
}
}
finally
{
dataHandle.Free();
infoHandle.Free();
}

return new NormData(objectHandle);
}

Expand Down
188 changes: 172 additions & 16 deletions src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,22 @@ public void StopsReceiver()
/// Generates text content
/// </summary>
/// <returns>The generated text content</returns>
private string GenerateTextContent()
private static string GenerateTextContent()
{
var faker = new Faker();
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)
{
var normEvents = new List<NormEvent>();
Expand Down Expand Up @@ -398,24 +408,85 @@ public void ReceivesFileWithRename()
}
}

[SkippableFact(typeof(IOException))]
public void EnqueuesData()
public static IEnumerable<object[]> GenerateData()
{
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();
//Create data to write to the stream
var expectedContent = GenerateTextContent();
byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent);
//Create data to write to enqueue
var data = Encoding.ASCII.GetBytes(dataContent);
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
{
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 actualEventTypes = GetEvents().Select(e => e.Type).ToList();
Assert.Equal(expectedEventTypes, actualEventTypes);
var actualData = normData.GetData();
Assert.Equal(expectedData, actualData);
var actualContent = Encoding.ASCII.GetString(actualData);
Assert.Equal(expectedContent, actualContent);
var actualDataContent = Encoding.ASCII.GetString(actualData);
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)
{
Expand All @@ -427,8 +498,81 @@ public void EnqueuesData()
}
}

[SkippableFact(typeof(IOException))]
public void ReceivesData()
public static IEnumerable<object[]> GenerateOutOfRangeData()
{
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);
StartSender();
Expand All @@ -441,12 +585,17 @@ public void ReceivesData()
_normInstance.SetCacheDirectory(cachePath);

//Create data to be sent
var expectedContent = GenerateTextContent();
var expectedData = Encoding.ASCII.GetBytes(expectedContent);
var data = Encoding.ASCII.GetBytes(content);
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
{
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_REMOTE_SENDER_NEW,
Expand All @@ -468,8 +617,15 @@ public void ReceivesData()

var actualData = actualNormData.GetData();
Assert.Equal(expectedData, actualData);
var actualContent = Encoding.ASCII.GetString(actualData);
Assert.Equal(expectedContent, actualContent);
var actualDataContent = Encoding.ASCII.GetString(actualData);
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)
{
Expand Down
Loading