|
|
/*************************************************************************
* * * 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 <verstamp.h>
SETVERSIONSTAMP(MVUT);
#include <mvopsys.h>
#include <misc.h>
#include <mem.h>
#include <iterror.h>
#include <wrapstor.h>
#include <mvsearch.h>
#include <groups.h>
#include <orkin.h>
#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<dwSmallestSize) { dwSmallestSize=dwNewSize; wCompressMode=COMPRESS_DELTA_INV; } // COMPRESS_GROUPRLE
// Start by counting number of 0's, then alternate 1's, 0's, ...
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; 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; 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<dwSmallestSize) { dwSmallestSize=dwNewSize; wCompressMode=COMPRESS_GROUPRLE; }
// We now have the smallest size dwSmallestSize and
// compression format wCompressMode
dwNewSize=dwSmallestSize;
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<<wShift); lpbMem+=(wShift>>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<<wShift); lpbMem+=(wShift>>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<<wShift); lpbMem+=(wShift>>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<<wShift); lpbMem+=(wShift>>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)<<wShiftDelta; wShiftDelta+=3; lpbSource+=(wShift>>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)<<wShiftRunSize; wShiftRunSize+=3; lpbSource+=(wShift>>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); }
|