diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCBase85.h b/internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCBase85.h new file mode 100644 index 00000000..eccb7540 --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCBase85.h @@ -0,0 +1,35 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSData (GNCBase85) + +/** Creates a Base85 encoded string from the data using websafe characters and no padding. */ +- (NSString *)base85EncodedString; + +/** + * Initializes a data object with the given Base85 encoded string. + * + * @param base85String A Base85 encoded string. + * @return A data object built by Base85 decoding the provided string. Returns @c nil if the data + * object could not be decoded. + */ +- (nullable instancetype)initWithBase85EncodedString:(NSString *)base85String; + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCBase85.mm b/internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCBase85.mm new file mode 100644 index 00000000..70e5366f --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCBase85.mm @@ -0,0 +1,49 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCBase85.h" + +#import + +#include +#include + +#include "internal/encoding/base85.h" + +#import "GoogleToolboxForMac/GTMLogger.h" + +@implementation NSData (GNCBase85) + +- (NSString *)base85EncodedString { + const char *data = static_cast(self.bytes); + NSUInteger length = self.length; + std::optional base85String = + nearby::encoding::Base85Encode(std::string(data, length)); + if (!base85String.has_value()) { + return @""; + } + + return @(base85String->c_str()); +} + +- (nullable instancetype)initWithBase85EncodedString:(NSString *)base85String { + std::optional decodedData = + nearby::encoding::Base85Decode(std::string(base85String.UTF8String)); + if (!decodedData.has_value()) { + return nil; + } + return [NSData dataWithBytes:decodedData->data() length:decodedData->size()]; +} + +@end diff --git a/internal/platform/implementation/apple/Mediums/BUILD b/internal/platform/implementation/apple/Mediums/BUILD index 705c492d..204f2351 100644 --- a/internal/platform/implementation/apple/Mediums/BUILD +++ b/internal/platform/implementation/apple/Mediums/BUILD @@ -29,6 +29,7 @@ objc_library( "BLEv2/GNCCentralManager.m", "BLEv2/GNCPeripheral.m", "BLEv2/GNCPeripheralManager.m", + "BLEv2/NSData+GNCBase85.mm", "BLEv2/NSData+GNCWebSafeBase64.m", "Ble/GNCMBleConnection.m", "Ble/GNCMBleUtils.mm", @@ -51,6 +52,7 @@ objc_library( "BLEv2/GNCCentralManager.h", "BLEv2/GNCPeripheral.h", "BLEv2/GNCPeripheralManager.h", + "BLEv2/NSData+GNCBase85.h", "BLEv2/NSData+GNCWebSafeBase64.h", "Ble/GNCMBleConnection.h", "Ble/GNCMBleUtils.h", @@ -62,6 +64,7 @@ objc_library( "WiFiCommon/GNCNWFrameworkSocket.h", ], deps = [ + "//internal/encoding:base85", "//internal/platform/implementation/apple:Shared", "//internal/platform/implementation/apple/Mediums/Ble/Sockets:Shared", "//proto/mediums:ble_frames_cc_proto", diff --git a/internal/platform/implementation/apple/Tests/BUILD b/internal/platform/implementation/apple/Tests/BUILD index c11daf73..216635bc 100644 --- a/internal/platform/implementation/apple/Tests/BUILD +++ b/internal/platform/implementation/apple/Tests/BUILD @@ -44,6 +44,7 @@ objc_library( "GNCScheduledExecutorTest.mm", "GNCSingleThreadExecutorTest.mm", "GNCUtilsTest.mm", + "NSData+GNCBase85Test.m", "NSData+GNCWebSafeBase64Test.m", ], deps = [ diff --git a/internal/platform/implementation/apple/Tests/NSData+GNCBase85Test.m b/internal/platform/implementation/apple/Tests/NSData+GNCBase85Test.m new file mode 100644 index 00000000..dd4483c7 --- /dev/null +++ b/internal/platform/implementation/apple/Tests/NSData+GNCBase85Test.m @@ -0,0 +1,60 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCBase85.h" + +#import +#import + +@interface NSData_GNCBase85Test : XCTestCase +@end + +@implementation NSData_GNCBase85Test + +- (void)testEncoding { + NSString *expected = @"@:E^"; + NSData *data = [@"abc" dataUsingEncoding:NSUTF8StringEncoding]; + NSString *actual = [data base85EncodedString]; + XCTAssertEqualObjects(expected, actual); +} + +- (void)testEncodingDataWithZero { + NSString *expected = @"z"; + NSData *data = [@"\0\0" dataUsingEncoding:NSUTF8StringEncoding]; + NSString *actual = [data base85EncodedString]; + XCTAssertEqualObjects(expected, actual); +} + +- (void)testDecoding { + NSData *data = [@"abc" dataUsingEncoding:NSUTF8StringEncoding]; + NSString *actual = [data base85EncodedString]; + NSData *decodedData = [[NSData alloc] initWithBase85EncodedString:actual]; + XCTAssertEqualObjects(decodedData, data); +} + +- (void)testEncodingEmptyData { + XCTAssertEqualObjects([[@"" dataUsingEncoding:NSUTF8StringEncoding] base85EncodedString], @""); +} + +- (void)testDecodingEmptyData { + XCTAssertEqualObjects([[NSData alloc] initWithBase85EncodedString:@""], + [@"" dataUsingEncoding:NSUTF8StringEncoding]); +} + +- (void)testDecodingInvalidString { + NSString *invalidString = @"invalid"; + XCTAssertNil([[NSData alloc] initWithBase85EncodedString:invalidString]); +} + +@end