App Completed

Basic Prototype App Completed. Proposal, Literature Review and Final
Report to Follow.
This commit is contained in:
Alexander Davis
2017-04-13 18:18:38 +01:00
parent 933a4acff6
commit 0018e82ee5
510 changed files with 86109 additions and 1315 deletions

View File

@@ -0,0 +1,100 @@
//
// GTMDebugSelectorValidation.h
//
// This file should only be included within an implimation file. In any
// function that takes an object and selector to invoke, you should call:
//
// GTMAssertSelectorNilOrImplementedWithArguments(obj, sel, @encode(arg1type), ..., NULL)
// or
// GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(obj, sel, @encode(returnType), @encode(arg1type), ..., NULL)
//
// This will then validate that the selector is defined and using the right
// type(s), this can help catch errors much earlier then waiting for the
// selector to actually fire (and in the case of error selectors, might never
// really be tested until in the field).
//
// Copyright 2007-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.
//
#if DEBUG
#import <stdarg.h>
#import "GTMDefines.h"
static void GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(id obj, SEL sel, const char *retType, ...) {
// verify that the object's selector is implemented with the proper
// number and type of arguments
va_list argList;
va_start(argList, retType);
if (obj && sel) {
// check that the selector is implemented
_GTMDevAssert([obj respondsToSelector:sel],
@"\"%@\" selector \"%@\" is unimplemented or misnamed",
NSStringFromClass([obj class]),
NSStringFromSelector(sel));
const char *expectedArgType;
NSUInteger argCount = 2; // skip self and _cmd
NSMethodSignature *sig = [obj methodSignatureForSelector:sel];
// check that each expected argument is present and of the correct type
while ((expectedArgType = va_arg(argList, const char*)) != 0) {
if ([sig numberOfArguments] > argCount) {
const char *foundArgType = [sig getArgumentTypeAtIndex:argCount];
_GTMDevAssert(0 == strncmp(foundArgType, expectedArgType, strlen(expectedArgType)),
@"\"%@\" selector \"%@\" argument %u should be type %s",
NSStringFromClass([obj class]),
NSStringFromSelector(sel),
(uint32_t)(argCount - 2),
expectedArgType);
}
argCount++;
}
// check that the proper number of arguments are present in the selector
_GTMDevAssert(argCount == [sig numberOfArguments],
@"\"%@\" selector \"%@\" should have %u arguments",
NSStringFromClass([obj class]),
NSStringFromSelector(sel),
(uint32_t)(argCount - 2));
// if asked, validate the return type
if (retType && (strcmp("gtm_skip_return_test", retType) != 0)) {
const char *foundRetType = [sig methodReturnType];
_GTMDevAssert(0 == strncmp(foundRetType, retType, strlen(retType)),
@"\"%@\" selector \"%@\" return type should be type %s",
NSStringFromClass([obj class]),
NSStringFromSelector(sel),
retType);
}
}
va_end(argList);
}
#define GTMAssertSelectorNilOrImplementedWithArguments(obj, sel, ...) \
GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments((obj), (sel), "gtm_skip_return_test", __VA_ARGS__)
#else // DEBUG
// make it go away if not debug
#define GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(obj, sel, retType, ...) do { } while (0)
#define GTMAssertSelectorNilOrImplementedWithArguments(obj, sel, ...) do { } while (0)
#endif // DEBUG

View File

@@ -0,0 +1,44 @@
//
// GTMDebugThreadValidation.h
//
// Copyright 2016 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 "GTMDefines.h"
#import <Foundation/Foundation.h>
// GTMCheckCurrentQueue, GTMIsCurrentQueue
//
// GTMCheckCurrentQueue takes a target queue and uses _GTMDevAssert to
// report if that is not the currently executing queue.
//
// GTMIsCurrentQueue takes a target queue and returns true if the target queue
// is the currently executing dispatch queue. This can be passed to another
// assertion call in debug builds; it should never be used in release code.
//
// The dispatch queue must have a label.
#define GTMCheckCurrentQueue(targetQueue) \
_GTMDevAssert(GTMIsCurrentQueue(targetQueue), \
@"Current queue is %s (expected %s)", \
_GTMQueueName(DISPATCH_CURRENT_QUEUE_LABEL), \
_GTMQueueName(targetQueue))
#define GTMIsCurrentQueue(targetQueue) \
(strcmp(_GTMQueueName(DISPATCH_CURRENT_QUEUE_LABEL), \
_GTMQueueName(targetQueue)) == 0)
#define _GTMQueueName(queue) \
(strlen(dispatch_queue_get_label(queue)) > 0 ? \
dispatch_queue_get_label(queue) : "unnamed")

View File

@@ -0,0 +1,69 @@
//
// GTMMethodCheck.h
//
// Copyright 2006-2016 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 <Foundation/Foundation.h>
#import <stdio.h>
#import <sysexits.h>
/// A macro for enforcing debug time checks to make sure all required methods are linked in
//
// When using categories, it can be very easy to forget to include the
// implementation of a category.
// Let's say you had a class foo that depended on method bar of class baz, and
// method bar was implemented as a member of a category.
// You could add the following code:
//
// GTM_METHOD_CHECK(baz, bar)
//
// and the code would check to make sure baz was implemented just before main
// was called. This works for both dynamic libraries, and executables.
//
//
// This is not compiled into release builds.
#ifdef DEBUG
// This is the "magic".
// A) we need a multi layer define here so that the preprocessor expands
// __LINE__ the way we want it. We need __LINE__ so that each of our
// GTM_METHOD_CHECKs generates a unique function name.
#define GTM_METHOD_CHECK(class, method) GTM_METHOD_CHECK_INNER(class, method, __LINE__)
#define GTM_METHOD_CHECK_INNER(class, method, line) \
GTM_METHOD_CHECK_INNER_INNER(class, method, line)
// B) define a function that is called at startup to check that |class| has an
// implementation for |method| (either a class method or an instance method).
#define GTM_METHOD_CHECK_INNER_INNER(class, method, line) \
__attribute__ ((constructor, visibility("hidden"))) \
static void xxGTMMethodCheckMethod ## class ## line () { \
@autoreleasepool { \
if (![class instancesRespondToSelector:@selector(method)] \
&& ![class respondsToSelector:@selector(method)]) { \
fprintf(stderr, "%s:%d: error: We need method '%s' to be linked in for class '%s'\n", \
__FILE__, line, #method, #class); \
exit(EX_SOFTWARE); \
} \
} \
}
#else // DEBUG
// Do nothing in release.
#define GTM_METHOD_CHECK(class, method)
#endif // DEBUG

View File

