Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// Copyright 2018 Google
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
 
15
#import "GoogleUtilities/NSData+zlib/Public/GoogleUtilities/GULNSData+zlib.h"
16
 
17
#import <zlib.h>
18
 
19
#define kChunkSize 1024
20
#define Z_DEFAULT_COMPRESSION (-1)
21
 
22
NSString *const GULNSDataZlibErrorDomain = @"com.google.GULNSDataZlibErrorDomain";
23
NSString *const GULNSDataZlibErrorKey = @"GULNSDataZlibErrorKey";
24
NSString *const GULNSDataZlibRemainingBytesKey = @"GULNSDataZlibRemainingBytesKey";
25
 
26
@implementation NSData (GULGzip)
27
 
28
+ (NSData *)gul_dataByInflatingGzippedData:(NSData *)data error:(NSError **)error {
29
  const void *bytes = [data bytes];
30
  NSUInteger length = [data length];
31
  if (!bytes || !length) {
32
    return nil;
33
  }
34
 
35
#if defined(__LP64__) && __LP64__
36
  // Don't support > 32bit length for 64 bit, see note in header.
37
  if (length > UINT_MAX) {
38
    return nil;
39
  }
40
#endif
41
 
42
  z_stream strm;
43
  bzero(&strm, sizeof(z_stream));
44
 
45
  // Setup the input.
46
  strm.avail_in = (unsigned int)length;
47
  strm.next_in = (unsigned char *)bytes;
48
 
49
  int windowBits = 15;  // 15 to enable any window size
50
  windowBits += 32;     // and +32 to enable zlib or gzip header detection.
51
 
52
  int retCode;
53
  if ((retCode = inflateInit2(&strm, windowBits)) != Z_OK) {
54
    if (error) {
55
      NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
56
                                                           forKey:GULNSDataZlibErrorKey];
57
      *error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
58
                                   code:GULNSDataZlibErrorInternal
59
                               userInfo:userInfo];
60
    }
61
    return nil;
62
  }
63
 
64
  // Hint the size at 4x the input size.
65
  NSMutableData *result = [NSMutableData dataWithCapacity:(length * 4)];
66
  unsigned char output[kChunkSize];
67
 
68
  // Loop to collect the data.
69
  do {
70
    // Update what we're passing in.
71
    strm.avail_out = kChunkSize;
72
    strm.next_out = output;
73
    retCode = inflate(&strm, Z_NO_FLUSH);
74
    if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
75
      if (error) {
76
        NSMutableDictionary *userInfo =
77
            [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
78
                                               forKey:GULNSDataZlibErrorKey];
79
        if (strm.msg) {
80
          NSString *message = [NSString stringWithUTF8String:strm.msg];
81
          if (message) {
82
            [userInfo setObject:message forKey:NSLocalizedDescriptionKey];
83
          }
84
        }
85
        *error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
86
                                     code:GULNSDataZlibErrorInternal
87
                                 userInfo:userInfo];
88
      }
89
      inflateEnd(&strm);
90
      return nil;
91
    }
92
    // Collect what we got.
93
    unsigned gotBack = kChunkSize - strm.avail_out;
94
    if (gotBack > 0) {
95
      [result appendBytes:output length:gotBack];
96
    }
97
 
98
  } while (retCode == Z_OK);
99
 
100
  // Make sure there wasn't more data tacked onto the end of a valid compressed stream.
101
  if (strm.avail_in != 0) {
102
    if (error) {
103
      NSDictionary *userInfo =
104
          [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:strm.avail_in]
105
                                      forKey:GULNSDataZlibRemainingBytesKey];
106
      *error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
107
                                   code:GULNSDataZlibErrorDataRemaining
108
                               userInfo:userInfo];
109
    }
110
    result = nil;
111
  }
112
  // The only way out of the loop was by hitting the end of the stream.
113
  NSAssert(retCode == Z_STREAM_END,
114
           @"Thought we finished inflate w/o getting a result of stream end, code %d", retCode);
115
 
116
  // Clean up.
117
  inflateEnd(&strm);
118
 
119
  return result;
120
}
121
 
122
+ (NSData *)gul_dataByGzippingData:(NSData *)data error:(NSError **)error {
123
  const void *bytes = [data bytes];
124
  NSUInteger length = [data length];
125
 
126
  int level = Z_DEFAULT_COMPRESSION;
127
  if (!bytes || !length) {
128
    return nil;
129
  }
130
 
131
#if defined(__LP64__) && __LP64__
132
  // Don't support > 32bit length for 64 bit, see note in header.
133
  if (length > UINT_MAX) {
134
    if (error) {
135
      *error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
136
                                   code:GULNSDataZlibErrorGreaterThan32BitsToCompress
137
                               userInfo:nil];
138
    }
139
    return nil;
140
  }
141
#endif
142
 
143
  z_stream strm;
144
  bzero(&strm, sizeof(z_stream));
145
 
146
  int memLevel = 8;          // Default.
147
  int windowBits = 15 + 16;  // Enable gzip header instead of zlib header.
148
 
149
  int retCode;
150
  if ((retCode = deflateInit2(&strm, level, Z_DEFLATED, windowBits, memLevel,
151
                              Z_DEFAULT_STRATEGY)) != Z_OK) {
152
    if (error) {
153
      NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
154
                                                           forKey:GULNSDataZlibErrorKey];
155
      *error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
156
                                   code:GULNSDataZlibErrorInternal
157
                               userInfo:userInfo];
158
    }
159
    return nil;
160
  }
161
 
162
  // Hint the size at 1/4 the input size.
163
  NSMutableData *result = [NSMutableData dataWithCapacity:(length / 4)];
164
  unsigned char output[kChunkSize];
165
 
166
  // Setup the input.
167
  strm.avail_in = (unsigned int)length;
168
  strm.next_in = (unsigned char *)bytes;
169
 
170
  // Collect the data.
171
  do {
172
    // update what we're passing in
173
    strm.avail_out = kChunkSize;
174
    strm.next_out = output;
175
    retCode = deflate(&strm, Z_FINISH);
176
    if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
177
      if (error) {
178
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
179
                                                             forKey:GULNSDataZlibErrorKey];
180
        *error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
181
                                     code:GULNSDataZlibErrorInternal
182
                                 userInfo:userInfo];
183
      }
184
      deflateEnd(&strm);
185
      return nil;
186
    }
187
    // Collect what we got.
188
    unsigned gotBack = kChunkSize - strm.avail_out;
189
    if (gotBack > 0) {
190
      [result appendBytes:output length:gotBack];
191
    }
192
 
193
  } while (retCode == Z_OK);
194
 
195
  // If the loop exits, it used all input and the stream ended.
196
  NSAssert(strm.avail_in == 0,
197
           @"Should have finished deflating without using all input, %u bytes left", strm.avail_in);
198
  NSAssert(retCode == Z_STREAM_END,
199
           @"thought we finished deflate w/o getting a result of stream end, code %d", retCode);
200
 
201
  // Clean up.
202
  deflateEnd(&strm);
203
 
204
  return result;
205
}
206
 
207
@end