• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 *
3 * Copyright 2015 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19#import "NSData+GRPC.h"
20
21#include <grpc/byte_buffer.h>
22#include <grpc/byte_buffer_reader.h>
23#include <string.h>
24
25// TODO(jcanizales): Move these two incantations to the C library.
26
27static void MallocAndCopyByteBufferToCharArray(grpc_byte_buffer *buffer, size_t *length,
28                                               char **array) {
29  grpc_byte_buffer_reader reader;
30  if (!grpc_byte_buffer_reader_init(&reader, buffer)) {
31    // grpc_byte_buffer_reader_init can fail if the data sent by the server
32    // could not be decompressed for any reason. This is an issue with the data
33    // coming from the server and thus we want the RPC to fail with error code
34    // INTERNAL.
35    *array = NULL;
36    *length = 0;
37    return;
38  }
39  // The slice contains uncompressed data even if compressed data was received
40  // because the reader takes care of automatically decompressing it
41  grpc_slice slice = grpc_byte_buffer_reader_readall(&reader);
42  size_t uncompressed_length = GRPC_SLICE_LENGTH(slice);
43  char *result = malloc(uncompressed_length);
44  if (result) {
45    memcpy(result, GRPC_SLICE_START_PTR(slice), uncompressed_length);
46  }
47  grpc_slice_unref(slice);
48  *array = result;
49  *length = uncompressed_length;
50
51  grpc_byte_buffer_reader_destroy(&reader);
52}
53
54static grpc_byte_buffer *CopyCharArrayToNewByteBuffer(const char *array, size_t length) {
55  grpc_slice slice = grpc_slice_from_copied_buffer(array, length);
56  grpc_byte_buffer *buffer = grpc_raw_byte_buffer_create(&slice, 1);
57  grpc_slice_unref(slice);
58  return buffer;
59}
60
61@implementation NSData (GRPC)
62+ (instancetype)grpc_dataWithByteBuffer:(grpc_byte_buffer *)buffer {
63  if (buffer == NULL) {
64    return nil;
65  }
66  char *array;
67  size_t length;
68  MallocAndCopyByteBufferToCharArray(buffer, &length, &array);
69  if (!array) {
70    // TODO(jcanizales): grpc_byte_buffer is reference-counted, so we can
71    // prevent this memory problem by implementing a subclass of NSData
72    // that wraps the grpc_byte_buffer. Then enumerateByteRangesUsingBlock:
73    // can be implemented using a grpc_byte_buffer_reader.
74    return nil;
75  }
76  // Not depending upon size assumption of NSUInteger
77  NSUInteger length_max = MIN(length, UINT_MAX);
78  return [self dataWithBytesNoCopy:array length:length_max freeWhenDone:YES];
79}
80
81- (grpc_byte_buffer *)grpc_byteBuffer {
82  // Some implementations of NSData, as well as grpc_byte_buffer, support O(1)
83  // appending of byte arrays by not using internally a single contiguous memory
84  // block for representation.
85  // The following implementation is thus not optimal, sometimes requiring two
86  // copies (one by self.bytes and another by grpc_slice_from_copied_buffer).
87  // If it turns out to be an issue, we can use enumerateByteRangesUsingblock:
88  // to create an array of grpc_slice objects to pass to
89  // grpc_raw_byte_buffer_create.
90  // That would make it do exactly one copy, always.
91  return CopyCharArrayToNewByteBuffer((const char *)self.bytes, (size_t)self.length);
92}
93@end
94