blob: 96775c835e8bc62309020f36ebb443abf13e722c [file] [log] [blame]
/**
* 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.
*/
#ifndef SBUF_H
#define SBUF_H
#include <string.h>
#include <stdint.h>
#include "AEEstd.h"
/**
* lightweight serialize/deserialize buffer.
For example
struct sbuf;
//initialize empty buffer;
sbuf_init(&sbuf, 0, 0, 0);
//fill it with data
sbuf_align(&sbuf, 8);
sbuf_write(&sbuf, ptr1, 10);
sbuf_align(&sbuf, 8);
sbuf_write(&sbuf, ptr2, 20);
//allocate the memory needed
mem = malloc(sbuf_needed(&sbuf));
//initialize with the data
sbuf_init(&sbuf, 0, mem, sbuf_needed(&sbuf));
//fill it with data, since it has memory, it will actually copy
sbuf_align(&sbuf, 8);
sbuf_write(&sbuf, ptr1, 10);
sbuf_align(&sbuf, 8);
sbuf_write(&sbuf, ptr2, 20);
See sbuf_q.c for more examples
*/
struct sbuf {
uintptr_t buf; //! start of valid memory
uintptr_t bufEnd; //! end of valid memory
uintptr_t bufStart; //! start with optinal offset from valid mem
uintptr_t bufCur; //! current position, could be outside of valid range
};
/**
* @param buf, the buffer structure instance
* @param offset, this value indicates how far ahead the data buffer is
* start = data - offset
* @param data, the valid memory
* @param dataLen, the length ov valid memory
*/
static __inline void sbuf_init(struct sbuf* buf, int offset, void* data, int dataLen) {
buf->buf = (uintptr_t)data;
buf->bufStart = buf->bufCur = (uintptr_t)data - offset;
buf->bufEnd = (uintptr_t)data + dataLen;
}
//! move the current pointer by len
static __inline void sbuf_advance(struct sbuf* buf, int len) {
buf->bufCur += len;
}
/**
* @retval, the amount of memory needed for everything from the start (with the offset)
* to the current position of the buffer
*/
static __inline int sbuf_needed(struct sbuf* buf) {
return buf->bufCur - buf->bufStart;
}
/**
* @retval, the space left in the buffer. A negative value indicates overflow.
* A positive value includes the offset.
*/
static __inline int sbuf_left(struct sbuf* buf) {
return buf->bufEnd - buf->bufCur;
}
//! @retval the current head pointer
static __inline void* sbuf_head(struct sbuf* buf) {
return (void*)buf->bufCur;
}
//! @retval true if the current pointer is valid
static __inline int sbuf_valid(struct sbuf* buf) {
return buf->bufCur >= buf->buf && buf->bufCur < buf->bufEnd;
}
//! advance the head pointer so the "needed" is aligned to the align value
#define _SBUF_ALIGN(x, y) (((x) + ((y)-1)) & ~((y)-1))
static __inline void sbuf_align(struct sbuf* buf, uint32_t align) {
sbuf_advance(buf, _SBUF_ALIGN(sbuf_needed(buf), align) - sbuf_needed(buf));
}
/**
* Write to the buffer.
* @param src, the memory to read from. Will write srcLen bytes to buf from src
* from the buf's current position. Only the valid portion of data will
* be written.
* @param srcLen, the length of src. The buffer will be advanced by srcLen.
*/
static __inline void sbuf_write(struct sbuf* buf, void *psrc, int srcLen) {
uintptr_t src = (uintptr_t)psrc;
if(buf->bufCur + srcLen > buf->buf) {
int writeLen;
if(buf->bufCur < buf->buf) {
int len = buf->buf - buf->bufCur;
srcLen -= len;
src += len;
sbuf_advance(buf, len);
}
writeLen = STD_MIN(srcLen, sbuf_left(buf));
if(writeLen > 0) {
std_memsmove((void*)buf->bufCur, buf->bufEnd - buf->bufCur, (void*)src, writeLen);
}
}
sbuf_advance(buf, srcLen);
}
/**
* Read from the buffer into dst.
* @param dst, the data to write to. Will write dstLen to dst from buf
* from the current position of buf. Only valid memory
* will be written to dst. Invalid overlapping memory will
* remain untouched.
* @param dstLen, the length of dst. buf will be advanced by dstLen
*/
static __inline void sbuf_read(struct sbuf* buf, void *pdst, int dstLen) {
uintptr_t dst = (uintptr_t)pdst;
if(buf->bufCur + dstLen > buf->buf) {
int readLen;
if(buf->bufCur < buf->buf) {
int len = buf->buf - buf->bufCur;
dstLen -= len;
dst += len;
sbuf_advance(buf, len);
}
readLen = STD_MIN(dstLen, sbuf_left(buf));
if(readLen > 0) {
std_memsmove((void*)dst, dstLen, (void*)buf->bufCur, readLen);
}
}
sbuf_advance(buf, dstLen);
}
#endif