1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     rdt_RingBuffer.cpp
4 
5   Copyright (C)2009-2012 Nintendo Co., Ltd.  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   $Rev: 46347 $
14  *---------------------------------------------------------------------------*/
15 
16 #include "stdafx.h"
17 
18 #include "rdt_RingBuffer.h"
19 
20 #include "rdt_Stopwatch.h"
21 
22 #include "Test.h"
23 
24 
25 #include <string.h>
26 
27 
28 namespace
29 {
30 
31 }  // End of anonymous namespace
32 
33 namespace nn { namespace rdt { namespace CTR {
34 
35 //
RingBuffer(void)36 RingBuffer::RingBuffer(void)
37      :m_initialized(false)
38 {
39 }
40 
41 
42 //
RingBuffer(void * pBuf,size_t len)43 RingBuffer::RingBuffer(void *pBuf, size_t len)
44 {
45     Initialize(pBuf, len);
46 }
47 
48 
49 //
~RingBuffer(void)50 RingBuffer::~RingBuffer(void)
51 {
52     if(m_initialized)
53     {
54         Finalize();
55     }
56 }
57 
58 
Initialize(void * pBuf,size_t len)59 void RingBuffer::Initialize(void *pBuf, size_t len)
60 {
61     ASSERT(pBuf!=NULL);
62     ASSERT(len > 0);
63 
64     m_pHeadBuffer = static_cast<u8*>(pBuf);
65     m_bufLength   = len;
66     m_pHeadData   = m_pHeadBuffer;
67     m_dataLength  = 0;
68 
69     m_initialized = true;
70 }
71 
72 
Finalize(void)73 void RingBuffer::Finalize(void)
74 {
75     m_initialized = false;
76 }
77 
78 
Pop(size_t n)79 void RingBuffer::Pop(size_t n)
80 {
81     ASSERT(m_initialized);
82 
83     WARNINGMSG(n > 0, "Argument is not positive number.  Do nothing.\n");
84 
85     if(n > 0)
86     {
87         size_t len = min(n, m_dataLength);
88         ASSERT((0<len) && (len<=m_dataLength));
89 
90         u8 *p = m_pHeadData += n;
91         if(p >= getEndOfBuffer())
92         {
93             m_pHeadData = p - m_bufLength;
94         }
95         else
96         {
97             m_pHeadData = p;
98         }
99         ASSERT(isValidAddress(m_pHeadData));
100 
101         m_dataLength -= len;
102         ASSERT((0<=len) && (len<=m_bufLength));
103     }
104 }
105 
106 
Push(const void * pBuf,size_t n)107 bool RingBuffer::Push(const void *pBuf, size_t n)
108 {
109     ASSERT(m_initialized);
110 
111     // TODO. Is n=0 a problem?
112     if(n <= 0)
113     {
114         PANIC("Size of buffer is not positive number.(n=%d)\n", n);
115         return false;
116     }
117 
118     if(m_dataLength + n > m_bufLength)
119     {
120         // Cannot fit in buffer.
121         return false;
122     }
123     else
124     {
125         static detail::Stopwatch s_sw("memcpy() in RingBuffer::Push()");
126         s_sw.Start();
127 
128         // 01234567
129         u8 *dst = getEndOfData();
130         if(dst + n > getEndOfBuffer())
131         {
132             // Case of data overflow.
133             // The overflow is written to the start of the buffer.
134             size_t n1 = getEndOfBuffer() - dst;
135             size_t n2 = n - n1;
136             memcpy(dst, pBuf, n1);
137             memcpy(getHeadOfBuffer(), static_cast<const u8*>(pBuf)+n1, n2);
138         }
139         else
140         {
141             memcpy(dst, pBuf, n);
142         }
143         s_sw.Stop();
144 
145         m_dataLength += n;
146         return true;
147     }
148 }
149 
150 
Read(void * pBuf,size_t n) const151 size_t RingBuffer::Read(void *pBuf, size_t n) const
152 {
153     ASSERT(pBuf!=NULL);
154     ASSERT(n > 0);
155 
156     return read(pBuf, n, 0);
157 }
158 
159 
Read(void * pBuf,size_t n,size_t offset) const160 size_t RingBuffer::Read(void *pBuf, size_t n, size_t offset) const
161 {
162     ASSERT(pBuf!=NULL);
163     ASSERT(n > 0);
164     ASSERT((0 <= offset) && (offset < m_bufLength));
165 
166     return read(pBuf, n, offset);
167 }
168 
169 
read(void * pBuf,size_t n,size_t offset) const170 size_t RingBuffer::read(void *pBuf, size_t n, size_t offset) const
171 {
172     ASSERT(m_initialized);
173     ASSERT(n > 0);
174     ASSERT((0 <= offset) && (offset < m_bufLength));
175 
176     if(offset >= m_dataLength)
177     {
178         // The offset is larger than the data length and cannot be read.
179         return 0;
180     }
181 
182     // Calculate the number of bytes to be read.
183     // Even if a large n is passed, it is corrected to the length of the existing data.
184     size_t len = min(m_dataLength - offset, n);
185 
186     // Calculate the position to start the read. This also prevents a buffer overflow.
187     u8 *from = m_pHeadData + offset;
188     if(from >= getEndOfBuffer())
189     {
190         from = from - m_bufLength;
191     }
192     ASSERT(isValidAddress(from));
193 
194     if(from + len <= getEndOfBuffer())
195     {
196         // When copy does not have to be divided into two parts
197         memcpy(pBuf, from, len);
198     }
199     else
200     {
201         // When copy is divided into two parts because the data to be read straddles the ring buffer boundary
202         //
203         size_t len1 = getEndOfBuffer() - from;
204         size_t len2 = len - len1;
205         memcpy(pBuf, from, len1);
206         memcpy(static_cast<u8*>(pBuf) + len1, getHeadOfBuffer(), len2);
207     }
208 
209     return len;
210 }
211 
212 
213 // Returns the address of the location where the data is to be written next.
getEndOfData(void) const214 u8* RingBuffer::getEndOfData(void) const
215 {
216     u8 *ret = m_pHeadData + m_dataLength;
217     if(ret < getEndOfBuffer())
218     {
219         // Because the data does not extend beyond the buffer, you do not need to modify the ret.
220     }
221     else
222     {
223         ret -= m_bufLength;
224     }
225 
226     ASSERT(isValidAddress(ret));
227 
228     return ret;
229 }
230 
231 
232 #ifdef _WIN32
Test(void)233 void RingBuffer::Test(void)
234 {
235     const int BUFSIZE = 8;
236     u8 buffer[BUFSIZE];
237 
238     RingBuffer r(buffer, BUFSIZE);
239 
240     CU_ASSERT(r.IsEmpty());  // If just created, should be empty.
241 
242     CU_ASSERT(r.Push("ABC", 3));
243     CU_ASSERT(!r.IsEmpty());
244     r.Pop(3);
245     CU_ASSERT(r.IsEmpty());
246 
247     {
248         r.Clear();
249         const char *msg = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
250         CU_ASSERT(!r.Push(msg, strlen(msg)));  // Push of long character strings must fail
251     }
252 
253     {
254         r.Clear();
255         CU_ASSERT(r.Push("ABCD", 4));
256         CU_ASSERT(r.GetDataSize()==4);
257         CU_ASSERT(r.Push("EFGH", 4));
258         CU_ASSERT(r.GetDataSize()==8);
259         CU_ASSERT(!(r.Push("XYZ", 3)));  // Does not fit
260         char msgBuf[32];
261         CU_ASSERT(r.Read(msgBuf, 32)==8);  // 8 characters, should be able to be read
262         CU_ASSERT(strncmp(msgBuf, "ABCDEFGH", 8)==0);
263 
264         r.Pop(2);
265         CU_ASSERT(r.GetDataSize()==6);
266         CU_ASSERT(r.Read(msgBuf, 32)==6);
267         CU_ASSERT(strncmp(msgBuf, "CDEFGH", 6)==0);
268 
269         r.Pop(2);
270         CU_ASSERT(r.GetDataSize()==4);
271         CU_ASSERT(r.Push("IJKL", 4));
272         CU_ASSERT(r.GetDataSize()==8);
273         CU_ASSERT(r.Read(msgBuf, 32)==8);
274         CU_ASSERT(strncmp(msgBuf, "EFGHIJKL", 8)==0);
275 
276         r.Pop(3);
277         CU_ASSERT(r.GetDataSize()==5);
278         r.Pop(5);
279         CU_ASSERT(r.GetDataSize()==0);
280         CU_ASSERT(r.IsEmpty());
281 
282         CU_ASSERT(r.Push("M", 1));
283         CU_ASSERT(r.Push("N", 1));
284         CU_ASSERT(r.GetDataSize()==2);
285         CU_ASSERT(r.Push("O", 1));
286         CU_ASSERT(r.Push("P", 1));
287         CU_ASSERT(r.Push("Q", 1));
288         CU_ASSERT(r.GetDataSize()==5);
289         CU_ASSERT(r.Push("R", 1));
290         CU_ASSERT(r.Push("S", 1));
291         CU_ASSERT(r.Push("T", 1));
292         CU_ASSERT(r.GetDataSize()==8);
293         CU_ASSERT(!r.Push("Z", 1));
294         CU_ASSERT(!r.Push("Z", 1));
295 
296         r.Pop(1);
297         CU_ASSERT(r.GetDataSize()==7);
298         CU_ASSERT(r.Push("U", 1));
299         r.Pop(2);
300         CU_ASSERT(r.GetDataSize()==6);
301         CU_ASSERT(r.Push("VW", 2));
302         CU_ASSERT(r.Read(msgBuf, 7)==7);
303         CU_ASSERT(strncmp(msgBuf, "PQRSTUV", 7)==0);
304         CU_ASSERT(r.GetDataSize()==8);
305         // r.Pop(0);  // Issue WARNING
306         CU_ASSERT(r.GetDataSize()==8);
307 
308         // Zero bytes read out
309         r.Pop(8);
310         CU_ASSERT(r.Read(msgBuf, 8)==0);
311         CU_ASSERT(r.IsEmpty());
312 
313         // Test of a Read function with an offset.
314         CU_ASSERT(r.Push("123456", 6));
315         CU_ASSERT(!r.IsEmpty());
316         CU_ASSERT(r.GetDataSize()==6);
317         CU_ASSERT(r.Read(msgBuf, 8, 4)==2);
318         CU_ASSERT(strncmp(msgBuf, "56", 2)==0);
319         r.Pop(3);
320         CU_ASSERT(r.Push("789", 3));
321         CU_ASSERT(r.GetDataSize()==6);
322         CU_ASSERT(r.Read(msgBuf, 8, 5)==1);
323         CU_ASSERT(strncmp(msgBuf, "9", 1)==0);
324     }
325 }
326 #endif  // end of _WIN32
327 
328 
329 }}} // namespace nn::rdt::CTR
330