1 /* converted by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software. It comes without any warranty, to
5 * the extent permitted by applicable law. You can redistribute it
6 * and/or modify it under the terms of the Do What The Fuck You Want
7 * To Public License, Version 2, as published by Sam Hocevar. See
8 * http://www.wtfpl.net/txt/copying/ for more details.
10 // LZMA Reference Decoder
11 // 2015-06-14 : Igor Pavlov : Public domain
12 // This code implements LZMA file decoding according to LZMA specification.
13 // This code is not optimized for speed.
14 module iv
.dlzma
.refdec
;
19 CLzmaDecoder lzmaDecoder;
21 ubyte[5] header = void; // lzma properties
22 fi.rawReadExact(header[]);
23 lzmaDecoder.decodeProperties(header.ptr);
24 lzmaDecoder.markerIsMandatory = false;
25 lzmaDecoder.create(totalUnpackedSize);
28 auto res = lzmaDecoder.decodeStep(
29 delegate () { ubyte b; fi.rawReadExact((&b)[0..1]); return b; },
30 (ubyte b) { fo.rawWriteExact((&b)[0..1]); }
33 case CLzmaDecoder.Result.Continue: break;
34 case CLzmaDecoder.Result.Error: throw new VFSException("LZMA stream corrupted");
35 case CLzmaDecoder.Result.FinishedWithMarker:
36 case CLzmaDecoder.Result.FinishedWithoutMarker:
38 default: assert(0, "LZMA internal error");
44 // ////////////////////////////////////////////////////////////////////////// //
50 FinishedWithMarker
= 1,
51 FinishedWithoutMarker
= 2,
55 CProb
[kNumStates
<<kNumPosBitsMax
] isMatch
;
56 CProb
[kNumStates
] isRep
;
57 CProb
[kNumStates
] isRepG0
;
58 CProb
[kNumStates
] isRepG1
;
59 CProb
[kNumStates
] isRepG2
;
60 CProb
[kNumStates
<<kNumPosBitsMax
] isRep0Long
;
62 CLenDecoder lenDecoder
;
63 CLenDecoder repLenDecoder
;
66 usize litProbsAllotedBytes
;
67 CBitTreeDecoder
!6[kNumLenToPosStates
] posSlotDecoder
;
68 CBitTreeDecoder
!kNumAlignBits alignDecoder
;
69 CProb
[1+kNumFullDistances
-kEndPosModelIndex
] posDecoders
;
71 uint rep0
, rep1
, rep2
, rep3
;
73 bool unpackSizeDefined
;
77 CRangeDecoder rangeDec
;
80 bool markerIsMandatory
;
83 uint dictSizeInProperties
;
86 //@disable this (this);
89 import core
.stdc
.stdlib
: free
;
90 if (litProbs
!is null) free(litProbs
);
96 void decodeProperties (const(ubyte)* properties
) {
97 enum LZMAMinDictSize
= 1U<<12;
98 uint d
= properties
[0];
99 if (d
>= 9*5*5) throw new Exception("Incorrect LZMA properties");
104 dictSizeInProperties
= 0;
105 for (int i
= 0; i
< 4; ++i
) dictSizeInProperties |
= cast(uint)properties
[i
+1]<<(8*i
);
106 dictSize
= dictSizeInProperties
;
107 if (dictSize
< LZMAMinDictSize
) dictSize
= LZMAMinDictSize
;
110 void create (ulong aunpackSize
) {
112 unpackSizeDefined
= true;
113 unpackSize
= aunpackSize
;
118 unpackSizeDefined
= false;
123 Result
decodeStep (scope ubyte delegate () readByte
, scope void delegate (ubyte b
) writeByte
) {
124 void decodeLiteral (uint state
, uint rep0
) {
126 if (!outWindow
.isEmpty()) prevByte
= outWindow
.getByte(1);
129 uint litState
= ((outWindow
.totalPos
&((1<<lp
)-1))<<lc
)+(prevByte
>>(8-lc
));
130 CProb
* probs
= litProbs
+(0x300U
*litState
);
133 uint matchByte
= outWindow
.getByte(rep0
+1);
135 uint matchBit
= (matchByte
>>7)&1;
137 uint bit
= rangeDec
.decodeBit(&probs
[((1+matchBit
)<<8)+symbol
], readByte
);
138 symbol
= (symbol
<<1)|bit
;
139 if (matchBit
!= bit
) break;
140 } while (symbol
< 0x100);
141 } while (symbol
< 0x100)
142 symbol
= (symbol
<<1)|rangeDec
.decodeBit(&probs
[symbol
], readByte
);
143 outWindow
.putByte(cast(ubyte)(symbol
-0x100), writeByte
);
146 uint decodeDistance (uint len
) {
148 if (lenState
> kNumLenToPosStates
-1) lenState
= kNumLenToPosStates
-1;
150 uint posSlot
= posSlotDecoder
[lenState
].decode(rangeDec
, readByte
);
151 if (posSlot
< 4) return posSlot
;
153 uint numDirectBits
= cast(uint)((posSlot
>>1)-1);
154 uint dist
= ((2|
(posSlot
&1))<<numDirectBits
);
155 if (posSlot
< kEndPosModelIndex
) {
156 dist
+= bitTreeReverseDecode(posDecoders
.ptr
+dist
-posSlot
, numDirectBits
, rangeDec
, readByte
);
158 dist
+= rangeDec
.decodeDirectBits(numDirectBits
-kNumAlignBits
, readByte
)<<kNumAlignBits
;
159 dist
+= alignDecoder
.reverseDecode(rangeDec
, readByte
);
166 outWindow
.create(dictSize
);
168 if (!rangeDec
.initialize(readByte
)) throw new Exception("can't initialize lzma range decoder");
170 rep0
= rep1
= rep2
= rep3
= 0;
174 if (unpackSizeDefined
&& unpackSize
== 0 && !markerIsMandatory
) {
175 if (rangeDec
.isFinishedOK()) return Result
.FinishedWithoutMarker
;
178 uint posState
= outWindow
.totalPos
&((1<<pb
)-1);
180 if (rangeDec
.decodeBit(&isMatch
[(state
<<kNumPosBitsMax
)+posState
], readByte
) == 0) {
181 if (unpackSizeDefined
&& unpackSize
== 0) return Result
.Error
;
182 decodeLiteral(state
, rep0
);
183 state
= updateStateLiteral(state
);
185 return Result
.Continue
;
190 if (rangeDec
.decodeBit(&isRep
[state
], readByte
) != 0) {
191 if (unpackSizeDefined
&& unpackSize
== 0) return Result
.Error
;
192 if (outWindow
.isEmpty()) return Result
.Error
;
193 if (rangeDec
.decodeBit(&isRepG0
[state
], readByte
) == 0) {
194 if (rangeDec
.decodeBit(&isRep0Long
[(state
<<kNumPosBitsMax
)+posState
], readByte
) == 0) {
195 state
= updateStateShortRep(state
);
196 outWindow
.putByte(outWindow
.getByte(rep0
+1), writeByte
);
198 return Result
.Continue
;
202 if (rangeDec
.decodeBit(&isRepG1
[state
], readByte
) == 0) {
205 if (rangeDec
.decodeBit(&isRepG2
[state
], readByte
) == 0) {
216 len
= repLenDecoder
.decode(rangeDec
, posState
, readByte
);
217 state
= updateStateRep(state
);
222 len
= lenDecoder
.decode(rangeDec
, posState
, readByte
);
223 state
= updateStateMatch(state
);
224 rep0
= decodeDistance(len
);
225 if (rep0
== 0xFFFFFFFF) return (rangeDec
.isFinishedOK() ? Result
.FinishedWithMarker
: Result
.Error
);
226 if (unpackSizeDefined
&& unpackSize
== 0) return Result
.Error
;
227 if (rep0
>= dictSize ||
!outWindow
.checkDistance(rep0
)) return Result
.Error
;
230 bool isError
= false;
231 if (unpackSizeDefined
&& unpackSize
< len
) {
232 len
= cast(uint)unpackSize
;
235 outWindow
.copyMatch(rep0
+1, len
, writeByte
);
237 if (isError
) return Result
.Error
;
238 return Result
.Continue
;
242 void createLiterals () {
243 //litProbs = new CProb[](0x300U<<(lc+lp));
244 import core
.stdc
.stdlib
: realloc
;
245 //import core.stdc.string : memset;
246 usize toalloc
= (0x300U
<<(lc
+lp
))*CProb
.sizeof
;
247 if (litProbs
is null || toalloc
> litProbsAllotedBytes
) {
248 auto nb
= cast(CProb
*)realloc(litProbs
, toalloc
);
249 if (nb
is null) throw new Exception("LZMA: out of memory");
250 litProbsAllotedBytes
= toalloc
;
253 //memset(litProbs, 0, (0x300U<<(lc+lp))*CProb.sizeof);
256 void initLiterals () {
257 uint num
= 0x300U
<<(lc
+lp
);
258 //for (uint i = 0; i < num; ++i) litProbs[i] = ProbInitValue;
259 litProbs
[0..num
] = ProbInitValue
;
263 for (uint i
= 0; i
< kNumLenToPosStates
; i
++) posSlotDecoder
[i
].initialize();
264 alignDecoder
.initialize();
265 posDecoders
[] = ProbInitValue
;
272 isMatch
[] = ProbInitValue
;
273 isRep
[] = ProbInitValue
;
274 isRepG0
[] = ProbInitValue
;
275 isRepG1
[] = ProbInitValue
;
276 isRepG2
[] = ProbInitValue
;
277 isRep0Long
[] = ProbInitValue
;
279 lenDecoder
.initialize();
280 repLenDecoder
.initialize();
295 //@disable this (this);
298 import core
.stdc
.stdlib
: free
;
300 //{ import core.stdc.stdio : printf; printf("LZMA: freeing: buf=%p; bufsize=%u\n", buf, bufsize); }
306 void create (uint dictSize
) @trusted {
307 import core
.stdc
.stdlib
: realloc
;
308 if (buf
is null || bufsize
< dictSize
) {
309 auto nb
= cast(ubyte*)realloc(buf
, dictSize
);
311 //{ import core.stdc.stdio : printf; printf("*** buf=%p; nb=%p; bufsize=%u; dictSize=%u\n", buf, nb, bufsize, dictSize); }
312 throw new Exception("LZMA: cannot allocate sliding window");
325 buf
[0..size
] = 0; // just in case
328 ubyte getByte (uint dist
) const pure nothrow @trusted @nogc { pragma(inline
, true); return (buf
[dist
<= pos ? pos
-dist
: size
-dist
+pos
]); }
330 void putByte (ubyte b
, scope void delegate (ubyte b
) writeByte
) {
333 if (pos
== size
) { pos
= 0; isFull
= true; }
337 void copyMatch (uint dist
, uint len
, scope void delegate (ubyte b
) writeByte
) {
338 pragma(inline
, true);
340 //putByte(getByte(dist));
341 ubyte b
= getByte(dist
);
344 if (pos
== size
) { pos
= 0; isFull
= true; }
349 bool checkDistance (uint dist
) const pure nothrow @trusted @nogc { pragma(inline
, true); return (dist
<= pos || isFull
); }
351 bool isEmpty () const pure nothrow @trusted @nogc { pragma(inline
, true); return (pos
== 0 && !isFull
); }
355 enum kNumBitModelTotalBits
= 11;
356 enum kNumMoveBits
= 5;
358 alias CProb
= ushort;
360 enum ProbInitValue
= (1U<<kNumBitModelTotalBits
)/2;
363 struct CRangeDecoder
{
365 enum kTopValue
= 1U<<24;
371 void normalize (scope ubyte delegate () readByte
) {
372 if (range
< kTopValue
) {
374 code
= (code
<<8)|
readByte();
381 //@disable this (this);
383 bool initialize (scope ubyte delegate () readByte
) {
387 ubyte b
= readByte();
388 for (int i
= 0; i
< 4; i
++) code
= (code
<<8)|
readByte();
389 if (b
!= 0 || code
== range
) corrupted
= true;
393 bool isFinishedOK () const pure nothrow @safe @nogc { pragma(inline
, true); return (code
== 0); }
395 uint decodeDirectBits (uint numBits
, scope ubyte delegate () readByte
) {
400 uint t
= 0U-(cast(uint)code
>>31);
402 if (code
== range
) corrupted
= true;
410 uint decodeBit (CProb
* prob
, scope ubyte delegate () readByte
) {
412 uint bound = (range
>>kNumBitModelTotalBits
)*v
;
415 v
+= ((1<<kNumBitModelTotalBits
)-v
)>>kNumMoveBits
;
419 v
-= v
>>kNumMoveBits
;
424 *prob
= cast(CProb
)v
;
431 uint bitTreeReverseDecode (CProb
* probs
, uint numBits
, ref CRangeDecoder rc
, scope ubyte delegate () readByte
) {
434 for (uint i
= 0; i
< numBits
; ++i
) {
435 uint bit
= rc
.decodeBit(probs
+m
, readByte
);
444 struct CBitTreeDecoder(uint NumBits
) {
445 CProb
[1U<<NumBits
] probs
= ProbInitValue
;
448 //@disable this (this);
450 void initialize () { probs
[] = ProbInitValue
; }
452 uint decode (ref CRangeDecoder rc
, scope ubyte delegate () readByte
) {
454 for (uint i
= 0; i
< NumBits
; ++i
) m
= (m
<<1)+rc
.decodeBit(&probs
[m
], readByte
);
455 return m
-(1U<<NumBits
);
458 uint reverseDecode (ref CRangeDecoder rc
, scope ubyte delegate () readByte
) { pragma(inline
, true); return bitTreeReverseDecode(probs
.ptr
, NumBits
, rc
, readByte
); }
462 enum kNumPosBitsMax
= 4;
464 enum kNumStates
= 12;
465 enum kNumLenToPosStates
= 4;
466 enum kNumAlignBits
= 4;
467 enum kStartPosModelIndex
= 4;
468 enum kEndPosModelIndex
= 14;
469 enum kNumFullDistances
= 1U<<(kEndPosModelIndex
>>1);
470 enum kMatchMinLen
= 2;
476 CBitTreeDecoder
!3[1U<<kNumPosBitsMax
] lowCoder
;
477 CBitTreeDecoder
!3[1U<<kNumPosBitsMax
] midCoder
;
478 CBitTreeDecoder
!8 highCoder
;
481 //@disable this (this);
484 choice
= ProbInitValue
;
485 choice2
= ProbInitValue
;
486 highCoder
.initialize();
487 for (uint i
= 0; i
< (1<<kNumPosBitsMax
); ++i
) {
488 lowCoder
[i
].initialize();
489 midCoder
[i
].initialize();
493 uint decode (ref CRangeDecoder rc
, uint posState
, scope ubyte delegate () readByte
) {
494 if (rc
.decodeBit(&choice
, readByte
) == 0) return lowCoder
[posState
].decode(rc
, readByte
);
495 if (rc
.decodeBit(&choice2
, readByte
) == 0) return 8+midCoder
[posState
].decode(rc
, readByte
);
496 return 16+highCoder
.decode(rc
, readByte
);
501 uint updateStateLiteral (uint state
) pure nothrow @safe @nogc {
502 pragma(inline
, true);
504 if (state < 4) return 0;
505 if (state < 10) return state-3;
508 return (state
< 4 ?
0 : state
< 10 ? state
-3 : state
-6);
510 uint updateStateMatch (uint state
) pure nothrow @safe @nogc { pragma(inline
, true); return (state
< 7 ?
7 : 10); }
511 uint updateStateRep (uint state
) pure nothrow @safe @nogc { pragma(inline
, true); return (state
< 7 ?
8 : 11); }
512 uint updateStateShortRep (uint state
) pure nothrow @safe @nogc { pragma(inline
, true); return (state
< 7 ?
9 : 11); }