@@ -0,0 +1,508 @@
//
// GTMLogger.h
//
// Copyright 2007-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.
//
// Key Abstractions
// ----------------
//
// This file declares multiple classes and protocols that are used by the
// GTMLogger logging system. The 4 main abstractions used in this file are the
// following:
//
// * logger (GTMLogger) - The main logging class that users interact with. It
// has methods for logging at different levels and uses a log writer, a log
// formatter, and a log filter to get the job done.
//
// * log writer (GTMLogWriter) - Writes a given string to some log file, where
// a "log file" can be a physical file on disk, a POST over HTTP to some URL,
// or even some in-memory structure (e.g., a ring buffer).
//
// * log formatter (GTMLogFormatter) - Given a format string and arguments as
// a va_list, returns a single formatted NSString. A "formatted string" could
// be a string with the date prepended, a string with values in a CSV format,
// or even a string of XML.
//
// * log filter (GTMLogFilter) - Given a formatted log message as an NSString
// and the level at which the message is to be logged, this class will decide
// whether the given message should be logged or not. This is a flexible way
// to filter out messages logged at a certain level, messages that contain
// certain text, or filter nothing out at all. This gives the caller the
// flexibility to dynamically enable debug logging in Release builds.
//
// This file also declares some classes to handle the common log writer, log
// formatter, and log filter cases. Callers can also create their own writers,
// formatters, and filters and they can even build them on top of the ones
// declared here. Keep in mind that your custom writer/formatter/filter may be
// called from multiple threads, so it must be thread-safe.
#import <Foundation/Foundation.h>
#import "GTMDefines.h"
// Predeclaration of used protocols that are declared later in this file.
@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter;
// GTMLogger
//
// GTMLogger is the primary user-facing class for an object-oriented logging
// system. It is built on the concept of log formatters (GTMLogFormatter), log
// writers (GTMLogWriter), and log filters (GTMLogFilter). When a message is
// sent to a GTMLogger to log a message, the message is formatted using the log
// formatter, then the log filter is consulted to see if the message should be
// logged, and if so, the message is sent to the log writer to be written out.
//
// GTMLogger is intended to be a flexible and thread-safe logging solution. Its
// flexibility comes from the fact that GTMLogger instances can be customized
// with user defined formatters, filters, and writers. And these writers,
// filters, and formatters can be combined, stacked, and customized in arbitrary
// ways to suit the needs at hand. For example, multiple writers can be used at
// the same time, and a GTMLogger instance can even be used as another
// GTMLogger's writer. This allows for arbitrarily deep logging trees.
//
// A standard GTMLogger uses a writer that sends messages to standard out, a
// formatter that smacks a timestamp and a few other bits of interesting
// information on the message, and a filter that filters out debug messages from
// release builds. Using the standard log settings, a log message will look like
// the following:
//
// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo=<Foo: 0x123>
//
// The output contains the date and time of the log message, the name of the
// process followed by its process ID/thread ID, the log level at which the
// message was logged (in the previous example the level was 1:
// kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in
// this case, the log message was @"foo=%@", foo).
//
// Multiple instances of GTMLogger can be created, each configured their own
// way. Though GTMLogger is not a singleton (in the GoF sense), it does provide
// access to a shared (i.e., globally accessible) GTMLogger instance. This makes
// it convenient for all code in a process to use the same GTMLogger instance.
// The shared GTMLogger instance can also be configured in an arbitrary, and
// these configuration changes will affect all code that logs through the shared
// instance.
//
// Log Levels
// ----------
// GTMLogger has 3 different log levels: Debug, Info, and Error. GTMLogger
// doesn't take any special action based on the log level; it simply forwards
// this information on to formatters, filters, and writers, each of which may
// optionally take action based on the level. Since log level filtering is
// performed at runtime, log messages are typically not filtered out at compile
// time. The exception to this rule is that calls to the GTMLoggerDebug() macro
// *ARE* filtered out of non-DEBUG builds. This is to be backwards compatible
// with behavior that many developers are currently used to. Note that this
// means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but
// [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out.
//
// Standard loggers are created with the GTMLogLevelFilter log filter, which
// filters out certain log messages based on log level, and some other settings.
//
// In addition to the -logDebug:, -logInfo:, and -logError: methods defined on
// GTMLogger itself, there are also C macros that make usage of the shared
// GTMLogger instance very convenient. These macros are:
//
// GTMLoggerDebug(...)
// GTMLoggerInfo(...)
// GTMLoggerError(...)
//
// Again, a notable feature of these macros is that GTMLogDebug() calls *will be
// compiled out of non-DEBUG builds*.
//
// Standard Loggers
// ----------------
// GTMLogger has the concept of "standard loggers". A standard logger is simply
// a logger that is pre-configured with some standard/common writer, formatter,
// and filter combination. Standard loggers are created using the creation
// methods beginning with "standard". The alternative to a standard logger is a
// regular logger, which will send messages to stdout, with no special
// formatting, and no filtering.
//
// How do I use GTMLogger?
// ----------------------
// The typical way you will want to use GTMLogger is to simply use the
// GTMLogger*() macros for logging from code. That way we can easily make
// changes to the GTMLogger class and simply update the macros accordingly. Only
// your application startup code (perhaps, somewhere in main()) should use the
// GTMLogger class directly in order to configure the shared logger, which all
// of the code using the macros will be using. Again, this is just the typical
// situation.
//
// To be complete, there are cases where you may want to use GTMLogger directly,
// or even create separate GTMLogger instances for some reason. That's fine,
// too.
//
// Examples
// --------
// The following show some common GTMLogger use cases.
//
// 1. You want to log something as simply as possible. Also, this call will only
// appear in debug builds. In non-DEBUG builds it will be completely removed.
//
// GTMLoggerDebug(@"foo = %@", foo);
//
// 2. The previous example is similar to the following. The major difference is
// that the previous call (example 1) will be compiled out of Release builds
// but this statement will not be compiled out.
//
// [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo];
//
// 3. Send all logging output from the shared logger to a file. We do this by
// creating an NSFileHandle for writing associated with a file, and setting
// that file handle as the logger's writer.
//
// NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log"
// create:YES];
// [[GTMLogger sharedLogger] setWriter:f];
// GTMLoggerError(@"hi"); // This will be sent to /tmp/f.log
//
// 4. Create a new GTMLogger that will log to a file. This example differs from
// the previous one because here we create a new GTMLogger that is different
// from the shared logger.
//
// GTMLogger *logger = [GTMLogger standardLoggerWithPath:@"/tmp/temp.log"];
// [logger logInfo:@"hi temp log file"];
//
// 5. Create a logger that writes to stdout and does NOT do any formatting to
// the log message. This might be useful, for example, when writing a help
// screen for a command-line tool to standard output.
//
// GTMLogger *logger = [GTMLogger logger];
// [logger logInfo:@"%@ version 0.1 usage", progName];
//
// 6. Send log output to stdout AND to a log file. The trick here is that
// NSArrays function as composite log writers, which means when an array is
// set as the log writer, it forwards all logging messages to all of its
// contained GTMLogWriters.
//
// // Create array of GTMLogWriters
// NSArray *writers = [NSArray arrayWithObjects:
// [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES],
// [NSFileHandle fileHandleWithStandardOutput], nil];
//
// GTMLogger *logger = [GTMLogger standardLogger];
// [logger setWriter:writers];
// [logger logInfo:@"hi"]; // Output goes to stdout and /tmp/f.log
//
// For futher details on log writers, formatters, and filters, see the
// documentation below.
//
// NOTE: GTMLogger is application level logging. By default it does nothing
// with _GTMDevLog/_GTMDevAssert (see GTMDefines.h). An application can choose
// to bridge _GTMDevLog/_GTMDevAssert to GTMLogger by providing macro
// definitions in its prefix header (see GTMDefines.h for how one would do
// that).
//
@interface GTMLogger : NSObject {
@private
id<GTMLogWriter> writer_;
id<GTMLogFormatter> formatter_;
id<GTMLogFilter> filter_;
}
//
// Accessors for the shared logger instance
//
// Returns a shared/global standard GTMLogger instance. Callers should typically
// use this method to get a GTMLogger instance, unless they explicitly want
// their own instance to configure for their own needs. This is the only method
// that returns a shared instance; all the rest return new GTMLogger instances.
+ (id)sharedLogger;
// Sets the shared logger instance to |logger|. Future calls to +sharedLogger
// will return |logger| instead.
+ (void)setSharedLogger:(GTMLogger *)logger;
//
// Creation methods
//
// Returns a new autoreleased GTMLogger instance that will log to stdout, using
// the GTMLogStandardFormatter, and the GTMLogLevelFilter filter.
+ (id)standardLogger;
// Same as +standardLogger, but logs to stderr.
+ (id)standardLoggerWithStderr;
// Same as +standardLogger but levels >= kGTMLoggerLevelError are routed to
// stderr, everything else goes to stdout.
+ (id)standardLoggerWithStdoutAndStderr;
// Returns a new standard GTMLogger instance with a log writer that will
// write to the file at |path|, and will use the GTMLogStandardFormatter and
// GTMLogLevelFilter classes. If |path| does not exist, it will be created.
+ (id)standardLoggerWithPath:(NSString *)path;
// Returns an autoreleased GTMLogger instance that will use the specified
// |writer|, |formatter|, and |filter|.
+ (id)loggerWithWriter:(id<GTMLogWriter>)writer
formatter:(id<GTMLogFormatter>)formatter
filter:(id<GTMLogFilter>)filter;
// Returns an autoreleased GTMLogger instance that logs to stdout, with the
// basic formatter, and no filter. The returned logger differs from the logger
// returned by +standardLogger because this one does not do any filtering and
// does not do any special log formatting; this is the difference between a
// "regular" logger and a "standard" logger.
+ (id)logger;
// Designated initializer. This method returns a GTMLogger initialized with the
// specified |writer|, |formatter|, and |filter|. See the setter methods below
// for what values will be used if nil is passed for a parameter.
- (id)initWithWriter:(id<GTMLogWriter>)writer
formatter:(id<GTMLogFormatter>)formatter
filter:(id<GTMLogFilter>)filter;
//
// Logging methods
//
// Logs a message at the debug level (kGTMLoggerLevelDebug).
- (void)logDebug:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
// Logs a message at the info level (kGTMLoggerLevelInfo).
- (void)logInfo:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
// Logs a message at the error level (kGTMLoggerLevelError).
- (void)logError:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
// Logs a message at the assert level (kGTMLoggerLevelAssert).
- (void)logAssert:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
//
// Accessors
//
// Accessor methods for the log writer. If the log writer is set to nil,
// [NSFileHandle fileHandleWithStandardOutput] is used.
- (id<GTMLogWriter>)writer;
- (void)setWriter:(id<GTMLogWriter>)writer;
// Accessor methods for the log formatter. If the log formatter is set to nil,
// GTMLogBasicFormatter is used. This formatter will format log messages in a
// plain printf style.
- (id<GTMLogFormatter>)formatter;
- (void)setFormatter:(id<GTMLogFormatter>)formatter;
// Accessor methods for the log filter. If the log filter is set to nil,
// GTMLogNoFilter is used, which allows all log messages through.
- (id<GTMLogFilter>)filter;
- (void)setFilter:(id<GTMLogFilter>)filter;
@end // GTMLogger
// Helper functions that are used by the convenience GTMLogger*() macros that
// enable the logging of function names.
@interface GTMLogger (GTMLoggerMacroHelpers)
- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ...
NS_FORMAT_FUNCTION(2, 3);
- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ...
NS_FORMAT_FUNCTION(2, 3);
- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ...
NS_FORMAT_FUNCTION(2, 3);
- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ...
NS_FORMAT_FUNCTION(2, 3);
@end // GTMLoggerMacroHelpers
// The convenience macros are only defined if they haven't already been defined.
#ifndef GTMLoggerInfo
// Convenience macros that log to the shared GTMLogger instance. These macros
// are how users should typically log to GTMLogger. Notice that GTMLoggerDebug()
// calls will be compiled out of non-Debug builds.
#define GTMLoggerDebug(...) \
[[GTMLogger sharedLogger] logFuncDebug:__func__ msg:__VA_ARGS__]
#define GTMLoggerInfo(...) \
[[GTMLogger sharedLogger] logFuncInfo:__func__ msg:__VA_ARGS__]
#define GTMLoggerError(...) \
[[GTMLogger sharedLogger] logFuncError:__func__ msg:__VA_ARGS__]
#define GTMLoggerAssert(...) \
[[GTMLogger sharedLogger] logFuncAssert:__func__ msg:__VA_ARGS__]
// If we're not in a debug build, remove the GTMLoggerDebug statements. This
// makes calls to GTMLoggerDebug "compile out" of Release builds
#ifndef DEBUG
#undef GTMLoggerDebug
#define GTMLoggerDebug(...) do {} while(0)
#endif
#endif // !defined(GTMLoggerInfo)
// Log levels.
typedef enum {
kGTMLoggerLevelUnknown,
kGTMLoggerLevelDebug,
kGTMLoggerLevelInfo,
kGTMLoggerLevelError,
kGTMLoggerLevelAssert,
} GTMLoggerLevel;
//
// Log Writers
//
// Protocol to be implemented by a GTMLogWriter instance.
@protocol GTMLogWriter <NSObject>
// Writes the given log message to where the log writer is configured to write.
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level;
@end // GTMLogWriter
// Simple category on NSFileHandle that makes NSFileHandles valid log writers.
// This is convenient because something like, say, +fileHandleWithStandardError
// now becomes a valid log writer. Log messages are written to the file handle
// with a newline appended.
@interface NSFileHandle (GTMFileHandleLogWriter) <GTMLogWriter>
// Opens the file at |path| in append mode, and creates the file with |mode|
// if it didn't previously exist.
+ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode;
@end // NSFileHandle
// This category makes NSArray a GTMLogWriter that can be composed of other
// GTMLogWriters. This is the classic Composite GoF design pattern. When the
// GTMLogWriter -logMessage:level: message is sent to the array, the array
// forwards the message to all of its elements that implement the GTMLogWriter
// protocol.
//
// This is useful in situations where you would like to send log output to
// multiple log writers at the same time. Simply create an NSArray of the log
// writers you wish to use, then set the array as the "writer" for your
// GTMLogger instance.
@interface NSArray (GTMArrayCompositeLogWriter) <GTMLogWriter>
@end // GTMArrayCompositeLogWriter
// This category adapts the GTMLogger interface so that it can be used as a log
// writer; it's an "adapter" in the GoF Adapter pattern sense.
//
// This is useful when you want to configure a logger to log to a specific
// writer with a specific formatter and/or filter. But you want to also compose
// that with a different log writer that may have its own formatter and/or
// filter.
@interface GTMLogger (GTMLoggerLogWriter) <GTMLogWriter>
@end // GTMLoggerLogWriter
//
// Log Formatters
//
// Protocol to be implemented by a GTMLogFormatter instance.
@protocol GTMLogFormatter <NSObject>
// Returns a formatted string using the format specified in |fmt| and the va
// args specified in |args|.
- (NSString *)stringForFunc:(NSString *)func
withFormat:(NSString *)fmt
valist:(va_list)args
level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
@end // GTMLogFormatter
// A basic log formatter that formats a string the same way that NSLog (or
// printf) would. It does not do anything fancy, nor does it add any data of its
// own.
@interface GTMLogBasicFormatter : NSObject <GTMLogFormatter>
// Helper method for prettying C99 __func__ and GCC __PRETTY_FUNCTION__
- (NSString *)prettyNameForFunc:(NSString *)func;
@end // GTMLogBasicFormatter
// A log formatter that formats the log string like the basic formatter, but
// also prepends a timestamp and some basic process info to the message, as
// shown in the following sample output.
// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] log mesage here
@interface GTMLogStandardFormatter : GTMLogBasicFormatter {
@private
NSDateFormatter *dateFormatter_; // yyyy-MM-dd HH:mm:ss.SSS
NSString *pname_;
pid_t pid_;
}
@end // GTMLogStandardFormatter
//
// Log Filters
//
// Protocol to be implemented by a GTMLogFilter instance.
@protocol GTMLogFilter <NSObject>
// Returns YES if |msg| at |level| should be logged; NO otherwise.
- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level;
@end // GTMLogFilter
// A log filter that filters messages at the kGTMLoggerLevelDebug level out of
// non-debug builds. Messages at the kGTMLoggerLevelInfo level are also filtered
// out of non-debug builds unless GTMVerboseLogging is set in the environment or
// the processes's defaults. Messages at the kGTMLoggerLevelError level are
// never filtered.
@interface GTMLogLevelFilter : NSObject <GTMLogFilter> {
@private
BOOL verboseLoggingEnabled_;
NSUserDefaults *userDefaults_;
}
@end // GTMLogLevelFilter
// A simple log filter that does NOT filter anything out;
// -filterAllowsMessage:level will always return YES. This can be a convenient
// way to enable debug-level logging in release builds (if you so desire).
@interface GTMLogNoFilter : NSObject <GTMLogFilter>
@end // GTMLogNoFilter
// Base class for custom level filters. Not for direct use, use the minimum
// or maximum level subclasses below.
@interface GTMLogAllowedLevelFilter : NSObject <GTMLogFilter> {
@private
NSIndexSet *allowedLevels_;
}
@end
// A log filter that allows you to set a minimum log level. Messages below this
// level will be filtered.
@interface GTMLogMininumLevelFilter : GTMLogAllowedLevelFilter
// Designated initializer, logs at levels < |level| will be filtered.
- (id)initWithMinimumLevel:(GTMLoggerLevel)level;
@end
// A log filter that allows you to set a maximum log level. Messages whose level
// exceeds this level will be filtered. This is really only useful if you have
// a composite GTMLogger that is sending the other messages elsewhere.
@interface GTMLogMaximumLevelFilter : GTMLogAllowedLevelFilter
// Designated initializer, logs at levels > |level| will be filtered.
- (id)initWithMaximumLevel:(GTMLoggerLevel)level;
@end
// For subclasses only
@interface GTMLogger (PrivateMethods)
- (void)logInternalFunc:(const char *)func
format:(NSString *)fmt
valist:(va_list)args
level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
@end

