
    jN                        d Z ddlZddlZddlZddlmZ ddlmZ ddl	Z
ddlmZ ddlmZ 	 ddlmZ d	efd
Zej&                  dk\  r
ddlmZ d ZneZ	 ddlmZ ddZd Z G d de
j<                        Z G d d      Z  G d d      Z! G d de      Z"y# e$ r	 ddlmZ Y qw xY w# e$ r* 	 ddlmZ n# e$ r  ej6                  d       eZY nw xY wY {w xY w)a  
caching.py
-----------

Functions and classes that help with tracking changes
in `numpy.ndarray` and clearing cached values based
on those changes.

You should really `pip install xxhash`:

```
In [23]: %timeit int(blake2b(d).hexdigest(), 16)
102 us +/- 684 ns per loop

In [24]: %timeit int(sha256(d).hexdigest(), 16)
142 us +/- 3.73 us

In [25]: %timeit xxh3_64_intdigest(d)
3.37 us +/- 116 ns per loop
```
    N)wraps)sha256   )log)is_sequence)Mappingreturnc                 H    t        t        |       j                         d      S )N   )int_sha256	hexdigestitems    </DATA/.local/lib/python3.12/site-packages/trimesh/caching.pyr   r   (   s    wt}&&("--    )   	   )blake2bc                 L    t        t        | d      j                         d      S )NF)usedforsecurityr   )r   _blake2br   r   s    r   hash_fallbackr   0   s    8D%8BBDbIIr   )xxh3_64_intdigest)xxh64_intdigestzPfalling back to hashlib hashing: `pip install xxhash`for 50x faster cache checksc                     | g } t        j                  | |      j                  t              }|j                  d   sJ |S )a  
    Properly subclass a numpy ndarray to track changes.

    Avoids some pitfalls of subclassing by forcing contiguous
    arrays and does a view into a TrackedArray.

    Parameters
    ------------
    array : array- like object
      To be turned into a TrackedArray
    dtype : np.dtype
      Which dtype to use for the array

    Returns
    ------------
    tracked : TrackedArray
      Contains input array data.
    dtypeC_CONTIGUOUS)npascontiguousarrayviewTrackedArrayflags)arrayr   trackeds      r   tracked_arrayr'   I   sB    ( }""56;;LIG==(((Nr   c                 @     t                fd       }t        |      S )aE  
    A decorator for class methods, replaces @property
    but will store and retrieve function return values
    in object cache.

    Parameters
    ------------
    function : method
      This is used as a decorator:
      ```
      @cache_decorator
      def foo(self, things):
        return 'happy days'
      ```
    c                     | d   }j                   }|j                  j                          ||j                  j                  v r|j                  j                  |   S  | i |}|j                  j                  r5t        |d      r)t        |j                        dkD  rd|j                  _	        ||j                  j                  |<   |S )z_
        Only execute the function if its value isn't stored
        in cache already.
        r   r$   F)
__name___cacheverifycacheforce_immutablehasattrlenshaper$   	writeable)argskwargsselfnamevaluefunctions        r   
get_cachedz#cache_decorator.<locals>.get_cachedy   s     Aw   	 4;;$$$;;$$T**$)&) KK''w'EKK 1$$)EKK!"'$r   )r   property)r8   r9   s   ` r   cache_decoratorr;   g   s)    $ 8_ B Jr   c                   F    e Zd ZdZd ZddZed        Zej                  d        Zd Z	 fdZ
 fdZ fd	Z fd
Z fdZ fdZ fdZ fdZ fdZ fdZ fdZ fdZ fdZ fdZ fdZ fdZ fdZ fdZ fdZ fdZ fdZ fdZ fdZ  xZ!S )r#   aE  
    Subclass of numpy.ndarray that provides hash methods
    to track changes.

    General method is to aggressively set 'modified' flags
    on operations which might (but don't necessarily) alter
    the array, ideally we sometimes compute hashes when we
    don't need to, but we don't return wrong hashes ever.

    We store boolean modified flag for each hash type to
    make checks fast even for queries of different hashes.

    Methods
    ----------
    __hash__ : int
      Runs the fastest available hash in this order:
        `xxh3_64, xxh_64, blake2b, sha256`
    c                 L    d| _         t        |t        |             rd|_         yy)z
        Sets a modified flag on every TrackedArray
        This flag will be set on every change as well as
        during copies and certain types of slicing.
        TN)_dirty_hash
