Skip to content

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

Draft
Copilot wants to merge 11 commits intomainfrom
copilot/update-hash-algorithm-to-xxhash64
Draft

[TrimmableTypeMap] Use Crc64 package naming by default with LowercaseCrc64 compatibility#11193
Copilot wants to merge 11 commits intomainfrom
copilot/update-hash-algorithm-to-xxhash64

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 23, 2026

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
      • 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).
    • 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.
  • Related plumbing updates

    • Passed package naming policy through the trimmable typemap target/task into TrimmableTypeMapGenerator and scanner hashing logic.
return packageNamingPolicy switch {
	HashedPackageNamingPolicy.LowercaseCrc64 => "crc64" + ScannerHashingHelper.ToLegacyCrc64 (ns, assemblyName),
	HashedPackageNamingPolicy.Crc64 => "crc64" + ScannerHashingHelper.ToCrc64 (ns, assemblyName),
	_ => throw new InvalidOperationException ($"Unsupported package naming policy: {packageNamingPolicy}"),
};

Copilot AI and others added 2 commits April 23, 2026 07:33
Agent-Logs-Url: https://github.com/dotnet/android/sessions/425dd1fe-e6a4-43e0-b0a9-de6b1e343c11

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/android/sessions/425dd1fe-e6a4-43e0-b0a9-de6b1e343c11

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copilot AI changed the title [WIP] Use xxhash64 instead of crc64 at build time [TrimmableTypeMap] Use XxHash64 for auto-generated Java package hashing Apr 23, 2026
Copilot AI requested a review from simonrozsival April 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.

Agent-Logs-Url: https://github.com/dotnet/android/sessions/b04a9e6a-bf80-4ff1-8601-4078c145728e

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 23, 2026

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

Copy link
Copy Markdown
Member

@jonathanpeppers jonathanpeppers left a comment

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

…efault

Agent-Logs-Url: https://github.com/dotnet/android/sessions/a90e2abd-ca59-4b98-a498-64caa983b1d0

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 23, 2026

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

Copilot AI changed the title [TrimmableTypeMap] Use XxHash64 for auto-generated Java package hashing [TrimmableTypeMap] Use XxHash64 package naming by default with LowercaseCrc64 compatibility Apr 23, 2026
Comment on lines +1517 to +1519
var data = System.Text.Encoding.UTF8.GetBytes (value);
var hash = System.IO.Hashing.XxHash64.Hash (data);
return BitConverter.ToString (hash).Replace ("-", "").ToLowerInvariant ();
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 we should take inspiration from dotnet/android-tools#335 and avoid so many allocations here - use stackalloc where possible and non-allocating method overloads. Make sure to reuse the ToHexString method which is a lot more efficient than what we have right now. Also I don't think we need to hash UTF-8, we can simply do ReadOnlySpan<byte> utf16Bytes = MemoryMarshal.AsBytes(text.AsSpan()); , right? We hash a lot of strings during the build process, so let's make sure we don't unnecessarily slow down builds.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Implemented in 8d5709b: reduced allocations in JavaPeerScanner by switching to span-based hex formatting (with stackalloc), using non-allocating XxHash64.Hash(ReadOnlySpan<byte>, Span<byte>), and removing intermediate "{ns}:{assembly}" string allocations via pooled UTF-8 buffers. I kept UTF-8 input for hash compatibility with existing outputs/policies.

Copy link
Copy Markdown
Member

@simonrozsival simonrozsival left a comment

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.

<AndroidVersionCodePattern Condition=" '$(AndroidUseLegacyVersionCode)' != 'True' And '$(AndroidVersionCodePattern)' == '' ">{abi}{versionCode:D5}</AndroidVersionCodePattern>
<AndroidResourceGeneratorTargetName>UpdateGeneratedFiles</AndroidResourceGeneratorTargetName>
<AndroidUseApkSigner Condition=" '$(AndroidUseApkSigner)' == '' ">True</AndroidUseApkSigner>
<AndroidPackageNamingPolicy Condition=" '$(AndroidPackageNamingPolicy)' == '' ">LowercaseCrc64</AndroidPackageNamingPolicy>
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 XxHash64 should be the default just for the trimmable typemap

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 05cebc7: reverted the global AndroidPackageNamingPolicy default and scoped XxHash64 as the default specifically for trimmable typemap generation.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 24, 2026

