/*-------------------------------------------------------------------------- Project: HorizonSDK File: rdt_RingBuffer.cpp Copyright 2009 Nintendo. 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. $Date:: 2010-07-26#$ $Rev: 21852 $ $Author: hiratsu_daisuke $ *-------------------------------------------------------------------------*/ #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. n=0でも問題ないか? if(n <= 0) { PANIC("Size of buffer is not positive number.(n=%d)\n", n); return false; } if(m_dataLength + n > m_bufLength) { // バッファに入りきらない。 return false; } else { static detail::Stopwatch s_sw("memcpy() in RingBuffer::Push()"); s_sw.Start(); // 01234567 u8 *dst = getEndOfData(); if(dst + n > getEndOfBuffer()) { // データの一部がはみ出すケース。 // はみ出した分は、バッファの先頭から書き込む。 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) { // オフセットがデータ長より大きすぎて、読み出し不可能。 return 0; } // 実際に読み出すことになるバイト数を算出。 // 大きなnが与えられても、既存のデータ列の長さに矯正する。 size_t len = min(m_dataLength - offset, n); // 読み出し開始位置を算出。バッファからはみ出す場合もケアする。 u8 *from = m_pHeadData + offset; if(from >= getEndOfBuffer()) { from = from - m_bufLength; } ASSERT(isValidAddress(from)); if(from + len <= getEndOfBuffer()) { // コピーを2回に分けなくても良い場合 memcpy(pBuf, from, len); } else { // 読み出し対象のデータがリングバッファの境界をまたぐために、 // コピーを2回に分ける場合 size_t len1 = getEndOfBuffer() - from; size_t len2 = len - len1; memcpy(pBuf, from, len1); memcpy(static_cast(pBuf) + len1, getHeadOfBuffer(), len2); } return len; } // 次にデータを書き込むべき場所のアドレスを返します。 u8* RingBuffer::getEndOfData(void) const { u8 *ret = m_pHeadData + m_dataLength; if(ret < getEndOfBuffer()) { // バッファを飛び出さないので、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()); // 作ったばかりなら、カラッポのはず。 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は失敗しなくてはならない } { 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))); // 入りきらない char msgBuf[32]; CU_ASSERT(r.Read(msgBuf, 32)==8); // 8文字、読み出せるはず 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); // WARNING発生 CU_ASSERT(r.GetDataSize()==8); // ゼロバイト読み込み r.Pop(8); CU_ASSERT(r.Read(msgBuf, 8)==0); CU_ASSERT(r.IsEmpty()); // オフセット付きRead()のテスト。 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