yaz0 0.1.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/ext/yaz0/ext_yaz0.c +165 -0
- data/ext/yaz0/ext_yaz0.h +8 -0
- data/ext/yaz0/extconf.rb +11 -1
- data/lib/yaz0/version.rb +1 -1
- data/lib/yaz0/yaz0.bundle +0 -0
- data/lib/yaz0.rb +42 -0
- data/libyaz0/include/yaz0.h +34 -0
- data/libyaz0/src/libyaz0/CMakeLists.txt +3 -0
- data/libyaz0/src/libyaz0/compress.c +434 -0
- data/libyaz0/src/libyaz0/decompress.c +197 -0
- data/libyaz0/src/libyaz0/libyaz0.c +64 -0
- data/libyaz0/src/libyaz0/libyaz0.h +49 -0
- data/libyaz0/src/libyaz0/util.c +6 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/yaz0_spec.rb +39 -0
- data/yaz0.gemspec +13 -3
- metadata +19 -16
- data/.gitignore +0 -19
- data/.travis.yml +0 -6
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/ext/yaz0/buffer.c +0 -29
- data/ext/yaz0/compress.c +0 -319
- data/ext/yaz0/decompress.c +0 -64
- data/ext/yaz0/yaz0.c +0 -48
- data/ext/yaz0/yaz0.h +0 -38
@@ -0,0 +1,434 @@
|
|
1
|
+
#include <string.h>
|
2
|
+
#include <stdio.h>
|
3
|
+
#include "libyaz0.h"
|
4
|
+
|
5
|
+
static const uint32_t kProbesPerLevel[] = {
|
6
|
+
0x0,
|
7
|
+
0x1,
|
8
|
+
0x2,
|
9
|
+
0x4,
|
10
|
+
0x8,
|
11
|
+
0x10,
|
12
|
+
0x40,
|
13
|
+
0x100,
|
14
|
+
0x200,
|
15
|
+
0x1000
|
16
|
+
};
|
17
|
+
|
18
|
+
static uint32_t hash(uint8_t a, uint8_t b, uint8_t c)
|
19
|
+
{
|
20
|
+
uint32_t x = (uint32_t)a | ((uint32_t)b << 8) | ((uint32_t)c << 16);
|
21
|
+
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
22
|
+
x = (x >> 16) ^ x;
|
23
|
+
return x;
|
24
|
+
}
|
25
|
+
|
26
|
+
static void hashWrite(Yaz0Stream* s, uint32_t h, uint32_t offset)
|
27
|
+
{
|
28
|
+
uint32_t maxProbes;
|
29
|
+
uint32_t bucket;
|
30
|
+
uint32_t tmpBucket;
|
31
|
+
uint32_t oldest;
|
32
|
+
uint32_t entry;
|
33
|
+
int32_t pos;
|
34
|
+
|
35
|
+
oldest = 0xffffffff;
|
36
|
+
maxProbes = kProbesPerLevel[s->level];
|
37
|
+
for (uint32_t i = 0; i < maxProbes; ++i)
|
38
|
+
{
|
39
|
+
tmpBucket = (h + i) % HASH_MAX_ENTRIES;
|
40
|
+
entry = s->htEntries[tmpBucket];
|
41
|
+
if (entry == 0xffffffff)
|
42
|
+
{
|
43
|
+
s->htSize++;
|
44
|
+
bucket = tmpBucket;
|
45
|
+
break;
|
46
|
+
}
|
47
|
+
pos = (int32_t)(s->totalOut - entry);
|
48
|
+
if (pos > 0x1000)
|
49
|
+
{
|
50
|
+
bucket = tmpBucket;
|
51
|
+
break;
|
52
|
+
}
|
53
|
+
if (entry < oldest)
|
54
|
+
{
|
55
|
+
oldest = entry;
|
56
|
+
bucket = tmpBucket;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
s->htEntries[bucket] = s->totalOut + offset;
|
60
|
+
s->htHashes[bucket] = h;
|
61
|
+
}
|
62
|
+
|
63
|
+
static void rebuildHashTable(Yaz0Stream* s)
|
64
|
+
{
|
65
|
+
uint32_t newEntries[HASH_MAX_ENTRIES];
|
66
|
+
uint32_t newHashes[HASH_MAX_ENTRIES];
|
67
|
+
uint32_t entry;
|
68
|
+
uint32_t pos;
|
69
|
+
uint32_t h;
|
70
|
+
uint32_t bucket;
|
71
|
+
uint32_t size;
|
72
|
+
|
73
|
+
memset(newEntries, 0xff, sizeof(newEntries));
|
74
|
+
memset(newHashes, 0xff, sizeof(newHashes));
|
75
|
+
size = 0;
|
76
|
+
|
77
|
+
for (uint32_t i = 0; i < HASH_MAX_ENTRIES; ++i)
|
78
|
+
{
|
79
|
+
entry = s->htEntries[i];
|
80
|
+
if (entry == 0xffffffff)
|
81
|
+
continue;
|
82
|
+
pos = s->totalOut - entry;
|
83
|
+
if (pos > 0x1000)
|
84
|
+
continue;
|
85
|
+
|
86
|
+
/* Entry still good - move to the table */
|
87
|
+
h = s->htHashes[i];
|
88
|
+
bucket = h % HASH_MAX_ENTRIES;
|
89
|
+
for (;;)
|
90
|
+
{
|
91
|
+
if (newEntries[bucket] == 0xffffffff)
|
92
|
+
{
|
93
|
+
newEntries[bucket] = entry;
|
94
|
+
newHashes[bucket] = h;
|
95
|
+
size++;
|
96
|
+
break;
|
97
|
+
}
|
98
|
+
bucket = (bucket + 1) % HASH_MAX_ENTRIES;
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
s->htSize = size;
|
103
|
+
memcpy(s->htEntries, newEntries, sizeof(newEntries));
|
104
|
+
memcpy(s->htHashes, newHashes, sizeof(newHashes));
|
105
|
+
}
|
106
|
+
|
107
|
+
static uint32_t maxSize(Yaz0Stream* stream)
|
108
|
+
{
|
109
|
+
/* the extra byte is for look-aheads */
|
110
|
+
static const uint32_t maxNecessary = 0x888 + 1;
|
111
|
+
uint32_t max;
|
112
|
+
|
113
|
+
max = stream->decompSize - stream->totalOut;
|
114
|
+
if (max > maxNecessary)
|
115
|
+
max = maxNecessary;
|
116
|
+
return max;
|
117
|
+
}
|
118
|
+
|
119
|
+
/* start: start of avail data */
|
120
|
+
/* end: end of avail data */
|
121
|
+
/* We need to write more data at the end of the window */
|
122
|
+
|
123
|
+
static int feed(Yaz0Stream* s)
|
124
|
+
{
|
125
|
+
uint32_t avail;
|
126
|
+
uint32_t min;
|
127
|
+
uint32_t max;
|
128
|
+
uint32_t size;
|
129
|
+
int ret;
|
130
|
+
|
131
|
+
/* Check how much data we have */
|
132
|
+
if (s->window_start > s->window_end)
|
133
|
+
avail = WINDOW_SIZE - s->window_start + s->window_end;
|
134
|
+
else
|
135
|
+
avail = s->window_end - s->window_start;
|
136
|
+
if (avail >= maxSize(s))
|
137
|
+
return YAZ0_OK;
|
138
|
+
|
139
|
+
/* We need more data */
|
140
|
+
min = maxSize(s) - avail;
|
141
|
+
max = WINDOW_SIZE - 0x1000 - avail;
|
142
|
+
if (max > s->sizeIn - s->cursorIn)
|
143
|
+
max = s->sizeIn - s->cursorIn;
|
144
|
+
if (max < min)
|
145
|
+
ret = YAZ0_NEED_AVAIL_IN;
|
146
|
+
else
|
147
|
+
ret = YAZ0_OK;
|
148
|
+
if (s->window_end + max >= WINDOW_SIZE)
|
149
|
+
{
|
150
|
+
/* We might need two copies */
|
151
|
+
size = WINDOW_SIZE - s->window_end;
|
152
|
+
memcpy(s->window + s->window_end, s->in + s->cursorIn, size);
|
153
|
+
s->cursorIn += size;
|
154
|
+
s->window_end = 0;
|
155
|
+
max -= size;
|
156
|
+
}
|
157
|
+
memcpy(s->window + s->window_end, s->in + s->cursorIn, max);
|
158
|
+
s->cursorIn += max;
|
159
|
+
s->window_end += max;
|
160
|
+
return ret;
|
161
|
+
}
|
162
|
+
|
163
|
+
static uint32_t matchSize(Yaz0Stream* s, uint32_t offset, uint32_t pos, uint32_t hintSize)
|
164
|
+
{
|
165
|
+
uint32_t size = 0;
|
166
|
+
uint32_t start = s->window_start + offset;
|
167
|
+
uint32_t cursorA = (start + WINDOW_SIZE - pos) % WINDOW_SIZE;
|
168
|
+
uint32_t cursorB = start % WINDOW_SIZE;
|
169
|
+
uint32_t maxSize;
|
170
|
+
|
171
|
+
maxSize = s->decompSize - s->totalOut;
|
172
|
+
if (maxSize > 0x111)
|
173
|
+
maxSize = 0x111;
|
174
|
+
if (hintSize)
|
175
|
+
{
|
176
|
+
if (s->window[(cursorA + hintSize) % WINDOW_SIZE] != s->window[(cursorB + hintSize) % WINDOW_SIZE])
|
177
|
+
return 0;
|
178
|
+
}
|
179
|
+
for (;;)
|
180
|
+
{
|
181
|
+
if (s->window[(cursorA + size) % WINDOW_SIZE] != s->window[(cursorB + size) % WINDOW_SIZE])
|
182
|
+
break;
|
183
|
+
size++;
|
184
|
+
if (size == maxSize)
|
185
|
+
break;
|
186
|
+
}
|
187
|
+
return size;
|
188
|
+
}
|
189
|
+
|
190
|
+
static void findHashMatch(Yaz0Stream* s, uint32_t h, uint32_t offset, uint32_t* outSize, uint32_t* outPos)
|
191
|
+
{
|
192
|
+
uint32_t bucket;
|
193
|
+
uint32_t entry;
|
194
|
+
uint32_t bestSize;
|
195
|
+
uint32_t bestPos;
|
196
|
+
uint32_t size;
|
197
|
+
uint32_t pos;
|
198
|
+
uint32_t maxProbes;
|
199
|
+
|
200
|
+
bestSize = 0;
|
201
|
+
bestPos = 0;
|
202
|
+
maxProbes = kProbesPerLevel[s->level];
|
203
|
+
for (uint32_t i = 0; i < maxProbes; ++i)
|
204
|
+
{
|
205
|
+
bucket = (h + i) % HASH_MAX_ENTRIES;
|
206
|
+
entry = s->htEntries[bucket];
|
207
|
+
if (entry == 0xffffffff)
|
208
|
+
break;
|
209
|
+
if (s->htHashes[bucket] == h)
|
210
|
+
{
|
211
|
+
pos = s->totalOut + offset - entry;
|
212
|
+
if (pos > 0x1000)
|
213
|
+
continue;
|
214
|
+
size = matchSize(s, offset, pos, bestSize);
|
215
|
+
if (size > bestSize)
|
216
|
+
{
|
217
|
+
bestSize = size;
|
218
|
+
bestPos = pos;
|
219
|
+
}
|
220
|
+
}
|
221
|
+
}
|
222
|
+
|
223
|
+
if (bestSize < 3)
|
224
|
+
{
|
225
|
+
*outSize = 0;
|
226
|
+
}
|
227
|
+
else
|
228
|
+
{
|
229
|
+
*outSize = bestSize;
|
230
|
+
*outPos = bestPos;
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
static void findEarlyZeroes(Yaz0Stream* s, uint32_t* outSize, uint32_t* outPos)
|
235
|
+
{
|
236
|
+
uint32_t size;
|
237
|
+
uint32_t bestSize;
|
238
|
+
uint32_t bestPos;
|
239
|
+
|
240
|
+
bestSize = 0;
|
241
|
+
bestPos = 0;
|
242
|
+
for (uint32_t pos = 0x1000; pos > 0x990; --pos)
|
243
|
+
{
|
244
|
+
size = matchSize(s, 0, pos, 0);
|
245
|
+
if (size > bestSize)
|
246
|
+
{
|
247
|
+
bestSize = size;
|
248
|
+
bestPos = pos;
|
249
|
+
}
|
250
|
+
}
|
251
|
+
|
252
|
+
if (bestSize < 3)
|
253
|
+
{
|
254
|
+
*outSize = 0;
|
255
|
+
}
|
256
|
+
else
|
257
|
+
{
|
258
|
+
*outSize = bestSize;
|
259
|
+
*outPos = bestPos;
|
260
|
+
}
|
261
|
+
}
|
262
|
+
|
263
|
+
static void emitGroup(Yaz0Stream* s, int count, const uint32_t* arrSize, const uint32_t* arrPos)
|
264
|
+
{
|
265
|
+
uint8_t header;
|
266
|
+
uint32_t size;
|
267
|
+
uint32_t pos;
|
268
|
+
|
269
|
+
header = 0;
|
270
|
+
for (int i = 0; i < count; ++i)
|
271
|
+
{
|
272
|
+
if (!arrSize[i])
|
273
|
+
header |= (1 << (7 - i));
|
274
|
+
}
|
275
|
+
s->out[s->cursorOut++] = header;
|
276
|
+
for (int i = 0; i < count; ++i)
|
277
|
+
{
|
278
|
+
size = arrSize[i];
|
279
|
+
pos = arrPos[i];
|
280
|
+
if (!size)
|
281
|
+
s->out[s->cursorOut++] = (uint8_t)pos;
|
282
|
+
else
|
283
|
+
{
|
284
|
+
pos--;
|
285
|
+
if (size >= 0x12)
|
286
|
+
{
|
287
|
+
/* 3 bytes */
|
288
|
+
s->out[s->cursorOut++] = (uint8_t)(pos >> 8);
|
289
|
+
s->out[s->cursorOut++] = (uint8_t)pos;
|
290
|
+
s->out[s->cursorOut++] = (uint8_t)(size - 0x12);
|
291
|
+
}
|
292
|
+
else
|
293
|
+
{
|
294
|
+
/* 2 bytes */
|
295
|
+
s->out[s->cursorOut++] = (uint8_t)(pos >> 8) | (uint8_t)((size - 2) << 4);
|
296
|
+
s->out[s->cursorOut++] = (uint8_t)pos;
|
297
|
+
}
|
298
|
+
}
|
299
|
+
}
|
300
|
+
}
|
301
|
+
|
302
|
+
static void compressGroup(Yaz0Stream* s)
|
303
|
+
{
|
304
|
+
int groupCount;
|
305
|
+
uint32_t h;
|
306
|
+
uint32_t size;
|
307
|
+
uint32_t pos;
|
308
|
+
uint32_t nextSize;
|
309
|
+
uint32_t nextPos;
|
310
|
+
uint32_t arrSize[8];
|
311
|
+
uint32_t arrPos[8];
|
312
|
+
uint8_t a;
|
313
|
+
uint8_t b;
|
314
|
+
uint8_t c;
|
315
|
+
uint8_t d;
|
316
|
+
|
317
|
+
for (groupCount = 0; groupCount < 8; ++groupCount)
|
318
|
+
{
|
319
|
+
a = s->window[s->window_start];
|
320
|
+
b = s->window[(s->window_start + 1) % WINDOW_SIZE];
|
321
|
+
c = s->window[(s->window_start + 2) % WINDOW_SIZE];
|
322
|
+
d = s->window[(s->window_start + 3) % WINDOW_SIZE];
|
323
|
+
h = hash(a, b, c);
|
324
|
+
findHashMatch(s, h, 0, &size, &pos);
|
325
|
+
hashWrite(s, h, 0);
|
326
|
+
|
327
|
+
/* Check for early zero */
|
328
|
+
if (s->window[s->window_start] == 0 && s->totalOut < 0x1000)
|
329
|
+
{
|
330
|
+
findEarlyZeroes(s, &nextSize, &nextPos);
|
331
|
+
if (nextSize > size)
|
332
|
+
{
|
333
|
+
size = nextSize;
|
334
|
+
pos = nextPos;
|
335
|
+
}
|
336
|
+
}
|
337
|
+
|
338
|
+
nextSize = 0;
|
339
|
+
nextPos = 0;
|
340
|
+
h = hash(b, c, d);
|
341
|
+
findHashMatch(s, h, 1, &nextSize, &nextPos);
|
342
|
+
|
343
|
+
if (!size || nextSize > size)
|
344
|
+
{
|
345
|
+
arrSize[groupCount] = 0;
|
346
|
+
arrPos[groupCount] = s->window[s->window_start];
|
347
|
+
s->window_start += 1;
|
348
|
+
s->totalOut += 1;
|
349
|
+
}
|
350
|
+
else
|
351
|
+
{
|
352
|
+
arrSize[groupCount] = size;
|
353
|
+
arrPos[groupCount] = pos;
|
354
|
+
for (uint32_t i = 1; i < size; ++i)
|
355
|
+
{
|
356
|
+
a = b;
|
357
|
+
b = c;
|
358
|
+
c = s->window[(s->window_start + 2 + i) % WINDOW_SIZE];
|
359
|
+
h = hash(a, b, c);
|
360
|
+
hashWrite(s, h, i);
|
361
|
+
}
|
362
|
+
s->window_start += size;
|
363
|
+
s->totalOut += size;
|
364
|
+
}
|
365
|
+
s->window_start %= WINDOW_SIZE;
|
366
|
+
if (s->totalOut >= s->decompSize)
|
367
|
+
{
|
368
|
+
groupCount++;
|
369
|
+
break;
|
370
|
+
}
|
371
|
+
}
|
372
|
+
if (s->htSize > HASH_REBUILD)
|
373
|
+
rebuildHashTable(s);
|
374
|
+
emitGroup(s, groupCount, arrSize, arrPos);
|
375
|
+
}
|
376
|
+
|
377
|
+
int yaz0ModeCompress(Yaz0Stream* s, uint32_t size, int level)
|
378
|
+
{
|
379
|
+
memset(s, 0, sizeof(*s));
|
380
|
+
s->mode = MODE_COMPRESS;
|
381
|
+
s->decompSize = size;
|
382
|
+
if (level < 1)
|
383
|
+
level = 1;
|
384
|
+
else if (level > 9)
|
385
|
+
level = 9;
|
386
|
+
s->level = level;
|
387
|
+
for (int i = 0; i < HASH_MAX_ENTRIES; ++i)
|
388
|
+
{
|
389
|
+
s->htHashes[i] = 0xffffffff;
|
390
|
+
s->htEntries[i] = 0xffffffff;
|
391
|
+
}
|
392
|
+
return YAZ0_OK;
|
393
|
+
}
|
394
|
+
|
395
|
+
int yaz0_RunCompress(Yaz0Stream* stream)
|
396
|
+
{
|
397
|
+
uint32_t tmp;
|
398
|
+
int ret;
|
399
|
+
|
400
|
+
/* Write headers */
|
401
|
+
if (!stream->headersDone)
|
402
|
+
{
|
403
|
+
if (stream->sizeOut < 16)
|
404
|
+
return YAZ0_NEED_AVAIL_OUT;
|
405
|
+
memcpy(stream->out, "Yaz0", 4);
|
406
|
+
tmp = swap32(stream->decompSize);
|
407
|
+
memcpy(stream->out + 4, &tmp, 4);
|
408
|
+
tmp = 0;
|
409
|
+
memcpy(stream->out + 8, &tmp, 4);
|
410
|
+
memcpy(stream->out + 12, &tmp, 4);
|
411
|
+
stream->cursorOut += 16;
|
412
|
+
stream->headersDone = 1;
|
413
|
+
}
|
414
|
+
|
415
|
+
/* Compress */
|
416
|
+
for (;;)
|
417
|
+
{
|
418
|
+
/* Check EOF */
|
419
|
+
if (stream->totalOut >= stream->decompSize)
|
420
|
+
return YAZ0_OK;
|
421
|
+
|
422
|
+
/* Check output space */
|
423
|
+
if (stream->sizeOut - stream->cursorOut < 1 + 8 * 3)
|
424
|
+
return YAZ0_NEED_AVAIL_OUT;
|
425
|
+
|
426
|
+
/* Check that we have consumed enough input */
|
427
|
+
ret = feed(stream);
|
428
|
+
if (ret)
|
429
|
+
return ret;
|
430
|
+
|
431
|
+
/* Compress one chunk */
|
432
|
+
compressGroup(stream);
|
433
|
+
}
|
434
|
+
}
|
@@ -0,0 +1,197 @@
|
|
1
|
+
#include <string.h>
|
2
|
+
#include <stdio.h>
|
3
|
+
#include "libyaz0.h"
|
4
|
+
|
5
|
+
int yaz0ModeDecompress(Yaz0Stream* s)
|
6
|
+
{
|
7
|
+
memset(s, 0, sizeof(*s));
|
8
|
+
s->mode = MODE_DECOMPRESS;
|
9
|
+
return YAZ0_OK;
|
10
|
+
}
|
11
|
+
|
12
|
+
void loadAux(Yaz0Stream* stream, uint32_t size)
|
13
|
+
{
|
14
|
+
if (stream->sizeIn - stream->cursorIn < size)
|
15
|
+
size = stream->sizeIn - stream->cursorIn;
|
16
|
+
for (uint32_t i = 0; i < size; i++)
|
17
|
+
stream->auxBuf[stream->auxSize++] = stream->in[stream->cursorIn++];
|
18
|
+
}
|
19
|
+
|
20
|
+
int flush(Yaz0Stream* stream)
|
21
|
+
{
|
22
|
+
size_t chunkSize;
|
23
|
+
size_t outSize;
|
24
|
+
|
25
|
+
if (stream->window_start == stream->window_end)
|
26
|
+
return YAZ0_OK;
|
27
|
+
if (stream->cursorOut >= stream->sizeOut)
|
28
|
+
return YAZ0_NEED_AVAIL_OUT;
|
29
|
+
outSize = stream->sizeOut - stream->cursorOut;
|
30
|
+
if (stream->window_start > stream->window_end)
|
31
|
+
{
|
32
|
+
/* Wrap around - we will need 2 copies */
|
33
|
+
chunkSize = WINDOW_SIZE - stream->window_start;
|
34
|
+
if (chunkSize > outSize)
|
35
|
+
chunkSize = outSize;
|
36
|
+
memcpy(stream->out + stream->cursorOut, stream->window + stream->window_start, chunkSize);
|
37
|
+
stream->cursorOut += chunkSize;
|
38
|
+
stream->window_start += chunkSize;
|
39
|
+
stream->window_start %= WINDOW_SIZE;
|
40
|
+
outSize -= chunkSize;
|
41
|
+
}
|
42
|
+
/* Copy the rest */
|
43
|
+
chunkSize = stream->window_end - stream->window_start;
|
44
|
+
if (chunkSize > outSize)
|
45
|
+
chunkSize = outSize;
|
46
|
+
memcpy(stream->out + stream->cursorOut, stream->window + stream->window_start, chunkSize);
|
47
|
+
stream->cursorOut += chunkSize;
|
48
|
+
stream->window_start += chunkSize;
|
49
|
+
return YAZ0_OK;
|
50
|
+
}
|
51
|
+
|
52
|
+
static int yaz0_ReadHeaders(Yaz0Stream* stream)
|
53
|
+
{
|
54
|
+
loadAux(stream, 16 - stream->auxSize);
|
55
|
+
if (stream->auxSize < 16)
|
56
|
+
return YAZ0_NEED_AVAIL_IN;
|
57
|
+
if (memcmp(stream->auxBuf, "Yaz0", 4))
|
58
|
+
return YAZ0_BAD_MAGIC;
|
59
|
+
stream->decompSize = swap32(*(uint32_t*)&stream->auxBuf[4]);
|
60
|
+
stream->auxSize = 0;
|
61
|
+
return YAZ0_OK;
|
62
|
+
}
|
63
|
+
|
64
|
+
static uint32_t windowFreeSize(Yaz0Stream* stream)
|
65
|
+
{
|
66
|
+
uint32_t size;
|
67
|
+
if (stream->window_start > stream->window_end)
|
68
|
+
size = WINDOW_SIZE - stream->window_start + stream->window_end;
|
69
|
+
else
|
70
|
+
size = stream->window_end - stream->window_start;
|
71
|
+
return WINDOW_SIZE - size;
|
72
|
+
}
|
73
|
+
|
74
|
+
static int ensureWindowFree(Yaz0Stream* stream)
|
75
|
+
{
|
76
|
+
static const uint32_t maxSize = 0x111 * 8;
|
77
|
+
|
78
|
+
if (windowFreeSize(stream) < maxSize)
|
79
|
+
{
|
80
|
+
flush(stream);
|
81
|
+
if (windowFreeSize(stream) < maxSize)
|
82
|
+
return YAZ0_NEED_AVAIL_OUT;
|
83
|
+
}
|
84
|
+
return YAZ0_OK;
|
85
|
+
}
|
86
|
+
|
87
|
+
int yaz0_DoDecompress(Yaz0Stream* stream)
|
88
|
+
{
|
89
|
+
uint8_t groupBit;
|
90
|
+
uint8_t byte;
|
91
|
+
int ret;
|
92
|
+
uint16_t n;
|
93
|
+
uint16_t r;
|
94
|
+
for (;;)
|
95
|
+
{
|
96
|
+
/* No group and no EOF - We need to read */
|
97
|
+
if (stream->groupCount == 0)
|
98
|
+
{
|
99
|
+
/* Before we read, we eant to ensure the window is large enough */
|
100
|
+
/* This will avoid a lot of checks and let us write the whole group */
|
101
|
+
ret = ensureWindowFree(stream);
|
102
|
+
if (ret)
|
103
|
+
return ret;
|
104
|
+
if (stream->cursorIn >= stream->sizeIn)
|
105
|
+
return YAZ0_NEED_AVAIL_IN;
|
106
|
+
stream->groupHeader = stream->in[stream->cursorIn++];
|
107
|
+
stream->groupCount = 8;
|
108
|
+
}
|
109
|
+
|
110
|
+
/* We have a group! */
|
111
|
+
while (stream->groupCount)
|
112
|
+
{
|
113
|
+
/* Get the group bit */
|
114
|
+
groupBit = stream->groupHeader & (1 << (stream->groupCount - 1));
|
115
|
+
if (groupBit)
|
116
|
+
{
|
117
|
+
/* Group bit is 1 - direct write */
|
118
|
+
if (stream->cursorIn >= stream->sizeIn)
|
119
|
+
return YAZ0_NEED_AVAIL_IN;
|
120
|
+
|
121
|
+
/* Everything ok, copy the byte */
|
122
|
+
byte = stream->in[stream->cursorIn++];
|
123
|
+
stream->window[stream->window_end++] = byte;
|
124
|
+
stream->window_end %= WINDOW_SIZE;
|
125
|
+
stream->totalOut++;
|
126
|
+
}
|
127
|
+
else
|
128
|
+
{
|
129
|
+
if (stream->auxSize < 2)
|
130
|
+
{
|
131
|
+
loadAux(stream, 2 - stream->auxSize);
|
132
|
+
if (stream->auxSize < 2)
|
133
|
+
return YAZ0_NEED_AVAIL_IN;
|
134
|
+
}
|
135
|
+
/* Do we need 2 or 3 bytes? */
|
136
|
+
n = ((uint8_t)stream->auxBuf[0]) >> 4;
|
137
|
+
if (!n)
|
138
|
+
{
|
139
|
+
/* We have a large chunk */
|
140
|
+
loadAux(stream, 1);
|
141
|
+
if (stream->auxSize < 3)
|
142
|
+
return YAZ0_NEED_AVAIL_IN;
|
143
|
+
n = (uint8_t)stream->auxBuf[2] + 0x12;
|
144
|
+
}
|
145
|
+
else
|
146
|
+
{
|
147
|
+
/* We have a small chunk */
|
148
|
+
n = ((uint8_t)stream->auxBuf[0] >> 4) + 2;
|
149
|
+
}
|
150
|
+
r = ((uint16_t)(((uint8_t)stream->auxBuf[0] & 0x0f) << 8) | ((uint8_t)stream->auxBuf[1]));
|
151
|
+
r++;
|
152
|
+
/* Reset the aux buffer */
|
153
|
+
uint32_t cursor = (stream->window_end + WINDOW_SIZE - r) % WINDOW_SIZE;
|
154
|
+
for (int i = 0; i < n; ++i)
|
155
|
+
{
|
156
|
+
stream->window[stream->window_end++] = stream->window[cursor++];
|
157
|
+
stream->window_end %= WINDOW_SIZE;
|
158
|
+
cursor %= WINDOW_SIZE;
|
159
|
+
}
|
160
|
+
stream->auxSize = 0;
|
161
|
+
stream->totalOut += n;
|
162
|
+
}
|
163
|
+
stream->groupCount--;
|
164
|
+
/* Check for EOF */
|
165
|
+
if (stream->totalOut >= stream->decompSize)
|
166
|
+
return YAZ0_OK;
|
167
|
+
}
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
int yaz0_RunDecompress(Yaz0Stream* stream)
|
172
|
+
{
|
173
|
+
int ret;
|
174
|
+
|
175
|
+
/* Check the headers */
|
176
|
+
if (!stream->headersDone)
|
177
|
+
{
|
178
|
+
ret = yaz0_ReadHeaders(stream);
|
179
|
+
if (ret)
|
180
|
+
return ret;
|
181
|
+
stream->headersDone = 1;
|
182
|
+
}
|
183
|
+
|
184
|
+
if (stream->totalOut < stream->decompSize)
|
185
|
+
{
|
186
|
+
/* We did not decompress everything */
|
187
|
+
ret = yaz0_DoDecompress(stream);
|
188
|
+
if (ret)
|
189
|
+
return ret;
|
190
|
+
}
|
191
|
+
|
192
|
+
/* We did decompress everything */
|
193
|
+
flush(stream);
|
194
|
+
if (stream->window_start != stream->window_end)
|
195
|
+
return YAZ0_NEED_AVAIL_OUT;
|
196
|
+
return YAZ0_OK;
|
197
|
+
}
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#include <stdlib.h>
|
2
|
+
#include "libyaz0.h"
|
3
|
+
|
4
|
+
int yaz0Init(Yaz0Stream** ptr)
|
5
|
+
{
|
6
|
+
Yaz0Stream* s;
|
7
|
+
|
8
|
+
s = malloc(sizeof(*s));
|
9
|
+
if (!s)
|
10
|
+
return YAZ0_OUT_OF_MEMORY;
|
11
|
+
s->mode = MODE_NONE;
|
12
|
+
s->cursorOut = 0;
|
13
|
+
s->decompSize = 0;
|
14
|
+
*ptr = s;
|
15
|
+
return YAZ0_OK;
|
16
|
+
}
|
17
|
+
|
18
|
+
int yaz0Destroy(Yaz0Stream* stream)
|
19
|
+
{
|
20
|
+
free(stream);
|
21
|
+
return YAZ0_OK;
|
22
|
+
}
|
23
|
+
|
24
|
+
int yaz0Run(Yaz0Stream* s)
|
25
|
+
{
|
26
|
+
switch (s->mode)
|
27
|
+
{
|
28
|
+
case MODE_NONE:
|
29
|
+
return YAZ0_OK;
|
30
|
+
case MODE_DECOMPRESS:
|
31
|
+
return yaz0_RunDecompress(s);
|
32
|
+
case MODE_COMPRESS:
|
33
|
+
return yaz0_RunCompress(s);
|
34
|
+
default:
|
35
|
+
unreachable();
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
int yaz0Input(Yaz0Stream* stream, const void* data, uint32_t size)
|
40
|
+
{
|
41
|
+
stream->in = data;
|
42
|
+
stream->sizeIn = size;
|
43
|
+
stream->cursorIn = 0;
|
44
|
+
|
45
|
+
return YAZ0_OK;
|
46
|
+
}
|
47
|
+
|
48
|
+
int yaz0Output(Yaz0Stream* stream, void* data, uint32_t size)
|
49
|
+
{
|
50
|
+
stream->out = data;
|
51
|
+
stream->sizeOut = size;
|
52
|
+
stream->cursorOut = 0;
|
53
|
+
return YAZ0_OK;
|
54
|
+
}
|
55
|
+
|
56
|
+
uint32_t yaz0OutputChunkSize(const Yaz0Stream* stream)
|
57
|
+
{
|
58
|
+
return stream->cursorOut;
|
59
|
+
}
|
60
|
+
|
61
|
+
uint32_t yaz0DecompressedSize(const Yaz0Stream* stream)
|
62
|
+
{
|
63
|
+
return stream->decompSize;
|
64
|
+
}
|