U
    a+d                     @   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   9/tmp/pip-unpacked-wheel-xbmu82vq/imageio/plugins/_bsdf.py	<genexpr>0   s     r      c                 C   s"   | dkrt d| S t dd| S dS )z?Encode an unsigned integer into a variable sized blob of bytes.   <B<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.r   r   r   r   <Q   )	strunpackread)fnr   r   r   	lendecodeY   s    r   c                 C   s2   |dk	r*| d}|  tt| | S | S dS )z9Encode the type identifier, with or without extension id.NUTF-8)encodeupperr   len)bext_idbbr   r   r   encode_type_ida   s    
r'   c                 C   s*   t | to(td| tjo(td| dkS )z8Use of str.isidentifier() for Legacy Python, but slower.z^\w+$z^[0-9]N)
isinstancestring_typesrematchUNICODE)sr   r   r   _isidentifierj   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 )BsdfSerializeraz  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}|D ]}| | q| jf | d S r	   )_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   )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)r5   compressionuse_checksumZfloat64Zload_streamingZ	lazy_blobmr   r   r   r4      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}|D ]}t |tstdq|D ]}||jf| j|< q|| j	|< |S )zAdd 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.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	Extensionr?   namer
   r#   	NameErrorr0   loggerwarningclstuplelist
classtypesr!   r1   )r5   Zextension_classr8   rM   rQ   Zclssr   r   r   r3      s@    




zBsdfSerializer.add_extensionc                 C   s^   t |tstd|| jkr(| j| t| j D ]"}| j| d |kr6| j| q6dS )z&Remove a converted by its unique name.rI   r   N)r(   r
   r?   r0   poprS   r1   keys)r5   rM   rQ   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| n`t|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
|  || nzt|ttfr||d|t	t
|  |D ]}| |||d qnn.t|tr$||d|t	t
|  | D ]d\}}tr| stnt|st|d}	|t	t
|	 ||	 | |||d qnt|tr`||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	r(td| | j!"|j#d}|dkrz| j$ D ](\}}|%| |rL||jf} qzqLd}|dk	r|\}}| ||| ||| nd}t||j#j& dS ) zMain encoder function.N   vT   yF   ni i     hh   i<q   d<d   f<fr       s   l   m   b)rF   rG   wz$Cannot serialize a read-mode stream.r      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   floatrC   unicode_typesr!   r   r#   rS   rR   _encodedictitemsPY3isidentifierAssertionErrorr.   bytesBlobr@   rB   _to_file
BaseStreammode
ValueError
ListStreamr?   append	_activate_decoder1   r=   	__class__r0   r+   __name__)r5   r   valuestreamsr%   r   r&   vkeyZname_bblobexrM   cZext_id2Zextension_encodetr   r   r   rm      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 }nf|dkrtd  dd }nD|dkrRtd  dd }|dkr>td  dd }  |d}n|dkrRtd  dd }|dkr|dk}td  dd }jrt|r|nd}| jj	 nT|r fddt
|D }n4g }z|	  qW n tk
r   Y nX n6|dkr8td  dd } fddt
|D }n|dkrt }td  dd }|dkrtd  dd }t
|D ]d}	td  dd }
|
dkrtd  dd }
|
dkst  |
d}	 ||< qnB|d kr:jr$t df}nt d
f}| }ntd!| |dk	rj|d}|dk	rv||}ntd"|  |S )#zMain decoder function.r   r   r   r    NrX   rY   TrZ   Fr[   z<hr   r]   r^   r   ra   rb      r_   r`   rc   r   r   rd      rc                    s   g | ]}  qS r   r|   r   r   r5   r   r   
<listcomp>r  s     z*BsdfSerializer._decode.<locals>.<listcomp>c                    s   g | ]}  qS r   r   r   r   r   r   r   ~  s     re   rf   zParse error %rz'BSDF warning: no extension found for %r)r   r>   EOFErrorr   decoderD   ry   r{   rm   r|   rangerz   rn   rr   rE   rt   	get_bytesRuntimeErrorr0   r=   rO   rP   )r5   r   charr   r   r%   r   Zn_sclosedr   Zn_namerM   r   r8   r   r   r   r|   D  s    













