X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcommon%2Fstrbuf.c;h=320e912932ac86ca1c9a15979a522a22e38cc98e;hb=0d8e8a95338560a1e0d96258d6def311cf2ad368;hp=66c07b875d912f820483a261b77395d21661b2b8;hpb=725afdee94b176cd6c0e4d591745a176f831b0c0;p=cc65 diff --git a/src/common/strbuf.c b/src/common/strbuf.c index 66c07b875..320e91293 100644 --- a/src/common/strbuf.c +++ b/src/common/strbuf.c @@ -6,8 +6,8 @@ /* */ /* */ /* */ -/* (C) 2001-2004 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* (C) 2001-2008 Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ @@ -34,10 +34,14 @@ #include +#include /* common */ -#include "xmalloc.h" +#include "chartype.h" #include "strbuf.h" +#include "va_copy.h" +#include "xmalloc.h" +#include "xsprintf.h" @@ -58,22 +62,37 @@ const StrBuf EmptyStrBuf = STATIC_STRBUF_INITIALIZER; -StrBuf* InitStrBuf (StrBuf* B) +#if !defined(HAVE_INLINE) +StrBuf* SB_Init (StrBuf* B) /* Initialize a string buffer */ +{ + *B = EmptyStrBuf; + return B; +} +#endif + + + +StrBuf* SB_InitFromString (StrBuf* B, const char* S) +/* Initialize a string buffer from a literal string. Beware: The buffer won't + * store a copy but a pointer to the actual string. + */ { B->Allocated = 0; - B->Len = 0; + B->Len = strlen (S); B->Index = 0; - B->Buf = 0; + B->Buf = (char*) S; return B; } -void DoneStrBuf (StrBuf* B) +void SB_Done (StrBuf* B) /* Free the data of a string buffer (but not the struct itself) */ { - xfree (B->Buf); + if (B->Allocated) { + xfree (B->Buf); + } } @@ -85,7 +104,7 @@ StrBuf* NewStrBuf (void) StrBuf* B = xmalloc (sizeof (StrBuf)); /* Initialize the struct... */ - InitStrBuf (B); + SB_Init (B); /* ...and return it */ return B; @@ -96,13 +115,13 @@ StrBuf* NewStrBuf (void) void FreeStrBuf (StrBuf* B) /* Free a string buffer */ { - DoneStrBuf (B); + SB_Done (B); xfree (B); } -static void SB_Realloc (StrBuf* B, unsigned NewSize) +void SB_Realloc (StrBuf* B, unsigned NewSize) /* Reallocate the string buffer space, make sure at least NewSize bytes are * available. */ @@ -118,8 +137,50 @@ static void SB_Realloc (StrBuf* B, unsigned NewSize) NewAllocated *= 2; } - /* Reallocate the buffer */ - B->Buf = xrealloc (B->Buf, NewAllocated); + /* Reallocate the buffer. Beware: The allocated size may be zero while the + * length is not. This means that we have a buffer that wasn't allocated + * on the heap. + */ + if (B->Allocated) { + /* Just reallocate the block */ + B->Buf = xrealloc (B->Buf, NewAllocated); + } else { + /* Allocate a new block and copy */ + B->Buf = memcpy (xmalloc (NewAllocated), B->Buf, B->Len); + } + + /* Remember the new block size */ + B->Allocated = NewAllocated; +} + + + +static void SB_CheapRealloc (StrBuf* B, unsigned NewSize) +/* Reallocate the string buffer space, make sure at least NewSize bytes are + * available. This function won't copy the old buffer contents over to the new + * buffer and may be used if the old contents are overwritten later. + */ +{ + /* Get the current size, use a minimum of 8 bytes */ + unsigned NewAllocated = B->Allocated; + if (NewAllocated == 0) { + NewAllocated = 8; + } + + /* Round up to the next power of two */ + while (NewAllocated < NewSize) { + NewAllocated *= 2; + } + + /* Free the old buffer if there is one */ + if (B->Allocated) { + xfree (B->Buf); + } + + /* Allocate a fresh block */ + B->Buf = xmalloc (NewAllocated); + + /* Remember the new block size */ B->Allocated = NewAllocated; } @@ -164,11 +225,13 @@ void SB_Terminate (StrBuf* B) void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size) /* Copy Buf to Target, discarding the old contents of Target */ -{ - if (Target->Allocated < Size) { - SB_Realloc (Target, Size); +{ + if (Size) { + if (Target->Allocated < Size) { + SB_CheapRealloc (Target, Size); + } + memcpy (Target->Buf, Buf, Size); } - memcpy (Target->Buf, Buf, Size); Target->Len = Size; } @@ -267,8 +330,9 @@ void SB_Slice (StrBuf* Target, const StrBuf* Source, unsigned Start, unsigned Le /* Target will be empty */ SB_Clear (Target); return; - } else if (Start + Len > Source->Len) { - Len = (Start + Len) - Source->Len; + } + if (Start + Len > Source->Len) { + Len = Source->Len - Start; } /* Make sure we have enough room in the target string buffer */ @@ -289,7 +353,7 @@ void SB_Move (StrBuf* Target, StrBuf* Source) */ { /* Free the target string */ - if (Target->Buf) { + if (Target->Allocated) { xfree (Target->Buf); } @@ -297,14 +361,42 @@ void SB_Move (StrBuf* Target, StrBuf* Source) *Target = *Source; /* Clear Source */ - InitStrBuf (Source); + SB_Init (Source); +} + + + +void SB_ToLower (StrBuf* S) +/* Convert all characters in S to lower case */ +{ + unsigned I; + char* B = S->Buf; + for (I = 0; I < S->Len; ++I, ++B) { + if (IsUpper (*B)) { + *B = tolower (*B); + } + } +} + + + +void SB_ToUpper (StrBuf* S) +/* Convert all characters in S to upper case */ +{ + unsigned I; + char* B = S->Buf; + for (I = 0; I < S->Len; ++I, ++B) { + if (IsLower (*B)) { + *B = toupper (*B); + } + } } int SB_Compare (const StrBuf* S1, const StrBuf* S2) /* Do a lexical compare of S1 and S2. See strcmp for result codes. */ -{ +{ int Result; if (S1->Len < S2->Len) { Result = memcmp (S1->Buf, S2->Buf, S1->Len); @@ -326,3 +418,78 @@ int SB_Compare (const StrBuf* S1, const StrBuf* S2) +int SB_CompareStr (const StrBuf* S1, const char* S2) +/* Do a lexical compare of S1 and S2. See strcmp for result codes. */ +{ + int Result; + unsigned S2Len = strlen (S2); + if (S1->Len < S2Len) { + Result = memcmp (S1->Buf, S2, S1->Len); + if (Result == 0) { + /* S1 considered lesser because it's shorter */ + Result = -1; + } + } else if (S1->Len > S2Len) { + Result = memcmp (S1->Buf, S2, S2Len); + if (Result == 0) { + /* S2 considered lesser because it's shorter */ + Result = 1; + } + } else { + Result = memcmp (S1->Buf, S2, S1->Len); + } + return Result; +} + + + +void SB_VPrintf (StrBuf* S, const char* Format, va_list ap) +/* printf function with S as target. The function is safe, which means that + * the current contents of S are discarded, and are allocated again with + * a matching size for the output. The function will call FAIL when problems + * are detected (anything that let xsnprintf return -1). + */ +{ + va_list tmp; + int SizeNeeded; + + /* Since we must determine the space needed anyway, we will try with + * the currently allocated memory. If the call succeeds, we've saved + * an allocation. If not, we have to reallocate and try again. + */ + va_copy (tmp, ap); + SizeNeeded = xvsnprintf (S->Buf, S->Allocated, Format, tmp); + va_end (tmp); + + /* Check the result, the xvsnprintf function should not fail */ + CHECK (SizeNeeded >= 0); + + /* Check if we must reallocate */ + if ((unsigned) SizeNeeded >= S->Allocated) { + /* Must retry. Use CheapRealloc to avoid copying */ + SB_CheapRealloc (S, SizeNeeded + 1); /* Account for '\0' */ + (void) xvsnprintf (S->Buf, S->Allocated, Format, ap); + } + + /* Update string buffer variables */ + S->Len = SizeNeeded; + S->Index = 0; +} + + + +void SB_Printf (StrBuf* S, const char* Format, ...) +/* vprintf function with S as target. The function is safe, which means that + * the current contents of S are discarded, and are allocated again with + * a matching size for the output. The function will call FAIL when problems + * are detected (anything that let xsnprintf return -1). + */ +{ + va_list ap; + va_start (ap, Format); + SB_VPrintf (S, Format, ap); + va_end (ap); +} + + +