Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1953 lines
51 KiB

  1. /*************************************************************************
  2. * *
  3. * GROUPCOM.C *
  4. * *
  5. * Copyright (C) Microsoft Corporation 1990-1994 *
  6. * All Rights reserved. *
  7. * *
  8. **************************************************************************
  9. * *
  10. * Module Intent *
  11. * This module contains miscellaneous functions that are shared *
  12. * between index and search. Those modules can be related to stop *
  13. * words, groups, word wheels, catalogs, etc. The purpose is to *
  14. * share as much code as possible *
  15. * *
  16. **************************************************************************
  17. * *
  18. * Current Owner: GarrG *
  19. * *
  20. **************************************************************************
  21. * *
  22. * Released by Development: (date) *
  23. * *
  24. *************************************************************************/
  25. #include <verstamp.h>
  26. SETVERSIONSTAMP(MVUT);
  27. #include <mvopsys.h>
  28. #include <misc.h>
  29. #include <mem.h>
  30. #include <iterror.h>
  31. #include <wrapstor.h>
  32. #include <mvsearch.h>
  33. #include <groups.h>
  34. #include <orkin.h>
  35. #include <_mvutil.h> // File System
  36. #ifdef _DEBUG
  37. static char s_aszModule[] = __FILE__; // Used by error return functions.
  38. #endif
  39. #define cbitWORD (CBIT)16 // Number of bits in a word.
  40. #define cbitBYTE (CBIT)8 // Number of bits in a byte.
  41. #define SETERR(a,b) (*a=b)
  42. /*************************************************************************
  43. *
  44. * INTERNAL PRIVATE FUNCTIONS
  45. * All of them should be declared near
  46. *************************************************************************/
  47. static _LPGROUP NEAR PASCAL GroupCheckAndCreate(_LPGROUP, _LPGROUP, PHRESULT);
  48. static BOOL NEAR PASCAL GroupCheck (_LPGROUP lpGroup);
  49. static int PASCAL NEAR HiBitSet (BYTE c)
  50. {
  51. register int cBit = 7;
  52. while ((c & 128) == 0)
  53. {
  54. c <<= 1;
  55. cBit--;
  56. }
  57. return cBit;
  58. }
  59. static int PASCAL NEAR LoBitSet (BYTE c)
  60. {
  61. register int cBit = 0;
  62. while ((c & 1) == 0)
  63. {
  64. c >>= 1;
  65. cBit++;
  66. }
  67. return cBit;
  68. }
  69. /*************************************************************************
  70. * @doc INTERNAL
  71. *
  72. * @func BOOL NEAR PASCAL | GroupCheck |
  73. * This function will check the validity of a given group
  74. *
  75. * @parm _LPGROUP | lpGroup |
  76. * Pointer to group
  77. *
  78. * @rdesc Return S_OK if valid group, fail otherwize
  79. *************************************************************************/
  80. static BOOL NEAR PASCAL GroupCheck (_LPGROUP lpGroup)
  81. {
  82. // Changing to a MACRO
  83. // if (lpGroup == NULL ||
  84. // (lpGroup->version < 7 || lpGroup->version > GROUPVER) ||
  85. // lpGroup->FileStamp != GROUP_STAMP)
  86. // return ERR_FAILED;
  87. // return S_OK;
  88. VALIDATE_GROUP(lpGroup);
  89. }
  90. /*************************************************************************
  91. * @doc API INDEX RETRIEVAL
  92. *
  93. * @func LPGROUP FAR PASCAL | GroupInitiate |
  94. * This function creates and initializes a new group. The size of the
  95. * group is based on the total number of items
  96. *
  97. * @parm DWORD | lcGrpItem |
  98. * The maximum number of items in the group. If lcGrpItem is equal
  99. * to ITGROUPMAX, then the size of the group will grow
  100. * as necessary to fit the GrpItem (up to that limit)
  101. *
  102. * @parm PHRESULT | phr |
  103. * Error buffer
  104. *
  105. * @rdesc The function will return a pointer to the newly created group
  106. * if succeeded, NULL otherwise. The error buffer will contain
  107. * information about the cause of the failure
  108. *
  109. *************************************************************************/
  110. PUBLIC _LPGROUP FAR PASCAL GroupInitiate(DWORD lcGrpItem, PHRESULT phr)
  111. {
  112. _LPGROUP lpGroup;
  113. DWORD size;
  114. BYTE fGroupExpandable;
  115. /* Check to see if ther are too many GrpItem or not */
  116. if (lcGrpItem > LCBITGROUPMAX)
  117. {
  118. SetErrCode(phr, E_GROUPIDTOOBIG);
  119. return NULL;
  120. }
  121. if (fGroupExpandable = (BYTE)(lcGrpItem == LCBITGROUPMAX))
  122. {
  123. /* Expandable bitvector, start with one block */
  124. size = GROUP_BLOCK_SIZE;
  125. }
  126. else
  127. {
  128. size = ((lcGrpItem + cbitBYTE - 1) / cbitBYTE) * sizeof(BYTE);
  129. }
  130. if ((lpGroup = GroupCreate(size, lcGrpItem, phr)) == NULL)
  131. return NULL;
  132. if (fGroupExpandable)
  133. {
  134. lpGroup->wFlag |= GROUP_EXPAND;
  135. lpGroup->maxItem = 0;
  136. }
  137. else
  138. lpGroup->maxItem = lcGrpItem;
  139. lpGroup->minItem = LCBITGROUPMAX;
  140. return lpGroup;
  141. }
  142. /*************************************************************************
  143. * @doc API INDEX RETRIEVAL
  144. *
  145. * @func ERR FAR PASCAL | GroupAddItem |
  146. * This function will add a group item number into the given group
  147. *
  148. * @parm LPGROUP | lpGroup |
  149. * Pointer to group
  150. *
  151. * @parm DWORD | dwGrpItem |
  152. * Group Item to be added into the group. The value must be
  153. * 0 << dwGrpItem < 524280
  154. *
  155. * @rdesc S_OK if succeeded. The function can fail if the GrpItem
  156. * value is too large, ie. exceed the maximum value preset in
  157. * lpGroup when calling GroupInitiate()
  158. *
  159. * @xref GroupInitiate()
  160. *************************************************************************/
  161. PUBLIC ERR FAR PASCAL GroupAddItem(_LPGROUP lpGroup,
  162. DWORD dwGrpItem)
  163. {
  164. HANDLE hBitVect;
  165. DWORD size;
  166. BYTE bitSet;
  167. LPBYTE lpb;
  168. if (lpGroup == NULL) /* Safeguard check */
  169. return E_INVALIDARG; // Bad argument
  170. if (dwGrpItem > LCBITGROUPMAX)
  171. return E_GROUPIDTOOBIG;
  172. if (dwGrpItem >= lpGroup->dwSize * cbitBYTE)
  173. {
  174. if ((lpGroup->wFlag & GROUP_EXPAND) == 0)
  175. return E_GROUPIDTOOBIG;
  176. /* The BitVect needs to grow. Calculate the next needed size */
  177. size = ((dwGrpItem / (cbitBYTE * GROUP_BLOCK_SIZE)) + 1) * GROUP_BLOCK_SIZE;
  178. if (lpGroup->hGrpBitVect)
  179. {
  180. _GLOBALUNLOCK(lpGroup->hGrpBitVect);
  181. if ((hBitVect = lpGroup->hGrpBitVect =
  182. _GLOBALREALLOC(lpGroup->hGrpBitVect, size,
  183. DLLGMEM_ZEROINIT)) == NULL)
  184. return E_OUTOFMEMORY;
  185. }
  186. else
  187. {
  188. if ((hBitVect = lpGroup->hGrpBitVect =
  189. _GLOBALALLOC(DLLGMEM_ZEROINIT,size))==NULL)
  190. return E_OUTOFMEMORY;
  191. }
  192. lpGroup->lpbGrpBitVect = (LPBYTE)_GLOBALLOCK(hBitVect);
  193. lpGroup->dwSize = size;
  194. }
  195. if (lpGroup->maxItemAllGroup <= dwGrpItem)
  196. lpGroup->maxItemAllGroup = dwGrpItem+1;
  197. if (dwGrpItem > lpGroup->maxItem)
  198. lpGroup->maxItem = dwGrpItem;
  199. if (dwGrpItem < lpGroup->minItem)
  200. lpGroup->minItem = dwGrpItem;
  201. /* Set the bit */
  202. lpb = &lpGroup->lpbGrpBitVect[(UINT)(dwGrpItem / 8)];
  203. bitSet = 1 << (dwGrpItem % 8);
  204. if ((*lpb & bitSet) == 0)
  205. {
  206. *lpb |= bitSet;
  207. lpGroup->lcItem ++;
  208. }
  209. lpGroup->nCache = 0;
  210. return S_OK;
  211. }
  212. /*************************************************************************
  213. *
  214. * @doc API INDEX RETRIEVAL
  215. *
  216. * @func ERR FAR PASCAL | GroupRemoveItem |
  217. * This function will remove a group item number from the given group
  218. *
  219. * @parm LPGROUP | lpGroup |
  220. * Pointer to group. Must be non-null
  221. *
  222. * @parm DWORD | dwGrpItem |
  223. * Group Item to be removed from group.
  224. *
  225. * @rdesc S_OK if succeeded. The function can fail for bad argument
  226. * (lpGroup == NULL)
  227. *
  228. * @xref GroupInitiate()
  229. *************************************************************************/
  230. PUBLIC ERR FAR PASCAL GroupRemoveItem(_LPGROUP lpGroup,
  231. DWORD dwGrpItem)
  232. {
  233. LPBYTE lpb;
  234. BYTE bitSet;
  235. if (lpGroup == NULL) /* Safeguard check */
  236. return E_INVALIDARG; // Bad argument
  237. if (dwGrpItem < lpGroup->minItem || dwGrpItem > lpGroup->maxItem)
  238. return S_OK;
  239. /* Unset the bit */
  240. lpb = &lpGroup->lpbGrpBitVect[(UINT)(dwGrpItem / 8)];
  241. bitSet = (1 << (dwGrpItem % 8));
  242. /* Only update the bitvector and the item count if that item exists */
  243. if (*lpb & bitSet)
  244. {
  245. *lpb &= ~bitSet;
  246. lpGroup->lcItem--;
  247. }
  248. lpGroup->nCache = 0;
  249. return S_OK;
  250. }
  251. /*************************************************************************
  252. * @doc API INDEX RETRIEVAL
  253. *
  254. * @func LPGROUP FAR PASCAL | GroupCreate |
  255. * This function will create a new group according to the
  256. * group size
  257. *
  258. * @parm DWORD | size |
  259. * Size of group. In case the size is unknown, it should be set to be
  260. * (dwMaxItemAllGroup / 8)
  261. *
  262. * @parm DWORD | dwMaxItemAllGroup |
  263. * Maximum group item value that a set of group can have
  264. *
  265. * @parm PHRESULT | phr |
  266. * Error buffer
  267. *
  268. * @rdesc The function will return a pointer to the newly created group
  269. * if succeeded, NULL otherwise. The error buffer will contain
  270. * information about the cause of the failure
  271. *************************************************************************/
  272. PUBLIC _LPGROUP FAR PASCAL GroupCreate (DWORD size,
  273. DWORD dwMaxItemAllGroup, PHRESULT phr)
  274. {
  275. HANDLE handle;
  276. _LPGROUP lpGroup;
  277. /* Allocate structure */
  278. if ((handle = _GLOBALALLOC(DLLGMEM_ZEROINIT,
  279. sizeof(_GROUP))) == NULL)
  280. {
  281. SetErrCode(phr, E_OUTOFMEMORY);
  282. return NULL;
  283. }
  284. lpGroup = (_LPGROUP)_GLOBALLOCK(handle);
  285. lpGroup->dwSize = size;
  286. lpGroup->maxItemAllGroup = dwMaxItemAllGroup;
  287. lpGroup->hGroup = handle;
  288. lpGroup->FileStamp = GROUP_STAMP;
  289. lpGroup->version = GROUPVER;
  290. /* Allocate group BitVect */
  291. // +1 is added as a bug fix since in GroupFileBuild it sometimes writes out
  292. // an extra byte that has not been allocated. This is the "safe" fix.
  293. // For post 2.0 we should remove this and do a real fix in GroupFileBuild
  294. if ((lpGroup->hGrpBitVect = handle = _GLOBALALLOC(GMEM_FIXED | GMEM_ZEROINIT,
  295. size+1)) == NULL) // WinNT4.0 seemed to mishandle moveable memory here
  296. // when this code was called through a COM wrapper and proxy layer.
  297. // Very bizarre (a-ericry, thoroughly discussed with kevynct, 3 Apr 97)
  298. {
  299. /* Out of memory. Free the allocated structure */
  300. _GLOBALUNLOCK(lpGroup->hGroup);
  301. _GLOBALFREE(lpGroup->hGroup);
  302. SetErrCode (phr, E_OUTOFMEMORY);
  303. return NULL;
  304. }
  305. lpGroup->lpbGrpBitVect = (LPBYTE)_GLOBALLOCK(handle);
  306. lpGroup->lperr = phr;
  307. return lpGroup;
  308. }
  309. /*************************************************************************
  310. * @doc API INDEX RETRIEVAL
  311. *
  312. * @func VOID FAR PASCAL | GroupFree |
  313. * This function will free all memory associated with a group
  314. *
  315. * @parm LPGROUP | lpGroup |
  316. * Pointer to group to be freed
  317. *************************************************************************/
  318. PUBLIC VOID FAR PASCAL GroupFree(_LPGROUP lpGroup)
  319. {
  320. if (lpGroup == NULL)
  321. return;
  322. /* Free the memory */
  323. if (lpGroup->hGrpBitVect)
  324. {
  325. _GLOBALUNLOCK(lpGroup->hGrpBitVect);
  326. _GLOBALFREE(lpGroup->hGrpBitVect);
  327. }
  328. _GLOBALUNLOCK(lpGroup->hGroup);
  329. _GLOBALFREE(lpGroup->hGroup);
  330. }
  331. // Saves the deltas between samples in high-bit 'nibble' compression.
  332. // If the high bit of low nibble not set, value is 0-7, else
  333. // value is 8-63 unless high nibble set, etc.
  334. //
  335. // [B x x x A x x x] [D x x x C x x x]...
  336. //
  337. #define COMPRESS_DELTA 0
  338. #define COMPRESS_DELTA_INV 1
  339. // The Group RLE method should work by saving the lengths of runs of
  340. // consecutive 1's and runs of 0's. So 00001111111001111 would be
  341. // saved as the values 4,7,2,4 in the above nibble format. Great
  342. // savings can be made if the bits are in large groups.
  343. #define COMPRESS_GROUPRLE 2
  344. // We have room for 13 more types of compression if needed!
  345. HANDLE NEAR PASCAL GroupCompressDelta(LPBYTE lpb, DWORD * pdwSize)
  346. {
  347. DWORD dwSize;
  348. DWORD dwNewSize=1;
  349. LPBYTE lpbCursor;
  350. DWORD dwBits;
  351. DWORD dwNumber;
  352. DWORD dwLastNumber;
  353. DWORD dwDelta;
  354. HANDLE hMem=NULL;
  355. LPBYTE lpbMem=NULL;
  356. WORD wShift=0;
  357. DWORD dwPartialDelta;
  358. WORD wCompressMode=COMPRESS_DELTA;
  359. DWORD dwSmallestSize;
  360. BOOL bCountingOnes=FALSE; // Used in GROUPRLE
  361. DWORD dwTotalCount=0; // Used in GROUPRLE
  362. // See how much space we're going to take first for the various
  363. // compression schemes
  364. // if no scheme works, no need alloc mem!
  365. // COMPRESS_DELTA
  366. dwNumber=(DWORD)-1;
  367. dwLastNumber=0;
  368. dwSize=*pdwSize;
  369. lpbCursor=lpb;
  370. dwBits=0;
  371. while (dwSize--)
  372. { WORD b;
  373. WORD wBit=0;
  374. b=(WORD)*lpbCursor;
  375. while (b)
  376. { if (b&1)
  377. { // add in bit number dwBits+wBit
  378. dwLastNumber=dwNumber;
  379. dwNumber=dwBits+wBit;
  380. dwDelta=dwNumber-dwLastNumber;
  381. do
  382. {
  383. dwPartialDelta=(dwDelta&0x7);
  384. if (dwDelta&0xfffffff8)
  385. dwPartialDelta|=0x8;
  386. dwNewSize++;
  387. dwDelta>>=3;
  388. } while (dwPartialDelta&0x8);
  389. }
  390. b>>=1;
  391. wBit++;
  392. }
  393. lpbCursor++;
  394. dwBits+=8;
  395. }
  396. dwNewSize=(dwNewSize+1)/2;
  397. dwSmallestSize=dwNewSize;
  398. // COMPRESS_DELTA_INV
  399. dwNewSize=1; // 1 for the compression code nibble
  400. dwNumber=(DWORD)-1;
  401. dwLastNumber=0;
  402. dwSize=*pdwSize;
  403. lpbCursor=lpb;
  404. dwBits=0;
  405. while (dwSize--)
  406. { WORD b;
  407. WORD wBit=0;
  408. b=(WORD)(~(*lpbCursor))&0xff;
  409. while (b)
  410. { if (b&1)
  411. { // add in bit number dwBits+wBit
  412. dwLastNumber=dwNumber;
  413. dwNumber=dwBits+wBit;
  414. dwDelta=dwNumber-dwLastNumber;
  415. do
  416. {
  417. dwPartialDelta=(dwDelta&0x7);
  418. if (dwDelta&0xfffffff8)
  419. dwPartialDelta|=0x8;
  420. dwNewSize++;
  421. dwDelta>>=3;
  422. } while (dwPartialDelta&0x8);
  423. }
  424. b>>=1;
  425. wBit++;
  426. }
  427. lpbCursor++;
  428. dwBits+=8;
  429. if ((dwNewSize+1)/2 > dwSmallestSize)
  430. break;
  431. }
  432. dwNewSize=(dwNewSize+1)/2;
  433. if (dwNewSize<dwSmallestSize)
  434. { dwSmallestSize=dwNewSize;
  435. wCompressMode=COMPRESS_DELTA_INV;
  436. }
  437. // COMPRESS_GROUPRLE
  438. // Start by counting number of 0's, then alternate 1's, 0's, ...
  439. bCountingOnes=FALSE;
  440. dwTotalCount=0;
  441. dwNewSize=1; // 1 for the compression code nibble
  442. dwNumber=(DWORD)-1;
  443. dwLastNumber=0;
  444. dwSize=*pdwSize;
  445. lpbCursor=lpb;
  446. while (dwSize--)
  447. { WORD b;
  448. WORD wBit=0;
  449. b=(WORD)*lpbCursor;
  450. while (wBit<8)
  451. { if (b&1)
  452. { if (bCountingOnes)
  453. dwTotalCount++;
  454. else // we were counting 0's, output zero count now
  455. {
  456. do
  457. {
  458. dwPartialDelta=(dwTotalCount&0x7);
  459. if (dwTotalCount&0xfffffff8)
  460. dwPartialDelta|=0x8;
  461. dwNewSize++;
  462. dwTotalCount>>=3;
  463. } while (dwPartialDelta&0x8);
  464. bCountingOnes=TRUE;
  465. dwTotalCount=1;
  466. }
  467. }
  468. else
  469. {
  470. if (!bCountingOnes)
  471. dwTotalCount++;
  472. else
  473. { // we were counting ones, output count of 1's now
  474. do
  475. {
  476. dwPartialDelta=(dwTotalCount&0x7);
  477. if (dwTotalCount&0xfffffff8)
  478. dwPartialDelta|=0x8;
  479. dwNewSize++;
  480. dwTotalCount>>=3;
  481. } while (dwPartialDelta&0x8);
  482. bCountingOnes=FALSE;
  483. dwTotalCount=1;
  484. }
  485. }
  486. b>>=1;
  487. wBit++;
  488. }
  489. lpbCursor++;
  490. if ((dwNewSize+1)/2 > dwSmallestSize)
  491. break;
  492. }
  493. dwNewSize=(dwNewSize+1)/2;
  494. if (dwNewSize<dwSmallestSize)
  495. { dwSmallestSize=dwNewSize;
  496. wCompressMode=COMPRESS_GROUPRLE;
  497. }
  498. // We now have the smallest size dwSmallestSize and
  499. // compression format wCompressMode
  500. dwNewSize=dwSmallestSize;
  501. if (dwNewSize>=*pdwSize)
  502. {
  503. *pdwSize=dwNewSize;
  504. return NULL;
  505. }
  506. hMem=_GLOBALALLOC(DLLGMEM_ZEROINIT,dwNewSize);
  507. lpbMem=(LPBYTE)_GLOBALLOCK(hMem);
  508. // Write compression code type nibble
  509. *lpbMem|=(BYTE)(wCompressMode&0xf);
  510. wShift=4;
  511. if ((wCompressMode==COMPRESS_DELTA) || (wCompressMode==COMPRESS_DELTA_INV))
  512. {
  513. dwNumber=(DWORD)-1;
  514. dwLastNumber=0;
  515. dwSize=*pdwSize;
  516. lpbCursor=lpb;
  517. dwBits=0;
  518. dwNewSize=1;
  519. while (dwSize--)
  520. { WORD b;
  521. WORD wBit=0;
  522. b=(WORD)*lpbCursor;
  523. if (wCompressMode==COMPRESS_DELTA_INV)
  524. b=(~b)&0xff;
  525. while (b)
  526. { if (b&1)
  527. { // add in bit number dwBits+wBit
  528. dwLastNumber=dwNumber;
  529. dwNumber=dwBits+wBit;
  530. dwDelta=dwNumber-dwLastNumber; // We could subt 1, but end case won't detect
  531. // if last nibble not filled.
  532. do
  533. {
  534. dwPartialDelta=(dwDelta&0x7);
  535. if (dwDelta&0xfffffff8)
  536. dwPartialDelta|=0x8;
  537. *lpbMem|=(BYTE)(dwPartialDelta<<wShift);
  538. lpbMem+=(wShift>>2);
  539. wShift=4-wShift;
  540. dwNewSize++;
  541. dwDelta>>=3;
  542. } while (dwPartialDelta&0x8);
  543. }
  544. b>>=1;
  545. wBit++;
  546. }
  547. lpbCursor++;
  548. dwBits+=8;
  549. }
  550. dwNewSize=(dwNewSize+1)/2;
  551. }
  552. else if (wCompressMode==COMPRESS_GROUPRLE)
  553. {
  554. bCountingOnes=FALSE;
  555. dwTotalCount=0;
  556. dwNewSize=1; // 1 for the compression code nibble
  557. dwNumber=(DWORD)-1;
  558. dwLastNumber=0;
  559. dwSize=*pdwSize;
  560. lpbCursor=lpb;
  561. while (dwSize--)
  562. { WORD b;
  563. WORD wBit=0;
  564. b=(WORD)*lpbCursor;
  565. while (wBit<8)
  566. { if (b&1)
  567. { if (bCountingOnes)
  568. dwTotalCount++;
  569. else // we were counting 0's, output zero count now
  570. {
  571. do
  572. {
  573. dwPartialDelta=(dwTotalCount&0x7);
  574. if (dwTotalCount&0xfffffff8)
  575. dwPartialDelta|=0x8;
  576. *lpbMem|=(BYTE)(dwPartialDelta<<wShift);
  577. lpbMem+=(wShift>>2);
  578. wShift=4-wShift;
  579. dwNewSize++;
  580. dwTotalCount>>=3;
  581. } while (dwPartialDelta&0x8);
  582. bCountingOnes=TRUE;
  583. dwTotalCount=1;
  584. }
  585. }
  586. else
  587. {
  588. if (!bCountingOnes)
  589. dwTotalCount++;
  590. else
  591. { // we were counting ones, output count of 1's now
  592. do
  593. {
  594. dwPartialDelta=(dwTotalCount&0x7);
  595. if (dwTotalCount&0xfffffff8)
  596. dwPartialDelta|=0x8;
  597. *lpbMem|=(BYTE)(dwPartialDelta<<wShift);
  598. lpbMem+=(wShift>>2);
  599. wShift=4-wShift;
  600. dwNewSize++;
  601. dwTotalCount>>=3;
  602. } while (dwPartialDelta&0x8);
  603. bCountingOnes=FALSE;
  604. dwTotalCount=1;
  605. }
  606. }
  607. b>>=1;
  608. wBit++;
  609. }
  610. lpbCursor++;
  611. }
  612. // Catch tail end if 1's at end (fixes bug848)
  613. if ((bCountingOnes) && (dwTotalCount))
  614. {
  615. do
  616. {
  617. dwPartialDelta=(dwTotalCount&0x7);
  618. if (dwTotalCount&0xfffffff8)
  619. dwPartialDelta|=0x8;
  620. *lpbMem|=(BYTE)(dwPartialDelta<<wShift);
  621. lpbMem+=(wShift>>2);
  622. wShift=4-wShift;
  623. dwNewSize++;
  624. dwTotalCount>>=3;
  625. } while (dwPartialDelta&0x8);
  626. }
  627. dwNewSize=(dwNewSize+1)/2;
  628. }
  629. *pdwSize=dwNewSize;
  630. _GLOBALUNLOCK (hMem);
  631. return hMem;
  632. }
  633. DWORD NEAR PASCAL GroupDecompressDelta(LPBYTE lpbDest, LPBYTE lpbSource, DWORD dwSize)
  634. {
  635. DWORD dwNewSize=0;
  636. LPBYTE lpbCursor=lpbSource;
  637. WORD wShift=0;
  638. WORD wNibble;
  639. WORD wCompressMode=0;
  640. DWORD dwTotalSize;
  641. DWORD dwNumber=(DWORD)-1;
  642. wCompressMode=(WORD)((*lpbSource)&0xf);
  643. wShift=4;
  644. if ((wCompressMode==COMPRESS_DELTA) || (wCompressMode==COMPRESS_DELTA_INV))
  645. {
  646. DWORD dwDelta;
  647. WORD wShiftDelta=0;
  648. DWORD dwLastNumber;
  649. while (dwSize)
  650. { // Get a delta
  651. dwDelta=0;
  652. wShiftDelta=0;
  653. do
  654. { wNibble=((*lpbSource)>>wShift);
  655. dwDelta|=(wNibble&0x7)<<wShiftDelta;
  656. wShiftDelta+=3;
  657. lpbSource+=(wShift>>2);
  658. dwSize-=(wShift>>2);
  659. wShift=4-wShift;
  660. } while (wNibble&0x8);
  661. dwLastNumber=dwNumber;
  662. dwNumber=dwLastNumber+dwDelta;
  663. // Set the dwNumber bit
  664. if (dwNumber!=(DWORD)-1)
  665. *(lpbDest+(dwNumber>>3))|=(BYTE)(1<<(dwNumber&0x7));
  666. }
  667. dwTotalSize=(dwNumber+7)>>3;
  668. if (wCompressMode==COMPRESS_DELTA_INV) // invert entire group
  669. { DWORD dwCt=dwTotalSize;
  670. BYTE * lpb=lpbDest;
  671. while (dwCt--)
  672. {
  673. *lpb=~*lpb;
  674. lpb++;
  675. }
  676. }
  677. }
  678. else if (wCompressMode==COMPRESS_GROUPRLE)
  679. {
  680. DWORD dwRunSize;
  681. WORD wShiftRunSize=0;
  682. BOOL bExpandingOnes=0;
  683. dwNumber=0;
  684. while (dwSize)
  685. { // Get a delta
  686. dwRunSize=0;
  687. wShiftRunSize=0;
  688. do
  689. { wNibble=((*lpbSource)>>wShift);
  690. dwRunSize|=(wNibble&0x7)<<wShiftRunSize;
  691. wShiftRunSize+=3;
  692. lpbSource+=(wShift>>2);
  693. dwSize-=(wShift>>2);
  694. wShift=4-wShift;
  695. } while (wNibble&0x8);
  696. // write dwRunSize 0's or 1's starting at dwNumber
  697. if (bExpandingOnes)
  698. { while (dwRunSize--)
  699. {
  700. *(lpbDest+(dwNumber>>3))|=(BYTE)(1<<(dwNumber&0x7));
  701. dwNumber++;
  702. }
  703. }
  704. else
  705. dwNumber+=dwRunSize;
  706. bExpandingOnes=!bExpandingOnes;
  707. }
  708. dwTotalSize=(dwNumber+7)>>3;
  709. }
  710. return dwTotalSize;
  711. }
  712. /*************************************************************************
  713. *
  714. * @doc API INDEX RETRIEVAL
  715. *
  716. * @func PUBLIC int FAR PASCAL | GroupFileBuild |
  717. * This function will save a group to a file. The file
  718. * may be a regular DOS file, or a system .MVB subfile.
  719. *
  720. * @parm HFPB | hfpbSysFile |
  721. * If non-zero, this is the handle of an already opened system file,
  722. * the group file is a FS subfile
  723. * If zero, the file is a regular DOS file
  724. *
  725. * @parm LPSTR | lszGrpFilename |
  726. * Group's filename. It must be non-null
  727. *
  728. * @parm _LPGROUP | lpGroup |
  729. * Pointer to a group. This group may come from GroupLoad(), or
  730. * may be a result of groups' operations
  731. *
  732. * @rdesc S_OK if succeeded, else other error codes
  733. *
  734. *************************************************************************/
  735. PUBLIC int FAR PASCAL EXPORT_API FAR GroupFileBuild
  736. (HFPB hfpbSysFile, LPSTR lszGrpFilename, _LPGROUP lpGroup)
  737. {
  738. HFPB hfpbGroup;
  739. HRESULT fRet = S_OK;
  740. DWORD dwMinItem;
  741. DWORD dwMaxItem;
  742. #if 0
  743. /* UNDONE:
  744. * All the #if 0 stuffs are ifdef out because at this point we don't
  745. * support low end optimization yet
  746. */
  747. LPBYTE lpbStart;
  748. #endif
  749. LPBYTE lpbEnd;
  750. HRESULT hr;
  751. char ScratchBuf [GROUP_HDR_SIZE];
  752. /* Check for error */
  753. if (lszGrpFilename == NULL || lpGroup == NULL ||
  754. lpGroup->fFlag > TRIMMED_GROUP)
  755. return E_INVALIDARG;
  756. if ((hfpbGroup = FileCreate(hfpbSysFile, lszGrpFilename,
  757. hfpbSysFile ? FS_SUBFILE : REGULAR_FILE, &hr)) == 0) {
  758. return hr;
  759. }
  760. // Check for zero length word wheels
  761. if (0 == lpGroup->lcItem)
  762. {
  763. // Free bit vector
  764. if (lpGroup->hGrpBitVect)
  765. {
  766. _GLOBALUNLOCK (lpGroup->hGrpBitVect);
  767. _GLOBALFREE (lpGroup->hGrpBitVect);
  768. lpGroup->hGrpBitVect = 0;
  769. }
  770. // Reset internal variables
  771. lpGroup->lpbGrpBitVect = NULL;
  772. lpGroup->minItem = lpGroup->maxItem = lpGroup->dwSize = 0;
  773. }
  774. /* Do some optimizations for the group data space only if it has not
  775. * been done yet, ie. fFlag == BITVECT_GROUP. The most saving will
  776. * come from changing a bitmap group into a hilo group
  777. */
  778. dwMinItem = lpGroup->minItem;
  779. dwMaxItem = lpGroup->maxItem;
  780. if (lpGroup->fFlag == BITVECT_GROUP) {
  781. if (!lpGroup->lcItem || lpGroup->lcItem == dwMaxItem - dwMinItem + 1)
  782. {
  783. /* This is a hilo group */
  784. lpGroup->fFlag = HILO_GROUP;
  785. lpGroup->dwSize = 0;
  786. }
  787. else
  788. {
  789. /* Change into a TRIMMED_GROUP */
  790. lpbEnd = (LPBYTE)&lpGroup->lpbGrpBitVect [dwMaxItem / 8];
  791. #if 0
  792. lpbStart = (LPBYTE)&lpGroup->lpbGrpBitVect [dwMinItem / 8];
  793. lpGroup->dwSize = (DWORD)(lpbEnd - lpbStart + 1);
  794. #else
  795. lpGroup->dwSize = (DWORD)(lpbEnd - lpGroup->lpbGrpBitVect + 1);
  796. #endif
  797. lpGroup->fFlag = TRIMMED_GROUP;
  798. }
  799. }
  800. lpGroup->FileStamp = GROUP_STAMP;
  801. lpGroup->version = GROUPVER;
  802. /* Nullify the file header space */
  803. MEMSET (ScratchBuf, 0, GROUP_HDR_SIZE);
  804. MEMCPY (ScratchBuf, lpGroup, sizeof(GROUP_HDR));
  805. // Write out header and data (either compressed or normal)
  806. { DWORD dwNewSize=lpGroup->dwSize;
  807. HANDLE hCompressed=NULL;
  808. LPBYTE lpbBitfield=(LPBYTE)lpGroup->lpbGrpBitVect;
  809. if ((lpGroup->dwSize) &&
  810. ((lpGroup->fFlag==BITVECT_GROUP) || (lpGroup->fFlag==TRIMMED_GROUP)))
  811. {
  812. // Special case if group is empty
  813. hCompressed=GroupCompressDelta((LPBYTE)lpGroup->lpbGrpBitVect,&dwNewSize);
  814. if (!hCompressed) // no savings
  815. {
  816. dwNewSize=lpGroup->dwSize;
  817. }
  818. }
  819. if (hCompressed)
  820. {
  821. if (lpGroup->fFlag==BITVECT_GROUP)
  822. ((_LPGROUP)ScratchBuf)->fFlag=DISKCOMP_GROUP;
  823. else
  824. ((_LPGROUP)ScratchBuf)->fFlag=DISKCOMP_TRIMMED_GROUP;
  825. lpbBitfield=(LPBYTE)_GLOBALLOCK(hCompressed);
  826. }
  827. ((_LPGROUP)ScratchBuf)->dwSize=dwNewSize;
  828. if (FileSeekWrite(hfpbGroup, (LPVOID)ScratchBuf, foNil,
  829. GROUP_HDR_SIZE, NULL)==GROUP_HDR_SIZE)
  830. if (dwNewSize)
  831. {
  832. fRet = (DWORD)FileSeekWrite(hfpbGroup,
  833. (LPVOID)lpbBitfield, MakeFo(GROUP_HDR_SIZE,0),
  834. dwNewSize, NULL) == dwNewSize ? S_OK : E_FAIL;
  835. }
  836. else if (lpGroup->fFlag != HILO_GROUP)
  837. {
  838. fRet = E_FAIL;
  839. }
  840. if (hCompressed)
  841. {
  842. _GLOBALUNLOCK(hCompressed);
  843. _GLOBALFREE(hCompressed);
  844. hCompressed=NULL;
  845. }
  846. }
  847. if (FileClose(hfpbGroup) != S_OK)
  848. fRet = E_FAIL;
  849. return fRet;
  850. }
  851. /*************************************************************************
  852. * @doc INTERNAL
  853. *
  854. * @func DWORD FAR PASCAL | LrgbBitCount |
  855. * This function return the number of bits set in a BitVect
  856. *
  857. * @parm LPBYTE | lpbBitVect |
  858. * Pointer to BitVect
  859. *
  860. * @parm DWORD | dwSize |
  861. * Size of BitVect (in term of BYTE)
  862. *************************************************************************/
  863. PUBLIC DWORD FAR PASCAL LrgbBitCount(LPBYTE lpbBitVect, DWORD dwSize)
  864. {
  865. register BYTE bValue; // Value of the current byte
  866. register WORD cwBitOn; // Number of bits set
  867. register DWORD lcTotalBitOn; // Total number of bits set in the bitvector
  868. DWORD size = dwSize;
  869. /* Count how many bits are set. This correspond to the number
  870. of GrpItem in the group
  871. */
  872. lcTotalBitOn = 0;
  873. cwBitOn = 0;
  874. for (; size > 0 ; size--)
  875. {
  876. bValue = *lpbBitVect++; // Get the current word
  877. for (; bValue; cwBitOn++)
  878. bValue &= bValue - 1;
  879. /* Only do an add every 32K to save time */
  880. if (cwBitOn & 0x8000) {
  881. lcTotalBitOn += cwBitOn;
  882. cwBitOn = 0;
  883. }
  884. }
  885. lcTotalBitOn += cwBitOn;
  886. return lcTotalBitOn;
  887. }
  888. /*************************************************************************
  889. * @doc INTERNAL
  890. *
  891. * @func DWORD FAR PASCAL | LrgbBitFind |
  892. * This function returns the position of the specified bit
  893. *
  894. * @parm LPBYTE | lpbBitVect |
  895. * Pointer to bitvector
  896. *
  897. * @parm DWORD | dwCount |
  898. * The bit to count to (0 means first bit)
  899. *************************************************************************/
  900. PUBLIC DWORD FAR PASCAL LrgbBitFind(LPBYTE lpbBitVect, DWORD dwCount, BYTE FAR *pHold)
  901. {
  902. BYTE bValue; // Value of the current byte
  903. BYTE bHold;
  904. LPBYTE lpbBitVectSave = lpbBitVect; // save pointer to beginning
  905. DWORD dwRval;
  906. dwCount++; // switch from 0 based to 1 based
  907. while (dwCount)
  908. {
  909. bValue = *lpbBitVect++; // Get the current byte
  910. for (; bValue && dwCount; dwCount--)
  911. {
  912. bHold = bValue;
  913. bValue &= bValue - 1;
  914. }
  915. }
  916. if (pHold) *pHold = bHold;
  917. dwRval = (DWORD) (((DWORD_PTR)lpbBitVect-(DWORD_PTR)lpbBitVectSave-1)*8);
  918. for (;bHold&&!(bHold&1);bHold>>=1)
  919. {
  920. dwRval++;
  921. dwCount--;
  922. }
  923. return dwRval;
  924. }
  925. /*************************************************************************
  926. * @doc API INDEX RETRIEVAL
  927. *
  928. * @func int PASCAL FAR | GroupTrimmed |
  929. * This function will trim down the size of the group's bit vector
  930. *
  931. * @parm _LPGROUP | lpGroup |
  932. * Pointer to group
  933. *
  934. * @rdesc Return S_OK if a new trimmed group is created,
  935. * E_OUTOFMEMORY in case of out-of-memory
  936. *************************************************************************/
  937. int PASCAL FAR GroupTrimmed (_LPGROUP lpGroup)
  938. {
  939. unsigned int cbSize;
  940. LPBYTE lpbBit;
  941. HANDLE hBitVect;
  942. LONG cItem;
  943. if (lpGroup == NULL)
  944. return E_INVALIDARG;
  945. if (lpGroup->fFlag == TRIMMED_GROUP ||
  946. (cbSize = (unsigned int)lpGroup->dwSize) == 0)
  947. {
  948. // reset group cache
  949. lpGroup->nCache = 0;
  950. return S_OK;
  951. }
  952. cItem = (LONG)(cbSize - 1) * 8;
  953. /* Truncate all 0's bytes at the high end of the bit vector */
  954. lpbBit = lpGroup->lpbGrpBitVect + cbSize - 1;
  955. while (cbSize > 0 && *lpbBit == 0)
  956. {
  957. cbSize --;
  958. lpbBit--;
  959. cItem -= 8;
  960. }
  961. if (cbSize == 0)
  962. {
  963. /* This is an empty group */
  964. lpGroup->dwSize = lpGroup->lcItem = 0;
  965. lpGroup->maxItem = lpGroup->minItem = 0;
  966. /* Release the memory block */
  967. _GLOBALUNLOCK (hBitVect = lpGroup->hGrpBitVect);
  968. _GLOBALFREE (hBitVect);
  969. lpGroup->hGrpBitVect = 0;
  970. lpGroup->lpbGrpBitVect = NULL;
  971. return S_OK;
  972. }
  973. /* Reset maxItem */
  974. lpGroup->maxItem = cItem + HiBitSet (*lpbBit);
  975. /* Reset minItem */
  976. cItem = -1;
  977. lpbBit = lpGroup->lpbGrpBitVect;
  978. while (*lpbBit == 0)
  979. {
  980. lpbBit++;
  981. cItem += 8;
  982. }
  983. //assert (*lpbBit);
  984. lpGroup->minItem = cItem + LoBitSet (*lpbBit) + 1;
  985. _GLOBALUNLOCK (hBitVect =lpGroup->hGrpBitVect);
  986. /* Reallocate the size of the bitvector */
  987. if ((lpGroup->hGrpBitVect =
  988. _GLOBALREALLOC (hBitVect, (DWORD) cbSize, GMEM_MOVEABLE)) == NULL)
  989. {
  990. lpGroup->lpbGrpBitVect = NULL;
  991. return E_OUTOFMEMORY;
  992. }
  993. /* Update pointer to the new bit vector */
  994. lpGroup->lpbGrpBitVect = _GLOBALLOCK(lpGroup->hGrpBitVect);
  995. lpGroup->lcItem = LrgbBitCount(lpGroup->lpbGrpBitVect, cbSize);
  996. lpGroup->fFlag = TRIMMED_GROUP;
  997. lpGroup->dwSize = cbSize;
  998. lpGroup->nCache = 0;
  999. return S_OK;
  1000. }
  1001. /*************************************************************************
  1002. * @doc API INDEX RETRIEVAL
  1003. *
  1004. * @func int PASCAL FAR | GroupMake |
  1005. * Creates a group from a bitvector.
  1006. *
  1007. * @parm LPBYTE | lpBits |
  1008. * Pointer to bitfield
  1009. * @parm DWORD | dwSize |
  1010. * Number of bytes in bitfield.
  1011. * @parm DWORD | dwItems |
  1012. * Number of items (if not exactly dwSize*8). If dwItems==0, dwSize*8
  1013. * will be used.
  1014. *
  1015. * @rdesc Return S_OK if a new trimmed group is created,
  1016. * E_OUTOFMEMORY in case of out-of-memory
  1017. *************************************************************************/
  1018. _LPGROUP PASCAL FAR GroupMake (LPBYTE lpBits, DWORD dwSize, DWORD dwItems)
  1019. {
  1020. _LPGROUP lpGroup;
  1021. ERRB err;
  1022. if (dwItems==0) dwItems = dwSize*8;
  1023. if ((lpGroup = GroupCreate(dwSize, dwItems, &err)) == NULL)
  1024. return NULL;
  1025. MEMCPY(lpGroup->lpbGrpBitVect,lpBits,dwSize);
  1026. GroupTrimmed(lpGroup);
  1027. return lpGroup; // mv20c version had this return ERR_SUCCESS ?
  1028. }
  1029. /*************************************************************************
  1030. * @doc API RETRIEVAL
  1031. *
  1032. * @func DWORD FAR PASCAL | GroupFind |
  1033. * Given a pointer to a group and a count to count from the first
  1034. * topic number of the group (dwCount), this function return the
  1035. * topic number of the nth (dwCount) item of the list (counting from 0),
  1036. * or -1 if not found
  1037. *
  1038. * @parm LPGROUP | lpGroup |
  1039. * Pointer to the group
  1040. *
  1041. * @parm DWORD | dwCount |
  1042. * The index count in to the group. Count is 0-based
  1043. *
  1044. * @parm PHRESULT | phr |
  1045. * Pointer to error buffer
  1046. *
  1047. * @rdesc The topic number, or -1 if not found or other errors. In case
  1048. * of error, phr will contain the error code
  1049. *
  1050. *************************************************************************/
  1051. PUBLIC DWORD EXPORT_API FAR PASCAL GroupFind(_LPGROUP lpGroup,
  1052. DWORD dwCount, PHRESULT phr)
  1053. {
  1054. DWORD dwRes;
  1055. BYTE bHold;
  1056. if (lpGroup == NULL || dwCount>=((_LPGROUP)lpGroup)->lcItem)
  1057. {
  1058. SetErrCode (phr, E_INVALIDARG);
  1059. return ((DWORD)-1);
  1060. }
  1061. if (phr)
  1062. *phr = S_OK;
  1063. if (lpGroup->nCache && dwCount > lpGroup->dwCount)
  1064. {
  1065. dwRes = (DWORD)LrgbBitFind(lpGroup->lpbGrpBitVect+lpGroup->nCache, dwCount - lpGroup->dwCount,&bHold);
  1066. if (dwRes!=(DWORD)-1)
  1067. dwRes += ((DWORD)lpGroup->nCache)*8;
  1068. }
  1069. else
  1070. dwRes = (DWORD)LrgbBitFind(lpGroup->lpbGrpBitVect, dwCount, &bHold);
  1071. if (dwRes!=(DWORD)-1)
  1072. {
  1073. BYTE bValue;
  1074. // save this latest position.
  1075. lpGroup->nCache = (UINT)(dwRes/8);
  1076. lpGroup->dwCount = dwCount;
  1077. bValue = *(lpGroup->lpbGrpBitVect + lpGroup->nCache);
  1078. while (bValue && (bValue != bHold))
  1079. {
  1080. bValue &= bValue - 1;
  1081. lpGroup->dwCount--;
  1082. }
  1083. }
  1084. return dwRes;
  1085. }
  1086. /*************************************************************************
  1087. * @doc API RETRIEVAL
  1088. *
  1089. * @func DWORD FAR PASCAL | GroupFindOffset |
  1090. * Given a pointer to a group and a topic number,
  1091. * this function return the position of the item in the
  1092. * group that has "dwTopicNum" as a topic number or -1 if error.
  1093. * This is the counter-API of GroupFind().
  1094. *
  1095. * @parm LPGROUP | lpGroup |
  1096. * Pointer to the group
  1097. *
  1098. * @parm DWORD | dwTopicNum |
  1099. * The index count in to the group. Count is 0-based
  1100. *
  1101. * @parm PHRESULT | phr |
  1102. * Pointer to error buffer
  1103. *
  1104. * @rdesc The position of the item in the group. Will return -1 if an
  1105. * error occured. If the dwTopicNum is not part of the group, the
  1106. * error flag will be set to ERR_NOTEXIST and the function will return
  1107. * the closest UID less than dwTopicNum. In case of error, phr will
  1108. * contain the error code
  1109. *
  1110. *************************************************************************/
  1111. PUBLIC DWORD EXPORT_API FAR PASCAL GroupFindOffset(_LPGROUP lpGroup,
  1112. DWORD dwTopicNum, PHRESULT phr)
  1113. {
  1114. DWORD dwRes, dwByteNum = (++dwTopicNum)/8;
  1115. if (lpGroup == NULL || dwTopicNum > lpGroup->maxItemAllGroup)
  1116. {
  1117. SetErrCode (phr, E_INVALIDARG);
  1118. return ((DWORD)-1);
  1119. }
  1120. // If the UID isn't in the group, ERR_NOTEXIT will be set, but we still continue the process
  1121. // to get the nearest entry. This is done for WordWheelPrefix().
  1122. if (phr)
  1123. *phr = GroupIsBitSet(lpGroup, dwTopicNum - (DWORD)1) ? S_OK : E_NOTEXIST;
  1124. if (lpGroup->nCache && dwTopicNum > ((DWORD)lpGroup->nCache) * 8)
  1125. {
  1126. dwRes = (DWORD)LrgbBitCount(lpGroup->lpbGrpBitVect+lpGroup->nCache, dwByteNum - lpGroup->nCache);
  1127. if (dwRes!=(DWORD)-1)
  1128. dwRes += lpGroup->dwCount;
  1129. }
  1130. else
  1131. dwRes = (DWORD)LrgbBitCount(lpGroup->lpbGrpBitVect, dwByteNum);
  1132. if (dwRes!=(DWORD)-1)
  1133. {
  1134. BYTE bShift = (BYTE) (dwTopicNum%8);
  1135. BYTE bValue = *(lpGroup->lpbGrpBitVect + dwByteNum);
  1136. BYTE bMask = 0xFF;
  1137. // save this latest position.
  1138. lpGroup->nCache = (UINT)(dwTopicNum/8);
  1139. lpGroup->dwCount = dwRes;
  1140. if (bShift)
  1141. {
  1142. bMask >>= 8 - bShift;
  1143. bValue &= bMask;
  1144. dwRes += (DWORD) LrgbBitCount(&bValue, 1);
  1145. }
  1146. }
  1147. else
  1148. return dwRes; // Error.
  1149. return dwRes - 1; // Let's keep it zero-based.
  1150. }
  1151. /*************************************************************************
  1152. * @doc RETRIEVAL
  1153. *
  1154. * @func LPGROUP FAR PASCAL | GroupOr |
  1155. * The function will generate a new group resulting from the ORing
  1156. * of two groups
  1157. * @parm LPGROUP | lpGroup1 |
  1158. * Pointer to first group
  1159. *
  1160. * @parm LPGROUP | lpGroup2 |
  1161. * Pointer to second group
  1162. *
  1163. * @parm PHRESULT | lperr |
  1164. * Pointer to error buffer
  1165. *
  1166. * @rdesc If succeeded, the function will return a pointer the newly
  1167. * created group. The error buffer has the information about the
  1168. * cause of the failure
  1169. *************************************************************************/
  1170. PUBLIC _LPGROUP EXPORT_API FAR PASCAL GroupOr(_LPGROUP lpGroup1,
  1171. _LPGROUP lpGroup2, PHRESULT lperr)
  1172. {
  1173. _LPGROUP lpResGroup;
  1174. LPBYTE lpbBitVect2; // Pointer to Group bitmap 2
  1175. LPBYTE lpbResBitVect;// Pointer to result Group bitmap
  1176. register DWORD i; // Counter
  1177. /* Check for groups validity, and create a new one */
  1178. if ((lpResGroup = GroupCheckAndCreate(lpGroup1, lpGroup2, lperr)) == NULL)
  1179. return NULL;
  1180. /* Initialize variables */
  1181. lpbBitVect2 = lpGroup2->lpbGrpBitVect;
  1182. lpbResBitVect = lpResGroup->lpbGrpBitVect;
  1183. /* Copy Group 1's bit vector */
  1184. MEMCPY (lpbResBitVect, lpGroup1->lpbGrpBitVect, (UINT)lpGroup1->dwSize);
  1185. /* Do the operation */
  1186. for (i = lpGroup2->dwSize; i > 0; i--)
  1187. {
  1188. *lpbResBitVect++ |= *lpbBitVect2++ ;
  1189. }
  1190. if (GroupTrimmed (lpResGroup) != S_OK)
  1191. {
  1192. GroupFree (lpResGroup);
  1193. lpResGroup = NULL;
  1194. }
  1195. return lpResGroup;
  1196. }
  1197. /*************************************************************************
  1198. * @doc RETRIEVAL
  1199. *
  1200. * @func LPGROUP FAR PASCAL | GroupNot |
  1201. * The function will generate a new group resulting from the NOTing
  1202. * of the given group
  1203. *
  1204. * @parm LPGROUP | lpGroup |
  1205. * Pointer to first group
  1206. *
  1207. * @parm PHRESULT | lperr |
  1208. * Pointer to error buffer
  1209. *
  1210. * @rdesc If succeeded, the function will return a pointer the newly
  1211. * created group. The error buffer has the information about the
  1212. * cause of the fialure
  1213. *************************************************************************/
  1214. PUBLIC _LPGROUP EXPORT_API FAR PASCAL GroupNot(_LPGROUP lpGroup,
  1215. PHRESULT lperr)
  1216. {
  1217. _LPGROUP lpResGroup;
  1218. LPBYTE lpbBitVect; // Pointer to Group bitmap 1
  1219. LPBYTE lpbResBitVect; // Pointer to result Group bitmap 1
  1220. DWORD dwSize; // minimum size;
  1221. register DWORD i; // Counter
  1222. ERR fRet;
  1223. DWORD dwMaxItemAllGroup;
  1224. /* Check for groups validity, and create a new one */
  1225. if ((fRet = GroupCheck (lpGroup)) != S_OK)
  1226. {
  1227. SetErrCode(lperr, fRet);
  1228. return NULL;
  1229. }
  1230. /*****************************************************
  1231. *
  1232. * THERE ARE SOME COMPLICATIONS FOR GROUPNOT THAT WE
  1233. * HAVE TO CONSIDER:
  1234. * - GROUPNOT SHOULD INCLUDE ALL THE ITEM THE GROUPS
  1235. * SET.
  1236. * - WHEN DOING THE NOT, ALL ITEMS > MAXITEM SHOULD
  1237. * BE RESET TO ZERO, SINCE THEY ARE OUTSIDE OF THE
  1238. * UNIVERSE.
  1239. *
  1240. *****************************************************/
  1241. if (lpGroup->maxItemAllGroup==0)
  1242. return GroupDuplicate (lpGroup, lperr);
  1243. dwMaxItemAllGroup = lpGroup->maxItemAllGroup;
  1244. if ((lpResGroup = GroupCreate(((dwMaxItemAllGroup + 7) / 8),dwMaxItemAllGroup, lperr)) == NULL)
  1245. return NULL;
  1246. /* Initialize variables */
  1247. lpbBitVect = lpGroup->lpbGrpBitVect;
  1248. lpbResBitVect = lpResGroup->lpbGrpBitVect;
  1249. /* Do the operation */
  1250. dwSize = min(lpGroup->dwSize,lpResGroup->dwSize);
  1251. for (i = dwSize; i > 0; i--)
  1252. {
  1253. *lpbResBitVect++ = ~*lpbBitVect ;
  1254. lpbBitVect++;
  1255. }
  1256. /* Set all the remaining bits to 1. Note that after the operations
  1257. * all bits that followed the dwMaxItemAllGroup's bit are set. They
  1258. * should be dealt with properly
  1259. */
  1260. if (i = lpResGroup->dwSize - dwSize)
  1261. MEMSET (lpbResBitVect, 0xff, i);
  1262. /********************************************************
  1263. *
  1264. * THE NEXT STEP IS TO RESET ALL THE BITS OUTSIDE OF THE
  1265. * LIMITS TO 0'S
  1266. *
  1267. ********************************************************/
  1268. // GarrG. 12-14-94. Subtracted one to really point to the last byte.
  1269. if (lpGroup->maxItemAllGroup % 8)
  1270. {
  1271. lpbResBitVect += i-1; // Move to the last byte
  1272. i = 1 << (lpGroup->maxItemAllGroup % 8);
  1273. // maxItemAllGroup is actually number, not the maximum index.
  1274. // so I removed i <<= 1; -GarrG
  1275. while (i <= 0x80)
  1276. {
  1277. *lpbResBitVect &= ~i;
  1278. i <<= 1;
  1279. }
  1280. }
  1281. if ((fRet = GroupTrimmed (lpResGroup)) != S_OK)
  1282. {
  1283. SetErrCode (lperr, fRet);
  1284. GroupFree (lpResGroup);
  1285. lpResGroup = NULL;
  1286. }
  1287. return lpResGroup;
  1288. }
  1289. /*************************************************************************
  1290. * @doc RETRIEVAL
  1291. *
  1292. * @func LPGROUP FAR PASCAL | GroupAnd |
  1293. * The function will generate a new group resulting from the ANDing
  1294. * of two groups
  1295. *
  1296. * @parm LPGROUP | lpGroup1 |
  1297. * Pointer to first group
  1298. *
  1299. * @parm LPGROUP | lpGroup2 |
  1300. * Pointer to second group
  1301. *
  1302. * @parm PHRESULT | lperr |
  1303. * Pointer to error buffer
  1304. *
  1305. * @rdesc If succeeded, the function will return a pointer the newly
  1306. * created group. The error buffer has the information about the
  1307. * cause of the fialure
  1308. *************************************************************************/
  1309. PUBLIC _LPGROUP EXPORT_API FAR PASCAL GroupAnd(_LPGROUP lpGroup1,
  1310. _LPGROUP lpGroup2, PHRESULT lperr)
  1311. {
  1312. _LPGROUP lpResGroup;
  1313. LPBYTE lpbBitVect1; // Pointer to Group bitmap 1
  1314. LPBYTE lpbBitVect2; // Pointer to Group bitmap 2
  1315. LPBYTE lpbResBitVect; // Pointer to result Group bitmap
  1316. DWORD i; // Counter
  1317. DWORD dwMinOverlapTopic;
  1318. DWORD dwMaxOverlapTopic;
  1319. ERR fRet;
  1320. /* Check for groups validity, and create a new one */
  1321. if ((lpResGroup = GroupCheckAndCreate(lpGroup1, lpGroup2, lperr)) == NULL)
  1322. return NULL;
  1323. if (lpGroup1->lcItem && lpGroup2->lcItem)
  1324. {
  1325. /* Only do a GroupAND for non empty group */
  1326. /* Get the overlap */
  1327. if ((dwMinOverlapTopic = lpGroup1->minItem) < lpGroup2->minItem)
  1328. dwMinOverlapTopic = lpGroup2->minItem;
  1329. if ((dwMaxOverlapTopic = lpGroup1->maxItem) > lpGroup2->maxItem)
  1330. dwMaxOverlapTopic = lpGroup2->maxItem;
  1331. if (dwMinOverlapTopic <= dwMaxOverlapTopic)
  1332. {
  1333. /* Change to bytes */
  1334. dwMinOverlapTopic /= 8;
  1335. dwMaxOverlapTopic /= 8;
  1336. /* Initialize variables */
  1337. lpbBitVect1 = &lpGroup1->lpbGrpBitVect [dwMinOverlapTopic];
  1338. lpbBitVect2 = &lpGroup2->lpbGrpBitVect [dwMinOverlapTopic];
  1339. lpbResBitVect = &lpResGroup->lpbGrpBitVect [dwMinOverlapTopic];
  1340. for (i = dwMaxOverlapTopic - dwMinOverlapTopic + 1;
  1341. i > 0; i--)
  1342. {
  1343. *lpbResBitVect = *lpbBitVect1 & *lpbBitVect2;
  1344. lpbResBitVect++;
  1345. lpbBitVect1++;
  1346. lpbBitVect2++;
  1347. }
  1348. }
  1349. }
  1350. if ((fRet = GroupTrimmed (lpResGroup)) != S_OK)
  1351. {
  1352. SetErrCode (lperr, fRet);
  1353. GroupFree (lpResGroup);
  1354. lpResGroup = NULL;
  1355. }
  1356. return lpResGroup;
  1357. }
  1358. /*************************************************************************
  1359. * @doc API RETRIEVAL
  1360. *
  1361. * @func _LPGROUP | GroupDuplicate |
  1362. * This fucntion creates a copy for the specified group
  1363. *
  1364. * @parm _LPGROUP | lpGroup|
  1365. * Pointer to group to be duplicated
  1366. *
  1367. * @parm PHRESULT | lperr |
  1368. * A pointer to an error buffer, which will receive the error
  1369. * code in case that the function fails
  1370. *
  1371. * @rdesc Return a new copy of group if succeeded, NULL if failed
  1372. *************************************************************************/
  1373. PUBLIC _LPGROUP PASCAL FAR GroupDuplicate (_LPGROUP lpGroup,
  1374. PHRESULT lperr)
  1375. {
  1376. _LPGROUP lpDupGroup;
  1377. /* Safety check */
  1378. if (lpGroup == NULL)
  1379. {
  1380. SetErrCode (lperr, E_INVALIDARG);
  1381. return NULL;
  1382. }
  1383. /* Create the group */
  1384. if ((lpDupGroup = GroupCreate (lpGroup->dwSize, lpGroup->maxItemAllGroup,
  1385. lperr)) == NULL)
  1386. return NULL;
  1387. /* Copy the information over */
  1388. *(GROUP_HDR FAR *)lpDupGroup = *(GROUP_HDR FAR *)lpGroup;
  1389. /* Check for empty group */
  1390. if (lpGroup->hGrpBitVect)
  1391. {
  1392. MEMCPY (lpDupGroup->lpbGrpBitVect, lpGroup->lpbGrpBitVect,
  1393. lpGroup->dwSize);
  1394. }
  1395. else
  1396. {
  1397. _GLOBALUNLOCK (lpDupGroup->hGrpBitVect);
  1398. _GLOBALFREE (lpDupGroup->hGrpBitVect);
  1399. lpDupGroup->hGrpBitVect = 0;
  1400. lpDupGroup->lpbGrpBitVect = NULL;
  1401. }
  1402. return lpDupGroup;
  1403. }
  1404. /*************************************************************************
  1405. * @doc API RETRIEVAL
  1406. *
  1407. * @func ERR _LPGROUP PASCAL FAR | GroupCopy |
  1408. * This fucntion copies the bitfield data of one group to another
  1409. *
  1410. * @parm _LPGROUP | lpGroupDest|
  1411. * Pointer to destination group
  1412. *
  1413. * @parm _LPGROUP | lpGroupSrc|
  1414. * Pointer to source group
  1415. *
  1416. * @rdesc Returns S_OK if succeeded, an error otherwise
  1417. *************************************************************************/
  1418. PUBLIC ERR PASCAL FAR GroupCopy (_LPGROUP lpGroupDst,
  1419. _LPGROUP lpGroupSrc)
  1420. {
  1421. ERR err=S_OK;
  1422. HANDLE hNewGroupMem=NULL;
  1423. // Safety check
  1424. if ((lpGroupSrc == NULL) || (lpGroupDst==NULL))
  1425. {
  1426. return E_INVALIDARG;
  1427. }
  1428. if (lpGroupSrc->hGrpBitVect) // Source group is NOT empty
  1429. {
  1430. if ((NULL == lpGroupDst->hGrpBitVect) || (NULL == lpGroupDst->lpbGrpBitVect)
  1431. || (lpGroupDst->dwSize!=lpGroupSrc->dwSize))
  1432. {
  1433. if ((hNewGroupMem = _GLOBALALLOC(DLLGMEM_ZEROINIT,
  1434. lpGroupSrc->dwSize)) == NULL)
  1435. return E_OUTOFMEMORY;
  1436. }
  1437. // Copy the information over
  1438. *(GROUP_HDR FAR *)lpGroupDst = *(GROUP_HDR FAR *)lpGroupSrc;
  1439. // Remove old info from Destination group and create new if
  1440. // differing sizes
  1441. if (hNewGroupMem)
  1442. {
  1443. if (NULL != lpGroupDst->hGrpBitVect)
  1444. {
  1445. _GLOBALUNLOCK(lpGroupDst->hGrpBitVect);
  1446. _GLOBALFREE(lpGroupDst->hGrpBitVect);
  1447. }
  1448. lpGroupDst->hGrpBitVect = hNewGroupMem;
  1449. lpGroupDst->lpbGrpBitVect = (LPBYTE)_GLOBALLOCK(hNewGroupMem);
  1450. }
  1451. // Copy actual bits
  1452. MEMCPY (lpGroupDst->lpbGrpBitVect, lpGroupSrc->lpbGrpBitVect,
  1453. lpGroupSrc->dwSize);
  1454. }
  1455. else
  1456. {
  1457. // Group is empty, make destination empty
  1458. *(GROUP_HDR FAR *)lpGroupDst = *(GROUP_HDR FAR *)lpGroupSrc;
  1459. // Remove old info from Destination group
  1460. if (lpGroupDst->hGrpBitVect)
  1461. {
  1462. _GLOBALUNLOCK(lpGroupDst->hGrpBitVect);
  1463. _GLOBALFREE(lpGroupDst->hGrpBitVect);
  1464. lpGroupDst->hGrpBitVect=NULL;
  1465. lpGroupDst->lpbGrpBitVect = NULL;
  1466. }
  1467. }
  1468. return err;
  1469. }
  1470. /*************************************************************************
  1471. * @doc INTERNAL
  1472. *
  1473. * @func LPGROUP NEAR PASCAL | GroupCheckAndCreate |
  1474. * Given 2 groups, this function will check their validity, and if
  1475. * they are valid, create a new group
  1476. *
  1477. * @parm LPGROUP | lpGroup1 |
  1478. * Pointer to group 1
  1479. *
  1480. * @parm LPGROUP | lpGroup2 |
  1481. * Pointer to group 2
  1482. *
  1483. * @parm PHRESULT | lperr |
  1484. * Pointer to error buffer
  1485. *
  1486. * @rdesc The function will return a pointer to a newly created group if
  1487. * succeeded, NULL otherwise. The error buffer contains information
  1488. * about the cause of the failure
  1489. *************************************************************************/
  1490. static _LPGROUP NEAR PASCAL GroupCheckAndCreate(_LPGROUP lpGroup1,
  1491. _LPGROUP lpGroup2, PHRESULT lperr)
  1492. {
  1493. DWORD dwSize;
  1494. DWORD maxItemAllGroup;
  1495. /* Check the validity of the groups */
  1496. if (GroupCheck(lpGroup1) != S_OK || GroupCheck(lpGroup2) != S_OK)
  1497. {
  1498. SetErrCode(lperr, E_BADVERSION);
  1499. return NULL;
  1500. }
  1501. if ((dwSize = lpGroup1->dwSize) < lpGroup2->dwSize)
  1502. {
  1503. dwSize = lpGroup2->dwSize;
  1504. }
  1505. if ((maxItemAllGroup = lpGroup1->maxItemAllGroup) <
  1506. lpGroup2->maxItemAllGroup)
  1507. {
  1508. maxItemAllGroup = lpGroup2->maxItemAllGroup;
  1509. }
  1510. /* Create a new Group */
  1511. return (GroupCreate(dwSize, maxItemAllGroup, lperr));
  1512. }
  1513. /*************************************************************************
  1514. * @doc EXTERNAL API
  1515. *
  1516. * @func _LPGROUP PASCAL FAR | GroupBufferCreate |
  1517. * This function will create group from a buffer
  1518. *
  1519. * @parm HANDLE | h |
  1520. * Handle to memory buffer containig raw group file data
  1521. *
  1522. * @parm PHRESULT | phr |
  1523. * Pointer to error buffer
  1524. *
  1525. * @rdesc If succeeded, the function will return a pointer to the loaded
  1526. * group, else NULL. The error buffer will contain information about
  1527. * the cause of the failure
  1528. *************************************************************************/
  1529. _LPGROUP PASCAL FAR GroupBufferCreate (HANDLE h, PHRESULT phr)
  1530. {
  1531. GROUP_HDR FAR *lpGroupHdr;
  1532. GROUP_HDR GroupHdr;
  1533. ERR fRet;
  1534. _LPGROUP lpGroup = NULL;
  1535. char cBitSet; // This must be signed !!!
  1536. LPBYTE lpGroupBitVect;
  1537. DWORD dwStartByte;
  1538. DWORD dwVectorSize;
  1539. DWORD dwCurMaxTopic;
  1540. DWORD dwBytes;
  1541. LPBYTE lp=NULL;
  1542. lpGroupHdr = &GroupHdr;
  1543. lp = (LPBYTE)_GLOBALLOCK(h);
  1544. dwBytes = (DWORD) GlobalSize(h);
  1545. fRet = E_BADFILE;
  1546. if (dwBytes < sizeof(GROUP_HDR))
  1547. {
  1548. goto exit00;
  1549. }
  1550. MEMCPY (lpGroupHdr, lp, sizeof(GROUP_HDR));
  1551. /* BigEndian codes. They will optimized out under Windows */
  1552. lpGroupHdr->FileStamp = SWAPWORD(lpGroupHdr->FileStamp);
  1553. lpGroupHdr->version = SWAPWORD(lpGroupHdr->version);
  1554. lpGroupHdr->dwSize = SWAPLONG(lpGroupHdr->dwSize);
  1555. lpGroupHdr->maxItem = SWAPLONG(lpGroupHdr->maxItem);
  1556. lpGroupHdr->minItem = SWAPLONG(lpGroupHdr->minItem);
  1557. lpGroupHdr->lcItem = SWAPLONG(lpGroupHdr->lcItem);
  1558. lpGroupHdr->maxItemAllGroup = SWAPLONG(lpGroupHdr->maxItemAllGroup);
  1559. lpGroupHdr->fFlag = SWAPWORD(lpGroupHdr->fFlag);
  1560. /* Set maxItemAllgroup and fFlag properly, since those fields
  1561. * didn't exist before
  1562. */
  1563. if (lpGroupHdr->version < 9)
  1564. {
  1565. lpGroupHdr->maxItemAllGroup = lpGroupHdr->dwSize * 8;
  1566. lpGroupHdr->fFlag = BITVECT_GROUP;
  1567. }
  1568. /* Check to see if the data read in is valid */
  1569. if (GroupCheck((_LPGROUP)lpGroupHdr) != S_OK)
  1570. goto exit00;
  1571. if (lpGroupHdr->dwSize == 0 && lpGroupHdr->maxItem)
  1572. lpGroupHdr->dwSize = lpGroupHdr->maxItem / 8 + 1;
  1573. /* Get the vector size. This is a shorthand version */
  1574. dwVectorSize = lpGroupHdr->dwSize;
  1575. /* Create a new group. It is assuming that GroupCreate() will
  1576. * allocate enough memory to store all the data
  1577. */
  1578. if ((lpGroup = GroupCreate( (lpGroupHdr->maxItemAllGroup+7)>>3, //dwVectorSize,
  1579. lpGroupHdr->maxItemAllGroup, phr)) == NULL)
  1580. {
  1581. fRet = E_OUTOFMEMORY;
  1582. goto exit00;
  1583. }
  1584. /* Copy the group header information */
  1585. *(GROUP_HDR FAR *)lpGroup = *lpGroupHdr;
  1586. if (lpGroup->fFlag == HILO_GROUP && lpGroup->minItem && lpGroup->maxItem)
  1587. {
  1588. /* The work now is to set all the bits to 1. It is broken into 3 parts:
  1589. * - The beginning byte : which may not have all bit set
  1590. * - The ending byte : which may not have all bit set
  1591. * - Every byte between the above two: all bits are set
  1592. */
  1593. dwStartByte = (lpGroup->minItem / 8);
  1594. lpGroupBitVect = lpGroup->lpbGrpBitVect + dwStartByte;
  1595. dwCurMaxTopic = dwStartByte * 8;
  1596. /* Set the beginning byte */
  1597. cBitSet = (char)(lpGroup->minItem - dwStartByte * 8);
  1598. while (cBitSet < 8)
  1599. {
  1600. *lpGroupBitVect |= 1 << cBitSet;
  1601. cBitSet ++;
  1602. // a-kevct: (MV1.3 #27) Changed test from >= to > condition
  1603. if (dwCurMaxTopic + cBitSet > lpGroup->maxItem)
  1604. { lpGroup->fFlag = TRIMMED_GROUP;
  1605. goto DoneGroup;
  1606. }
  1607. }
  1608. /* Set the body */
  1609. MEMSET (lpGroupBitVect + 1, 0xff,
  1610. dwVectorSize - dwStartByte - 2);
  1611. /* Set the ending byte */
  1612. lpGroupBitVect = lpGroup->lpbGrpBitVect + dwVectorSize - 1;
  1613. cBitSet = (int)(lpGroup->maxItem - (lpGroup->maxItem / 8) * 8);
  1614. /* Note that in the following calculation, we end at 0. Suppose
  1615. * that maxItem = 8, then the 1st bit of the second byte must be set
  1616. */
  1617. while (cBitSet >= 0)
  1618. {
  1619. *lpGroupBitVect |= 1 << cBitSet;
  1620. cBitSet --;
  1621. }
  1622. lpGroup->fFlag = TRIMMED_GROUP;
  1623. }
  1624. else
  1625. {
  1626. /* Seek to position from start of file */
  1627. lp += GROUP_HDR_SIZE;
  1628. if (dwBytes < (DWORD)(GROUP_HDR_SIZE+dwVectorSize))
  1629. goto exit00;
  1630. if (lpGroup->fFlag==DISKCOMP_GROUP)
  1631. { DWORD dwNewSize=GroupDecompressDelta((LPBYTE)lpGroup->lpbGrpBitVect,lp,dwVectorSize);
  1632. lpGroup->fFlag=BITVECT_GROUP;
  1633. dwVectorSize=lpGroup->dwSize=(lpGroupHdr->maxItemAllGroup+7)>>3;
  1634. }
  1635. else if (lpGroup->fFlag==DISKCOMP_TRIMMED_GROUP)
  1636. { DWORD dwNewSize=GroupDecompressDelta((LPBYTE)lpGroup->lpbGrpBitVect,lp,dwVectorSize);
  1637. lpGroup->fFlag=TRIMMED_GROUP;
  1638. dwVectorSize=lpGroup->dwSize=(lpGroupHdr->maxItemAllGroup+7)>>3;
  1639. }
  1640. else
  1641. MEMCPY ((LPBYTE)lpGroup->lpbGrpBitVect,lp,dwVectorSize);
  1642. /* This piece of code is to support old version of the
  1643. * group format. It can be deleted after everybody has converted to the
  1644. * new format
  1645. */
  1646. if (lpGroup->version < 9)
  1647. {
  1648. if ((fRet = GroupTrimmed (lpGroup)) != S_OK)
  1649. goto exit00;
  1650. dwVectorSize = lpGroup->dwSize;
  1651. }
  1652. }
  1653. DoneGroup:
  1654. #if 0
  1655. if (lpGroup->lcItem != LrgbBitCount(lpGroup->lpbGrpBitVect,
  1656. dwVectorSize))
  1657. goto exit00;
  1658. #else
  1659. lpGroup->lcItem = LrgbBitCount(lpGroup->lpbGrpBitVect,
  1660. dwVectorSize);
  1661. #endif
  1662. fRet = S_OK;
  1663. exit00:
  1664. /* Close the subfile */
  1665. /* handle is allocated normally, so free normally */
  1666. if (lp) _GLOBALUNLOCK(h);
  1667. if (fRet == S_OK)
  1668. return lpGroup;
  1669. SetErrCode (phr, fRet);
  1670. if (lpGroup)
  1671. GroupFree (lpGroup);
  1672. return NULL;
  1673. }
  1674. /*************************************************************************
  1675. * @doc API RETRIEVAL
  1676. *
  1677. * @func DWORD FAR PASCAL | GroupIsBitSet |
  1678. * Given a pointer to a group and a topic number in the group,
  1679. * this function will return TRUE if the bit is set or FALSE if
  1680. * the bit is not set.
  1681. *
  1682. * @parm LPGROUP | lpGroup |
  1683. * Pointer to the group
  1684. *
  1685. * @parm DWORD | dwTopicNum |
  1686. * The index count in to the group. dwTopicNum is 0-based
  1687. *
  1688. * @rdesc TRUE if the bit indecated by dwTopicNum is set or
  1689. * FALSE if the bit is not set.
  1690. *
  1691. *************************************************************************/
  1692. PUBLIC BOOL EXPORT_API FAR PASCAL GroupIsBitSet
  1693. (_LPGROUP lpGroup, DWORD dwTopicNum)
  1694. {
  1695. if (lpGroup == NULL
  1696. || lpGroup->lcItem == 0
  1697. || lpGroup->minItem > dwTopicNum
  1698. || lpGroup->maxItem < dwTopicNum)
  1699. {
  1700. return (FALSE);
  1701. }
  1702. return GROUPISBITSET (lpGroup, dwTopicNum);
  1703. }