Added allocation size handling to GetString and AllocString

pull/60/head
David Hall 2019-03-21 09:34:36 -06:00
parent b8fe8bdf89
commit ef2f74cd83
2 changed files with 65 additions and 8 deletions

View File

@ -70,12 +70,22 @@ namespace Vanara.Extensions
/// <param name="charSet">The character set.</param>
/// <param name="memAllocator">The method used to allocate the memory.</param>
/// <returns>The allocated memory block, or 0 if <paramref name="s"/> is null.</returns>
public static IntPtr AllocString(string s, CharSet charSet, Func<int, IntPtr> memAllocator)
public static IntPtr AllocString(string s, CharSet charSet, Func<int, IntPtr> memAllocator) => AllocString(s, charSet, memAllocator, out _);
/// <summary>
/// Copies the contents of a managed String to a block of memory allocated from a supplied allocation method.
/// </summary>
/// <param name="s">A managed string to be copied.</param>
/// <param name="charSet">The character set.</param>
/// <param name="memAllocator">The method used to allocate the memory.</param>
/// <param name="allocatedBytes">Returns the number of allocated bytes for the string.</param>
/// <returns>The allocated memory block, or 0 if <paramref name="s" /> is null.</returns>
public static IntPtr AllocString(string s, CharSet charSet, Func<int, IntPtr> memAllocator, out int allocatedBytes)
{
if (s == null) return IntPtr.Zero;
if (s == null) { allocatedBytes = 0; return IntPtr.Zero; }
var b = s.GetBytes(true, charSet);
var p = memAllocator(b.Length);
Marshal.Copy(b, 0, p, b.Length);
Marshal.Copy(b, 0, p, allocatedBytes = b.Length);
return p;
}
@ -139,11 +149,36 @@ namespace Vanara.Extensions
/// <returns>The size of a standard character, in bytes, from <paramref name="charSet"/>.</returns>
public static int GetCharSize(CharSet charSet = CharSet.Auto) => charSet == CharSet.Auto ? Marshal.SystemDefaultCharSize : (charSet == CharSet.Unicode ? 2 : 1);
/// <summary>Allocates a managed String and copies all characters up to the first null character from a string stored in unmanaged memory into it.</summary>
/// <summary>
/// Allocates a managed String and copies all characters up to the first null character or the end of the allocated memory pool from a string stored in unmanaged memory into it.
/// </summary>
/// <param name="ptr">The address of the first character.</param>
/// <param name="charSet">The character set of the string.</param>
/// <returns>A managed string that holds a copy of the unmanaged string if the value of the <paramref name="ptr"/> parameter is not null; otherwise, this method returns null.</returns>
public static string GetString(IntPtr ptr, CharSet charSet = CharSet.Auto) => IsValue(ptr) ? null : (charSet == CharSet.Auto ? Marshal.PtrToStringAuto(ptr) : (charSet == CharSet.Unicode ? Marshal.PtrToStringUni(ptr) : Marshal.PtrToStringAnsi(ptr)));
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>
/// A managed string that holds a copy of the unmanaged string if the value of the <paramref name="ptr"/> parameter is not null;
/// otherwise, this method returns null.
/// </returns>
public static string GetString(IntPtr ptr, CharSet charSet = CharSet.Auto, long allocatedBytes = long.MaxValue)
{
if (IsValue(ptr)) return null;
var sb = new System.Text.StringBuilder();
unsafe
{
var chkLen = 0L;
if (GetCharSize(charSet) == 1)
{
for (var uptr = (byte*)ptr; chkLen < allocatedBytes && *uptr != 0; chkLen++, uptr++)
sb.Append((char)*uptr);
}
else
{
for (var uptr = (ushort*)ptr; chkLen < allocatedBytes && *uptr != 0; chkLen += 2, uptr++)
sb.Append((char)*uptr);
}
}
return sb.ToString();
}
/// <summary>
/// Allocates a managed String and copies all characters up to the first null character or at most <paramref name="length"/> characters from a string stored in unmanaged memory into it.
@ -155,8 +190,7 @@ namespace Vanara.Extensions
/// A managed string that holds a copy of the unmanaged string if the value of the <paramref name="ptr"/> parameter is not null;
/// otherwise, this method returns null.
/// </returns>
public static string GetString(IntPtr ptr, int length, CharSet charSet = CharSet.Auto) =>
IsValue(ptr) ? null : (charSet == CharSet.Auto ? Marshal.PtrToStringAuto(ptr, length) : (charSet == CharSet.Unicode ? Marshal.PtrToStringUni(ptr, length) : Marshal.PtrToStringAnsi(ptr, length)));
public static string GetString(IntPtr ptr, int length, CharSet charSet = CharSet.Auto) => GetString(ptr, charSet, length * GetCharSize(charSet));
/// <summary>Refreshes the memory block from the unmanaged COM task allocator and copies the contents of a new managed String.</summary>
/// <param name="ptr">The address of the first character.</param>

View File

@ -87,5 +87,28 @@ namespace Vanara.Extensions.Tests
{
Assert.That(StringHelper.GetByteCount(value, nullTerm, cs), Is.EqualTo(ret));
}
[TestCase("BOO", CharSet.Ansi)]
[TestCase("BOO", CharSet.Unicode)]
[TestCase("", CharSet.Ansi)]
[TestCase("", CharSet.Unicode)]
[TestCase(null, CharSet.Ansi)]
[TestCase(null, CharSet.Unicode)]
public void GetStringTest1(string value, CharSet cs)
{
IntPtr ptr = default;
try
{
ptr = StringHelper.AllocString(value, cs, Marshal.AllocCoTaskMem, out var count);
Assert.That(count, Is.EqualTo((value?.Length + 1) * StringHelper.GetCharSize(cs) ?? 0));
Assert.That(StringHelper.GetString(ptr, cs), Is.EqualTo(value));
Assert.That(StringHelper.GetString(ptr, cs, count * 2), Is.EqualTo(value));
Assert.That(StringHelper.GetString(ptr, cs, count / 2), Is.EqualTo(string.IsNullOrEmpty(value) ? value : value.Substring(0, (value.Length + 1) / 2)));
}
finally
{
Marshal.FreeCoTaskMem(ptr);
}
}
}
}