added iOS source code
[wl-app.git] / iOS / Pods / GoogleUtilities / GoogleUtilities / NSData+zlib / GULNSData+zlib.m
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 "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