Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
bb13039
Fix singleton cctor cycle (#3817) by replacing pre-registration with …
ramezgerges May 26, 2026
2972aa5
Restore [ModuleInitializer] for the native-library version check only
ramezgerges May 26, 2026
a9b145b
Narrow promote-existing race by setting IgnorePublicDispose under the…
ramezgerges May 26, 2026
707ee98
Remove incorrect SKPath stale-HD-entry comment
ramezgerges May 26, 2026
e782f52
Make IgnorePublicDispose a one-way latch via private setter
ramezgerges May 26, 2026
008f4bf
Eager-init SKFontStyle singletons via cctor
ramezgerges May 26, 2026
0b01719
Atomicize singleton init via recursive HD lock; rename immortal→dispo…
ramezgerges May 27, 2026
a75db47
Document race-safety reasoning; drop unreliable #3817 repro test
ramezgerges May 27, 2026
19df400
Migrate remaining manual PreventPublicDisposal callers to GetDisposeP…
ramezgerges May 28, 2026
dcd2b1c
Drop redundant write lock from PreventPublicDisposal
ramezgerges May 28, 2026
8082219
Drop LockRecursionPolicy.SupportsRecursion (no longer needed)
ramezgerges May 28, 2026
8153b72
Add SKSingletonInitTest covering the new dispose-protected init mecha…
ramezgerges May 28, 2026
efac0e6
Harden dispose-protected guard, fix comment accuracy, add interleavin…
mattleibow May 29, 2026
3f1b129
Fix stream-callback NREs: restore RevokeOwnership
ramezgerges May 31, 2026
adc4c42
Add standalone pure-process singleton-init regression test
mattleibow Jun 2, 2026
18fba73
Add concurrency/lifecycle corner-case tests for the singleton rework
mattleibow Jun 2, 2026
535048a
Add re-entrancy contract tests for HandleDictionary.GetOrAddObject
mattleibow Jun 2, 2026
3ff3e5c
Guard AssemblyLoadContext singleton test for .NET Framework
mattleibow Jun 2, 2026
005db38
Fix indentation in HandleDictionary re-entrancy test
mattleibow Jun 2, 2026
8809e45
Guard concurrency tests for single-threaded WASM and 32-bit targets
mattleibow Jun 3, 2026
4a4b604
Harden concurrency test joins and cold-start rethrow
mattleibow Jun 3, 2026
73c4c3b
Make concurrency test helpers net48-compatible
mattleibow Jun 3, 2026
62ab1c9
Make singleton/lifecycle tests robust on the Mono interpreter (device…
mattleibow Jun 3, 2026
0804354
Isolate threading and refcount-sensitive tests into a serialized coll…
mattleibow Jun 3, 2026
1445975
Hold native-wrapper arguments alive across P/Invoke calls
ramezgerges Jun 9, 2026
ddada70
Keep 'this' alive across instance P/Invoke calls (1C lifetime hardening)
ramezgerges Jun 9, 2026
a260adb
Extend lifetime KeepAlives to Skottie/SceneGraph/Resources bindings
ramezgerges Jun 9, 2026
14f9a29
Fix teardown heap corruption: never unref immortal Skia singletons
ramezgerges Jun 9, 2026
52d8a46
Skip PR #4080 use-after-free test on single-threaded WASM
ramezgerges Jun 10, 2026
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
32 changes: 28 additions & 4 deletions binding/HarfBuzzSharp/Blob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,49 @@ protected override void DisposeHandler ()
}
}

public int Length => (int)HarfBuzzApi.hb_blob_get_length (Handle);
public int Length {
get {
var r = (int)HarfBuzzApi.hb_blob_get_length (Handle);
GC.KeepAlive (this);
return r;
}
}

public int FaceCount => (int)HarfBuzzApi.hb_face_count (Handle);
public int FaceCount {
get {
var r = (int)HarfBuzzApi.hb_face_count (Handle);
GC.KeepAlive (this);
return r;
}
}

