Make System.Formats.Nrbf public - #103232

Merged
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic
Jun 14, 2024
Merged

Make System.Formats.Nrbf public#103232
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic

Conversation

@adamsitnik

@adamsitnikadamsitnik commented Jun 10, 2024

Copy link
Copy Markdown
Member

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
@adamsitnikadamsitnik added the binaryformatter-migration Issues related to the removal of BinaryFormatter and migrations away from it label Jun 10, 2024
@dotnet-policy-service

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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.

[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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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. :)

Comment threadsrc/libraries/System.Formats.Nrbf/ref/System.Formats.Nrbf.csproj Outdated
@jkotas

Copy link
Copy Markdown
Member

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.

@JeremyKuhneJeremyKuhne left a comment

Copy link
Copy Markdown
Member

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

adamsitnik commented Jun 13, 2024

Copy link
Copy Markdown
MemberAuthor

@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

ericstj commented Jun 13, 2024

Copy link
Copy Markdown
Member

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
Copy Markdown
MemberAuthor

@ericstj thanks a lot!

Comment threadsrc/libraries/System.Formats.Nrbf/src/System.Formats.Nrbf.csproj Outdated
@adamsitnik

Copy link
Copy Markdown
MemberAuthor

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

image

I'll try the bypass feature..

@adamsitnik

Copy link
Copy Markdown
MemberAuthor

/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:mainJun 14, 2024
@ericstj

Copy link
Copy Markdown
Member

@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
Copy Markdown
Contributor

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
Copy Markdown
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-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 28, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Formats.Nrbfbinaryformatter-migrationIssues 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

@adamsitnik@jkotas@ericstj@miloush@bartonjs@carlossanlop@JeremyKuhne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Make System.Formats.Nrbf public - #103232

Merged
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic
Jun 14, 2024
Merged

Make System.Formats.Nrbf public#103232
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic

Conversation

@adamsitnik

@adamsitnikadamsitnik commented Jun 10, 2024

Copy link
Copy Markdown
Member

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
@adamsitnikadamsitnik added the binaryformatter-migration Issues related to the removal of BinaryFormatter and migrations away from it label Jun 10, 2024
@dotnet-policy-service

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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.

[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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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. :)

Comment threadsrc/libraries/System.Formats.Nrbf/ref/System.Formats.Nrbf.csproj Outdated
@jkotas

Copy link
Copy Markdown
Member

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.

@JeremyKuhneJeremyKuhne left a comment

Copy link
Copy Markdown
Member

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

adamsitnik commented Jun 13, 2024

Copy link
Copy Markdown
MemberAuthor

@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

ericstj commented Jun 13, 2024

Copy link
Copy Markdown
Member

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
Copy Markdown
MemberAuthor

@ericstj thanks a lot!

Comment threadsrc/libraries/System.Formats.Nrbf/src/System.Formats.Nrbf.csproj Outdated
@adamsitnik

Copy link
Copy Markdown
MemberAuthor

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

image

I'll try the bypass feature..

@adamsitnik

Copy link
Copy Markdown
MemberAuthor

/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:mainJun 14, 2024
@ericstj

Copy link
Copy Markdown
Member

@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
Copy Markdown
Contributor

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
Copy Markdown
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-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 28, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Formats.Nrbfbinaryformatter-migrationIssues 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

@adamsitnik@jkotas@ericstj@miloush@bartonjs@carlossanlop@JeremyKuhne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Make System.Formats.Nrbf public - #103232

Merged
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic
Jun 14, 2024
Merged

Make System.Formats.Nrbf public#103232
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic

Conversation

@adamsitnik

@adamsitnikadamsitnik commented Jun 10, 2024

Copy link
Copy Markdown
Member

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
@adamsitnikadamsitnik added the binaryformatter-migration Issues related to the removal of BinaryFormatter and migrations away from it label Jun 10, 2024
@dotnet-policy-service

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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.

[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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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. :)

Comment threadsrc/libraries/System.Formats.Nrbf/ref/System.Formats.Nrbf.csproj Outdated
@jkotas

Copy link
Copy Markdown
Member

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.

@JeremyKuhneJeremyKuhne left a comment

Copy link
Copy Markdown
Member

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

adamsitnik commented Jun 13, 2024

Copy link
Copy Markdown
MemberAuthor

@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

ericstj commented Jun 13, 2024

Copy link
Copy Markdown
Member

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
Copy Markdown
MemberAuthor

@ericstj thanks a lot!

Comment threadsrc/libraries/System.Formats.Nrbf/src/System.Formats.Nrbf.csproj Outdated
@adamsitnik

