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