예제 #1
0
unsigned int OSSerialize::ensureCapacity(unsigned int newCapacity)
{
	char *newData;

	if (newCapacity <= capacity)
		return capacity;

	// round up
	newCapacity = round_page_32(newCapacity);

	kern_return_t rc = kmem_realloc(kernel_map,
					(vm_offset_t)data,
					capacity,
					(vm_offset_t *)&newData,
					newCapacity);
	if (!rc) {
	    ACCUMSIZE(newCapacity);

	    // kmem realloc does not free the old address range
	    kmem_free(kernel_map, (vm_offset_t)data, capacity); 
	    ACCUMSIZE(-capacity);
	    
	    // kmem realloc does not zero out the new memory
	    // and this could end up going to user land
	    bzero(&newData[capacity], newCapacity - capacity);
		
	    data = newData;
	    capacity = newCapacity;
	}

	return capacity;
}
예제 #2
0
파일: OSSymbol.cpp 프로젝트: SbIm/xnu-env
OSSymbol *OSSymbolPool::insertSymbol(OSSymbol *sym)
{
    const char *cString = sym->string;
    Bucket *thisBucket;
    unsigned int j, inLen, hash;
    OSSymbol *probeSymbol, **list;

    hashSymbol(cString, &hash, &inLen); inLen++;
    thisBucket = &buckets[hash % nBuckets];
    j = thisBucket->count;

    if (!j) {
        thisBucket->symbolP = (OSSymbol **) sym;
        thisBucket->count++;
        count++;
        return sym;
    }

    if (j == 1) {
        probeSymbol = (OSSymbol *) thisBucket->symbolP;

        if (inLen == probeSymbol->length
        &&  strncmp(probeSymbol->string, cString, probeSymbol->length) == 0)
            return probeSymbol;

        list = (OSSymbol **) kalloc(2 * sizeof(OSSymbol *));
        ACCUMSIZE(2 * sizeof(OSSymbol *));
        /* @@@ gvdl: Zero test and panic if can't set up pool */
        list[0] = sym;
        list[1] = probeSymbol;
        thisBucket->symbolP = list;
        thisBucket->count++;
        count++;
        GROW_POOL();

        return sym;
    }

    for (list = thisBucket->symbolP; j--; list++) {
        probeSymbol = *list;
        if (inLen == probeSymbol->length
        &&  strncmp(probeSymbol->string, cString, probeSymbol->length) == 0)
            return probeSymbol;
    }

    j = thisBucket->count++;
    count++;
    list = (OSSymbol **) kalloc(thisBucket->count * sizeof(OSSymbol *));
    ACCUMSIZE(thisBucket->count * sizeof(OSSymbol *));
    /* @@@ gvdl: Zero test and panic if can't set up pool */
    list[0] = sym;
    bcopy(thisBucket->symbolP, list + 1, j * sizeof(OSSymbol *));
    kfree(thisBucket->symbolP, j * sizeof(OSSymbol *));
    ACCUMSIZE(-(j * sizeof(OSSymbol *)));
    thisBucket->symbolP = list;
    GROW_POOL();

    return sym;
}
예제 #3
0
unsigned int OSDictionary::ensureCapacity(unsigned int newCapacity)
{
    dictEntry *newDict;
    int oldSize, newSize;

    if (newCapacity <= capacity)
        return capacity;

    // round up
    newCapacity = (((newCapacity - 1) / capacityIncrement) + 1)
                * capacityIncrement;
    newSize = sizeof(dictEntry) * newCapacity;

    newDict = (dictEntry *) kalloc(newSize);
    if (newDict) {
        oldSize = sizeof(dictEntry) * capacity;

        bcopy(dictionary, newDict, oldSize);
        bzero(&newDict[capacity], newSize - oldSize);

        ACCUMSIZE(newSize - oldSize);
        kfree((vm_offset_t)dictionary, oldSize);

        dictionary = newDict;
        capacity = newCapacity;
    }

    return capacity;
}
예제 #4
0
unsigned int OSOrderedSet::ensureCapacity(unsigned int newCapacity)
{
    _Element *newArray;
    int oldSize, newSize;

    if (newCapacity <= capacity)
        return capacity;

    // round up
    newCapacity = (((newCapacity - 1) / capacityIncrement) + 1)
                * capacityIncrement;
    newSize = sizeof(_Element) * newCapacity;

    newArray = (_Element *) kalloc(newSize);
    if (newArray) {
        oldSize = sizeof(_Element) * capacity;

        ACCUMSIZE(newSize - oldSize);

        bcopy(array, newArray, oldSize);
        bzero(&newArray[capacity], newSize - oldSize);
        kfree((vm_offset_t)array, oldSize);
        array = newArray;
        capacity = newCapacity;
    }

    return capacity;
}
예제 #5
0
bool OSSerialize::initWithCapacity(unsigned int inCapacity)
{
    if (!super::init())
            return false;

    tags = OSDictionary::withCapacity(32);
    if (!tags) {
        return false;
    }

    tag = 0;
    length = 1;
    capacity = (inCapacity) ? round_page_32(inCapacity) : round_page_32(1);
    capacityIncrement = capacity;

    // allocate from the kernel map so that we can safely map this data
    // into user space (the primary use of the OSSerialize object)
    
    kern_return_t rc = kmem_alloc(kernel_map, (vm_offset_t *)&data, capacity);
    if (rc) {
        tags->release();
        tags = 0;
        return false;
    }
    bzero((void *)data, capacity);


    ACCUMSIZE(capacity);

    return true;
}
예제 #6
0
void *OSMetaClass::preModLoad(const char *kmodName)
{
    if (!loadLock) {
        loadLock = mutex_alloc(ETAP_IO_AHA);
	mutex_lock(loadLock);
    }
    else
	mutex_lock(loadLock);

    sStalled = (StalledData *) kalloc(sizeof(*sStalled));
    if (sStalled) {
	sStalled->classes  = (OSMetaClass **)
			kalloc(kKModCapacityIncrement * sizeof(OSMetaClass *));
	if (!sStalled->classes) {
	    kfree((vm_offset_t) sStalled, sizeof(*sStalled));
	    return 0;
	}
	ACCUMSIZE((kKModCapacityIncrement * sizeof(OSMetaClass *)) + sizeof(*sStalled));

        sStalled->result   = kOSReturnSuccess;
	sStalled->capacity = kKModCapacityIncrement;
	sStalled->count	   = 0;
	sStalled->kmodName = kmodName;
	bzero(sStalled->classes, kKModCapacityIncrement * sizeof(OSMetaClass *));
    }

    return sStalled;
}
예제 #7
0
파일: OSSymbol.cpp 프로젝트: SbIm/xnu-env
void OSSymbolPool::reconstructSymbols(bool grow)
{
    unsigned int new_nBuckets = nBuckets;
    OSSymbol *insert;
    OSSymbolPoolState state;

    if (grow) {
        new_nBuckets += new_nBuckets + 1;
    } else {
       /* Don't shrink the pool below the default initial size.
        */
        if (nBuckets <= INITIAL_POOL_SIZE) {
            return;
        }
        new_nBuckets = (new_nBuckets - 1) / 2;
    }

   /* Create old pool to iterate after doing above check, cause it
    * gets finalized at return.
    */
    OSSymbolPool old(this);

    count = 0;
    nBuckets = new_nBuckets;
    buckets = (Bucket *) kalloc(nBuckets * sizeof(Bucket));
    ACCUMSIZE(nBuckets * sizeof(Bucket));
    /* @@@ gvdl: Zero test and panic if can't set up pool */
    bzero(buckets, nBuckets * sizeof(Bucket));

    state = old.initHashState();
    while ( (insert = old.nextHashState(&state)) )
        insertSymbol(insert);
}
예제 #8
0
파일: OSMetaClass.cpp 프로젝트: Prajna/xnu
void *
OSMetaClass::preModLoad(const char * kextIdentifier)
{
    IOLockLock(sStalledClassesLock);

    assert (sStalled == NULL);
    sStalled = (StalledData *)kalloc(sizeof(* sStalled));
    if (sStalled) {
        sStalled->classes = (OSMetaClass **)
            kalloc(kKModCapacityIncrement * sizeof(OSMetaClass *));
        if (!sStalled->classes) {
            kfree(sStalled, sizeof(*sStalled));
            return 0;
        }
        ACCUMSIZE((kKModCapacityIncrement * sizeof(OSMetaClass *)) +
            sizeof(*sStalled));

        sStalled->result   = kOSReturnSuccess;
        sStalled->capacity = kKModCapacityIncrement;
        sStalled->count    = 0;
        sStalled->kextIdentifier = kextIdentifier;
        bzero(sStalled->classes, kKModCapacityIncrement * sizeof(OSMetaClass *));
    }

    // keep sStalledClassesLock locked until postModLoad
    
    return sStalled;
}
예제 #9
0
unsigned int OSOrderedSet::ensureCapacity(unsigned int newCapacity)
{
    _Element *newArray;
    unsigned int finalCapacity, oldSize, newSize;

    if (newCapacity <= capacity)
        return capacity;

    // round up
    finalCapacity = (((newCapacity - 1) / capacityIncrement) + 1)
                * capacityIncrement;
    if ((finalCapacity < newCapacity) ||
        (finalCapacity > (UINT_MAX / sizeof(_Element)))) {
        return capacity;
    }
    newSize = sizeof(_Element) * finalCapacity;

    newArray = (_Element *) kalloc(newSize);
    if (newArray) {
        oldSize = sizeof(_Element) * capacity;

        ACCUMSIZE(newSize - oldSize);

        bcopy(array, newArray, oldSize);
        bzero(&newArray[capacity], newSize - oldSize);
        kfree(array, oldSize);
        array = newArray;
        capacity = finalCapacity;
    }

    return capacity;
}
예제 #10
0
bool OSSerialize::initWithCapacity(unsigned int inCapacity)
{
    if (!super::init())
            return false;

    tags = OSDictionary::withCapacity(32);
    if (!tags) {
        return false;
    }

    tag = 0;
    length = 1;
    capacity = (inCapacity) ? inCapacity : (100);
    capacityIncrement = capacity;

    // allocate from the kernel map so that we can safely map this data
    // into user space (the primary use of the OSSerialize object)
    
    data = (char*)kalloc(capacity);
	assert(data);
	
    bzero((void *)data, capacity);


    ACCUMSIZE(capacity);

    return true;
}
예제 #11
0
bool OSOrderedSet::
initWithCapacity(unsigned int inCapacity,
                 OSOrderFunction inOrdering, void *inOrderingRef)
{
    unsigned int size;

    if (!super::init())
        return false;

    if (inCapacity > (UINT_MAX / sizeof(_Element)))
        return false;

    size = sizeof(_Element) * inCapacity;
    array = (_Element *) kalloc(size);
    if (!array)
        return false;

    count = 0;
    capacity = inCapacity;
    capacityIncrement = (inCapacity)? inCapacity : 16;
    ordering = inOrdering;
    orderingRef = inOrderingRef;

    bzero(array, size);
    ACCUMSIZE(size);

    return this;	
}
예제 #12
0
void OSString::free()
{
    if ( !(flags & kOSStringNoCopy) && string) {
        kfree(string, (vm_size_t)length);
        ACCUMSIZE(-length);
    }

    super::free();
}
예제 #13
0
파일: OSSymbol.cpp 프로젝트: SbIm/xnu-env
void * OSSymbolPool::operator new(size_t size)
{
    void *mem = (void *)kalloc(size);
    ACCUMSIZE(size);
    assert(mem);
    bzero(mem, size);

    return mem;
}
예제 #14
0
파일: OSSymbol.cpp 프로젝트: SbIm/xnu-env
OSSymbolPool::~OSSymbolPool()
{
    if (buckets) {
        kfree(buckets, nBuckets * sizeof(Bucket));
        ACCUMSIZE(-(nBuckets * sizeof(Bucket)));
    }

    if (poolGate)
        lck_mtx_free(poolGate, IOLockGroup);
}
예제 #15
0
void OSDictionary::free()
{
    flushCollection();
    if (dictionary) {
        kfree((vm_offset_t)dictionary, capacity * sizeof(dictEntry));
        ACCUMSIZE( -(capacity * sizeof(dictEntry)) );
    }

    super::free();
}
예제 #16
0
void OSSerialize::free()
{
    if (tags)
        tags->release();

    if (data) {
	kmem_free(kernel_map, (vm_offset_t)data, capacity); 
        ACCUMSIZE( -capacity );
    }
    super::free();
}
예제 #17
0
void OSSerialize::free()
{
    if (tags)
        tags->release();

    if (data) {
		kfree(data, capacity);
        ACCUMSIZE( -capacity );
    }
    super::free();
}
예제 #18
0
void OSOrderedSet::free()
{
    flushCollection();

    if (array) {
        kfree((vm_offset_t)array, sizeof(_Element) * capacity);
        ACCUMSIZE( -(sizeof(_Element) * capacity) );
    }

    super::free();
}
예제 #19
0
void OSOrderedSet::free()
{
    (void) super::setOptions(0, kImmutable);
    flushCollection();

    if (array) {
        kfree(array, sizeof(_Element) * capacity);
        ACCUMSIZE( -(sizeof(_Element) * capacity) );
    }

    super::free();
}
예제 #20
0
파일: OSSymbol.cpp 프로젝트: SbIm/xnu-env
bool OSSymbolPool::init()
{
    count = 0;
    nBuckets = INITIAL_POOL_SIZE;
    buckets = (Bucket *) kalloc(nBuckets * sizeof(Bucket));
    ACCUMSIZE(nBuckets * sizeof(Bucket));
    if (!buckets)
        return false;

    bzero(buckets, nBuckets * sizeof(Bucket));

    poolGate = lck_mtx_alloc_init(IOLockGroup, LCK_ATTR_NULL);

    return poolGate != 0;
}
예제 #21
0
bool OSCollectionIterator::isValid()
{
    if (!collIterator) {
        collIterator = (void *)kalloc(collection->iteratorSize());
	ACCUMSIZE(collection->iteratorSize());
        if (!collection->initIterator(collIterator))
            return false;
        initialUpdateStamp = collection->updateStamp;
        valid = true;
    }
    else if (!valid || collection->updateStamp != initialUpdateStamp)
        return false;
    
    return true;
}
예제 #22
0
void OSCollectionIterator::free()
{
    if (collIterator) {
        kfree(collIterator, collection->iteratorSize());
	ACCUMSIZE(-(collection->iteratorSize()));
        collIterator = 0;
    }

    if (collection) {
        collection->release();
        collection = 0;
    }

    super::free();
}
예제 #23
0
bool OSString::initWithCString(const char *cString)
{
    if (!cString || !super::init())
        return false;

    length = strlen(cString) + 1;
    string = (char *) kalloc(length);
    if (!string)
        return false;

    bcopy(cString, string, length);

    ACCUMSIZE(length);

    return true;
}
예제 #24
0
파일: OSMetaClass.cpp 프로젝트: Prajna/xnu
/*********************************************************************
* The core constructor for a MetaClass (defined with this name always
* but within the scope of its represented class).
*
* MetaClass constructors are invoked in OSRuntimeInitializeCPP(),
* in between calls to OSMetaClass::preModLoad(), which sets up for
* registration, and OSMetaClass::postModLoad(), which actually
* records all the class/kext relationships of the new MetaClasses.
*********************************************************************/
OSMetaClass::OSMetaClass(
    const char        * inClassName,
    const OSMetaClass * inSuperClass,
    unsigned int        inClassSize)
{
    instanceCount = 0;
    classSize = inClassSize;
    superClassLink = inSuperClass;

   /* Hack alert: We are just casting inClassName and storing it in
    * an OSString * instance variable. This may be because you can't
    * create C++ objects in static constructors, but I really don't know!
    */
    className = (const OSSymbol *)inClassName;

    // sStalledClassesLock taken in preModLoad
    if (!sStalled) {
       /* There's no way we can look up the kext here, unfortunately.
        */
        OSKextLog(/* kext */ NULL, kOSMetaClassLogSpec,
            "OSMetaClass: preModLoad() wasn't called for class %s "
            "(runtime internal error).",
            inClassName);
    } else if (!sStalled->result) {
        // Grow stalled array if neccessary
        if (sStalled->count >= sStalled->capacity) {
            OSMetaClass **oldStalled = sStalled->classes;
            int oldSize = sStalled->capacity * sizeof(OSMetaClass *);
            int newSize = oldSize
                + kKModCapacityIncrement * sizeof(OSMetaClass *);

            sStalled->classes = (OSMetaClass **)kalloc(newSize);
            if (!sStalled->classes) {
                sStalled->classes = oldStalled;
                sStalled->result = kOSMetaClassNoTempData;
                return;
            }

            sStalled->capacity += kKModCapacityIncrement;
            memmove(sStalled->classes, oldStalled, oldSize);
            kfree(oldStalled, oldSize);
            ACCUMSIZE(newSize - oldSize);
        }

        sStalled->classes[sStalled->count++] = this;
    }
}
예제 #25
0
bool OSString::initWithStringOfLength(const char *cString, size_t inlength)
{
    if (!cString || !super::init())
        return false;

    length = inlength + 1;
    string = (char *) kalloc(length);
    if (!string)
        return false;

    bcopy(cString, string, inlength);
    string[inlength] = 0;

    ACCUMSIZE(length);

    return true;
}
예제 #26
0
void OSCollectionIterator::reset()
{
    valid = false;

    if (!collIterator) {
        collIterator = (void *)kalloc(collection->iteratorSize());
	ACCUMSIZE(collection->iteratorSize());
        if (!collIterator)
            return;
    }

    if (!collection->initIterator(collIterator))
        return;

    initialUpdateStamp = collection->updateStamp;
    valid = true;
}
예제 #27
0
파일: OSSymbol.cpp 프로젝트: SbIm/xnu-env
void OSSymbol::checkForPageUnload(void *startAddr, void *endAddr)
{
    OSSymbol *probeSymbol;
    OSSymbolPoolState state;

    pool->closeGate();
    state = pool->initHashState();
    while ( (probeSymbol = pool->nextHashState(&state)) ) {
        if (probeSymbol->string >= startAddr && probeSymbol->string < endAddr) {
            const char *oldString = probeSymbol->string;

            probeSymbol->string = (char *) kalloc(probeSymbol->length);
	    ACCUMSIZE(probeSymbol->length);
            bcopy(oldString, probeSymbol->string, probeSymbol->length);
            probeSymbol->flags &= ~kOSStringNoCopy;
        }
    }
    pool->openGate();
}
예제 #28
0
bool OSDictionary::initWithCapacity(unsigned int inCapacity)
{
    if (!super::init())
        return false;

    int size = inCapacity * sizeof(dictEntry);

    dictionary = (dictEntry *) kalloc(size);
    if (!dictionary)
        return false;

    bzero(dictionary, size);
    ACCUMSIZE(size);

    count = 0;
    capacity = inCapacity;
    capacityIncrement = (inCapacity)? inCapacity : 16;

    return true;	
}
예제 #29
0
OSMetaClass::OSMetaClass(const char *inClassName,
                         const OSMetaClass *inSuperClass,
                         unsigned int inClassSize)
{
    instanceCount = 0;
    classSize = inClassSize;
    superClassLink = inSuperClass;

    className = (const OSSymbol *) inClassName;

    if (!sStalled) {
	printf("OSMetaClass::preModLoad wasn't called for %s, "
	       "runtime internal error\n", inClassName);
    } else if (!sStalled->result) {
	// Grow stalled array if neccessary
	if (sStalled->count >= sStalled->capacity) {
	    OSMetaClass **oldStalled = sStalled->classes;
	    int oldSize = sStalled->capacity * sizeof(OSMetaClass *);
	    int newSize = oldSize
			+ kKModCapacityIncrement * sizeof(OSMetaClass *);

	    sStalled->classes = (OSMetaClass **) kalloc(newSize);
	    if (!sStalled->classes) {
		sStalled->classes = oldStalled;
		sStalled->result = kOSMetaClassNoTempData;
		return;
	    }

	    sStalled->capacity += kKModCapacityIncrement;
	    memmove(sStalled->classes, oldStalled, oldSize);
	    kfree((vm_offset_t)oldStalled, oldSize);
	    ACCUMSIZE(newSize - oldSize);
	}

	sStalled->classes[sStalled->count++] = this;
    }
}
예제 #30
0
파일: OSMetaClass.cpp 프로젝트: Prajna/xnu
OSReturn
OSMetaClass::postModLoad(void * loadHandle)
{
    OSReturn         result     = kOSReturnSuccess;
    OSSymbol       * myKextName = 0;  // must release
    OSKext         * myKext     = 0;  // must release

    if (!sStalled || loadHandle != sStalled) {
        result = kOSMetaClassInternal;
        goto finish;
    }
    
    if (sStalled->result) {
        result = sStalled->result;
    } else switch (sBootstrapState) {

        case kNoDictionaries:
            sBootstrapState = kMakingDictionaries;
            // No break; fall through
            
        case kMakingDictionaries:
            sAllClassesDict = OSDictionary::withCapacity(kClassCapacityIncrement);
            if (!sAllClassesDict) {
                result = kOSMetaClassNoDicts;
                break;
            }

        // No break; fall through

        case kCompletedBootstrap:
        {
            unsigned int i;
            myKextName = const_cast<OSSymbol *>(OSSymbol::withCStringNoCopy(
                sStalled->kextIdentifier));
            
            if (!sStalled->count) {
                break;  // Nothing to do so just get out
            }
            
            myKext = OSKext::lookupKextWithIdentifier(myKextName);
            if (!myKext) {
                result = kOSMetaClassNoKext;

               /* Log this error here so we can include the kext name.
                */
                OSKextLog(/* kext */ NULL, kOSMetaClassLogSpec,
                    "OSMetaClass: Can't record classes for kext %s - kext not found.",
                    sStalled->kextIdentifier);
                break;
            }
            
           /* First pass checking classes aren't already loaded. If any already
            * exist, we don't register any, and so we don't technically have
            * to do any C++ teardown.
            *
            * Hack alert: me->className has been a C string until now.
            * We only release the OSSymbol if we store the kext.
            */
            IOLockLock(sAllClassesLock);
            for (i = 0; i < sStalled->count; i++) {
                OSMetaClass * me = sStalled->classes[i];
                OSMetaClass * orig = OSDynamicCast(OSMetaClass,
                    sAllClassesDict->getObject((const char *)me->className));
                
                if (orig) {

                   /* Log this error here so we can include the class name.
                    * xxx - we should look up the other kext that defines the class
                    */
                    OSKextLog(myKext, kOSMetaClassLogSpec,
                        "OSMetaClass: Kext %s class %s is a duplicate;"
                        "kext %s already has a class by that name.",
                         sStalled->kextIdentifier, (const char *)me->className,
                        ((OSKext *)orig->reserved)->getIdentifierCString());
                    result = kOSMetaClassDuplicateClass;
                    break;
                }
            }
            IOLockUnlock(sAllClassesLock);
            
           /* Bail if we didn't go through the entire list of new classes
            * (if we hit a duplicate).
            */
            if (i != sStalled->count) {
                break;
            }

            // Second pass symbolling strings and inserting classes in dictionary
            IOLockLock(sAllClassesLock);
            for (i = 0; i < sStalled->count; i++) {
                OSMetaClass * me = sStalled->classes[i];
                
               /* Hack alert: me->className has been a C string until now.
                * We only release the OSSymbol in ~OSMetaClass()
                * if we set the reference to the kext.
                */
                me->className = 
                    OSSymbol::withCStringNoCopy((const char *)me->className);

                // xxx - I suppose if these fail we're going to panic soon....
                sAllClassesDict->setObject(me->className, me);
                
               /* Do not retain the kext object here.
                */
                me->reserved = (ExpansionData *)myKext;
                if (myKext) {
                    result = myKext->addClass(me, sStalled->count);
                    if (result != kOSReturnSuccess) {
                       /* OSKext::addClass() logs with kOSMetaClassNoInsKModSet. */
                        break;
                    }
                }
            }
            IOLockUnlock(sAllClassesLock);
            sBootstrapState = kCompletedBootstrap;
            break;
        }
            
        default:
            result = kOSMetaClassInternal;
            break;
    }
    
finish:
   /* Don't call logError() for success or the conditions logged above
    * or by called function.
    */
    if (result != kOSReturnSuccess &&
        result != kOSMetaClassNoInsKModSet &&
        result != kOSMetaClassDuplicateClass &&
        result != kOSMetaClassNoKext) {

        OSMetaClassLogErrorForKext(result, myKext);
    }

    OSSafeRelease(myKextName);
    OSSafeRelease(myKext);

    if (sStalled) {
        ACCUMSIZE(-(sStalled->capacity * sizeof(OSMetaClass *) +
            sizeof(*sStalled)));
        kfree(sStalled->classes, sStalled->capacity * sizeof(OSMetaClass *));
        kfree(sStalled, sizeof(*sStalled));
        sStalled = 0;
    }
    
    IOLockUnlock(sStalledClassesLock);

    return result;
}