B
    	Z\ˀ                 @   s  d Z ddlmZmZmZ ddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlZddlZddlmZ eeZdZddd eD Zejd d	kZereZeZeZeZeZn&e   e!Ze"Ze!Zee#fZeej$fZe
j%Z&e
j'Z(d
d Z)dd Z*dd Z+dd Z,G dd de-Z.G dd de-Z/G dd de/Z0G dd de-Z1d)ddZ2d*ddZ3d+ddZ4d,d d!Z5e4Z6e2Z7G d"d# d#e-Z8G d$d% d%e8Z9G d&d' d'e8Z:e9e:gZ;ed(krddl<Z<e<=  dS )-a!  
Python implementation of the Binary Structured Data Format (BSDF).

BSDF is a binary format for serializing structured (scientific) data.
See http://bsdf.io for more information.

This is the reference implementation, which is relatively relatively
sophisticated, providing e.g. lazy loading of blobs and streamed
reading/writing. A simpler Python implementation is available as
``bsdf_lite.py``.

This module has no dependencies and works on Python 2.7 and 3.4+.

Note: on Legacy Python (Python 2.7), non-Unicode strings are encoded as bytes.
    )absolute_importdivisionprint_functionN)BytesIO)      r   .c             c   s   | ]}t |V  qd S )N)str).0i r   4lib/python3.7/site-packages/imageio/plugins/_bsdf.py	<genexpr>0   s    r      c             C   s"   | dkrt d| S t dd| S dS )zE Encode an unsigned integer into a variable sized blob of bytes.
       z<Bz<BQ   N)spack)xr   r   r   lencodeJ   s    
r   c             C   s4   t d| dd }|dkr0t d| dd }|S )z- Decode an unsigned integer from a file.
    z<Br   r   r   z<Q   )	strunpackread)fnr   r   r   	lendecodeZ   s    r   c             C   s2   |dk	r*| d}|  tt| | S | S dS )z? Encode the type identifier, with or without extension id.
    NzUTF-8)encodeupperr   len)bext_idbbr   r   r   encode_type_idc   s    
r!   c             C   s*   t | to(td| tjo(td| dkS )z> Use of str.isidentifier() for Legacy Python, but slower.
    z^\w+$z^[0-9]N)
isinstancestring_typesrematchUNICODE)sr   r   r   _isidentifierm   s    
r(   c               @   sd   e Zd ZdZdddZddd	Zd
d Zdd Zdd Zdd Z	dd Z
dd Zdd Zdd ZdS )BsdfSerializera{   Instances of this class represent a BSDF encoder/decoder.

    It acts as a placeholder for a set of extensions and encoding/decoding
    options. Use this to predefine extensions and options for high
    performance encoding/decoding. For general use, see the functions
    `save()`, `encode()`, `load()`, and `decode()`.

    This implementation of BSDF supports streaming lists (keep adding
    to a list after writing the main file), lazy loading of blobs, and
    in-place editing of blobs (for streams opened with a+).

    Options for encoding:

    * compression (int or str): ``0`` or "no" for no compression (default),
      ``1`` or "zlib" for Zlib compression (same as zip files and PNG), and
      ``2`` or "bz2" for Bz2 compression (more compact but slower writing).
      Note that some BSDF implementations (e.g. JavaScript) may not support
      compression.
    * use_checksum (bool): whether to include a checksum with binary blobs.
    * float64 (bool): Whether to write floats as 64 bit (default) or 32 bit.

    Options for decoding:

    * load_streaming (bool): if True, and the final object in the structure was
      a stream, will make it available as a stream in the decoded object.
    * lazy_blob (bool): if True, bytes are represented as Blob objects that can
      be used to lazily access the data, and also overwrite the data if the
      file is open in a+ mode.
    Nc             K   s@   i | _ i | _|d krt}x|D ]}| | qW | jf | d S )N)_extensions_extensions_by_clsstandard_extensionsadd_extension_parse_options)self
