FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

[Testing] tweaks to data reading in Ole files by Numpsy · Pull Request #420 · openmcdf/openmcdf · GitHub

[Testing] tweaks to data reading in Ole files - #420

Draft
Numpsy wants to merge 6 commits into
openmcdf:mainfrom
Numpsy:property_reading_tweaks
Draft

Numpsy wants to merge 6 commits into
openmcdf:mainfrom
Numpsy:property_reading_tweaks

Conversation

Numpsy commented Apr 21, 2026 •
edited
Loading

Copy link
Copy Markdown
Contributor

Some musings based on comments in #409 and #411

Just a couple of places for testing, could be done elsewhere -

  • Add a SkipPadding extension which uses ReadExactly instead of ReadBytes so that it fails when there isn't enough data, and uses stackalloc to avoid allocating lots of tiny arrays.
  • Add a ReadGuid extension which uses ReadExactly so that it fails when there isn't enough data, and uses stackalloc on .NET core (where Guid has a constructor which takes a Span)
  • Add some ReadString extensions which use ReadExactly so that it fails when there isn't enough data, deduplicates a bit of the null terminator handling logic, and uses stackalloc for small length strings.
  • Passes the Encodings to use around instead of getting them continually. This is faster, though does make VT_LPSTR_Property instances use more memory.

Most of the complexity is pollyfilling Span based functions in .NET Standard and that can be tuned if this goes anywhere, and the same changes could be done elsewhere as well.

So, this is a combination of correctness (using ReadExactly instead of ReadBytes which might return less data than asked for), and performance (removes some allocations, removes some duplicate work)

Comment thread OpenMcdf.Ole/DictionaryProperty.cs Outdated
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows')) == false">net10.0</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">net48;net10.0-windows</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows')) == false">net10.0;net8.0</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">net48;net10.0-windows;net8.0-windows</TargetFrameworks>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Just added .NET 8 for testing because BinaryReader.ReadExactly isn't built into .NET 8 like the Stream equivalent is

Comment thread OpenMcdf.Ole/StreamExtensions.cs Outdated
#if !NET
private static int Read(this Stream target, Span<byte> buffer)
{
var sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Borrowed from PollyFill to get things building on .NET Standard

Numpsy commented Apr 22, 2026 •
edited
Loading

Copy link
Copy Markdown
Contributor Author

Before:

| Method                                   | Mean     | Error     | StdDev    | Gen0   | Allocated |
|----------------------------------------- |---------:|----------:|----------:|-------:|----------:|
| ReadSummaryInformation                   | 2.632 us | 0.0759 us | 0.0042 us | 0.1678 |   2.79 KB |
| ReadDocumentSummaryInformation           | 5.589 us | 0.5313 us | 0.0291 us | 0.2899 |    4.8 KB |
| ReadWinUnicodeDocumentSummaryInformation | 4.634 us | 0.3511 us | 0.0192 us | 0.3510 |   5.73 KB |

After:

| Method                                   | Mean     | Error     | StdDev    | Gen0   | Allocated |
|----------------------------------------- |---------:|----------:|----------:|-------:|----------:|
| ReadSummaryInformation                   | 2.598 us | 0.0834 us | 0.0046 us | 0.1526 |   2.55 KB |
| ReadDocumentSummaryInformation           | 4.123 us | 0.0845 us | 0.0046 us | 0.2441 |   4.11 KB |
| ReadWinUnicodeDocumentSummaryInformation | 4.073 us | 0.0664 us | 0.0036 us | 0.2823 |    4.7 KB |

Numpsy force-pushed the property_reading_tweaks branch from a8abc8c to 0a39349 Compare April 23, 2026 19:07
Numpsy changed the title [Testing] tweaks to string reading [Testing] tweaks to data reading in Ole files Apr 23, 2026
Numpsy force-pushed the property_reading_tweaks branch from dd42db9 to c1cc7c2 Compare April 28, 2026 08:29
Comment thread OpenMcdf.Ole/BinaryReaderExtensions.cs Outdated

// Read a null terminated string from the specified BinaryReader, using the specified length and codepage
// Note: Encoding.GetEncoding seems to actually be quite slow, so allow callers that already have the Encoding to provide it directly.
public static string ReadNullTerminatedStringWithEncoding(this BinaryReader target, int byteLength, int codePage, Encoding encoding)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

int codePage is a redundant parameter since it's specified by Encoding - it's not validated and so prone to usage error.

byte[] nameBytes = new byte[byteLength];
target.ReadExactly(nameBytes.AsSpan());
#else
// @@TBD@@ What max stack alloc size should be used here?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

My understanding is that stackalloc up to 1024 bytes is generally considered to be OK

Comment thread OpenMcdf.Ole/BinaryReaderExtensions.cs Outdated
#endif
}

public static string ReadNullTerminatedWideString(this BinaryReader target, int characterLength) => target.ReadNullTerminatedStringWithEncoding(byteLength: characterLength * 2, CodePages.WinUnicode, Encoding.Unicode);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

There are more encodings that are wide than just WinUnicode (even if they're technically not allowed by OLE). The API here is also open to misuse wrt byte vs char vs code point length (you only know from the parameter name). Probably better if there was just one method that took the encoding along with the code point length or better still just read the length itself.

Comment thread OpenMcdf.Ole/CodePages.cs
// @@TODO@@ Is it worth special caseing UTF-8 as well?
internal static Encoding GetEncodingForCodePage(int codePage)
{
return codePage switch

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Special-casing common code pages for performance is OK, but the greater win here would be to introduce additional validation. There are code pages which will give invalid results from the encoder for OLE rather than throw an error.

Numpsy commented May 4, 2026 •
edited
Loading

Copy link
Copy Markdown
Contributor Author

I'll get back to this next week, the HWP related stuff is something I could do with sorting first.
Also might just break the ReadGuid stuff out into another PR as it's unrelated to code pages and such

Numpsy added 6 commits May 4, 2026 11:26
Just a couple of places for testing, could be done elsewhere -
- Use ReadExactly for reading dictionary strings and padding
- Use ReadExactly with stackalloc for skipping padding, to remove some allocations

Most of the complexity is pollyfilling Span based functions in .NET Standard - it's be much simpler just for .NET Core
Recuces memory allocations, and improves performance
…perty accessors don't need to look it up themselves.

This branch has not been deployed

No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants


Back | FazBrowse Home | New Git URL