From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-lj1-x22f.google.com (mail-lj1-x22f.google.com [IPv6:2a00:1450:4864:20::22f]) by sourceware.org (Postfix) with ESMTPS id A2245385840F; Thu, 26 Oct 2023 20:52:29 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A2245385840F Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org A2245385840F Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::22f ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1698353553; cv=none; b=hB9nx6zRkqVxUa934I6G1FHAR/coJ8ByrUiTi7ShWJhtZI7wrJaWtzz4zEJHLu7VXFI7WmFpp1AFJGjMq25lZO23DpH4GmxZQOHNyP3PjBfkJsZY006ZcVrw9JoEGIhj1wEAUEm8sfuspXWBfEx+3NCz403Hl7HkzvLpmHGbIjA= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1698353553; c=relaxed/simple; bh=sb6EBExU3egqYo6U25HhfuhomMQYU2IwotzxKjzV8bM=; h=DKIM-Signature:Message-ID:Date:MIME-Version:From:Subject:To; b=fs1m+OhAIG4lhDBCUD5BEMl/EgJEuVmhq+StRfmzpHZsEgAs8IsmfoJt0iwVdoPpALldE/5db+SgXBAz+RmZznUoRevi1HmS9Ht/QlcGMAviqhs+d0+Ju7m0Vg4ZWCS8B36vKLDtzmOZwdr9oI9NsZ6sjyo5OTkIETBvzRx+pjk= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-lj1-x22f.google.com with SMTP id 38308e7fff4ca-2bb9a063f26so20384721fa.2; Thu, 26 Oct 2023 13:52:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1698353548; x=1698958348; darn=gcc.gnu.org; h=in-reply-to:content-language:references:cc:to:subject:from :user-agent:mime-version:date:message-id:from:to:cc:subject:date :message-id:reply-to; bh=+LBcpUiL4qBhVJnqRw/gW58gw9OyaS+Cl5LDBbqZT/E=; b=ddZHtQeUQs+U40otWxniJF4JnP/3jRlVHT2cwlKec6JotH3yc5ivKaXQsOtJHzBocr 0fkTJIkHCHDCWdtDhK3v4764Qi3wb2iviU4J/5i1jLt1mgyUZCoiaP3sEAQjb1f964nQ 9zZw6spfZPrs/Rv9Vs1iPdcByybfISd1LEt+rQQIU2QhXAzD3XxCjitkOux6ehw9XG+4 CB4ZX8PZREhRjhLTjH8Nv0M3qTDz91nXBrzUJzdhrtoO0vJ2assMr+0adSfFgVIsZjyn MqGNrkW+eCwLu+igeh2571VaHmBQV2kMUn/4LwkPjgKx8NuSLmgaHyVLmBcZUr02eeRN yG2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698353548; x=1698958348; h=in-reply-to:content-language:references:cc:to:subject:from :user-agent:mime-version:date:message-id:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=+LBcpUiL4qBhVJnqRw/gW58gw9OyaS+Cl5LDBbqZT/E=; b=naSLCYvWS4C1siRvneqztMVPVLJ4Cb7kjG6wKZHnbQeqdy7sA+WK1gjsli7W5dBYV4 FvVdJUBSX26Q95Xw5ptzRGdOgrE/Rv5jFEAf8/+hUU6Km4lzdNm2oGIpesCwl0Vm55e9 KulxjL727sbymMA93mFBtZtdSYxEQd+3hlcjK6GDjzo7SUHH4eYhMdS51kicSfMoO6MY o42tCVYTj7hiiB+WpRAy00mrlpqICu8xHxbaVWAXN+QgkVUP3Q8snb/ultzkfhwXO7lQ tU2aczuOonKWpNiuaWkNSVPQi2HHYv/aX33IDu07o81AInv/mUDn7CVYUwVivcd0DsxH VXcQ== X-Gm-Message-State: AOJu0Yy5VE7+K0qXZe1ngXch6tvOn/+KNtGcfdHRILlH+mKM8hpXvymX Xb18otgM9+F23grwIPhbXZ0= X-Google-Smtp-Source: AGHT+IGdQjhfLiyYszV9b89m6h2FRWrAC2D90udDkz9rhfZlVAeP1ELx8xpTtMgoGR6wj4mx0321jA== X-Received: by 2002:a2e:b545:0:b0:2c1:5645:a2c0 with SMTP id a5-20020a2eb545000000b002c15645a2c0mr550590ljn.35.1698353547496; Thu, 26 Oct 2023 13:52:27 -0700 (PDT) Received: from ?IPV6:2a01:e0a:1dc:b1c0:90a2:30a:7012:b51? ([2a01:e0a:1dc:b1c0:90a2:30a:7012:b51]) by smtp.gmail.com with ESMTPSA id i39-20020a05600c4b2700b00405391f485fsm3402506wmp.41.2023.10.26.13.52.26 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 26 Oct 2023 13:52:26 -0700 (PDT) Content-Type: multipart/mixed; boundary="------------uRmYSIBSmuuoSATXY9A3ZYFI" Message-ID: Date: Thu, 26 Oct 2023 22:52:26 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird From: =?UTF-8?Q?Fran=C3=A7ois_Dumont?= Subject: Re: [PATCH][_Hashtable] Use RAII to restore Rehash state To: Jonathan Wakely Cc: libstdc++ , gcc-patches References: <7f61df18-dd99-4ff5-9fcd-8ca7820403d4@gmail.com> Content-Language: en-US In-Reply-To: X-Spam-Status: No, score=-9.6 required=5.0 tests=BAYES_00,BODY_8BITS,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: This is a multi-part message in MIME format. --------------uRmYSIBSmuuoSATXY9A3ZYFI Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit On 26/10/2023 12:43, Jonathan Wakely wrote: > On 26/10/23 07:18 +0200, François Dumont wrote: >>     libstdc++: [_Hashtable] Use RAII type to manage rehash functor state >> >>     Replace usage of __try/__catch with a RAII type to restore rehash >> functor >>     state when needed. > > Generally I really like replacing try-catch with RAII but I have some > questions below. > >>     libstdc++-v3/ChangeLog: >> >>             * include/bits/hashtable_policy.h (_RehashStateGuard): New. >>             (_Insert_base<>::_M_insert_range(_IIt, _IIt, const >> _NodeGet&, false_type)): >>             Adapt. >>             * include/bits/hashtable.h (__rehash_guard_t): New. >>             (__rehash_state): Remove. >>             (_M_rehash): Remove. >>             (_M_rehash_aux): Rename into _M_rehash. >>             (_M_assign_elements, _M_insert_unique_node, >> _M_insert_multi_node): Adapt. >>             (rehash): Adapt. >> >> >> Tested under Linux x64. >> >> Ok to commit ? >> >> François > >> diff --git a/libstdc++-v3/include/bits/hashtable.h >> b/libstdc++-v3/include/bits/hashtable.h >> index 0857448f7ed..64071ac1fb2 100644 >> --- a/libstdc++-v3/include/bits/hashtable.h >> +++ b/libstdc++-v3/include/bits/hashtable.h >> @@ -234,6 +234,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>                           _RehashPolicy, _Traits>; >>       using __enable_default_ctor >>     = _Hashtable_enable_default_ctor<_Equal, _Hash, _Alloc>; >> +      using __rehash_guard_t >> +    = __detail::_RehashStateGuard<_RehashPolicy>; >> >>     public: >>       typedef _Key                        key_type; >> @@ -264,7 +266,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> >>     private: >>       using __rehash_type = _RehashPolicy; >> -      using __rehash_state = typename __rehash_type::_State; >> >>       using __unique_keys = typename __traits_type::__unique_keys; >> >> @@ -1200,14 +1201,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> >>     private: >>       // Helper rehash method used when keys are unique. >> -      void _M_rehash_aux(size_type __bkt_count, true_type __uks); >> +      void _M_rehash(size_type __bkt_count, true_type __uks); >> >>       // Helper rehash method used when keys can be non-unique. >> -      void _M_rehash_aux(size_type __bkt_count, false_type __uks); >> - >> -      // Unconditionally change size of bucket array to n, restore >> -      // hash policy state to __state on exception. >> -      void _M_rehash(size_type __bkt_count, const __rehash_state& >> __state); >> +      void _M_rehash(size_type __bkt_count, false_type __uks); >>     }; >> >>   // Definitions of class template _Hashtable's out-of-line member >> functions. >> @@ -1337,7 +1334,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>       { >>     __buckets_ptr __former_buckets = nullptr; >>     std::size_t __former_bucket_count = _M_bucket_count; >> -    const __rehash_state& __former_state = _M_rehash_policy._M_state(); >> +    __rehash_guard_t __rehash_guard(_M_rehash_policy); >> >>     if (_M_bucket_count != __ht._M_bucket_count) >>       { >> @@ -1359,6 +1356,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>         _M_assign(std::forward<_Ht>(__ht), __roan); >>         if (__former_buckets) >>           _M_deallocate_buckets(__former_buckets, >> __former_bucket_count); >> +        __rehash_guard._M_reset = false; > > I find this confusing. Usually "reset" means that something is > cleared, so won't take an action in the destructor. e.g. if you use > std::unique_ptr::reset() then the object is destroyed immediately, and > then nothing happens in the destructor. Here it's the opposite, > _M_reset=true means that it _sould_ do something in the destructor. > > The problem is the ambiguity between "reset the state in the > destructor later" and "reset the object to an empty state now". > > If the member was called _M_guarded then it might be clearer. > _M_guarded=true means the guard is active, and will restore the state > later. Or _M_active, or _M_armed, or even _M_reset_in_dtor. Any of > those names avoids the confusion with the semantics of > std::unique_ptr::reset() and similar well-known APIs. > > Or what I usually do is store a pointer to the guarded object in the > RAII guard type, and then just null the pointer to disarm the guard. > That means you don't need a separate bool member variable. If > _RehashStateGuard::_M_rehash_policy was called _M_guarded_obj and was > a _RehashPolicy* instead of _RehashPolicy& then disarming it would be: > >        __rehash_guard._M_guarded_obj = nullptr; > > This seems clear to me, as it says that the guard no longer has > anything to guard, so won't do anything in the destructor. > Looks clearer to me too. I started with a _RehashStateScope and a _M_commit() method, it could have been worst :-) > >>       } >>     __catch(...) >>       { >> @@ -1366,7 +1364,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>           { >>         // Restore previous buckets. >>         _M_deallocate_buckets(); >> -        _M_rehash_policy._M_reset(__former_state); >>         _M_buckets = __former_buckets; >>         _M_bucket_count = __former_bucket_count; >>           } >> @@ -2142,17 +2139,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>               __node_ptr __node, size_type __n_elt) >>     -> iterator >>     { >> -      const __rehash_state& __saved_state = >> _M_rehash_policy._M_state(); >> +      __rehash_guard_t __rehash_guard(_M_rehash_policy); >>       std::pair __do_rehash >>     = _M_rehash_policy._M_need_rehash(_M_bucket_count, _M_element_count, >>                       __n_elt); >> >>       if (__do_rehash.first) >>     { >> -      _M_rehash(__do_rehash.second, __saved_state); >> +      _M_rehash(__do_rehash.second, true_type{}); >>       __bkt = _M_bucket_index(__code); >>     } >> >> +      __rehash_guard._M_reset = false; >>       this->_M_store_code(*__node, __code); > > This changes behaviour. Previously the try-catch was inside the call to > _M_rehash. Now we're starting to guard before calling _M_need_rehash, We were capturing _M_rehash_policy state before the call to _M_need_rehash so it's not different. We really need to instantiate the guard before we call _M_need_rehash cause it is the method changing the _M_rehash_policy state. > and we run the destructor unconditionally even if we never call > _M_rehash. Here we only want to reset rehash state on exception. Note that with the 2 rehash policy implementations we provide it doesn't matter if we reset or not when we do not call _M_rehash because their state changes only when rehash is needed. But some user's fancy rehash policy might update their state even if no rehash is requested and in this case it is better to reset this state only on exception. > I think that's OK, because _M_need_rehash is noexcept (but > it's confusing because we have a const overload of _M_need_rehash > which is noexcept(false) and a non-const overload which is > noexcept(true) ... should they both be noexcept?) That's another subject but yes, _M_need_rehash should be non-const and noexcept on both rehash policy implementations. I considered doing such a thing but won't it impact abi as _Prime_rehash_policy._M_need_rehash symbols is coming from the lib ? > > Can we do this instead: > > > -      const __rehash_state& __saved_state = _M_rehash_policy._M_state(); >        std::pair __do_rehash >      = _M_rehash_policy._M_need_rehash(_M_bucket_count, _M_element_count, >                        __n_elt); > >        if (__do_rehash.first) >      { > +      __rehash_guard_t __rehash_guard(_M_rehash_policy); Clearly no, we need to capture state before _M_need_rehash. Like it was done with the __saved_state above. > -      _M_rehash(__do_rehash.second, __saved_state); > +      _M_rehash(__do_rehash.second, true_type{}); > +      __rehash_guard._M_reset = false; >        __bkt = _M_bucket_index(__code); >      } > >        this->_M_store_code(*__node, __code); > > This only creates a guard object if we're actually going to rehash, so > we don't run its destructor (and check if we need t restore anything) > when no rehash is needed. > > N.B. _M_bucket_index is also confusing. There are two overloads of it, > one is noexcept and one isn't. The one that we call here is > noexcept(false), so that call could throw ... is it OK that we don't > restore the old state if that happens? Should that be guarded too? (I > have no idea). > > Also, I see that the noexcept(true) overload of _M_bucket_index calls > __hash_code_base::_M_bucket_index, which is noexcept(/* condition */), > so the caller says it cannot throw, but it calls something that > sometimes throws. Is that correct? It can be noexcept because usage of hash code cache depends on either hash functor is noexcept or not. > >> >>       // Always insert at the beginning of the bucket. >> @@ -2172,13 +2170,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>              __hash_code __code, __node_ptr __node) >>     -> iterator >>     { >> -      const __rehash_state& __saved_state = >> _M_rehash_policy._M_state(); >> +      __rehash_guard_t __rehash_guard(_M_rehash_policy); >>       std::pair __do_rehash >>     = _M_rehash_policy._M_need_rehash(_M_bucket_count, >> _M_element_count, 1); >> >>       if (__do_rehash.first) >> -    _M_rehash(__do_rehash.second, __saved_state); >> +    _M_rehash(__do_rehash.second, false_type{}); >> >> +      __rehash_guard._M_reset = false; > > Again, we're creating the guard object in a larger scope than needed. > >>       this->_M_store_code(*__node, __code); >>       const key_type& __k = _ExtractKey{}(__node->_M_v()); >>       size_type __bkt = _M_bucket_index(__code); >> @@ -2509,39 +2508,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>            _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>:: >>     rehash(size_type __bkt_count) >>     { >> -      const __rehash_state& __saved_state = >> _M_rehash_policy._M_state(); >> +      __rehash_guard_t __rehash_guard(_M_rehash_policy); >>       __bkt_count >>     = std::max(_M_rehash_policy._M_bkt_for_elements(_M_element_count >> + 1), >>            __bkt_count); >>       __bkt_count = _M_rehash_policy._M_next_bkt(__bkt_count); >> >>       if (__bkt_count != _M_bucket_count) >> -    _M_rehash(__bkt_count, __saved_state); >> -      else >> -    // No rehash, restore previous state to keep it consistent with >> -    // container state. >> -    _M_rehash_policy._M_reset(__saved_state); >> -    } >> - >> -  template> -       typename _ExtractKey, typename _Equal, >> -       typename _Hash, typename _RangeHash, typename _Unused, >> -       typename _RehashPolicy, typename _Traits> >> -    void >> -    _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, >> -           _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>:: >> -    _M_rehash(size_type __bkt_count, const __rehash_state& __state) >> -    { >> -      __try >> -    { >> -      _M_rehash_aux(__bkt_count, __unique_keys{}); >> -    } >> -      __catch(...) >>     { >> -      // A failure here means that buckets allocation failed. We only >> -      // have to restore hash policy previous state. >> -      _M_rehash_policy._M_reset(__state); >> -      __throw_exception_again; >> +      _M_rehash(__bkt_count, __unique_keys{}); >> +      __rehash_guard._M_reset = false; > > And a larger scope again. > >>     } >>     } >> >> @@ -2553,7 +2529,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>     void >>     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, >>            _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>:: >> -    _M_rehash_aux(size_type __bkt_count, true_type /* __uks */) >> +    _M_rehash(size_type __bkt_count, true_type /* __uks */) >>     { >>       __buckets_ptr __new_buckets = _M_allocate_buckets(__bkt_count); >>       __node_ptr __p = _M_begin(); >> @@ -2596,7 +2572,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>     void >>     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, >>            _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>:: >> -    _M_rehash_aux(size_type __bkt_count, false_type /* __uks */) >> +    _M_rehash(size_type __bkt_count, false_type /* __uks */) >>     { >>       __buckets_ptr __new_buckets = _M_allocate_buckets(__bkt_count); >>       __node_ptr __p = _M_begin(); >> diff --git a/libstdc++-v3/include/bits/hashtable_policy.h >> b/libstdc++-v3/include/bits/hashtable_policy.h >> index 5d162463dc3..8b9626b1575 100644 >> --- a/libstdc++-v3/include/bits/hashtable_policy.h >> +++ b/libstdc++-v3/include/bits/hashtable_policy.h >> @@ -715,6 +715,26 @@ namespace __detail >>     std::size_t    _M_next_resize; >>   }; >> >> +  template >> +    struct _RehashStateGuard >> +    { >> +      _RehashPolicy& _M_rehash_policy; >> +      typename _RehashPolicy::_State _M_prev_state; >> +      bool _M_reset = true; > > This could be: > > +      _RehashPolicy* _M_guarded_obj; > +      typename _RehashPolicy::_State _M_prev_state; > >> + >> +      _RehashStateGuard(_RehashPolicy& __policy) >> +      : _M_rehash_policy(__policy) >> +      , _M_prev_state(__policy._M_state()) > > +      _RehashStateGuard(_RehashPolicy& __policy) > +      : _M_guarded_obj(std::__addressof(__policy)) > +      , _M_prev_state(__policy._M_state()) > >> +      { } >> +      _RehashStateGuard(const _RehashStateGuard&) = delete; >> + >> +      ~_RehashStateGuard() >> +      { >> +    if (_M_reset) >> +      _M_rehash_policy._M_reset(_M_prev_state); > > +    if (_M_guarded_obj) > +      _M_guarded_obj->_M_reset(_M_prev_state); > >> +      } >> +    }; >> + >>   // Base classes for std::_Hashtable.  We define these base classes >>   // because in some cases we want to do different things depending on >>   // the value of a policy class.  In some cases the policy class >> @@ -1007,7 +1027,7 @@ namespace __detail >>               const _NodeGetter& __node_gen, false_type __uks) >>       { >>     using __rehash_type = typename __hashtable::__rehash_type; >> -    using __rehash_state = typename __hashtable::__rehash_state; >> +    using __rehash_guard_t = typename __hashtable::__rehash_guard_t; >>     using pair_type = std::pair; >> >>     size_type __n_elt = __detail::__distance_fw(__first, __last); >> @@ -1016,14 +1036,15 @@ namespace __detail >> >>     __hashtable& __h = _M_conjure_hashtable(); >>     __rehash_type& __rehash = __h._M_rehash_policy; >> -    const __rehash_state& __saved_state = __rehash._M_state(); >> +    __rehash_guard_t __rehash_guard(__rehash); >>     pair_type __do_rehash = __rehash._M_need_rehash(__h._M_bucket_count, >>                             __h._M_element_count, >>                             __n_elt); >> >>     if (__do_rehash.first) >> -      __h._M_rehash(__do_rehash.second, __saved_state); >> +      __h._M_rehash(__do_rehash.second, __uks); >> >> +    __rehash_guard._M_reset = false; > > A larger scope again. Note that there is only one place where we reset rehash policy state even if no exception took place. It is in the std rehash method when the user requests a rehash that has no effect. Here is the updated patch, ok to commit ? --------------uRmYSIBSmuuoSATXY9A3ZYFI Content-Type: text/plain; charset=UTF-8; name="raii_rehash_state.txt" Content-Disposition: attachment; filename="raii_rehash_state.txt" Content-Transfer-Encoding: base64 ZGlmZiAtLWdpdCBhL2xpYnN0ZGMrKy12My9pbmNsdWRlL2JpdHMvaGFzaHRhYmxlLmggYi9s aWJzdGRjKystdjMvaW5jbHVkZS9iaXRzL2hhc2h0YWJsZS5oCmluZGV4IDA4NTc0NDhmN2Vk Li44OTEzMjQzMGYzZSAxMDA2NDQKLS0tIGEvbGlic3RkYysrLXYzL2luY2x1ZGUvYml0cy9o YXNodGFibGUuaAorKysgYi9saWJzdGRjKystdjMvaW5jbHVkZS9iaXRzL2hhc2h0YWJsZS5o CkBAIC0yMzQsNiArMjM0LDggQEAgX0dMSUJDWFhfQkVHSU5fTkFNRVNQQUNFX1ZFUlNJT04K IAkJCQkJICAgICAgX1JlaGFzaFBvbGljeSwgX1RyYWl0cz47CiAgICAgICB1c2luZyBfX2Vu YWJsZV9kZWZhdWx0X2N0b3IKIAk9IF9IYXNodGFibGVfZW5hYmxlX2RlZmF1bHRfY3Rvcjxf RXF1YWwsIF9IYXNoLCBfQWxsb2M+OworICAgICAgdXNpbmcgX19yZWhhc2hfZ3VhcmRfdAor CT0gX19kZXRhaWw6Ol9SZWhhc2hTdGF0ZUd1YXJkPF9SZWhhc2hQb2xpY3k+OwogCiAgICAg cHVibGljOgogICAgICAgdHlwZWRlZiBfS2V5CQkJCQkJa2V5X3R5cGU7CkBAIC0yNjQsNyAr MjY2LDYgQEAgX0dMSUJDWFhfQkVHSU5fTkFNRVNQQUNFX1ZFUlNJT04KIAogICAgIHByaXZh dGU6CiAgICAgICB1c2luZyBfX3JlaGFzaF90eXBlID0gX1JlaGFzaFBvbGljeTsKLSAgICAg IHVzaW5nIF9fcmVoYXNoX3N0YXRlID0gdHlwZW5hbWUgX19yZWhhc2hfdHlwZTo6X1N0YXRl OwogCiAgICAgICB1c2luZyBfX3VuaXF1ZV9rZXlzID0gdHlwZW5hbWUgX190cmFpdHNfdHlw ZTo6X191bmlxdWVfa2V5czsKIApAQCAtMTIwMCwxNCArMTIwMSwxMCBAQCBfR0xJQkNYWF9C RUdJTl9OQU1FU1BBQ0VfVkVSU0lPTgogCiAgICAgcHJpdmF0ZToKICAgICAgIC8vIEhlbHBl ciByZWhhc2ggbWV0aG9kIHVzZWQgd2hlbiBrZXlzIGFyZSB1bmlxdWUuCi0gICAgICB2b2lk IF9NX3JlaGFzaF9hdXgoc2l6ZV90eXBlIF9fYmt0X2NvdW50LCB0cnVlX3R5cGUgX191a3Mp OworICAgICAgdm9pZCBfTV9yZWhhc2goc2l6ZV90eXBlIF9fYmt0X2NvdW50LCB0cnVlX3R5 cGUgX191a3MpOwogCiAgICAgICAvLyBIZWxwZXIgcmVoYXNoIG1ldGhvZCB1c2VkIHdoZW4g a2V5cyBjYW4gYmUgbm9uLXVuaXF1ZS4KLSAgICAgIHZvaWQgX01fcmVoYXNoX2F1eChzaXpl X3R5cGUgX19ia3RfY291bnQsIGZhbHNlX3R5cGUgX191a3MpOwotCi0gICAgICAvLyBVbmNv bmRpdGlvbmFsbHkgY2hhbmdlIHNpemUgb2YgYnVja2V0IGFycmF5IHRvIG4sIHJlc3RvcmUK LSAgICAgIC8vIGhhc2ggcG9saWN5IHN0YXRlIHRvIF9fc3RhdGUgb24gZXhjZXB0aW9uLgot ICAgICAgdm9pZCBfTV9yZWhhc2goc2l6ZV90eXBlIF9fYmt0X2NvdW50LCBjb25zdCBfX3Jl aGFzaF9zdGF0ZSYgX19zdGF0ZSk7CisgICAgICB2b2lkIF9NX3JlaGFzaChzaXplX3R5cGUg X19ia3RfY291bnQsIGZhbHNlX3R5cGUgX191a3MpOwogICAgIH07CiAKICAgLy8gRGVmaW5p dGlvbnMgb2YgY2xhc3MgdGVtcGxhdGUgX0hhc2h0YWJsZSdzIG91dC1vZi1saW5lIG1lbWJl ciBmdW5jdGlvbnMuCkBAIC0xMzM3LDcgKzEzMzQsNyBAQCBfR0xJQkNYWF9CRUdJTl9OQU1F U1BBQ0VfVkVSU0lPTgogICAgICAgewogCV9fYnVja2V0c19wdHIgX19mb3JtZXJfYnVja2V0 cyA9IG51bGxwdHI7CiAJc3RkOjpzaXplX3QgX19mb3JtZXJfYnVja2V0X2NvdW50ID0gX01f YnVja2V0X2NvdW50OwotCWNvbnN0IF9fcmVoYXNoX3N0YXRlJiBfX2Zvcm1lcl9zdGF0ZSA9 IF9NX3JlaGFzaF9wb2xpY3kuX01fc3RhdGUoKTsKKwlfX3JlaGFzaF9ndWFyZF90IF9fcmVo YXNoX2d1YXJkKF9NX3JlaGFzaF9wb2xpY3kpOwogCiAJaWYgKF9NX2J1Y2tldF9jb3VudCAh PSBfX2h0Ll9NX2J1Y2tldF9jb3VudCkKIAkgIHsKQEAgLTEzNTksNiArMTM1Niw3IEBAIF9H TElCQ1hYX0JFR0lOX05BTUVTUEFDRV9WRVJTSU9OCiAJICAgIF9NX2Fzc2lnbihzdGQ6OmZv cndhcmQ8X0h0PihfX2h0KSwgX19yb2FuKTsKIAkgICAgaWYgKF9fZm9ybWVyX2J1Y2tldHMp CiAJICAgICAgX01fZGVhbGxvY2F0ZV9idWNrZXRzKF9fZm9ybWVyX2J1Y2tldHMsIF9fZm9y bWVyX2J1Y2tldF9jb3VudCk7CisJICAgIF9fcmVoYXNoX2d1YXJkLl9NX2d1YXJkZWRfb2Jq ID0gbnVsbHB0cjsKIAkgIH0KIAlfX2NhdGNoKC4uLikKIAkgIHsKQEAgLTEzNjYsNyArMTM2 NCw2IEBAIF9HTElCQ1hYX0JFR0lOX05BTUVTUEFDRV9WRVJTSU9OCiAJICAgICAgewogCQkv LyBSZXN0b3JlIHByZXZpb3VzIGJ1Y2tldHMuCiAJCV9NX2RlYWxsb2NhdGVfYnVja2V0cygp OwotCQlfTV9yZWhhc2hfcG9saWN5Ll9NX3Jlc2V0KF9fZm9ybWVyX3N0YXRlKTsKIAkJX01f YnVja2V0cyA9IF9fZm9ybWVyX2J1Y2tldHM7CiAJCV9NX2J1Y2tldF9jb3VudCA9IF9fZm9y bWVyX2J1Y2tldF9jb3VudDsKIAkgICAgICB9CkBAIC0yMTQyLDE3ICsyMTM5LDE4IEBAIF9H TElCQ1hYX0JFR0lOX05BTUVTUEFDRV9WRVJTSU9OCiAJCQkgIF9fbm9kZV9wdHIgX19ub2Rl LCBzaXplX3R5cGUgX19uX2VsdCkKICAgICAtPiBpdGVyYXRvcgogICAgIHsKLSAgICAgIGNv bnN0IF9fcmVoYXNoX3N0YXRlJiBfX3NhdmVkX3N0YXRlID0gX01fcmVoYXNoX3BvbGljeS5f TV9zdGF0ZSgpOworICAgICAgX19yZWhhc2hfZ3VhcmRfdCBfX3JlaGFzaF9ndWFyZChfTV9y ZWhhc2hfcG9saWN5KTsKICAgICAgIHN0ZDo6cGFpcjxib29sLCBzdGQ6OnNpemVfdD4gX19k b19yZWhhc2gKIAk9IF9NX3JlaGFzaF9wb2xpY3kuX01fbmVlZF9yZWhhc2goX01fYnVja2V0 X2NvdW50LCBfTV9lbGVtZW50X2NvdW50LAogCQkJCQkgIF9fbl9lbHQpOwogCiAgICAgICBp ZiAoX19kb19yZWhhc2guZmlyc3QpCiAJewotCSAgX01fcmVoYXNoKF9fZG9fcmVoYXNoLnNl Y29uZCwgX19zYXZlZF9zdGF0ZSk7CisJICBfTV9yZWhhc2goX19kb19yZWhhc2guc2Vjb25k LCB0cnVlX3R5cGV7fSk7CiAJICBfX2JrdCA9IF9NX2J1Y2tldF9pbmRleChfX2NvZGUpOwog CX0KIAorICAgICAgX19yZWhhc2hfZ3VhcmQuX01fZ3VhcmRlZF9vYmogPSBudWxscHRyOwog ICAgICAgdGhpcy0+X01fc3RvcmVfY29kZSgqX19ub2RlLCBfX2NvZGUpOwogCiAgICAgICAv LyBBbHdheXMgaW5zZXJ0IGF0IHRoZSBiZWdpbm5pbmcgb2YgdGhlIGJ1Y2tldC4KQEAgLTIx NzIsMTMgKzIxNzAsMTQgQEAgX0dMSUJDWFhfQkVHSU5fTkFNRVNQQUNFX1ZFUlNJT04KIAkJ CSBfX2hhc2hfY29kZSBfX2NvZGUsIF9fbm9kZV9wdHIgX19ub2RlKQogICAgIC0+IGl0ZXJh dG9yCiAgICAgewotICAgICAgY29uc3QgX19yZWhhc2hfc3RhdGUmIF9fc2F2ZWRfc3RhdGUg PSBfTV9yZWhhc2hfcG9saWN5Ll9NX3N0YXRlKCk7CisgICAgICBfX3JlaGFzaF9ndWFyZF90 IF9fcmVoYXNoX2d1YXJkKF9NX3JlaGFzaF9wb2xpY3kpOwogICAgICAgc3RkOjpwYWlyPGJv b2wsIHN0ZDo6c2l6ZV90PiBfX2RvX3JlaGFzaAogCT0gX01fcmVoYXNoX3BvbGljeS5fTV9u ZWVkX3JlaGFzaChfTV9idWNrZXRfY291bnQsIF9NX2VsZW1lbnRfY291bnQsIDEpOwogCiAg ICAgICBpZiAoX19kb19yZWhhc2guZmlyc3QpCi0JX01fcmVoYXNoKF9fZG9fcmVoYXNoLnNl Y29uZCwgX19zYXZlZF9zdGF0ZSk7CisJX01fcmVoYXNoKF9fZG9fcmVoYXNoLnNlY29uZCwg ZmFsc2VfdHlwZXt9KTsKIAorICAgICAgX19yZWhhc2hfZ3VhcmQuX01fZ3VhcmRlZF9vYmog PSBudWxscHRyOwogICAgICAgdGhpcy0+X01fc3RvcmVfY29kZSgqX19ub2RlLCBfX2NvZGUp OwogICAgICAgY29uc3Qga2V5X3R5cGUmIF9fayA9IF9FeHRyYWN0S2V5e30oX19ub2RlLT5f TV92KCkpOwogICAgICAgc2l6ZV90eXBlIF9fYmt0ID0gX01fYnVja2V0X2luZGV4KF9fY29k ZSk7CkBAIC0yNTA5LDM5ICsyNTA4LDE2IEBAIF9HTElCQ1hYX0JFR0lOX05BTUVTUEFDRV9W RVJTSU9OCiAJICAgICAgIF9IYXNoLCBfUmFuZ2VIYXNoLCBfVW51c2VkLCBfUmVoYXNoUG9s aWN5LCBfVHJhaXRzPjo6CiAgICAgcmVoYXNoKHNpemVfdHlwZSBfX2JrdF9jb3VudCkKICAg ICB7Ci0gICAgICBjb25zdCBfX3JlaGFzaF9zdGF0ZSYgX19zYXZlZF9zdGF0ZSA9IF9NX3Jl aGFzaF9wb2xpY3kuX01fc3RhdGUoKTsKKyAgICAgIF9fcmVoYXNoX2d1YXJkX3QgX19yZWhh c2hfZ3VhcmQoX01fcmVoYXNoX3BvbGljeSk7CiAgICAgICBfX2JrdF9jb3VudAogCT0gc3Rk OjptYXgoX01fcmVoYXNoX3BvbGljeS5fTV9ia3RfZm9yX2VsZW1lbnRzKF9NX2VsZW1lbnRf Y291bnQgKyAxKSwKIAkJICAgX19ia3RfY291bnQpOwogICAgICAgX19ia3RfY291bnQgPSBf TV9yZWhhc2hfcG9saWN5Ll9NX25leHRfYmt0KF9fYmt0X2NvdW50KTsKIAogICAgICAgaWYg KF9fYmt0X2NvdW50ICE9IF9NX2J1Y2tldF9jb3VudCkKLQlfTV9yZWhhc2goX19ia3RfY291 bnQsIF9fc2F2ZWRfc3RhdGUpOwotICAgICAgZWxzZQotCS8vIE5vIHJlaGFzaCwgcmVzdG9y ZSBwcmV2aW91cyBzdGF0ZSB0byBrZWVwIGl0IGNvbnNpc3RlbnQgd2l0aAotCS8vIGNvbnRh aW5lciBzdGF0ZS4KLQlfTV9yZWhhc2hfcG9saWN5Ll9NX3Jlc2V0KF9fc2F2ZWRfc3RhdGUp OwotICAgIH0KLQotICB0ZW1wbGF0ZTx0eXBlbmFtZSBfS2V5LCB0eXBlbmFtZSBfVmFsdWUs IHR5cGVuYW1lIF9BbGxvYywKLQkgICB0eXBlbmFtZSBfRXh0cmFjdEtleSwgdHlwZW5hbWUg X0VxdWFsLAotCSAgIHR5cGVuYW1lIF9IYXNoLCB0eXBlbmFtZSBfUmFuZ2VIYXNoLCB0eXBl bmFtZSBfVW51c2VkLAotCSAgIHR5cGVuYW1lIF9SZWhhc2hQb2xpY3ksIHR5cGVuYW1lIF9U cmFpdHM+Ci0gICAgdm9pZAotICAgIF9IYXNodGFibGU8X0tleSwgX1ZhbHVlLCBfQWxsb2Ms IF9FeHRyYWN0S2V5LCBfRXF1YWwsCi0JICAgICAgIF9IYXNoLCBfUmFuZ2VIYXNoLCBfVW51 c2VkLCBfUmVoYXNoUG9saWN5LCBfVHJhaXRzPjo6Ci0gICAgX01fcmVoYXNoKHNpemVfdHlw ZSBfX2JrdF9jb3VudCwgY29uc3QgX19yZWhhc2hfc3RhdGUmIF9fc3RhdGUpCi0gICAgewot ICAgICAgX190cnkKLQl7Ci0JICBfTV9yZWhhc2hfYXV4KF9fYmt0X2NvdW50LCBfX3VuaXF1 ZV9rZXlze30pOwotCX0KLSAgICAgIF9fY2F0Y2goLi4uKQogCXsKLQkgIC8vIEEgZmFpbHVy ZSBoZXJlIG1lYW5zIHRoYXQgYnVja2V0cyBhbGxvY2F0aW9uIGZhaWxlZC4gIFdlIG9ubHkK LQkgIC8vIGhhdmUgdG8gcmVzdG9yZSBoYXNoIHBvbGljeSBwcmV2aW91cyBzdGF0ZS4KLQkg IF9NX3JlaGFzaF9wb2xpY3kuX01fcmVzZXQoX19zdGF0ZSk7Ci0JICBfX3Rocm93X2V4Y2Vw dGlvbl9hZ2FpbjsKKwkgIF9NX3JlaGFzaChfX2JrdF9jb3VudCwgX191bmlxdWVfa2V5c3t9 KTsKKwkgIF9fcmVoYXNoX2d1YXJkLl9NX2d1YXJkZWRfb2JqID0gbnVsbHB0cjsKIAl9CiAg ICAgfQogCkBAIC0yNTUzLDcgKzI1MjksNyBAQCBfR0xJQkNYWF9CRUdJTl9OQU1FU1BBQ0Vf VkVSU0lPTgogICAgIHZvaWQKICAgICBfSGFzaHRhYmxlPF9LZXksIF9WYWx1ZSwgX0FsbG9j LCBfRXh0cmFjdEtleSwgX0VxdWFsLAogCSAgICAgICBfSGFzaCwgX1JhbmdlSGFzaCwgX1Vu dXNlZCwgX1JlaGFzaFBvbGljeSwgX1RyYWl0cz46OgotICAgIF9NX3JlaGFzaF9hdXgoc2l6 ZV90eXBlIF9fYmt0X2NvdW50LCB0cnVlX3R5cGUgLyogX191a3MgKi8pCisgICAgX01fcmVo YXNoKHNpemVfdHlwZSBfX2JrdF9jb3VudCwgdHJ1ZV90eXBlIC8qIF9fdWtzICovKQogICAg IHsKICAgICAgIF9fYnVja2V0c19wdHIgX19uZXdfYnVja2V0cyA9IF9NX2FsbG9jYXRlX2J1 Y2tldHMoX19ia3RfY291bnQpOwogICAgICAgX19ub2RlX3B0ciBfX3AgPSBfTV9iZWdpbigp OwpAQCAtMjU5Niw3ICsyNTcyLDcgQEAgX0dMSUJDWFhfQkVHSU5fTkFNRVNQQUNFX1ZFUlNJ T04KICAgICB2b2lkCiAgICAgX0hhc2h0YWJsZTxfS2V5LCBfVmFsdWUsIF9BbGxvYywgX0V4 dHJhY3RLZXksIF9FcXVhbCwKIAkgICAgICAgX0hhc2gsIF9SYW5nZUhhc2gsIF9VbnVzZWQs IF9SZWhhc2hQb2xpY3ksIF9UcmFpdHM+OjoKLSAgICBfTV9yZWhhc2hfYXV4KHNpemVfdHlw ZSBfX2JrdF9jb3VudCwgZmFsc2VfdHlwZSAvKiBfX3VrcyAqLykKKyAgICBfTV9yZWhhc2go c2l6ZV90eXBlIF9fYmt0X2NvdW50LCBmYWxzZV90eXBlIC8qIF9fdWtzICovKQogICAgIHsK ICAgICAgIF9fYnVja2V0c19wdHIgX19uZXdfYnVja2V0cyA9IF9NX2FsbG9jYXRlX2J1Y2tl dHMoX19ia3RfY291bnQpOwogICAgICAgX19ub2RlX3B0ciBfX3AgPSBfTV9iZWdpbigpOwpk aWZmIC0tZ2l0IGEvbGlic3RkYysrLXYzL2luY2x1ZGUvYml0cy9oYXNodGFibGVfcG9saWN5 LmggYi9saWJzdGRjKystdjMvaW5jbHVkZS9iaXRzL2hhc2h0YWJsZV9wb2xpY3kuaAppbmRl eCA1ZDE2MjQ2M2RjMy4uNWI5N2IyNGUxNTYgMTAwNjQ0Ci0tLSBhL2xpYnN0ZGMrKy12My9p bmNsdWRlL2JpdHMvaGFzaHRhYmxlX3BvbGljeS5oCisrKyBiL2xpYnN0ZGMrKy12My9pbmNs dWRlL2JpdHMvaGFzaHRhYmxlX3BvbGljeS5oCkBAIC03MTUsNiArNzE1LDI1IEBAIG5hbWVz cGFjZSBfX2RldGFpbAogICAgIHN0ZDo6c2l6ZV90CV9NX25leHRfcmVzaXplOwogICB9Owog CisgIHRlbXBsYXRlPHR5cGVuYW1lIF9SZWhhc2hQb2xpY3k+CisgICAgc3RydWN0IF9SZWhh c2hTdGF0ZUd1YXJkCisgICAgeworICAgICAgX1JlaGFzaFBvbGljeSogX01fZ3VhcmRlZF9v Ymo7CisgICAgICB0eXBlbmFtZSBfUmVoYXNoUG9saWN5OjpfU3RhdGUgX01fcHJldl9zdGF0 ZTsKKworICAgICAgX1JlaGFzaFN0YXRlR3VhcmQoX1JlaGFzaFBvbGljeSYgX19wb2xpY3kp CisgICAgICA6IF9NX2d1YXJkZWRfb2JqKHN0ZDo6X19hZGRyZXNzb2YoX19wb2xpY3kpKQor ICAgICAgLCBfTV9wcmV2X3N0YXRlKF9fcG9saWN5Ll9NX3N0YXRlKCkpCisgICAgICB7IH0K KyAgICAgIF9SZWhhc2hTdGF0ZUd1YXJkKGNvbnN0IF9SZWhhc2hTdGF0ZUd1YXJkJikgPSBk ZWxldGU7CisKKyAgICAgIH5fUmVoYXNoU3RhdGVHdWFyZCgpCisgICAgICB7CisJaWYgKF9N X2d1YXJkZWRfb2JqKQorCSAgX01fZ3VhcmRlZF9vYmotPl9NX3Jlc2V0KF9NX3ByZXZfc3Rh dGUpOworICAgICAgfQorICAgIH07CisKICAgLy8gQmFzZSBjbGFzc2VzIGZvciBzdGQ6Ol9I YXNodGFibGUuICBXZSBkZWZpbmUgdGhlc2UgYmFzZSBjbGFzc2VzCiAgIC8vIGJlY2F1c2Ug aW4gc29tZSBjYXNlcyB3ZSB3YW50IHRvIGRvIGRpZmZlcmVudCB0aGluZ3MgZGVwZW5kaW5n IG9uCiAgIC8vIHRoZSB2YWx1ZSBvZiBhIHBvbGljeSBjbGFzcy4gIEluIHNvbWUgY2FzZXMg dGhlIHBvbGljeSBjbGFzcwpAQCAtMTAwNiwyNCArMTAyNSwyNCBAQCBuYW1lc3BhY2UgX19k ZXRhaWwKICAgICAgIF9NX2luc2VydF9yYW5nZShfSW5wdXRJdGVyYXRvciBfX2ZpcnN0LCBf SW5wdXRJdGVyYXRvciBfX2xhc3QsCiAJCSAgICAgIGNvbnN0IF9Ob2RlR2V0dGVyJiBfX25v ZGVfZ2VuLCBmYWxzZV90eXBlIF9fdWtzKQogICAgICAgewotCXVzaW5nIF9fcmVoYXNoX3R5 cGUgPSB0eXBlbmFtZSBfX2hhc2h0YWJsZTo6X19yZWhhc2hfdHlwZTsKLQl1c2luZyBfX3Jl aGFzaF9zdGF0ZSA9IHR5cGVuYW1lIF9faGFzaHRhYmxlOjpfX3JlaGFzaF9zdGF0ZTsKLQl1 c2luZyBwYWlyX3R5cGUgPSBzdGQ6OnBhaXI8Ym9vbCwgc3RkOjpzaXplX3Q+OworCXVzaW5n IF9fcmVoYXNoX2d1YXJkX3QgPSB0eXBlbmFtZSBfX2hhc2h0YWJsZTo6X19yZWhhc2hfZ3Vh cmRfdDsKKwl1c2luZyBfX3BhaXJfdHlwZSA9IHN0ZDo6cGFpcjxib29sLCBzdGQ6OnNpemVf dD47CiAKIAlzaXplX3R5cGUgX19uX2VsdCA9IF9fZGV0YWlsOjpfX2Rpc3RhbmNlX2Z3KF9f Zmlyc3QsIF9fbGFzdCk7CiAJaWYgKF9fbl9lbHQgPT0gMCkKIAkgIHJldHVybjsKIAogCV9f aGFzaHRhYmxlJiBfX2ggPSBfTV9jb25qdXJlX2hhc2h0YWJsZSgpOwotCV9fcmVoYXNoX3R5 cGUmIF9fcmVoYXNoID0gX19oLl9NX3JlaGFzaF9wb2xpY3k7Ci0JY29uc3QgX19yZWhhc2hf c3RhdGUmIF9fc2F2ZWRfc3RhdGUgPSBfX3JlaGFzaC5fTV9zdGF0ZSgpOwotCXBhaXJfdHlw ZSBfX2RvX3JlaGFzaCA9IF9fcmVoYXNoLl9NX25lZWRfcmVoYXNoKF9faC5fTV9idWNrZXRf Y291bnQsCi0JCQkJCQkJX19oLl9NX2VsZW1lbnRfY291bnQsCi0JCQkJCQkJX19uX2VsdCk7 CisJX19yZWhhc2hfZ3VhcmRfdCBfX3JlaGFzaF9ndWFyZChfX2guX01fcmVoYXNoX3BvbGlj eSk7CisJX19wYWlyX3R5cGUgX19kb19yZWhhc2gKKwkgID0gX19oLl9NX3JlaGFzaF9wb2xp Y3kuX01fbmVlZF9yZWhhc2goX19oLl9NX2J1Y2tldF9jb3VudCwKKwkJCQkJCV9faC5fTV9l bGVtZW50X2NvdW50LAorCQkJCQkJX19uX2VsdCk7CiAKIAlpZiAoX19kb19yZWhhc2guZmly c3QpCi0JICBfX2guX01fcmVoYXNoKF9fZG9fcmVoYXNoLnNlY29uZCwgX19zYXZlZF9zdGF0 ZSk7CisJICBfX2guX01fcmVoYXNoKF9fZG9fcmVoYXNoLnNlY29uZCwgX191a3MpOwogCisJ X19yZWhhc2hfZ3VhcmQuX01fZ3VhcmRlZF9vYmogPSBudWxscHRyOwogCWZvciAoOyBfX2Zp cnN0ICE9IF9fbGFzdDsgKytfX2ZpcnN0KQogCSAgX19oLl9NX2luc2VydCgqX19maXJzdCwg X19ub2RlX2dlbiwgX191a3MpOwogICAgICAgfQo= --------------uRmYSIBSmuuoSATXY9A3ZYFI--