extensionsoptions	extensionr   r   r   __init__   s    
zBsdfSerializer.__init__r   FTc             C   sh   t |tr&dddd}|| |}|dkr6td|| _t|| _t|| _t|| _	t|| _
d S )Nr   r   r   )Znozlibbz2)r   r   r   z3Compression must be 0, 1, 2, "no", "zlib", or "bz2")r"   r#   getlower	TypeError_compressionbool_use_checksum_float64_load_streaming
_lazy_blob)r/   compressionuse_checksumZfloat64Zload_streamingZ	lazy_blobmr   r   r   r.      s    




zBsdfSerializer._parse_optionsc             C   s   t |trt|tstd| }|j}t |ts:tdt|dksRt|dkrZtd|| j	krrt
d|  |j}|sg }nt |ttfr|}n|g}x|D ]}t |tstdqW x|D ]}||jf| j|< qW || j	|< |S )z Add an extension to this serializer instance, which must be
        a subclass of Extension. Can be used as a decorator.
        z*add_extension() expects a Extension class.zExtension name must be str.r   r   z<Extension names must be nonempty and shorter than 251 chars.zABSDF warning: overwriting extension "%s", consider removing firstz Extension classes must be types.)r"   type
issubclass	Extensionr8   namer	   r   	NameErrorr*   loggerwarningclstuplelist
classtypesr   r+   )r/   Zextension_classr2   rE   rI   Zclssr   r   r   r-      s4    





zBsdfSerializer.add_extensionc             C   sb   t |tstd|| jkr(| j| x4t| j D ]"}| j| d |kr8| j| q8W dS )z0 Remove a converted by its unique name.
        zExtension name must be str.r   N)r"   r	   r8   r*   poprK   r+   keys)r/   rE   rI   r   r   r   remove_extension   s    

zBsdfSerializer.remove_extensionc             C   s  t }|dkr ||d| n|dkr<||d| n|dkrX||d| nht|trd|  krvdkrn n||d	|td
|  n||d|td|  nt|tr| jr||d|td|  n||d|td|  nt|tr>|d}||d|t	t
|  || nt|ttfr||d|t	t
|  x|D ]}| |||d qpW n2t|tr,||d|t	t
|  xp| D ]d\}}tr| stnt|st|d}	|t	t
|	 ||	 | |||d qW nt|trh||d| t|| j| jd}
|
| nXt|tr||d| || n.t|tr|jdkrtdn2t|tr||d|tddd  ntdt
|dkrtd|| ||| j| j  n|dk	r0td| | j!"|j#d}|dkrx6| j$ D ]$\}}|%| |rV||jf}P qVW d}|dk	r|\}}| ||| ||| nd}t||j#j& dS ) z  Main encoder function.
        N   vT   yF   ni i     hh   iz<q   dz<d   fz<fzUTF-8   s   l   m   b)r?   r@   wz$Cannot serialize a read-mode stream.z<BQ   r   zOnly ListStream is supportedz"Can only have one stream per file.zExtension %s wronfully encodes object to another extension object (though it may encode to a list/dict that contains other extension objects).zJClass %r is not a valid base BSDF type, nor is it handled by an extension.)'r!   writer"   integer_typesr   floatr<   unicode_typesr   r   r   rK   rJ   _encodedictitemsPY3isidentifierAssertionErrorr(   bytesBlobr9   r;   _to_file
BaseStreammode
ValueError
ListStreamr8   append	_activate_decoder+   r6   	__class__r*   r%   __name__)r/   r   valuestreamsr   r   r    vkeyZname_bblobexrE   cZext_id2Zextension_encodetr   r   r   rb      s    











