2 * Copyright (c) 2008, Charles Wilson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * A copy of the GNU General Public License can be found at
12 * Written by Charles Wilson <cygwin@cygwin.com>
16 #include "compress_lzma.h"
25 * allocator for LzmaDec
27 static void *SzAlloc(void *p
, size_t size
) { p
= p
; return malloc(size
); }
28 static void SzFree(void *p
, void *address
) { p
= p
; free(address
); }
29 static ISzAlloc g_Alloc
= { SzAlloc
, SzFree
};
32 * Predicate: the stream is open for read.
34 compress_lzma::compress_lzma (io_stream
* parent
)
53 if (!parent
|| parent
->error())
60 inbuf
= (unsigned char *) malloc (IN_BUF_SIZE
);
67 outbuf
= (unsigned char *) malloc (OUT_BUF_SIZE
);
76 check_header (); /* parse the header, and position fp */
88 compress_lzma::read (void *buffer
, size_t len
)
91 if (!this->initializedOk
)
94 /* there is no recovery from a busted stream */
101 /* peekbuf is layered on top of existing buffering code */
104 ssize_t tmplen
= std::min (this->peeklen
, len
);
105 this->peeklen
-= tmplen
;
106 memcpy (buffer
, this->peekbuf
, tmplen
);
107 memmove (this->peekbuf
, this->peekbuf
+ tmplen
, sizeof(this->peekbuf
) - tmplen
);
108 ssize_t tmpread
= read (&((char *) buffer
)[tmplen
], len
- tmplen
);
110 return tmpread
+ tmplen
;
115 if (this->outp
< this->outbuf
+ this->outPos
)
116 /* outp - outbuf < outPos, but avoid sign/unsigned warning */
118 ssize_t tmplen
= std::min ((size_t)(this->outbuf
+ this->outPos
- this->outp
), len
);
119 memcpy (buffer
, this->outp
, tmplen
);
120 this->outp
+= tmplen
;
121 ssize_t tmpread
= read (&((char *) buffer
)[tmplen
], len
- tmplen
);
123 return tmpread
+ tmplen
;
128 size_t lenRemaining
= len
;
129 unsigned char * bufp
= (unsigned char *)buffer
;
130 /* if we made it here, any existing uncompressed data in outbuf
131 * has been consumed, so reset outp and outPos
133 this->outp
= this->outbuf
;
137 if (this->inPos
== this->inSize
)
139 this->inSize
= (size_t) this->original
->read(this->inbuf
, IN_BUF_SIZE
);
143 /* at this point, these two variables actually hold the number
144 * of bytes *available* to be processed or filled
146 this->inProcessed
= this->inSize
- this->inPos
;
147 this->outProcessed
= OUT_BUF_SIZE
- this->outPos
;
150 ELzmaFinishMode finishMode
= LZMA_FINISH_ANY
;
151 if (this->thereIsSize
&& this->outProcessed
> this->unpackSize
)
153 this->outProcessed
= unpackSize
;
154 finishMode
= LZMA_FINISH_END
;
158 int res
= LzmaDec_DecodeToBuf(
160 this->outbuf
+ this->outPos
,
161 &(this->outProcessed
),
162 this->inbuf
+ this->inPos
,
163 &(this->inProcessed
),
166 this->inPos
+= (UInt32
)this->inProcessed
;
167 this->outPos
+= this->outProcessed
;
168 this->unpackSize
-= this->outProcessed
;
170 ssize_t tmplen
= std::min (this->outProcessed
, lenRemaining
);
171 memcpy (bufp
, outp
, tmplen
);
174 lenRemaining
-= tmplen
;
183 if (this->thereIsSize
&& this->unpackSize
== 0)
188 if (this->inProcessed
== 0 && this->outProcessed
== 0)
190 if (this->thereIsSize
|| status
!= LZMA_STATUS_FINISHED_WITH_MARK
)
197 if (lenRemaining
== 0)
199 /* fulfilled request */
205 return (len
- lenRemaining
);
209 compress_lzma::write (const void *buffer
, size_t len
)
211 throw new logic_error("compress_lzma::write is not implemented");
215 compress_lzma::peek (void *buffer
, size_t len
)
217 /* can only peek 512 bytes */
221 if (len
> this->peeklen
)
223 size_t want
= len
- this->peeklen
;
224 ssize_t got
= read (&(this->peekbuf
[peeklen
]), want
);
226 this->peeklen
+= got
;
230 /* we may have read less than requested. */
231 memcpy (buffer
, this->peekbuf
, this->peeklen
);
232 return this->peeklen
;
236 memcpy (buffer
, this->peekbuf
, len
);
243 compress_lzma::tell ()
245 throw new logic_error("compress_lzma::tell is not implemented");
249 compress_lzma::seek (long where
, io_stream_seek_t whence
)
251 throw new logic_error("compress_lzma::seek is not implemented");
255 compress_lzma::error ()
261 compress_lzma::set_mtime (time_t mtime
)
264 return original
->set_mtime (mtime
);
269 compress_lzma::get_mtime ()
272 return original
->get_mtime ();
277 compress_lzma::get_mode ()
280 return original
->get_mode ();
285 compress_lzma::release_original ()
287 owns_original
= false;
291 compress_lzma::destroy ()
293 if (this->initializedOk
)
295 LzmaDec_Free(&(this->state
), &g_Alloc
);
308 if (original
&& owns_original
)
312 compress_lzma::~compress_lzma ()
317 /* ===========================================================================
318 * Check the header of a lzma_stream opened for reading.
320 * the stream s has already been created sucessfully
321 * this method is called only once per stream
324 compress_lzma::check_header ()
326 this->initializedOk
= false;
327 this->thereIsSize
= false;
328 this->unpackSize
= 0;
330 /* read properties */
331 if (this->original
->read(this->header
, sizeof(this->header
)) != sizeof(this->header
))
333 this->lasterr
= (errno
? errno
: EIO
);
337 /* read uncompressed size */
338 for (int i
= 0; i
< 8; i
++)
340 unsigned char b
= this->header
[LZMA_PROPS_SIZE
+ i
];
343 this->thereIsSize
= true;
345 this->unpackSize
+= (UInt64
)b
<< (i
* 8);
348 /* Decode LZMA properties and allocate memory */
349 LzmaDec_Construct(&(this->state
));
350 int res
= LzmaDec_Allocate(&(this->state
), this->header
, LZMA_PROPS_SIZE
, &g_Alloc
);
357 LzmaDec_Init(&(this->state
));
358 initializedOk
= true;
361 /* ===========================================================================
362 * duplicates a lot of code in check_header, but we don't want
363 * to read "too much" from the stream in the check_header case.
364 * Here, we don't care because we know compress will call this
365 * method with a peek'ed buffer. We also do not want to modify
366 * the member variables 'header' and 'state'.
369 compress_lzma::is_lzma (void * buffer
, size_t len
)
371 unsigned char local_header
[LZMA_PROPS_SIZE
+ 8];
372 CLzmaDec local_state
;
373 UInt64 local_unpackSize
= 0;
374 bool local_thereIsSize
= false;
377 if (len
< LZMA_PROPS_SIZE
+ 8)
381 memcpy((void*)local_header
, buffer
, sizeof(local_header
));
383 /* read uncompressed size */
384 for (int i
= 0; i
< 8; i
++)
386 unsigned char b
= local_header
[LZMA_PROPS_SIZE
+ i
];
389 local_thereIsSize
= true;
391 local_unpackSize
+= (UInt64
)b
<< (i
* 8);
395 if (LzmaProps_Decode(&(local_state
.prop
), local_header
, LZMA_PROPS_SIZE
) != SZ_OK
)