@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

…lgorithm-to-xxhash64

# Conflicts:
#	src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 27, 2026

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

@simonrozsival
Copy link
Copy Markdown
Member

@copilot in dotnet/android-tools#338 we actually showed that System.IO.Hashing.Crc64 is faster than xxhash64. This PR will still be useful, but in a different way:

  • HashedPackageNamingPolicy.LowercaseCrc64 - the legacy Crc64 code for backwards compatibility
  • HashedPackageNamingPolicy.Crc64 - the much faster System.IO.Hashing.Crc64 implementation - new default for trimmable type map

…seCrc64 legacy

Agent-Logs-Url: https://github.com/dotnet/android/sessions/fdb58e79-42f0-4d7d-8180-2b58c52ae3f8

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 27, 2026

@copilot in dotnet/android-tools#338 we actually showed that System.IO.Hashing.Crc64 is faster than xxhash64. This PR will still be useful, but in a different way:

  • `Hash...

Done in e9ae72c. Trimmable default is now Crc64 (using System.IO.Hashing.Crc64), while LowercaseCrc64 remains the legacy custom CRC64 path for compatibility. I also kept explicit XxHash64 support and updated tests for all three policy behaviors.

Copilot AI changed the title [TrimmableTypeMap] Use XxHash64 package naming by default with LowercaseCrc64 compatibility [TrimmableTypeMap] Use Crc64 package naming by default with LowercaseCrc64 compatibility Apr 27, 2026
Comment on lines +22 to +26
[Fact]
public void ToCrc64_KnownInput_HasStableOutput ()
{
Assert.Equal ("eb3df85c64aa1af6", ScannerHashingHelper.ToCrc64 ("MyApp", "TestFixtures"));
}
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 turn this into a Theory with multiple inputs + expected outputs values passed to it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in fa8493d. I converted this to a [Theory] with multiple input/output vectors for ToCrc64. No UI changes (screenshot N/A).

{
enum HashedPackageNamingPolicy {
Crc64,
XxHash64,
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 I think we should drop xxhash64, it doesn't bring any value after all. Let's keep the compat crc64 + the new "fast" crc64

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in fa8493d. I removed the trimmable XxHash64 path and now keep only legacy LowercaseCrc64 + fast Crc64. No UI changes (screenshot N/A).

internal static string ToCrc64 (string ns, string assemblyName)
{
int byteCount = GetNamespaceAssemblyUtf8ByteCount (ns, assemblyName);
byte[] rented = ArrayPool<byte>.Shared.Rent (byteCount);
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 we should stackalloc the buffer for utf-8 encoded name with a new byte[N] fallback for very long (and rare!) names (>256 chars) instead of renting the arrays

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in fa8493d. ToCrc64 now uses stackalloc for UTF-8 buffers with new byte[N] fallback for larger values (>256 bytes). No UI changes (screenshot N/A).

Copilot AI and others added 2 commits April 27, 2026 11:10
Comment on lines +35 to +37
Span<byte> hash = stackalloc byte [8];
System.IO.Hashing.Crc64.Hash (utf8Buffer.Slice (0, bytesWritten), hash);
return ToHexString (hash, lowercase: true);
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.

One problem is if you have "\0" and "\0\0\0\0" both with all zeros, then the crc value will be the same. Is that a problem here?

We XOR the length on this one:

int slash = javaName.IndexOf ('/');
if (slash > 0) {
return "crc64.../" + javaName.Substring (slash + 1);
return "hash.../" + javaName.Substring (slash + 1);
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.

We prefixed crc64 so the algorithm was clear. Should we pick something less generic than hash?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TrimmableTypeMap] Use xxhash64 instead of crc64 at build time

3 participants