Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make System.Formats.Nrbf public #103232

Merged
merged 23 commits into from
Jun 14, 2024
Merged

Make System.Formats.Nrbf public #103232

merged 23 commits into from
Jun 14, 2024

Conversation

adamsitnik
Copy link
Member

@adamsitnik adamsitnik commented Jun 10, 2024

Changes:

  • apply the new library name: System.Formats.Nrbf
    • rename the folders, project and solution files
    • rename the namespace
    • set IsPackable to true and reference the project, not the source files
  • rename PayloadReader to NrbfDecoder, ArrayRecord<T> to SZArrayRecord<T> and Read to Decode
  • remove ClassRecord.GetArrayOfPrimitiveType, add GetArrayRecord
  • introduce non-generic PrimitiveTypeRecord, use it where it simplifies the code
  • introduce SerializationRecordId, use it
  • make BinaryArrayType internal, remove support for custom offset arrays
  • expose ClassRecord.TypeName
  • rename IsTypeNameMatching to TypeNameMatches, make it non-virtual and ignore assembly names
  • rename RecordType to SerializationRecordType
  • remove all the SerializationRecordType values that can not be used by the user
  • make SerializationRecordType : int, not SerializationRecordType : byte
  • add ref project

fixes #102014

I am going to try to add ArrayRecord.TotalElementsCount in a separate PR, as I need to verify how to implement it for jagged arrays and get the name approved

- rename the folders, project and solution files
- rename the namespace
- set IsPackable to true and reference the project, not the source files
- PayloadReader -> NrbfDecoder
- ArrayRecord<T> -> SZArrayRecord<T>
- Read -> Decode
@adamsitnik adamsitnik added the binaryformatter-migration Issues related to the removal of BinaryFormatter and migrations away from it label Jun 10, 2024
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-security, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

- introduce SerializationRecord.TypeName, remove ArrayRecord.ElementType
- rename IsTypeNameMatching to TypeNameMatches, make it non-virtual and ignore assembly names
- remove type-forwarding logic, use it only for testing

cc @jkotas
…nstraint, re-add all values present in the NRBF spec
{
int id = reader.ReadInt32();

if (id == 0)
Copy link
Member Author

Choose a reason for hiding this comment

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

0 is the only illegal value. It allowed me to avoid the need of introducing Null property, as all IsNull checks are now Equals(default)

I know it's less pretty, but this is the API that got approved and TBH 99.99% of users should not use it at all.

_ => throw new NotSupportedException(),
};

[RequiresUnreferencedCode("Calls System.Windows.Forms.BinaryFormat.BinaryFormattedObject.TypeResolver.GetType(TypeName)")]
internal static Array? GetSimpleBinaryArray(ArrayRecord arrayRecord, BinaryFormattedObject.ITypeResolver typeResolver)
{
if (arrayRecord.ArrayType is not (BinaryArrayType.Single or BinaryArrayType.Jagged or BinaryArrayType.Rectangular))
Copy link
Member Author

Choose a reason for hiding this comment

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

these types are now simply rejected by the decoder itself

@adamsitnik adamsitnik marked this pull request as ready for review June 11, 2024 17:07

// Keeping a separate stack for ids for fast infinite loop checks.
private readonly Stack<int> _parseStack = [];
private readonly Stack<SerializationRecordId> _parseStack = [];
Copy link
Member

Choose a reason for hiding this comment

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

This will significantly hurt performance for deep graphs if SerializationRecordId is not RuntimeHelpers.IsBitwiseEquatable<T>(). If it isn't, or can't be made so, we should allow getting the raw value.

Copy link
Member Author

Choose a reason for hiding this comment

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

PTAL at daa7431, I think I was able to remove the need for having this separate stack

Copy link
Member

Choose a reason for hiding this comment

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

I think you can just throw there.

/// <summary>
/// The ID of <see cref="SerializationRecord" />.
/// </summary>
public readonly struct SerializationRecordId : IEquatable<SerializationRecordId>
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

@adamsitnik, @GrabYourPitchforks overriding GetHashCode or Equals makes this id not bitwise equatable from the runtime's perspective. This makes searching for existing ids in arrays suboptimal. Is there a reason we can't expose the underlying value?

Copy link
Member Author

Choose a reason for hiding this comment

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

This makes searching for existing ids in arrays suboptimal.

But with my latest changes (daa7431) we don't need to do that anymore?

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

overriding GetHashCode or Equals makes this id not bitwise equatable from the runtime's perspective

That just because we have not implemented general bitwise equatable optimization in the runtime. I would be best to avoid introducing workarounds for that in public API designs. Instead, add ref-counts to the general bitwise equatable.

Copy link
Member

Choose a reason for hiding this comment

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

@adamsitnik we don't for that specific usage, but other usages would hurt.

@jkotas I'm not sure what you mean? Can you point out the general bitwise equatable?

Copy link
Member

Choose a reason for hiding this comment

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

The issue that I have linked in my comment has the design discussion about it.

Copy link
Member

Choose a reason for hiding this comment

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

@jkotas, sorry I misunderstood your comment. I thought there was some sort of internal back door, not that we needed to add links to the existing issue. :)

