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