123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646 |
- /*
- * Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
- /*
- hashtable2.m
- Copyright 1989-1996 NeXT Software, Inc.
- Created by Bertrand Serlet, Feb 89
- */
- #include "objc-private.h"
- #include "hashtable2.h"
- /* In order to improve efficiency, buckets contain a pointer to an array or directly the data when the array size is 1 */
- typedef union {
- const void *one;
- const void **many;
- } oneOrMany;
- /* an optimization consists of storing directly data when count = 1 */
-
- typedef struct {
- unsigned count;
- oneOrMany elements;
- } HashBucket;
- /* private data structure; may change */
-
- /*************************************************************************
- *
- * Macros and utilities
- *
- *************************************************************************/
- #define PTRSIZE sizeof(void *)
- #if !SUPPORT_ZONES
- # define DEFAULT_ZONE NULL
- # define ZONE_FROM_PTR(p) NULL
- # define ALLOCTABLE(z) ((NXHashTable *) malloc (sizeof (NXHashTable)))
- # define ALLOCBUCKETS(z,nb)((HashBucket *) calloc (nb, sizeof (HashBucket)))
- /* Return interior pointer so a table of classes doesn't look like objects */
- # define ALLOCPAIRS(z,nb) (1+(const void **) calloc (nb+1, sizeof (void *)))
- # define FREEPAIRS(p) (free((void*)(-1+p)))
- #else
- # define DEFAULT_ZONE malloc_default_zone()
- # define ZONE_FROM_PTR(p) malloc_zone_from_ptr(p)
- # define ALLOCTABLE(z) ((NXHashTable *) malloc_zone_malloc ((malloc_zone_t *)z,sizeof (NXHashTable)))
- # define ALLOCBUCKETS(z,nb)((HashBucket *) malloc_zone_calloc ((malloc_zone_t *)z, nb, sizeof (HashBucket)))
- /* Return interior pointer so a table of classes doesn't look like objects */
- # define ALLOCPAIRS(z,nb) (1+(const void **) malloc_zone_calloc ((malloc_zone_t *)z, nb+1, sizeof (void *)))
- # define FREEPAIRS(p) (free((void*)(-1+p)))
- #endif
- #if !SUPPORT_MOD
- /* nbBuckets must be a power of 2 */
- # define BUCKETOF(table, data) (((HashBucket *)table->buckets)+((*table->prototype->hash)(table->info, data) & (table->nbBuckets-1)))
- # define GOOD_CAPACITY(c) (c <= 1 ? 1 : 1 << (log2u (c-1)+1))
- # define MORE_CAPACITY(b) (b*2)
- #else
- /* iff necessary this modulo can be optimized since the nbBuckets is of the form 2**n-1 */
- # define BUCKETOF(table, data) (((HashBucket *)table->buckets)+((*table->prototype->hash)(table->info, data) % table->nbBuckets))
- # define GOOD_CAPACITY(c) (exp2m1u (log2u (c)+1))
- # define MORE_CAPACITY(b) (b*2+1)
- #endif
- #define ISEQUAL(table, data1, data2) ((data1 == data2) || (*table->prototype->isEqual)(table->info, data1, data2))
- /* beware of double evaluation */
-
- /*************************************************************************
- *
- * Global data and bootstrap
- *
- *************************************************************************/
-
- static int isEqualPrototype (const void *info, const void *data1, const void *data2) {
- NXHashTablePrototype *proto1 = (NXHashTablePrototype *) data1;
- NXHashTablePrototype *proto2 = (NXHashTablePrototype *) data2;
-
- return (proto1->hash == proto2->hash) && (proto1->isEqual == proto2->isEqual) && (proto1->free == proto2->free) && (proto1->style == proto2->style);
- };
-
- static uintptr_t hashPrototype (const void *info, const void *data) {
- NXHashTablePrototype *proto = (NXHashTablePrototype *) data;
-
- return NXPtrHash(info, (void*)proto->hash) ^ NXPtrHash(info, (void*)proto->isEqual) ^ NXPtrHash(info, (void*)proto->free) ^ (uintptr_t) proto->style;
- };
- void NXNoEffectFree (const void *info, void *data) {};
- static NXHashTablePrototype protoPrototype = {
- hashPrototype, isEqualPrototype, NXNoEffectFree, 0
- };
- static NXHashTable *prototypes = NULL;
- /* table of all prototypes */
- static void bootstrap (void) {
- free(malloc(8));
- prototypes = ALLOCTABLE (DEFAULT_ZONE);
- prototypes->prototype = &protoPrototype;
- prototypes->count = 1;
- prototypes->nbBuckets = 1; /* has to be 1 so that the right bucket is 0 */
- prototypes->buckets = ALLOCBUCKETS(DEFAULT_ZONE, 1);
- prototypes->info = NULL;
- ((HashBucket *) prototypes->buckets)[0].count = 1;
- ((HashBucket *) prototypes->buckets)[0].elements.one = &protoPrototype;
- };
- int NXPtrIsEqual (const void *info, const void *data1, const void *data2) {
- return data1 == data2;
- };
- /*************************************************************************
- *
- * On z'y va
- *
- *************************************************************************/
- NXHashTable *NXCreateHashTable (NXHashTablePrototype prototype, unsigned capacity, const void *info) {
- return NXCreateHashTableFromZone(prototype, capacity, info, DEFAULT_ZONE);
- }
- NXHashTable *NXCreateHashTableFromZone (NXHashTablePrototype prototype, unsigned capacity, const void *info, void *z) {
- NXHashTable *table;
- NXHashTablePrototype *proto;
-
- table = ALLOCTABLE(z);
- if (! prototypes) bootstrap ();
- if (! prototype.hash) prototype.hash = NXPtrHash;
- if (! prototype.isEqual) prototype.isEqual = NXPtrIsEqual;
- if (! prototype.free) prototype.free = NXNoEffectFree;
- if (prototype.style) {
- _objc_inform ("*** NXCreateHashTable: invalid style\n");
- return NULL;
- };
- proto = (NXHashTablePrototype *)NXHashGet (prototypes, &prototype);
- if (! proto) {
- proto
- = (NXHashTablePrototype *) malloc(sizeof (NXHashTablePrototype));
- bcopy ((const char*)&prototype, (char*)proto, sizeof (NXHashTablePrototype));
- (void) NXHashInsert (prototypes, proto);
- proto = (NXHashTablePrototype *)NXHashGet (prototypes, &prototype);
- if (! proto) {
- _objc_inform ("*** NXCreateHashTable: bug\n");
- return NULL;
- };
- };
- table->prototype = proto; table->count = 0; table->info = info;
- table->nbBuckets = GOOD_CAPACITY(capacity);
- table->buckets = ALLOCBUCKETS(z, table->nbBuckets);
- return table;
- }
- static void freeBucketPairs (void (*freeProc)(const void *info, void *data), HashBucket bucket, const void *info) {
- unsigned j = bucket.count;
- const void **pairs;
-
- if (j == 1) {
- (*freeProc) (info, (void *) bucket.elements.one);
- return;
- };
- pairs = bucket.elements.many;
- while (j--) {
- (*freeProc) (info, (void *) *pairs);
- pairs ++;
- };
- FREEPAIRS (bucket.elements.many);
- };
-
- static void freeBuckets (NXHashTable *table, int freeObjects) {
- unsigned i = table->nbBuckets;
- HashBucket *buckets = (HashBucket *) table->buckets;
-
- while (i--) {
- if (buckets->count) {
- freeBucketPairs ((freeObjects) ? table->prototype->free : NXNoEffectFree, *buckets, table->info);
- buckets->count = 0;
- buckets->elements.one = NULL;
- };
- buckets++;
- };
- };
-
- void NXFreeHashTable (NXHashTable *table) {
- freeBuckets (table, YES);
- free (table->buckets);
- free (table);
- };
-
- void NXEmptyHashTable (NXHashTable *table) {
- freeBuckets (table, NO);
- table->count = 0;
- }
- void NXResetHashTable (NXHashTable *table) {
- freeBuckets (table, YES);
- table->count = 0;
- }
- BOOL NXCompareHashTables (NXHashTable *table1, NXHashTable *table2) {
- if (table1 == table2) return YES;
- if (NXCountHashTable (table1) != NXCountHashTable (table2)) return NO;
- else {
- void *data;
- NXHashState state = NXInitHashState (table1);
- while (NXNextHashState (table1, &state, &data)) {
- if (! NXHashMember (table2, data)) return NO;
- }
- return YES;
- }
- }
- NXHashTable *NXCopyHashTable (NXHashTable *table) {
- NXHashTable *newt;
- NXHashState state = NXInitHashState (table);
- void *data;
- __unused void *z = ZONE_FROM_PTR(table);
-
- newt = ALLOCTABLE(z);
- newt->prototype = table->prototype; newt->count = 0;
- newt->info = table->info;
- newt->nbBuckets = table->nbBuckets;
- newt->buckets = ALLOCBUCKETS(z, newt->nbBuckets);
- while (NXNextHashState (table, &state, &data))
- NXHashInsert (newt, data);
- return newt;
- }
- unsigned NXCountHashTable (NXHashTable *table) {
- return table->count;
- }
- int NXHashMember (NXHashTable *table, const void *data) {
- HashBucket *bucket = BUCKETOF(table, data);
- unsigned j = bucket->count;
- const void **pairs;
-
- if (! j) return 0;
- if (j == 1) {
- return ISEQUAL(table, data, bucket->elements.one);
- };
- pairs = bucket->elements.many;
- while (j--) {
- /* we don't cache isEqual because lists are short */
- if (ISEQUAL(table, data, *pairs)) return 1;
- pairs ++;
- };
- return 0;
- }
- void *NXHashGet (NXHashTable *table, const void *data) {
- HashBucket *bucket = BUCKETOF(table, data);
- unsigned j = bucket->count;
- const void **pairs;
-
- if (! j) return NULL;
- if (j == 1) {
- return ISEQUAL(table, data, bucket->elements.one)
- ? (void *) bucket->elements.one : NULL;
- };
- pairs = bucket->elements.many;
- while (j--) {
- /* we don't cache isEqual because lists are short */
- if (ISEQUAL(table, data, *pairs)) return (void *) *pairs;
- pairs ++;
- };
- return NULL;
- }
- unsigned _NXHashCapacity (NXHashTable *table) {
- return table->nbBuckets;
- }
- void _NXHashRehashToCapacity (NXHashTable *table, unsigned newCapacity) {
- /* Rehash: we create a pseudo table pointing really to the old guys,
- extend self, copy the old pairs, and free the pseudo table */
- NXHashTable *old;
- NXHashState state;
- void *aux;
- __unused void *z = ZONE_FROM_PTR(table);
-
- old = ALLOCTABLE(z);
- old->prototype = table->prototype; old->count = table->count;
- old->nbBuckets = table->nbBuckets; old->buckets = table->buckets;
- table->nbBuckets = newCapacity;
- table->count = 0; table->buckets = ALLOCBUCKETS(z, table->nbBuckets);
- state = NXInitHashState (old);
- while (NXNextHashState (old, &state, &aux))
- (void) NXHashInsert (table, aux);
- freeBuckets (old, NO);
- if (old->count != table->count)
- _objc_inform("*** hashtable: count differs after rehashing; probably indicates a broken invariant: there are x and y such as isEqual(x, y) is TRUE but hash(x) != hash (y)\n");
- free (old->buckets);
- free (old);
- }
- static void _NXHashRehash (NXHashTable *table) {
- _NXHashRehashToCapacity (table, MORE_CAPACITY(table->nbBuckets));
- }
- void *NXHashInsert (NXHashTable *table, const void *data) {
- HashBucket *bucket = BUCKETOF(table, data);
- unsigned j = bucket->count;
- const void **pairs;
- const void **newt;
- __unused void *z = ZONE_FROM_PTR(table);
-
- if (! j) {
- bucket->count++; bucket->elements.one = data;
- table->count++;
- return NULL;
- };
- if (j == 1) {
- if (ISEQUAL(table, data, bucket->elements.one)) {
- const void *old = bucket->elements.one;
- bucket->elements.one = data;
- return (void *) old;
- };
- newt = ALLOCPAIRS(z, 2);
- newt[1] = bucket->elements.one;
- *newt = data;
- bucket->count++; bucket->elements.many = newt;
- table->count++;
- if (table->count > table->nbBuckets) _NXHashRehash (table);
- return NULL;
- };
- pairs = bucket->elements.many;
- while (j--) {
- /* we don't cache isEqual because lists are short */
- if (ISEQUAL(table, data, *pairs)) {
- const void *old = *pairs;
- *pairs = data;
- return (void *) old;
- };
- pairs ++;
- };
- /* we enlarge this bucket; and put new data in front */
- newt = ALLOCPAIRS(z, bucket->count+1);
- if (bucket->count) bcopy ((const char*)bucket->elements.many, (char*)(newt+1), bucket->count * PTRSIZE);
- *newt = data;
- FREEPAIRS (bucket->elements.many);
- bucket->count++; bucket->elements.many = newt;
- table->count++;
- if (table->count > table->nbBuckets) _NXHashRehash (table);
- return NULL;
- }
- void *NXHashInsertIfAbsent (NXHashTable *table, const void *data) {
- HashBucket *bucket = BUCKETOF(table, data);
- unsigned j = bucket->count;
- const void **pairs;
- const void **newt;
- __unused void *z = ZONE_FROM_PTR(table);
-
- if (! j) {
- bucket->count++; bucket->elements.one = data;
- table->count++;
- return (void *) data;
- };
- if (j == 1) {
- if (ISEQUAL(table, data, bucket->elements.one))
- return (void *) bucket->elements.one;
- newt = ALLOCPAIRS(z, 2);
- newt[1] = bucket->elements.one;
- *newt = data;
- bucket->count++; bucket->elements.many = newt;
- table->count++;
- if (table->count > table->nbBuckets) _NXHashRehash (table);
- return (void *) data;
- };
- pairs = bucket->elements.many;
- while (j--) {
- /* we don't cache isEqual because lists are short */
- if (ISEQUAL(table, data, *pairs))
- return (void *) *pairs;
- pairs ++;
- };
- /* we enlarge this bucket; and put new data in front */
- newt = ALLOCPAIRS(z, bucket->count+1);
- if (bucket->count) bcopy ((const char*)bucket->elements.many, (char*)(newt+1), bucket->count * PTRSIZE);
- *newt = data;
- FREEPAIRS (bucket->elements.many);
- bucket->count++; bucket->elements.many = newt;
- table->count++;
- if (table->count > table->nbBuckets) _NXHashRehash (table);
- return (void *) data;
- }
- void *NXHashRemove (NXHashTable *table, const void *data) {
- HashBucket *bucket = BUCKETOF(table, data);
- unsigned j = bucket->count;
- const void **pairs;
- const void **newt;
- __unused void *z = ZONE_FROM_PTR(table);
-
- if (! j) return NULL;
- if (j == 1) {
- if (! ISEQUAL(table, data, bucket->elements.one)) return NULL;
- data = bucket->elements.one;
- table->count--; bucket->count--; bucket->elements.one = NULL;
- return (void *) data;
- };
- pairs = bucket->elements.many;
- if (j == 2) {
- if (ISEQUAL(table, data, pairs[0])) {
- bucket->elements.one = pairs[1]; data = pairs[0];
- }
- else if (ISEQUAL(table, data, pairs[1])) {
- bucket->elements.one = pairs[0]; data = pairs[1];
- }
- else return NULL;
- FREEPAIRS (pairs);
- table->count--; bucket->count--;
- return (void *) data;
- };
- while (j--) {
- if (ISEQUAL(table, data, *pairs)) {
- data = *pairs;
- /* we shrink this bucket */
- newt = (bucket->count-1)
- ? ALLOCPAIRS(z, bucket->count-1) : NULL;
- if (bucket->count-1 != j)
- bcopy ((const char*)bucket->elements.many, (char*)newt, PTRSIZE*(bucket->count-j-1));
- if (j)
- bcopy ((const char*)(bucket->elements.many + bucket->count-j), (char*)(newt+bucket->count-j-1), PTRSIZE*j);
- FREEPAIRS (bucket->elements.many);
- table->count--; bucket->count--; bucket->elements.many = newt;
- return (void *) data;
- };
- pairs ++;
- };
- return NULL;
- }
- NXHashState NXInitHashState (NXHashTable *table) {
- NXHashState state;
-
- state.i = table->nbBuckets;
- state.j = 0;
- return state;
- };
-
- int NXNextHashState (NXHashTable *table, NXHashState *state, void **data) {
- HashBucket *buckets = (HashBucket *) table->buckets;
-
- while (state->j == 0) {
- if (state->i == 0) return NO;
- state->i--; state->j = buckets[state->i].count;
- }
- state->j--;
- buckets += state->i;
- *data = (void *) ((buckets->count == 1)
- ? buckets->elements.one : buckets->elements.many[state->j]);
- return YES;
- };
- /*************************************************************************
- *
- * Conveniences
- *
- *************************************************************************/
- uintptr_t NXPtrHash (const void *info, const void *data) {
- return (((uintptr_t) data) >> 16) ^ ((uintptr_t) data);
- };
-
- uintptr_t NXStrHash (const void *info, const void *data) {
- uintptr_t hash = 0;
- unsigned char *s = (unsigned char *) data;
- /* unsigned to avoid a sign-extend */
- /* unroll the loop */
- if (s) for (; ; ) {
- if (*s == '\0') break;
- hash ^= (uintptr_t) *s++;
- if (*s == '\0') break;
- hash ^= (uintptr_t) *s++ << 8;
- if (*s == '\0') break;
- hash ^= (uintptr_t) *s++ << 16;
- if (*s == '\0') break;
- hash ^= (uintptr_t) *s++ << 24;
- }
- return hash;
- };
-
- int NXStrIsEqual (const void *info, const void *data1, const void *data2) {
- if (data1 == data2) return YES;
- if (! data1) return ! strlen ((char *) data2);
- if (! data2) return ! strlen ((char *) data1);
- if (((char *) data1)[0] != ((char *) data2)[0]) return NO;
- return (strcmp ((char *) data1, (char *) data2)) ? NO : YES;
- };
-
- void NXReallyFree (const void *info, void *data) {
- free (data);
- };
- /* All the following functions are really private, made non-static only for the benefit of shlibs */
- static uintptr_t hashPtrStructKey (const void *info, const void *data) {
- return NXPtrHash(info, *((void **) data));
- };
- static int isEqualPtrStructKey (const void *info, const void *data1, const void *data2) {
- return NXPtrIsEqual (info, *((void **) data1), *((void **) data2));
- };
- static uintptr_t hashStrStructKey (const void *info, const void *data) {
- return NXStrHash(info, *((char **) data));
- };
- static int isEqualStrStructKey (const void *info, const void *data1, const void *data2) {
- return NXStrIsEqual (info, *((char **) data1), *((char **) data2));
- };
- const NXHashTablePrototype NXPtrPrototype = {
- NXPtrHash, NXPtrIsEqual, NXNoEffectFree, 0
- };
- const NXHashTablePrototype NXStrPrototype = {
- NXStrHash, NXStrIsEqual, NXNoEffectFree, 0
- };
- const NXHashTablePrototype NXPtrStructKeyPrototype = {
- hashPtrStructKey, isEqualPtrStructKey, NXReallyFree, 0
- };
- const NXHashTablePrototype NXStrStructKeyPrototype = {
- hashStrStructKey, isEqualStrStructKey, NXReallyFree, 0
- };
- /*************************************************************************
- *
- * Unique strings
- *
- *************************************************************************/
- #if !__OBJC2__ && !TARGET_OS_WIN32
- /* the implementation could be made faster at the expense of memory if the size of the strings were kept around */
- static NXHashTable *uniqueStrings = NULL;
- /* this is based on most apps using a few K of strings, and an average string size of 15 using sqrt(2*dataAlloced*perChunkOverhead) */
- #define CHUNK_SIZE 360
- static int accessUniqueString = 0;
- static char *z = NULL;
- static size_t zSize = 0;
- mutex_t NXUniqueStringLock;
- static const char *CopyIntoReadOnly (const char *str) {
- size_t len = strlen (str) + 1;
- char *result;
-
- if (len > CHUNK_SIZE/2) { /* dont let big strings waste space */
- result = (char *)malloc (len);
- bcopy (str, result, len);
- return result;
- }
- mutex_locker_t lock(NXUniqueStringLock);
- if (zSize < len) {
- zSize = CHUNK_SIZE *((len + CHUNK_SIZE - 1) / CHUNK_SIZE);
- /* not enough room, we try to allocate. If no room left, too bad */
- z = (char *)malloc (zSize);
- };
-
- result = z;
- bcopy (str, result, len);
- z += len;
- zSize -= len;
- return result;
- };
-
- NXAtom NXUniqueString (const char *buffer) {
- const char *previous;
-
- if (! buffer) return buffer;
- accessUniqueString++;
- if (! uniqueStrings)
- uniqueStrings = NXCreateHashTable (NXStrPrototype, 0, NULL);
- previous = (const char *) NXHashGet (uniqueStrings, buffer);
- if (previous) return previous;
- previous = CopyIntoReadOnly (buffer);
- if (NXHashInsert (uniqueStrings, previous)) {
- _objc_inform ("*** NXUniqueString: invariant broken\n");
- return NULL;
- };
- return previous;
- };
- NXAtom NXUniqueStringNoCopy (const char *string) {
- accessUniqueString++;
- if (! uniqueStrings)
- uniqueStrings = NXCreateHashTable (NXStrPrototype, 0, NULL);
- return (const char *) NXHashInsertIfAbsent (uniqueStrings, string);
- };
- #define BUF_SIZE 256
- NXAtom NXUniqueStringWithLength (const char *buffer, int length) {
- NXAtom atom;
- char *nullTermStr;
- char stackBuf[BUF_SIZE];
- if (length+1 > BUF_SIZE)
- nullTermStr = (char *)malloc (length+1);
- else
- nullTermStr = stackBuf;
- bcopy (buffer, nullTermStr, length);
- nullTermStr[length] = '\0';
- atom = NXUniqueString (nullTermStr);
- if (length+1 > BUF_SIZE)
- free (nullTermStr);
- return atom;
- };
- char *NXCopyStringBufferFromZone (const char *str, void *zone) {
- #if !SUPPORT_ZONES
- return strdup(str);
- #else
- return strcpy ((char *) malloc_zone_malloc((malloc_zone_t *)zone, strlen (str) + 1), str);
- #endif
- };
-
- char *NXCopyStringBuffer (const char *str) {
- return strdup(str);
- };
- #endif
|