Merge pull request #1086 from skyline75489/fix/NSUserDefaults-dictionaryRepresentation
diff --git a/CoreFoundation/Base.subproj/CFRuntime.c b/CoreFoundation/Base.subproj/CFRuntime.c
index d7550a4..ead676d 100644
--- a/CoreFoundation/Base.subproj/CFRuntime.c
+++ b/CoreFoundation/Base.subproj/CFRuntime.c
@@ -1699,15 +1699,6 @@
}
}
-// For CF functions with 'Get' semantics, the compiler currently assumes that the result is autoreleased and must be retained. It does so on all platforms by emitting a call to objc_retainAutoreleasedReturnValue. On Darwin, this is implemented by the ObjC runtime. On Linux, there is no runtime, and therefore we have to stub it out here ourselves. The compiler will eventually call swift_release to balance the retain below. This is a workaround until the compiler no longer emits this callout on Linux.
-void * objc_retainAutoreleasedReturnValue(void *obj) {
- if (obj) {
- swift_retain(obj);
- return obj;
- }
- else return NULL;
-}
-
CFHashCode __CFHashDouble(double d) {
return _CFHashDouble(d);
}
diff --git a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
index d6d28ff..c1e3610 100644
--- a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
+++ b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
@@ -254,6 +254,8 @@
CF_EXPORT void _CFRuntimeBridgeTypeToClass(CFTypeID type, const void *isa);
+CF_EXPORT CFNumberType _CFNumberGetType2(CFNumberRef number);
+
typedef unsigned char __cf_uuid[16];
typedef char __cf_uuid_string[37];
typedef __cf_uuid _cf_uuid_t;
diff --git a/CoreFoundation/NumberDate.subproj/CFTimeZone.c b/CoreFoundation/NumberDate.subproj/CFTimeZone.c
index 434203a..427a3b1 100644
--- a/CoreFoundation/NumberDate.subproj/CFTimeZone.c
+++ b/CoreFoundation/NumberDate.subproj/CFTimeZone.c
@@ -33,9 +33,12 @@
#endif
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
#include <tzfile.h>
+#define MACOS_TZDIR1 "/usr/share/zoneinfo/" // 10.12 and earlier
+#define MACOS_TZDIR2 "/var/db/timezone/zoneinfo/" // 10.13 onwards
+
#elif DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
#ifndef TZDIR
-#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
+#define TZDIR "/usr/share/zoneinfo/" /* Time zone object file directory */
#endif /* !defined TZDIR */
#ifndef TZDEFAULT
@@ -56,16 +59,9 @@
#include <time.h>
-#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
-#define TZZONELINK TZDEFAULT
-#define TZZONEINFO TZDIR "/"
-#elif DEPLOYMENT_TARGET_WINDOWS
static CFStringRef __tzZoneInfo = NULL;
static char *__tzDir = NULL;
static void __InitTZStrings(void);
-#else
-#error Unknown or unspecified DEPLOYMENT_TARGET
-#endif
CONST_STRING_DECL(kCFTimeZoneSystemTimeZoneDidChangeNotification, "kCFTimeZoneSystemTimeZoneDidChangeNotification")
@@ -147,13 +143,9 @@
#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
static CFMutableArrayRef __CFCopyRecursiveDirectoryList() {
CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
-#if DEPLOYMENT_TARGET_WINDOWS
if (!__tzDir) __InitTZStrings();
if (!__tzDir) return result;
int fd = open(__tzDir, O_RDONLY);
-#else
- int fd = open(TZDIR "/zone.tab", O_RDONLY);
-#endif
for (; 0 <= fd;) {
uint8_t buffer[4096];
@@ -686,7 +678,7 @@
}
extern CFStringRef _CFGetWindowsAppleSystemLibraryDirectory(void);
-void __InitTZStrings(void) {
+static void __InitTZStrings(void) {
static CFLock_t __CFTZDirLock = CFLockInit;
__CFLock(&__CFTZDirLock);
if (!__tzZoneInfo) {
@@ -704,6 +696,50 @@
}
__CFUnlock(&__CFTZDirLock);
}
+
+#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
+static void __InitTZStrings(void) {
+ static dispatch_once_t initOnce = 0;
+
+ dispatch_once(&initOnce, ^{
+ unsigned int major = 0, minor = 0, patch = 0;
+
+ CFDictionaryRef dict = _CFCopySystemVersionDictionary();
+ if (dict) {
+ CFStringRef version = CFDictionaryGetValue(dict, _kCFSystemVersionProductVersionKey);
+ if (version) {
+ const char *cStr = CFStringGetCStringPtr(version, kCFStringEncodingASCII);
+ if (cStr) {
+ if (sscanf(cStr, "%u.%u.%u", &major, &minor, &patch) != 3) {
+ major = 0;
+ minor = 0;
+ patch = 0;
+ }
+ }
+ }
+ CFRelease(dict);
+ }
+
+ // Timezone files moved in High Sierra(10.13)
+ if (major == 10 && minor < 13) {
+ // older versions
+ __tzZoneInfo = CFSTR(MACOS_TZDIR1);
+ __tzDir = MACOS_TZDIR1 "zone.tab";
+ } else {
+ __tzZoneInfo = CFSTR(MACOS_TZDIR2);
+ __tzDir = MACOS_TZDIR2 "zone.tab";
+ }
+ });
+}
+
+#elif DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
+static void __InitTZStrings(void) {
+ __tzZoneInfo = CFSTR(TZDIR);
+ __tzDir = TZDIR "zone.tab";
+}
+
+#else
+#error Unknown or unspecified DEPLOYMENT_TARGET
#endif
static CFTimeZoneRef __CFTimeZoneCreateSystem(void) {
@@ -748,11 +784,16 @@
CFRelease(name);
if (result) return result;
}
- ret = readlink(TZZONELINK, linkbuf, sizeof(linkbuf));
- if (0 < ret) {
+
+ if (!__tzZoneInfo) __InitTZStrings();
+ ret = readlink(TZDEFAULT, linkbuf, sizeof(linkbuf));
+ if (__tzZoneInfo && (0 < ret)) {
linkbuf[ret] = '\0';
- if (strncmp(linkbuf, TZZONEINFO, sizeof(TZZONEINFO) - 1) == 0) {
- name = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (uint8_t *)linkbuf + sizeof(TZZONEINFO) - 1, strlen(linkbuf) - sizeof(TZZONEINFO) + 1, kCFStringEncodingUTF8, false);
+ const char *tzZoneInfo = CFStringGetCStringPtr(__tzZoneInfo, kCFStringEncodingASCII);
+ size_t zoneInfoDirLen = CFStringGetLength(__tzZoneInfo);
+ if (strncmp(linkbuf, tzZoneInfo, zoneInfoDirLen) == 0) {
+ name = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (uint8_t *)linkbuf + zoneInfoDirLen,
+ strlen(linkbuf) - zoneInfoDirLen + 2, kCFStringEncodingUTF8, false);
} else {
name = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (uint8_t *)linkbuf, strlen(linkbuf), kCFStringEncodingUTF8, false);
}
@@ -1133,12 +1174,12 @@
CFIndex length;
Boolean result = false;
-#if DEPLOYMENT_TARGET_WINDOWS
if (!__tzZoneInfo) __InitTZStrings();
if (!__tzZoneInfo) return NULL;
+#if DEPLOYMENT_TARGET_WINDOWS
baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLWindowsPathStyle, true);
#else
- baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, CFSTR(TZZONEINFO), kCFURLPOSIXPathStyle, true);
+ baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLPOSIXPathStyle, true);
#endif
if (tryAbbrev) {
CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary();
@@ -1159,15 +1200,9 @@
CFStringRef mapping = CFDictionaryGetValue(dict, name);
if (mapping) {
name = mapping;
-#if DEPLOYMENT_TARGET_WINDOWS
} else if (CFStringHasPrefix(name, __tzZoneInfo)) {
CFMutableStringRef unprefixed = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, CFStringGetLength(name), name);
CFStringDelete(unprefixed, CFRangeMake(0, CFStringGetLength(__tzZoneInfo)));
-#else
- } else if (CFStringHasPrefix(name, CFSTR(TZZONEINFO))) {
- CFMutableStringRef unprefixed = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, CFStringGetLength(name), name);
- CFStringDelete(unprefixed, CFRangeMake(0, sizeof(TZZONEINFO)));
-#endif
mapping = CFDictionaryGetValue(dict, unprefixed);
if (mapping) {
name = mapping;
@@ -1340,12 +1375,12 @@
void *bytes;
CFIndex length;
-#if DEPLOYMENT_TARGET_WINDOWS
if (!__tzZoneInfo) __InitTZStrings();
if (!__tzZoneInfo) return NULL;
+#if DEPLOYMENT_TARGET_WINDOWS
baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLWindowsPathStyle, true);
#else
- baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, CFSTR(TZZONEINFO), kCFURLPOSIXPathStyle, true);
+ baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLPOSIXPathStyle, true);
#endif
if (tryAbbrev) {
CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary();
@@ -1366,15 +1401,9 @@
CFStringRef mapping = CFDictionaryGetValue(dict, name);
if (mapping) {
name = mapping;
-#if DEPLOYMENT_TARGET_WINDOWS
} else if (CFStringHasPrefix(name, __tzZoneInfo)) {
CFMutableStringRef unprefixed = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, CFStringGetLength(name), name);
CFStringDelete(unprefixed, CFRangeMake(0, CFStringGetLength(__tzZoneInfo)));
-#else
- } else if (CFStringHasPrefix(name, CFSTR(TZZONEINFO))) {
- CFMutableStringRef unprefixed = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, CFStringGetLength(name), name);
- CFStringDelete(unprefixed, CFRangeMake(0, sizeof(TZZONEINFO)));
-#endif
mapping = CFDictionaryGetValue(dict, unprefixed);
if (mapping) {
name = mapping;
@@ -1549,6 +1578,4 @@
return dict;
}
-#undef TZZONEINFO
-#undef TZZONELINK
diff --git a/CoreFoundation/Parsing.subproj/CFXMLInterface.c b/CoreFoundation/Parsing.subproj/CFXMLInterface.c
index 97f9340..0c1dbae 100644
--- a/CoreFoundation/Parsing.subproj/CFXMLInterface.c
+++ b/CoreFoundation/Parsing.subproj/CFXMLInterface.c
@@ -20,6 +20,7 @@
#include <libxml/xmlmemory.h>
#include <libxml/xmlsave.h>
#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
#include <libxml/dict.h>
#include "CFInternal.h"
@@ -57,6 +58,7 @@
CFIndex _kCFXMLTypeAttribute = XML_ATTRIBUTE_NODE;
CFIndex _kCFXMLTypeDTD = XML_DTD_NODE;
CFIndex _kCFXMLDocTypeHTML = XML_DOC_HTML;
+CFIndex _kCFXMLTypeNamespace = 22; // libxml2 does not define namespaces as nodes, so we have to fake it
CFIndex _kCFXMLDTDNodeTypeEntity = XML_ENTITY_DECL;
CFIndex _kCFXMLDTDNodeTypeAttribute = XML_ATTRIBUTE_DECL;
@@ -360,11 +362,7 @@
return xmlNewProp(node, name, value);
}
-_CFXMLNamespacePtr _CFXMLNewNamespace(_CFXMLNodePtr node, const unsigned char* uri, const unsigned char* prefix) {
- return xmlNewNs(node, uri, prefix);
-}
-
-CF_RETURNS_RETAINED CFStringRef _CFXMLNodeURI(_CFXMLNodePtr node) {
+CFStringRef _CFXMLNodeCopyURI(_CFXMLNodePtr node) {
xmlNodePtr nodePtr = (xmlNodePtr)node;
switch (nodePtr->type) {
case XML_ATTRIBUTE_NODE:
@@ -447,15 +445,51 @@
return ((xmlNodePtr)node)->type;
}
-const char* _CFXMLNodeGetName(_CFXMLNodePtr node) {
- return (const char*)(((xmlNodePtr)node)->name);
+static inline xmlChar* _getQName(xmlNodePtr node) {
+ const xmlChar* prefix = NULL;
+ const xmlChar* ncname = node->name;
+
+ switch (node->type) {
+ case XML_NOTATION_NODE:
+ case XML_DTD_NODE:
+ case XML_ELEMENT_DECL:
+ case XML_ATTRIBUTE_DECL:
+ case XML_ENTITY_DECL:
+ case XML_NAMESPACE_DECL:
+ case XML_XINCLUDE_START:
+ case XML_XINCLUDE_END:
+ break;
+
+ default:
+ if (node->ns != NULL) {
+ prefix = node->ns->prefix;
+ }
+ }
+
+ return xmlBuildQName(ncname, prefix, NULL, 0);
+}
+
+CFStringRef _Nullable _CFXMLNodeCopyName(_CFXMLNodePtr node) {
+ xmlNodePtr xmlNode = (xmlNodePtr)node;
+
+ xmlChar* qName = _getQName(xmlNode);
+
+ if (qName != NULL) {
+ CFStringRef result = CFStringCreateWithCString(NULL, (const char*)qName, kCFStringEncodingUTF8);
+ if (qName != xmlNode->name) {
+ xmlFree(qName);
+ }
+ return result;
+ } else {
+ return NULL;
+ }
}
void _CFXMLNodeSetName(_CFXMLNodePtr node, const char* name) {
xmlNodeSetName(node, (const xmlChar*)name);
}
-CFStringRef _CFXMLNodeGetContent(_CFXMLNodePtr node) {
+CFStringRef _CFXMLNodeCopyContent(_CFXMLNodePtr node) {
switch (((xmlNodePtr)node)->type) {
case XML_ELEMENT_DECL:
{
@@ -497,7 +531,7 @@
// xmlElementContent structures, let's leverage what we've already got.
CFMutableStringRef xmlString = CFStringCreateMutable(NULL, 0);
CFStringAppend(xmlString, CFSTR("<!ELEMENT "));
- CFStringAppendCString(xmlString, _CFXMLNodeGetName(node), kCFStringEncodingUTF8);
+ CFStringAppendCString(xmlString, (const char*)element->name, kCFStringEncodingUTF8);
CFStringAppend(xmlString, CFSTR(" "));
CFStringAppendCString(xmlString, (const char*)content, kCFStringEncodingUTF8);
CFStringAppend(xmlString, CFSTR(">"));
@@ -647,7 +681,7 @@
xmlDocSetRootElement(doc, node);
}
-CF_RETURNS_RETAINED CFStringRef _CFXMLDocCharacterEncoding(_CFXMLDocPtr doc) {
+CFStringRef _CFXMLDocCopyCharacterEncoding(_CFXMLDocPtr doc) {
return CFStringCreateWithCString(NULL, (const char*)((xmlDocPtr)doc)->encoding, kCFStringEncodingUTF8);
}
@@ -661,7 +695,7 @@
docPtr->encoding = xmlStrdup(encoding);
}
-CF_RETURNS_RETAINED CFStringRef _CFXMLDocVersion(_CFXMLDocPtr doc) {
+CFStringRef _CFXMLDocCopyVersion(_CFXMLDocPtr doc) {
return CFStringCreateWithCString(NULL, (const char*)((xmlDocPtr)doc)->version, kCFStringEncodingUTF8);
}
@@ -748,7 +782,7 @@
return xmlGetParameterEntity(doc, (const xmlChar*)entity);
}
-CFStringRef _CFXMLGetEntityContent(_CFXMLEntityPtr entity) {
+CFStringRef _CFXMLCopyEntityContent(_CFXMLEntityPtr entity) {
const xmlChar* content = ((xmlEntityPtr)entity)->content;
if (!content) {
return NULL;
@@ -760,7 +794,7 @@
return result;
}
-CFStringRef _CFXMLStringWithOptions(_CFXMLNodePtr node, uint32_t options) {
+CFStringRef _CFXMLCopyStringWithOptions(_CFXMLNodePtr node, uint32_t options) {
if (((xmlNodePtr)node)->type == XML_ENTITY_DECL &&
((xmlEntityPtr)node)->etype == XML_INTERNAL_PREDEFINED_ENTITY) {
// predefined entities need special handling, libxml2 just tosses an error and returns a NULL string
@@ -832,17 +866,26 @@
return result;
}
-CF_RETURNS_RETAINED CFArrayRef _CFXMLNodesForXPath(_CFXMLNodePtr node, const unsigned char* xpath) {
+CFArrayRef _CFXMLNodesForXPath(_CFXMLNodePtr node, const unsigned char* xpath) {
if (((xmlNodePtr)node)->doc == NULL) {
return NULL;
}
-
+
+ if (((xmlNodePtr)node)->type == XML_DOCUMENT_NODE) {
+ node = ((xmlDocPtr)node)->children;
+ }
+
xmlXPathContextPtr context = xmlXPathNewContext(((xmlNodePtr)node)->doc);
+ xmlNsPtr ns = ((xmlNodePtr)node)->ns;
+ while (ns != NULL) {
+ xmlXPathRegisterNs(context, ns->prefix, ns->href);
+ ns = ns->next;
+ }
xmlXPathObjectPtr evalResult = xmlXPathNodeEval(node, xpath, context);
xmlNodeSetPtr nodes = evalResult->nodesetval;
-
+
int count = nodes->nodeNr;
CFMutableArrayRef results = CFArrayCreateMutable(NULL, count, NULL);
@@ -856,8 +899,15 @@
return results;
}
-_CFXMLNodePtr _CFXMLNodeHasProp(_CFXMLNodePtr node, const unsigned char* propertyName) {
- return xmlHasProp(node, propertyName);
+CFStringRef _Nullable _CFXMLCopyPathForNode(_CFXMLNodePtr node) {
+ xmlChar* path = xmlGetNodePath(node);
+ CFStringRef result = CFStringCreateWithCString(NULL, (const char*)path, kCFStringEncodingUTF8);
+ xmlFree(path);
+ return result;
+}
+
+_CFXMLNodePtr _CFXMLNodeHasProp(_CFXMLNodePtr node, const char* propertyName) {
+ return xmlHasProp(node, (const xmlChar*)propertyName);
}
_CFXMLDocPtr _CFXMLDocPtrFromDataWithOptions(CFDataRef data, int options) {
@@ -876,19 +926,26 @@
if (options & _kCFXMLNodeLoadExternalEntitiesAlways) {
xmlOptions |= XML_PARSE_DTDLOAD;
}
-
+
+ xmlOptions |= XML_PARSE_RECOVER;
+ xmlOptions |= XML_PARSE_NSCLEAN;
+
return xmlReadMemory((const char*)CFDataGetBytePtr(data), CFDataGetLength(data), NULL, NULL, xmlOptions);
}
-CF_RETURNS_RETAINED CFStringRef _CFXMLNodeLocalName(_CFXMLNodePtr node) {
- int length = 0;
- const xmlChar* result = xmlSplitQName3(((xmlNodePtr)node)->name, &length);
+CFStringRef _CFXMLNodeCopyLocalName(_CFXMLNodePtr node) {
+ xmlChar* prefix = NULL;
+ const xmlChar* result = xmlSplitQName2(_getQName((xmlNodePtr)node), &prefix);
+ if (result == NULL) {
+ result = ((xmlNodePtr)node)->name;
+ }
+
return CFStringCreateWithCString(NULL, (const char*)result, kCFStringEncodingUTF8);
}
-CF_RETURNS_RETAINED CFStringRef _CFXMLNodePrefix(_CFXMLNodePtr node) {
+CFStringRef _CFXMLNodeCopyPrefix(_CFXMLNodePtr node) {
xmlChar* result = NULL;
- xmlChar* unused = xmlSplitQName2(((xmlNodePtr)node)->name, &result);
+ xmlChar* unused = xmlSplitQName2(_getQName((xmlNodePtr)node), &result);
CFStringRef resultString = CFStringCreateWithCString(NULL, (const char*)result, kCFStringEncodingUTF8);
xmlFree(result);
@@ -983,7 +1040,7 @@
return dtd;
}
-CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLDTDExternalID(_CFXMLDTDPtr dtd) {
+CFStringRef _Nullable _CFXMLDTDCopyExternalID(_CFXMLDTDPtr dtd) {
const unsigned char* externalID = ((xmlDtdPtr)dtd)->ExternalID;
if (externalID) {
return CFStringCreateWithCString(NULL, (const char*)externalID, kCFStringEncodingUTF8);
@@ -1008,7 +1065,7 @@
dtdPtr->ExternalID = xmlStrdup(externalID);
}
-CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLDTDSystemID(_CFXMLDTDPtr dtd) {
+CFStringRef _Nullable _CFXMLDTDCopySystemID(_CFXMLDTDPtr dtd) {
const unsigned char* systemID = ((xmlDtdPtr)dtd)->SystemID;
if (systemID) {
return CFStringCreateWithCString(NULL, (const char*)systemID, kCFStringEncodingUTF8);
@@ -1095,7 +1152,7 @@
return ((xmlAttributePtr)node)->atype;
}
-CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLDTDNodeGetSystemID(_CFXMLDTDNodePtr node) {
+CFStringRef _Nullable _CFXMLDTDNodeCopySystemID(_CFXMLDTDNodePtr node) {
switch (((xmlNodePtr)node)->type) {
case XML_ENTITY_DECL:
return CFStringCreateWithCString(NULL, (const char*)((xmlEntityPtr)node)->SystemID, kCFStringEncodingUTF8);
@@ -1137,7 +1194,7 @@
}
}
-CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLDTDNodeGetPublicID(_CFXMLDTDNodePtr node) {
+CFStringRef _Nullable _CFXMLDTDNodeCopyPublicID(_CFXMLDTDNodePtr node) {
switch (((xmlNodePtr)node)->type) {
case XML_ENTITY_DECL:
return CFStringCreateWithCString(NULL, (const char*)((xmlEntityPtr)node)->ExternalID, kCFStringEncodingUTF8);
@@ -1179,6 +1236,131 @@
}
}
+// Namespaces
+_CFXMLNodePtr _Nonnull * _Nullable _CFXMLNamespaces(_CFXMLNodePtr node, CFIndex* count) {
+ *count = 0;
+ xmlNs* ns = ((xmlNode*)node)->ns;
+ while (ns != NULL) {
+ (*count)++;
+ ns = ns->next;
+ }
+
+ _CFXMLNodePtr* result = calloc(*count, sizeof(_CFXMLNodePtr));
+ ns = ((xmlNode*)node)->ns;
+ for (int i = 0; i < *count; i++) {
+ xmlNode* temp = xmlNewNode(ns, (unsigned char *)"");
+
+ temp->type = _kCFXMLTypeNamespace;
+ result[i] = temp;
+ ns = ns->next;
+ }
+ return result;
+}
+
+static inline void _removeAllNamespaces(xmlNodePtr node);
+static inline void _removeAllNamespaces(xmlNodePtr node) {
+ xmlNsPtr ns = node->ns;
+ if (ns != NULL) {
+ xmlFreeNsList(ns);
+ node->ns = NULL;
+ }
+}
+
+void _CFXMLSetNamespaces(_CFXMLNodePtr node, _CFXMLNodePtr* _Nullable nodes, CFIndex count) {
+ _removeAllNamespaces(node);
+
+ if (nodes == NULL || count == 0) {
+ return;
+ }
+
+ xmlNodePtr nsNode = (xmlNodePtr)nodes[0];
+ ((xmlNodePtr)node)->ns = xmlCopyNamespace(nsNode->ns);
+ xmlNsPtr currNs = ((xmlNodePtr)node)->ns;
+ for (CFIndex i = 1; i < count; i++) {
+ currNs->next = xmlCopyNamespace(((xmlNodePtr)nodes[i])->ns);
+ currNs = currNs->next;
+ }
+}
+
+CFStringRef _Nullable _CFXMLNamespaceCopyValue(_CFXMLNodePtr node) {
+ xmlNsPtr ns = ((xmlNode*)node)->ns;
+
+ if (ns->href == NULL) {
+ return NULL;
+ }
+
+ return CFStringCreateWithCString(NULL, (const char*)ns->href, kCFStringEncodingUTF8);
+}
+
+void _CFXMLNamespaceSetValue(_CFXMLNodePtr node, const char* value, int64_t length) {
+ xmlNsPtr ns = ((xmlNodePtr)node)->ns;
+ ns->href = xmlStrndup((const xmlChar*)value, length);
+}
+
+CFStringRef _Nullable _CFXMLNamespaceCopyPrefix(_CFXMLNodePtr node) {
+ xmlNsPtr ns = ((xmlNodePtr)node)->ns;
+
+ if (ns->prefix == NULL) {
+ return NULL;
+ }
+
+ return CFStringCreateWithCString(NULL, (const char*)ns->prefix, kCFStringEncodingUTF8);
+}
+
+void _CFXMLNamespaceSetPrefix(_CFXMLNodePtr node, const char* prefix, int64_t length) {
+ xmlNsPtr ns = ((xmlNodePtr)node)->ns;
+
+ ns->prefix = xmlStrndup((const xmlChar*)prefix, length);
+}
+
+_CFXMLNodePtr _CFXMLNewNamespace(const char* name, const char* stringValue) {
+ xmlNsPtr ns = xmlNewNs(NULL, (const xmlChar*)stringValue, (const xmlChar*)name);
+ xmlNodePtr node = xmlNewNode(ns, (const xmlChar*)"");
+
+ node->type = _kCFXMLTypeNamespace;
+
+ return node;
+}
+
+void _CFXMLAddNamespace(_CFXMLNodePtr node, _CFXMLNodePtr nsNode) {
+ xmlNodePtr nodePtr = (xmlNodePtr)node;
+ xmlNsPtr ns = xmlCopyNamespace(((xmlNodePtr)nsNode)->ns);
+ ns->context = nodePtr->doc;
+
+ xmlNsPtr currNs = nodePtr->ns;
+ if (currNs == NULL) {
+ nodePtr->ns = ns;
+ return;
+ }
+
+ while(currNs->next != NULL) {
+ currNs = currNs->next;
+ }
+
+ currNs->next = ns;
+}
+
+void _CFXMLRemoveNamespace(_CFXMLNodePtr node, const char* prefix) {
+ xmlNodePtr nodePtr = (xmlNodePtr)node;
+ xmlNsPtr ns = nodePtr->ns;
+ if (ns != NULL && xmlStrcmp((const xmlChar*)prefix, ns->prefix) == 0) {
+ nodePtr->ns = ns->next;
+ xmlFreeNs(ns);
+ return;
+ }
+
+ while (ns->next != NULL) {
+ if (xmlStrcmp(ns->next->prefix, (const xmlChar*)prefix) == 0) {
+ xmlNsPtr next = ns->next;
+ ns->next = ns->next->next;
+ xmlFreeNs(next);
+ return;
+ }
+
+ ns = ns->next;
+ }
+}
+
void _CFXMLFreeNode(_CFXMLNodePtr node) {
if (!node) {
return;
@@ -1231,6 +1413,13 @@
}
default:
+ // we first need to check if this node is one of our custom
+ // namespace nodes, which don't actually exist in libxml2
+ if (((xmlNodePtr)node)->type == _kCFXMLTypeNamespace) {
+ // resetting its type to XML_ELEMENT_NODE will cause the enclosed namespace
+ // to be properly freed by libxml2
+ ((xmlNodePtr)node)->type = XML_ELEMENT_NODE;
+ }
xmlFreeNode(node);
}
}
diff --git a/CoreFoundation/Parsing.subproj/CFXMLInterface.h b/CoreFoundation/Parsing.subproj/CFXMLInterface.h
index bd9f6fe..f9ac170 100644
--- a/CoreFoundation/Parsing.subproj/CFXMLInterface.h
+++ b/CoreFoundation/Parsing.subproj/CFXMLInterface.h
@@ -55,6 +55,7 @@
extern CFIndex _kCFXMLTypeAttribute;
extern CFIndex _kCFXMLTypeDTD;
extern CFIndex _kCFXMLDocTypeHTML;
+extern CFIndex _kCFXMLTypeNamespace;
extern CFIndex _kCFXMLDTDNodeTypeEntity;
extern CFIndex _kCFXMLDTDNodeTypeAttribute;
@@ -144,18 +145,17 @@
_CFXMLNodePtr _CFXMLNewTextNode(const unsigned char* value);
_CFXMLNodePtr _CFXMLNewComment(const unsigned char* value);
_CFXMLNodePtr _CFXMLNewProperty(_CFXMLNodePtr _Nullable node, const unsigned char* name, const unsigned char* value);
-_CFXMLNamespacePtr _CFXMLNewNamespace(_CFXMLNodePtr _Nullable node, const unsigned char* uri, const unsigned char* prefix);
-CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLNodeURI(_CFXMLNodePtr node);
+CFStringRef _Nullable _CFXMLNodeCopyURI(_CFXMLNodePtr node);
void _CFXMLNodeSetURI(_CFXMLNodePtr node, const unsigned char* _Nullable URI);
void _CFXMLNodeSetPrivateData(_CFXMLNodePtr node, void* data);
void* _Nullable _CFXMLNodeGetPrivateData(_CFXMLNodePtr node);
_CFXMLNodePtr _Nullable _CFXMLNodeProperties(_CFXMLNodePtr node);
CFIndex _CFXMLNodeGetType(_CFXMLNodePtr node);
-const char* _Nullable _CFXMLNodeGetName(_CFXMLNodePtr node);
+CFStringRef _Nullable _CFXMLNodeCopyName(_CFXMLNodePtr node);
void _CFXMLNodeSetName(_CFXMLNodePtr node, const char* name);
-CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLNodeGetContent(_CFXMLNodePtr node);
+CFStringRef _Nullable _CFXMLNodeCopyContent(_CFXMLNodePtr node);
void _CFXMLNodeSetContent(_CFXMLNodePtr node, const unsigned char* _Nullable content);
void _CFXMLUnlinkNode(_CFXMLNodePtr node);
@@ -176,9 +176,9 @@
void _CFXMLDocSetStandalone(_CFXMLDocPtr doc, bool standalone);
_CFXMLNodePtr _Nullable _CFXMLDocRootElement(_CFXMLDocPtr doc);
void _CFXMLDocSetRootElement(_CFXMLDocPtr doc, _CFXMLNodePtr node);
-CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLDocCharacterEncoding(_CFXMLDocPtr doc);
+CFStringRef _Nullable _CFXMLDocCopyCharacterEncoding(_CFXMLDocPtr doc);
void _CFXMLDocSetCharacterEncoding(_CFXMLDocPtr doc, const unsigned char* _Nullable encoding);
-CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLDocVersion(_CFXMLDocPtr doc);
+CFStringRef _Nullable _CFXMLDocCopyVersion(_CFXMLDocPtr doc);
void _CFXMLDocSetVersion(_CFXMLDocPtr doc, const unsigned char* _Nullable version);
int _CFXMLDocProperties(_CFXMLDocPtr doc);
void _CFXMLDocSetProperties(_CFXMLDocPtr doc, int newProperties);
@@ -190,18 +190,19 @@
_CFXMLEntityPtr _Nullable _CFXMLGetDTDEntity(_CFXMLDocPtr doc, const char* entity);
_CFXMLEntityPtr _Nullable _CFXMLGetParameterEntity(_CFXMLDocPtr doc, const char* entity);
-CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLGetEntityContent(_CFXMLEntityPtr entity);
+CFStringRef _Nullable _CFXMLCopyEntityContent(_CFXMLEntityPtr entity);
-CF_RETURNS_RETAINED CFStringRef _CFXMLStringWithOptions(_CFXMLNodePtr node, uint32_t options);
+CFStringRef _CFXMLCopyStringWithOptions(_CFXMLNodePtr node, uint32_t options);
CF_RETURNS_RETAINED CFArrayRef _Nullable _CFXMLNodesForXPath(_CFXMLNodePtr node, const unsigned char* xpath);
+CFStringRef _Nullable _CFXMLCopyPathForNode(_CFXMLNodePtr node);
-_CFXMLNodePtr _Nullable _CFXMLNodeHasProp(_CFXMLNodePtr node, const unsigned char* propertyName);
+_CFXMLNodePtr _Nullable _CFXMLNodeHasProp(_CFXMLNodePtr node, const char* propertyName);
_CFXMLDocPtr _CFXMLDocPtrFromDataWithOptions(CFDataRef data, int options);
-CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLNodeLocalName(_CFXMLNodePtr node);
-CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLNodePrefix(_CFXMLNodePtr node);
+CFStringRef _Nullable _CFXMLNodeCopyLocalName(_CFXMLNodePtr node);
+CFStringRef _Nullable _CFXMLNodeCopyPrefix(_CFXMLNodePtr node);
bool _CFXMLDocValidate(_CFXMLDocPtr doc, CFErrorRef _Nullable * error);
@@ -209,9 +210,9 @@
_CFXMLDTDNodePtr _Nullable _CFXMLParseDTDNode(const unsigned char* xmlString);
_CFXMLDTDPtr _Nullable _CFXMLParseDTD(const unsigned char* URL);
_CFXMLDTDPtr _Nullable _CFXMLParseDTDFromData(CFDataRef data, CFErrorRef _Nullable * error);
-CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLDTDExternalID(_CFXMLDTDPtr dtd);
+CFStringRef _Nullable _CFXMLDTDCopyExternalID(_CFXMLDTDPtr dtd);
void _CFXMLDTDSetExternalID(_CFXMLDTDPtr dtd, const unsigned char* _Nullable externalID);
-CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLDTDSystemID(_CFXMLDTDPtr dtd);
+CFStringRef _Nullable _CFXMLDTDCopySystemID(_CFXMLDTDPtr dtd);
void _CFXMLDTDSetSystemID(_CFXMLDTDPtr dtd, const unsigned char* _Nullable systemID);
_CFXMLDTDNodePtr _Nullable _CFXMLDTDGetElementDesc(_CFXMLDTDPtr dtd, const unsigned char* name);
@@ -227,11 +228,22 @@
CFIndex _CFXMLDTDEntityNodeGetType(_CFXMLDTDNodePtr node);
CFIndex _CFXMLDTDAttributeNodeGetType(_CFXMLDTDNodePtr node);
-CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLDTDNodeGetSystemID(_CFXMLDTDNodePtr node);
+CFStringRef _Nullable _CFXMLDTDNodeCopySystemID(_CFXMLDTDNodePtr node);
void _CFXMLDTDNodeSetSystemID(_CFXMLDTDNodePtr node, const unsigned char* _Nullable systemID);
-CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLDTDNodeGetPublicID(_CFXMLDTDNodePtr node);
+CFStringRef _Nullable _CFXMLDTDNodeCopyPublicID(_CFXMLDTDNodePtr node);
void _CFXMLDTDNodeSetPublicID(_CFXMLDTDNodePtr node, const unsigned char* _Nullable publicID);
+// Namespaces
+_CFXMLNodePtr _Nonnull * _Nullable _CFXMLNamespaces(_CFXMLNodePtr node, CFIndex* count);
+void _CFXMLSetNamespaces(_CFXMLNodePtr node, _CFXMLNodePtr _Nonnull * _Nullable nodes, CFIndex count);
+CFStringRef _Nullable _CFXMLNamespaceCopyValue(_CFXMLNodePtr node);
+void _CFXMLNamespaceSetValue(_CFXMLNodePtr node, const char* _Nullable value, int64_t length);
+CFStringRef _Nullable _CFXMLNamespaceCopyPrefix(_CFXMLNodePtr node);
+void _CFXMLNamespaceSetPrefix(_CFXMLNodePtr node, const char* _Nullable prefix, int64_t length);
+_CFXMLNodePtr _CFXMLNewNamespace(const char* name, const char* stringValue);
+void _CFXMLAddNamespace(_CFXMLNodePtr node, _CFXMLNodePtr nsNode);
+void _CFXMLRemoveNamespace(_CFXMLNodePtr node, const char* prefix);
+
void _CFXMLFreeNode(_CFXMLNodePtr node);
void _CFXMLFreeDocument(_CFXMLDocPtr doc);
void _CFXMLFreeDTD(_CFXMLDTDPtr dtd);
diff --git a/Foundation.xcodeproj/project.pbxproj b/Foundation.xcodeproj/project.pbxproj
index 94d542d..d216523 100644
--- a/Foundation.xcodeproj/project.pbxproj
+++ b/Foundation.xcodeproj/project.pbxproj
@@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
0383A1751D2E558A0052E5D1 /* TestNSStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0383A1741D2E558A0052E5D1 /* TestNSStream.swift */; };
+ 03B6F5841F15F339004F25AF /* TestNSURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B6F5831F15F339004F25AF /* TestNSURLProtocol.swift */; };
1520469B1D8AEABE00D02E36 /* HTTPServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1520469A1D8AEABE00D02E36 /* HTTPServer.swift */; };
159884921DCC877700E3314C /* TestNSHTTPCookieStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 159884911DCC877700E3314C /* TestNSHTTPCookieStorage.swift */; };
231503DB1D8AEE5D0061694D /* TestNSDecimal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231503DA1D8AEE5D0061694D /* TestNSDecimal.swift */; };
@@ -353,6 +354,7 @@
D51239DF1CD9DA0800D433EE /* CFSocket.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B5D88E01BBC9B0300234F36 /* CFSocket.c */; };
D512D17C1CD883F00032E6A5 /* TestFileHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D512D17B1CD883F00032E6A5 /* TestFileHandle.swift */; };
D5C40F331CDA1D460005690C /* TestNSOperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5C40F321CDA1D460005690C /* TestNSOperationQueue.swift */; };
+ DCA8120B1F046D13000D0C86 /* TestCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA8120A1F046D13000D0C86 /* TestCodable.swift */; };
E1A03F361C4828650023AF4D /* PropertyList-1.0.dtd in Resources */ = {isa = PBXBuildFile; fileRef = E1A03F351C4828650023AF4D /* PropertyList-1.0.dtd */; };
E1A03F381C482C730023AF4D /* NSXMLDTDTestData.xml in Resources */ = {isa = PBXBuildFile; fileRef = E1A03F371C482C730023AF4D /* NSXMLDTDTestData.xml */; };
E1A3726F1C31EBFB0023AF4D /* NSXMLDocumentTestData.xml in Resources */ = {isa = PBXBuildFile; fileRef = E1A3726E1C31EBFB0023AF4D /* NSXMLDocumentTestData.xml */; };
@@ -479,6 +481,7 @@
/* Begin PBXFileReference section */
0383A1741D2E558A0052E5D1 /* TestNSStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSStream.swift; sourceTree = "<group>"; };
+ 03B6F5831F15F339004F25AF /* TestNSURLProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestNSURLProtocol.swift; sourceTree = "<group>"; };
1520469A1D8AEABE00D02E36 /* HTTPServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPServer.swift; sourceTree = "<group>"; };
159884911DCC877700E3314C /* TestNSHTTPCookieStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestNSHTTPCookieStorage.swift; sourceTree = "<group>"; };
22B9C1E01C165D7A00DECFF9 /* TestNSDate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSDate.swift; sourceTree = "<group>"; };
@@ -820,6 +823,7 @@
D512D17B1CD883F00032E6A5 /* TestFileHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestFileHandle.swift; sourceTree = "<group>"; };
D5C40F321CDA1D460005690C /* TestNSOperationQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSOperationQueue.swift; sourceTree = "<group>"; };
D834F9931C31C4060023812A /* TestNSOrderedSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSOrderedSet.swift; sourceTree = "<group>"; };
+ DCA8120A1F046D13000D0C86 /* TestCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestCodable.swift; sourceTree = "<group>"; };
DCDBB8321C1768AC00313299 /* TestNSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSData.swift; sourceTree = "<group>"; };
E1A03F351C4828650023AF4D /* PropertyList-1.0.dtd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "PropertyList-1.0.dtd"; sourceTree = "<group>"; };
E1A03F371C482C730023AF4D /* NSXMLDTDTestData.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = NSXMLDTDTestData.xml; sourceTree = "<group>"; };
@@ -1421,8 +1425,7 @@
A058C2011E529CF100B07AA1 /* TestMassFormatter.swift */,
BF8E65301DC3B3CB005AB5C3 /* TestNotification.swift */,
3EA9D66F1EF0532D00B362D6 /* TestJSONEncoder.swift */,
- 159884911DCC877700E3314C /* TestNSHTTPCookieStorage.swift */,
- D4FE895A1D703D1100DA7986 /* TestURLRequest.swift */,
+ DCA8120A1F046D13000D0C86 /* TestCodable.swift */,
C93559281C12C49F009FD6A9 /* TestNSAffineTransform.swift */,
EA66F63C1BF1619600136161 /* TestNSArray.swift */,
294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */,
@@ -1484,6 +1487,7 @@
CC5249BF1D341D23007CB54D /* TestUnitConverter.swift */,
D4FE895A1D703D1100DA7986 /* TestURLRequest.swift */,
5B6F17961C48631C00935030 /* TestUtils.swift */,
+ 03B6F5831F15F339004F25AF /* TestNSURLProtocol.swift */,
);
name = Tests;
sourceTree = "<group>";
@@ -2372,6 +2376,7 @@
BF8E65311DC3B3CB005AB5C3 /* TestNotification.swift in Sources */,
63DCE9D41EAA432400E9CB02 /* TestISO8601DateFormatter.swift in Sources */,
EA01AAEC1DA839C4008F4E07 /* TestProgress.swift in Sources */,
+ 03B6F5841F15F339004F25AF /* TestNSURLProtocol.swift in Sources */,
5B13B3411C582D4C00651CE2 /* TestNSRegularExpression.swift in Sources */,
5B13B3491C582D4C00651CE2 /* TestNSTimeZone.swift in Sources */,
5B13B34B1C582D4C00651CE2 /* TestNSURLRequest.swift in Sources */,
@@ -2426,6 +2431,7 @@
5B13B3271C582D4C00651CE2 /* TestNSArray.swift in Sources */,
5B13B3461C582D4C00651CE2 /* TestProcess.swift in Sources */,
555683BD1C1250E70041D4C6 /* TestNSUserDefaults.swift in Sources */,
+ DCA8120B1F046D13000D0C86 /* TestCodable.swift in Sources */,
7900433B1CACD33E00ECCBF1 /* TestNSCompoundPredicate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
diff --git a/Foundation/CGFloat.swift b/Foundation/CGFloat.swift
index 03371f3..904600d 100644
--- a/Foundation/CGFloat.swift
+++ b/Foundation/CGFloat.swift
@@ -949,3 +949,35 @@
return native._cVarArgAlignment
}
}
+
+extension CGFloat : Codable {
+ @_transparent
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.singleValueContainer()
+ do {
+ self.native = try container.decode(NativeType.self)
+ } catch DecodingError.typeMismatch(let type, let context) {
+ // We may have encoded as a different type on a different platform. A
+ // strict fixed-format decoder may disallow a conversion, so let's try the
+ // other type.
+ do {
+ if NativeType.self == Float.self {
+ self.native = NativeType(try container.decode(Double.self))
+ } else {
+ self.native = NativeType(try container.decode(Float.self))
+ }
+ } catch {
+ // Failed to decode as the other type, too. This is neither a Float nor
+ // a Double. Throw the old error; we don't want to clobber the original
+ // info.
+ throw DecodingError.typeMismatch(type, context)
+ }
+ }
+ }
+
+ @_transparent
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.singleValueContainer()
+ try container.encode(self.native)
+ }
+}
diff --git a/Foundation/CharacterSet.swift b/Foundation/CharacterSet.swift
index bb346f7..84fa74c 100644
--- a/Foundation/CharacterSet.swift
+++ b/Foundation/CharacterSet.swift
@@ -1,4 +1,4 @@
- //===----------------------------------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
@@ -13,7 +13,7 @@
private func _utfRangeToNSRange(_ inRange : Range<UnicodeScalar>) -> NSRange {
return NSMakeRange(Int(inRange.lowerBound.value), Int(inRange.upperBound.value - inRange.lowerBound.value))
}
-
+
private func _utfRangeToNSRange(_ inRange : ClosedRange<UnicodeScalar>) -> NSRange {
return NSMakeRange(Int(inRange.lowerBound.value), Int(inRange.upperBound.value - inRange.lowerBound.value + 1))
}
@@ -57,9 +57,9 @@
deinit {
releaseWrappedObject()
}
-
-
- override func copy(with zone: NSZone? = nil) -> Any {
+
+
+ override func copy(with zone: NSZone? = nil) -> Any {
return _mapUnmanaged { $0.copy(with: zone) }
}
@@ -74,26 +74,32 @@
override var bitmapRepresentation: Data {
return _mapUnmanaged { $0.bitmapRepresentation }
}
-
+
override var inverted : CharacterSet {
return _mapUnmanaged { $0.inverted }
}
-
+
override func hasMemberInPlane(_ thePlane: UInt8) -> Bool {
return _mapUnmanaged {$0.hasMemberInPlane(thePlane) }
}
-
+
override func characterIsMember(_ member: unichar) -> Bool {
return _mapUnmanaged { $0.characterIsMember(member) }
}
-
+
override func longCharacterIsMember(_ member: UInt32) -> Bool {
return _mapUnmanaged { $0.longCharacterIsMember(member) }
}
-
+
override func isSuperset(of other: CharacterSet) -> Bool {
return _mapUnmanaged { $0.isSuperset(of: other) }
}
+
+ override var _cfObject: CFType {
+ // We cannot inherit super's unsafeBitCast(self, to: CFType.self) here, because layout of _SwiftNSCharacterSet
+ // is not compatible with CFCharacterSet. We need to bitcast the underlying NSCharacterSet instead.
+ return _mapUnmanaged { unsafeBitCast($0, to: CFType.self) }
+ }
}
/**
@@ -367,7 +373,7 @@
// -----
// MARK: -
// MARK: SetAlgebraType
-
+
/// Insert a `UnicodeScalar` representation of a character into the `CharacterSet`.
///
/// `UnicodeScalar` values are available on `Swift.String.UnicodeScalarView`.
@@ -441,17 +447,17 @@
$0.formIntersection(with: other)
}
}
-
+
/// Returns a `CharacterSet` created by removing elements in `other` from `self`.
public func subtracting(_ other: CharacterSet) -> CharacterSet {
return intersection(other.inverted)
}
-
+
/// Sets the value to a `CharacterSet` created by removing elements in `other` from `self`.
public mutating func subtract(_ other: CharacterSet) {
self = subtracting(other)
}
-
+
/// Returns an exclusive or of the `CharacterSet` with another `CharacterSet`.
public func symmetricDifference(_ other: CharacterSet) -> CharacterSet {
return union(other).subtracting(intersection(other))
@@ -466,16 +472,16 @@
public func isSuperset(of other: CharacterSet) -> Bool {
return _mapUnmanaged { $0.isSuperset(of: other) }
}
-
+
/// Returns true if the two `CharacterSet`s are equal.
public static func ==(lhs : CharacterSet, rhs: CharacterSet) -> Bool {
- return lhs._wrapped.isEqual(rhs._bridgeToObjectiveC()) // TODO: mlehew - as NSCharacterSet
+ return lhs._mapUnmanaged { $0.isEqual(rhs) }
}
}
// MARK: Objective-C Bridging
- extension CharacterSet : _ObjectTypeBridgeable {
+extension CharacterSet : _ObjectTypeBridgeable {
public static func _isBridgedToObjectiveC() -> Bool {
return true
}
@@ -503,3 +509,20 @@
}
}
+
+extension CharacterSet : Codable {
+ private enum CodingKeys : Int, CodingKey {
+ case bitmap
+ }
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ let bitmap = try container.decode(Data.self, forKey: .bitmap)
+ self.init(bitmapRepresentation: bitmap)
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(self.bitmapRepresentation, forKey: .bitmap)
+ }
+}
diff --git a/Foundation/Codable.swift b/Foundation/Codable.swift
index 06bdc21..3e34692 100644
--- a/Foundation/Codable.swift
+++ b/Foundation/Codable.swift
@@ -8,64 +8,6 @@
//
//===----------------------------------------------------------------------===//
-// Errors
-//===----------------------------------------------------------------------===//
-
-// Adding the following extensions to EncodingError and DecodingError allows them to bridge to NSErrors implicitly.
-
-fileprivate let NSCodingPathErrorKey = "NSCodingPath"
-fileprivate let NSDebugDescriptionErrorKey = "NSDebugDescription"
-
-extension EncodingError : CustomNSError {
- public static var errorDomain: String = NSCocoaErrorDomain
-
- public var errorCode: Int {
- switch self {
- case .invalidValue(_, _): return CocoaError.coderInvalidValue.rawValue
- }
- }
-
- public var errorUserInfo: [String : Any] {
- let context: Context
- switch self {
- case .invalidValue(_, let c): context = c
- }
-
- return [NSCodingPathErrorKey: context.codingPath,
- NSDebugDescriptionErrorKey: context.debugDescription]
- }
-}
-
-extension DecodingError : CustomNSError {
- public static var errorDomain: String = NSCocoaErrorDomain
-
- public var errorCode: Int {
- switch self {
- case .valueNotFound(_, _): fallthrough
- case .keyNotFound(_, _):
- return CocoaError.coderValueNotFound.rawValue
-
- case .typeMismatch(_, _): fallthrough
- case .dataCorrupted(_):
- return CocoaError.coderReadCorrupt.rawValue
- }
- }
-
- public var errorUserInfo: [String : Any] {
- let context: Context
- switch self {
- case .typeMismatch(_, let c): context = c
- case .valueNotFound(_, let c): context = c
- case .keyNotFound(_, let c): context = c
- case .dataCorrupted(let c): context = c
- }
-
- return [NSCodingPathErrorKey: context.codingPath,
- NSDebugDescriptionErrorKey: context.debugDescription]
- }
-}
-
-//===----------------------------------------------------------------------===//
// Error Utilities
//===----------------------------------------------------------------------===//
@@ -76,7 +18,7 @@
/// - parameter expectation: The type expected to be encountered.
/// - parameter reality: The value that was encountered instead of the expected type.
/// - returns: A `DecodingError` with the appropriate path and debug description.
- internal static func _typeMismatch(at path: [CodingKey?], expectation: Any.Type, reality: Any) -> DecodingError {
+ internal static func _typeMismatch(at path: [CodingKey], expectation: Any.Type, reality: Any) -> DecodingError {
let description = "Expected to decode \(expectation) but found \(_typeDescription(of: reality)) instead."
return .typeMismatch(expectation, Context(codingPath: path, debugDescription: description))
}
diff --git a/Foundation/Data.swift b/Foundation/Data.swift
index 5fe69e2..aa63929 100644
--- a/Foundation/Data.swift
+++ b/Foundation/Data.swift
@@ -1514,7 +1514,6 @@
@inline(__always)
public func subdata(in range: Range<Index>) -> Data {
_validateRange(range)
- let length = count
if count == 0 {
return Data()
}
diff --git a/Foundation/ExtraStringAPIs.swift b/Foundation/ExtraStringAPIs.swift
index 792c2f6..2f5d4f1 100644
--- a/Foundation/ExtraStringAPIs.swift
+++ b/Foundation/ExtraStringAPIs.swift
@@ -17,18 +17,16 @@
/// Construct from an integer offset.
public init(_ offset: Int) {
_precondition(offset >= 0, "Negative UTF16 index offset not allowed")
- self.init(_offset: offset)
+ self.init(encodedOffset: offset)
}
public func distance(to other: String.UTF16View.Index) -> Int {
- return _offset.distance(to: other._offset)
+ return encodedOffset.distance(to: other.encodedOffset)
}
public func advanced(by n: Int) -> String.UTF16View.Index {
- return String.UTF16View.Index(_offset.advanced(by: n))
+ return String.UTF16View.Index(encodedOffset.advanced(by: n))
}
}
-extension String.UTF16View : RandomAccessCollection {}
-extension String.UTF16View.Indices : RandomAccessCollection {}
diff --git a/Foundation/FileHandle.swift b/Foundation/FileHandle.swift
index 645dc5d..28bf722 100644
--- a/Foundation/FileHandle.swift
+++ b/Foundation/FileHandle.swift
@@ -76,7 +76,7 @@
fatalError("Unable to fetch current file offset")
}
if off_t(statbuf.st_size) > offset {
- var remaining = size_t(statbuf.st_size - offset)
+ var remaining = size_t(off_t(statbuf.st_size) - offset)
remaining = min(remaining, size_t(length))
dynamicBuffer = malloc(remaining)
diff --git a/Foundation/FileManager.swift b/Foundation/FileManager.swift
index ba4649f..c2f1417 100644
--- a/Foundation/FileManager.swift
+++ b/Foundation/FileManager.swift
@@ -624,9 +624,12 @@
}
}
- open func createFile(atPath path: String, contents data: Data?, attributes attr: [String : Any]? = [:]) -> Bool {
+ open func createFile(atPath path: String, contents data: Data?, attributes attr: [FileAttributeKey : Any]? = nil) -> Bool {
do {
try (data ?? Data()).write(to: URL(fileURLWithPath: path), options: .atomic)
+ if let attr = attr {
+ try self.setAttributes(attr, ofItemAtPath: path)
+ }
return true
} catch _ {
return false
diff --git a/Foundation/IndexPath.swift b/Foundation/IndexPath.swift
index b85aaf6..748c03a 100644
--- a/Foundation/IndexPath.swift
+++ b/Foundation/IndexPath.swift
@@ -851,4 +851,3 @@
}
}
}
-
diff --git a/Foundation/JSONEncoder.swift b/Foundation/JSONEncoder.swift
index c6f09e0..59a3844 100644
--- a/Foundation/JSONEncoder.swift
+++ b/Foundation/JSONEncoder.swift
@@ -1,11 +1,16 @@
+//===----------------------------------------------------------------------===//
+//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
-// See http://swift.org/LICENSE.txt for license information
-// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+// See https://swift.org/LICENSE.txt for license information
+// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
+//===----------------------------------------------------------------------===//
+
+import CoreFoundation
//===----------------------------------------------------------------------===//
// JSON Encoder
@@ -138,7 +143,7 @@
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: [], debugDescription: "Top-level \(T.self) encoded as string JSON fragment."))
}
- var writingOptions = JSONSerialization.WritingOptions(rawValue: self.outputFormatting.rawValue)
+ let writingOptions = JSONSerialization.WritingOptions(rawValue: self.outputFormatting.rawValue)
return try JSONSerialization.data(withJSONObject: topLevel, options: writingOptions)
}
}
@@ -149,23 +154,23 @@
// MARK: Properties
/// The encoder's storage.
- var storage: _JSONEncodingStorage
+ fileprivate var storage: _JSONEncodingStorage
/// Options set on the top-level encoder.
- let options: JSONEncoder._Options
+ fileprivate let options: JSONEncoder._Options
/// The path to the current point in encoding.
- var codingPath: [CodingKey?]
+ public var codingPath: [CodingKey]
/// Contextual user-provided information for use during encoding.
- var userInfo: [CodingUserInfoKey : Any] {
+ public var userInfo: [CodingUserInfoKey : Any] {
return self.options.userInfo
}
// MARK: - Initialization
/// Initializes `self` with the given top-level encoder options.
- init(options: JSONEncoder._Options, codingPath: [CodingKey?] = []) {
+ fileprivate init(options: JSONEncoder._Options, codingPath: [CodingKey] = []) {
self.options = options
self.storage = _JSONEncodingStorage()
self.codingPath = codingPath
@@ -177,7 +182,7 @@
///
/// - parameter key: The key to push. May be nil for unkeyed containers.
/// - parameter work: The work to perform with the key in the path.
- func with<T>(pushedKey key: CodingKey?, _ work: () throws -> T) rethrows -> T {
+ fileprivate func with<T>(pushedKey key: CodingKey, _ work: () throws -> T) rethrows -> T {
self.codingPath.append(key)
let ret: T = try work()
self.codingPath.removeLast()
@@ -187,7 +192,7 @@
/// Returns whether a new element can be encoded at this coding path.
///
/// `true` if an element has not yet been encoded at this coding path; `false` otherwise.
- var canEncodeNewElement: Bool {
+ fileprivate var canEncodeNewValue: Bool {
// Every time a new value gets encoded, the key it's encoded for is pushed onto the coding path (even if it's a nil key from an unkeyed container).
// At the same time, every time a container is requested, a new value gets pushed onto the storage stack.
// If there are more values on the storage stack than on the coding path, it means the value is requesting more than one container, which violates the precondition.
@@ -197,39 +202,43 @@
return self.storage.count == self.codingPath.count
}
- /// Asserts that a new container can be requested at this coding path.
- /// `preconditionFailure()`s if one cannot be requested.
- func assertCanRequestNewContainer() {
- guard self.canEncodeNewElement else {
- let previousContainerType: String
- if self.storage.containers.last is NSDictionary {
- previousContainerType = "keyed"
- } else if self.storage.containers.last is NSArray {
- previousContainerType = "unkeyed"
- } else {
- previousContainerType = "single value"
+ // MARK: - Encoder Methods
+ public func container<Key>(keyedBy: Key.Type) -> KeyedEncodingContainer<Key> {
+ // If an existing keyed container was already requested, return that one.
+ let topContainer: NSMutableDictionary
+ if self.canEncodeNewValue {
+ // We haven't yet pushed a container at this level; do so here.
+ topContainer = self.storage.pushKeyedContainer()
+ } else {
+ guard let container = self.storage.containers.last as? NSMutableDictionary else {
+ preconditionFailure("Attempt to push new keyed encoding container when already previously encoded at this path.")
}
- preconditionFailure("Attempt to encode with new container when already encoded with \(previousContainerType) container.")
+ topContainer = container
}
- }
- // MARK: - Encoder Methods
- func container<Key>(keyedBy: Key.Type) -> KeyedEncodingContainer<Key> {
- assertCanRequestNewContainer()
- let topContainer = self.storage.pushKeyedContainer()
let container = _JSONKeyedEncodingContainer<Key>(referencing: self, codingPath: self.codingPath, wrapping: topContainer)
return KeyedEncodingContainer(container)
}
- func unkeyedContainer() -> UnkeyedEncodingContainer {
- assertCanRequestNewContainer()
- let topContainer = self.storage.pushUnkeyedContainer()
+ public func unkeyedContainer() -> UnkeyedEncodingContainer {
+ // If an existing unkeyed container was already requested, return that one.
+ let topContainer: NSMutableArray
+ if self.canEncodeNewValue {
+ // We haven't yet pushed a container at this level; do so here.
+ topContainer = self.storage.pushUnkeyedContainer()
+ } else {
+ guard let container = self.storage.containers.last as? NSMutableArray else {
+ preconditionFailure("Attempt to push new unkeyed encoding container when already previously encoded at this path.")
+ }
+
+ topContainer = container
+ }
+
return _JSONUnkeyedEncodingContainer(referencing: self, codingPath: self.codingPath, wrapping: topContainer)
}
- func singleValueContainer() -> SingleValueEncodingContainer {
- assertCanRequestNewContainer()
+ public func singleValueContainer() -> SingleValueEncodingContainer {
return self
}
}
@@ -241,36 +250,36 @@
/// The container stack.
/// Elements may be any one of the JSON types (NSNull, NSNumber, NSString, NSArray, NSDictionary).
- private(set) var containers: [NSObject] = []
+ private(set) fileprivate var containers: [NSObject] = []
// MARK: - Initialization
/// Initializes `self` with no containers.
- init() {}
+ fileprivate init() {}
// MARK: - Modifying the Stack
- var count: Int {
+ fileprivate var count: Int {
return self.containers.count
}
- mutating func pushKeyedContainer() -> NSMutableDictionary {
+ fileprivate mutating func pushKeyedContainer() -> NSMutableDictionary {
let dictionary = NSMutableDictionary()
self.containers.append(dictionary)
return dictionary
}
- mutating func pushUnkeyedContainer() -> NSMutableArray {
+ fileprivate mutating func pushUnkeyedContainer() -> NSMutableArray {
let array = NSMutableArray()
self.containers.append(array)
return array
}
- mutating func push(container: NSObject) {
+ fileprivate mutating func push(container: NSObject) {
self.containers.append(container)
}
- mutating func popContainer() -> NSObject {
+ fileprivate mutating func popContainer() -> NSObject {
precondition(self.containers.count > 0, "Empty container stack.")
return self.containers.popLast()!
}
@@ -284,18 +293,18 @@
// MARK: Properties
/// A reference to the encoder we're writing to.
- let encoder: _JSONEncoder
+ private let encoder: _JSONEncoder
/// A reference to the container we're writing to.
- let container: NSMutableDictionary
+ private let container: NSMutableDictionary
/// The path of coding keys taken to get to this point in encoding.
- var codingPath: [CodingKey?]
+ private(set) public var codingPath: [CodingKey]
// MARK: - Initialization
/// Initializes `self` with the given references.
- init(referencing encoder: _JSONEncoder, codingPath: [CodingKey?], wrapping container: NSMutableDictionary) {
+ fileprivate init(referencing encoder: _JSONEncoder, codingPath: [CodingKey], wrapping container: NSMutableDictionary) {
self.encoder = encoder
self.codingPath = codingPath
self.container = container
@@ -307,7 +316,7 @@
///
/// - parameter key: The key to push. May be nil for unkeyed containers.
/// - parameter work: The work to perform with the key in the path.
- mutating func with<T>(pushedKey key: CodingKey?, _ work: () throws -> T) rethrows -> T {
+ fileprivate mutating func with<T>(pushedKey key: CodingKey, _ work: () throws -> T) rethrows -> T {
self.codingPath.append(key)
let ret: T = try work()
self.codingPath.removeLast()
@@ -316,40 +325,41 @@
// MARK: - KeyedEncodingContainerProtocol Methods
- mutating func encode(_ value: Bool, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
- mutating func encode(_ value: Int, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
- mutating func encode(_ value: Int8, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
- mutating func encode(_ value: Int16, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
- mutating func encode(_ value: Int32, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
- mutating func encode(_ value: Int64, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
- mutating func encode(_ value: UInt, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
- mutating func encode(_ value: UInt8, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
- mutating func encode(_ value: UInt16, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
- mutating func encode(_ value: UInt32, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
- mutating func encode(_ value: UInt64, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
- mutating func encode(_ value: String, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
+ public mutating func encodeNil(forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = NSNull() }
+ public mutating func encode(_ value: Bool, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
+ public mutating func encode(_ value: Int, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
+ public mutating func encode(_ value: Int8, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
+ public mutating func encode(_ value: Int16, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
+ public mutating func encode(_ value: Int32, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
+ public mutating func encode(_ value: Int64, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
+ public mutating func encode(_ value: UInt, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
+ public mutating func encode(_ value: UInt8, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
+ public mutating func encode(_ value: UInt16, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
+ public mutating func encode(_ value: UInt32, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
+ public mutating func encode(_ value: UInt64, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
+ public mutating func encode(_ value: String, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
- mutating func encode(_ value: Float, forKey key: Key) throws {
+ public mutating func encode(_ value: Float, forKey key: Key) throws {
// Since the float may be invalid and throw, the coding path needs to contain this key.
try self.encoder.with(pushedKey: key) {
self.container[key.stringValue._bridgeToObjectiveC()] = try self.encoder.box(value)
}
}
- mutating func encode(_ value: Double, forKey key: Key) throws {
+ public mutating func encode(_ value: Double, forKey key: Key) throws {
// Since the double may be invalid and throw, the coding path needs to contain this key.
try self.encoder.with(pushedKey: key) {
self.container[key.stringValue._bridgeToObjectiveC()] = try self.encoder.box(value)
}
}
- mutating func encode<T : Encodable>(_ value: T, forKey key: Key) throws {
+ public mutating func encode<T : Encodable>(_ value: T, forKey key: Key) throws {
try self.encoder.with(pushedKey: key) {
self.container[key.stringValue._bridgeToObjectiveC()] = try self.encoder.box(value)
}
}
- mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> {
+ public mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> {
let dictionary = NSMutableDictionary()
self.container[key.stringValue._bridgeToObjectiveC()] = dictionary
@@ -359,7 +369,7 @@
}
}
- mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
+ public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
let array = NSMutableArray()
self.container[key.stringValue._bridgeToObjectiveC()] = array
@@ -368,11 +378,11 @@
}
}
- mutating func superEncoder() -> Encoder {
- return _JSONReferencingEncoder(referencing: self.encoder, at: _JSONSuperKey.super, wrapping: self.container)
+ public mutating func superEncoder() -> Encoder {
+ return _JSONReferencingEncoder(referencing: self.encoder, at: _JSONKey.super, wrapping: self.container)
}
- mutating func superEncoder(forKey key: Key) -> Encoder {
+ public mutating func superEncoder(forKey key: Key) -> Encoder {
return _JSONReferencingEncoder(referencing: self.encoder, at: key, wrapping: self.container)
}
}
@@ -381,18 +391,23 @@
// MARK: Properties
/// A reference to the encoder we're writing to.
- let encoder: _JSONEncoder
+ private let encoder: _JSONEncoder
/// A reference to the container we're writing to.
- let container: NSMutableArray
+ private let container: NSMutableArray
/// The path of coding keys taken to get to this point in encoding.
- var codingPath: [CodingKey?]
+ private(set) public var codingPath: [CodingKey]
+
+ /// The number of elements encoded into the container.
+ public var count: Int {
+ return self.container.count
+ }
// MARK: - Initialization
/// Initializes `self` with the given references.
- init(referencing encoder: _JSONEncoder, codingPath: [CodingKey?], wrapping container: NSMutableArray) {
+ fileprivate init(referencing encoder: _JSONEncoder, codingPath: [CodingKey], wrapping container: NSMutableArray) {
self.encoder = encoder
self.codingPath = codingPath
self.container = container
@@ -404,7 +419,7 @@
///
/// - parameter key: The key to push. May be nil for unkeyed containers.
/// - parameter work: The work to perform with the key in the path.
- mutating func with<T>(pushedKey key: CodingKey?, _ work: () throws -> T) rethrows -> T {
+ fileprivate mutating func with<T>(pushedKey key: CodingKey, _ work: () throws -> T) rethrows -> T {
self.codingPath.append(key)
let ret: T = try work()
self.codingPath.removeLast()
@@ -413,164 +428,150 @@
// MARK: - UnkeyedEncodingContainer Methods
- mutating func encode(_ value: Bool) throws { self.container.add(self.encoder.box(value)) }
- mutating func encode(_ value: Int) throws { self.container.add(self.encoder.box(value)) }
- mutating func encode(_ value: Int8) throws { self.container.add(self.encoder.box(value)) }
- mutating func encode(_ value: Int16) throws { self.container.add(self.encoder.box(value)) }
- mutating func encode(_ value: Int32) throws { self.container.add(self.encoder.box(value)) }
- mutating func encode(_ value: Int64) throws { self.container.add(self.encoder.box(value)) }
- mutating func encode(_ value: UInt) throws { self.container.add(self.encoder.box(value)) }
- mutating func encode(_ value: UInt8) throws { self.container.add(self.encoder.box(value)) }
- mutating func encode(_ value: UInt16) throws { self.container.add(self.encoder.box(value)) }
- mutating func encode(_ value: UInt32) throws { self.container.add(self.encoder.box(value)) }
- mutating func encode(_ value: UInt64) throws { self.container.add(self.encoder.box(value)) }
- mutating func encode(_ value: String) throws { self.container.add(self.encoder.box(value)) }
+ public mutating func encodeNil() throws { self.container.add(NSNull()) }
+ public mutating func encode(_ value: Bool) throws { self.container.add(self.encoder.box(value)) }
+ public mutating func encode(_ value: Int) throws { self.container.add(self.encoder.box(value)) }
+ public mutating func encode(_ value: Int8) throws { self.container.add(self.encoder.box(value)) }
+ public mutating func encode(_ value: Int16) throws { self.container.add(self.encoder.box(value)) }
+ public mutating func encode(_ value: Int32) throws { self.container.add(self.encoder.box(value)) }
+ public mutating func encode(_ value: Int64) throws { self.container.add(self.encoder.box(value)) }
+ public mutating func encode(_ value: UInt) throws { self.container.add(self.encoder.box(value)) }
+ public mutating func encode(_ value: UInt8) throws { self.container.add(self.encoder.box(value)) }
+ public mutating func encode(_ value: UInt16) throws { self.container.add(self.encoder.box(value)) }
+ public mutating func encode(_ value: UInt32) throws { self.container.add(self.encoder.box(value)) }
+ public mutating func encode(_ value: UInt64) throws { self.container.add(self.encoder.box(value)) }
+ public mutating func encode(_ value: String) throws { self.container.add(self.encoder.box(value)) }
- mutating func encode(_ value: Float) throws {
+ public mutating func encode(_ value: Float) throws {
// Since the float may be invalid and throw, the coding path needs to contain this key.
- try self.encoder.with(pushedKey: nil) {
+ try self.encoder.with(pushedKey: _JSONKey(index: self.count)) {
self.container.add(try self.encoder.box(value))
}
}
- mutating func encode(_ value: Double) throws {
+ public mutating func encode(_ value: Double) throws {
// Since the double may be invalid and throw, the coding path needs to contain this key.
- try self.encoder.with(pushedKey: nil) {
+ try self.encoder.with(pushedKey: _JSONKey(index: self.count)) {
self.container.add(try self.encoder.box(value))
}
}
- mutating func encode<T : Encodable>(_ value: T) throws {
- try self.encoder.with(pushedKey: nil) {
+ public mutating func encode<T : Encodable>(_ value: T) throws {
+ try self.encoder.with(pushedKey: _JSONKey(index: self.count)) {
self.container.add(try self.encoder.box(value))
}
}
- mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> {
+ public mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> {
let dictionary = NSMutableDictionary()
self.container.add(dictionary)
- return self.with(pushedKey: nil) {
+ // self.count - 1 to accommodate the fact that we just pushed a container.
+ return self.with(pushedKey: _JSONKey(index: self.count - 1)) {
let container = _JSONKeyedEncodingContainer<NestedKey>(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary)
return KeyedEncodingContainer(container)
}
}
- mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
+ public mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
let array = NSMutableArray()
self.container.add(array)
- return self.with(pushedKey: nil) {
+ // self.count - 1 to accommodate the fact that we just pushed a container.
+ return self.with(pushedKey: _JSONKey(index: self.count - 1)) {
return _JSONUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array)
}
}
- mutating func superEncoder() -> Encoder {
+ public mutating func superEncoder() -> Encoder {
return _JSONReferencingEncoder(referencing: self.encoder, at: self.container.count, wrapping: self.container)
}
}
extension _JSONEncoder : SingleValueEncodingContainer {
- // MARK: - Utility Methods
-
- /// Asserts that a single value can be encoded at the current coding path (i.e. that one has not already been encoded through this container).
- /// `preconditionFailure()`s if one cannot be encoded.
- ///
- /// This is similar to assertCanRequestNewContainer above.
- func assertCanEncodeSingleValue() {
- guard self.canEncodeNewElement else {
- let previousContainerType: String
- if self.storage.containers.last is NSDictionary {
- previousContainerType = "keyed"
- } else if self.storage.containers.last is NSArray {
- previousContainerType = "unkeyed"
- } else {
- preconditionFailure("Attempt to encode multiple values in a single value container.")
- }
-
- preconditionFailure("Attempt to encode with new container when already encoded with \(previousContainerType) container.")
- }
- }
-
// MARK: - SingleValueEncodingContainer Methods
- func encodeNil() throws {
- assertCanEncodeSingleValue()
+ fileprivate func assertCanEncodeNewValue() {
+ precondition(self.canEncodeNewValue, "Attempt to encode value through single value container when previously value already encoded.")
+ }
+
+ public func encodeNil() throws {
+ assertCanEncodeNewValue()
self.storage.push(container: NSNull())
}
- func encode(_ value: Bool) throws {
- assertCanEncodeSingleValue()
+ public func encode(_ value: Bool) throws {
+ assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
- func encode(_ value: Int) throws {
- assertCanEncodeSingleValue()
+ public func encode(_ value: Int) throws {
+ assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
- func encode(_ value: Int8) throws {
- assertCanEncodeSingleValue()
+ public func encode(_ value: Int8) throws {
+ assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
- func encode(_ value: Int16) throws {
- assertCanEncodeSingleValue()
+ public func encode(_ value: Int16) throws {
+ assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
- func encode(_ value: Int32) throws {
- assertCanEncodeSingleValue()
+ public func encode(_ value: Int32) throws {
+ assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
- func encode(_ value: Int64) throws {
- assertCanEncodeSingleValue()
+ public func encode(_ value: Int64) throws {
+ assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
- func encode(_ value: UInt) throws {
- assertCanEncodeSingleValue()
+ public func encode(_ value: UInt) throws {
+ assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
- func encode(_ value: UInt8) throws {
- assertCanEncodeSingleValue()
+ public func encode(_ value: UInt8) throws {
+ assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
- func encode(_ value: UInt16) throws {
- assertCanEncodeSingleValue()
+ public func encode(_ value: UInt16) throws {
+ assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
- func encode(_ value: UInt32) throws {
- assertCanEncodeSingleValue()
+ public func encode(_ value: UInt32) throws {
+ assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
- func encode(_ value: UInt64) throws {
- assertCanEncodeSingleValue()
+ public func encode(_ value: UInt64) throws {
+ assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
- func encode(_ value: String) throws {
- assertCanEncodeSingleValue()
+ public func encode(_ value: String) throws {
+ assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
- func encode(_ value: Float) throws {
- assertCanEncodeSingleValue()
+ public func encode(_ value: Float) throws {
+ assertCanEncodeNewValue()
try self.storage.push(container: box(value))
}
- func encode(_ value: Double) throws {
- assertCanEncodeSingleValue()
+ public func encode(_ value: Double) throws {
+ assertCanEncodeNewValue()
try self.storage.push(container: box(value))
}
- func encode<T : Encodable>(_ value: T) throws {
- assertCanEncodeSingleValue()
+ public func encode<T : Encodable>(_ value: T) throws {
+ assertCanEncodeNewValue()
try self.storage.push(container: box(value))
}
}
@@ -597,7 +598,7 @@
guard case let .convertToString(positiveInfinity: posInfString,
negativeInfinity: negInfString,
nan: nanString) = self.options.nonConformingFloatEncodingStrategy else {
- throw EncodingError._invalidFloatingPointValue(float, at: codingPath)
+ throw EncodingError._invalidFloatingPointValue(float, at: codingPath)
}
if float == Float.infinity {
@@ -617,7 +618,7 @@
guard case let .convertToString(positiveInfinity: posInfString,
negativeInfinity: negInfString,
nan: nanString) = self.options.nonConformingFloatEncodingStrategy else {
- throw EncodingError._invalidFloatingPointValue(double, at: codingPath)
+ throw EncodingError._invalidFloatingPointValue(double, at: codingPath)
}
if double == Double.infinity {
@@ -698,6 +699,11 @@
} else if T.self == URL.self {
// Encode URLs as single strings.
return self.box((value as! URL).absoluteString)
+ } else if T.self == Decimal.self {
+ // On Darwin we get ((value as! Decimal) as NSDecimalNumber) since JSONSerialization can consume NSDecimalNumber values.
+ // FIXME: Attempt to create a Decimal value if JSONSerialization on Linux consume one.
+ let doubleValue = (value as! Decimal).doubleValue
+ return try self.box(doubleValue)
}
// The value should request a container from the _JSONEncoder.
@@ -722,7 +728,7 @@
// MARK: Reference types.
/// The type of container we're referencing.
- enum Reference {
+ private enum Reference {
/// Referencing a specific index in an array container.
case array(NSMutableArray, Int)
@@ -733,24 +739,24 @@
// MARK: - Properties
/// The encoder we're referencing.
- let encoder: _JSONEncoder
+ fileprivate let encoder: _JSONEncoder
/// The container reference itself.
- let reference: Reference
+ private let reference: Reference
// MARK: - Initialization
/// Initializes `self` by referencing the given array container in the given encoder.
- init(referencing encoder: _JSONEncoder, at index: Int, wrapping array: NSMutableArray) {
+ fileprivate init(referencing encoder: _JSONEncoder, at index: Int, wrapping array: NSMutableArray) {
self.encoder = encoder
self.reference = .array(array, index)
super.init(options: encoder.options, codingPath: encoder.codingPath)
- self.codingPath.append(nil)
+ self.codingPath.append(_JSONKey(index: index))
}
/// Initializes `self` by referencing the given dictionary container in the given encoder.
- init(referencing encoder: _JSONEncoder, at key: CodingKey, wrapping dictionary: NSMutableDictionary) {
+ fileprivate init(referencing encoder: _JSONEncoder, at key: CodingKey, wrapping dictionary: NSMutableDictionary) {
self.encoder = encoder
self.reference = .dictionary(dictionary, key.stringValue)
super.init(options: encoder.options, codingPath: encoder.codingPath)
@@ -760,7 +766,7 @@
// MARK: - Coding Path Operations
- override var canEncodeNewElement: Bool {
+ fileprivate override var canEncodeNewValue: Bool {
// With a regular encoder, the storage and coding path grow together.
// A referencing encoder, however, inherits its parents coding path, as well as the key it was created for.
// We have to take this into account.
@@ -891,23 +897,23 @@
// MARK: Properties
/// The decoder's storage.
- var storage: _JSONDecodingStorage
+ fileprivate var storage: _JSONDecodingStorage
/// Options set on the top-level decoder.
- let options: JSONDecoder._Options
+ fileprivate let options: JSONDecoder._Options
/// The path to the current point in encoding.
- var codingPath: [CodingKey?]
+ private(set) public var codingPath: [CodingKey]
/// Contextual user-provided information for use during encoding.
- var userInfo: [CodingUserInfoKey : Any] {
+ public var userInfo: [CodingUserInfoKey : Any] {
return self.options.userInfo
}
// MARK: - Initialization
/// Initializes `self` with the given top-level container and options.
- init(referencing container: Any, at codingPath: [CodingKey?] = [], options: JSONDecoder._Options) {
+ fileprivate init(referencing container: Any, at codingPath: [CodingKey] = [], options: JSONDecoder._Options) {
self.storage = _JSONDecodingStorage()
self.storage.push(container: container)
self.codingPath = codingPath
@@ -920,7 +926,7 @@
///
/// - parameter key: The key to push. May be nil for unkeyed containers.
/// - parameter work: The work to perform with the key in the path.
- func with<T>(pushedKey key: CodingKey?, _ work: () throws -> T) rethrows -> T {
+ fileprivate func with<T>(pushedKey key: CodingKey, _ work: () throws -> T) rethrows -> T {
self.codingPath.append(key)
let ret: T = try work()
self.codingPath.removeLast()
@@ -929,7 +935,7 @@
// MARK: - Decoder Methods
- func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> {
+ public func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> {
guard !(self.storage.topContainer is NSNull) else {
throw DecodingError.valueNotFound(KeyedDecodingContainer<Key>.self,
DecodingError.Context(codingPath: self.codingPath,
@@ -944,7 +950,7 @@
return KeyedDecodingContainer(container)
}
- func unkeyedContainer() throws -> UnkeyedDecodingContainer {
+ public func unkeyedContainer() throws -> UnkeyedDecodingContainer {
guard !(self.storage.topContainer is NSNull) else {
throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self,
DecodingError.Context(codingPath: self.codingPath,
@@ -958,7 +964,7 @@
return _JSONUnkeyedDecodingContainer(referencing: self, wrapping: topContainer)
}
- func singleValueContainer() throws -> SingleValueDecodingContainer {
+ public func singleValueContainer() throws -> SingleValueDecodingContainer {
return self
}
}
@@ -970,29 +976,29 @@
/// The container stack.
/// Elements may be any one of the JSON types (NSNull, NSNumber, String, Array, [String : Any]).
- private(set) var containers: [Any] = []
+ private(set) fileprivate var containers: [Any] = []
// MARK: - Initialization
/// Initializes `self` with no containers.
- init() {}
+ fileprivate init() {}
// MARK: - Modifying the Stack
- var count: Int {
+ fileprivate var count: Int {
return self.containers.count
}
- var topContainer: Any {
+ fileprivate var topContainer: Any {
precondition(self.containers.count > 0, "Empty container stack.")
return self.containers.last!
}
- mutating func push(container: Any) {
+ fileprivate mutating func push(container: Any) {
self.containers.append(container)
}
- mutating func popContainer() {
+ fileprivate mutating func popContainer() {
precondition(self.containers.count > 0, "Empty container stack.")
self.containers.removeLast()
}
@@ -1006,18 +1012,18 @@
// MARK: Properties
/// A reference to the decoder we're reading from.
- let decoder: _JSONDecoder
+ private let decoder: _JSONDecoder
/// A reference to the container we're reading from.
- let container: [String : Any]
+ private let container: [String : Any]
/// The path of coding keys taken to get to this point in decoding.
- var codingPath: [CodingKey?]
+ private(set) public var codingPath: [CodingKey]
// MARK: - Initialization
/// Initializes `self` by referencing the given decoder and container.
- init(referencing decoder: _JSONDecoder, wrapping container: [String : Any]) {
+ fileprivate init(referencing decoder: _JSONDecoder, wrapping container: [String : Any]) {
self.decoder = decoder
self.container = container
self.codingPath = decoder.codingPath
@@ -1025,111 +1031,233 @@
// MARK: - KeyedDecodingContainerProtocol Methods
- var allKeys: [Key] {
+ public var allKeys: [Key] {
return self.container.keys.flatMap { Key(stringValue: $0) }
}
- func contains(_ key: Key) -> Bool {
+ public func contains(_ key: Key) -> Bool {
return self.container[key.stringValue] != nil
}
- func decodeIfPresent(_ type: Bool.Type, forKey key: Key) throws -> Bool? {
+ public func decodeNil(forKey key: Key) throws -> Bool {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
+ return entry is NSNull
+ }
+
+ public func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: Bool.self)
+ guard let value = try self.decoder.unbox(entry, as: Bool.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
+ }
+
+ return value
}
}
- func decodeIfPresent(_ type: Int.Type, forKey key: Key) throws -> Int? {
+ public func decode(_ type: Int.Type, forKey key: Key) throws -> Int {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: Int.self)
+ guard let value = try self.decoder.unbox(entry, as: Int.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
+ }
+
+ return value
}
}
- func decodeIfPresent(_ type: Int8.Type, forKey key: Key) throws -> Int8? {
+ public func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: Int8.self)
+ guard let value = try self.decoder.unbox(entry, as: Int8.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
+ }
+
+ return value
}
}
- func decodeIfPresent(_ type: Int16.Type, forKey key: Key) throws -> Int16? {
+ public func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: Int16.self)
+ guard let value = try self.decoder.unbox(entry, as: Int16.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
+ }
+
+ return value
}
}
- func decodeIfPresent(_ type: Int32.Type, forKey key: Key) throws -> Int32? {
+ public func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: Int32.self)
+ guard let value = try self.decoder.unbox(entry, as: Int32.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
+ }
+
+ return value
}
}
- func decodeIfPresent(_ type: Int64.Type, forKey key: Key) throws -> Int64? {
+ public func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: Int64.self)
+ guard let value = try self.decoder.unbox(entry, as: Int64.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
+ }
+
+ return value
}
}
- func decodeIfPresent(_ type: UInt.Type, forKey key: Key) throws -> UInt? {
+ public func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: UInt.self)
+ guard let value = try self.decoder.unbox(entry, as: UInt.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
+ }
+
+ return value
}
}
- func decodeIfPresent(_ type: UInt8.Type, forKey key: Key) throws -> UInt8? {
+ public func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: UInt8.self)
+ guard let value = try self.decoder.unbox(entry, as: UInt8.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
+ }
+
+ return value
}
}
- func decodeIfPresent(_ type: UInt16.Type, forKey key: Key) throws -> UInt16? {
+ public func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: UInt16.self)
+ guard let value = try self.decoder.unbox(entry, as: UInt16.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
+ }
+
+ return value
}
}
- func decodeIfPresent(_ type: UInt32.Type, forKey key: Key) throws -> UInt32? {
+ public func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: UInt32.self)
+ guard let value = try self.decoder.unbox(entry, as: UInt32.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
+ }
+
+ return value
}
}
- func decodeIfPresent(_ type: UInt64.Type, forKey key: Key) throws -> UInt64? {
+ public func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: UInt64.self)
+ guard let value = try self.decoder.unbox(entry, as: UInt64.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
+ }
+
+ return value
}
}
- func decodeIfPresent(_ type: Float.Type, forKey key: Key) throws -> Float? {
+ public func decode(_ type: Float.Type, forKey key: Key) throws -> Float {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: Float.self)
+ guard let value = try self.decoder.unbox(entry, as: Float.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
+ }
+
+ return value
}
}
- func decodeIfPresent(_ type: Double.Type, forKey key: Key) throws -> Double? {
+ public func decode(_ type: Double.Type, forKey key: Key) throws -> Double {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: Double.self)
+ guard let value = try self.decoder.unbox(entry, as: Double.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
+ }
+
+ return value
}
}
- func decodeIfPresent(_ type: String.Type, forKey key: Key) throws -> String? {
+ public func decode(_ type: String.Type, forKey key: Key) throws -> String {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: String.self)
+ guard let value = try self.decoder.unbox(entry, as: String.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
+ }
+
+ return value
}
}
- func decodeIfPresent(_ type: Data.Type, forKey key: Key) throws -> Data? {
+ public func decode<T : Decodable>(_ type: T.Type, forKey key: Key) throws -> T {
+ guard let entry = self.container[key.stringValue] else {
+ throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
+ }
+
return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: Data.self)
+ guard let value = try self.decoder.unbox(entry, as: T.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
+ }
+
+ return value
}
}
- func decodeIfPresent<T : Decodable>(_ type: T.Type, forKey key: Key) throws -> T? {
- return try self.decoder.with(pushedKey: key) {
- return try self.decoder.unbox(self.container[key.stringValue], as: T.self)
- }
- }
-
- func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> {
+ public func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> {
return try self.decoder.with(pushedKey: key) {
guard let value = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key,
@@ -1146,7 +1274,7 @@
}
}
- func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
+ public func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
return try self.decoder.with(pushedKey: key) {
guard let value = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key,
@@ -1162,23 +1290,18 @@
}
}
- func _superDecoder(forKey key: CodingKey) throws -> Decoder {
- return try self.decoder.with(pushedKey: key) {
- guard let value = self.container[key.stringValue] else {
- throw DecodingError.keyNotFound(key,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Cannot get superDecoder() -- no value found for key \"\(key.stringValue)\""))
- }
-
+ private func _superDecoder(forKey key: CodingKey) throws -> Decoder {
+ return self.decoder.with(pushedKey: key) {
+ let value: Any = self.container[key.stringValue] ?? NSNull()
return _JSONDecoder(referencing: value, at: self.decoder.codingPath, options: self.decoder.options)
}
}
- func superDecoder() throws -> Decoder {
- return try _superDecoder(forKey: _JSONSuperKey.super)
+ public func superDecoder() throws -> Decoder {
+ return try _superDecoder(forKey: _JSONKey.super)
}
- func superDecoder(forKey key: Key) throws -> Decoder {
+ public func superDecoder(forKey key: Key) throws -> Decoder {
return try _superDecoder(forKey: key)
}
}
@@ -1187,21 +1310,21 @@
// MARK: Properties
/// A reference to the decoder we're reading from.
- let decoder: _JSONDecoder
+ private let decoder: _JSONDecoder
/// A reference to the container we're reading from.
- let container: [Any]
+ private let container: [Any]
/// The path of coding keys taken to get to this point in decoding.
- var codingPath: [CodingKey?]
+ private(set) public var codingPath: [CodingKey]
/// The index of the element we're about to decode.
- var currentIndex: Int
+ private(set) public var currentIndex: Int
// MARK: - Initialization
/// Initializes `self` by referencing the given decoder and container.
- init(referencing decoder: _JSONDecoder, wrapping container: [Any]) {
+ fileprivate init(referencing decoder: _JSONDecoder, wrapping container: [Any]) {
self.decoder = decoder
self.container = container
self.codingPath = decoder.codingPath
@@ -1210,176 +1333,254 @@
// MARK: - UnkeyedDecodingContainer Methods
- var count: Int? {
+ public var count: Int? {
return self.container.count
}
- var isAtEnd: Bool {
+ public var isAtEnd: Bool {
return self.currentIndex >= self.count!
}
- mutating func decodeIfPresent(_ type: Bool.Type) throws -> Bool? {
- guard !self.isAtEnd else { return nil }
+ public mutating func decodeNil() throws -> Bool {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(Any?.self, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Bool.self)
+ if self.container[self.currentIndex] is NSNull {
+ self.currentIndex += 1
+ return true
+ } else {
+ return false
+ }
+ }
+
+ public mutating func decode(_ type: Bool.Type) throws -> Bool {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
+
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
+ guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Bool.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
+ }
+
self.currentIndex += 1
return decoded
}
}
- mutating func decodeIfPresent(_ type: Int.Type) throws -> Int? {
- guard !self.isAtEnd else { return nil }
+ public mutating func decode(_ type: Int.Type) throws -> Int {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int.self)
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
+ guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
+ }
+
self.currentIndex += 1
return decoded
}
}
- mutating func decodeIfPresent(_ type: Int8.Type) throws -> Int8? {
- guard !self.isAtEnd else { return nil }
+ public mutating func decode(_ type: Int8.Type) throws -> Int8 {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int8.self)
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
+ guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int8.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
+ }
+
self.currentIndex += 1
return decoded
}
}
- mutating func decodeIfPresent(_ type: Int16.Type) throws -> Int16? {
- guard !self.isAtEnd else { return nil }
+ public mutating func decode(_ type: Int16.Type) throws -> Int16 {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int16.self)
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
+ guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int16.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
+ }
+
self.currentIndex += 1
return decoded
}
}
- mutating func decodeIfPresent(_ type: Int32.Type) throws -> Int32? {
- guard !self.isAtEnd else { return nil }
+ public mutating func decode(_ type: Int32.Type) throws -> Int32 {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int32.self)
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
+ guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int32.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
+ }
+
self.currentIndex += 1
return decoded
}
}
- mutating func decodeIfPresent(_ type: Int64.Type) throws -> Int64? {
- guard !self.isAtEnd else { return nil }
+ public mutating func decode(_ type: Int64.Type) throws -> Int64 {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int64.self)
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
+ guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int64.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
+ }
+
self.currentIndex += 1
return decoded
}
}
- mutating func decodeIfPresent(_ type: UInt.Type) throws -> UInt? {
- guard !self.isAtEnd else { return nil }
+ public mutating func decode(_ type: UInt.Type) throws -> UInt {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt.self)
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
+ guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
+ }
+
self.currentIndex += 1
return decoded
}
}
- mutating func decodeIfPresent(_ type: UInt8.Type) throws -> UInt8? {
- guard !self.isAtEnd else { return nil }
+ public mutating func decode(_ type: UInt8.Type) throws -> UInt8 {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt8.self)
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
+ guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt8.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
+ }
+
self.currentIndex += 1
return decoded
}
}
- mutating func decodeIfPresent(_ type: UInt16.Type) throws -> UInt16? {
- guard !self.isAtEnd else { return nil }
+ public mutating func decode(_ type: UInt16.Type) throws -> UInt16 {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt16.self)
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
+ guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt16.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
+ }
+
self.currentIndex += 1
return decoded
}
}
- mutating func decodeIfPresent(_ type: UInt32.Type) throws -> UInt32? {
- guard !self.isAtEnd else { return nil }
+ public mutating func decode(_ type: UInt32.Type) throws -> UInt32 {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt32.self)
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
+ guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt32.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
+ }
+
self.currentIndex += 1
return decoded
}
}
- mutating func decodeIfPresent(_ type: UInt64.Type) throws -> UInt64? {
- guard !self.isAtEnd else { return nil }
+ public mutating func decode(_ type: UInt64.Type) throws -> UInt64 {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt64.self)
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
+ guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt64.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
+ }
+
self.currentIndex += 1
return decoded
}
}
- mutating func decodeIfPresent(_ type: Float.Type) throws -> Float? {
- guard !self.isAtEnd else { return nil }
+ public mutating func decode(_ type: Float.Type) throws -> Float {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Float.self)
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
+ guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Float.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
+ }
+
self.currentIndex += 1
return decoded
}
}
- mutating func decodeIfPresent(_ type: Double.Type) throws -> Double? {
- guard !self.isAtEnd else { return nil }
+ public mutating func decode(_ type: Double.Type) throws -> Double {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Double.self)
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
+ guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Double.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
+ }
+
self.currentIndex += 1
return decoded
}
}
- mutating func decodeIfPresent(_ type: String.Type) throws -> String? {
- guard !self.isAtEnd else { return nil }
+ public mutating func decode(_ type: String.Type) throws -> String {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: String.self)
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
+ guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: String.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
+ }
+
self.currentIndex += 1
return decoded
}
}
- mutating func decodeIfPresent(_ type: Data.Type) throws -> Data? {
- guard !self.isAtEnd else { return nil }
+ public mutating func decode<T : Decodable>(_ type: T.Type) throws -> T {
+ guard !self.isAtEnd else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
+ }
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Data.self)
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
+ guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: T.self) else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
+ }
+
self.currentIndex += 1
return decoded
}
}
- mutating func decodeIfPresent<T : Decodable>(_ type: T.Type) throws -> T? {
- guard !self.isAtEnd else { return nil }
-
- return try self.decoder.with(pushedKey: nil) {
- let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: T.self)
- self.currentIndex += 1
- return decoded
- }
- }
-
- mutating func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> {
- return try self.decoder.with(pushedKey: nil) {
+ public mutating func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> {
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(KeyedDecodingContainer<NestedKey>.self,
DecodingError.Context(codingPath: self.codingPath,
@@ -1403,8 +1604,8 @@
}
}
- mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
- return try self.decoder.with(pushedKey: nil) {
+ public mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self,
DecodingError.Context(codingPath: self.codingPath,
@@ -1427,8 +1628,8 @@
}
}
- mutating func superDecoder() throws -> Decoder {
- return try self.decoder.with(pushedKey: nil) {
+ public mutating func superDecoder() throws -> Decoder {
+ return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(Decoder.self,
DecodingError.Context(codingPath: self.codingPath,
@@ -1436,12 +1637,6 @@
}
let value = self.container[self.currentIndex]
- guard !(value is NSNull) else {
- throw DecodingError.valueNotFound(Decoder.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Cannot get superDecoder() -- found null value instead."))
- }
-
self.currentIndex += 1
return _JSONDecoder(referencing: value, at: self.decoder.codingPath, options: self.decoder.options)
}
@@ -1451,159 +1646,89 @@
extension _JSONDecoder : SingleValueDecodingContainer {
// MARK: SingleValueDecodingContainer Methods
- func decodeNil() -> Bool {
+ private func expectNonNull<T>(_ type: T.Type) throws {
+ guard !self.decodeNil() else {
+ throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "Expected \(type) but found null value instead."))
+ }
+ }
+
+ public func decodeNil() -> Bool {
return self.storage.topContainer is NSNull
}
- // These all unwrap the result, since we couldn't have gotten a single value container if the topContainer was null.
- func decode(_ type: Bool.Type) throws -> Bool {
- guard let value = try self.unbox(self.storage.topContainer, as: Bool.self) else {
- throw DecodingError.valueNotFound(Bool.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Expected Bool but found null value instead."))
- }
-
- return value
+ public func decode(_ type: Bool.Type) throws -> Bool {
+ try expectNonNull(Bool.self)
+ return try self.unbox(self.storage.topContainer, as: Bool.self)!
}
- func decode(_ type: Int.Type) throws -> Int {
- guard let value = try self.unbox(self.storage.topContainer, as: Int.self) else {
- throw DecodingError.valueNotFound(Int.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Expected Int but found null value instead."))
- }
-
- return value
+ public func decode(_ type: Int.Type) throws -> Int {
+ try expectNonNull(Int.self)
+ return try self.unbox(self.storage.topContainer, as: Int.self)!
}
- func decode(_ type: Int8.Type) throws -> Int8 {
- guard let value = try self.unbox(self.storage.topContainer, as: Int8.self) else {
- throw DecodingError.valueNotFound(Int8.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Expected Int8 but found null value instead."))
- }
-
- return value
+ public func decode(_ type: Int8.Type) throws -> Int8 {
+ try expectNonNull(Int8.self)
+ return try self.unbox(self.storage.topContainer, as: Int8.self)!
}
- func decode(_ type: Int16.Type) throws -> Int16 {
- guard let value = try self.unbox(self.storage.topContainer, as: Int16.self) else {
- throw DecodingError.valueNotFound(Int16.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Expected Int16 but found null value instead."))
- }
-
- return value
+ public func decode(_ type: Int16.Type) throws -> Int16 {
+ try expectNonNull(Int16.self)
+ return try self.unbox(self.storage.topContainer, as: Int16.self)!
}
- func decode(_ type: Int32.Type) throws -> Int32 {
- guard let value = try self.unbox(self.storage.topContainer, as: Int32.self) else {
- throw DecodingError.valueNotFound(Int32.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Expected Int32 but found null value instead."))
- }
-
- return value
+ public func decode(_ type: Int32.Type) throws -> Int32 {
+ try expectNonNull(Int32.self)
+ return try self.unbox(self.storage.topContainer, as: Int32.self)!
}
- func decode(_ type: Int64.Type) throws -> Int64 {
- guard let value = try self.unbox(self.storage.topContainer, as: Int64.self) else {
- throw DecodingError.valueNotFound(Int64.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Expected Int64 but found null value instead."))
- }
-
- return value
+ public func decode(_ type: Int64.Type) throws -> Int64 {
+ try expectNonNull(Int64.self)
+ return try self.unbox(self.storage.topContainer, as: Int64.self)!
}
- func decode(_ type: UInt.Type) throws -> UInt {
- guard let value = try self.unbox(self.storage.topContainer, as: UInt.self) else {
- throw DecodingError.valueNotFound(UInt.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Expected UInt but found null value instead."))
- }
-
- return value
+ public func decode(_ type: UInt.Type) throws -> UInt {
+ try expectNonNull(UInt.self)
+ return try self.unbox(self.storage.topContainer, as: UInt.self)!
}
- func decode(_ type: UInt8.Type) throws -> UInt8 {
- guard let value = try self.unbox(self.storage.topContainer, as: UInt8.self) else {
- throw DecodingError.valueNotFound(UInt8.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Expected UInt8 but found null value instead."))
- }
-
- return value
+ public func decode(_ type: UInt8.Type) throws -> UInt8 {
+ try expectNonNull(UInt8.self)
+ return try self.unbox(self.storage.topContainer, as: UInt8.self)!
}
- func decode(_ type: UInt16.Type) throws -> UInt16 {
- guard let value = try self.unbox(self.storage.topContainer, as: UInt16.self) else {
- throw DecodingError.valueNotFound(UInt16.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Expected UInt16 but found null value instead."))
- }
-
- return value
+ public func decode(_ type: UInt16.Type) throws -> UInt16 {
+ try expectNonNull(UInt16.self)
+ return try self.unbox(self.storage.topContainer, as: UInt16.self)!
}
- func decode(_ type: UInt32.Type) throws -> UInt32 {
- guard let value = try self.unbox(self.storage.topContainer, as: UInt32.self) else {
- throw DecodingError.valueNotFound(UInt32.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Expected UInt32 but found null value instead."))
- }
-
- return value
+ public func decode(_ type: UInt32.Type) throws -> UInt32 {
+ try expectNonNull(UInt32.self)
+ return try self.unbox(self.storage.topContainer, as: UInt32.self)!
}
- func decode(_ type: UInt64.Type) throws -> UInt64 {
- guard let value = try self.unbox(self.storage.topContainer, as: UInt64.self) else {
- throw DecodingError.valueNotFound(UInt64.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Expected UInt64 but found null value instead."))
- }
-
- return value
+ public func decode(_ type: UInt64.Type) throws -> UInt64 {
+ try expectNonNull(UInt64.self)
+ return try self.unbox(self.storage.topContainer, as: UInt64.self)!
}
- func decode(_ type: Float.Type) throws -> Float {
- guard let value = try self.unbox(self.storage.topContainer, as: Float.self) else {
- throw DecodingError.valueNotFound(Float.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Expected Float but found null value instead."))
- }
-
- return value
+ public func decode(_ type: Float.Type) throws -> Float {
+ try expectNonNull(Float.self)
+ return try self.unbox(self.storage.topContainer, as: Float.self)!
}
- func decode(_ type: Double.Type) throws -> Double {
- guard let value = try self.unbox(self.storage.topContainer, as: Double.self) else {
- throw DecodingError.valueNotFound(Double.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Expected Double but found null value instead."))
- }
-
- return value
+ public func decode(_ type: Double.Type) throws -> Double {
+ try expectNonNull(Double.self)
+ return try self.unbox(self.storage.topContainer, as: Double.self)!
}
- func decode(_ type: String.Type) throws -> String {
- guard let value = try self.unbox(self.storage.topContainer, as: String.self) else {
- throw DecodingError.valueNotFound(String.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Expected String but found null value instead."))
- }
-
- return value
+ public func decode(_ type: String.Type) throws -> String {
+ try expectNonNull(String.self)
+ return try self.unbox(self.storage.topContainer, as: String.self)!
}
- func decode<T : Decodable>(_ type: T.Type) throws -> T {
- guard let value = try self.unbox(self.storage.topContainer, as: T.self) else {
- throw DecodingError.valueNotFound(T.self,
- DecodingError.Context(codingPath: self.codingPath,
- debugDescription: "Expected \(T.self) but found null value instead."))
- }
-
- return value
+ public func decode<T : Decodable>(_ type: T.Type) throws -> T {
+ try expectNonNull(T.self)
+ return try self.unbox(self.storage.topContainer, as: T.self)!
}
}
@@ -1611,8 +1736,7 @@
extension _JSONDecoder {
/// Returns the given value unboxed from a container.
- fileprivate func unbox(_ value: Any?, as type: Bool.Type) throws -> Bool? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: Bool.Type) throws -> Bool? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber else {
@@ -1620,20 +1744,14 @@
}
// TODO: Add a flag to coerce non-boolean numbers into Bools?
- guard number._objCType == .Bool else {
+ guard number._cfTypeID == CFBooleanGetTypeID() else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
- let bool = number.boolValue
- guard NSNumber(value: bool) == number else {
- throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type)."))
- }
-
- return bool
+ return number.boolValue
}
- fileprivate func unbox(_ value: Any?, as type: Int.Type) throws -> Int? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: Int.Type) throws -> Int? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber else {
@@ -1648,8 +1766,7 @@
return int
}
- fileprivate func unbox(_ value: Any?, as type: Int8.Type) throws -> Int8? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: Int8.Type) throws -> Int8? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber else {
@@ -1664,8 +1781,7 @@
return int8
}
- fileprivate func unbox(_ value: Any?, as type: Int16.Type) throws -> Int16? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: Int16.Type) throws -> Int16? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber else {
@@ -1680,8 +1796,7 @@
return int16
}
- fileprivate func unbox(_ value: Any?, as type: Int32.Type) throws -> Int32? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: Int32.Type) throws -> Int32? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber else {
@@ -1696,8 +1811,7 @@
return int32
}
- fileprivate func unbox(_ value: Any?, as type: Int64.Type) throws -> Int64? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: Int64.Type) throws -> Int64? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber else {
@@ -1712,8 +1826,7 @@
return int64
}
- fileprivate func unbox(_ value: Any?, as type: UInt.Type) throws -> UInt? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: UInt.Type) throws -> UInt? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber else {
@@ -1728,8 +1841,7 @@
return uint
}
- fileprivate func unbox(_ value: Any?, as type: UInt8.Type) throws -> UInt8? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: UInt8.Type) throws -> UInt8? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber else {
@@ -1744,8 +1856,7 @@
return uint8
}
- fileprivate func unbox(_ value: Any?, as type: UInt16.Type) throws -> UInt16? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: UInt16.Type) throws -> UInt16? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber else {
@@ -1760,8 +1871,7 @@
return uint16
}
- fileprivate func unbox(_ value: Any?, as type: UInt32.Type) throws -> UInt32? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: UInt32.Type) throws -> UInt32? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber else {
@@ -1776,8 +1886,7 @@
return uint32
}
- fileprivate func unbox(_ value: Any?, as type: UInt64.Type) throws -> UInt64? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: UInt64.Type) throws -> UInt64? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber else {
@@ -1792,8 +1901,7 @@
return uint64
}
- fileprivate func unbox(_ value: Any?, as type: Float.Type) throws -> Float? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: Float.Type) throws -> Float? {
guard !(value is NSNull) else { return nil }
if let number = value as? NSNumber {
@@ -1810,20 +1918,20 @@
return Float(double)
- /* FIXME: If swift-corelibs-foundation doesn't change to use NSNumber, this code path will need to be included and tested:
- } else if let double = value as? Double {
- if abs(double) <= Double(Float.max) {
- return Float(double)
- }
+ /* FIXME: If swift-corelibs-foundation doesn't change to use NSNumber, this code path will need to be included and tested:
+ } else if let double = value as? Double {
+ if abs(double) <= Double(Float.max) {
+ return Float(double)
+ }
- overflow = true
- } else if let int = value as? Int {
- if let float = Float(exactly: int) {
- return float
- }
+ overflow = true
+ } else if let int = value as? Int {
+ if let float = Float(exactly: int) {
+ return float
+ }
- overflow = true
- */
+ overflow = true
+ */
} else if let string = value as? String,
case .convertFromString(let posInfString, let negInfString, let nanString) = self.options.nonConformingFloatDecodingStrategy {
@@ -1839,8 +1947,7 @@
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
- func unbox(_ value: Any?, as type: Double.Type) throws -> Double? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: Double.Type) throws -> Double? {
guard !(value is NSNull) else { return nil }
if let number = value as? NSNumber {
@@ -1850,16 +1957,16 @@
// * If it was Decimal, you will get back the nearest approximation
return number.doubleValue
- /* FIXME: If swift-corelibs-foundation doesn't change to use NSNumber, this code path will need to be included and tested:
- } else if let double = value as? Double {
- return double
- } else if let int = value as? Int {
- if let double = Double(exactly: int) {
- return double
- }
+ /* FIXME: If swift-corelibs-foundation doesn't change to use NSNumber, this code path will need to be included and tested:
+ } else if let double = value as? Double {
+ return double
+ } else if let int = value as? Int {
+ if let double = Double(exactly: int) {
+ return double
+ }
- overflow = true
- */
+ overflow = true
+ */
} else if let string = value as? String,
case .convertFromString(let posInfString, let negInfString, let nanString) = self.options.nonConformingFloatDecodingStrategy {
@@ -1875,8 +1982,7 @@
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
- func unbox(_ value: Any?, as type: String.Type) throws -> String? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: String.Type) throws -> String? {
guard !(value is NSNull) else { return nil }
guard let string = value as? String else {
@@ -1886,8 +1992,7 @@
return string
}
- func unbox(_ value: Any?, as type: Date.Type) throws -> Date? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: Date.Type) throws -> Date? {
guard !(value is NSNull) else { return nil }
switch self.options.dateDecodingStrategy {
@@ -1933,8 +2038,7 @@
}
}
- func unbox(_ value: Any?, as type: Data.Type) throws -> Data? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: Data.Type) throws -> Data? {
guard !(value is NSNull) else { return nil }
switch self.options.dataDecodingStrategy {
@@ -1957,15 +2061,23 @@
}
}
- func unbox<T : Decodable>(_ value: Any?, as type: T.Type) throws -> T? {
- guard let value = value else { return nil }
+ fileprivate func unbox(_ value: Any, as type: Decimal.Type) throws -> Decimal? {
guard !(value is NSNull) else { return nil }
+ // On Darwin we get (value as? Decimal) since JSONSerialization can produce NSDecimalNumber values.
+ // FIXME: Attempt to grab a Decimal value if JSONSerialization on Linux produces one.
+ let doubleValue = try self.unbox(value, as: Double.self)!
+ return Decimal(doubleValue)
+ }
+
+ fileprivate func unbox<T : Decodable>(_ value: Any, as type: T.Type) throws -> T? {
let decoded: T
if T.self == Date.self {
- decoded = (try self.unbox(value, as: Date.self) as! T)
+ guard let date = try self.unbox(value, as: Date.self) else { return nil }
+ decoded = date as! T
} else if T.self == Data.self {
- decoded = (try self.unbox(value, as: Data.self) as! T)
+ guard let data = try self.unbox(value, as: Data.self) else { return nil }
+ decoded = data as! T
} else if T.self == URL.self {
guard let urlString = try self.unbox(value, as: String.self) else {
return nil
@@ -1975,24 +2087,45 @@
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
debugDescription: "Invalid URL string."))
}
-
+
decoded = (url as! T)
+ } else if T.self == Decimal.self {
+ guard let decimal = try self.unbox(value, as: Decimal.self) else { return nil }
+ decoded = decimal as! T
} else {
self.storage.push(container: value)
decoded = try T(from: self)
self.storage.popContainer()
}
-
+
return decoded
}
}
//===----------------------------------------------------------------------===//
-// Shared Super Key
+// Shared Key Types
//===----------------------------------------------------------------------===//
-fileprivate enum _JSONSuperKey : String, CodingKey {
- case `super`
+fileprivate struct _JSONKey : CodingKey {
+ public var stringValue: String
+ public var intValue: Int?
+
+ public init?(stringValue: String) {
+ self.stringValue = stringValue
+ self.intValue = nil
+ }
+
+ public init?(intValue: Int) {
+ self.stringValue = "\(intValue)"
+ self.intValue = intValue
+ }
+
+ fileprivate init(index: Int) {
+ self.stringValue = "Index \(index)"
+ self.intValue = index
+ }
+
+ fileprivate static let `super` = _JSONKey(stringValue: "super")!
}
//===----------------------------------------------------------------------===//
@@ -2018,7 +2151,7 @@
/// - parameter value: The value that was invalid to encode.
/// - parameter path: The path of `CodingKey`s taken to encode this value.
/// - returns: An `EncodingError` with the appropriate path and debug description.
- fileprivate static func _invalidFloatingPointValue<T : FloatingPoint>(_ value: T, at codingPath: [CodingKey?]) -> EncodingError {
+ fileprivate static func _invalidFloatingPointValue<T : FloatingPoint>(_ value: T, at codingPath: [CodingKey]) -> EncodingError {
let valueDescription: String
if value == T.infinity {
valueDescription = "\(T.self).infinity"
@@ -2027,7 +2160,7 @@
} else {
valueDescription = "\(T.self).nan"
}
-
+
let debugDescription = "Unable to encode \(valueDescription) directly in JSON. Use JSONEncoder.NonConformingFloatEncodingStrategy.convertToString to specify how the value should be encoded."
return .invalidValue(value, EncodingError.Context(codingPath: codingPath, debugDescription: debugDescription))
}
diff --git a/Foundation/Locale.swift b/Foundation/Locale.swift
index 7b81c63..1544767 100644
--- a/Foundation/Locale.swift
+++ b/Foundation/Locale.swift
@@ -466,3 +466,20 @@
return result!
}
}
+
+extension Locale : Codable {
+ private enum CodingKeys : Int, CodingKey {
+ case identifier
+ }
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ let identifier = try container.decode(String.self, forKey: .identifier)
+ self.init(identifier: identifier)
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(self.identifier, forKey: .identifier)
+ }
+}
diff --git a/Foundation/NSAffineTransform.swift b/Foundation/NSAffineTransform.swift
index 0b3fc61..cfd0504 100644
--- a/Foundation/NSAffineTransform.swift
+++ b/Foundation/NSAffineTransform.swift
@@ -455,3 +455,25 @@
return AffineTransform._unconditionallyBridgeFromObjectiveC(self)
}
}
+
+extension AffineTransform : Codable {
+ public init(from decoder: Decoder) throws {
+ var container = try decoder.unkeyedContainer()
+ m11 = try container.decode(CGFloat.self)
+ m12 = try container.decode(CGFloat.self)
+ m21 = try container.decode(CGFloat.self)
+ m22 = try container.decode(CGFloat.self)
+ tX = try container.decode(CGFloat.self)
+ tY = try container.decode(CGFloat.self)
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.unkeyedContainer()
+ try container.encode(self.m11)
+ try container.encode(self.m12)
+ try container.encode(self.m21)
+ try container.encode(self.m22)
+ try container.encode(self.tX)
+ try container.encode(self.tY)
+ }
+}
diff --git a/Foundation/NSCFBoolean.swift b/Foundation/NSCFBoolean.swift
index 90e223b..23577ff 100644
--- a/Foundation/NSCFBoolean.swift
+++ b/Foundation/NSCFBoolean.swift
@@ -78,7 +78,10 @@
override var objCType: UnsafePointer<Int8> {
// This must never be fixed to be "B", although that would
// cause correct old-style archiving when this is unarchived.
- return UnsafePointer<Int8>(bitPattern: UInt(_NSSimpleObjCType.Char.rawValue.value))!
+ func _objCType(_ staticString: StaticString) -> UnsafePointer<Int8> {
+ return UnsafeRawPointer(staticString.utf8Start).assumingMemoryBound(to: Int8.self)
+ }
+ return _objCType("c")
}
internal override func _cfNumberType() -> CFNumberType {
diff --git a/Foundation/NSCFString.swift b/Foundation/NSCFString.swift
index 6d0d8a1..1eb29b1 100644
--- a/Foundation/NSCFString.swift
+++ b/Foundation/NSCFString.swift
@@ -59,13 +59,21 @@
internal final class _NSCFConstantString : _NSCFString {
internal var _ptr : UnsafePointer<UInt8> {
- let offset = MemoryLayout<OpaquePointer>.size + MemoryLayout<Int32>.size + MemoryLayout<Int32>.size + MemoryLayout<_CFInfo>.size
+ // FIXME: Split expression as a work-around for slow type
+ // checking (tracked by SR-5322).
+ let offTemp1 = MemoryLayout<OpaquePointer>.size + MemoryLayout<Int32>.size
+ let offTemp2 = MemoryLayout<Int32>.size + MemoryLayout<_CFInfo>.size
+ let offset = offTemp1 + offTemp2
let ptr = Unmanaged.passUnretained(self).toOpaque()
return ptr.load(fromByteOffset: offset, as: UnsafePointer<UInt8>.self)
}
private var _lenOffset : Int {
- return MemoryLayout<OpaquePointer>.size + MemoryLayout<Int32>.size + MemoryLayout<Int32>.size + MemoryLayout<_CFInfo>.size + MemoryLayout<UnsafePointer<UInt8>>.size
+ // FIXME: Split expression as a work-around for slow type
+ // checking (tracked by SR-5322).
+ let offTemp1 = MemoryLayout<OpaquePointer>.size + MemoryLayout<Int32>.size
+ let offTemp2 = MemoryLayout<Int32>.size + MemoryLayout<_CFInfo>.size
+ return offTemp1 + offTemp2 + MemoryLayout<UnsafePointer<UInt8>>.size
}
private var _lenPtr : UnsafeMutableRawPointer {
diff --git a/Foundation/NSCharacterSet.swift b/Foundation/NSCharacterSet.swift
index c2c72b7..ce490af 100644
--- a/Foundation/NSCharacterSet.swift
+++ b/Foundation/NSCharacterSet.swift
@@ -50,13 +50,18 @@
}
open override func isEqual(_ value: Any?) -> Bool {
+ guard let runtimeClass = _CFRuntimeGetClassWithTypeID(CFCharacterSetGetTypeID()) else {
+ fatalError("Could not obtain CFRuntimeClass of CFCharacterSet")
+ }
+
+ guard let equalFunction = runtimeClass.pointee.equal else {
+ fatalError("Could not obtain equal function from CFRuntimeClass of CFCharacterSet")
+ }
+
switch value {
- case let other as CharacterSet:
- return CFEqual(_cfObject, other._cfObject)
- case let other as NSCharacterSet:
- return CFEqual(_cfObject, other._cfObject)
- default:
- return false
+ case let other as CharacterSet: return equalFunction(self._cfObject, other._cfObject) == true
+ case let other as NSCharacterSet: return equalFunction(self._cfObject, other._cfObject) == true
+ default: return false
}
}
diff --git a/Foundation/NSDecimal.swift b/Foundation/NSDecimal.swift
index e22cfcd..0eac26a 100644
--- a/Foundation/NSDecimal.swift
+++ b/Foundation/NSDecimal.swift
@@ -24,7 +24,7 @@
return Int32(__exponent)
}
set {
- __exponent = Int8(extendingOrTruncating: newValue)
+ __exponent = Int8(truncatingIfNeeded: newValue)
}
}
// length == 0 && isNegative -> NaN
@@ -90,7 +90,7 @@
public init(_exponent: Int32, _length: UInt32, _isNegative: UInt32, _isCompact: UInt32, _reserved: UInt32, _mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)){
self._mantissa = _mantissa
- self.__exponent = Int8(extendingOrTruncating: _exponent)
+ self.__exponent = Int8(truncatingIfNeeded: _exponent)
self.__lengthAndFlags = UInt8(_length & 0b1111)
self.__reserved = 0
self._isNegative = _isNegative
@@ -412,21 +412,21 @@
while mantissa != 0 && i < NSDecimalMaxSize {
switch i {
case 0:
- _mantissa.0 = UInt16(extendingOrTruncating:mantissa)
+ _mantissa.0 = UInt16(truncatingIfNeeded:mantissa)
case 1:
- _mantissa.1 = UInt16(extendingOrTruncating:mantissa)
+ _mantissa.1 = UInt16(truncatingIfNeeded:mantissa)
case 2:
- _mantissa.2 = UInt16(extendingOrTruncating:mantissa)
+ _mantissa.2 = UInt16(truncatingIfNeeded:mantissa)
case 3:
- _mantissa.3 = UInt16(extendingOrTruncating:mantissa)
+ _mantissa.3 = UInt16(truncatingIfNeeded:mantissa)
case 4:
- _mantissa.4 = UInt16(extendingOrTruncating:mantissa)
+ _mantissa.4 = UInt16(truncatingIfNeeded:mantissa)
case 5:
- _mantissa.5 = UInt16(extendingOrTruncating:mantissa)
+ _mantissa.5 = UInt16(truncatingIfNeeded:mantissa)
case 6:
- _mantissa.6 = UInt16(extendingOrTruncating:mantissa)
+ _mantissa.6 = UInt16(truncatingIfNeeded:mantissa)
case 7:
- _mantissa.7 = UInt16(extendingOrTruncating:mantissa)
+ _mantissa.7 = UInt16(truncatingIfNeeded:mantissa)
default:
fatalError("initialization overflow")
}
@@ -592,13 +592,13 @@
for i in 0..<d._length {
let accumulator: UInt32 = UInt32(d[i]) * UInt32(mul) + carry
carry = accumulator >> 16
- d[i] = UInt16(extendingOrTruncating: accumulator)
+ d[i] = UInt16(truncatingIfNeeded: accumulator)
}
if carry != 0 {
if d._length >= Decimal.maxSize {
return .overflow
}
- d[d._length] = UInt16(extendingOrTruncating: carry)
+ d[d._length] = UInt16(truncatingIfNeeded: carry)
d._length += 1
}
return .noError
@@ -609,13 +609,13 @@
for i in 0..<d._length {
let accumulator: UInt32 = UInt32(d[i]) + carry
carry = accumulator >> 16
- d[i] = UInt16(extendingOrTruncating: accumulator)
+ d[i] = UInt16(truncatingIfNeeded: accumulator)
}
if carry != 0 {
if d._length >= Decimal.maxSize {
return .overflow
}
- d[d._length] = UInt16(extendingOrTruncating: carry)
+ d[d._length] = UInt16(truncatingIfNeeded: carry)
d._length += 1
}
return .noError
@@ -763,8 +763,8 @@
if i + j < big._length {
let bigij = UInt32(big[i+j])
accumulator = UInt32(carry) + bigij + UInt32(right[j]) * UInt32(left[i])
- carry = UInt16(extendingOrTruncating:accumulator >> 16)
- big[i+j] = UInt16(extendingOrTruncating:accumulator)
+ carry = UInt16(truncatingIfNeeded:accumulator >> 16)
+ big[i+j] = UInt16(truncatingIfNeeded:accumulator)
} else if carry != 0 || (right[j] == 0 && left[j] == 0) {
return .overflow
}
@@ -904,7 +904,7 @@
acc = acc & 0xffff;
acc = 0xffff + UInt32(u[ul - vl + i - UInt32(j) - UInt32(1)]) - acc + sk; // subtract
sk = acc >> 16;
- u[ul - vl + i - UInt32(j) - UInt32(1)] = UInt16(extendingOrTruncating:acc)
+ u[ul - vl + i - UInt32(j) - UInt32(1)] = UInt16(truncatingIfNeeded:acc)
}
// D5: test remainder
@@ -920,7 +920,7 @@
let vl = v._length
acc = UInt32(v[i]) + UInt32(u[UInt32(ul) - UInt32(vl) + UInt32(i) - UInt32(j) - UInt32(1)]) + k
k = acc >> 16;
- u[UInt32(ul) - UInt32(vl) + UInt32(i) - UInt32(j) - UInt32(1)] = UInt16(extendingOrTruncating:acc)
+ u[UInt32(ul) - UInt32(vl) + UInt32(i) - UInt32(j) - UInt32(1)] = UInt16(truncatingIfNeeded:acc)
}
// k must be == 1 here
}
@@ -1173,8 +1173,8 @@
let li = UInt32(left[i])
let ri = UInt32(right[i])
accumulator = li + ri + UInt32(carry)
- carry = UInt16(extendingOrTruncating:accumulator >> 16)
- result[i] = UInt16(extendingOrTruncating:accumulator)
+ carry = UInt16(truncatingIfNeeded:accumulator >> 16)
+ result[i] = UInt16(truncatingIfNeeded:accumulator)
i += 1
}
@@ -1182,8 +1182,8 @@
if carry != 0 {
let li = UInt32(left[i])
accumulator = li + UInt32(carry)
- carry = UInt16(extendingOrTruncating:accumulator >> 16)
- result[i] = UInt16(extendingOrTruncating:accumulator)
+ carry = UInt16(truncatingIfNeeded:accumulator >> 16)
+ result[i] = UInt16(truncatingIfNeeded:accumulator)
i += 1
} else {
while i < left._length {
@@ -1197,8 +1197,8 @@
if carry != 0 {
let ri = UInt32(right[i])
accumulator = ri + UInt32(carry)
- carry = UInt16(extendingOrTruncating:accumulator >> 16)
- result[i] = UInt16(extendingOrTruncating:accumulator)
+ carry = UInt16(truncatingIfNeeded:accumulator >> 16)
+ result[i] = UInt16(truncatingIfNeeded:accumulator)
i += 1
} else {
while i < right._length {
@@ -1240,8 +1240,8 @@
let li = UInt32(left[i])
let ri = UInt32(right[i])
accumulator = 0xffff + li - ri + UInt32(carry)
- carry = UInt16(extendingOrTruncating:accumulator >> 16)
- result[i] = UInt16(extendingOrTruncating:accumulator)
+ carry = UInt16(truncatingIfNeeded:accumulator >> 16)
+ result[i] = UInt16(truncatingIfNeeded:accumulator)
i += 1
}
@@ -1249,8 +1249,8 @@
if carry != 0 {
let li = UInt32(left[i])
accumulator = 0xffff + li // + no carry
- carry = UInt16(extendingOrTruncating:accumulator >> 16)
- result[i] = UInt16(extendingOrTruncating:accumulator)
+ carry = UInt16(truncatingIfNeeded:accumulator >> 16)
+ result[i] = UInt16(truncatingIfNeeded:accumulator)
i += 1
} else {
while i < left._length {
@@ -1263,8 +1263,8 @@
while i < right._length {
let ri = UInt32(right[i])
accumulator = 0xffff - ri + UInt32(carry)
- carry = UInt16(extendingOrTruncating:accumulator >> 16)
- result[i] = UInt16(extendingOrTruncating:accumulator)
+ carry = UInt16(truncatingIfNeeded:accumulator >> 16)
+ result[i] = UInt16(truncatingIfNeeded:accumulator)
i += 1
}
@@ -2087,3 +2087,58 @@
return result
}
}
+
+extension Decimal : Codable {
+ private enum CodingKeys : Int, CodingKey {
+ case exponent
+ case length
+ case isNegative
+ case isCompact
+ case mantissa
+ }
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ let exponent = try container.decode(CInt.self, forKey: .exponent)
+ let length = try container.decode(CUnsignedInt.self, forKey: .length)
+ let isNegative = try container.decode(Bool.self, forKey: .isNegative)
+ let isCompact = try container.decode(Bool.self, forKey: .isCompact)
+
+ var mantissaContainer = try container.nestedUnkeyedContainer(forKey: .mantissa)
+ var mantissa: (CUnsignedShort, CUnsignedShort, CUnsignedShort, CUnsignedShort,
+ CUnsignedShort, CUnsignedShort, CUnsignedShort, CUnsignedShort) = (0,0,0,0,0,0,0,0)
+ mantissa.0 = try mantissaContainer.decode(CUnsignedShort.self)
+ mantissa.1 = try mantissaContainer.decode(CUnsignedShort.self)
+ mantissa.2 = try mantissaContainer.decode(CUnsignedShort.self)
+ mantissa.3 = try mantissaContainer.decode(CUnsignedShort.self)
+ mantissa.4 = try mantissaContainer.decode(CUnsignedShort.self)
+ mantissa.5 = try mantissaContainer.decode(CUnsignedShort.self)
+ mantissa.6 = try mantissaContainer.decode(CUnsignedShort.self)
+ mantissa.7 = try mantissaContainer.decode(CUnsignedShort.self)
+
+ self.init(_exponent: exponent,
+ _length: length,
+ _isNegative: CUnsignedInt(isNegative ? 1 : 0),
+ _isCompact: CUnsignedInt(isCompact ? 1 : 0),
+ _reserved: 0,
+ _mantissa: mantissa)
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(_exponent, forKey: .exponent)
+ try container.encode(_length, forKey: .length)
+ try container.encode(_isNegative == 0 ? false : true, forKey: .isNegative)
+ try container.encode(_isCompact == 0 ? false : true, forKey: .isCompact)
+
+ var mantissaContainer = container.nestedUnkeyedContainer(forKey: .mantissa)
+ try mantissaContainer.encode(_mantissa.0)
+ try mantissaContainer.encode(_mantissa.1)
+ try mantissaContainer.encode(_mantissa.2)
+ try mantissaContainer.encode(_mantissa.3)
+ try mantissaContainer.encode(_mantissa.4)
+ try mantissaContainer.encode(_mantissa.5)
+ try mantissaContainer.encode(_mantissa.6)
+ try mantissaContainer.encode(_mantissa.7)
+ }
+}
diff --git a/Foundation/NSDecimalNumber.swift b/Foundation/NSDecimalNumber.swift
index 1e049d6..06d4aa4 100644
--- a/Foundation/NSDecimalNumber.swift
+++ b/Foundation/NSDecimalNumber.swift
@@ -408,7 +408,7 @@
}
}
- open class func `default`() -> NSDecimalNumberHandler {
+ open class var `default`: NSDecimalNumberHandler {
return defaultBehavior
}
// rounding mode: NSRoundPlain
diff --git a/Foundation/NSError.swift b/Foundation/NSError.swift
index 14d05cc..abec7fa 100644
--- a/Foundation/NSError.swift
+++ b/Foundation/NSError.swift
@@ -274,6 +274,37 @@
var errorUserInfo: [String : Any] { get }
}
+public extension CustomNSError {
+ /// Default domain of the error.
+ static var errorDomain: String {
+ return String(reflecting: self)
+ }
+
+ /// The error code within the given domain.
+ var errorCode: Int {
+ return _swift_getDefaultErrorCode(self)
+ }
+
+ /// The default user-info dictionary.
+ var errorUserInfo: [String : Any] {
+ return [:]
+ }
+}
+
+extension CustomNSError where Self: RawRepresentable, Self.RawValue: SignedInteger {
+ // The error code of Error with integral raw values is the raw value.
+ public var errorCode: Int {
+ return numericCast(self.rawValue)
+ }
+}
+
+extension CustomNSError where Self: RawRepresentable, Self.RawValue: UnsignedInteger {
+ // The error code of Error with integral raw values is the raw value.
+ public var errorCode: Int {
+ return numericCast(self.rawValue)
+ }
+}
+
public extension Error where Self : CustomNSError {
/// Default implementation for customized NSErrors.
var _domain: String { return Self.errorDomain }
@@ -282,6 +313,16 @@
var _code: Int { return self.errorCode }
}
+public extension Error where Self: CustomNSError, Self: RawRepresentable, Self.RawValue: SignedInteger {
+ /// Default implementation for customized NSErrors.
+ var _code: Int { return self.errorCode }
+}
+
+public extension Error where Self: CustomNSError, Self: RawRepresentable, Self.RawValue: UnsignedInteger {
+ /// Default implementation for customized NSErrors.
+ var _code: Int { return self.errorCode }
+}
+
public extension Error {
/// Retrieve the localized description for this error.
var localizedDescription: String {
diff --git a/Foundation/NSGeometry.swift b/Foundation/NSGeometry.swift
index 454014a..80c8d25 100644
--- a/Foundation/NSGeometry.swift
+++ b/Foundation/NSGeometry.swift
@@ -25,6 +25,20 @@
}
}
+extension CGPoint {
+ public static var zero: CGPoint {
+ return CGPoint(x: CGFloat(0), y: CGFloat(0))
+ }
+
+ public init(x: Int, y: Int) {
+ self.init(x: CGFloat(x), y: CGFloat(y))
+ }
+
+ public init(x: Double, y: Double) {
+ self.init(x: CGFloat(x), y: CGFloat(y))
+ }
+}
+
extension CGPoint: Equatable {
public static func ==(lhs: CGPoint, rhs: CGPoint) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
@@ -76,6 +90,21 @@
}
}
+extension CGPoint : Codable {
+ public init(from decoder: Decoder) throws {
+ var container = try decoder.unkeyedContainer()
+ let x = try container.decode(CGFloat.self)
+ let y = try container.decode(CGFloat.self)
+ self.init(x: x, y: y)
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.unkeyedContainer()
+ try container.encode(x)
+ try container.encode(y)
+ }
+}
+
public struct CGSize {
public var width: CGFloat
public var height: CGFloat
@@ -88,6 +117,20 @@
}
}
+extension CGSize {
+ public static var zero: CGSize {
+ return CGSize(width: CGFloat(0), height: CGFloat(0))
+ }
+
+ public init(width: Int, height: Int) {
+ self.init(width: CGFloat(width), height: CGFloat(height))
+ }
+
+ public init(width: Double, height: Double) {
+ self.init(width: CGFloat(width), height: CGFloat(height))
+ }
+}
+
extension CGSize: Equatable {
public static func ==(lhs: CGSize, rhs: CGSize) -> Bool {
return lhs.width == rhs.width && lhs.height == rhs.height
@@ -139,6 +182,21 @@
}
}
+extension CGSize : Codable {
+ public init(from decoder: Decoder) throws {
+ var container = try decoder.unkeyedContainer()
+ let width = try container.decode(CGFloat.self)
+ let height = try container.decode(CGFloat.self)
+ self.init(width: width, height: height)
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.unkeyedContainer()
+ try container.encode(width)
+ try container.encode(height)
+ }
+}
+
public struct CGRect {
public var origin: CGPoint
public var size: CGSize
@@ -151,12 +209,57 @@
}
}
+extension CGRect {
+ public static var zero: CGRect {
+ return CGRect(origin: CGPoint(), size: CGSize())
+ }
+
+ public init(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) {
+ self.init(origin: CGPoint(x: x, y: y), size: CGSize(width: width, height: height))
+ }
+
+ public init(x: Double, y: Double, width: Double, height: Double) {
+ self.init(origin: CGPoint(x: x, y: y), size: CGSize(width: width, height: height))
+ }
+
+ public init(x: Int, y: Int, width: Int, height: Int) {
+ self.init(origin: CGPoint(x: x, y: y), size: CGSize(width: width, height: height))
+ }
+}
+
+extension CGRect {
+ public static let null = CGRect(x: CGFloat.infinity,
+ y: CGFloat.infinity,
+ width: CGFloat(0),
+ height: CGFloat(0))
+
+ public static let infinite = CGRect(x: -CGFloat.greatestFiniteMagnitude / 2,
+ y: -CGFloat.greatestFiniteMagnitude / 2,
+ width: CGFloat.greatestFiniteMagnitude,
+ height: CGFloat.greatestFiniteMagnitude)
+}
+
extension CGRect: Equatable {
public static func ==(lhs: CGRect, rhs: CGRect) -> Bool {
return lhs.origin == rhs.origin && lhs.size == rhs.size
}
}
+extension CGRect : Codable {
+ public init(from decoder: Decoder) throws {
+ var container = try decoder.unkeyedContainer()
+ let origin = try container.decode(CGPoint.self)
+ let size = try container.decode(CGSize.self)
+ self.init(origin: origin, size: size)
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.unkeyedContainer()
+ try container.encode(origin)
+ try container.encode(size)
+ }
+}
+
public typealias NSPoint = CGPoint
public typealias NSPointPointer = UnsafeMutablePointer<NSPoint>
diff --git a/Foundation/NSJSONSerialization.swift b/Foundation/NSJSONSerialization.swift
index ebc7bea..13e639e 100644
--- a/Foundation/NSJSONSerialization.swift
+++ b/Foundation/NSJSONSerialization.swift
@@ -31,6 +31,7 @@
public init(rawValue: UInt) { self.rawValue = rawValue }
public static let prettyPrinted = WritingOptions(rawValue: 1 << 0)
+ public static let sortedKeys = WritingOptions(rawValue: 1 << 1)
}
}
@@ -116,6 +117,7 @@
var writer = JSONWriter(
pretty: opt.contains(.prettyPrinted),
+ sortedKeys: opt.contains(.sortedKeys),
writer: { (str: String?) in
if let str = str {
jsonStr.append(str)
@@ -289,6 +291,7 @@
private let maxIntLength = String(describing: Int.max).characters.count
var indent = 0
let pretty: Bool
+ let sortedKeys: Bool
let writer: (String?) -> Void
private lazy var _numberformatter: CFNumberFormatter = {
@@ -299,8 +302,9 @@
return formatter
}()
- init(pretty: Bool = false, writer: @escaping (String?) -> Void) {
+ init(pretty: Bool = false, sortedKeys: Bool = false, writer: @escaping (String?) -> Void) {
self.pretty = pretty
+ self.sortedKeys = sortedKeys
self.writer = writer
}
@@ -459,8 +463,8 @@
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.propertyListReadCorrupt.rawValue, userInfo: ["NSDebugDescription" : "Number cannot be infinity or NaN"])
}
- switch num._objCType {
- case .Bool:
+ switch num._cfTypeID {
+ case CFBooleanGetTypeID():
serializeBool(num.boolValue)
default:
writer(_serializationString(for: num))
@@ -499,10 +503,10 @@
writer("\n")
incAndWriteIndent()
}
-
+
var first = true
-
- for (key, value) in dict {
+
+ func serializeDictionaryElement(key: AnyHashable, value: Any) throws {
if first {
first = false
} else if pretty {
@@ -511,15 +515,37 @@
} else {
writer(",")
}
-
- if key is String {
- try serializeString(key as! String)
+
+ if let key = key as? String {
+ try serializeString(key)
} else {
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.propertyListReadCorrupt.rawValue, userInfo: ["NSDebugDescription" : "NSDictionary key must be NSString"])
}
pretty ? writer(": ") : writer(":")
try serializeJSON(value)
}
+
+ if sortedKeys {
+ let elems = try dict.sorted(by: { a, b in
+ guard let a = a.key as? String,
+ let b = b.key as? String else {
+ throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.propertyListReadCorrupt.rawValue, userInfo: ["NSDebugDescription" : "NSDictionary key must be NSString"])
+ }
+ let options: NSString.CompareOptions = [.numeric, .caseInsensitive, .forcedOrdering]
+ let range: Range<String.Index> = a.startIndex..<a.endIndex
+ let locale = NSLocale.system
+
+ return a.compare(b, options: options, range: range, locale: locale) == .orderedAscending
+ })
+ for elem in elems {
+ try serializeDictionaryElement(key: elem.key, value: elem.value)
+ }
+ } else {
+ for (key, value) in dict {
+ try serializeDictionaryElement(key: key, value: value)
+ }
+ }
+
if pretty {
writer("\n")
decAndWriteIndent()
diff --git a/Foundation/NSKeyedUnarchiver.swift b/Foundation/NSKeyedUnarchiver.swift
index 47fc2f8..b977cc8 100644
--- a/Foundation/NSKeyedUnarchiver.swift
+++ b/Foundation/NSKeyedUnarchiver.swift
@@ -683,10 +683,10 @@
}
open override func decodeBool(forKey key: String) -> Bool {
- guard let result : NSNumber = _decodeValue(forKey: key) else {
+ guard let result : Bool = _decodeValue(forKey: key) else {
return false
}
- return result.boolValue
+ return result
}
open override func decodeInt32(forKey key: String) -> Int32 {
diff --git a/Foundation/NSLocale.swift b/Foundation/NSLocale.swift
index 266f377..4b1b44f 100644
--- a/Foundation/NSLocale.swift
+++ b/Foundation/NSLocale.swift
@@ -84,7 +84,7 @@
return CFLocaleCopyCurrent()._swiftObject
}
- open class func systemLocale() -> Locale {
+ open class var system: Locale {
return CFLocaleGetSystem()._swiftObject
}
}
diff --git a/Foundation/NSNotification.swift b/Foundation/NSNotification.swift
index 223512b..688b86a 100644
--- a/Foundation/NSNotification.swift
+++ b/Foundation/NSNotification.swift
@@ -90,6 +90,7 @@
fileprivate var name: Notification.Name?
fileprivate var block: ((Notification) -> Void)?
fileprivate var sender: AnyObject?
+ fileprivate var queue: OperationQueue?
}
extension Sequence where Iterator.Element : NSNotificationReceiver {
@@ -159,7 +160,12 @@
continue
}
- block(notification)
+ if let queue = observer.queue, queue != OperationQueue.current {
+ queue.addOperation { block(notification) }
+ queue.waitUntilAllOperationsAreFinished()
+ } else {
+ block(notification)
+ }
}
}
@@ -183,10 +189,6 @@
}
open func addObserver(forName name: Notification.Name?, object obj: Any?, queue: OperationQueue?, usingBlock block: @escaping (Notification) -> Void) -> NSObjectProtocol {
- if queue != nil {
- NSUnimplemented()
- }
-
let object = NSObject()
let newObserver = NSNotificationReceiver()
@@ -194,6 +196,7 @@
newObserver.name = name
newObserver.block = block
newObserver.sender = _SwiftValue.store(obj)
+ newObserver.queue = queue
_observersLock.synchronized({
_observers.append(newObserver)
diff --git a/Foundation/NSNumber.swift b/Foundation/NSNumber.swift
index 5cc73a6..1e225ee 100644
--- a/Foundation/NSNumber.swift
+++ b/Foundation/NSNumber.swift
@@ -198,14 +198,17 @@
}
+private struct CFSInt128Struct {
+ var high: Int64
+ var low: UInt64
+}
+
open class NSNumber : NSValue {
typealias CFType = CFNumber
// This layout MUST be the same as CFNumber so that they are bridgeable
private var _base = _CFInfo(typeID: CFNumberGetTypeID())
private var _pad: UInt64 = 0
- internal let _objCType: _NSSimpleObjCType
-
internal var _cfObject: CFType {
return unsafeBitCast(self, to: CFType.self)
}
@@ -230,93 +233,123 @@
}
open override var objCType: UnsafePointer<Int8> {
- return UnsafePointer<Int8>(bitPattern: UInt(_objCType.rawValue.value))!
+ func _objCType(_ staticString: StaticString) -> UnsafePointer<Int8> {
+ return UnsafeRawPointer(staticString.utf8Start).assumingMemoryBound(to: Int8.self)
+ }
+ let numberType = _CFNumberGetType2(_cfObject)
+ switch numberType {
+ case kCFNumberSInt8Type:
+ return _objCType("c")
+ case kCFNumberSInt16Type:
+ return _objCType("s")
+ case kCFNumberSInt32Type:
+ return _objCType("i")
+ case kCFNumberSInt64Type:
+ return _objCType("q")
+ case kCFNumberFloat32Type:
+ return _objCType("f")
+ case kCFNumberFloat64Type:
+ return _objCType("d")
+ case kCFNumberSInt128Type:
+ return _objCType("Q")
+ default:
+ fatalError("unsupported CFNumberType: '\(numberType)'")
+ }
}
deinit {
_CFDeinit(self)
}
- public init(value: Int8) {
- _objCType = .Char
- super.init()
- _CFNumberInitInt8(_cfObject, value)
+ private convenience init(bytes: UnsafeRawPointer, numberType: CFNumberType) {
+ let cfnumber = CFNumberCreate(nil, numberType, bytes)
+ self.init(factory: { unsafeBitCast(cfnumber, to: NSNumber.self) })
}
- public init(value: UInt8) {
- _objCType = .UChar
- super.init()
- _CFNumberInitUInt8(_cfObject, value)
+ public convenience init(value: Int8) {
+ var value = value
+ self.init(bytes: &value, numberType: kCFNumberSInt8Type)
}
- public init(value: Int16) {
- _objCType = .Short
- super.init()
- _CFNumberInitInt16(_cfObject, value)
+ public convenience init(value: UInt8) {
+ var value = Int16(value)
+ self.init(bytes: &value, numberType: kCFNumberSInt16Type)
}
- public init(value: UInt16) {
- _objCType = .UShort
- super.init()
- _CFNumberInitUInt16(_cfObject, value)
+ public convenience init(value: Int16) {
+ var value = value
+ self.init(bytes: &value, numberType: kCFNumberSInt16Type)
}
- public init(value: Int32) {
- _objCType = .Long
- super.init()
- _CFNumberInitInt32(_cfObject, value)
+ public convenience init(value: UInt16) {
+ var value = Int32(value)
+ self.init(bytes: &value, numberType: kCFNumberSInt32Type)
}
- public init(value: UInt32) {
- _objCType = .ULong
- super.init()
- _CFNumberInitUInt32(_cfObject, value)
+ public convenience init(value: Int32) {
+ var value = value
+ self.init(bytes: &value, numberType: kCFNumberSInt32Type)
}
- public init(value: Int) {
- _objCType = .Int
- super.init()
- _CFNumberInitInt(_cfObject, value)
+ public convenience init(value: UInt32) {
+ var value = Int64(value)
+ self.init(bytes: &value, numberType: kCFNumberSInt64Type)
}
- public init(value: UInt) {
- _objCType = .UInt
- super.init()
- _CFNumberInitUInt(_cfObject, value)
+ public convenience init(value: Int) {
+ var value = value
+ #if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le)
+ self.init(bytes: &value, numberType: kCFNumberSInt64Type)
+ #elseif arch(i386) || arch(arm)
+ self.init(bytes: &value, numberType: kCFNumberSInt32Type)
+ #endif
}
- public init(value: Int64) {
- _objCType = .LongLong
- super.init()
- _CFNumberInitInt64(_cfObject, value)
+ public convenience init(value: UInt) {
+ #if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le)
+ if value > UInt64(Int64.max) {
+ var value = CFSInt128Struct(high: 0, low: UInt64(value))
+ self.init(bytes: &value, numberType: kCFNumberSInt128Type)
+ } else {
+ var value = Int64(value)
+ self.init(bytes: &value, numberType: kCFNumberSInt64Type)
+ }
+ #elseif arch(i386) || arch(arm)
+ var value = Int64(value)
+ self.init(bytes: &value, numberType: kCFNumberSInt64Type)
+ #endif
}
- public init(value: UInt64) {
- _objCType = .ULongLong
- super.init()
- _CFNumberInitUInt64(_cfObject, value)
+ public convenience init(value: Int64) {
+ var value = value
+ self.init(bytes: &value, numberType: kCFNumberSInt64Type)
}
- public init(value: Float) {
- _objCType = .Float
- super.init()
- _CFNumberInitFloat(_cfObject, value)
+ public convenience init(value: UInt64) {
+ if value > UInt64(Int64.max) {
+ var value = CFSInt128Struct(high: 0, low: UInt64(value))
+ self.init(bytes: &value, numberType: kCFNumberSInt128Type)
+ } else {
+ var value = Int64(value)
+ self.init(bytes: &value, numberType: kCFNumberSInt64Type)
+ }
}
- public init(value: Double) {
- _objCType = .Double
- super.init()
- _CFNumberInitDouble(_cfObject, value)
+ public convenience init(value: Float) {
+ var value = value
+ self.init(bytes: &value, numberType: kCFNumberFloatType)
}
- public init(value: Bool) {
- _objCType = .Bool
- super.init()
- _CFNumberInitBool(_cfObject, value)
+ public convenience init(value: Double) {
+ var value = value
+ self.init(bytes: &value, numberType: kCFNumberDoubleType)
+ }
+
+ public convenience init(value: Bool) {
+ self.init(factory: value._bridgeToObjectiveC)
}
override internal init() {
- _objCType = .Undef
super.init()
}
@@ -381,83 +414,63 @@
}
open var int8Value: Int8 {
- var val: Int8 = 0
- withUnsafeMutablePointer(to: &val) { (value: UnsafeMutablePointer<Int8>) -> Void in
- CFNumberGetValue(_cfObject, kCFNumberCharType, value)
- }
- return val
+ var value: Int64 = 0
+ CFNumberGetValue(_cfObject, kCFNumberSInt64Type, &value)
+ return .init(truncatingIfNeeded: value)
}
open var uint8Value: UInt8 {
- var val: UInt8 = 0
- withUnsafeMutablePointer(to: &val) { (value: UnsafeMutablePointer<UInt8>) -> Void in
- CFNumberGetValue(_cfObject, kCFNumberCharType, value)
- }
- return val
+ var value: Int64 = 0
+ CFNumberGetValue(_cfObject, kCFNumberSInt64Type, &value)
+ return .init(truncatingIfNeeded: value)
}
open var int16Value: Int16 {
- var val: Int16 = 0
- withUnsafeMutablePointer(to: &val) { (value: UnsafeMutablePointer<Int16>) -> Void in
- CFNumberGetValue(_cfObject, kCFNumberShortType, value)
- }
- return val
+ var value: Int64 = 0
+ CFNumberGetValue(_cfObject, kCFNumberSInt64Type, &value)
+ return .init(truncatingIfNeeded: value)
}
open var uint16Value: UInt16 {
- var val: UInt16 = 0
- withUnsafeMutablePointer(to: &val) { (value: UnsafeMutablePointer<UInt16>) -> Void in
- CFNumberGetValue(_cfObject, kCFNumberShortType, value)
- }
- return val
+ var value: Int64 = 0
+ CFNumberGetValue(_cfObject, kCFNumberSInt64Type, &value)
+ return .init(truncatingIfNeeded: value)
}
open var int32Value: Int32 {
- var val: Int32 = 0
- withUnsafeMutablePointer(to: &val) { (value: UnsafeMutablePointer<Int32>) -> Void in
- CFNumberGetValue(_cfObject, kCFNumberIntType, value)
- }
- return val
+ var value: Int64 = 0
+ CFNumberGetValue(_cfObject, kCFNumberSInt64Type, &value)
+ return .init(truncatingIfNeeded: value)
}
open var uint32Value: UInt32 {
- var val: UInt32 = 0
- withUnsafeMutablePointer(to: &val) { (value: UnsafeMutablePointer<UInt32>) -> Void in
- CFNumberGetValue(_cfObject, kCFNumberIntType, value)
- }
- return val
+ var value: Int64 = 0
+ CFNumberGetValue(_cfObject, kCFNumberSInt64Type, &value)
+ return .init(truncatingIfNeeded: value)
}
open var int64Value: Int64 {
- var val: Int64 = 0
- withUnsafeMutablePointer(to: &val) { (value: UnsafeMutablePointer<Int64>) -> Void in
- CFNumberGetValue(_cfObject, kCFNumberLongLongType, value)
- }
- return val
+ var value: Int64 = 0
+ CFNumberGetValue(_cfObject, kCFNumberSInt64Type, &value)
+ return .init(truncatingIfNeeded: value)
}
open var uint64Value: UInt64 {
- var val: UInt64 = 0
- withUnsafeMutablePointer(to: &val) { (value: UnsafeMutablePointer<UInt64>) -> Void in
- CFNumberGetValue(_cfObject, kCFNumberLongLongType, value)
- }
- return val
+ var value = CFSInt128Struct(high: 0, low: 0)
+ CFNumberGetValue(_cfObject, kCFNumberSInt128Type, &value)
+ return .init(truncatingIfNeeded: value.low)
}
open var floatValue: Float {
- var val: Float = 0
- withUnsafeMutablePointer(to: &val) { (value: UnsafeMutablePointer<Float>) -> Void in
- CFNumberGetValue(_cfObject, kCFNumberFloatType, value)
- }
- return val
+ var value: Float = 0
+ CFNumberGetValue(_cfObject, kCFNumberFloatType, &value)
+ return value
}
open var doubleValue: Double {
- var val: Double = 0
- withUnsafeMutablePointer(to: &val) { (value: UnsafeMutablePointer<Double>) -> Void in
- CFNumberGetValue(_cfObject, kCFNumberDoubleType, value)
- }
- return val
+ var value: Double = 0
+ CFNumberGetValue(_cfObject, kCFNumberDoubleType, &value)
+ return value
}
open var boolValue: Bool {
@@ -503,7 +516,24 @@
}
open func compare(_ otherNumber: NSNumber) -> ComparisonResult {
- return ._fromCF(CFNumberCompare(_cfObject, otherNumber._cfObject, nil))
+ switch (objCType.pointee, otherNumber.objCType.pointee) {
+ case (0x66, _), (_, 0x66), (0x66, 0x66): fallthrough // 'f' float
+ case (0x64, _), (_, 0x64), (0x64, 0x64): // 'd' double
+ let (lhs, rhs) = (doubleValue, otherNumber.doubleValue)
+ if lhs < rhs { return .orderedAscending }
+ if lhs > rhs { return .orderedDescending }
+ return .orderedSame
+ case (0x51, _), (_, 0x51), (0x51, 0x51): // 'q' unsigned long long
+ let (lhs, rhs) = (uint64Value, otherNumber.uint64Value)
+ if lhs < rhs { return .orderedAscending }
+ if lhs > rhs { return .orderedDescending }
+ return .orderedSame
+ case (_, _):
+ let (lhs, rhs) = (int64Value, otherNumber.int64Value)
+ if lhs < rhs { return .orderedAscending }
+ if lhs > rhs { return .orderedDescending }
+ return .orderedSame
+ }
}
open func description(withLocale locale: Locale?) -> String {
@@ -612,7 +642,7 @@
}
}
}
-
+
open override var classForCoder: AnyClass { return NSNumber.self }
}
diff --git a/Foundation/NSPersonNameComponents.swift b/Foundation/NSPersonNameComponents.swift
index df2ca90..52e5a0d 100644
--- a/Foundation/NSPersonNameComponents.swift
+++ b/Foundation/NSPersonNameComponents.swift
@@ -64,6 +64,28 @@
}
return copy
}
+
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let object = object else { return false }
+
+ switch object {
+ case let other as NSPersonNameComponents: return self.isEqual(other)
+ case let other as PersonNameComponents: return self.isEqual(other._bridgeToObjectiveC())
+ default: return false
+ }
+ }
+
+ private func isEqual(_ other: NSPersonNameComponents) -> Bool {
+ if self === other { return true }
+
+ return (self.namePrefix == other.namePrefix
+ && self.givenName == other.givenName
+ && self.middleName == other.middleName
+ && self.familyName == other.familyName
+ && self.nameSuffix == other.nameSuffix
+ && self.nickname == other.nickname
+ && self.phoneticRepresentation == other.phoneticRepresentation)
+ }
/* The below examples all assume the full name Dr. Johnathan Maple Appleseed Esq., nickname "Johnny" */
diff --git a/Foundation/NSRange.swift b/Foundation/NSRange.swift
index 699ebb1..91a8aba 100644
--- a/Foundation/NSRange.swift
+++ b/Foundation/NSRange.swift
@@ -126,7 +126,7 @@
public var hashValue: Int {
#if arch(i386) || arch(arm)
return Int(bitPattern: (UInt(bitPattern: location) | (UInt(bitPattern: length) << 16)))
- #elseif arch(x86_64) || arch(arm64)
+ #elseif arch(x86_64) || arch(arm64) || arch(s390x)
return Int(bitPattern: (UInt(bitPattern: location) | (UInt(bitPattern: length) << 32)))
#endif
}
@@ -261,8 +261,8 @@
where R.Bound == S.Index, S.Index == String.Index {
let r = region.relative(to: target)
self = NSRange(
- location: r.lowerBound._utf16Index - target.startIndex._utf16Index,
- length: r.upperBound._utf16Index - r.lowerBound._utf16Index
+ location: r.lowerBound.encodedOffset - target.startIndex.encodedOffset,
+ length: r.upperBound.encodedOffset - r.lowerBound.encodedOffset
)
}
diff --git a/Foundation/NSSortDescriptor.swift b/Foundation/NSSortDescriptor.swift
index 7a09d86..9bd70d8 100644
--- a/Foundation/NSSortDescriptor.swift
+++ b/Foundation/NSSortDescriptor.swift
@@ -35,10 +35,13 @@
open var key: String? { NSUnimplemented() }
open var ascending: Bool { NSUnimplemented() }
+ public var keyPath: AnyKeyPath? { NSUnimplemented() }
open func allowEvaluation() { NSUnimplemented() } // Force a sort descriptor which was securely decoded to allow evaluation
public init(key: String?, ascending: Bool, comparator cmptr: Comparator) { NSUnimplemented() }
+ public convenience init<Root, Value>(keyPath: KeyPath<Root, Value>, ascending: Bool) { NSUnimplemented() }
+ public convenience init<Root, Value>(keyPath: KeyPath<Root, Value>, ascending: Bool, comparator cmptr: @escaping Comparator) { NSUnimplemented() }
open var comparator: Comparator { NSUnimplemented() }
diff --git a/Foundation/NSString.swift b/Foundation/NSString.swift
index 97212d5..5b43cf5 100644
--- a/Foundation/NSString.swift
+++ b/Foundation/NSString.swift
@@ -346,49 +346,7 @@
let start = _storage.utf16.startIndex
let min = start.advanced(by: range.location)
let max = start.advanced(by: range.location + range.length)
- if let substr = String(_storage.utf16[min..<max]) {
- return substr
- }
- //If we come here, then the range has created unpaired surrogates on either end.
- //An unpaired surrogate is replaced by OXFFFD - the Unicode Replacement Character.
- //The CRLF ("\r\n") sequence is also treated like a surrogate pair, but its constinuent
- //characters "\r" and "\n" can exist outside the pair!
-
- let replacementCharacter = String(describing: UnicodeScalar(0xFFFD)!)
- let CR: UInt16 = 13 //carriage return
- let LF: UInt16 = 10 //new line
-
- //make sure the range is of non-zero length
- guard range.length > 0 else { return "" }
-
- //if the range is pointing to a single unpaired surrogate
- if range.length == 1 {
- switch _storage.utf16[min] {
- case CR: return "\r"
- case LF: return "\n"
- default: return replacementCharacter
- }
- }
-
- //set the prefix and suffix characters
- let prefix = _storage.utf16[min] == LF ? "\n" : replacementCharacter
- let suffix = _storage.utf16[max.advanced(by: -1)] == CR ? "\r" : replacementCharacter
-
- //if the range breaks a surrogate pair at the beginning of the string
- if let substrSuffix = String(_storage.utf16[min.advanced(by: 1)..<max]) {
- return prefix + substrSuffix
- }
-
- //if the range breaks a surrogate pair at the end of the string
- if let substrPrefix = String(_storage.utf16[min..<max.advanced(by: -1)]) {
- return substrPrefix + suffix
- }
-
- //the range probably breaks surrogate pairs at both the ends
- guard min.advanced(by: 1) <= max.advanced(by: -1) else { return prefix + suffix }
-
- let substr = String(_storage.utf16[min.advanced(by: 1)..<max.advanced(by: -1)])!
- return prefix + substr + suffix
+ return String(decoding: _storage.utf16[min..<max], as: UTF16.self)
} else {
let buff = UnsafeMutablePointer<unichar>.allocate(capacity: range.length)
getCharacters(buff, range: range)
diff --git a/Foundation/NSStringAPI.swift b/Foundation/NSStringAPI.swift
index baa36f8..49d374a 100644
--- a/Foundation/NSStringAPI.swift
+++ b/Foundation/NSStringAPI.swift
@@ -30,8 +30,8 @@
func _toNSRange(_ r: Range<String.Index>) -> NSRange {
return NSRange(
- location: r.lowerBound._utf16Index,
- length: r.upperBound._utf16Index - r.lowerBound._utf16Index)
+ location: r.lowerBound.encodedOffset,
+ length: r.upperBound.encodedOffset - r.lowerBound.encodedOffset)
}
extension String {
@@ -47,10 +47,7 @@
/// Return an `Index` corresponding to the given offset in our UTF-16
/// representation.
func _index(_ utf16Index: Int) -> Index {
- return Index(
- _base: String.UnicodeScalarView.Index(_position: utf16Index),
- in: characters
- )
+ return Index(encodedOffset: utf16Index)
}
/// Return a `Range<Index>` corresponding to the given `NSRange` of
@@ -1076,7 +1073,7 @@
public
func rangeOfComposedCharacterSequence(at anIndex: Index) -> Range<Index> {
return _range(
- _ns.rangeOfComposedCharacterSequence(at: anIndex._utf16Index))
+ _ns.rangeOfComposedCharacterSequence(at: anIndex.encodedOffset))
}
// - (NSRange)rangeOfComposedCharacterSequencesForRange:(NSRange)range
@@ -1392,7 +1389,7 @@
/// Returns a new string containing the characters of the
/// `String` from the one at a given index to the end.
public func substring(from index: Index) -> String {
- return _ns.substring(from: index._utf16Index)
+ return _ns.substring(from: index.encodedOffset)
}
// - (NSString *)substringToIndex:(NSUInteger)anIndex
@@ -1400,7 +1397,7 @@
/// Returns a new string containing the characters of the
/// `String` up to, but not including, the one at a given index.
public func substring(to index: Index) -> String {
- return _ns.substring(to: index._utf16Index)
+ return _ns.substring(to: index.encodedOffset)
}
// - (NSString *)substringWithRange:(NSRange)aRange
diff --git a/Foundation/NSURLSession/NSURLSessionConfiguration.swift b/Foundation/NSURLSession/NSURLSessionConfiguration.swift
index 8ae00cd..70c3623 100644
--- a/Foundation/NSURLSession/NSURLSessionConfiguration.swift
+++ b/Foundation/NSURLSession/NSURLSessionConfiguration.swift
@@ -47,6 +47,7 @@
self.urlCredentialStorage = nil
self.urlCache = nil
self.shouldUseExtendedBackgroundIdleMode = false
+ self.protocolClasses = [_HTTPURLProtocol.self]
super.init()
}
diff --git a/Foundation/NSURLSession/NSURLSessionTask.swift b/Foundation/NSURLSession/NSURLSessionTask.swift
index bcf06da..f819ff0 100644
--- a/Foundation/NSURLSession/NSURLSessionTask.swift
+++ b/Foundation/NSURLSession/NSURLSessionTask.swift
@@ -547,6 +547,11 @@
guard let session = task.session as? URLSession else { fatalError() }
switch session.behaviour(for: task) {
case .taskDelegate(let delegate):
+ if let downloadDelegate = delegate as? URLSessionDownloadDelegate, let downloadTask = task as? URLSessionDownloadTask {
+ session.delegateQueue.addOperation {
+ downloadDelegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: `protocol`.properties[URLProtocol._PropertyKey.temporaryFileURL] as! URL)
+ }
+ }
session.delegateQueue.addOperation {
delegate.urlSession(session, task: task, didCompleteWithError: nil)
task.state = .completed
diff --git a/Foundation/NSURLSession/http/EasyHandle.swift b/Foundation/NSURLSession/http/EasyHandle.swift
index 2fe0ca1..4da41b5 100644
--- a/Foundation/NSURLSession/http/EasyHandle.swift
+++ b/Foundation/NSURLSession/http/EasyHandle.swift
@@ -52,11 +52,14 @@
/// `Dispatch` only -- it is intentionally **not** thread safe.
internal final class _EasyHandle {
let rawHandle = CFURLSessionEasyHandleInit()
- unowned let delegate: _EasyHandleDelegate
+ weak var delegate: _EasyHandleDelegate?
fileprivate var headerList: _CurlStringList?
fileprivate var pauseState: _PauseState = []
internal var fileLength: Int64 = 0
internal var timeoutTimer: _TimeoutSource!
+ #if os(Android)
+ static fileprivate var _CAInfoFile: UnsafeMutablePointer<Int8>?
+ #endif
init(delegate: _EasyHandleDelegate) {
self.delegate = delegate
@@ -88,7 +91,7 @@
internal extension _EasyHandle {
func completedTransfer(withErrorCode errorCode: Int?) {
- delegate.transferCompleted(withErrorCode: errorCode)
+ delegate?.transferCompleted(withErrorCode: errorCode)
}
}
internal protocol _EasyHandleDelegate: class {
@@ -168,6 +171,20 @@
let protocols = (CFURLSessionProtocolHTTP | CFURLSessionProtocolHTTPS)
try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionPROTOCOLS, protocols).asError()
try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionREDIR_PROTOCOLS, protocols).asError()
+ #if os(Android)
+ // See https://curl.haxx.se/docs/sslcerts.html
+ // For SSL to work you need "cacert.pem" to be accessable
+ // at the path pointed to by the URLSessionCAInfo env var.
+ // Downloadable here: https://curl.haxx.se/ca/cacert.pem
+ if let caInfo = _EasyHandle._CAInfoFile {
+ if String(cString: caInfo) == "UNSAFE_SSL_NOVERIFY" {
+ try! CFURLSession_easy_setopt_int(rawHandle, CFURLSessionOptionSSL_VERIFYPEER, 0).asError()
+ }
+ else {
+ try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionCAINFO, caInfo).asError()
+ }
+ }
+ #endif
//TODO: Added in libcurl 7.45.0
//TODO: Set default protocol for schemeless URLs
//CURLOPT_DEFAULT_PROTOCOL available only in libcurl 7.45.0
@@ -268,7 +285,7 @@
fileprivate func printLibcurlDebug(type: CFURLSessionInfo, data: String, task: URLSessionTask) {
// libcurl sends is data with trailing CRLF which inserts lots of newlines into our output.
- print("[\(task.taskIdentifier)] \(type.debugHeader) \(data.mapControlToPictures)")
+ NSLog("[\(task.taskIdentifier)] \(type.debugHeader) \(data.mapControlToPictures)")
}
fileprivate extension String {
@@ -479,12 +496,15 @@
func didReceive(data: UnsafeMutablePointer<Int8>, size: Int, nmemb: Int) -> Int {
let d: Int = {
let buffer = Data(bytes: data, count: size*nmemb)
- switch delegate.didReceive(data: buffer) {
- case .proceed: return size * nmemb
- case .abort: return 0
- case .pause:
+ switch delegate?.didReceive(data: buffer) {
+ case .some(.proceed): return size * nmemb
+ case .some(.abort): return 0
+ case .some(.pause):
pauseState.insert(.receivePaused)
return Int(CFURLSessionWriteFuncPause)
+ case .none:
+ /* the delegate disappeared */
+ return 0
}
}()
return d
@@ -497,12 +517,15 @@
self.fileLength = Int64(fileLength)
let d: Int = {
let buffer = Data(bytes: data, count: size*nmemb)
- switch delegate.didReceive(headerData: buffer) {
- case .proceed: return size * nmemb
- case .abort: return 0
- case .pause:
+ switch delegate?.didReceive(headerData: buffer) {
+ case .some(.proceed): return size * nmemb
+ case .some(.abort): return 0
+ case .some(.pause):
pauseState.insert(.receivePaused)
return Int(CFURLSessionWriteFuncPause)
+ case .none:
+ /* the delegate disappeared */
+ return 0
}
}()
return d
@@ -514,14 +537,17 @@
func fill(writeBuffer data: UnsafeMutablePointer<Int8>, size: Int, nmemb: Int) -> Int {
let d: Int = {
let buffer = UnsafeMutableBufferPointer(start: data, count: size * nmemb)
- switch delegate.fill(writeBuffer: buffer) {
- case .pause:
+ switch delegate?.fill(writeBuffer: buffer) {
+ case .some(.pause):
pauseState.insert(.sendPaused)
return Int(CFURLSessionReadFuncPause)
- case .abort:
+ case .some(.abort):
return Int(CFURLSessionReadFuncAbort)
- case .bytes(let length):
+ case .some(.bytes(let length)):
return length
+ case .none:
+ /* the delegate disappeared */
+ return Int(CFURLSessionReadFuncAbort)
}
}()
return d
@@ -541,7 +567,7 @@
// <https://en.wikipedia.org/wiki/Quality_of_service>
}
func updateProgressMeter(with propgress: _Progress) {
- delegate.updateProgressMeter(with: propgress)
+ delegate?.updateProgressMeter(with: propgress)
}
func seekInputStream(offset: Int64, origin: CInt) -> CInt {
@@ -549,8 +575,12 @@
/// libcurl should only use SEEK_SET
guard origin == SEEK_SET else { fatalError("Unexpected 'origin' in seek.") }
do {
- try delegate.seekInputStream(to: UInt64(offset))
- return CFURLSessionSeekOk
+ if let delegate = delegate {
+ try delegate.seekInputStream(to: UInt64(offset))
+ return CFURLSessionSeekOk
+ } else {
+ return CFURLSessionSeekCantSeek
+ }
} catch {
return CFURLSessionSeekCantSeek
}
@@ -602,6 +632,19 @@
}
}
+#if os(Android)
+extension URLSession {
+
+ public static func setCAInfoFile(_ _CAInfoFile: String) {
+ free(_EasyHandle._CAInfoFile)
+ _CAInfoFile.withCString {
+ _EasyHandle._CAInfoFile = strdup($0)
+ }
+ }
+
+}
+#endif
+
extension CFURLSessionEasyCode : Equatable {
public static func ==(lhs: CFURLSessionEasyCode, rhs: CFURLSessionEasyCode) -> Bool {
return lhs.value == rhs.value
diff --git a/Foundation/NSURLSession/http/HTTPMessage.swift b/Foundation/NSURLSession/http/HTTPMessage.swift
index 8b82fb3..f8bff6f 100644
--- a/Foundation/NSURLSession/http/HTTPMessage.swift
+++ b/Foundation/NSURLSession/http/HTTPMessage.swift
@@ -222,7 +222,7 @@
/// Split a request line into its 3 parts: *Method*, *Request-URI*, and *HTTP-Version*.
/// - SeeAlso: https://tools.ietf.org/html/rfc2616#section-5.1
func splitRequestLine() -> (String, String, String)? {
- let scalars = self.unicodeScalars
+ let scalars = self.unicodeScalars[...]
guard let firstSpace = scalars.rangeOfSpace else { return nil }
let remainingRange = firstSpace.upperBound..<scalars.endIndex
let remainder = scalars[remainingRange]
@@ -285,7 +285,7 @@
// recipient MAY replace any linear white space with a single SP before
// interpreting the field value or forwarding the message downstream.
guard let (head, tail) = lines.decompose else { return nil }
- let headView = head.unicodeScalars
+ let headView = head.unicodeScalars[...]
guard let nameRange = headView.rangeOfTokenPrefix else { return nil }
guard headView.index(after: nameRange.upperBound) <= headView.endIndex && headView[nameRange.upperBound] == _HTTPCharacters.Colon else { return nil }
let name = String(headView[nameRange])
@@ -302,10 +302,10 @@
}
do {
var t = tail
- while t.first?.unicodeScalars.hasSPHTPrefix ?? false {
+ while t.first?.unicodeScalars[...].hasSPHTPrefix ?? false {
guard let (h2, t2) = t.decompose else { return nil }
t = t2
- guard let v = h2.unicodeScalars.trimSPHTPrefix else { return nil }
+ guard let v = h2.unicodeScalars[...].trimSPHTPrefix else { return nil }
let valuePart = String(v)
value = value.map { $0 + " " + valuePart } ?? valuePart
}
@@ -321,7 +321,7 @@
return (head, tail)
}
}
-private extension String.UnicodeScalarView {
+private extension String.UnicodeScalarView.SubSequence {
/// The range of *Token* characters as specified by RFC 2616.
var rangeOfTokenPrefix: Range<Index>? {
var end = startIndex
@@ -344,7 +344,7 @@
}
/// Unicode scalars after removing the leading spaces (SP) and horizontal tabs (HT).
/// Returns `nil` if the unicode scalars do not start with a SP or HT.
- var trimSPHTPrefix: String.UnicodeScalarView? {
+ var trimSPHTPrefix: SubSequence? {
guard !isEmpty else { return nil }
var idx = startIndex
while idx < endIndex {
diff --git a/Foundation/NSURLSession/http/HTTPURLProtocol.swift b/Foundation/NSURLSession/http/HTTPURLProtocol.swift
index efb647b..b105b15 100644
--- a/Foundation/NSURLSession/http/HTTPURLProtocol.swift
+++ b/Foundation/NSURLSession/http/HTTPURLProtocol.swift
@@ -14,6 +14,8 @@
fileprivate var easyHandle: _EasyHandle!
fileprivate var totalDownloaded = 0
+ fileprivate var totalUploaded: Int64 = 0
+ fileprivate var requestBodyLength: Int64 = 0
fileprivate var tempFileURL: URL
public required init(task: URLSessionTask, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) {
@@ -127,6 +129,7 @@
set(requestBodyLength: .noBody)
case (_, .some(let length)):
set(requestBodyLength: .length(length))
+ requestBodyLength = Int64(length)
case (_, .none):
set(requestBodyLength: .unknown)
}
@@ -439,8 +442,14 @@
extension _HTTPURLProtocol: _EasyHandleDelegate {
func didReceive(data: Data) -> _EasyHandle._Action {
- guard case .transferInProgress(let ts) = internalState else { fatalError("Received body data, but no transfer in progress.") }
- guard ts.isHeaderComplete else { fatalError("Received body data, but the header is not complete, yet.") }
+ guard case .transferInProgress(var ts) = internalState else { fatalError("Received body data, but no transfer in progress.") }
+ if !ts.isHeaderComplete {
+ ts.response = HTTPURLResponse(url: ts.url, statusCode: 200, httpVersion: "HTTP/0.9", headerFields: [:])
+ /* we received body data before CURL tells us that the headers are complete, that happens for HTTP/0.9 simple responses, see
+ - https://www.w3.org/Protocols/HTTP/1.0/spec.html#Message-Types
+ - https://github.com/curl/curl/issues/467
+ */
+ }
notifyDelegate(aboutReceivedData: data)
internalState = .transferInProgress(ts.byAppending(bodyData: data))
return .proceed
@@ -449,13 +458,10 @@
fileprivate func notifyDelegate(aboutReceivedData data: Data) {
guard let t = self.task else { fatalError("Cannot notify") }
if case .taskDelegate(let delegate) = t.session.behaviour(for: self.task!),
- let dataDelegate = delegate as? URLSessionDataDelegate,
- let task = self.task as? URLSessionDataTask {
- // Forward to the delegate:
- guard let s = self.task?.session as? URLSession else { fatalError() }
- s.delegateQueue.addOperation {
- dataDelegate.urlSession(s, dataTask: task, didReceive: data)
- }
+ let _ = delegate as? URLSessionDataDelegate,
+ let _ = self.task as? URLSessionDataTask {
+ // Forward to protocol client:
+ self.client?.urlProtocol(self, didLoad: data)
} else if case .taskDelegate(let delegate) = t.session.behaviour(for: self.task!),
let downloadDelegate = delegate as? URLSessionDownloadDelegate,
let task = self.task as? URLSessionDownloadTask {
@@ -471,9 +477,7 @@
}
if Int(self.easyHandle.fileLength) == self.totalDownloaded {
fileHandle.closeFile()
- s.delegateQueue.addOperation {
- downloadDelegate.urlSession(s, downloadTask: task, didFinishDownloadingTo: self.tempFileURL)
- }
+ self.properties[.temporaryFileURL] = self.tempFileURL
}
}
}
@@ -494,6 +498,16 @@
}
}
+ fileprivate func notifyDelegate(aboutUploadedData count: Int64) {
+ let session = self.task?.session as! URLSession
+ guard case .taskDelegate(let delegate) = session.behaviour(for: self.task!), self.task is URLSessionUploadTask else { return }
+ totalUploaded += count
+ session.delegateQueue.addOperation {
+ delegate.urlSession(session, task: self.task!, didSendBodyData: count,
+ totalBytesSent: self.totalUploaded, totalBytesExpectedToSend: self.requestBodyLength)
+ }
+ }
+
func fill(writeBuffer buffer: UnsafeMutableBufferPointer<Int8>) -> _EasyHandle._WriteBufferResult {
guard case .transferInProgress(let ts) = internalState else { fatalError("Requested to fill write buffer, but transfer isn't in progress.") }
guard let source = ts.requestBodySource else { fatalError("Requested to fill write buffer, but transfer state has no body source.") }
@@ -502,6 +516,7 @@
copyDispatchData(data, infoBuffer: buffer)
let count = data.count
assert(count > 0)
+ notifyDelegate(aboutUploadedData: Int64(count))
return .bytes(count)
case .done:
return .bytes(0)
@@ -759,14 +774,14 @@
/// Whenever we receive a response (i.e. a complete header) from libcurl,
/// this method gets called.
func didReceiveResponse() {
- guard let dt = task as? URLSessionDataTask else { return }
+ guard let _ = task as? URLSessionDataTask else { return }
guard case .transferInProgress(let ts) = self.internalState else { fatalError("Transfer not in progress.") }
guard let response = ts.response else { fatalError("Header complete, but not URL response.") }
guard let session = task?.session as? URLSession else { fatalError() }
switch session.behaviour(for: self.task!) {
case .noDelegate:
break
- case .taskDelegate(let delegate as URLSessionDataDelegate):
+ case .taskDelegate(_):
//TODO: There's a problem with libcurl / with how we're using it.
// We're currently unable to pause the transfer / the easy handle:
// https://curl.haxx.se/mail/lib-2016-03/0222.html
@@ -777,14 +792,8 @@
case 301, 302, 303, 307:
break
default:
- session.delegateQueue.addOperation {
- delegate.urlSession(session, dataTask: dt, didReceive: response, completionHandler: { _ in
- URLSession.printDebug("warning: Ignoring disposition from completion handler.")
- })
- }
+ self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
}
- case .taskDelegate:
- break
case .dataCompletionHandler:
break
case .downloadCompletionHandler:
diff --git a/Foundation/NSValue.swift b/Foundation/NSValue.swift
index 6b255d0..3971b21 100644
--- a/Foundation/NSValue.swift
+++ b/Foundation/NSValue.swift
@@ -154,3 +154,14 @@
}
}
+extension NSValue : _Factory {}
+
+internal protocol _Factory {
+ init(factory: () -> Self)
+}
+
+extension _Factory {
+ init(factory: () -> Self) {
+ self = factory()
+ }
+}
diff --git a/Foundation/NSXMLDTD.swift b/Foundation/NSXMLDTD.swift
index df55ada..5f3566b 100644
--- a/Foundation/NSXMLDTD.swift
+++ b/Foundation/NSXMLDTD.swift
@@ -22,7 +22,7 @@
NSUnimplemented()
}
- public convenience init(contentsOf url: URL, options: Options = []) throws {
+ public convenience init(contentsOf url: URL, options mask: XMLNode.Options = []) throws {
let urlString = url.absoluteString
guard let node = _CFXMLParseDTD(urlString) else {
@@ -32,7 +32,7 @@
self.init(ptr: node)
}
- public convenience init(data: Data, options: Options = []) throws {
+ public convenience init(data: Data, options mask: XMLNode.Options = []) throws {
var unmanagedError: Unmanaged<CFError>? = nil
guard let node = _CFXMLParseDTDFromData(data._cfObject, &unmanagedError) else {
@@ -52,7 +52,7 @@
*/
open var publicID: String? {
get {
- return _CFXMLDTDExternalID(_xmlDTD)?._swiftObject
+ return _CFXMLDTDCopyExternalID(_xmlDTD)?._swiftObject
}
set {
@@ -70,7 +70,7 @@
*/
open var systemID: String? {
get {
- return _CFXMLDTDSystemID(_xmlDTD)?._swiftObject
+ return _CFXMLDTDCopySystemID(_xmlDTD)?._swiftObject
}
set {
diff --git a/Foundation/NSXMLDTDNode.swift b/Foundation/NSXMLDTDNode.swift
index 6d28bb2..b8adaff 100644
--- a/Foundation/NSXMLDTDNode.swift
+++ b/Foundation/NSXMLDTDNode.swift
@@ -80,7 +80,7 @@
super.init(ptr: ptr)
} //primitive
- public override init(kind: Kind, options: Options = []) {
+ public override init(kind: XMLNode.Kind, options: XMLNode.Options = []) {
let ptr: _CFXMLNodePtr
switch kind {
@@ -99,7 +99,7 @@
@method dtdKind
@abstract Sets the DTD sub kind.
*/
- open var dtdKind: DTDKind {
+ open var dtdKind: XMLDTDNode.DTDKind {
switch _CFXMLNodeGetType(_xmlNode) {
case _kCFXMLDTDNodeTypeElement:
switch _CFXMLDTDElementNodeGetType(_xmlNode) {
@@ -200,7 +200,7 @@
*/
open var publicID: String? {
get {
- return _CFXMLDTDNodeGetPublicID(_xmlNode)?._swiftObject
+ return _CFXMLDTDNodeCopyPublicID(_xmlNode)?._swiftObject
}
set {
if let value = newValue {
@@ -217,7 +217,7 @@
*/
open var systemID: String? {
get {
- return _CFXMLDTDNodeGetSystemID(_xmlNode)?._swiftObject
+ return _CFXMLDTDNodeCopySystemID(_xmlNode)?._swiftObject
}
set {
if let value = newValue {
@@ -238,7 +238,7 @@
return nil
}
- return _CFXMLGetEntityContent(_xmlNode)?._swiftObject
+ return _CFXMLCopyEntityContent(_xmlNode)?._swiftObject
}
set {
guard dtdKind == .unparsed else {
diff --git a/Foundation/NSXMLDocument.swift b/Foundation/NSXMLDocument.swift
index ca82b6f..245ad1f 100644
--- a/Foundation/NSXMLDocument.swift
+++ b/Foundation/NSXMLDocument.swift
@@ -68,34 +68,34 @@
@method initWithXMLString:options:error:
@abstract Returns a document created from either XML or HTML, if the HTMLTidy option is set. Parse errors are returned in <tt>error</tt>.
*/
- public convenience init(xmlString string: String, options: Options) throws {
+ public convenience init(xmlString string: String, options mask: XMLNode.Options = []) throws {
guard let data = string.data(using: .utf8) else {
// TODO: Throw an error
fatalError("String: '\(string)' could not be converted to NSData using UTF-8 encoding")
}
- try self.init(data: data, options: options)
+ try self.init(data: data, options: mask)
}
/*!
@method initWithContentsOfURL:options:error:
@abstract Returns a document created from the contents of an XML or HTML URL. Connection problems such as 404, parse errors are returned in <tt>error</tt>.
*/
- public convenience init(contentsOf url: URL, options: Options) throws {
+ public convenience init(contentsOf url: URL, options mask: XMLNode.Options = []) throws {
let data = try Data(contentsOf: url, options: .mappedIfSafe)
- try self.init(data: data, options: options)
+ try self.init(data: data, options: mask)
}
/*!
@method initWithData:options:error:
@abstract Returns a document created from data. Parse errors are returned in <tt>error</tt>.
*/
- public init(data: Data, options: Options) throws {
- let docPtr = _CFXMLDocPtrFromDataWithOptions(data._cfObject, Int32(options.rawValue))
+ public init(data: Data, options mask: XMLNode.Options = []) throws {
+ let docPtr = _CFXMLDocPtrFromDataWithOptions(data._cfObject, Int32(mask.rawValue))
super.init(ptr: _CFXMLNodePtr(docPtr))
- if options.contains(.documentValidate) {
+ if mask.contains(.documentValidate) {
try validate()
}
}
@@ -124,7 +124,7 @@
*/
open var characterEncoding: String? {
get {
- return _CFXMLDocCharacterEncoding(_xmlDoc)?._swiftObject
+ return _CFXMLDocCopyCharacterEncoding(_xmlDoc)?._swiftObject
}
set {
if let value = newValue {
@@ -141,7 +141,7 @@
*/
open var version: String? {
get {
- return _CFXMLDocVersion(_xmlDoc)?._swiftObject
+ return _CFXMLDocCopyVersion(_xmlDoc)?._swiftObject
}
set {
if let value = newValue {
@@ -170,7 +170,7 @@
@method documentContentKind
@abstract The kind of document.
*/
- open var documentContentKind: ContentKind {
+ open var documentContentKind: XMLDocument.ContentKind {
get {
let properties = _CFXMLDocProperties(_xmlDoc)
@@ -311,14 +311,14 @@
@method XMLData
@abstract Invokes XMLDataWithOptions with NSXMLNodeOptionsNone.
*/
- /*@NSCopying*/ open var xmlData: Data { return xmlData(withOptions: []) }
+ /*@NSCopying*/ open var xmlData: Data { return xmlData() }
/*!
@method XMLDataWithOptions:
@abstract The representation of this node as it would appear in an XML document, encoded based on characterEncoding.
*/
- open func xmlData(withOptions options: Options) -> Data {
- let string = xmlString(withOptions: options)
+ open func xmlData(options: XMLNode.Options = []) -> Data {
+ let string = xmlString(options: options)
// TODO: support encodings other than UTF-8
return string.data(using: .utf8) ?? Data()
diff --git a/Foundation/NSXMLElement.swift b/Foundation/NSXMLElement.swift
index aa12142..1b91296 100644
--- a/Foundation/NSXMLElement.swift
+++ b/Foundation/NSXMLElement.swift
@@ -27,9 +27,9 @@
@method initWithName:URI:
@abstract Returns an element whose full QName is specified.
*/
- public init(name: String, uri: String?) {
+ public init(name: String, uri URI: String?) {
super.init(kind: .element, options: [])
- self.uri = uri
+ self.uri = URI
self.name = name
}
@@ -49,11 +49,18 @@
@method initWithXMLString:error:
@abstract Returns an element created from a string. Parse errors are collected in <tt>error</tt>.
*/
- public init(xmlString string: String) throws {
- NSUnimplemented()
+ public convenience init(xmlString string: String) throws {
+ // If we prepend the XML line to the string
+ let docString = """
+ <?xml version="1.0" encoding="utf-8" standalone="yes"?>\(string)
+ """
+ // we can use the document string parser to get the element
+ let doc = try XMLDocument(xmlString: docString, options: [])
+ // We know the doc has a root element and first child or else the above line would have thrown
+ self.init(ptr: _CFXMLCopyNode(_CFXMLNodeGetFirstChild(doc._xmlNode)!, true))
}
- public convenience override init(kind: Kind, options: Options = []) {
+ public convenience override init(kind: XMLNode.Kind, options: XMLNode.Options = []) {
self.init(name: "", uri: nil)
}
@@ -69,17 +76,21 @@
@method elementsForLocalName:URI
@abstract Returns all of the child elements that match this localname URI pair.
*/
- open func elements(forLocalName localName: String, uri: String?) -> [XMLElement] { NSUnimplemented() }
+ open func elements(forLocalName localName: String, uri URI: String?) -> [XMLElement] {
+ return self.filter({ _CFXMLNodeGetType($0._xmlNode) == _kCFXMLTypeElement }).filter({ $0.localName == localName && $0.uri == uri }).flatMap({ $0 as? XMLElement })
+ }
/*!
@method addAttribute:
@abstract Adds an attribute. Attributes with duplicate names are not added.
*/
open func addAttribute(_ attribute: XMLNode) {
- let name = _CFXMLNodeGetName(attribute._xmlNode)!
- let len = strlen(name)
- name.withMemoryRebound(to: UInt8.self, capacity: Int(len)) {
- guard _CFXMLNodeHasProp(_xmlNode, $0) == nil else { return }
+ guard let name = _CFXMLNodeCopyName(attribute._xmlNode)?._swiftObject else {
+ fatalError("Attributes must have a name!")
+ }
+
+ name.cString(using: .utf8)!.withUnsafeBufferPointer() {
+ guard let ptr = $0.baseAddress, _CFXMLNodeHasProp(_xmlNode, ptr) == nil else { return }
addChild(attribute)
}
}
@@ -169,7 +180,7 @@
@method attributeForLocalName:URI:
@abstract Returns an attribute matching this localname URI pair.
*/
- open func attribute(forLocalName localName: String, uri: String?) -> XMLNode? {
+ open func attribute(forLocalName localName: String, uri URI: String?) -> XMLNode? {
NSUnimplemented()
}
@@ -178,7 +189,10 @@
@abstract Adds a namespace. Namespaces with duplicate names are not added.
*/
open func addNamespace(_ aNamespace: XMLNode) {
- NSUnimplemented()
+ if ((namespaces ?? []).flatMap({ $0.name }).contains(aNamespace.name ?? "")) {
+ return
+ }
+ _CFXMLAddNamespace(_xmlNode, aNamespace._xmlNode)
}
/*!
@@ -186,7 +200,7 @@
@abstract Removes a namespace with a particular name.
*/
open func removeNamespace(forPrefix name: String) {
- NSUnimplemented()
+ _CFXMLRemoveNamespace(_xmlNode, name)
}
/*!
@@ -194,7 +208,28 @@
@abstract Set the namespaces. In the case of duplicate names, the first namespace with the name is used.
*/
open var namespaces: [XMLNode]? {
- NSUnimplemented()
+ get {
+ var count: Int = 0
+ if let result = _CFXMLNamespaces(_xmlNode, &count) {
+ defer {
+ free(result)
+ }
+ let namespacePtrs = UnsafeBufferPointer<_CFXMLNodePtr>(start: result, count: count)
+ return namespacePtrs.map { XMLNode._objectNodeForNode($0) }
+ }
+
+ return nil
+ }
+
+ set {
+ if var nodes = newValue?.map({ $0._xmlNode }) {
+ nodes.withUnsafeMutableBufferPointer({ (bufPtr) in
+ _CFXMLSetNamespaces(_xmlNode, bufPtr.baseAddress, bufPtr.count)
+ })
+ } else {
+ _CFXMLSetNamespaces(_xmlNode, nil, 0);
+ }
+ }
}
/*!
diff --git a/Foundation/NSXMLNode.swift b/Foundation/NSXMLNode.swift
index 576b355..f240b3e 100644
--- a/Foundation/NSXMLNode.swift
+++ b/Foundation/NSXMLNode.swift
@@ -42,7 +42,7 @@
case processingInstruction
case comment
case text
- case dtd
+ case DTDKind
case entityDeclaration
case attributeDeclaration
case elementDeclaration
@@ -52,26 +52,26 @@
public struct Options : OptionSet {
public let rawValue : UInt
public init(rawValue: UInt) { self.rawValue = rawValue }
-
+
public static let nodeIsCDATA = Options(rawValue: 1 << 0)
public static let nodeExpandEmptyElement = Options(rawValue: 1 << 1)
public static let nodeCompactEmptyElement = Options(rawValue: 1 << 2)
public static let nodeUseSingleQuotes = Options(rawValue: 1 << 3)
public static let nodeUseDoubleQuotes = Options(rawValue: 1 << 4)
public static let nodeNeverEscapeContents = Options(rawValue: 1 << 5)
-
+
public static let documentTidyHTML = Options(rawValue: 1 << 9)
public static let documentTidyXML = Options(rawValue: 1 << 10)
public static let documentValidate = Options(rawValue: 1 << 13)
-
+
public static let nodeLoadExternalEntitiesAlways = Options(rawValue: 1 << 14)
public static let nodeLoadExternalEntitiesSameOriginOnly = Options(rawValue: 1 << 15)
public static let nodeLoadExternalEntitiesNever = Options(rawValue: 1 << 19)
-
+
public static let documentXInclude = Options(rawValue: 1 << 16)
public static let nodePrettyPrint = Options(rawValue: 1 << 17)
public static let documentIncludeContentTypeDeclaration = Options(rawValue: 1 << 18)
-
+
public static let nodePreserveNamespaceOrder = Options(rawValue: 1 << 20)
public static let nodePreserveAttributeOrder = Options(rawValue: 1 << 21)
public static let nodePreserveEntities = Options(rawValue: 1 << 22)
@@ -101,7 +101,7 @@
@method initWithKind:
@abstract Invokes @link initWithKind:options: @/link with options set to NSXMLNodeOptionsNone
*/
- public convenience init(kind: Kind) {
+ public convenience init(kind: XMLNode.Kind) {
self.init(kind: kind, options: [])
}
@@ -109,7 +109,7 @@
@method initWithKind:options:
@abstract Inits a node with fidelity options as description NSXMLNodeOptions.h
*/
- public init(kind: Kind, options: Options = []) {
+ public init(kind: XMLNode.Kind, options: XMLNode.Options = []) {
switch kind {
case .document:
@@ -123,9 +123,12 @@
case .attribute:
_xmlNode = _CFXMLNodePtr(_CFXMLNewProperty(nil, "", ""))
- case .dtd:
+ case .DTDKind:
_xmlNode = _CFXMLNewDTD(nil, "", "", "")
-
+
+ case .namespace:
+ _xmlNode = _CFXMLNewNamespace("", "")
+
default:
fatalError("invalid node kind for this initializer")
}
@@ -216,7 +219,8 @@
@abstract Returns a namespace <tt>xmlns:name="stringValue"</tt>.
*/
open class func namespace(withName name: String, stringValue: String) -> Any {
- NSUnimplemented()
+ let node = _CFXMLNewNamespace(name, stringValue)
+ return XMLNode(ptr: node)
}
/*!
@@ -260,7 +264,7 @@
@method kind
@abstract Returns an element, attribute, entity, or notation DTD node based on the full XML string.
*/
- open var kind: Kind {
+ open var kind: XMLNode.Kind {
switch _CFXMLNodeGetType(_xmlNode) {
case _kCFXMLTypeElement:
return .element
@@ -272,7 +276,7 @@
return .document
case _kCFXMLTypeDTD:
- return .dtd
+ return .DTDKind
case _kCFXMLDTDNodeTypeElement:
return .elementDeclaration
@@ -286,6 +290,9 @@
case _kCFXMLDTDNodeTypeAttribute:
return .attributeDeclaration
+ case _kCFXMLTypeNamespace:
+ return .namespace
+
default:
return .invalid
}
@@ -297,17 +304,21 @@
*/
open var name: String? {
get {
- if let ptr = _CFXMLNodeGetName(_xmlNode) {
- return String(cString: ptr)
- } else {
- return nil
+ if case .namespace = kind {
+ return _CFXMLNamespaceCopyPrefix(_xmlNode)?._swiftObject
}
+
+ return _CFXMLNodeCopyName(_xmlNode)?._swiftObject
}
set {
- if let newName = newValue {
- _CFXMLNodeSetName(_xmlNode, newName)
+ if case .namespace = kind {
+ _CFXMLNamespaceSetPrefix(_xmlNode, newValue, Int64(newValue?.utf8.count ?? 0))
} else {
- _CFXMLNodeSetName(_xmlNode, "")
+ if let newName = newValue {
+ _CFXMLNodeSetName(_xmlNode, newName)
+ } else {
+ _CFXMLNodeSetName(_xmlNode, "")
+ }
}
}
}
@@ -346,13 +357,25 @@
get {
switch kind {
case .entityDeclaration:
- return _CFXMLGetEntityContent(_CFXMLEntityPtr(_xmlNode))?._swiftObject
+ return _CFXMLCopyEntityContent(_CFXMLEntityPtr(_xmlNode))?._swiftObject
+
+ case .namespace:
+ return _CFXMLNamespaceCopyValue(_xmlNode)?._swiftObject
default:
- return _CFXMLNodeGetContent(_xmlNode)?._swiftObject
+ return _CFXMLNodeCopyContent(_xmlNode)?._swiftObject
}
}
set {
+ if case .namespace = kind {
+ if let newValue = newValue {
+ precondition(URL(string: newValue) != nil, "namespace stringValue must be a valid href")
+ }
+
+ _CFXMLNamespaceSetValue(_xmlNode, newValue, Int64(newValue?.utf8.count ?? 0))
+ return
+ }
+
_removeAllChildNodesExceptAttributes() // in case anyone is holding a reference to any of these children we're about to destroy
if let string = newValue {
@@ -428,7 +451,7 @@
entityPtr = _CFXMLGetParameterEntity(doc, entity)
}
if let validEntity = entityPtr {
- let replacement = _CFXMLGetEntityContent(validEntity)?._swiftObject ?? ""
+ let replacement = _CFXMLCopyEntityContent(validEntity)?._swiftObject ?? ""
result.replaceSubrange(range, with: replacement.characters)
} else {
result.replaceSubrange(range, with: []) // This appears to be how Darwin Foundation does it
@@ -503,7 +526,7 @@
fallthrough
case .element:
fallthrough
- case .dtd:
+ case .DTDKind:
return Array<XMLNode>(self as XMLNode)
default:
@@ -546,7 +569,7 @@
@method previousNode:
@abstract Returns the previous node in document order. This can be used to walk the tree backwards.
*/
- /*@NSCopying*/ open var previousNode: XMLNode? {
+ /*@NSCopying*/ open var previous: XMLNode? {
if let previousSibling = self.previousSibling {
if let lastChild = _CFXMLNodeGetLastChild(previousSibling._xmlNode) {
return XMLNode._objectNodeForNode(lastChild)
@@ -564,7 +587,7 @@
@method nextNode:
@abstract Returns the next node in document order. This can be used to walk the tree forwards.
*/
- /*@NSCopying*/ open var nextNode: XMLNode? {
+ /*@NSCopying*/ open var next: XMLNode? {
if let children = _CFXMLNodeGetFirstChild(_xmlNode) {
return XMLNode._objectNodeForNode(children)
} else if let next = nextSibling {
@@ -585,7 +608,7 @@
_CFXMLUnlinkNode(_xmlNode)
guard let parentNodePtr = _CFXMLNodeGetPrivateData(parentPtr) else { return }
-
+
let parent: XMLNode = NSObject.unretainedReference(parentNodePtr)
parent._childNodes.remove(self)
}
@@ -597,53 +620,7 @@
open var xPath: String? {
guard _CFXMLNodeGetDocument(_xmlNode) != nil else { return nil }
- var pathComponents: [String?] = []
- var parent = _CFXMLNodeGetParent(_xmlNode)
- if parent != nil {
- let parentObj = XMLNode._objectNodeForNode(parent!)
- let siblingsWithSameName = parentObj.filter { $0.name == self.name }
-
- if siblingsWithSameName.count > 1 {
- guard let index = siblingsWithSameName.index(of: self) else { return nil }
-
- pathComponents.append("\(self.name ?? "")[\(index + 1)]")
- } else {
- pathComponents.append(self.name)
- }
- } else {
- return self.name
- }
- while true {
- if let parentNode = _CFXMLNodeGetParent(parent!) {
- let grandparent = XMLNode._objectNodeForNode(parentNode)
- let possibleParentNodes = grandparent.filter { $0.name == self.parent?.name }
- let count = possibleParentNodes.reduce(0) { (x, _) in
- return x + 1
- }
-
- if count <= 1 {
- pathComponents.append(XMLNode._objectNodeForNode(parent!).name)
- } else {
- var parentNumber = 1
- for possibleParent in possibleParentNodes {
- if possibleParent == self.parent {
- break
- }
- parentNumber += 1
- }
-
- pathComponents.append("\(self.parent?.name ?? "")[\(parentNumber)]")
- }
-
- parent = _CFXMLNodeGetParent(parent!)
-
- } else {
- pathComponents.append(XMLNode._objectNodeForNode(parent!).name)
- break
- }
- }
-
- return pathComponents.reversed().flatMap({ return $0 }).joined(separator: "/")
+ return _CFXMLCopyPathForNode(_xmlNode)?._swiftObject
}
/*!
@@ -651,7 +628,7 @@
@abstract Returns the local name bar if this attribute or element's name is foo:bar
*/
open var localName: String? {
- return _CFXMLNodeLocalName(_xmlNode)?._swiftObject
+ return _CFXMLNodeCopyLocalName(_xmlNode)?._swiftObject
}
/*!
@@ -659,7 +636,7 @@
@abstract Returns the prefix foo if this attribute or element's name if foo:bar
*/
open var prefix: String? {
- return _CFXMLNodePrefix(_xmlNode)?._swiftObject
+ return _CFXMLNodeCopyPrefix(_xmlNode)?._swiftObject
}
/*!
@@ -668,7 +645,7 @@
*/
open var uri: String? {
get {
- return _CFXMLNodeURI(_xmlNode)?._swiftObject
+ return _CFXMLNodeCopyURI(_xmlNode)?._swiftObject
}
set {
if let URI = newValue {
@@ -728,15 +705,15 @@
@abstract The representation of this node as it would appear in an XML document.
*/
open var xmlString: String {
- return xmlString(withOptions: [])
+ return xmlString(options: [])
}
/*!
@method XMLStringWithOptions:
@abstract The representation of this node as it would appear in an XML document, with various output options available.
*/
- open func xmlString(withOptions options: Options) -> String {
- return _CFXMLStringWithOptions(_xmlNode, UInt32(options.rawValue))._swiftObject
+ open func xmlString(options: Options) -> String {
+ return _CFXMLCopyStringWithOptions(_xmlNode, UInt32(options.rawValue))._swiftObject
}
/*!
@@ -788,12 +765,12 @@
case .document:
_CFXMLFreeDocument(_CFXMLDocPtr(_xmlNode))
- case .dtd:
+ case .DTDKind:
_CFXMLFreeDTD(_CFXMLDTDPtr(_xmlNode))
case .attribute:
_CFXMLFreeProperty(_xmlNode)
-
+
default:
_CFXMLFreeNode(_xmlNode)
}
@@ -809,7 +786,7 @@
let parentNode = XMLNode._objectNodeForNode(parent)
parentNode._childNodes.insert(self)
}
-
+
withUnretainedReference {
_CFXMLNodeSetPrivateData(_xmlNode, $0)
}
@@ -968,4 +945,3 @@
}
}
}
-
diff --git a/Foundation/NSXMLParser.swift b/Foundation/NSXMLParser.swift
index a2409f8..f36b07c 100644
--- a/Foundation/NSXMLParser.swift
+++ b/Foundation/NSXMLParser.swift
@@ -22,11 +22,10 @@
extension XMLParser {
public enum ExternalEntityResolvingPolicy : UInt {
-
- case resolveExternalEntitiesNever // default
- case resolveExternalEntitiesNoNetwork
- case resolveExternalEntitiesSameOriginOnly //only applies to NSXMLParser instances initialized with -initWithContentsOfURL:
- case resolveExternalEntitiesAlways
+ case never // default
+ case noNetwork
+ case sameOriginOnly //only applies to NSXMLParser instances initialized with -initWithContentsOfURL:
+ case always
}
}
@@ -44,8 +43,8 @@
private func UTF8STRING(_ bytes: UnsafePointer<UInt8>?) -> String? {
guard let bytes = bytes else { return nil }
- // strlen operates on the wrong type, char*. We can't rebind the memory to a different type without knowing it's length,
- // but since we know strlen is in libc, its safe to directly bitcast the pointer without worrying about multiple accesses
+ // strlen operates on the wrong type, char*. We can't rebind the memory to a different type without knowing its length,
+ // but since we know strlen is in libc, it's safe to directly bitcast the pointer without worrying about multiple accesses
// of different types visible to the compiler.
let len = strlen(unsafeBitCast(bytes, to: UnsafePointer<Int8>.self))
let str = String._fromCodeUnitSequence(UTF8.self, input: UnsafeBufferPointer(start: bytes, count: Int(len)))
@@ -75,7 +74,7 @@
}
if let url = a {
let allowed = allowedEntityURLs.contains(url)
- if allowed || policy != .resolveExternalEntitiesSameOriginOnly {
+ if allowed || policy != .sameOriginOnly {
if allowed {
return originalLoaderFunction(urlStr, identifier, context)
}
@@ -84,7 +83,7 @@
}
switch policy {
- case .resolveExternalEntitiesSameOriginOnly:
+ case .sameOriginOnly:
guard let url = parser._url else { break }
if a == nil {
@@ -119,11 +118,11 @@
if !matches {
return nil
}
- case .resolveExternalEntitiesAlways:
+ case .always:
break
- case .resolveExternalEntitiesNever:
+ case .never:
return nil
- case .resolveExternalEntitiesNoNetwork:
+ case .noNetwork:
return _CFXMLInterfaceNoNetExternalEntityLoader(urlStr, identifier, context)
}
@@ -456,7 +455,7 @@
open var shouldReportNamespacePrefixes: Bool = false
//defaults to NSXMLNodeLoadExternalEntitiesNever
- open var externalEntityResolvingPolicy: ExternalEntityResolvingPolicy = .resolveExternalEntitiesNever
+ open var externalEntityResolvingPolicy: ExternalEntityResolvingPolicy = .never
open var allowedExternalEntityURLs: Set<URL>?
@@ -954,7 +953,7 @@
case uriFragmentError
- case nodtdError
+ case noDTDError
case delegateAbortedParseError
}
diff --git a/Foundation/NumberFormatter.swift b/Foundation/NumberFormatter.swift
index e702d29..3d4444a 100644
--- a/Foundation/NumberFormatter.swift
+++ b/Foundation/NumberFormatter.swift
@@ -895,16 +895,14 @@
}
}
- // FIXME: Uncomment this when NSDecimalNumberHandler.default() gets rid of NSUnimplementend()
- // This is currently commented out so that NSNumberFormatter instances can be tested
-// internal var _roundingBehavior: NSDecimalNumberHandler = NSDecimalNumberHandler.default()
-// /*@NSCopying*/ open var roundingBehavior: NSDecimalNumberHandler {
-// get {
-// return _roundingBehavior
-// }
-// set {
-// _reset()
-// _roundingBehavior = newValue
-// }
-// }
+ internal var _roundingBehavior: NSDecimalNumberHandler = NSDecimalNumberHandler.default
+ /*@NSCopying*/ open var roundingBehavior: NSDecimalNumberHandler {
+ get {
+ return _roundingBehavior
+ }
+ set {
+ _reset()
+ _roundingBehavior = newValue
+ }
+ }
}
diff --git a/Foundation/Operation.swift b/Foundation/Operation.swift
index 988aef8..d74b2a8 100644
--- a/Foundation/Operation.swift
+++ b/Foundation/Operation.swift
@@ -48,7 +48,13 @@
}
open func start() {
+ lock.lock()
+ _executing = true
+ lock.unlock()
main()
+ lock.lock()
+ _executing = false
+ lock.unlock()
finish()
}
@@ -85,7 +91,12 @@
}
open var isExecuting: Bool {
- return _executing
+ let wasExecuting: Bool
+ lock.lock()
+ wasExecuting = _executing
+ lock.unlock()
+
+ return wasExecuting
}
open var isFinished: Bool {
@@ -306,7 +317,13 @@
let lock = NSLock()
#if DEPLOYMENT_ENABLE_LIBDISPATCH
var __concurrencyGate: DispatchSemaphore?
- var __underlyingQueue: DispatchQueue?
+ var __underlyingQueue: DispatchQueue? {
+ didSet {
+ let key = OperationQueue.OperationQueueKey
+ oldValue?.setSpecific(key: key, value: nil)
+ __underlyingQueue?.setSpecific(key: key, value: Unmanaged.passUnretained(self))
+ }
+ }
let queueGroup = DispatchGroup()
#endif
@@ -552,29 +569,27 @@
open class var current: OperationQueue? {
#if DEPLOYMENT_ENABLE_LIBDISPATCH
- let specific = DispatchQueue.getSpecific(key: OperationQueue.OperationQueueKey)
- if specific == nil {
+ guard let specific = DispatchQueue.getSpecific(key: OperationQueue.OperationQueueKey) else {
if pthread_main_np() == 1 {
return OperationQueue.main
} else {
return nil
}
- } else {
- return specific!.takeUnretainedValue()
}
+
+ return specific.takeUnretainedValue()
#else
return nil
#endif
}
+#if DEPLOYMENT_ENABLE_LIBDISPATCH
+ private static let _main = OperationQueue(_queue: .main, maxConcurrentOperations: 1)
+#endif
+
open class var main: OperationQueue {
#if DEPLOYMENT_ENABLE_LIBDISPATCH
- let specific = DispatchQueue.main.getSpecific(key: OperationQueue.OperationQueueKey)
- if specific == nil {
- return OperationQueue(_queue: DispatchQueue.main, maxConcurrentOperations: 1)
- } else {
- return specific!.takeUnretainedValue()
- }
+ return _main
#else
fatalError("NSOperationQueue requires libdispatch")
#endif
diff --git a/Foundation/PersonNameComponents.swift b/Foundation/PersonNameComponents.swift
index 2b8a934..c9fc5a0 100644
--- a/Foundation/PersonNameComponents.swift
+++ b/Foundation/PersonNameComponents.swift
@@ -137,3 +137,36 @@
return AnyHashable(self._bridgeToSwift())
}
}
+
+extension PersonNameComponents : Codable {
+ private enum CodingKeys : Int, CodingKey {
+ case namePrefix
+ case givenName
+ case middleName
+ case familyName
+ case nameSuffix
+ case nickname
+ }
+
+ public init(from decoder: Decoder) throws {
+ self.init()
+
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ self.namePrefix = try container.decodeIfPresent(String.self, forKey: .namePrefix)
+ self.givenName = try container.decodeIfPresent(String.self, forKey: .givenName)
+ self.middleName = try container.decodeIfPresent(String.self, forKey: .middleName)
+ self.familyName = try container.decodeIfPresent(String.self, forKey: .familyName)
+ self.nameSuffix = try container.decodeIfPresent(String.self, forKey: .nameSuffix)
+ self.nickname = try container.decodeIfPresent(String.self, forKey: .nickname)
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ if let np = self.namePrefix { try container.encode(np, forKey: .namePrefix) }
+ if let gn = self.givenName { try container.encode(gn, forKey: .givenName) }
+ if let mn = self.middleName { try container.encode(mn, forKey: .middleName) }
+ if let fn = self.familyName { try container.encode(fn, forKey: .familyName) }
+ if let ns = self.nameSuffix { try container.encode(ns, forKey: .nameSuffix) }
+ if let nn = self.nickname { try container.encode(nn, forKey: .nickname) }
+ }
+}
diff --git a/Foundation/Process.swift b/Foundation/Process.swift
index d104183..07a958e 100644
--- a/Foundation/Process.swift
+++ b/Foundation/Process.swift
@@ -387,11 +387,30 @@
posix(posix_spawn_file_actions_addclose(&fileActions, fd))
}
+ let fileManager = FileManager()
+ let previousDirectoryPath = fileManager.currentDirectoryPath
+ if !fileManager.changeCurrentDirectoryPath(currentDirectoryPath) {
+ // Foundation throws an NSException when changing the working directory fails,
+ // and unfortunately launch() is not marked `throws`, so we get away with a
+ // fatalError.
+ switch errno {
+ case ENOENT:
+ fatalError("Process: The specified working directory does not exist.")
+ case EACCES:
+ fatalError("Process: The specified working directory cannot be accessed.")
+ default:
+ fatalError("Process: The specified working directory cannot be set.")
+ }
+ }
+
// Launch
var pid = pid_t()
posix(posix_spawn(&pid, launchPath, &fileActions, nil, argv, envp))
+ // Reset the previous working directory path.
+ fileManager.changeCurrentDirectoryPath(previousDirectoryPath)
+
// Close the write end of the input and output pipes.
if let pipe = standardInput as? Pipe {
pipe.fileHandleForReading.closeFile()
diff --git a/Foundation/ProgressFraction.swift b/Foundation/ProgressFraction.swift
index 7dac53a..58a7147 100644
--- a/Foundation/ProgressFraction.swift
+++ b/Foundation/ProgressFraction.swift
@@ -54,7 +54,7 @@
return _ProgressFraction(completed: simplified.0, total: simplified.1)
}
- static private func _math(lhs: _ProgressFraction, rhs: _ProgressFraction, whichOperator: (_ lhs : Double, _ rhs : Double) -> Double, whichOverflow : (_ lhs: Int64, _ rhs: Int64) -> (Int64, overflow: ArithmeticOverflow)) -> _ProgressFraction {
+ static private func _math(lhs: _ProgressFraction, rhs: _ProgressFraction, whichOperator: (_ lhs : Double, _ rhs : Double) -> Double, whichOverflow : (_ lhs: Int64, _ rhs: Int64) -> (Int64, overflow: Bool)) -> _ProgressFraction {
// Mathematically, it is nonsense to add or subtract something with a denominator of 0. However, for the purposes of implementing Progress' fractions, we just assume that a zero-denominator fraction is "weightless" and return the other value. We still need to check for the case where they are both nonsense though.
precondition(!(lhs.total == 0 && rhs.total == 0), "Attempt to add or subtract invalid fraction")
guard lhs.total != 0 else {
@@ -71,7 +71,7 @@
if let lcm = _leastCommonMultiple(lhs.total, rhs.total) {
let result = whichOverflow(lhs.completed * (lcm / lhs.total), rhs.completed * (lcm / rhs.total))
- if result.overflow == .overflow {
+ if result.overflow {
return _ProgressFraction(double: whichOperator(lhs.fractionCompleted, rhs.fractionCompleted), overflow: true)
} else {
return _ProgressFraction(completed: result.0, total: lcm)
@@ -83,7 +83,7 @@
if let lcm = _leastCommonMultiple(lhsSimplified.total, rhsSimplified.total) {
let result = whichOverflow(lhsSimplified.completed * (lcm / lhsSimplified.total), rhsSimplified.completed * (lcm / rhsSimplified.total))
- if result.overflow == .overflow {
+ if result.overflow {
// Use original lhs/rhs here
return _ProgressFraction(double: whichOperator(lhs.fractionCompleted, rhs.fractionCompleted), overflow: true)
} else {
@@ -113,7 +113,7 @@
let newCompleted = lhs.completed.multipliedReportingOverflow(by: rhs.completed)
let newTotal = lhs.total.multipliedReportingOverflow(by: rhs.total)
- if newCompleted.overflow == .overflow || newTotal.overflow == .overflow {
+ if newCompleted.overflow || newTotal.overflow {
// Try simplifying, then do it again
let lhsSimplified = lhs.simplified()
let rhsSimplified = rhs.simplified()
@@ -121,7 +121,7 @@
let newCompletedSimplified = lhsSimplified.completed.multipliedReportingOverflow(by: rhsSimplified.completed)
let newTotalSimplified = lhsSimplified.total.multipliedReportingOverflow(by: rhsSimplified.total)
- if newCompletedSimplified.overflow == .overflow || newTotalSimplified.overflow == .overflow {
+ if newCompletedSimplified.overflow || newTotalSimplified.overflow {
// Still overflow
return _ProgressFraction(double: lhs.fractionCompleted * rhs.fractionCompleted, overflow: true)
} else {
@@ -140,12 +140,12 @@
let newTotal = lhs.total.multipliedReportingOverflow(by: rhs)
- if newTotal.overflow == .overflow {
+ if newTotal.overflow {
let simplified = lhs.simplified()
let newTotalSimplified = simplified.total.multipliedReportingOverflow(by: rhs)
- if newTotalSimplified.overflow == .overflow {
+ if newTotalSimplified.overflow {
// Still overflow
return _ProgressFraction(double: lhs.fractionCompleted / Double(rhs), overflow: true)
} else {
@@ -178,7 +178,7 @@
let left = lhs.completed.multipliedReportingOverflow(by: rhs.total)
let right = lhs.total.multipliedReportingOverflow(by: rhs.completed)
- if left.overflow == .none && right.overflow == .none {
+ if !left.overflow && !right.overflow {
if left.0 == right.0 {
return true
}
@@ -190,7 +190,7 @@
let leftSimplified = lhsSimplified.completed.multipliedReportingOverflow(by: rhsSimplified.total)
let rightSimplified = lhsSimplified.total.multipliedReportingOverflow(by: rhsSimplified.completed)
- if leftSimplified.overflow == .none && rightSimplified.overflow == .none {
+ if !leftSimplified.overflow && !rightSimplified.overflow {
if leftSimplified.0 == rightSimplified.0 {
return true
}
@@ -261,7 +261,7 @@
// This division always results in an integer value because gcd(a,b) is a divisor of a.
// lcm(a,b) == (|a|/gcd(a,b))*b == (|b|/gcd(a,b))*a
let result = (a / _greatestCommonDivisor(a, b)).multipliedReportingOverflow(by: b)
- if result.overflow == .overflow {
+ if result.overflow {
return nil
} else {
return result.0
diff --git a/Foundation/URLComponents.swift b/Foundation/URLComponents.swift
index fdf6d95..16bcabb 100644
--- a/Foundation/URLComponents.swift
+++ b/Foundation/URLComponents.swift
@@ -188,8 +188,8 @@
private func _toStringRange(_ r : NSRange) -> Range<String.Index>? {
guard r.location != NSNotFound else { return nil }
- let utf16Start = String.UTF16View.Index(_offset: r.location)
- let utf16End = String.UTF16View.Index(_offset: r.location + r.length)
+ let utf16Start = String.UTF16View.Index(encodedOffset: r.location)
+ let utf16End = String.UTF16View.Index(encodedOffset: r.location + r.length)
guard let s = self.string else { return nil }
guard let start = String.Index(utf16Start, within: s) else { return nil }
diff --git a/Foundation/UUID.swift b/Foundation/UUID.swift
index 269a9a2..9b51302 100644
--- a/Foundation/UUID.swift
+++ b/Foundation/UUID.swift
@@ -163,3 +163,21 @@
}
}
+extension UUID : Codable {
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.singleValueContainer()
+ let uuidString = try container.decode(String.self)
+
+ guard let uuid = UUID(uuidString: uuidString) else {
+ throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath,
+ debugDescription: "Attempted to decode UUID from invalid UUID string."))
+ }
+
+ self = uuid
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.singleValueContainer()
+ try container.encode(self.uuidString)
+ }
+}
diff --git a/TestFoundation/HTTPServer.swift b/TestFoundation/HTTPServer.swift
index bec7079..b129f74 100644
--- a/TestFoundation/HTTPServer.swift
+++ b/TestFoundation/HTTPServer.swift
@@ -17,10 +17,12 @@
#if DEPLOYMENT_RUNTIME_OBJC || os(Linux)
import Foundation
import Glibc
+ import XCTest
#else
import CoreFoundation
import SwiftFoundation
import Darwin
+ import SwiftXCTest
#endif
public let globalDispatchQueue = DispatchQueue.global()
@@ -104,6 +106,12 @@
return String(str[startIndex..<endIndex])
}
}
+
+ func writeRawData(_ data: Data) throws {
+ _ = try data.withUnsafeBytes { ptr in
+ try attempt("write", valid: isNotNegative, CInt(write(connectionSocket, ptr, data.count)))
+ }
+ }
func writeData(header: String, body: String, sendDelay: TimeInterval? = nil, bodyChunks: Int? = nil) throws {
var header = Array(header.utf8)
@@ -173,6 +181,69 @@
semaphore.wait()
}
+
+ func respondWithBrokenResponses(uri: String) throws {
+ let responseData: Data
+ switch uri {
+ case "/LandOfTheLostCities/Pompeii":
+ /* this is an example of what you get if you connect to an HTTP2
+ server using HTTP/1.1. Curl interprets that as a HTTP/0.9
+ simple-response and therefore sends this back as a response
+ body. Go figure! */
+ responseData = Data(bytes: [
+ 0x00, 0x00, 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x05, 0x00, 0x00, 0x40, 0x00, 0x00, 0x06, 0x00,
+ 0x00, 0x1f, 0x40, 0x00, 0x00, 0x86, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x32, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x70, 0x72, 0x65, 0x66, 0x61, 0x63,
+ 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x6d,
+ 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x72, 0x20,
+ 0x63, 0x6f, 0x72, 0x72, 0x75, 0x70, 0x74, 0x2e, 0x20, 0x48,
+ 0x65, 0x78, 0x20, 0x64, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64,
+ 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x3a, 0x20, 0x34, 0x37,
+ 0x34, 0x35, 0x35, 0x34, 0x32, 0x30, 0x32, 0x66, 0x33, 0x33,
+ 0x32, 0x66, 0x36, 0x34, 0x36, 0x35, 0x37, 0x36, 0x36, 0x39,
+ 0x36, 0x33, 0x36, 0x35, 0x32, 0x66, 0x33, 0x31, 0x33, 0x32,
+ 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x37,
+ 0x33, 0x38, 0x33, 0x39, 0x33, 0x30])
+ case "/LandOfTheLostCities/Sodom":
+ /* a technically valid HTTP/0.9 simple-response */
+ responseData = ("technically, this is a valid HTTP/0.9 " +
+ "simple-response. I know it's odd but CURL supports it " +
+ "still...\r\nFind out more in those URLs:\r\n " +
+ " - https://www.w3.org/Protocols/HTTP/1.0/spec.html#Message-Types\r\n" +
+ " - https://github.com/curl/curl/issues/467\r\n").data(using: .utf8)!
+ case "/LandOfTheLostCities/Gomorrah":
+ /* just broken, hope that's not officially HTTP/0.9 :p */
+ responseData = "HTTP/1.1\r\n\r\n\r\n".data(using: .utf8)!
+ case "/LandOfTheLostCities/Myndus":
+ responseData = ("HTTP/1.1 200 OK\r\n" +
+ "\r\n" +
+ "this is a body that isn't legal as it's " +
+ "neither chunked encoding nor any Content-Length\r\n").data(using: .utf8)!
+ case "/LandOfTheLostCities/Kameiros":
+ responseData = ("HTTP/1.1 999 Wrong Code\r\n" +
+ "illegal: status code (too large)\r\n" +
+ "\r\n").data(using: .utf8)!
+ case "/LandOfTheLostCities/Dinavar":
+ responseData = ("HTTP/1.1 20 Too Few Digits\r\n" +
+ "illegal: status code (too few digits)\r\n" +
+ "\r\n").data(using: .utf8)!
+ case "/LandOfTheLostCities/Kuhikugu":
+ responseData = ("HTTP/1.1 2000 Too Many Digits\r\n" +
+ "illegal: status code (too many digits)\r\n" +
+ "\r\n").data(using: .utf8)!
+ default:
+ responseData = ("HTTP/1.1 500 Internal Server Error\r\n" +
+ "case-missing-in: TestFoundation/HTTPServer.swift\r\n" +
+ "\r\n").data(using: .utf8)!
+ }
+ try self.socket.writeRawData(responseData)
+ }
+
}
struct _HTTPRequest {
@@ -249,11 +320,17 @@
}
public func readAndRespond() throws {
- try httpServer.respond(with: process(request: httpServer.request()), startDelay: self.startDelay, sendDelay: self.sendDelay, bodyChunks: self.bodyChunks)
+ let req = try httpServer.request()
+ if req.uri.hasPrefix("/LandOfTheLostCities/") {
+ /* these are all misbehaving servers */
+ try httpServer.respondWithBrokenResponses(uri: req.uri)
+ } else {
+ try httpServer.respond(with: process(request: req), startDelay: self.startDelay, sendDelay: self.sendDelay, bodyChunks: self.bodyChunks)
+ }
}
func process(request: _HTTPRequest) -> _HTTPResponse {
- if request.method == .GET || request.method == .POST {
+ if request.method == .GET || request.method == .POST || request.method == .PUT {
return getResponse(request: request)
} else {
fatalError("Unsupported method!")
@@ -262,14 +339,20 @@
func getResponse(request: _HTTPRequest) -> _HTTPResponse {
let uri = request.uri
+
+ if uri == "/upload" {
+ let text = "Upload completed!"
+ return _HTTPResponse(response: .OK, headers: "Content-Length: \(text.data(using: .utf8)!.count)", body: text)
+ }
+
if uri == "/country.txt" {
let text = capitals[String(uri.characters.dropFirst())]!
- return _HTTPResponse(response: .OK, headers: "Content-Length: \(text.characters.count)", body: text)
+ return _HTTPResponse(response: .OK, headers: "Content-Length: \(text.data(using: .utf8)!.count)", body: text)
}
if uri == "/requestHeaders" {
let text = request.getCommaSeparatedHeaders()
- return _HTTPResponse(response: .OK, headers: "Content-Length: \(text.characters.count)", body: text)
+ return _HTTPResponse(response: .OK, headers: "Content-Length: \(text.data(using: .utf8)!.count)", body: text)
}
if uri == "/UnitedStates" {
@@ -319,3 +402,39 @@
dispatchSemaphore.signal()
}
}
+
+class LoopbackServerTest : XCTestCase {
+ static var serverPort: Int = -1
+
+ override class func setUp() {
+ super.setUp()
+ func runServer(with condition: ServerSemaphore, startDelay: TimeInterval? = nil, sendDelay: TimeInterval? = nil, bodyChunks: Int? = nil) throws {
+ let start = 21961
+ for port in start...(start+100) { //we must find at least one port to bind
+ do {
+ serverPort = port
+ let test = try TestURLSessionServer(port: UInt16(port), startDelay: startDelay, sendDelay: sendDelay, bodyChunks: bodyChunks)
+ try test.start(started: condition)
+ try test.readAndRespond()
+ test.stop()
+ } catch let e as ServerError {
+ if e.operation == "bind" { continue }
+ throw e
+ }
+ }
+ }
+
+ let serverReady = ServerSemaphore()
+ globalDispatchQueue.async {
+ do {
+ try runServer(with: serverReady)
+
+ } catch {
+ XCTAssertTrue(true)
+ return
+ }
+ }
+
+ serverReady.wait()
+ }
+}
diff --git a/TestFoundation/TestCodable.swift b/TestFoundation/TestCodable.swift
new file mode 100644
index 0000000..0ff8297
--- /dev/null
+++ b/TestFoundation/TestCodable.swift
@@ -0,0 +1,328 @@
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See http://swift.org/LICENSE.txt for license information
+// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+
+#if DEPLOYMENT_RUNTIME_OBJC || os(Linux)
+import Foundation
+import XCTest
+#else
+import SwiftFoundation
+import SwiftXCTest
+#endif
+
+// MARK: - Helper Functions
+
+private func makePersonNameComponents(namePrefix: String? = nil,
+ givenName: String? = nil,
+ middleName: String? = nil,
+ familyName: String? = nil,
+ nameSuffix: String? = nil,
+ nickname: String? = nil) -> PersonNameComponents {
+ var result = PersonNameComponents()
+ result.namePrefix = namePrefix
+ result.givenName = givenName
+ result.middleName = middleName
+ result.familyName = familyName
+ result.nameSuffix = nameSuffix
+ result.nickname = nickname
+ return result
+}
+
+func expectRoundTripEquality<T : Codable>(of value: T, encode: (T) throws -> Data, decode: (Data) throws -> T) where T : Equatable {
+ let data: Data
+ do {
+ data = try encode(value)
+ } catch {
+ fatalError("Unable to encode \(T.self) <\(value)>: \(error)")
+ }
+
+ let decoded: T
+ do {
+ decoded = try decode(data)
+ } catch {
+ fatalError("Unable to decode \(T.self) <\(value)>: \(error)")
+ }
+
+ XCTAssertEqual(value, decoded, "Decoded \(T.self) <\(decoded)> not equal to original <\(value)>")
+}
+
+func expectRoundTripEqualityThroughJSON<T : Codable>(for value: T) where T : Equatable {
+ let inf = "INF", negInf = "-INF", nan = "NaN"
+ let encode = { (_ value: T) throws -> Data in
+ let encoder = JSONEncoder()
+ encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: inf,
+ negativeInfinity: negInf,
+ nan: nan)
+ return try encoder.encode(value)
+ }
+
+ let decode = { (_ data: Data) throws -> T in
+ let decoder = JSONDecoder()
+ decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: inf,
+ negativeInfinity: negInf,
+ nan: nan)
+ return try decoder.decode(T.self, from: data)
+ }
+
+ expectRoundTripEquality(of: value, encode: encode, decode: decode)
+}
+
+// MARK: - Helper Types
+// A wrapper around a UUID that will allow it to be encoded at the top level of an encoder.
+struct UUIDCodingWrapper : Codable, Equatable {
+ let value: UUID
+
+ init(_ value: UUID) {
+ self.value = value
+ }
+
+ static func ==(_ lhs: UUIDCodingWrapper, _ rhs: UUIDCodingWrapper) -> Bool {
+ return lhs.value == rhs.value
+ }
+}
+
+// MARK: - Tests
+class TestCodable : XCTestCase {
+
+ // MARK: - PersonNameComponents
+ lazy var personNameComponentsValues: [PersonNameComponents] = [
+ makePersonNameComponents(givenName: "John", familyName: "Appleseed"),
+ makePersonNameComponents(givenName: "John", familyName: "Appleseed", nickname: "Johnny"),
+ makePersonNameComponents(namePrefix: "Dr.", givenName: "Jane", middleName: "A.", familyName: "Appleseed", nameSuffix: "Esq.", nickname: "Janie")
+ ]
+
+ func test_PersonNameComponents_JSON() {
+ for components in personNameComponentsValues {
+ expectRoundTripEqualityThroughJSON(for: components)
+ }
+ }
+
+ // MARK: - UUID
+ lazy var uuidValues: [UUID] = [
+ UUID(),
+ UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!,
+ UUID(uuidString: "e621e1f8-c36c-495a-93fc-0c247a3e6e5f")!,
+ UUID(uuid: uuid_t(0xe6,0x21,0xe1,0xf8,0xc3,0x6c,0x49,0x5a,0x93,0xfc,0x0c,0x24,0x7a,0x3e,0x6e,0x5f))
+ ]
+
+ func test_UUID_JSON() {
+ for uuid in uuidValues {
+ // We have to wrap the UUID since we cannot have a top-level string.
+ expectRoundTripEqualityThroughJSON(for: UUIDCodingWrapper(uuid))
+ }
+ }
+
+ // MARK: - URL
+ lazy var urlValues: [URL] = [
+ URL(fileURLWithPath: NSTemporaryDirectory()),
+ URL(fileURLWithPath: "/"),
+ URL(string: "http://apple.com")!,
+ URL(string: "swift", relativeTo: URL(string: "http://apple.com")!)!,
+ URL(fileURLWithPath: "bin/sh", relativeTo: URL(fileURLWithPath: "/"))
+ ]
+
+ func test_URL_JSON() {
+ for url in urlValues {
+ expectRoundTripEqualityThroughJSON(for: url)
+ }
+ }
+
+ // MARK: - NSRange
+ lazy var nsrangeValues: [NSRange] = [
+ NSRange(),
+ NSRange(location: 0, length: Int.max),
+ NSRange(location: NSNotFound, length: 0),
+ ]
+
+ func test_NSRange_JSON() {
+ for range in nsrangeValues {
+ expectRoundTripEqualityThroughJSON(for: range)
+ }
+ }
+
+ // MARK: - Locale
+ lazy var localeValues: [Locale] = [
+ Locale(identifier: ""),
+ Locale(identifier: "en"),
+ Locale(identifier: "en_US"),
+ Locale(identifier: "en_US_POSIX"),
+ Locale(identifier: "uk"),
+ Locale(identifier: "fr_FR"),
+ Locale(identifier: "fr_BE"),
+ Locale(identifier: "zh-Hant-HK")
+ ]
+
+ func test_Locale_JSON() {
+ for locale in localeValues {
+ expectRoundTripEqualityThroughJSON(for: locale)
+ }
+ }
+
+ // MARK: - IndexSet
+ lazy var indexSetValues: [IndexSet] = [
+ IndexSet(),
+ IndexSet(integer: 42),
+ IndexSet(integersIn: 0 ..< Int.max)
+ ]
+
+ func test_IndexSet_JSON() {
+ for indexSet in indexSetValues {
+ expectRoundTripEqualityThroughJSON(for: indexSet)
+ }
+ }
+
+ // MARK: - IndexPath
+ lazy var indexPathValues: [IndexPath] = [
+ IndexPath(), // empty
+ IndexPath(index: 0), // single
+ IndexPath(indexes: [1, 2]), // pair
+ IndexPath(indexes: [3, 4, 5, 6, 7, 8]), // array
+ ]
+
+ func test_IndexPath_JSON() {
+ for indexPath in indexPathValues {
+ expectRoundTripEqualityThroughJSON(for: indexPath)
+ }
+ }
+
+ // MARK: - AffineTransform
+ lazy var affineTransformValues: [AffineTransform] = [
+ AffineTransform.identity,
+ AffineTransform(),
+ AffineTransform(translationByX: 2.0, byY: 2.0),
+ AffineTransform(scale: 2.0),
+
+ // Disabled due to a bug: JSONSerialization loses precision for m12 and m21
+ // 0.02741213359204429 is serialized to 0.0274121335920443
+ // AffineTransform(rotationByDegrees: .pi / 2),
+
+ AffineTransform(m11: 1.0, m12: 2.5, m21: 66.2, m22: 40.2, tX: -5.5, tY: 3.7),
+ AffineTransform(m11: -55.66, m12: 22.7, m21: 1.5, m22: 0.0, tX: -22, tY: -33),
+ AffineTransform(m11: 4.5, m12: 1.1, m21: 0.025, m22: 0.077, tX: -0.55, tY: 33.2),
+ AffineTransform(m11: 7.0, m12: -2.3, m21: 6.7, m22: 0.25, tX: 0.556, tY: 0.99),
+ AffineTransform(m11: 0.498, m12: -0.284, m21: -0.742, m22: 0.3248, tX: 12, tY: 44)
+ ]
+
+ func test_AffineTransform_JSON() {
+ for transform in affineTransformValues {
+ expectRoundTripEqualityThroughJSON(for: transform)
+ }
+ }
+
+ // MARK: - Decimal
+ lazy var decimalValues: [Decimal] = [
+ Decimal.leastFiniteMagnitude,
+ Decimal.greatestFiniteMagnitude,
+ Decimal.leastNormalMagnitude,
+ Decimal.leastNonzeroMagnitude,
+ Decimal.pi,
+ Decimal()
+ ]
+
+ func test_Decimal_JSON() {
+ for decimal in decimalValues {
+ expectRoundTripEqualityThroughJSON(for: decimal)
+ }
+ }
+
+ // MARK: - CGPoint
+ lazy var cgpointValues: [CGPoint] = [
+ CGPoint(),
+ CGPoint.zero,
+ CGPoint(x: 10, y: 20),
+ CGPoint(x: -10, y: -20),
+ // Disabled due to limit on magnitude in JSON. See SR-5346
+ // CGPoint(x: .greatestFiniteMagnitude, y: .greatestFiniteMagnitude),
+ ]
+
+ func test_CGPoint_JSON() {
+ for point in cgpointValues {
+ expectRoundTripEqualityThroughJSON(for: point)
+ }
+ }
+
+ // MARK: - CGSize
+ lazy var cgsizeValues: [CGSize] = [
+ CGSize(),
+ CGSize.zero,
+ CGSize(width: 30, height: 40),
+ CGSize(width: -30, height: -40),
+ // Disabled due to limit on magnitude in JSON. See SR-5346
+ // CGSize(width: .greatestFiniteMagnitude, height: .greatestFiniteMagnitude),
+ ]
+
+ func test_CGSize_JSON() {
+ for size in cgsizeValues {
+ expectRoundTripEqualityThroughJSON(for: size)
+ }
+ }
+
+ // MARK: - CGRect
+ lazy var cgrectValues: [CGRect] = [
+ CGRect(),
+ CGRect.zero,
+ CGRect(origin: CGPoint(x: 10, y: 20), size: CGSize(width: 30, height: 40)),
+ CGRect(origin: CGPoint(x: -10, y: -20), size: CGSize(width: -30, height: -40)),
+ CGRect.null,
+ // Disabled due to limit on magnitude in JSON. See SR-5346
+ // CGRect.infinite
+ ]
+
+ func test_CGRect_JSON() {
+ for rect in cgrectValues {
+ expectRoundTripEqualityThroughJSON(for: rect)
+ }
+ }
+
+ // MARK: - CharacterSet
+ lazy var characterSetValues: [CharacterSet] = [
+ CharacterSet.controlCharacters,
+ CharacterSet.whitespaces,
+ CharacterSet.whitespacesAndNewlines,
+ CharacterSet.decimalDigits,
+ CharacterSet.letters,
+ CharacterSet.lowercaseLetters,
+ CharacterSet.uppercaseLetters,
+ CharacterSet.nonBaseCharacters,
+ CharacterSet.alphanumerics,
+ CharacterSet.decomposables,
+ CharacterSet.illegalCharacters,
+ CharacterSet.punctuationCharacters,
+ CharacterSet.capitalizedLetters,
+ CharacterSet.symbols,
+ CharacterSet.newlines,
+ CharacterSet(charactersIn: "abcd")
+ ]
+
+ func test_CharacterSet_JSON() {
+ for characterSet in characterSetValues {
+ expectRoundTripEqualityThroughJSON(for: characterSet)
+ }
+ }
+
+}
+
+extension TestCodable {
+ static var allTests: [(String, (TestCodable) -> () throws -> Void)] {
+ return [
+ ("test_PersonNameComponents_JSON", test_PersonNameComponents_JSON),
+ ("test_UUID_JSON", test_UUID_JSON),
+ ("test_URL_JSON", test_URL_JSON),
+ ("test_NSRange_JSON", test_NSRange_JSON),
+ ("test_Locale_JSON", test_Locale_JSON),
+ ("test_IndexSet_JSON", test_IndexSet_JSON),
+ ("test_IndexPath_JSON", test_IndexPath_JSON),
+ ("test_AffineTransform_JSON", test_AffineTransform_JSON),
+ ("test_Decimal_JSON", test_Decimal_JSON),
+ ("test_CGPoint_JSON", test_CGPoint_JSON),
+ ("test_CGSize_JSON", test_CGSize_JSON),
+ ("test_CGRect_JSON", test_CGRect_JSON),
+ ("test_CharacterSet_JSON", test_CharacterSet_JSON),
+ ]
+ }
+}
diff --git a/TestFoundation/TestFileManager.swift b/TestFoundation/TestFileManager.swift
index ab43967..a9cb932 100644
--- a/TestFoundation/TestFileManager.swift
+++ b/TestFoundation/TestFileManager.swift
@@ -84,6 +84,29 @@
} catch {
XCTFail("Failed to clean up file")
}
+
+ let permissions = NSNumber(value: Int16(0o753))
+ let attributes = [FileAttributeKey.posixPermissions: permissions]
+ XCTAssertTrue(fm.createFile(atPath: path, contents: Data(),
+ attributes: attributes))
+ guard let retrievedAtributes = try? fm.attributesOfItem(atPath: path) else {
+ XCTFail("Failed to retrieve file attributes from created file")
+ return
+ }
+
+ XCTAssertTrue(retrievedAtributes.contains(where: { (attribute) -> Bool in
+ guard let attributeValue = attribute.value as? NSNumber else {
+ return false
+ }
+ return (attribute.key == .posixPermissions)
+ && (attributeValue == permissions)
+ }))
+
+ do {
+ try fm.removeItem(atPath: path)
+ } catch {
+ XCTFail("Failed to clean up file")
+ }
}
func test_moveFile() {
diff --git a/TestFoundation/TestJSONEncoder.swift b/TestFoundation/TestJSONEncoder.swift
index 212a0b3..8631442 100644
--- a/TestFoundation/TestJSONEncoder.swift
+++ b/TestFoundation/TestJSONEncoder.swift
@@ -693,6 +693,28 @@
// MARK: - Helper Types
+/// A key type which can take on any string or integer value.
+/// This needs to mirror _JSONKey.
+fileprivate struct _TestKey : CodingKey {
+ var stringValue: String
+ var intValue: Int?
+
+ init?(stringValue: String) {
+ self.stringValue = stringValue
+ self.intValue = nil
+ }
+
+ init?(intValue: Int) {
+ self.stringValue = "\(intValue)"
+ self.intValue = intValue
+ }
+
+ init(index: Int) {
+ self.stringValue = "Index \(index)"
+ self.intValue = index
+ }
+}
+
/// Wraps a type T so that it can be encoded at the top level of a payload.
fileprivate struct TopLevelArrayWrapper<T> : Codable, Equatable where T : Codable, T : Equatable {
let value: T
@@ -883,24 +905,24 @@
// Nested Unkeyed Container
do {
// Nested container for key should have a new key pushed on.
- var secondLevelContainer = firstLevelContainer.nestedUnkeyedContainer(forKey: .a)
+ var secondLevelContainer = firstLevelContainer.nestedUnkeyedContainer(forKey: .b)
expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.")
expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.")
- expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "New second-level keyed container had unexpected codingPath.")
+ expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "New second-level keyed container had unexpected codingPath.")
// Appending a keyed container should not change existing coding paths.
let thirdLevelContainerKeyed = secondLevelContainer.nestedContainer(keyedBy: IntermediateCodingKeys.self)
expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.")
expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.")
- expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level unkeyed container's codingPath changed.")
- expectEqualPaths(thirdLevelContainerKeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.a, nil], "New third-level keyed container had unexpected codingPath.")
+ expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.")
+ expectEqualPaths(thirdLevelContainerKeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.b, _TestKey(index: 0)], "New third-level keyed container had unexpected codingPath.")
// Appending an unkeyed container should not change existing coding paths.
let thirdLevelContainerUnkeyed = secondLevelContainer.nestedUnkeyedContainer()
expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.")
expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.")
- expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level unkeyed container's codingPath changed.")
- expectEqualPaths(thirdLevelContainerUnkeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.a, nil], "New third-level unkeyed container had unexpected codingPath.")
+ expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.")
+ expectEqualPaths(thirdLevelContainerUnkeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.b, _TestKey(index: 1)], "New third-level unkeyed container had unexpected codingPath.")
}
}
}
diff --git a/TestFoundation/TestNSCharacterSet.swift b/TestFoundation/TestNSCharacterSet.swift
index 9371f59..c56da0d 100644
--- a/TestFoundation/TestNSCharacterSet.swift
+++ b/TestFoundation/TestNSCharacterSet.swift
@@ -1,4 +1,4 @@
- // This source file is part of the Swift.org open source project
+// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
@@ -7,8 +7,6 @@
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
-
-
#if DEPLOYMENT_RUNTIME_OBJC || os(Linux)
import Foundation
import XCTest
@@ -17,7 +15,47 @@
import SwiftXCTest
#endif
+private struct Box: Equatable {
+ private let ns: NSCharacterSet
+ private let swift: CharacterSet
+
+ private init(ns: NSCharacterSet, swift: CharacterSet) {
+ self.ns = ns
+ self.swift = swift
+ }
+
+ init(charactersIn string: String) {
+ self.ns = NSCharacterSet(charactersIn: string)
+ self.swift = CharacterSet(charactersIn: string)
+ }
+
+ static var alphanumerics: Box {
+ return Box(ns: NSCharacterSet.alphanumerics._bridgeToObjectiveC(),
+ swift: CharacterSet.alphanumerics)
+ }
+
+ static var decimalDigits: Box {
+ return Box(ns: NSCharacterSet.decimalDigits._bridgeToObjectiveC(),
+ swift: CharacterSet.decimalDigits)
+ }
+ // MARK: Equatable
+
+ static func ==(lhs: Box, rhs: Box) -> Bool {
+ return lhs.ns == rhs.ns
+ && lhs.swift == rhs.swift
+ && lhs.ns._bridgeToSwift() == rhs.ns._bridgeToSwift()
+ && lhs.swift._bridgeToObjectiveC() == rhs.swift._bridgeToObjectiveC()
+ && lhs.ns.isEqual(rhs.ns)
+ && lhs.ns.isEqual(rhs.swift)
+ && lhs.ns.isEqual(rhs.ns._bridgeToSwift())
+ && lhs.ns.isEqual(rhs.swift._bridgeToObjectiveC())
+ && lhs.swift._bridgeToObjectiveC().isEqual(rhs.ns)
+ && lhs.swift._bridgeToObjectiveC().isEqual(rhs.swift)
+ && lhs.swift._bridgeToObjectiveC().isEqual(rhs.ns._bridgeToSwift())
+ && lhs.swift._bridgeToObjectiveC().isEqual(rhs.swift._bridgeToObjectiveC())
+ }
+}
class TestNSCharacterSet : XCTestCase {
@@ -36,6 +74,7 @@
("test_AnnexPlanes", test_AnnexPlanes),
("test_Planes", test_Planes),
("test_InlineBuffer", test_InlineBuffer),
+ ("test_Equatable", test_Equatable),
// The following tests must remain disabled until SR-2509 is resolved.
// ("test_Subtracting", test_Subtracting),
// ("test_SubtractEmptySet", test_SubtractEmptySet),
@@ -282,5 +321,36 @@
let expected = CharacterSet(charactersIn: "abc")
XCTAssertEqual(expected, symmetricDifference)
}
-}
+
+ func test_Equatable() {
+ let equalPairs = [
+ ("", ""),
+ ("a", "a"),
+ ("abcde", "abcde"),
+ ("12345", "12345")
+ ]
+
+ /*
+ Tests disabled due to CoreFoundation bug?
+ These NSCharacterSet pairs are (wrongly?) evaluated to be equal. Same behaviour can be observed on macOS 10.12.
+ Interestingly, on iOS 11 Simulator, they are evaluted to be _not_ equal,
+ while on iOS 10.3.1 Simulator, they are evaluted to be equal.
+ */
+ let notEqualPairs = [
+ ("abc", "123"),
+// ("ab", "abc"),
+// ("abc", "")
+ ]
+
+ for pair in equalPairs {
+ XCTAssertEqual(Box(charactersIn: pair.0), Box(charactersIn: pair.1))
+ }
+ XCTAssertEqual(Box.alphanumerics, Box.alphanumerics)
+
+ for pair in notEqualPairs {
+ XCTAssertNotEqual(Box(charactersIn: pair.0), Box(charactersIn: pair.1))
+ }
+ XCTAssertNotEqual(Box.alphanumerics, Box.decimalDigits)
+ }
+}
diff --git a/TestFoundation/TestNSDecimal.swift b/TestFoundation/TestNSDecimal.swift
index 6599281..9cacc4e 100644
--- a/TestFoundation/TestNSDecimal.swift
+++ b/TestFoundation/TestNSDecimal.swift
@@ -185,7 +185,7 @@
_length: 0xff,
_isNegative: 3,
_isCompact: 4,
- _reserved: UInt32(1<<18 + 1<<17 + 1),
+ _reserved: 1<<18 + 1<<17 + 1,
_mantissa: (6, 7, 8, 9, 10, 11, 12, 13)
)
XCTAssertEqual(0x7f, explicit._exponent)
diff --git a/TestFoundation/TestNSError.swift b/TestFoundation/TestNSError.swift
index 43c9a47..8960aa6 100644
--- a/TestFoundation/TestNSError.swift
+++ b/TestFoundation/TestNSError.swift
@@ -16,6 +16,8 @@
import SwiftXCTest
#endif
+struct SwiftCustomNSError: Error, CustomNSError {
+}
class TestNSError : XCTestCase {
@@ -23,6 +25,11 @@
return [
("test_LocalizedError_errorDescription", test_LocalizedError_errorDescription),
("test_NSErrorAsError_localizedDescription", test_NSErrorAsError_localizedDescription),
+ ("test_CustomNSError_domain", test_CustomNSError_domain),
+ ("test_CustomNSError_userInfo", test_CustomNSError_userInfo),
+ ("test_CustomNSError_errorCode", test_CustomNSError_errorCode),
+ ("test_CustomNSError_errorCodeRawInt", test_CustomNSError_errorCodeRawInt),
+ ("test_CustomNSError_errorCodeRawUInt", test_CustomNSError_errorCodeRawUInt),
]
}
@@ -40,4 +47,45 @@
let error = nsError as Error
XCTAssertEqual(error.localizedDescription, "Localized!")
}
+
+ func test_CustomNSError_domain() {
+ XCTAssertEqual(SwiftCustomNSError.errorDomain, "TestFoundation.SwiftCustomNSError")
+ }
+
+ func test_CustomNSError_userInfo() {
+ let userInfo = SwiftCustomNSError().errorUserInfo
+ XCTAssertTrue(userInfo.isEmpty)
+ }
+
+ func test_CustomNSError_errorCode() {
+ enum SwiftError : Error, CustomNSError {
+ case zero
+ case one
+ case two
+ }
+
+ XCTAssertEqual(SwiftCustomNSError().errorCode, 1)
+
+ XCTAssertEqual(SwiftError.zero.errorCode, 0)
+ XCTAssertEqual(SwiftError.one.errorCode, 1)
+ XCTAssertEqual(SwiftError.two.errorCode, 2)
+ }
+
+ func test_CustomNSError_errorCodeRawInt() {
+ enum SwiftError : Int, Error, CustomNSError {
+ case minusOne = -1
+ case fortyTwo = 42
+ }
+
+ XCTAssertEqual(SwiftError.minusOne.errorCode, -1)
+ XCTAssertEqual(SwiftError.fortyTwo.errorCode, 42)
+ }
+
+ func test_CustomNSError_errorCodeRawUInt() {
+ enum SwiftError : UInt, Error, CustomNSError {
+ case fortyTwo = 42
+ }
+
+ XCTAssertEqual(SwiftError.fortyTwo.errorCode, 42)
+ }
}
diff --git a/TestFoundation/TestNSGeometry.swift b/TestFoundation/TestNSGeometry.swift
index edd9d37..9367431 100644
--- a/TestFoundation/TestNSGeometry.swift
+++ b/TestFoundation/TestNSGeometry.swift
@@ -27,8 +27,12 @@
("test_CGFloat_LessThanOrEqual", test_CGFloat_LessThanOrEqual),
("test_CGFloat_GreaterThanOrEqual", test_CGFloat_GreaterThanOrEqual),
("test_CGPoint_BasicConstruction", test_CGPoint_BasicConstruction),
+ ("test_CGPoint_ExtendedConstruction", test_CGPoint_ExtendedConstruction),
("test_CGSize_BasicConstruction", test_CGSize_BasicConstruction),
+ ("test_CGSize_ExtendedConstruction", test_CGSize_ExtendedConstruction),
("test_CGRect_BasicConstruction", test_CGRect_BasicConstruction),
+ ("test_CGRect_ExtendedConstruction", test_CGRect_ExtendedConstruction),
+ ("test_CGRect_SpecialValues", test_CGRect_SpecialValues),
("test_NSEdgeInsets_BasicConstruction", test_NSEdgeInsets_BasicConstruction),
("test_NSEdgeInsetsEqual", test_NSEdgeInsetsEqual),
("test_NSMakePoint", test_NSMakePoint),
@@ -112,6 +116,20 @@
XCTAssertEqual(p2.x, CGFloat(3.6))
XCTAssertEqual(p2.y, CGFloat(4.5))
}
+
+ func test_CGPoint_ExtendedConstruction() {
+ let p1 = CGPoint.zero
+ XCTAssertEqual(p1.x, CGFloat(0))
+ XCTAssertEqual(p1.y, CGFloat(0))
+
+ let p2 = CGPoint(x: Int(3), y: Int(4))
+ XCTAssertEqual(p2.x, CGFloat(3))
+ XCTAssertEqual(p2.y, CGFloat(4))
+
+ let p3 = CGPoint(x: Double(3.6), y: Double(4.5))
+ XCTAssertEqual(p3.x, CGFloat(3.6))
+ XCTAssertEqual(p3.y, CGFloat(4.5))
+ }
func test_CGSize_BasicConstruction() {
let s1 = CGSize()
@@ -122,6 +140,20 @@
XCTAssertEqual(s2.width, CGFloat(3.6))
XCTAssertEqual(s2.height, CGFloat(4.5))
}
+
+ func test_CGSize_ExtendedConstruction() {
+ let s1 = CGSize.zero
+ XCTAssertEqual(s1.width, CGFloat(0))
+ XCTAssertEqual(s1.height, CGFloat(0))
+
+ let s2 = CGSize(width: Int(3), height: Int(4))
+ XCTAssertEqual(s2.width, CGFloat(3))
+ XCTAssertEqual(s2.height, CGFloat(4))
+
+ let s3 = CGSize(width: Double(3.6), height: Double(4.5))
+ XCTAssertEqual(s3.width, CGFloat(3.6))
+ XCTAssertEqual(s3.height, CGFloat(4.5))
+ }
func test_CGRect_BasicConstruction() {
let r1 = CGRect()
@@ -138,6 +170,46 @@
XCTAssertEqual(r2.size.width, s.width)
XCTAssertEqual(r2.size.height, s.height)
}
+
+ func test_CGRect_ExtendedConstruction() {
+ let r1 = CGRect.zero
+ XCTAssertEqual(r1.origin.x, CGFloat(0.0))
+ XCTAssertEqual(r1.origin.y, CGFloat(0.0))
+ XCTAssertEqual(r1.size.width, CGFloat(0.0))
+ XCTAssertEqual(r1.size.height, CGFloat(0.0))
+
+ let r2 = CGRect(x: CGFloat(1.2), y: CGFloat(2.3), width: CGFloat(3.4), height: CGFloat(4.5))
+ XCTAssertEqual(r2.origin.x, CGFloat(1.2))
+ XCTAssertEqual(r2.origin.y, CGFloat(2.3))
+ XCTAssertEqual(r2.size.width, CGFloat(3.4))
+ XCTAssertEqual(r2.size.height, CGFloat(4.5))
+
+ let r3 = CGRect(x: Double(1.2), y: Double(2.3), width: Double(3.4), height: Double(4.5))
+ XCTAssertEqual(r3.origin.x, CGFloat(1.2))
+ XCTAssertEqual(r3.origin.y, CGFloat(2.3))
+ XCTAssertEqual(r3.size.width, CGFloat(3.4))
+ XCTAssertEqual(r3.size.height, CGFloat(4.5))
+
+ let r4 = CGRect(x: Int(1), y: Int(2), width: Int(3), height: Int(4))
+ XCTAssertEqual(r4.origin.x, CGFloat(1))
+ XCTAssertEqual(r4.origin.y, CGFloat(2))
+ XCTAssertEqual(r4.size.width, CGFloat(3))
+ XCTAssertEqual(r4.size.height, CGFloat(4))
+ }
+
+ func test_CGRect_SpecialValues() {
+ let r1 = CGRect.null
+ XCTAssertEqual(r1.origin.x, CGFloat.infinity)
+ XCTAssertEqual(r1.origin.y, CGFloat.infinity)
+ XCTAssertEqual(r1.size.width, CGFloat(0.0))
+ XCTAssertEqual(r1.size.height, CGFloat(0.0))
+
+ let r2 = CGRect.infinite
+ XCTAssertEqual(r2.origin.x, -CGFloat.greatestFiniteMagnitude / 2)
+ XCTAssertEqual(r2.origin.y, -CGFloat.greatestFiniteMagnitude / 2)
+ XCTAssertEqual(r2.size.width, CGFloat.greatestFiniteMagnitude)
+ XCTAssertEqual(r2.size.height, CGFloat.greatestFiniteMagnitude)
+ }
func test_NSEdgeInsets_BasicConstruction() {
let i1 = NSEdgeInsets()
diff --git a/TestFoundation/TestNSIndexPath.swift b/TestFoundation/TestNSIndexPath.swift
index 018875d..e260cdc 100644
--- a/TestFoundation/TestNSIndexPath.swift
+++ b/TestFoundation/TestNSIndexPath.swift
@@ -277,8 +277,10 @@
let ip2: IndexPath = [1, 1, 1]
XCTAssertNotEqual(ip1.hashValue, ip2.hashValue)
-
- IndexPath(indexes: [Int.max >> 8, 2, Int.max >> 36]).hashValue // this should not cause an overflow crash
+
+ // this should not cause an overflow crash
+ let hash: Int? = IndexPath(indexes: [Int.max >> 8, 2, Int.max >> 36]).hashValue
+ XCTAssertNotNil(hash)
}
func testEquality() {
diff --git a/TestFoundation/TestNSJSONSerialization.swift b/TestFoundation/TestNSJSONSerialization.swift
index 38dfe10..961026e 100644
--- a/TestFoundation/TestNSJSONSerialization.swift
+++ b/TestFoundation/TestNSJSONSerialization.swift
@@ -945,11 +945,12 @@
("test_booleanJSONObject", test_booleanJSONObject),
("test_serialize_dictionaryWithDecimal", test_serialize_dictionaryWithDecimal),
("test_serializeDecimalNumberJSONObject", test_serializeDecimalNumberJSONObject),
+ ("test_serializeSortedKeys", test_serializeSortedKeys),
]
}
- func trySerialize(_ obj: Any) throws -> String {
- let data = try JSONSerialization.data(withJSONObject: obj, options: [])
+ func trySerialize(_ obj: Any, options: JSONSerialization.WritingOptions = []) throws -> String {
+ let data = try JSONSerialization.data(withJSONObject: obj, options: options)
guard let string = String(data: data, encoding: .utf8) else {
XCTFail("Unable to create string")
return ""
@@ -1334,6 +1335,19 @@
} catch {
XCTFail("Failed during serialization")
}
+ }
+
+ func test_serializeSortedKeys() {
+ var dict: [String: Any]
+
+ dict = ["z": 1, "y": 1, "x": 1, "w": 1, "v": 1, "u": 1, "t": 1, "s": 1, "r": 1, "q": 1, ]
+ XCTAssertEqual(try trySerialize(dict, options: .sortedKeys), "{\"q\":1,\"r\":1,\"s\":1,\"t\":1,\"u\":1,\"v\":1,\"w\":1,\"x\":1,\"y\":1,\"z\":1}")
+
+ dict = ["aaaa": 1, "aaa": 1, "aa": 1, "a": 1]
+ XCTAssertEqual(try trySerialize(dict, options: .sortedKeys), "{\"a\":1,\"aa\":1,\"aaa\":1,\"aaaa\":1}")
+
+ dict = ["c": ["c":1,"b":1,"a":1],"b":["c":1,"b":1,"a":1],"a":["c":1,"b":1,"a":1]]
+ XCTAssertEqual(try trySerialize(dict, options: .sortedKeys), "{\"a\":{\"a\":1,\"b\":1,\"c\":1},\"b\":{\"a\":1,\"b\":1,\"c\":1},\"c\":{\"a\":1,\"b\":1,\"c\":1}}")
}
fileprivate func createTestFile(_ path: String,_contents: Data) -> String? {
diff --git a/TestFoundation/TestNSNotificationCenter.swift b/TestFoundation/TestNSNotificationCenter.swift
index 0e75761..cea4b47 100644
--- a/TestFoundation/TestNSNotificationCenter.swift
+++ b/TestFoundation/TestNSNotificationCenter.swift
@@ -26,6 +26,10 @@
("test_postMultipleNotifications", test_postMultipleNotifications),
("test_addObserverForNilName", test_addObserverForNilName),
("test_removeObserver", test_removeObserver),
+ ("test_observeOnPostingQueue", test_observeOnPostingQueue),
+ ("test_observeOnSpecificQueuePostFromMainQueue", test_observeOnSpecificQueuePostFromMainQueue),
+ ("test_observeOnSpecificQueuePostFromObservedQueue", test_observeOnSpecificQueuePostFromObservedQueue),
+ ("test_observeOnSpecificQueuePostFromUnrelatedQueue", test_observeOnSpecificQueuePostFromUnrelatedQueue),
]
}
@@ -149,5 +153,104 @@
notificationCenter.post(name: notificationName, object: nil)
XCTAssertTrue(flag)
}
-
+
+ func test_observeOnPostingQueue() {
+ let notificationCenter = NotificationCenter()
+ let name = Notification.Name(rawValue: "\(#function)_name")
+ let postingQueue = OperationQueue()
+ let expectation = self.expectation(description: "Observer was not notified.")
+
+ _ = notificationCenter.addObserver(forName: name, object: nil, queue: nil) { _ in
+ XCTAssertEqual(OperationQueue.current, postingQueue)
+ expectation.fulfill()
+ }
+
+ postingQueue.addOperation {
+ notificationCenter.post(name: name, object: nil)
+ }
+
+ self.waitForExpectations(timeout: 1)
+ }
+
+ func test_observeOnSpecificQueuePostFromMainQueue() {
+ let name = Notification.Name(rawValue: "\(#function)_name")
+ let notificationCenter = NotificationCenter()
+ let operationQueue = OperationQueue()
+ var flag1 = false
+ var flag2 = false
+
+ _ = notificationCenter.addObserver(forName: name, object: nil, queue: operationQueue) { _ in
+ XCTAssertEqual(OperationQueue.current, operationQueue)
+ flag1 = true
+ }
+
+ _ = notificationCenter.addObserver(forName: name, object: nil, queue: .main) { _ in
+ XCTAssertEqual(OperationQueue.current, .main)
+ flag2 = true
+ }
+
+ notificationCenter.post(name: name, object: nil)
+ // All observers should be notified synchronously regardless of the observer queue.
+ XCTAssertTrue(flag1)
+ XCTAssertTrue(flag2)
+ }
+
+ func test_observeOnSpecificQueuePostFromObservedQueue() {
+ let name = Notification.Name(rawValue: "\(#function)_name")
+ let notificationCenter = NotificationCenter()
+ let observingQueue = OperationQueue()
+ let expectation = self.expectation(description: "Notification posting operation was not executed.")
+ var flag1 = false
+ var flag2 = false
+
+ _ = notificationCenter.addObserver(forName: name, object: nil, queue: observingQueue) { _ in
+ XCTAssertEqual(OperationQueue.current, observingQueue)
+ flag1 = true
+ }
+
+ _ = notificationCenter.addObserver(forName: name, object: nil, queue: .main) { _ in
+ XCTAssertEqual(OperationQueue.current, .main)
+ flag2 = true
+ }
+
+ observingQueue.addOperation {
+ notificationCenter.post(name: name, object: nil)
+ // All observers should be notified synchronously regardless of the observer queue.
+ XCTAssertTrue(flag1)
+ XCTAssertTrue(flag2)
+ expectation.fulfill()
+ }
+
+ self.waitForExpectations(timeout: 1)
+ }
+
+ func test_observeOnSpecificQueuePostFromUnrelatedQueue() {
+ let name = Notification.Name(rawValue: "\(#function)_name")
+ let notificationCenter = NotificationCenter()
+ let operationQueue = OperationQueue()
+ let postingQueue = OperationQueue()
+ let expectation = self.expectation(description: "Notification posting operation was not executed.")
+ var flag1 = false
+ var flag2 = false
+
+ _ = notificationCenter.addObserver(forName: name, object: nil, queue: operationQueue) { _ in
+ XCTAssertEqual(OperationQueue.current, operationQueue)
+ flag1 = true
+ }
+
+ _ = notificationCenter.addObserver(forName: name, object: nil, queue: .main) { _ in
+ XCTAssertEqual(OperationQueue.current, .main)
+ flag2 = true
+ }
+
+ postingQueue.addOperation {
+ notificationCenter.post(name: name, object: nil)
+ // All observers should be notified synchronously regardless of the observer queue.
+ XCTAssertTrue(flag1)
+ XCTAssertTrue(flag2)
+ expectation.fulfill()
+ }
+
+ self.waitForExpectations(timeout: 1)
+ }
}
diff --git a/TestFoundation/TestNSNumber.swift b/TestFoundation/TestNSNumber.swift
index 62e16c9..d3ddde8 100644
--- a/TestFoundation/TestNSNumber.swift
+++ b/TestFoundation/TestNSNumber.swift
@@ -21,6 +21,7 @@
static var allTests: [(String, (TestNSNumber) -> () throws -> Void)] {
return [
("test_NumberWithBool", test_NumberWithBool ),
+ ("test_CFBoolean", test_CFBoolean ),
("test_numberWithChar", test_numberWithChar ),
("test_numberWithUnsignedChar", test_numberWithUnsignedChar ),
("test_numberWithShort", test_numberWithShort ),
@@ -41,6 +42,7 @@
("test_compareNumberWithDouble", test_compareNumberWithDouble ),
("test_description", test_description ),
("test_descriptionWithLocale", test_descriptionWithLocale ),
+ ("test_objCType", test_objCType ),
]
}
@@ -977,4 +979,44 @@
XCTAssertEqual(receivedDesc, expectedDesc, "expected \(expectedDesc) but received \(receivedDesc)")
}
}
+
+ func test_objCType() {
+ let objCType: (NSNumber) -> UnicodeScalar = { number in
+ return UnicodeScalar(UInt8(number.objCType.pointee))
+ }
+
+ XCTAssertEqual("c" /* 0x63 */, objCType(NSNumber(value: true)))
+
+ XCTAssertEqual("c" /* 0x63 */, objCType(NSNumber(value: Int8.max)))
+ XCTAssertEqual("s" /* 0x73 */, objCType(NSNumber(value: UInt8(Int8.max))))
+ XCTAssertEqual("s" /* 0x73 */, objCType(NSNumber(value: UInt8(Int8.max) + 1)))
+
+ XCTAssertEqual("s" /* 0x73 */, objCType(NSNumber(value: Int16.max)))
+ XCTAssertEqual("i" /* 0x69 */, objCType(NSNumber(value: UInt16(Int16.max))))
+ XCTAssertEqual("i" /* 0x69 */, objCType(NSNumber(value: UInt16(Int16.max) + 1)))
+
+ XCTAssertEqual("i" /* 0x69 */, objCType(NSNumber(value: Int32.max)))
+ XCTAssertEqual("q" /* 0x71 */, objCType(NSNumber(value: UInt32(Int32.max))))
+ XCTAssertEqual("q" /* 0x71 */, objCType(NSNumber(value: UInt32(Int32.max) + 1)))
+
+ XCTAssertEqual("q" /* 0x71 */, objCType(NSNumber(value: Int64.max)))
+ // When value is lower equal to `Int64.max`, it returns 'q' even if using `UInt64`
+ XCTAssertEqual("q" /* 0x71 */, objCType(NSNumber(value: UInt64(Int64.max))))
+ XCTAssertEqual("Q" /* 0x51 */, objCType(NSNumber(value: UInt64(Int64.max) + 1)))
+
+ // Depends on architectures
+ #if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le)
+ XCTAssertEqual("q" /* 0x71 */, objCType(NSNumber(value: Int.max)))
+ // When value is lower equal to `Int.max`, it returns 'q' even if using `UInt`
+ XCTAssertEqual("q" /* 0x71 */, objCType(NSNumber(value: UInt(Int.max))))
+ XCTAssertEqual("Q" /* 0x51 */, objCType(NSNumber(value: UInt(Int.max) + 1)))
+ #elseif arch(i386) || arch(arm)
+ XCTAssertEqual("i" /* 0x71 */, objCType(NSNumber(value: Int.max)))
+ XCTAssertEqual("q" /* 0x71 */, objCType(NSNumber(value: UInt(Int.max))))
+ XCTAssertEqual("q" /* 0x51 */, objCType(NSNumber(value: UInt(Int.max) + 1)))
+ #endif
+
+ XCTAssertEqual("f" /* 0x66 */, objCType(NSNumber(value: Float.greatestFiniteMagnitude)))
+ XCTAssertEqual("d" /* 0x64 */, objCType(NSNumber(value: Double.greatestFiniteMagnitude)))
+ }
}
diff --git a/TestFoundation/TestNSOperationQueue.swift b/TestFoundation/TestNSOperationQueue.swift
index 783115f..57d7835 100644
--- a/TestFoundation/TestNSOperationQueue.swift
+++ b/TestFoundation/TestNSOperationQueue.swift
@@ -23,7 +23,13 @@
return [
("test_OperationPriorities", test_OperationPriorities),
("test_OperationCount", test_OperationCount),
- ("test_AsyncOperation", test_AsyncOperation)
+ ("test_AsyncOperation", test_AsyncOperation),
+ ("test_isExecutingWorks", test_isExecutingWorks),
+ ("test_MainQueueGetter", test_MainQueueGetter),
+ ("test_CurrentQueueOnMainQueue", test_CurrentQueueOnMainQueue),
+ ("test_CurrentQueueOnBackgroundQueue", test_CurrentQueueOnBackgroundQueue),
+ ("test_CurrentQueueWithCustomUnderlyingQueue", test_CurrentQueueWithCustomUnderlyingQueue),
+ ("test_CurrentQueueWithUnderlyingQueueResetToNil", test_CurrentQueueWithUnderlyingQueueResetToNil),
]
}
@@ -68,6 +74,26 @@
XCTAssertEqual(msgOperations[3], "Operation4 executed")
}
+ func test_isExecutingWorks() {
+ class _OperationBox {
+ var operation: Operation?
+ init() {
+ self.operation = nil
+ }
+ }
+ let queue = OperationQueue()
+ let opBox = _OperationBox()
+ let op = BlockOperation(block: { XCTAssertEqual(true, opBox.operation?.isExecuting) })
+ opBox.operation = op
+ XCTAssertFalse(op.isExecuting)
+
+ queue.addOperation(op)
+ queue.waitUntilAllOperationsAreFinished()
+ XCTAssertFalse(op.isExecuting)
+
+ opBox.operation = nil /* break the reference cycle op -> <closure> -> opBox -> op */
+ }
+
func test_AsyncOperation() {
let operation = AsyncOperation()
XCTAssertFalse(operation.isExecuting)
@@ -82,6 +108,61 @@
XCTAssertFalse(operation.isExecuting)
XCTAssertTrue(operation.isFinished)
}
+
+ func test_MainQueueGetter() {
+ XCTAssertTrue(OperationQueue.main === OperationQueue.main)
+
+ /*
+ This call is only to check if OperationQueue.main returns a living instance.
+ There used to be a bug where subsequent OperationQueue.main call would return a "dangling pointer".
+ */
+ XCTAssertFalse(OperationQueue.main.isSuspended)
+ }
+
+ func test_CurrentQueueOnMainQueue() {
+ XCTAssertTrue(OperationQueue.main === OperationQueue.current)
+ }
+
+ func test_CurrentQueueOnBackgroundQueue() {
+ let expectation = self.expectation(description: "Background execution")
+
+ let operationQueue = OperationQueue()
+ operationQueue.addOperation {
+ XCTAssertEqual(operationQueue, OperationQueue.current)
+ expectation.fulfill()
+ }
+
+ waitForExpectations(timeout: 1)
+ }
+
+ func test_CurrentQueueWithCustomUnderlyingQueue() {
+ let expectation = self.expectation(description: "Background execution")
+
+ let operationQueue = OperationQueue()
+ operationQueue.underlyingQueue = DispatchQueue(label: "underlying_queue")
+
+ operationQueue.addOperation {
+ XCTAssertEqual(operationQueue, OperationQueue.current)
+ expectation.fulfill()
+ }
+
+ waitForExpectations(timeout: 1)
+ }
+
+ func test_CurrentQueueWithUnderlyingQueueResetToNil() {
+ let expectation = self.expectation(description: "Background execution")
+
+ let operationQueue = OperationQueue()
+ operationQueue.underlyingQueue = DispatchQueue(label: "underlying_queue")
+ operationQueue.underlyingQueue = nil
+
+ operationQueue.addOperation {
+ XCTAssertEqual(operationQueue, OperationQueue.current)
+ expectation.fulfill()
+ }
+
+ waitForExpectations(timeout: 1)
+ }
}
class AsyncOperation: Operation {
diff --git a/TestFoundation/TestNSPersonNameComponents.swift b/TestFoundation/TestNSPersonNameComponents.swift
index 78e75df..4f0bd45 100644
--- a/TestFoundation/TestNSPersonNameComponents.swift
+++ b/TestFoundation/TestNSPersonNameComponents.swift
@@ -17,13 +17,42 @@
import SwiftXCTest
#endif
+private func assertEqual(_ lhs:PersonNameComponents,
+ _ rhs: PersonNameComponents,
+ file: StaticString = #file,
+ line: UInt = #line) {
+ assert(equal: true, lhs, rhs, file: file, line: line)
+}
+private func assertNotEqual(_ lhs:PersonNameComponents,
+ _ rhs: PersonNameComponents,
+ file: StaticString = #file,
+ line: UInt = #line) {
+ assert(equal: false, lhs, rhs, file: file, line: line)
+}
+
+private func assert(equal: Bool,
+ _ lhs:PersonNameComponents,
+ _ rhs: PersonNameComponents,
+ file: StaticString = #file,
+ line: UInt = #line) {
+ if equal {
+ XCTAssertEqual(lhs, rhs, file: file, line: line)
+ XCTAssertEqual(lhs._bridgeToObjectiveC(), rhs._bridgeToObjectiveC(), file: file, line: line)
+ XCTAssertTrue(lhs._bridgeToObjectiveC().isEqual(rhs), file: file, line: line)
+ } else {
+ XCTAssertNotEqual(lhs, rhs, file: file, line: line)
+ XCTAssertNotEqual(lhs._bridgeToObjectiveC(), rhs._bridgeToObjectiveC(), file: file, line: line)
+ XCTAssertFalse(lhs._bridgeToObjectiveC().isEqual(rhs), file: file, line: line)
+ }
+}
class TestNSPersonNameComponents : XCTestCase {
static var allTests: [(String, (TestNSPersonNameComponents) -> () throws -> Void)] {
return [
("testCopy", testCopy),
+ ("testEquality", testEquality),
]
}
@@ -39,6 +68,78 @@
XCTAssertEqual(original.phoneticRepresentation!.givenName,copy.phoneticRepresentation!.givenName)
XCTAssertNil(copy.phoneticRepresentation!.phoneticRepresentation)
}
+
+ func testEquality() {
+ do {
+ let lhs = PersonNameComponents()
+ let rhs = PersonNameComponents()
+ assertEqual(lhs, rhs)
+ }
+
+ let lhs = self.makePersonNameComponentsWithTestValues()
+ do {
+ let rhs = self.makePersonNameComponentsWithTestValues()
+ assertEqual(lhs, rhs)
+ }
+ do {
+ var rhs = self.makePersonNameComponentsWithTestValues()
+ rhs.namePrefix = "differentValue"
+ assertNotEqual(lhs, rhs)
+ }
+ do {
+ var rhs = self.makePersonNameComponentsWithTestValues()
+ rhs.givenName = "differentValue"
+ assertNotEqual(lhs, rhs)
+ }
+ do {
+ var rhs = self.makePersonNameComponentsWithTestValues()
+ rhs.middleName = "differentValue"
+ assertNotEqual(lhs, rhs)
+ }
+ do {
+ var rhs = self.makePersonNameComponentsWithTestValues()
+ rhs.familyName = "differentValue"
+ assertNotEqual(lhs, rhs)
+ }
+ do {
+ var rhs = self.makePersonNameComponentsWithTestValues()
+ rhs.nameSuffix = "differentValue"
+ assertNotEqual(lhs, rhs)
+ }
+ do {
+ var rhs = self.makePersonNameComponentsWithTestValues()
+ rhs.nickname = "differentValue"
+ assertNotEqual(lhs, rhs)
+ }
+ do {
+ var rhs = self.makePersonNameComponentsWithTestValues()
+ rhs.phoneticRepresentation?.namePrefix = "differentValue"
+ assertNotEqual(lhs, rhs)
+ }
+ }
+
+ // MARK: - Helpers
+
+ private func makePersonNameComponentsWithTestValues() -> PersonNameComponents {
+ var components = PersonNameComponents()
+ components.namePrefix = "namePrefix"
+ components.givenName = "givenName"
+ components.middleName = "middleName"
+ components.familyName = "familyName"
+ components.nameSuffix = "nameSuffix"
+ components.nickname = "nickname"
+ components.phoneticRepresentation = {
+ var components = PersonNameComponents()
+ components.namePrefix = "phonetic_namePrefix"
+ components.givenName = "phonetic_givenName"
+ components.middleName = "phonetic_middleName"
+ components.familyName = "phonetic_familyName"
+ components.nameSuffix = "phonetic_nameSuffix"
+ components.nickname = "phonetic_nickname"
+ return components
+ }()
+ return components
+ }
}
diff --git a/TestFoundation/TestNSPredicate.swift b/TestFoundation/TestNSPredicate.swift
index 641d605..4e88e33 100644
--- a/TestFoundation/TestNSPredicate.swift
+++ b/TestFoundation/TestNSPredicate.swift
@@ -101,6 +101,9 @@
let predicateA = NSPredicate(value: true)
let predicateB = NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: predicateA)) as! NSPredicate
XCTAssertEqual(predicateA, predicateB, "Archived then unarchived uuid must be equal.")
+ let predicateC = NSPredicate(value: false)
+ let predicateD = NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: predicateC)) as! NSPredicate
+ XCTAssertEqual(predicateC, predicateD, "Archived then unarchived uuid must be equal.")
}
func test_copy() {
diff --git a/TestFoundation/TestNSString.swift b/TestFoundation/TestNSString.swift
index 58a1c8a..85dac2a 100644
--- a/TestFoundation/TestNSString.swift
+++ b/TestFoundation/TestNSString.swift
@@ -1134,6 +1134,10 @@
let s5 = NSString(string: "\r\ncats😺")
XCTAssertEqual(s5.substring(with: NSMakeRange(1,6)), "\ncats�")
+
+ // SR-3363
+ let s6 = NSString(string: "Beyonce\u{301} and Tay")
+ XCTAssertEqual(s6.substring(with: NSMakeRange(7, 9)), "\u{301} and Tay")
}
}
diff --git a/TestFoundation/TestNSURLProtocol.swift b/TestFoundation/TestNSURLProtocol.swift
new file mode 100644
index 0000000..4a49798
--- /dev/null
+++ b/TestFoundation/TestNSURLProtocol.swift
@@ -0,0 +1,165 @@
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See http://swift.org/LICENSE.txt for license information
+// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+#if DEPLOYMENT_RUNTIME_OBJC || os(Linux)
+import Foundation
+import XCTest
+#else
+import SwiftFoundation
+import SwiftXCTest
+#endif
+
+class TestNSURLProtocol : LoopbackServerTest {
+
+ static var allTests: [(String, (TestNSURLProtocol) -> () throws -> Void)] {
+ return [
+ ("test_interceptResponse", test_interceptResponse),
+ ("test_interceptRequest", test_interceptRequest),
+ ("test_multipleCustomProtocols", test_multipleCustomProtocols),
+ ("test_customProtocolResponseWithDelegate", test_customProtocolResponseWithDelegate),
+ ("test_customProtocolSetDataInResponseWithDelegate", test_customProtocolSetDataInResponseWithDelegate),
+ ]
+ }
+
+ func test_interceptResponse() {
+ let urlString = "http://127.0.0.1:\(TestNSURLProtocol.serverPort)/USA"
+ let url = URL(string: urlString)!
+ let config = URLSessionConfiguration.default
+ config.protocolClasses = [CustomProtocol.self]
+ config.timeoutIntervalForRequest = 8
+ let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
+ let expect = expectation(description: "GET \(urlString): with a custom protocol")
+ let task = session.dataTask(with: url) { data, response, error in
+ defer { expect.fulfill() }
+ if let e = error as? URLError {
+ XCTAssertEqual(e.code, .timedOut, "Unexpected error code")
+ return
+ }
+ let httpResponse = response as! HTTPURLResponse?
+ XCTAssertEqual(429, httpResponse!.statusCode, "HTTP response code is not 429")
+ }
+ task.resume()
+ waitForExpectations(timeout: 12)
+ }
+
+ func test_interceptRequest() {
+ let urlString = "ssh://127.0.0.1:\(TestNSURLProtocol.serverPort)/USA"
+ let url = URL(string: urlString)!
+ let config = URLSessionConfiguration.default
+ config.protocolClasses = [InterceptableRequest.self]
+ config.timeoutIntervalForRequest = 8
+ let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
+ let expect = expectation(description: "GET \(urlString): with a custom protocol")
+ let task = session.dataTask(with: url) { data, response, error in
+ defer { expect.fulfill() }
+ if let e = error as? URLError {
+ XCTAssertEqual(e.code, .timedOut, "Unexpected error code")
+ return
+ }
+ let httpResponse = response as! HTTPURLResponse?
+ let responseURL = URL(string: "http://google.com")
+ XCTAssertEqual(responseURL, httpResponse?.url, "Unexpected url")
+ XCTAssertEqual(200, httpResponse!.statusCode, "HTTP response code is not 200")
+ }
+ task.resume()
+ waitForExpectations(timeout: 12)
+ }
+
+ func test_multipleCustomProtocols() {
+ let urlString = "http://127.0.0.1:\(TestNSURLProtocol.serverPort)/Nepal"
+ let url = URL(string: urlString)!
+ let config = URLSessionConfiguration.default
+ config.protocolClasses = [InterceptableRequest.self, CustomProtocol.self]
+ let expect = expectation(description: "GET \(urlString): with a custom protocol")
+ let session = URLSession(configuration: config)
+ let task = session.dataTask(with: url) { data, response, error in
+ defer { expect.fulfill() }
+ if let e = error as? URLError {
+ XCTAssertEqual(e.code, .timedOut, "Unexpected error code")
+ return
+ }
+ let httpResponse = response as! HTTPURLResponse
+ print(httpResponse.statusCode)
+ XCTAssertEqual(429, httpResponse.statusCode, "Status code is not 429")
+ }
+ task.resume()
+ waitForExpectations(timeout: 12)
+ }
+
+ func test_customProtocolResponseWithDelegate() {
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Peru"
+ let url = URL(string: urlString)!
+ let d = DataTask(with: expectation(description: "GET \(urlString): with a custom protocol and delegate"), protocolClasses: [CustomProtocol.self])
+ d.responseReceivedExpectation = expectation(description: "GET \(urlString): response received")
+ d.run(with: url)
+ waitForExpectations(timeout: 12)
+ }
+
+ func test_customProtocolSetDataInResponseWithDelegate() {
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Nepal"
+ let url = URL(string: urlString)!
+ let d = DataTask(with: expectation(description: "GET \(urlString): with a custom protocol and delegate"), protocolClasses: [CustomProtocol.self])
+ d.run(with: url)
+ waitForExpectations(timeout: 12)
+ if !d.error {
+ XCTAssertEqual(d.capital, "Kathmandu", "test_dataTaskWithURLRequest returned an unexpected result")
+ }
+ }
+}
+
+class InterceptableRequest : URLProtocol {
+
+ override class func canInit(with request: URLRequest) -> Bool {
+ return request.url?.scheme == "ssh"
+ }
+
+ override class func canonicalRequest(for request: URLRequest) -> URLRequest {
+ return request
+ }
+
+ override func startLoading() {
+ let urlString = "http://google.com"
+ let url = URL(string: urlString)!
+ let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: [:])
+ self.client?.urlProtocol(self, didReceive: response!, cacheStoragePolicy: .notAllowed)
+ self.client?.urlProtocolDidFinishLoading(self)
+
+ }
+
+ override func stopLoading() {
+ return
+ }
+}
+
+class CustomProtocol : URLProtocol {
+
+ override class func canInit(with request: URLRequest) -> Bool {
+ return true
+ }
+
+ func sendResponse(statusCode: Int, headers: [String: String] = [:], data: Data) {
+ let response = HTTPURLResponse(url: self.request.url!, statusCode: statusCode, httpVersion: "HTTP/1.1", headerFields: headers)
+ let capital = "Kathmandu"
+ let data = capital.data(using: String.Encoding.utf8)
+ self.client?.urlProtocol(self, didReceive: response!, cacheStoragePolicy: .notAllowed)
+ self.client?.urlProtocol(self, didLoad: data!)
+ self.client?.urlProtocolDidFinishLoading(self)
+ }
+
+ override class func canonicalRequest(for request: URLRequest) -> URLRequest {
+ return request
+ }
+
+ override func startLoading() {
+ sendResponse(statusCode: 429, data: Data())
+ }
+
+ override func stopLoading() {
+ return
+ }
+}
diff --git a/TestFoundation/TestNSURLSession.swift b/TestFoundation/TestNSURLSession.swift
index 285ddd4..cf44ab6 100644
--- a/TestFoundation/TestNSURLSession.swift
+++ b/TestFoundation/TestNSURLSession.swift
@@ -15,9 +15,7 @@
import SwiftXCTest
#endif
-class TestURLSession : XCTestCase {
-
- static var serverPort: Int = -1
+class TestURLSession : LoopbackServerTest {
static var allTests: [(String, (TestURLSession) -> () throws -> Void)] {
return [
@@ -37,49 +35,21 @@
("test_verifyRequestHeaders", test_verifyRequestHeaders),
("test_verifyHttpAdditionalHeaders", test_verifyHttpAdditionalHeaders),
("test_timeoutInterval", test_timeoutInterval),
- ("test_customProtocol", test_customProtocol),
- ("test_customProtocolResponseWithDelegate", test_customProtocolResponseWithDelegate),
("test_httpRedirection", test_httpRedirection),
("test_httpRedirectionTimeout", test_httpRedirectionTimeout),
+ ("test_http0_9SimpleResponses", test_http0_9SimpleResponses),
+ ("test_outOfRangeButCorrectlyFormattedHTTPCode", test_outOfRangeButCorrectlyFormattedHTTPCode),
+ ("test_missingContentLengthButStillABody", test_missingContentLengthButStillABody),
+ ("test_illegalHTTPServerResponses", test_illegalHTTPServerResponses),
+ ("test_dataTaskWithSharedDelegate", test_dataTaskWithSharedDelegate),
+ ("test_simpleUploadWithDelegate", test_simpleUploadWithDelegate),
]
}
- override class func setUp() {
- super.setUp()
- func runServer(with condition: ServerSemaphore, startDelay: TimeInterval? = nil, sendDelay: TimeInterval? = nil, bodyChunks: Int? = nil) throws {
- let start = 21961
- for port in start...(start+100) { //we must find at least one port to bind
- do {
- serverPort = port
- let test = try TestURLSessionServer(port: UInt16(port), startDelay: startDelay, sendDelay: sendDelay, bodyChunks: bodyChunks)
- try test.start(started: condition)
- try test.readAndRespond()
- test.stop()
- } catch let e as ServerError {
- if e.operation == "bind" { continue }
- throw e
- }
- }
- }
-
- let serverReady = ServerSemaphore()
- globalDispatchQueue.async {
- do {
- try runServer(with: serverReady)
-
- } catch {
- XCTAssertTrue(true)
- return
- }
- }
-
- serverReady.wait()
- }
-
func test_dataTaskWithURL() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Nepal"
let url = URL(string: urlString)!
- let d = DataTask(with: expectation(description: "data task"))
+ let d = DataTask(with: expectation(description: "GET \(urlString): with a delegate"))
d.run(with: url)
waitForExpectations(timeout: 12)
if !d.error {
@@ -93,7 +63,7 @@
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
- let expect = expectation(description: "URL test with completion handler")
+ let expect = expectation(description: "GET \(urlString): with a completion handler")
var expectedResult = "unknown"
let task = session.dataTask(with: url) { data, response, error in
defer { expect.fulfill() }
@@ -112,7 +82,7 @@
func test_dataTaskWithURLRequest() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Peru"
let urlRequest = URLRequest(url: URL(string: urlString)!)
- let d = DataTask(with: expectation(description: "data task"))
+ let d = DataTask(with: expectation(description: "GET \(urlString): with a delegate"))
d.run(with: urlRequest)
waitForExpectations(timeout: 12)
if !d.error {
@@ -126,7 +96,7 @@
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
- let expect = expectation(description: "URL test with completion handler")
+ let expect = expectation(description: "GET \(urlString): with a completion handler")
var expectedResult = "unknown"
let task = session.dataTask(with: urlRequest) { data, response, error in
defer { expect.fulfill() }
@@ -145,7 +115,7 @@
func test_downloadTaskWithURL() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt"
let url = URL(string: urlString)!
- let d = DownloadTask(with: expectation(description: "download task with delegate"))
+ let d = DownloadTask(with: expectation(description: "Download GET \(urlString): with a delegate"))
d.run(with: url)
waitForExpectations(timeout: 12)
}
@@ -153,7 +123,7 @@
func test_downloadTaskWithURLRequest() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt"
let urlRequest = URLRequest(url: URL(string: urlString)!)
- let d = DownloadTask(with: expectation(description: "download task with delegate"))
+ let d = DownloadTask(with: expectation(description: "Download GET \(urlString): with a delegate"))
d.run(with: urlRequest)
waitForExpectations(timeout: 12)
}
@@ -161,9 +131,10 @@
func test_downloadTaskWithRequestAndHandler() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
- let expect = expectation(description: "download task with handler")
- let req = URLRequest(url: URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt")!)
+ let expect = expectation(description: "Download GET \(urlString): with a completion handler")
+ let req = URLRequest(url: URL(string: urlString)!)
let task = session.downloadTask(with: req) { (_, _, error) -> Void in
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
expect.fulfill()
@@ -175,9 +146,10 @@
func test_downloadTaskWithURLAndHandler() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
- let expect = expectation(description: "download task with handler")
- let req = URLRequest(url: URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt")!)
+ let expect = expectation(description: "Download GET \(urlString): with a completion handler")
+ let req = URLRequest(url: URL(string: urlString)!)
let task = session.downloadTask(with: req) { (_, _, error) -> Void in
if let e = error as? URLError {
XCTAssertEqual(e.code, .timedOut, "Unexpected error code")
@@ -189,12 +161,13 @@
}
func test_finishTasksAndInvalidate() {
- let invalidateExpectation = expectation(description: "URLSession wasn't invalidated")
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Nepal"
+ let invalidateExpectation = expectation(description: "Session invalidation")
let delegate = SessionDelegate(invalidateExpectation: invalidateExpectation)
- let url = URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/Nepal")!
+ let url = URL(string: urlString)!
let session = URLSession(configuration: URLSessionConfiguration.default,
delegate: delegate, delegateQueue: nil)
- let completionExpectation = expectation(description: "dataTask completion block wasn't called")
+ let completionExpectation = expectation(description: "GET \(urlString): task completion before session invalidation")
let task = session.dataTask(with: url) { (_, _, _) in
completionExpectation.fulfill()
}
@@ -204,11 +177,12 @@
}
func test_taskError() {
- let url = URL(string: "http://127.0.0.1:-1/Nepal")!
+ let urlString = "http://127.0.0.1:-1/Nepal"
+ let url = URL(string: urlString)!
let session = URLSession(configuration: URLSessionConfiguration.default,
delegate: nil,
delegateQueue: nil)
- let completionExpectation = expectation(description: "dataTask completion block wasn't called")
+ let completionExpectation = expectation(description: "GET \(urlString): Bad URL error")
let task = session.dataTask(with: url) { (_, _, result) in
let error = result as? URLError
XCTAssertNotNil(error)
@@ -237,9 +211,10 @@
}
func test_cancelTask() {
- let url = URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/Peru")!
- let d = DataTask(with: expectation(description: "Task to be canceled"))
- d.cancelExpectation = expectation(description: "URLSessionTask wasn't canceled")
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Peru"
+ let url = URL(string: urlString)!
+ let d = DataTask(with: expectation(description: "GET \(urlString): task cancelation"))
+ d.cancelExpectation = expectation(description: "GET \(urlString): task canceled")
d.run(with: url)
d.cancel()
waitForExpectations(timeout: 12)
@@ -248,9 +223,10 @@
func test_verifyRequestHeaders() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 5
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/requestHeaders"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
- var expect = expectation(description: "download task with handler")
- var req = URLRequest(url: URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/requestHeaders")!)
+ var expect = expectation(description: "POST \(urlString): get request headers")
+ var req = URLRequest(url: URL(string: urlString)!)
let headers = ["header1": "value1"]
req.httpMethod = "POST"
req.allHTTPHeaderFields = headers
@@ -274,9 +250,10 @@
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 5
config.httpAdditionalHeaders = ["header2": "svalue2", "header3": "svalue3"]
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/requestHeaders"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
- var expect = expectation(description: "download task with handler")
- var req = URLRequest(url: URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/requestHeaders")!)
+ var expect = expectation(description: "POST \(urlString) with additional headers")
+ var req = URLRequest(url: URL(string: urlString)!)
let headers = ["header1": "rvalue1", "header2": "rvalue2"]
req.httpMethod = "POST"
req.allHTTPHeaderFields = headers
@@ -297,9 +274,10 @@
func test_taskTimeout() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 5
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Peru"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
- var expect = expectation(description: "download task with handler")
- let req = URLRequest(url: URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/Peru")!)
+ var expect = expectation(description: "GET \(urlString): no timeout")
+ let req = URLRequest(url: URL(string: urlString)!)
var task = session.dataTask(with: req) { (data, _, error) -> Void in
defer { expect.fulfill() }
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
@@ -312,8 +290,9 @@
func test_timeoutInterval() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 10
+ let urlString = "http://127.0.0.1:-1/Peru"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
- var expect = expectation(description: "download task with handler")
+ var expect = expectation(description: "GET \(urlString): will timeout")
var req = URLRequest(url: URL(string: "http://127.0.0.1:-1/Peru")!)
req.timeoutInterval = 1
var task = session.dataTask(with: req) { (data, _, error) -> Void in
@@ -325,48 +304,20 @@
waitForExpectations(timeout: 30)
}
- func test_customProtocol () {
- let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/USA"
- let url = URL(string: urlString)!
- let config = URLSessionConfiguration.default
- config.protocolClasses = [CustomProtocol.self]
- config.timeoutIntervalForRequest = 8
- let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
- let expect = expectation(description: "URL test with custom protocol")
- let task = session.dataTask(with: url) { data, response, error in
- defer { expect.fulfill() }
- if let e = error as? URLError {
- XCTAssertEqual(e.code, .timedOut, "Unexpected error code")
- return
- }
- let httpResponse = response as! HTTPURLResponse?
- XCTAssertEqual(429, httpResponse!.statusCode, "HTTP response code is not 429")
- }
- task.resume()
- waitForExpectations(timeout: 12)
- }
-
- func test_customProtocolResponseWithDelegate() {
- let url = URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/Peru")!
- let d = DataTask(with: expectation(description: "Custom protocol with delegate"), protocolClasses: [CustomProtocol.self])
- d.responseReceivedExpectation = expectation(description: "A response wasn't received")
- d.run(with: url)
- waitForExpectations(timeout: 12)
- }
-
func test_httpRedirection() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/UnitedStates"
let url = URL(string: urlString)!
- let d = HTTPRedirectionDataTask(with: expectation(description: "data task"))
+ let d = HTTPRedirectionDataTask(with: expectation(description: "GET \(urlString): with HTTP redirection"))
d.run(with: url)
waitForExpectations(timeout: 12)
}
func test_httpRedirectionTimeout() {
- var req = URLRequest(url: URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/UnitedStates")!)
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/UnitedStates"
+ var req = URLRequest(url: URL(string: urlString)!)
req.timeoutInterval = 3
let config = URLSessionConfiguration.default
- var expect = expectation(description: "download task with handler")
+ var expect = expectation(description: "GET \(urlString): timeout with redirection ")
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
let task = session.dataTask(with: req) { data, response, error in
defer { expect.fulfill() }
@@ -378,8 +329,154 @@
task.resume()
waitForExpectations(timeout: 12)
}
+
+ func test_http0_9SimpleResponses() {
+ for brokenCity in ["Pompeii", "Sodom"] {
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/LandOfTheLostCities/\(brokenCity)"
+ let url = URL(string: urlString)!
+
+ let config = URLSessionConfiguration.default
+ config.timeoutIntervalForRequest = 8
+ let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
+ let expect = expectation(description: "GET \(urlString): simple HTTP/0.9 response")
+ var expectedResult = "unknown"
+ let task = session.dataTask(with: url) { data, response, error in
+ XCTAssertNotNil(data)
+ XCTAssertNotNil(response)
+ XCTAssertNil(error)
+
+ defer { expect.fulfill() }
+
+ guard let httpResponse = response as? HTTPURLResponse else {
+ XCTFail("response (\(response.debugDescription)) invalid")
+ return
+ }
+ XCTAssertEqual(200, httpResponse.statusCode, "HTTP response code is not 200")
+ }
+ task.resume()
+ waitForExpectations(timeout: 12)
+ }
+ }
+
+ func test_outOfRangeButCorrectlyFormattedHTTPCode() {
+ let brokenCity = "Kameiros"
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/LandOfTheLostCities/\(brokenCity)"
+ let url = URL(string: urlString)!
+
+ let config = URLSessionConfiguration.default
+ config.timeoutIntervalForRequest = 8
+ let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
+ let expect = expectation(description: "GET \(urlString): out of range HTTP code")
+ let task = session.dataTask(with: url) { data, response, error in
+ XCTAssertNotNil(data)
+ XCTAssertNotNil(response)
+ XCTAssertNil(error)
+
+ defer { expect.fulfill() }
+
+ guard let httpResponse = response as? HTTPURLResponse else {
+ XCTFail("response (\(response.debugDescription)) invalid")
+ return
+ }
+ XCTAssertEqual(999, httpResponse.statusCode, "HTTP response code is not 999")
+ }
+ task.resume()
+ waitForExpectations(timeout: 12)
+ }
+
+ func test_missingContentLengthButStillABody() {
+ let brokenCity = "Myndus"
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/LandOfTheLostCities/\(brokenCity)"
+ let url = URL(string: urlString)!
+
+ let config = URLSessionConfiguration.default
+ config.timeoutIntervalForRequest = 8
+ let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
+ let expect = expectation(description: "GET \(urlString): missing content length")
+ let task = session.dataTask(with: url) { data, response, error in
+ XCTAssertNotNil(data)
+ XCTAssertNotNil(response)
+ XCTAssertNil(error)
+
+ defer { expect.fulfill() }
+
+ guard let httpResponse = response as? HTTPURLResponse else {
+ XCTFail("response (\(response.debugDescription)) invalid")
+ return
+ }
+ XCTAssertEqual(200, httpResponse.statusCode, "HTTP response code is not 200")
+ }
+ task.resume()
+ waitForExpectations(timeout: 12)
+ }
+
+
+ func test_illegalHTTPServerResponses() {
+ for brokenCity in ["Gomorrah", "Dinavar", "Kuhikugu"] {
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/LandOfTheLostCities/\(brokenCity)"
+ let url = URL(string: urlString)!
+
+ let config = URLSessionConfiguration.default
+ config.timeoutIntervalForRequest = 8
+ let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
+ let expect = expectation(description: "GET \(urlString): illegal response")
+ let task = session.dataTask(with: url) { data, response, error in
+ XCTAssertNil(data)
+ XCTAssertNil(response)
+ XCTAssertNotNil(error)
+
+ defer { expect.fulfill() }
+ }
+ task.resume()
+ waitForExpectations(timeout: 12)
+ }
+ }
+
+ func test_dataTaskWithSharedDelegate() {
+ let sharedDelegate = SharedDelegate()
+ let urlString0 = "http://127.0.0.1:\(TestURLSession.serverPort)/Nepal"
+ let session = URLSession(configuration: .default, delegate: sharedDelegate, delegateQueue: nil)
+
+ let dataRequest = URLRequest(url: URL(string: urlString0)!)
+ let dataTask = session.dataTask(with: dataRequest)
+
+ sharedDelegate.dataCompletionExpectation = expectation(description: "GET \(urlString0)")
+ dataTask.resume()
+ waitForExpectations(timeout: 20)
+ }
+
+ func test_simpleUploadWithDelegate() {
+ let delegate = HTTPUploadDelegate()
+ let session = URLSession(configuration: .default, delegate: delegate, delegateQueue: nil)
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/upload"
+ var request = URLRequest(url: URL(string: urlString)!)
+ request.httpMethod = "PUT"
+
+ delegate.uploadCompletedExpectation = expectation(description: "PUT \(urlString): Upload data")
+
+ let fileData = Data(count: 16*1024)
+ let task = session.uploadTask(with: request, from: fileData)
+ task.resume()
+ waitForExpectations(timeout: 20)
+ }
}
+class SharedDelegate: NSObject {
+ var dataCompletionExpectation: XCTestExpectation!
+}
+
+extension SharedDelegate: URLSessionDataDelegate {
+ func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
+ dataCompletionExpectation.fulfill()
+ }
+}
+
+extension SharedDelegate: URLSessionDownloadDelegate {
+ func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
+ }
+}
+
+
class SessionDelegate: NSObject, URLSessionDelegate {
let invalidateExpectation: XCTestExpectation
init(invalidateExpectation: XCTestExpectation){
@@ -511,31 +608,6 @@
}
}
-class CustomProtocol : URLProtocol {
-
- override class func canInit(with request: URLRequest) -> Bool {
- return true
- }
-
- func sendResponse(statusCode: Int, headers: [String: String] = [:], data: Data) {
- let response = HTTPURLResponse(url: self.request.url!, statusCode: statusCode, httpVersion: "HTTP/1.1", headerFields: headers)
- self.client?.urlProtocol(self, didReceive: response!, cacheStoragePolicy: .notAllowed)
- self.client?.urlProtocolDidFinishLoading(self)
- }
-
- override class func canonicalRequest(for request: URLRequest) -> URLRequest {
- return request
- }
-
- override func startLoading() {
- sendResponse(statusCode: 429, data: Data())
- }
-
- override func stopLoading() {
- return
- }
-}
-
class HTTPRedirectionDataTask : NSObject {
let dataTaskExpectation: XCTestExpectation!
var session: URLSession! = nil
@@ -593,3 +665,21 @@
completionHandler(request)
}
}
+
+class HTTPUploadDelegate: NSObject {
+ var uploadCompletedExpectation: XCTestExpectation!
+ var totalBytesSent: Int64 = 0
+}
+
+extension HTTPUploadDelegate: URLSessionTaskDelegate {
+ func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
+ self.totalBytesSent = totalBytesSent
+ }
+}
+
+extension HTTPUploadDelegate: URLSessionDataDelegate {
+ func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
+ XCTAssertEqual(self.totalBytesSent, 16*1024)
+ uploadCompletedExpectation.fulfill()
+ }
+}
diff --git a/TestFoundation/TestNSXMLDocument.swift b/TestFoundation/TestNSXMLDocument.swift
index 5ebe9ee..728cc6c 100644
--- a/TestFoundation/TestNSXMLDocument.swift
+++ b/TestFoundation/TestNSXMLDocument.swift
@@ -41,7 +41,11 @@
("test_dtd", test_dtd),
("test_documentWithDTD", test_documentWithDTD),
("test_dtd_attributes", test_dtd_attributes),
- ("test_documentWithEncodingSetDoesntCrash", test_documentWithEncodingSetDoesntCrash)
+ ("test_documentWithEncodingSetDoesntCrash", test_documentWithEncodingSetDoesntCrash),
+ ("test_nodeFindingWithNamespaces", test_nodeFindingWithNamespaces),
+ ("test_createElement", test_createElement),
+ ("test_addNamespace", test_addNamespace),
+ ("test_removeNamespace", test_removeNamespace),
]
#else // On Linux, currently the tests that rely on NSError are segfaulting in swift_dynamicCast
return [
@@ -62,7 +66,11 @@
("test_dtd", test_dtd),
// ("test_documentWithDTD", test_documentWithDTD),
("test_dtd_attributes", test_dtd_attributes),
- ("test_documentWithEncodingSetDoesntCrash", test_documentWithEncodingSetDoesntCrash)
+ ("test_documentWithEncodingSetDoesntCrash", test_documentWithEncodingSetDoesntCrash),
+ ("test_nodeFindingWithNamespaces", test_nodeFindingWithNamespaces),
+ ("test_createElement", test_createElement),
+ ("test_addNamespace", test_addNamespace),
+ ("test_removeNamespace", test_removeNamespace),
]
#endif
}
@@ -79,6 +87,26 @@
let element = doc.rootElement()!
XCTAssert(element === node)
}
+
+ func test_createElement() throws {
+ let element = try XMLElement(xmlString: "<D:propfind xmlns:D=\"DAV:\"><D:prop></D:prop></D:propfind>")
+ XCTAssert(element.name! == "D:propfind")
+ XCTAssert(element.rootDocument == nil)
+ if let namespace = element.namespaces?.first {
+ XCTAssert(namespace.prefix == "D")
+ XCTAssert(namespace.stringValue == "DAV:")
+ } else {
+ XCTFail("Namespace was not parsed correctly")
+ }
+
+ if let child = element.elements(forName: "D:prop").first {
+ XCTAssert(child.localName == "prop")
+ XCTAssert(child.prefix == "D")
+ XCTAssert(child.name == "D:prop")
+ } else {
+ XCTFail("Child element was not parsed correctly!")
+ }
+ }
func test_nextPreviousNode() {
let doc = XMLDocument(rootElement: nil)
@@ -93,18 +121,18 @@
fooNode.addChild(bazNode)
node.addChild(barNode)
- XCTAssert(doc.nextNode === node)
- XCTAssert(doc.nextNode?.nextNode === fooNode)
- XCTAssert(doc.nextNode?.nextNode?.nextNode === bazNode)
- XCTAssert(doc.nextNode?.nextNode?.nextNode?.nextNode === barNode)
+ XCTAssert(doc.next === node)
+ XCTAssert(doc.next?.next === fooNode)
+ XCTAssert(doc.next?.next?.next === bazNode)
+ XCTAssert(doc.next?.next?.next?.next === barNode)
- XCTAssert(barNode.previousNode === bazNode)
- XCTAssert(barNode.previousNode?.previousNode === fooNode)
- XCTAssert(barNode.previousNode?.previousNode?.previousNode === node)
- XCTAssert(barNode.previousNode?.previousNode?.previousNode?.previousNode === doc)
+ XCTAssert(barNode.previous === bazNode)
+ XCTAssert(barNode.previous?.previous === fooNode)
+ XCTAssert(barNode.previous?.previous?.previous === node)
+ XCTAssert(barNode.previous?.previous?.previous?.previous === doc)
}
- func test_xpath() {
+ func test_xpath() throws {
let doc = XMLDocument(rootElement: nil)
let foo = XMLElement(name: "foo")
let bar1 = XMLElement(name: "bar")
@@ -117,20 +145,40 @@
foo.addChild(bar2)
foo.addChild(bar3)
bar2.addChild(baz)
-
- XCTAssertEqual(baz.xPath, "foo/bar[2]/baz")
-
- let baz2 = XMLElement(name: "baz")
+
+ XCTAssertEqual(baz.xPath, "/foo/bar[2]/baz")
+
+ let baz2 = XMLElement(name: "/baz")
bar2.addChild(baz2)
- XCTAssertEqual(baz.xPath, "foo/bar[2]/baz[1]")
+ XCTAssertEqual(baz.xPath, "/foo/bar[2]/baz")
XCTAssertEqual(try! doc.nodes(forXPath:baz.xPath!).first, baz)
- let nodes = try! doc.nodes(forXPath:"foo/bar")
+ let nodes = try! doc.nodes(forXPath:"/foo/bar")
XCTAssertEqual(nodes.count, 3)
XCTAssertEqual(nodes[0], bar1)
XCTAssertEqual(nodes[1], bar2)
XCTAssertEqual(nodes[2], bar3)
+
+ let xmlString = """
+ <?xml version="1.0" encoding="utf-8" standalone="yes"?>
+ <D:propfind xmlns:D="DAV:">
+ <D:prop>
+ <D:getlastmodified></D:getlastmodified>
+ <D:getcontentlength></D:getcontentlength>
+ <D:creationdate></D:creationdate>
+ <D:resourcetype></D:resourcetype>
+ </D:prop>
+ </D:propfind>
+ """
+
+ let namespaceDoc = try XMLDocument(xmlString: xmlString, options: [])
+ let propNodes = try namespaceDoc.nodes(forXPath: "//D:prop")
+ if let propNode = propNodes.first {
+ XCTAssert(propNode.name == "D:prop")
+ } else {
+ XCTAssert(false, "propNode should have existed, but was nil")
+ }
}
func test_elementCreation() {
@@ -152,7 +200,7 @@
XCTAssertEqual(element.elements(forName:"bar"), [bar, bar2])
XCTAssertFalse(element.elements(forName:"foo").contains(bar))
XCTAssertFalse(element.elements(forName:"foo").contains(bar2))
-
+
let baz = XMLElement(name: "baz")
element.insertChild(baz, at: 2)
XCTAssertEqual(element.children?[2], baz)
@@ -290,6 +338,43 @@
XCTAssertEqual(element.prefix, "xml")
XCTAssertEqual(element.localName, "root")
}
+
+ func test_addNamespace() {
+ let doc = XMLDocument(rootElement: XMLElement(name: "Foo"))
+ let ns = XMLNode.namespace(withName: "F", stringValue: "http://example.com/fakenamespace") as! XMLNode
+ doc.rootElement()?.addNamespace(ns)
+ XCTAssert((doc.rootElement()?.namespaces ?? []).map({ $0.stringValue ?? "foo" }).contains(ns.stringValue ?? "bar"), "namespaces didn't include the added namespace!")
+ XCTAssert(doc.rootElement()?.uri == "http://example.com/fakenamespace", "uri was \(doc.rootElement()?.uri ?? "null") instead of http://example.com/fakenamespace")
+
+ let otherNS = XMLNode.namespace(withName: "R", stringValue: "http://example.com/rnamespace") as! XMLNode
+ doc.rootElement()?.addNamespace(otherNS)
+ XCTAssert((doc.rootElement()?.namespaces ?? []).map({ $0.stringValue ?? "foo" }).contains(ns.stringValue ?? "bar"), "lost original namespace")
+ XCTAssert((doc.rootElement()?.namespaces ?? []).map({ $0.stringValue ?? "foo" }).contains(otherNS.stringValue ?? "bar"), "Lost new namespace")
+ doc.rootElement()?.addNamespace(XMLNode.namespace(withName: "R", stringValue: "http://example.com/rnamespace") as! XMLNode)
+ XCTAssert(doc.rootElement()?.namespaces?.count == 2, "incorrectly added a namespace with duplicate name!")
+
+ let otherDoc = XMLDocument(rootElement: XMLElement(name: "Bar"))
+ otherDoc.rootElement()?.namespaces = [XMLNode.namespace(withName: "R", stringValue: "http://example.com/rnamespace") as! XMLNode, XMLNode.namespace(withName: "F", stringValue: "http://example.com/fakenamespace") as! XMLNode]
+ XCTAssert(otherDoc.rootElement()?.namespaces?.count == 2)
+ XCTAssert(otherDoc.rootElement()?.namespaces?.flatMap({ $0.name })[0] == "R" && otherDoc.rootElement()?.namespaces?.flatMap({ $0.name })[1] == "F")
+ otherDoc.rootElement()?.namespaces = nil
+ XCTAssert((otherDoc.rootElement()?.namespaces?.count ?? 0) == 0)
+ }
+
+ func test_removeNamespace() {
+ let doc = XMLDocument(rootElement: XMLElement(name: "Foo"))
+ let ns = XMLNode.namespace(withName: "F", stringValue: "http://example.com/fakenamespace") as! XMLNode
+ let otherNS = XMLNode.namespace(withName: "R", stringValue: "http://example.com/rnamespace") as! XMLNode
+
+ doc.rootElement()?.addNamespace(ns)
+ doc.rootElement()?.addNamespace(otherNS)
+ XCTAssert(doc.rootElement()?.namespaces?.count == 2)
+
+ doc.rootElement()?.removeNamespace(forPrefix: "F")
+
+ XCTAssert(doc.rootElement()?.namespaces?.count == 1)
+ XCTAssert(doc.rootElement()?.namespaces?.first?.name == "R")
+ }
/*
* <rdar://31567922> Re-enable these tests in a way that does not depend on the internet.
@@ -339,7 +424,7 @@
func test_dtd() throws {
let node = XMLNode.dtdNode(withXMLString:"<!ELEMENT foo (#PCDATA)>") as! XMLDTDNode
XCTAssert(node.name == "foo")
-
+
let dtd = try XMLDTD(contentsOf: testBundle().url(forResource: "PropertyList-1.0", withExtension: "dtd")!, options: [])
// dtd.systemID = testBundle().URLForResource("PropertyList-1.0", withExtension: "dtd")?.absoluteString
dtd.name = "plist"
@@ -372,13 +457,14 @@
elementDecl.name = "MyElement"
elementDecl.stringValue = "(#PCDATA | array)*"
XCTAssert(elementDecl.stringValue == "(#PCDATA | array)*", elementDecl.stringValue ?? "nil string value")
+ XCTAssert(elementDecl.name == "MyElement")
}
func test_documentWithDTD() throws {
let doc = try XMLDocument(contentsOf: testBundle().url(forResource: "NSXMLDTDTestData", withExtension: "xml")!, options: [])
let dtd = doc.dtd
XCTAssert(dtd?.name == "root")
-
+
let notation = dtd?.notationDeclaration(forName:"myNotation")
notation?.detach()
XCTAssert(notation?.name == "myNotation")
@@ -419,5 +505,66 @@
makeSureDocumentIsAllocatedAndFreed()
XCTAssertNil(weakDoc, "document not freed even through it should have")
}
-
+
+ func test_nodeFindingWithNamespaces() throws {
+ let xmlString = """
+ <?xml version="1.0" encoding="utf-8" standalone="yes"?>
+ <D:propfind xmlns:D="DAV:">
+ <D:prop>
+ <D:getlastmodified></D:getlastmodified>
+ <D:getcontentlength></D:getcontentlength>
+ <D:creationdate></D:creationdate>
+ <D:resourcetype></D:resourcetype>
+ </D:prop>
+ </D:propfind>
+ """
+
+ let doc = try XMLDocument(xmlString: xmlString, options: [])
+ let namespace = (doc.rootElement()?.namespaces?.first)!
+ XCTAssert(namespace.kind == .namespace, "The node was not a namespace but was a \(namespace.kind)")
+ XCTAssert(namespace.stringValue == "DAV:", "expected a string value of DAV: got \(namespace.stringValue as Any)")
+ XCTAssert(namespace.name == "D", "expected a name of D, got \(namespace.name as Any)")
+
+ let newNS = XMLNode.namespace(withName: "R", stringValue: "http://apple.com") as! XMLNode
+ XCTAssert(newNS.name == "R", "expected name R, got name \(newNS.name as Any)")
+ XCTAssert(newNS.stringValue == "http://apple.com", "expected stringValue http://apple.com, got stringValue \(newNS.stringValue as Any)")
+ newNS.stringValue = "FOO:"
+ XCTAssert(newNS.stringValue == "FOO:")
+ newNS.name = "F"
+ XCTAssert(newNS.name == "F")
+
+ let root = doc.rootElement()!
+ XCTAssert(root.localName == "propfind")
+ XCTAssert(root.name == "D:propfind")
+ XCTAssert(root.prefix == "D")
+ let node = doc.findFirstChild(named: "prop")
+ XCTAssert(node != nil, "failed to find existing node")
+ XCTAssert(node?.localName == "prop")
+
+ XCTAssert(doc.rootElement()?.elements(forLocalName: "prop", uri: "DAV:").first?.name == "D:prop", "failed to get elements, got \(doc.rootElement()?.elements(forLocalName: "prop", uri: "DAV:").first as Any)")
+ }
}
+
+fileprivate extension XMLNode {
+ fileprivate func findFirstChild(named name: String) -> XMLNode? {
+ guard let children = self.children else {
+ return nil
+ }
+
+ for child in children {
+ if let childName = child.localName {
+ if childName == name {
+ return child
+ }
+ }
+
+ if let result = child.findFirstChild(named: name) {
+ return result
+ }
+ }
+
+ return nil
+ }
+}
+
+
diff --git a/TestFoundation/TestProcess.swift b/TestFoundation/TestProcess.swift
index cbac7a9..163dc31 100644
--- a/TestFoundation/TestProcess.swift
+++ b/TestFoundation/TestProcess.swift
@@ -28,6 +28,7 @@
("test_pipe_stdin", test_pipe_stdin),
("test_pipe_stdout", test_pipe_stdout),
("test_pipe_stderr", test_pipe_stderr),
+ ("test_current_working_directory", test_current_working_directory),
// disabled for now
// ("test_pipe_stdout_and_stderr_same_pipe", test_pipe_stdout_and_stderr_same_pipe),
("test_file_stdout", test_file_stdout),
@@ -262,6 +263,19 @@
XCTFail("Test failed: \(error)")
}
}
+
+ func test_current_working_directory() {
+ do {
+ let previousWorkingDirectory = FileManager.default.currentDirectoryPath
+
+ // `bash` will not be found if the current working directory is not set correctly.
+ let _ = try runTask(["bash", "-c", "exit 0"], currentDirectoryPath: "/bin")
+
+ XCTAssertEqual(previousWorkingDirectory, FileManager.default.currentDirectoryPath)
+ } catch let error {
+ XCTFail("Test failed: \(error)")
+ }
+ }
}
private func mkstemp(template: String, body: (FileHandle) throws -> Void) rethrows {
@@ -284,7 +298,7 @@
case InvalidEnvironmentVariable(String)
}
-private func runTask(_ arguments: [String], environment: [String: String]? = nil) throws -> String {
+private func runTask(_ arguments: [String], environment: [String: String]? = nil, currentDirectoryPath: String? = nil) throws -> String {
let process = Process()
var arguments = arguments
@@ -292,6 +306,10 @@
process.arguments = arguments
process.environment = environment
+ if let directoryPath = currentDirectoryPath {
+ process.currentDirectoryPath = directoryPath
+ }
+
let pipe = Pipe()
process.standardOutput = pipe
process.standardError = pipe
diff --git a/TestFoundation/main.swift b/TestFoundation/main.swift
index 1bbdeb0..5788e03 100644
--- a/TestFoundation/main.swift
+++ b/TestFoundation/main.swift
@@ -10,15 +10,20 @@
#if DEPLOYMENT_RUNTIME_OBJC || os(Linux)
import Foundation
import XCTest
+import Glibc
#else
import SwiftFoundation
import SwiftXCTest
+import Darwin
#endif
internal func testBundle() -> Bundle {
return Bundle.main
}
+// ignore SIGPIPE which is sent when writing to closed file descriptors.
+_ = signal(SIGPIPE, SIG_IGN)
+
// For the Swift version of the Foundation tests, we must manually list all test cases here.
XCTMain([
testCase(TestNSAffineTransform.allTests),
@@ -76,6 +81,7 @@
testCase(TestNSURL.allTests),
testCase(TestNSURLComponents.allTests),
testCase(TestNSURLCredential.allTests),
+ testCase(TestNSURLProtocol.allTests),
testCase(TestNSURLRequest.allTests),
testCase(TestURLRequest.allTests),
testCase(TestNSURLResponse.allTests),
@@ -97,4 +103,5 @@
testCase(TestNotification.allTests),
testCase(TestMassFormatter.allTests),
testCase(TestJSONEncoder.allTests),
+ testCase(TestCodable.allTests),
])
diff --git a/lib/product.py b/lib/product.py
index f4c85f6..3e9e432 100644
--- a/lib/product.py
+++ b/lib/product.py
@@ -182,6 +182,8 @@
def generate(self, objects = []):
self.rule = "Archive"
self.product_name = Configuration.current.target.static_library_prefix + self.name + Configuration.current.target.static_library_suffix
+ self.conformance_begin = ''
+ self.conformance_end = ''
return Library.generate(self, [], objects)
class StaticAndDynamicLibrary(StaticLibrary, DynamicLibrary):