Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Configuration.props
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@
<SqliteSourceDirectory Condition=" '$(SqliteSourceDirectory)' == '' ">$(MSBuildThisFileDirectory)external\sqlite</SqliteSourceDirectory>
<LibUnwindSourceDirectory Condition=" '$(LibUnwindSourceDirectory)' == '' ">$(MSBuildThisFileDirectory)external\libunwind</LibUnwindSourceDirectory>
<LibUnwindGeneratedHeadersDirectory Condition=" '$(LibUnwindGeneratedHeadersDirectory)' == '' ">$(BootstrapOutputDirectory)\libunwind</LibUnwindGeneratedHeadersDirectory>
<LZ4SourceDirectory Condition=" '$(LZ4SourceDirectory)' == '' ">$(MSBuildThisFileDirectory)external\lz4</LZ4SourceDirectory>
<XamarinAndroidSourcePath>$(MSBuildThisFileDirectory)</XamarinAndroidSourcePath>
<ThirdPartySourcePath>$(MSBuildThisFileDirectory)src-ThirdParty\</ThirdPartySourcePath>
<AllSupported32BitTargetAndroidAbis>armeabi-v7a;x86</AllSupported32BitTargetAndroidAbis>
Expand Down Expand Up @@ -171,7 +170,6 @@
<SqliteSourceFullPath>$([System.IO.Path]::GetFullPath ('$(SqliteSourceDirectory)'))</SqliteSourceFullPath>
<LibUnwindSourceFullPath>$([System.IO.Path]::GetFullPath ('$(LibUnwindSourceDirectory)'))</LibUnwindSourceFullPath>
<LibUnwindGeneratedHeadersFullPath>$([System.IO.Path]::GetFullPath ('$(LibUnwindGeneratedHeadersDirectory)'))</LibUnwindGeneratedHeadersFullPath>
<LZ4SourceFullPath>$([System.IO.Path]::GetFullPath ('$(LZ4SourceDirectory)'))</LZ4SourceFullPath>
<JavaInteropTargetFrameworkVersion>net10.0</JavaInteropTargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
Expand Down
4 changes: 2 additions & 2 deletions src/Xamarin.Android.Build.Tasks/Tasks/CompressAssemblies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
namespace Xamarin.Android.Tasks;

/// <summary>
/// Compresses assemblies using LZ4 compression before placing them in the APK.
/// Compresses assemblies using Zstandard compression before placing them in the APK.
/// Note this is independent of whether they are stored compressed with ZIP in the APK.
/// Our runtime bits will LZ4 decompress them at assembly load time.
/// Our runtime bits will Zstd decompress them at assembly load time.
/// </summary>
public class CompressAssemblies : AndroidTask
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
"Size": 402352
},
"lib/arm64-v8a/libassembly-store.so": {
"Size": 3461344
"Size": 2903016
},
"lib/arm64-v8a/libclrjit.so": {
"Size": 2804464
"Size": 2824392
},
"lib/arm64-v8a/libcoreclr.so": {
"Size": 4872088
"Size": 4890432
},
"lib/arm64-v8a/libmonodroid.so": {
"Size": 1325808
"Size": 1265504
},
"lib/arm64-v8a/libSystem.Globalization.Native.so": {
"Size": 72112
Expand All @@ -32,7 +32,7 @@
"Size": 168080
},
"lib/arm64-v8a/libxamarin-app.so": {
"Size": 20776
"Size": 20984
},
"res/drawable-hdpi-v4/icon.png": {
"Size": 2178
Expand All @@ -59,5 +59,5 @@
"Size": 1904
}
},
"PackageSize": 7497147
"PackageSize": 7452091
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@
"Size": 2396
},
"lib/arm64-v8a/libassembly-store.so": {
"Size": 14137920
"Size": 11658648
},
"lib/arm64-v8a/libclrjit.so": {
"Size": 2804464
"Size": 2824392
},
"lib/arm64-v8a/libcoreclr.so": {
"Size": 4872088
"Size": 4890432
},
"lib/arm64-v8a/libmonodroid.so": {
"Size": 1325808
"Size": 1265504
},
"lib/arm64-v8a/libSystem.Globalization.Native.so": {
"Size": 72112
Expand All @@ -56,7 +56,7 @@
"Size": 168080
},
"lib/arm64-v8a/libxamarin-app.so": {
"Size": 147616
"Size": 147824
},
"META-INF/androidx.activity_activity.version": {
"Size": 6
Expand Down Expand Up @@ -2234,5 +2234,5 @@
"Size": 794696
}
},
"PackageSize": 20778573
"PackageSize": 20786765
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Buffers;
using System.IO;

using K4os.Compression.LZ4;
using Microsoft.Android.Build.Tasks;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
Expand Down Expand Up @@ -45,7 +44,7 @@ public void SetData (string sourcePath, uint descriptorIndex)
}
}

