U
    楡cxk                     @   s0  d dl mZ d dlZd dlZd dlZd dlZd dlmZmZm	Z	m
Z
mZ d dlmZmZmZmZmZ d dlmZmZ d dlmZ dZzd dlmZ d	ZW n ek
r   Y nX d
ZG dd deZG dd deZG dd dZG dd deZ G dd deZ!G dd deZ"G dd dZ#G dd dZ$dS )    )BytesION)msb_sizestream_copyapply_delta_dataconnect_deltasdelta_types)allocate_memory	LazyMixinmake_shawriteclose)	NULL_BYTE
BYTE_SPACE)force_bytesF)apply_deltaT)	DecompressMemMapReaderFDCompressedSha1WriterDeltaApplyReader
Sha1WriterFlexibleSha1WriterZippedStoreShaWriterr   FDStream
NullStreamc                   @   s~   e Zd ZdZdZdZdddZdd Zd	d
 Zdd Z	e
dddZdd Zdd Zdd ZeeddfddZdddZdS ) r   a  Reads data in chunks from a memory map and decompresses it. The client sees
    only the uncompressed data, respective file-like read calls are handling on-demand
    buffered decompression accordingly

    A constraint on the total size of bytes is activated, simulating
    a logical file within a possibly larger physical memory area

    To read efficiently, you clearly don't want to read individual bytes, instead,
    read a few kilobytes at least.

    **Note:** The chunk-size should be carefully selected as it will involve quite a bit
        of string copying due to the way the zlib is implemented. Its very wasteful,
        hence we try to find a good tradeoff between allocation time and number of
        times we actually allocate. An own zlib implementation would be good here
        to better support streamed reading - it would only need to keep the mmap
        and decompress it into chunks, that's all ... )_m_zip_buf_buflen_br_cws_cwe_s_close_cbr_phii   Nc                 C   sR   || _ t | _d| _d| _|dk	r*|| _d| _d| _d| _	d| _
d| _|| _dS )z|Initialize with mmap for stream reading
        :param m: must be content data - use new if you have object data and no sizeNr   F)r   zlibdecompressobjr   r   r   r    r   r   r   r"   r#   r!   )selfmclose_on_deletionsize r*   0/tmp/pip-unpacked-wheel-jjg5dubb/gitdb/stream.py__init__E   s    
zDecompressMemMapReader.__init__c                 C   s   |dkst |   d S )Nr    )AssertionError_parse_header_infor&   attrr*   r*   r+   _set_cache_U   s    z"DecompressMemMapReader._set_cache_c                 C   s   |    d S N)r   r&   r*   r*   r+   __del__[   s    zDecompressMemMapReader.__del__c                 C   s~   d}|| _ | |}|t}|d| t\}}t|}|| _ d| _|d7 }t||d | _	t
|| | _d| _||fS )zIf this stream contains object data, parse the header info and skip the
        stream to a point where each read will yield object content

        :return: parsed type_string, sizei    Nr      T)r    readfindr   splitr   intr   r   r   lenr   r#   )r&   ZmaxbhdrZhdrendtypr)   r*   r*   r+   r.   ^   s    

z)DecompressMemMapReader._parse_header_infoFc                 C   s"   t ||d}| \}}|||fS )a  Create a new DecompressMemMapReader instance for acting as a read-only stream
        This method parses the object header from m and returns the parsed
        type and size, as well as the created stream instance.

        :param m: memory map on which to operate. It must be object data ( header + contents )
        :param close_on_deletion: if True, the memory map will be closed once we are
            being deletedr   )r   r.   )r&   r'   r(   instr<   r)   r*   r*   r+   new{   s    	zDecompressMemMapReader.newc                 C   s   | j S )z8:return: random access compatible data we are working on)r   r3   r*   r*   r+   data   s    zDecompressMemMapReader.datac                 C   s&   | j r"t| jdr| j  d| _ dS )zClose our underlying stream of compressed bytes if this was allowed during initialization
        :return: True if we closed the underlying stream
        :note: can be called safely 
        r   FN)r!   hasattrr   r   r3   r*   r*   r+   r      s    
