Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. // unique_ptr implementation -*- C++ -*-
  2.  
  3. // Copyright (C) 2008-2013 Free Software Foundation, Inc.
  4. //
  5. // This file is part of the GNU ISO C++ Library.  This library is free
  6. // software; you can redistribute it and/or modify it under the
  7. // terms of the GNU General Public License as published by the
  8. // Free Software Foundation; either version 3, or (at your option)
  9. // any later version.
  10.  
  11. // This library is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. // GNU General Public License for more details.
  15.  
  16. // Under Section 7 of GPL version 3, you are granted additional
  17. // permissions described in the GCC Runtime Library Exception, version
  18. // 3.1, as published by the Free Software Foundation.
  19.  
  20. // You should have received a copy of the GNU General Public License and
  21. // a copy of the GCC Runtime Library Exception along with this program;
  22. // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
  23. // <http://www.gnu.org/licenses/>.
  24.  
  25. /** @file bits/unique_ptr.h
  26.  *  This is an internal header file, included by other library headers.
  27.  *  Do not attempt to use it directly. @headername{memory}
  28.  */
  29.  
  30. #ifndef _UNIQUE_PTR_H
  31. #define _UNIQUE_PTR_H 1
  32.  
  33. #include <bits/c++config.h>
  34. #include <debug/debug.h>
  35. #include <type_traits>
  36. #include <utility>
  37. #include <tuple>
  38.  
  39. namespace std _GLIBCXX_VISIBILITY(default)
  40. {
  41. _GLIBCXX_BEGIN_NAMESPACE_VERSION
  42.  
  43.   /**
  44.    * @addtogroup pointer_abstractions
  45.    * @{
  46.    */
  47.  
  48. #if _GLIBCXX_USE_DEPRECATED
  49.   template<typename> class auto_ptr;
  50. #endif
  51.  
  52.   /// Primary template, default_delete.
  53.   template<typename _Tp>
  54.     struct default_delete
  55.     {
  56.       constexpr default_delete() noexcept = default;
  57.  
  58.       template<typename _Up, typename = typename
  59.                enable_if<is_convertible<_Up*, _Tp*>::value>::type>
  60.         default_delete(const default_delete<_Up>&) noexcept { }
  61.  
  62.       void
  63.       operator()(_Tp* __ptr) const
  64.       {
  65.         static_assert(sizeof(_Tp)>0,
  66.                       "can't delete pointer to incomplete type");
  67.         delete __ptr;
  68.       }
  69.     };
  70.  
  71.   // _GLIBCXX_RESOLVE_LIB_DEFECTS
  72.   // DR 740 - omit specialization for array objects with a compile time length
  73.   /// Specialization, default_delete.
  74.   template<typename _Tp>
  75.     struct default_delete<_Tp[]>
  76.     {
  77.     private:
  78.       template<typename _Up>
  79.         using __remove_cv = typename remove_cv<_Up>::type;
  80.  
  81.       // Like is_base_of<_Tp, _Up> but false if unqualified types are the same
  82.       template<typename _Up>
  83.         using __is_derived_Tp
  84.           = __and_< is_base_of<_Tp, _Up>,
  85.                     __not_<is_same<__remove_cv<_Tp>, __remove_cv<_Up>>> >;
  86.  
  87.     public:
  88.       constexpr default_delete() noexcept = default;
  89.  
  90.       template<typename _Up, typename = typename
  91.                enable_if<!__is_derived_Tp<_Up>::value>::type>
  92.         default_delete(const default_delete<_Up[]>&) noexcept { }
  93.  
  94.       void
  95.       operator()(_Tp* __ptr) const
  96.       {
  97.         static_assert(sizeof(_Tp)>0,
  98.                       "can't delete pointer to incomplete type");
  99.         delete [] __ptr;
  100.       }
  101.  
  102.       template<typename _Up>
  103.         typename enable_if<__is_derived_Tp<_Up>::value>::type
  104.         operator()(_Up*) const = delete;
  105.     };
  106.  
  107.   /// 20.7.1.2 unique_ptr for single objects.
  108.   template <typename _Tp, typename _Dp = default_delete<_Tp> >
  109.     class unique_ptr
  110.     {
  111.       // use SFINAE to determine whether _Del::pointer exists
  112.       class _Pointer
  113.       {
  114.         template<typename _Up>
  115.           static typename _Up::pointer __test(typename _Up::pointer*);
  116.  
  117.         template<typename _Up>
  118.           static _Tp* __test(...);
  119.  
  120.         typedef typename remove_reference<_Dp>::type _Del;
  121.  
  122.       public:
  123.         typedef decltype(__test<_Del>(0)) type;
  124.       };
  125.  
  126.       typedef std::tuple<typename _Pointer::type, _Dp>  __tuple_type;
  127.       __tuple_type                                      _M_t;
  128.  
  129.     public:
  130.       typedef typename _Pointer::type   pointer;
  131.       typedef _Tp                       element_type;
  132.       typedef _Dp                       deleter_type;
  133.  
  134.       // Constructors.
  135.       constexpr unique_ptr() noexcept
  136.       : _M_t()
  137.       { static_assert(!is_pointer<deleter_type>::value,
  138.                      "constructed with null function pointer deleter"); }
  139.  
  140.       explicit
  141.       unique_ptr(pointer __p) noexcept
  142.       : _M_t(__p, deleter_type())
  143.       { static_assert(!is_pointer<deleter_type>::value,
  144.                      "constructed with null function pointer deleter"); }
  145.  
  146.       unique_ptr(pointer __p,
  147.           typename conditional<is_reference<deleter_type>::value,
  148.             deleter_type, const deleter_type&>::type __d) noexcept
  149.       : _M_t(__p, __d) { }
  150.  
  151.       unique_ptr(pointer __p,
  152.           typename remove_reference<deleter_type>::type&& __d) noexcept
  153.       : _M_t(std::move(__p), std::move(__d))
  154.       { static_assert(!std::is_reference<deleter_type>::value,
  155.                       "rvalue deleter bound to reference"); }
  156.  
  157.       constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { }
  158.  
  159.       // Move constructors.
  160.       unique_ptr(unique_ptr&& __u) noexcept
  161.       : _M_t(__u.release(), std::forward<deleter_type>(__u.get_deleter())) { }
  162.  
  163.       template<typename _Up, typename _Ep, typename = _Require<
  164.                is_convertible<typename unique_ptr<_Up, _Ep>::pointer, pointer>,
  165.                __not_<is_array<_Up>>,
  166.                typename conditional<is_reference<_Dp>::value,
  167.                                     is_same<_Ep, _Dp>,
  168.                                     is_convertible<_Ep, _Dp>>::type>>
  169.         unique_ptr(unique_ptr<_Up, _Ep>&& __u) noexcept
  170.         : _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter()))
  171.         { }
  172.  
  173. #if _GLIBCXX_USE_DEPRECATED
  174.       template<typename _Up, typename = _Require<
  175.                is_convertible<_Up*, _Tp*>, is_same<_Dp, default_delete<_Tp>>>>
  176.         unique_ptr(auto_ptr<_Up>&& __u) noexcept;
  177. #endif
  178.  
  179.       // Destructor.
  180.       ~unique_ptr() noexcept
  181.       {
  182.         auto& __ptr = std::get<0>(_M_t);
  183.         if (__ptr != nullptr)
  184.           get_deleter()(__ptr);
  185.         __ptr = pointer();
  186.       }
  187.  
  188.       // Assignment.
  189.       unique_ptr&
  190.       operator=(unique_ptr&& __u) noexcept
  191.       {
  192.         reset(__u.release());
  193.         get_deleter() = std::forward<deleter_type>(__u.get_deleter());
  194.         return *this;
  195.       }
  196.  
  197.       template<typename _Up, typename _Ep>
  198.         typename enable_if< __and_<
  199.           is_convertible<typename unique_ptr<_Up, _Ep>::pointer, pointer>,
  200.           __not_<is_array<_Up>>
  201.           >::value,
  202.           unique_ptr&>::type
  203.         operator=(unique_ptr<_Up, _Ep>&& __u) noexcept
  204.         {
  205.           reset(__u.release());
  206.           get_deleter() = std::forward<_Ep>(__u.get_deleter());
  207.           return *this;
  208.         }
  209.  
  210.       unique_ptr&
  211.       operator=(nullptr_t) noexcept
  212.       {
  213.         reset();
  214.         return *this;
  215.       }
  216.  
  217.       // Observers.
  218.       typename add_lvalue_reference<element_type>::type
  219.       operator*() const
  220.       {
  221.         _GLIBCXX_DEBUG_ASSERT(get() != pointer());
  222.         return *get();
  223.       }
  224.  
  225.       pointer
  226.       operator->() const noexcept
  227.       {
  228.         _GLIBCXX_DEBUG_ASSERT(get() != pointer());
  229.         return get();
  230.       }
  231.  
  232.       pointer
  233.       get() const noexcept
  234.       { return std::get<0>(_M_t); }
  235.  
  236.       deleter_type&
  237.       get_deleter() noexcept
  238.       { return std::get<1>(_M_t); }
  239.  
  240.       const deleter_type&
  241.       get_deleter() const noexcept
  242.       { return std::get<1>(_M_t); }
  243.  
  244.       explicit operator bool() const noexcept
  245.       { return get() == pointer() ? false : true; }
  246.  
  247.       // Modifiers.
  248.       pointer
  249.       release() noexcept
  250.       {
  251.         pointer __p = get();
  252.         std::get<0>(_M_t) = pointer();
  253.         return __p;
  254.       }
  255.  
  256.       void
  257.       reset(pointer __p = pointer()) noexcept
  258.       {
  259.         using std::swap;
  260.         swap(std::get<0>(_M_t), __p);
  261.         if (__p != pointer())
  262.           get_deleter()(__p);
  263.       }
  264.  
  265.       void
  266.       swap(unique_ptr& __u) noexcept
  267.       {
  268.         using std::swap;
  269.         swap(_M_t, __u._M_t);
  270.       }
  271.  
  272.       // Disable copy from lvalue.
  273.       unique_ptr(const unique_ptr&) = delete;
  274.       unique_ptr& operator=(const unique_ptr&) = delete;
  275.   };
  276.  
  277.   /// 20.7.1.3 unique_ptr for array objects with a runtime length
  278.   // [unique.ptr.runtime]
  279.   // _GLIBCXX_RESOLVE_LIB_DEFECTS
  280.   // DR 740 - omit specialization for array objects with a compile time length
  281.   template<typename _Tp, typename _Dp>
  282.     class unique_ptr<_Tp[], _Dp>
  283.     {
  284.       // use SFINAE to determine whether _Del::pointer exists
  285.       class _Pointer
  286.       {
  287.         template<typename _Up>
  288.           static typename _Up::pointer __test(typename _Up::pointer*);
  289.  
  290.         template<typename _Up>
  291.           static _Tp* __test(...);
  292.  
  293.         typedef typename remove_reference<_Dp>::type _Del;
  294.  
  295.       public:
  296.         typedef decltype(__test<_Del>(0)) type;
  297.       };
  298.  
  299.       typedef std::tuple<typename _Pointer::type, _Dp>  __tuple_type;
  300.       __tuple_type                                      _M_t;
  301.  
  302.       template<typename _Up>
  303.         using __remove_cv = typename remove_cv<_Up>::type;
  304.  
  305.       // like is_base_of<_Tp, _Up> but false if unqualified types are the same
  306.       template<typename _Up>
  307.         using __is_derived_Tp
  308.           = __and_< is_base_of<_Tp, _Up>,
  309.                     __not_<is_same<__remove_cv<_Tp>, __remove_cv<_Up>>> >;
  310.  
  311.       template<typename _Up, typename _Ep,
  312.                typename _Tp_pointer = typename _Pointer::type,
  313.                typename _Up_pointer = typename unique_ptr<_Up, _Ep>::pointer>
  314.         using __safe_conversion = __and_<
  315.             is_convertible<_Up_pointer, _Tp_pointer>,
  316.             is_array<_Up>,
  317.             __or_<__not_<is_pointer<_Up_pointer>>,
  318.                   __not_<is_pointer<_Tp_pointer>>,
  319.                   __not_<__is_derived_Tp<typename remove_extent<_Up>::type>>
  320.             >
  321.           >;
  322.  
  323.     public:
  324.       typedef typename _Pointer::type   pointer;
  325.       typedef _Tp                       element_type;
  326.       typedef _Dp                       deleter_type;
  327.  
  328.       // Constructors.
  329.       constexpr unique_ptr() noexcept
  330.       : _M_t()
  331.       { static_assert(!std::is_pointer<deleter_type>::value,
  332.                       "constructed with null function pointer deleter"); }
  333.  
  334.       explicit
  335.       unique_ptr(pointer __p) noexcept
  336.       : _M_t(__p, deleter_type())
  337.       { static_assert(!is_pointer<deleter_type>::value,
  338.                       "constructed with null function pointer deleter"); }
  339.  
  340.       template<typename _Up, typename = _Require<is_pointer<pointer>,
  341.                is_convertible<_Up*, pointer>, __is_derived_Tp<_Up>>>
  342.         explicit
  343.         unique_ptr(_Up* __p) = delete;
  344.  
  345.       unique_ptr(pointer __p,
  346.           typename conditional<is_reference<deleter_type>::value,
  347.               deleter_type, const deleter_type&>::type __d) noexcept
  348.       : _M_t(__p, __d) { }
  349.  
  350.       unique_ptr(pointer __p, typename
  351.                  remove_reference<deleter_type>::type&& __d) noexcept
  352.       : _M_t(std::move(__p), std::move(__d))
  353.       { static_assert(!is_reference<deleter_type>::value,
  354.                       "rvalue deleter bound to reference"); }
  355.  
  356.       // Move constructor.
  357.       unique_ptr(unique_ptr&& __u) noexcept
  358.       : _M_t(__u.release(), std::forward<deleter_type>(__u.get_deleter())) { }
  359.  
  360.       constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { }
  361.  
  362.       template<typename _Up, typename _Ep,
  363.                typename = _Require<__safe_conversion<_Up, _Ep>,
  364.                  typename conditional<is_reference<_Dp>::value,
  365.                                       is_same<_Ep, _Dp>,
  366.                                       is_convertible<_Ep, _Dp>>::type
  367.                >>
  368.         unique_ptr(unique_ptr<_Up, _Ep>&& __u) noexcept
  369.         : _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter()))
  370.         { }
  371.  
  372.       // Destructor.
  373.       ~unique_ptr()
  374.       {
  375.         auto& __ptr = std::get<0>(_M_t);
  376.         if (__ptr != nullptr)
  377.           get_deleter()(__ptr);
  378.         __ptr = pointer();
  379.       }
  380.  
  381.       // Assignment.
  382.       unique_ptr&
  383.       operator=(unique_ptr&& __u) noexcept
  384.       {
  385.         reset(__u.release());
  386.         get_deleter() = std::forward<deleter_type>(__u.get_deleter());
  387.         return *this;
  388.       }
  389.  
  390.       template<typename _Up, typename _Ep>
  391.         typename
  392.         enable_if<__safe_conversion<_Up, _Ep>::value, unique_ptr&>::type
  393.         operator=(unique_ptr<_Up, _Ep>&& __u) noexcept
  394.         {
  395.           reset(__u.release());
  396.           get_deleter() = std::forward<_Ep>(__u.get_deleter());
  397.           return *this;
  398.         }
  399.  
  400.       unique_ptr&
  401.       operator=(nullptr_t) noexcept
  402.       {
  403.         reset();
  404.         return *this;
  405.       }
  406.  
  407.       // Observers.
  408.       typename std::add_lvalue_reference<element_type>::type
  409.       operator[](size_t __i) const
  410.       {
  411.         _GLIBCXX_DEBUG_ASSERT(get() != pointer());
  412.         return get()[__i];
  413.       }
  414.  
  415.       pointer
  416.       get() const noexcept
  417.       { return std::get<0>(_M_t); }
  418.  
  419.       deleter_type&
  420.       get_deleter() noexcept
  421.       { return std::get<1>(_M_t); }
  422.  
  423.       const deleter_type&
  424.       get_deleter() const noexcept
  425.       { return std::get<1>(_M_t); }
  426.  
  427.       explicit operator bool() const noexcept
  428.       { return get() == pointer() ? false : true; }
  429.  
  430.       // Modifiers.
  431.       pointer
  432.       release() noexcept
  433.       {
  434.         pointer __p = get();
  435.         std::get<0>(_M_t) = pointer();
  436.         return __p;
  437.       }
  438.  
  439.       void
  440.       reset() noexcept
  441.       { reset(pointer()); }
  442.  
  443.       void
  444.       reset(pointer __p) noexcept
  445.       {
  446.         using std::swap;
  447.         swap(std::get<0>(_M_t), __p);
  448.         if (__p != nullptr)
  449.           get_deleter()(__p);
  450.       }
  451.  
  452.       template<typename _Up, typename = _Require<is_pointer<pointer>,
  453.                is_convertible<_Up*, pointer>, __is_derived_Tp<_Up>>>
  454.         void reset(_Up*) = delete;
  455.  
  456.       void
  457.       swap(unique_ptr& __u) noexcept
  458.       {
  459.         using std::swap;
  460.         swap(_M_t, __u._M_t);
  461.       }
  462.  
  463.       // Disable copy from lvalue.
  464.       unique_ptr(const unique_ptr&) = delete;
  465.       unique_ptr& operator=(const unique_ptr&) = delete;
  466.  
  467.       // Disable construction from convertible pointer types.
  468.       template<typename _Up, typename = _Require<is_pointer<pointer>,
  469.                is_convertible<_Up*, pointer>, __is_derived_Tp<_Up>>>
  470.         unique_ptr(_Up*, typename
  471.                    conditional<is_reference<deleter_type>::value,
  472.                    deleter_type, const deleter_type&>::type) = delete;
  473.  
  474.       template<typename _Up, typename = _Require<is_pointer<pointer>,
  475.                is_convertible<_Up*, pointer>, __is_derived_Tp<_Up>>>
  476.         unique_ptr(_Up*, typename
  477.                    remove_reference<deleter_type>::type&&) = delete;
  478.     };
  479.  
  480.   template<typename _Tp, typename _Dp>
  481.     inline void
  482.     swap(unique_ptr<_Tp, _Dp>& __x,
  483.          unique_ptr<_Tp, _Dp>& __y) noexcept
  484.     { __x.swap(__y); }
  485.  
  486.   template<typename _Tp, typename _Dp,
  487.            typename _Up, typename _Ep>
  488.     inline bool
  489.     operator==(const unique_ptr<_Tp, _Dp>& __x,
  490.                const unique_ptr<_Up, _Ep>& __y)
  491.     { return __x.get() == __y.get(); }
  492.  
  493.   template<typename _Tp, typename _Dp>
  494.     inline bool
  495.     operator==(const unique_ptr<_Tp, _Dp>& __x, nullptr_t) noexcept
  496.     { return !__x; }
  497.  
  498.   template<typename _Tp, typename _Dp>
  499.     inline bool
  500.     operator==(nullptr_t, const unique_ptr<_Tp, _Dp>& __x) noexcept
  501.     { return !__x; }
  502.  
  503.   template<typename _Tp, typename _Dp,
  504.            typename _Up, typename _Ep>
  505.     inline bool
  506.     operator!=(const unique_ptr<_Tp, _Dp>& __x,
  507.                const unique_ptr<_Up, _Ep>& __y)
  508.     { return __x.get() != __y.get(); }
  509.  
  510.   template<typename _Tp, typename _Dp>
  511.     inline bool
  512.     operator!=(const unique_ptr<_Tp, _Dp>& __x, nullptr_t) noexcept
  513.     { return (bool)__x; }
  514.  
  515.   template<typename _Tp, typename _Dp>
  516.     inline bool
  517.     operator!=(nullptr_t, const unique_ptr<_Tp, _Dp>& __x) noexcept
  518.     { return (bool)__x; }
  519.  
  520.   template<typename _Tp, typename _Dp,
  521.            typename _Up, typename _Ep>
  522.     inline bool
  523.     operator<(const unique_ptr<_Tp, _Dp>& __x,
  524.               const unique_ptr<_Up, _Ep>& __y)
  525.     {
  526.       typedef typename
  527.         std::common_type<typename unique_ptr<_Tp, _Dp>::pointer,
  528.                          typename unique_ptr<_Up, _Ep>::pointer>::type _CT;
  529.       return std::less<_CT>()(__x.get(), __y.get());
  530.     }
  531.  
  532.   template<typename _Tp, typename _Dp>
  533.     inline bool
  534.     operator<(const unique_ptr<_Tp, _Dp>& __x, nullptr_t)
  535.     { return std::less<typename unique_ptr<_Tp, _Dp>::pointer>()(__x.get(),
  536.                                                                  nullptr); }
  537.  
  538.   template<typename _Tp, typename _Dp>
  539.     inline bool
  540.     operator<(nullptr_t, const unique_ptr<_Tp, _Dp>& __x)
  541.     { return std::less<typename unique_ptr<_Tp, _Dp>::pointer>()(nullptr,
  542.                                                                  __x.get()); }
  543.  
  544.   template<typename _Tp, typename _Dp,
  545.            typename _Up, typename _Ep>
  546.     inline bool
  547.     operator<=(const unique_ptr<_Tp, _Dp>& __x,
  548.                const unique_ptr<_Up, _Ep>& __y)
  549.     { return !(__y < __x); }
  550.  
  551.   template<typename _Tp, typename _Dp>
  552.     inline bool
  553.     operator<=(const unique_ptr<_Tp, _Dp>& __x, nullptr_t)
  554.     { return !(nullptr < __x); }
  555.  
  556.   template<typename _Tp, typename _Dp>
  557.     inline bool
  558.     operator<=(nullptr_t, const unique_ptr<_Tp, _Dp>& __x)
  559.     { return !(__x < nullptr); }
  560.  
  561.   template<typename _Tp, typename _Dp,
  562.            typename _Up, typename _Ep>
  563.     inline bool
  564.     operator>(const unique_ptr<_Tp, _Dp>& __x,
  565.               const unique_ptr<_Up, _Ep>& __y)
  566.     { return (__y < __x); }
  567.  
  568.   template<typename _Tp, typename _Dp>
  569.     inline bool
  570.     operator>(const unique_ptr<_Tp, _Dp>& __x, nullptr_t)
  571.     { return std::less<typename unique_ptr<_Tp, _Dp>::pointer>()(nullptr,
  572.                                                                  __x.get()); }
  573.  
  574.   template<typename _Tp, typename _Dp>
  575.     inline bool
  576.     operator>(nullptr_t, const unique_ptr<_Tp, _Dp>& __x)
  577.     { return std::less<typename unique_ptr<_Tp, _Dp>::pointer>()(__x.get(),
  578.                                                                  nullptr); }
  579.  
  580.   template<typename _Tp, typename _Dp,
  581.            typename _Up, typename _Ep>
  582.     inline bool
  583.     operator>=(const unique_ptr<_Tp, _Dp>& __x,
  584.                const unique_ptr<_Up, _Ep>& __y)
  585.     { return !(__x < __y); }
  586.  
  587.   template<typename _Tp, typename _Dp>
  588.     inline bool
  589.     operator>=(const unique_ptr<_Tp, _Dp>& __x, nullptr_t)
  590.     { return !(__x < nullptr); }
  591.  
  592.   template<typename _Tp, typename _Dp>
  593.     inline bool
  594.     operator>=(nullptr_t, const unique_ptr<_Tp, _Dp>& __x)
  595.     { return !(nullptr < __x); }
  596.  
  597.   /// std::hash specialization for unique_ptr.
  598.   template<typename _Tp, typename _Dp>
  599.     struct hash<unique_ptr<_Tp, _Dp>>
  600.     : public __hash_base<size_t, unique_ptr<_Tp, _Dp>>
  601.     {
  602.       size_t
  603.       operator()(const unique_ptr<_Tp, _Dp>& __u) const noexcept
  604.       {
  605.         typedef unique_ptr<_Tp, _Dp> _UP;
  606.         return std::hash<typename _UP::pointer>()(__u.get());
  607.       }
  608.     };
  609.  
  610.   // @} group pointer_abstractions
  611.  
  612. _GLIBCXX_END_NAMESPACE_VERSION
  613. } // namespace
  614.  
  615. #endif /* _UNIQUE_PTR_H */
  616.