FIRSESNanoPBHelpers.m 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. //
  2. // Copyright 2022 Google LLC
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. #import <Foundation/Foundation.h>
  16. #import <GoogleUtilities/GULNetworkInfo.h>
  17. #import "FirebaseSessions/SourcesObjC/NanoPB/FIRSESNanoPBHelpers.h"
  18. #import "FirebaseSessions/SourcesObjC/Protogen/nanopb/sessions.nanopb.h"
  19. #import <nanopb/pb.h>
  20. #import <nanopb/pb_decode.h>
  21. #import <nanopb/pb_encode.h>
  22. #import <sys/sysctl.h>
  23. NS_ASSUME_NONNULL_BEGIN
  24. void nanopb_free(void *_Nullable ptr) {
  25. pb_free(ptr);
  26. }
  27. NSError *FIRSESMakeEncodeError(NSString *description) {
  28. return [NSError errorWithDomain:@"FIRSESEncodeError"
  29. code:-1
  30. userInfo:@{@"NSLocalizedDescriptionKey" : description}];
  31. }
  32. NSString *FIRSESPBGetError(pb_istream_t istream) {
  33. return [NSString stringWithCString:PB_GET_ERROR(&istream) encoding:NSASCIIStringEncoding];
  34. }
  35. // It seems impossible to specify the nullability of the `fields` parameter below,
  36. // yet the compiler complains that it's missing a nullability specifier. Google
  37. // yields no results at this time.
  38. //
  39. // Note 4/17/2023: The warning seems to be spurious (pb_field_t is a non-pointer
  40. // type) and is not present on Xcode 14+. This pragma can be removed after the
  41. // minimum supported Xcode version is above 14.
  42. #pragma clang diagnostic push
  43. #pragma clang diagnostic ignored "-Wnullability-completeness"
  44. NSData *_Nullable FIRSESEncodeProto(const pb_field_t fields[],
  45. const void *_Nonnull proto,
  46. NSError **error) {
  47. pb_ostream_t sizestream = PB_OSTREAM_SIZING;
  48. // Encode 1 time to determine the size.
  49. if (!pb_encode(&sizestream, fields, proto)) {
  50. NSString *errorString = [NSString
  51. stringWithFormat:@"Error in nanopb encoding to get size: %s", PB_GET_ERROR(&sizestream)];
  52. if (error != NULL) {
  53. *error = FIRSESMakeEncodeError(errorString);
  54. }
  55. return nil;
  56. }
  57. // Encode a 2nd time to actually get the bytes from it.
  58. size_t bufferSize = sizestream.bytes_written;
  59. CFMutableDataRef dataRef = CFDataCreateMutable(CFAllocatorGetDefault(), bufferSize);
  60. CFDataSetLength(dataRef, bufferSize);
  61. pb_ostream_t ostream = pb_ostream_from_buffer((void *)CFDataGetBytePtr(dataRef), bufferSize);
  62. if (!pb_encode(&ostream, fields, proto)) {
  63. NSString *errorString =
  64. [NSString stringWithFormat:@"Error in nanopb encoding: %s", PB_GET_ERROR(&sizestream)];
  65. if (error != NULL) {
  66. *error = FIRSESMakeEncodeError(errorString);
  67. }
  68. CFBridgingRelease(dataRef);
  69. return nil;
  70. }
  71. return CFBridgingRelease(dataRef);
  72. }
  73. #pragma clang diagnostic pop
  74. /** Mallocs a pb_bytes_array and copies the given NSData bytes into the bytes array.
  75. * @note Memory needs to be free manually, through pb_free or pb_release.
  76. * @param data The data to copy into the new bytes array.
  77. */
  78. pb_bytes_array_t *_Nullable FIRSESEncodeData(NSData *_Nullable data) {
  79. pb_bytes_array_t *pbBytes = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data.length));
  80. if (pbBytes == NULL) {
  81. return NULL;
  82. }
  83. [data getBytes:pbBytes->bytes length:data.length];
  84. pbBytes->size = (pb_size_t)data.length;
  85. return pbBytes;
  86. }
  87. /** Mallocs a pb_bytes_array and copies the given NSString's bytes into the bytes array.
  88. * @note Memory needs to be freed manually, through pb_free or pb_release.
  89. * @param string The string to encode as pb_bytes.
  90. */
  91. pb_bytes_array_t *_Nullable FIRSESEncodeString(NSString *_Nullable string) {
  92. if ([string isMemberOfClass:[NSNull class]]) {
  93. string = nil;
  94. }
  95. NSString *stringToEncode = string ? string : @"";
  96. NSData *stringBytes = [stringToEncode dataUsingEncoding:NSUTF8StringEncoding];
  97. return FIRSESEncodeData(stringBytes);
  98. }
  99. NSData *FIRSESDecodeData(pb_bytes_array_t *pbData) {
  100. NSData *data = [NSData dataWithBytes:&(pbData->bytes) length:pbData->size];
  101. return data;
  102. }
  103. NSString *FIRSESDecodeString(pb_bytes_array_t *pbData) {
  104. if (pbData->size == 0) {
  105. return @"";
  106. }
  107. NSData *data = FIRSESDecodeData(pbData);
  108. // There was a bug where length 32 strings were sometimes null after encoding
  109. // and decoding. We found that this was due to the null terminator sometimes not
  110. // being included in the decoded code. Using stringWithCString assumes the string
  111. // is null terminated, so we switched to initWithBytes because it takes a length.
  112. return [[NSString alloc] initWithBytes:data.bytes
  113. length:data.length
  114. encoding:NSUTF8StringEncoding];
  115. }
  116. BOOL FIRSESIsPBArrayEqual(pb_bytes_array_t *_Nullable array, pb_bytes_array_t *_Nullable expected) {
  117. // Treat the empty string as the same as a missing field
  118. if (array == nil) {
  119. return expected->size == 0;
  120. }
  121. if (array->size != expected->size) {
  122. return false;
  123. }
  124. for (int i = 0; i < array->size; i++) {
  125. if (expected->bytes[i] != array->bytes[i]) {
  126. return false;
  127. }
  128. }
  129. return true;
  130. }
  131. BOOL FIRSESIsPBStringEqual(pb_bytes_array_t *_Nullable pbString, NSString *_Nullable str) {
  132. pb_bytes_array_t *expected = FIRSESEncodeString(str);
  133. return FIRSESIsPBArrayEqual(pbString, expected);
  134. }
  135. BOOL FIRSESIsPBDataEqual(pb_bytes_array_t *_Nullable pbArray, NSData *_Nullable data) {
  136. pb_bytes_array_t *expected = FIRSESEncodeData(data);
  137. BOOL equal = FIRSESIsPBArrayEqual(pbArray, expected);
  138. free(expected);
  139. return equal;
  140. }
  141. pb_size_t FIRSESGetAppleApplicationInfoTag(void) {
  142. return firebase_appquality_sessions_ApplicationInfo_apple_app_info_tag;
  143. }
  144. /// Copied from a private method in GULAppEnvironmentUtil.
  145. NSString *_Nullable FIRSESGetSysctlEntry(const char *sysctlKey) {
  146. static NSString *entryValue;
  147. size_t size;
  148. sysctlbyname(sysctlKey, NULL, &size, NULL, 0);
  149. if (size > 0) {
  150. char *entryValueCStr = malloc(size);
  151. sysctlbyname(sysctlKey, entryValueCStr, &size, NULL, 0);
  152. entryValue = [NSString stringWithCString:entryValueCStr encoding:NSUTF8StringEncoding];
  153. free(entryValueCStr);
  154. return entryValue;
  155. } else {
  156. return nil;
  157. }
  158. }
  159. NS_ASSUME_NONNULL_END