zDecompressMemMapReader.closec                 C   sx   | j | jkrr| jjsrd| _ t| jdrD| jjtjkrj| t	j
 q&n&| jjsj| jt| jkrj| t	j
 qD| j| _ | jS )z
        :return: number of compressed bytes read. This includes the bytes it
            took to decompress the header ( if there was one )r   status)r   r    r   unused_datar@   rA   r$   ZZ_OKr6   mmapPAGESIZEr"   r:   r   r3   r*   r*   r+   compressed_bytes_read   s    z,DecompressMemMapReader.compressed_bytes_readSEEK_SETr   c                 C   sV   |dks|t tddkr tdt | _d | _ | _ | _| _	| j
rRd| _
| `dS )zgAllows to reset the stream to restart reading
        :raise ValueError: If offset and whence are not 0r   rF   Can only seek to position 0FN)getattros
ValueErrorr$   r%   r   r   r   r   r"   r#   r    r&   offsetwhencer*   r*   r+   seek   s    
zDecompressMemMapReader.seekc                 C   s  |dk r| j | j }nt|| j | j }|dkr4dS d}| jr| j|krt| j|}|  j|8  _|  j|7  _|S | j }|| j8 }|  j| j7  _d| _d | _| jj}|r| jt	| | _
| j
| | _n| j
}| j| _
|| | _| j| j
 dk r| j
d | _| j| j
| j }| j
t	| | _| j||}tjdkrZtjdksZt	| jj}nt	| jjt	| jj }|  jt	|| 7  _|  jt	|7  _|r|| }|rt	|t	| |k r| j| j k r|| |t	| 7 }|S )Nr5   r          )z1.2.7z1.2.5darwin)r    r   minr   r   r6   r   unconsumed_tailr   r:   r   r   
decompressr$   ZLIB_VERSIONsysplatformrB   r"   )r&   r)   ZdattailZcwsZindataZdcompdatZunused_datalenr*   r*   r+   r6      sN    



	*zDecompressMemMapReader.read)N)F)rO   )__name__
__module____qualname____doc__	__slots__Zmax_read_sizer,   r1   r4   r.   classmethodr>   r?   r   rE   rH   rI   rN   r6   r*   r*   r*   r+   r   .   s   
1r   c                   @   s   e Zd ZdZdZdZdd Zdd Zdd	 Ze	s6eZ
neZ
dddZeedd
fddZedd Zedd Zedd Zedd ZdS )r   a  A reader which dynamically applies pack deltas to a base object, keeping the
    memory demands to a minimum.

    The size of the final object is only obtainable once all deltas have been
    applied, unless it is retrieved from a pack index.

    The uncompressed Delta has the following layout (MSB being a most significant
    bit encoded dynamic size):

    * MSB Source Size - the size of the base against which the delta was created
    * MSB Target Size - the size of the resulting data after the delta was applied
    * A list of one byte commands (cmd) which are followed by a specific protocol:

     * cmd & 0x80 - copy delta_data[offset:offset+size]

      * Followed by an encoded offset into the delta data
      * Followed by an encoded size of the chunk to copy

     *  cmd & 0x7f - insert

      * insert cmd bytes from the delta buffer into the output stream

     * cmd == 0 - invalid operation ( or error in delta stream )
    )_bstream	_dstreams
_mm_target_sizer   ic                 C   s:   t |dkstd|d | _t|dd | _d| _dS )zInitialize this instance with a list of streams, the first stream being
        the delta to apply on top of all following deltas, the last stream being the
        base object onto which to apply the deltasr5   z+Need at least one delta and one base streamrO   Nr   )r:   r-   r`   tuplera   r   )r&   stream_listr*   r*   r+   r,   h  s    
zDeltaApplyReader.__init__c                 C   s   t | jdkr| |S t| j}| dkrBd| _td| _d S | | _t| j| _t| jj	}t
| jj|j| jj	dtj  | jj}||| | jd d S )Nr5   r      )r:   ra   _set_cache_brute_r   Zrboundrc   r   rb   r`   r)   r   r6   r   rC   rD   applyrN   )r&   r0   Zdclbbufr   r*   r*   r+   _set_cache_too_slow_without_cr  s    



z.DeltaApplyReader._set_cache_too_slow_without_cc                 C   sn  t  }d}| jD ]L}|d}t|\}}t||\}}|||d |||f t||}q| jj}	|}t| jdkrt|	| }	}t	|	}
t
| jj|
j|	dtj  t	|}d}tt|t| jD ]\\}}}}}t	|j| }|| t
|j|j|jdtj  dt kr$t|
|| nt|
||t||j ||
 }
}|
d |d |}q|
| _|| _dS )z*If we are here, we apply the actual deltasr   i   Nr5   rf   c_apply_delta)listra   r6   r   appendmaxr`   r)   r:   r   r   r   rC   rD   zipreversedglobalsrk   r   rN   rb   rc   )r&   r0   Zbuffer_info_listZmax_target_sizeZdstreambufrL   Zsrc_sizeZtarget_sizeZ	base_sizeri   ZtbufZfinal_target_sizedbufZddatar*   r*   r+   rg     s:    

