diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
new file mode 100644
index 0000000..781bf4b
--- /dev/null
+++ b/.github/workflows/dotnet.yml
@@ -0,0 +1,29 @@
+# This workflow will build a .NET project
+
+name: .NET
+
+on:
+ push:
+ branches: [ "master", "develop" ]
+ pull_request:
+ branches: [ "master" ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: 6.0.x
+ - name: Install FFTW
+ run: sudo apt-get install libfftw3-bin
+ - name: Restore dependencies
+ run: dotnet restore
+ - name: Build
+ run: dotnet build --no-restore
+ - name: Test
+ run: dotnet test --no-build --verbosity normal
diff --git a/SharpFFTW.Tests/Double/TestManaged.cs b/SharpFFTW.Tests/Double/TestManaged.cs
index 9e1026e..66a9705 100644
--- a/SharpFFTW.Tests/Double/TestManaged.cs
+++ b/SharpFFTW.Tests/Double/TestManaged.cs
@@ -1,46 +1,34 @@
namespace SharpFFTW.Tests.Double
{
+ using NUnit.Framework;
using SharpFFTW;
using SharpFFTW.Double;
using System;
+ using System.Collections.Concurrent;
+ using System.Threading;
+ using System.Threading.Tasks;
///
/// Test managed FFTW interface (1D).
///
public class TestManaged
{
- ///
- /// Run examples.
- ///
- /// Logical size of the transform.
- public static void Run(int length)
+ [Test]
+ public void Test()
{
- Console.WriteLine("Starting managed test with FFT size = " + length + " (Type: double)");
- Console.WriteLine();
-
- try
- {
- Example1(length);
- Example2(length);
- Example3(length);
- }
- catch (BadImageFormatException)
- {
- Util.Write("Couldn't load native FFTW image (Type: double)", false);
- }
- catch (Exception e)
- {
- Util.Write(e.Message, false);
- }
+ const int SIZE = 8192;
- Console.WriteLine();
+ Assert.True(Example1(SIZE));
+ Assert.True(Example2(SIZE));
+ Assert.True(Example3(SIZE));
+ Assert.True(Example4(2000, false));
}
///
/// Complex to complex transform.
///
- static void Example1(int length)
+ bool Example1(int length)
{
Console.Write("Test 1: complex transform ... ");
@@ -69,13 +57,13 @@ static void Example1(int length)
input.CopyTo(data);
// Check and see how we did.
- Util.CheckResults(length, length, data);
+ return Util.CheckResults(length, length, data);
}
///
/// Real to complex transform.
///
- static void Example2(int length)
+ bool Example2(int length)
{
Console.Write("Test 2: real to complex transform ... ");
@@ -103,13 +91,13 @@ static void Example2(int length)
input.CopyTo(data);
// Check and see how we did.
- Util.CheckResults(n, n, data);
+ return Util.CheckResults(n, n, data);
}
///
/// Real to half-complex transform.
///
- static void Example3(int length)
+ bool Example3(int length)
{
Console.Write("Test 3: real to half-complex transform ... ");
@@ -137,7 +125,68 @@ static void Example3(int length)
input.CopyTo(data);
// Check and see how we did.
- Util.CheckResults(n, n, data);
+ return Util.CheckResults(n, n, data);
+ }
+
+ ///
+ /// Parallel execution.
+ ///
+ bool Example4(int tasks, bool print)
+ {
+ Console.Write("Test 4: parallel real to complex transform ... ");
+
+ var plans = new ConcurrentDictionary>();
+
+ const int size = 4096;
+
+ var result = Parallel.For(0, tasks, (i, state) =>
+ {
+ int thread = Thread.CurrentThread.ManagedThreadId;
+
+ var (input, _, plan1, plan2) = plans.GetOrAdd(thread, (i) =>
+ {
+ var input = new RealArray(size);
+ var output = new ComplexArray(size / 2 + 1);
+
+ var plan1 = Plan.Create1(size, input, output, Options.Estimate);
+ var plan2 = Plan.Create1(size, output, input, Options.Estimate);
+
+ return new Tuple(input, output, plan1, plan2);
+ });
+
+ var data = Util.GenerateSignal(size);
+
+ input.Set(data);
+
+ plan1.Execute();
+ plan2.Execute();
+
+ Array.Clear(data, 0, size);
+
+ input.CopyTo(data);
+
+ var success = Util.CheckResults(size, size, data);
+
+ if (print)
+ {
+ Console.WriteLine($"{i,5}: current thread = {thread}, success = {success}");
+ }
+
+ if (!success)
+ {
+ state.Break();
+ }
+ });
+
+ foreach (var (input, output, plan1, plan2) in plans.Values)
+ {
+ plan1.Dispose();
+ plan2.Dispose();
+ input.Dispose();
+ output.Dispose();
+ }
+
+ return result.IsCompleted;
}
}
}
\ No newline at end of file
diff --git a/SharpFFTW.Tests/Double/TestNative.cs b/SharpFFTW.Tests/Double/TestNative.cs
index 8b96078..1cf3f6c 100644
--- a/SharpFFTW.Tests/Double/TestNative.cs
+++ b/SharpFFTW.Tests/Double/TestNative.cs
@@ -1,6 +1,7 @@
namespace SharpFFTW.Tests.Double
{
+ using NUnit.Framework;
using SharpFFTW;
using SharpFFTW.Double;
using System;
@@ -11,33 +12,16 @@ namespace SharpFFTW.Tests.Double
///
public class TestNative
{
- ///
- /// Run examples.
- ///
- /// Logical size of the transform.
- public static void Run(int length)
+ [Test]
+ public void Test()
{
- Console.WriteLine("Starting native test with FFT size = " + length + " (Type: double)");
- Console.WriteLine();
-
- try
- {
- Example1(length);
- Example2(length);
- }
- catch (BadImageFormatException)
- {
- Util.Write("Couldn't load native FFTW image (Type: double)", false);
- }
- catch (Exception e)
- {
- Util.Write(e.Message, false);
- }
-
- Console.WriteLine();
+ const int SIZE = 8192;
+
+ Assert.True(Example1(SIZE));
+ Assert.True(Example2(SIZE));
}
- static void Example1(int length)
+ bool Example1(int length)
{
Console.Write("Test 1: complex transform ... ");
@@ -73,16 +57,18 @@ static void Example1(int length)
Marshal.Copy(pin, fin, 0, size);
// Check and see how we did.
- Util.CheckResults(length, length, fin);
+ bool success = Util.CheckResults(length, length, fin);
// Don't forget to free the memory after finishing.
NativeMethods.fftw_free(pin);
NativeMethods.fftw_free(pout);
NativeMethods.fftw_destroy_plan(plan1);
NativeMethods.fftw_destroy_plan(plan2);
+
+ return success;
}
- static void Example2(int length)
+ bool Example2(int length)
{
Console.Write("Test 2: complex transform ... ");
@@ -116,7 +102,7 @@ static void Example2(int length)
NativeMethods.fftw_execute(plan2);
// Check and see how we did.
- Util.CheckResults(length, length, fin);
+ bool success = Util.CheckResults(length, length, fin);
// Don't forget to free the memory after finishing.
NativeMethods.fftw_destroy_plan(plan1);
@@ -124,6 +110,8 @@ static void Example2(int length)
hin.Free();
hout.Free();
+
+ return success;
}
}
}
\ No newline at end of file
diff --git a/SharpFFTW.Tests/Double/Util.cs b/SharpFFTW.Tests/Double/Util.cs
index 853c180..cefedcf 100644
--- a/SharpFFTW.Tests/Double/Util.cs
+++ b/SharpFFTW.Tests/Double/Util.cs
@@ -16,7 +16,7 @@ public static double[] GenerateSignal(int n)
return data;
}
- public static void CheckResults(int n, int scale, double[] data, double eps = 1e-3)
+ public static bool CheckResults(int n, int scale, double[] data, double eps = 1e-3)
{
for (int i = 0; i < n; i++)
{
@@ -26,14 +26,25 @@ public static void CheckResults(int n, int scale, double[] data, double eps = 1e
// Check against original value.
if (double.IsNaN(result) || Math.Abs(result - (i % 50)) > eps)
{
- Write("failed", false);
- return;
+ return false;
}
}
// Yeah, alright. So this was kind of a trivial test and of course
// it's gonna work. But still.
- Write("ok", true);
+ return true;
+ }
+
+ public static void PrintResult(bool succsess)
+ {
+ if (succsess)
+ {
+ Write("ok", true);
+ }
+ else
+ {
+ Write("failed", false);
+ }
}
public static void Write(string message, bool ok)
diff --git a/SharpFFTW.Tests/Program.cs b/SharpFFTW.Tests/Program.cs
deleted file mode 100644
index fbb1c88..0000000
--- a/SharpFFTW.Tests/Program.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-
-namespace SharpFFTW.Tests
-{
- class Program
- {
- const int FFT_SIZE = 8192;
-
- static void Main(string[] args)
- {
- Double.TestNative.Run(FFT_SIZE);
- Double.TestManaged.Run(FFT_SIZE);
-
- Single.TestNative.Run(FFT_SIZE);
- Single.TestManaged.Run(FFT_SIZE);
-
- Console.WriteLine("\nDone. Press any key to exit.");
- Console.ReadLine();
- }
- }
-}
diff --git a/SharpFFTW.Tests/Setup.cs b/SharpFFTW.Tests/Setup.cs
new file mode 100644
index 0000000..4d3dbc6
--- /dev/null
+++ b/SharpFFTW.Tests/Setup.cs
@@ -0,0 +1,14 @@
+using NUnit.Framework;
+
+namespace SharpFFTW.Tests
+{
+ [SetUpFixture]
+ public class Setup
+ {
+ [OneTimeSetUp]
+ public void GlobalSetup()
+ {
+ Library.SetImportResolver();
+ }
+ }
+}
diff --git a/SharpFFTW.Tests/SharpFFTW.Tests.csproj b/SharpFFTW.Tests/SharpFFTW.Tests.csproj
index 985dced..dbd76ed 100644
--- a/SharpFFTW.Tests/SharpFFTW.Tests.csproj
+++ b/SharpFFTW.Tests/SharpFFTW.Tests.csproj
@@ -1,17 +1,22 @@
-
+
net6.0
false
+ true
AnyCPU;x64
-
- Exe
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/SharpFFTW.Tests/Single/TestManaged.cs b/SharpFFTW.Tests/Single/TestManaged.cs
index 709ec8a..6ca4484 100644
--- a/SharpFFTW.Tests/Single/TestManaged.cs
+++ b/SharpFFTW.Tests/Single/TestManaged.cs
@@ -1,46 +1,34 @@
namespace SharpFFTW.Tests.Single
{
+ using NUnit.Framework;
using SharpFFTW;
using SharpFFTW.Single;
using System;
+ using System.Collections.Concurrent;
+ using System.Threading;
+ using System.Threading.Tasks;
///
/// Test managed FFTW interface (1D).
///
public class TestManaged
{
- ///
- /// Run examples.
- ///
- /// Logical size of the transform.
- public static void Run(int length)
+ [Test]
+ public void Test()
{
- Console.WriteLine("Starting managed test with FFT size = " + length + " (Type: single)");
- Console.WriteLine();
-
- try
- {
- Example1(length);
- Example2(length);
- Example3(length);
- }
- catch (BadImageFormatException)
- {
- Util.Write("Couldn't load native FFTW image (Type: single)", false);
- }
- catch (Exception e)
- {
- Util.Write(e.Message, false);
- }
+ const int SIZE = 8192;
- Console.WriteLine();
+ Assert.True(Example1(SIZE));
+ Assert.True(Example2(SIZE));
+ Assert.True(Example3(SIZE));
+ Assert.True(Example4(2000, false));
}
///
/// Complex to complex transform.
///
- static void Example1(int length)
+ bool Example1(int length)
{
Console.Write("Test 1: complex transform ... ");
@@ -69,13 +57,13 @@ static void Example1(int length)
input.CopyTo(data);
// Check and see how we did.
- Util.CheckResults(length, length, data);
+ return Util.CheckResults(length, length, data);
}
///
/// Real to complex transform.
///
- static void Example2(int length)
+ bool Example2(int length)
{
Console.Write("Test 2: real to complex transform ... ");
@@ -103,13 +91,13 @@ static void Example2(int length)
input.CopyTo(data);
// Check and see how we did.
- Util.CheckResults(n, n, data);
+ return Util.CheckResults(n, n, data);
}
///
/// Real to half-complex transform.
///
- static void Example3(int length)
+ bool Example3(int length)
{
Console.Write("Test 3: real to half-complex transform ... ");
@@ -137,7 +125,68 @@ static void Example3(int length)
input.CopyTo(data);
// Check and see how we did.
- Util.CheckResults(n, n, data);
+ return Util.CheckResults(n, n, data);
+ }
+
+ ///
+ /// Parallel execution.
+ ///
+ bool Example4(int tasks, bool print)
+ {
+ Console.Write("Test 4: parallel real to complex transform ... ");
+
+ var plans = new ConcurrentDictionary>();
+
+ const int size = 4096;
+
+ var result = Parallel.For(0, tasks, (i, state) =>
+ {
+ int thread = Thread.CurrentThread.ManagedThreadId;
+
+ var (input, output, plan1, plan2) = plans.GetOrAdd(thread, (i) =>
+ {
+ var input = new RealArray(size);
+ var output = new ComplexArray(size / 2 + 1);
+
+ var plan1 = Plan.Create1(size, input, output, Options.Estimate);
+ var plan2 = Plan.Create1(size, output, input, Options.Estimate);
+
+ return new Tuple(input, output, plan1, plan2);
+ });
+
+ var data = Util.GenerateSignal(size);
+
+ input.Set(data);
+
+ plan1.Execute();
+ plan2.Execute();
+
+ Array.Clear(data, 0, size);
+
+ input.CopyTo(data);
+
+ var success = Util.CheckResults(size, size, data);
+
+ if (print)
+ {
+ Console.WriteLine($"{i,5}: current thread = {thread}, success = {success}");
+ }
+
+ if (!success)
+ {
+ state.Break();
+ }
+ });
+
+ foreach (var (input, output, plan1, plan2) in plans.Values)
+ {
+ plan1.Dispose();
+ plan2.Dispose();
+ input.Dispose();
+ output.Dispose();
+ }
+
+ return result.IsCompleted;
}
}
}
\ No newline at end of file
diff --git a/SharpFFTW.Tests/Single/TestNative.cs b/SharpFFTW.Tests/Single/TestNative.cs
index 01930e6..694354e 100644
--- a/SharpFFTW.Tests/Single/TestNative.cs
+++ b/SharpFFTW.Tests/Single/TestNative.cs
@@ -1,6 +1,7 @@
namespace SharpFFTW.Tests.Single
{
+ using NUnit.Framework;
using SharpFFTW;
using SharpFFTW.Single;
using System;
@@ -11,33 +12,16 @@ namespace SharpFFTW.Tests.Single
///
public class TestNative
{
- ///
- /// Run examples.
- ///
- /// Logical size of the transform.
- public static void Run(int length)
+ [Test]
+ public void Test()
{
- Console.WriteLine("Starting native test with FFT size = " + length + " (Type: single)");
- Console.WriteLine();
-
- try
- {
- Example1(length);
- Example2(length);
- }
- catch (BadImageFormatException)
- {
- Util.Write("Couldn't load native FFTW image (Type: single)", false);
- }
- catch (Exception e)
- {
- Util.Write(e.Message, false);
- }
-
- Console.WriteLine();
+ const int SIZE = 8192;
+
+ Assert.True(Example1(SIZE));
+ Assert.True(Example2(SIZE));
}
- static void Example1(int length)
+ bool Example1(int length)
{
Console.Write("Test 1: complex transform ... ");
@@ -73,16 +57,18 @@ static void Example1(int length)
Marshal.Copy(pin, fin, 0, size);
// Check and see how we did.
- Util.CheckResults(length, length, fin);
+ bool success = Util.CheckResults(length, length, fin);
// Don't forget to free the memory after finishing.
NativeMethods.fftwf_free(pin);
NativeMethods.fftwf_free(pout);
NativeMethods.fftwf_destroy_plan(plan1);
NativeMethods.fftwf_destroy_plan(plan2);
+
+ return success;
}
- static void Example2(int length)
+ bool Example2(int length)
{
Console.Write("Test 2: complex transform ... ");
@@ -116,7 +102,7 @@ static void Example2(int length)
NativeMethods.fftwf_execute(plan2);
// Check and see how we did.
- Util.CheckResults(length, length, fin);
+ bool success = Util.CheckResults(length, length, fin);
// Don't forget to free the memory after finishing.
NativeMethods.fftwf_destroy_plan(plan1);
@@ -124,6 +110,8 @@ static void Example2(int length)
hin.Free();
hout.Free();
+
+ return success;
}
}
}
\ No newline at end of file
diff --git a/SharpFFTW.Tests/Single/Util.cs b/SharpFFTW.Tests/Single/Util.cs
index f4f1aa2..efd8748 100644
--- a/SharpFFTW.Tests/Single/Util.cs
+++ b/SharpFFTW.Tests/Single/Util.cs
@@ -16,7 +16,7 @@ public static float[] GenerateSignal(int n)
return data;
}
- public static void CheckResults(int n, int scale, float[] data, double eps = 1e-3)
+ public static bool CheckResults(int n, int scale, float[] data, float eps = 1e-3f)
{
for (int i = 0; i < n; i++)
{
@@ -24,16 +24,27 @@ public static void CheckResults(int n, int scale, float[] data, double eps = 1e-
float result = data[i] / scale;
// Check against original value.
- if (double.IsNaN(result) || Math.Abs(result - (i % 50)) > eps)
+ if (float.IsNaN(result) || Math.Abs(result - (i % 50)) > eps)
{
- Write("failed", false);
- return;
+ return false;
}
}
// Yeah, alright. So this was kind of a trivial test and of course
// it's gonna work. But still.
- Write("ok", true);
+ return true;
+ }
+
+ public static void PrintResult(bool succsess)
+ {
+ if (succsess)
+ {
+ Write("ok", true);
+ }
+ else
+ {
+ Write("failed", false);
+ }
}
public static void Write(string message, bool ok)
diff --git a/SharpFFTW.sln b/SharpFFTW.sln
index 0aa906d..9beb613 100644
--- a/SharpFFTW.sln
+++ b/SharpFFTW.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.28917.181
+# Visual Studio Version 17
+VisualStudioVersion = 17.4.33110.190
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpFFTW.Tests", "SharpFFTW.Tests\SharpFFTW.Tests.csproj", "{DA20704B-23A7-41A4-B4E2-174A0FED5F3D}"
ProjectSection(ProjectDependencies) = postProject
@@ -32,7 +32,9 @@ Global
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DA20704B-23A7-41A4-B4E2-174A0FED5F3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DA20704B-23A7-41A4-B4E2-174A0FED5F3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA20704B-23A7-41A4-B4E2-174A0FED5F3D}.Debug|x64.ActiveCfg = Debug|x64
+ {DA20704B-23A7-41A4-B4E2-174A0FED5F3D}.Debug|x64.Build.0 = Debug|x64
{DA20704B-23A7-41A4-B4E2-174A0FED5F3D}.Release|Any CPU.ActiveCfg = Release|x86
{DA20704B-23A7-41A4-B4E2-174A0FED5F3D}.Release|x64.ActiveCfg = Release|x64
{BEA875B8-E28A-49C5-8E7E-6512DA65F7E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
diff --git a/SharpFFTW/AbstractArray.cs b/SharpFFTW/AbstractArray.cs
index f9a817a..8f87bb6 100644
--- a/SharpFFTW/AbstractArray.cs
+++ b/SharpFFTW/AbstractArray.cs
@@ -2,11 +2,12 @@
namespace SharpFFTW
{
using System;
- using System.Runtime.InteropServices;
+ using System.Threading;
///
/// Abstract base class for native FFTW arrays.
///
+ /// Supported types are and .
public abstract class AbstractArray : IDisposable
where T : struct, IEquatable, IFormattable
{
@@ -24,18 +25,24 @@ public abstract class AbstractArray : IDisposable
private T[] data;
///
- /// Creates a new array of complex numbers.
+ /// Creates a new .
///
/// Logical length of the array.
public AbstractArray(int length)
{
- this.Length = length;
+ Length = length;
}
///
- /// Copy contents of given array to native memory.
+ /// Copy items from the given array
+ /// to native memory.
///
/// The data to copy.
+ ///
+ /// For real valued data the array length must be at
+ /// least , for complex valued data the length must be at
+ /// least 2 * Length.
+ ///
public abstract void Set(T[] source);
///
@@ -57,20 +64,33 @@ public AbstractArray(int length)
#region IDisposable implementation
- protected bool hasDisposed = false;
+ protected int _disposeCount;
///
public void Dispose()
{
- Dispose(true);
- GC.SuppressFinalize(this);
+ if (Interlocked.Increment(ref _disposeCount) == 1)
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
}
+ ///
+ /// Performs application-defined tasks associated with disposing of resources.
+ ///
+ /// Indicates whether the method call comes from a Dispose method
+ /// (value is true) or from a finalizer (value is false).
+ ///
+ /// Override this method in derived classes. Unmanaged resources should always be
+ /// released when this method is called. Managed resources may only be disposed
+ /// of if is true.
+ ///
public abstract void Dispose(bool disposing);
~AbstractArray()
{
- Dispose(true);
+ Dispose(false);
}
#endregion
diff --git a/SharpFFTW/AbstractPlan.cs b/SharpFFTW/AbstractPlan.cs
index 16fc05e..4ead4de 100644
--- a/SharpFFTW/AbstractPlan.cs
+++ b/SharpFFTW/AbstractPlan.cs
@@ -59,20 +59,33 @@ public AbstractArray Output
#region IDisposable implementation
- protected bool hasDisposed = false;
+ protected int _disposeCount;
///
public void Dispose()
{
- Dispose(true);
- GC.SuppressFinalize(this);
+ if (Interlocked.Increment(ref _disposeCount) == 1)
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
}
+ ///
+ /// Performs application-defined tasks associated with disposing of resources.
+ ///
+ /// Indicates whether the method call comes from a Dispose method
+ /// (value is true) or from a finalizer (value is false).
+ ///
+ /// Override this method in derived classes. Unmanaged resources should always be
+ /// released when this method is called. Managed resources may only be disposed
+ /// of if is true.
+ ///
public abstract void Dispose(bool disposing);
~AbstractPlan()
{
- Dispose(true);
+ Dispose(false);
}
#endregion
diff --git a/SharpFFTW/Double/ComplexArray.cs b/SharpFFTW/Double/ComplexArray.cs
index 1005edc..578c1e0 100644
--- a/SharpFFTW/Double/ComplexArray.cs
+++ b/SharpFFTW/Double/ComplexArray.cs
@@ -22,7 +22,7 @@ public class ComplexArray : AbstractArray
public ComplexArray(int length)
: base(length)
{
- Handle = NativeMethods.fftw_malloc(this.Length * SIZE);
+ Handle = NativeMethods.fftw_malloc(Length * SIZE);
}
///
@@ -32,7 +32,7 @@ public ComplexArray(int length)
public ComplexArray(double[] data)
: this(data.Length / 2)
{
- this.Set(data);
+ Set(data);
}
///
@@ -42,22 +42,17 @@ public ComplexArray(double[] data)
public ComplexArray(Complex[] data)
: this(data.Length)
{
- this.Set(data);
+ Set(data);
}
///
public override void Dispose(bool disposing)
{
- if (!hasDisposed)
+ if (Handle != IntPtr.Zero)
{
- if (Handle != IntPtr.Zero)
- {
- NativeMethods.fftw_free(Handle);
- Handle = IntPtr.Zero;
- }
+ NativeMethods.fftw_free(Handle);
+ Handle = IntPtr.Zero;
}
-
- hasDisposed = disposing;
}
///
@@ -68,9 +63,9 @@ public override void Set(double[] source)
{
int size = 2 * Length;
- if (source.Length != size)
+ if (source.Length < size)
{
- throw new ArgumentException("Array length mismatch.");
+ throw new ArgumentException("Array length mismatch.", nameof(source));
}
Marshal.Copy(source, 0, Handle, size);
@@ -82,9 +77,9 @@ public override void Set(double[] source)
/// Array of complex numbers.
public void Set(Complex[] source)
{
- if (source.Length != this.Length)
+ if (source.Length != Length)
{
- throw new ArgumentException("Array length mismatch.");
+ throw new ArgumentException("Array length mismatch.", nameof(source));
}
var temp = GetTemporaryData(2 * Length);
@@ -95,19 +90,17 @@ public void Set(Complex[] source)
temp[2 * i + 1] = source[i].Imaginary;
}
- Marshal.Copy(temp, 0, Handle, this.Length * 2);
+ Marshal.Copy(temp, 0, Handle, Length * 2);
}
- ///
- /// Set the data to zeros.
- ///
+ ///
public override void Clear()
{
var temp = GetTemporaryData(2 * Length);
Array.Clear(temp, 0, temp.Length);
- Marshal.Copy(temp, 0, Handle, this.Length * 2);
+ Marshal.Copy(temp, 0, Handle, Length * 2);
}
///
@@ -116,9 +109,9 @@ public override void Clear()
/// Array of complex numbers.
public void CopyTo(Complex[] target)
{
- if (target.Length != this.Length)
+ if (target.Length < Length)
{
- throw new Exception();
+ throw new ArgumentException("Array length mismatch.", nameof(target));
}
var temp = GetTemporaryData(2 * Length);
@@ -141,7 +134,7 @@ public override void CopyTo(double[] target)
if (target.Length < size)
{
- throw new Exception();
+ throw new ArgumentException("Array length mismatch.", nameof(target));
}
Marshal.Copy(Handle, target, 0, size);
diff --git a/SharpFFTW/Double/Plan.cs b/SharpFFTW/Double/Plan.cs
index da53130..73e0896 100644
--- a/SharpFFTW/Double/Plan.cs
+++ b/SharpFFTW/Double/Plan.cs
@@ -53,22 +53,19 @@ public override string ToString()
///
public override void Dispose(bool disposing)
{
- if (!hasDisposed)
+ if (handle != IntPtr.Zero)
{
- if (handle != IntPtr.Zero)
- {
- NativeMethods.fftw_destroy_plan(handle);
- handle = IntPtr.Zero;
- }
-
- if (ownsArrays)
- {
- input.Dispose();
- output.Dispose();
- }
+ mutex.WaitOne();
+ NativeMethods.fftw_destroy_plan(handle);
+ handle = IntPtr.Zero;
+ mutex.ReleaseMutex();
}
- hasDisposed = disposing;
+ if (disposing && ownsArrays)
+ {
+ input.Dispose();
+ output.Dispose();
+ }
}
#region 1D plan creation
diff --git a/SharpFFTW/Double/RealArray.cs b/SharpFFTW/Double/RealArray.cs
index 3b0ec87..6b512a7 100644
--- a/SharpFFTW/Double/RealArray.cs
+++ b/SharpFFTW/Double/RealArray.cs
@@ -21,7 +21,7 @@ public class RealArray : AbstractArray
public RealArray(int length)
: base(length)
{
- Handle = NativeMethods.fftw_malloc(this.Length * SIZE);
+ Handle = NativeMethods.fftw_malloc(Length * SIZE);
}
///
@@ -31,50 +31,40 @@ public RealArray(int length)
public RealArray(double[] data)
: this(data.Length)
{
- this.Set(data);
+ Set(data);
}
///
public override void Dispose(bool disposing)
{
- if (!hasDisposed)
+ if (Handle != IntPtr.Zero)
{
- if (Handle != IntPtr.Zero)
- {
- NativeMethods.fftw_free(Handle);
- Handle = IntPtr.Zero;
- }
+ NativeMethods.fftw_free(Handle);
+ Handle = IntPtr.Zero;
}
-
- hasDisposed = disposing;
}
- ///
- /// Set the data to an array of doubles.
- ///
- /// Array of doubles, alternating real and imaginary.
+ ///
public override void Set(double[] source)
{
int size = Length;
- if (source.Length != size)
+ if (source.Length < size)
{
- throw new ArgumentException("Array length mismatch.");
+ throw new ArgumentException("Array length mismatch.", nameof(source));
}
Marshal.Copy(source, 0, Handle, size);
}
- ///
- /// Set the data to zeros.
- ///
+ ///
public override void Clear()
{
var temp = GetTemporaryData(Length);
Array.Clear(temp, 0, temp.Length);
- Marshal.Copy(temp, 0, Handle, this.Length);
+ Marshal.Copy(temp, 0, Handle, Length);
}
///
@@ -85,9 +75,9 @@ public override void CopyTo(double[] target)
{
int size = Length;
- if (target.Length != size)
+ if (target.Length < size)
{
- throw new Exception();
+ throw new ArgumentException("Array length mismatch.", nameof(target));
}
Marshal.Copy(Handle, target, 0, size);
diff --git a/SharpFFTW/Library.cs b/SharpFFTW/Library.cs
new file mode 100644
index 0000000..e475e3b
--- /dev/null
+++ b/SharpFFTW/Library.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+namespace SharpFFTW
+{
+#if NET5_0_OR_GREATER
+ public class Library
+ {
+ public static void SetImportResolver()
+ {
+ NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), DllImportResolver);
+ }
+
+ private static IntPtr DllImportResolver(string library, Assembly assembly, DllImportSearchPath? searchPath)
+ {
+ // Trying to find FFTW on Ubuntu.
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && library.StartsWith("fftw"))
+ {
+ return NativeLibrary.Load("lib" + library + ".so.3", assembly, searchPath);
+ }
+
+ // Otherwise, fall back to default import resolver.
+ return IntPtr.Zero;
+ }
+ }
+#endif
+}
diff --git a/SharpFFTW/Single/ComplexArray.cs b/SharpFFTW/Single/ComplexArray.cs
index 094c1ca..7873a09 100644
--- a/SharpFFTW/Single/ComplexArray.cs
+++ b/SharpFFTW/Single/ComplexArray.cs
@@ -21,7 +21,7 @@ public class ComplexArray : AbstractArray
public ComplexArray(int length)
: base(length)
{
- Handle = NativeMethods.fftwf_malloc(this.Length * SIZE);
+ Handle = NativeMethods.fftwf_malloc(Length * SIZE);
}
///
@@ -31,7 +31,7 @@ public ComplexArray(int length)
public ComplexArray(float[] data)
: this(data.Length / 2)
{
- this.Set(data);
+ Set(data);
}
///
@@ -41,22 +41,17 @@ public ComplexArray(float[] data)
public ComplexArray(Complex32[] data)
: this(data.Length)
{
- this.Set(data);
+ Set(data);
}
///
public override void Dispose(bool disposing)
{
- if (!hasDisposed)
+ if (Handle != IntPtr.Zero)
{
- if (Handle != IntPtr.Zero)
- {
- NativeMethods.fftwf_free(Handle);
- Handle = IntPtr.Zero;
- }
+ NativeMethods.fftwf_free(Handle);
+ Handle = IntPtr.Zero;
}
-
- hasDisposed = disposing;
}
///
@@ -67,9 +62,9 @@ public override void Set(float[] source)
{
int size = 2 * Length;
- if (source.Length != size)
+ if (source.Length < size)
{
- throw new ArgumentException("Array length mismatch.");
+ throw new ArgumentException("Array length mismatch.", nameof(source));
}
Marshal.Copy(source, 0, Handle, size);
@@ -81,9 +76,9 @@ public override void Set(float[] source)
/// Array of complex numbers.
public void Set(Complex32[] source)
{
- if (source.Length != this.Length)
+ if (source.Length < Length)
{
- throw new ArgumentException("Array length mismatch.");
+ throw new ArgumentException("Array length mismatch.", nameof(source));
}
var temp = GetTemporaryData(2 * Length);
@@ -94,19 +89,17 @@ public void Set(Complex32[] source)
temp[2 * i + 1] = source[i].Imaginary;
}
- Marshal.Copy(temp, 0, Handle, this.Length * 2);
+ Marshal.Copy(temp, 0, Handle, Length * 2);
}
- ///
- /// Set the data to zeros.
- ///
+ ///
public override void Clear()
{
var temp = GetTemporaryData(2 * Length);
Array.Clear(temp, 0, temp.Length);
- Marshal.Copy(temp, 0, Handle, this.Length * 2);
+ Marshal.Copy(temp, 0, Handle, Length * 2);
}
///
@@ -115,9 +108,9 @@ public override void Clear()
/// Array of complex numbers.
public void CopyTo(Complex32[] target)
{
- if (target.Length != this.Length)
+ if (target.Length < Length)
{
- throw new Exception();
+ throw new ArgumentException("Array length mismatch.", nameof(target));
}
var temp = GetTemporaryData(2 * Length);
@@ -138,9 +131,9 @@ public override void CopyTo(float[] target)
{
int size = 2 * Length;
- if (target.Length != size)
+ if (target.Length < size)
{
- throw new Exception();
+ throw new ArgumentException("Array length mismatch.", nameof(target));
}
Marshal.Copy(Handle, target, 0, size);
diff --git a/SharpFFTW/Single/Plan.cs b/SharpFFTW/Single/Plan.cs
index 45b1f50..2d5b39d 100644
--- a/SharpFFTW/Single/Plan.cs
+++ b/SharpFFTW/Single/Plan.cs
@@ -46,29 +46,26 @@ public override string ToString()
{
// NOTE: this leaks native memory, since the returned char* pointer isn't free'd.
var p = NativeMethods.fftwf_sprint_plan(handle);
-
+
return Marshal.PtrToStringAnsi(p);
}
///
public override void Dispose(bool disposing)
{
- if (!hasDisposed)
+ if (handle != IntPtr.Zero)
{
- if (handle != IntPtr.Zero)
- {
- NativeMethods.fftwf_destroy_plan(handle);
- handle = IntPtr.Zero;
- }
-
- if (ownsArrays)
- {
- input.Dispose();
- output.Dispose();
- }
+ mutex.WaitOne();
+ NativeMethods.fftwf_destroy_plan(handle);
+ handle = IntPtr.Zero;
+ mutex.ReleaseMutex();
}
- hasDisposed = disposing;
+ if (disposing && ownsArrays)
+ {
+ input.Dispose();
+ output.Dispose();
+ }
}
#region 1D plan creation
diff --git a/SharpFFTW/Single/RealArray.cs b/SharpFFTW/Single/RealArray.cs
index abce9e8..be48448 100644
--- a/SharpFFTW/Single/RealArray.cs
+++ b/SharpFFTW/Single/RealArray.cs
@@ -21,7 +21,7 @@ public class RealArray : AbstractArray
public RealArray(int length)
: base(length)
{
- Handle = NativeMethods.fftwf_malloc(this.Length * SIZE);
+ Handle = NativeMethods.fftwf_malloc(Length * SIZE);
}
///
@@ -31,50 +31,40 @@ public RealArray(int length)
public RealArray(float[] data)
: this(data.Length)
{
- this.Set(data);
+ Set(data);
}
///
public override void Dispose(bool disposing)
{
- if (!hasDisposed)
+ if (Handle != IntPtr.Zero)
{
- if (Handle != IntPtr.Zero)
- {
- NativeMethods.fftwf_free(Handle);
- Handle = IntPtr.Zero;
- }
+ NativeMethods.fftwf_free(Handle);
+ Handle = IntPtr.Zero;
}
-
- hasDisposed = disposing;
}
- ///
- /// Set the data to an array of floats.
- ///
- /// Array of 4-byte floating point numbers.
+ ///
public override void Set(float[] source)
{
int size = Length;
- if (source.Length != size)
+ if (source.Length < size)
{
- throw new ArgumentException("Array length mismatch.");
+ throw new ArgumentException("Array length mismatch.", nameof(source));
}
Marshal.Copy(source, 0, Handle, size);
}
- ///
- /// Set the data to zeros.
- ///
+ ///
public override void Clear()
{
var temp = GetTemporaryData(Length);
Array.Clear(temp, 0, temp.Length);
- Marshal.Copy(temp, 0, Handle, this.Length);
+ Marshal.Copy(temp, 0, Handle, Length);
}
///
@@ -85,9 +75,9 @@ public override void CopyTo(float[] target)
{
int size = Length;
- if (target.Length != size)
+ if (target.Length < size)
{
- throw new Exception();
+ throw new ArgumentException("Array length mismatch.", nameof(target));
}
Marshal.Copy(Handle, target, 0, size);
diff --git a/fftbench/fftbench.Common/fftbench.Common.csproj b/fftbench/fftbench.Common/fftbench.Common.csproj
index 0e1ec43..6327ab2 100644
--- a/fftbench/fftbench.Common/fftbench.Common.csproj
+++ b/fftbench/fftbench.Common/fftbench.Common.csproj
@@ -15,7 +15,7 @@
-
+