llptrskiplist.h
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:17k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llptrskiplist.h
  3.  * @brief Skip list implementation.
  4.  *
  5.  * $LicenseInfo:firstyear=2001&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2001-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32. #ifndef LL_LLPTRSKIPLIST_H
  33. #define LL_LLPTRSKIPLIST_H
  34. #include "llerror.h"
  35. #include "llrand.h"
  36. //#include "vmath.h"
  37. #include "llrand.h"
  38. /////////////////////////////////////////////
  39. //
  40. // LLPtrSkipList implementation - skip list for pointers to objects
  41. //
  42. template <class DATA_TYPE, S32 BINARY_DEPTH = 8>
  43. class LLPtrSkipList
  44. {
  45. public:
  46. friend class LLPtrSkipNode;
  47. // basic constructor
  48. LLPtrSkipList();
  49. // basic constructor including sorter
  50. LLPtrSkipList(BOOL (*insert_first)(DATA_TYPE *first, DATA_TYPE *second), 
  51.   BOOL (*equals)(DATA_TYPE *first, DATA_TYPE *second));
  52. ~LLPtrSkipList();
  53. inline void setInsertFirst(BOOL (*insert_first)(const DATA_TYPE *first, const DATA_TYPE *second));
  54. inline void setEquals(BOOL (*equals)(const DATA_TYPE *first, const DATA_TYPE *second));
  55. inline BOOL addData(DATA_TYPE *data);
  56. inline BOOL checkData(const DATA_TYPE *data);
  57. inline S32 getLength(); // returns number of items in the list - NOT constant time!
  58. inline BOOL removeData(const DATA_TYPE *data);
  59. // note that b_sort is ignored
  60. inline BOOL moveData(const DATA_TYPE *data, LLPtrSkipList *newlist, BOOL b_sort);
  61. inline BOOL moveCurrentData(LLPtrSkipList *newlist, BOOL b_sort);
  62. // resort -- use when the value we're sorting by changes
  63. /* IW 12/6/02 - This doesn't work!
  64.    Instead, remove the data BEFORE you change it
  65.    Then re-insert it after you change it
  66. BOOL resortData(DATA_TYPE *data)
  67. */
  68. // remove all nodes from the list but do not delete data
  69. inline void removeAllNodes();
  70. inline BOOL deleteData(const DATA_TYPE *data);
  71. // remove all nodes from the list and delete data
  72. inline void deleteAllData();
  73. // place mCurrentp on first node
  74. inline void resetList();
  75. // return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
  76. inline DATA_TYPE *getCurrentData();
  77. // same as getCurrentData() but a more intuitive name for the operation
  78. inline DATA_TYPE *getNextData();
  79. // remove the Node at mCurentOperatingp
  80. // leave mCurrentp and mCurentOperatingp on the next entry
  81. inline void removeCurrentData();
  82. // delete the Node at mCurentOperatingp
  83. // leave mCurrentp and mCurentOperatingp on the next entry
  84. inline void deleteCurrentData();
  85. // reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
  86. inline DATA_TYPE *getFirstData();
  87. // TRUE if nodes are not in sorted order
  88. inline BOOL corrupt();
  89. protected:
  90. class LLPtrSkipNode
  91. {
  92. public:
  93. LLPtrSkipNode()
  94. : mData(NULL)
  95. {
  96. S32 i;
  97. for (i = 0; i < BINARY_DEPTH; i++)
  98. {
  99. mForward[i] = NULL;
  100. }
  101. }
  102. LLPtrSkipNode(DATA_TYPE *data)
  103. : mData(data)
  104. {
  105. S32 i;
  106. for (i = 0; i < BINARY_DEPTH; i++)
  107. {
  108. mForward[i] = NULL;
  109. }
  110. }
  111. ~LLPtrSkipNode()
  112. {
  113. if (mData)
  114. {
  115. llerror("Attempting to call LLPtrSkipNode destructor with a non-null mDatap!", 1);
  116. }
  117. }
  118. // delete associated data and NULLs out pointer
  119. void deleteData()
  120. {
  121. delete mData;
  122. mData = NULL;
  123. }
  124. // NULLs out pointer
  125. void removeData()
  126. {
  127. mData = NULL;
  128. }
  129. DATA_TYPE *mData;
  130. LLPtrSkipNode *mForward[BINARY_DEPTH];
  131. };
  132. static BOOL defaultEquals(const DATA_TYPE *first, const DATA_TYPE *second)
  133. {
  134. return first == second;
  135. }
  136. LLPtrSkipNode mHead;
  137. LLPtrSkipNode *mUpdate[BINARY_DEPTH];
  138. LLPtrSkipNode *mCurrentp;
  139. LLPtrSkipNode *mCurrentOperatingp;
  140. S32 mLevel;
  141. BOOL (*mInsertFirst)(const DATA_TYPE *first, const DATA_TYPE *second);
  142. BOOL (*mEquals)(const DATA_TYPE *first, const DATA_TYPE *second);
  143. };
  144. // basic constructor
  145. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  146. LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::LLPtrSkipList()
  147. : mInsertFirst(NULL), mEquals(defaultEquals)
  148. {
  149. if (BINARY_DEPTH < 2)
  150. {
  151. llerrs << "Trying to create skip list with too little depth, "
  152. "must be 2 or greater" << llendl;
  153. }
  154. S32 i;
  155. for (i = 0; i < BINARY_DEPTH; i++)
  156. {
  157. mUpdate[i] = NULL;
  158. }
  159. mLevel = 1;
  160. mCurrentp = *(mHead.mForward);
  161. mCurrentOperatingp = *(mHead.mForward);
  162. }
  163. // basic constructor including sorter
  164. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  165. LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::LLPtrSkipList(BOOL (*insert_first)(DATA_TYPE *first, DATA_TYPE *second), 
  166.   BOOL (*equals)(DATA_TYPE *first, DATA_TYPE *second)) 
  167. :mInsertFirst(insert_first), mEquals(equals)
  168. {
  169. if (BINARY_DEPTH < 2)
  170. {
  171. llerrs << "Trying to create skip list with too little depth, "
  172. "must be 2 or greater" << llendl;
  173. }
  174. mLevel = 1;
  175. S32 i;
  176. for (i = 0; i < BINARY_DEPTH; i++)
  177. {
  178. mHead.mForward[i] = NULL;
  179. mUpdate[i] = NULL;
  180. }
  181. mCurrentp = *(mHead.mForward);
  182. mCurrentOperatingp = *(mHead.mForward);
  183. }
  184. template <class DATA_TYPE, S32 BINARY_DEPTH>
  185. inline LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::~LLPtrSkipList()
  186. {
  187. removeAllNodes();
  188. }
  189. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  190. inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::setInsertFirst(BOOL (*insert_first)(const DATA_TYPE *first, const DATA_TYPE *second))
  191. {
  192. mInsertFirst = insert_first;
  193. }
  194. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  195. inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::setEquals(BOOL (*equals)(const DATA_TYPE *first, const DATA_TYPE *second))
  196. {
  197. mEquals = equals;
  198. }
  199. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  200. inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::addData(DATA_TYPE *data)
  201. {
  202. S32 level;
  203. LLPtrSkipNode *current = &mHead;
  204. LLPtrSkipNode *temp;
  205. // find the pointer one in front of the one we want
  206. if (mInsertFirst)
  207. {
  208. for (level = mLevel - 1; level >= 0; level--)
  209. {
  210. temp = *(current->mForward + level);
  211. while (  (temp)
  212.    &&(mInsertFirst(temp->mData, data)))
  213. {
  214. current = temp;
  215. temp = *(current->mForward + level);
  216. }
  217. *(mUpdate + level) = current;
  218. }
  219. }
  220. else
  221. {
  222. for (level = mLevel - 1; level >= 0; level--)
  223. {
  224. temp = *(current->mForward + level);
  225. while (  (temp)
  226.    &&(temp->mData < data))
  227. {
  228. current = temp;
  229. temp = *(current->mForward + level);
  230. }
  231. *(mUpdate + level) = current;
  232. }
  233. }
  234. // we're now just in front of where we want to be . . . take one step forward
  235. current = *current->mForward;
  236. // now add the new node
  237. S32 newlevel;
  238. for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
  239. {
  240. if (ll_frand() < 0.5f)
  241. break;
  242. }
  243. LLPtrSkipNode *snode = new LLPtrSkipNode(data);
  244. if (newlevel > mLevel)
  245. {
  246. mHead.mForward[mLevel] = NULL;
  247. mUpdate[mLevel] = &mHead;
  248. mLevel = newlevel;
  249. }
  250. for (level = 0; level < newlevel; level++)
  251. {
  252. snode->mForward[level] = mUpdate[level]->mForward[level];
  253. mUpdate[level]->mForward[level] = snode;
  254. }
  255. return TRUE;
  256. }
  257. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  258. inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::checkData(const DATA_TYPE *data)
  259. {
  260. S32 level;
  261. LLPtrSkipNode *current = &mHead;
  262. LLPtrSkipNode *temp;
  263. // find the pointer one in front of the one we want
  264. if (mInsertFirst)
  265. {
  266. for (level = mLevel - 1; level >= 0; level--)
  267. {
  268. temp = *(current->mForward + level);
  269. while (  (temp)
  270.    &&(mInsertFirst(temp->mData, data)))
  271. {
  272. current = temp;
  273. temp = *(current->mForward + level);
  274. }
  275. *(mUpdate + level) = current;
  276. }
  277. }
  278. else
  279. {
  280. for (level = mLevel - 1; level >= 0; level--)
  281. {
  282. temp = *(current->mForward + level);
  283. while (  (temp)
  284.    &&(temp->mData < data))
  285. {
  286. current = temp;
  287. temp = *(current->mForward + level);
  288. }
  289. *(mUpdate + level) = current;
  290. }
  291. }
  292. // we're now just in front of where we want to be . . . take one step forward
  293. current = *current->mForward;
  294. if (current)
  295. {
  296. return mEquals(current->mData, data);
  297. }
  298. else
  299. {
  300. return FALSE;
  301. }
  302. }
  303. // returns number of items in the list
  304. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  305. inline S32 LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getLength()
  306. {
  307. U32 length = 0;
  308. for (LLPtrSkipNode* temp = *(mHead.mForward); temp != NULL; temp = temp->mForward[0])
  309. {
  310. length++;
  311. }
  312. return length;
  313. }
  314. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  315. inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::removeData(const DATA_TYPE *data)
  316. {
  317. S32 level;
  318. LLPtrSkipNode *current = &mHead;
  319. LLPtrSkipNode *temp;
  320. // find the pointer one in front of the one we want
  321. if (mInsertFirst)
  322. {
  323. for (level = mLevel - 1; level >= 0; level--)
  324. {
  325. temp = *(current->mForward + level);
  326. while (  (temp)
  327.    &&(mInsertFirst(temp->mData, data)))
  328. {
  329. current = temp;
  330. temp = *(current->mForward + level);
  331. }
  332. *(mUpdate + level) = current;
  333. }
  334. }
  335. else
  336. {
  337. for (level = mLevel - 1; level >= 0; level--)
  338. {
  339. temp = *(current->mForward + level);
  340. while (  (temp)
  341.    &&(temp->mData < data))
  342. {
  343. current = temp;
  344. temp = *(current->mForward + level);
  345. }
  346. *(mUpdate + level) = current;
  347. }
  348. }
  349. // we're now just in front of where we want to be . . . take one step forward
  350. current = *current->mForward;
  351. if (!current)
  352. {
  353. // empty list or beyond the end!
  354. return FALSE;
  355. }
  356. // is this the one we want?
  357. if (!mEquals(current->mData, data))
  358. {
  359. // nope!
  360. return FALSE;
  361. }
  362. else
  363. {
  364. // yes it is!  change pointers as required
  365. for (level = 0; level < mLevel; level++)
  366. {
  367. if (mUpdate[level]->mForward[level] != current)
  368. {
  369. // cool, we've fixed all the pointers!
  370. break;
  371. }
  372. mUpdate[level]->mForward[level] = current->mForward[level];
  373. }
  374. // clean up cuurent
  375. current->removeData();
  376. delete current;
  377. // clean up mHead
  378. while (  (mLevel > 1)
  379.    &&(!mHead.mForward[mLevel - 1]))
  380. {
  381. mLevel--;
  382. }
  383. }
  384. return TRUE;
  385. }
  386. // note that b_sort is ignored
  387. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  388. inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::moveData(const DATA_TYPE *data, LLPtrSkipList *newlist, BOOL b_sort)
  389. {
  390. BOOL removed = removeData(data);
  391. BOOL added = newlist->addData(data);
  392. return removed && added;
  393. }
  394. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  395. inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::moveCurrentData(LLPtrSkipList *newlist, BOOL b_sort)
  396. {
  397. if (mCurrentOperatingp)
  398. {
  399. mCurrentp = mCurrentOperatingp->mForward[0];
  400. BOOL removed = removeData(mCurrentOperatingp);
  401. BOOL added = newlist->addData(mCurrentOperatingp);
  402. mCurrentOperatingp = mCurrentp;
  403. return removed && added;
  404. }
  405. return FALSE;
  406. }
  407. // resort -- use when the value we're sorting by changes
  408. /* IW 12/6/02 - This doesn't work!
  409.    Instead, remove the data BEFORE you change it
  410.    Then re-insert it after you change it
  411. BOOL resortData(DATA_TYPE *data)
  412. {
  413. removeData(data);
  414. addData(data);
  415. }
  416. */
  417. // remove all nodes from the list but do not delete data
  418. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  419. inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::removeAllNodes()
  420. {
  421. LLPtrSkipNode *temp;
  422. // reset mCurrentp
  423. mCurrentp = *(mHead.mForward);
  424. while (mCurrentp)
  425. {
  426. temp = mCurrentp->mForward[0];
  427. mCurrentp->removeData();
  428. delete mCurrentp;
  429. mCurrentp = temp;
  430. }
  431. S32 i;
  432. for (i = 0; i < BINARY_DEPTH; i++)
  433. {
  434. mHead.mForward[i] = NULL;
  435. mUpdate[i] = NULL;
  436. }
  437. mCurrentp = *(mHead.mForward);
  438. mCurrentOperatingp = *(mHead.mForward);
  439. }
  440. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  441. inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::deleteData(const DATA_TYPE *data)
  442. {
  443. S32 level;
  444. LLPtrSkipNode *current = &mHead;
  445. LLPtrSkipNode *temp;
  446. // find the pointer one in front of the one we want
  447. if (mInsertFirst)
  448. {
  449. for (level = mLevel - 1; level >= 0; level--)
  450. {
  451. temp = *(current->mForward + level);
  452. while (  (temp)
  453.    &&(mInsertFirst(temp->mData, data)))
  454. {
  455. current = temp;
  456. temp = *(current->mForward + level);
  457. }
  458. *(mUpdate + level) = current;
  459. }
  460. }
  461. else
  462. {
  463. for (level = mLevel - 1; level >= 0; level--)
  464. {
  465. temp = *(current->mForward + level);
  466. while (  (temp)
  467.    &&(temp->mData < data))
  468. {
  469. current = temp;
  470. temp = *(current->mForward + level);
  471. }
  472. *(mUpdate + level) = current;
  473. }
  474. }
  475. // we're now just in front of where we want to be . . . take one step forward
  476. current = *current->mForward;
  477. if (!current)
  478. {
  479. // empty list or beyond the end!
  480. return FALSE;
  481. }
  482. // is this the one we want?
  483. if (!mEquals(current->mData, data))
  484. {
  485. // nope!
  486. return FALSE;
  487. }
  488. else
  489. {
  490. // do we need to fix current or currentop?
  491. if (current == mCurrentp)
  492. {
  493. mCurrentp = current->mForward[0];
  494. }
  495. if (current == mCurrentOperatingp)
  496. {
  497. mCurrentOperatingp = current->mForward[0];
  498. }
  499. // yes it is!  change pointers as required
  500. for (level = 0; level < mLevel; level++)
  501. {
  502. if (mUpdate[level]->mForward[level] != current)
  503. {
  504. // cool, we've fixed all the pointers!
  505. break;
  506. }
  507. mUpdate[level]->mForward[level] = current->mForward[level];
  508. }
  509. // clean up cuurent
  510. current->deleteData();
  511. delete current;
  512. // clean up mHead
  513. while (  (mLevel > 1)
  514.    &&(!mHead.mForward[mLevel - 1]))
  515. {
  516. mLevel--;
  517. }
  518. }
  519. return TRUE;
  520. }
  521. // remove all nodes from the list and delete data
  522. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  523. inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::deleteAllData()
  524. {
  525. LLPtrSkipNode *temp;
  526. // reset mCurrentp
  527. mCurrentp = *(mHead.mForward);
  528. while (mCurrentp)
  529. {
  530. temp = mCurrentp->mForward[0];
  531. mCurrentp->deleteData();
  532. delete mCurrentp;
  533. mCurrentp = temp;
  534. }
  535. S32 i;
  536. for (i = 0; i < BINARY_DEPTH; i++)
  537. {
  538. mHead.mForward[i] = NULL;
  539. mUpdate[i] = NULL;
  540. }
  541. mCurrentp = *(mHead.mForward);
  542. mCurrentOperatingp = *(mHead.mForward);
  543. }
  544. // place mCurrentp on first node
  545. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  546. inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::resetList()
  547. {
  548. mCurrentp = *(mHead.mForward);
  549. mCurrentOperatingp = *(mHead.mForward);
  550. }
  551. // return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
  552. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  553. inline DATA_TYPE *LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getCurrentData()
  554. {
  555. if (mCurrentp)
  556. {
  557. mCurrentOperatingp = mCurrentp;
  558. mCurrentp = *mCurrentp->mForward;
  559. return mCurrentOperatingp->mData;
  560. }
  561. else
  562. {
  563. //return NULL; // causes compile warning
  564. return 0;  // equivalent, but no warning
  565. }
  566. }
  567. // same as getCurrentData() but a more intuitive name for the operation
  568. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  569. inline DATA_TYPE *LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getNextData()
  570. {
  571. if (mCurrentp)
  572. {
  573. mCurrentOperatingp = mCurrentp;
  574. mCurrentp = *mCurrentp->mForward;
  575. return mCurrentOperatingp->mData;
  576. }
  577. else
  578. {
  579. //return NULL; // causes compile warning
  580. return 0;  // equivalent, but no warning
  581. }
  582. }
  583. // remove the Node at mCurentOperatingp
  584. // leave mCurrentp and mCurentOperatingp on the next entry
  585. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  586. inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::removeCurrentData()
  587. {
  588. if (mCurrentOperatingp)
  589. {
  590. removeData(mCurrentOperatingp->mData);
  591. }
  592. }
  593. // delete the Node at mCurentOperatingp
  594. // leave mCurrentp and mCurentOperatingp on the next entry
  595. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  596. inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::deleteCurrentData()
  597. {
  598. if (mCurrentOperatingp)
  599. {
  600. deleteData(mCurrentOperatingp->mData);
  601. }
  602. }
  603. // reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
  604. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  605. inline DATA_TYPE *LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getFirstData()
  606. {
  607. mCurrentp = *(mHead.mForward);
  608. mCurrentOperatingp = *(mHead.mForward);
  609. if (mCurrentp)
  610. {
  611. mCurrentOperatingp = mCurrentp;
  612. mCurrentp = mCurrentp->mForward[0];
  613. return mCurrentOperatingp->mData;
  614. }
  615. else
  616. {
  617. //return NULL; // causes compile warning
  618. return 0;  // equivalent, but no warning
  619. }
  620. }
  621. template <class DATA_TYPE, S32 BINARY_DEPTH> 
  622. inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::corrupt()
  623. {
  624. LLPtrSkipNode *previous = mHead.mForward[0];
  625. // Empty lists are not corrupt.
  626. if (!previous) return FALSE;
  627. LLPtrSkipNode *current = previous->mForward[0];
  628. while(current)
  629. {
  630. if (!mInsertFirst(previous->mData, current->mData))
  631. {
  632. // prev shouldn't be in front of cur!
  633. return TRUE;
  634. }
  635. current = current->mForward[0];
  636. }
  637. return FALSE;
  638. }
  639. #endif