Skip to content

[TrimmableTypeMap] Use Crc64 package naming by default with LowercaseCrc64 compatibility - #11193

Merged
jonathanpeppers merged 14 commits into
mainfrom
copilot/update-hash-algorithm-to-xxhash64
May 13, 2026
Merged

[TrimmableTypeMap] Use Crc64 package naming by default with LowercaseCrc64 compatibility#11193
jonathanpeppers merged 14 commits into
mainfrom
copilot/update-hash-algorithm-to-xxhash64

Conversation

CopilotAI commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

This updates trimmable type map generation to default to Crc64-based package naming for unregistered types, while preserving backwards compatibility for projects that explicitly set:

<AndroidPackageNamingPolicy>LowercaseCrc64</AndroidPackageNamingPolicy>
  • Package naming policy support in trimmable typemap

    • Wired AndroidPackageNamingPolicy into trimmable typemap generation.
    • Added policy-aware hashing:
      • Crc64 (trimmable default) → crc64... using System.IO.Hashing.Crc64 with length mixing
      • LowercaseCrc64crc64... using the existing legacy/custom CRC64 implementation for compatibility with previous naming
    • Removed trimmable XxHash64 handling, as it no longer provides value for this path.
    • Kept Mono.Android namespace-preserving behavior unchanged.
  • Default policy update

    • Crc64 is now the default only for trimmable typemap generation.
    • The global AndroidPackageNamingPolicy default remains LowercaseCrc64 for non-trimmable typemap paths.
  • Hashing implementation refactor

    • Moved scanner hashing logic out of JavaPeerScanner into a dedicated static ScannerHashingHelper for easier focused testing.
    • Added dedicated ScannerHashingHelper.ToCrc64() for System.IO.Hashing.Crc64.
    • Updated UTF-8 buffer handling for the fast Crc64 path to use stackalloc with new byte[N] fallback for larger buffers (>256 bytes).
    • Added length mixing (crc64 ^ byteLength) in ToCrc64() to avoid equal outputs for different-length all-zero inputs.
    • Kept legacy CRC64 compatibility behavior intact.
  • Targeted test updates

    • Updated scanner expectations for crc64 trimmable defaults.
    • Added coverage to verify:
      • default Crc64 output,
      • legacy LowercaseCrc64 compatibility output.
    • Removed XxHash64-specific scanner expectations.
    • Kept fixed-vector tests for legacy CRC64 known input/output pairs to validate backward compatibility behavior.
    • Converted Crc64 known-output validation into a [Theory] with multiple input/output vectors.
    • Added a regression test to ensure different-length zero-byte inputs produce different Crc64 outputs.
  • Related plumbing updates

    • Passed package naming policy through the trimmable typemap target/task into TrimmableTypeMapGenerator and scanner hashing logic.
    • Merged latest main conflict changes so trimmable typemap generation keeps both package naming policy support and MaxArrayRank plumbing.
returnpackageNamingPolicyswitch{HashedPackageNamingPolicy.LowercaseCrc64=>"crc64"+ScannerHashingHelper.ToLegacyCrc64(ns,assemblyName),HashedPackageNamingPolicy.Crc64=>"crc64"+ScannerHashingHelper.ToCrc64(ns,assemblyName),
_ =>thrownewInvalidOperationException($"Unsupported package naming policy: {packageNamingPolicy}"),};

CopilotAI linked an issue Apr 23, 2026 that may be closed by this pull request
CopilotAI changed the title [WIP] Use xxhash64 instead of crc64 at build time[TrimmableTypeMap] Use XxHash64 for auto-generated Java package hashingApr 23, 2026
CopilotAI requested a review from simonrozsivalApril 23, 2026 07:38
@simonrozsival

Copy link
Copy Markdown
Member

@copilot I would prefer changing the prefix from crc64 to xx64 to reflect the change.

CopilotAI commented Apr 23, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot I would prefer changing the prefix from crc64 to xx64 to reflect the change.

Updated in 1049678: auto-generated TrimmableTypeMap package names now use the xx64 prefix (with XxHash64), and related unit/integration test expectations were updated accordingly.

@jonathanpeppersjonathanpeppers 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.

Note that there is the property:

<AndroidPackageNamingPolicy>LowercaseCrc64</AndroidPackageNamingPolicy>

We will probably want to introduce a new one for XxHash64.

It is a slight breaking change, because people could have a hardcoded package name in a script for launching their main activity, etc.

