diff --git a/src/objective-c/GRPCClient/private/NSData+GRPC.m b/src/objective-c/GRPCClient/private/NSData+GRPC.m index e6a6c3c605a7e5e018c1482c2140c07544c02c73..1238374af3d1e2dfc9d7f62fc9aee1637505ff9d 100644 --- a/src/objective-c/GRPCClient/private/NSData+GRPC.m +++ b/src/objective-c/GRPCClient/private/NSData+GRPC.m @@ -39,17 +39,21 @@ // TODO(jcanizales): Move these two incantations to the C library. -static void CopyByteBufferToCharArray(grpc_byte_buffer *buffer, char *array) { - size_t offset = 0; +static void MallocAndCopyByteBufferToCharArray(grpc_byte_buffer *buffer, + size_t *length, char **array) { grpc_byte_buffer_reader reader; grpc_byte_buffer_reader_init(&reader, buffer); - gpr_slice next; - while (grpc_byte_buffer_reader_next(&reader, &next) != 0){ - memcpy(array + offset, GPR_SLICE_START_PTR(next), - (size_t)GPR_SLICE_LENGTH(next)); - offset += GPR_SLICE_LENGTH(next); - gpr_slice_unref(next); + // The slice contains uncompressed data even if compressed data was received + // because the reader takes care of automatically decompressing it + gpr_slice slice = grpc_byte_buffer_reader_readall(&reader); + size_t uncompressed_length = GPR_SLICE_LENGTH(slice); + char *result = malloc(uncompressed_length); + if (result) { + memcpy(result, GPR_SLICE_START_PTR(slice), uncompressed_length); } + gpr_slice_unref(slice); + *array = result; + *length = uncompressed_length; } static grpc_byte_buffer *CopyCharArrayToNewByteBuffer(const char *array, @@ -65,8 +69,9 @@ static grpc_byte_buffer *CopyCharArrayToNewByteBuffer(const char *array, if (buffer == NULL) { return nil; } - NSUInteger length = grpc_byte_buffer_length(buffer); - char *array = malloc(length * sizeof(*array)); + char *array; + size_t length; + MallocAndCopyByteBufferToCharArray(buffer, &length, &array); if (!array) { // TODO(jcanizales): grpc_byte_buffer is reference-counted, so we can // prevent this memory problem by implementing a subclass of NSData @@ -74,8 +79,9 @@ static grpc_byte_buffer *CopyCharArrayToNewByteBuffer(const char *array, // can be implemented using a grpc_byte_buffer_reader. return nil; } - CopyByteBufferToCharArray(buffer, array); - return [self dataWithBytesNoCopy:array length:length freeWhenDone:YES]; + // Not depending upon size assumption of NSUInteger + NSUInteger length_max = MIN(length, UINT_MAX); + return [self dataWithBytesNoCopy:array length:length_max freeWhenDone:YES]; } - (grpc_byte_buffer *)grpc_byteBuffer { @@ -85,8 +91,10 @@ static grpc_byte_buffer *CopyCharArrayToNewByteBuffer(const char *array, // The following implementation is thus not optimal, sometimes requiring two // copies (one by self.bytes and another by gpr_slice_from_copied_buffer). // If it turns out to be an issue, we can use enumerateByteRangesUsingblock: - // to create an array of gpr_slice objects to pass to grpc_raw_byte_buffer_create. + // to create an array of gpr_slice objects to pass to + // grpc_raw_byte_buffer_create. // That would make it do exactly one copy, always. - return CopyCharArrayToNewByteBuffer((const char *)self.bytes, (size_t)self.length); + return CopyCharArrayToNewByteBuffer((const char *)self.bytes, + (size_t)self.length); } @end