NSData+GZip.m 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. //
  2. // GZIP.m
  3. //
  4. // Version 1.2.1
  5. //
  6. // Created by Nick Lockwood on 03/06/2012.
  7. // Copyright (C) 2012 Charcoal Design
  8. //
  9. // Distributed under the permissive zlib License
  10. // Get the latest version from here:
  11. //
  12. // https://github.com/nicklockwood/GZIP
  13. //
  14. // This software is provided 'as-is', without any express or implied
  15. // warranty. In no event will the authors be held liable for any damages
  16. // arising from the use of this software.
  17. //
  18. // Permission is granted to anyone to use this software for any purpose,
  19. // including commercial applications, and to alter it and redistribute it
  20. // freely, subject to the following restrictions:
  21. //
  22. // 1. The origin of this software must not be misrepresented; you must not
  23. // claim that you wrote the original software. If you use this software
  24. // in a product, an acknowledgment in the product documentation would be
  25. // appreciated but is not required.
  26. //
  27. // 2. Altered source versions must be plainly marked as such, and must not be
  28. // misrepresented as being the original software.
  29. //
  30. // 3. This notice may not be removed or altered from any source distribution.
  31. //
  32. #import "NSData+GZip.h"
  33. #import <zlib.h>
  34. #pragma clang diagnostic ignored "-Wcast-qual"
  35. @implementation NSData (GZIP)
  36. - (NSData *)gzippedDataWithCompressionLevel:(float)level
  37. {
  38. if (self.length == 0 || [self isGzippedData])
  39. {
  40. return self;
  41. }
  42. z_stream stream;
  43. stream.zalloc = Z_NULL;
  44. stream.zfree = Z_NULL;
  45. stream.opaque = Z_NULL;
  46. stream.avail_in = (uint)self.length;
  47. stream.next_in = (Bytef *)(void *)self.bytes;
  48. stream.total_out = 0;
  49. stream.avail_out = 0;
  50. static const NSUInteger ChunkSize = 16384;
  51. NSMutableData *output = nil;
  52. int compression = (level < 0.0f)? Z_DEFAULT_COMPRESSION: (int)(roundf(level * 9));
  53. if (deflateInit2(&stream, compression, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) == Z_OK)
  54. {
  55. output = [NSMutableData dataWithLength:ChunkSize];
  56. while (stream.avail_out == 0)
  57. {
  58. if (stream.total_out >= output.length)
  59. {
  60. output.length += ChunkSize;
  61. }
  62. stream.next_out = (uint8_t *)output.mutableBytes + stream.total_out;
  63. stream.avail_out = (uInt)(output.length - stream.total_out);
  64. deflate(&stream, Z_FINISH);
  65. }
  66. deflateEnd(&stream);
  67. output.length = stream.total_out;
  68. }
  69. return output;
  70. }
  71. - (NSData *)gzippedData
  72. {
  73. return [self gzippedDataWithCompressionLevel:-1.0f];
  74. }
  75. - (NSData *)gunzippedData
  76. {
  77. if (self.length == 0 || ![self isGzippedData])
  78. {
  79. return self;
  80. }
  81. z_stream stream;
  82. stream.zalloc = Z_NULL;
  83. stream.zfree = Z_NULL;
  84. stream.avail_in = (uint)self.length;
  85. stream.next_in = (Bytef *)self.bytes;
  86. stream.total_out = 0;
  87. stream.avail_out = 0;
  88. NSMutableData *output = nil;
  89. if (inflateInit2(&stream, 47) == Z_OK)
  90. {
  91. int status = Z_OK;
  92. output = [NSMutableData dataWithCapacity:self.length * 2];
  93. while (status == Z_OK)
  94. {
  95. if (stream.total_out >= output.length)
  96. {
  97. output.length += self.length / 2;
  98. }
  99. stream.next_out = (uint8_t *)output.mutableBytes + stream.total_out;
  100. stream.avail_out = (uInt)(output.length - stream.total_out);
  101. status = inflate (&stream, Z_SYNC_FLUSH);
  102. }
  103. if (inflateEnd(&stream) == Z_OK)
  104. {
  105. if (status == Z_STREAM_END)
  106. {
  107. output.length = stream.total_out;
  108. }
  109. }
  110. }
  111. return output;
  112. }
  113. - (BOOL)isGzippedData
  114. {
  115. const UInt8 *bytes = (const UInt8 *)self.bytes;
  116. return (self.length >= 2 && bytes[0] == 0x1f && bytes[1] == 0x8b);
  117. }
  118. @end