zBsdfSerializer._decodec                 C   s   t  }| || | S )zSave the given object to bytes.)r   savegetvalue)r5   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 )z0Write the given object to the given file object.   BSDFr   r   r   Nz8The stream object must be the last object to be encoded.)	ri   structpackVERSIONrm   r#   
_start_postellrx   )r5   r   r   r   streamr   r   r   r     s    
zBsdfSerializer.savec                 C   s   t |}| |S )z@Load the data structure that is BSDF-encoded in the given bytes.)r   load)r5   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 )
z6Load a BSDF-encoded object from the given file object.r   r   z'This does not look like a BSDF file: %rr   r   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__rO   rP   r|   )r5   r   Zf4major_versionminor_versionZfile_versionr   r   r   r   r     s    
zBsdfSerializer.load)N)r   FTFF)r~   
__module____qualname____doc__r9   r4   r3   rW   rm   r|   r!   r   r   r   r   r   r   r   r/   t   s    
     
+
[Zr/   c                   @   s.   e Zd ZdZd
ddZdd Zedd Zd	S )rv   zBase class for streams.rg   c                 C   sR   d| _ d| _t|tr"|| _d}n|dkr0d| _|dks<t|| _d | _d| _d S )Nr   r   rg   )r   rg   )_i_countr(   intrr   _mode_fr   )r5   rw   r   r   r   r9     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   rm   r|   )r5   fileZencode_funcZdecode_funcr   r   r   r{     s    
zBaseStream._activatec                 C   s   | j S )z$The mode of this stream: 'r' or 'w'.)r   r5   r   r   r   rw     s    zBaseStream.modeN)rg   )r~   r   r   r   r9   r{   propertyrw   r   r   r   r   rv     s
   
rv   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 )ry   zjA streamable list object used for writing or reading.
    In read mode, it can also be iterated over.
    c                 C   s   | j S )zdThe 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 )z/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   rm   )r5   itemr   r   r   rz     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 )zClose 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.N-ListStream is not associated with a file yet.z&Cannot close a stream on a close file.r   r   r   r   r   r   )
r   r   r   r   r   r   seekr   ri   r   )r5   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 z | | j}|  jd7  _|W S  tk
r   | j| _t Y nX dS )	zyRead 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.Nr   r   z'Cannot read a stream from a close file.r   r   )	r   r   r   getattrr   r   StopIterationr|   r   )r5   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__E  s    
zListStream.__iter__c                 C   s   |   S r	   )r   r   r   r   r   __next__J  s    zListStream.__next__N)F)r~   r   r   r   r   r   r   rz   r   r   r   r   r   r   r   r   ry     s   


ry   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 )rt   a8  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(   rs   r   _from_bytes
compressedrF   	used_sizeallocated_sizerG   rR   r#   hasattr
_from_file	_modifiedr?   )r5   r&   rF   
extra_sizerG   
allow_seekr   r   r   r9   X  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)r;   compressr<   rr   r#   	data_sizer   )r5   r   rF   r   r   r   r   r   g  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 )zBPrivate friend method called by encoder to write a blob to a file.r   r   r   r   r   B       r   r   N)r   rF   ri   r   r   r   r   rG   hashlibmd5r   digestr   )r5   r   	alignmentr   r   r   ru   v  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 )	z(Used when a blob is read by the decoder.r   r   r   r   r   r      N)r   r   r   	start_posend_posr   r   r   rF   rG   r   r   r   )
r5   r   r   r   r   r   rF   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 )z8Seek 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   )r5   pr   r   r   r     s    