const uint CompressedDataMagic = 0x5A4C4158; // 'XALZ', little-endian
const uint CompressedDataMagic = 0x535A4158; // 'XAZS', little-endian

static readonly ArrayPool<byte> bytePool = ArrayPool<byte>.Shared;

Expand Down Expand Up @@ -75,11 +74,25 @@ static CompressionResult Compress (AssemblyData data, string outputFilePath)
int fileSize = checked((int)fi.Length);
sourceBytes = bytePool.Rent (fileSize);
using (var fs = File.Open (data.SourcePath, FileMode.Open, FileAccess.Read, FileShare.Read)) {
bytesRead = fs.Read (sourceBytes, 0, fileSize);
bytesRead = 0;
while (bytesRead < fileSize) {
int read = fs.Read (sourceBytes, bytesRead, fileSize - bytesRead);
if (read == 0)
break;

bytesRead += read;
}
}

destBytes = bytePool.Rent (LZ4Codec.MaximumOutputSize (bytesRead));
int encodedLength = LZ4Codec.Encode (sourceBytes, 0, bytesRead, destBytes, 0, destBytes.Length, LZ4Level.L12_MAX);
if (bytesRead != fileSize)
return CompressionResult.EncodingFailed;

int maxOutputSize = Zstd.MaximumOutputSize (bytesRead);
if (maxOutputSize <= 0)
return CompressionResult.EncodingFailed;

destBytes = bytePool.Rent (maxOutputSize);
int encodedLength = Zstd.Compress (sourceBytes, bytesRead, destBytes);
if (encodedLength < 0)
return CompressionResult.EncodingFailed;

