@@ -15,7 +15,8 @@ use serde::{Deserialize, Serialize};
1515use std:: collections:: { HashMap , HashSet } ;
1616use std:: fmt:: Debug ;
1717use std:: fs:: File ;
18- use std:: io:: { prelude:: * , BufReader } ;
18+ use std:: io:: { prelude:: * , BufReader , BufWriter } ;
19+ use std:: marker:: PhantomData ;
1920use std:: path:: { Path , PathBuf } ;
2021use std:: str:: FromStr ;
2122use thiserror:: Error ;
@@ -141,7 +142,7 @@ pub struct BlobWriter<'a> {
141142 /// Compute checksum
142143 hash : Hasher ,
143144 /// Target file
144- target : Option < cap_tempfile:: TempFile < ' a > > ,
145+ target : Option < BufWriter < cap_tempfile:: TempFile < ' a > > > ,
145146 size : u64 ,
146147}
147148
@@ -154,13 +155,6 @@ impl Debug for BlobWriter<'_> {
154155 }
155156}
156157
157- /// Create an OCI tar+gzip layer.
158- pub struct GzipLayerWriter < ' a > ( Sha256Writer < GzEncoder < BlobWriter < ' a > > > ) ;
159-
160- #[ cfg( feature = "zstd" ) ]
161- /// Writer for a OCI tar+zstd layer.
162- pub struct ZstdLayerWriter < ' a > ( Sha256Writer < zstd:: Encoder < ' static , BlobWriter < ' a > > > ) ;
163-
164158#[ derive( Debug ) ]
165159/// An opened OCI directory.
166160pub struct OciDir {
@@ -280,17 +274,32 @@ impl OciDir {
280274 BlobWriter :: new ( & self . dir )
281275 }
282276
277+ /// Create a layer writer with a custom encoder and
278+ /// media type
279+ pub fn create_custom_layer < ' a , W : WriteComplete < BlobWriter < ' a > > > (
280+ & ' a self ,
281+ create : impl FnOnce ( BlobWriter < ' a > ) -> std:: io:: Result < W > ,
282+ media_type : MediaType ,
283+ ) -> Result < LayerWriter < ' a , W > > {
284+ let bw = BlobWriter :: new ( & self . dir ) ?;
285+ Ok ( LayerWriter :: new ( create ( bw) ?, media_type) )
286+ }
287+
283288 /// Create a writer for a new gzip+tar blob; the contents
284289 /// are not parsed, but are expected to be a tarball.
285- pub fn create_gzip_layer ( & self , c : Option < flate2:: Compression > ) -> Result < GzipLayerWriter > {
286- GzipLayerWriter :: new ( & self . dir , c)
290+ pub fn create_gzip_layer < ' a > (
291+ & ' a self ,
292+ c : Option < flate2:: Compression > ,
293+ ) -> Result < LayerWriter < ' a , GzEncoder < BlobWriter < ' a > > > > {
294+ let creator = |bw : BlobWriter < ' a > | Ok ( GzEncoder :: new ( bw, c. unwrap_or_default ( ) ) ) ;
295+ self . create_custom_layer ( creator, MediaType :: ImageLayerGzip )
287296 }
288297
289298 /// Create a tar output stream, backed by a blob
290299 pub fn create_layer (
291300 & self ,
292301 c : Option < flate2:: Compression > ,
293- ) -> Result < tar:: Builder < GzipLayerWriter > > {
302+ ) -> Result < tar:: Builder < LayerWriter < GzEncoder < BlobWriter > > > > {
294303 Ok ( tar:: Builder :: new ( self . create_gzip_layer ( c) ?) )
295304 }
296305
@@ -299,8 +308,12 @@ impl OciDir {
299308 /// are not parsed, but are expected to be a tarball.
300309 ///
301310 /// This method is only available when the `zstd` feature is enabled.
302- pub fn create_layer_zstd ( & self , compression_level : Option < i32 > ) -> Result < ZstdLayerWriter > {
303- ZstdLayerWriter :: new ( & self . dir , compression_level)
311+ pub fn create_layer_zstd < ' a > (
312+ & ' a self ,
313+ compression_level : Option < i32 > ,
314+ ) -> Result < LayerWriter < ' a , zstd:: Encoder < ' static , BlobWriter < ' a > > > > {
315+ let creator = |bw : BlobWriter < ' a > | zstd:: Encoder :: new ( bw, compression_level. unwrap_or ( 0 ) ) ;
316+ self . create_custom_layer ( creator, MediaType :: ImageLayerZstd )
304317 }
305318
306319 #[ cfg( feature = "zstdmt" ) ]
@@ -312,12 +325,17 @@ impl OciDir {
312325 /// [zstd::Encoder::multithread]]
313326 ///
314327 /// This method is only available when the `zstdmt` feature is enabled.
315- pub fn create_layer_zstd_multithread (
316- & self ,
328+ pub fn create_layer_zstd_multithread < ' a > (
329+ & ' a self ,
317330 compression_level : Option < i32 > ,
318331 n_workers : u32 ,
319- ) -> Result < ZstdLayerWriter > {
320- ZstdLayerWriter :: multithread ( & self . dir , compression_level, n_workers)
332+ ) -> Result < LayerWriter < ' a , zstd:: Encoder < ' static , BlobWriter < ' a > > > > {
333+ let creator = |bw : BlobWriter < ' a > | {
334+ let mut encoder = zstd:: Encoder :: new ( bw, compression_level. unwrap_or ( 0 ) ) ?;
335+ encoder. multithread ( n_workers) ?;
336+ Ok ( encoder)
337+ } ;
338+ self . create_custom_layer ( creator, MediaType :: ImageLayerZstd )
321339 }
322340
323341 /// Add a layer to the top of the image stack. The firsh pushed layer becomes the root.
@@ -655,7 +673,7 @@ impl<'a> BlobWriter<'a> {
655673 Ok ( Self {
656674 hash : Hasher :: new ( MessageDigest :: sha256 ( ) ) ?,
657675 // FIXME add ability to choose filename after completion
658- target : Some ( cap_tempfile:: TempFile :: new ( ocidir) ?) ,
676+ target : Some ( BufWriter :: new ( cap_tempfile:: TempFile :: new ( ocidir) ?) ) ,
659677 size : 0 ,
660678 } )
661679 }
@@ -684,7 +702,7 @@ impl<'a> BlobWriter<'a> {
684702 fn complete_as ( mut self , sha256_digest : & str ) -> Result < Blob > {
685703 let destname = & format ! ( "{}/{}" , BLOBDIR , sha256_digest) ;
686704 let target = self . target . take ( ) . unwrap ( ) ;
687- target. replace ( destname) ?;
705+ target. into_inner ( ) . unwrap ( ) . replace ( destname) ?;
688706 Ok ( Blob {
689707 sha256 : Sha256Digest :: from_str ( sha256_digest) . unwrap ( ) ,
690708 size : self . size ,
@@ -700,94 +718,84 @@ impl<'a> BlobWriter<'a> {
700718
701719impl std:: io:: Write for BlobWriter < ' _ > {
702720 fn write ( & mut self , srcbuf : & [ u8 ] ) -> std:: io:: Result < usize > {
703- self . hash . update ( srcbuf) ?;
704- self . target
705- . as_mut ( )
706- . unwrap ( )
707- . as_file_mut ( )
708- . write_all ( srcbuf) ?;
709- self . size += srcbuf. len ( ) as u64 ;
710- Ok ( srcbuf. len ( ) )
721+ let written = self . target . as_mut ( ) . unwrap ( ) . write ( srcbuf) ?;
722+ self . hash . update ( & srcbuf[ ..written] ) ?;
723+ self . size += written as u64 ;
724+ Ok ( written)
711725 }
712726
713727 fn flush ( & mut self ) -> std:: io:: Result < ( ) > {
714728 Ok ( ( ) )
715729 }
716730}
717731
718- impl < ' a > GzipLayerWriter < ' a > {
719- /// Create a writer for a gzip compressed layer blob.
720- fn new ( ocidir : & ' a Dir , c : Option < flate2:: Compression > ) -> Result < Self > {
721- let bw = BlobWriter :: new ( ocidir) ?;
722- let enc = flate2:: write:: GzEncoder :: new ( bw, c. unwrap_or_default ( ) ) ;
723- Ok ( Self ( Sha256Writer :: new ( enc) ) )
724- }
732+ /// A writer that can be finalized to return an inner writer.
733+ pub trait WriteComplete < W > : Write {
734+ fn complete ( self ) -> std:: io:: Result < W > ;
735+ }
725736
726- /// Consume this writer, flushing buffered data and put the blob in place.
727- pub fn complete ( self ) -> Result < Layer > {
728- let ( uncompressed_sha256, enc) = self . 0 . finish ( ) ;
729- let blob = enc. finish ( ) ?. complete ( ) ?;
730- Ok ( Layer {
731- blob,
732- uncompressed_sha256,
733- media_type : MediaType :: ImageLayerGzip ,
734- } )
737+ impl < W > WriteComplete < W > for GzEncoder < W >
738+ where
739+ W : Write ,
740+ {
741+ fn complete ( self ) -> std:: io:: Result < W > {
742+ self . finish ( )
735743 }
736744}
737745
738- impl std:: io:: Write for GzipLayerWriter < ' _ > {
739- fn write ( & mut self , data : & [ u8 ] ) -> std:: io:: Result < usize > {
740- self . 0 . write ( data)
746+ #[ cfg( feature = "zstd" ) ]
747+ impl < W > WriteComplete < W > for zstd:: Encoder < ' _ , W >
748+ where
749+ W : Write ,
750+ {
751+ fn complete ( self ) -> std:: io:: Result < W > {
752+ self . finish ( )
741753 }
754+ }
742755
743- fn flush ( & mut self ) -> std:: io:: Result < ( ) > {
744- self . 0 . flush ( )
745- }
756+ /// A writer for a layer.
757+ pub struct LayerWriter < ' a , W >
758+ where
759+ W : WriteComplete < BlobWriter < ' a > > ,
760+ {
761+ inner : Sha256Writer < W > ,
762+ media_type : MediaType ,
763+ marker : PhantomData < & ' a ( ) > ,
746764}
747765
748- #[ cfg( feature = "zstd" ) ]
749- impl < ' a > ZstdLayerWriter < ' a > {
750- /// Create a writer for a gzip compressed layer blob.
751- fn new ( ocidir : & ' a Dir , c : Option < i32 > ) -> Result < Self > {
752- let bw = BlobWriter :: new ( ocidir) ?;
753- let encoder = zstd:: Encoder :: new ( bw, c. unwrap_or ( 0 ) ) ?;
754- Ok ( Self ( Sha256Writer :: new ( encoder) ) )
766+ impl < ' a , W > LayerWriter < ' a , W >
767+ where
768+ W : WriteComplete < BlobWriter < ' a > > ,
769+ {
770+ pub fn new ( inner : W , media_type : oci_image:: MediaType ) -> Self {
771+ Self {
772+ inner : Sha256Writer :: new ( inner) ,
773+ media_type,
774+ marker : PhantomData ,
775+ }
755776 }
756777
757- /// Consume this writer, flushing buffered data and put the blob in place.
758778 pub fn complete ( self ) -> Result < Layer > {
759- let ( uncompressed_sha256, enc) = self . 0 . finish ( ) ;
760- let blob = enc. finish ( ) ?. complete ( ) ?;
779+ let ( uncompressed_sha256, enc) = self . inner . finish ( ) ;
780+ let blob = enc. complete ( ) ?. complete ( ) ?;
761781 Ok ( Layer {
762782 blob,
763783 uncompressed_sha256,
764- media_type : MediaType :: ImageLayerZstd ,
784+ media_type : self . media_type ,
765785 } )
766786 }
767787}
768788
769- #[ cfg( feature = "zstdmt" ) ]
770- impl < ' a > ZstdLayerWriter < ' a > {
771- /// Create a writer for a zstd compressed layer blob, with multithreaded compression enabled.
772- ///
773- /// The `n_workers` parameter specifies the number of threads to use for compression, per
774- /// [Encoder::multithread]]
775- fn multithread ( ocidir : & ' a Dir , c : Option < i32 > , n_workers : u32 ) -> Result < Self > {
776- let bw = BlobWriter :: new ( ocidir) ?;
777- let mut encoder = zstd:: Encoder :: new ( bw, c. unwrap_or ( 0 ) ) ?;
778- encoder. multithread ( n_workers) ?;
779- Ok ( Self ( Sha256Writer :: new ( encoder) ) )
780- }
781- }
782-
783- #[ cfg( feature = "zstd" ) ]
784- impl std:: io:: Write for ZstdLayerWriter < ' _ > {
789+ impl < ' a , W > std:: io:: Write for LayerWriter < ' a , W >
790+ where
791+ W : WriteComplete < BlobWriter < ' a > > ,
792+ {
785793 fn write ( & mut self , data : & [ u8 ] ) -> std:: io:: Result < usize > {
786- self . 0 . write ( data)
794+ self . inner . write ( data)
787795 }
788796
789797 fn flush ( & mut self ) -> std:: io:: Result < ( ) > {
790- self . 0 . flush ( )
798+ self . inner . flush ( )
791799 }
792800}
793801
0 commit comments