Skip to content

Commit 027ee05

Browse files
committed
perf: Optimized special streams
1 parent 99425fe commit 027ee05

2 files changed

Lines changed: 94 additions & 34 deletions

File tree

MemoryModule/NativeAssembly.cs

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,68 @@ private unsafe NativeAssembly()
5757
/// for dependency resolution. Can be null, to exclude from resolution.</param>
5858
/// <returns>A new NativeAssembly</returns>
5959
public static NativeAssembly Load(byte[] data, string name = null)
60+
{
61+
unsafe
62+
{
63+
fixed (byte* dataPtr = &data[0])
64+
{
65+
return LoadInternal(dataPtr, data.Length, name);
66+
}
67+
}
68+
}
69+
70+
/// <summary>
71+
/// Loads a NativeAssembly, from the specified Stream.
72+
/// This function does not dispose the given stream.
73+
/// </summary>
74+
/// <param name="data">A valid Stream containing the assembly</param>
75+
/// <param name="name">The name this assembly should be associated with
76+
/// for dependency resolution. Can be null, to exclude from resolution.</param>
77+
/// <returns>A new NativeAssembly</returns>
78+
public static NativeAssembly Load(Stream data, string name = null)
79+
{
80+
if (data is MemoryStream mstream)
81+
{
82+
// Avoids more copying...
83+
return Load(mstream.ToArray(), name);
84+
}
85+
// The most common stream in resoure files.
86+
else if (data is UnmanagedMemoryStream umstream)
87+
{
88+
unsafe
89+
{
90+
try
91+
{
92+
return LoadInternal(
93+
umstream.PositionPointer,
94+
umstream.Length - umstream.Position,
95+
name);
96+
}
97+
// There's no quick and easy way to do this,
98+
// but most resource streams are initialized using
99+
// byte*'s anyway.
100+
catch (NotSupportedException)
101+
{
102+
goto loadNormally;
103+
}
104+
}
105+
}
106+
loadNormally:
107+
var ms = new MemoryStream();
108+
data.CopyTo(ms);
109+
var arr = ms.ToArray();
110+
ms.Dispose();
111+
return Load(arr, name);
112+
}
113+
114+
private static unsafe NativeAssembly LoadInternal(byte* data, long length, string name = null)
60115
{
61116
lock (_handles)
62117
{
63118
var asm = new NativeAssembly();
64119
IntPtr handle = NativeAssemblyImpl.LoadLibrary(
65120
data,
121+
length,
66122
loadLibrary: asm.LoadLibraryDelegate,
67123
getProcAddress: GetProcAddressDelegate,
68124
freeLibrary: FreeLibraryDelegate
@@ -84,23 +140,6 @@ public static NativeAssembly Load(byte[] data, string name = null)
84140
}
85141
}
86142

87-
/// <summary>
88-
/// Loads a NativeAssembly, from the specified Stream.
89-
/// This function does not dispose the given stream.
90-
/// </summary>
91-
/// <param name="data">A valid Stream containing the assembly</param>
92-
/// <param name="name">The name this assembly should be associated with
93-
/// for dependency resolution. Can be null, to exclude from resolution.</param>
94-
/// <returns>A new NativeAssembly</returns>
95-
public static NativeAssembly Load(Stream data, string name = null)
96-
{
97-
var ms = new MemoryStream();
98-
data.CopyTo(ms);
99-
var arr = ms.ToArray();
100-
ms.Dispose();
101-
return Load(arr, null);
102-
}
103-
104143
/// <summary>
105144
/// Unloads the library. You do not need to
106145
/// call this function, <code>Dispose()</code>

MemoryModule/NativeAssemblyImpl.cs

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -61,31 +61,52 @@ public static IntPtr LoadLibrary(
6161
CustomGetProcAddressFunc getProcAddress = null,
6262
CustomFreeLibraryFunc freeLibrary = null
6363
)
64+
{
65+
fixed (byte* dataPtr = &data[0])
66+
{
67+
return LoadLibrary(dataPtr, data.LongLength);
68+
}
69+
}
70+
71+
/// <summary>
72+
/// Load EXE/DLL from memory location with the given size.
73+
/// All dependencies are resolved using default LoadLibrary/GetProcAddress
74+
/// calls through the Windows API, or through passed delegates.
75+
/// </summary>
76+
/// <param name="data">The assembly's code</param>
77+
/// <param name="length">The assembly's code's length</param>
78+
/// <returns>A handle to the loaded assembly</returns>
79+
public static IntPtr LoadLibrary(
80+
byte* dataPtr,
81+
long length,
82+
CustomAllocFunc allocMemory = null,
83+
CustomFreeFunc freeMemory = null,
84+
CustomLoadLibraryFunc loadLibrary = null,
85+
CustomGetProcAddressFunc getProcAddress = null,
86+
CustomFreeLibraryFunc freeLibrary = null
87+
)
6488
{
6589
allocMemory = allocMemory ?? MemoryDefaultAllocDelegate;
6690
freeMemory = freeMemory ?? MemoryDefaultFreeDelegate;
6791
loadLibrary = loadLibrary ?? MemoryDefaultLoadLibraryDelegate;
6892
getProcAddress = getProcAddress ?? MemoryDefaultGetProcAddressDelegate;
6993
freeLibrary = freeLibrary ?? MemoryDefaultFreeLibraryDelegate;
70-
71-
fixed (void* dataPtr = &data[0])
94+
95+
var handle = MemoryLoadLibraryEx(
96+
dataPtr,
97+
(ulong)length,
98+
allocMemory,
99+
freeMemory,
100+
loadLibrary,
101+
getProcAddress,
102+
freeLibrary,
103+
null
104+
);
105+
if (handle == null)
72106
{
73-
var handle = MemoryLoadLibraryEx(
74-
dataPtr,
75-
(ulong)data.Length,
76-
allocMemory,
77-
freeMemory,
78-
loadLibrary,
79-
getProcAddress,
80-
freeLibrary,
81-
null
82-
);
83-
if (handle == null)
84-
{
85-
throw new Win32Exception((int)GetLastError());
86-
}
87-
return new IntPtr(handle);
107+
throw new Win32Exception((int)GetLastError());
88108
}
109+
return new IntPtr(handle);
89110
}
90111

91112
/// <summary>

0 commit comments

Comments
 (0)