mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-08 03:22:21 +03:00
Add the {quote: StrAccum} object
for accumulating strings. Revamp xprintf to use the new object. Rewrite the group_concat() function to use the new object. Productize and test the group_concat() function. (CVS 4578) FossilOrigin-Name: 221aee72be040769e8026b91648f03c6366a8821
This commit is contained in:
272
src/printf.c
272
src/printf.c
@@ -166,6 +166,20 @@ static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
||||
}
|
||||
#endif /* SQLITE_OMIT_FLOATING_POINT */
|
||||
|
||||
/*
|
||||
** Append N space characters to the given string buffer.
|
||||
*/
|
||||
static void appendSpace(StrAccum *pAccum, int N){
|
||||
static const char zSpaces[] = " ";
|
||||
while( N>=sizeof(zSpaces)-1 ){
|
||||
sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1);
|
||||
N -= sizeof(zSpaces)-1;
|
||||
}
|
||||
if( N>0 ){
|
||||
sqlite3StrAccumAppend(pAccum, zSpaces, N);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** On machines with a small stack size, you can redefine the
|
||||
** SQLITE_PRINT_BUF_SIZE to be less than 350. But beware - for
|
||||
@@ -203,9 +217,8 @@ static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
||||
** seems to make a big difference in determining how fast this beast
|
||||
** will run.
|
||||
*/
|
||||
static int vxprintf(
|
||||
void (*func)(void*,const char*,int), /* Consumer of text */
|
||||
void *arg, /* First argument to the consumer */
|
||||
static void vxprintf(
|
||||
StrAccum *pAccum, /* Accumulate results here */
|
||||
int useExtended, /* Allow extended %-conversions */
|
||||
const char *fmt, /* Format string */
|
||||
va_list ap /* arguments */
|
||||
@@ -215,7 +228,6 @@ static int vxprintf(
|
||||
int precision; /* Precision of the current field */
|
||||
int length; /* Length of the field */
|
||||
int idx; /* A general purpose loop counter */
|
||||
int count; /* Total number of characters output */
|
||||
int width; /* Width of the current field */
|
||||
etByte flag_leftjustify; /* True if "-" flag is present */
|
||||
etByte flag_plussign; /* True if "+" flag is present */
|
||||
@@ -234,9 +246,6 @@ static int vxprintf(
|
||||
etByte errorflag = 0; /* True if an error is encountered */
|
||||
etByte xtype; /* Conversion paradigm */
|
||||
char *zExtra; /* Extra memory used for etTCLESCAPE conversions */
|
||||
static const char spaces[] =
|
||||
" ";
|
||||
#define etSPACESIZE (sizeof(spaces)-1)
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
int exp, e2; /* exponent of real numbers */
|
||||
double rounder; /* Used for rounding floating point values */
|
||||
@@ -246,8 +255,7 @@ static int vxprintf(
|
||||
int nsd; /* Number of significant digits returned */
|
||||
#endif
|
||||
|
||||
func(arg,"",0);
|
||||
count = length = 0;
|
||||
length = 0;
|
||||
bufpt = 0;
|
||||
for(; (c=(*fmt))!=0; ++fmt){
|
||||
if( c!='%' ){
|
||||
@@ -255,14 +263,12 @@ static int vxprintf(
|
||||
bufpt = (char *)fmt;
|
||||
amt = 1;
|
||||
while( (c=(*++fmt))!='%' && c!=0 ) amt++;
|
||||
(*func)(arg,bufpt,amt);
|
||||
count += amt;
|
||||
sqlite3StrAccumAppend(pAccum, bufpt, amt);
|
||||
if( c==0 ) break;
|
||||
}
|
||||
if( (c=(*++fmt))==0 ){
|
||||
errorflag = 1;
|
||||
(*func)(arg,"%",1);
|
||||
count++;
|
||||
sqlite3StrAccumAppend(pAccum, "%", 1);
|
||||
break;
|
||||
}
|
||||
/* Find out what flags are present */
|
||||
@@ -336,14 +342,14 @@ static int vxprintf(
|
||||
if( useExtended || (infop->flags & FLAG_INTERN)==0 ){
|
||||
xtype = infop->type;
|
||||
}else{
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
zExtra = 0;
|
||||
if( infop==0 ){
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -581,7 +587,7 @@ static int vxprintf(
|
||||
#endif
|
||||
break;
|
||||
case etSIZE:
|
||||
*(va_arg(ap,int*)) = count;
|
||||
*(va_arg(ap,int*)) = pAccum->nChar;
|
||||
length = width = 0;
|
||||
break;
|
||||
case etPERCENT:
|
||||
@@ -627,7 +633,7 @@ static int vxprintf(
|
||||
n += i + 1 + needQuote*2;
|
||||
if( n>etBUFSIZE ){
|
||||
bufpt = zExtra = sqlite3_malloc( n );
|
||||
if( bufpt==0 ) return -1;
|
||||
if( bufpt==0 ) return;
|
||||
}else{
|
||||
bufpt = buf;
|
||||
}
|
||||
@@ -647,7 +653,7 @@ static int vxprintf(
|
||||
case etTOKEN: {
|
||||
Token *pToken = va_arg(ap, Token*);
|
||||
if( pToken && pToken->z ){
|
||||
(*func)(arg, (char*)pToken->z, pToken->n);
|
||||
sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n);
|
||||
}
|
||||
length = width = 0;
|
||||
break;
|
||||
@@ -658,10 +664,10 @@ static int vxprintf(
|
||||
struct SrcList_item *pItem = &pSrc->a[k];
|
||||
assert( k>=0 && k<pSrc->nSrc );
|
||||
if( pItem->zDatabase && pItem->zDatabase[0] ){
|
||||
(*func)(arg, pItem->zDatabase, strlen(pItem->zDatabase));
|
||||
(*func)(arg, ".", 1);
|
||||
sqlite3StrAccumAppend(pAccum, pItem->zDatabase, -1);
|
||||
sqlite3StrAccumAppend(pAccum, ".", 1);
|
||||
}
|
||||
(*func)(arg, pItem->zName, strlen(pItem->zName));
|
||||
sqlite3StrAccumAppend(pAccum, pItem->zName, -1);
|
||||
length = width = 0;
|
||||
break;
|
||||
}
|
||||
@@ -675,143 +681,112 @@ static int vxprintf(
|
||||
register int nspace;
|
||||
nspace = width-length;
|
||||
if( nspace>0 ){
|
||||
count += nspace;
|
||||
while( nspace>=etSPACESIZE ){
|
||||
(*func)(arg,spaces,etSPACESIZE);
|
||||
nspace -= etSPACESIZE;
|
||||
}
|
||||
if( nspace>0 ) (*func)(arg,spaces,nspace);
|
||||
appendSpace(pAccum, nspace);
|
||||
}
|
||||
}
|
||||
if( length>0 ){
|
||||
(*func)(arg,bufpt,length);
|
||||
count += length;
|
||||
sqlite3StrAccumAppend(pAccum, bufpt, length);
|
||||
}
|
||||
if( flag_leftjustify ){
|
||||
register int nspace;
|
||||
nspace = width-length;
|
||||
if( nspace>0 ){
|
||||
count += nspace;
|
||||
while( nspace>=etSPACESIZE ){
|
||||
(*func)(arg,spaces,etSPACESIZE);
|
||||
nspace -= etSPACESIZE;
|
||||
}
|
||||
if( nspace>0 ) (*func)(arg,spaces,nspace);
|
||||
appendSpace(pAccum, nspace);
|
||||
}
|
||||
}
|
||||
if( zExtra ){
|
||||
sqlite3_free(zExtra);
|
||||
}
|
||||
}/* End for loop over the format string */
|
||||
return errorflag ? -1 : count;
|
||||
} /* End of function */
|
||||
|
||||
|
||||
/* This structure is used to store state information about the
|
||||
** write to memory that is currently in progress.
|
||||
/*
|
||||
** Append N bytes of text from z to the StrAccum object.
|
||||
*/
|
||||
struct sgMprintf {
|
||||
char *zBase; /* A base allocation */
|
||||
char *zText; /* The string collected so far */
|
||||
int nChar; /* Length of the string so far */
|
||||
int nTotal; /* Output size if unconstrained */
|
||||
int nAlloc; /* Amount of space allocated in zText */
|
||||
void *(*xRealloc)(void*,int); /* Function used to realloc memory */
|
||||
int iMallocFailed; /* True if xRealloc() has failed */
|
||||
};
|
||||
|
||||
/*
|
||||
** This function implements the callback from vxprintf.
|
||||
**
|
||||
** This routine add nNewChar characters of text in zNewText to
|
||||
** the sgMprintf structure pointed to by "arg".
|
||||
*/
|
||||
static void mout(void *arg, const char *zNewText, int nNewChar){
|
||||
struct sgMprintf *pM = (struct sgMprintf*)arg;
|
||||
if( pM->iMallocFailed ) return;
|
||||
pM->nTotal += nNewChar;
|
||||
if( pM->zText ){
|
||||
if( pM->nChar + nNewChar + 1 > pM->nAlloc ){
|
||||
if( pM->xRealloc==0 ){
|
||||
nNewChar = pM->nAlloc - pM->nChar - 1;
|
||||
}else{
|
||||
int nAlloc = pM->nChar + nNewChar*2 + 1;
|
||||
if( pM->zText==pM->zBase ){
|
||||
pM->zText = pM->xRealloc(0, nAlloc);
|
||||
if( pM->zText==0 ){
|
||||
pM->nAlloc = 0;
|
||||
pM->iMallocFailed = 1;
|
||||
return;
|
||||
}else if( pM->nChar ){
|
||||
memcpy(pM->zText, pM->zBase, pM->nChar);
|
||||
}
|
||||
}else{
|
||||
char *zNew;
|
||||
zNew = pM->xRealloc(pM->zText, nAlloc);
|
||||
if( zNew ){
|
||||
pM->zText = zNew;
|
||||
}else{
|
||||
pM->iMallocFailed = 1;
|
||||
pM->xRealloc(pM->zText, 0);
|
||||
pM->zText = 0;
|
||||
pM->nAlloc = 0;
|
||||
return;
|
||||
}
|
||||
void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
if( p->tooBig | p->mallocFailed ){
|
||||
return;
|
||||
}
|
||||
if( N<0 ){
|
||||
N = strlen(z);
|
||||
}
|
||||
if( N==0 ){
|
||||
return;
|
||||
}
|
||||
if( p->nChar+N >= p->nAlloc ){
|
||||
char *zNew;
|
||||
if( !p->useMalloc ){
|
||||
p->tooBig = 1;
|
||||
N = p->nAlloc - p->nChar - 1;
|
||||
if( N<=0 ){
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
p->nAlloc += p->nAlloc + N + 1;
|
||||
if( p->nAlloc > SQLITE_MAX_LENGTH ){
|
||||
p->nAlloc = SQLITE_MAX_LENGTH;
|
||||
if( p->nChar+N >= p->nAlloc ){
|
||||
sqlite3StrAccumReset(p);
|
||||
p->tooBig = 1;
|
||||
return;
|
||||
}
|
||||
pM->nAlloc = nAlloc;
|
||||
}
|
||||
}
|
||||
if( nNewChar>0 ){
|
||||
memcpy(&pM->zText[pM->nChar], zNewText, nNewChar);
|
||||
pM->nChar += nNewChar;
|
||||
}
|
||||
pM->zText[pM->nChar] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is a wrapper around xprintf() that invokes mout() as
|
||||
** the consumer.
|
||||
*/
|
||||
static char *base_vprintf(
|
||||
void *(*xRealloc)(void*, int), /* realloc() function. May be NULL */
|
||||
int useInternal, /* Use internal %-conversions if true */
|
||||
char *zInitBuf, /* Initially write here, before mallocing */
|
||||
int nInitBuf, /* Size of zInitBuf[] */
|
||||
const char *zFormat, /* format string */
|
||||
va_list ap /* arguments */
|
||||
){
|
||||
struct sgMprintf sM;
|
||||
sM.zBase = sM.zText = zInitBuf;
|
||||
sM.nChar = sM.nTotal = 0;
|
||||
sM.nAlloc = nInitBuf;
|
||||
sM.xRealloc = xRealloc;
|
||||
sM.iMallocFailed = 0;
|
||||
vxprintf(mout, &sM, useInternal, zFormat, ap);
|
||||
assert(sM.iMallocFailed==0 || sM.zText==0);
|
||||
if( xRealloc && !sM.iMallocFailed ){
|
||||
if( sM.zText==sM.zBase ){
|
||||
sM.zText = xRealloc(0, sM.nChar+1);
|
||||
if( sM.zText ){
|
||||
memcpy(sM.zText, sM.zBase, sM.nChar+1);
|
||||
}
|
||||
}else if( sM.nAlloc>sM.nChar+10 ){
|
||||
char *zNew;
|
||||
sqlite3MallocBenignFailure(1);
|
||||
zNew = xRealloc(sM.zText, sM.nChar+1);
|
||||
zNew = sqlite3_malloc( p->nAlloc );
|
||||
if( zNew ){
|
||||
sM.zText = zNew;
|
||||
memcpy(zNew, p->zText, p->nChar);
|
||||
sqlite3StrAccumReset(p);
|
||||
p->zText = zNew;
|
||||
}else{
|
||||
p->mallocFailed = 1;
|
||||
sqlite3StrAccumReset(p);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sM.zText;
|
||||
memcpy(&p->zText[p->nChar], z, N);
|
||||
p->nChar += N;
|
||||
}
|
||||
|
||||
/*
|
||||
** Realloc that is a real function, not a macro.
|
||||
** Finish off a string by making sure it is zero-terminated.
|
||||
** Return a pointer to the resulting string. Return a NULL
|
||||
** pointer if any kind of error was encountered.
|
||||
*/
|
||||
static void *printf_realloc(void *old, int size){
|
||||
return sqlite3_realloc(old, size);
|
||||
char *sqlite3StrAccumFinish(StrAccum *p){
|
||||
if( p->zText ){
|
||||
p->zText[p->nChar] = 0;
|
||||
if( p->useMalloc && p->zText==p->zBase ){
|
||||
p->zText = sqlite3_malloc( p->nChar+1 );
|
||||
if( p->zText ){
|
||||
memcpy(p->zText, p->zBase, p->nChar+1);
|
||||
}else{
|
||||
p->mallocFailed = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return p->zText;
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset an StrAccum string. Reclaim all malloced memory.
|
||||
*/
|
||||
void sqlite3StrAccumReset(StrAccum *p){
|
||||
if( p->zText!=p->zBase ){
|
||||
sqlite3_free(p->zText);
|
||||
p->zText = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize a string accumulator
|
||||
*/
|
||||
static void sqlite3StrAccumInit(StrAccum *p, char *zBase, int n){
|
||||
p->zText = p->zBase = zBase;
|
||||
p->nChar = 0;
|
||||
p->nAlloc = n;
|
||||
p->useMalloc = 1;
|
||||
p->tooBig = 0;
|
||||
p->mallocFailed = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -821,8 +796,11 @@ static void *printf_realloc(void *old, int size){
|
||||
char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){
|
||||
char *z;
|
||||
char zBase[SQLITE_PRINT_BUF_SIZE];
|
||||
z = base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap);
|
||||
if( z==0 && db!=0 ){
|
||||
StrAccum acc;
|
||||
sqlite3StrAccumInit(&acc, zBase, sizeof(zBase));
|
||||
vxprintf(&acc, 1, zFormat, ap);
|
||||
z = sqlite3StrAccumFinish(&acc);
|
||||
if( acc.mallocFailed && db ){
|
||||
db->mallocFailed = 1;
|
||||
}
|
||||
return z;
|
||||
@@ -835,13 +813,9 @@ char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){
|
||||
char *sqlite3MPrintf(sqlite3 *db, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
char *z;
|
||||
char zBase[SQLITE_PRINT_BUF_SIZE];
|
||||
va_start(ap, zFormat);
|
||||
z = base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap);
|
||||
z = sqlite3VMPrintf(db, zFormat, ap);
|
||||
va_end(ap);
|
||||
if( z==0 && db!=0 ){
|
||||
db->mallocFailed = 1;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
@@ -850,8 +824,13 @@ char *sqlite3MPrintf(sqlite3 *db, const char *zFormat, ...){
|
||||
** %-conversion extensions.
|
||||
*/
|
||||
char *sqlite3_vmprintf(const char *zFormat, va_list ap){
|
||||
char *z;
|
||||
char zBase[SQLITE_PRINT_BUF_SIZE];
|
||||
return base_vprintf(sqlite3_realloc, 0, zBase, sizeof(zBase), zFormat, ap);
|
||||
StrAccum acc;
|
||||
sqlite3StrAccumInit(&acc, zBase, sizeof(zBase));
|
||||
vxprintf(&acc, 0, zFormat, ap);
|
||||
z = sqlite3StrAccumFinish(&acc);
|
||||
return z;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -876,14 +855,17 @@ char *sqlite3_mprintf(const char *zFormat, ...){
|
||||
char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
|
||||
char *z;
|
||||
va_list ap;
|
||||
StrAccum acc;
|
||||
|
||||
if( n<=0 ){
|
||||
return zBuf;
|
||||
}
|
||||
zBuf[0] = 0;
|
||||
sqlite3StrAccumInit(&acc, zBuf, n);
|
||||
acc.useMalloc = 0;
|
||||
va_start(ap,zFormat);
|
||||
z = base_vprintf(0, 0, zBuf, n, zFormat, ap);
|
||||
vxprintf(&acc, 0, zFormat, ap);
|
||||
va_end(ap);
|
||||
z = sqlite3StrAccumFinish(&acc);
|
||||
return z;
|
||||
}
|
||||
|
||||
@@ -894,11 +876,13 @@ char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
|
||||
** and segfaults if you give it a long long int.
|
||||
*/
|
||||
void sqlite3DebugPrintf(const char *zFormat, ...){
|
||||
extern int getpid(void);
|
||||
va_list ap;
|
||||
StrAccum acc;
|
||||
char zBuf[500];
|
||||
va_start(ap, zFormat);
|
||||
base_vprintf(0, 0, zBuf, sizeof(zBuf), zFormat, ap);
|
||||
sqlite3StrAccumInit(&acc, zBuf, sizeof(zBuf));
|
||||
acc.useMalloc = 0;
|
||||
va_start(ap,zFormat);
|
||||
vxprintf(&acc, 0, zFormat, ap);
|
||||
va_end(ap);
|
||||
fprintf(stdout,"%s", zBuf);
|
||||
fflush(stdout);
|
||||
|
||||
Reference in New Issue
Block a user