zBsdfSerializer._encodec                s    d}| }|st n2||krLtd  dd }  |d}nd}|dkr`d}n|dkrpd}n|d	krd
}n|dkrtd  dd }n|dkrtd  dd }n|dkrtd  dd }nn|dkrtd  dd }nL|dkrRtd  dd }|dkr>td  dd }  |d}n|dkrVtd  dd }|dkr|dk}td  dd }jrt|r|nd}| jj	 nX|r fddt
|D }n8g }yx|	  qW W n tk
r   Y nX n6|dkr<td  dd } fddt
|D }n|dkrt }td  dd }|dkrtd  dd }xt
|D ]d}	td  dd }
|
dkrtd  dd }
|
dkst  |
d}	 ||< qW nB|d krBjr,t df}nt d
f}| }ntd!| |dk	rj|d}|dk	r~||}ntd"|  |S )#z  Main decoder function.
        r   z<Br   zUTF-8NrP   rQ   TrR   FrS   z<hr   rU   z<qr   rW   z<f   rV   z<drX   r   z<QrY      rc                s   g | ]}  qS r   )rq   )r
   r   )r   r/   r   r   
<listcomp>z  s    z*BsdfSerializer._decode.<locals>.<listcomp>c                s   g | ]}  qS r   )rq   )r
   r   )r   r/   r   r   r     s    rZ   r[   zParse error %rz'BSDF warning: no extension found for %r)r   r7   EOFErrorr   decoder=   rn   rp   rb   rq   rangero   rc   rg   r>   ri   	get_bytesRuntimeErrorr*   r6   rG   rH   )r/   r   charrz   r   r   rt   Zn_sclosedr   Zn_namerE   rx   r2   r   )r   r/   r   rq   K  s    













zBsdfSerializer._decodec             C   s   t  }| || | S )z) Save the given object to bytes.
        )r   savegetvalue)r/   obr   r   r   r   r     s    zBsdfSerializer.encodec             C   sx   | d | tdtd  | tdtd  g }| |||d t|dkrt|d }|j| krttddS )z: Write the given object to the given file object.
        s   BSDFz<Br   r   Nz8The stream object must be the last object to be encoded.)	r^   structpackVERSIONrb   r   
_start_postellrm   )r/   r   r   ru   streamr   r   r   r     s    
zBsdfSerializer.savec             C   s   t |}| |S )zJ Load the data structure that is BSDF-encoded in the given bytes.
        )r   load)r/   r    r   r   r   r   r     s    zBsdfSerializer.decodec             C   s   | d}|dkrtd| td| dd }td| dd }d||f }|td krrd}t|t|f |td krd	}t|t|f  | |S )
z@ Load a BSDF-encoded object from the given file object.
        r|   s   BSDFz'This does not look like a BSDF file: %rz<Br   r   z%i.%izLReading file with different major version (%s) from the implementation (%s).zWBSDF warning: reading file with higher minor version (%s) than the implementation (%s).)r   r   r   r   __version__rG   rH   rq   )r/   r   Zf4Zmajor_versionZminor_versionZfile_versionr{   r   r   r   r     s    
zBsdfSerializer.load)N)r   FTFF)rs   
__module____qualname____doc__r3   r.   r-   rO   rb   rq   r   r   r   r   r   r   r   r   r)   x   s   
    
+\[r)   c               @   s.   e Zd ZdZd
ddZdd Zedd Zd	S )rk   z Base class for streams.
    r\   c             C   sR   d| _ d| _t|tr"|| _d}n|dkr0d| _|dks<t|| _d | _d| _d S )Nr   r~   r\   )r~   r\   )_i_countr"   intrg   _mode_fr   )r/   rl   r   r   r   r3     s    
zBaseStream.__init__c             C   s4   | j d k	rtd|| _ | j  | _|| _|| _d S )Nz(Stream object cannot be activated twice?)r   IOErrorr   r   rb   rq   )r/   fileZencode_funcZdecode_funcr   r   r   rp     s    
zBaseStream._activatec             C   s   | j S )z. The mode of this stream: 'r' or 'w'.
        )r   )r/   r   r   r   rl     s    zBaseStream.modeN)r\   )rs   r   r   r   r3   rp   propertyrl   r   r   r   r   rk     s   
rk   c               @   sR   e Zd ZdZedd Zedd Zdd Zdd	d
Zdd Z	dd Z
dd ZdS )rn   zk A streamable list object used for writing or reading.
    In read mode, it can also be iterated over.
    c             C   s   | j S )ze The number of elements in the stream (can be -1 for unclosed
        streams in read-mode).
        )r   )r/   r   r   r   count
  s    zListStream.countc             C   s   | j S )z9 The current index of the element to read/write.
        )r   )r/   r   r   r   index  s    zListStream.indexc             C   sj   | j | jkrtd| jdkr&td| jjr6td| | j|| gd |  jd7  _|  j d7  _ dS )z Append an item to the streaming list. The object is immediately
        serialized and written to the underlying file.
        z/Can only append items to the end of the stream.Nz.List stream is not associated with a file yet.zCannot stream to a close file.r   )r   r   r   r   r   rb   )r/   itemr   r   r   ro     s    
zListStream.appendFc             C   s   | j | jkrtd| jdkr&td| jjr6td| j }| j| jd d  | jt	d|rhdnd	 | jt	d
| j  | j| dS )z Close the stream, marking the number of written elements. New
        elements may still be appended, but they won't be read during decoding.
        If ``unstream`` is False, the stream is turned into a regular list
        (not streaming).
        z-Can only close when at the end of the stream.Nz-ListStream is not associated with a file yet.z&Cannot close a stream on a close file.r   r   z<Br   r}   z<Q)
r   r   r   r   r   r   seekr   r^   r   )r/   Zunstreamr   r   r   r   close'  s    

zListStream.closec             C   s   | j dkrtd| jdkr$tdt| jddr:td| jdkrp| j| jkrVt |  jd7  _| | jS y| | j}|  jd7  _|S  tk
r   | j| _t Y nX dS )	zz Read and return the next element in the streaming list.
        Raises StopIteration if the stream is exhausted.
        r~   z$This ListStream in not in read mode.Nz-ListStream is not associated with a file yet.r   z'Cannot read a stream from a close file.r   r   )	r   r   r   getattrr   r   StopIterationrq   r   )r/   Zresr   r   r   next;  s$    


zListStream.nextc             C   s   | j dkrtd| S )Nr~   z/Cannot iterate: ListStream in not in read mode.)r   r   )r/   r   r   r   __iter__T  s    
zListStream.__iter__c             C   s   |   S )N)r   )r/   r   r   r   __next__Y  s    zListStream.__next__N)F)rs   r   r   r   r   r   r   ro   r   r   r   r   r   r   r   r   rn     s   
rn   c               @   sb   e Zd ZdZdddZdd Zdd	 Zd
d Zdd Zdd Z	dd Z
dd Zdd Zdd ZdS )ri   a9   Object to represent a blob of bytes. When used to write a BSDF file,
    it's a wrapper for bytes plus properties such as what compression to apply.
    When used to read a BSDF file, it can be used to read the data lazily, and
    also modify the data if reading in 'r+' mode and the blob isn't compressed.
    r   Fc             C   s   t |tr8d | _| ||| _|| _| j| | _|| _nRt |t	rt
|dkrt|d dr|\| _}d | _| | j| d| _ntdd S )Nr   r   r   FzWrong argument to create Blob.)r"   rh   r   _from_bytes
compressedr?   	used_sizeallocated_sizer@   rJ   r   hasattr
_from_file	_modifiedr8   )r/   r    r?   Z
extra_sizer@   
allow_seekr   r   r   r3   g  s    
$
zBlob.__init__c             C   s^   |dkr|}n8|dkr$t |d}n"|dkr:t|d}ndsFtdt|| _t|| _|S )z, When used to wrap bytes in a blob.
        r   r   	   r   FzUnknown compression identifier)r4   compressr5   rg   r   	data_sizer   )r/   rt   r?   r   r   r   r   r   v  s    

zBlob._from_bytesc             C   s:  | j dkrJ| jdkrJ|td| j  |td| j |t| j n<|tdd| j  |tdd| j |tdd| j |td| j | jr|dt	| j
   n
|d | jdkrd	| d
 d	  }|td| |d|  n|tdd || j
 |d| j | j   dS )zL Private friend method called by encoder to write a blob to a file.
        r   r   z<Bz<BQr   B       r   r   N)r   r?   r^   r   r   r   r   r@   hashlibmd5r   digestr   )r/   r   	alignmentr   r   r   rj     s$    
zBlob._to_filec       
      C   sj  t d|dd }|dkr0t d|dd }t d|dd }|dkr`t d|dd }t d|dd }|dkrt d|dd }t d|dd }t d|dd }|r|d}t d|dd }	||	 |r| | _| j| | _|| j|  n&d| _d| _||| _|||  |	| _|| _|rN|nd| _	|| _
