-
Notifications
You must be signed in to change notification settings - Fork 18
/
lzf_src.h
356 lines (324 loc) · 9.58 KB
/
lzf_src.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
///////////////////////////////////////////////////////////////////////////////
//
// (C) 2008 SRC, LLC - All rights reserved
//
///////////////////////////////////////////////////////////////////////////////
//
// Module: LZF_SRC.H
//
///////////////////////////////////////////////////////////////////////////////
#ifndef __LZF_SRC_H__
#define __LZF_SRC_H__
//#include <srclib.h>
extern "C"
{
#include "lzf.h"
}
namespace SRC
{
// TFileP is usually a SmartPointer to a file
template <class TFileP, class TEngine, unsigned BufferSize=0x40000> class LZFBufferedOutput : public SmartPointerRefObj_Base
{
struct CompressBuffer
{
unsigned char m_pOutBuffer[BufferSize];
unsigned m_nOutBufferUsed;
unsigned char m_pInBuffer[BufferSize];
unsigned m_nInBufferUsed;
TFileP m_pFile;
#ifndef __GNUC__
HANDLE m_hEvent;
#endif
inline CompressBuffer(TFileP pFile, bool bCreateEvent)
: m_nOutBufferUsed(0)
, m_nInBufferUsed(0)
, m_pFile(pFile)
{
#ifndef __GNUC__
if (bCreateEvent)
m_hEvent = CreateEvent(NULL, false, false, NULL);
else
m_hEvent = NULL;
#endif
}
inline ~CompressBuffer()
{
#ifndef __GNUC__
if (m_hEvent!=NULL)
CloseHandle(m_hEvent);
#endif
}
void DoCompress()
{
assert(m_nInBufferUsed!=0);
m_nOutBufferUsed = lzf_compress(m_pInBuffer, m_nInBufferUsed, m_pOutBuffer, m_nInBufferUsed-1);
}
// this will be called in a worker thread
// hence it cannot modify m_nInBufferUsed because
// we use that in the master thread to know if anything happened
static void __stdcall DoCompress(void *_pThis)
{
CompressBuffer *pThis = static_cast<CompressBuffer *>(_pThis);
pThis->DoCompress();
#ifndef __GNUC__
SetEvent(pThis->m_hEvent);
#endif
}
#ifndef __GNUC__
inline void WaitForCompletion()
{
WaitForSingleObject(m_hEvent, INFINITE);
}
#endif
// this should always be called from the master thread
inline void DoWrite()
{
assert(m_nInBufferUsed!=0);
// it wasn't able to compress, just write the data straight out
if (m_nOutBufferUsed==0)
{
assert(m_nInBufferUsed<=BufferSize);
unsigned nResultBytes = m_nInBufferUsed;
if (BufferSize<=0x7fff)
{
unsigned short snResultBytes = static_cast<unsigned short>(nResultBytes | 0x8000);
m_pFile->Write(&snResultBytes, sizeof(snResultBytes));
}
else
{
nResultBytes |= 0x80000000;
m_pFile->Write(&nResultBytes, sizeof(nResultBytes));
}
m_pFile->Write((const char *)m_pInBuffer, m_nInBufferUsed);
}
else
{
assert(m_nOutBufferUsed<=BufferSize);
if (BufferSize<=0x7fff)
{
unsigned short snResultBytes = static_cast<unsigned short>(m_nOutBufferUsed);
m_pFile->Write(&snResultBytes, sizeof(snResultBytes));
}
else
m_pFile->Write(&m_nOutBufferUsed, sizeof(m_nOutBufferUsed));
m_pFile->Write((const char *)m_pOutBuffer, m_nOutBufferUsed);
}
m_nInBufferUsed = 0;
m_nOutBufferUsed = 0;
}
};
inline void DoWrite(CompressBuffer *pCurrentBuffer)
{
try
{
pCurrentBuffer->DoWrite();
}
catch (...)
{
pCurrentBuffer->m_nInBufferUsed = 0;
pCurrentBuffer->m_nOutBufferUsed = 0;
// go through all the buffers and flush them WITHOUT Writing, starting with the next one.
if (m_pNextBuffer->m_nInBufferUsed!=0)
{
#ifndef __GNUC__
m_pNextBuffer->WaitForCompletion();
#endif
m_pNextBuffer->m_nInBufferUsed = 0;
m_pNextBuffer->m_nOutBufferUsed = 0;
}
if (m_pCurrentBuffer->m_nInBufferUsed!=0)
{
#ifndef __GNUC__
m_pCurrentBuffer->WaitForCompletion();
#endif
m_pCurrentBuffer->m_nInBufferUsed = 0;
m_pCurrentBuffer->m_nOutBufferUsed = 0;
}
throw;
}
}
CompressBuffer m_buffer1;
// this isn't always used, so we don't allocate it until we need it.
std::auto_ptr<CompressBuffer> m_pBuffer2;
CompressBuffer *m_pCurrentBuffer;
CompressBuffer *m_pNextBuffer;
const TEngine *m_pEngine;
public:
LZFBufferedOutput(const TEngine *pEngine, TFileP pFile);
inline void FlushBuffer();
~LZFBufferedOutput();
void Write(const void *pBuffer, unsigned nSize);
};
template <class TFileP, class TEngine, unsigned BufferSize> LZFBufferedOutput<TFileP, TEngine, BufferSize>::LZFBufferedOutput(const TEngine *pEngine, TFileP pFile)
: m_buffer1(pFile, pEngine!=NULL)
, m_pEngine(pEngine)
{
m_pCurrentBuffer = &m_buffer1;
if (m_pEngine)
{
m_pBuffer2.reset(new CompressBuffer(pFile, pEngine!=NULL));
m_pNextBuffer = m_pBuffer2.get();
}
else
m_pNextBuffer = NULL;
}
template <class TFileP, class TEngine, unsigned BufferSize> void LZFBufferedOutput<TFileP, TEngine, BufferSize>::FlushBuffer()
{
#ifndef __GNUC__
if (m_pEngine)
{
// queue up compressing this buffer so it can happen while we are writing the previos data
if (m_pCurrentBuffer->m_nInBufferUsed!=0)
m_pEngine->QueueThread(CompressBuffer::DoCompress, m_pCurrentBuffer);
// the Next in this case might actually be the previous so we have to write it 1st
// if it has no data, the write does nothing
if (m_pNextBuffer->m_nInBufferUsed!=0)
{
m_pNextBuffer->WaitForCompletion();
DoWrite(m_pNextBuffer);
}
if (m_pCurrentBuffer->m_nInBufferUsed!=0)
{
m_pCurrentBuffer->WaitForCompletion();
DoWrite(m_pCurrentBuffer);
}
}
else
#endif
{
// single threaded mode
if (m_pCurrentBuffer->m_nInBufferUsed!=0)
{
m_pCurrentBuffer->DoCompress();
DoWrite(m_pCurrentBuffer);
}
}
}
template <class TFileP, class TEngine, unsigned BufferSize> void LZFBufferedOutput<TFileP, TEngine, BufferSize>::Write(const void *pBuffer, unsigned nSize)
{
while (nSize>0)
{
unsigned nCopySize = std::min(BufferSize-m_pCurrentBuffer->m_nInBufferUsed, nSize);
memcpy(m_pCurrentBuffer->m_pInBuffer+m_pCurrentBuffer->m_nInBufferUsed, pBuffer, nCopySize);
m_pCurrentBuffer->m_nInBufferUsed += nCopySize;
nSize -= nCopySize;
pBuffer = ((char *)pBuffer) + nCopySize;
if (m_pCurrentBuffer->m_nInBufferUsed==BufferSize)
{
#ifndef __GNUC__
if (m_pEngine)
{
// queue up the data for compressing
m_pEngine->QueueThread(CompressBuffer::DoCompress, m_pCurrentBuffer);
// switch to the next buffer that is hopefully already done compressing
std::swap(m_pCurrentBuffer, m_pNextBuffer);
// and write any previously compressed data in this buffer
// the DoWrite will take care of waiting for the compression to be done if needed
if (m_pCurrentBuffer->m_nInBufferUsed!=0)
{
m_pCurrentBuffer->WaitForCompletion();
DoWrite(m_pCurrentBuffer);
}
}
else
#endif
{
// single threaded mode
m_pCurrentBuffer->DoCompress();
DoWrite(m_pCurrentBuffer);
}
}
}
}
template <class TFileP, class TEngine, unsigned BufferSize> LZFBufferedOutput<TFileP, TEngine, BufferSize>::~LZFBufferedOutput()
{
FlushBuffer();
}
////////////////////////////////////////////////////////////////////////////////
// class LZFBufferedOutput
// TFileP is usually a SmartPointer to a file
template <class TFileP, unsigned BufferSize=0x40000> class LZFBufferedInput : public SmartPointerRefObj_Base
{
unsigned char m_pOutBuffer[BufferSize];
unsigned char m_pInBuffer[BufferSize];
unsigned nInBufferNext;
unsigned nInBufferSize;
TFileP m_pFile;
public:
LZFBufferedInput(TFileP pFile);
void Reset()
{
nInBufferSize = 0;
nInBufferNext = 0;
}
unsigned Read(void *pBuffer, unsigned nSize);
TFileP GetFile() { return m_pFile;}
};
template <class TFileP, unsigned BufferSize> LZFBufferedInput<TFileP, BufferSize>::LZFBufferedInput(TFileP pFile)
: nInBufferNext(0), nInBufferSize(0)
{
m_pFile = pFile;
}
template <class TFileP, unsigned BufferSize> unsigned LZFBufferedInput<TFileP, BufferSize>::Read(void *pBuffer, unsigned nSize)
{
unsigned nRet = nSize;
while (nSize>0)
{
if (nInBufferSize<=nInBufferNext)
{
unsigned nResultBytes = 0;
bool bUncompressed = false;
if (BufferSize<=0x7fff)
{
unsigned short snResultBytes = static_cast<unsigned short>(nResultBytes);
unsigned nBytesRead = m_pFile->Read(&snResultBytes, sizeof(snResultBytes));
if (nBytesRead==0)
return nRet-nSize; // EOF
if (sizeof(snResultBytes)!=nBytesRead)
throw Error("Internal Error in LZFBufferedInput<TFileP, BufferSize>::Read");
if (snResultBytes & 0x8000)
{
snResultBytes &= 0x7fff;
bUncompressed = true;
}
nResultBytes = snResultBytes;
}
else
{
m_pFile->Read(&nResultBytes, sizeof(nResultBytes));
if (nResultBytes & 0x80000000)
{
nResultBytes &= 0x7fffffff;
bUncompressed = true;
}
}
if (bUncompressed)
{
nInBufferSize = nResultBytes;
if (nInBufferSize>sizeof(m_pOutBuffer))
{
assert(false);
throw Error("Internal Error in LZFBufferedInput<TFileP>::Read: corrupt file.");
}
if (nInBufferSize!=m_pFile->Read(m_pOutBuffer, nInBufferSize))
throw Error("Internal Error in LZFBufferedInput<TFileP>::Read: Not enough bytes read");
}
else
{
size_t nBytesRead = m_pFile->Read(m_pInBuffer, nResultBytes);
nInBufferSize = lzf_decompress(m_pInBuffer, unsigned(nBytesRead), m_pOutBuffer, unsigned(sizeof(m_pOutBuffer)));
if (nInBufferSize == 0)
return 0; // EOF - may get in infinite loop
}
nInBufferNext = 0;
}
unsigned nCopySize = std::min(unsigned(nInBufferSize-nInBufferNext), nSize);
memcpy(pBuffer, m_pOutBuffer+nInBufferNext, nCopySize);
nInBufferNext += nCopySize;
nSize -= nCopySize;
pBuffer = ((char *)pBuffer) + nCopySize;
}
return nRet;
}
}
#endif //__LZF_SRC_H__