/************************************************************************* * * * ICATALOG.C * * * * Copyright (C) Microsoft Corporation 1990-1994 * * All Rights reserved. * * * ************************************************************************** * * * Module Intent * * Catalog generating * * * ************************************************************************** * * * Current Owner: BinhN * * * ************************************************************************** * * * Revision History: * * * ************************************************************************** * * * Released by Development: (date) * * * *************************************************************************/ #include #include #include #include #include #include "common.h" #include "index.h" #ifdef _DEBUG static BYTE NEAR s_aszModule[] = __FILE__; /* Used by error return functions.*/ #endif #define CAT_BUF_SIZE CB_HUGE_BUF // Catalog buffer's size /************************************************************************* * * API FUNCTIONS * Those functions should be exported in a .DEF file *************************************************************************/ PUBLIC LPCAT EXPORT_API PASCAL FAR CatalogInitiate (WORD, PHRESULT); PUBLIC HRESULT EXPORT_API PASCAL FAR CatalogAddItem (LPCAT, DWORD, LPB); PUBLIC HRESULT EXPORT_API PASCAL FAR CatalogBuild (HFPB, LPCAT, LSZ, INTERRUPT_FUNC, LPV); PUBLIC VOID EXPORT_API PASCAL FAR CatalogDispose (LPCAT lpCat); /************************************************************************* * * INTERNAL PRIVATE FUNCTIONS * All of them should be declared near *************************************************************************/ PRIVATE HRESULT PASCAL NEAR CatalogFlush (CAT_INDEX FAR *); PRIVATE HRESULT PASCAL NEAR CatalogSort (CAT_INDEX FAR *); PRIVATE HRESULT PASCAL NEAR CatalogMerge (CAT_INDEX FAR *); /************************************************************************* * @doc API INDEX * * @func LPCAT PASCAL FAR | CatalogInitiate | * Create and initialize different structures and temporary files * needed for the creation of a catalog * * @parm WORD | wElemSize | * Size of each element in a catalog * * @parm PHRESULT | phr | * Error buffer to be used in subsequent catalog related function calls * * @rdesc NULL if failed. The error buffer will contain descriptions about * the cause of the failure *************************************************************************/ PUBLIC LPCAT EXPORT_API PASCAL FAR CatalogInitiate (WORD wElemSize, PHRESULT phr) { CAT_INDEX FAR *lpCat; HANDLE handle; // Temporary variable // Initialization phase. Allocate memory, create temporary // and permanent files. // if ((handle = _GLOBALALLOC(DLLGMEM_ZEROINIT, sizeof(CAT_INDEX))) == NULL) { SetErrCode(phr, E_OUTOFMEMORY); return NULL; } lpCat = (LPCAT)_GLOBALLOCK(handle); lpCat->hCat = handle; lpCat->lperrb = phr; lpCat->wElemSize = wElemSize; lpCat->FileStamp = CATALOG_STAMP; lpCat->version = VERCURRENT; lpCat->dwCurElem = lpCat->dwFirstElem = (DWORD)-1; /* Assume that all incoming data are already sorted in ascending order */ lpCat->fFlags = CAT_SORTED; lpCat->aszTempFile = lpCat->TmpFileNames; /* Create temp file */ (void)GETTEMPFILENAME((char)0, (LSZ)"cat", (WORD)0, (LSZ)lpCat->aszTempFile); if ((lpCat->hResultFile = _lcreat (lpCat->aszTempFile, 0)) == (HFILE)-1) { exit01: _GLOBALUNLOCK(lpCat->hCat); _GLOBALFREE(lpCat->hCat); return NULL; } /* Allocate memory */ if ((handle = _GLOBALALLOC(DLLGMEM, CAT_BUF_SIZE)) == NULL) { SetErrCode(phr, E_OUTOFMEMORY); /* Remove temp file */ _lclose(lpCat->hResultFile); FileUnlink(NULL,lpCat->aszTempFile,REGULAR_FILE); goto exit01; } lpCat->lpCatBuf = _GLOBALLOCK(lpCat->hCatBuf = handle); lpCat->ibBufOffset = 0; return lpCat; } /************************************************************************* * @doc API INDEX * * @func HRESULT PASCAL FAR | CatalogAddItem | * Add an item into the catalog * * @parm LPCAT | lpCat | * Pointer to catalog structure (returned by CatalogInitiate()) * * @parm DWORD | dwItemN | * Item's id * * @parm LPB | lpCatElement | * Buffer contained the value of the catalog item. The size of this * buffer is pre-determined in CatalogInitiate() * * @rdesc S_OK if succeeded. The error buffer provided in CatalogInitiate() * will contain descriptions about the cause of the failure * * @xref CatalogInitiate() *************************************************************************/ PUBLIC HRESULT EXPORT_API PASCAL FAR CatalogAddItem (CAT_INDEX FAR *lpCat, DWORD dwItemN, LPB lpCatElement) { WORD wNewOffset; LPB lpBuf; /* Sanity check */ if (lpCat == NULL || lpCatElement == NULL) return E_INVALIDARG; if (lpCat->dwFirstElem == (DWORD)-1) { lpCat->dwFirstElem = dwItemN; } else { if (dwItemN <= lpCat->dwCurElem) { /* The elements are not sorted */ lpCat->fFlags &= ~CAT_SORTED; if (lpCat->dwFirstElem > dwItemN) lpCat->dwFirstElem = dwItemN; } } /* Check to see if we have enough room in the buffer. If not * just flush the buffer to make room for new data */ if (lpCat->ibBufOffset + (wNewOffset = lpCat->wElemSize + sizeof(DWORD)) >= CAT_BUF_SIZE) { if (CatalogFlush(lpCat) != S_OK) return E_FAIL; } /* Have enough room, just copy data */ lpBuf = lpCat->lpCatBuf + lpCat->ibBufOffset; *(LPDW)lpBuf = dwItemN; lpBuf += sizeof (DWORD); MEMCPY(lpBuf, lpCatElement, lpCat->wElemSize); /* Only update buffer pointer if the last datum is not the same as this one */ if (lpCat->dwCurElem != dwItemN) { lpCat->dwCurElem = dwItemN; lpCat->ibBufOffset += wNewOffset; lpCat->cElement ++; } return S_OK; } /************************************************************************* * @doc API INDEX * * @func int PASCAL FAR | CatalogBuild | * Write the catalog as a subfile of the system file * * @parm HFPB | hfpbSysFile | * If non-zero, this is the handle of an already opened system file * * @parm LPCAT | lpCat | * Pointer to catalog structure (returned by CatalogInitiate()) * * @parm LSZ | lszFilename | * If hpfbSysFile is non-zero, this is the name of the catalog's subfile * else this is a regular DOS file * * @rdesc S_OK if succeeded, other errors otherwise * the failure * * @xref CatalogInitiate() *************************************************************************/ PUBLIC HRESULT EXPORT_API PASCAL FAR CatalogBuild (HFPB hfpbSysFile, CAT_INDEX FAR *lpCat, LSZ lszCatName, INTERRUPT_FUNC fnInterrupt, LPV lpParm) { WORD cbBufSize; // Buffer size WORD cbBytePerItem; // Number of bytes per item WORD cItemRead; // Number of items read at once int cbRead; // Number of bytes read at once HANDLE hResultBuf; // Handle of temporary buffer LPB lpbResultBuf; // Temporary result buffer LPB lpbCurItem; // Pointer at current item LPB lpbInBuf; // Pointer for input buffer DWORD lfoFileOffset; // Current offset in catalog subfile WORD cbTotalInBuf; // Total number of bytes in tmpbuf PHRESULT phr; // Error buffer HRESULT fRet; // Return value BYTE Dummy[CATALOG_HDR_SIZE]; // Dummy buffer to write 0 long LastItem; // Last item long CurItem; // Current item WORD wElemSize; // Element size if (lpCat == NULL || lszCatName == NULL) return E_INVALIDARG; phr = lpCat->lperrb; fRet = E_FAIL; // Default fRet /* Flush the catalog buffer */ if ((fRet = CatalogFlush(lpCat)) != S_OK) { SetErrCode(phr, fRet); goto exit00; } /* Rewind the temp file */ if (_llseek(lpCat->hResultFile, 0L, 0) == (LONG)-1) { SetErrCode(phr, E_FILESEEK); goto exit00; } /* Open system file & catalog subfile */ if ((lpCat->hfpbCatalog = FileCreate(hfpbSysFile, lszCatName, hfpbSysFile ? FS_SUBFILE : REGULAR_FILE, phr)) == NULL) { goto exit00; } /* Inititalize various variables */ cbBytePerItem = sizeof (DWORD) + (wElemSize = lpCat->wElemSize); cItemRead = CAT_BUF_SIZE / cbBytePerItem; cbBufSize = cItemRead * cbBytePerItem; LastItem = lpCat->dwFirstElem - 1; lpCat->cElement = 0; /* Allocate a temporary buffer */ if ((hResultBuf = _GLOBALALLOC(DLLGMEM, CAT_BUF_SIZE)) == NULL) { SetErrCode(phr, E_OUTOFMEMORY); goto exit01; } lpbResultBuf = _GLOBALLOCK(hResultBuf); lfoFileOffset = 0; /* Reserve space for catalog information */ lpbCurItem = lpbResultBuf + (cbTotalInBuf = CATALOG_HDR_SIZE); for (;;) { if (fnInterrupt != NULL) { /* Call user's interrupt functions */ if ((fRet = (*fnInterrupt)(lpParm)) != S_OK) { SetErrCode(phr, fRet); goto FreeAll; } } /* Read in a block */ if ((cbRead = _lread(lpCat->hResultFile, lpCat->lpCatBuf, cbBufSize)) == (LONG)-1) { SetErrCode(phr, E_FILEREAD); goto FreeAll; } if (cbRead == 0) // EOF check break; for (lpbInBuf = lpCat->lpCatBuf; cbRead > 0;) { if ((LastItem > *(long far *)lpbInBuf)) { SetErrCode(phr, E_ASSERT); goto FreeAll; } CurItem = *(LPDW)lpbInBuf; lpbInBuf += sizeof(DWORD); /* Fill up the "holes", ie. missing indices. For random access retrieval to work, all items need to be present. So if we only have keys 1 and 5, we have to fill in "holes" of key 2, 3, 4. */ while (LastItem < CurItem - 1) { /* If buffer is full, flush it to subfile */ if (cbTotalInBuf + cbBytePerItem > cbBufSize) { if (fnInterrupt != NULL) { /* Call user's interrupt functions */ if ((fRet = (*fnInterrupt)(lpParm)) != S_OK) { SetErrCode(phr, fRet); goto FreeAll; } } if (FileWrite(lpCat->hfpbCatalog, lpbResultBuf, cbTotalInBuf,NULL) != cbTotalInBuf) { goto FreeAll; } /* Reset the variables */ lfoFileOffset += cbTotalInBuf; lpbCurItem = lpbResultBuf; cbTotalInBuf = 0; } /* Fill the missing element with -1 */ MEMSET(lpbCurItem, (BYTE)-1, wElemSize); lpbCurItem += wElemSize; cbTotalInBuf += wElemSize; lpCat->cElement++; LastItem++; } /* If buffer is full, flush it to subfile */ if (cbTotalInBuf + cbBytePerItem > cbBufSize) { if (FileWrite(lpCat->hfpbCatalog, lpbResultBuf, cbTotalInBuf,NULL) != cbTotalInBuf) { goto FreeAll; } /* Reset the variables */ lfoFileOffset += cbTotalInBuf; lpbCurItem = lpbResultBuf; cbTotalInBuf = 0; } /* Copy the data */ MEMCPY(lpbCurItem, lpbInBuf, wElemSize); /* Only update pointer if data indices are different */ if (LastItem != CurItem) { lpbCurItem += wElemSize; cbTotalInBuf += wElemSize; LastItem = CurItem; lpCat->cElement++; } lpbInBuf += wElemSize; cbRead -= wElemSize + sizeof(DWORD); } } /* Flush the buffer */ if (cbTotalInBuf > 0) { if (FileWrite(lpCat->hfpbCatalog, lpbResultBuf, cbTotalInBuf, phr) != cbTotalInBuf) { fRet = *phr; goto FreeAll; } } /* Write catalog header information. */ MEMSET(Dummy, (BYTE)0, CATALOG_HDR_SIZE); /* Write all zero to the header */ if (FileSeekWrite(lpCat->hfpbCatalog, Dummy, foNil, CATALOG_HDR_SIZE, phr) != CATALOG_HDR_SIZE) { fRet = *phr; goto FreeAll; } if (FileSeekWrite(lpCat->hfpbCatalog, lpCat, foNil, sizeof(CAT_HEADER),phr)!=sizeof (CAT_HEADER)) fRet = *phr; FreeAll: _GLOBALUNLOCK(hResultBuf); _GLOBALFREE(hResultBuf); exit01: FileClose(lpCat->hfpbCatalog); exit00: return fRet; } /************************************************************************* * @doc API INDEX * * @func VOID PASCAL FAR | CatalogDispose | * Release all memories and temporary files associated with * the catalog * * @parm LPCAT | lpCat | * Pointer to catalog structure (returned by CatalogInitiate()) *************************************************************************/ PUBLIC VOID EXPORT_API PASCAL FAR CatalogDispose (CAT_INDEX FAR *lpCat) { if (lpCat == NULL) return; if (lpCat->hResultFile) { _lclose(lpCat->hResultFile); FileUnlink(NULL,lpCat->aszTempFile,REGULAR_FILE); } /* Free catalog buffer */ _GLOBALUNLOCK(lpCat->hCatBuf); _GLOBALFREE(lpCat->hCatBuf); /* Free temporary index array */ if (lpCat->hIndexArray) { _GLOBALUNLOCK(lpCat->hIndexArray); _GLOBALFREE(lpCat->hIndexArray); } /* Free catalog structure */ _GLOBALUNLOCK(lpCat->hCat); _GLOBALFREE(lpCat->hCat); } /************************************************************************* * @doc INTERNAL * * @func PRIVATE HRESULT PASCAL NEAR | CatalogFlush | * Flush out the catalog buffer. The function will perform sort * if necessary * * @parm CAT_INDEX FAR * | lpCat | * Pointer to catalog buffer *************************************************************************/ PRIVATE HRESULT PASCAL NEAR CatalogFlush (CAT_INDEX FAR *lpCat) { HRESULT fRet; // Returned value fRet = S_OK; if (lpCat->ibBufOffset == 0) { /* There is nothing to flush */ return S_OK; } /* Sort the temporary buffer if it is not already sorted */ if ((lpCat->fFlags & CAT_SORTED) == 0) { if ((fRet = CatalogSort (lpCat)) != S_OK) return fRet; } /* Write the sorted result to the temp file */ if ((fRet = CatalogMerge (lpCat)) != S_OK) return fRet; /* Reset various variables */ lpCat->fFlags |= CAT_SORTED; lpCat->cElement = lpCat->ibBufOffset = 0; return S_OK; } /************************************************************************* * @doc INTERNAL * * @func PRIVATE HRESULT PASCAL NEAR | CatalogMerge | * Flush out the catalog buffer by merging the catalog buffer * with the already flushed data in the temp file * * @parm CAT_INDEX FAR * | lpCat | * Pointer to catalog buffer * * @rdesc The function returns S_OK if succeeded *************************************************************************/ PRIVATE HRESULT PASCAL NEAR CatalogMerge (CAT_INDEX FAR *lpCat) { HRESULT fRet; if (lpCat->fFlags & CAT_SORTED) { /* everything is still sorted. Just add the buffer to the end of the file */ if (_lwrite(lpCat->hResultFile, lpCat->lpCatBuf, lpCat->ibBufOffset) != lpCat->ibBufOffset) { return E_DISKFULL; } } else { if ((fRet = IndexMergeSort ((HFILE FAR *)&lpCat->hResultFile, (LSZ)lpCat->aszTempFile, lpCat->IndexArray, lpCat->lpCatBuf, lpCat->wElemSize + sizeof(DWORD), (WORD)lpCat->cElement)) != S_OK) return fRet; } return S_OK; } /************************************************************************* * @doc INTERNAL * * @func PRIVATE HRESULT PASCAL NEAR | CatalogSort | * Given the array ofdata in lpCat->lpbCatBuf, the function will * perform an "indirect" sort. First, an index array containing * the offsets of the elements in the buffer will be created. * The function then call IndexSort() to do the sorting * * @parm CAT_INDEX FAR * | lpCat | * Pointer to catalog buffer * * @rdesc The function returns S_OK if succeeded *************************************************************************/ PRIVATE HRESULT PASCAL NEAR CatalogSort (CAT_INDEX FAR *lpCat) { LPW lpIndexArray; int cbElement; register int i, j; WORD wElemSize; if (lpCat->hIndexArray == 0) { /* Allocate the index buffer */ if ((lpCat->hIndexArray= _GLOBALALLOC(DLLGMEM, (cbElement = (CAT_BUF_SIZE / lpCat->wElemSize)*sizeof(WORD)))) == NULL) { return E_OUTOFMEMORY; } lpCat->IndexArray = (LPW)_GLOBALLOCK(lpCat->hIndexArray); } /* Initialize the index array */ cbElement = (WORD)lpCat->cElement; wElemSize = lpCat->wElemSize + sizeof(DWORD); for (j = 0, i = cbElement, lpIndexArray = lpCat->IndexArray; i > 0; i--, j += wElemSize) { *lpIndexArray++ = (WORD) j; } /* Do the sorting */ if (IndexSort (lpCat->IndexArray, lpCat->lpCatBuf, cbElement) != S_OK) return E_FAIL; /* Update the current element to be the largest element. This will avoiding future sorting if this is the only time things get out of order (lpCat->fFlags CAT_SORTED will be set) */ lpCat->dwCurElem = *(LPDW)(lpCat->lpCatBuf + lpCat->IndexArray[cbElement - 1]); return S_OK; }