#ifndef _SCB_H #define _SCB_H // Redirect Asserts in inline code to seem to fire from this file #define szAssertFilename __FILE__ // includes #include // tune these constants for optimal performance // maximum amount of fast memory (cache) to use for sorting #define cbSortMemFast ( 16 * ( 4088 + 1 ) ) // maximum amount of normal memory to use for sorting #define cbSortMemNorm ( 1024 * 1024L ) // maximum size for memory resident Temp Table #define cbResidentTTMax ( 64 * 1024L ) // minimum count of sort pairs effectively sorted by Quicksort // NOTE: must be greater than 2! #define cspairQSortMin ( 32 ) // maximum partition stack depth for Quicksort #define cpartQSortMax ( 16 ) // maximum count of runs to merge at once (fan-in) #define crunFanInMax ( 16 ) // I/O cluster size (in pages) #define cpgClusterSize ( 2 ) // define to use predictive preread instead of prereading all runs //#define PRED_PREREAD // Sort Page structure // // This is a custom page layout for use in the temporary database by sorting // only. Sufficient structure still remains so that other page reading code // can recognize that they do not know this format and can continue on their // merry way. #pragma pack(1) typedef struct _spage { ULONG ulChecksum; // page checksum #ifdef PRED_PREREAD USHORT ibLastSREC; // offset to last unbroken SREC #endif // PRED_PREREAD BYTE rgbData[ // free data space = cbPage // page size - sizeof( ULONG ) // - ulChecksum #ifdef PRED_PREREAD - sizeof( USHORT ) // - ibLastSREC #endif // PRED_PREREAD - sizeof( PGTYP ) // - pgtyp - sizeof( THREEBYTES ) // - pgnoThisPage ]; PGTYP pgtyp; // page type (== pgtypSort) THREEBYTES pgnoThisPage; // this page's page number } SPAGE; #pragma pack() // returns start of free data area in a sort page STATIC INLINE BYTE *PbDataStartPspage( SPAGE *pspage ) { return (BYTE *)( &pspage->rgbData); } // returns end of free data area in a sort page + 1 STATIC INLINE BYTE *PbDataEndPspage( SPAGE *pspage ) { return (BYTE *)( &pspage->pgtyp ); } // free data space per SPAGE #define cbFreeSPAGE ( offsetof( SPAGE, pgtyp ) - offsetof( SPAGE, rgbData ) ) // maximum count of SPAGEs' data that can be stored in normal sort memory #define cspageSortMax ( cbSortMemNorm / cbFreeSPAGE ) // amount of normal memory actually used for sorting // (designed to make original runs fill pages exactly) #define cbSortMemNormUsed ( cspageSortMax * cbFreeSPAGE ) // Sort Pair in fast sort memory // // (key prefix, index) pairs are sorted so that most of the data that needs // to be examined during a sort will be loaded into cache memory, allowing // the sort to run very fast. If two key prefixes are equal, we must go out // to slower memory to compare the remainder of the keys (if any) to determine // the proper sort order. This makes it important for the prefixes to be as // discriminatory as possible for each record. // // CONSIDER: adding a flag to indicate that the entire key is present // // Indexes are compressed pointers that describe the record's position in // the slow memory sort buffer. Each record's position can only be known to // a granularity designated by the size of the normal sort memory. For // example, if you specify 128KB of normal memory, the granularity is 2 // because the index can only take on 65536 values: // ceil( ( 128 * 1024 ) / 65536 ) = 2. // size of key prefix (in bytes) #define cbKeyPrefix ( 14 ) #pragma pack(1) // NOTE: sizeof(SPAIR) must be a power of 2 >= 8 typedef struct _spair { USHORT irec; // record index BYTE rgbKey[cbKeyPrefix]; // key prefix } SPAIR; #pragma pack() // addressing granularity of record indexes (fit indexes into USHORT) // (run disk usage is optimal for cbIndexGran == 1) #define cbIndexGran ( ( cbSortMemNormUsed + 0xFFFFL ) / 0x10000L ) // maximum index of records that can be stored in normal memory #define irecSortMax ( cbSortMemNormUsed / cbIndexGran ) // maximum count of SPAIRs' data that can be stored in fast sort memory // NOTE: we are reserving one for temporary sort key storage (at cspairSortMax) #define cspairSortMax ( cbSortMemFast / sizeof( SPAIR ) - 1 ) // amount of fast memory actually used for sorting (counting reserve SPAIR) #define cbSortMemFastUsed ( ( cspairSortMax + 1 ) * sizeof( SPAIR ) ) // count of "Sort Record indexes" required to store count bytes of data // (This is fast if numbers are chosen to make cbIndexGran a power of 2 // (especially 1) due to compiler optimizations) STATIC INLINE LONG CirecToStoreCb( LONG cb ) { return ( cb + cbIndexGran - 1 ) / cbIndexGran; } // generalized Sort Record type (encompasses all types) // NOTE: using void blocks illegal declarations, pointer math, etc typedef VOID SREC; // Unique run identifier (first page of run = run id) typedef PGNO RUN; #define runNull ( (RUN) pgnoNull ) #define crunAll ( 0x7FFFFFFFL ) // Run Information structure typedef struct _runinfo { RUN run; // this run CPG cpg; // count of pages in run LONG cb; // count of bytes of data in run LONG crec; // count of records in each run CPG cpgUsed; // count of pages actually used } RUNINFO; // Run Link structure (used in RUNLIST) typedef struct _runlink { struct _runlink *prunlinkNext; // next run RUNINFO runinfo; // runinfo for this run } RUNLINK; #define prunlinkNil ( (RUNLINK *) 0 ) // RUNLINK allocation operators #define PrunlinkRUNLINKAlloc() ( (RUNLINK *) LAlloc( 1, sizeof( RUNLINK ) ) ) #ifdef DEBUG /* Debug check for illegal use of freed runlink */ #define RUNLINKReleasePrcb(prunlink) { LFree( prunlink ); prunlink = prunlinkNil; } #else #define RUNLINKReleasePrcb(prunlink) { LFree( prunlink ); } #endif // Run List structure typedef struct _runlist { RUNLINK *prunlinkHead; // head of runlist LONG crun; // count of runs in list } RUNLIST; // Merge Tree Node // // These nodes are used in the replacement-selection sort tree that merges // the incoming runs into one large run. Due to the way the tree is set up, // each node acts as both an internal (loser) node and as an external (input) // node, with the exception of node 0, which keeps the last winner instead // of a loser. typedef struct _mtnode { // external node struct _rcb *prcb; // input run struct _mtnode *pmtnodeExtUp; // pointer to father node // internal node SREC *psrec; // current record struct _mtnode *pmtnodeSrc; // record's source node struct _mtnode *pmtnodeIntUp; // pointer to father node } MTNODE; // Special values for psrec for replacement-selection sort. psrecNegInf is a // sentinel value less than any possible key and is used for merge tree // initialization. psrecInf is a sentinel value greater than any possible key // and is used to indicate the end of the input stream. #define psrecNegInf ( (SREC *) -1L ) #define psrecInf ( (SREC *) NULL ) // Optimized Tree Merge Node // // These nodes are used to build the merge plan for the depth first merge of // an optimized tree merge. This tree is built so that we perform the merges // from the smaller side of the tree to the larger side of the tree, all in // the interest of increasing our cache locality during the merge process. typedef struct _otnode { RUNLIST runlist; // list of runs for this node struct _otnode *rgpotnode[crunFanInMax]; // subtrees for this node struct _otnode *potnodeAllocNext; // next node (allocation) struct _otnode *potnodeLevelNext; // next node (level) } OTNODE; #define potnodeNil ( (OTNODE *) 0 ) // Special value for potnode for the optimized tree merge tree build routine. // potnodeLevel0 means that the current level is comprised of original runs, // not of other merge nodes. #define potnodeLevel0 ( (OTNODE *) -1L ) // OTNODE allocation operators #define PotnodeOTNODEAlloc() ( (OTNODE *) LAlloc( 1, sizeof( OTNODE ) ) ) #ifdef DEBUG /* Debug check for illegal use of freed otnode */ #define OTNODEReleasePotnode(potnode) { LFree( potnode ); potnode = potnodeNil; } #else #define OTNODEReleasePotnode(potnode) { LFree( potnode ); } #endif // Sort Control Block (SCB) typedef struct _scb { FCB fcb; // FCB MUST BE FIRST FIELD IN STRUCTURE JET_GRBIT grbit; // sort grbit INT fFlags; // sort flags LONG cRecords; // count of records in sort // memory-resident sorting SPAIR *rgspair; // sort pair buffer LONG ispairMac; // next available sort pair BYTE *rgbRec; // record buffer LONG cbCommit; // amount of committed buffer space LONG irecMac; // next available record index LONG crecBuf; // count of records in buffer LONG cbData; // total record data size (actual) // disk-resident sorting LONG crun; // count of original runs generated RUNLIST runlist; // list of runs to be merged // sort/merge run output PGNO pgnoNext; // next page in output run struct _bf *pbfOut; // current output buffer BYTE *pbOutMac; // next available byte in page BYTE *pbOutMax; // end of available page // merge (replacement-selection sort) LONG crunMerge; // count of runs being read/merged MTNODE rgmtnode[crunFanInMax]; // merge tree // merge duplicate removal BOOL fUnique; // remove duplicates during merge struct _bf *pbfLast; // last used read ahead buffer struct _bf *pbfAssyLast; // last used assembly buffer #ifdef PCACHE_OPTIMIZATION /* pad to multiple of 32 bytes /**/ BYTE rgbFiller[12]; #endif } SCB; // SCB allocation operators #define PscbMEMAlloc() (SCB *)PbMEMAlloc( iresSCB ) #ifdef DEBUG /* Debug check for illegal use of freed scb */ #define MEMReleasePscb(pscb) { MEMRelease( iresSCB, (BYTE *) ( pscb ) ); pscb = pscbNil; } #else #define MEMReleasePscb(pscb) { MEMRelease( iresSCB, (BYTE *) ( pscb ) ); } #endif // SCB fFlags #define fSCBInsert (1<<0) #define fSCBIndex (1<<1) #define fSCBUnique (1<<2) // SCB fFlags operators STATIC INLINE VOID SCBSetInsert( SCB *pscb ) { pscb->fFlags |= fSCBInsert; } STATIC INLINE VOID SCBResetInsert( SCB *pscb ) { pscb->fFlags &= ~fSCBInsert; } STATIC INLINE BOOL FSCBInsert( SCB *pscb ) { return pscb->fFlags & fSCBInsert; } STATIC INLINE VOID SCBSetIndex( SCB *pscb ) { pscb->fFlags |= fSCBIndex; } STATIC INLINE VOID SCBResetIndex( SCB *pscb ) { pscb->fFlags &= ~fSCBIndex; } STATIC INLINE BOOL FSCBIndex( SCB *pscb ) { return pscb->fFlags & fSCBIndex; } STATIC INLINE VOID SCBSetUnique( SCB *pscb ) { pscb->fFlags |= fSCBUnique; } STATIC INLINE VOID SCBResetUnique( SCB *pscb ) { pscb->fFlags &= ~fSCBUnique; } STATIC INLINE BOOL FSCBUnique( SCB *pscb ) { return pscb->fFlags & fSCBUnique; } // Sort Record in normal sort memory // // There are two types of Sort Records. One type, SRECD, is used for general // sort records and can have an abitrary record data field. The second type, // SRECI, is used when we know we are sorting Key/SRID records during index // creation. SRECI is more compact and therefore allows more records to fit // in each run in this special (and common) case. #pragma pack(1) typedef struct _srecd { USHORT cbRec; // record size BYTE cbKey; // key size BYTE rgbKey[]; // key // BYTE rgbData[]; // data (just for illustration) } UNALIGNED SRECD; typedef struct _sreci { BYTE cbKey; // key size BYTE rgbKey[]; // key // SRID srid; // srid (just for illistration) } UNALIGNED SRECI; #pragma pack() // minimum amount of record that must be read in order to retrieve its size #define cbSRECReadMin ( offsetof( SRECD, cbKey ) ) // the following functions abstract different operations on a sort record pointer // to perform the appropriate operations, depending on the flags set in the SCB // returns size of an existing sort record STATIC INLINE LONG CbSRECSizePscbPsrec( SCB *pscb, SREC *psrec ) { if ( FSCBIndex( pscb ) ) return sizeof( SRECI ) + ( (SRECI *) psrec )->cbKey + sizeof( SRID ); return ( (SRECD * ) psrec )->cbRec; } // calculates size of a potential sort record STATIC INLINE LONG CbSRECSizePscbCbCb( SCB *pscb, LONG cbKey, LONG cbData ) { if ( FSCBIndex( pscb ) ) return sizeof( SRECI ) + cbKey + sizeof( SRID ); return sizeof( SRECD ) + cbKey + cbData; } // sets size of sort record STATIC INLINE VOID SRECSizePscbPsrecCb( SCB *pscb, SREC *psrec, LONG cb ) { if ( !FSCBIndex( pscb ) ) ( (SRECD * ) psrec )->cbRec = (USHORT) cb; } // returns size of sort record key STATIC INLINE LONG CbSRECKeyPscbPsrec( SCB *pscb, SREC *psrec ) { if ( FSCBIndex( pscb ) ) return ( (SRECI *) psrec )->cbKey; return ( (SRECD * ) psrec )->cbKey; } // sets size of sort record key STATIC INLINE VOID SRECKeySizePscbPsrecCb( SCB *pscb, SREC *psrec, LONG cb ) { if ( FSCBIndex( pscb ) ) ( (SRECI *) psrec )->cbKey = (BYTE) cb; else ( (SRECD *) psrec )->cbKey = (BYTE) cb; } // returns sort record key buffer pointer STATIC INLINE BYTE *PbSRECKeyPscbPsrec( SCB *pscb, SREC *psrec ) { if ( FSCBIndex( pscb ) ) return ( (SRECI *) psrec )->rgbKey; return ( (SRECD *) psrec )->rgbKey; } // returns sort record key as a Pascal string STATIC INLINE BYTE *StSRECKeyPscbPsrec( SCB *pscb, SREC *psrec ) { if ( FSCBIndex( pscb ) ) return &( (SRECI *) psrec )->cbKey; return &( (SRECD *) psrec )->cbKey; } // returns size of sort record data STATIC INLINE LONG CbSRECDataPscbPsrec( SCB *pscb, SREC *psrec ) { if ( FSCBIndex( pscb ) ) return sizeof( SRID ); return ( (SRECD *) psrec )->cbRec - ( (SRECD *) psrec )->cbKey - sizeof( SRECD ); } // returns sort record data buffer pointer STATIC INLINE BYTE *PbSRECDataPscbPsrec( SCB *pscb, SREC *psrec ) { if ( FSCBIndex( pscb ) ) return ( (SRECI *) psrec )->rgbKey + ( (SRECI *) psrec )->cbKey; return ( (SRECD * ) psrec )->rgbKey + ( (SRECD * ) psrec )->cbKey; } // returns pointer to a sort record given a base address and a Sort Record Index STATIC INLINE SREC *PsrecFromPbIrec( BYTE *pb, LONG irec ) { return (SREC *) ( pb + irec * cbIndexGran ); } // Run Control Block // // This control block is used for multiple instance use of the run input // functions ErrSORTIRunOpen, ErrSORTIRunNext, and ErrSORTIRunClose. typedef struct _rcb { SCB *pscb; // associated SCB RUNINFO runinfo; // run information struct _bf *rgpbf[cpgClusterSize]; // pinned read ahead buffers LONG ipbf; // current buffer BYTE *pbInMac; // next byte in page data BYTE *pbInMax; // end of page data LONG cbRemaining; // remaining bytes of data in run #ifdef PRED_PREREAD SREC *psrecPred; // SREC used for predictive preread #endif // PRED_PREREAD struct _bf *pbfAssy; // record assembly buffer } RCB; #define prcbNil ( (RCB *) 0 ) // RCB allocation operators #define PrcbRCBAlloc() ( (RCB *) LAlloc( 1, sizeof( RCB ) ) ) #ifdef DEBUG /* Debug check for illegal use of freed rcb */ #define RCBReleasePrcb(prcb) { LFree( prcb ); prcb = prcbNil; } #else #define RCBReleasePrcb(prcb) { LFree( prcb ); } #endif //#define UtilPerfDumpStats( a ) ( 0 ) // End Assert redirection #undef szAssertFilename #endif // _SCB_H