@@ -40,6 +40,33 @@ namespace Xamarin.Tools.Zip
4040 /// </summary>
4141 public abstract partial class ZipArchive : IDisposable , IEnumerable < ZipEntry >
4242 {
43+ internal class CallbackContext : IDisposable {
44+ bool disposed ;
45+ public Stream Source = null ;
46+ public Stream Destination = null ;
47+
48+ protected virtual void Dispose ( bool disposing )
49+ {
50+ if ( ! disposed ) {
51+ if ( disposing ) {
52+ if ( Source != null ) {
53+ Source . Dispose ( ) ;
54+ Source = null ;
55+ }
56+ if ( Destination != null ) {
57+ Destination . Dispose ( ) ;
58+ Destination = null ;
59+ }
60+ }
61+ disposed = true ;
62+ }
63+ }
64+ public void Dispose ( )
65+ {
66+ Dispose ( true ) ;
67+ GC . SuppressFinalize ( this ) ;
68+ }
69+ }
4370 public const EntryPermissions DefaultFilePermissions = EntryPermissions . OwnerRead | EntryPermissions . OwnerWrite | EntryPermissions . GroupRead | EntryPermissions . WorldRead ;
4471 public const EntryPermissions DefaultDirectoryPermissions = EntryPermissions . OwnerAll | EntryPermissions . GroupRead | EntryPermissions . GroupExecute | EntryPermissions . WorldRead | EntryPermissions . WorldExecute ;
4572
@@ -87,9 +114,12 @@ internal ZipArchive (Stream stream, IPlatformOptions options, OpenFlags flags =
87114 throw new ArgumentNullException ( nameof ( options ) ) ;
88115 Options = options ;
89116 Native . zip_error_t errorp ;
90- var streamHandle = GCHandle . Alloc ( stream , GCHandleType . Normal ) ;
91- IntPtr h = GCHandle . ToIntPtr ( streamHandle ) ;
92- IntPtr source = Native . zip_source_function_create ( callback , h , out errorp ) ;
117+ CallbackContext context = new CallbackContext ( ) {
118+ Source = stream ,
119+ Destination = null ,
120+ } ;
121+ var contextHandle = GCHandle . Alloc ( context , GCHandleType . Normal ) ;
122+ IntPtr source = Native . zip_source_function_create ( callback , GCHandle . ToIntPtr ( contextHandle ) , out errorp ) ;
93123 archive = Native . zip_open_from_source ( source , flags , out errorp ) ;
94124 if ( archive == IntPtr . Zero ) {
95125 // error;
@@ -323,7 +353,10 @@ public ZipEntry AddStream (Stream stream, string archivePath, EntryPermissions p
323353 throw new ArgumentNullException ( nameof ( stream ) ) ;
324354 sources . Add ( stream ) ;
325355 string destPath = EnsureArchivePath ( archivePath ) ;
326- var handle = GCHandle . Alloc ( stream , GCHandleType . Normal ) ;
356+ var context = new CallbackContext ( ) {
357+ Source = stream ,
358+ } ;
359+ var handle = GCHandle . Alloc ( context , GCHandleType . Normal ) ;
327360 IntPtr h = GCHandle . ToIntPtr ( handle ) ;
328361 IntPtr source = Native . zip_source_function ( archive , callback , h ) ;
329362 long index = Native . zip_file_add ( archive , destPath , source , overwriteExisting ? OperationFlags . Overwrite : OperationFlags . None ) ;
@@ -758,9 +791,13 @@ internal static unsafe Int64 stream_callback (IntPtr state, IntPtr data, UInt64
758791 var handle = GCHandle . FromIntPtr ( state ) ;
759792 if ( ! handle . IsAllocated )
760793 return - 1 ;
761- var stream = handle . Target as Stream ;
794+ var context = handle . Target as CallbackContext ;
795+ if ( context == null )
796+ return - 1 ;
797+ var stream = context . Source ;
762798 if ( stream == null )
763799 return - 1 ;
800+ var destination = context . Destination ?? context . Source ;
764801 switch ( cmd ) {
765802 case SourceCommand . Stat :
766803 if ( len < ( UInt64 ) sizeof ( Native . zip_stat_t ) )
@@ -773,36 +810,56 @@ internal static unsafe Int64 stream_callback (IntPtr state, IntPtr data, UInt64
773810 return ( Int64 ) sizeof ( Native . zip_stat_t ) ;
774811
775812 case SourceCommand . Tell :
776- case SourceCommand . TellWrite :
777813 return ( Int64 ) stream . Position ;
814+ case SourceCommand . TellWrite :
815+ return ( Int64 ) destination . Position ;
778816
779817 case SourceCommand . Write :
780818 buffer = ArrayPool < byte > . Shared . Rent ( length ) ;
781819 try {
782820 Marshal . Copy ( data , buffer , 0 , length ) ;
783- stream . Write ( buffer , 0 , length ) ;
821+ destination . Write ( buffer , 0 , length ) ;
784822 return length ;
785823 } finally {
786824 ArrayPool < byte > . Shared . Return ( buffer ) ;
787825 }
788826
789827 case SourceCommand . SeekWrite :
790- case SourceCommand . Seek :
791828 Native . zip_error_t error ;
792- Int64 offset = Native . zip_source_seek_compute_offset ( ( UInt64 ) stream . Position , ( UInt64 ) stream . Length , data , len , out error ) ;
793- if ( offset < 0 )
829+ Int64 offset = Native . zip_source_seek_compute_offset ( ( UInt64 ) destination . Position , ( UInt64 ) destination . Length , data , len , out error ) ;
830+ if ( offset < 0 ) {
831+ return offset ;
832+ }
833+ if ( offset != stream . Seek ( offset , SeekOrigin . Begin ) ) {
834+ return - 1 ;
835+ }
836+ break ;
837+ case SourceCommand . Seek :
838+ offset = Native . zip_source_seek_compute_offset ( ( UInt64 ) stream . Position , ( UInt64 ) stream . Length , data , len , out error ) ;
839+ if ( offset < 0 ) {
794840 return offset ;
795- stream . Seek ( ( long ) offset , SeekOrigin . Begin ) ;
841+ }
842+ if ( offset != stream . Seek ( offset , SeekOrigin . Begin ) ) {
843+ return - 1 ;
844+ }
796845 break ;
797846
798847 case SourceCommand . CommitWrite :
799- stream . Flush ( ) ;
848+ destination . Flush ( ) ;
849+ stream . Position = 0 ;
850+ destination . Position = 0 ;
851+ stream . SetLength ( destination . Length ) ;
852+ destination . CopyTo ( stream ) ;
853+ stream . Position = 0 ;
854+ destination . Dispose ( ) ;
855+ context . Destination = null ;
856+ break ;
857+ case SourceCommand . RollbackWrite :
858+ // err do something?
800859 break ;
801860
802861 case SourceCommand . Read :
803- if ( length > stream . Length - stream . Position ) {
804- length = ( int ) ( stream . Length - stream . Position ) ;
805- }
862+ length = ( int ) Math . Min ( stream . Length - stream . Position , length ) ;
806863 buffer = ArrayPool < byte > . Shared . Rent ( length ) ;
807864 try {
808865 int bytesRead = stream . Read ( buffer , 0 , length ) ;
@@ -811,8 +868,11 @@ internal static unsafe Int64 stream_callback (IntPtr state, IntPtr data, UInt64
811868 } finally {
812869 ArrayPool < byte > . Shared . Return ( buffer ) ;
813870 }
814-
815871 case SourceCommand . BeginWrite :
872+ context . Destination = new MemoryStream ( ) ;
873+ destination = context . Destination ;
874+ destination . Position = 0 ;
875+ break ;
816876 case SourceCommand . Open :
817877 stream . Position = 0 ;
818878 return 0 ;
@@ -826,6 +886,12 @@ internal static unsafe Int64 stream_callback (IntPtr state, IntPtr data, UInt64
826886 handle . Free ( ) ;
827887 break ;
828888
889+ case SourceCommand . Error :
890+ break ;
891+
892+ case SourceCommand . Remove :
893+ break ;
894+
829895 case SourceCommand . Supports :
830896 var supports = ( Int64 ) Native . zip_source_make_command_bitmap (
831897 SourceCommand . Open ,
0 commit comments