isinstancetype)r5   objs     r   __array_finalize__zTrackedArray.__array_finalize__   s&      c4:&"CO 'r   c                 n    |j                   r%t        j                  j                  | ||g|i |S |d   S )zn
        Return a numpy scalar if array is 0d.
        See https://github.com/numpy/numpy/issues/5819
         )ndimr    ndarray__array_wrap__)r5   out_arrcontextr3   r4   s        r   rG   zTrackedArray.__array_wrap__   s8    
 <<::,,T7GUdUfUUr{r   c                      | j                   d   S )N	WRITEABLE)r$   r5   s    r   mutablezTrackedArray.mutable   s    zz+&&r   c                 &    || j                   _        y N)r$   r2   )r5   r7   s     r   rM   zTrackedArray.mutable   s    $

r   c                     | j                   st        | d      r| j                  S t        | j	                  d            }|| _        d| _         |S )z
        Return a fast hash of the contents of the array.

        Returns
        -------------
        hash : long int
          A hash of the array contents.
        _hashedC)orderF)r>   r/   rQ   	hash_fasttobytes)r5   hasheds     r   __hash__zTrackedArray.__hash__   sM     GD)$<<< 4<<c<23  r   c                 D    d| _         t        | j                  |   |i |S )z
        In-place addition.

        The i* operations are in- place and modify the array,
        so we better catch all of them.
        T)r>   super	__class____iadd__r5   r3   r4   rZ   s      r   r[   zTrackedArray.__iadd__   s(      T^^T3TDVDDr   c                 D    d| _         t        | j                  |   |i |S NT)r>   rY   rZ   __isub__r\   s      r   r_   zTrackedArray.__isub__   &    T^^T3TDVDDr   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   fillr\   s      r   rb   zTrackedArray.fill   &    T^^T/@@@r   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   	partitionr\   s      r   re   zTrackedArray.partition   s&    T^^T4dEfEEr   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   putr\   s      r   rg   zTrackedArray.put   s&    T^^T.???r   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   byteswapr\   s      r   ri   zTrackedArray.byteswap  r`   r   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   itemsetr\   s      r   rk   zTrackedArray.itemset  &    T^^T2DCFCCr   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   sortr\   s      r   rn   zTrackedArray.sort	  rc   r   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   setflagsr\   s      r   rp   zTrackedArray.setflags  r`   r   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   __imul__r\   s      r   rr   zTrackedArray.__imul__  r`   r   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   __idiv__r\   s      r   rt   zTrackedArray.__idiv__  r`   r   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   __itruediv__r\   s      r   rv   zTrackedArray.__itruediv__  &    T^^T7HHHr   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   __imatmul__r\   s      r   ry   zTrackedArray.__imatmul__  &    T^^T6GGGr   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   __ipow__r\   s      r   r|   zTrackedArray.__ipow__!  r`   r   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   __imod__r\   s      r   r~   zTrackedArray.__imod__%  r`   r   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   __ifloordiv__r\   s      r   r   zTrackedArray.__ifloordiv__)  s&    T^^T8$I&IIr   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   __ilshift__r\   s      r   r   zTrackedArray.__ilshift__-  rz   r   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   __irshift__r\   s      r   r   zTrackedArray.__irshift__1  rz   r   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   __iand__r\   s      r   r   zTrackedArray.__iand__5  r`   r   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   __ixor__r\   s      r   r   zTrackedArray.__ixor__9  r`   r   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   __ior__r\   s      r   r   zTrackedArray.__ior__=  rl   r   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   __setitem__r\   s      r   r   zTrackedArray.__setitem__A  rz   r   c                 D    d| _         t        | j                  |   |i |S r^   )r>   rY   rZ   __setslice__r\   s      r   r   zTrackedArray.__setslice__E  rw   r   rO   )"r*   
__module____qualname____doc__rB   rG   r:   rM   setterrW   r[   r_   rb   re   rg   ri   rk   rn   rp   rr   rt   rv   ry   r|   r~   r   r   r   r   r   r   r   r   __classcell__)rZ   s   @r   r#   r#      s    &	# ' ' ^^% %.	EEAF@EDAEEEIHEEJHHEEDHI Ir   r#   c                   \    e Zd ZdZddZd Zd ZddZd Zd Z	d	 Z
d
 Zd Zd Zd Zd Zy)Cachezd
    Class to cache values which will be stored until the
    result of an ID function changes.
    c                 \    || _         t        |      | _        d| _        d| _        i | _        y)z
        Create a cache object.

        Parameters
        ------------
        id_function : function
          Returns hashable value
        force_immutable : bool
          If set will make all numpy arrays read-only
        Nr   )_id_functionboolr.   
id_current_lockr-   )r5   id_functionr.   s      r   __init__zCache.__init__P  s.     (#O4

r   c                 Z    || j                   v r| j                   j                  |d       yy)z.
        Remove a key from the cache.
        N)r-   popr5   keys     r   deletezCache.deletee  s'     $**JJNN3% r   c           
      X   | j                   dk7  ry| j                         }|| j                  k7  r{t        | j                        dkD  rTt        j                  dt        | j                        t        t        | j                  j                                            i | _        || _        yy)z
        Verify that the cached values are still for the same
        value of id_function and delete all stored items if
        the value of id_function has changed.
        r   Nz%d items cleared from cache: %s)