View File

@@ -0,0 +1,648 @@
//
// GTMLogger.m
//
// Copyright 2007-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.
//
#import "GTMLogger.h"
#import <fcntl.h>
#import <unistd.h>
#import <stdlib.h>
#import <pthread.h>
#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
// Some versions of GCC (4.2 and below AFAIK) aren't great about supporting
// -Wmissing-format-attribute
// when the function is anything more complex than foo(NSString *fmt, ...).
// You see the error inside the function when you turn ... into va_args and
// attempt to call another function (like vsprintf for example).
// So we just shut off the warning for this file. We reenable it at the end.
#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
#endif // !__clang__
// Reference to the shared GTMLogger instance. This is not a singleton, it's
// just an easy reference to one shared instance.
static GTMLogger *gSharedLogger = nil;
@implementation GTMLogger
// Returns a pointer to the shared logger instance. If none exists, a standard
// logger is created and returned.
+ (id)sharedLogger {
@synchronized(self) {
if (gSharedLogger == nil) {
gSharedLogger = [[self standardLogger] retain];
}
}
return [[gSharedLogger retain] autorelease];
}
+ (void)setSharedLogger:(GTMLogger *)logger {
@synchronized(self) {
[gSharedLogger autorelease];
gSharedLogger = [logger retain];
}
}
+ (id)standardLogger {
// Don't trust NSFileHandle not to throw
@try {
id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput];
id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init]
autorelease];
id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease];
return [[[self alloc] initWithWriter:writer
formatter:fr
filter:filter] autorelease];
}
@catch (id e) {
// Ignored
}
return nil;
}
+ (id)standardLoggerWithStderr {
// Don't trust NSFileHandle not to throw
@try {
id me = [self standardLogger];
[me setWriter:[NSFileHandle fileHandleWithStandardError]];
return me;
}
@catch (id e) {
// Ignored
}
return nil;
}
+ (id)standardLoggerWithStdoutAndStderr {
// We're going to take advantage of the GTMLogger to GTMLogWriter adaptor
// and create a composite logger that an outer "standard" logger can use
// as a writer. Our inner loggers should apply no formatting since the main
// logger does that and we want the caller to be able to change formatters
// or add writers without knowing the inner structure of our composite.
// Don't trust NSFileHandle not to throw
@try {
GTMLogBasicFormatter *formatter = [[[GTMLogBasicFormatter alloc] init]
autorelease];
GTMLogger *stdoutLogger =
[self loggerWithWriter:[NSFileHandle fileHandleWithStandardOutput]
formatter:formatter
filter:[[[GTMLogMaximumLevelFilter alloc]
initWithMaximumLevel:kGTMLoggerLevelInfo]
autorelease]];
GTMLogger *stderrLogger =
[self loggerWithWriter:[NSFileHandle fileHandleWithStandardError]
formatter:formatter
filter:[[[GTMLogMininumLevelFilter alloc]
initWithMinimumLevel:kGTMLoggerLevelError]
autorelease]];
GTMLogger *compositeWriter =
[self loggerWithWriter:[NSArray arrayWithObjects:
stdoutLogger, stderrLogger, nil]
formatter:formatter
filter:[[[GTMLogNoFilter alloc] init] autorelease]];
GTMLogger *outerLogger = [self standardLogger];
[outerLogger setWriter:compositeWriter];
return outerLogger;
}
@catch (id e) {
// Ignored
}
return nil;
}
+ (id)standardLoggerWithPath:(NSString *)path {
@try {
NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644];
if (fh == nil) return nil;
id me = [self standardLogger];
[me setWriter:fh];
return me;
}
@catch (id e) {
// Ignored
}
return nil;
}
+ (id)loggerWithWriter:(id<GTMLogWriter>)writer
formatter:(id<GTMLogFormatter>)formatter
filter:(id<GTMLogFilter>)filter {
return [[[self alloc] initWithWriter:writer
formatter:formatter
filter:filter] autorelease];
}
+ (id)logger {
return [[[self alloc] init] autorelease];
}
- (id)init {
return [self initWithWriter:nil formatter:nil filter:nil];
}
- (id)initWithWriter:(id<GTMLogWriter>)writer
formatter:(id<GTMLogFormatter>)formatter
filter:(id<GTMLogFilter>)filter {
if ((self = [super init])) {
[self setWriter:writer];
[self setFormatter:formatter];
[self setFilter:filter];
}
return self;
}
- (void)dealloc {
// Unlikely, but |writer_| may be an NSFileHandle, which can throw
@try {
[formatter_ release];
[filter_ release];
[writer_ release];
}
@catch (id e) {
// Ignored
}
[super dealloc];
}
- (id<GTMLogWriter>)writer {
return [[writer_ retain] autorelease];
}
- (void)setWriter:(id<GTMLogWriter>)writer {
@synchronized(self) {
[writer_ autorelease];
writer_ = nil;
if (writer == nil) {
// Try to use stdout, but don't trust NSFileHandle
@try {
writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain];
}
@catch (id e) {
// Leave |writer_| nil
}
} else {
writer_ = [writer retain];
}
}
}
- (id<GTMLogFormatter>)formatter {
return [[formatter_ retain] autorelease];
}
- (void)setFormatter:(id<GTMLogFormatter>)formatter {
@synchronized(self) {
[formatter_ autorelease];
formatter_ = nil;
if (formatter == nil) {
@try {
formatter_ = [[GTMLogBasicFormatter alloc] init];
}
@catch (id e) {
// Leave |formatter_| nil
}
} else {
formatter_ = [formatter retain];
}
}
}
- (id<GTMLogFilter>)filter {
return [[filter_ retain] autorelease];
}
- (void)setFilter:(id<GTMLogFilter>)filter {
@synchronized(self) {
[filter_ autorelease];
filter_ = nil;
if (filter == nil) {
@try {
filter_ = [[GTMLogNoFilter alloc] init];
}
@catch (id e) {
// Leave |filter_| nil
}
} else {
filter_ = [filter retain];
}
}
}
- (void)logDebug:(NSString *)fmt, ... {
va_list args;
va_start(args, fmt);
[self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelDebug];
va_end(args);
}
- (void)logInfo:(NSString *)fmt, ... {
va_list args;
va_start(args, fmt);
[self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelInfo];
va_end(args);
}
- (void)logError:(NSString *)fmt, ... {
va_list args;
va_start(args, fmt);
[self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelError];
va_end(args);
}
- (void)logAssert:(NSString *)fmt, ... {
va_list args;
va_start(args, fmt);
[self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelAssert];
va_end(args);
}
@end // GTMLogger
@implementation GTMLogger (GTMLoggerMacroHelpers)
- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... {
va_list args;
va_start(args, fmt);
[self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelDebug];
va_end(args);
}
- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... {
va_list args;
va_start(args, fmt);
[self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelInfo];
va_end(args);
}
- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... {
va_list args;
va_start(args, fmt);
[self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelError];
va_end(args);
}
- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... {
va_list args;
va_start(args, fmt);
[self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelAssert];
va_end(args);
}
@end // GTMLoggerMacroHelpers
@implementation GTMLogger (PrivateMethods)
- (void)logInternalFunc:(const char *)func
format:(NSString *)fmt
valist:(va_list)args
level:(GTMLoggerLevel)level {
// Primary point where logging happens, logging should never throw, catch
// everything.
@try {
NSString *fname = func ? [NSString stringWithUTF8String:func] : nil;
NSString *msg = [formatter_ stringForFunc:fname
withFormat:fmt
valist:args
level:level];
if (msg && [filter_ filterAllowsMessage:msg level:level])
[writer_ logMessage:msg level:level];
}
@catch (id e) {
// Ignored
}
}
@end // PrivateMethods
@implementation NSFileHandle (GTMFileHandleLogWriter)
+ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode {
int fd = -1;
if (path) {
int flags = O_WRONLY | O_APPEND | O_CREAT;
fd = open([path fileSystemRepresentation], flags, mode);
}
if (fd == -1) return nil;
return [[[self alloc] initWithFileDescriptor:fd
closeOnDealloc:YES] autorelease];
}
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
@synchronized(self) {
// Closed pipes should not generate exceptions in our caller. Catch here
// as well [GTMLogger logInternalFunc:...] so that an exception in this
// writer does not prevent other writers from having a chance.
@try {
NSString *line = [NSString stringWithFormat:@"%@\n", msg];
[self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
}
@catch (id e) {
// Ignored
}
}
}
@end // GTMFileHandleLogWriter
@implementation NSArray (GTMArrayCompositeLogWriter)
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
@synchronized(self) {
id<GTMLogWriter> child = nil;
for (child in self) {
if ([child conformsToProtocol:@protocol(GTMLogWriter)])
[child logMessage:msg level:level];
}
}
}
@end // GTMArrayCompositeLogWriter
@implementation GTMLogger (GTMLoggerLogWriter)
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
switch (level) {
case kGTMLoggerLevelDebug:
[self logDebug:@"%@", msg];
break;
case kGTMLoggerLevelInfo:
[self logInfo:@"%@", msg];
break;
case kGTMLoggerLevelError:
[self logError:@"%@", msg];
break;
case kGTMLoggerLevelAssert:
[self logAssert:@"%@", msg];
break;
default:
// Ignore the message.
break;
}
}
@end // GTMLoggerLogWriter
@implementation GTMLogBasicFormatter
- (NSString *)prettyNameForFunc:(NSString *)func {
NSString *name = [func stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *function = @"(unknown)";
if ([name length]) {
if (// Objective C __func__ and __PRETTY_FUNCTION__
[name hasPrefix:@"-["] || [name hasPrefix:@"+["] ||
// C++ __PRETTY_FUNCTION__ and other preadorned formats
[name hasSuffix:@")"]) {
function = name;
} else {
// Assume C99 __func__
function = [NSString stringWithFormat:@"%@()", name];
}
}
return function;
}
- (NSString *)stringForFunc:(NSString *)func
withFormat:(NSString *)fmt
valist:(va_list)args
level:(GTMLoggerLevel)level {
// Performance note: We may want to do a quick check here to see if |fmt|
// contains a '%', and if not, simply return 'fmt'.
if (!(fmt && args)) return nil;
return [[[NSString alloc] initWithFormat:fmt arguments:args] autorelease];
}
@end // GTMLogBasicFormatter
@implementation GTMLogStandardFormatter
- (id)init {
if ((self = [super init])) {
dateFormatter_ = [[NSDateFormatter alloc] init];
[dateFormatter_ setFormatterBehavior:NSDateFormatterBehavior10_4];
[dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"];
pname_ = [[[NSProcessInfo processInfo] processName] copy];
pid_ = [[NSProcessInfo processInfo] processIdentifier];
if (!(dateFormatter_ && pname_)) {
[self release];
return nil;
}
}
return self;
}
- (void)dealloc {
[dateFormatter_ release];
[pname_ release];
[super dealloc];
}
- (NSString *)stringForFunc:(NSString *)func
withFormat:(NSString *)fmt
valist:(va_list)args
level:(GTMLoggerLevel)level {
NSString *tstamp = nil;
@synchronized (dateFormatter_) {
tstamp = [dateFormatter_ stringFromDate:[NSDate date]];
}
return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@",
tstamp, pname_, pid_, pthread_self(),
level, [self prettyNameForFunc:func],
// |super| has guard for nil |fmt| and |args|
[super stringForFunc:func withFormat:fmt valist:args level:level]];
}
@end // GTMLogStandardFormatter
static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging";
// Check the environment and the user preferences for the GTMVerboseLogging key
// to see if verbose logging has been enabled. The environment variable will
// override the defaults setting, so check the environment first.
// COV_NF_START
static BOOL IsVerboseLoggingEnabled(NSUserDefaults *userDefaults) {
NSString *value = [[[NSProcessInfo processInfo] environment]
objectForKey:kVerboseLoggingKey];
if (value) {
// Emulate [NSString boolValue] for pre-10.5
value = [value stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([[value uppercaseString] hasPrefix:@"Y"] ||
[[value uppercaseString] hasPrefix:@"T"] ||
[value intValue]) {
return YES;
} else {
return NO;
}
}
return [userDefaults boolForKey:kVerboseLoggingKey];
}
// COV_NF_END
@implementation GTMLogLevelFilter
- (id)init {
self = [super init];
if (self) {
// Keep a reference to standardUserDefaults, avoiding a crash if client code calls
// "NSUserDefaults resetStandardUserDefaults" which releases it from memory. We are still
// notified of changes through our instance. Note: resetStandardUserDefaults does not actually
// clear settings:
// https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/index.html#//apple_ref/occ/clm/NSUserDefaults/resetStandardUserDefaults
// and so should only be called in test code if necessary.
userDefaults_ = [[NSUserDefaults standardUserDefaults] retain];
[userDefaults_ addObserver:self
forKeyPath:kVerboseLoggingKey
options:NSKeyValueObservingOptionNew
context:nil];
verboseLoggingEnabled_ = IsVerboseLoggingEnabled(userDefaults_);
}
return self;
}
- (void)dealloc {
[userDefaults_ removeObserver:self forKeyPath:kVerboseLoggingKey];
[userDefaults_ release];
[super dealloc];
}
// In DEBUG builds, log everything. If we're not in a debug build we'll assume
// that we're in a Release build.
- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
#if defined(DEBUG) && DEBUG
return YES;
#endif
BOOL allow = YES;
switch (level) {
case kGTMLoggerLevelDebug:
allow = NO;
break;
case kGTMLoggerLevelInfo:
allow = verboseLoggingEnabled_;
break;
case kGTMLoggerLevelError:
allow = YES;
break;
case kGTMLoggerLevelAssert:
allow = YES;
break;
default:
allow = YES;
break;
}
return allow;
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if([keyPath isEqual:kVerboseLoggingKey]) {
verboseLoggingEnabled_ = IsVerboseLoggingEnabled(userDefaults_);
}
}
@end // GTMLogLevelFilter
@implementation GTMLogNoFilter
- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
return YES; // Allow everything through
}
@end // GTMLogNoFilter
@implementation GTMLogAllowedLevelFilter
// Private designated initializer
- (id)initWithAllowedLevels:(NSIndexSet *)levels {
self = [super init];
if (self != nil) {
allowedLevels_ = [levels retain];
// Cap min/max level
if (!allowedLevels_ ||
// NSIndexSet is unsigned so only check the high bound, but need to
// check both first and last index because NSIndexSet appears to allow
// wraparound.
([allowedLevels_ firstIndex] > kGTMLoggerLevelAssert) ||
([allowedLevels_ lastIndex] > kGTMLoggerLevelAssert)) {
[self release];
return nil;
}
}
return self;
}
- (id)init {
// Allow all levels in default init
return [self initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
NSMakeRange(kGTMLoggerLevelUnknown,
(kGTMLoggerLevelAssert - kGTMLoggerLevelUnknown + 1))]];
}
- (void)dealloc {
[allowedLevels_ release];
[super dealloc];
}
- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
return [allowedLevels_ containsIndex:level];
}
@end // GTMLogAllowedLevelFilter
@implementation GTMLogMininumLevelFilter
- (id)initWithMinimumLevel:(GTMLoggerLevel)level {
return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
NSMakeRange(level,
(kGTMLoggerLevelAssert - level + 1))]];
}
@end // GTMLogMininumLevelFilter
@implementation GTMLogMaximumLevelFilter
- (id)initWithMaximumLevel:(GTMLoggerLevel)level {
return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
NSMakeRange(kGTMLoggerLevelUnknown, level + 1)]];
}
@end // GTMLogMaximumLevelFilter
#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
// See comment at top of file.
#pragma GCC diagnostic error "-Wmissing-format-attribute"
#endif // !__clang__

