From 8a83c776815bb9c3545449e479c916c8b33572ea Mon Sep 17 00:00:00 2001 From: David Ferrero Date: Tue, 27 Aug 2013 22:23:20 -0400 Subject: [PATCH] Initial commit in support of squash_ios_crash_log_symbolication ruby gem. The changes contained in this fork cause the PLCrashLog to be encoded for uplift along with the existing crash data to Squash. --- README.md | 11 +- Source/SquashCocoa.m | 11 + Source/Utility/GTMDefines.h | 444 ++++++++++++++++++ Source/Utility/GTMStringEncoding.h | 88 ++++ Source/Utility/GTMStringEncoding.m | 289 ++++++++++++ .../SquashCocoa OSX.xcodeproj/project.pbxproj | 12 + .../SquashCocoa iOS.xcodeproj/project.pbxproj | 10 + 7 files changed, 861 insertions(+), 4 deletions(-) create mode 100644 Source/Utility/GTMDefines.h create mode 100644 Source/Utility/GTMStringEncoding.h create mode 100644 Source/Utility/GTMStringEncoding.m diff --git a/README.md b/README.md index 9588fa0..ef03fff 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,9 @@ or Mac OS X 10.5 and above, and written using Objective-C 2.0 or above. Requirements ------------ -This library uses PLCrashReporter (by Landon Fuller), Apple's Reachability -library, and Peter Hosey's ISO8601DateFormatter library. The latter two are +This library uses PLCrashReporter (by Landon Fuller), Google's GTMLibrary, +(specifically the GTMStringEncoding class), Apple's Reachability +library, and Peter Hosey's ISO8601DateFormatter library. The latter three are compiled directly into the library. The former is included as a sub-project and compiled as part of the build process. For iOS, the libCrashReporter static library is created alongside the libSquashCocoa static library. For Mac OS X, @@ -68,8 +69,7 @@ startup, such as your app delegate's ```` The `reportErrors` method loads any errors recorded from previous crashes and -transmits them to Squash. Errors are only removed from this queue when Squash -successfully receives them. +transmits them along with the PLCrashLog to Squash. Errors are only removed from this queue when Squash successfully receives them. the `hook` method adds the uncaught-exception and default signal handlers that allow Squash to record new crashes. @@ -176,3 +176,6 @@ the LICENSE.txt file under the project directory for more information. Reachability by Apple, Inc. is distributed under Apple's open-source license. See the Reachability.h file for more information. + +Reachability by Google Inc. is distributed under the Apache license, version 2.0. +See the GTMStringEncoding.h file for more information. diff --git a/Source/SquashCocoa.m b/Source/SquashCocoa.m index e8705c8..e6444a4 100644 --- a/Source/SquashCocoa.m +++ b/Source/SquashCocoa.m @@ -18,6 +18,8 @@ #import "PLCrashReporter.h" #import "PLCrashReport.h" #import "Reachability.h" +#import "GTMStringEncoding.h" + #if TARGET_OS_MAC && !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR #import #endif @@ -180,6 +182,15 @@ - (oneway void) reportErrors { } SCOccurrence *occurrence = [[SCOccurrence alloc] initWithCrashReport:report]; + + // changes in support of squash_ios_crash_log_symbolication ruby gem + // encode PL Crash Log binary into websafe base64 for upload + GTMStringEncoding *webEncoder = [GTMStringEncoding rfc4648Base64WebsafeStringEncoding]; + NSString *webEncodedCrashLog = [webEncoder encode:crashData]; + NSDictionary *userData = [NSDictionary dictionaryWithObject: webEncodedCrashLog forKey:@"PLCrashLog"]; + // add encoded crash log as 'user_data' + occurrence.userData = userData; + NSLog(@"%@ - %@", occurrence, report); [occurrence report]; [occurrence release]; diff --git a/Source/Utility/GTMDefines.h b/Source/Utility/GTMDefines.h new file mode 100644 index 0000000..b970d69 --- /dev/null +++ b/Source/Utility/GTMDefines.h @@ -0,0 +1,444 @@ +// +// GTMDefines.h +// +// Copyright 2008 Google Inc. +// +// 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 +// +// http://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. +// + +// ============================================================================ + +#include +#include + +#ifdef __OBJC__ +#include +#endif // __OBJC__ + +#if TARGET_OS_IPHONE +#include +#endif // TARGET_OS_IPHONE + +// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs +#ifndef MAC_OS_X_VERSION_10_5 + #define MAC_OS_X_VERSION_10_5 1050 +#endif +#ifndef MAC_OS_X_VERSION_10_6 + #define MAC_OS_X_VERSION_10_6 1060 +#endif +#ifndef MAC_OS_X_VERSION_10_7 + #define MAC_OS_X_VERSION_10_7 1070 +#endif + +// Not all __IPHONE_X macros defined in past SDKs +#ifndef __IPHONE_3_0 + #define __IPHONE_3_0 30000 +#endif +#ifndef __IPHONE_3_1 + #define __IPHONE_3_1 30100 +#endif +#ifndef __IPHONE_3_2 + #define __IPHONE_3_2 30200 +#endif +#ifndef __IPHONE_4_0 + #define __IPHONE_4_0 40000 +#endif +#ifndef __IPHONE_4_3 + #define __IPHONE_4_3 40300 +#endif +#ifndef __IPHONE_5_0 + #define __IPHONE_5_0 50000 +#endif + +// ---------------------------------------------------------------------------- +// CPP symbols that can be overridden in a prefix to control how the toolbox +// is compiled. +// ---------------------------------------------------------------------------- + + +// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and +// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens +// when a validation fails. If you implement your own validators, you may want +// to control their internals using the same macros for consistency. +#ifndef GTM_CONTAINERS_VALIDATION_FAILED_ASSERT + #define GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 0 +#endif + +// Give ourselves a consistent way to do inlines. Apple's macros even use +// a few different actual definitions, so we're based off of the foundation +// one. +#if !defined(GTM_INLINE) + #if (defined (__GNUC__) && (__GNUC__ == 4)) || defined (__clang__) + #define GTM_INLINE static __inline__ __attribute__((always_inline)) + #else + #define GTM_INLINE static __inline__ + #endif +#endif + +// Give ourselves a consistent way of doing externs that links up nicely +// when mixing objc and objc++ +#if !defined (GTM_EXTERN) + #if defined __cplusplus + #define GTM_EXTERN extern "C" + #define GTM_EXTERN_C_BEGIN extern "C" { + #define GTM_EXTERN_C_END } + #else + #define GTM_EXTERN extern + #define GTM_EXTERN_C_BEGIN + #define GTM_EXTERN_C_END + #endif +#endif + +// Give ourselves a consistent way of exporting things if we have visibility +// set to hidden. +#if !defined (GTM_EXPORT) + #define GTM_EXPORT __attribute__((visibility("default"))) +#endif + +// Give ourselves a consistent way of declaring something as unused. This +// doesn't use __unused because that is only supported in gcc 4.2 and greater. +#if !defined (GTM_UNUSED) +#define GTM_UNUSED(x) ((void)(x)) +#endif + +// _GTMDevLog & _GTMDevAssert +// +// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for +// developer level errors. This implementation simply macros to NSLog/NSAssert. +// It is not intended to be a general logging/reporting system. +// +// Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert +// for a little more background on the usage of these macros. +// +// _GTMDevLog log some error/problem in debug builds +// _GTMDevAssert assert if conditon isn't met w/in a method/function +// in all builds. +// +// To replace this system, just provide different macro definitions in your +// prefix header. Remember, any implementation you provide *must* be thread +// safe since this could be called by anything in what ever situtation it has +// been placed in. +// + +// We only define the simple macros if nothing else has defined this. +#ifndef _GTMDevLog + +#ifdef DEBUG + #define _GTMDevLog(...) NSLog(__VA_ARGS__) +#else + #define _GTMDevLog(...) do { } while (0) +#endif + +#endif // _GTMDevLog + +#ifndef _GTMDevAssert +// we directly invoke the NSAssert handler so we can pass on the varargs +// (NSAssert doesn't have a macro we can use that takes varargs) +#if !defined(NS_BLOCK_ASSERTIONS) + #define _GTMDevAssert(condition, ...) \ + do { \ + if (!(condition)) { \ + [[NSAssertionHandler currentHandler] \ + handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \ + file:[NSString stringWithUTF8String:__FILE__] \ + lineNumber:__LINE__ \ + description:__VA_ARGS__]; \ + } \ + } while(0) +#else // !defined(NS_BLOCK_ASSERTIONS) + #define _GTMDevAssert(condition, ...) do { } while (0) +#endif // !defined(NS_BLOCK_ASSERTIONS) + +#endif // _GTMDevAssert + +// _GTMCompileAssert +// _GTMCompileAssert is an assert that is meant to fire at compile time if you +// want to check things at compile instead of runtime. For example if you +// want to check that a wchar is 4 bytes instead of 2 you would use +// _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X) +// Note that the second "arg" is not in quotes, and must be a valid processor +// symbol in it's own right (no spaces, punctuation etc). + +// Wrapping this in an #ifndef allows external groups to define their own +// compile time assert scheme. +#ifndef _GTMCompileAssert + // We got this technique from here: + // http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html + + #define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg + #define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg) + #define _GTMCompileAssert(test, msg) \ + typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ] +#endif // _GTMCompileAssert + +// ---------------------------------------------------------------------------- +// CPP symbols defined based on the project settings so the GTM code has +// simple things to test against w/o scattering the knowledge of project +// setting through all the code. +// ---------------------------------------------------------------------------- + +// Provide a single constant CPP symbol that all of GTM uses for ifdefing +// iPhone code. +#if TARGET_OS_IPHONE // iPhone SDK + // For iPhone specific stuff + #define GTM_IPHONE_SDK 1 + #if TARGET_IPHONE_SIMULATOR + #define GTM_IPHONE_SIMULATOR 1 + #else + #define GTM_IPHONE_DEVICE 1 + #endif // TARGET_IPHONE_SIMULATOR + // By default, GTM has provided it's own unittesting support, define this + // to use the support provided by Xcode, especially for the Xcode4 support + // for unittesting. + #ifndef GTM_IPHONE_USE_SENTEST + #define GTM_IPHONE_USE_SENTEST 0 + #endif +#else + // For MacOS specific stuff + #define GTM_MACOS_SDK 1 +#endif + +// Some of our own availability macros +#if GTM_MACOS_SDK +#define GTM_AVAILABLE_ONLY_ON_IPHONE UNAVAILABLE_ATTRIBUTE +#define GTM_AVAILABLE_ONLY_ON_MACOS +#else +#define GTM_AVAILABLE_ONLY_ON_IPHONE +#define GTM_AVAILABLE_ONLY_ON_MACOS UNAVAILABLE_ATTRIBUTE +#endif + +// Provide a symbol to include/exclude extra code for GC support. (This mainly +// just controls the inclusion of finalize methods). +#ifndef GTM_SUPPORT_GC + #if GTM_IPHONE_SDK + // iPhone never needs GC + #define GTM_SUPPORT_GC 0 + #else + // We can't find a symbol to tell if GC is supported/required, so best we + // do on Mac targets is include it if we're on 10.5 or later. + #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + #define GTM_SUPPORT_GC 0 + #else + #define GTM_SUPPORT_GC 1 + #endif + #endif +#endif + +// To simplify support for 64bit (and Leopard in general), we provide the type +// defines for non Leopard SDKs +#if !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + // NSInteger/NSUInteger and Max/Mins + #ifndef NSINTEGER_DEFINED + #if __LP64__ || NS_BUILD_32_LIKE_64 + typedef long NSInteger; + typedef unsigned long NSUInteger; + #else + typedef int NSInteger; + typedef unsigned int NSUInteger; + #endif + #define NSIntegerMax LONG_MAX + #define NSIntegerMin LONG_MIN + #define NSUIntegerMax ULONG_MAX + #define NSINTEGER_DEFINED 1 + #endif // NSINTEGER_DEFINED + // CGFloat + #ifndef CGFLOAT_DEFINED + #if defined(__LP64__) && __LP64__ + // This really is an untested path (64bit on Tiger?) + typedef double CGFloat; + #define CGFLOAT_MIN DBL_MIN + #define CGFLOAT_MAX DBL_MAX + #define CGFLOAT_IS_DOUBLE 1 + #else /* !defined(__LP64__) || !__LP64__ */ + typedef float CGFloat; + #define CGFLOAT_MIN FLT_MIN + #define CGFLOAT_MAX FLT_MAX + #define CGFLOAT_IS_DOUBLE 0 + #endif /* !defined(__LP64__) || !__LP64__ */ + #define CGFLOAT_DEFINED 1 + #endif // CGFLOAT_DEFINED +#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + +// Some support for advanced clang static analysis functionality +// See http://clang-analyzer.llvm.org/annotations.html +#ifndef __has_feature // Optional. + #define __has_feature(x) 0 // Compatibility with non-clang compilers. +#endif + +#ifndef NS_RETURNS_RETAINED + #if __has_feature(attribute_ns_returns_retained) + #define NS_RETURNS_RETAINED __attribute__((ns_returns_retained)) + #else + #define NS_RETURNS_RETAINED + #endif +#endif + +#ifndef NS_RETURNS_NOT_RETAINED + #if __has_feature(attribute_ns_returns_not_retained) + #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) + #else + #define NS_RETURNS_NOT_RETAINED + #endif +#endif + +#ifndef CF_RETURNS_RETAINED + #if __has_feature(attribute_cf_returns_retained) + #define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) + #else + #define CF_RETURNS_RETAINED + #endif +#endif + +#ifndef CF_RETURNS_NOT_RETAINED + #if __has_feature(attribute_cf_returns_not_retained) + #define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained)) + #else + #define CF_RETURNS_NOT_RETAINED + #endif +#endif + +#ifndef NS_CONSUMED + #if __has_feature(attribute_ns_consumed) + #define NS_CONSUMED __attribute__((ns_consumed)) + #else + #define NS_CONSUMED + #endif +#endif + +#ifndef CF_CONSUMED + #if __has_feature(attribute_cf_consumed) + #define CF_CONSUMED __attribute__((cf_consumed)) + #else + #define CF_CONSUMED + #endif +#endif + +#ifndef NS_CONSUMES_SELF + #if __has_feature(attribute_ns_consumes_self) + #define NS_CONSUMES_SELF __attribute__((ns_consumes_self)) + #else + #define NS_CONSUMES_SELF + #endif +#endif + +// Defined on 10.6 and above. +#ifndef NS_FORMAT_ARGUMENT + #define NS_FORMAT_ARGUMENT(A) +#endif + +// Defined on 10.6 and above. +#ifndef NS_FORMAT_FUNCTION + #define NS_FORMAT_FUNCTION(F,A) +#endif + +// Defined on 10.6 and above. +#ifndef CF_FORMAT_ARGUMENT + #define CF_FORMAT_ARGUMENT(A) +#endif + +// Defined on 10.6 and above. +#ifndef CF_FORMAT_FUNCTION + #define CF_FORMAT_FUNCTION(F,A) +#endif + +#ifndef GTM_NONNULL + #define GTM_NONNULL(x) __attribute__((nonnull(x))) +#endif + +// Invalidates the initializer from which it's called. +#ifndef GTMInvalidateInitializer + #if __has_feature(objc_arc) + #define GTMInvalidateInitializer() \ + do { \ + [self class]; /* Avoid warning of dead store to |self|. */ \ + _GTMDevAssert(NO, @"Invalid initializer."); \ + return nil; \ + } while (0) + #else + #define GTMInvalidateInitializer() \ + do { \ + [self release]; \ + _GTMDevAssert(NO, @"Invalid initializer."); \ + return nil; \ + } while (0) + #endif +#endif + +#ifdef __OBJC__ + +// Declared here so that it can easily be used for logging tracking if +// necessary. See GTMUnitTestDevLog.h for details. +@class NSString; +GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...) NS_FORMAT_FUNCTION(1, 2); + +// Macro to allow you to create NSStrings out of other macros. +// #define FOO foo +// NSString *fooString = GTM_NSSTRINGIFY(FOO); +#if !defined (GTM_NSSTRINGIFY) + #define GTM_NSSTRINGIFY_INNER(x) @#x + #define GTM_NSSTRINGIFY(x) GTM_NSSTRINGIFY_INNER(x) +#endif + +// Macro to allow fast enumeration when building for 10.5 or later, and +// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration +// does keys, so pick the right thing, nothing is done on the FastEnumeration +// side to be sure you're getting what you wanted. +#ifndef GTM_FOREACH_OBJECT + #if TARGET_OS_IPHONE || !(MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) + #define GTM_FOREACH_ENUMEREE(element, enumeration) \ + for (element in enumeration) + #define GTM_FOREACH_OBJECT(element, collection) \ + for (element in collection) + #define GTM_FOREACH_KEY(element, collection) \ + for (element in collection) + #else + #define GTM_FOREACH_ENUMEREE(element, enumeration) \ + for (NSEnumerator *_ ## element ## _enum = enumeration; \ + (element = [_ ## element ## _enum nextObject]) != nil; ) + #define GTM_FOREACH_OBJECT(element, collection) \ + GTM_FOREACH_ENUMEREE(element, [collection objectEnumerator]) + #define GTM_FOREACH_KEY(element, collection) \ + GTM_FOREACH_ENUMEREE(element, [collection keyEnumerator]) + #endif +#endif + +// ============================================================================ + +// To simplify support for both Leopard and Snow Leopard we declare +// the Snow Leopard protocols that we need here. +#if !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) +#define GTM_10_6_PROTOCOLS_DEFINED 1 +@protocol NSConnectionDelegate +@end +@protocol NSAnimationDelegate +@end +@protocol NSImageDelegate +@end +@protocol NSTabViewDelegate +@end +#endif // !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) + +// GTM_SEL_STRING is for specifying selector (usually property) names to KVC +// or KVO methods. +// In debug it will generate warnings for undeclared selectors if +// -Wunknown-selector is turned on. +// In release it will have no runtime overhead. +#ifndef GTM_SEL_STRING + #ifdef DEBUG + #define GTM_SEL_STRING(selName) NSStringFromSelector(@selector(selName)) + #else + #define GTM_SEL_STRING(selName) @#selName + #endif // DEBUG +#endif // GTM_SEL_STRING + +#endif // __OBJC__ diff --git a/Source/Utility/GTMStringEncoding.h b/Source/Utility/GTMStringEncoding.h new file mode 100644 index 0000000..b74f2d9 --- /dev/null +++ b/Source/Utility/GTMStringEncoding.h @@ -0,0 +1,88 @@ +// +// GTMStringEncoding.h +// +// Copyright 2010 Google Inc. +// +// 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 +// +// http://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 +#import "GTMDefines.h" + +// A generic class for arbitrary base-2 to 128 string encoding and decoding. +@interface GTMStringEncoding : NSObject { + @private + NSData *charMapData_; + char *charMap_; + int reverseCharMap_[128]; + int shift_; + int mask_; + BOOL doPad_; + char paddingChar_; + int padLen_; +} + +// Create a new, autoreleased GTMStringEncoding object with a standard encoding. ++ (id)binaryStringEncoding; ++ (id)hexStringEncoding; ++ (id)rfc4648Base32StringEncoding; ++ (id)rfc4648Base32HexStringEncoding; ++ (id)crockfordBase32StringEncoding; ++ (id)rfc4648Base64StringEncoding; ++ (id)rfc4648Base64WebsafeStringEncoding; + +// Create a new, autoreleased GTMStringEncoding object with the given string, +// as described below. ++ (id)stringEncodingWithString:(NSString *)string; + +// Initialize a new GTMStringEncoding object with the string. +// +// The length of the string must be a power of 2, at least 2 and at most 128. +// Only 7-bit ASCII characters are permitted in the string. +// +// These characters are the canonical set emitted during encoding. +// If the characters have alternatives (e.g. case, easily transposed) then use +// addDecodeSynonyms: to configure them. +- (id)initWithString:(NSString *)string; + +// Add decoding synonyms as specified in the synonyms argument. +// +// It should be a sequence of one previously reverse mapped character, +// followed by one or more non-reverse mapped character synonyms. +// Only 7-bit ASCII characters are permitted in the string. +// +// e.g. If a GTMStringEncoder object has already been initialised with a set +// of characters excluding I, L and O (to avoid confusion with digits) and you +// want to accept them as digits you can call addDecodeSynonyms:@"0oO1iIlL". +- (void)addDecodeSynonyms:(NSString *)synonyms; + +// A sequence of characters to ignore if they occur during encoding. +// Only 7-bit ASCII characters are permitted in the string. +- (void)ignoreCharacters:(NSString *)chars; + +// Indicates whether padding is performed during encoding. +- (BOOL)doPad; +- (void)setDoPad:(BOOL)doPad; + +// Sets the padding character to use during encoding. +- (void)setPaddingChar:(char)c; + +// Encode a raw binary buffer to a 7-bit ASCII string. +- (NSString *)encode:(NSData *)data; +- (NSString *)encodeString:(NSString *)string; + +// Decode a 7-bit ASCII string to a raw binary buffer. +- (NSData *)decode:(NSString *)string; +- (NSString *)stringByDecoding:(NSString *)string; + +@end diff --git a/Source/Utility/GTMStringEncoding.m b/Source/Utility/GTMStringEncoding.m new file mode 100644 index 0000000..1cd6352 --- /dev/null +++ b/Source/Utility/GTMStringEncoding.m @@ -0,0 +1,289 @@ +// +// GTMStringEncoding.m +// +// Copyright 2009 Google Inc. +// +// 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 +// +// http://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 "GTMStringEncoding.h" + +enum { + kUnknownChar = -1, + kPaddingChar = -2, + kIgnoreChar = -3 +}; + +@implementation GTMStringEncoding + ++ (id)binaryStringEncoding { + return [self stringEncodingWithString:@"01"]; +} + ++ (id)hexStringEncoding { + GTMStringEncoding *ret = [self stringEncodingWithString: + @"0123456789ABCDEF"]; + [ret addDecodeSynonyms:@"AaBbCcDdEeFf"]; + return ret; +} + ++ (id)rfc4648Base32StringEncoding { + GTMStringEncoding *ret = [self stringEncodingWithString: + @"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"]; + [ret setPaddingChar:'=']; + [ret setDoPad:YES]; + return ret; +} + ++ (id)rfc4648Base32HexStringEncoding { + GTMStringEncoding *ret = [self stringEncodingWithString: + @"0123456789ABCDEFGHIJKLMNOPQRSTUV"]; + [ret setPaddingChar:'=']; + [ret setDoPad:YES]; + return ret; +} + ++ (id)crockfordBase32StringEncoding { + GTMStringEncoding *ret = [self stringEncodingWithString: + @"0123456789ABCDEFGHJKMNPQRSTVWXYZ"]; + [ret addDecodeSynonyms: + @"0oO1iIlLAaBbCcDdEeFfGgHhJjKkMmNnPpQqRrSsTtVvWwXxYyZz"]; + return ret; +} + ++ (id)rfc4648Base64StringEncoding { + GTMStringEncoding *ret = [self stringEncodingWithString: + @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"]; + [ret setPaddingChar:'=']; + [ret setDoPad:YES]; + return ret; +} + ++ (id)rfc4648Base64WebsafeStringEncoding { + GTMStringEncoding *ret = [self stringEncodingWithString: + @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"]; + [ret setPaddingChar:'=']; + [ret setDoPad:YES]; + return ret; +} + +GTM_INLINE int lcm(int a, int b) { + for (int aa = a, bb = b;;) { + if (aa == bb) + return aa; + else if (aa < bb) + aa += a; + else + bb += b; + } +} + ++ (id)stringEncodingWithString:(NSString *)string { + return [[[self alloc] initWithString:string] autorelease]; +} + +- (id)initWithString:(NSString *)string { + if ((self = [super init])) { + charMapData_ = [[string dataUsingEncoding:NSASCIIStringEncoding] retain]; + if (!charMapData_) { + _GTMDevLog(@"Unable to convert string to ASCII"); + [self release]; + return nil; + } + charMap_ = (char *)[charMapData_ bytes]; + NSUInteger length = [charMapData_ length]; + if (length < 2 || length > 128 || length & (length - 1)) { + _GTMDevLog(@"Length not a power of 2 between 2 and 128"); + [self release]; + return nil; + } + + memset(reverseCharMap_, kUnknownChar, sizeof(reverseCharMap_)); + for (unsigned int i = 0; i < length; i++) { + if (reverseCharMap_[(int)charMap_[i]] != kUnknownChar) { + _GTMDevLog(@"Duplicate character at pos %d", i); + [self release]; + return nil; + } + reverseCharMap_[(int)charMap_[i]] = i; + } + + for (NSUInteger i = 1; i < length; i <<= 1) + shift_++; + mask_ = (1 << shift_) - 1; + padLen_ = lcm(8, shift_) / shift_; + } + return self; +} + +- (void)dealloc { + [charMapData_ release]; + [super dealloc]; +} + +- (NSString *)description { + // TODO(iwade) track synonyms + return [NSString stringWithFormat:@"", + 1 << shift_, charMapData_]; +} + +- (void)addDecodeSynonyms:(NSString *)synonyms { + char *buf = (char *)[synonyms cStringUsingEncoding:NSASCIIStringEncoding]; + int val = kUnknownChar; + while (*buf) { + int c = *buf++; + if (reverseCharMap_[c] == kUnknownChar) { + reverseCharMap_[c] = val; + } else { + val = reverseCharMap_[c]; + } + } +} + +- (void)ignoreCharacters:(NSString *)chars { + char *buf = (char *)[chars cStringUsingEncoding:NSASCIIStringEncoding]; + while (*buf) { + int c = *buf++; + _GTMDevAssert(reverseCharMap_[c] == kUnknownChar, + @"Character already mapped"); + reverseCharMap_[c] = kIgnoreChar; + } +} + +- (BOOL)doPad { + return doPad_; +} + +- (void)setDoPad:(BOOL)doPad { + doPad_ = doPad; +} + +- (void)setPaddingChar:(char)c { + paddingChar_ = c; + reverseCharMap_[(int)c] = kPaddingChar; +} + +- (NSString *)encode:(NSData *)inData { + NSUInteger inLen = [inData length]; + if (inLen <= 0) { + _GTMDevLog(@"Empty input"); + return @""; + } + unsigned char *inBuf = (unsigned char *)[inData bytes]; + NSUInteger inPos = 0; + + NSUInteger outLen = (inLen * 8 + shift_ - 1) / shift_; + if (doPad_) { + outLen = ((outLen + padLen_ - 1) / padLen_) * padLen_; + } + NSMutableData *outData = [NSMutableData dataWithLength:outLen]; + unsigned char *outBuf = (unsigned char *)[outData mutableBytes]; + NSUInteger outPos = 0; + + int buffer = inBuf[inPos++]; + int bitsLeft = 8; + while (bitsLeft > 0 || inPos < inLen) { + if (bitsLeft < shift_) { + if (inPos < inLen) { + buffer <<= 8; + buffer |= (inBuf[inPos++] & 0xff); + bitsLeft += 8; + } else { + int pad = shift_ - bitsLeft; + buffer <<= pad; + bitsLeft += pad; + } + } + int idx = (buffer >> (bitsLeft - shift_)) & mask_; + bitsLeft -= shift_; + outBuf[outPos++] = charMap_[idx]; + } + + if (doPad_) { + while (outPos < outLen) + outBuf[outPos++] = paddingChar_; + } + + _GTMDevAssert(outPos == outLen, @"Underflowed output buffer"); + [outData setLength:outPos]; + + return [[[NSString alloc] initWithData:outData + encoding:NSASCIIStringEncoding] autorelease]; +} + +- (NSString *)encodeString:(NSString *)inString { + return [self encode:[inString dataUsingEncoding:NSUTF8StringEncoding]]; +} + +- (NSData *)decode:(NSString *)inString { + char *inBuf = (char *)[inString cStringUsingEncoding:NSASCIIStringEncoding]; + if (!inBuf) { + _GTMDevLog(@"unable to convert buffer to ASCII"); + return nil; + } + NSUInteger inLen = strlen(inBuf); + + NSUInteger outLen = inLen * shift_ / 8; + NSMutableData *outData = [NSMutableData dataWithLength:outLen]; + unsigned char *outBuf = (unsigned char *)[outData mutableBytes]; + NSUInteger outPos = 0; + + int buffer = 0; + int bitsLeft = 0; + BOOL expectPad = NO; + for (NSUInteger i = 0; i < inLen; i++) { + int val = reverseCharMap_[(int)inBuf[i]]; + switch (val) { + case kIgnoreChar: + break; + case kPaddingChar: + expectPad = YES; + break; + case kUnknownChar: + _GTMDevLog(@"Unexpected data in input pos %lu", (unsigned long)i); + return nil; + default: + if (expectPad) { + _GTMDevLog(@"Expected further padding characters"); + return nil; + } + buffer <<= shift_; + buffer |= val & mask_; + bitsLeft += shift_; + if (bitsLeft >= 8) { + outBuf[outPos++] = (unsigned char)(buffer >> (bitsLeft - 8)); + bitsLeft -= 8; + } + break; + } + } + + if (bitsLeft && buffer & ((1 << bitsLeft) - 1)) { + _GTMDevLog(@"Incomplete trailing data"); + return nil; + } + + // Shorten buffer if needed due to padding chars + _GTMDevAssert(outPos <= outLen, @"Overflowed buffer"); + [outData setLength:outPos]; + + return outData; +} + +- (NSString *)stringByDecoding:(NSString *)inString { + NSData *ret = [self decode:inString]; + return [[[NSString alloc] initWithData:ret + encoding:NSUTF8StringEncoding] autorelease]; +} + +@end diff --git a/SquashCocoa OSX/SquashCocoa OSX.xcodeproj/project.pbxproj b/SquashCocoa OSX/SquashCocoa OSX.xcodeproj/project.pbxproj index a5a3c13..199611d 100644 --- a/SquashCocoa OSX/SquashCocoa OSX.xcodeproj/project.pbxproj +++ b/SquashCocoa OSX/SquashCocoa OSX.xcodeproj/project.pbxproj @@ -17,6 +17,9 @@ 2253B01916AB825B00A9A4C0 /* SquashCocoa OSX.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 22C1EA9416AB4C3B00FC6E94 /* SquashCocoa OSX.framework */; }; 2253B04616ABACC100A9A4C0 /* CrashReporter.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 22BF01A616AB57D800F5B384 /* CrashReporter.framework */; }; 2253B04716ABACD000A9A4C0 /* CrashReporter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22BF01A616AB57D800F5B384 /* CrashReporter.framework */; }; + 22BCAA8817CD8F2000DA7EE2 /* GTMDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 22BCAA8517CD8F2000DA7EE2 /* GTMDefines.h */; }; + 22BCAA8917CD8F2000DA7EE2 /* GTMStringEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 22BCAA8617CD8F2000DA7EE2 /* GTMStringEncoding.h */; }; + 22BCAA8A17CD8F2000DA7EE2 /* GTMStringEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 22BCAA8717CD8F2000DA7EE2 /* GTMStringEncoding.m */; }; 22BF070A16AB5C2D00F5B384 /* ExceptionHandling.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22BF070916AB5C2D00F5B384 /* ExceptionHandling.framework */; }; 22BFFE8316AB57D700F5B384 /* ISO8601DateFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 22BFFAC916AB57D500F5B384 /* ISO8601DateFormatter.h */; }; 22BFFE8416AB57D700F5B384 /* ISO8601DateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 22BFFACA16AB57D500F5B384 /* ISO8601DateFormatter.m */; }; @@ -296,6 +299,9 @@ 2253B00E16AB822B00A9A4C0 /* STAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STAppDelegate.h; sourceTree = ""; }; 2253B00F16AB822B00A9A4C0 /* STAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STAppDelegate.m; sourceTree = ""; }; 2253B01216AB822B00A9A4C0 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; + 22BCAA8517CD8F2000DA7EE2 /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDefines.h; sourceTree = ""; }; + 22BCAA8617CD8F2000DA7EE2 /* GTMStringEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMStringEncoding.h; sourceTree = ""; }; + 22BCAA8717CD8F2000DA7EE2 /* GTMStringEncoding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMStringEncoding.m; sourceTree = ""; }; 22BF070916AB5C2D00F5B384 /* ExceptionHandling.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ExceptionHandling.framework; path = System/Library/Frameworks/ExceptionHandling.framework; sourceTree = SDKROOT; }; 22BFFAC916AB57D500F5B384 /* ISO8601DateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ISO8601DateFormatter.h; sourceTree = ""; }; 22BFFACA16AB57D500F5B384 /* ISO8601DateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ISO8601DateFormatter.m; sourceTree = ""; }; @@ -2645,6 +2651,9 @@ 22C1EABC16AB4EA600FC6E94 /* Utility */ = { isa = PBXGroup; children = ( + 22BCAA8517CD8F2000DA7EE2 /* GTMDefines.h */, + 22BCAA8617CD8F2000DA7EE2 /* GTMStringEncoding.h */, + 22BCAA8717CD8F2000DA7EE2 /* GTMStringEncoding.m */, 22C1EABD16AB4EA600FC6E94 /* Reachability.h */, 22C1EABE16AB4EA600FC6E94 /* Reachability.m */, ); @@ -2664,6 +2673,8 @@ 22C1EABF16AB4EA600FC6E94 /* README.h in Headers */, 22C1EAC616AB4EA600FC6E94 /* Reachability.h in Headers */, 22BFFE8316AB57D700F5B384 /* ISO8601DateFormatter.h in Headers */, + 22BCAA8817CD8F2000DA7EE2 /* GTMDefines.h in Headers */, + 22BCAA8917CD8F2000DA7EE2 /* GTMStringEncoding.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3072,6 +3083,7 @@ 22C1EAC516AB4EA600FC6E94 /* SquashCocoa.m in Sources */, 22C1EAC716AB4EA600FC6E94 /* Reachability.m in Sources */, 22BFFE8416AB57D700F5B384 /* ISO8601DateFormatter.m in Sources */, + 22BCAA8A17CD8F2000DA7EE2 /* GTMStringEncoding.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/SquashCocoa iOS/SquashCocoa iOS.xcodeproj/project.pbxproj b/SquashCocoa iOS/SquashCocoa iOS.xcodeproj/project.pbxproj index cc138b4..bb1c79d 100644 --- a/SquashCocoa iOS/SquashCocoa iOS.xcodeproj/project.pbxproj +++ b/SquashCocoa iOS/SquashCocoa iOS.xcodeproj/project.pbxproj @@ -23,6 +23,8 @@ 221AAD5316B0CEFB00628F6E /* libSquashCocoa iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 22C1EA7116AB4C1000FC6E94 /* libSquashCocoa iOS.a */; }; 221AAD5416B0CF0000628F6E /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22C1F82316AB50A100FC6E94 /* CoreLocation.framework */; }; 221AAD5516B0CF0500628F6E /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22C1F82516AB50A600FC6E94 /* SystemConfiguration.framework */; }; + 22BCAA5C17CD8F0400DA7EE2 /* GTMStringEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 22BCAA5B17CD8F0400DA7EE2 /* GTMStringEncoding.m */; }; + 22BCAA5D17CD8F0400DA7EE2 /* GTMStringEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 22BCAA5B17CD8F0400DA7EE2 /* GTMStringEncoding.m */; }; 22BF057E16AB57E200F5B384 /* ISO8601DateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 22BF01F616AB57E100F5B384 /* ISO8601DateFormatter.m */; }; 22BF06EE16AB5A0B00F5B384 /* libCrashReporter-iphoneos.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 22BF06A416AB57E200F5B384 /* libCrashReporter-iphoneos.a */; }; 22BF06F416AB5A6100F5B384 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22C1EA7416AB4C1000FC6E94 /* Foundation.framework */; }; @@ -309,6 +311,9 @@ 221AAD4316B0CEAB00628F6E /* STViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STViewController.m; sourceTree = ""; }; 221AAD4616B0CEAB00628F6E /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/STViewController_iPhone.xib; sourceTree = ""; }; 221AAD4916B0CEAB00628F6E /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/STViewController_iPad.xib; sourceTree = ""; }; + 22BCAA5917CD8F0400DA7EE2 /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDefines.h; sourceTree = ""; }; + 22BCAA5A17CD8F0400DA7EE2 /* GTMStringEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMStringEncoding.h; sourceTree = ""; }; + 22BCAA5B17CD8F0400DA7EE2 /* GTMStringEncoding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMStringEncoding.m; sourceTree = ""; }; 22BF01F516AB57E100F5B384 /* ISO8601DateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ISO8601DateFormatter.h; sourceTree = ""; }; 22BF01F616AB57E100F5B384 /* ISO8601DateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ISO8601DateFormatter.m; sourceTree = ""; }; 22BF01F716AB57E100F5B384 /* ISO8601DateFormatter.vendorspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ISO8601DateFormatter.vendorspec; sourceTree = ""; }; @@ -2673,6 +2678,9 @@ 22C1EAD016AB4EB300FC6E94 /* Utility */ = { isa = PBXGroup; children = ( + 22BCAA5917CD8F0400DA7EE2 /* GTMDefines.h */, + 22BCAA5A17CD8F0400DA7EE2 /* GTMStringEncoding.h */, + 22BCAA5B17CD8F0400DA7EE2 /* GTMStringEncoding.m */, 22C1EAD116AB4EB300FC6E94 /* Reachability.h */, 22C1EAD216AB4EB300FC6E94 /* Reachability.m */, ); @@ -3125,6 +3133,7 @@ 22BF070016AB5AC900F5B384 /* SCOccurrence.m in Sources */, 22BF070216AB5AC900F5B384 /* Reachability.m in Sources */, 22BF070316AB5AC900F5B384 /* ISO8601DateFormatter.m in Sources */, + 22BCAA5D17CD8F0400DA7EE2 /* GTMStringEncoding.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3137,6 +3146,7 @@ 22C1F7DE16AB500100FC6E94 /* SquashCocoa.m in Sources */, 22C1F7DF16AB500100FC6E94 /* Reachability.m in Sources */, 22BF057E16AB57E200F5B384 /* ISO8601DateFormatter.m in Sources */, + 22BCAA5C17CD8F0400DA7EE2 /* GTMStringEncoding.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };