1 /*
2  *  CircularByteBuffer.cpp
3  *  created for Marathon: Aleph One <http://source.bungie.org/>
4 
5 	Copyright (C) 2003 Woody Zenfell, III
6 
7 	This program is free software; you can redistribute it and/or modify
8 	it under the terms of the GNU General Public License as published by
9 	the Free Software Foundation; either version 3 of the License, or
10 	(at your option) any later version.
11 
12 	This program is distributed in the hope that it will be useful,
13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 	GNU General Public License for more details.
16 
17 	This license is contained in the file "COPYING",
18 	which is included with this source code; it is available online at
19 	http://www.gnu.org/licenses/gpl.html
20 
21  *  A circular queue of bytes, with support for mass enqueueing from/peeking to caller's buffer
22  *
23  *  Created by Woody Zenfell, III on Sun Jun 29 2003.
24 
25  July 19, 2003 (Woody Zenfell):
26 	Additional "NoCopy" interface may let clients avoid a copy, though it's not as safe.
27 
28  */
29 
30 #include "cseries.h" // assert()
31 
32 #include "CircularByteBuffer.h"
33 
34 #include <algorithm> // std::min()
35 
36 // static
37 std::pair<unsigned int, unsigned int>
splitIntoChunks(unsigned int inByteCount,unsigned int inStartingIndex,unsigned int inQueueSize)38 CircularByteBuffer::splitIntoChunks(unsigned int inByteCount, unsigned int inStartingIndex, unsigned int inQueueSize)
39 {
40 	// Copy, potentially, two separate chunks (one at end of buffer; one at beginning)
41 	unsigned int theSpaceAtEndOfBuffer = inQueueSize - inStartingIndex;
42 	unsigned int theFirstChunkSize = std::min(inByteCount, theSpaceAtEndOfBuffer);
43 	unsigned int theSecondChunkSize = inByteCount - theFirstChunkSize;
44 
45 	return std::pair<unsigned int, unsigned int>(theFirstChunkSize, theSecondChunkSize);
46 }
47 
48 
49 void
enqueueBytes(const void * inBytes,unsigned int inByteCount)50 CircularByteBuffer::enqueueBytes(const void* inBytes, unsigned int inByteCount)
51 {
52 	// I believe everything works right without this check, but it makes me feel safer anyway.
53 	if(inByteCount > 0)
54 	{
55 		assert(inByteCount <= getRemainingSpace());
56 
57 		const char* theBytes = static_cast<const char*>(inBytes);
58 
59 		std::pair<unsigned int, unsigned int> theChunkSizes = splitIntoChunks(inByteCount, mWriteIndex, mQueueSize);
60 
61 		memcpy(&(mData[mWriteIndex]), theBytes, theChunkSizes.first);
62 
63 		if(theChunkSizes.second > 0)
64 			memcpy(mData, &(theBytes[theChunkSizes.first]), theChunkSizes.second);
65 
66 		advanceWriteIndex(inByteCount);
67 	}
68 }
69 
70 
71 void
enqueueBytesNoCopyStart(unsigned int inByteCount,void ** outFirstBytes,unsigned int * outFirstByteCount,void ** outSecondBytes,unsigned int * outSecondByteCount)72 CircularByteBuffer::enqueueBytesNoCopyStart(unsigned int inByteCount, void** outFirstBytes, unsigned int* outFirstByteCount,
73 			      void** outSecondBytes, unsigned int* outSecondByteCount)
74 {
75 	void* theFirstBytes = NULL;
76 	void* theSecondBytes = NULL;
77 	unsigned int theFirstByteCount = 0;
78 	unsigned int theSecondByteCount = 0;
79 
80 	if(inByteCount > 0)
81 	{
82 		assert(inByteCount <= getRemainingSpace());
83 
84 		std::pair<unsigned int, unsigned int> theChunkSizes = splitIntoChunks(inByteCount, mWriteIndex, mQueueSize);
85 
86 		theFirstBytes = &(mData[getWriteIndex()]);
87 		theFirstByteCount = theChunkSizes.first;
88 		theSecondByteCount = theChunkSizes.second;
89 		theSecondBytes = (theSecondByteCount > 0) ? mData : NULL;
90 	}
91 
92 	if(outFirstBytes != NULL)
93 		*outFirstBytes = theFirstBytes;
94 
95 	if(outFirstByteCount != NULL)
96 		*outFirstByteCount = theFirstByteCount;
97 
98 	if(outSecondBytes != NULL)
99 		*outSecondBytes = theSecondBytes;
100 
101 	if(outSecondByteCount != NULL)
102 		*outSecondByteCount = theSecondByteCount;
103 }
104 
105 
106 void
enqueueBytesNoCopyFinish(unsigned int inActualByteCount)107 CircularByteBuffer::enqueueBytesNoCopyFinish(unsigned int inActualByteCount)
108 {
109 	if(inActualByteCount > 0)
110 		advanceWriteIndex(inActualByteCount);
111 }
112 
113 
114 void
peekBytes(void * outBytes,unsigned int inByteCount)115 CircularByteBuffer::peekBytes(void* outBytes, unsigned int inByteCount)
116 {
117 	// I believe everything works right without this check, but it makes me feel safer anyway.
118 	if(inByteCount > 0)
119 	{
120 		assert(inByteCount <= getCountOfElements());
121 
122 		char* theBytes = static_cast<char*>(outBytes);
123 
124 		std::pair<unsigned int, unsigned int> theChunkSizes = splitIntoChunks(inByteCount, mReadIndex, mQueueSize);
125 
126 		memcpy(theBytes, &(mData[mReadIndex]), theChunkSizes.first);
127 
128 		if(theChunkSizes.second > 0)
129 			memcpy(&(theBytes[theChunkSizes.first]), mData, theChunkSizes.second);
130 	}
131 }
132 
133 
134 void
peekBytesNoCopy(unsigned int inByteCount,const void ** outFirstBytes,unsigned int * outFirstByteCount,const void ** outSecondBytes,unsigned int * outSecondByteCount)135 CircularByteBuffer::peekBytesNoCopy(unsigned int inByteCount, const void** outFirstBytes, unsigned int* outFirstByteCount,
136 				    const void** outSecondBytes, unsigned int* outSecondByteCount)
137 {
138 	void* theFirstBytes = NULL;
139 	void* theSecondBytes = NULL;
140 	unsigned int theFirstByteCount = 0;
141 	unsigned int theSecondByteCount = 0;
142 
143 	if(inByteCount > 0)
144 	{
145 		assert(inByteCount <= getCountOfElements());
146 
147 		std::pair<unsigned int, unsigned int> theChunkSizes = splitIntoChunks(inByteCount, mReadIndex, mQueueSize);
148 
149 		theFirstBytes = &(mData[getReadIndex()]);
150 		theFirstByteCount = theChunkSizes.first;
151 		theSecondByteCount = theChunkSizes.second;
152 		theSecondBytes = (theSecondByteCount > 0) ? mData : NULL;
153 	}
154 
155 	if(outFirstBytes != NULL)
156 		*outFirstBytes = theFirstBytes;
157 
158 	if(outFirstByteCount != NULL)
159 		*outFirstByteCount = theFirstByteCount;
160 
161 	if(outSecondBytes != NULL)
162 		*outSecondBytes = theSecondBytes;
163 
164 	if(outSecondByteCount != NULL)
165 		*outSecondByteCount = theSecondByteCount;
166 }
167