| /* |
| * Copyright (c) 2019, The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * 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. |
| * * Neither the name of The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. |
| */ |
| /*============================================================================== |
| |
| FILE: AEEBufBound.c |
| |
| SERVICES: |
| AEEBufBound APIs |
| |
| GENERAL DESCRIPTION: |
| AEEBufBound provides a "bounded buffer" API that facilitates |
| measuring strings or character output. It's design accomodates |
| the implementation of functions that can have the same exact logic |
| for measuring and outputting char buffer content. |
| |
| REVISION HISTORY: |
| Sun Mar 06 11:23:10 2005 Created |
| |
| ==============================================================================*/ |
| #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); |
| } |
| } |