diff options
Diffstat (limited to 'src/lib/BufBound.c')
-rw-r--r-- | src/lib/BufBound.c | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/src/lib/BufBound.c b/src/lib/BufBound.c new file mode 100644 index 0000000..ab85e86 --- /dev/null +++ b/src/lib/BufBound.c @@ -0,0 +1,228 @@ +/* +* Copyright (c) 2017, The Linux Foundation. All rights reserved. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its contributors +* may be used to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ +#include <limits.h> +#include "AEEBufBound.h" +#include "AEEstd.h" + +/* Note on bounds-checking logic and saturation: + Simple pointer comparisons are not adequate for bounds checking. pcBuf + and pcEnd are assumed to be valid pointers in the address space. But + pcWrite is not ... it is a theoretical value that can exceed pcEnd, and + may in fact wrap around the end of the address space. In that case the + test for (pcWrite < pcEnd) will yield TRUE, although pcWrite is outside + the buffer. Use (pcEnd-pcWrite) > 0 to be accurate. + + In order to ensure this works in all cases, we need to avoid integer + overflows. We do this by restricting pcWrite to the range + [pcBuf..pcBuf+INT_MAX]. The ensures that pcWrite-pcBuf and pcWrite-pcBuf + will always be valid integers. It also allows us to ensure that + BufBound_Wrote() will not return wildly misleading results. + + PCSAT + pcBuf pcEnd pcBuf+MAXINT + |-------------------| . . . . . . . . . | + ^ ^ + pcWrite: (a) (b) */ + +#define PCSAT(me) ((me)->pcBuf + INT_MAX) + + +/* Advance me->pcWrite, saturating. + On entry: + *pnLen = number of bytes to be written (non-negative) + On exit: + return value = where to write (pointer into the buffer) + *pnLen = number of bytes to write */ +static char *BufBound_ValidateWrite(BufBound *me, int *pnLen) +{ + int nLen = *pnLen; + char *pcWrite = me->pcWrite; + int nMaxCopy = me->pcEnd - pcWrite; /* could be negative! */ + + if (nMaxCopy < nLen) + { + /* Must check PCSAT to validate advance */ + int nMaxAdvance = PCSAT(me) - pcWrite; /* max amount to advance */ + + if (nLen > nMaxAdvance) + { + nLen = nMaxAdvance; + } + if (nMaxCopy < 0) + { + nMaxCopy = 0; + } + } + else + { + /* Simple case: all fits in the buffer */ + nMaxCopy = nLen; + } + + *pnLen = nMaxCopy; + me->pcWrite = pcWrite + nLen; + return pcWrite; +} + +void BufBound_Write(BufBound *me, const char *pc, int nLen) +{ + if (nLen > 0) + { + char *pcDest = BufBound_ValidateWrite(me, &nLen); + + while (--nLen >= 0) + { + pcDest[nLen] = pc[nLen]; + } + } +} + +void BufBound_Putnc(BufBound *me, char c, int nLen) +{ + if (nLen > 0) + { + char *pcDest = BufBound_ValidateWrite(me, &nLen); + + while (--nLen >= 0) + { + pcDest[nLen] = c; + } + } +} + +void BufBound_Advance(BufBound *me, int nLen) +{ + uint32 uOffset = (uint32)((me->pcWrite - me->pcBuf) + nLen); + + if (uOffset > INT_MAX) + { + uOffset = INT_MAX; + if (nLen < 0) + { + uOffset = 0; + } + } + me->pcWrite = me->pcBuf + uOffset; +} + +void BufBound_Init(BufBound *me, char *pBuf, int nLen) +{ + if (nLen < 0) + { + nLen = 0; + } + me->pcWrite = me->pcBuf = pBuf; + me->pcEnd = pBuf + nLen; +} + +void BufBound_Putc(BufBound *me, char c) +{ + if ((me->pcEnd - me->pcWrite) > 0) + { + *me->pcWrite++ = c; + } + else if (me->pcWrite != PCSAT(me)) + { + ++me->pcWrite; + } +} + +void BufBound_ForceNullTerm(BufBound *me) +{ + if ((me->pcEnd - me->pcWrite) > 0) + { + *me->pcWrite++ = '\0'; + } + else + { + if (me->pcWrite != PCSAT(me)) + { + ++me->pcWrite; + } + /* Ensure null termination if non-empty buffer */ + if (me->pcEnd != me->pcBuf) + { + me->pcEnd[-1] = '\0'; + } + } +} + +void BufBound_Puts(BufBound *me, const char *cpsz) +{ + BufBound_Write(me, cpsz, std_strlen(cpsz)); +} + +int BufBound_BufSize(BufBound *me) +{ + return me->pcEnd - me->pcBuf; +} + +int BufBound_Left(BufBound *me) +{ + return (me->pcEnd - me->pcWrite); +} + +int BufBound_ReallyWrote(BufBound *me) +{ + return STD_MIN(me->pcEnd - me->pcBuf, me->pcWrite - me->pcBuf); +} + +int BufBound_Wrote(BufBound *me) +{ + return (me->pcWrite - me->pcBuf); +} + +void BufBound_WriteLE(BufBound *me, + const void *pvSrc, int nSrcSize, + const char *pszFields) +{ + if (nSrcSize > 0) + { + int nLen = nSrcSize; + char *pcDest = BufBound_ValidateWrite(me, &nLen); + + (void)std_CopyLE(pcDest, nLen, pvSrc, nSrcSize, pszFields); + } +} + +void BufBound_WriteBE(BufBound *me, + const void *pvSrc, int nSrcSize, + const char *pszFields) +{ + if (nSrcSize > 0) + { + int nLen = nSrcSize; + char *pcDest = BufBound_ValidateWrite(me, &nLen); + + (void)std_CopyBE(pcDest, nLen, pvSrc, nSrcSize, pszFields); + } +} + |