1 /*--------------------------------------------------------------------------
2 Project: HorizonSDK
3 File: rdt_RingBuffer.cpp
4 Copyright 2009 Nintendo. All rights reserved.
5 These coded instructions, statements, and computer programs contain
6 proprietary information of Nintendo of America Inc. and/or Nintendo
7 Company Ltd., and are protected by Federal copyright law. They may
8 not be disclosed to third parties or copied or duplicated in any form,
9 in whole or in part, without the prior written consent of Nintendo.
10 $Date:: 2010-07-26#$
11 $Rev: 21852 $
12 $Author: hiratsu_daisuke $
13 *-------------------------------------------------------------------------
14
15
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. Is n=0 a problem?
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 // Cannot fit in buffer.
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 // Case of data overflow.
135 // The overflow is written to the beginning of the buffer.
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 // The offset is larger than the data length and cannot be read.
181 return 0;
182 }
183
184 // Calculates the number of bytes that can actually be read.
185 // Even if a large n is passed, it is corrected to the length of the existing data string.
186 size_t len = min(m_dataLength - offset, n);
187
188 // Calculates the position to start the read. Also takes care of the case of a buffer overflow.
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 // When copy does not have to be divided into two parts
199 memcpy(pBuf, from, len);
200 }
201 else
202 {
203 // When copy is divided into two parts because the data to be read straddles the ring buffer boundary
204 //
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 // Returns the address of the location where the data is to be written next.
getEndOfData(void) const216 u8* RingBuffer::getEndOfData(void) const
217 {
218 u8 *ret = m_pHeadData + m_dataLength;
219 if(ret < getEndOfBuffer())
220 {
221 // Since it does not extend beyond the buffer, you do not need to modify the 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()); // If just created, should be empty.
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 of long text strings must fail
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))); // Doesn't fit
262 char msgBuf[32];
263 CU_ASSERT(r.Read(msgBuf, 32)==8); // 8 characters, should be able to be read
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); // Issue WARNING
308 CU_ASSERT(r.GetDataSize()==8);
309
310 // Read zero byte
311 r.Pop(8);
312 CU_ASSERT(r.Read(msgBuf, 8)==0);
313 CU_ASSERT(r.IsEmpty());
314
315 // Test for Read function with offset.
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