OLD | NEW |
| (Empty) |
1 // Protocol Buffers - Google's data interchange format | |
2 // Copyright 2008 Google Inc. All rights reserved. | |
3 // https://developers.google.com/protocol-buffers/ | |
4 // | |
5 // Redistribution and use in source and binary forms, with or without | |
6 // modification, are permitted provided that the following conditions are | |
7 // met: | |
8 // | |
9 // * Redistributions of source code must retain the above copyright | |
10 // notice, this list of conditions and the following disclaimer. | |
11 // * Redistributions in binary form must reproduce the above | |
12 // copyright notice, this list of conditions and the following disclaimer | |
13 // in the documentation and/or other materials provided with the | |
14 // distribution. | |
15 // * Neither the name of Google Inc. nor the names of its | |
16 // contributors may be used to endorse or promote products derived from | |
17 // this software without specific prior written permission. | |
18 // | |
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
30 | |
31 #import "GPBCodedInputStream_PackagePrivate.h" | |
32 | |
33 #import "GPBDictionary_PackagePrivate.h" | |
34 #import "GPBMessage_PackagePrivate.h" | |
35 #import "GPBUnknownFieldSet_PackagePrivate.h" | |
36 #import "GPBUtilities_PackagePrivate.h" | |
37 #import "GPBWireFormat.h" | |
38 | |
39 static const NSUInteger kDefaultRecursionLimit = 64; | |
40 | |
41 static void CheckSize(GPBCodedInputStreamState *state, size_t size) { | |
42 size_t newSize = state->bufferPos + size; | |
43 if (newSize > state->bufferSize) { | |
44 [NSException raise:NSParseErrorException format:@""]; | |
45 } | |
46 if (newSize > state->currentLimit) { | |
47 // Fast forward to end of currentLimit; | |
48 state->bufferPos = state->currentLimit; | |
49 [NSException raise:NSParseErrorException format:@""]; | |
50 } | |
51 } | |
52 | |
53 static int8_t ReadRawByte(GPBCodedInputStreamState *state) { | |
54 CheckSize(state, sizeof(int8_t)); | |
55 return ((int8_t *)state->bytes)[state->bufferPos++]; | |
56 } | |
57 | |
58 static int32_t ReadRawLittleEndian32(GPBCodedInputStreamState *state) { | |
59 CheckSize(state, sizeof(int32_t)); | |
60 int32_t value = OSReadLittleInt32(state->bytes, state->bufferPos); | |
61 state->bufferPos += sizeof(int32_t); | |
62 return value; | |
63 } | |
64 | |
65 static int64_t ReadRawLittleEndian64(GPBCodedInputStreamState *state) { | |
66 CheckSize(state, sizeof(int64_t)); | |
67 int64_t value = OSReadLittleInt64(state->bytes, state->bufferPos); | |
68 state->bufferPos += sizeof(int64_t); | |
69 return value; | |
70 } | |
71 | |
72 static int32_t ReadRawVarint32(GPBCodedInputStreamState *state) { | |
73 int8_t tmp = ReadRawByte(state); | |
74 if (tmp >= 0) { | |
75 return tmp; | |
76 } | |
77 int32_t result = tmp & 0x7f; | |
78 if ((tmp = ReadRawByte(state)) >= 0) { | |
79 result |= tmp << 7; | |
80 } else { | |
81 result |= (tmp & 0x7f) << 7; | |
82 if ((tmp = ReadRawByte(state)) >= 0) { | |
83 result |= tmp << 14; | |
84 } else { | |
85 result |= (tmp & 0x7f) << 14; | |
86 if ((tmp = ReadRawByte(state)) >= 0) { | |
87 result |= tmp << 21; | |
88 } else { | |
89 result |= (tmp & 0x7f) << 21; | |
90 result |= (tmp = ReadRawByte(state)) << 28; | |
91 if (tmp < 0) { | |
92 // Discard upper 32 bits. | |
93 for (int i = 0; i < 5; i++) { | |
94 if (ReadRawByte(state) >= 0) { | |
95 return result; | |
96 } | |
97 } | |
98 [NSException raise:NSParseErrorException | |
99 format:@"Unable to read varint32"]; | |
100 } | |
101 } | |
102 } | |
103 } | |
104 return result; | |
105 } | |
106 | |
107 static int64_t ReadRawVarint64(GPBCodedInputStreamState *state) { | |
108 int32_t shift = 0; | |
109 int64_t result = 0; | |
110 while (shift < 64) { | |
111 int8_t b = ReadRawByte(state); | |
112 result |= (int64_t)(b & 0x7F) << shift; | |
113 if ((b & 0x80) == 0) { | |
114 return result; | |
115 } | |
116 shift += 7; | |
117 } | |
118 [NSException raise:NSParseErrorException format:@"Unable to read varint64"]; | |
119 return 0; | |
120 } | |
121 | |
122 static void SkipRawData(GPBCodedInputStreamState *state, size_t size) { | |
123 CheckSize(state, size); | |
124 state->bufferPos += size; | |
125 } | |
126 | |
127 double GPBCodedInputStreamReadDouble(GPBCodedInputStreamState *state) { | |
128 int64_t value = ReadRawLittleEndian64(state); | |
129 return GPBConvertInt64ToDouble(value); | |
130 } | |
131 | |
132 float GPBCodedInputStreamReadFloat(GPBCodedInputStreamState *state) { | |
133 int32_t value = ReadRawLittleEndian32(state); | |
134 return GPBConvertInt32ToFloat(value); | |
135 } | |
136 | |
137 uint64_t GPBCodedInputStreamReadUInt64(GPBCodedInputStreamState *state) { | |
138 uint64_t value = ReadRawVarint64(state); | |
139 return value; | |
140 } | |
141 | |
142 uint32_t GPBCodedInputStreamReadUInt32(GPBCodedInputStreamState *state) { | |
143 uint32_t value = ReadRawVarint32(state); | |
144 return value; | |
145 } | |
146 | |
147 int64_t GPBCodedInputStreamReadInt64(GPBCodedInputStreamState *state) { | |
148 int64_t value = ReadRawVarint64(state); | |
149 return value; | |
150 } | |
151 | |
152 int32_t GPBCodedInputStreamReadInt32(GPBCodedInputStreamState *state) { | |
153 int32_t value = ReadRawVarint32(state); | |
154 return value; | |
155 } | |
156 | |
157 uint64_t GPBCodedInputStreamReadFixed64(GPBCodedInputStreamState *state) { | |
158 uint64_t value = ReadRawLittleEndian64(state); | |
159 return value; | |
160 } | |
161 | |
162 uint32_t GPBCodedInputStreamReadFixed32(GPBCodedInputStreamState *state) { | |
163 uint32_t value = ReadRawLittleEndian32(state); | |
164 return value; | |
165 } | |
166 | |
167 int32_t GPBCodedInputStreamReadEnum(GPBCodedInputStreamState *state) { | |
168 int32_t value = ReadRawVarint32(state); | |
169 return value; | |
170 } | |
171 | |
172 int32_t GPBCodedInputStreamReadSFixed32(GPBCodedInputStreamState *state) { | |
173 int32_t value = ReadRawLittleEndian32(state); | |
174 return value; | |
175 } | |
176 | |
177 int64_t GPBCodedInputStreamReadSFixed64(GPBCodedInputStreamState *state) { | |
178 int64_t value = ReadRawLittleEndian64(state); | |
179 return value; | |
180 } | |
181 | |
182 int32_t GPBCodedInputStreamReadSInt32(GPBCodedInputStreamState *state) { | |
183 int32_t value = GPBDecodeZigZag32(ReadRawVarint32(state)); | |
184 return value; | |
185 } | |
186 | |
187 int64_t GPBCodedInputStreamReadSInt64(GPBCodedInputStreamState *state) { | |
188 int64_t value = GPBDecodeZigZag64(ReadRawVarint64(state)); | |
189 return value; | |
190 } | |
191 | |
192 BOOL GPBCodedInputStreamReadBool(GPBCodedInputStreamState *state) { | |
193 return ReadRawVarint32(state) != 0; | |
194 } | |
195 | |
196 int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state) { | |
197 if (GPBCodedInputStreamIsAtEnd(state)) { | |
198 state->lastTag = 0; | |
199 return 0; | |
200 } | |
201 | |
202 state->lastTag = ReadRawVarint32(state); | |
203 if (state->lastTag == 0) { | |
204 // If we actually read zero, that's not a valid tag. | |
205 [NSException raise:NSParseErrorException | |
206 format:@"Invalid last tag %d", state->lastTag]; | |
207 } | |
208 return state->lastTag; | |
209 } | |
210 | |
211 NSString *GPBCodedInputStreamReadRetainedString( | |
212 GPBCodedInputStreamState *state) { | |
213 int32_t size = ReadRawVarint32(state); | |
214 NSString *result; | |
215 if (size == 0) { | |
216 result = @""; | |
217 } else { | |
218 CheckSize(state, size); | |
219 result = GPBCreateGPBStringWithUTF8(&state->bytes[state->bufferPos], size); | |
220 state->bufferPos += size; | |
221 } | |
222 return result; | |
223 } | |
224 | |
225 NSData *GPBCodedInputStreamReadRetainedBytes(GPBCodedInputStreamState *state) { | |
226 int32_t size = ReadRawVarint32(state); | |
227 if (size < 0) return nil; | |
228 CheckSize(state, size); | |
229 NSData *result = [[NSData alloc] initWithBytes:state->bytes + state->bufferPos | |
230 length:size]; | |
231 state->bufferPos += size; | |
232 return result; | |
233 } | |
234 | |
235 NSData *GPBCodedInputStreamReadRetainedBytesNoCopy( | |
236 GPBCodedInputStreamState *state) { | |
237 int32_t size = ReadRawVarint32(state); | |
238 if (size < 0) return nil; | |
239 CheckSize(state, size); | |
240 // Cast is safe because freeWhenDone is NO. | |
241 NSData *result = [[NSData alloc] | |
242 initWithBytesNoCopy:(void *)(state->bytes + state->bufferPos) | |
243 length:size | |
244 freeWhenDone:NO]; | |
245 state->bufferPos += size; | |
246 return result; | |
247 } | |
248 | |
249 size_t GPBCodedInputStreamPushLimit(GPBCodedInputStreamState *state, | |
250 size_t byteLimit) { | |
251 byteLimit += state->bufferPos; | |
252 size_t oldLimit = state->currentLimit; | |
253 if (byteLimit > oldLimit) { | |
254 [NSException raise:NSInvalidArgumentException | |
255 format:@"byteLimit > oldLimit: %tu > %tu", byteLimit, oldLimit]; | |
256 } | |
257 state->currentLimit = byteLimit; | |
258 return oldLimit; | |
259 } | |
260 | |
261 void GPBCodedInputStreamPopLimit(GPBCodedInputStreamState *state, | |
262 size_t oldLimit) { | |
263 state->currentLimit = oldLimit; | |
264 } | |
265 | |
266 size_t GPBCodedInputStreamBytesUntilLimit(GPBCodedInputStreamState *state) { | |
267 if (state->currentLimit == SIZE_T_MAX) { | |
268 return state->currentLimit; | |
269 } | |
270 | |
271 return state->currentLimit - state->bufferPos; | |
272 } | |
273 | |
274 BOOL GPBCodedInputStreamIsAtEnd(GPBCodedInputStreamState *state) { | |
275 return (state->bufferPos == state->bufferSize) || | |
276 (state->bufferPos == state->currentLimit); | |
277 } | |
278 | |
279 void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, | |
280 int32_t value) { | |
281 if (state->lastTag != value) { | |
282 [NSException raise:NSParseErrorException | |
283 format:@"Last tag: %d should be %d", state->lastTag, value]; | |
284 } | |
285 } | |
286 | |
287 @implementation GPBCodedInputStream | |
288 | |
289 + (instancetype)streamWithData:(NSData *)data { | |
290 return [[[self alloc] initWithData:data] autorelease]; | |
291 } | |
292 | |
293 - (instancetype)initWithData:(NSData *)data { | |
294 if ((self = [super init])) { | |
295 #ifdef DEBUG | |
296 NSCAssert([self class] == [GPBCodedInputStream class], | |
297 @"Subclassing of GPBCodedInputStream is not allowed."); | |
298 #endif | |
299 buffer_ = [data retain]; | |
300 state_.bytes = (const uint8_t *)[data bytes]; | |
301 state_.bufferSize = [data length]; | |
302 state_.currentLimit = NSUIntegerMax; | |
303 } | |
304 return self; | |
305 } | |
306 | |
307 - (void)dealloc { | |
308 [buffer_ release]; | |
309 [super dealloc]; | |
310 } | |
311 | |
312 - (int32_t)readTag { | |
313 return GPBCodedInputStreamReadTag(&state_); | |
314 } | |
315 | |
316 - (void)checkLastTagWas:(int32_t)value { | |
317 GPBCodedInputStreamCheckLastTagWas(&state_, value); | |
318 } | |
319 | |
320 - (BOOL)skipField:(int32_t)tag { | |
321 switch (GPBWireFormatGetTagWireType(tag)) { | |
322 case GPBWireFormatVarint: | |
323 GPBCodedInputStreamReadInt32(&state_); | |
324 return YES; | |
325 case GPBWireFormatFixed64: | |
326 SkipRawData(&state_, sizeof(int64_t)); | |
327 return YES; | |
328 case GPBWireFormatLengthDelimited: | |
329 SkipRawData(&state_, ReadRawVarint32(&state_)); | |
330 return YES; | |
331 case GPBWireFormatStartGroup: | |
332 [self skipMessage]; | |
333 GPBCodedInputStreamCheckLastTagWas( | |
334 &state_, GPBWireFormatMakeTag(GPBWireFormatGetTagFieldNumber(tag), | |
335 GPBWireFormatEndGroup)); | |
336 return YES; | |
337 case GPBWireFormatEndGroup: | |
338 return NO; | |
339 case GPBWireFormatFixed32: | |
340 SkipRawData(&state_, sizeof(int32_t)); | |
341 return YES; | |
342 } | |
343 [NSException raise:NSParseErrorException format:@"Invalid tag %d", tag]; | |
344 return NO; | |
345 } | |
346 | |
347 - (void)skipMessage { | |
348 while (YES) { | |
349 int32_t tag = GPBCodedInputStreamReadTag(&state_); | |
350 if (tag == 0 || ![self skipField:tag]) { | |
351 return; | |
352 } | |
353 } | |
354 } | |
355 | |
356 - (double)readDouble { | |
357 return GPBCodedInputStreamReadDouble(&state_); | |
358 } | |
359 | |
360 - (float)readFloat { | |
361 return GPBCodedInputStreamReadFloat(&state_); | |
362 } | |
363 | |
364 - (uint64_t)readUInt64 { | |
365 return GPBCodedInputStreamReadUInt64(&state_); | |
366 } | |
367 | |
368 - (int64_t)readInt64 { | |
369 return GPBCodedInputStreamReadInt64(&state_); | |
370 } | |
371 | |
372 - (int32_t)readInt32 { | |
373 return GPBCodedInputStreamReadInt32(&state_); | |
374 } | |
375 | |
376 - (uint64_t)readFixed64 { | |
377 return GPBCodedInputStreamReadFixed64(&state_); | |
378 } | |
379 | |
380 - (uint32_t)readFixed32 { | |
381 return GPBCodedInputStreamReadFixed32(&state_); | |
382 } | |
383 | |
384 - (BOOL)readBool { | |
385 return GPBCodedInputStreamReadBool(&state_); | |
386 } | |
387 | |
388 - (NSString *)readString { | |
389 return [GPBCodedInputStreamReadRetainedString(&state_) autorelease]; | |
390 } | |
391 | |
392 - (void)readGroup:(int32_t)fieldNumber | |
393 message:(GPBMessage *)message | |
394 extensionRegistry:(GPBExtensionRegistry *)extensionRegistry { | |
395 if (state_.recursionDepth >= kDefaultRecursionLimit) { | |
396 [NSException raise:NSParseErrorException | |
397 format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth, | |
398 kDefaultRecursionLimit]; | |
399 } | |
400 ++state_.recursionDepth; | |
401 [message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry]; | |
402 GPBCodedInputStreamCheckLastTagWas( | |
403 &state_, GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup)); | |
404 --state_.recursionDepth; | |
405 } | |
406 | |
407 - (void)readUnknownGroup:(int32_t)fieldNumber | |
408 message:(GPBUnknownFieldSet *)message { | |
409 if (state_.recursionDepth >= kDefaultRecursionLimit) { | |
410 [NSException raise:NSParseErrorException | |
411 format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth, | |
412 kDefaultRecursionLimit]; | |
413 } | |
414 ++state_.recursionDepth; | |
415 [message mergeFromCodedInputStream:self]; | |
416 GPBCodedInputStreamCheckLastTagWas( | |
417 &state_, GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup)); | |
418 --state_.recursionDepth; | |
419 } | |
420 | |
421 - (void)readMessage:(GPBMessage *)message | |
422 extensionRegistry:(GPBExtensionRegistry *)extensionRegistry { | |
423 int32_t length = ReadRawVarint32(&state_); | |
424 if (state_.recursionDepth >= kDefaultRecursionLimit) { | |
425 [NSException raise:NSParseErrorException | |
426 format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth, | |
427 kDefaultRecursionLimit]; | |
428 } | |
429 size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length); | |
430 ++state_.recursionDepth; | |
431 [message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry]; | |
432 GPBCodedInputStreamCheckLastTagWas(&state_, 0); | |
433 --state_.recursionDepth; | |
434 GPBCodedInputStreamPopLimit(&state_, oldLimit); | |
435 } | |
436 | |
437 - (void)readMapEntry:(id)mapDictionary | |
438 extensionRegistry:(GPBExtensionRegistry *)extensionRegistry | |
439 field:(GPBFieldDescriptor *)field | |
440 parentMessage:(GPBMessage *)parentMessage { | |
441 int32_t length = ReadRawVarint32(&state_); | |
442 if (state_.recursionDepth >= kDefaultRecursionLimit) { | |
443 [NSException raise:NSParseErrorException | |
444 format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth, | |
445 kDefaultRecursionLimit]; | |
446 } | |
447 size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length); | |
448 ++state_.recursionDepth; | |
449 GPBDictionaryReadEntry(mapDictionary, self, extensionRegistry, field, | |
450 parentMessage); | |
451 GPBCodedInputStreamCheckLastTagWas(&state_, 0); | |
452 --state_.recursionDepth; | |
453 GPBCodedInputStreamPopLimit(&state_, oldLimit); | |
454 } | |
455 | |
456 - (NSData *)readBytes { | |
457 return [GPBCodedInputStreamReadRetainedBytes(&state_) autorelease]; | |
458 } | |
459 | |
460 - (uint32_t)readUInt32 { | |
461 return GPBCodedInputStreamReadUInt32(&state_); | |
462 } | |
463 | |
464 - (int32_t)readEnum { | |
465 return GPBCodedInputStreamReadEnum(&state_); | |
466 } | |
467 | |
468 - (int32_t)readSFixed32 { | |
469 return GPBCodedInputStreamReadSFixed32(&state_); | |
470 } | |
471 | |
472 - (int64_t)readSFixed64 { | |
473 return GPBCodedInputStreamReadSFixed64(&state_); | |
474 } | |
475 | |
476 - (int32_t)readSInt32 { | |
477 return GPBCodedInputStreamReadSInt32(&state_); | |
478 } | |
479 | |
480 - (int64_t)readSInt64 { | |
481 return GPBCodedInputStreamReadSInt64(&state_); | |
482 } | |
483 | |
484 @end | |
485 | |
486 @implementation GPBString { | |
487 @package | |
488 CFStringRef string_; | |
489 unsigned char *utf8_; | |
490 NSUInteger utf8Len_; | |
491 | |
492 // This lock is used to gate access to utf8_. Once GPBStringInitStringValue() | |
493 // has been called, string_ will be filled in, and utf8_ will be NULL. | |
494 OSSpinLock lock_; | |
495 | |
496 BOOL hasBOM_; | |
497 BOOL is7BitAscii_; | |
498 } | |
499 | |
500 // Returns true if the passed in bytes are 7 bit ascii. | |
501 // This routine needs to be fast. | |
502 static bool AreBytesIn7BitASCII(const uint8_t *bytes, NSUInteger len) { | |
503 // In the loops below, it's more efficient to collect rather than do | |
504 // conditional at every step. | |
505 #if __LP64__ | |
506 // Align bytes. This is especially important in case of 3 byte BOM. | |
507 while (len > 0 && ((size_t)bytes & 0x07)) { | |
508 if (*bytes++ & 0x80) return false; | |
509 len--; | |
510 } | |
511 while (len >= 32) { | |
512 uint64_t val = *(const uint64_t *)bytes; | |
513 uint64_t hiBits = (val & 0x8080808080808080ULL); | |
514 bytes += 8; | |
515 val = *(const uint64_t *)bytes; | |
516 hiBits |= (val & 0x8080808080808080ULL); | |
517 bytes += 8; | |
518 val = *(const uint64_t *)bytes; | |
519 hiBits |= (val & 0x8080808080808080ULL); | |
520 bytes += 8; | |
521 val = *(const uint64_t *)bytes; | |
522 if (hiBits | (val & 0x8080808080808080ULL)) return false; | |
523 bytes += 8; | |
524 len -= 32; | |
525 } | |
526 | |
527 while (len >= 16) { | |
528 uint64_t val = *(const uint64_t *)bytes; | |
529 uint64_t hiBits = (val & 0x8080808080808080ULL); | |
530 bytes += 8; | |
531 val = *(const uint64_t *)bytes; | |
532 if (hiBits | (val & 0x8080808080808080ULL)) return false; | |
533 bytes += 8; | |
534 len -= 16; | |
535 } | |
536 | |
537 while (len >= 8) { | |
538 uint64_t val = *(const uint64_t *)bytes; | |
539 if (val & 0x8080808080808080ULL) return false; | |
540 bytes += 8; | |
541 len -= 8; | |
542 } | |
543 #else // __LP64__ | |
544 // Align bytes. This is especially important in case of 3 byte BOM. | |
545 while (len > 0 && ((size_t)bytes & 0x03)) { | |
546 if (*bytes++ & 0x80) return false; | |
547 len--; | |
548 } | |
549 while (len >= 16) { | |
550 uint32_t val = *(const uint32_t *)bytes; | |
551 uint32_t hiBits = (val & 0x80808080U); | |
552 bytes += 4; | |
553 val = *(const uint32_t *)bytes; | |
554 hiBits |= (val & 0x80808080U); | |
555 bytes += 4; | |
556 val = *(const uint32_t *)bytes; | |
557 hiBits |= (val & 0x80808080U); | |
558 bytes += 4; | |
559 val = *(const uint32_t *)bytes; | |
560 if (hiBits | (val & 0x80808080U)) return false; | |
561 bytes += 4; | |
562 len -= 16; | |
563 } | |
564 | |
565 while (len >= 8) { | |
566 uint32_t val = *(const uint32_t *)bytes; | |
567 uint32_t hiBits = (val & 0x80808080U); | |
568 bytes += 4; | |
569 val = *(const uint32_t *)bytes; | |
570 if (hiBits | (val & 0x80808080U)) return false; | |
571 bytes += 4; | |
572 len -= 8; | |
573 } | |
574 #endif // __LP64__ | |
575 | |
576 while (len >= 4) { | |
577 uint32_t val = *(const uint32_t *)bytes; | |
578 if (val & 0x80808080U) return false; | |
579 bytes += 4; | |
580 len -= 4; | |
581 } | |
582 | |
583 while (len--) { | |
584 if (*bytes++ & 0x80) return false; | |
585 } | |
586 | |
587 return true; | |
588 } | |
589 | |
590 static void GPBStringInitStringValue(GPBString *string) { | |
591 OSSpinLockLock(&string->lock_); | |
592 GPBStringInitStringValueAlreadyLocked(string); | |
593 OSSpinLockUnlock(&string->lock_); | |
594 } | |
595 | |
596 static void GPBStringInitStringValueAlreadyLocked(GPBString *string) { | |
597 if (string->string_ == NULL && string->utf8_ != NULL) { | |
598 // Using kCFAllocatorMalloc for contentsDeallocator, as buffer in | |
599 // string->utf8_ is being handed off. | |
600 string->string_ = CFStringCreateWithBytesNoCopy( | |
601 NULL, string->utf8_, string->utf8Len_, kCFStringEncodingUTF8, false, | |
602 kCFAllocatorMalloc); | |
603 if (!string->string_) { | |
604 #ifdef DEBUG | |
605 // https://developers.google.com/protocol-buffers/docs/proto#scalar | |
606 NSLog(@"UTF8 failure, is some field type 'string' when it should be " | |
607 @"'bytes'?"); | |
608 #endif | |
609 string->string_ = CFSTR(""); | |
610 string->utf8Len_ = 0; | |
611 // On failure, we have to clean up the buffer. | |
612 free(string->utf8_); | |
613 } | |
614 string->utf8_ = NULL; | |
615 } | |
616 } | |
617 | |
618 GPBString *GPBCreateGPBStringWithUTF8(const void *bytes, NSUInteger length) { | |
619 GPBString *result = [[GPBString alloc] initWithBytes:bytes length:length]; | |
620 return result; | |
621 } | |
622 | |
623 - (instancetype)initWithBytes:(const void *)bytes length:(NSUInteger)length { | |
624 self = [super init]; | |
625 if (self) { | |
626 utf8_ = malloc(length); | |
627 memcpy(utf8_, bytes, length); | |
628 utf8Len_ = length; | |
629 lock_ = OS_SPINLOCK_INIT; | |
630 is7BitAscii_ = AreBytesIn7BitASCII(bytes, length); | |
631 if (length >= 3 && memcmp(utf8_, "\xef\xbb\xbf", 3) == 0) { | |
632 // We can't just remove the BOM from the string here, because in the case | |
633 // where we have > 1 BOM at the beginning of the string, we will remove on
e, | |
634 // and the internal NSString we create will remove the next one, and we wi
ll | |
635 // end up with a GPBString != NSString issue. | |
636 // We also just can't remove all the BOMs because then we would end up wit
h | |
637 // potential cases where a GPBString and an NSString made with the same | |
638 // UTF8 buffer would in fact be different. | |
639 // We record the fact we have a BOM, and use it as necessary to simulate | |
640 // what NSString would return for various calls. | |
641 hasBOM_ = YES; | |
642 #if DEBUG | |
643 // Sending BOMs across the line is just wasting bits. | |
644 NSLog(@"Bad data? String should not have BOM!"); | |
645 #endif // DEBUG | |
646 } | |
647 } | |
648 return self; | |
649 } | |
650 | |
651 - (void)dealloc { | |
652 if (string_ != NULL) { | |
653 CFRelease(string_); | |
654 } | |
655 if (utf8_ != NULL) { | |
656 free(utf8_); | |
657 } | |
658 [super dealloc]; | |
659 } | |
660 | |
661 // Required NSString overrides. | |
662 - (NSUInteger)length { | |
663 if (is7BitAscii_) { | |
664 return utf8Len_; | |
665 } else { | |
666 GPBStringInitStringValue(self); | |
667 return CFStringGetLength(string_); | |
668 } | |
669 } | |
670 | |
671 - (unichar)characterAtIndex:(NSUInteger)anIndex { | |
672 OSSpinLockLock(&lock_); | |
673 if (is7BitAscii_ && utf8_) { | |
674 unichar result = utf8_[anIndex]; | |
675 OSSpinLockUnlock(&lock_); | |
676 return result; | |
677 } else { | |
678 GPBStringInitStringValueAlreadyLocked(self); | |
679 OSSpinLockUnlock(&lock_); | |
680 return CFStringGetCharacterAtIndex(string_, anIndex); | |
681 } | |
682 } | |
683 | |
684 // Override a couple of methods that typically want high performance. | |
685 | |
686 - (id)copyWithZone:(NSZone *)zone { | |
687 GPBStringInitStringValue(self); | |
688 return [(NSString *)string_ copyWithZone:zone]; | |
689 } | |
690 | |
691 - (id)mutableCopyWithZone:(NSZone *)zone { | |
692 GPBStringInitStringValue(self); | |
693 return [(NSString *)string_ mutableCopyWithZone:zone]; | |
694 } | |
695 | |
696 - (NSUInteger)hash { | |
697 // Must convert to string here to make sure that the hash is always | |
698 // consistent no matter what state the GPBString is in. | |
699 GPBStringInitStringValue(self); | |
700 return CFHash(string_); | |
701 } | |
702 | |
703 - (BOOL)isEqual:(id)object { | |
704 if (self == object) { | |
705 return YES; | |
706 } | |
707 if ([object isKindOfClass:[NSString class]]) { | |
708 GPBStringInitStringValue(self); | |
709 return CFStringCompare(string_, (CFStringRef)object, 0) == | |
710 kCFCompareEqualTo; | |
711 } | |
712 return NO; | |
713 } | |
714 | |
715 - (void)getCharacters:(unichar *)buffer range:(NSRange)aRange { | |
716 OSSpinLockLock(&lock_); | |
717 if (is7BitAscii_ && utf8_) { | |
718 unsigned char *bytes = &(utf8_[aRange.location]); | |
719 for (NSUInteger i = 0; i < aRange.length; ++i) { | |
720 buffer[i] = bytes[i]; | |
721 } | |
722 OSSpinLockUnlock(&lock_); | |
723 } else { | |
724 GPBStringInitStringValueAlreadyLocked(self); | |
725 OSSpinLockUnlock(&lock_); | |
726 CFStringGetCharacters(string_, CFRangeMake(aRange.location, aRange.length), | |
727 buffer); | |
728 } | |
729 } | |
730 | |
731 - (NSUInteger)lengthOfBytesUsingEncoding:(NSStringEncoding)encoding { | |
732 if ((encoding == NSUTF8StringEncoding) || | |
733 (encoding == NSASCIIStringEncoding && is7BitAscii_)) { | |
734 return utf8Len_ - (hasBOM_ ? 3 : 0); | |
735 } else { | |
736 GPBStringInitStringValue(self); | |
737 return [(NSString *)string_ lengthOfBytesUsingEncoding:encoding]; | |
738 } | |
739 } | |
740 | |
741 - (BOOL)getBytes:(void *)buffer | |
742 maxLength:(NSUInteger)maxLength | |
743 usedLength:(NSUInteger *)usedLength | |
744 encoding:(NSStringEncoding)encoding | |
745 options:(NSStringEncodingConversionOptions)options | |
746 range:(NSRange)range | |
747 remainingRange:(NSRangePointer)remainingRange { | |
748 // [NSString getBytes:maxLength:usedLength:encoding:options:range:remainingRan
ge] | |
749 // does not return reliable results if the maxLength argument is 0 | |
750 // (Radar 16385183). Therefore we have special cased it as a slow case so | |
751 // that it behaves however Apple intends it to behave. It should be a rare | |
752 // case. | |
753 // | |
754 // [NSString getBytes:maxLength:usedLength:encoding:options:range:remainingRan
ge] | |
755 // does not return reliable results if the range is outside of the strings | |
756 // length (Radar 16396177). Therefore we have special cased it as a slow | |
757 // case so that it behaves however Apple intends it to behave. It should | |
758 // be a rare case. | |
759 // | |
760 // We can optimize the UTF8StringEncoding and NSASCIIStringEncoding with no | |
761 // options cases. | |
762 if ((options == 0) && | |
763 (encoding == NSUTF8StringEncoding || encoding == NSASCIIStringEncoding) && | |
764 (maxLength != 0) && | |
765 (NSMaxRange(range) <= utf8Len_)) { | |
766 // Might be able to optimize it. | |
767 OSSpinLockLock(&lock_); | |
768 if (is7BitAscii_ && utf8_) { | |
769 NSUInteger length = range.length; | |
770 length = (length < maxLength) ? length : maxLength; | |
771 memcpy(buffer, utf8_ + range.location, length); | |
772 if (usedLength) { | |
773 *usedLength = length; | |
774 } | |
775 if (remainingRange) { | |
776 remainingRange->location = range.location + length; | |
777 remainingRange->length = range.length - length; | |
778 } | |
779 OSSpinLockUnlock(&lock_); | |
780 if (length > 0) { | |
781 return YES; | |
782 } else { | |
783 return NO; | |
784 } | |
785 } else { | |
786 GPBStringInitStringValueAlreadyLocked(self); | |
787 OSSpinLockUnlock(&lock_); | |
788 } | |
789 } else { | |
790 GPBStringInitStringValue(self); | |
791 } | |
792 return [(NSString *)string_ getBytes:buffer | |
793 maxLength:maxLength | |
794 usedLength:usedLength | |
795 encoding:encoding | |
796 options:options | |
797 range:range | |
798 remainingRange:remainingRange]; | |
799 } | |
800 | |
801 @end | |
OLD | NEW |