#ifndef __TRANSFORM__ #define __TRANSFORM__ #include #include "CoreFoundationBasics.h" #include "SecTransform.h" #include "SecCustomTransform.h" #include #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