/************************************************************************* * * * GROUPCOM.C * * * * Copyright (C) Microsoft Corporation 1990-1994 * * All Rights reserved. * * * ************************************************************************** * * * Module Intent * * This module contains miscellaneous functions that are shared * * between index and search. Those modules can be related to stop * * words, groups, word wheels, catalogs, etc. The purpose is to * * share as much code as possible * * * ************************************************************************** * * * Current Owner: GarrG * * * ************************************************************************** * * * Released by Development: (date) * * * *************************************************************************/ #include SETVERSIONSTAMP(MVUT); #include #include #include #include #include #include #include #include #include <_mvutil.h> // File System #ifdef _DEBUG static char s_aszModule[] = __FILE__; // Used by error return functions. #endif #define cbitWORD (CBIT)16 // Number of bits in a word. #define cbitBYTE (CBIT)8 // Number of bits in a byte. #define SETERR(a,b) (*a=b) /************************************************************************* * * INTERNAL PRIVATE FUNCTIONS * All of them should be declared near *************************************************************************/ static _LPGROUP NEAR PASCAL GroupCheckAndCreate(_LPGROUP, _LPGROUP, PHRESULT); static BOOL NEAR PASCAL GroupCheck (_LPGROUP lpGroup); static int PASCAL NEAR HiBitSet (BYTE c) { register int cBit = 7; while ((c & 128) == 0) { c <<= 1; cBit--; } return cBit; } static int PASCAL NEAR LoBitSet (BYTE c) { register int cBit = 0; while ((c & 1) == 0) { c >>= 1; cBit++; } return cBit; } /************************************************************************* * @doc INTERNAL * * @func BOOL NEAR PASCAL | GroupCheck | * This function will check the validity of a given group * * @parm _LPGROUP | lpGroup | * Pointer to group * * @rdesc Return S_OK if valid group, fail otherwize *************************************************************************/ static BOOL NEAR PASCAL GroupCheck (_LPGROUP lpGroup) { // Changing to a MACRO // if (lpGroup == NULL || // (lpGroup->version < 7 || lpGroup->version > GROUPVER) || // lpGroup->FileStamp != GROUP_STAMP) // return ERR_FAILED; // return S_OK; VALIDATE_GROUP(lpGroup); } /************************************************************************* * @doc API INDEX RETRIEVAL * * @func LPGROUP FAR PASCAL | GroupInitiate | * This function creates and initializes a new group. The size of the * group is based on the total number of items * * @parm DWORD | lcGrpItem | * The maximum number of items in the group. If lcGrpItem is equal * to ITGROUPMAX, then the size of the group will grow * as necessary to fit the GrpItem (up to that limit) * * @parm PHRESULT | phr | * Error buffer * * @rdesc The function will return a pointer to the newly created group * if succeeded, NULL otherwise. The error buffer will contain * information about the cause of the failure * *************************************************************************/ PUBLIC _LPGROUP FAR PASCAL GroupInitiate(DWORD lcGrpItem, PHRESULT phr) { _LPGROUP lpGroup; DWORD size; BYTE fGroupExpandable; /* Check to see if ther are too many GrpItem or not */ if (lcGrpItem > LCBITGROUPMAX) { SetErrCode(phr, E_GROUPIDTOOBIG); return NULL; } if (fGroupExpandable = (BYTE)(lcGrpItem == LCBITGROUPMAX)) { /* Expandable bitvector, start with one block */ size = GROUP_BLOCK_SIZE; } else { size = ((lcGrpItem + cbitBYTE - 1) / cbitBYTE) * sizeof(BYTE); } if ((lpGroup = GroupCreate(size, lcGrpItem, phr)) == NULL) return NULL; if (fGroupExpandable) { lpGroup->wFlag |= GROUP_EXPAND; lpGroup->maxItem = 0; } else lpGroup->maxItem = lcGrpItem; lpGroup->minItem = LCBITGROUPMAX; return lpGroup; } /************************************************************************* * @doc API INDEX RETRIEVAL * * @func ERR FAR PASCAL | GroupAddItem | * This function will add a group item number into the given group * * @parm LPGROUP | lpGroup | * Pointer to group * * @parm DWORD | dwGrpItem | * Group Item to be added into the group. The value must be * 0 << dwGrpItem < 524280 * * @rdesc S_OK if succeeded. The function can fail if the GrpItem * value is too large, ie. exceed the maximum value preset in * lpGroup when calling GroupInitiate() * * @xref GroupInitiate() *************************************************************************/ PUBLIC ERR FAR PASCAL GroupAddItem(_LPGROUP lpGroup, DWORD dwGrpItem) { HANDLE hBitVect; DWORD size; BYTE bitSet; LPBYTE lpb; if (lpGroup == NULL) /* Safeguard check */ return E_INVALIDARG; // Bad argument if (dwGrpItem > LCBITGROUPMAX) return E_GROUPIDTOOBIG; if (dwGrpItem >= lpGroup->dwSize * cbitBYTE) { if ((lpGroup->wFlag & GROUP_EXPAND) == 0) return E_GROUPIDTOOBIG; /* The BitVect needs to grow. Calculate the next needed size */ size = ((dwGrpItem / (cbitBYTE * GROUP_BLOCK_SIZE)) + 1) * GROUP_BLOCK_SIZE; if (lpGroup->hGrpBitVect) { _GLOBALUNLOCK(lpGroup->hGrpBitVect); if ((hBitVect = lpGroup->hGrpBitVect = _GLOBALREALLOC(lpGroup->hGrpBitVect, size, DLLGMEM_ZEROINIT)) == NULL) return E_OUTOFMEMORY; } else { if ((hBitVect = lpGroup->hGrpBitVect = _GLOBALALLOC(DLLGMEM_ZEROINIT,size))==NULL) return E_OUTOFMEMORY; } lpGroup->lpbGrpBitVect = (LPBYTE)_GLOBALLOCK(hBitVect); lpGroup->dwSize = size; } if (lpGroup->maxItemAllGroup <= dwGrpItem) lpGroup->maxItemAllGroup = dwGrpItem+1; if (dwGrpItem > lpGroup->maxItem) lpGroup->maxItem = dwGrpItem; if (dwGrpItem < lpGroup->minItem) lpGroup->minItem = dwGrpItem; /* Set the bit */ lpb = &lpGroup->lpbGrpBitVect[(UINT)(dwGrpItem / 8)]; bitSet = 1 << (dwGrpItem % 8); if ((*lpb & bitSet) == 0) { *lpb |= bitSet; lpGroup->lcItem ++; } lpGroup->nCache = 0; return S_OK; } /************************************************************************* * * @doc API INDEX RETRIEVAL * * @func ERR FAR PASCAL | GroupRemoveItem | * This function will remove a group item number from the given group * * @parm LPGROUP | lpGroup | * Pointer to group. Must be non-null * * @parm DWORD | dwGrpItem | * Group Item to be removed from group. * * @rdesc S_OK if succeeded. The function can fail for bad argument * (lpGroup == NULL) * * @xref GroupInitiate() *************************************************************************/ PUBLIC ERR FAR PASCAL GroupRemoveItem(_LPGROUP lpGroup, DWORD dwGrpItem) { LPBYTE lpb; BYTE bitSet; if (lpGroup == NULL) /* Safeguard check */ return E_INVALIDARG; // Bad argument if (dwGrpItem < lpGroup->minItem || dwGrpItem > lpGroup->maxItem) return S_OK; /* Unset the bit */ lpb = &lpGroup->lpbGrpBitVect[(UINT)(dwGrpItem / 8)]; bitSet = (1 << (dwGrpItem % 8)); /* Only update the bitvector and the item count if that item exists */ if (*lpb & bitSet) { *lpb &= ~bitSet; lpGroup->lcItem--; } lpGroup->nCache = 0; return S_OK; } /************************************************************************* * @doc API INDEX RETRIEVAL * * @func LPGROUP FAR PASCAL | GroupCreate | * This function will create a new group according to the * group size * * @parm DWORD | size | * Size of group. In case the size is unknown, it should be set to be * (dwMaxItemAllGroup / 8) * * @parm DWORD | dwMaxItemAllGroup | * Maximum group item value that a set of group can have * * @parm PHRESULT | phr | * Error buffer * * @rdesc The function will return a pointer to the newly created group * if succeeded, NULL otherwise. The error buffer will contain * information about the cause of the failure *************************************************************************/ PUBLIC _LPGROUP FAR PASCAL GroupCreate (DWORD size, DWORD dwMaxItemAllGroup, PHRESULT phr) { HANDLE handle; _LPGROUP lpGroup; /* Allocate structure */ if ((handle = _GLOBALALLOC(DLLGMEM_ZEROINIT, sizeof(_GROUP))) == NULL) { SetErrCode(phr, E_OUTOFMEMORY); return NULL; } lpGroup = (_LPGROUP)_GLOBALLOCK(handle); lpGroup->dwSize = size; lpGroup->maxItemAllGroup = dwMaxItemAllGroup; lpGroup->hGroup = handle; lpGroup->FileStamp = GROUP_STAMP; lpGroup->version = GROUPVER; /* Allocate group BitVect */ // +1 is added as a bug fix since in GroupFileBuild it sometimes writes out // an extra byte that has not been allocated. This is the "safe" fix. // For post 2.0 we should remove this and do a real fix in GroupFileBuild if ((lpGroup->hGrpBitVect = handle = _GLOBALALLOC(GMEM_FIXED | GMEM_ZEROINIT, size+1)) == NULL) // WinNT4.0 seemed to mishandle moveable memory here // when this code was called through a COM wrapper and proxy layer. // Very bizarre (a-ericry, thoroughly discussed with kevynct, 3 Apr 97) { /* Out of memory. Free the allocated structure */ _GLOBALUNLOCK(lpGroup->hGroup); _GLOBALFREE(lpGroup->hGroup); SetErrCode (phr, E_OUTOFMEMORY); return NULL; } lpGroup->lpbGrpBitVect = (LPBYTE)_GLOBALLOCK(handle); lpGroup->lperr = phr; return lpGroup; } /************************************************************************* * @doc API INDEX RETRIEVAL * * @func VOID FAR PASCAL | GroupFree | * This function will free all memory associated with a group * * @parm LPGROUP | lpGroup | * Pointer to group to be freed *************************************************************************/ PUBLIC VOID FAR PASCAL GroupFree(_LPGROUP lpGroup) { if (lpGroup == NULL) return; /* Free the memory */ if (lpGroup->hGrpBitVect) { _GLOBALUNLOCK(lpGroup->hGrpBitVect); _GLOBALFREE(lpGroup->hGrpBitVect); } _GLOBALUNLOCK(lpGroup->hGroup); _GLOBALFREE(lpGroup->hGroup); } // Saves the deltas between samples in high-bit 'nibble' compression. // If the high bit of low nibble not set, value is 0-7, else // value is 8-63 unless high nibble set, etc. // // [B x x x A x x x] [D x x x C x x x]... // #define COMPRESS_DELTA 0 #define COMPRESS_DELTA_INV 1 // The Group RLE method should work by saving the lengths of runs of // consecutive 1's and runs of 0's. So 00001111111001111 would be // saved as the values 4,7,2,4 in the above nibble format. Great // savings can be made if the bits are in large groups. #define COMPRESS_GROUPRLE 2 // We have room for 13 more types of compression if needed! HANDLE NEAR PASCAL GroupCompressDelta(LPBYTE lpb, DWORD * pdwSize) { DWORD dwSize; DWORD dwNewSize=1; LPBYTE lpbCursor; DWORD dwBits; DWORD dwNumber; DWORD dwLastNumber; DWORD dwDelta; HANDLE hMem=NULL; LPBYTE lpbMem=NULL; WORD wShift=0; DWORD dwPartialDelta; WORD wCompressMode=COMPRESS_DELTA; DWORD dwSmallestSize; BOOL bCountingOnes=FALSE; // Used in GROUPRLE DWORD dwTotalCount=0; // Used in GROUPRLE // See how much space we're going to take first for the various // compression schemes // if no scheme works, no need alloc mem! // COMPRESS_DELTA dwNumber=(DWORD)-1; dwLastNumber=0; dwSize=*pdwSize; lpbCursor=lpb; dwBits=0; while (dwSize--) { WORD b; WORD wBit=0; b=(WORD)*lpbCursor; while (b) { if (b&1) { // add in bit number dwBits+wBit dwLastNumber=dwNumber; dwNumber=dwBits+wBit; dwDelta=dwNumber-dwLastNumber; do { dwPartialDelta=(dwDelta&0x7); if (dwDelta&0xfffffff8) dwPartialDelta|=0x8; dwNewSize++; dwDelta>>=3; } while (dwPartialDelta&0x8); } b>>=1; wBit++; } lpbCursor++; dwBits+=8; } dwNewSize=(dwNewSize+1)/2; dwSmallestSize=dwNewSize; // COMPRESS_DELTA_INV dwNewSize=1; // 1 for the compression code nibble dwNumber=(DWORD)-1; dwLastNumber=0; dwSize=*pdwSize; lpbCursor=lpb; dwBits=0; while (dwSize--) { WORD b; WORD wBit=0; b=(WORD)(~(*lpbCursor))&0xff; while (b) { if (b&1) { // add in bit number dwBits+wBit dwLastNumber=dwNumber; dwNumber=dwBits+wBit; dwDelta=dwNumber-dwLastNumber; do { dwPartialDelta=(dwDelta&0x7); if (dwDelta&0xfffffff8) dwPartialDelta|=0x8; dwNewSize++; dwDelta>>=3; } while (dwPartialDelta&0x8); } b>>=1; wBit++; } lpbCursor++; dwBits+=8; if ((dwNewSize+1)/2 > dwSmallestSize) break; } dwNewSize=(dwNewSize+1)/2; if (dwNewSize>=3; } while (dwPartialDelta&0x8); bCountingOnes=TRUE; dwTotalCount=1; } } else { if (!bCountingOnes) dwTotalCount++; else { // we were counting ones, output count of 1's now do { dwPartialDelta=(dwTotalCount&0x7); if (dwTotalCount&0xfffffff8) dwPartialDelta|=0x8; dwNewSize++; dwTotalCount>>=3; } while (dwPartialDelta&0x8); bCountingOnes=FALSE; dwTotalCount=1; } } b>>=1; wBit++; } lpbCursor++; if ((dwNewSize+1)/2 > dwSmallestSize) break; } dwNewSize=(dwNewSize+1)/2; if (dwNewSize=*pdwSize) { *pdwSize=dwNewSize; return NULL; } hMem=_GLOBALALLOC(DLLGMEM_ZEROINIT,dwNewSize); lpbMem=(LPBYTE)_GLOBALLOCK(hMem); // Write compression code type nibble *lpbMem|=(BYTE)(wCompressMode&0xf); wShift=4; if ((wCompressMode==COMPRESS_DELTA) || (wCompressMode==COMPRESS_DELTA_INV)) { dwNumber=(DWORD)-1; dwLastNumber=0; dwSize=*pdwSize; lpbCursor=lpb; dwBits=0; dwNewSize=1; while (dwSize--) { WORD b; WORD wBit=0; b=(WORD)*lpbCursor; if (wCompressMode==COMPRESS_DELTA_INV) b=(~b)&0xff; while (b) { if (b&1) { // add in bit number dwBits+wBit dwLastNumber=dwNumber; dwNumber=dwBits+wBit; dwDelta=dwNumber-dwLastNumber; // We could subt 1, but end case won't detect // if last nibble not filled. do { dwPartialDelta=(dwDelta&0x7); if (dwDelta&0xfffffff8) dwPartialDelta|=0x8; *lpbMem|=(BYTE)(dwPartialDelta<>2); wShift=4-wShift; dwNewSize++; dwDelta>>=3; } while (dwPartialDelta&0x8); } b>>=1; wBit++; } lpbCursor++; dwBits+=8; } dwNewSize=(dwNewSize+1)/2; } else if (wCompressMode==COMPRESS_GROUPRLE) { bCountingOnes=FALSE; dwTotalCount=0; dwNewSize=1; // 1 for the compression code nibble dwNumber=(DWORD)-1; dwLastNumber=0; dwSize=*pdwSize; lpbCursor=lpb; while (dwSize--) { WORD b; WORD wBit=0; b=(WORD)*lpbCursor; while (wBit<8) { if (b&1) { if (bCountingOnes) dwTotalCount++; else // we were counting 0's, output zero count now { do { dwPartialDelta=(dwTotalCount&0x7); if (dwTotalCount&0xfffffff8) dwPartialDelta|=0x8; *lpbMem|=(BYTE)(dwPartialDelta<>2); wShift=4-wShift; dwNewSize++; dwTotalCount>>=3; } while (dwPartialDelta&0x8); bCountingOnes=TRUE; dwTotalCount=1; } } else { if (!bCountingOnes) dwTotalCount++; else { // we were counting ones, output count of 1's now do { dwPartialDelta=(dwTotalCount&0x7); if (dwTotalCount&0xfffffff8) dwPartialDelta|=0x8; *lpbMem|=(BYTE)(dwPartialDelta<>2); wShift=4-wShift; dwNewSize++; dwTotalCount>>=3; } while (dwPartialDelta&0x8); bCountingOnes=FALSE; dwTotalCount=1; } } b>>=1; wBit++; } lpbCursor++; } // Catch tail end if 1's at end (fixes bug848) if ((bCountingOnes) && (dwTotalCount)) { do { dwPartialDelta=(dwTotalCount&0x7); if (dwTotalCount&0xfffffff8) dwPartialDelta|=0x8; *lpbMem|=(BYTE)(dwPartialDelta<>2); wShift=4-wShift; dwNewSize++; dwTotalCount>>=3; } while (dwPartialDelta&0x8); } dwNewSize=(dwNewSize+1)/2; } *pdwSize=dwNewSize; _GLOBALUNLOCK (hMem); return hMem; } DWORD NEAR PASCAL GroupDecompressDelta(LPBYTE lpbDest, LPBYTE lpbSource, DWORD dwSize) { DWORD dwNewSize=0; LPBYTE lpbCursor=lpbSource; WORD wShift=0; WORD wNibble; WORD wCompressMode=0; DWORD dwTotalSize; DWORD dwNumber=(DWORD)-1; wCompressMode=(WORD)((*lpbSource)&0xf); wShift=4; if ((wCompressMode==COMPRESS_DELTA) || (wCompressMode==COMPRESS_DELTA_INV)) { DWORD dwDelta; WORD wShiftDelta=0; DWORD dwLastNumber; while (dwSize) { // Get a delta dwDelta=0; wShiftDelta=0; do { wNibble=((*lpbSource)>>wShift); dwDelta|=(wNibble&0x7)<>2); dwSize-=(wShift>>2); wShift=4-wShift; } while (wNibble&0x8); dwLastNumber=dwNumber; dwNumber=dwLastNumber+dwDelta; // Set the dwNumber bit if (dwNumber!=(DWORD)-1) *(lpbDest+(dwNumber>>3))|=(BYTE)(1<<(dwNumber&0x7)); } dwTotalSize=(dwNumber+7)>>3; if (wCompressMode==COMPRESS_DELTA_INV) // invert entire group { DWORD dwCt=dwTotalSize; BYTE * lpb=lpbDest; while (dwCt--) { *lpb=~*lpb; lpb++; } } } else if (wCompressMode==COMPRESS_GROUPRLE) { DWORD dwRunSize; WORD wShiftRunSize=0; BOOL bExpandingOnes=0; dwNumber=0; while (dwSize) { // Get a delta dwRunSize=0; wShiftRunSize=0; do { wNibble=((*lpbSource)>>wShift); dwRunSize|=(wNibble&0x7)<>2); dwSize-=(wShift>>2); wShift=4-wShift; } while (wNibble&0x8); // write dwRunSize 0's or 1's starting at dwNumber if (bExpandingOnes) { while (dwRunSize--) { *(lpbDest+(dwNumber>>3))|=(BYTE)(1<<(dwNumber&0x7)); dwNumber++; } } else dwNumber+=dwRunSize; bExpandingOnes=!bExpandingOnes; } dwTotalSize=(dwNumber+7)>>3; } return dwTotalSize; } /************************************************************************* * * @doc API INDEX RETRIEVAL * * @func PUBLIC int FAR PASCAL | GroupFileBuild | * This function will save a group to a file. The file * may be a regular DOS file, or a system .MVB subfile. * * @parm HFPB | hfpbSysFile | * If non-zero, this is the handle of an already opened system file, * the group file is a FS subfile * If zero, the file is a regular DOS file * * @parm LPSTR | lszGrpFilename | * Group's filename. It must be non-null * * @parm _LPGROUP | lpGroup | * Pointer to a group. This group may come from GroupLoad(), or * may be a result of groups' operations * * @rdesc S_OK if succeeded, else other error codes * *************************************************************************/ PUBLIC int FAR PASCAL EXPORT_API FAR GroupFileBuild (HFPB hfpbSysFile, LPSTR lszGrpFilename, _LPGROUP lpGroup) { HFPB hfpbGroup; HRESULT fRet = S_OK; DWORD dwMinItem; DWORD dwMaxItem; #if 0 /* UNDONE: * All the #if 0 stuffs are ifdef out because at this point we don't * support low end optimization yet */ LPBYTE lpbStart; #endif LPBYTE lpbEnd; HRESULT hr; char ScratchBuf [GROUP_HDR_SIZE]; /* Check for error */ if (lszGrpFilename == NULL || lpGroup == NULL || lpGroup->fFlag > TRIMMED_GROUP) return E_INVALIDARG; if ((hfpbGroup = FileCreate(hfpbSysFile, lszGrpFilename, hfpbSysFile ? FS_SUBFILE : REGULAR_FILE, &hr)) == 0) { return hr; } // Check for zero length word wheels if (0 == lpGroup->lcItem) { // Free bit vector if (lpGroup->hGrpBitVect) { _GLOBALUNLOCK (lpGroup->hGrpBitVect); _GLOBALFREE (lpGroup->hGrpBitVect); lpGroup->hGrpBitVect = 0; } // Reset internal variables lpGroup->lpbGrpBitVect = NULL; lpGroup->minItem = lpGroup->maxItem = lpGroup->dwSize = 0; } /* Do some optimizations for the group data space only if it has not * been done yet, ie. fFlag == BITVECT_GROUP. The most saving will * come from changing a bitmap group into a hilo group */ dwMinItem = lpGroup->minItem; dwMaxItem = lpGroup->maxItem; if (lpGroup->fFlag == BITVECT_GROUP) { if (!lpGroup->lcItem || lpGroup->lcItem == dwMaxItem - dwMinItem + 1) { /* This is a hilo group */ lpGroup->fFlag = HILO_GROUP; lpGroup->dwSize = 0; } else { /* Change into a TRIMMED_GROUP */ lpbEnd = (LPBYTE)&lpGroup->lpbGrpBitVect [dwMaxItem / 8]; #if 0 lpbStart = (LPBYTE)&lpGroup->lpbGrpBitVect [dwMinItem / 8]; lpGroup->dwSize = (DWORD)(lpbEnd - lpbStart + 1); #else lpGroup->dwSize = (DWORD)(lpbEnd - lpGroup->lpbGrpBitVect + 1); #endif lpGroup->fFlag = TRIMMED_GROUP; } } lpGroup->FileStamp = GROUP_STAMP; lpGroup->version = GROUPVER; /* Nullify the file header space */ MEMSET (ScratchBuf, 0, GROUP_HDR_SIZE); MEMCPY (ScratchBuf, lpGroup, sizeof(GROUP_HDR)); // Write out header and data (either compressed or normal) { DWORD dwNewSize=lpGroup->dwSize; HANDLE hCompressed=NULL; LPBYTE lpbBitfield=(LPBYTE)lpGroup->lpbGrpBitVect; if ((lpGroup->dwSize) && ((lpGroup->fFlag==BITVECT_GROUP) || (lpGroup->fFlag==TRIMMED_GROUP))) { // Special case if group is empty hCompressed=GroupCompressDelta((LPBYTE)lpGroup->lpbGrpBitVect,&dwNewSize); if (!hCompressed) // no savings { dwNewSize=lpGroup->dwSize; } } if (hCompressed) { if (lpGroup->fFlag==BITVECT_GROUP) ((_LPGROUP)ScratchBuf)->fFlag=DISKCOMP_GROUP; else ((_LPGROUP)ScratchBuf)->fFlag=DISKCOMP_TRIMMED_GROUP; lpbBitfield=(LPBYTE)_GLOBALLOCK(hCompressed); } ((_LPGROUP)ScratchBuf)->dwSize=dwNewSize; if (FileSeekWrite(hfpbGroup, (LPVOID)ScratchBuf, foNil, GROUP_HDR_SIZE, NULL)==GROUP_HDR_SIZE) if (dwNewSize) { fRet = (DWORD)FileSeekWrite(hfpbGroup, (LPVOID)lpbBitfield, MakeFo(GROUP_HDR_SIZE,0), dwNewSize, NULL) == dwNewSize ? S_OK : E_FAIL; } else if (lpGroup->fFlag != HILO_GROUP) { fRet = E_FAIL; } if (hCompressed) { _GLOBALUNLOCK(hCompressed); _GLOBALFREE(hCompressed); hCompressed=NULL; } } if (FileClose(hfpbGroup) != S_OK) fRet = E_FAIL; return fRet; } /************************************************************************* * @doc INTERNAL * * @func DWORD FAR PASCAL | LrgbBitCount | * This function return the number of bits set in a BitVect * * @parm LPBYTE | lpbBitVect | * Pointer to BitVect * * @parm DWORD | dwSize | * Size of BitVect (in term of BYTE) *************************************************************************/ PUBLIC DWORD FAR PASCAL LrgbBitCount(LPBYTE lpbBitVect, DWORD dwSize) { register BYTE bValue; // Value of the current byte register WORD cwBitOn; // Number of bits set register DWORD lcTotalBitOn; // Total number of bits set in the bitvector DWORD size = dwSize; /* Count how many bits are set. This correspond to the number of GrpItem in the group */ lcTotalBitOn = 0; cwBitOn = 0; for (; size > 0 ; size--) { bValue = *lpbBitVect++; // Get the current word for (; bValue; cwBitOn++) bValue &= bValue - 1; /* Only do an add every 32K to save time */ if (cwBitOn & 0x8000) { lcTotalBitOn += cwBitOn; cwBitOn = 0; } } lcTotalBitOn += cwBitOn; return lcTotalBitOn; } /************************************************************************* * @doc INTERNAL * * @func DWORD FAR PASCAL | LrgbBitFind | * This function returns the position of the specified bit * * @parm LPBYTE | lpbBitVect | * Pointer to bitvector * * @parm DWORD | dwCount | * The bit to count to (0 means first bit) *************************************************************************/ PUBLIC DWORD FAR PASCAL LrgbBitFind(LPBYTE lpbBitVect, DWORD dwCount, BYTE FAR *pHold) { BYTE bValue; // Value of the current byte BYTE bHold; LPBYTE lpbBitVectSave = lpbBitVect; // save pointer to beginning DWORD dwRval; dwCount++; // switch from 0 based to 1 based while (dwCount) { bValue = *lpbBitVect++; // Get the current byte for (; bValue && dwCount; dwCount--) { bHold = bValue; bValue &= bValue - 1; } } if (pHold) *pHold = bHold; dwRval = (DWORD) (((DWORD_PTR)lpbBitVect-(DWORD_PTR)lpbBitVectSave-1)*8); for (;bHold&&!(bHold&1);bHold>>=1) { dwRval++; dwCount--; } return dwRval; } /************************************************************************* * @doc API INDEX RETRIEVAL * * @func int PASCAL FAR | GroupTrimmed | * This function will trim down the size of the group's bit vector * * @parm _LPGROUP | lpGroup | * Pointer to group * * @rdesc Return S_OK if a new trimmed group is created, * E_OUTOFMEMORY in case of out-of-memory *************************************************************************/ int PASCAL FAR GroupTrimmed (_LPGROUP lpGroup) { unsigned int cbSize; LPBYTE lpbBit; HANDLE hBitVect; LONG cItem; if (lpGroup == NULL) return E_INVALIDARG; if (lpGroup->fFlag == TRIMMED_GROUP || (cbSize = (unsigned int)lpGroup->dwSize) == 0) { // reset group cache lpGroup->nCache = 0; return S_OK; } cItem = (LONG)(cbSize - 1) * 8; /* Truncate all 0's bytes at the high end of the bit vector */ lpbBit = lpGroup->lpbGrpBitVect + cbSize - 1; while (cbSize > 0 && *lpbBit == 0) { cbSize --; lpbBit--; cItem -= 8; } if (cbSize == 0) { /* This is an empty group */ lpGroup->dwSize = lpGroup->lcItem = 0; lpGroup->maxItem = lpGroup->minItem = 0; /* Release the memory block */ _GLOBALUNLOCK (hBitVect = lpGroup->hGrpBitVect); _GLOBALFREE (hBitVect); lpGroup->hGrpBitVect = 0; lpGroup->lpbGrpBitVect = NULL; return S_OK; } /* Reset maxItem */ lpGroup->maxItem = cItem + HiBitSet (*lpbBit); /* Reset minItem */ cItem = -1; lpbBit = lpGroup->lpbGrpBitVect; while (*lpbBit == 0) { lpbBit++; cItem += 8; } //assert (*lpbBit); lpGroup->minItem = cItem + LoBitSet (*lpbBit) + 1; _GLOBALUNLOCK (hBitVect =lpGroup->hGrpBitVect); /* Reallocate the size of the bitvector */ if ((lpGroup->hGrpBitVect = _GLOBALREALLOC (hBitVect, (DWORD) cbSize, GMEM_MOVEABLE)) == NULL) { lpGroup->lpbGrpBitVect = NULL; return E_OUTOFMEMORY; } /* Update pointer to the new bit vector */ lpGroup->lpbGrpBitVect = _GLOBALLOCK(lpGroup->hGrpBitVect); lpGroup->lcItem = LrgbBitCount(lpGroup->lpbGrpBitVect, cbSize); lpGroup->fFlag = TRIMMED_GROUP; lpGroup->dwSize = cbSize; lpGroup->nCache = 0; return S_OK; } /************************************************************************* * @doc API INDEX RETRIEVAL * * @func int PASCAL FAR | GroupMake | * Creates a group from a bitvector. * * @parm LPBYTE | lpBits | * Pointer to bitfield * @parm DWORD | dwSize | * Number of bytes in bitfield. * @parm DWORD | dwItems | * Number of items (if not exactly dwSize*8). If dwItems==0, dwSize*8 * will be used. * * @rdesc Return S_OK if a new trimmed group is created, * E_OUTOFMEMORY in case of out-of-memory *************************************************************************/ _LPGROUP PASCAL FAR GroupMake (LPBYTE lpBits, DWORD dwSize, DWORD dwItems) { _LPGROUP lpGroup; ERRB err; if (dwItems==0) dwItems = dwSize*8; if ((lpGroup = GroupCreate(dwSize, dwItems, &err)) == NULL) return NULL; MEMCPY(lpGroup->lpbGrpBitVect,lpBits,dwSize); GroupTrimmed(lpGroup); return lpGroup; // mv20c version had this return ERR_SUCCESS ? } /************************************************************************* * @doc API RETRIEVAL * * @func DWORD FAR PASCAL | GroupFind | * Given a pointer to a group and a count to count from the first * topic number of the group (dwCount), this function return the * topic number of the nth (dwCount) item of the list (counting from 0), * or -1 if not found * * @parm LPGROUP | lpGroup | * Pointer to the group * * @parm DWORD | dwCount | * The index count in to the group. Count is 0-based * * @parm PHRESULT | phr | * Pointer to error buffer * * @rdesc The topic number, or -1 if not found or other errors. In case * of error, phr will contain the error code * *************************************************************************/ PUBLIC DWORD EXPORT_API FAR PASCAL GroupFind(_LPGROUP lpGroup, DWORD dwCount, PHRESULT phr) { DWORD dwRes; BYTE bHold; if (lpGroup == NULL || dwCount>=((_LPGROUP)lpGroup)->lcItem) { SetErrCode (phr, E_INVALIDARG); return ((DWORD)-1); } if (phr) *phr = S_OK; if (lpGroup->nCache && dwCount > lpGroup->dwCount) { dwRes = (DWORD)LrgbBitFind(lpGroup->lpbGrpBitVect+lpGroup->nCache, dwCount - lpGroup->dwCount,&bHold); if (dwRes!=(DWORD)-1) dwRes += ((DWORD)lpGroup->nCache)*8; } else dwRes = (DWORD)LrgbBitFind(lpGroup->lpbGrpBitVect, dwCount, &bHold); if (dwRes!=(DWORD)-1) { BYTE bValue; // save this latest position. lpGroup->nCache = (UINT)(dwRes/8); lpGroup->dwCount = dwCount; bValue = *(lpGroup->lpbGrpBitVect + lpGroup->nCache); while (bValue && (bValue != bHold)) { bValue &= bValue - 1; lpGroup->dwCount--; } } return dwRes; } /************************************************************************* * @doc API RETRIEVAL * * @func DWORD FAR PASCAL | GroupFindOffset | * Given a pointer to a group and a topic number, * this function return the position of the item in the * group that has "dwTopicNum" as a topic number or -1 if error. * This is the counter-API of GroupFind(). * * @parm LPGROUP | lpGroup | * Pointer to the group * * @parm DWORD | dwTopicNum | * The index count in to the group. Count is 0-based * * @parm PHRESULT | phr | * Pointer to error buffer * * @rdesc The position of the item in the group. Will return -1 if an * error occured. If the dwTopicNum is not part of the group, the * error flag will be set to ERR_NOTEXIST and the function will return * the closest UID less than dwTopicNum. In case of error, phr will * contain the error code * *************************************************************************/ PUBLIC DWORD EXPORT_API FAR PASCAL GroupFindOffset(_LPGROUP lpGroup, DWORD dwTopicNum, PHRESULT phr) { DWORD dwRes, dwByteNum = (++dwTopicNum)/8; if (lpGroup == NULL || dwTopicNum > lpGroup->maxItemAllGroup) { SetErrCode (phr, E_INVALIDARG); return ((DWORD)-1); } // If the UID isn't in the group, ERR_NOTEXIT will be set, but we still continue the process // to get the nearest entry. This is done for WordWheelPrefix(). if (phr) *phr = GroupIsBitSet(lpGroup, dwTopicNum - (DWORD)1) ? S_OK : E_NOTEXIST; if (lpGroup->nCache && dwTopicNum > ((DWORD)lpGroup->nCache) * 8) { dwRes = (DWORD)LrgbBitCount(lpGroup->lpbGrpBitVect+lpGroup->nCache, dwByteNum - lpGroup->nCache); if (dwRes!=(DWORD)-1) dwRes += lpGroup->dwCount; } else dwRes = (DWORD)LrgbBitCount(lpGroup->lpbGrpBitVect, dwByteNum); if (dwRes!=(DWORD)-1) { BYTE bShift = (BYTE) (dwTopicNum%8); BYTE bValue = *(lpGroup->lpbGrpBitVect + dwByteNum); BYTE bMask = 0xFF; // save this latest position. lpGroup->nCache = (UINT)(dwTopicNum/8); lpGroup->dwCount = dwRes; if (bShift) { bMask >>= 8 - bShift; bValue &= bMask; dwRes += (DWORD) LrgbBitCount(&bValue, 1); } } else return dwRes; // Error. return dwRes - 1; // Let's keep it zero-based. } /************************************************************************* * @doc RETRIEVAL * * @func LPGROUP FAR PASCAL | GroupOr | * The function will generate a new group resulting from the ORing * of two groups * @parm LPGROUP | lpGroup1 | * Pointer to first group * * @parm LPGROUP | lpGroup2 | * Pointer to second group * * @parm PHRESULT | lperr | * Pointer to error buffer * * @rdesc If succeeded, the function will return a pointer the newly * created group. The error buffer has the information about the * cause of the failure *************************************************************************/ PUBLIC _LPGROUP EXPORT_API FAR PASCAL GroupOr(_LPGROUP lpGroup1, _LPGROUP lpGroup2, PHRESULT lperr) { _LPGROUP lpResGroup; LPBYTE lpbBitVect2; // Pointer to Group bitmap 2 LPBYTE lpbResBitVect;// Pointer to result Group bitmap register DWORD i; // Counter /* Check for groups validity, and create a new one */ if ((lpResGroup = GroupCheckAndCreate(lpGroup1, lpGroup2, lperr)) == NULL) return NULL; /* Initialize variables */ lpbBitVect2 = lpGroup2->lpbGrpBitVect; lpbResBitVect = lpResGroup->lpbGrpBitVect; /* Copy Group 1's bit vector */ MEMCPY (lpbResBitVect, lpGroup1->lpbGrpBitVect, (UINT)lpGroup1->dwSize); /* Do the operation */ for (i = lpGroup2->dwSize; i > 0; i--) { *lpbResBitVect++ |= *lpbBitVect2++ ; } if (GroupTrimmed (lpResGroup) != S_OK) { GroupFree (lpResGroup); lpResGroup = NULL; } return lpResGroup; } /************************************************************************* * @doc RETRIEVAL * * @func LPGROUP FAR PASCAL | GroupNot | * The function will generate a new group resulting from the NOTing * of the given group * * @parm LPGROUP | lpGroup | * Pointer to first group * * @parm PHRESULT | lperr | * Pointer to error buffer * * @rdesc If succeeded, the function will return a pointer the newly * created group. The error buffer has the information about the * cause of the fialure *************************************************************************/ PUBLIC _LPGROUP EXPORT_API FAR PASCAL GroupNot(_LPGROUP lpGroup, PHRESULT lperr) { _LPGROUP lpResGroup; LPBYTE lpbBitVect; // Pointer to Group bitmap 1 LPBYTE lpbResBitVect; // Pointer to result Group bitmap 1 DWORD dwSize; // minimum size; register DWORD i; // Counter ERR fRet; DWORD dwMaxItemAllGroup; /* Check for groups validity, and create a new one */ if ((fRet = GroupCheck (lpGroup)) != S_OK) { SetErrCode(lperr, fRet); return NULL; } /***************************************************** * * THERE ARE SOME COMPLICATIONS FOR GROUPNOT THAT WE * HAVE TO CONSIDER: * - GROUPNOT SHOULD INCLUDE ALL THE ITEM THE GROUPS * SET. * - WHEN DOING THE NOT, ALL ITEMS > MAXITEM SHOULD * BE RESET TO ZERO, SINCE THEY ARE OUTSIDE OF THE * UNIVERSE. * *****************************************************/ if (lpGroup->maxItemAllGroup==0) return GroupDuplicate (lpGroup, lperr); dwMaxItemAllGroup = lpGroup->maxItemAllGroup; if ((lpResGroup = GroupCreate(((dwMaxItemAllGroup + 7) / 8),dwMaxItemAllGroup, lperr)) == NULL) return NULL; /* Initialize variables */ lpbBitVect = lpGroup->lpbGrpBitVect; lpbResBitVect = lpResGroup->lpbGrpBitVect; /* Do the operation */ dwSize = min(lpGroup->dwSize,lpResGroup->dwSize); for (i = dwSize; i > 0; i--) { *lpbResBitVect++ = ~*lpbBitVect ; lpbBitVect++; } /* Set all the remaining bits to 1. Note that after the operations * all bits that followed the dwMaxItemAllGroup's bit are set. They * should be dealt with properly */ if (i = lpResGroup->dwSize - dwSize) MEMSET (lpbResBitVect, 0xff, i); /******************************************************** * * THE NEXT STEP IS TO RESET ALL THE BITS OUTSIDE OF THE * LIMITS TO 0'S * ********************************************************/ // GarrG. 12-14-94. Subtracted one to really point to the last byte. if (lpGroup->maxItemAllGroup % 8) { lpbResBitVect += i-1; // Move to the last byte i = 1 << (lpGroup->maxItemAllGroup % 8); // maxItemAllGroup is actually number, not the maximum index. // so I removed i <<= 1; -GarrG while (i <= 0x80) { *lpbResBitVect &= ~i; i <<= 1; } } if ((fRet = GroupTrimmed (lpResGroup)) != S_OK) { SetErrCode (lperr, fRet); GroupFree (lpResGroup); lpResGroup = NULL; } return lpResGroup; } /************************************************************************* * @doc RETRIEVAL * * @func LPGROUP FAR PASCAL | GroupAnd | * The function will generate a new group resulting from the ANDing * of two groups * * @parm LPGROUP | lpGroup1 | * Pointer to first group * * @parm LPGROUP | lpGroup2 | * Pointer to second group * * @parm PHRESULT | lperr | * Pointer to error buffer * * @rdesc If succeeded, the function will return a pointer the newly * created group. The error buffer has the information about the * cause of the fialure *************************************************************************/ PUBLIC _LPGROUP EXPORT_API FAR PASCAL GroupAnd(_LPGROUP lpGroup1, _LPGROUP lpGroup2, PHRESULT lperr) { _LPGROUP lpResGroup; LPBYTE lpbBitVect1; // Pointer to Group bitmap 1 LPBYTE lpbBitVect2; // Pointer to Group bitmap 2 LPBYTE lpbResBitVect; // Pointer to result Group bitmap DWORD i; // Counter DWORD dwMinOverlapTopic; DWORD dwMaxOverlapTopic; ERR fRet; /* Check for groups validity, and create a new one */ if ((lpResGroup = GroupCheckAndCreate(lpGroup1, lpGroup2, lperr)) == NULL) return NULL; if (lpGroup1->lcItem && lpGroup2->lcItem) { /* Only do a GroupAND for non empty group */ /* Get the overlap */ if ((dwMinOverlapTopic = lpGroup1->minItem) < lpGroup2->minItem) dwMinOverlapTopic = lpGroup2->minItem; if ((dwMaxOverlapTopic = lpGroup1->maxItem) > lpGroup2->maxItem) dwMaxOverlapTopic = lpGroup2->maxItem; if (dwMinOverlapTopic <= dwMaxOverlapTopic) { /* Change to bytes */ dwMinOverlapTopic /= 8; dwMaxOverlapTopic /= 8; /* Initialize variables */ lpbBitVect1 = &lpGroup1->lpbGrpBitVect [dwMinOverlapTopic]; lpbBitVect2 = &lpGroup2->lpbGrpBitVect [dwMinOverlapTopic]; lpbResBitVect = &lpResGroup->lpbGrpBitVect [dwMinOverlapTopic]; for (i = dwMaxOverlapTopic - dwMinOverlapTopic + 1; i > 0; i--) { *lpbResBitVect = *lpbBitVect1 & *lpbBitVect2; lpbResBitVect++; lpbBitVect1++; lpbBitVect2++; } } } if ((fRet = GroupTrimmed (lpResGroup)) != S_OK) { SetErrCode (lperr, fRet); GroupFree (lpResGroup); lpResGroup = NULL; } return lpResGroup; } /************************************************************************* * @doc API RETRIEVAL * * @func _LPGROUP | GroupDuplicate | * This fucntion creates a copy for the specified group * * @parm _LPGROUP | lpGroup| * Pointer to group to be duplicated * * @parm PHRESULT | lperr | * A pointer to an error buffer, which will receive the error * code in case that the function fails * * @rdesc Return a new copy of group if succeeded, NULL if failed *************************************************************************/ PUBLIC _LPGROUP PASCAL FAR GroupDuplicate (_LPGROUP lpGroup, PHRESULT lperr) { _LPGROUP lpDupGroup; /* Safety check */ if (lpGroup == NULL) { SetErrCode (lperr, E_INVALIDARG); return NULL; } /* Create the group */ if ((lpDupGroup = GroupCreate (lpGroup->dwSize, lpGroup->maxItemAllGroup, lperr)) == NULL) return NULL; /* Copy the information over */ *(GROUP_HDR FAR *)lpDupGroup = *(GROUP_HDR FAR *)lpGroup; /* Check for empty group */ if (lpGroup->hGrpBitVect) { MEMCPY (lpDupGroup->lpbGrpBitVect, lpGroup->lpbGrpBitVect, lpGroup->dwSize); } else { _GLOBALUNLOCK (lpDupGroup->hGrpBitVect); _GLOBALFREE (lpDupGroup->hGrpBitVect); lpDupGroup->hGrpBitVect = 0; lpDupGroup->lpbGrpBitVect = NULL; } return lpDupGroup; } /************************************************************************* * @doc API RETRIEVAL * * @func ERR _LPGROUP PASCAL FAR | GroupCopy | * This fucntion copies the bitfield data of one group to another * * @parm _LPGROUP | lpGroupDest| * Pointer to destination group * * @parm _LPGROUP | lpGroupSrc| * Pointer to source group * * @rdesc Returns S_OK if succeeded, an error otherwise *************************************************************************/ PUBLIC ERR PASCAL FAR GroupCopy (_LPGROUP lpGroupDst, _LPGROUP lpGroupSrc) { ERR err=S_OK; HANDLE hNewGroupMem=NULL; // Safety check if ((lpGroupSrc == NULL) || (lpGroupDst==NULL)) { return E_INVALIDARG; } if (lpGroupSrc->hGrpBitVect) // Source group is NOT empty { if ((NULL == lpGroupDst->hGrpBitVect) || (NULL == lpGroupDst->lpbGrpBitVect) || (lpGroupDst->dwSize!=lpGroupSrc->dwSize)) { if ((hNewGroupMem = _GLOBALALLOC(DLLGMEM_ZEROINIT, lpGroupSrc->dwSize)) == NULL) return E_OUTOFMEMORY; } // Copy the information over *(GROUP_HDR FAR *)lpGroupDst = *(GROUP_HDR FAR *)lpGroupSrc; // Remove old info from Destination group and create new if // differing sizes if (hNewGroupMem) { if (NULL != lpGroupDst->hGrpBitVect) { _GLOBALUNLOCK(lpGroupDst->hGrpBitVect); _GLOBALFREE(lpGroupDst->hGrpBitVect); } lpGroupDst->hGrpBitVect = hNewGroupMem; lpGroupDst->lpbGrpBitVect = (LPBYTE)_GLOBALLOCK(hNewGroupMem); } // Copy actual bits MEMCPY (lpGroupDst->lpbGrpBitVect, lpGroupSrc->lpbGrpBitVect, lpGroupSrc->dwSize); } else { // Group is empty, make destination empty *(GROUP_HDR FAR *)lpGroupDst = *(GROUP_HDR FAR *)lpGroupSrc; // Remove old info from Destination group if (lpGroupDst->hGrpBitVect) { _GLOBALUNLOCK(lpGroupDst->hGrpBitVect); _GLOBALFREE(lpGroupDst->hGrpBitVect); lpGroupDst->hGrpBitVect=NULL; lpGroupDst->lpbGrpBitVect = NULL; } } return err; } /************************************************************************* * @doc INTERNAL * * @func LPGROUP NEAR PASCAL | GroupCheckAndCreate | * Given 2 groups, this function will check their validity, and if * they are valid, create a new group * * @parm LPGROUP | lpGroup1 | * Pointer to group 1 * * @parm LPGROUP | lpGroup2 | * Pointer to group 2 * * @parm PHRESULT | lperr | * Pointer to error buffer * * @rdesc The function will return a pointer to a newly created group if * succeeded, NULL otherwise. The error buffer contains information * about the cause of the failure *************************************************************************/ static _LPGROUP NEAR PASCAL GroupCheckAndCreate(_LPGROUP lpGroup1, _LPGROUP lpGroup2, PHRESULT lperr) { DWORD dwSize; DWORD maxItemAllGroup; /* Check the validity of the groups */ if (GroupCheck(lpGroup1) != S_OK || GroupCheck(lpGroup2) != S_OK) { SetErrCode(lperr, E_BADVERSION); return NULL; } if ((dwSize = lpGroup1->dwSize) < lpGroup2->dwSize) { dwSize = lpGroup2->dwSize; } if ((maxItemAllGroup = lpGroup1->maxItemAllGroup) < lpGroup2->maxItemAllGroup) { maxItemAllGroup = lpGroup2->maxItemAllGroup; } /* Create a new Group */ return (GroupCreate(dwSize, maxItemAllGroup, lperr)); } /************************************************************************* * @doc EXTERNAL API * * @func _LPGROUP PASCAL FAR | GroupBufferCreate | * This function will create group from a buffer * * @parm HANDLE | h | * Handle to memory buffer containig raw group file data * * @parm PHRESULT | phr | * Pointer to error buffer * * @rdesc If succeeded, the function will return a pointer to the loaded * group, else NULL. The error buffer will contain information about * the cause of the failure *************************************************************************/ _LPGROUP PASCAL FAR GroupBufferCreate (HANDLE h, PHRESULT phr) { GROUP_HDR FAR *lpGroupHdr; GROUP_HDR GroupHdr; ERR fRet; _LPGROUP lpGroup = NULL; char cBitSet; // This must be signed !!! LPBYTE lpGroupBitVect; DWORD dwStartByte; DWORD dwVectorSize; DWORD dwCurMaxTopic; DWORD dwBytes; LPBYTE lp=NULL; lpGroupHdr = &GroupHdr; lp = (LPBYTE)_GLOBALLOCK(h); dwBytes = (DWORD) GlobalSize(h); fRet = E_BADFILE; if (dwBytes < sizeof(GROUP_HDR)) { goto exit00; } MEMCPY (lpGroupHdr, lp, sizeof(GROUP_HDR)); /* BigEndian codes. They will optimized out under Windows */ lpGroupHdr->FileStamp = SWAPWORD(lpGroupHdr->FileStamp); lpGroupHdr->version = SWAPWORD(lpGroupHdr->version); lpGroupHdr->dwSize = SWAPLONG(lpGroupHdr->dwSize); lpGroupHdr->maxItem = SWAPLONG(lpGroupHdr->maxItem); lpGroupHdr->minItem = SWAPLONG(lpGroupHdr->minItem); lpGroupHdr->lcItem = SWAPLONG(lpGroupHdr->lcItem); lpGroupHdr->maxItemAllGroup = SWAPLONG(lpGroupHdr->maxItemAllGroup); lpGroupHdr->fFlag = SWAPWORD(lpGroupHdr->fFlag); /* Set maxItemAllgroup and fFlag properly, since those fields * didn't exist before */ if (lpGroupHdr->version < 9) { lpGroupHdr->maxItemAllGroup = lpGroupHdr->dwSize * 8; lpGroupHdr->fFlag = BITVECT_GROUP; } /* Check to see if the data read in is valid */ if (GroupCheck((_LPGROUP)lpGroupHdr) != S_OK) goto exit00; if (lpGroupHdr->dwSize == 0 && lpGroupHdr->maxItem) lpGroupHdr->dwSize = lpGroupHdr->maxItem / 8 + 1; /* Get the vector size. This is a shorthand version */ dwVectorSize = lpGroupHdr->dwSize; /* Create a new group. It is assuming that GroupCreate() will * allocate enough memory to store all the data */ if ((lpGroup = GroupCreate( (lpGroupHdr->maxItemAllGroup+7)>>3, //dwVectorSize, lpGroupHdr->maxItemAllGroup, phr)) == NULL) { fRet = E_OUTOFMEMORY; goto exit00; } /* Copy the group header information */ *(GROUP_HDR FAR *)lpGroup = *lpGroupHdr; if (lpGroup->fFlag == HILO_GROUP && lpGroup->minItem && lpGroup->maxItem) { /* The work now is to set all the bits to 1. It is broken into 3 parts: * - The beginning byte : which may not have all bit set * - The ending byte : which may not have all bit set * - Every byte between the above two: all bits are set */ dwStartByte = (lpGroup->minItem / 8); lpGroupBitVect = lpGroup->lpbGrpBitVect + dwStartByte; dwCurMaxTopic = dwStartByte * 8; /* Set the beginning byte */ cBitSet = (char)(lpGroup->minItem - dwStartByte * 8); while (cBitSet < 8) { *lpGroupBitVect |= 1 << cBitSet; cBitSet ++; // a-kevct: (MV1.3 #27) Changed test from >= to > condition if (dwCurMaxTopic + cBitSet > lpGroup->maxItem) { lpGroup->fFlag = TRIMMED_GROUP; goto DoneGroup; } } /* Set the body */ MEMSET (lpGroupBitVect + 1, 0xff, dwVectorSize - dwStartByte - 2); /* Set the ending byte */ lpGroupBitVect = lpGroup->lpbGrpBitVect + dwVectorSize - 1; cBitSet = (int)(lpGroup->maxItem - (lpGroup->maxItem / 8) * 8); /* Note that in the following calculation, we end at 0. Suppose * that maxItem = 8, then the 1st bit of the second byte must be set */ while (cBitSet >= 0) { *lpGroupBitVect |= 1 << cBitSet; cBitSet --; } lpGroup->fFlag = TRIMMED_GROUP; } else { /* Seek to position from start of file */ lp += GROUP_HDR_SIZE; if (dwBytes < (DWORD)(GROUP_HDR_SIZE+dwVectorSize)) goto exit00; if (lpGroup->fFlag==DISKCOMP_GROUP) { DWORD dwNewSize=GroupDecompressDelta((LPBYTE)lpGroup->lpbGrpBitVect,lp,dwVectorSize); lpGroup->fFlag=BITVECT_GROUP; dwVectorSize=lpGroup->dwSize=(lpGroupHdr->maxItemAllGroup+7)>>3; } else if (lpGroup->fFlag==DISKCOMP_TRIMMED_GROUP) { DWORD dwNewSize=GroupDecompressDelta((LPBYTE)lpGroup->lpbGrpBitVect,lp,dwVectorSize); lpGroup->fFlag=TRIMMED_GROUP; dwVectorSize=lpGroup->dwSize=(lpGroupHdr->maxItemAllGroup+7)>>3; } else MEMCPY ((LPBYTE)lpGroup->lpbGrpBitVect,lp,dwVectorSize); /* This piece of code is to support old version of the * group format. It can be deleted after everybody has converted to the * new format */ if (lpGroup->version < 9) { if ((fRet = GroupTrimmed (lpGroup)) != S_OK) goto exit00; dwVectorSize = lpGroup->dwSize; } } DoneGroup: #if 0 if (lpGroup->lcItem != LrgbBitCount(lpGroup->lpbGrpBitVect, dwVectorSize)) goto exit00; #else lpGroup->lcItem = LrgbBitCount(lpGroup->lpbGrpBitVect, dwVectorSize); #endif fRet = S_OK; exit00: /* Close the subfile */ /* handle is allocated normally, so free normally */ if (lp) _GLOBALUNLOCK(h); if (fRet == S_OK) return lpGroup; SetErrCode (phr, fRet); if (lpGroup) GroupFree (lpGroup); return NULL; } /************************************************************************* * @doc API RETRIEVAL * * @func DWORD FAR PASCAL | GroupIsBitSet | * Given a pointer to a group and a topic number in the group, * this function will return TRUE if the bit is set or FALSE if * the bit is not set. * * @parm LPGROUP | lpGroup | * Pointer to the group * * @parm DWORD | dwTopicNum | * The index count in to the group. dwTopicNum is 0-based * * @rdesc TRUE if the bit indecated by dwTopicNum is set or * FALSE if the bit is not set. * *************************************************************************/ PUBLIC BOOL EXPORT_API FAR PASCAL GroupIsBitSet (_LPGROUP lpGroup, DWORD dwTopicNum) { if (lpGroup == NULL || lpGroup->lcItem == 0 || lpGroup->minItem > dwTopicNum || lpGroup->maxItem < dwTopicNum) { return (FALSE); } return GROUPISBITSET (lpGroup, dwTopicNum); }