public bool IsImmutable => HarfBuzzApi.hb_blob_is_immutable (Handle);
public bool IsImmutable {
get {
var r = HarfBuzzApi.hb_blob_is_immutable (Handle);
GC.KeepAlive (this);
return r;
}
}

public void MakeImmutable () => HarfBuzzApi.hb_blob_make_immutable (Handle);
public void MakeImmutable ()
{
HarfBuzzApi.hb_blob_make_immutable (Handle);
GC.KeepAlive (this);
}

public unsafe Stream AsStream ()
{
uint length;
var dataPtr = HarfBuzzApi.hb_blob_get_data (Handle, &length);
GC.KeepAlive (this);
return new UnmanagedMemoryStream ((byte*)dataPtr, length);
}

public unsafe Span<byte> AsSpan ()
{
uint length;
var dataPtr = HarfBuzzApi.hb_blob_get_data (Handle, &length);
GC.KeepAlive (this);
return new Span<byte> (dataPtr, (int)length);
}

Expand Down
157 changes: 132 additions & 25 deletions binding/HarfBuzzSharp/Buffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,53 +22,125 @@ public Buffer ()
}

public ContentType ContentType {
get => HarfBuzzApi.hb_buffer_get_content_type (Handle);
set => HarfBuzzApi.hb_buffer_set_content_type (Handle, value);
get {
var r = HarfBuzzApi.hb_buffer_get_content_type (Handle);
GC.KeepAlive (this);
return r;
}
set {
HarfBuzzApi.hb_buffer_set_content_type (Handle, value);
GC.KeepAlive (this);
}
}

public Direction Direction {
get => HarfBuzzApi.hb_buffer_get_direction (Handle);
set => HarfBuzzApi.hb_buffer_set_direction (Handle, value);
get {
var r = HarfBuzzApi.hb_buffer_get_direction (Handle);
GC.KeepAlive (this);
return r;
}
set {
HarfBuzzApi.hb_buffer_set_direction (Handle, value);
GC.KeepAlive (this);
}
}

public Language Language {
get => new Language (HarfBuzzApi.hb_buffer_get_language (Handle));
set => HarfBuzzApi.hb_buffer_set_language (Handle, value.Handle);
get {
var r = new Language (HarfBuzzApi.hb_buffer_get_language (Handle));
GC.KeepAlive (this);
return r;
}
set {
HarfBuzzApi.hb_buffer_set_language (Handle, value.Handle);
GC.KeepAlive (value);
GC.KeepAlive (this);
}
}

public BufferFlags Flags {
get => HarfBuzzApi.hb_buffer_get_flags (Handle);
set => HarfBuzzApi.hb_buffer_set_flags (Handle, value);
get {
var r = HarfBuzzApi.hb_buffer_get_flags (Handle);
GC.KeepAlive (this);
return r;
}
set {
HarfBuzzApi.hb_buffer_set_flags (Handle, value);
GC.KeepAlive (this);
}
}

public ClusterLevel ClusterLevel {
get => HarfBuzzApi.hb_buffer_get_cluster_level (Handle);
set => HarfBuzzApi.hb_buffer_set_cluster_level (Handle, value);
get {
var r = HarfBuzzApi.hb_buffer_get_cluster_level (Handle);
GC.KeepAlive (this);
return r;
}
set {
HarfBuzzApi.hb_buffer_set_cluster_level (Handle, value);
GC.KeepAlive (this);
}
}

public uint ReplacementCodepoint {
get => HarfBuzzApi.hb_buffer_get_replacement_codepoint (Handle);
set => HarfBuzzApi.hb_buffer_set_replacement_codepoint (Handle, value);
get {
var r = HarfBuzzApi.hb_buffer_get_replacement_codepoint (Handle);
GC.KeepAlive (this);
return r;
}
set {
HarfBuzzApi.hb_buffer_set_replacement_codepoint (Handle, value);
GC.KeepAlive (this);
}
}