$



z"DeltaApplyReader._set_cache_brute_r   c                 C   sB   | j | j }|dk s||kr |}| j|}|  jt|7  _|S )Nr5   )rc   r   rb   r6   r:   )r&   countblr?   r*   r*   r+   r6     s    zDeltaApplyReader.readrF   c                 C   s6   |dks|t tddkr tdd| _| jd dS )zhAllows to reset the stream to restart reading

        :raise ValueError: If offset and whence are not 0r   rF   rG   N)rH   rI   rJ   r   rb   rN   rK   r*   r*   r+   rN     s    zDeltaApplyReader.seekc                 C   s<   t |dk rtd|d jtkr4td|d j | |S )a  
        Convert the given list of streams into a stream which resolves deltas
        when reading from it.

        :param stream_list: two or more stream objects, first stream is a Delta
            to the object that you want to resolve, followed by N additional delta
            streams. The list's last stream must be a non-delta stream.

        :return: Non-Delta OPackStream object whose stream can be used to obtain
            the decompressed resolved data
        :raise ValueError: if the stream list cannot be handled   zNeed at least two streamsrO   zNCannot resolve deltas if there is no base object stream, last one was type: %s)r:   rJ   type_idr   type)clsre   r*   r*   r+   r>     s    zDeltaApplyReader.newc                 C   s   | j jS r2   )r`   rx   r3   r*   r*   r+   rx     s    zDeltaApplyReader.typec                 C   s   | j jS r2   )r`   rw   r3   r*   r*   r+   rw     s    zDeltaApplyReader.type_idc                 C   s   | j S )z3:return: number of uncompressed bytes in the stream)rc   r3   r*   r*   r+   r)     s    zDeltaApplyReader.sizeN)r   )rZ   r[   r\   r]   r^   Zk_max_memory_mover,   rj   rg   has_perf_modr1   r6   rH   rI   rN   r_   r>   propertyrx   rw   r)   r*   r*   r*   r+   r   B  s&   	
"K




r   c                   @   s.   e Zd ZdZdZdd Zdd Zddd	Zd
S )r   zpSimple stream writer which produces a sha whenever you like as it degests
    everything it is supposed to writesha1c                 C   s   t  | _d S r2   )r
   r|   r3   r*   r*   r+   r,   2  s    zSha1Writer.__init__c                 C   s   | j | t|S )z{:raise IOError: If not all bytes could be written
        :param data: byte object
        :return: length of incoming data)r|   updater:   r&   r?   r*   r*   r+   r   7  s    zSha1Writer.writeFc                 C   s   |r| j  S | j  S )z]:return: sha so far
        :param as_hex: if True, sha will be hex-encoded, binary otherwise)r|   	hexdigestdigest)r&   Zas_hexr*   r*   r+   shaD  s    
zSha1Writer.shaN)F)rZ   r[   r\   r]   r^   r,   r   r   r*   r*   r*   r+   r   ,  s
   r   c                   @   s$   e Zd ZdZdZdd Zdd ZdS )r   zZWriter producing a sha1 while passing on the written bytes to the given
    write functionwriterc                 C   s   t |  || _d S r2   )r   r,   r   )r&   r   r*   r*   r+   r,   T  s    
zFlexibleSha1Writer.__init__c                 C   s   t | | | | d S r2   )r   r   r   r~   r*   r*   r+   r   X  s    zFlexibleSha1Writer.writeN)rZ   r[   r\   r]   r^   r,   r   r*   r*   r*   r+   r   N  s   r   c                   @   sP   e Zd ZdZdZdd Zdd Zdd Zd	d
 Ze	e
