Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using System.Runtime.InteropServices;

namespace CoreBluetooth.Foundation
{
public class NSString : IDisposable
{
internal SafeNSStringHandle Handle { get; private set; }

public NSString(string str)
{
if (str is null)
throw new ArgumentNullException(nameof(str));

Handle = NativeMethods.ns_string_new(str);
}

internal NSString(SafeNSStringHandle handle)
{
Handle = handle;
}

internal NSString(IntPtr handle)
{
Handle = new SafeNSStringHandle(handle);
}

public int LengthOfBytesUtf8()
{
ExceptionUtils.ThrowObjectDisposedExceptionIf(Handle.IsInvalid, this);
return NativeMethods.ns_string_length_of_bytes_utf8(Handle);
}

public override string ToString()
{
ExceptionUtils.ThrowObjectDisposedExceptionIf(Handle.IsInvalid, this);
return HandleToString(Handle);
}

public void Dispose()
{
if (Handle != null && !Handle.IsInvalid)
Handle.Dispose();
}

internal static string HandleToString(SafeNSStringHandle handle)
{
if (handle.IsInvalid)
return null;

NativeMethods.ns_string_get_cstring_and_length(handle, out IntPtr ptr, out int length);
if (ptr == IntPtr.Zero)
return null;

if (length == 0)
return string.Empty;

return Marshal.PtrToStringUTF8(ptr, length);
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,14 @@ internal static class NativeMethods

[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
internal static extern int ns_number_int_value(SafeNSNumberHandle handle);

[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
internal static extern SafeNSStringHandle ns_string_new([MarshalAs(UnmanagedType.LPStr)] string str);

[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
internal static extern int ns_string_length_of_bytes_utf8(SafeNSStringHandle handle);

[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
internal static extern void ns_string_get_cstring_and_length(SafeNSStringHandle handle, out IntPtr ptr, out int length);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;

namespace CoreBluetooth.Foundation
{
internal class SafeNSStringHandle : SafeNSObjectHandle
{
public SafeNSStringHandle() : base() { }
public SafeNSStringHandle(IntPtr handle) : base(handle) { }
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using CoreBluetooth.Foundation;
using NUnit.Framework;

namespace CoreBluetoothTests.Foundation
{
public class NSStringTests
{
[Test]
public void New()
{
using var nsString = new NSString("dummy");
Assert.That(nsString.Handle, Is.Not.Null);
Assert.That(nsString.Handle.IsInvalid, Is.False);
}

[Test]
public void LengthOfBytesUtf8()
{
using (var nsString = new NSString("a"))
{
Assert.That(nsString.LengthOfBytesUtf8(), Is.EqualTo(1));
}

using (var nsString = new NSString("あ"))
{
Assert.That(nsString.LengthOfBytesUtf8(), Is.EqualTo(3));
}

using (var nsString = new NSString("𠮷"))
{
Assert.That(nsString.LengthOfBytesUtf8(), Is.EqualTo(4));
}
}

[Test]
public void HandleToString()
{
using (var nsString = new NSString("dummy"))
{
Assert.That(NSString.HandleToString(nsString.Handle), Is.EqualTo("dummy"));
}

using (var nsString = new NSString("あいうえお"))
{
Assert.That(NSString.HandleToString(nsString.Handle), Is.EqualTo("あいうえお"));
}

using (var nsString = new NSString(string.Empty))
{
Assert.That(NSString.HandleToString(nsString.Handle), Is.EqualTo(string.Empty));
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,27 @@ public func ns_number_int_value(_ handle: UnsafeRawPointer) -> Int32 {
let instance = Unmanaged<NSNumber>.fromOpaque(handle).takeUnretainedValue()
return instance.int32Value
}

@_cdecl("ns_string_new")
public func ns_string_new(_ str: UnsafePointer<CChar>) -> UnsafeMutableRawPointer {
let nsstring = NSString(utf8String: str)!
return Unmanaged.passRetained(nsstring).toOpaque()
}

@_cdecl("ns_string_length_of_bytes_utf8")
public func ns_string_length_of_bytes_utf8(_ handle: UnsafeRawPointer) -> Int32 {
let nsstring = Unmanaged<NSString>.fromOpaque(handle).takeUnretainedValue()
return Int32(nsstring.lengthOfBytes(using: String.Encoding.utf8.rawValue))
}

@_cdecl("ns_string_get_cstring_and_length")
public func ns_string_get_cstring_and_length(_ handle: UnsafeRawPointer, _ ptr: UnsafeMutablePointer<UnsafePointer<CChar>?>, _ length: UnsafeMutablePointer<Int32>) {
let nsstring = Unmanaged<NSString>.fromOpaque(handle).takeUnretainedValue()
if let cstring = nsstring.utf8String {
ptr.pointee = UnsafePointer(cstring)
length.pointee = Int32(strlen(cstring))
} else {
ptr.pointee = nil
length.pointee = 0
}
}