30
30
using System . Collections . Generic ;
31
31
using System . Diagnostics ;
32
32
using System . IO ;
33
+ using System . Net . Mime ;
33
34
using System . Runtime . InteropServices ;
34
35
using System . Text ;
35
36
using Xamarin . Tools . Zip . Properties ;
@@ -42,9 +43,10 @@ namespace Xamarin.Tools.Zip
42
43
public abstract partial class ZipArchive : IDisposable , IEnumerable < ZipEntry >
43
44
{
44
45
internal class CallbackContext {
45
- public Stream Source { get ; set ; } = null ;
46
- public Stream Destination { get ; set ; } = null ;
47
- public string DestinationFileName { get ; set ; } = null ;
46
+ public Stream Source = null ;
47
+ public Stream Destination = null ;
48
+ public string DestinationFileName = null ;
49
+ public bool UseTempFile = true ;
48
50
}
49
51
public const EntryPermissions DefaultFilePermissions = EntryPermissions . OwnerRead | EntryPermissions . OwnerWrite | EntryPermissions . GroupRead | EntryPermissions . WorldRead ;
50
52
public const EntryPermissions DefaultDirectoryPermissions = EntryPermissions . OwnerAll | EntryPermissions . GroupRead | EntryPermissions . GroupExecute | EntryPermissions . WorldRead | EntryPermissions . WorldExecute ;
@@ -106,7 +108,7 @@ internal ZipArchive (string defaultExtractionDir, IPlatformOptions options)
106
108
Options = options ;
107
109
}
108
110
109
- internal ZipArchive ( Stream stream , IPlatformOptions options , OpenFlags flags = OpenFlags . RDOnly )
111
+ internal ZipArchive ( Stream stream , IPlatformOptions options , OpenFlags flags = OpenFlags . RDOnly , bool useTempFile = true )
110
112
{
111
113
if ( options == null )
112
114
throw new ArgumentNullException ( nameof ( options ) ) ;
@@ -115,6 +117,7 @@ internal ZipArchive (Stream stream, IPlatformOptions options, OpenFlags flags =
115
117
CallbackContext context = new CallbackContext ( ) {
116
118
Source = stream ,
117
119
Destination = null ,
120
+ UseTempFile = useTempFile ,
118
121
} ;
119
122
var contextHandle = GCHandle . Alloc ( context , GCHandleType . Normal ) ;
120
123
IntPtr source = Native . zip_source_function_create ( callback , GCHandle . ToIntPtr ( contextHandle ) , out errorp ) ;
@@ -197,12 +200,13 @@ ErrorCode Open (string path, OpenFlags flags)
197
200
/// <param name="stream">The stream to open</param>
198
201
/// <param name="options">Platform-specific options</param>
199
202
/// <param name="strictConsistencyChecks">Perform strict consistency checks.</param>
200
- public static ZipArchive Open ( Stream stream , IPlatformOptions options = null , bool strictConsistencyChecks = false )
203
+ /// <param name="useTempFile">Use a temporary file for the archive</param>
204
+ public static ZipArchive Open ( Stream stream , IPlatformOptions options = null , bool strictConsistencyChecks = false , bool useTempFile = true )
201
205
{
202
206
OpenFlags flags = OpenFlags . None ;
203
207
if ( strictConsistencyChecks )
204
208
flags |= OpenFlags . CheckCons ;
205
- return ZipArchive . CreateInstanceFromStream ( stream , flags , options ) ;
209
+ return ZipArchive . CreateInstanceFromStream ( stream , flags , options , useTempFile ) ;
206
210
}
207
211
208
212
/// <summary>
@@ -211,12 +215,13 @@ public static ZipArchive Open (Stream stream, IPlatformOptions options = null, b
211
215
/// <param name="stream">The stream to create the arhive in</param>
212
216
/// <param name="options">Platform-specific options</param>
213
217
/// <param name="strictConsistencyChecks">Perform strict consistency checks.</param>
214
- public static ZipArchive Create ( Stream stream , IPlatformOptions options = null , bool strictConsistencyChecks = false )
218
+ /// <param name="useTempFile">Use a temporary file for the archive</param>
219
+ public static ZipArchive Create ( Stream stream , IPlatformOptions options = null , bool strictConsistencyChecks = false , bool useTempFile = true )
215
220
{
216
221
OpenFlags flags = OpenFlags . Create | OpenFlags . Truncate ;
217
222
if ( strictConsistencyChecks )
218
223
flags |= OpenFlags . CheckCons ;
219
- return ZipArchive . CreateInstanceFromStream ( stream , flags , options ) ;
224
+ return ZipArchive . CreateInstanceFromStream ( stream , flags , options , useTempFile ) ;
220
225
}
221
226
222
227
/// <summary>
@@ -231,8 +236,9 @@ public static ZipArchive Create (Stream stream, IPlatformOptions options = null,
231
236
/// <param name="defaultExtractionDir">default target directory</param>
232
237
/// <param name="strictConsistencyChecks">Perform strict consistency checks.</param>
233
238
/// <param name="options">Platform-specific options, or <c>null</c> if none necessary (the default)</param>
239
+ /// <param name="useTempFile">Use a temporary file for the archive</param>
234
240
/// <returns>Opened ZIP archive</returns>
235
- public static ZipArchive Open ( string path , FileMode mode , string defaultExtractionDir = null , bool strictConsistencyChecks = false , IPlatformOptions options = null )
241
+ public static ZipArchive Open ( string path , FileMode mode , string defaultExtractionDir = null , bool strictConsistencyChecks = false , IPlatformOptions options = null , bool useTempFile = true )
236
242
{
237
243
if ( String . IsNullOrEmpty ( path ) )
238
244
throw new ArgumentException ( string . Format ( Resources . MustNotBeNullOrEmpty_string , nameof ( path ) ) , nameof ( path ) ) ;
@@ -316,7 +322,7 @@ public static ZipArchive Open (string path, FileMode mode, string defaultExtract
316
322
/// Extracts all the entries from the archive and places them in the
317
323
/// directory indicated by the <paramref name="destinationDirectory"/> parameter.
318
324
/// If <paramref name="destinationDirectory"/> is <c>null</c> or empty, the default destination directory
319
- /// as passed to <see cref="ZipArchive.Open (string,FileMode,string,bool,IPlatformOptions)"/> is used.
325
+ /// as passed to <see cref="ZipArchive.Open (string,FileMode,string,bool,IPlatformOptions, bool )"/> is used.
320
326
/// </summary>
321
327
/// <returns>The all.</returns>
322
328
/// <param name="destinationDirectory">Destination directory.</param>
@@ -362,7 +368,7 @@ public ZipEntry AddStream (Stream stream, string archivePath, EntryPermissions p
362
368
sources . Add ( stream ) ;
363
369
string destPath = EnsureArchivePath ( archivePath ) ;
364
370
var context = new CallbackContext ( ) {
365
- Source = stream ,
371
+ Source = stream
366
372
} ;
367
373
var handle = GCHandle . Alloc ( context , GCHandleType . Normal ) ;
368
374
IntPtr h = GCHandle . ToIntPtr ( handle ) ;
@@ -376,14 +382,15 @@ public ZipEntry AddStream (Stream stream, string archivePath, EntryPermissions p
376
382
permissions = DefaultFilePermissions ;
377
383
PlatformServices . Instance . SetEntryPermissions ( this , ( ulong ) index , permissions , false ) ;
378
384
ZipEntry entry = ReadEntry ( ( ulong ) index ) ;
379
- IList < ExtraField > fields = new List < ExtraField > ( ) ;
380
385
ExtraField_ExtendedTimestamp timestamp = new ExtraField_ExtendedTimestamp ( entry , 0 , modificationTime : modificationTime ?? DateTime . UtcNow ) ;
381
- fields . Add ( timestamp ) ;
382
- if ( ! PlatformServices . Instance . WriteExtraFields ( this , entry , fields ) )
386
+ timestamp . Encode ( ) ;
387
+ if ( ! PlatformServices . Instance . WriteExtraFields ( this , entry , timestamp ) ) {
383
388
throw GetErrorException ( ) ;
389
+ }
384
390
return entry ;
385
391
}
386
392
393
+
387
394
/// <summary>
388
395
/// Adds the file to archive directory. The file is added to either the root directory of
389
396
/// the ZIP archive (if <paramref name="archiveDirectory"/> is <c>null</c> or empty) or to
@@ -455,16 +462,16 @@ public ZipEntry AddFile (string sourcePath, string archivePath = null,
455
462
throw GetErrorException ( ) ;
456
463
PlatformServices . Instance . SetEntryPermissions ( this , sourcePath , ( ulong ) index , permissions ) ;
457
464
ZipEntry entry = ReadEntry ( ( ulong ) index ) ;
458
- IList < ExtraField > fields = new List < ExtraField > ( ) ;
459
465
ExtraField_ExtendedTimestamp timestamp = new ExtraField_ExtendedTimestamp (
460
466
entry , 0 ,
461
467
createTime : File . GetCreationTimeUtc ( sourcePath ) ,
462
468
accessTime : File . GetLastAccessTimeUtc ( sourcePath ) ,
463
469
modificationTime : File . GetLastWriteTimeUtc ( sourcePath )
464
470
) ;
465
- fields . Add ( timestamp ) ;
466
- if ( ! PlatformServices . Instance . WriteExtraFields ( this , entry , fields ) )
471
+ timestamp . Encode ( ) ;
472
+ if ( ! PlatformServices . Instance . WriteExtraFields ( this , entry , timestamp ) ) {
467
473
throw GetErrorException ( ) ;
474
+ }
468
475
return entry ;
469
476
}
470
477
@@ -796,7 +803,9 @@ internal ZipException GetErrorException ()
796
803
797
804
internal static unsafe Int64 stream_callback ( IntPtr state , IntPtr data , UInt64 len , SourceCommand cmd )
798
805
{
806
+ #if ! NET6_0_OR_GREATER
799
807
byte [ ] buffer = null ;
808
+ #endif
800
809
Native . zip_error_t error ;
801
810
int length = ( int ) len ;
802
811
var handle = GCHandle . FromIntPtr ( state ) ;
@@ -828,6 +837,14 @@ internal static unsafe Int64 stream_callback (IntPtr state, IntPtr data, UInt64
828
837
return ( Int64 ) destination . Position ;
829
838
830
839
case SourceCommand . Write :
840
+ #if NET6_0_OR_GREATER
841
+ unsafe
842
+ {
843
+ byte * ptr = ( byte * ) data ;
844
+ destination . Write ( new ReadOnlySpan < byte > ( ptr , length ) ) ;
845
+ }
846
+ return length ;
847
+ #else
831
848
buffer = ArrayPool < byte > . Shared . Rent ( length ) ;
832
849
try {
833
850
Marshal . Copy ( data , buffer , 0 , length ) ;
@@ -836,7 +853,7 @@ internal static unsafe Int64 stream_callback (IntPtr state, IntPtr data, UInt64
836
853
} finally {
837
854
ArrayPool < byte > . Shared . Return ( buffer ) ;
838
855
}
839
-
856
+ #endif
840
857
case SourceCommand . SeekWrite :
841
858
Native . zip_source_args_seek_t args ;
842
859
if ( ! Native . ZipSourceGetArgs ( data , len , out args ) ) {
@@ -895,6 +912,14 @@ internal static unsafe Int64 stream_callback (IntPtr state, IntPtr data, UInt64
895
912
896
913
case SourceCommand . Read :
897
914
length = ( int ) Math . Min ( stream . Length - stream . Position , length ) ;
915
+ #if NET6_0_OR_GREATER
916
+ unsafe
917
+ {
918
+ byte * ptr = ( byte * ) data ;
919
+ int bytesRead = stream . Read ( new Span < byte > ( ptr , length ) ) ;
920
+ return bytesRead ;
921
+ }
922
+ #else
898
923
buffer = ArrayPool < byte > . Shared . Rent ( length ) ;
899
924
try {
900
925
int bytesRead = 0 ;
@@ -909,11 +934,16 @@ internal static unsafe Int64 stream_callback (IntPtr state, IntPtr data, UInt64
909
934
} finally {
910
935
ArrayPool < byte > . Shared . Return ( buffer ) ;
911
936
}
937
+ #endif
912
938
case SourceCommand . BeginWrite :
913
939
try {
914
- string tempFile = Path . GetTempFileName ( ) ;
915
- context . Destination = File . Open ( tempFile , FileMode . OpenOrCreate , FileAccess . ReadWrite ) ;
916
- context . DestinationFileName = tempFile ;
940
+ if ( context . UseTempFile ) {
941
+ string tempFile = Path . GetTempFileName ( ) ;
942
+ context . Destination = File . Open ( tempFile , FileMode . OpenOrCreate , FileAccess . ReadWrite ) ;
943
+ context . DestinationFileName = tempFile ;
944
+ } else {
945
+ context . Destination = new MemoryStream ( ) ;
946
+ }
917
947
} catch ( IOException ) {
918
948
// ok use a memory stream as a backup
919
949
context . Destination = new MemoryStream ( ) ;
0 commit comments