@jkotas
Copy link
Member

jkotas commented Jun 13, 2024

I do not have any more comments, but I have not done thorough review and I am not intimately familiar with NRBF details to sign-off.

Copy link
Member

@JeremyKuhne JeremyKuhne left a comment

Choose a reason for hiding this comment

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

I have no more blocking comments. The deserializer code looks good.

@adamsitnik
Copy link
Member Author

adamsitnik commented Jun 13, 2024

@ericstj @carlossanlop one of the CI legs keeps failing with the following error (and it's the last error so it's blocking me from merging this PR):

❌artifacts\bin\testPackages\projects\System.Resources.Extensions\project.csproj(0,0): error NU1101: Unable to find package System.Formats.Nrbf. No packages exist with this id in source(s): D:\a\_work\1\s\artifacts\packages\Debug\, dotnet-eng, dotnet-libraries, dotnet-libraries-transport, dotnet-public, dotnet-tools, dotnet9, dotnet9-transport

System.Resources.Extensions is an existing package, so far it was referencing this code via links to source files, now it's going to just reference the new package: System.Formats.Nrbf

Is it some kind of a chicken and egg problem? How do I fix it? My current guess is to add sth similar to https://github.com/dotnet/runtime/blob/main/src/libraries/testPackages/packageSettings/System.Windows.Extensions/settings.targets

but it's just a guess.

FWIW the magic repro command:

.\build.cmd -c Release -test -s tools+libs+libs.tests /p:TestAssemblies=false /p:TestPackages=true

@ericstj
Copy link
Member

ericstj commented Jun 13, 2024

For brand new packages you need to tell APICompat that a previous version doesn't exist. Do that by adding

<!-- Disabling baseline validation since this is a brand new package.
Once this package has shipped a stable version, the following line
should be removed in order to re-enable validation. -->
<DisablePackageBaselineValidation>true</DisablePackageBaselineValidation>

I'll make a suggestion that fixes it.

Also - you can reproduce this by running dotnet pack on your source project. That will build all configurations of it and test the production of a package. It's a good idea when adding a new package that you try this out and even experiment consuming your package in projects as a user would - just to double check that you're getting the behavior you expect. This is something that's possible with packages, but not with our big product.

Co-authored-by: Eric StJohn <ericstj@microsoft.com>
@adamsitnik
Copy link
Member Author

@ericstj thanks a lot!

@adamsitnik
Copy link
Member Author

The build analysis is red, despite the only failing test being known:

image

I'll try the bypass feature..

@adamsitnik
Copy link
Member Author

/ba-g There is only one test failure and it's known, so the Build Anslysis should be green.

@adamsitnik adamsitnik merged commit 121230d into dotnet:main Jun 14, 2024
81 of 83 checks passed
@ericstj
Copy link
Member

ericstj commented Jun 14, 2024

@ericstj thanks a lot!

No problem - I see this didn't add the Package.md - please make sure to do that in a follow up PR. I filed #103481 to track it.

@miloush
Copy link
Contributor

miloush commented Jun 27, 2024

System.Formats.Nrbf isn't a great ".NET like" name, it's not clear what the acronym stands for and it wouldn't be easy to find. Couldn't it just be BinaryFormat, RemotingBinaryFormat or something else that is not a 4 letter acronym?

Citing from General Naming Conventions
✔️ DO favor readability over brevity.
❌ DO NOT use any acronyms that are not widely accepted, and even if they are, only when necessary.

@bartonjs
Copy link
Member

The name "Nrbf" comes from the name of the spec: [MS-NRBF]. Just like we picked "Cbor" for the spec that called itself "CBOR" and "Cose" for the one calling itself "COSE".

@github-actions github-actions bot locked and limited conversation to collaborators Jul 28, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Formats.Nrbf binaryformatter-migration Issues related to the removal of BinaryFormatter and migrations away from it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

BinaryFormatter PayloadReader API
7 participants