--- /dev/null
+#ifndef __TRANSFORM__
+#define __TRANSFORM__
+
+#include <CoreFoundation/CFError.h>
+#include "CoreFoundationBasics.h"
+#include "SecTransform.h"
+#include "SecCustomTransform.h"
+#include <dispatch/dispatch.h>
+#include "misc.h"
+
+// Since we are doing everything in CF, we just define an
+// attribute as a CFDictionary containing a value and
+// a CFArray of objects which need notification when that
+// value changes
+
+// defines for transform externalization
+#define EXTERN_TRANSFORM_TRANSFORM_ARRAY CFSTR("TRANSFORMS")
+#define EXTERN_TRANSFORM_CONNECTION_ARRAY CFSTR("ARRAY")
+#define EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY CFSTR("CUSTOM_EXPORTS")
+
+#define EXTERN_TRANSFORM_NAME CFSTR("NAME")
+#define EXTERN_TRANSFORM_TYPE CFSTR("TYPE")
+#define EXTERN_TRANSFORM_STATE CFSTR("STATE")
+#define EXTERN_TRANSFORM_FROM_NAME CFSTR("FROM_NAME")
+#define EXTERN_TRANSFORM_FROM_ATTRIBUTE CFSTR("FROM_ATTRIBUTE")
+#define EXTERN_TRANSFORM_TO_NAME CFSTR("TO_NAME")
+#define EXTERN_TRANSFORM_TO_ATTRIBUTE CFSTR("TO_ATTRIBUTE")
+
+#ifndef __clang__
+#define GCC_BUG_WORKAROUND ::
+#else
+#define GCC_BUG_WORKAROUND
+#endif
+
+
+class Monitor;
+typedef CFTypeRef SecMonitorRef;
+
+struct transform_attribute {
+ CFStringRef name;
+ CFTypeRef value;
+ CFMutableArrayRef connections;
+ // NOTE: this does NOT have a reference.
+ class Transform *transform;
+ static CFTypeID cftype;
+
+ // NOTE: NULL is a valid value to pushback, so we can't just use pushback_value==NULL for "nothing pushed back"
+ // pb_empty => no value currently pushed back
+ // pb_value => we have a value, we havn't presented it yet (if pushback_value==NULL we won't present again until another attribute changes)
+ // pb_repush => pushed back value currently being re-processed
+ // pb_presented_once => we have a value, and tried to process it and got it back again (don't present until another attribute changes)
+ //
+ enum pushback_states { pb_empty, pb_value, pb_repush, pb_presented_once, pb_discard } pushback_state;
+ CFTypeRef pushback_value;
+
+ // (for pushback support; also need pushback state & value)
+ dispatch_queue_t q;
+ dispatch_semaphore_t semaphore;
+
+ // This attribute needs a value set, or to have something connected to it before running the transform
+ unsigned int required:1;
+ // This attribute needs to have an outgoing connection before running the transform
+ unsigned int requires_outbound_connection:1;
+ // This attribute should not be presented to the transform until after execution starts
+ unsigned int deferred:1;
+ // This attribute comes in N chunks followed by a NULL
+ unsigned int stream:1;
+ // This attribute should be saved when externalizing
+ unsigned int ignore_while_externalizing:1;
+ // Set by Transform::Connect
+ unsigned int has_incoming_connection:1;
+ // CustomTransform should't special case CFErrors for this attribute
+ unsigned int direct_error_handling:1;
+ // External sets are problematic, I think they should be disallowed full stop, but 7947393 says we need them sometimes
+ unsigned int allow_external_sets:1;
+ // Value has been created as a source (therefore deferred), give it special treatment
+ unsigned int has_been_deferred:1;
+
+ void *attribute_changed_block;
+ void *attribute_validate_block;
+};
+
+typedef void (^ActivityMonitor)(CFStringRef name, CFTypeRef value);
+
+class GroupTransform; // Forward reference so we do not have to include
+ // the header and break a circular dependency
+class BlockMonitor;
+
+class Transform : public CoreFoundationObject
+{
+ friend CFTypeRef SecTransformExecute(SecTransformRef tranformRef, CFErrorRef* errorRef);
+ friend CFTypeRef SecTransformGetAttribute(SecTransformRef transformRef, CFStringRef key);
+ friend class BlockMonitor;
+protected:
+ dispatch_queue_t mDispatchQueue, mActivationQueue;
+ dispatch_group_t mActivationPending;
+ CFMutableSetRef mAttributes;
+ CFMutableArrayRef mPushedback;
+ Boolean mIsActive;
+ Boolean mIsFinalizing;
+ Boolean mAlwaysSelfNotify, mProcessingPushbacks;
+ GroupTransform *mGroup;
+ CFErrorRef mAbortError;
+ CFStringRef mTypeName;
+
+ SecTransformAttributeRef AbortAH, DebugAH;
+
+ Transform(CFStringRef transformType, CFStringRef CFobjectType = CFSTR("SecTransform"));
+
+ transform_attribute *getTA(SecTransformStringOrAttributeRef attr, bool create_ok);
+ void TAGetAll(transform_attribute **attributes);
+ CFIndex GetAttributeCount();
+
+ CFDictionaryRef GetAHDictForSaveState(SecTransformStringOrAttributeRef key);
+
+ CFTypeRef ValueForNewAttribute(CFStringRef key, CFTypeRef value);
+ CFMutableDictionaryRef AddNewAttribute(CFStringRef key, CFTypeRef value);
+ CFErrorRef SetAttributeNoCallback(SecTransformStringOrAttributeRef key, CFTypeRef value);
+
+ CFErrorRef ProcessExecute(CFStringRef &outputAttached, SecMonitorRef monitor);
+ typedef void (^AccumulateDictonary)(CFDictionaryRef d);
+ CFErrorRef ProcessExternalize(CFMutableArrayRef transforms, CFMutableArrayRef connections);
+
+ void FinalizeForClang();
+
+ virtual void Finalize();
+ // subclasses with non-trivial finalization can implement this (default: delete this)
+ virtual void FinalizePhase2();
+ // subclasses that want to reject some connections can use this
+ virtual bool validConnectionPoint(CFStringRef attributeName);
+
+ void try_pushbacks();
+
+ void Initialize();
+
+ void ActivateInputs();
+
+ virtual std::string DebugDescription();
+
+ typedef CFErrorRef (^TransformOperation)(Transform*);
+ typedef void (^TransformAsyncOperation)(Transform*);
+ CFErrorRef ForAllNodes(bool parallel, bool includeOwningGroup, TransformOperation op);
+
+ CFErrorRef TraverseTransform(CFMutableSetRef visited, TransformOperation t);
+
+
+ CFErrorRef SendAttribute(SecTransformStringOrAttributeRef key, CFTypeRef value);
+ CFErrorRef SendMetaAttribute(SecTransformStringOrAttributeRef key, SecTransformMetaAttributeType type, CFTypeRef value);
+
+ // Abort all transforms in this transform's RootGroup, including this transform
+ virtual void AbortAllTransforms(CFTypeRef error);
+ // Abort just this transform (and maybe schedule a later call to AbortAllTransforms), should only be
+ // called via AbortAllTransforms
+ virtual void AbortJustThisTransform(CFErrorRef abortMsg);
+
+ void phase3Activation();
+ void DoPhase3Activation();
+
+ bool HasNoInboundConnections();
+ bool HasNoOutboundConnections();
+
+private:
+ CFErrorRef ExecuteOperation(CFStringRef &outputAttached, SecMonitorRef output, dispatch_queue_t phase2, dispatch_queue_t phase3);
+ SecTransformAttributeRef makeAH(transform_attribute *ta);
+
+public:
+
+ static CFTypeID GetCFTypeID();
+
+ // these functions are overloaded to implement the functionality of your transform
+ virtual ~Transform();
+
+ // this is called when one of your attributes (e.g. input) changes
+ virtual void AttributeChanged(SecTransformAttributeRef ah, CFTypeRef value);
+ // this is for backwards compatibility only (XXX: convert all existing Transform subclasses to not use it then remove)
+ virtual void AttributeChanged(CFStringRef name, CFTypeRef value);
+
+ // overload to return true if your transform can be externalized (generally true unless you are a monitor)
+ virtual bool IsExternalizable();
+
+ // Base implementation saves all attributes that have kSecTransformMetaAttributeExternalize TRUE (which is the default).
+ // If that isn't useful for your transform overload to return a CFDictionary that contains the state of
+ // your transform. Values returned should be serializable. Remember that this state will be restored
+ // before SecTransformExecute is called. Do not include the transform name in your state (this will be
+ // done for you by SecTransformCopyExternalRep).
+ virtual CFDictionaryRef CopyState();
+
+ // overload to restore the state of your transform
+ virtual void RestoreState(CFDictionaryRef state);
+ virtual void SetCustomExternalData(CFDictionaryRef customData);
+
+ virtual Boolean TransformCanExecute();
+ virtual CFErrorRef TransformStartingExecution();
+
+ SecTransformAttributeRef getAH(SecTransformStringOrAttributeRef attr, bool create_ok =true, bool create_undesrscore_ok =false);
+ CFArrayRef GetAllAH();
+
+ CFStringRef GetName();
+
+ // Output debugging information if the DEBUG attribute is set for this transform
+ void Debug(const char *fmt, ...);
+
+ CFErrorRef RefactorErrorToIncludeAbortingTransform(CFErrorRef sourceError);
+
+public:
+ CFErrorRef Connect(GroupTransform *group, Transform* destinationTransform, CFStringRef myKey, CFStringRef hisKey);
+ CFErrorRef Disconnect(Transform* destinationTransform, CFStringRef myKey, CFStringRef hisKey);
+
+ CFErrorRef ExternalSetAttribute(SecTransformStringOrAttributeRef key, CFTypeRef value);
+ CFErrorRef SetAttribute(SecTransformStringOrAttributeRef key, CFTypeRef value);
+ CFTypeRef GetAttribute(SecTransformStringOrAttributeRef key);
+ CFTypeRef GetMetaAttribute(SecTransformStringOrAttributeRef key, SecTransformMetaAttributeType type);
+
+ CFErrorRef Pushback(SecTransformAttributeRef ah, CFTypeRef value);
+
+ void Do(SecTransformAttributeRef name, CFTypeRef value);
+
+ CFTypeRef Execute(dispatch_queue_t deliveryQueue, SecMessageBlock deliveryBlock, CFErrorRef* errorRef);
+
+ // set to get notified every time this transform does something -- used for debugging
+ void SetActivityMonitor(ActivityMonitor am);
+
+ virtual CFDictionaryRef Externalize(CFErrorRef *error);
+
+ // Returns NULL if not in a group; can return this
+ GroupTransform* GetRootGroup();
+
+ friend class GroupTransform;
+ friend Transform::TransformOperation makeIdleOp(dispatch_group_t idle_group);
+
+ void SetGroup(GroupTransform* group) {mGroup = group;}
+ CFDictionaryRef GetCustomExternalData();
+};
+
+
+inline struct transform_attribute *ah2ta(SecTransformAttributeRef ah) {
+ // CF stores our data just after the CFRuntimeBase, we just have a single pointer there.
+ return *(struct transform_attribute **)(1 + (CFRuntimeBase*)ah);
+}
+
+#endif