|| _|| _dS )	z2 Used when a blob is read by the decoder.
        z<Br   r   r   z<Qr      N)r   r   r   	start_posend_posr   r   r   r?   r@   r   r   r   )
r/   r   r   r   r   r   r?   Zhas_checksumZchecksumr   r   r   r   r     s:    


zBlob._from_filec             C   sT   | j dkrtd|dk r$| j| }|dk s6|| jkr>td| j | j|  dS )zB Seek to the given position (relative to the blob start).
        Nz>Cannot seek in a blob that is not created by the BSDF decoder.r   zSeek beyond blob boundaries.)r   r   r   r   r   r   )r/   pr   r   r   r     s    

z	Blob.seekc             C   s"   | j dkrtd| j  | j S )zM Get the current file pointer position (relative to the blob start).
        Nz>Cannot tell in a blob that is not created by the BSDF decoder.)r   r   r   r   )r/   r   r   r   r     s    
z	Blob.tellc             C   sR   | j dkrtd| jr td| j  t| | jkr@tdd| _| j |S )z" Write bytes to the blob.
        Nz?Cannot write in a blob that is not created by the BSDF decoder.z,Cannot arbitrarily write in compressed blob.zWrite beyond blob boundaries.T)	r   r   r?   r   r   r   r   r   r^   )r/   r    r   r   r   r^     s    
