1 //===---------------------SharingPtr.h --------------------------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #ifndef utility_SharingPtr_h_
11 #define utility_SharingPtr_h_
12
13 #include <algorithm>
14 #include <memory>
15
16 //#define ENABLE_SP_LOGGING 1 // DON'T CHECK THIS LINE IN UNLESS COMMENTED OUT
17 #if defined (ENABLE_SP_LOGGING)
18
19 extern "C" void track_sp (void *sp_this, void *ptr, long count);
20
21 #endif
22
23 namespace lldb_private {
24
25 namespace imp {
26
27 template <class T>
28 inline T
increment(T & t)29 increment(T& t)
30 {
31 return __sync_add_and_fetch(&t, 1);
32 }
33
34 template <class T>
35 inline T
decrement(T & t)36 decrement(T& t)
37 {
38 return __sync_add_and_fetch(&t, -1);
39 }
40
41 class shared_count
42 {
43 shared_count(const shared_count&);
44 shared_count& operator=(const shared_count&);
45
46 protected:
47 long shared_owners_;
48 virtual ~shared_count();
49 private:
50 virtual void on_zero_shared() = 0;
51
52 public:
53 explicit shared_count(long refs = 0)
shared_owners_(refs)54 : shared_owners_(refs) {}
55
56 void add_shared();
57 void release_shared();
use_count()58 long use_count() const {return shared_owners_ + 1;}
59 };
60
61 template <class T>
62 class shared_ptr_pointer
63 : public shared_count
64 {
65 T data_;
66 public:
shared_ptr_pointer(T p)67 shared_ptr_pointer(T p)
68 : data_(p) {}
69
70 private:
71 virtual void on_zero_shared();
72
73 // Outlaw copy constructor and assignment operator to keep effictive C++
74 // warnings down to a minumum
75 shared_ptr_pointer (const shared_ptr_pointer &);
76 shared_ptr_pointer & operator=(const shared_ptr_pointer &);
77 };
78
79 template <class T>
80 void
on_zero_shared()81 shared_ptr_pointer<T>::on_zero_shared()
82 {
83 delete data_;
84 }
85
86 template <class T>
87 class shared_ptr_emplace
88 : public shared_count
89 {
90 T data_;
91 public:
92
shared_ptr_emplace()93 shared_ptr_emplace()
94 : data_() {}
95
96 template <class A0>
shared_ptr_emplace(A0 & a0)97 shared_ptr_emplace(A0& a0)
98 : data_(a0) {}
99
100 template <class A0, class A1>
shared_ptr_emplace(A0 & a0,A1 & a1)101 shared_ptr_emplace(A0& a0, A1& a1)
102 : data_(a0, a1) {}
103
104 template <class A0, class A1, class A2>
shared_ptr_emplace(A0 & a0,A1 & a1,A2 & a2)105 shared_ptr_emplace(A0& a0, A1& a1, A2& a2)
106 : data_(a0, a1, a2) {}
107
108 template <class A0, class A1, class A2, class A3>
shared_ptr_emplace(A0 & a0,A1 & a1,A2 & a2,A3 & a3)109 shared_ptr_emplace(A0& a0, A1& a1, A2& a2, A3& a3)
110 : data_(a0, a1, a2, a3) {}
111
112 template <class A0, class A1, class A2, class A3, class A4>
shared_ptr_emplace(A0 & a0,A1 & a1,A2 & a2,A3 & a3,A4 & a4)113 shared_ptr_emplace(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4)
114 : data_(a0, a1, a2, a3, a4) {}
115
116 private:
117 virtual void on_zero_shared();
118 public:
get()119 T* get() {return &data_;}
120 };
121
122 template <class T>
123 void
on_zero_shared()124 shared_ptr_emplace<T>::on_zero_shared()
125 {
126 }
127
128 } // namespace
129
130 template<class T>
131 class SharingPtr
132 {
133 public:
134 typedef T element_type;
135 private:
136 element_type* ptr_;
137 imp::shared_count* cntrl_;
138
139 struct nat {int for_bool_;};
140 public:
141 SharingPtr();
142 template<class Y> explicit SharingPtr(Y* p);
143 template<class Y> explicit SharingPtr(Y* p, imp::shared_count *ctrl_block);
144 template<class Y> SharingPtr(const SharingPtr<Y>& r, element_type *p);
145 SharingPtr(const SharingPtr& r);
146 template<class Y>
147 SharingPtr(const SharingPtr<Y>& r);
148
149 ~SharingPtr();
150
151 SharingPtr& operator=(const SharingPtr& r);
152 template<class Y> SharingPtr& operator=(const SharingPtr<Y>& r);
153
154 void swap(SharingPtr& r);
155 void reset();
156 template<class Y> void reset(Y* p);
157
get()158 element_type* get() const {return ptr_;}
159 element_type& operator*() const {return *ptr_;}
160 element_type* operator->() const {return ptr_;}
use_count()161 long use_count() const {return cntrl_ ? cntrl_->use_count() : 0;}
unique()162 bool unique() const {return use_count() == 1;}
empty()163 bool empty() const {return cntrl_ == 0;}
164 operator nat*() const {return (nat*)get();}
165
166 static SharingPtr<T> make_shared();
167
168 template<class A0>
169 static SharingPtr<T> make_shared(A0&);
170
171 template<class A0, class A1>
172 static SharingPtr<T> make_shared(A0&, A1&);
173
174 template<class A0, class A1, class A2>
175 static SharingPtr<T> make_shared(A0&, A1&, A2&);
176
177 template<class A0, class A1, class A2, class A3>
178 static SharingPtr<T> make_shared(A0&, A1&, A2&, A3&);
179
180 template<class A0, class A1, class A2, class A3, class A4>
181 static SharingPtr<T> make_shared(A0&, A1&, A2&, A3&, A4&);
182
183 private:
184
185 template <class U> friend class SharingPtr;
186 };
187
188 template<class T>
189 inline
SharingPtr()190 SharingPtr<T>::SharingPtr()
191 : ptr_(0),
192 cntrl_(0)
193 {
194 }
195
196 template<class T>
197 template<class Y>
SharingPtr(Y * p)198 SharingPtr<T>::SharingPtr(Y* p)
199 : ptr_(p), cntrl_(0)
200 {
201 std::unique_ptr<Y> hold(p);
202 typedef imp::shared_ptr_pointer<Y*> _CntrlBlk;
203 cntrl_ = new _CntrlBlk(p);
204 hold.release();
205 }
206
207 template<class T>
208 template<class Y>
SharingPtr(Y * p,imp::shared_count * cntrl_block)209 SharingPtr<T>::SharingPtr(Y* p, imp::shared_count *cntrl_block)
210 : ptr_(p), cntrl_(cntrl_block)
211 {
212 }
213
214 template<class T>
215 template<class Y>
216 inline
SharingPtr(const SharingPtr<Y> & r,element_type * p)217 SharingPtr<T>::SharingPtr(const SharingPtr<Y>& r, element_type *p)
218 : ptr_(p),
219 cntrl_(r.cntrl_)
220 {
221 if (cntrl_)
222 cntrl_->add_shared();
223 }
224
225 template<class T>
226 inline
SharingPtr(const SharingPtr & r)227 SharingPtr<T>::SharingPtr(const SharingPtr& r)
228 : ptr_(r.ptr_),
229 cntrl_(r.cntrl_)
230 {
231 if (cntrl_)
232 cntrl_->add_shared();
233 }
234
235 template<class T>
236 template<class Y>
237 inline
SharingPtr(const SharingPtr<Y> & r)238 SharingPtr<T>::SharingPtr(const SharingPtr<Y>& r)
239 : ptr_(r.ptr_),
240 cntrl_(r.cntrl_)
241 {
242 if (cntrl_)
243 cntrl_->add_shared();
244 }
245
246 template<class T>
~SharingPtr()247 SharingPtr<T>::~SharingPtr()
248 {
249 if (cntrl_)
250 cntrl_->release_shared();
251 }
252
253 template<class T>
254 inline
255 SharingPtr<T>&
256 SharingPtr<T>::operator=(const SharingPtr& r)
257 {
258 SharingPtr(r).swap(*this);
259 return *this;
260 }
261
262 template<class T>
263 template<class Y>
264 inline
265 SharingPtr<T>&
266 SharingPtr<T>::operator=(const SharingPtr<Y>& r)
267 {
268 SharingPtr(r).swap(*this);
269 return *this;
270 }
271
272 template<class T>
273 inline
274 void
swap(SharingPtr & r)275 SharingPtr<T>::swap(SharingPtr& r)
276 {
277 std::swap(ptr_, r.ptr_);
278 std::swap(cntrl_, r.cntrl_);
279 }
280
281 template<class T>
282 inline
283 void
reset()284 SharingPtr<T>::reset()
285 {
286 SharingPtr().swap(*this);
287 }
288
289 template<class T>
290 template<class Y>
291 inline
292 void
reset(Y * p)293 SharingPtr<T>::reset(Y* p)
294 {
295 SharingPtr(p).swap(*this);
296 }
297
298 template<class T>
299 SharingPtr<T>
make_shared()300 SharingPtr<T>::make_shared()
301 {
302 typedef imp::shared_ptr_emplace<T> CntrlBlk;
303 SharingPtr<T> r;
304 r.cntrl_ = new CntrlBlk();
305 r.ptr_ = static_cast<CntrlBlk*>(r.cntrl_)->get();
306 return r;
307 }
308
309 template<class T>
310 template<class A0>
311 SharingPtr<T>
make_shared(A0 & a0)312 SharingPtr<T>::make_shared(A0& a0)
313 {
314 typedef imp::shared_ptr_emplace<T> CntrlBlk;
315 SharingPtr<T> r;
316 r.cntrl_ = new CntrlBlk(a0);
317 r.ptr_ = static_cast<CntrlBlk*>(r.cntrl_)->get();
318 return r;
319 }
320
321 template<class T>
322 template<class A0, class A1>
323 SharingPtr<T>
make_shared(A0 & a0,A1 & a1)324 SharingPtr<T>::make_shared(A0& a0, A1& a1)
325 {
326 typedef imp::shared_ptr_emplace<T> CntrlBlk;
327 SharingPtr<T> r;
328 r.cntrl_ = new CntrlBlk(a0, a1);
329 r.ptr_ = static_cast<CntrlBlk*>(r.cntrl_)->get();
330 return r;
331 }
332
333 template<class T>
334 template<class A0, class A1, class A2>
335 SharingPtr<T>
make_shared(A0 & a0,A1 & a1,A2 & a2)336 SharingPtr<T>::make_shared(A0& a0, A1& a1, A2& a2)
337 {
338 typedef imp::shared_ptr_emplace<T> CntrlBlk;
339 SharingPtr<T> r;
340 r.cntrl_ = new CntrlBlk(a0, a1, a2);
341 r.ptr_ = static_cast<CntrlBlk*>(r.cntrl_)->get();
342 return r;
343 }
344
345 template<class T>
346 template<class A0, class A1, class A2, class A3>
347 SharingPtr<T>
make_shared(A0 & a0,A1 & a1,A2 & a2,A3 & a3)348 SharingPtr<T>::make_shared(A0& a0, A1& a1, A2& a2, A3& a3)
349 {
350 typedef imp::shared_ptr_emplace<T> CntrlBlk;
351 SharingPtr<T> r;
352 r.cntrl_ = new CntrlBlk(a0, a1, a2, a3);
353 r.ptr_ = static_cast<CntrlBlk*>(r.cntrl_)->get();
354 return r;
355 }
356
357 template<class T>
358 template<class A0, class A1, class A2, class A3, class A4>
359 SharingPtr<T>
make_shared(A0 & a0,A1 & a1,A2 & a2,A3 & a3,A4 & a4)360 SharingPtr<T>::make_shared(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4)
361 {
362 typedef imp::shared_ptr_emplace<T> CntrlBlk;
363 SharingPtr<T> r;
364 r.cntrl_ = new CntrlBlk(a0, a1, a2, a3, a4);
365 r.ptr_ = static_cast<CntrlBlk*>(r.cntrl_)->get();
366 return r;
367 }
368
369 template<class T>
370 inline
371 SharingPtr<T>
make_shared()372 make_shared()
373 {
374 return SharingPtr<T>::make_shared();
375 }
376
377 template<class T, class A0>
378 inline
379 SharingPtr<T>
make_shared(A0 & a0)380 make_shared(A0& a0)
381 {
382 return SharingPtr<T>::make_shared(a0);
383 }
384
385 template<class T, class A0, class A1>
386 inline
387 SharingPtr<T>
make_shared(A0 & a0,A1 & a1)388 make_shared(A0& a0, A1& a1)
389 {
390 return SharingPtr<T>::make_shared(a0, a1);
391 }
392
393 template<class T, class A0, class A1, class A2>
394 inline
395 SharingPtr<T>
make_shared(A0 & a0,A1 & a1,A2 & a2)396 make_shared(A0& a0, A1& a1, A2& a2)
397 {
398 return SharingPtr<T>::make_shared(a0, a1, a2);
399 }
400
401 template<class T, class A0, class A1, class A2, class A3>
402 inline
403 SharingPtr<T>
make_shared(A0 & a0,A1 & a1,A2 & a2,A3 & a3)404 make_shared(A0& a0, A1& a1, A2& a2, A3& a3)
405 {
406 return SharingPtr<T>::make_shared(a0, a1, a2, a3);
407 }
408
409 template<class T, class A0, class A1, class A2, class A3, class A4>
410 inline
411 SharingPtr<T>
make_shared(A0 & a0,A1 & a1,A2 & a2,A3 & a3,A4 & a4)412 make_shared(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4)
413 {
414 return SharingPtr<T>::make_shared(a0, a1, a2, a3, a4);
415 }
416
417
418 template<class T, class U>
419 inline
420 bool
421 operator==(const SharingPtr<T>& __x, const SharingPtr<U>& __y)
422 {
423 return __x.get() == __y.get();
424 }
425
426 template<class T, class U>
427 inline
428 bool
429 operator!=(const SharingPtr<T>& __x, const SharingPtr<U>& __y)
430 {
431 return !(__x == __y);
432 }
433
434 template<class T, class U>
435 inline
436 bool
437 operator<(const SharingPtr<T>& __x, const SharingPtr<U>& __y)
438 {
439 return __x.get() < __y.get();
440 }
441
442 template<class T>
443 inline
444 void
swap(SharingPtr<T> & __x,SharingPtr<T> & __y)445 swap(SharingPtr<T>& __x, SharingPtr<T>& __y)
446 {
447 __x.swap(__y);
448 }
449
450 template<class T, class U>
451 inline
452 SharingPtr<T>
static_pointer_cast(const SharingPtr<U> & r)453 static_pointer_cast(const SharingPtr<U>& r)
454 {
455 return SharingPtr<T>(r, static_cast<T*>(r.get()));
456 }
457
458 template<class T, class U>
459 SharingPtr<T>
const_pointer_cast(const SharingPtr<U> & r)460 const_pointer_cast(const SharingPtr<U>& r)
461 {
462 return SharingPtr<T>(r, const_cast<T*>(r.get()));
463 }
464
465 template <class T>
466 class LoggingSharingPtr
467 : public SharingPtr<T>
468 {
469 typedef SharingPtr<T> base;
470
471 public:
472 typedef void (*Callback)(void*, const LoggingSharingPtr&, bool action);
473 // action: false means increment just happened
474 // true means decrement is about to happen
475
476 private:
477 Callback cb_;
478 void* baton_;
479
480 public:
LoggingSharingPtr()481 LoggingSharingPtr() : cb_(0), baton_(0) {}
LoggingSharingPtr(Callback cb,void * baton)482 LoggingSharingPtr(Callback cb, void* baton)
483 : cb_(cb), baton_(baton)
484 {
485 if (cb_)
486 cb_(baton_, *this, false);
487 }
488
489 template <class Y>
LoggingSharingPtr(Y * p)490 LoggingSharingPtr(Y* p)
491 : base(p), cb_(0), baton_(0) {}
492
493 template <class Y>
LoggingSharingPtr(Y * p,Callback cb,void * baton)494 LoggingSharingPtr(Y* p, Callback cb, void* baton)
495 : base(p), cb_(cb), baton_(baton)
496 {
497 if (cb_)
498 cb_(baton_, *this, false);
499 }
500
~LoggingSharingPtr()501 ~LoggingSharingPtr()
502 {
503 if (cb_)
504 cb_(baton_, *this, true);
505 }
506
LoggingSharingPtr(const LoggingSharingPtr & p)507 LoggingSharingPtr(const LoggingSharingPtr& p)
508 : base(p), cb_(p.cb_), baton_(p.baton_)
509 {
510 if (cb_)
511 cb_(baton_, *this, false);
512 }
513
514 LoggingSharingPtr& operator=(const LoggingSharingPtr& p)
515 {
516 if (cb_)
517 cb_(baton_, *this, true);
518 base::operator=(p);
519 cb_ = p.cb_;
520 baton_ = p.baton_;
521 if (cb_)
522 cb_(baton_, *this, false);
523 return *this;
524 }
525
reset()526 void reset()
527 {
528 if (cb_)
529 cb_(baton_, *this, true);
530 base::reset();
531 }
532
533 template <class Y>
reset(Y * p)534 void reset(Y* p)
535 {
536 if (cb_)
537 cb_(baton_, *this, true);
538 base::reset(p);
539 if (cb_)
540 cb_(baton_, *this, false);
541 }
542
SetCallback(Callback cb,void * baton)543 void SetCallback(Callback cb, void* baton)
544 {
545 cb_ = cb;
546 baton_ = baton;
547 }
548
ClearCallback()549 void ClearCallback()
550 {
551 cb_ = 0;
552 baton_ = 0;
553 }
554 };
555
556
557 template <class T>
558 class IntrusiveSharingPtr;
559
560 template <class T>
561 class ReferenceCountedBase
562 {
563 public:
ReferenceCountedBase()564 explicit ReferenceCountedBase()
565 : shared_owners_(-1)
566 {
567 }
568
569 void
570 add_shared();
571
572 void
573 release_shared();
574
575 long
use_count()576 use_count() const
577 {
578 return shared_owners_ + 1;
579 }
580
581 protected:
582 long shared_owners_;
583
584 friend class IntrusiveSharingPtr<T>;
585
586 private:
587 ReferenceCountedBase(const ReferenceCountedBase&);
588 ReferenceCountedBase& operator=(const ReferenceCountedBase&);
589 };
590
591 template <class T>
592 void
add_shared()593 lldb_private::ReferenceCountedBase<T>::add_shared()
594 {
595 imp::increment(shared_owners_);
596 }
597
598 template <class T>
599 void
release_shared()600 lldb_private::ReferenceCountedBase<T>::release_shared()
601 {
602 if (imp::decrement(shared_owners_) == -1)
603 delete static_cast<T*>(this);
604 }
605
606
607 template <class T>
608 class ReferenceCountedBaseVirtual : public imp::shared_count
609 {
610 public:
ReferenceCountedBaseVirtual()611 explicit ReferenceCountedBaseVirtual () :
612 imp::shared_count(-1)
613 {
614 }
615
616 virtual
~ReferenceCountedBaseVirtual()617 ~ReferenceCountedBaseVirtual ()
618 {
619 }
620
621 virtual void on_zero_shared ();
622
623 };
624
625 template <class T>
626 void
on_zero_shared()627 ReferenceCountedBaseVirtual<T>::on_zero_shared()
628 {
629 }
630
631 template <typename T>
632 class IntrusiveSharingPtr
633 {
634 public:
635 typedef T element_type;
636
637 explicit
IntrusiveSharingPtr()638 IntrusiveSharingPtr () :
639 ptr_(0)
640 {
641 }
642
643 explicit
IntrusiveSharingPtr(T * ptr)644 IntrusiveSharingPtr (T* ptr) :
645 ptr_(ptr)
646 {
647 add_shared();
648 }
649
IntrusiveSharingPtr(const IntrusiveSharingPtr & rhs)650 IntrusiveSharingPtr (const IntrusiveSharingPtr& rhs) :
651 ptr_(rhs.ptr_)
652 {
653 add_shared();
654 }
655
656 template <class X>
IntrusiveSharingPtr(const IntrusiveSharingPtr<X> & rhs)657 IntrusiveSharingPtr (const IntrusiveSharingPtr<X>& rhs)
658 : ptr_(rhs.get())
659 {
660 add_shared();
661 }
662
663 IntrusiveSharingPtr&
664 operator= (const IntrusiveSharingPtr& rhs)
665 {
666 reset(rhs.get());
667 return *this;
668 }
669
670 template <class X> IntrusiveSharingPtr&
671 operator= (const IntrusiveSharingPtr<X>& rhs)
672 {
673 reset(rhs.get());
674 return *this;
675 }
676
677 IntrusiveSharingPtr&
678 operator= (T *ptr)
679 {
680 reset(ptr);
681 return *this;
682 }
683
~IntrusiveSharingPtr()684 ~IntrusiveSharingPtr()
685 {
686 release_shared();
687 #if defined (LLDB_CONFIGURATION_DEBUG) || defined (LLDB_CONFIGURATION_RELEASE)
688 // NULL out the pointer in objects which can help with leaks detection.
689 // We don't enable this for LLDB_CONFIGURATION_BUILD_AND_INTEGRATION or
690 // when none of the LLDB_CONFIGURATION_XXX macros are defined since
691 // those would be builds for release. But for debug and release builds
692 // that are for development, we NULL out the pointers to catch potential
693 // issues.
694 ptr_ = NULL;
695 #endif // #if defined (LLDB_CONFIGURATION_DEBUG) || defined (LLDB_CONFIGURATION_RELEASE)
696 }
697
698 T&
699 operator*() const
700 {
701 return *ptr_;
702 }
703
704 T*
705 operator->() const
706 {
707 return ptr_;
708 }
709
710 T*
get()711 get() const
712 {
713 return ptr_;
714 }
715
716 operator bool() const
717 {
718 return ptr_ != 0;
719 }
720
721 void
swap(IntrusiveSharingPtr & rhs)722 swap (IntrusiveSharingPtr& rhs)
723 {
724 std::swap(ptr_, rhs.ptr_);
725 #if defined (ENABLE_SP_LOGGING)
726 track_sp (this, ptr_, use_count());
727 track_sp (&rhs, rhs.ptr_, rhs.use_count());
728 #endif
729 }
730
731 void
732 reset(T* ptr = NULL)
733 {
734 IntrusiveSharingPtr(ptr).swap(*this);
735 }
736
737 long
use_count()738 use_count () const
739 {
740 if (ptr_)
741 return ptr_->use_count();
742 return 0;
743 }
744
745 bool
unique()746 unique () const
747 {
748 return use_count () == 1;
749 }
750
751 private:
752 element_type *ptr_;
753
754 void
add_shared()755 add_shared()
756 {
757 if (ptr_)
758 {
759 ptr_->add_shared();
760 #if defined (ENABLE_SP_LOGGING)
761 track_sp (this, ptr_, ptr_->use_count());
762 #endif
763 }
764 }
765 void
release_shared()766 release_shared()
767 {
768 if (ptr_)
769 {
770 #if defined (ENABLE_SP_LOGGING)
771 track_sp (this, NULL, ptr_->use_count() - 1);
772 #endif
773 ptr_->release_shared();
774 }
775 }
776 };
777
778 template<class T, class U>
779 inline bool operator== (const IntrusiveSharingPtr<T>& lhs, const IntrusiveSharingPtr<U>& rhs)
780 {
781 return lhs.get() == rhs.get();
782 }
783
784 template<class T, class U>
785 inline bool operator!= (const IntrusiveSharingPtr<T>& lhs, const IntrusiveSharingPtr<U>& rhs)
786 {
787 return lhs.get() != rhs.get();
788 }
789
790 template<class T, class U>
791 inline bool operator== (const IntrusiveSharingPtr<T>& lhs, U* rhs)
792 {
793 return lhs.get() == rhs;
794 }
795
796 template<class T, class U>
797 inline bool operator!= (const IntrusiveSharingPtr<T>& lhs, U* rhs)
798 {
799 return lhs.get() != rhs;
800 }
801
802 template<class T, class U>
803 inline bool operator== (T* lhs, const IntrusiveSharingPtr<U>& rhs)
804 {
805 return lhs == rhs.get();
806 }
807
808 template<class T, class U>
809 inline bool operator!= (T* lhs, const IntrusiveSharingPtr<U>& rhs)
810 {
811 return lhs != rhs.get();
812 }
813
814 } // namespace lldb_private
815
816 #endif // utility_SharingPtr_h_
817