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