ddfddZdd ZdS )r   z=Remembers everything someone writes to it and generates a sha)rr   ro   c                 C   s$   t |  t | _ttj| _d S r2   )r   r,   r   rr   r$   compressobjZ_BEST_SPEEDro   r3   r*   r*   r+   r,   b  s    
zZippedStoreShaWriter.__init__c                 C   s   t | j|S r2   )rH   rr   r/   r*   r*   r+   __getattr__g  s    z ZippedStoreShaWriter.__getattr__c                 C   s$   t | |}| j| j| |S r2   )r   r   rr   ro   compress)r&   r?   alenr*   r*   r+   r   j  s    zZippedStoreShaWriter.writec                 C   s   | j | j  d S r2   )rr   r   ro   flushr3   r*   r*   r+   r   p  s    zZippedStoreShaWriter.closerF   r   c                 C   s0   |dks|t tddkr td| jd dS )z`Seeking currently only supports to rewind written data
        Multiple writes are not supportedr   rF   rG   N)rH   rI   rJ   rr   rN   rK   r*   r*   r+   rN   s  s    zZippedStoreShaWriter.seekc                 C   s
   | j  S )zA:return: string value from the current stream position to the end)rr   getvaluer3   r*   r*   r+   r   {  s    zZippedStoreShaWriter.getvalueN)rZ   r[   r\   r]   r^   r,   r   r   r   rH   rI   rN   r   r*   r*   r*   r+   r   ]  s   r   c                       s<   e Zd ZdZdZedZ fddZdd Zdd	 Z	  Z
S )
r   zDigests data written to it, making the sha available, then compress the
    data and write it to the file descriptor

    **Note:** operates on raw file descriptors
    **Note:** for this to work, you have to use the close-method of this instance)fdr|   ro   z+Failed to write all bytes to filedescriptorc                    s"   t    || _ttj| _d S r2   )superr,   r   r$   r   r   ro   r&   r   	__class__r*   r+   r,     s    
zFDCompressedSha1Writer.__init__c                 C   s>   | j | | j|}t| j|}|t|kr6| jt|S )zZ:raise IOError: If not all bytes could be written
        :return: length of incoming data)r|   r}   ro   r   r   r   r:   exc)r&   r?   cdataZbytes_writtenr*   r*   r+   r     s    zFDCompressedSha1Writer.writec                 C   s.   | j  }t| j|t|kr$| jt| jS r2   )ro   r   r   r   r:   r   r   )r&   	remainderr*   r*   r+   r     s    
zFDCompressedSha1Writer.close)rZ   r[   r\   r]   r^   IOErrorr   r,   r   r   __classcell__r*   r*   r   r+   r     s   r   c                   @   sF   e Zd ZdZdZdd Zdd Zddd	Zd
d Zdd Z	dd Z
dS )r   zA simple wrapper providing the most basic functions on a file descriptor
    with the fileobject interface. Cannot use os.fdopen as the resulting stream
    takes ownership_fd_posc                 C   s   || _ d| _d S Nr   r   r   r*   r*   r+   r,     s    zFDStream.__init__c                 C   s$   |  j t|7  _ t| j| d S r2   )r   r:   rI   r   r   r~   r*   r*   r+   r     s    zFDStream.writer   c                 C   s:   |dkrt j| j}t | j|}|  jt|7  _|S r   )rI   pathgetsizeZ	_filepathr6   r   r   r:   )r&   rt   bytesr*   r*   r+   r6     s
    zFDStream.readc                 C   s   | j S r2   )r   r3   r*   r*   r+   fileno  s    zFDStream.filenoc                 C   s   | j S r2   )r   r3   r*   r*   r+   tell  s    zFDStream.tellc                 C   s   t | j d S r2   )r   r   r3   r*   r*   r+   r     s    zFDStream.closeN)r   )rZ   r[   r\   r]   r^   r,   r   r6   r   r   r   r*   r*   r*   r+   r     s   
	r   c                   @   s0   e Zd ZdZe Zd
ddZdd Zdd Zd	S )r   zVA stream that does nothing but providing a stream interface.
    Use it like /dev/nullr   c                 C   s   dS )N r*   )r&   r)   r*   r*   r+   r6     s    zNullStream.readc                 C   s   d S r2   r*   r3   r*   r*   r+   r     s    zNullStream.closec                 C   s   t |S r2   )r:   r~   r*   r*   r+   r     s    zNullStream.writeN)r   )	rZ   r[   r\   r]   rd   r^   r6   r   r   r*   r*   r*   r+   r     s
   
r   )%ior   rC   rI   rW   r$   Z	gitdb.funr   r   r   r   r   Z
gitdb.utilr   r	   r
   r   r   Zgitdb.constr   r   Zgitdb.utils.encodingr   rz   Zgitdb_speedups._perfr   rk   ImportError__all__r   r   r   r   r   r   r   r   r*   r*   r*   r+   <module>   s4      k"#("