r   r   r   r0   r-   r   debugstrlistkeys)r5   id_news     r   r,   zCache.verifyl  s     ::? ""$ T__$4::"		5

OTZZ__./0 DJ$DO %r   Nc                     |i | _         y| j                   j                         D ci c]  \  }}||v s|| c}}| _         yc c}}w )z
        Remove elements in the cache.

        Parameters
        -----------
        exclude : list
          List of keys in cache to not clear.
        N)r-   items)r5   excludekvs       r   clearzCache.clear  sF     ?DJ+/::+;+;+=N+=41ag!Q$+=NDJNs
   AAc                    | j                   j                  |       | j                  rV| j                   j                         D ]9  }t	        |d      st        |j                        dkD  s)d|j                  _        ; | j                          y)zg
        Update the cache with a set of key, value pairs without
        checking id_function.
        r$   r   FN)
r-   updater.   valuesr/   r0   r1   r$   r2   id_set)r5   r   r   s      r   r   zCache.update  sf    
 	

% ZZ&&(1g&3qww<!+;(-AGG% ) 	r   c                 .    | j                         | _        y)zE
        Set the current ID to the value of the ID function.
        N)r   r   rL   s    r   r   zCache.id_set  s     ++-r   c                 ^    | j                          || j                  v r| j                  |   S y)a/  
        Get an item from the cache. If the item
        is not in the cache, it will return None

        Parameters
        -------------
        key : hashable
               Key in dict

        Returns
        -------------
        cached : object, or None
          Object that was stored
        Nr,   r-   r   s     r   __getitem__zCache.__getitem__  s*     	$**::c?"r   c                     | j                          | j                  r5t        |d      r)t        |j                        dkD  rd|j
                  _        || j                  |<   |S )z
        Add an item to the cache.

        Parameters
        ------------
        key : hashable
          Key to reference value
        value : any
          Value to store in cache
        r$   r   F)r,   r.   r/   r0   r1   r$   r2   r-   )r5   r   r7   s      r   r   zCache.__setitem__  sN     	GE7$;EKK@PST@T$)EKK!

3r   c                 >    | j                          || j                  v S rO   r   r   s     r   __contains__zCache.__contains__  s    djj  r   c                 L    | j                          t        | j                        S rO   )r,   r0   r-   rL   s    r   __len__zCache.__len__  s    4::r   c                 .    | xj                   dz  c_         y Nr   )r   rL   s    r   	__enter__zCache.__enter__  s    

a
r   c                 X    | xj                   dz  c_         | j                         | _        y r   )r   r   r   )r5   r3   s     r   __exit__zCache.__exit__  s    

a
++-r   )FrO   )r*   r   r   r   r   r   r,   r   r   r   r   r   r   r   r   r   rD   r   r   r   r   J  sD    
*&%8O.(*!.r   r   c                       e Zd ZdZddZd Zy)	DiskCachez
    Store results of expensive operations on disk
    with an option to expire the results. This is used
    to cache the multi-gigabyte test corpuses in
    `tests/corpus.py`
    c                     || _         t        j                  j                  t        j                  j	                  |            | _        t        j
                  | j                  d       y)a  
        Create a cache on disk for storing expensive results.

        Parameters
        --------------
        path : str
          A writeable location on the current file path.
        expire_days : int or float
          How old should results be considered expired.

        T)exist_okN)expire_daysospathabspath
expandusermakedirs)r5   r   r   s      r   r   zDiskCache.__init__  sA     'GGOOBGG$6$6t$<=	
DII-r   c                 ~   t        |j                  d            j                         }t        j                  j                  | j                  |      }t        j                  j                  |      rlt        j                         t        j                  |      j                  z
  dz  }|| j                  k  r&t        |d      5 }|j                         cddd       S t        j                  d| d        |       }t        |d      5 }|j                  |       ddd       |S # 1 sw Y   QxY w# 1 sw Y   |S xY w)a  
        Get a key from the cache or run a calculation.

        Parameters
        -----------
        key : str
          Key to reference item with
        fetch : function
          If key isn't stored and recent run this
          function and store its result on disk.
        zutf-8g     @rbNznot in cache fetching: ``wb)r   encoder   r   r   joinisfiletimestatst_mtimer   openreadr   r   write)r5   r   fetchkey_hashr   age_daysfraws           r   getzDiskCache.get  s     3::g./99;ww||DIIx0 77>>$		bggdm&<&<<GH$*** $%668 &% 			,SE34 g$GGCL  
 &%  