View File

@@ -0,0 +1,199 @@
//
// GTMNSData+zlib.h
//
// Copyright 2007-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.
//
#import <Foundation/Foundation.h>
#import "GTMDefines.h"
/// Helpers for dealing w/ zlib inflate/deflate calls.
@interface NSData (GTMZLibAdditions)
// NOTE: For 64bit, none of these apis handle input sizes >32bits, they will
// return nil when given such data. To handle data of that size you really
// should be streaming it rather then doing it all in memory.
#pragma mark Gzip Compression
/// Return an autoreleased NSData w/ the result of gzipping the bytes.
//
// Uses the default compression level.
+ (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
length:(NSUInteger)length;
+ (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
length:(NSUInteger)length
error:(NSError **)error;
/// Return an autoreleased NSData w/ the result of gzipping the payload of |data|.
//
// Uses the default compression level.
+ (NSData *)gtm_dataByGzippingData:(NSData *)data __attribute__((deprecated("Use error variant")));
+ (NSData *)gtm_dataByGzippingData:(NSData *)data
error:(NSError **)error;
/// Return an autoreleased NSData w/ the result of gzipping the bytes using |level| compression level.
//
// |level| can be 1-9, any other values will be clipped to that range.
+ (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
length:(NSUInteger)length
compressionLevel:(int)level __attribute__((deprecated("Use error variant")));
+ (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
length:(NSUInteger)length
compressionLevel:(int)level
error:(NSError **)error;
/// Return an autoreleased NSData w/ the result of gzipping the payload of |data| using |level| compression level.
+ (NSData *)gtm_dataByGzippingData:(NSData *)data
compressionLevel:(int)level __attribute__((deprecated("Use error variant")));
+ (NSData *)gtm_dataByGzippingData:(NSData *)data
compressionLevel:(int)level
error:(NSError **)error;
#pragma mark Zlib "Stream" Compression
// NOTE: deflate is *NOT* gzip. deflate is a "zlib" stream. pick which one
// you really want to create. (the inflate api will handle either)
/// Return an autoreleased NSData w/ the result of deflating the bytes.
//
// Uses the default compression level.
+ (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
length:(NSUInteger)length __attribute__((deprecated("Use error variant")));
+ (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
length:(NSUInteger)length
error:(NSError **)error;
/// Return an autoreleased NSData w/ the result of deflating the payload of |data|.
//
// Uses the default compression level.
+ (NSData *)gtm_dataByDeflatingData:(NSData *)data __attribute__((deprecated("Use error variant")));
+ (NSData *)gtm_dataByDeflatingData:(NSData *)data
error:(NSError **)error;
/// Return an autoreleased NSData w/ the result of deflating the bytes using |level| compression level.
//
// |level| can be 1-9, any other values will be clipped to that range.
+ (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
length:(NSUInteger)length
compressionLevel:(int)level __attribute__((deprecated("Use error variant")));
+ (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
length:(NSUInteger)length
compressionLevel:(int)level
error:(NSError **)error;
/// Return an autoreleased NSData w/ the result of deflating the payload of |data| using |level| compression level.
+ (NSData *)gtm_dataByDeflatingData:(NSData *)data
compressionLevel:(int)level __attribute__((deprecated("Use error variant")));
+ (NSData *)gtm_dataByDeflatingData:(NSData *)data
compressionLevel:(int)level
error:(NSError **)error;
#pragma mark Uncompress of Gzip or Zlib
/// Return an autoreleased NSData w/ the result of decompressing the bytes.
//
// The bytes to decompress can be zlib or gzip payloads.
+ (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
length:(NSUInteger)length __attribute__((deprecated("Use error variant")));
+ (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
length:(NSUInteger)length
error:(NSError **)error;
/// Return an autoreleased NSData w/ the result of decompressing the payload of |data|.
//
// The data to decompress can be zlib or gzip payloads.
+ (NSData *)gtm_dataByInflatingData:(NSData *)data __attribute__((deprecated("Use error variant")));
+ (NSData *)gtm_dataByInflatingData:(NSData *)data
error:(NSError **)error;
#pragma mark "Raw" Compression Support
// NOTE: raw deflate is *NOT* gzip or deflate. it does not include a header
// of any form and should only be used within streams here an external crc/etc.
// is done to validate the data. The RawInflate apis can be used on data
// processed like this.
/// Return an autoreleased NSData w/ the result of *raw* deflating the bytes.
//
// Uses the default compression level.
// *No* header is added to the resulting data.
+ (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
length:(NSUInteger)length __attribute__((deprecated("Use error variant")));
+ (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
length:(NSUInteger)length
error:(NSError **)error;
/// Return an autoreleased NSData w/ the result of *raw* deflating the payload of |data|.
//
// Uses the default compression level.
// *No* header is added to the resulting data.
+ (NSData *)gtm_dataByRawDeflatingData:(NSData *)data __attribute__((deprecated("Use error variant")));
+ (NSData *)gtm_dataByRawDeflatingData:(NSData *)data
error:(NSError **)error;
/// Return an autoreleased NSData w/ the result of *raw* deflating the bytes using |level| compression level.
//
// |level| can be 1-9, any other values will be clipped to that range.
// *No* header is added to the resulting data.
+ (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
length:(NSUInteger)length
compressionLevel:(int)level __attribute__((deprecated("Use error variant")));
+ (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
length:(NSUInteger)length
compressionLevel:(int)level
error:(NSError **)error;
/// Return an autoreleased NSData w/ the result of *raw* deflating the payload of |data| using |level| compression level.
// *No* header is added to the resulting data.
+ (NSData *)gtm_dataByRawDeflatingData:(NSData *)data
compressionLevel:(int)level __attribute__((deprecated("Use error variant")));
+ (NSData *)gtm_dataByRawDeflatingData:(NSData *)data
compressionLevel:(int)level
error:(NSError **)error;
/// Return an autoreleased NSData w/ the result of *raw* decompressing the bytes.
//
// The data to decompress, it should *not* have any header (zlib nor gzip).
+ (NSData *)gtm_dataByRawInflatingBytes:(const void *)bytes
length:(NSUInteger)length __attribute__((deprecated("Use error variant")));
+ (NSData *)gtm_dataByRawInflatingBytes:(const void *)bytes
length:(NSUInteger)length
error:(NSError **)error;
/// Return an autoreleased NSData w/ the result of *raw* decompressing the payload of |data|.
//
// The data to decompress, it should *not* have any header (zlib nor gzip).
+ (NSData *)gtm_dataByRawInflatingData:(NSData *)data __attribute__((deprecated("Use error variant")));
+ (NSData *)gtm_dataByRawInflatingData:(NSData *)data
error:(NSError **)error;
@end
FOUNDATION_EXPORT NSString *const GTMNSDataZlibErrorDomain;
FOUNDATION_EXPORT NSString *const GTMNSDataZlibErrorKey; // NSNumber
FOUNDATION_EXPORT NSString *const GTMNSDataZlibRemainingBytesKey; // NSNumber
typedef NS_ENUM(NSInteger, GTMNSDataZlibError) {
GTMNSDataZlibErrorGreaterThan32BitsToCompress = 1024,
// An internal zlib error.
// GTMNSDataZlibErrorKey will contain the error value.
// NSLocalizedDescriptionKey may contain an error string from zlib.
// Look in zlib.h for list of errors.
GTMNSDataZlibErrorInternal,
// There was left over data in the buffer that was not used.
// GTMNSDataZlibRemainingBytesKey will contain number of remaining bytes.
GTMNSDataZlibErrorDataRemaining
};

View File

@@ -0,0 +1,531 @@
//
// GTMNSData+zlib.m
//
// Copyright 2007-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.
//
#import "GTMNSData+zlib.h"
#import <zlib.h>
#import "GTMDefines.h"
#define kChunkSize 1024
NSString *const GTMNSDataZlibErrorDomain = @"com.google.GTMNSDataZlibErrorDomain";
NSString *const GTMNSDataZlibErrorKey = @"GTMNSDataZlibErrorKey";
NSString *const GTMNSDataZlibRemainingBytesKey = @"GTMNSDataZlibRemainingBytesKey";
typedef enum {
CompressionModeZlib,
CompressionModeGzip,
CompressionModeRaw,
} CompressionMode;
@interface NSData (GTMZlibAdditionsPrivate)
+ (NSData *)gtm_dataByCompressingBytes:(const void *)bytes
length:(NSUInteger)length
compressionLevel:(int)level
mode:(CompressionMode)mode
error:(NSError **)error;
+ (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
length:(NSUInteger)length
isRawData:(BOOL)isRawData
error:(NSError **)error;
@end
@implementation NSData (GTMZlibAdditionsPrivate)
+ (NSData *)gtm_dataByCompressingBytes:(const void *)bytes
length:(NSUInteger)length
compressionLevel:(int)level
mode:(CompressionMode)mode
error:(NSError **)error {
if (!bytes || !length) {
return nil;
}
#if defined(__LP64__) && __LP64__
// Don't support > 32bit length for 64 bit, see note in header.
if (length > UINT_MAX) {
if (error) {
*error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
code:GTMNSDataZlibErrorGreaterThan32BitsToCompress
userInfo:nil];
}
return nil;
}
#endif
if (level == Z_DEFAULT_COMPRESSION) {
// the default value is actually outside the range, so we have to let it
// through specifically.
} else if (level < Z_BEST_SPEED) {
level = Z_BEST_SPEED;
} else if (level > Z_BEST_COMPRESSION) {
level = Z_BEST_COMPRESSION;
}
z_stream strm;
bzero(&strm, sizeof(z_stream));
int memLevel = 8; // the default
int windowBits = 15; // the default
switch (mode) {
case CompressionModeZlib:
// nothing to do
break;
case CompressionModeGzip:
windowBits += 16; // enable gzip header instead of zlib header
break;
case CompressionModeRaw:
windowBits *= -1; // Negative to mean no header.
break;
}
int retCode;
if ((retCode = deflateInit2(&strm, level, Z_DEFLATED, windowBits,
memLevel, Z_DEFAULT_STRATEGY)) != Z_OK) {
// COV_NF_START - no real way to force this in a unittest (we guard all args)
if (error) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
forKey:GTMNSDataZlibErrorKey];
*error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
code:GTMNSDataZlibErrorInternal
userInfo:userInfo];
}
return nil;
// COV_NF_END
}
// hint the size at 1/4 the input size
NSMutableData *result = [NSMutableData dataWithCapacity:(length/4)];
unsigned char output[kChunkSize];
// setup the input
strm.avail_in = (unsigned int)length;
strm.next_in = (unsigned char*)bytes;
// loop to collect the data
do {
// update what we're passing in
strm.avail_out = kChunkSize;
strm.next_out = output;
retCode = deflate(&strm, Z_FINISH);
if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
// COV_NF_START - no real way to force this in a unittest
// (in inflate, we can feed bogus/truncated data to test, but an error
// here would be some internal issue w/in zlib, and there isn't any real
// way to test it)
if (error) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
forKey:GTMNSDataZlibErrorKey];
*error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
code:GTMNSDataZlibErrorInternal
userInfo:userInfo];
}
deflateEnd(&strm);
return nil;
// COV_NF_END
}
// collect what we got
unsigned gotBack = kChunkSize - strm.avail_out;
if (gotBack > 0) {
[result appendBytes:output length:gotBack];
}
} while (retCode == Z_OK);
// if the loop exits, we used all input and the stream ended
_GTMDevAssert(strm.avail_in == 0,
@"thought we finished deflate w/o using all input, %u bytes left",
strm.avail_in);
_GTMDevAssert(retCode == Z_STREAM_END,
@"thought we finished deflate w/o getting a result of stream end, code %d",
retCode);
// clean up
deflateEnd(&strm);
return result;
} // gtm_dataByCompressingBytes:length:compressionLevel:useGzip:
+ (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
length:(NSUInteger)length
isRawData:(BOOL)isRawData
error:(NSError **)error {
if (!bytes || !length) {
return nil;
}
#if defined(__LP64__) && __LP64__
// Don't support > 32bit length for 64 bit, see note in header.
if (length > UINT_MAX) {
return nil;
}
#endif
z_stream strm;
bzero(&strm, sizeof(z_stream));
// setup the input
strm.avail_in = (unsigned int)length;
strm.next_in = (unsigned char*)bytes;
int windowBits = 15; // 15 to enable any window size
if (isRawData) {
windowBits *= -1; // make it negative to signal no header.
} else {
windowBits += 32; // and +32 to enable zlib or gzip header detection.
}
int retCode;
if ((retCode = inflateInit2(&strm, windowBits)) != Z_OK) {
// COV_NF_START - no real way to force this in a unittest (we guard all args)
if (error) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
forKey:GTMNSDataZlibErrorKey];
*error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
code:GTMNSDataZlibErrorInternal
userInfo:userInfo];
}
return nil;
// COV_NF_END
}
// hint the size at 4x the input size
NSMutableData *result = [NSMutableData dataWithCapacity:(length*4)];
unsigned char output[kChunkSize];
// loop to collect the data
do {
// update what we're passing in
strm.avail_out = kChunkSize;
strm.next_out = output;
retCode = inflate(&strm, Z_NO_FLUSH);
if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
if (error) {
NSMutableDictionary *userInfo =
[NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
forKey:GTMNSDataZlibErrorKey];
if (strm.msg) {
NSString *message = [NSString stringWithUTF8String:strm.msg];
if (message) {
[userInfo setObject:message forKey:NSLocalizedDescriptionKey];
}
}
*error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
code:GTMNSDataZlibErrorInternal
userInfo:userInfo];
}
inflateEnd(&strm);
return nil;
}
// collect what we got
unsigned gotBack = kChunkSize - strm.avail_out;
if (gotBack > 0) {
[result appendBytes:output length:gotBack];
}
} while (retCode == Z_OK);
// make sure there wasn't more data tacked onto the end of a valid compressed
// stream.
if (strm.avail_in != 0) {
if (error) {
NSDictionary *userInfo =
[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:strm.avail_in]
forKey:GTMNSDataZlibRemainingBytesKey];
*error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
code:GTMNSDataZlibErrorDataRemaining
userInfo:userInfo];
}
result = nil;
}
// the only way out of the loop was by hitting the end of the stream
_GTMDevAssert(retCode == Z_STREAM_END,
@"thought we finished inflate w/o getting a result of stream end, code %d",
retCode);
// clean up
inflateEnd(&strm);
return result;
} // gtm_dataByInflatingBytes:length:windowBits:
@end
@implementation NSData (GTMZLibAdditions)
+ (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
length:(NSUInteger)length {
return [self gtm_dataByGzippingBytes:bytes length:length error:NULL];
} // gtm_dataByGzippingBytes:length:
+ (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
length:(NSUInteger)length
error:(NSError **)error {
return [self gtm_dataByCompressingBytes:bytes
length:length
compressionLevel:Z_DEFAULT_COMPRESSION
mode:CompressionModeGzip
error:error];
} // gtm_dataByGzippingBytes:length:error:
+ (NSData *)gtm_dataByGzippingData:(NSData *)data {
return [self gtm_dataByGzippingData:data error:NULL];
} // gtm_dataByGzippingData:
+ (NSData *)gtm_dataByGzippingData:(NSData *)data error:(NSError **)error {
return [self gtm_dataByCompressingBytes:[data bytes]
length:[data length]
compressionLevel:Z_DEFAULT_COMPRESSION
mode:CompressionModeGzip
error:error];
} // gtm_dataByGzippingData:error:
+ (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
length:(NSUInteger)length
compressionLevel:(int)level {
return [self gtm_dataByGzippingBytes:bytes
length:length
compressionLevel:level
error:NULL];
} // gtm_dataByGzippingBytes:length:level:
+ (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
length:(NSUInteger)length
compressionLevel:(int)level
error:(NSError **)error{
return [self gtm_dataByCompressingBytes:bytes
length:length
compressionLevel:level
mode:CompressionModeGzip
error:error];
} // gtm_dataByGzippingBytes:length:level:error
+ (NSData *)gtm_dataByGzippingData:(NSData *)data
compressionLevel:(int)level {
return [self gtm_dataByGzippingData:data
compressionLevel:level
error:NULL];
} // gtm_dataByGzippingData:level:
+ (NSData *)gtm_dataByGzippingData:(NSData *)data
compressionLevel:(int)level
error:(NSError **)error{
return [self gtm_dataByCompressingBytes:[data bytes]
length:[data length]
compressionLevel:level
mode:CompressionModeGzip
error:error];
} // gtm_dataByGzippingData:level:error
#pragma mark -
+ (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
length:(NSUInteger)length {
return [self gtm_dataByDeflatingBytes:bytes
length:length
error:NULL];
} // gtm_dataByDeflatingBytes:length:
+ (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
length:(NSUInteger)length
error:(NSError **)error{
return [self gtm_dataByCompressingBytes:bytes
length:length
compressionLevel:Z_DEFAULT_COMPRESSION
mode:CompressionModeZlib
error:error];
} // gtm_dataByDeflatingBytes:length:error
+ (NSData *)gtm_dataByDeflatingData:(NSData *)data {
return [self gtm_dataByDeflatingData:data error:NULL];
} // gtm_dataByDeflatingData:
+ (NSData *)gtm_dataByDeflatingData:(NSData *)data error:(NSError **)error {
return [self gtm_dataByCompressingBytes:[data bytes]
length:[data length]
compressionLevel:Z_DEFAULT_COMPRESSION
mode:CompressionModeZlib
error:error];
} // gtm_dataByDeflatingData:
+ (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
length:(NSUInteger)length
compressionLevel:(int)level {
return [self gtm_dataByDeflatingBytes:bytes
length:length
compressionLevel:level
error:NULL];
} // gtm_dataByDeflatingBytes:length:level:
+ (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
length:(NSUInteger)length
compressionLevel:(int)level
error:(NSError **)error {
return [self gtm_dataByCompressingBytes:bytes
length:length
compressionLevel:level
mode:CompressionModeZlib
error:error];
} // gtm_dataByDeflatingBytes:length:level:error:
+ (NSData *)gtm_dataByDeflatingData:(NSData *)data
compressionLevel:(int)level {
return [self gtm_dataByDeflatingData:data
compressionLevel:level
error:NULL];
} // gtm_dataByDeflatingData:level:
+ (NSData *)gtm_dataByDeflatingData:(NSData *)data
compressionLevel:(int)level
error:(NSError **)error {
return [self gtm_dataByCompressingBytes:[data bytes]
length:[data length]
compressionLevel:level
mode:CompressionModeZlib
error:error];
} // gtm_dataByDeflatingData:level:error:
#pragma mark -
+ (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
length:(NSUInteger)length {
return [self gtm_dataByInflatingBytes:bytes
length:length
error:NULL];
} // gtm_dataByInflatingBytes:length:
+ (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
length:(NSUInteger)length
error:(NSError **)error {
return [self gtm_dataByInflatingBytes:bytes
length:length
isRawData:NO
error:error];
} // gtm_dataByInflatingBytes:length:error:
+ (NSData *)gtm_dataByInflatingData:(NSData *)data {
return [self gtm_dataByInflatingData:data error:NULL];
} // gtm_dataByInflatingData:
+ (NSData *)gtm_dataByInflatingData:(NSData *)data
error:(NSError **)error {
return [self gtm_dataByInflatingBytes:[data bytes]
length:[data length]
isRawData:NO
error:error];
} // gtm_dataByInflatingData:
#pragma mark -
+ (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
length:(NSUInteger)length {
return [self gtm_dataByRawDeflatingBytes:(const void *)bytes
length:(NSUInteger)length
error:NULL];
} // gtm_dataByRawDeflatingBytes:length:
+ (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
length:(NSUInteger)length
error:(NSError **)error {
return [self gtm_dataByCompressingBytes:bytes
length:length
compressionLevel:Z_DEFAULT_COMPRESSION
mode:CompressionModeRaw
error:error];
} // gtm_dataByRawDeflatingBytes:length:error:
+ (NSData *)gtm_dataByRawDeflatingData:(NSData *)data {
return [self gtm_dataByRawDeflatingData:data error:NULL];
} // gtm_dataByRawDeflatingData:
+ (NSData *)gtm_dataByRawDeflatingData:(NSData *)data error:(NSError **)error {
return [self gtm_dataByCompressingBytes:[data bytes]
length:[data length]
compressionLevel:Z_DEFAULT_COMPRESSION
mode:CompressionModeRaw
error:error];
} // gtm_dataByRawDeflatingData:error:
+ (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
length:(NSUInteger)length
compressionLevel:(int)level {
return [self gtm_dataByRawDeflatingBytes:bytes
length:length
compressionLevel:level
error:NULL];
} // gtm_dataByRawDeflatingBytes:length:compressionLevel:
+ (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
length:(NSUInteger)length
compressionLevel:(int)level
error:(NSError **)error{
return [self gtm_dataByCompressingBytes:bytes
length:length
compressionLevel:level
mode:CompressionModeRaw
error:error];
} // gtm_dataByRawDeflatingBytes:length:compressionLevel:error:
+ (NSData *)gtm_dataByRawDeflatingData:(NSData *)data
compressionLevel:(int)level {
return [self gtm_dataByRawDeflatingData:data
compressionLevel:level
error:NULL];
} // gtm_dataByRawDeflatingData:compressionLevel:
+ (NSData *)gtm_dataByRawDeflatingData:(NSData *)data
compressionLevel:(int)level
error:(NSError **)error {
return [self gtm_dataByCompressingBytes:[data bytes]
length:[data length]
compressionLevel:level
mode:CompressionModeRaw
error:error];
} // gtm_dataByRawDeflatingData:compressionLevel:error:
+ (NSData *)gtm_dataByRawInflatingBytes:(const void *)bytes
length:(NSUInteger)length {
return [self gtm_dataByInflatingBytes:bytes
length:length
error:NULL];
} // gtm_dataByRawInflatingBytes:length:
+ (NSData *)gtm_dataByRawInflatingBytes:(const void *)bytes
length:(NSUInteger)length
error:(NSError **)error{
return [self gtm_dataByInflatingBytes:bytes
length:length
isRawData:YES
error:error];
} // gtm_dataByRawInflatingBytes:length:error:
+ (NSData *)gtm_dataByRawInflatingData:(NSData *)data {
return [self gtm_dataByRawInflatingData:data
error:NULL];
} // gtm_dataByRawInflatingData:
+ (NSData *)gtm_dataByRawInflatingData:(NSData *)data
error:(NSError **)error {
return [self gtm_dataByInflatingBytes:[data bytes]
length:[data length]
isRawData:YES
error:error];
} // gtm_dataByRawInflatingData:error:
@end

View File

@@ -0,0 +1,40 @@
//
// GTMNSDictionary+URLArguments.h
//
// Copyright 2006-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.
//
#import <Foundation/Foundation.h>
/// Utility for building a URL or POST argument string.
@interface NSDictionary (GTMNSDictionaryURLArgumentsAdditions)
/// Returns a dictionary of the decoded key-value pairs in a http arguments
/// string of the form key1=value1&key2=value2&...&keyN=valueN.
/// Keys and values will be unescaped automatically.
/// Only the first value for a repeated key is returned.
///
/// NOTE: Apps targeting iOS 8 or OS X 10.10 and later should use
/// NSURLComponents and NSURLQueryItem to create URLs with
/// query arguments instead of using these category methods.
+ (NSDictionary *)gtm_dictionaryWithHttpArgumentsString:(NSString *)argString;
/// Gets a string representation of the dictionary in the form
/// key1=value1&key2=value2&...&keyN=valueN, suitable for use as either
/// URL arguments (after a '?') or POST body. Keys and values will be escaped
/// automatically, so should be unescaped in the dictionary.
- (NSString *)gtm_httpArgumentsString;
@end

View File

@@ -0,0 +1,77 @@
//
// GTMNSDictionary+URLArguments.m
//
// Copyright 2006-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.
//
#import "GTMNSDictionary+URLArguments.h"
#import "GTMNSString+URLArguments.h"
#import "GTMMethodCheck.h"
#import "GTMDefines.h"
// Export a nonsense symbol to suppress a libtool warning when this is linked alone in a static lib.
__attribute__((visibility("default")))
char GTMNSDictionaryURLArgumentsExportToSuppressLibToolWarning = 0;
@implementation NSDictionary (GTMNSDictionaryURLArgumentsAdditions)
GTM_METHOD_CHECK(NSString, gtm_stringByEscapingForURLArgument);
GTM_METHOD_CHECK(NSString, gtm_stringByUnescapingFromURLArgument);
+ (NSDictionary *)gtm_dictionaryWithHttpArgumentsString:(NSString *)argString {
NSMutableDictionary* ret = [NSMutableDictionary dictionary];
NSArray* components = [argString componentsSeparatedByString:@"&"];
NSString* component;
// Use reverse order so that the first occurrence of a key replaces
// those subsequent.
for (component in [components reverseObjectEnumerator]) {
if ([component length] == 0)
continue;
NSRange pos = [component rangeOfString:@"="];
NSString *key;
NSString *val;
if (pos.location == NSNotFound) {
key = [component gtm_stringByUnescapingFromURLArgument];
val = @"";
} else {
key = [[component substringToIndex:pos.location]
gtm_stringByUnescapingFromURLArgument];
val = [[component substringFromIndex:pos.location + pos.length]
gtm_stringByUnescapingFromURLArgument];
}
// gtm_stringByUnescapingFromURLArgument returns nil on invalid UTF8
// and NSMutableDictionary raises an exception when passed nil values.
if (!key) key = @"";
if (!val) val = @"";
[ret setObject:val forKey:key];
}
return ret;
}
- (NSString *)gtm_httpArgumentsString {
NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:[self count]];
NSString* key;
for (key in self) {
[arguments addObject:[NSString stringWithFormat:@"%@=%@",
[key gtm_stringByEscapingForURLArgument],
[[[self objectForKey:key] description] gtm_stringByEscapingForURLArgument]]];
}
return [arguments componentsJoinedByString:@"&"];
}
@end

View File

@@ -0,0 +1,45 @@
//
// GTMNSString+URLArguments.h
//
// Copyright 2006-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.
//
#import <Foundation/Foundation.h>
/// Utilities for encoding and decoding URL arguments.
@interface NSString (GTMNSStringURLArgumentsAdditions)
/// Returns a string that is escaped properly to be a URL argument.
///
/// This differs from stringByAddingPercentEscapesUsingEncoding: in that it
/// will escape all the reserved characters (per RFC 3986
/// <http://www.ietf.org/rfc/rfc3986.txt>) which
/// stringByAddingPercentEscapesUsingEncoding would leave.
///
/// This will also escape '%', so this should not be used on a string that has
/// already been escaped unless double-escaping is the desired result.
///
/// NOTE: Apps targeting iOS 8 or OS X 10.10 and later should use
/// NSURLComponents and NSURLQueryItem to create properly-escaped
/// URLs instead of using these category methods.
- (NSString*)gtm_stringByEscapingForURLArgument;
/// Returns the unescaped version of a URL argument
///
/// This has the same behavior as stringByReplacingPercentEscapesUsingEncoding:,
/// except that it will also convert '+' to space.
- (NSString*)gtm_stringByUnescapingFromURLArgument;
@end

View File

@@ -0,0 +1,48 @@
//
// GTMNSString+URLArguments.m
//
// Copyright 2006-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.
//
#import "GTMNSString+URLArguments.h"
@implementation NSString (GTMNSStringURLArgumentsAdditions)
- (NSString *)gtm_stringByEscapingForURLArgument {
// Encode all the reserved characters, per RFC 3986
// (<http://www.ietf.org/rfc/rfc3986.txt>)
CFStringRef escaped =
CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)self,
NULL,
(CFStringRef)@"!*'();:@&=+$,/?%#[]",
kCFStringEncodingUTF8);
#if defined(__has_feature) && __has_feature(objc_arc)
return CFBridgingRelease(escaped);
#else
return [(NSString *)escaped autorelease];
#endif
}
- (NSString *)gtm_stringByUnescapingFromURLArgument {
NSMutableString *resultString = [NSMutableString stringWithString:self];
[resultString replaceOccurrencesOfString:@"+"
withString:@" "
options:NSLiteralSearch
range:NSMakeRange(0, [resultString length])];
return [resultString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}
@end

View File

@@ -0,0 +1,392 @@
//
// 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 <AvailabilityMacros.h>
#include <TargetConditionals.h>
#ifdef __OBJC__
#include <Foundation/NSObjCRuntime.h>
#endif // __OBJC__
#if TARGET_OS_IPHONE
#include <Availability.h>
#endif // TARGET_OS_IPHONE
// ----------------------------------------------------------------------------
// 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
// Ensure __has_feature and __has_extension are safe to use.
// See http://clang-analyzer.llvm.org/annotations.html
#ifndef __has_feature // Optional.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif
#ifndef __has_extension
#define __has_extension __has_feature // Compatibility with pre-3.0 compilers.
#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 condition 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 *) \
[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \
file:(NSString *)[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
//
// Note: Software for current compilers should just use _Static_assert directly
// instead of this macro.
//
// _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
#if __has_feature(c_static_assert) || __has_extension(c_static_assert)
#define _GTMCompileAssert(test, msg) _Static_assert((test), #msg)
#else
// Pre-Xcode 7 support.
//
// 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 // __has_feature(c_static_assert) || __has_extension(c_static_assert)
#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_DEVICE 0
#define GTM_IPHONE_SIMULATOR 1
#else
#define GTM_IPHONE_DEVICE 1
#define GTM_IPHONE_SIMULATOR 0
#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_USING_XCTEST
#define GTM_USING_XCTEST 0
#endif
#define GTM_MACOS_SDK 0
#else
// For MacOS specific stuff
#define GTM_MACOS_SDK 1
#define GTM_IPHONE_SDK 0
#define GTM_IPHONE_SIMULATOR 0
#define GTM_IPHONE_DEVICE 0
#ifndef GTM_USING_XCTEST
#define GTM_USING_XCTEST 0
#endif
#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
// GC was dropped by Apple, define the old constant incase anyone still keys
// off of it.
#ifndef GTM_SUPPORT_GC
#define GTM_SUPPORT_GC 0
#endif
// Some support for advanced clang static analysis functionality
#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
#ifndef GTM_NONNULL
#if defined(__has_attribute)
#if __has_attribute(nonnull)
#define GTM_NONNULL(x) __attribute__((nonnull x))
#else
#define GTM_NONNULL(x)
#endif
#else
#define GTM_NONNULL(x)
#endif
#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
#ifndef GTMCFAutorelease
// GTMCFAutorelease returns an id. In contrast, Apple's CFAutorelease returns
// a CFTypeRef.
#if __has_feature(objc_arc)
#define GTMCFAutorelease(x) CFBridgingRelease(x)
#else
#define GTMCFAutorelease(x) ([(id)x autorelease])
#endif
#endif
#ifdef __OBJC__
// 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
// ============================================================================
// 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
#ifndef GTM_WEAK
#if __has_feature(objc_arc_weak)
// With ARC enabled, __weak means a reference that isn't implicitly
// retained. __weak objects are accessed through runtime functions, so
// they are zeroed out, but this requires OS X 10.7+.
// At clang r251041+, ARC-style zeroing weak references even work in
// non-ARC mode.
#define GTM_WEAK __weak
#elif __has_feature(objc_arc)
// ARC, but targeting 10.6 or older, where zeroing weak references don't
// exist.
#define GTM_WEAK __unsafe_unretained
#else
// With manual reference counting, __weak used to be silently ignored.
// clang r251041 gives it the ARC semantics instead. This means they
// now require a deployment target of 10.7, while some clients of GTM
// still target 10.6. In these cases, expand to __unsafe_unretained instead
#define GTM_WEAK
#endif
#endif
#endif // __OBJC__

202
My Mind/Pods/GoogleToolboxForMac/LICENSE generated Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -0,0 +1,15 @@
# GTM: Google Toolbox for Mac #
**Project site** <https://github.com/google/google-toolbox-for-mac><br>
**Discussion group** <http://groups.google.com/group/google-toolbox-for-mac>
# Google Toolbox for Mac #
A collection of source from different Google projects that may be of use to
developers working other iOS or OS X projects.
If you find a problem/bug or want a new feature to be included in the Google
Toolbox for Mac, please join the
[discussion group](http://groups.google.com/group/google-toolbox-for-mac)
or submit an
[issue](https://github.com/google/google-toolbox-for-mac/issues).