z
Blob.writec             C   sH   | j dkrtd| jr td| j  | | jkr<td| j |S )z% Read n bytes from the blob.
        Nz>Cannot read in a blob that is not created by the BSDF decoder.z+Cannot arbitrarily read in compressed blob.zRead beyond blob boundaries.)r   r   r?   r   r   r   r   )r/   r   r   r   r   r     s    
z	Blob.readc             C   s   | j dk	r| j }n.| j }| d | j| j}| j| | jdkrP|}n:| jdkrft|}n$| jdkr|t	|}nt
d| j |S )z0 Get the contents of the blob as bytes.
        Nr   r   r   zInvalid compression %i)r   r   r   r   r   r   r?   r4   Z
decompressr5   r   )r/   r   r   rt   r   r   r   r     s    





zBlob.get_bytesc             C   sZ   | j rV| jrV| d | j| j}| j| j| j d d  | jt	
|  dS )z[ Reset the blob's checksum if present. Call this after modifying
        the data.
        r   r   r   N)r@   r   r   r   r   r   r   r   r^   r   r   r   )r/   r   r   r   r   update_checksum  s
    
zBlob.update_checksumN)r   r   F)rs   r   r   r   r3   r   rj   r   r   r   r^   r   r   r   r   r   r   r   ri   ]  s   
(	ri   c             K   s   t |f|}|| S )zs Save (BSDF-encode) the given object to bytes.
    See `BSDFSerializer` for details on extensions and options.
    )r)   r   )r   r0   r1   r'   r   r   r   r     s    r   c          	   K   sF   t |f|}t| tr6t| d}|||S Q R X n|| |S dS )z Save (BSDF-encode) the given object to the given filename or
    file object. See` BSDFSerializer` for details on extensions and options.
    wbN)r)   r"   r#   openr   )r   r   r0   r1   r'   fpr   r   r   r   '  s
    
r   c             K   s   t |f|}|| S )zq Load a (BSDF-encoded) structure from bytes.
    See `BSDFSerializer` for details on extensions and options.
    )r)   r   )r    r0   r1   r'   r   r   r   r   3  s    r   c          	   K   sX   t |f|}t| trJ| dr,tj| } t| d}||S Q R X n
