xref: /reactos/sdk/lib/atl/atlcoll.h (revision 9393fc32)
1 #ifndef __ATLCOLL_H__
2 #define __ATLCOLL_H__
3 
4 #pragma once
5 #include "atlbase.h"
6 #include "atlexcept.h"
7 
8 // FIXME: We need to include <new> for placement new, but that would mean everyone using atl
9 // would also need to set the option 'WITH_STL'..
10 // For now we just copy the definition here, under a guard..
11 #ifndef _NEW
12 inline void* operator new (size_t size, void* ptr) throw() { return ptr; }
13 inline void operator delete (void* ptr, void* voidptr2) throw() { }
14 #endif
15 
16 
17 struct __POSITION
18 {
19 };
20 typedef __POSITION* POSITION;
21 
22 
23 namespace ATL
24 {
25 
26 class CAtlPlex
27 {
28 public:
29     CAtlPlex* m_Next;
30 
31 #if (_AFX_PACKING >= 8)
32     DWORD dwReserved[1];
33 #endif
34 
35     static inline CAtlPlex* Create(
36         _Inout_ CAtlPlex*& Entry,
37         _In_ size_t MaxElements,
38         _In_ size_t ElementSize
39         )
40     {
41         CAtlPlex* Block;
42 
43         ATLASSERT(MaxElements > 0);
44         ATLASSERT(ElementSize > 0);
45 
46         size_t BufferSize = sizeof(CAtlPlex) + (MaxElements * ElementSize);
47 
48         void *Buffer = HeapAlloc(GetProcessHeap(), 0, BufferSize);
49         if (Buffer == NULL) return NULL;
50 
51         Block = static_cast< CAtlPlex* >(Buffer);
52         Block->m_Next = Entry;
53         Entry = Block;
54 
55         return Block;
56     }
57 
58     void* GetData()
59     {
60         return (this + 1);
61     }
62 
63     inline void Destroy()
64     {
65         CAtlPlex* Block;
66         CAtlPlex* Next;
67 
68         Block = this;
69         while (Block != NULL)
70         {
71             Next = Block->m_Next;
72             HeapFree(GetProcessHeap(), 0, Block);
73             Block = Next;
74         }
75     }
76 };
77 
78 
79 template<typename T>
80 class CElementTraitsBase
81 {
82 public:
83     typedef const T& INARGTYPE;
84     typedef T& OUTARGTYPE;
85 
86     static void CopyElements(
87         _Out_writes_all_(NumElements) T* Dest,
88         _In_reads_(NumElements) const T* Source,
89         _In_ size_t NumElements)
90     {
91         for (size_t i = 0; i < NumElements; i++)
92         {
93             Dest[i] = Source[i];
94         }
95     }
96 
97     static void RelocateElements(
98         _Out_writes_all_(NumElements) T* Dest,
99         _In_reads_(NumElements) T* Source,
100         _In_ size_t NumElements)
101     {
102         // A simple memmove works for most of the types.
103         // You'll have to override this for types that have pointers to their
104         // own members.
105 
106 #if defined(__GNUC__) && __GNUC__ >= 8
107     #pragma GCC diagnostic push
108     #pragma GCC diagnostic ignored "-Wclass-memaccess"
109 #endif
110         memmove(Dest, Source, NumElements * sizeof(T));
111 #if defined(__GNUC__) && __GNUC__ >= 8
112     #pragma GCC diagnostic pop
113 #endif
114     }
115 };
116 
117 template<typename T>
118 class CDefaultCompareTraits
119 {
120 public:
121     static bool CompareElements(
122         _In_ const T& Val1,
123         _In_ const T& Val2)
124     {
125         return (Val1 == Val2);
126     }
127 
128     static int CompareElementsOrdered(
129         _In_ const T& Val1,
130         _In_ const T& Val2)
131     {
132         if (Val1 < Val2)
133         {
134             return -1;
135         }
136         else if (Val1 > Val2)
137         {
138             return 1;
139         }
140 
141         return 0; // equal
142     }
143 };
144 
145 template<typename T>
146 class CDefaultElementTraits :
147     public CElementTraitsBase<T>,
148     public CDefaultCompareTraits<T>
149 {
150 };
151 
152 
153 template<typename T>
154 class CElementTraits :
155     public CDefaultElementTraits<T>
156 {
157 };
158 
159 
160 template<typename T, class Allocator = CCRTAllocator>
161 class CHeapPtrElementTraits :
162     public CDefaultElementTraits< CHeapPtr<T, Allocator> >
163 {
164 public:
165     typedef CHeapPtr<T, Allocator>& INARGTYPE;
166     typedef T*& OUTARGTYPE;
167 };
168 
169 
170 
171 template<typename E, class ETraits = CElementTraits<E> >
172 class CAtlArray
173 {
174 public:
175     typedef typename ETraits::INARGTYPE INARGTYPE;
176     typedef typename ETraits::OUTARGTYPE OUTARGTYPE;
177 
178 private:
179     E* m_pData;
180     size_t m_Size;
181     size_t m_AllocatedSize;
182     size_t m_GrowBy;
183 
184 
185 #pragma push_macro("new")
186 #undef new
187 
188     void CreateItems(E* pData, size_t Size)
189     {
190         for (size_t n = 0; n < Size; ++n)
191         {
192             ::new (pData + n) E;
193         }
194     }
195 
196 #pragma pop_macro("new")
197 
198     void DestructItems(E* pData, size_t Size)
199     {
200         for (size_t n = 0; n < Size; ++n)
201         {
202             pData[n].~E();
203         }
204     }
205 
206     bool GrowAllocatedData(size_t nNewSize)
207     {
208         if (m_pData)
209         {
210             size_t addSize = m_GrowBy > 0 ? m_GrowBy : m_AllocatedSize / 2;
211             size_t allocSize = m_AllocatedSize + addSize;
212             if (allocSize < nNewSize)
213                 allocSize = nNewSize;
214 
215             E* pData = (E*)malloc(nNewSize * sizeof(E));
216 
217             if (pData == NULL)
218             {
219                 return false;
220             }
221 
222             // Copy the objects over (default implementation will just move them without calling anything
223             ETraits::RelocateElements(pData, m_pData, m_Size);
224 
225             free(m_pData);
226             m_pData = pData;
227             m_AllocatedSize = nNewSize;
228         }
229         else
230         {
231             // We need to allocate a new buffer
232             size_t allocSize = m_GrowBy > nNewSize ? m_GrowBy : nNewSize;
233             m_pData = (E*)malloc(allocSize * sizeof(E));
234             if (m_pData == NULL)
235             {
236                 return false;
237             }
238             m_AllocatedSize = allocSize;
239         }
240         return true;
241     }
242 
243     /* The CAtlArray class does not support construction by copy */
244 private:
245     CAtlArray(_In_ const CAtlArray&);
246     CAtlArray& operator=(_In_ const CAtlArray&);
247 
248 public:
249     CAtlArray();
250     ~CAtlArray();
251 
252     size_t Add(INARGTYPE element);
253     size_t Add();
254 
255     bool SetCount(size_t nNewSize, int nGrowBy = - 1);
256     size_t GetCount() const;
257 
258     E& operator[](size_t ielement);
259     const E& operator[](size_t ielement) const;
260 
261     E& GetAt(size_t iElement);
262     const E& GetAt(size_t iElement) const;
263 
264     E* GetData();
265     const E* GetData() const;
266 
267 
268     //FIXME: Most of this class is missing!
269 };
270 
271 //
272 // CAtlArray public methods
273 //
274 
275 template<typename E, class ETraits>
276 CAtlArray< E, ETraits >::CAtlArray()
277     : m_pData(NULL)
278     , m_Size(0)
279     , m_AllocatedSize(0)
280     , m_GrowBy(0)
281 {
282 }
283 
284 template<typename E, class ETraits>
285 CAtlArray< E, ETraits >::~CAtlArray()
286 {
287     // Destroy all items
288     SetCount(0, -1);
289 }
290 
291 #pragma push_macro("new")
292 #undef new
293 
294 template<typename E, class ETraits>
295 size_t CAtlArray<E, ETraits>::Add(INARGTYPE element)
296 {
297     if (m_Size >= m_AllocatedSize)
298     {
299         if (!GrowAllocatedData(m_Size + 1))
300         {
301             AtlThrow(E_OUTOFMEMORY);
302         }
303     }
304 
305     ::new (m_pData + m_Size) E(element);
306     m_Size++;
307 
308     return m_Size - 1;
309 }
310 
311 #pragma pop_macro("new")
312 
313 template<typename E, class ETraits>
314 size_t CAtlArray<E, ETraits>::Add()
315 {
316     if (!SetCount(m_Size + 1))
317     {
318         AtlThrow(E_OUTOFMEMORY);
319     }
320 
321     return m_Size - 1;
322 }
323 
324 template<typename E, class ETraits>
325 bool CAtlArray<E, ETraits>::SetCount(size_t nNewSize, int nGrowBy /*= -1*/)
326 {
327 
328     if (nGrowBy > -1)
329     {
330         m_GrowBy = (size_t)nGrowBy;
331     }
332 
333     if (nNewSize == m_Size)
334     {
335         // Do nothing
336     }
337     else if (nNewSize == 0)
338     {
339         if (m_pData)
340         {
341             DestructItems(m_pData, m_Size);
342             m_pData = NULL;
343         }
344         m_Size = m_AllocatedSize = NULL;
345     }
346     else if (nNewSize < m_AllocatedSize)
347     {
348         if (nNewSize > m_Size)
349         {
350             CreateItems(m_pData + m_Size, nNewSize - m_Size);
351         }
352         else
353         {
354             DestructItems(m_pData + nNewSize, m_Size - nNewSize);
355         }
356         m_Size = nNewSize;
357     }
358     else
359     {
360         if (!GrowAllocatedData(nNewSize))
361         {
362             return false;
363         }
364 
365         CreateItems(m_pData + m_Size, nNewSize - m_Size);
366         m_Size = nNewSize;
367     }
368 
369     return true;
370 }
371 
372 template<typename E, class ETraits>
373 size_t CAtlArray<E, ETraits>::GetCount() const
374 {
375     return m_Size;
376 }
377 
378 template<typename E, class ETraits>
379 E& CAtlArray<E, ETraits>::operator[](size_t iElement)
380 {
381     ATLASSERT(iElement < m_Size);
382 
383     return m_pData[iElement];
384 }
385 
386 template<typename E, class ETraits>
387 const E& CAtlArray<E, ETraits>::operator[](size_t iElement) const
388 {
389     ATLASSERT(iElement < m_Size);
390 
391     return m_pData[iElement];
392 }
393 
394 template<typename E, class ETraits>
395 E& CAtlArray<E, ETraits>::GetAt(size_t iElement)
396 {
397     ATLASSERT(iElement < m_Size);
398 
399     return m_pData[iElement];
400 }
401 
402 template<typename E, class ETraits>
403 const E& CAtlArray<E, ETraits>::GetAt(size_t iElement) const
404 {
405     ATLASSERT(iElement < m_Size);
406 
407     return m_pData[iElement];
408 }
409 
410 template<typename E, class ETraits>
411 E* CAtlArray<E, ETraits>::GetData()
412 {
413     return m_pData;
414 }
415 
416 template<typename E, class ETraits>
417 const E* CAtlArray<E, ETraits>::GetData() const
418 {
419     return m_pData;
420 }
421 
422 
423 template<typename E, class ETraits = CElementTraits<E> >
424 class CAtlList
425 {
426 private:
427     typedef typename ETraits::INARGTYPE INARGTYPE;
428 
429 private:
430     class CNode :  public __POSITION
431     {
432     public:
433         CNode* m_Next;
434         CNode* m_Prev;
435         E m_Element;
436 
437     public:
438         CNode(INARGTYPE Element) :
439             m_Element(Element)
440         {
441         }
442 
443     /* The CNode class does not support construction by copy */
444     private:
445         CNode(_In_ const CNode&);
446         CNode& operator=(_In_ const CNode&);
447     };
448 
449 private:
450     CAtlPlex* m_Blocks;
451     UINT m_BlockSize;
452     CNode* m_HeadNode;
453     CNode* m_TailNode;
454     CNode* m_FreeNode;
455     size_t m_NumElements;
456 
457 /* The CAtlList class does not support construction by copy */
458 private:
459     CAtlList(_In_ const CAtlList&);
460     CAtlList& operator=(_In_ const CAtlList&);
461 
462 public:
463     CAtlList(_In_ UINT nBlockSize = 10);
464     ~CAtlList();
465 
466     size_t GetCount() const;
467     bool IsEmpty() const;
468 
469     POSITION GetHeadPosition() const;
470     POSITION GetTailPosition() const;
471 
472     E& GetNext(_Inout_ POSITION& pos);
473     const E& GetNext(_Inout_ POSITION& pos) const;
474     E& GetPrev(_Inout_ POSITION& pos);
475     const E& GetPrev(_Inout_ POSITION& pos) const;
476 
477     E& GetAt(_In_ POSITION pos);
478     const E& GetAt(_In_ POSITION pos) const;
479 
480     POSITION AddHead(INARGTYPE element);
481     POSITION AddTail(INARGTYPE element);
482 
483     E RemoveHead();
484     E RemoveTail();
485 
486     POSITION InsertBefore(_In_ POSITION pos, INARGTYPE element);
487     POSITION InsertAfter(_In_ POSITION pos, INARGTYPE element);
488 
489     void RemoveAll();
490     void RemoveAt(_In_ POSITION pos);
491 
492     POSITION Find(
493         INARGTYPE element,
494         _In_opt_ POSITION posStartAfter = NULL) const;
495     POSITION FindIndex(_In_ size_t iElement) const;
496 
497 private:
498     CNode* CreateNode(
499         INARGTYPE element,
500         _In_opt_ CNode* pPrev,
501         _In_opt_ CNode* pNext
502         );
503 
504     void FreeNode(
505         _Inout_ CNode* pNode
506         );
507 
508     CNode* GetFreeNode(
509         );
510 
511 };
512 
513 
514 //
515 // CAtlist public methods
516 //
517 
518 template<typename E, class ETraits>
519 CAtlList< E, ETraits >::CAtlList(_In_ UINT nBlockSize) :
520     m_Blocks(NULL),
521     m_BlockSize(nBlockSize),
522     m_HeadNode(NULL),
523     m_TailNode(NULL),
524     m_FreeNode(NULL),
525     m_NumElements(0)
526 {
527     ATLASSERT(nBlockSize > 0);
528 }
529 
530 template<typename E, class ETraits>
531 CAtlList<E, ETraits >::~CAtlList(void)
532 {
533     RemoveAll();
534 }
535 
536 template<typename E, class ETraits>
537 inline size_t CAtlList< E, ETraits >::GetCount() const
538 {
539     return m_NumElements;
540 }
541 
542 template<typename E, class ETraits>
543 inline bool CAtlList< E, ETraits >::IsEmpty() const
544 {
545     return (m_NumElements == 0);
546 }
547 
548 template<typename E, class ETraits>
549 inline POSITION CAtlList<E, ETraits>::GetHeadPosition() const
550 {
551     return (POSITION)m_HeadNode;
552 }
553 
554 template<typename E, class ETraits>
555 inline POSITION CAtlList<E, ETraits>::GetTailPosition() const
556 {
557     return (POSITION)m_TailNode;
558 }
559 
560 template<typename E, class ETraits>
561 inline E& CAtlList< E, ETraits >::GetNext(_Inout_ POSITION& pos)
562 {
563     CNode* Node = (CNode*)pos;
564     pos = (POSITION)Node->m_Next;
565     return Node->m_Element;
566 }
567 
568 template<typename E, class ETraits>
569 inline const E& CAtlList< E, ETraits >::GetNext(_Inout_ POSITION& pos) const
570 {
571     CNode* Node = (CNode*)pos;
572     pos = (POSITION)Node->m_Next;
573     return Node->m_Element;
574 }
575 
576 template<typename E, class ETraits>
577 inline E& CAtlList< E, ETraits >::GetPrev(_Inout_ POSITION& pos)
578 {
579     CNode* Node = (CNode*)pos;
580     pos = (POSITION)Node->m_Prev;
581     return Node->m_Element;
582 }
583 
584 template<typename E, class ETraits>
585 inline const E& CAtlList< E, ETraits >::GetPrev(_Inout_ POSITION& pos) const
586 {
587     CNode* Node = (CNode*)pos;
588     pos = (POSITION)Node->m_Prev;
589     return Node->m_Element;
590 }
591 
592 template<typename E, class ETraits>
593 inline E& CAtlList< E, ETraits >::GetAt(_In_ POSITION pos)
594 {
595     CNode* Node = (CNode*)pos;
596     return Node->m_Element;
597 }
598 
599 template<typename E, class ETraits>
600 inline const E& CAtlList< E, ETraits >::GetAt(_In_ POSITION pos) const
601 {
602     CNode* Node = (CNode*)pos;
603     return Node->m_Element;
604 }
605 
606 template<typename E, class ETraits>
607 POSITION CAtlList<E, ETraits>::AddHead(INARGTYPE element)
608 {
609     CNode* Node = CreateNode(element, NULL, m_HeadNode);
610     if (m_HeadNode)
611     {
612         m_HeadNode->m_Prev = Node;
613     }
614     else
615     {
616         m_TailNode = Node;
617     }
618     m_HeadNode = Node;
619 
620     return (POSITION)Node;
621 }
622 
623 template<typename E, class ETraits>
624 POSITION CAtlList<E, ETraits>::AddTail(INARGTYPE element)
625 {
626     CNode* Node = CreateNode(element, m_TailNode, NULL);
627     if (m_TailNode)
628     {
629         m_TailNode->m_Next = Node;
630     }
631     else
632     {
633         m_HeadNode = Node;
634     }
635     m_TailNode = Node;
636 
637     return (POSITION)Node;
638 }
639 
640 template<typename E, class ETraits>
641 E CAtlList<E, ETraits>::RemoveHead()
642 {
643     CNode* Node = m_HeadNode;
644     E Element(Node->m_Element);
645 
646     m_HeadNode = Node->m_Next;
647     if (m_HeadNode)
648     {
649         m_HeadNode->m_Prev = NULL;
650     }
651     else
652     {
653         m_TailNode = NULL;
654     }
655     FreeNode(Node);
656 
657     return Element;
658 }
659 
660 template<typename E, class ETraits>
661 E CAtlList<E, ETraits>::RemoveTail()
662 {
663     CNode* Node = m_TailNode;
664     E Element(Node->m_Element);
665 
666     m_TailNode = Node->m_Prev;
667     if (m_TailNode)
668     {
669         m_TailNode->m_Next = NULL;
670     }
671     else
672     {
673         m_HeadNode = NULL;
674     }
675     FreeNode(Node);
676 
677     return Element;
678 }
679 
680 template<typename E, class ETraits>
681 POSITION CAtlList<E, ETraits >::InsertBefore(_In_ POSITION pos, _In_ INARGTYPE element)
682 {
683     if (pos == NULL)
684         return AddHead(element);
685 
686     CNode* OldNode = (CNode*)pos;
687     CNode* Node = CreateNode(element, OldNode->m_Prev, OldNode);
688 
689     if (OldNode->m_Prev != NULL)
690     {
691         OldNode->m_Prev->m_Next = Node;
692     }
693     else
694     {
695         m_HeadNode = Node;
696     }
697     OldNode->m_Prev = Node;
698 
699     return (POSITION)Node;
700 }
701 
702 template<typename E, class ETraits>
703 POSITION CAtlList<E, ETraits >::InsertAfter(_In_ POSITION pos, _In_ INARGTYPE element)
704 {
705     if (pos == NULL)
706         return AddTail(element);
707 
708     CNode* OldNode = (CNode*)pos;
709     CNode* Node = CreateNode(element, OldNode, OldNode->m_Next);
710 
711     if (OldNode->m_Next != NULL)
712     {
713         OldNode->m_Next->m_Prev = Node;
714     }
715     else
716     {
717         m_TailNode = Node;
718     }
719     OldNode->m_Next = Node;
720 
721     return (POSITION)Node;
722 }
723 
724 template<typename E, class ETraits>
725 void CAtlList<E, ETraits >::RemoveAll()
726 {
727     while (m_NumElements > 0)
728     {
729         CNode* Node = m_HeadNode;
730         m_HeadNode = m_HeadNode->m_Next;
731         FreeNode(Node);
732     }
733 
734     m_HeadNode = NULL;
735     m_TailNode = NULL;
736     m_FreeNode = NULL;
737 
738     if (m_Blocks)
739     {
740         m_Blocks->Destroy();
741         m_Blocks = NULL;
742     }
743 }
744 
745 template<typename E, class ETraits>
746 void CAtlList<E, ETraits >::RemoveAt(_In_ POSITION pos)
747 {
748     ATLASSERT(pos != NULL);
749 
750     CNode* OldNode = (CNode*)pos;
751     if (OldNode == m_HeadNode)
752     {
753         m_HeadNode = OldNode->m_Next;
754     }
755     else
756     {
757         OldNode->m_Prev->m_Next = OldNode->m_Next;
758     }
759     if (OldNode == m_TailNode)
760     {
761         m_TailNode = OldNode->m_Prev;
762     }
763     else
764     {
765         OldNode->m_Next->m_Prev = OldNode->m_Prev;
766     }
767     FreeNode(OldNode);
768 }
769 
770 template<typename E, class ETraits>
771 POSITION CAtlList< E, ETraits >::Find(
772     INARGTYPE element,
773     _In_opt_ POSITION posStartAfter) const
774 {
775     CNode* Node = (CNode*)posStartAfter;
776     if (Node == NULL)
777     {
778         Node = m_HeadNode;
779     }
780     else
781     {
782         Node = Node->m_Next;
783     }
784 
785     for (; Node != NULL; Node = Node->m_Next)
786     {
787         if (ETraits::CompareElements(Node->m_Element, element))
788             return (POSITION)Node;
789     }
790 
791     return NULL;
792 }
793 
794 template<typename E, class ETraits>
795 POSITION CAtlList< E, ETraits >::FindIndex(_In_ size_t iElement) const
796 {
797     if (iElement >= m_NumElements)
798         return NULL;
799 
800     if (m_HeadNode == NULL)
801         return NULL;
802 
803     CNode* Node = m_HeadNode;
804     for (size_t i = 0; i < iElement; i++)
805     {
806         Node = Node->m_Next;
807     }
808 
809     return (POSITION)Node;
810 }
811 
812 
813 //
814 // CAtlist private methods
815 //
816 
817 template<typename E, class ETraits>
818 typename CAtlList<E, ETraits>::CNode* CAtlList<E, ETraits>::CreateNode(
819     INARGTYPE element,
820     _In_opt_ CNode* Prev,
821     _In_opt_ CNode* Next
822     )
823 {
824     GetFreeNode();
825 
826     CNode* NewNode = m_FreeNode;
827     CNode* NextFree = m_FreeNode->m_Next;
828 
829     NewNode = new CNode(element);
830 
831     m_FreeNode = NextFree;
832     NewNode->m_Prev = Prev;
833     NewNode->m_Next = Next;
834     m_NumElements++;
835 
836     return NewNode;
837 }
838 
839 template<typename E, class ETraits>
840 void CAtlList<E, ETraits>::FreeNode(
841     _Inout_ CNode* pNode
842     )
843 {
844     pNode->~CNode();
845     pNode->m_Next = m_FreeNode;
846     m_FreeNode = pNode;
847 
848     m_NumElements--;
849     if (m_NumElements == 0)
850     {
851         RemoveAll();
852     }
853 }
854 
855 template<typename E, class ETraits>
856 typename CAtlList<E, ETraits>::CNode* CAtlList< E, ETraits>::GetFreeNode()
857 {
858     if (m_FreeNode)
859     {
860         return m_FreeNode;
861     }
862 
863     CAtlPlex* Block = CAtlPlex::Create(m_Blocks, m_BlockSize, sizeof(CNode));
864     if (Block == NULL)
865     {
866         AtlThrowImp(E_OUTOFMEMORY);
867     }
868 
869     CNode* Node = (CNode*)Block->GetData();
870     Node += (m_BlockSize - 1);
871     for (int i = m_BlockSize - 1; i >= 0; i--)
872     {
873         Node->m_Next = m_FreeNode;
874         m_FreeNode = Node;
875         Node--;
876     }
877 
878     return m_FreeNode;
879 }
880 
881 
882 template<typename E, class Allocator = CCRTAllocator >
883 class CHeapPtrList :
884     public CAtlList<CHeapPtr<E, Allocator>, CHeapPtrElementTraits<E, Allocator> >
885 {
886 public:
887     CHeapPtrList(_In_ UINT nBlockSize = 10) :
888         CAtlList<CHeapPtr<E, Allocator>, CHeapPtrElementTraits<E, Allocator> >(nBlockSize)
889     {
890     }
891 
892 private:
893     CHeapPtrList(const CHeapPtrList&);
894     CHeapPtrList& operator=(const CHeapPtrList*);
895 };
896 
897 
898 }
899 
900 #endif