So, I think what we did in some really old Xamarin.Android release, we changed the default but allowed projects to still use the old one.

@simonrozsival

Copy link
Copy Markdown
Member

It is a slight breaking change, because people could have a hardcoded package name in a script for launching their main activity, etc.

So funny thing is that the CSC64 we have in our C++(or C#?) code and CSC64 that is in System.IO.Hashing produce different results. I decided to use the System.IO.Hashing one in the trimmable typemap so that would be a breaking change anyway. I will need to revisit this and try to make the LowercaseCrc64 option be consistent with previous naming. The scenario with launching a specific Activity makes sense.

@simonrozsival

Copy link
Copy Markdown
Member

@copilot let's respect <AndroidPackageNamingPolicy>LowercaseCrc64</AndroidPackageNamingPolicy> with a new (default) value of XxHash64. For backwards compatibility, let's use the custom Crc64 implementation that is in existing C# or C++ code used by the "llvm-ir" typemap

CopilotAI commented Apr 23, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot let's respect <AndroidPackageNamingPolicy>LowercaseCrc64</AndroidPackageNamingPolicy> with a new (default) value of XxHash64. For backwards compatibility, let's use the custom Crc64 imple...

Implemented in ec92067: trimmable typemap now respects AndroidPackageNamingPolicy, defaults to XxHash64, and uses the legacy custom CRC64 path when policy is LowercaseCrc64 for backwards compatibility.

Note that there is the property:

<AndroidPackageNamingPolicy>LowercaseCrc64</AndroidPackageNamingPolicy>

...

Addressed in ec92067: added trimmable support for AndroidPackageNamingPolicy with a new default of XxHash64, while keeping LowercaseCrc64 compatibility via the existing legacy CRC64 implementation.

CopilotAI changed the title [TrimmableTypeMap] Use XxHash64 for auto-generated Java package hashing[TrimmableTypeMap] Use XxHash64 package naming by default with LowercaseCrc64 compatibilityApr 23, 2026
Comment threadsrc/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs Outdated

@simonrozsivalsimonrozsival 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.

@copilot address these comments

I want to move the hashing code from JavaPeerScanner to a separate static ScannerHashingHelper class which can be better tested. I want a test that will just simply validate some known inputs to some known outputs of the legacy crc64 algorithm to prove we're maintaining backwards compatibility.

CopilotAI commented Apr 24, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot address these comments

I want to move the hashing code from JavaPeerScanner to a separate static ScannerHashingHelper class which can be better tested. I want a test that will just simply va...

Implemented in 05cebc7: moved scanner hashing into ScannerHashingHelper and added fixed-vector tests that validate known legacy CRC64 outputs to lock backward compatibility behavior.

@copilot let's respect <AndroidPackageNamingPolicy>LowercaseCrc64</AndroidPackageNamingPolicy> with a new (default) value of XxHash64. For backwards compatibility, let's use the custom Crc64 imple...

Handled in 05cebc7: trimmable typemap continues to respect explicit <AndroidPackageNamingPolicy>LowercaseCrc64</AndroidPackageNamingPolicy>, while using XxHash64 as the trimmable default and keeping the legacy custom CRC64 path for compatibility.

@copilot I would prefer changing the prefix from crc64 to xx64 to reflect the change.

xx64 is used for the new hash-based default path, while crc64 remains only for explicit LowercaseCrc64 compatibility. Confirmed in 05cebc7.

@simonrozsival

Copy link
Copy Markdown
Member

@copilot update the PR by pulling latest main and resolve conflicts

CopilotAI commented Apr 27, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot update the PR by pulling latest main and resolve conflicts

Done in 3722fd9: pulled latest main, merged it into this branch, and resolved the conflict in Microsoft.Android.Sdk.TypeMap.Trimmable.targets.

…CI fixes
# Conflicts:
#	build-tools/automation/yaml-templates/variables.yaml
#	src/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapGenerator.cs
#	src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets
#	src/Xamarin.Android.Build.Tasks/Tasks/GenerateTrimmableTypeMap.cs
@simonrozsival
simonrozsival marked this pull request as ready for review May 9, 2026 21:01
CopilotAI review requested due to automatic review settings May 9, 2026 21:01

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates trimmable typemap generation to default to Crc64-based hashed package naming for unregistered types, while preserving legacy LowercaseCrc64 behavior when explicitly requested via AndroidPackageNamingPolicy.

Changes:

  • Plumbs AndroidPackageNamingPolicy into the trimmable typemap pipeline and introduces a trimmable-specific default (Crc64).
  • Refactors hashing into a new ScannerHashingHelper with both modern System.IO.Hashing.Crc64 and legacy CRC64 implementations.
  • Updates and expands test coverage for both the new default and legacy compatibility behavior.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/ScannerHashingHelperTests.csAdds fixed-vector coverage for both legacy and new CRC64 hashing + length-mixing regression test.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csUpdates expected hashed package naming for the new default and adds a compatibility-difference test.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/FixtureTestBase.csAdds helpers to rescan fixtures using a specified package naming policy.
src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.props.inIntroduces a sentinel property to detect when AndroidPackageNamingPolicy is user-specified.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateTrimmableTypeMap.csPasses package naming policy through to the trimmable typemap generator.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targetsSets Crc64 as the trimmable default while honoring explicitly-set policy values.
src/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapGenerator.csThreads package naming policy into assembly scanning.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/ScannerHashingHelper.csNew helper implementing modern CRC64 hashing and legacy compatibility hashing.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csSwitches to policy-aware hashed package naming using ScannerHashingHelper.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Microsoft.Android.Sdk.TrimmableTypeMap.csprojEnables unsafe blocks and links legacy CRC64 sources for compatibility.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ManifestGenerator.csUpdates documentation wording to reflect “hashed package names”.

Comment threadsrc/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs Outdated
Comment threadsrc/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs Outdated
simonrozsivaland others added 2 commits May 11, 2026 09:52
- Remove unused 'using Java.Interop.Tools.JavaCallableWrappers;' from
JavaPeerScanner.cs (CS8019 would break warnings-as-errors).
- Wrap PEReader in 'using' inside FixtureTestBase.ScanFixtures (string)
so TestFixtures.dll is released even when Scan() throws.
- Make JavaPeerScanner.ParsePackageNamingPolicy strict: only null/empty,
'Crc64', and 'LowercaseCrc64' (case-insensitive) are accepted; anything
else now throws ArgumentException instead of silently falling back to
Crc64. Added xunit Theories covering supported and unsupported values.
- Validate AndroidPackageNamingPolicy in
Microsoft.Android.Sdk.TypeMap.Trimmable.targets: emit a build error
before the trimmable typemap generator runs when the user explicitly
set a policy other than Crc64 or LowercaseCrc64 (e.g. 'Lowercase').
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MSBuild conditions are case-sensitive by default but the C# parser in
JavaPeerScanner.ParsePackageNamingPolicy uses StringComparison.OrdinalIgnoreCase.
Lowercase the property before comparing so e.g. 'crc64' or 'lowercasecrc64'
do not falsely trigger the unsupported-policy build error.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@simonrozsivalsimonrozsival 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.

@copilot address these comments

CopilotAIand others added 2 commits May 11, 2026 08:35
…okSupport override
Agent-Logs-Url: https://github.com/dotnet/android/sessions/093b5a99-9f87-468c-b7f4-b6c221eb3561
Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/android/sessions/093b5a99-9f87-468c-b7f4-b6c221eb3561
Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>

CopilotAI commented May 11, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot address these comments

Addressed in 79586ba (plus follow-up 5668156): scanner hash expectations are now hardcoded, the outdated StartupHookSupport tweak was removed, and the test fixture lifetime adjustment was finalized. No UI changes (screenshot N/A).

@simonrozsival

Copy link
Copy Markdown
Member

/review

@github-actions

github-actionsBot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@copilot

⚠️ Needs Changes

Summary: This PR cleanly wires AndroidPackageNamingPolicy into the trimmable typemap path, defaulting to Crc64 (with length mixing) while preserving LowercaseCrc64 backward compatibility. The architecture is sound — hashing logic is well-extracted into ScannerHashingHelper, the MSBuild plumbing correctly detects user-set vs. default policies, and tests cover known-value stability, policy validation, and the zero-length collision edge case.

Issues by severity

SeverityCount
💡 Suggestion3

Positive callouts

  • Good design: The _AndroidPackageNamingPolicySetByUser sentinel in .props.in correctly captures user intent before the default is applied, avoiding the common MSBuild "is this the default or did the user set it?" ambiguity.
  • Length mixing: XORing the CRC64 hash with the byte length is a simple and effective way to prevent collisions for inputs of different lengths that hash to the same value — the zero-byte regression test validates this nicely.
  • Clean separation: Extracting ScannerHashingHelper as a standalone internal static class with focused tests is well-structured. The internal/InternalsVisibleTo pattern follows repo conventions.
  • Validation: The _ValidateTrimmableTypeMapPackageNamingPolicy target gives a clear, actionable error message for unsupported policies, consistent with the existing _ValidateTrimmableTypeMapRuntime pattern.

Notes

  • CI checks (dotnet-android, license/cla) are green.
  • The 3 inline suggestions are all 💡-level (convention alignment and removing hand-rolled utilities). None block merge.

Generated by Android PR Reviewer for issue #11193 · ● 8.4M

Comment threadsrc/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/ScannerHashingHelper.cs Outdated
Comment threadsrc/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs Outdated
Address review feedback:
- ScannerHashingHelper: replace hand-rolled WriteUInt64LittleEndian/
ReadUInt64LittleEndian helpers with System.Buffers.Binary.BinaryPrimitives
(available transitively via System.IO.Hashing).
- JavaPeerScanner.ParsePackageNamingPolicy: use the project-local
NullableExtensions.IsNullOrEmpty() extension method (NRT-aware) instead
of the static string.IsNullOrEmpty().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival added the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 11, 2026
@simonrozsival

Copy link
Copy Markdown
Member

/review

@github-actions

github-actionsBot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@copilot

Review Summary

✅ Overall Assessment

Well-structured PR that wires AndroidPackageNamingPolicy into the trimmable typemap path with proper backward compatibility. The implementation cleanly separates concerns:

  • ScannerHashingHelper isolates hashing logic for focused testing
  • HashedPackageNamingPolicy enum is correctly private to JavaPeerScanner
  • MSBuild plumbing correctly distinguishes user-set vs default policy via _AndroidPackageNamingPolicySetByUser
  • Incremental build correctness is maintained via the existing AndroidPackageNamingPolicy entry in _PropertyCacheItems
  • Validation exists at both the MSBuild target level (early, clear error) and C# level (defensive)

Verified

  • ✅ Hash output consistency: ToLegacyCrc64 and ToCrc64 test vectors match scanner expectations
  • ✅ Legacy compatibility: LowercaseCrc64 path uses the original Jones CRC64 implementation from Java.Interop
  • ✅ Length mixing prevents zero-byte collisions (with regression test)
  • ✅ Byte-level encoding: GetNamespaceAssemblyUtf8Bytes correctly handles the ns:assemblyName format
  • ✅ Incremental builds: AndroidPackageNamingPolicy is already in _PropertyCacheItems (line 991 of Xamarin.Android.Common.targets)
  • ParsePackageNamingPolicy uses IsNullOrEmpty() extension method per repo convention

CI Status

  • license/cla: ✅ Passed
  • dotnet-android: ✅ Passed
  • Xamarin.Android-PR: Not visible — may not have triggered yet

Issues Found

SeverityCount
⚠️ Warning1
💡 Suggestion3

No blocking issues. The warning is about ArrayPool consistency in ToCrc64's heap fallback path — low practical impact since namespace:assembly pairs are almost always under 256 bytes.

Generated by Android PR Reviewer for issue #11193 · ● 6M

Comment threadsrc/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/ScannerHashingHelper.cs Outdated
Comment threadsrc/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs Outdated
The new fast CRC64 path (System.IO.Hashing) now emits names with an
'scrc64' prefix so they are visibly distinct from the legacy 'crc64'
prefix produced by the LowercaseCrc64 policy. This avoids ambiguity
in acw-map.txt / manifests when comparing the two algorithms and
makes scanner-vs-generator diffs easier to read.
Also drops the unused 'lowercase' parameter from
ScannerHashingHelper.ToHexString / GetHexValue (it was always passed
as true).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
Member

@jonathanpeppers I suggest merging this PR as is

@jonathanpeppers
jonathanpeppers merged commit 3f54298 into mainMay 13, 2026
3 checks passed
@jonathanpeppers
jonathanpeppers deleted the copilot/update-hash-algorithm-to-xxhash64 branch May 13, 2026 17:12
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 13, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thisready-to-reviewThis PR is ready to review/merge, I think any CI failures are just flaky (ignorable).trimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TrimmableTypeMap] Use xxhash64 instead of crc64 at build time

4 participants

@simonrozsival@jonathanpeppers