|| S dS )z Load a (BSDF-encoded) structure from the given filename or file object.
    See `BSDFSerializer` for details on extensions and options.
    )z~/z~\rbN)	r)   r"   r#   
startswithospath
expanduserr   r   )r   r0   r1   r'   r   r   r   r   r   ;  s    

r   c               @   s8   e Zd ZdZdZdZdd Zdd Zdd	 Zd
d Z	dS )rD   aE   Base class to implement BSDF extensions for special data types.

    Extension classes are provided to the BSDF serializer, which
    instantiates the class. That way, the extension can be somewhat dynamic:
    e.g. the NDArrayExtension exposes the ndarray class only when numpy
    is imported.

    A extension instance must have two attributes. These can be attribiutes of
    the class, or of the instance set in ``__init__()``:

    * name (str): the name by which encoded values will be identified.
    * cls (type): the type (or list of types) to match values with.
      This is optional, but it makes the encoder select extensions faster.

    Further, it needs 3 methods:

    * `match(serializer, value) -> bool`: return whether the extension can
      convert the given value. The default is ``isinstance(value, self.cls)``.
    * `encode(serializer, value) -> encoded_value`: the function to encode a
      value to more basic data types.
    * `decode(serializer, encoded_value) -> value`: the function to decode an
      encoded value back to its intended representation.

     r   c             C   s   d| j tt| f S )Nz<BSDF extension %r at 0x%s>)rE   hexid)r/   r   r   r   __repr__r  s    zExtension.__repr__c             C   s   t || jS )N)r"   rI   )r/   r'   rv   r   r   r   r%   u  s    zExtension.matchc             C   s
   t  d S )N)NotImplementedError)r/   r'   rv   r   r   r   r   x  s    zExtension.encodec             C   s
   t  d S )N)r   )r/   r'   rv   r   r   r   r   {  s    zExtension.decodeN)
rs   r   r   r   rE   rI   r   r%   r   r   r   r   r   r   rD   U  s   rD   c               @   s$   e Zd ZdZeZdd Zdd ZdS )ComplexExtensionrz   c             C   s   |j |jfS )N)realimag)r/   r'   rv   r   r   r   r     s    zComplexExtension.encodec             C   s   t |d |d S )Nr   r   )complex)r/   r'   rv   r   r   r   r     s    zComplexExtension.decodeN)rs   r   r   rE   r   rI   r   r   r   r   r   r   r     s   r   c               @   s0   e Zd ZdZdd Zdd Zdd Zdd	 Zd
S )NDArrayExtensionndarrayc             C   s   dt jkrdd l}|j| _d S )Nnumpyr   )sysmodulesr   r   rI   )r/   npr   r   r   r3     s    
zNDArrayExtension.__init__c             C   s   t |dot |dot |dS )Nshapedtypetobytes)r   )r/   r'   rv   r   r   r   r%     s    zNDArrayExtension.matchc             C   s   t |jt|j| dS )N)r   r   data)rc   r   	text_typer   r   )r/   r'   rv   r   r   r   r     s    zNDArrayExtension.encodec             C   sF   ydd l }W n tk
r    |S X |j|d |d d}|d |_|S )Nr   r   r   )r   r   )r   ImportErrorZ
frombufferr   )r/   r'   rv   r   ar   r   r   r     s    
zNDArrayExtension.decodeN)rs   r   r   rE   r3   r%   r   r   r   r   r   r   r     s
   r   __main__)N)N)N)N)>r   Z
__future__r   r   r   r5   r   Zloggingr   r$   r   r   typesr4   ior   Z	getLoggerrs   rG   r   joinr   version_infore   r	   r   r#   ra   r   r_   rB   rL   ZbasicConfigZunicodeZ
basestringZlongZ	ClassTyper   r   Zunpackr   r   r   r!   r(   objectr)   rk   rn   ri   r   r   r   r   loadsdumpsrD   r   r   r,   Zbsdf_climainr   r   r   r   <module>   sj   


	
  o X C




*
