/*---------------------------------------------------------------------------* Project: Horizon File: rdt_RingBuffer.cpp Copyright (C)2009-2012 Nintendo Co., Ltd. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev: 46347 $ *---------------------------------------------------------------------------*/ #include "stdafx.h" #include "rdt_RingBuffer.h" #include "rdt_Stopwatch.h" #include "Test.h" #include namespace { } // End of anonymous namespace namespace nn { namespace rdt { namespace CTR { // RingBuffer::RingBuffer(void) :m_initialized(false) { } // RingBuffer::RingBuffer(void *pBuf, size_t len) { Initialize(pBuf, len); } // RingBuffer::~RingBuffer(void) { if(m_initialized) { Finalize(); } } void RingBuffer::Initialize(void *pBuf, size_t len) { ASSERT(pBuf!=NULL); ASSERT(len > 0); m_pHeadBuffer = static_cast(pBuf); m_bufLength = len; m_pHeadData = m_pHeadBuffer; m_dataLength = 0; m_initialized = true; } void RingBuffer::Finalize(void) { m_initialized = false; } void RingBuffer::Pop(size_t n) { ASSERT(m_initialized); WARNINGMSG(n > 0, "Argument is not positive number. Do nothing.\n"); if(n > 0) { size_t len = min(n, m_dataLength); ASSERT((0= getEndOfBuffer()) { m_pHeadData = p - m_bufLength; } else { m_pHeadData = p; } ASSERT(isValidAddress(m_pHeadData)); m_dataLength -= len; ASSERT((0<=len) && (len<=m_bufLength)); } } bool RingBuffer::Push(const void *pBuf, size_t n) { ASSERT(m_initialized); // TODO. Is n=0 a problem? if(n <= 0) { PANIC("Size of buffer is not positive number.(n=%d)\n", n); return false; } if(m_dataLength + n > m_bufLength) { // Cannot fit in buffer. return false; } else { static detail::Stopwatch s_sw("memcpy() in RingBuffer::Push()"); s_sw.Start(); // 01234567 u8 *dst = getEndOfData(); if(dst + n > getEndOfBuffer()) { // Case of data overflow. // The overflow is written to the start of the buffer. size_t n1 = getEndOfBuffer() - dst; size_t n2 = n - n1; memcpy(dst, pBuf, n1); memcpy(getHeadOfBuffer(), static_cast(pBuf)+n1, n2); } else { memcpy(dst, pBuf, n); } s_sw.Stop(); m_dataLength += n; return true; } } size_t RingBuffer::Read(void *pBuf, size_t n) const { ASSERT(pBuf!=NULL); ASSERT(n > 0); return read(pBuf, n, 0); } size_t RingBuffer::Read(void *pBuf, size_t n, size_t offset) const { ASSERT(pBuf!=NULL); ASSERT(n > 0); ASSERT((0 <= offset) && (offset < m_bufLength)); return read(pBuf, n, offset); } size_t RingBuffer::read(void *pBuf, size_t n, size_t offset) const { ASSERT(m_initialized); ASSERT(n > 0); ASSERT((0 <= offset) && (offset < m_bufLength)); if(offset >= m_dataLength) { // The offset is larger than the data length and cannot be read. return 0; } // Calculate the number of bytes to be read. // Even if a large n is passed, it is corrected to the length of the existing data. size_t len = min(m_dataLength - offset, n); // Calculate the position to start the read. This also prevents a buffer overflow. u8 *from = m_pHeadData + offset; if(from >= getEndOfBuffer()) { from = from - m_bufLength; } ASSERT(isValidAddress(from)); if(from + len <= getEndOfBuffer()) { // When copy does not have to be divided into two parts memcpy(pBuf, from, len); } else { // When copy is divided into two parts because the data to be read straddles the ring buffer boundary // size_t len1 = getEndOfBuffer() - from; size_t len2 = len - len1; memcpy(pBuf, from, len1); memcpy(static_cast(pBuf) + len1, getHeadOfBuffer(), len2); } return len; } // Returns the address of the location where the data is to be written next. u8* RingBuffer::getEndOfData(void) const { u8 *ret = m_pHeadData + m_dataLength; if(ret < getEndOfBuffer()) { // Because the data does not extend beyond the buffer, you do not need to modify the ret. } else { ret -= m_bufLength; } ASSERT(isValidAddress(ret)); return ret; } #ifdef _WIN32 void RingBuffer::Test(void) { const int BUFSIZE = 8; u8 buffer[BUFSIZE]; RingBuffer r(buffer, BUFSIZE); CU_ASSERT(r.IsEmpty()); // If just created, should be empty. CU_ASSERT(r.Push("ABC", 3)); CU_ASSERT(!r.IsEmpty()); r.Pop(3); CU_ASSERT(r.IsEmpty()); { r.Clear(); const char *msg = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; CU_ASSERT(!r.Push(msg, strlen(msg))); // Push of long character strings must fail } { r.Clear(); CU_ASSERT(r.Push("ABCD", 4)); CU_ASSERT(r.GetDataSize()==4); CU_ASSERT(r.Push("EFGH", 4)); CU_ASSERT(r.GetDataSize()==8); CU_ASSERT(!(r.Push("XYZ", 3))); // Does not fit char msgBuf[32]; CU_ASSERT(r.Read(msgBuf, 32)==8); // 8 characters, should be able to be read CU_ASSERT(strncmp(msgBuf, "ABCDEFGH", 8)==0); r.Pop(2); CU_ASSERT(r.GetDataSize()==6); CU_ASSERT(r.Read(msgBuf, 32)==6); CU_ASSERT(strncmp(msgBuf, "CDEFGH", 6)==0); r.Pop(2); CU_ASSERT(r.GetDataSize()==4); CU_ASSERT(r.Push("IJKL", 4)); CU_ASSERT(r.GetDataSize()==8); CU_ASSERT(r.Read(msgBuf, 32)==8); CU_ASSERT(strncmp(msgBuf, "EFGHIJKL", 8)==0); r.Pop(3); CU_ASSERT(r.GetDataSize()==5); r.Pop(5); CU_ASSERT(r.GetDataSize()==0); CU_ASSERT(r.IsEmpty()); CU_ASSERT(r.Push("M", 1)); CU_ASSERT(r.Push("N", 1)); CU_ASSERT(r.GetDataSize()==2); CU_ASSERT(r.Push("O", 1)); CU_ASSERT(r.Push("P", 1)); CU_ASSERT(r.Push("Q", 1)); CU_ASSERT(r.GetDataSize()==5); CU_ASSERT(r.Push("R", 1)); CU_ASSERT(r.Push("S", 1)); CU_ASSERT(r.Push("T", 1)); CU_ASSERT(r.GetDataSize()==8); CU_ASSERT(!r.Push("Z", 1)); CU_ASSERT(!r.Push("Z", 1)); r.Pop(1); CU_ASSERT(r.GetDataSize()==7); CU_ASSERT(r.Push("U", 1)); r.Pop(2); CU_ASSERT(r.GetDataSize()==6); CU_ASSERT(r.Push("VW", 2)); CU_ASSERT(r.Read(msgBuf, 7)==7); CU_ASSERT(strncmp(msgBuf, "PQRSTUV", 7)==0); CU_ASSERT(r.GetDataSize()==8); // r.Pop(0); // Issue WARNING CU_ASSERT(r.GetDataSize()==8); // Zero bytes read out r.Pop(8); CU_ASSERT(r.Read(msgBuf, 8)==0); CU_ASSERT(r.IsEmpty()); // Test of a Read function with an offset. CU_ASSERT(r.Push("123456", 6)); CU_ASSERT(!r.IsEmpty()); CU_ASSERT(r.GetDataSize()==6); CU_ASSERT(r.Read(msgBuf, 8, 4)==2); CU_ASSERT(strncmp(msgBuf, "56", 2)==0); r.Pop(3); CU_ASSERT(r.Push("789", 3)); CU_ASSERT(r.GetDataSize()==6); CU_ASSERT(r.Read(msgBuf, 8, 5)==1); CU_ASSERT(strncmp(msgBuf, "9", 1)==0); } } #endif // end of _WIN32 }}} // namespace nn::rdt::CTR