Flutter Engine
FlutterCodecs.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h"
6 
7 #include <cstring>
8 
9 @implementation FlutterBinaryCodec
10 + (instancetype)sharedInstance {
11  static id _sharedInstance = nil;
12  if (!_sharedInstance) {
13  _sharedInstance = [FlutterBinaryCodec new];
14  }
15  return _sharedInstance;
16 }
17 
18 - (NSData*)encode:(id)message {
19  NSAssert(!message || [message isKindOfClass:[NSData class]], @"");
20  return message;
21 }
22 
23 - (NSData*)decode:(NSData*)message {
24  return message;
25 }
26 @end
27 
28 @implementation FlutterStringCodec
29 + (instancetype)sharedInstance {
30  static id _sharedInstance = nil;
31  if (!_sharedInstance) {
32  _sharedInstance = [FlutterStringCodec new];
33  }
34  return _sharedInstance;
35 }
36 
37 - (NSData*)encode:(id)message {
38  if (message == nil)
39  return nil;
40  NSAssert([message isKindOfClass:[NSString class]], @"");
41  NSString* stringMessage = message;
42  const char* utf8 = stringMessage.UTF8String;
43  return [NSData dataWithBytes:utf8 length:strlen(utf8)];
44 }
45 
46 - (NSString*)decode:(NSData*)message {
47  if (message == nil)
48  return nil;
49  return [[[NSString alloc] initWithData:message encoding:NSUTF8StringEncoding] autorelease];
50 }
51 @end
52 
53 @implementation FlutterJSONMessageCodec
54 + (instancetype)sharedInstance {
55  static id _sharedInstance = nil;
56  if (!_sharedInstance) {
57  _sharedInstance = [FlutterJSONMessageCodec new];
58  }
59  return _sharedInstance;
60 }
61 
62 - (NSData*)encode:(id)message {
63  if (message == nil)
64  return nil;
65  NSData* encoding;
66  if ([message isKindOfClass:[NSArray class]] || [message isKindOfClass:[NSDictionary class]]) {
67  encoding = [NSJSONSerialization dataWithJSONObject:message options:0 error:nil];
68  } else {
69  // NSJSONSerialization does not support top-level simple values.
70  // We encode as singleton array, then extract the relevant bytes.
71  encoding = [NSJSONSerialization dataWithJSONObject:@[ message ] options:0 error:nil];
72  if (encoding) {
73  encoding = [encoding subdataWithRange:NSMakeRange(1, encoding.length - 2)];
74  }
75  }
76  NSAssert(encoding, @"Invalid JSON message, encoding failed");
77  return encoding;
78 }
79 
80 - (id)decode:(NSData*)message {
81  if (message == nil)
82  return nil;
83  BOOL isSimpleValue = NO;
84  id decoded = nil;
85  if (0 < message.length) {
86  UInt8 first;
87  [message getBytes:&first length:1];
88  isSimpleValue = first != '{' && first != '[';
89  if (isSimpleValue) {
90  // NSJSONSerialization does not support top-level simple values.
91  // We expand encoding to singleton array, then decode that and extract
92  // the single entry.
93  UInt8 begin = '[';
94  UInt8 end = ']';
95  NSMutableData* expandedMessage = [NSMutableData dataWithLength:message.length + 2];
96  [expandedMessage replaceBytesInRange:NSMakeRange(0, 1) withBytes:&begin];
97  [expandedMessage replaceBytesInRange:NSMakeRange(1, message.length) withBytes:message.bytes];
98  [expandedMessage replaceBytesInRange:NSMakeRange(message.length + 1, 1) withBytes:&end];
99  message = expandedMessage;
100  }
101  decoded = [NSJSONSerialization JSONObjectWithData:message options:0 error:nil];
102  }
103  NSAssert(decoded, @"Invalid JSON message, decoding failed");
104  return isSimpleValue ? ((NSArray*)decoded)[0] : decoded;
105 }
106 @end
107 
108 @implementation FlutterJSONMethodCodec
109 + (instancetype)sharedInstance {
110  static id _sharedInstance = nil;
111  if (!_sharedInstance) {
112  _sharedInstance = [FlutterJSONMethodCodec new];
113  }
114  return _sharedInstance;
115 }
116 
117 - (NSData*)encodeMethodCall:(FlutterMethodCall*)call {
118  return [[FlutterJSONMessageCodec sharedInstance] encode:@{
119  @"method" : call.method,
120  @"args" : [self wrapNil:call.arguments],
121  }];
122 }
123 
124 - (NSData*)encodeSuccessEnvelope:(id)result {
125  return [[FlutterJSONMessageCodec sharedInstance] encode:@[ [self wrapNil:result] ]];
126 }
127 
128 - (NSData*)encodeErrorEnvelope:(FlutterError*)error {
129  return [[FlutterJSONMessageCodec sharedInstance] encode:@[
130  error.code,
131  [self wrapNil:error.message],
132  [self wrapNil:error.details],
133  ]];
134 }
135 
136 - (FlutterMethodCall*)decodeMethodCall:(NSData*)message {
137  NSDictionary* dictionary = [[FlutterJSONMessageCodec sharedInstance] decode:message];
138  id method = dictionary[@"method"];
139  id arguments = [self unwrapNil:dictionary[@"args"]];
140  NSAssert([method isKindOfClass:[NSString class]], @"Invalid JSON method call");
141  return [FlutterMethodCall methodCallWithMethodName:method arguments:arguments];
142 }
143 
144 - (id)decodeEnvelope:(NSData*)envelope {
145  NSArray* array = [[FlutterJSONMessageCodec sharedInstance] decode:envelope];
146  if (array.count == 1)
147  return [self unwrapNil:array[0]];
148  NSAssert(array.count == 3, @"Invalid JSON envelope");
149  id code = array[0];
150  id message = [self unwrapNil:array[1]];
151  id details = [self unwrapNil:array[2]];
152  NSAssert([code isKindOfClass:[NSString class]], @"Invalid JSON envelope");
153  NSAssert(message == nil || [message isKindOfClass:[NSString class]], @"Invalid JSON envelope");
154  return [FlutterError errorWithCode:code message:message details:details];
155 }
156 
157 - (id)wrapNil:(id)value {
158  return value == nil ? [NSNull null] : value;
159 }
160 - (id)unwrapNil:(id)value {
161  return value == [NSNull null] ? nil : value;
162 }
163 @end
instancetype errorWithCode:message:details:(NSString *code, [message] NSString *_Nullable message, [details] id _Nullable details)
instancetype sharedInstance()
uint8_t value
instancetype methodCallWithMethodName:arguments:(NSString *method, [arguments] id _Nullable arguments)
int32_t id