Copy link
Copy Markdown
MemberAuthor

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

image

I'll try the bypass feature..

@adamsitnik

Copy link
Copy Markdown
MemberAuthor

/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:mainJun 14, 2024
@ericstj

Copy link
Copy Markdown
Member

@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
Copy Markdown
Contributor

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
Copy Markdown
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-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 28, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Formats.Nrbfbinaryformatter-migrationIssues 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

@adamsitnik@jkotas@ericstj@miloush@bartonjs@carlossanlop@JeremyKuhne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Make System.Formats.Nrbf public - #103232

Merged
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic
Jun 14, 2024
Merged

Make System.Formats.Nrbf public#103232
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic

Conversation

@adamsitnik

@adamsitnikadamsitnik commented Jun 10, 2024

Copy link
Copy Markdown
Member

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
@adamsitnikadamsitnik added the binaryformatter-migration Issues related to the removal of BinaryFormatter and migrations away from it label Jun 10, 2024
@dotnet-policy-service

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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.

[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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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. :)

Comment threadsrc/libraries/System.Formats.Nrbf/ref/System.Formats.Nrbf.csproj Outdated
@jkotas

Copy link
Copy Markdown
Member

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.

@JeremyKuhneJeremyKuhne left a comment

Copy link
Copy Markdown
Member

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

adamsitnik commented Jun 13, 2024

Copy link
Copy Markdown
MemberAuthor

@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

ericstj commented Jun 13, 2024

Copy link
Copy Markdown
Member

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
Copy Markdown
MemberAuthor

@ericstj thanks a lot!

Comment threadsrc/libraries/System.Formats.Nrbf/src/System.Formats.Nrbf.csproj Outdated
@adamsitnik

Copy link
Copy Markdown
MemberAuthor

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

image

I'll try the bypass feature..

@adamsitnik

Copy link
Copy Markdown
MemberAuthor

/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:mainJun 14, 2024
@ericstj

Copy link
Copy Markdown
Member

@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
Copy Markdown
Contributor

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
Copy Markdown
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-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 28, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Formats.Nrbfbinaryformatter-migrationIssues 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

@adamsitnik@jkotas@ericstj@miloush@bartonjs@carlossanlop@JeremyKuhne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Make System.Formats.Nrbf public - #103232

Merged
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic
Jun 14, 2024
Merged

Make System.Formats.Nrbf public#103232
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic

Conversation

@adamsitnik

@adamsitnikadamsitnik commented Jun 10, 2024

Copy link
Copy Markdown
Member

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
@adamsitnikadamsitnik added the binaryformatter-migration Issues related to the removal of BinaryFormatter and migrations away from it label Jun 10, 2024
@dotnet-policy-service

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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.

[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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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. :)

Comment threadsrc/libraries/System.Formats.Nrbf/ref/System.Formats.Nrbf.csproj Outdated
@jkotas

Copy link
Copy Markdown
Member

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.

@JeremyKuhneJeremyKuhne left a comment

Copy link
Copy Markdown
Member

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

adamsitnik commented Jun 13, 2024

Copy link
Copy Markdown
MemberAuthor

@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

ericstj commented Jun 13, 2024

Copy link
Copy Markdown
Member

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
Copy Markdown
MemberAuthor

@ericstj thanks a lot!

Comment threadsrc/libraries/System.Formats.Nrbf/src/System.Formats.Nrbf.csproj Outdated
@adamsitnik

Copy link
Copy Markdown
MemberAuthor

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

image

I'll try the bypass feature..

@adamsitnik

Copy link
Copy Markdown
MemberAuthor

/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:mainJun 14, 2024
@ericstj

Copy link
Copy Markdown
Member

@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
Copy Markdown
Contributor

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
Copy Markdown
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-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 28, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Formats.Nrbfbinaryformatter-migrationIssues 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

@adamsitnik@jkotas@ericstj@miloush@bartonjs@carlossanlop@JeremyKuhne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Make System.Formats.Nrbf public - #103232

Merged
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic
Jun 14, 2024
Merged

Make System.Formats.Nrbf public#103232
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic

Conversation

@adamsitnik

@adamsitnikadamsitnik commented Jun 10, 2024

Copy link
Copy Markdown
Member

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
@adamsitnikadamsitnik added the binaryformatter-migration Issues related to the removal of BinaryFormatter and migrations away from it label Jun 10, 2024
@dotnet-policy-service

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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.

[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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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. :)

Comment threadsrc/libraries/System.Formats.Nrbf/ref/System.Formats.Nrbf.csproj Outdated
@jkotas