public uint InvisibleGlyph {
get => HarfBuzzApi.hb_buffer_get_invisible_glyph (Handle);
set => HarfBuzzApi.hb_buffer_set_invisible_glyph (Handle, value);
get {
var r = HarfBuzzApi.hb_buffer_get_invisible_glyph (Handle);
GC.KeepAlive (this);
return r;
}
set {
HarfBuzzApi.hb_buffer_set_invisible_glyph (Handle, value);
GC.KeepAlive (this);
}
}

public Script Script {
get => HarfBuzzApi.hb_buffer_get_script (Handle);
set => HarfBuzzApi.hb_buffer_set_script (Handle, value);
get {
var r = HarfBuzzApi.hb_buffer_get_script (Handle);
GC.KeepAlive (this);
return r;
}
set {
HarfBuzzApi.hb_buffer_set_script (Handle, value);
GC.KeepAlive (this);
}
}

public int Length {
get => (int)HarfBuzzApi.hb_buffer_get_length (Handle);
set => HarfBuzzApi.hb_buffer_set_length (Handle, (uint)value);
get {
var r = (int)HarfBuzzApi.hb_buffer_get_length (Handle);
GC.KeepAlive (this);
return r;
}
set {
HarfBuzzApi.hb_buffer_set_length (Handle, (uint)value);
GC.KeepAlive (this);
}
}

public UnicodeFunctions UnicodeFunctions {
get => new UnicodeFunctions (HarfBuzzApi.hb_buffer_get_unicode_funcs (Handle));
set => HarfBuzzApi.hb_buffer_set_unicode_funcs (Handle, value.Handle);
get {
var r = new UnicodeFunctions (HarfBuzzApi.hb_buffer_get_unicode_funcs (Handle));
GC.KeepAlive (this);
return r;
}
set {
HarfBuzzApi.hb_buffer_set_unicode_funcs (Handle, value.Handle);
GC.KeepAlive (value);
GC.KeepAlive (this);
}
}

public GlyphInfo[] GlyphInfos {
Expand Down Expand Up @@ -97,6 +169,7 @@ public void Add (uint codepoint, uint cluster)
throw new InvalidOperationException ("ContentType must not be of type Glyphs");

HarfBuzzApi.hb_buffer_add (Handle, codepoint, cluster);
GC.KeepAlive (this);
}

public void AddUtf8 (string utf8text) => AddUtf8 (Encoding.UTF8.GetBytes (utf8text), 0, -1);
Expand Down Expand Up @@ -124,6 +197,7 @@ public void AddUtf8 (IntPtr text, int textLength, int itemOffset, int itemLength
throw new InvalidOperationException ("ContentType must not be Glyphs");

HarfBuzzApi.hb_buffer_add_utf8 (Handle, (void*)text, textLength, (uint)itemOffset, itemLength);
GC.KeepAlive (this);
}

public void AddUtf16 (string text) => AddUtf16 (text, 0, -1);
Expand Down Expand Up @@ -164,6 +238,7 @@ public void AddUtf16 (IntPtr text, int textLength, int itemOffset, int itemLengt
throw new InvalidOperationException ("ContentType must not be of type Glyphs");

HarfBuzzApi.hb_buffer_add_utf16 (Handle, (ushort*)text, textLength, (uint)itemOffset, itemLength);
GC.KeepAlive (this);
}

public void AddUtf32 (string text) => AddUtf32 (Encoding.UTF32.GetBytes (text));
Expand Down Expand Up @@ -206,6 +281,7 @@ public void AddUtf32 (IntPtr text, int textLength, int itemOffset, int itemLengt
throw new InvalidOperationException ("ContentType must not be of type Glyphs");

HarfBuzzApi.hb_buffer_add_utf32 (Handle, (uint*)text, textLength, (uint)itemOffset, itemLength);
GC.KeepAlive (this);
}

public void AddCodepoints (ReadOnlySpan<uint> text) => AddCodepoints (text, 0, -1);
Expand Down Expand Up @@ -238,19 +314,22 @@ public void AddCodepoints (IntPtr text, int textLength, int itemOffset, int item
throw new InvalidOperationException ("ContentType must not be of type Glyphs");

HarfBuzzApi.hb_buffer_add_codepoints (Handle, (uint*)text, textLength, (uint)itemOffset, itemLength);
GC.KeepAlive (this);
}

