2011-08-01 09:39:41 -07:00
//
// ASIDataDecompressor . m
// Part of ASIHTTPRequest -> http : // allseeing - i . com / ASIHTTPRequest
//
// Created by Ben Copsey on 17 / 08 / 2010.
// Copyright 2010 All - Seeing Interactive . All rights reserved .
//
# import "ASIDataDecompressor.h"
# import "ASIHTTPRequest.h"
# define DATA_CHUNK _SIZE 262144 // Deal with gzipped data in 256 KB chunks
@ interface ASIDataDecompressor ( )
+ ( NSError * ) inflateErrorWithCode : ( int ) code ;
@ end ;
@ implementation ASIDataDecompressor
+ ( id ) decompressor
{
ASIDataDecompressor * decompressor = [ [ [ self alloc ] init ] autorelease ] ;
[ decompressor setupStream ] ;
return decompressor ;
}
- ( void ) dealloc
{
if ( streamReady ) {
[ self closeStream ] ;
}
[ super dealloc ] ;
}
- ( NSError * ) setupStream
{
if ( streamReady ) {
return nil ;
}
// Setup the inflate stream
zStream . zalloc = Z_NULL ;
zStream . zfree = Z_NULL ;
zStream . opaque = Z_NULL ;
zStream . avail_in = 0 ;
zStream . next_in = 0 ;
int status = inflateInit2 ( & zStream , ( 15 + 32 ) ) ;
if ( status ! = Z_OK ) {
return [ [ self class ] inflateErrorWithCode : status ] ;
}
streamReady = YES ;
return nil ;
}
- ( NSError * ) closeStream
{
if ( ! streamReady ) {
return nil ;
}
// Close the inflate stream
streamReady = NO ;
int status = inflateEnd ( & zStream ) ;
if ( status ! = Z_OK ) {
return [ [ self class ] inflateErrorWithCode : status ] ;
}
return nil ;
}
- ( NSData * ) uncompressBytes : ( Bytef * ) bytes length : ( NSUInteger ) length error : ( NSError * * ) err
{
if ( length = = 0 ) return nil ;
NSUInteger halfLength = length / 2 ;
NSMutableData * outputData = [ NSMutableData dataWithLength : length + halfLength ] ;
int status ;
zStream . next_in = bytes ;
zStream . avail_in = ( unsigned int ) length ;
zStream . avail_out = 0 ;
2015-09-16 16:53:07 -07:00
NSUInteger bytesProcessedAlready = zStream . total_out ;
2011-08-01 09:39:41 -07:00
while ( zStream . avail_in ! = 0 ) {
if ( zStream . total_out - bytesProcessedAlready >= [ outputData length ] ) {
[ outputData increaseLengthBy : halfLength ] ;
}
2011-10-15 15:26:18 -07:00
zStream . next_out = ( Bytef * ) [ outputData mutableBytes ] + zStream . total_out - bytesProcessedAlready ;
2011-08-01 09:39:41 -07:00
zStream . avail_out = ( unsigned int ) ( [ outputData length ] - ( zStream . total_out - bytesProcessedAlready ) ) ;
status = inflate ( & zStream , Z_NO _FLUSH ) ;
if ( status = = Z_STREAM _END ) {
break ;
} else if ( status ! = Z_OK ) {
if ( err ) {
* err = [ [ self class ] inflateErrorWithCode : status ] ;
}
return nil ;
}
}
// Set real length
[ outputData setLength : zStream . total_out - bytesProcessedAlready ] ;
return outputData ;
}
+ ( NSData * ) uncompressData : ( NSData * ) compressedData error : ( NSError * * ) err
{
NSError * theError = nil ;
NSData * outputData = [ [ ASIDataDecompressor decompressor ] uncompressBytes : ( Bytef * ) [ compressedData bytes ] length : [ compressedData length ] error : & theError ] ;
if ( theError ) {
if ( err ) {
* err = theError ;
}
return nil ;
}
return outputData ;
}
+ ( BOOL ) uncompressDataFromFile : ( NSString * ) sourcePath toFile : ( NSString * ) destinationPath error : ( NSError * * ) err
{
NSFileManager * fileManager = [ [ [ NSFileManager alloc ] init ] autorelease ] ;
// Create an empty file at the destination path
if ( ! [ fileManager createFileAtPath : destinationPath contents : [ NSData data ] attributes : nil ] ) {
if ( err ) {
* err = [ NSError errorWithDomain : NetworkRequestErrorDomain code : ASICompressionError userInfo : [ NSDictionary dictionaryWithObjectsAndKeys : [ NSString stringWithFormat : @ "Decompression of %@ failed because we were to create a file at %@" , sourcePath , destinationPath ] , NSLocalizedDescriptionKey , nil ] ] ;
}
return NO ;
}
// Ensure the source file exists
if ( ! [ fileManager fileExistsAtPath : sourcePath ] ) {
if ( err ) {
* err = [ NSError errorWithDomain : NetworkRequestErrorDomain code : ASICompressionError userInfo : [ NSDictionary dictionaryWithObjectsAndKeys : [ NSString stringWithFormat : @ "Decompression of %@ failed the file does not exist" , sourcePath ] , NSLocalizedDescriptionKey , nil ] ] ;
}
return NO ;
}
UInt8 inputData [ DATA_CHUNK _SIZE ] ;
NSData * outputData ;
NSInteger readLength ;
NSError * theError = nil ;
ASIDataDecompressor * decompressor = [ ASIDataDecompressor decompressor ] ;
NSInputStream * inputStream = [ NSInputStream inputStreamWithFileAtPath : sourcePath ] ;
[ inputStream open ] ;
NSOutputStream * outputStream = [ NSOutputStream outputStreamToFileAtPath : destinationPath append : NO ] ;
[ outputStream open ] ;
while ( [ decompressor streamReady ] ) {
// Read some data from the file
2015-09-16 16:53:07 -07:00
readLength = [ inputStream read : inputData maxLength : DATA_CHUNK _SIZE ] ;
2011-08-01 09:39:41 -07:00
// Make sure nothing went wrong
2013-12-11 18:06:22 -08:00
if ( [ inputStream streamStatus ] = = NSStreamStatusError ) {
2011-08-01 09:39:41 -07:00
if ( err ) {
* err = [ NSError errorWithDomain : NetworkRequestErrorDomain code : ASICompressionError userInfo : [ NSDictionary dictionaryWithObjectsAndKeys : [ NSString stringWithFormat : @ "Decompression of %@ failed because we were unable to read from the source data file" , sourcePath ] , NSLocalizedDescriptionKey , [ inputStream streamError ] , NSUnderlyingErrorKey , nil ] ] ;
}
[ decompressor closeStream ] ;
return NO ;
}
// Have we reached the end of the input data ?
if ( ! readLength ) {
break ;
}
// Attempt to inflate the chunk of data
2015-09-16 16:53:07 -07:00
outputData = [ decompressor uncompressBytes : inputData length : ( NSUInteger ) readLength error : & theError ] ;
2011-08-01 09:39:41 -07:00
if ( theError ) {
if ( err ) {
* err = theError ;
}
[ decompressor closeStream ] ;
return NO ;
}
// Write the inflated data out to the destination file
2011-10-15 15:26:18 -07:00
[ outputStream write : ( Bytef * ) [ outputData bytes ] maxLength : [ outputData length ] ] ;
2011-08-01 09:39:41 -07:00
// Make sure nothing went wrong
2013-12-11 18:06:22 -08:00
if ( [ inputStream streamStatus ] = = NSStreamStatusError ) {
2011-08-01 09:39:41 -07:00
if ( err ) {
2011-10-15 15:26:18 -07:00
* err = [ NSError errorWithDomain : NetworkRequestErrorDomain code : ASICompressionError userInfo : [ NSDictionary dictionaryWithObjectsAndKeys : [ NSString stringWithFormat : @ "Decompression of %@ failed because we were unable to write to the destination data file at %@" , sourcePath , destinationPath ] , NSLocalizedDescriptionKey , [ outputStream streamError ] , NSUnderlyingErrorKey , nil ] ] ;
2011-08-01 09:39:41 -07:00
}
[ decompressor closeStream ] ;
return NO ;
}
}
[ inputStream close ] ;
[ outputStream close ] ;
NSError * error = [ decompressor closeStream ] ;
if ( error ) {
if ( err ) {
* err = error ;
}
return NO ;
}
return YES ;
}
+ ( NSError * ) inflateErrorWithCode : ( int ) code
{
2015-09-16 16:53:07 -07:00
return [ NSError errorWithDomain : NetworkRequestErrorDomain code : ASICompressionError userInfo : [ NSDictionary dictionaryWithObjectsAndKeys : [ NSString stringWithFormat : @ "Decompression of data failed with code %d" , code ] , NSLocalizedDescriptionKey , nil ] ] ;
2011-08-01 09:39:41 -07:00
}
@ synthesize streamReady ;
@ end