Copy link
Copy Markdown
Member

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.

@JeremyKuhneJeremyKuhne left a comment

Copy link
Copy Markdown
Member

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

adamsitnik commented Jun 13, 2024

Copy link
Copy Markdown
MemberAuthor

@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

ericstj commented Jun 13, 2024

Copy link
Copy Markdown
Member

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
Copy Markdown
MemberAuthor

@ericstj thanks a lot!

Comment threadsrc/libraries/System.Formats.Nrbf/src/System.Formats.Nrbf.csproj Outdated
@adamsitnik

Copy link
Copy Markdown
MemberAuthor

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

image

I'll try the bypass feature..

@adamsitnik

Copy link
Copy Markdown
MemberAuthor

/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:mainJun 14, 2024
@ericstj

Copy link
Copy Markdown
Member

@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
Copy Markdown
Contributor

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
Copy Markdown
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-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 28, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Formats.Nrbfbinaryformatter-migrationIssues 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

@adamsitnik@jkotas@ericstj@miloush@bartonjs@carlossanlop@JeremyKuhne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Make System.Formats.Nrbf public - #103232

Merged
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic
Jun 14, 2024
Merged

Make System.Formats.Nrbf public#103232
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic

Conversation

@adamsitnik

@adamsitnikadamsitnik commented Jun 10, 2024

Copy link
Copy Markdown
Member

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
@adamsitnikadamsitnik added the binaryformatter-migration Issues related to the removal of BinaryFormatter and migrations away from it label Jun 10, 2024
@dotnet-policy-service

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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.

[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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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. :)

Comment threadsrc/libraries/System.Formats.Nrbf/ref/System.Formats.Nrbf.csproj Outdated
@jkotas

Copy link
Copy Markdown
Member

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.

@JeremyKuhneJeremyKuhne left a comment

Copy link
Copy Markdown
Member

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

adamsitnik commented Jun 13, 2024

Copy link
Copy Markdown
MemberAuthor

@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

ericstj commented Jun 13, 2024

Copy link
Copy Markdown
Member

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
Copy Markdown
MemberAuthor

@ericstj thanks a lot!

Comment threadsrc/libraries/System.Formats.Nrbf/src/System.Formats.Nrbf.csproj Outdated
@adamsitnik

Copy link
Copy Markdown
MemberAuthor

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

image

I'll try the bypass feature..

@adamsitnik

Copy link
Copy Markdown
MemberAuthor

/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:mainJun 14, 2024
@ericstj

Copy link
Copy Markdown
Member

@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
Copy Markdown
Contributor

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
Copy Markdown
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-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 28, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Formats.Nrbfbinaryformatter-migrationIssues 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

@adamsitnik@jkotas@ericstj@miloush@bartonjs@carlossanlop@JeremyKuhne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Make System.Formats.Nrbf public - #103232

Merged
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic
Jun 14, 2024
Merged

Make System.Formats.Nrbf public#103232
adamsitnik merged 23 commits into
dotnet:mainfrom
adamsitnik:nrbfPublic

Conversation

@adamsitnik

@adamsitnikadamsitnik commented Jun 10, 2024

Copy link
Copy Markdown
Member

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
@adamsitnikadamsitnik added the binaryformatter-migration Issues related to the removal of BinaryFormatter and migrations away from it label Jun 10, 2024
@dotnet-policy-service

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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.

[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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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. :)

Comment threadsrc/libraries/System.Formats.Nrbf/ref/System.Formats.Nrbf.csproj Outdated
@jkotas

Copy link
Copy Markdown
Member

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.

@JeremyKuhneJeremyKuhne left a comment

Copy link
Copy Markdown
Member

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

adamsitnik commented Jun 13, 2024

Copy link
Copy Markdown
MemberAuthor

@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

ericstj commented Jun 13, 2024

Copy link
Copy Markdown
Member

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
Copy Markdown
MemberAuthor

@ericstj thanks a lot!

Comment threadsrc/libraries/System.Formats.Nrbf/src/System.Formats.Nrbf.csproj Outdated
@adamsitnik

Copy link
Copy Markdown
MemberAuthor

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

image

I'll try the bypass feature..

@adamsitnik

Copy link
Copy Markdown
MemberAuthor

/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:mainJun 14, 2024
@ericstj

Copy link
Copy Markdown
Member

@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
Copy Markdown
Contributor

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
Copy Markdown
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-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 28, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Formats.Nrbfbinaryformatter-migrationIssues 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

@adamsitnik@jkotas@ericstj@miloush@bartonjs@carlossanlop@JeremyKuhne