219 lines
7.7 KiB
C
219 lines
7.7 KiB
C
|
/****************************************************************
|
||
|
* *
|
||
|
* Copyright 2010 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. *
|
||
|
* *
|
||
|
****************************************************************/
|
||
|
|
||
|
#include "mdef.h"
|
||
|
|
||
|
#include "gtm_string.h"
|
||
|
|
||
|
#include "strpiecediff.h"
|
||
|
#include "matchc.h"
|
||
|
#include "stringpool.h"
|
||
|
|
||
|
#define PIECEDIFF_START_BUFSIZ 64
|
||
|
|
||
|
#define OLD_END_SEEN (1 << 0)
|
||
|
#define NEW_END_SEEN (1 << 1)
|
||
|
#define NO_END_SEEN 0
|
||
|
#define BOTH_END_SEEN (OLD_END_SEEN | NEW_END_SEEN)
|
||
|
|
||
|
#define SKIP_PIECES(DELIM_LEN, DELIM_PTR, LEN, PTR, END, TOP, SKIPPCS, END_SEEN, MASK) \
|
||
|
{ \
|
||
|
int match_res, skppcs; \
|
||
|
\
|
||
|
if (LEN) \
|
||
|
{ \
|
||
|
skppcs = SKIPPCS; /* store in local so input parameter is untouched */ \
|
||
|
END = (char *)(*match_fn)(DELIM_LEN, DELIM_PTR, LEN, (unsigned char *)PTR, &match_res, &skppcs);\
|
||
|
if (0 != match_res) \
|
||
|
{ \
|
||
|
LEN -= INTCAST(END - PTR); \
|
||
|
PTR = END; \
|
||
|
assert(PTR + LEN == TOP); \
|
||
|
} else \
|
||
|
{ \
|
||
|
LEN = 0; /* Could not skip all specified # of pieces ("skippcs") */ \
|
||
|
END_SEEN |= MASK; \
|
||
|
} \
|
||
|
} else \
|
||
|
END_SEEN |= MASK; \
|
||
|
}
|
||
|
|
||
|
#define MATCH_CURPIECE(DELIM_LEN, DELIM_PTR, LEN, PTR, MATCH_LEN, END, END_SEEN, MASK, PIECE_EXISTS) \
|
||
|
{ \
|
||
|
int match_res, numpcs; \
|
||
|
\
|
||
|
if (!(MASK & END_SEEN)) \
|
||
|
{ \
|
||
|
numpcs = 1; \
|
||
|
PIECE_EXISTS = TRUE; \
|
||
|
END = (char *)(*match_fn)(DELIM_LEN, DELIM_PTR, LEN, (unsigned char *)PTR, &match_res, &numpcs);\
|
||
|
MATCH_LEN = INTCAST(END - PTR); \
|
||
|
if (0 == match_res) \
|
||
|
{ \
|
||
|
assert(1 == numpcs); \
|
||
|
END_SEEN |= MASK; \
|
||
|
} else \
|
||
|
{ \
|
||
|
assert((0 == numpcs) && ((PTR + DELIM_LEN) <= END)); \
|
||
|
MATCH_LEN -= DELIM_LEN; \
|
||
|
} \
|
||
|
} else \
|
||
|
{ \
|
||
|
PIECE_EXISTS = FALSE; \
|
||
|
MATCH_LEN = 0; \
|
||
|
} \
|
||
|
}
|
||
|
|
||
|
#define UPDATE_PTR_LEN(LEN, PTR, END_SEEN, MASK, END, TOP) \
|
||
|
{ \
|
||
|
if (!(MASK & END_SEEN)) \
|
||
|
{ \
|
||
|
LEN -= INTCAST(END - PTR); \
|
||
|
PTR = END; \
|
||
|
assert(PTR + LEN == TOP); \
|
||
|
} \
|
||
|
}
|
||
|
|
||
|
typedef unsigned char * (*match_fnptr)(int del_len, unsigned char *del_str, int src_len,
|
||
|
unsigned char *src_str, int *res, int *numpcs);
|
||
|
|
||
|
/* Finds the list of pieces which differ between oldstr and newstr using "delim" as the delimiter.
|
||
|
* An optional piecelist can specify pieces of interest. If NULL, all pieces that differ are returned.
|
||
|
* use_matchc controls whether byte-oriented or character-oriented match is done.
|
||
|
* If TRUE, matchc() is used. If FALSE, matchb() is used.
|
||
|
*/
|
||
|
void strpiecediff(mstr *oldstr, mstr *newstr, mstr *delim,
|
||
|
uint4 numpieces, gtm_num_range_t *piecearray, boolean_t use_matchc, mstr *pcdiff_mstr)
|
||
|
{
|
||
|
int curpiece, bufflen, newbufflen, skippcs, save_skippcs;
|
||
|
int delim_len, old_len, new_len, match_res, old_match_len, new_match_len, pcdiff_len;
|
||
|
uint4 nextmin, nextmax, prev_nextmin;
|
||
|
char *old_ptr, *old_top, *new_ptr, *new_top, *next_ptr, *old_end, *new_end;
|
||
|
uchar_ptr_t delim_ptr;
|
||
|
static char pcdiff_buff[PIECEDIFF_START_BUFSIZ], *pcdiff_start = &pcdiff_buff[0], *pcdiff_top = ARRAYTOP(pcdiff_buff);
|
||
|
char *pcdiff_ptr = pcdiff_start, *tmpbuff;
|
||
|
match_fnptr match_fn;
|
||
|
uint4 end_seen;
|
||
|
boolean_t old_piece_exists, new_piece_exists;
|
||
|
# ifdef DEBUG
|
||
|
uint4 save_numpieces;
|
||
|
gtm_num_range_t *save_piecearray;
|
||
|
# endif
|
||
|
|
||
|
/* Initialize oldstr related fields */
|
||
|
assert(NULL != oldstr);
|
||
|
old_ptr = oldstr->addr;
|
||
|
old_len = oldstr->len;
|
||
|
old_top = old_ptr + old_len;
|
||
|
assert(old_ptr <= old_top);
|
||
|
/* Initialize newstr related fields */
|
||
|
assert(NULL != newstr);
|
||
|
new_ptr = newstr->addr;
|
||
|
new_len = newstr->len;
|
||
|
new_top = new_ptr + newstr->len;
|
||
|
assert(new_ptr <= new_top);
|
||
|
/* Initialize delim related fields */
|
||
|
assert(NULL != delim);
|
||
|
delim_ptr = (uchar_ptr_t)delim->addr;
|
||
|
delim_len = delim->len;
|
||
|
assert(0 < delim_len);
|
||
|
/* Initialize piecelist related fields */
|
||
|
DEBUG_ONLY(save_numpieces = numpieces;) /* for debugging purposes */
|
||
|
DEBUG_ONLY(save_piecearray = piecearray;) /* for debugging purposes */
|
||
|
if (0 != numpieces)
|
||
|
{
|
||
|
nextmin = piecearray[0].min;
|
||
|
nextmax = piecearray[0].max;
|
||
|
assert(nextmin <= nextmax);
|
||
|
piecearray++;
|
||
|
numpieces--;
|
||
|
} else
|
||
|
{
|
||
|
nextmin = 1;
|
||
|
nextmax = MAXPOSINT4;
|
||
|
}
|
||
|
DEBUG_ONLY(prev_nextmin = nextmin;)
|
||
|
/* Compute list of pieces that differ */
|
||
|
curpiece = 1;
|
||
|
match_fn = use_matchc ? matchc : matchb;
|
||
|
end_seen = NO_END_SEEN; /* end has not been seen in either old nor new */
|
||
|
for ( ; BOTH_END_SEEN != end_seen; )
|
||
|
{
|
||
|
assert(nextmin >= curpiece);
|
||
|
skippcs = nextmin - curpiece;
|
||
|
if (skippcs)
|
||
|
{ /* Scanpoint is currently at piece # "curpiece" but user has specified that the next piece of interest
|
||
|
* is "nextmin". So skip processing all intermediate pieces in both old and new strings.
|
||
|
*/
|
||
|
DEBUG_ONLY(save_skippcs = skippcs;)
|
||
|
SKIP_PIECES(delim_len, delim_ptr, old_len, old_ptr, old_end, old_top, skippcs, end_seen, OLD_END_SEEN);
|
||
|
assert(save_skippcs == skippcs);
|
||
|
SKIP_PIECES(delim_len, delim_ptr, new_len, new_ptr, new_end, new_top, skippcs, end_seen, NEW_END_SEEN);
|
||
|
assert(save_skippcs == skippcs);
|
||
|
if (BOTH_END_SEEN == end_seen)
|
||
|
break;
|
||
|
}
|
||
|
curpiece = nextmin;
|
||
|
MATCH_CURPIECE(delim_len, delim_ptr, old_len, old_ptr, old_match_len, old_end, end_seen,
|
||
|
OLD_END_SEEN, old_piece_exists);
|
||
|
MATCH_CURPIECE(delim_len, delim_ptr, new_len, new_ptr, new_match_len, new_end, end_seen,
|
||
|
NEW_END_SEEN, new_piece_exists);
|
||
|
if ((FALSE == old_piece_exists) && (FALSE == new_piece_exists))
|
||
|
break; /* both pieces do not exist. no more diff possible. break out */
|
||
|
else if ((old_piece_exists != new_piece_exists)
|
||
|
|| (old_match_len != new_match_len) || (0 != memcmp(old_ptr, new_ptr, old_match_len)))
|
||
|
{ /* Pieces differ. Add "curpiece" to list of differing pieces. First check if the piece # can
|
||
|
* be stored in the remaining buffer (use maximum possible piece # for this calculation).
|
||
|
*/
|
||
|
if (pcdiff_top < (pcdiff_ptr + MAX_DIGITS_IN_INT + 1)) /* + 1 is for a potential ',' */
|
||
|
{ /* No space to store next piece #. Expand buffer. */
|
||
|
assert(pcdiff_ptr != &pcdiff_buff[0]);
|
||
|
bufflen = INTCAST(pcdiff_top - pcdiff_start);
|
||
|
newbufflen = bufflen * 2;
|
||
|
tmpbuff = (char *)malloc(newbufflen); /* expand by doubling buffer size */
|
||
|
memcpy(tmpbuff, pcdiff_start, bufflen);
|
||
|
if (pcdiff_start != &pcdiff_buff[0])
|
||
|
free(pcdiff_start); /* obtained by malloc. so free it */
|
||
|
pcdiff_len = INTCAST(pcdiff_ptr - pcdiff_start);
|
||
|
pcdiff_start = tmpbuff;
|
||
|
pcdiff_ptr = tmpbuff + pcdiff_len;
|
||
|
pcdiff_top = tmpbuff + newbufflen;
|
||
|
}
|
||
|
assert(pcdiff_top >= (pcdiff_ptr + MAX_DIGITS_IN_INT + 1));
|
||
|
if (pcdiff_ptr != pcdiff_start)
|
||
|
*pcdiff_ptr++ = ','; /* Not the first piece in this list so prefix piece # with a ',' */
|
||
|
I2A_INLINE(pcdiff_ptr, curpiece);
|
||
|
assert(pcdiff_ptr > pcdiff_start);
|
||
|
assert(pcdiff_ptr <= pcdiff_top);
|
||
|
}
|
||
|
UPDATE_PTR_LEN(old_len, old_ptr, end_seen, OLD_END_SEEN, old_end, old_top);
|
||
|
UPDATE_PTR_LEN(new_len, new_ptr, end_seen, NEW_END_SEEN, new_end, new_top);
|
||
|
curpiece++;
|
||
|
assert(nextmin <= nextmax);
|
||
|
if (nextmin == nextmax)
|
||
|
{ /* Find next range */
|
||
|
if (0 == numpieces)
|
||
|
break;
|
||
|
nextmin = piecearray[0].min;
|
||
|
nextmax = piecearray[0].max;
|
||
|
piecearray++;
|
||
|
numpieces--;
|
||
|
assert(prev_nextmin < nextmin);
|
||
|
DEBUG_ONLY(prev_nextmin = nextmin;)
|
||
|
} else
|
||
|
nextmin++;
|
||
|
}
|
||
|
pcdiff_mstr->len = INTCAST(pcdiff_ptr - pcdiff_start);
|
||
|
pcdiff_mstr->addr = pcdiff_start;
|
||
|
assert(',' != *pcdiff_start);
|
||
|
return;
|
||
|
}
|