440 lines
13 KiB
C
440 lines
13 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2001, 2012 Fidelity Information Services, Inc *
|
|
* *
|
|
* This source code contains the intellectual property *
|
|
* of its copyright holder(s), and is made available *
|
|
* under a license. If you do not know the terms of *
|
|
* the license, please stop and do not read further. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
/*
|
|
* NOTE: See also GVCST_BLK_SEARCH.MAR for the VAX platform.
|
|
*
|
|
* -------------------------------------------------------------------
|
|
* Search a single gvcst block
|
|
*
|
|
* function definition
|
|
* enum cdb_sc gvcst_search_blk(pKey,pStat)
|
|
* gv_key *pKey; - target key
|
|
* srch_blk_status *pStat; - status block for this buffer
|
|
*
|
|
* function returns cdb_sc_normal if successful; otherwise,
|
|
* one of the failure codes cdb_sc_badoffset or cdb_sc_blklenerr.
|
|
*
|
|
* function definition
|
|
* enum cdb_sc gvcst_search_tail(pKey,pStat,pOldKey)
|
|
* gv_key *pKey; - target key
|
|
* srch_blk_status *pStat; - status block for this buffer
|
|
* gv_key *pOldKey; - key for status block
|
|
*
|
|
* gvcst_search_tail is identical to gvcst_search_blk,
|
|
* except instead of starting with the beginning
|
|
* of the block, it starts where the previous gvcst_search_blk
|
|
* left off, using the srch_blk_status to set-up.
|
|
*
|
|
* if successful, fills in srch_blk_status as follows:
|
|
*
|
|
* ---------------------------------
|
|
* | Block number (untouched) |
|
|
* ---------------------------------
|
|
* | Buffer address (input param) |
|
|
* ---------------------------------
|
|
* | Transaction number (untouched)|
|
|
* ---------------------------------
|
|
* | match | offset | previous record
|
|
* ---------------------------------
|
|
* | match | offset | current record
|
|
* ---------------------------------
|
|
*
|
|
* if the match is not found, or is found at the top or bottom of the
|
|
* block, then the values of the return fields are as follows:
|
|
*
|
|
* PREVIOUS REC CURRENT REC
|
|
* CONDITION MATCH OFFSET MATCH OFFSET
|
|
* --------- ---- ------ ----- ------
|
|
* Buffer empty 0 0 0 7
|
|
* Hit first key in block 0 0 a 7
|
|
* Hit star key b c 0 x
|
|
* Hit last key (leaf) b c a x
|
|
* Went past last key (leaf) b x 0 y
|
|
*
|
|
* where:
|
|
* a = size of target key
|
|
* b = number of characters which match on the previous
|
|
* key (including those which have been compressed away)
|
|
* c = offset of previous record
|
|
* x = offset for last record in the block
|
|
* y = top of buffer (same as block bsize)
|
|
*
|
|
*
|
|
* Block structure
|
|
* block : <blk_hdr> <block data>
|
|
* blk_hdr : <block size> <block level> <transaction number>
|
|
* blk_data : record...record
|
|
* record : <rec_hdr> [rec_data]
|
|
* rec_hdr : <record size> <compression count>
|
|
* rec_data : [byte...byte]
|
|
* -------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "mdef.h"
|
|
|
|
#include "gtm_stdio.h"
|
|
#include "gtm_string.h"
|
|
|
|
#include "gdsroot.h"
|
|
#include "gtm_facility.h"
|
|
#include "fileinfo.h"
|
|
#include "gdsbt.h"
|
|
#include "gdsfhead.h"
|
|
#include "gdsblk.h"
|
|
#include "copy.h"
|
|
#include "cdb_sc.h"
|
|
#include "gvcst_protos.h" /* for gvcst_search_tail,gvcst_search_blk prototype */
|
|
#include "send_msg.h"
|
|
|
|
GBLREF unsigned int t_tries;
|
|
GBLREF sgmnt_addrs *cs_addrs;
|
|
GBLREF gd_region *gv_cur_region;
|
|
|
|
error_def(ERR_TEXT);
|
|
|
|
#ifdef DEBUG
|
|
#include "gdscc.h"
|
|
|
|
#define DBG_CHECK_SRCH_HIST_AND_CSE_BUFFER_MATCH(pStat) \
|
|
{ \
|
|
GBLREF uint4 dollar_tlevel; \
|
|
\
|
|
srch_blk_status *tp_srch_status; \
|
|
cw_set_element *cse; \
|
|
\
|
|
if (dollar_tlevel) \
|
|
{ \
|
|
tp_srch_status = pStat->first_tp_srch_status; \
|
|
if (NULL != tp_srch_status) \
|
|
{ \
|
|
cse = tp_srch_status->cse; \
|
|
if (NULL != cse) \
|
|
assert(cse->new_buff == pStat->buffaddr); \
|
|
} \
|
|
} \
|
|
}
|
|
#else
|
|
#define DBG_CHECK_SRCH_HIST_AND_CSE_BUFFER_MATCH(pStat)
|
|
#endif
|
|
|
|
#define INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat) if (CDB_STAGNATE <= t_tries) gvcst_search_fail(pStat);
|
|
|
|
static void gvcst_search_fail(srch_blk_status *pStat)
|
|
{
|
|
char buff[1024], crbuff[256], regbuff[512];
|
|
|
|
assert(CDB_STAGNATE <= t_tries);
|
|
assert((NULL != pStat) && ((NULL != pStat->cr) || (dba_mm == gv_cur_region->dyn.addr->acc_meth)) && (NULL != cs_addrs));
|
|
if (NULL != pStat)
|
|
{
|
|
if (NULL != pStat->cr)
|
|
SPRINTF(crbuff, ": crbuff = 0x%lX", pStat->cr->buffaddr);
|
|
else
|
|
crbuff[0] = '\0';
|
|
memcpy(regbuff, gv_cur_region->rname, gv_cur_region->rname_len);
|
|
regbuff[gv_cur_region->rname_len] = '\0';
|
|
SPRINTF(buff, "Possible data corruption in region %s : blk = 0x%X : buff = 0x%lX : cr = 0x%lX %s : "
|
|
"csa = 0x%lX : csalock = 0x%lX", regbuff, pStat->blk_num, (long unsigned int) pStat->buffaddr,
|
|
(long unsigned int) pStat->cr, crbuff, (long unsigned int) cs_addrs,
|
|
(long unsigned int) cs_addrs->lock_addrs[0]);
|
|
send_msg(VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_STR(buff));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* --------------------------------------------------
|
|
* Search for a key in the block
|
|
*
|
|
* Return:
|
|
* cdb_sc_normal - success
|
|
* cdb_sc_badoffset - record with 0 length encountered,
|
|
* possibly a corrupt block
|
|
* cdb_sc_blklenerr - end of block reached without match
|
|
* --------------------------------------------------
|
|
*/
|
|
|
|
enum cdb_sc gvcst_search_blk (gv_key *pKey, srch_blk_status *pStat)
|
|
{
|
|
/* register variables named in perceived order of declining impact */
|
|
register int nFlg, nTargLen, nMatchCnt, nTmp;
|
|
sm_uc_ptr_t pBlkBase, pRecBase, pTop, pRec, pPrevRec;
|
|
unsigned char *pCurrTarg, *pTargKeyBase;
|
|
unsigned short nRecLen;
|
|
int tmp_cmpc;
|
|
|
|
/* the following load code (and code in a few other places) is coded in a "assember" style
|
|
* in an attempt to encourage the compiler to get it efficient;
|
|
* if instance, memory and non-memory instructions are interlaced to encourge pipelining.
|
|
* of course a great compiler doesn't need help, but this is portable code and ...
|
|
*/
|
|
DBG_CHECK_SRCH_HIST_AND_CSE_BUFFER_MATCH(pStat);
|
|
pBlkBase = pStat->buffaddr;
|
|
pRecBase = pBlkBase;
|
|
pTop = pBlkBase + ((blk_hdr_ptr_t)pBlkBase)->bsiz;
|
|
nRecLen = SIZEOF(blk_hdr);
|
|
pCurrTarg = pKey->base;
|
|
nMatchCnt = 0;
|
|
nTargLen = (int)pKey->end;
|
|
pTargKeyBase = pCurrTarg;
|
|
nTargLen++; /* for the terminating NUL on the key */
|
|
|
|
for (;;)
|
|
{
|
|
pRec = pRecBase + nRecLen;
|
|
|
|
if (pRec >= pTop)
|
|
{ /* Terminated at end of block */
|
|
if (pRec > pTop) /* If record goes off the end, then block must be bad */
|
|
{
|
|
INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat);
|
|
assert(CDB_STAGNATE > t_tries);
|
|
return cdb_sc_blklenerr;
|
|
}
|
|
nTargLen = 0;
|
|
if (((blk_hdr_ptr_t)pBlkBase)->levl == 0)
|
|
{ /* data block */
|
|
pPrevRec = pRecBase;
|
|
pRecBase = pRec;
|
|
}
|
|
else
|
|
nMatchCnt = 0; /* star key */
|
|
break;
|
|
}
|
|
GET_USHORT(nRecLen, &((rec_hdr_ptr_t)pRec)->rsiz);
|
|
if (nRecLen == 0) /* If record length is 0, then block must be bad */
|
|
{
|
|
INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat);
|
|
assert(CDB_STAGNATE > t_tries);
|
|
return cdb_sc_badoffset;
|
|
}
|
|
pPrevRec = pRecBase;
|
|
pRecBase = pRec;
|
|
|
|
/* If current compression count > last match, then this record
|
|
also matches on 'last match' characters; keep looping */
|
|
EVAL_CMPC2((rec_hdr_ptr_t)pRec, nTmp)
|
|
if (nTmp > nMatchCnt)
|
|
continue;
|
|
|
|
if (nTmp < nMatchCnt)
|
|
{ /* Terminate on compression count < previous match,
|
|
this key is after the target */
|
|
if (nRecLen == BSTAR_REC_SIZE && ((blk_hdr_ptr_t)pBlkBase)->levl != 0)
|
|
/* Star key has size of SIZEOF(rec_hdr) + SIZEOF(block_id), make match = 0 */
|
|
nTargLen = 0;
|
|
else
|
|
/* Data block, make match = current compression count */
|
|
nTargLen = nTmp;
|
|
break;
|
|
}
|
|
|
|
/* Compression count == match count; Compare current target with current record */
|
|
pRec += SIZEOF(rec_hdr);
|
|
|
|
do
|
|
{
|
|
if ((nFlg = *pCurrTarg - *pRec++) != 0)
|
|
break;
|
|
pCurrTarg++;
|
|
} while ( --nTargLen);
|
|
|
|
if (nFlg > 0)
|
|
nMatchCnt =(int)(pCurrTarg - pTargKeyBase);
|
|
else
|
|
{ /* Key is after target*/
|
|
if (nRecLen == BSTAR_REC_SIZE && (((blk_hdr_ptr_t)pBlkBase)->levl != 0))
|
|
/* Star key has size of SIZEOF(rec_hdr) + SIZEOF(block_id), make match = 0 */
|
|
nTargLen = 0;
|
|
else
|
|
nTargLen = (int)(pCurrTarg - pTargKeyBase);
|
|
break;
|
|
}
|
|
}
|
|
|
|
pStat->prev_rec.offset = (short)(pPrevRec - pBlkBase);
|
|
pStat->prev_rec.match = (short)nMatchCnt;
|
|
pStat->curr_rec.offset = (short)(pRecBase - pBlkBase);
|
|
pStat->curr_rec.match = (short)nTargLen;
|
|
|
|
return cdb_sc_normal;
|
|
}
|
|
|
|
|
|
/* search_tail is the "start anywhere" version of search_blk
|
|
getting started is a bit awkward, so excuse the gotos */
|
|
enum cdb_sc gvcst_search_tail (gv_key *pKey, srch_blk_status *pStat, gv_key *pOldKey)
|
|
{
|
|
/* register variables named in perceived order of declining impact */
|
|
register int nFlg, nTargLen, nMatchCnt, nTmp;
|
|
sm_uc_ptr_t pBlkBase, pRecBase, pRec, pTop, pPrevRec;
|
|
unsigned char *pCurrTarg, *pTargKeyBase, *pOldKeyBase, *pCurrTargPos;
|
|
unsigned short nRecLen;
|
|
int tmp_cmpc;
|
|
|
|
/* see comment in gvcst_search_blk above on coding style */
|
|
|
|
if (pStat->prev_rec.offset == 0)
|
|
return gvcst_search_blk(pKey, pStat); /* nice clean start at the begining of a block */
|
|
DBG_CHECK_SRCH_HIST_AND_CSE_BUFFER_MATCH(pStat);
|
|
pBlkBase = pStat->buffaddr;
|
|
pRecBase = pBlkBase + pStat->curr_rec.offset;
|
|
pRec = pRecBase;
|
|
pTop = pBlkBase + ((blk_hdr_ptr_t)pBlkBase)->bsiz;
|
|
nMatchCnt = pStat->prev_rec.match;
|
|
pCurrTarg = pKey->base;
|
|
pTargKeyBase = pCurrTarg;
|
|
pOldKeyBase = pOldKey->base;
|
|
pPrevRec = pBlkBase + pStat->prev_rec.offset;
|
|
nTargLen = pKey->end;
|
|
nTargLen++; /* for the NUL that terminates the key */
|
|
if (pRec >= pTop)
|
|
{ /* Terminated at end of block */
|
|
/* eob_tail: */ if (pRec > pTop)
|
|
{
|
|
INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat);
|
|
assert(CDB_STAGNATE > t_tries);
|
|
return cdb_sc_blklenerr;
|
|
}
|
|
if ((nTargLen = nMatchCnt) != 0)
|
|
{
|
|
do
|
|
{
|
|
if (*pCurrTarg++ != *pOldKeyBase++)
|
|
break;
|
|
} while (--nTargLen);
|
|
}
|
|
if (((blk_hdr_ptr_t)pBlkBase)->levl != 0)
|
|
nMatchCnt = 0; /* star key */
|
|
else
|
|
nMatchCnt -= nTargLen;
|
|
nTargLen = 0;
|
|
} else
|
|
{
|
|
GET_USHORT(nRecLen, &((rec_hdr_ptr_t)pRec)->rsiz);
|
|
EVAL_CMPC2((rec_hdr_ptr_t)pRec, nTmp);
|
|
nFlg = nTmp;
|
|
if (nFlg != 0)
|
|
{
|
|
do
|
|
{
|
|
if ((nFlg = *pCurrTarg - *pOldKeyBase++) != 0)
|
|
break;
|
|
pCurrTarg++;
|
|
} while (--nTmp);
|
|
if (nFlg > 0)
|
|
{
|
|
nMatchCnt = (int)(pCurrTarg - pTargKeyBase);
|
|
nTargLen -= nMatchCnt;
|
|
}
|
|
if (nFlg < 0)
|
|
{
|
|
nTargLen += (int)(pTargKeyBase - pCurrTarg);
|
|
goto match_term;
|
|
}
|
|
}
|
|
if (nFlg == 0)
|
|
{
|
|
nTmp = nMatchCnt;
|
|
nMatchCnt = (int)(pCurrTarg - pTargKeyBase);
|
|
nTargLen -= nMatchCnt;
|
|
nTmp -= nMatchCnt;
|
|
|
|
if (nTmp > 0)
|
|
{
|
|
pCurrTargPos = pCurrTarg;
|
|
|
|
do
|
|
{
|
|
if (*pCurrTargPos++ != *pOldKeyBase++)
|
|
break;
|
|
nMatchCnt++;
|
|
} while (--nTmp);
|
|
}
|
|
goto alt_loop_entry;
|
|
}
|
|
for (;;)
|
|
{
|
|
pRec = pRecBase + nRecLen;
|
|
|
|
if (pRec >= pTop)
|
|
{ /* Terminated at end of block */
|
|
if (pRec > pTop) /* If record goes off the end, then block must be bad */
|
|
{
|
|
INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat);
|
|
assert(CDB_STAGNATE > t_tries);
|
|
return cdb_sc_blklenerr;
|
|
}
|
|
nTargLen = 0;
|
|
|
|
if (((blk_hdr_ptr_t)pBlkBase)->levl == 0)
|
|
{ /* data block */
|
|
pPrevRec = pRecBase;
|
|
pRecBase = pRec;
|
|
}
|
|
else
|
|
nMatchCnt = 0; /* star key */
|
|
break;
|
|
}
|
|
GET_USHORT(nRecLen, &((rec_hdr_ptr_t)pRec)->rsiz);
|
|
if (nRecLen == 0) /* If record length is 0, then block must be bad */
|
|
{
|
|
INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat);
|
|
assert(CDB_STAGNATE > t_tries);
|
|
return cdb_sc_badoffset;
|
|
}
|
|
pPrevRec = pRecBase;
|
|
pRecBase = pRec;
|
|
/* If current compression count > last match, then this record
|
|
also matches on 'last match' characters; keep looping */
|
|
EVAL_CMPC2((rec_hdr_ptr_t)pRec, nTmp);
|
|
if (nTmp > nMatchCnt)
|
|
continue;
|
|
if (nTmp < nMatchCnt)
|
|
/* cc_term: */ { /* Terminated on compression count < previous match,
|
|
this key is after the target */
|
|
if (nRecLen == BSTAR_REC_SIZE && ((blk_hdr_ptr_t)pBlkBase)->levl != 0)
|
|
/* Star key has size of SIZEOF(rec_hdr) + SIZEOF(block_id), make match = 0 */
|
|
nTargLen = 0;
|
|
else
|
|
/* Data block, make match = current compression count */
|
|
nTargLen = nTmp;
|
|
break;
|
|
}
|
|
alt_loop_entry: /* Compression count == match count; Compare current target with current record */
|
|
pRec += SIZEOF(rec_hdr);
|
|
do
|
|
{
|
|
if ((nFlg = *pCurrTarg - *pRec++) != 0)
|
|
break;
|
|
pCurrTarg++;
|
|
} while (--nTargLen);
|
|
if (nFlg > 0)
|
|
nMatchCnt = (int)(pCurrTarg - pTargKeyBase);
|
|
else
|
|
match_term: { /* Key is after target*/
|
|
if (nRecLen == BSTAR_REC_SIZE && (((blk_hdr_ptr_t)pBlkBase)->levl != 0))
|
|
/* Star key has size of SIZEOF(rec_hdr) + SIZEOF(block_id), make match = 0 */
|
|
nTargLen = 0;
|
|
else
|
|
nTargLen = (int)(pCurrTarg - pTargKeyBase);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* clean_up: */
|
|
pStat->prev_rec.offset = (short)(pPrevRec - pBlkBase);
|
|
pStat->prev_rec.match = (short)nMatchCnt;
|
|
pStat->curr_rec.offset = (short)(pRecBase - pBlkBase);
|
|
pStat->curr_rec.match = (short)nTargLen;
|
|
return cdb_sc_normal;
|
|
}
|