s   D&
D2&D/2D<N)   )r*   r   r   r   r   r   rD   r   r   r   r     s    .&%r   r   c                       e Zd ZdZd Zd Zd Zd Zed        Z	e	j                  d        Z	d Zd	 Zd
 Zd Zd Zd Zd Zd Zy)	DataStorez
    A class to store multiple numpy arrays and track them all
    for changes.

    Operates like a dict that only stores numpy.ndarray
    c                     i | _         y rO   datarL   s    r   r   zDataStore.__init__-  s	    	r   c                 ,    t        | j                        S rO   )iterr   rL   s    r   __iter__zDataStore.__iter__0  s    DIIr   c                 :    | j                   j                  |d       S rO   r   r   r   s     r   r   zDataStore.pop3  s    yy}}S$''r   c                 <    | j                   j                  |d        y rO   r   r   s     r   __delitem__zDataStore.__delitem__6  s    		c4 r   c                     t        | dd      S )z
        Is data allowed to be altered or not.

        Returns
        -----------
        is_mutable : bool
          Can data be altered in the DataStore
        _mutableT)getattrrL   s    r   rM   zDataStore.mutable9  s     tZ..r   c                     t        |      }| j                  j                         D ]  }t        |t              s||_         || _        y)z
        Is data allowed to be altered or not.

        Parameters
        ------------
        is_mutable : bool
          Should data be allowed to be altered
        N)r   r   r   r?   r#   rM   r   )r5   r7   
is_mutabler   s       r   rM   zDataStore.mutableE  s>     %[
!!#A!\*!	 $ #r   c                     t        | j                        dk(  ry| j                  j                         D ]?  }t        |      rt        |      dk(  r y yt	        t        j                  |            s? y y)z
        Is the current DataStore empty or not.

        Returns
        ----------
        empty : bool
          False if there are items in the DataStore
        r   TF)r0   r   r   r   r   r    isrealr5   r   s     r   is_emptyzDataStore.is_emptyX  s^     tyy>Q!!#A1~q6Q; biil# $ r   c                     i | _         y)z5
        Remove all data from the DataStore.
        Nr   rL   s    r   r   zDataStore.clearm  s     	r   c                      | j                   |   S rO   r   r   s     r   r   zDataStore.__getitem__s  s    yy~r   c           	         | j                   st        d      t        |t              r|}nDt        |t        j
                  t        t        t        f      rt        |      }n	 t        |       |}t        | d      r| j                   |_         || j                  |<   y# t        $ r t        d| dt        |       d      w xY w)a  
        Store an item in the DataStore.

        Parameters
        -------------
        key
          A hashable key to store under
        data
          Usually a numpy array which will be subclassed
          but anything hashable should be able to be stored.
        z"DataStore is configured immutable!zunhashable `:r   r   N)rM   
ValueErrorr?   r#   r    rF   r   settupler'   hashBaseExceptionr@   r/   r   )r5   r   r   r&   s       r   r   zDataStore.__setitem__v  s     ||ABBdL)Grzz4e<=#D)GET
 G 4$"llGO 		# ! E <uAd4j\!CDDEs   "B %Cc                     || j                   v S rO   r   r   s     r   r   zDataStore.__contains__  s    diir   c                 ,    t        | j                        S rO   )r0   r   rL   s    r   r   zDataStore.__len__  s    499~r   c                 t    t        |t              st        d      |j                         D ]
  \  }}|| |<    y )Nz!Update only implemented for dicts)r?   dictr   r   )r5   r   r   r7   s       r   r   zDataStore.update  s5    &$'@AA ,,.JCDI )r   c                    t        t        j                  | j                  j	                         D cg c])  }|%t        |d      rt        |      dkD  rt        |      + c}t        j                        j                               S c c}w )z
        Get a hash reflecting everything in the DataStore.

        Returns
        ----------
        hash : str
          hash of data in hexadecimal
        r   r   r   )
rT   r    r%   r   r   r/   r0   r   int64rU   r   s     r   rW   zDataStore.__hash__  s{     HH "YY--//}ga.Cs1vPQz G/
 hh gi	
 		
s   .BN)r*   r   r   r   r   r   r   r   r:   rM   r   r   r   r   r   r   r   r   rW   rD   r   r   r   r   %  sp    (! 	/ 	/ ^^# #$*#!J 
r   r   rO   )#r   r   sysr   	functoolsr   hashlibr   r   numpyr    	constantsr   utilr   collections.abcr   r   r   version_infor   r   r   xxhashr   rT   r   r   r'   r;   rF   r#   r   r   r   rD   r   r   <module>r     s   , 
 
   %   ('
.C . v+J M"5<4 niI2:: iIXU. U.p@ @FV
 V
C  ('(.  "
"7 "		,	

 "	"	"sG   B B' B$#B$'C-B43C4CCCCC