Expand Down Expand Up @@ -154,7 +167,7 @@ public static bool TryGetDescriptorIndex (TaskLoggingHelper log, ITaskItem assem
public static string GetCompressedAssemblyOutputPath (ITaskItem assembly, string compressedOutputDir)
{
var assemblyOutputDir = GetCompressedAssemblyOutputDirectory (assembly, compressedOutputDir);
return Path.Combine (assemblyOutputDir, $"{Path.GetFileName (assembly.ItemSpec)}.lz4");
return Path.Combine (assemblyOutputDir, $"{Path.GetFileName (assembly.ItemSpec)}.zst");
}

static string GetCompressedAssemblyOutputDirectory (ITaskItem assembly, string compressedOutputDir)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public AssemblyStoreAssemblyInfo (string sourceFilePath, ITaskItem assembly, boo
throw new InvalidOperationException ("Internal error: info without assembly name");
}

if (name.EndsWith (".lz4", StringComparison.OrdinalIgnoreCase)) {
if (name.EndsWith (".zst", StringComparison.OrdinalIgnoreCase)) {
name = Path.GetFileNameWithoutExtension (name);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ public NativeRuntimeComponents (ITaskItem[]? monoComponents)
new AndroidArchive ("libruntime-base-common-release.a"),
new AndroidArchive ("libruntime-base-release.a"),
new AndroidArchive ("libxa-java-interop-release.a"),
new AndroidArchive ("libxa-lz4-release.a"),
new AndroidArchive ("libxa-shared-bits-release.a"),
new AndroidArchive ("libxamarin-startup-release.a"),

Expand Down
118 changes: 118 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Utilities/Zstd.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#nullable enable
using System;
using System.Runtime.InteropServices;

namespace Xamarin.Android.Tasks
Comment thread
simonrozsival marked this conversation as resolved.
{
/// <summary>
/// Minimal managed wrapper around the Zstandard compression functions exported by
/// <c>libSystem.IO.Compression.Native</c>, which ships in the .NET runtime pack.
/// We use it to compress assemblies that are placed in the assembly store; the native
/// runtime decompresses them at load time using the same library.
/// </summary>
static class Zstd
{
// libSystem.IO.Compression.Native exports the raw zstd entry points (no prefix).
const string ZstdLibrary = "System.IO.Compression.Native";
Comment thread
simonrozsival marked this conversation as resolved.

// ZSTD_cParameter value for ZSTD_c_compressionLevel (see zstd.h).
const int ZSTD_c_compressionLevel = 100;
Comment thread
simonrozsival marked this conversation as resolved.
const int ZstdCompressionLevel = 3;

[DllImport (ZstdLibrary, CallingConvention = CallingConvention.Cdecl)]
static extern UIntPtr ZSTD_compressBound (UIntPtr srcSize);

[DllImport (ZstdLibrary, CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ZSTD_createCCtx ();

[DllImport (ZstdLibrary, CallingConvention = CallingConvention.Cdecl)]
static extern UIntPtr ZSTD_freeCCtx (IntPtr cctx);

[DllImport (ZstdLibrary, CallingConvention = CallingConvention.Cdecl)]
static extern UIntPtr ZSTD_CCtx_setParameter (IntPtr cctx, int param, int value);

[DllImport (ZstdLibrary, CallingConvention = CallingConvention.Cdecl)]
static extern UIntPtr ZSTD_compress2 (IntPtr cctx, byte[] dst, UIntPtr dstCapacity, byte[] src, UIntPtr srcSize);

[DllImport (ZstdLibrary, CallingConvention = CallingConvention.Cdecl)]
static extern uint ZSTD_isError (UIntPtr code);

/// <summary>
/// Returns the maximum size that compressed data of <paramref name="inputSize"/> bytes can occupy.
/// </summary>
public static int MaximumOutputSize (int inputSize)
{
if (inputSize < 0)
return -1;

try {
UIntPtr result = ZSTD_compressBound ((UIntPtr) (uint) inputSize);
if (ZSTD_isError (result) != 0)
return -1;

ulong maxOutputSize = (ulong) result;
if (maxOutputSize > int.MaxValue)
return -1;

return (int) maxOutputSize;
} catch (DllNotFoundException) {
return -1;
} catch (EntryPointNotFoundException) {
return -1;
}
}

/// <summary>
/// Compresses <paramref name="inputLength"/> bytes from <paramref name="input"/> into
/// <paramref name="output"/> using zstd's default compression level. Returns the number of
/// bytes written to <paramref name="output"/>, or <c>-1</c> if compression failed.
/// </summary>
public static int Compress (byte[] input, int inputLength, byte[] output)
{
if (input == null)
throw new ArgumentNullException (nameof (input));
if (output == null)
throw new ArgumentNullException (nameof (output));
if (inputLength < 0 || inputLength > input.Length)
throw new ArgumentOutOfRangeException (nameof (inputLength));

IntPtr cctx;
try {
cctx = ZSTD_createCCtx ();
} catch (DllNotFoundException) {
return -1;
} catch (EntryPointNotFoundException) {
return -1;
}

if (cctx == IntPtr.Zero)
return -1;

try {
UIntPtr setParameterResult = ZSTD_CCtx_setParameter (cctx, ZSTD_c_compressionLevel, ZstdCompressionLevel);
if (ZSTD_isError (setParameterResult) != 0)
return -1;

UIntPtr result = ZSTD_compress2 (cctx, output, (UIntPtr) (uint) output.Length, input, (UIntPtr) (uint) inputLength);
if (ZSTD_isError (result) != 0)
return -1;

ulong encodedLength = (ulong) result;
if (encodedLength > int.MaxValue)
return -1;

return (int) encodedLength;
} catch (DllNotFoundException) {
return -1;
} catch (EntryPointNotFoundException) {
return -1;
} finally {
try {
ZSTD_freeCCtx (cctx);
} catch (DllNotFoundException) {
} catch (EntryPointNotFoundException) {
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2150,7 +2150,7 @@ because xbuild doesn't support framework reference assemblies.
This dependency chain should be addressed in a separate PR. -->
<CollectAssemblyFilesToCompress
Condition=" '$(_AndroidRuntime)' != 'NativeAOT' "
AssemblyCompressionDirectory="$(IntermediateOutputPath)android\lz4\"
AssemblyCompressionDirectory="$(IntermediateOutputPath)android\zstd\"
EmbedAssemblies="$(EmbedAssembliesIntoApk)"
EnableCompression="$(AndroidEnableAssemblyCompression)"
IncludeDebugSymbols="$(AndroidIncludeDebugSymbols)"
Expand All @@ -2165,8 +2165,8 @@ because xbuild doesn't support framework reference assemblies.
</Target>

<!--
LZ4 compress all compressable assemblies. Note this is an incremental MSBuild target.
@(AssembliesToCmpress) will only contain assemblies that have changed since the last build.
Zstd compresses all compressible assemblies. Note this is an incremental MSBuild target.
@(_AssembliesToCompress) will only contain assemblies that have changed since the last build.
-->
<Target Name="_CompressAssemblies"
DependsOnTargets="_CollectAssembliesToCompress"
Expand Down
1 change: 0 additions & 1 deletion src/native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,6 @@ else()

if (IS_MONO_RUNTIME OR IS_CLR_RUNTIME)
add_subdirectory(common/libunwind)
add_subdirectory(common/lz4)
add_subdirectory(common/runtime-base)

add_subdirectory(${SOURCES_PREFIX}/runtime-base)
Expand Down
3 changes: 1 addition & 2 deletions src/native/clr/host/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ macro(lib_target_options TARGET_NAME)
${TARGET_NAME}
PRIVATE
HAVE_CONFIG_H
HAVE_LZ4
JI_DLL_EXPORT
JI_NO_VISIBILITY
)
Expand Down Expand Up @@ -155,7 +154,7 @@ macro(lib_target_options TARGET_NAME)
xa::runtime-base
xa::java-interop
xa::pinvoke-override-precompiled
xa::lz4
-lSystem.IO.Compression.Native
-landroid
-llog
-lcoreclr
Expand Down
Loading