z	Blob.seekc                 C   s"   | j dkrtd| j  | j S )zCGet 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   rF   r   r   r#   r   r   ri   )r5   r&   r   r   r   ri     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   rF   r   r   r   r   )r5   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 )z&Get the contents of the blob as bytes.Nr   r   r   zInvalid compression %i)r   r   r   r   r   r   rF   r;   
decompressr<   r   )r5   r   r   r   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 )zZReset the blob's checksum if present. Call this after modifying
        the data.
        r   r   r   N)rG   r   r   r   r   r   r   r   ri   r   r   r   )r5   r   r   r   r   update_checksum  s
    
zBlob.update_checksumN)r   r   F)r~   r   r   r   r9   r   ru   r   r   r   ri   r   r   r   r   r   r   r   rt   N  s   	
'rt   c                 K   s   t |f|}|| S )zrSave (BSDF-encode) the given object to bytes.
    See `BSDFSerializer` for details on extensions and options.
    )r/   r!   )r   r6   r7   r-   r   r   r   r!     s    r!   c              
   K   sR   t |f|}t| trBt| d}|||W  5 Q R  S Q R X n|| |S dS )zSave (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   r6   r7   r-   fpr   r   r   r     s
    
 r   c                 K   s   t |f|}|| S )zpLoad a (BSDF-encoded) structure from bytes.
    See `BSDFSerializer` for details on extensions and options.
    )r/   r   )r&   r6   r7   r-   r   r   r   r     s    r   c              
   K   sd   t |f|}t| trV| dr,tj| } t| d}||W  5 Q R  S Q R X n
|| S dS )zLoad 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   r6   r7   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 )rL   aD  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>)rM   hexidr   r   r   r   __repr__[  s    zExtension.__repr__c                 C   s   t || jS r	   )r(   rQ   r5   r-   r   r   r   r   r+   ^  s    zExtension.matchc                 C   s
   t  d S r	   NotImplementedErrorr   r   r   r   r!   a  s    zExtension.encodec                 C   s
   t  d S r	   r   r   r   r   r   r   d  s    zExtension.decodeN)
r~   r   r   r   rM   rQ   r   r+   r!   r   r   r   r   r   rL   >  s   rL   c                   @   s$   e Zd ZdZeZdd Zdd ZdS )ComplexExtensionr   c                 C   s   |j |jfS r	   )realimagr   r   r   r   r!   l  s    zComplexExtension.encodec                 C   s   t |d |d S )Nr   r   )complexr   r   r   r   r   o  s    zComplexExtension.decodeN)r~   r   r   rM   r   rQ   r!   r   r   r   r   r   r   h  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   rQ   )r5   npr   r   r   r9   v  s    
zNDArrayExtension.__init__c                 C   s   t |dot |dot |dS )Nshapedtypetobytes)r   r   r   r   r   r+   |  s    zNDArrayExtension.matchc                 C   s   t |jt|j| dS )N)r   r   data)rn   r   	text_typer   r   r   r   r   r   r!     s    zNDArrayExtension.encodec                 C   sJ   zdd l }W n tk
r$   | Y S X |j|d |d d}|d |_|S )Nr   r   r   )r   r   )r   ImportErrorZ
frombufferr   )r5   r-   r   r   ar   r   r   r     s    

zNDArrayExtension.decodeN)r~   r   r   rM   r9   r+   r!   r   r   r   r   r   r   s  s
   r   __main__)N)N)N)N)>r   
__future__r   r   r   r<   r   loggingr   r*   r   r   typesr;   ior   	getLoggerr~   rO   r   joinr   version_inforp   r
   r   r)   rl   r   rj   rJ   rT   basicConfigunicode
basestringlongZ	ClassTyper   r   unpackr   r   r   r'   r.   objectr/   rv   ry   rt   r!   r   r   r   loadsdumpsrL   r   r   r2   Zbsdf_climainr   r   r   r   <module>   sj   


	
  gW ;




*
