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