1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14
15 #include "debug/assert.h"
16 #include "util/alignedstream.h"
17 #include "util/stream.h"
18 #include "util/math.h"
19
20 namespace AGS
21 {
22 namespace Common
23 {
24
AlignedStream(Stream * stream,AlignedStreamMode mode,ObjectOwnershipPolicy stream_ownership_policy,size_t base_alignment)25 AlignedStream::AlignedStream(Stream *stream, AlignedStreamMode mode, ObjectOwnershipPolicy stream_ownership_policy,
26 size_t base_alignment)
27 : ProxyStream(stream, stream_ownership_policy)
28 , _mode(mode)
29 , _baseAlignment(base_alignment)
30 , _maxAlignment(0)
31 , _block(0)
32 {
33 }
34
~AlignedStream()35 AlignedStream::~AlignedStream()
36 {
37 Close();
38 }
39
Reset()40 void AlignedStream::Reset()
41 {
42 if (!_stream)
43 {
44 return;
45 }
46
47 FinalizeBlock();
48 }
49
Close()50 void AlignedStream::Close()
51 {
52 if (!_stream)
53 {
54 return;
55 }
56
57 FinalizeBlock();
58 ProxyStream::Close();
59 }
60
CanRead() const61 bool AlignedStream::CanRead() const
62 {
63 return _stream ? (_mode == kAligned_Read && _stream->CanRead()) : false;
64 }
65
CanWrite() const66 bool AlignedStream::CanWrite() const
67 {
68 return _stream ? (_mode == kAligned_Write && _stream->CanWrite()) : false;
69 }
70
CanSeek() const71 bool AlignedStream::CanSeek() const
72 {
73 // Aligned stream does not support seeking, hence that will break padding count
74 return false;
75 }
76
Read(void * buffer,size_t size)77 size_t AlignedStream::Read(void *buffer, size_t size)
78 {
79 if (_stream)
80 {
81 ReadPadding(sizeof(int8_t));
82 size = _stream->Read(buffer, size);
83 _block += size;
84 return size;
85 }
86 return 0;
87 }
88
ReadByte()89 int32_t AlignedStream::ReadByte()
90 {
91 uint8_t b = 0;
92 if (_stream)
93 {
94 ReadPadding(sizeof(uint8_t));
95 b = _stream->ReadByte();
96 _block += sizeof(uint8_t);
97 }
98 return b;
99 }
100
ReadInt16()101 int16_t AlignedStream::ReadInt16()
102 {
103 int16_t val = 0;
104 if (_stream)
105 {
106 ReadPadding(sizeof(int16_t));
107 val = _stream->ReadInt16();
108 _block += sizeof(int16_t);
109 }
110 return val;
111 }
112
ReadInt32()113 int32_t AlignedStream::ReadInt32()
114 {
115 int32_t val = 0;
116 if (_stream)
117 {
118 ReadPadding(sizeof(int32_t));
119 val = _stream->ReadInt32();
120 _block += sizeof(int32_t);
121 }
122 return val;
123 }
124
ReadInt64()125 int64_t AlignedStream::ReadInt64()
126 {
127 int64_t val = 0;
128 if (_stream)
129 {
130 ReadPadding(sizeof(int64_t));
131 val = _stream->ReadInt64();
132 _block += sizeof(int64_t);
133 }
134 return val;
135 }
136
ReadArray(void * buffer,size_t elem_size,size_t count)137 size_t AlignedStream::ReadArray(void *buffer, size_t elem_size, size_t count)
138 {
139 if (_stream)
140 {
141 ReadPadding(elem_size);
142 count = _stream->ReadArray(buffer, elem_size, count);
143 _block += count * elem_size;
144 return count;
145 }
146 return 0;
147 }
148
ReadArrayOfInt16(int16_t * buffer,size_t count)149 size_t AlignedStream::ReadArrayOfInt16(int16_t *buffer, size_t count)
150 {
151 if (_stream)
152 {
153 ReadPadding(sizeof(int16_t));
154 count = _stream->ReadArrayOfInt16(buffer, count);
155 _block += count * sizeof(int16_t);
156 return count;
157 }
158 return 0;
159 }
160
ReadArrayOfInt32(int32_t * buffer,size_t count)161 size_t AlignedStream::ReadArrayOfInt32(int32_t *buffer, size_t count)
162 {
163 if (_stream)
164 {
165 ReadPadding(sizeof(int32_t));
166 count = _stream->ReadArrayOfInt32(buffer, count);
167 _block += count * sizeof(int32_t);
168 return count;
169 }
170 return 0;
171 }
172
ReadArrayOfInt64(int64_t * buffer,size_t count)173 size_t AlignedStream::ReadArrayOfInt64(int64_t *buffer, size_t count)
174 {
175 if (_stream)
176 {
177 ReadPadding(sizeof(int64_t));
178 count = _stream->ReadArrayOfInt64(buffer, count);
179 _block += count * sizeof(int64_t);
180 return count;
181 }
182 return 0;
183 }
184
Write(const void * buffer,size_t size)185 size_t AlignedStream::Write(const void *buffer, size_t size)
186 {
187 if (_stream)
188 {
189 WritePadding(sizeof(int8_t));
190 size = _stream->Write(buffer, size);
191 _block += size;
192 return size;
193 }
194 return 0;
195 }
196
WriteByte(uint8_t b)197 int32_t AlignedStream::WriteByte(uint8_t b)
198 {
199 if (_stream)
200 {
201 WritePadding(sizeof(uint8_t));
202 b = _stream->WriteByte(b);
203 _block += sizeof(uint8_t);
204 return b;
205 }
206 return 0;
207 }
208
WriteInt16(int16_t val)209 size_t AlignedStream::WriteInt16(int16_t val)
210 {
211 if (_stream)
212 {
213 WritePadding(sizeof(int16_t));
214 size_t size = _stream->WriteInt16(val);
215 _block += sizeof(int16_t);
216 return size;
217 }
218 return 0;
219 }
220
WriteInt32(int32_t val)221 size_t AlignedStream::WriteInt32(int32_t val)
222 {
223 if (_stream)
224 {
225 WritePadding(sizeof(int32_t));
226 size_t size = _stream->WriteInt32(val);
227 _block += sizeof(int32_t);
228 return size;
229 }
230 return 0;
231 }
232
WriteInt64(int64_t val)233 size_t AlignedStream::WriteInt64(int64_t val)
234 {
235 if (_stream)
236 {
237 WritePadding(sizeof(int64_t));
238 size_t size = _stream->WriteInt64(val);
239 _block += sizeof(int64_t);
240 return size;
241 }
242 return 0;
243 }
244
WriteArray(const void * buffer,size_t elem_size,size_t count)245 size_t AlignedStream::WriteArray(const void *buffer, size_t elem_size, size_t count)
246 {
247 if (_stream)
248 {
249 WritePadding(elem_size);
250 count = _stream->WriteArray(buffer, elem_size, count);
251 _block += count * elem_size;
252 return count;
253 }
254 return 0;
255 }
256
WriteArrayOfInt16(const int16_t * buffer,size_t count)257 size_t AlignedStream::WriteArrayOfInt16(const int16_t *buffer, size_t count)
258 {
259 if (_stream)
260 {
261 WritePadding(sizeof(int16_t));
262 count = _stream->WriteArrayOfInt16(buffer, count);
263 _block += count * sizeof(int16_t);
264 return count;
265 }
266 return 0;
267 }
268
WriteArrayOfInt32(const int32_t * buffer,size_t count)269 size_t AlignedStream::WriteArrayOfInt32(const int32_t *buffer, size_t count)
270 {
271 if (_stream)
272 {
273 WritePadding(sizeof(int32_t));
274 count = _stream->WriteArrayOfInt32(buffer, count);
275 _block += count * sizeof(int32_t);
276 return count;
277 }
278 return 0;
279 }
280
WriteArrayOfInt64(const int64_t * buffer,size_t count)281 size_t AlignedStream::WriteArrayOfInt64(const int64_t *buffer, size_t count)
282 {
283 if (_stream)
284 {
285 WritePadding(sizeof(int64_t));
286 count = _stream->WriteArrayOfInt64(buffer, count);
287 _block += count * sizeof(int64_t);
288 return count;
289 }
290 return 0;
291 }
292
Seek(int offset,StreamSeek origin)293 size_t AlignedStream::Seek(int offset, StreamSeek origin)
294 {
295 // TODO: split out Seekable Stream interface
296 assert(false); // aligned stream should not be used in cases
297 // where Seek() is required to be called
298 return GetPosition();
299 }
300
ReadPadding(size_t next_type)301 void AlignedStream::ReadPadding(size_t next_type)
302 {
303 if (!IsValid())
304 {
305 return;
306 }
307
308 if (next_type == 0)
309 {
310 return;
311 }
312
313 // The next is going to be evenly aligned data type,
314 // therefore a padding check must be made
315 if (next_type % _baseAlignment == 0)
316 {
317 int pad = _block % next_type;
318 // Read padding only if have to
319 if (pad)
320 {
321 // We do not know and should not care if the underlying stream
322 // supports seek, so use read to skip the padding instead.
323 for (size_t i = next_type - pad; i > 0; --i)
324 _stream->ReadByte();
325 _block += next_type - pad;
326 }
327
328 _maxAlignment = Math::Max(_maxAlignment, next_type);
329 // Data is evenly aligned now
330 if (_block % LargestPossibleType == 0)
331 {
332 _block = 0;
333 }
334 }
335 }
336
WritePadding(size_t next_type)337 void AlignedStream::WritePadding(size_t next_type)
338 {
339 if (!IsValid())
340 {
341 return;
342 }
343
344 if (next_type == 0)
345 {
346 return;
347 }
348
349 // The next is going to be evenly aligned data type,
350 // therefore a padding check must be made
351 if (next_type % _baseAlignment == 0)
352 {
353 int pad = _block % next_type;
354 // Write padding only if have to
355 if (pad)
356 {
357 _stream->WriteByteCount(0, next_type - pad);
358 _block += next_type - pad;
359 }
360
361 _maxAlignment = Math::Max(_maxAlignment, next_type);
362 // Data is evenly aligned now
363 if (_block % LargestPossibleType == 0)
364 {
365 _block = 0;
366 }
367 }
368 }
369
FinalizeBlock()370 void AlignedStream::FinalizeBlock()
371 {
372 if (!IsValid())
373 {
374 return;
375 }
376
377 // Force the stream to read or write remaining padding to match the alignment
378 if (_mode == kAligned_Read)
379 {
380 ReadPadding(_maxAlignment);
381 }
382 else if (_mode == kAligned_Write)
383 {
384 WritePadding(_maxAlignment);
385 }
386
387 _maxAlignment = 0;
388 _block = 0;
389 }
390
391 } // namespace Common
392 } // namespace AGS
393