1 /*--------------------------------------------------------------------------
2   Project:  HorizonSDK
3   File:     rdt_RingBuffer.cpp
4 
5   Copyright 2009 Nintendo.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2010-07-26#$
14   $Rev: 21852 $
15   $Author: hiratsu_daisuke $
16  *-------------------------------------------------------------------------*/
17 
18 #include "stdafx.h"
19 
20 #include "rdt_RingBuffer.h"
21 
22 #include "rdt_Stopwatch.h"
23 
24 #include "Test.h"
25 
26 
27 #include <string.h>
28 
29 
30 namespace
31 {
32 
33 }  // end of anonymous namespace
34 
35 namespace nn { namespace rdt { namespace CTR {
36 
37 ///< コンストラクタ
RingBuffer(void)38 RingBuffer::RingBuffer(void)
39      :m_initialized(false)
40 {
41 }
42 
43 
44 ///< コンストラクタ
RingBuffer(void * pBuf,size_t len)45 RingBuffer::RingBuffer(void *pBuf, size_t len)
46 {
47     Initialize(pBuf, len);
48 }
49 
50 
51 ///< デストラクタ
~RingBuffer(void)52 RingBuffer::~RingBuffer(void)
53 {
54     if(m_initialized)
55     {
56         Finalize();
57     }
58 }
59 
60 
Initialize(void * pBuf,size_t len)61 void RingBuffer::Initialize(void *pBuf, size_t len)
62 {
63     ASSERT(pBuf!=NULL);
64     ASSERT(len > 0);
65 
66     m_pHeadBuffer = static_cast<u8*>(pBuf);
67     m_bufLength   = len;
68     m_pHeadData   = m_pHeadBuffer;
69     m_dataLength  = 0;
70 
71     m_initialized = true;
72 }
73 
74 
Finalize(void)75 void RingBuffer::Finalize(void)
76 {
77     m_initialized = false;
78 }
79 
80 
Pop(size_t n)81 void RingBuffer::Pop(size_t n)
82 {
83     ASSERT(m_initialized);
84 
85     WARNINGMSG(n > 0, "Argument is not positive number.  Do nothing.\n");
86 
87     if(n > 0)
88     {
89         size_t len = min(n, m_dataLength);
90         ASSERT((0<len) && (len<=m_dataLength));
91 
92         u8 *p = m_pHeadData += n;
93         if(p >= getEndOfBuffer())
94         {
95             m_pHeadData = p - m_bufLength;
96         }
97         else
98         {
99             m_pHeadData = p;
100         }
101         ASSERT(isValidAddress(m_pHeadData));
102 
103         m_dataLength -= len;
104         ASSERT((0<=len) && (len<=m_bufLength));
105     }
106 }
107 
108 
Push(const void * pBuf,size_t n)109 bool RingBuffer::Push(const void *pBuf, size_t n)
110 {
111     ASSERT(m_initialized);
112 
113     // TODO. n=0でも問題ないか?
114     if(n <= 0)
115     {
116         PANIC("Size of buffer is not positive number.(n=%d)\n", n);
117         return false;
118     }
119 
120     if(m_dataLength + n > m_bufLength)
121     {
122         // バッファに入りきらない。
123         return false;
124     }
125     else
126     {
127         static detail::Stopwatch s_sw("memcpy() in RingBuffer::Push()");
128         s_sw.Start();
129 
130         // 01234567
131         u8 *dst = getEndOfData();
132         if(dst + n > getEndOfBuffer())
133         {
134             // データの一部がはみ出すケース。
135             // はみ出した分は、バッファの先頭から書き込む。
136             size_t n1 = getEndOfBuffer() - dst;
137             size_t n2 = n - n1;
138             memcpy(dst, pBuf, n1);
139             memcpy(getHeadOfBuffer(), static_cast<const u8*>(pBuf)+n1, n2);
140         }
141         else
142         {
143             memcpy(dst, pBuf, n);
144         }
145         s_sw.Stop();
146 
147         m_dataLength += n;
148         return true;
149     }
150 }
151 
152 
Read(void * pBuf,size_t n) const153 size_t RingBuffer::Read(void *pBuf, size_t n) const
154 {
155     ASSERT(pBuf!=NULL);
156     ASSERT(n > 0);
157 
158     return read(pBuf, n, 0);
159 }
160 
161 
Read(void * pBuf,size_t n,size_t offset) const162 size_t RingBuffer::Read(void *pBuf, size_t n, size_t offset) const
163 {
164     ASSERT(pBuf!=NULL);
165     ASSERT(n > 0);
166     ASSERT((0 <= offset) && (offset < m_bufLength));
167 
168     return read(pBuf, n, offset);
169 }
170 
171 
read(void * pBuf,size_t n,size_t offset) const172 size_t RingBuffer::read(void *pBuf, size_t n, size_t offset) const
173 {
174     ASSERT(m_initialized);
175     ASSERT(n > 0);
176     ASSERT((0 <= offset) && (offset < m_bufLength));
177 
178     if(offset >= m_dataLength)
179     {
180         // オフセットがデータ長より大きすぎて、読み出し不可能。
181         return 0;
182     }
183 
184     // 実際に読み出すことになるバイト数を算出。
185     // 大きなnが与えられても、既存のデータ列の長さに矯正する。
186     size_t len = min(m_dataLength - offset, n);
187 
188     // 読み出し開始位置を算出。バッファからはみ出す場合もケアする。
189     u8 *from = m_pHeadData + offset;
190     if(from >= getEndOfBuffer())
191     {
192         from = from - m_bufLength;
193     }
194     ASSERT(isValidAddress(from));
195 
196     if(from + len <= getEndOfBuffer())
197     {
198         // コピーを2回に分けなくても良い場合
199         memcpy(pBuf, from, len);
200     }
201     else
202     {
203         // 読み出し対象のデータがリングバッファの境界をまたぐために、
204         // コピーを2回に分ける場合
205         size_t len1 = getEndOfBuffer() - from;
206         size_t len2 = len - len1;
207         memcpy(pBuf, from, len1);
208         memcpy(static_cast<u8*>(pBuf) + len1, getHeadOfBuffer(), len2);
209     }
210 
211     return len;
212 }
213 
214 
215 // 次にデータを書き込むべき場所のアドレスを返します。
getEndOfData(void) const216 u8* RingBuffer::getEndOfData(void) const
217 {
218     u8 *ret = m_pHeadData + m_dataLength;
219     if(ret < getEndOfBuffer())
220     {
221         // バッファを飛び出さないので、retに手を加えずとも良い。
222     }
223     else
224     {
225         ret -= m_bufLength;
226     }
227 
228     ASSERT(isValidAddress(ret));
229 
230     return ret;
231 }
232 
233 
234 #ifdef _WIN32
Test(void)235 void RingBuffer::Test(void)
236 {
237     const int BUFSIZE = 8;
238     u8 buffer[BUFSIZE];
239 
240     RingBuffer r(buffer, BUFSIZE);
241 
242     CU_ASSERT(r.IsEmpty());  // 作ったばかりなら、カラッポのはず。
243 
244     CU_ASSERT(r.Push("ABC", 3));
245     CU_ASSERT(!r.IsEmpty());
246     r.Pop(3);
247     CU_ASSERT(r.IsEmpty());
248 
249     {
250         r.Clear();
251         const char *msg = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
252         CU_ASSERT(!r.Push(msg, strlen(msg)));  // 長い文字列のPushは失敗しなくてはならない
253     }
254 
255     {
256         r.Clear();
257         CU_ASSERT(r.Push("ABCD", 4));
258         CU_ASSERT(r.GetDataSize()==4);
259         CU_ASSERT(r.Push("EFGH", 4));
260         CU_ASSERT(r.GetDataSize()==8);
261         CU_ASSERT(!(r.Push("XYZ", 3)));  // 入りきらない
262         char msgBuf[32];
263         CU_ASSERT(r.Read(msgBuf, 32)==8);  // 8文字、読み出せるはず
264         CU_ASSERT(strncmp(msgBuf, "ABCDEFGH", 8)==0);
265 
266         r.Pop(2);
267         CU_ASSERT(r.GetDataSize()==6);
268         CU_ASSERT(r.Read(msgBuf, 32)==6);
269         CU_ASSERT(strncmp(msgBuf, "CDEFGH", 6)==0);
270 
271         r.Pop(2);
272         CU_ASSERT(r.GetDataSize()==4);
273         CU_ASSERT(r.Push("IJKL", 4));
274         CU_ASSERT(r.GetDataSize()==8);
275         CU_ASSERT(r.Read(msgBuf, 32)==8);
276         CU_ASSERT(strncmp(msgBuf, "EFGHIJKL", 8)==0);
277 
278         r.Pop(3);
279         CU_ASSERT(r.GetDataSize()==5);
280         r.Pop(5);
281         CU_ASSERT(r.GetDataSize()==0);
282         CU_ASSERT(r.IsEmpty());
283 
284         CU_ASSERT(r.Push("M", 1));
285         CU_ASSERT(r.Push("N", 1));
286         CU_ASSERT(r.GetDataSize()==2);
287         CU_ASSERT(r.Push("O", 1));
288         CU_ASSERT(r.Push("P", 1));
289         CU_ASSERT(r.Push("Q", 1));
290         CU_ASSERT(r.GetDataSize()==5);
291         CU_ASSERT(r.Push("R", 1));
292         CU_ASSERT(r.Push("S", 1));
293         CU_ASSERT(r.Push("T", 1));
294         CU_ASSERT(r.GetDataSize()==8);
295         CU_ASSERT(!r.Push("Z", 1));
296         CU_ASSERT(!r.Push("Z", 1));
297 
298         r.Pop(1);
299         CU_ASSERT(r.GetDataSize()==7);
300         CU_ASSERT(r.Push("U", 1));
301         r.Pop(2);
302         CU_ASSERT(r.GetDataSize()==6);
303         CU_ASSERT(r.Push("VW", 2));
304         CU_ASSERT(r.Read(msgBuf, 7)==7);
305         CU_ASSERT(strncmp(msgBuf, "PQRSTUV", 7)==0);
306         CU_ASSERT(r.GetDataSize()==8);
307         // r.Pop(0);  // WARNING発生
308         CU_ASSERT(r.GetDataSize()==8);
309 
310         // ゼロバイト読み込み
311         r.Pop(8);
312         CU_ASSERT(r.Read(msgBuf, 8)==0);
313         CU_ASSERT(r.IsEmpty());
314 
315         // オフセット付きRead()のテスト。
316         CU_ASSERT(r.Push("123456", 6));
317         CU_ASSERT(!r.IsEmpty());
318         CU_ASSERT(r.GetDataSize()==6);
319         CU_ASSERT(r.Read(msgBuf, 8, 4)==2);
320         CU_ASSERT(strncmp(msgBuf, "56", 2)==0);
321         r.Pop(3);
322         CU_ASSERT(r.Push("789", 3));
323         CU_ASSERT(r.GetDataSize()==6);
324         CU_ASSERT(r.Read(msgBuf, 8, 5)==1);
325         CU_ASSERT(strncmp(msgBuf, "9", 1)==0);
326     }
327 }
328 #endif  // end of _WIN32
329 
330 
331 }}} // namespace nn::rdt::CTR
332