/* * @file var.c * @brief MPR Universal Variable Type * @overview * * @copy default.m * * Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved. * Copyright (c) Michael O'Brien, 1994-1995. All Rights Reserved. * * This software is distributed under commercial and open source licenses. * You may use the GPL open source license described below or you may acquire * a commercial license from Mbedthis Software. You agree to be fully bound * by the terms of either license. Consult the LICENSE.TXT distributed with * this software for full details. * * This software is open source; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See the GNU General Public License for more * details at: http://www.mbedthis.com/downloads/gplLicense.html * * This program is distributed WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * This GPL license does NOT permit incorporating this software into * proprietary programs. If you are unable to comply with the GPL, you must * acquire a commercial license to use this software. Commercial licenses * for this software and support services are available from Mbedthis * Software at http://www.mbedthis.com * * @end */ /******************************* Documentation ********************************/ /* * This module is NOT multithreaded. * * Properties are variables that are stored in an object type variable. * Properties can be primitive data types, other objects or functions. * Properties are indexed by a character name. */ /********************************** Includes **********************************/ #include "lib/ejs/var.h" /*********************************** Locals ***********************************/ #if VAR_DEBUG static MprProperties objectList; /* Dummy head of objects list */ static int objectCount = -1; /* Count of objects */ #endif /***************************** Forward Declarations ***************************/ static int adjustRefCount(MprProperties *pp, int adj); static int adjustVarRefCount(MprVar *vp, int adj); static MprVar *allocProperty(const char *propertyName); static void copyVarCore(MprVar *dest, MprVar *src, int copyDepth); static MprProperties *createProperties(const char *name, int hashSize); static bool freeVar(MprVar *vp, int force); static bool freeVarStorage(MprVar *vp, int force); static MprVar *getObjChain(MprProperties *pp, const char *property); static int hash(MprProperties *pp, const char *property); static bool releaseProperties(MprProperties *pp, int force); /*********************************** Code *************************************/ /* * Destroy a variable and all referenced variables. Release any referenced * object regardless of whether other users still have references. Be VERY * careful using this routine. * * Return TRUE if the underlying data is freed. Objects may not be freed if * there are other users of the object. */ bool mprDestroyAllVars(MprVar *vp) { mprAssert(vp); if (vp->trigger) { if ((vp->trigger)(MPR_VAR_DELETE, vp->parentProperties, vp, 0, 0) == MPR_TRIGGER_ABORT) { return 0; } } /* * Free the actual value. If this var refers to an object, we will * recurse through all the properties freeing all vars. */ return freeVar(vp, 1); } /******************************************************************************/ /* * Destroy a variable. Release any referenced object (destroy if no other * users are referencing). * * Return TRUE if the underlying data is freed. Objects may not be freed if * there are other users of the object. */ bool mprDestroyVar(MprVar *vp) { mprAssert(vp); if (vp->trigger) { if ((vp->trigger)(MPR_VAR_DELETE, vp->parentProperties, vp, 0, 0) == MPR_TRIGGER_ABORT) { return 0; } } /* * Free the actual value. If this var refers to an object, we will * recurse through all the properties freeing all that have no other * references. */ return freeVar(vp, 0); } /******************************************************************************/ /* * Free the value in a variable for primitive types. Release objects. * * Return TRUE if the underlying data is freed. Objects may not be freed if * there are other users of the object. */ static bool freeVar(MprVar *vp, int force) { bool freed; mprAssert(vp); freed = freeVarStorage(vp, force); mprFree(vp->name); mprFree(vp->fullName); if (vp->allocatedVar) { mprFree(vp); } else { vp->name = 0; vp->fullName = 0; vp->type = MPR_TYPE_UNDEFINED; } return freed; } /******************************************************************************/ /* * Free the value in a variable for primitive types. Release objects. * * Return TRUE if the underlying data is freed. Objects may not be freed if * there are other users of the object. */ static bool freeVarStorage(MprVar *vp, int force) { MprArray *argList; bool freed; int i; freed = 1; mprAssert(vp); switch (vp->type) { default: break; case MPR_TYPE_STRING: if (vp->allocatedData && vp->string != 0) { mprFree(vp->string); vp->string = 0; vp->allocatedData = 0; } break; case MPR_TYPE_OBJECT: #if VAR_DEBUG /* * Recurse through all properties and release / delete. Release the * properties hash table. */ if (vp->properties->refCount > 1) { mprLog(7, "freeVar: ACT \"%s\", 0x%x, ref %d, force %d\n", vp->name, vp->properties, vp->properties->refCount, force); } else { mprLog(7, "freeVar: DEL \"%s\", 0x%x, ref %d, force %d\n", vp->name, vp->properties, vp->properties->refCount, force); } #endif if (vp->allocatedData) { freed = releaseProperties(vp->properties, force); } vp->properties = 0; break; case MPR_TYPE_FUNCTION: if (vp->allocatedData) { argList = vp->function.args; for (i = 0; i < argList->max; i++) { if (argList->handles[i] != 0) { mprFree(argList->handles[i]); } } mprDestroyArray(argList); vp->function.args = 0; mprFree(vp->function.body); vp->function.body = 0; } break; } vp->type = MPR_TYPE_UNDEFINED; return freed; } /******************************************************************************/ /* * Adjust the object reference count and return the currrent count of * users. */ static int adjustVarRefCount(MprVar *vp, int adj) { mprAssert(vp); if (vp->type != MPR_TYPE_OBJECT) { mprAssert(vp->type == MPR_TYPE_OBJECT); return 0; } return adjustRefCount(vp->properties, adj); } /******************************************************************************/ /* * Get the object reference count */ int mprGetVarRefCount(MprVar *vp) { mprAssert(vp); if (vp->type != MPR_TYPE_OBJECT) { mprAssert(vp->type == MPR_TYPE_OBJECT); return 0; } return adjustRefCount(vp->properties, 0); } /******************************************************************************/ /* * Update the variable's name */ void mprSetVarName(MprVar *vp, char *name) { mprAssert(vp); mprFree(vp->name); vp->name = mprStrdup(name); } /******************************************************************************/ /* * Append to the variable's full name */ void mprSetVarFullName(MprVar *vp, char *name) { #if VAR_DEBUG mprAssert(vp); mprFree(vp->fullName); vp->fullName = mprStrdup(name); if (vp->type == MPR_TYPE_OBJECT) { if (strcmp(vp->properties->name, "this") == 0) { mprStrcpy(vp->properties->name, sizeof(vp->properties->name), name); } } #endif } /******************************************************************************/ /* * Make a var impervious to recursive forced deletes. */ void mprSetVarDeleteProtect(MprVar *vp, int deleteProtect) { mprAssert(vp); if (vp->type == MPR_TYPE_OBJECT && vp->properties) { vp->properties->deleteProtect = deleteProtect; } } /******************************************************************************/ /* * Make a variable readonly. Can still be deleted. */ void mprSetVarReadonly(MprVar *vp, int readonly) { mprAssert(vp); vp->readonly = readonly; } /******************************************************************************/ MprVarTrigger mprAddVarTrigger(MprVar *vp, MprVarTrigger fn) { MprVarTrigger oldTrigger; mprAssert(vp); mprAssert(fn); oldTrigger = vp->trigger; vp->trigger = fn; return oldTrigger; } /******************************************************************************/ MprType mprGetVarType(MprVar *vp) { mprAssert(vp); return vp->type; } /******************************************************************************/ /********************************** Properties ********************************/ /******************************************************************************/ /* * Create a property in an object with a defined value. If the property * already exists in the object, then just write its value. */ MprVar *mprCreateProperty(MprVar *obj, const char *propertyName, MprVar *newValue) { MprVar *prop, *last; int bucketIndex; mprAssert(obj); mprAssert(propertyName && *propertyName); if (obj->type != MPR_TYPE_OBJECT) { mprAssert(obj->type == MPR_TYPE_OBJECT); return 0; } /* * See if property already exists and locate the bucket to hold the * property reference. */ last = 0; bucketIndex = hash(obj->properties, propertyName); prop = obj->properties->buckets[bucketIndex]; /* * Find the property in the hash chain if it exists */ for (last = 0; prop; last = prop, prop = prop->forw) { if (prop->name[0] == propertyName[0] && strcmp(prop->name, propertyName) == 0) { break; } } if (prop) { /* FUTURE -- remove. Just for debug. */ mprAssert(prop == 0); mprLog(0, "Attempting to create property %s in object %s\n", propertyName, obj->name); return 0; } if (obj->trigger) { if ((obj->trigger)(MPR_VAR_CREATE_PROPERTY, obj->properties, prop, newValue, 0) == MPR_TRIGGER_ABORT) { return 0; } } /* * Create a new property */ prop = allocProperty(propertyName); if (prop == 0) { mprAssert(prop); return 0; } copyVarCore(prop, newValue, MPR_SHALLOW_COPY); prop->bucketIndex = bucketIndex; if (last) { last->forw = prop; } else { obj->properties->buckets[bucketIndex] = prop; } prop->parentProperties = obj->properties; /* * Update the item counts */ obj->properties->numItems++; if (! mprVarIsFunction(prop->type)) { obj->properties->numDataItems++; } return prop; } /******************************************************************************/ /* * Create a property in an object with a defined value. If the property * already exists in the object, then just write its value. Same as * mprCreateProperty except that the new value is passed by value rather than * by pointer. */ MprVar *mprCreatePropertyValue(MprVar *obj, const char *propertyName, MprVar newValue) { return mprCreateProperty(obj, propertyName, &newValue); } /******************************************************************************/ /* * Create a new property */ static MprVar *allocProperty(const char *propertyName) { MprVar *prop; prop = (MprVar*) mprMalloc(sizeof(MprVar)); if (prop == 0) { mprAssert(prop); return 0; } memset(prop, 0, sizeof(MprVar)); prop->allocatedVar = 1; prop->name = mprStrdup(propertyName); prop->forw = (MprVar*) 0; return prop; } /******************************************************************************/ /* * Update a property in an object with a defined value. Create the property * if it doesn not already exist. */ MprVar *mprSetProperty(MprVar *obj, const char *propertyName, MprVar *newValue) { MprVar *prop, triggerValue; int rc; mprAssert(obj); mprAssert(propertyName && *propertyName); mprAssert(obj->type == MPR_TYPE_OBJECT); if (obj->type != MPR_TYPE_OBJECT) { mprAssert(0); return 0; } prop = mprGetProperty(obj, propertyName, 0); if (prop == 0) { return mprCreateProperty(obj, propertyName, newValue); } if (obj->trigger) { /* * Call the trigger before the update and pass it the new value. */ triggerValue = *newValue; triggerValue.allocatedVar = 0; triggerValue.allocatedData = 0; rc = (obj->trigger)(MPR_VAR_WRITE, obj->properties, obj, &triggerValue, 0); if (rc == MPR_TRIGGER_ABORT) { return 0; } else if (rc == MPR_TRIGGER_USE_NEW_VALUE) { /* * Trigger must copy to triggerValue a variable that is not * a structure copy of the existing data. */ copyVarCore(prop, &triggerValue, MPR_SHALLOW_COPY); mprDestroyVar(&triggerValue); return prop; } } copyVarCore(prop, newValue, MPR_SHALLOW_COPY); return prop; } /******************************************************************************/ /* * Update a property in an object with a defined value. Create the property * if it does not already exist. Same as mprSetProperty except that the * new value is passed by value rather than by pointer. */ MprVar *mprSetPropertyValue(MprVar *obj, const char *propertyName, MprVar newValue) { return mprSetProperty(obj, propertyName, &newValue); } /******************************************************************************/ /* * Delete a property from this object */ int mprDeleteProperty(MprVar *obj, const char *property) { MprVar *prop, *last; char *cp; int bucketIndex; mprAssert(obj); mprAssert(property && *property); mprAssert(obj->type == MPR_TYPE_OBJECT); if (obj->type != MPR_TYPE_OBJECT) { mprAssert(obj->type == MPR_TYPE_OBJECT); return 0; } last = 0; bucketIndex = hash(obj->properties, property); if ((prop = obj->properties->buckets[bucketIndex]) != 0) { for ( ; prop; prop = prop->forw) { cp = prop->name; if (cp[0] == property[0] && strcmp(cp, property) == 0) { break; } last = prop; } } if (prop == (MprVar*) 0) { mprAssert(prop); return MPR_ERR_NOT_FOUND; } if (prop->readonly) { mprAssert(! prop->readonly); return MPR_ERR_READ_ONLY; } if (obj->trigger) { if ((obj->trigger)(MPR_VAR_DELETE_PROPERTY, obj->properties, prop, 0, 0) == MPR_TRIGGER_ABORT) { return MPR_ERR_ABORTED; } } if (last) { last->forw = prop->forw; } else { obj->properties->buckets[bucketIndex] = prop->forw; } obj->properties->numItems--; if (! mprVarIsFunction(prop->type)) { obj->properties->numDataItems--; } mprDestroyVar(prop); return 0; } /******************************************************************************/ /* * Find a property in an object and return a pointer to it. If a value arg * is supplied, then copy the data into the var. */ MprVar *mprGetProperty(MprVar *obj, const char *property, MprVar *value) { MprVar *prop, triggerValue; int rc; if (obj == 0 || obj->type != MPR_TYPE_OBJECT || property == 0 || *property == '\0') { if (value) { value->type = MPR_TYPE_UNDEFINED; } return 0; } for (prop = getObjChain(obj->properties, property); prop; prop = prop->forw) { if (prop->name && prop->name[0] == property[0] && strcmp(prop->name, property) == 0) { break; } } if (prop == 0) { if (value) { value->type = MPR_TYPE_UNDEFINED; } return 0; } if (value) { if (prop->trigger) { triggerValue = *prop; triggerValue.allocatedVar = 0; triggerValue.allocatedData = 0; /* * Pass the trigger the current read value and may receive * a new value. */ rc = (prop->trigger)(MPR_VAR_READ, prop->parentProperties, prop, &triggerValue, 0); if (rc == MPR_TRIGGER_ABORT) { if (value) { value->type = MPR_TYPE_UNDEFINED; } return 0; } else if (rc == MPR_TRIGGER_USE_NEW_VALUE) { copyVarCore(prop, &triggerValue, MPR_SHALLOW_COPY); mprDestroyVar(&triggerValue); } } /* * Clone. No copy. */ *value = *prop; } return prop; } /******************************************************************************/ /* * Read a properties value. This returns the property's value. It does not * copy object/string data but returns a pointer directly into the variable. * The caller does not and should not call mprDestroy on the returned value. * If value is null, just read the property and run triggers. */ int mprReadProperty(MprVar *prop, MprVar *value) { MprVar triggerValue; int rc; mprAssert(prop); if (prop->trigger) { triggerValue = *prop; triggerValue.allocatedVar = 0; triggerValue.allocatedData = 0; rc = (prop->trigger)(MPR_VAR_READ, prop->parentProperties, prop, &triggerValue, 0); if (rc == MPR_TRIGGER_ABORT) { return MPR_ERR_ABORTED; } else if (rc == MPR_TRIGGER_USE_NEW_VALUE) { copyVarCore(prop, &triggerValue, MPR_SHALLOW_COPY); mprDestroyVar(&triggerValue); return 0; } } if (value) { *value = *prop; /* * Just so that if the user calls mprDestroyVar on value, it will do no * harm. */ value->allocatedVar = 0; value->allocatedData = 0; } return 0; } /******************************************************************************/ /* * Read a properties value. This returns a copy of the property variable. * However, if the property is an object or string, it returns a copy of the * reference to the underlying data. If copyDepth is set to MPR_DEEP_COPY, * then the underlying objects and strings data will be copied as well. If * copyDepth is set to MPR_SHALLOW_COPY, then only strings will be copied. If * it is set to MPR_NO_COPY, then no data will be copied. In all cases, the * user must call mprDestroyVar to free resources. This routine will run any * registered triggers which may modify the value the user receives (without * updating the properties real value). * * WARNING: the args are reversed to most other APIs. This conforms to the * strcpy(dest, src) standard instead. */ int mprCopyProperty(MprVar *dest, MprVar *prop, int copyDepth) { MprVar triggerValue; int rc; mprAssert(prop); mprAssert(dest); if (prop->trigger) { triggerValue = *prop; triggerValue.allocatedVar = 0; triggerValue.allocatedData = 0; rc = (prop->trigger)(MPR_VAR_READ, prop->parentProperties, prop, &triggerValue, copyDepth); if (rc == MPR_TRIGGER_ABORT) { return MPR_ERR_ABORTED; } else if (rc == MPR_TRIGGER_USE_NEW_VALUE) { copyVarCore(dest, &triggerValue, MPR_SHALLOW_COPY); mprDestroyVar(&triggerValue); return 0; } } mprCopyVar(dest, prop, copyDepth); return 0; } /******************************************************************************/ /* * Write a new value into an existing property in an object. */ int mprWriteProperty(MprVar *vp, MprVar *value) { MprVar triggerValue; int rc; mprAssert(vp); mprAssert(value); if (vp->readonly) { return MPR_ERR_READ_ONLY; } if (vp->trigger) { triggerValue = *value; rc = (vp->trigger)(MPR_VAR_WRITE, vp->parentProperties, vp, &triggerValue, 0); if (rc == MPR_TRIGGER_ABORT) { return MPR_ERR_ABORTED; } else if (rc == MPR_TRIGGER_USE_NEW_VALUE) { copyVarCore(vp, &triggerValue, MPR_SHALLOW_COPY); mprDestroyVar(&triggerValue); return 0; } /* Fall through */ } copyVarCore(vp, value, MPR_SHALLOW_COPY); return 0; } /******************************************************************************/ /* * Write a new value into an existing property in an object. */ int mprWritePropertyValue(MprVar *vp, MprVar value) { mprAssert(vp); return mprWriteProperty(vp, &value); } /******************************************************************************/ /* * Get the count of properties. */ int mprGetPropertyCount(MprVar *vp, int includeFlags) { mprAssert(vp); if (vp->type != MPR_TYPE_OBJECT) { return 0; } if (includeFlags == MPR_ENUM_DATA) { return vp->properties->numDataItems; } else { return vp->properties->numItems; } } /******************************************************************************/ /* * Get the first property in an object. Used for walking all properties in an * object. */ MprVar *mprGetFirstProperty(MprVar *obj, int includeFlags) { MprVar *prop; int i; mprAssert(obj); mprAssert(obj->type == MPR_TYPE_OBJECT); if (obj->type != MPR_TYPE_OBJECT) { mprAssert(obj->type == MPR_TYPE_OBJECT); return 0; } for (i = 0; i < (int) obj->properties->hashSize; i++) { for (prop = obj->properties->buckets[i]; prop; prop = prop->forw) { if (prop) { if (mprVarIsFunction(prop->type)) { if (!(includeFlags & MPR_ENUM_FUNCTIONS)) { continue; } } else { if (!(includeFlags & MPR_ENUM_DATA)) { continue; } } return prop; } break; } } return 0; } /******************************************************************************/ /* * Get the next property in sequence. */ MprVar *mprGetNextProperty(MprVar *obj, MprVar *last, int includeFlags) { MprProperties *properties; int i; mprAssert(obj); mprAssert(obj->type == MPR_TYPE_OBJECT); if (obj->type != MPR_TYPE_OBJECT) { mprAssert(obj->type == MPR_TYPE_OBJECT); return 0; } properties = obj->properties; if (last->forw) { return last->forw; } for (i = last->bucketIndex + 1; i < (int) properties->hashSize; i++) { for (last = properties->buckets[i]; last; last = last->forw) { if (mprVarIsFunction(last->type)) { if (!(includeFlags & MPR_ENUM_FUNCTIONS)) { continue; } } else { if (!(includeFlags & MPR_ENUM_DATA)) { continue; } } return last; } } return 0; } /******************************************************************************/ /************************** Internal Support Routines *************************/ /******************************************************************************/ /* * Create an hash table to hold and index properties. Properties are just * variables which may contain primitive data types, functions or other * objects. The hash table is the essence of an object. HashSize specifies * the size of the hash table to use and should be a prime number. */ static MprProperties *createProperties(const char *name, int hashSize) { MprProperties *pp; if (hashSize < 7) { hashSize = 7; } if ((pp = (MprProperties*) mprMalloc(sizeof(MprProperties))) == NULL) { mprAssert(0); return 0; } mprAssert(pp); memset(pp, 0, sizeof(MprProperties)); pp->numItems = 0; pp->numDataItems = 0; pp->hashSize = hashSize; pp->buckets = (MprVar**) mprMalloc(pp->hashSize * sizeof(MprVar*)); mprAssert(pp->buckets); memset(pp->buckets, 0, pp->hashSize * sizeof(MprVar*)); pp->refCount = 1; #if VAR_DEBUG if (objectCount == -1) { objectCount = 0; objectList.next = objectList.prev = &objectList; } mprStrcpy(pp->name, sizeof(pp->name), name); pp->next = &objectList; pp->prev = objectList.prev; objectList.prev->next = pp; objectList.prev = pp; objectCount++; #endif return pp; } /******************************************************************************/ /* * Release an object's properties hash table. If this is the last person * using it, free it. Return TRUE if the object is released. */ static bool releaseProperties(MprProperties *obj, int force) { MprProperties *pp; MprVar *prop, *forw; int i; mprAssert(obj); mprAssert(obj->refCount > 0); #if VAR_DEBUG /* * Debug sanity check */ mprAssert(obj->refCount < 20); #endif if (--obj->refCount > 0 && !force) { return 0; } #if VAR_DEBUG mprAssert(obj->prev); mprAssert(obj->next); mprAssert(obj->next->prev); mprAssert(obj->prev->next); obj->next->prev = obj->prev; obj->prev->next = obj->next; objectCount--; #endif for (i = 0; i < (int) obj->hashSize; i++) { for (prop = obj->buckets[i]; prop; prop = forw) { forw = prop->forw; if (prop->type == MPR_TYPE_OBJECT) { if (prop->properties == obj) { /* Self reference */ continue; } pp = prop->properties; if (pp->visited) { continue; } pp->visited = 1; if (! freeVar(prop, pp->deleteProtect ? 0 : force)) { pp->visited = 0; } } else { freeVar(prop, force); } } } mprFree((void*) obj->buckets); mprFree((void*) obj); return 1; } /******************************************************************************/ /* * Adjust the reference count */ static int adjustRefCount(MprProperties *pp, int adj) { mprAssert(pp); /* * Debug sanity check */ mprAssert(pp->refCount < 20); return pp->refCount += adj; } /******************************************************************************/ #if VAR_DEBUG /* * Print objects held */ void mprPrintObjects(char *msg) { MprProperties *pp, *np; MprVar *prop, *forw; char *buf; int i; mprLog(7, "%s: Object Store. %d objects.\n", msg, objectCount); pp = objectList.next; while (pp != &objectList) { mprLog(7, "%s: 0x%x, refCount %d, properties %d\n", pp->name, pp, pp->refCount, pp->numItems); for (i = 0; i < (int) pp->hashSize; i++) { for (prop = pp->buckets[i]; prop; prop = forw) { forw = prop->forw; if (prop->properties == pp) { /* Self reference */ continue; } mprVarToString(&buf, MPR_MAX_STRING, 0, prop); if (prop->type == MPR_TYPE_OBJECT) { np = objectList.next; while (np != &objectList) { if (prop->properties == np) { break; } np = np->next; } if (prop->properties == np) { mprLog(7, " %s: OBJECT 0x%x, <%s>\n", prop->name, prop->properties, prop->fullName); } else { mprLog(7, " %s: OBJECT NOT FOUND, %s <%s>\n", prop->name, buf, prop->fullName); } } else { mprLog(7, " %s: <%s> = %s\n", prop->name, prop->fullName, buf); } mprFree(buf); } } pp = pp->next; } } /******************************************************************************/ void mprPrintObjRefCount(MprVar *vp) { mprLog(7, "OBJECT 0x%x, refCount %d\n", vp->properties, vp->properties->refCount); } #endif /******************************************************************************/ /* * Get the bucket chain containing a property. */ static MprVar *getObjChain(MprProperties *obj, const char *property) { mprAssert(obj); return obj->buckets[hash(obj, property)]; } /******************************************************************************/ /* * Fast hash. The history of this algorithm is part of lost computer science * folk lore. */ static int hash(MprProperties *pp, const char *property) { uint sum; mprAssert(pp); mprAssert(property); sum = 0; while (*property) { sum += (sum * 33) + *property++; } return sum % pp->hashSize; } /******************************************************************************/ /*********************************** Constructors *****************************/ /******************************************************************************/ /* * Initialize an undefined value. */ MprVar mprCreateUndefinedVar() { MprVar v; memset(&v, 0x0, sizeof(v)); v.type = MPR_TYPE_UNDEFINED; return v; } /******************************************************************************/ /* * Initialize an null value. */ MprVar mprCreateNullVar() { MprVar v; memset(&v, 0x0, sizeof(v)); v.type = MPR_TYPE_NULL; return v; } /******************************************************************************/ MprVar mprCreateBoolVar(bool value) { MprVar v; memset(&v, 0x0, sizeof(v)); v.type = MPR_TYPE_BOOL; v.boolean = value; return v; } /******************************************************************************/ /* * Initialize a C function. */ MprVar mprCreateCFunctionVar(MprCFunction fn, void *thisPtr, int flags) { MprVar v; memset(&v, 0x0, sizeof(v)); v.type = MPR_TYPE_CFUNCTION; v.cFunction.fn = fn; v.cFunction.thisPtr = thisPtr; v.flags = flags; return v; } /******************************************************************************/ /* * Initialize a C function. */ MprVar mprCreateStringCFunctionVar(MprStringCFunction fn, void *thisPtr, int flags) { MprVar v; memset(&v, 0x0, sizeof(v)); v.type = MPR_TYPE_STRING_CFUNCTION; v.cFunctionWithStrings.fn = fn; v.cFunctionWithStrings.thisPtr = thisPtr; v.flags = flags; return v; } /* * Initialize an opaque pointer. */ MprVar mprCreatePtrVar(void *ptr, const char *name) { MprVar v; memset(&v, 0x0, sizeof(v)); v.type = MPR_TYPE_PTR; v.ptr = ptr; return v; } /******************************************************************************/ #if BLD_FEATURE_FLOATING_POINT /* * Initialize a floating value. */ MprVar mprCreateFloatVar(double value) { MprVar v; memset(&v, 0x0, sizeof(v)); v.type = MPR_TYPE_FLOAT; v.floating = value; return v; } #endif /******************************************************************************/ /* * Initialize an integer value. */ MprVar mprCreateIntegerVar(int value) { MprVar v; memset(&v, 0x0, sizeof(v)); v.type = MPR_TYPE_INT; v.integer = value; return v; } /******************************************************************************/ #if BLD_FEATURE_INT64 /* * Initialize a 64-bit integer value. */ MprVar mprCreateInteger64Var(int64 value) { MprVar v; memset(&v, 0x0, sizeof(v)); v.type = MPR_TYPE_INT64; v.integer64 = value; return v; } #endif /* BLD_FEATURE_INT64 */ /******************************************************************************/ /* * Initialize an number variable. Type is defined by configure. */ MprVar mprCreateNumberVar(MprNum value) { MprVar v; memset(&v, 0x0, sizeof(v)); v.type = BLD_FEATURE_NUM_TYPE_ID; #if BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_INT64 v.integer64 = value; #elif BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_FLOAT v.float = value; #else v.integer = value; #endif return v; } /******************************************************************************/ /* * Initialize a (bare) JavaScript function. args and body can be null. */ MprVar mprCreateFunctionVar(char *args, char *body, int flags) { MprVar v; char *cp, *arg, *last; int aid; memset(&v, 0x0, sizeof(v)); v.type = MPR_TYPE_FUNCTION; v.flags = flags; v.function.args = mprCreateArray(); if (args) { args = mprStrdup(args); arg = mprStrTok(args, ",", &last); while (arg) { while (isspace((int) *arg)) arg++; for (cp = &arg[strlen(arg) - 1]; cp > arg; cp--) { if (!isspace((int) *cp)) { break; } } cp[1] = '\0'; aid = mprAddToArray(v.function.args, mprStrdup(arg)); arg = mprStrTok(0, ",", &last); } mprFree(args); } if (body) { v.function.body = mprStrdup(body); } v.allocatedData = 1; return v; } /******************************************************************************/ /* * Initialize an object variable. Return type == MPR_TYPE_UNDEFINED if the * memory allocation for the properties table failed. */ MprVar mprCreateObjVar(const char *name, int hashSize) { MprVar v; mprAssert(name && *name); memset(&v, 0x0, sizeof(MprVar)); v.type = MPR_TYPE_OBJECT; if (hashSize <= 0) { hashSize = MPR_DEFAULT_HASH_SIZE; } v.properties = createProperties(name, hashSize); if (v.properties == 0) { /* Indicate failed memory allocation */ v.type = MPR_TYPE_UNDEFINED; } v.allocatedData = 1; v.name = mprStrdup(name); mprLog(7, "mprCreateObjVar %s, 0x%p\n", name, v.properties); return v; } /******************************************************************************/ /* * Initialize a string value. */ MprVar mprCreateStringVar(const char *value, bool allocate) { MprVar v; memset(&v, 0x0, sizeof(v)); v.type = MPR_TYPE_STRING; if (value == 0) { v.string = ""; } else if (allocate) { v.string = mprStrdup(value); v.allocatedData = 1; } else { v.string = value; } return v; } /******************************************************************************/ /* * Copy an objects core value (only). This preserves the destination object's * name. This implements copy by reference for objects and copy by value for * strings and other types. Caller must free dest prior to calling. */ static void copyVarCore(MprVar *dest, MprVar *src, int copyDepth) { MprVarTrigger saveTrigger; MprVar *srcProp, *destProp, *last; char **srcArgs; int i; mprAssert(dest); mprAssert(src); if (dest == src) { return; } /* * FUTURE: we should allow read-only triggers where the value is never * stored in the object. Currently, triggers override the readonly * status. */ if (dest->type != MPR_TYPE_UNDEFINED && dest->readonly && !dest->trigger) { mprAssert(0); return; } if (dest->type != MPR_TYPE_UNDEFINED) { saveTrigger = dest->trigger; freeVarStorage(dest, 0); } else { saveTrigger = 0; } switch (src->type) { default: case MPR_TYPE_UNDEFINED: case MPR_TYPE_NULL: break; case MPR_TYPE_BOOL: dest->boolean = src->boolean; break; case MPR_TYPE_STRING_CFUNCTION: dest->cFunctionWithStrings = src->cFunctionWithStrings; break; case MPR_TYPE_PTR: dest->ptr = src->ptr; break; case MPR_TYPE_CFUNCTION: dest->cFunction = src->cFunction; break; #if BLD_FEATURE_FLOATING_POINT case MPR_TYPE_FLOAT: dest->floating = src->floating; break; #endif case MPR_TYPE_INT: dest->integer = src->integer; break; #if BLD_FEATURE_INT64 case MPR_TYPE_INT64: dest->integer64 = src->integer64; break; #endif case MPR_TYPE_OBJECT: if (copyDepth == MPR_DEEP_COPY) { dest->properties = createProperties(src->name, src->properties->hashSize); dest->allocatedData = 1; for (i = 0; i < (int) src->properties->hashSize; i++) { last = 0; for (srcProp = src->properties->buckets[i]; srcProp; srcProp = srcProp->forw) { if (srcProp->visited) { continue; } destProp = allocProperty(srcProp->name); if (destProp == 0) { mprAssert(destProp); return; } destProp->bucketIndex = i; if (last) { last->forw = destProp; } else { dest->properties->buckets[i] = destProp; } destProp->parentProperties = dest->properties; /* * Recursively copy the object */ srcProp->visited = 1; copyVarCore(destProp, srcProp, copyDepth); srcProp->visited = 0; last = srcProp; } } dest->properties->numItems = src->properties->numItems; dest->properties->numDataItems = src->properties->numDataItems; dest->allocatedData = 1; } else if (copyDepth == MPR_SHALLOW_COPY) { dest->properties = src->properties; adjustVarRefCount(src, 1); dest->allocatedData = 1; } else { dest->properties = src->properties; dest->allocatedData = 0; } break; case MPR_TYPE_FUNCTION: if (copyDepth != MPR_NO_COPY) { dest->function.args = mprCreateArray(); srcArgs = (char**) src->function.args->handles; for (i = 0; i < src->function.args->max; i++) { if (srcArgs[i]) { mprAddToArray(dest->function.args, mprStrdup(srcArgs[i])); } } dest->function.body = mprStrdup(src->function.body); dest->allocatedData = 1; } else { dest->function.args = src->function.args; dest->function.body = src->function.body; dest->allocatedData = 0; } break; case MPR_TYPE_STRING: if (src->string && copyDepth != MPR_NO_COPY) { dest->string = mprStrdup(src->string); dest->allocatedData = 1; } else { dest->string = src->string; dest->allocatedData = 0; } break; } dest->type = src->type; dest->flags = src->flags; dest->trigger = saveTrigger; /* * Just for safety */ dest->spare = 0; } /******************************************************************************/ /* * Copy an entire object including name. */ void mprCopyVar(MprVar *dest, MprVar *src, int copyDepth) { mprAssert(dest); mprAssert(src); copyVarCore(dest, src, copyDepth); mprFree(dest->name); dest->name = mprStrdup(src->name); #if VAR_DEBUG if (src->type == MPR_TYPE_OBJECT) { mprFree(dest->fullName); dest->fullName = mprStrdup(src->fullName); mprLog(7, "mprCopyVar: object \"%s\", FDQ \"%s\" 0x%x, refCount %d\n", dest->name, dest->fullName, dest->properties, dest->properties->refCount); } #endif } /******************************************************************************/ /* * Copy an entire object including name. */ void mprCopyVarValue(MprVar *dest, MprVar src, int copyDepth) { mprAssert(dest); mprCopyVar(dest, &src, copyDepth); } /******************************************************************************/ /* * Copy an object. This implements copy by reference for objects and copy by * value for strings and other types. Caller must free dest prior to calling. */ MprVar *mprDupVar(MprVar *src, int copyDepth) { MprVar *dest; mprAssert(src); dest = (MprVar*) mprMalloc(sizeof(MprVar)); memset(dest, 0, sizeof(MprVar)); mprCopyVar(dest, src, copyDepth); return dest; } /******************************************************************************/ /* * Convert a value to a text based representation of its value * FUTURE -- conver this to use the format string in all cases. Allow * arbitrary format strings. */ void mprVarToString(char** out, int size, char *fmt, MprVar *obj) { char *src; mprAssert(out); *out = NULL; if (obj->trigger) { mprReadProperty(obj, 0); } switch (obj->type) { case MPR_TYPE_UNDEFINED: /* FUTURE -- spec says convert to "undefined" */ *out = mprStrdup(""); break; case MPR_TYPE_NULL: *out = mprStrdup("null"); break; case MPR_TYPE_BOOL: if (obj->boolean) { *out = mprStrdup("true"); } else { *out = mprStrdup("false"); } break; #if BLD_FEATURE_FLOATING_POINT case MPR_TYPE_FLOAT: if (fmt == NULL || *fmt == '\0') { mprAllocSprintf(out, size, "%f", obj->floating); } else { mprAllocSprintf(out, size, fmt, obj->floating); } break; #endif case MPR_TYPE_INT: if (fmt == NULL || *fmt == '\0') { mprAllocSprintf(out, size, "%d", obj->integer); } else { mprAllocSprintf(out, size, fmt, obj->integer); } break; #if BLD_FEATURE_INT64 case MPR_TYPE_INT64: if (fmt == NULL || *fmt == '\0') { #if BLD_GOAHEAD_WEBSERVER mprAllocSprintf(out, size, "%d", (int) obj->integer64); #else mprAllocSprintf(out, size, "%Ld", obj->integer64); #endif } else { mprAllocSprintf(out, size, fmt, obj->integer64); } break; #endif case MPR_TYPE_CFUNCTION: mprAllocSprintf(out, size, "[C Function]"); break; case MPR_TYPE_STRING_CFUNCTION: mprAllocSprintf(out, size, "[C StringFunction]"); break; case MPR_TYPE_PTR: mprAllocSprintf(out, size, "[C Pointer: %p]", obj->ptr); break; case MPR_TYPE_FUNCTION: mprAllocSprintf(out, size, "[JavaScript Function]"); break; case MPR_TYPE_OBJECT: /* FUTURE -- really want: [object class: name] */ mprAllocSprintf(out, size, "[object %s]", obj->name); break; case MPR_TYPE_STRING: src = obj->string; mprAssert(src); if (fmt && *fmt) { mprAllocSprintf(out, size, fmt, src); } else if (src == NULL) { *out = mprStrdup("null"); } else { *out = mprStrdup(src); } break; default: mprAssert(0); } } /******************************************************************************/ /* * Parse a string based on formatting instructions and intelligently * create a variable. */ MprVar mprParseVar(char *buf, MprType preferredType) { MprType type; char *cp; mprAssert(buf); if (preferredType == MPR_TYPE_UNDEFINED) { if (*buf == '-') { type = MPR_NUM_VAR; } else if (!isdigit((int) *buf)) { if (strcmp(buf, "true") == 0 || strcmp(buf, "false") == 0) { type = MPR_TYPE_BOOL; } else { type = MPR_TYPE_STRING; } } else if (isdigit((int) *buf)) { type = MPR_NUM_VAR; cp = buf; if (*cp && tolower(cp[1]) == 'x') { cp = &cp[2]; } for (cp = buf; *cp; cp++) { if (! isdigit((int) *cp)) { break; } } if (*cp != '\0') { #if BLD_FEATURE_FLOATING_POINT if (*cp == '.' || tolower(*cp) == 'e') { type = MPR_TYPE_FLOAT; } else #endif { type = MPR_NUM_VAR; } } } } else { type = preferredType; } switch (type) { case MPR_TYPE_OBJECT: case MPR_TYPE_UNDEFINED: case MPR_TYPE_NULL: default: break; case MPR_TYPE_BOOL: return mprCreateBoolVar(buf[0] == 't' ? 1 : 0); case MPR_TYPE_INT: return mprCreateIntegerVar(mprParseInteger(buf)); #if BLD_FEATURE_INT64 case MPR_TYPE_INT64: return mprCreateInteger64Var(mprParseInteger64(buf)); #endif case MPR_TYPE_STRING: if (strcmp(buf, "null") == 0) { return mprCreateNullVar(); } else if (strcmp(buf, "undefined") == 0) { return mprCreateUndefinedVar(); } return mprCreateStringVar(buf, 1); #if BLD_FEATURE_FLOATING_POINT case MPR_TYPE_FLOAT: return mprCreateFloatVar(atof(buf)); #endif } return mprCreateUndefinedVar(); } /******************************************************************************/ /* * Convert the variable to a boolean. Only for primitive types. */ bool mprVarToBool(MprVar *vp) { mprAssert(vp); switch (vp->type) { case MPR_TYPE_UNDEFINED: case MPR_TYPE_NULL: case MPR_TYPE_STRING_CFUNCTION: case MPR_TYPE_CFUNCTION: case MPR_TYPE_FUNCTION: case MPR_TYPE_OBJECT: return 0; case MPR_TYPE_PTR: return (vp->ptr != NULL); case MPR_TYPE_BOOL: return vp->boolean; #if BLD_FEATURE_FLOATING_POINT case MPR_TYPE_FLOAT: return (vp->floating != 0 && !mprIsNan(vp->floating)); #endif case MPR_TYPE_INT: return (vp->integer != 0); #if BLD_FEATURE_INT64 case MPR_TYPE_INT64: return (vp->integer64 != 0); #endif case MPR_TYPE_STRING: mprAssert(vp->string); return (vp->string[0] != '\0'); } /* Not reached */ return 0; } /******************************************************************************/ #if BLD_FEATURE_FLOATING_POINT /* * Convert the variable to a floating point number. Only for primitive types. */ double mprVarToFloat(MprVar *vp) { mprAssert(vp); switch (vp->type) { case MPR_TYPE_UNDEFINED: case MPR_TYPE_NULL: case MPR_TYPE_STRING_CFUNCTION: case MPR_TYPE_CFUNCTION: case MPR_TYPE_FUNCTION: case MPR_TYPE_OBJECT: case MPR_TYPE_PTR: return 0; case MPR_TYPE_BOOL: return (vp->boolean) ? 1.0 : 0.0; case MPR_TYPE_FLOAT: return vp->floating; case MPR_TYPE_INT: return (double) vp->integer; #if BLD_FEATURE_INT64 case MPR_TYPE_INT64: return (double) vp->integer64; #endif case MPR_TYPE_STRING: mprAssert(vp->string); return atof(vp->string); } /* Not reached */ return 0; } #endif /******************************************************************************/ /* * Convert the variable to a number type. Only works for primitive types. */ MprNum mprVarToNumber(MprVar *vp) { #if BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_INT64 return mprVarToInteger64(vp); #elif BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_FLOAT return mprVarToFloat(vp); #else return mprVarToInteger(vp); #endif } /******************************************************************************/ /* * Convert the variable to a number type. Only works for primitive types. */ MprNum mprParseNumber(char *s) { #if BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_INT64 return mprParseInteger64(s); #elif BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_FLOAT return mprParseFloat(s); #else return mprParseInteger(s); #endif } /******************************************************************************/ #if BLD_FEATURE_INT64 /* * Convert the variable to an Integer64 type. Only works for primitive types. */ int64 mprVarToInteger64(MprVar *vp) { mprAssert(vp); switch (vp->type) { case MPR_TYPE_UNDEFINED: case MPR_TYPE_NULL: case MPR_TYPE_STRING_CFUNCTION: case MPR_TYPE_CFUNCTION: case MPR_TYPE_FUNCTION: case MPR_TYPE_OBJECT: case MPR_TYPE_PTR: return 0; case MPR_TYPE_BOOL: return (vp->boolean) ? 1 : 0; #if BLD_FEATURE_FLOATING_POINT case MPR_TYPE_FLOAT: if (mprIsNan(vp->floating)) { return 0; } return (int64) vp->floating; #endif case MPR_TYPE_INT: return vp->integer; case MPR_TYPE_INT64: return vp->integer64; case MPR_TYPE_STRING: return mprParseInteger64(vp->string); } /* Not reached */ return 0; } /******************************************************************************/ /* * Convert the string buffer to an Integer64. */ int64 mprParseInteger64(char *str) { char *cp; int64 num64; int radix, c, negative; mprAssert(str); cp = str; num64 = 0; negative = 0; if (*cp == '-') { cp++; negative = 1; } /* * Parse a number. Observe hex and octal prefixes (0x, 0) */ if (*cp != '0') { /* * Normal numbers (Radix 10) */ while (isdigit((int) *cp)) { num64 = (*cp - '0') + (num64 * 10); cp++; } } else { cp++; if (tolower(*cp) == 'x') { cp++; radix = 16; while (*cp) { c = tolower(*cp); if (isdigit(c)) { num64 = (c - '0') + (num64 * radix); } else if (c >= 'a' && c <= 'f') { num64 = (c - 'a') + (num64 * radix); } else { break; } cp++; } } else{ radix = 8; while (*cp) { c = tolower(*cp); if (isdigit(c) && c < '8') { num64 = (c - '0') + (num64 * radix); } else { break; } cp++; } } } if (negative) { return 0 - num64; } return num64; } #endif /* BLD_FEATURE_INT64 */ /******************************************************************************/ /* * Convert the variable to an Integer type. Only works for primitive types. */ int mprVarToInteger(MprVar *vp) { mprAssert(vp); switch (vp->type) { case MPR_TYPE_UNDEFINED: case MPR_TYPE_NULL: case MPR_TYPE_STRING_CFUNCTION: case MPR_TYPE_CFUNCTION: case MPR_TYPE_FUNCTION: case MPR_TYPE_OBJECT: case MPR_TYPE_PTR: return 0; case MPR_TYPE_BOOL: return (vp->boolean) ? 1 : 0; #if BLD_FEATURE_FLOATING_POINT case MPR_TYPE_FLOAT: if (mprIsNan(vp->floating)) { return 0; } return (int) vp->floating; #endif case MPR_TYPE_INT: return vp->integer; #if BLD_FEATURE_INT64 case MPR_TYPE_INT64: return (int) vp->integer64; #endif case MPR_TYPE_STRING: return mprParseInteger(vp->string); } /* Not reached */ return 0; } /******************************************************************************/ /* * Convert the string buffer to an Integer. */ int mprParseInteger(char *str) { char *cp; int num; int radix, c, negative; mprAssert(str); cp = str; num = 0; negative = 0; if (*cp == '-') { cp++; negative = 1; } /* * Parse a number. Observe hex and octal prefixes (0x, 0) */ if (*cp != '0') { /* * Normal numbers (Radix 10) */ while (isdigit((int) *cp)) { num = (*cp - '0') + (num * 10); cp++; } } else { cp++; if (tolower(*cp) == 'x') { cp++; radix = 16; while (*cp) { c = tolower(*cp); if (isdigit(c)) { num = (c - '0') + (num * radix); } else if (c >= 'a' && c <= 'f') { num = (c - 'a') + (num * radix); } else { break; } cp++; } } else{ radix = 8; while (*cp) { c = tolower(*cp); if (isdigit(c) && c < '8') { num = (c - '0') + (num * radix); } else { break; } cp++; } } } if (negative) { return 0 - num; } return num; } /******************************************************************************/ #if BLD_FEATURE_FLOATING_POINT /* * Convert the string buffer to an Floating. */ double mprParseFloat(char *str) { return atof(str); } /******************************************************************************/ bool mprIsNan(double f) { #if WIN return _isnan(f); #elif VXWORKS /* FUTURE */ return (0); #else return (f == FP_NAN); #endif } /******************************************************************************/ bool mprIsInfinite(double f) { #if WIN return !_finite(f); #elif VXWORKS /* FUTURE */ return (0); #else return (f == FP_INFINITE); #endif } #endif /* BLD_FEATURE_FLOATING_POINT */ /******************************************************************************/ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim:tw=78 * vim600: sw=4 ts=4 fdm=marker * vim<600: sw=4 ts=4 */