@@ -86,7 +86,7 @@ The code in isValidCodePoint() is derived from the ICU code in
8686#include < string.h>
8787#include < assert.h>
8888#include < sys/errno.h>
89-
89+ # include < math.h >
9090
9191#import " JSONKit.h"
9292
@@ -97,8 +97,12 @@ The code in isValidCodePoint() is derived from the ICU code in
9797#include < CoreFoundation/CFNumber.h>
9898
9999// #import <Foundation/Foundation.h>
100+ #import < Foundation/NSArray.h>
101+ #import < Foundation/NSData.h>
100102#import < Foundation/NSDictionary.h>
101103#import < Foundation/NSException.h>
104+ #import < Foundation/NSNull.h>
105+ #import < Foundation/NSScriptClassDescription.h>
102106
103107// For DJB hash.
104108#define JK_HASH_INIT (5381UL )
@@ -120,6 +124,9 @@ The code in isValidCodePoint() is derived from the ICU code in
120124// JK_STACK_OBJS is the default number of spaces reserved on the stack for temporarily storing pointers to Obj-C objects before they can be transfered to a NSArray / NSDictionary.
121125#define JK_STACK_OBJS (1024UL * 1UL )
122126
127+ #define JK_JSONBUFFER_SIZE (1024UL * 64UL )
128+ #define JK_UTF8BUFFER_SIZE (1024UL * 16UL )
129+
123130
124131
125132#if defined (__GNUC__) && (__GNUC__ >= 4)
@@ -1388,3 +1395,308 @@ - (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags
13881395}
13891396
13901397@end
1398+
1399+ typedef struct {
1400+ void *stringClass;
1401+ void *numberClass;
1402+ void *arrayClass;
1403+ void *dictionaryClass;
1404+ void *nullClass;
1405+ } JKFastClassLookup;
1406+
1407+ enum {
1408+ JKClassUnknown = 0 ,
1409+ JKClassString = 1 ,
1410+ JKClassNumber = 2 ,
1411+ JKClassArray = 3 ,
1412+ JKClassDictionary= 4 ,
1413+ JKClassNull = 5 ,
1414+ };
1415+
1416+ typedef struct {
1417+ JKManagedBuffer utf8ConversionBuffer;
1418+ JKManagedBuffer stringBuffer;
1419+ size_t atIndex;
1420+ JKFastClassLookup fastClassLookup;
1421+ } JKEncodeState;
1422+
1423+ static int jk_printf (JKEncodeState *encodeState, const char *format, ...) {
1424+ va_list varArgsList;
1425+ va_start (varArgsList, format);
1426+ va_end (varArgsList);
1427+
1428+ if (encodeState->stringBuffer .bytes .length < encodeState->atIndex ) { return (1 ); }
1429+ if ((encodeState->stringBuffer .bytes .length - encodeState->atIndex ) < 1024L ) { if (jk_managedBuffer_resize (&encodeState->stringBuffer , encodeState->atIndex + 4096UL ) == NULL ) { return (1 ); } }
1430+
1431+ char *atPtr = (char *)encodeState->stringBuffer .bytes .ptr + encodeState->atIndex ;
1432+ ssize_t remaining = encodeState->stringBuffer .bytes .length - encodeState->atIndex ;
1433+
1434+ int printfAdded = vsnprintf (atPtr, remaining, format, varArgsList);
1435+
1436+ if (printfAdded > remaining) {
1437+ if (jk_managedBuffer_resize (&encodeState->stringBuffer , encodeState->atIndex + printfAdded + 1024UL ) == NULL ) { return (1 ); }
1438+ vsnprintf (atPtr, remaining, format, varArgsList);
1439+ }
1440+
1441+ encodeState->atIndex += printfAdded;
1442+ return (0 );
1443+ }
1444+
1445+ static int jk_write (JKEncodeState *encodeState, const char *format) {
1446+ if (encodeState->stringBuffer .bytes .length < encodeState->atIndex ) { return (1 ); }
1447+ if ((encodeState->stringBuffer .bytes .length - encodeState->atIndex ) < 1024L ) { if (jk_managedBuffer_resize (&encodeState->stringBuffer , encodeState->atIndex + 4096UL ) == NULL ) { return (1 ); } }
1448+
1449+ char *atPtr = (char *)encodeState->stringBuffer .bytes .ptr + encodeState->atIndex ;
1450+ ssize_t remaining = encodeState->stringBuffer .bytes .length - encodeState->atIndex ;
1451+
1452+ ssize_t idx = 0L , added = 0L ;
1453+ for (added = 0L , idx = 0L ; format[added] != 0 ; added++) { if (idx < remaining) { atPtr[idx++] = format[added]; } }
1454+
1455+ if (added > remaining) {
1456+ if (jk_managedBuffer_resize (&encodeState->stringBuffer , encodeState->atIndex + added + 1024UL ) == NULL ) { return (1 ); }
1457+ for (added = 0L , idx = 0L ; format[added] != 0 ; added++) { if (idx < remaining) { atPtr[idx++] = format[added]; } }
1458+ }
1459+
1460+ atPtr[idx] = 0 ;
1461+ encodeState->atIndex += added;
1462+ return (0 );
1463+ }
1464+
1465+
1466+
1467+ static int jk_add_atom_to_buffer (JKEncodeState *encodeState, void *objectPtr) {
1468+ NSCParameterAssert ((encodeState != NULL ) && (objectPtr != NULL ));
1469+ NSCParameterAssert (encodeState->atIndex < encodeState->stringBuffer.bytes.length);
1470+
1471+ id object = objectPtr;
1472+
1473+ if (((encodeState->atIndex + 256UL ) > encodeState->stringBuffer .bytes .length ) && (jk_managedBuffer_resize (&encodeState->stringBuffer , encodeState->atIndex + 256UL ) == NULL )) { return (1 ); }
1474+
1475+ int isClass = JKClassUnknown;
1476+
1477+ if (object->isa == encodeState->fastClassLookup .stringClass ) { isClass = JKClassString; }
1478+ else if (object->isa == encodeState->fastClassLookup .numberClass ) { isClass = JKClassNumber; }
1479+ else if (object->isa == encodeState->fastClassLookup .arrayClass ) { isClass = JKClassArray; }
1480+ else if (object->isa == encodeState->fastClassLookup .dictionaryClass ) { isClass = JKClassDictionary; }
1481+ else if (object->isa == encodeState->fastClassLookup .nullClass ) { isClass = JKClassNull; }
1482+ else {
1483+ if ([object isKindOfClass: [NSString class ]]) { encodeState->fastClassLookup .stringClass = object->isa ; isClass = JKClassString; }
1484+ else if ([object isKindOfClass: [NSNumber class ]]) { encodeState->fastClassLookup .numberClass = object->isa ; isClass = JKClassNumber; }
1485+ else if ([object isKindOfClass: [NSArray class ]]) { encodeState->fastClassLookup .arrayClass = object->isa ; isClass = JKClassArray; }
1486+ else if ([object isKindOfClass: [NSDictionary class ]]) { encodeState->fastClassLookup .dictionaryClass = object->isa ; isClass = JKClassDictionary; }
1487+ else if ([object isKindOfClass: [NSNull class ]]) { encodeState->fastClassLookup .nullClass = object->isa ; isClass = JKClassNull; }
1488+ }
1489+
1490+ switch (isClass) {
1491+ case JKClassString:
1492+ {
1493+ {
1494+ const unsigned char *cStringPtr = (const unsigned char *)CFStringGetCStringPtr ((CFStringRef)object, kCFStringEncodingMacRoman );
1495+ if (cStringPtr != NULL ) {
1496+ unsigned char *atPtr = encodeState->stringBuffer .bytes .ptr + encodeState->atIndex ;
1497+ const unsigned char *utf8String = cStringPtr;
1498+
1499+ size_t utf8Idx = 0UL , added = 0UL ;
1500+ atPtr[added++] = ' \" ' ;
1501+ for (utf8Idx = 0UL ; utf8String[utf8Idx] != 0 ; utf8Idx++) {
1502+ NSCParameterAssert (((&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex + added]) - encodeState->stringBuffer.bytes.ptr) < encodeState->stringBuffer.bytes.length);
1503+ NSCParameterAssert (encodeState->atIndex < encodeState->stringBuffer.bytes.length);
1504+ if (((encodeState->atIndex + added + 256UL ) > encodeState->stringBuffer .bytes .length )) {
1505+ if ((jk_managedBuffer_resize (&encodeState->stringBuffer , encodeState->atIndex + added + 1024UL ) == NULL )) { return (1 ); }
1506+ atPtr = encodeState->stringBuffer .bytes .ptr + encodeState->atIndex ;
1507+ }
1508+ if (utf8String[utf8Idx] >= 0x80 ) { goto slowUTF8Path; }
1509+ if (utf8String[utf8Idx] < 0x20 ) {
1510+ switch (utf8String[utf8Idx]) {
1511+ case ' \b ' : atPtr[added++] = ' \\ ' ; atPtr[added++] = ' b' ; break ;
1512+ case ' \f ' : atPtr[added++] = ' \\ ' ; atPtr[added++] = ' f' ; break ;
1513+ case ' \n ' : atPtr[added++] = ' \\ ' ; atPtr[added++] = ' n' ; break ;
1514+ case ' \r ' : atPtr[added++] = ' \\ ' ; atPtr[added++] = ' r' ; break ;
1515+ case ' \t ' : atPtr[added++] = ' \\ ' ; atPtr[added++] = ' t' ; break ;
1516+ default : jk_printf (encodeState, " \\ u%4.4x " , utf8String[utf8Idx]); break ;
1517+ }
1518+ } else {
1519+ if ((utf8String[utf8Idx] == ' \" ' ) || (utf8String[utf8Idx] == ' \\ ' )) { atPtr[added++] = ' \\ ' ; }
1520+ atPtr[added++] = utf8String[utf8Idx];
1521+ }
1522+ }
1523+ atPtr[added++] = ' \" ' ;
1524+ atPtr[added] = 0 ;
1525+ encodeState->atIndex += added;
1526+ return (0 );
1527+ }
1528+ }
1529+
1530+ slowUTF8Path:
1531+ {
1532+ CFIndex stringLength = CFStringGetLength ((CFStringRef)object);
1533+ CFIndex maxStringUTF8Length = CFStringGetMaximumSizeForEncoding (stringLength, kCFStringEncodingUTF8 ) + 32L ;
1534+
1535+ if (((size_t )maxStringUTF8Length > encodeState->utf8ConversionBuffer .bytes .length ) && (jk_managedBuffer_resize (&encodeState->utf8ConversionBuffer , maxStringUTF8Length + 1024UL ) == NULL )) { return (1 ); }
1536+
1537+ CFIndex usedBytes = 0L , convertedCount = 0L ;
1538+ convertedCount = CFStringGetBytes ((CFStringRef)object, CFRangeMake (0L , stringLength), kCFStringEncodingUTF8 , ' ?' , NO , encodeState->utf8ConversionBuffer .bytes .ptr , encodeState->utf8ConversionBuffer .bytes .length - 16L , &usedBytes);
1539+ encodeState->utf8ConversionBuffer .bytes .ptr [usedBytes] = 0 ;
1540+
1541+ if (((encodeState->atIndex + maxStringUTF8Length) > encodeState->stringBuffer .bytes .length ) && (jk_managedBuffer_resize (&encodeState->stringBuffer , encodeState->atIndex + maxStringUTF8Length + 1024UL ) == NULL )) { return (1 ); }
1542+
1543+ unsigned char *atPtr = encodeState->stringBuffer .bytes .ptr + encodeState->atIndex ;
1544+ const unsigned char *utf8String = encodeState->utf8ConversionBuffer .bytes .ptr ;
1545+
1546+ size_t utf8Idx = 0UL , added = 0UL ;
1547+ atPtr[added++] = ' \" ' ;
1548+ for (utf8Idx = 0UL ; utf8String[utf8Idx] != 0 ; utf8Idx++) {
1549+ NSCParameterAssert (((&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex + added]) - encodeState->stringBuffer.bytes.ptr) < encodeState->stringBuffer.bytes.length);
1550+ NSCParameterAssert (encodeState->atIndex < encodeState->stringBuffer.bytes.length);
1551+ NSCParameterAssert ((CFIndex)utf8Idx < usedBytes);
1552+ if (((encodeState->atIndex + added + 256UL ) > encodeState->stringBuffer .bytes .length )) {
1553+ if ((jk_managedBuffer_resize (&encodeState->stringBuffer , encodeState->atIndex + added + 1024UL ) == NULL )) { return (1 ); }
1554+ atPtr = encodeState->stringBuffer .bytes .ptr + encodeState->atIndex ;
1555+ }
1556+ if (utf8String[utf8Idx] < 0x20 ) {
1557+ switch (utf8String[utf8Idx]) {
1558+ case ' \b ' : atPtr[added++] = ' \\ ' ; atPtr[added++] = ' b' ; break ;
1559+ case ' \f ' : atPtr[added++] = ' \\ ' ; atPtr[added++] = ' f' ; break ;
1560+ case ' \n ' : atPtr[added++] = ' \\ ' ; atPtr[added++] = ' n' ; break ;
1561+ case ' \r ' : atPtr[added++] = ' \\ ' ; atPtr[added++] = ' r' ; break ;
1562+ case ' \t ' : atPtr[added++] = ' \\ ' ; atPtr[added++] = ' t' ; break ;
1563+ default : jk_printf (encodeState, " \\ u%4.4x " , utf8String[utf8Idx]); break ;
1564+ }
1565+ } else {
1566+ if ((utf8String[utf8Idx] == ' \" ' ) || (utf8String[utf8Idx] == ' \\ ' )) { atPtr[added++] = ' \\ ' ; }
1567+ atPtr[added++] = utf8String[utf8Idx];
1568+ }
1569+ }
1570+ atPtr[added++] = ' \" ' ;
1571+ atPtr[added] = 0 ;
1572+ encodeState->atIndex += added;
1573+ return (0 );
1574+ }
1575+ }
1576+ break ;
1577+
1578+ case JKClassNumber:
1579+ {
1580+ if (object == (id )kCFBooleanTrue ) { return (jk_write (encodeState, " true" )); break ; } else if (object == (id )kCFBooleanFalse ) { return (jk_write (encodeState, " false" )); break ; }
1581+
1582+ long long llv;
1583+ unsigned long long ullv;
1584+
1585+ const char *objCType = [object objCType ];
1586+ switch (objCType[0 ]) {
1587+ case ' B' :
1588+ case ' c' :
1589+ if (object == (id )kCFBooleanTrue ) { return (jk_write (encodeState, " true" )); }
1590+ else if (object == (id )kCFBooleanFalse ) { return (jk_write (encodeState, " false" )); }
1591+ else { if (CFNumberGetValue ((CFNumberRef)object, kCFNumberLongLongType , &llv)) { return (jk_printf (encodeState, " %lld " , llv)); } else { return (1 ); } }
1592+ break ;
1593+ case ' i' : case ' s' : case ' l' : case ' q' : if (CFNumberGetValue ((CFNumberRef)object, kCFNumberLongLongType , &llv)) { return (jk_printf (encodeState, " %lld " , llv)); } else { return (1 ); } break ;
1594+ case ' C' : case ' I' : case ' S' : case ' L' : case ' Q' : if (CFNumberGetValue ((CFNumberRef)object, kCFNumberLongLongType , &ullv)) { return (jk_printf (encodeState, " %llu " , ullv)); } else { return (1 ); } break ;
1595+
1596+ case ' f' : case ' d' :
1597+ {
1598+ double dv;
1599+ if (CFNumberGetValue ((CFNumberRef)object, kCFNumberDoubleType , &dv)) {
1600+ if (!isfinite (dv)) { return (1 ); }
1601+ return (jk_printf (encodeState, " %.16g " , dv));
1602+ }
1603+ }
1604+ break ;
1605+ default : jk_printf (encodeState, " /* NSNumber conversion error. Type: '%c ' / 0x%2.2x */" , objCType[0 ], objCType[0 ]); return (1 ); break ;
1606+ }
1607+ }
1608+ break ;
1609+
1610+ case JKClassArray:
1611+ {
1612+ int printComma = 0 ;
1613+ CFIndex arrayCount = CFArrayGetCount ((CFArrayRef)object), idx = 0L ;
1614+ if (jk_write (encodeState, " [" )) { return (1 ); }
1615+ if (arrayCount > 256L ) {
1616+ for (id arrayObject in object) { if (printComma) { if (jk_write (encodeState, " ," )) { return (1 ); } } printComma = 1 ; if (jk_add_atom_to_buffer (encodeState, arrayObject)) { return (1 ); } }
1617+ } else {
1618+ void *objects[256 ];
1619+ CFArrayGetValues ((CFArrayRef)object, CFRangeMake (0L , arrayCount), (const void **)objects);
1620+ for (idx = 0L ; idx < arrayCount; idx++) { if (printComma) { if (jk_write (encodeState, " ," )) { return (1 ); } } printComma = 1 ; if (jk_add_atom_to_buffer (encodeState, objects[idx])) { return (1 ); } }
1621+ }
1622+ if (jk_write (encodeState, " ]" )) { return (1 ); }
1623+ }
1624+ break ;
1625+ case JKClassDictionary:
1626+ {
1627+ int printComma = 0 ;
1628+ CFIndex dictionaryCount = CFDictionaryGetCount ((CFDictionaryRef)object), idx = 0L ;
1629+
1630+ if (jk_write (encodeState, " {" )) { return (1 ); }
1631+ if (dictionaryCount > 256L ) {
1632+ for (id keyObject in object) { if (printComma) { if (jk_write (encodeState, " ," )) { return (1 ); } } printComma = 1 ; if (jk_add_atom_to_buffer (encodeState, keyObject)) { return (1 ); } if (jk_write (encodeState, " :" )) { return (1 ); } if (jk_add_atom_to_buffer (encodeState, [object objectForKey: keyObject])) { return (1 ); } }
1633+ } else {
1634+ void *keys[256 ], *objects[256 ];
1635+ CFDictionaryGetKeysAndValues ((CFDictionaryRef)object, (const void **)keys, (const void **)objects);
1636+ for (idx = 0L ; idx < dictionaryCount; idx++) { if (printComma) { if (jk_write (encodeState, " ," )) { return (1 ); } } printComma = 1 ; if (jk_add_atom_to_buffer (encodeState, keys[idx])) { return (1 ); } if (jk_write (encodeState, " :" )) { return (1 ); } if (jk_add_atom_to_buffer (encodeState, objects[idx])) { return (1 ); } }
1637+ }
1638+ if (jk_write (encodeState, " }" )) { return (1 ); }
1639+ }
1640+ break ;
1641+
1642+ case JKClassNull: if (jk_write (encodeState, " null" )) { return (1 ); } break ;
1643+
1644+ default : jk_printf (encodeState, " /* Unable to convert class type of '%s ' */" , [[object className ] UTF8String ]); return (1 ); break ;
1645+ }
1646+
1647+
1648+ return (0 );
1649+ }
1650+
1651+
1652+ static NSData *jk_encode (void *object) {
1653+ JKEncodeState encodeState;
1654+ memset (&encodeState, 0 , sizeof (JKEncodeState));
1655+
1656+ encodeState.stringBuffer .roundSizeUpToMultipleOf = (1024UL * 32UL );
1657+ encodeState.utf8ConversionBuffer .roundSizeUpToMultipleOf = 4096UL ;
1658+
1659+ unsigned char stackJSONBuffer[JK_JSONBUFFER_SIZE] JK_ALIGNED (64 );
1660+ jk_managedBuffer_setToStackBuffer (&encodeState.stringBuffer , stackJSONBuffer, sizeof (stackJSONBuffer));
1661+
1662+ unsigned char stackUTF8Buffer[JK_UTF8BUFFER_SIZE] JK_ALIGNED (64 );
1663+ jk_managedBuffer_setToStackBuffer (&encodeState.utf8ConversionBuffer , stackUTF8Buffer, sizeof (stackUTF8Buffer));
1664+
1665+ NSData *jsonData = NULL ;
1666+ if (jk_add_atom_to_buffer (&encodeState, object) == 0 ) { jsonData = [NSData dataWithBytes: encodeState.stringBuffer.bytes.ptr length: encodeState.atIndex]; }
1667+
1668+ jk_managedBuffer_release (&encodeState.stringBuffer );
1669+ jk_managedBuffer_release (&encodeState.utf8ConversionBuffer );
1670+
1671+ return (jsonData);
1672+ }
1673+
1674+
1675+ @implementation NSArray (JSONKit)
1676+
1677+ - (NSData *)JSONData
1678+ {
1679+ return (jk_encode (self));
1680+ }
1681+
1682+ - (NSString *)JSONString
1683+ {
1684+ return ([[[NSString alloc ] initWithData: [self JSONData ] encoding: NSUTF8StringEncoding] autorelease ]);
1685+ }
1686+
1687+ @end
1688+
1689+ @implementation NSDictionary (JSONKit)
1690+
1691+
1692+ - (NSData *)JSONData
1693+ {
1694+ return (jk_encode (self));
1695+ }
1696+
1697+ - (NSString *)JSONString
1698+ {
1699+ return ([[[NSString alloc ] initWithData: [self JSONData ] encoding: NSUTF8StringEncoding] autorelease ]);
1700+ }
1701+
1702+ @end
0 commit comments