public unsafe ReadOnlySpan<GlyphInfo> GetGlyphInfoSpan ()
{
uint length;
var infoPtrs = HarfBuzzApi.hb_buffer_get_glyph_infos (Handle, &length);
GC.KeepAlive (this);
return new ReadOnlySpan<GlyphInfo> (infoPtrs, (int)length);
}

public unsafe ReadOnlySpan<GlyphPosition> GetGlyphPositionSpan ()
{
uint length;
var infoPtrs = HarfBuzzApi.hb_buffer_get_glyph_positions (Handle, &length);
GC.KeepAlive (this);
return new ReadOnlySpan<GlyphPosition> (infoPtrs, (int)length);
}

Expand All @@ -260,11 +339,20 @@ public void GuessSegmentProperties ()
throw new InvalidOperationException ("ContentType must be of type Unicode.");

HarfBuzzApi.hb_buffer_guess_segment_properties (Handle);
GC.KeepAlive (this);
}

public void ClearContents () => HarfBuzzApi.hb_buffer_clear_contents (Handle);
public void ClearContents ()
{
HarfBuzzApi.hb_buffer_clear_contents (Handle);
GC.KeepAlive (this);
}

public void Reset () => HarfBuzzApi.hb_buffer_reset (Handle);
public void Reset ()
{
HarfBuzzApi.hb_buffer_reset (Handle);
GC.KeepAlive (this);
}

public void Append (Buffer buffer) => Append (buffer, 0, -1);

Expand All @@ -276,6 +364,8 @@ public void Append (Buffer buffer, int start, int end)
throw new InvalidOperationException ("ContentType must be of same type.");

HarfBuzzApi.hb_buffer_append (Handle, buffer.Handle, (uint)start, (uint)(end == -1 ? buffer.Length : end));
GC.KeepAlive (buffer);
GC.KeepAlive (this);
}

public void NormalizeGlyphs ()
Expand All @@ -286,14 +376,26 @@ public void NormalizeGlyphs ()
throw new InvalidOperationException ("GlyphPositions can't be empty.");

HarfBuzzApi.hb_buffer_normalize_glyphs (Handle);
GC.KeepAlive (this);
}

public void Reverse () => HarfBuzzApi.hb_buffer_reverse (Handle);
public void Reverse ()
{
HarfBuzzApi.hb_buffer_reverse (Handle);
GC.KeepAlive (this);
}

public void ReverseRange (int start, int end) =>
public void ReverseRange (int start, int end)
{
HarfBuzzApi.hb_buffer_reverse_range (Handle, (uint)start, (uint)(end == -1 ? Length : end));
GC.KeepAlive (this);
}

public void ReverseClusters () => HarfBuzzApi.hb_buffer_reverse_clusters (Handle);
public void ReverseClusters ()
{
HarfBuzzApi.hb_buffer_reverse_clusters (Handle);
GC.KeepAlive (this);
}

public string SerializeGlyphs () =>
SerializeGlyphs (0, -1, null, SerializeFormat.Text, SerializeFlag.Default);
Expand Down Expand Up @@ -340,6 +442,9 @@ public unsafe string SerializeGlyphs (int start, int end, Font font, SerializeFo
builder.Append (Marshal.PtrToStringAnsi ((IntPtr)pinned.Pointer, (int)consumed));
}

GC.KeepAlive (font);
GC.KeepAlive (this);

return builder.ToString ();
}

Expand All @@ -357,6 +462,8 @@ public void DeserializeGlyphs (string data, Font font, SerializeFormat format)
throw new InvalidOperationException ("ContentType must not be Glyphs.");

HarfBuzzApi.hb_buffer_deserialize_glyphs (Handle, data, -1, null, font?.Handle ?? IntPtr.Zero, format);
GC.KeepAlive (font);
GC.KeepAlive (this);
}

protected override void Dispose (bool disposing) =>
Expand Down
Loading
Loading