Skip to content
This repository has been archived by the owner on Sep 19, 2018. It is now read-only.

Linux support #246

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion Sources/JSONParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
//

import Foundation
#if os(Linux)
import Glibc
#else
import Darwin
#endif

private struct Literal {
static let BACKSLASH = UInt8(ascii: "\\")
Expand Down Expand Up @@ -591,7 +596,11 @@ public struct JSONParser {
}

private func detectingFloatingPointErrors<T>(start loc: Int, _ f: () throws -> T) throws -> T {
let flags = FE_UNDERFLOW | FE_OVERFLOW

// Explicit type declaration is necessary
// for compilation on Linux to succeed.
let flags: Int32 = FE_UNDERFLOW | FE_OVERFLOW

feclearexcept(flags)
let value = try f()
guard fetestexcept(flags) == 0 else {
Expand Down
95 changes: 66 additions & 29 deletions Sources/JSONParsing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
// Copyright © 2015 Big Nerd Ranch. All rights reserved.
//

#if os(Linux)
import CoreFoundation
#endif

import Foundation

// MARK: - Deserialize JSON
Expand Down Expand Up @@ -37,6 +41,8 @@ extension JSON {

// MARK: - NSJSONSerialization



extension JSONSerialization: JSONParserType {

// MARK: Decode Data
Expand All @@ -57,37 +63,68 @@ extension JSONSerialization: JSONParserType {
public static func makeJSON(with object: Any) -> JSON {
switch object {
case let n as NSNumber:
let numberType = CFNumberGetType(n)
switch numberType {
case .charType:
return .bool(n.boolValue)

case .shortType, .intType, .longType, .cfIndexType, .nsIntegerType, .sInt8Type, .sInt16Type, .sInt32Type:
return .int(n.intValue)

case .sInt64Type, .longLongType /* overflows 32-bit Int */:
#if /* 32-bit arch */ arch(arm) || arch(i386)
// Why double, when the Freddy parser would bump to String?
//
// Returning Double avoids making the type depend on whether you're running
// 32-bit or 64-bit code when using the NSJSONSerialization parser.
// NSJSONSerialization appears to bump numbers larger than Int.max to Double on
// 64-bit platforms but use .SInt64Type on 32-bit platforms.
// If we returned a String here, you'd get a String value on 32-bit,
// but a Double value on 64-bit. Instead, we return Double.
//
// This means that, if you switch parsers,
// you'll have to switch from .double to .string for pulling out
// overflowing values, but if you stick with a single parser,
// you at least won't have architecture-dependent lookups!

#if os(Linux)

let cfNumber = unsafeBitCast(n, to: CFNumber.self)
let numberType = CFNumberGetType(cfNumber)
switch numberType {
case kCFNumberCharType:
return .bool(n.boolValue)

case kCFNumberShortType, kCFNumberIntType, kCFNumberLongType, kCFNumberCFIndexType, kCFNumberNSIntegerType, kCFNumberSInt8Type, kCFNumberSInt16Type, kCFNumberSInt32Type:
return .int(n.intValue)

case kCFNumberSInt64Type, kCFNumberLongLongType /* overflows 32-bit Int */:
#if arch(arm) || arch(i386)
return .double(n.doubleValue)
#else
return .int(n.intValue)
#endif

case kCFNumberFloat32Type, kCFNumberFloat64Type, kCFNumberFloatType, kCFNumberDoubleType, kCFNumberCGFloatType:
return .double(n.doubleValue)
#else

default:
return .null

}

#else

let numberType = CFNumberGetType(n)
switch numberType {
case .charType:
return .bool(n.boolValue)

case .shortType, .intType, .longType, .cfIndexType, .nsIntegerType, .sInt8Type, .sInt16Type, .sInt32Type:
return .int(n.intValue)
#endif

case .float32Type, .float64Type, .floatType, .doubleType, .cgFloatType:
return .double(n.doubleValue)
}

case .sInt64Type, .longLongType /* overflows 32-bit Int */:
#if /* 32-bit arch */ arch(arm) || arch(i386)
// Why double, when the Freddy parser would bump to String?
//
// Returning Double avoids making the type depend on whether you're running
// 32-bit or 64-bit code when using the NSJSONSerialization parser.
// NSJSONSerialization appears to bump numbers larger than Int.max to Double on
// 64-bit platforms but use .SInt64Type on 32-bit platforms.
// If we returned a String here, you'd get a String value on 32-bit,
// but a Double value on 64-bit. Instead, we return Double.
//
// This means that, if you switch parsers,
// you'll have to switch from .double to .string for pulling out
// overflowing values, but if you stick with a single parser,
// you at least won't have architecture-dependent lookups!
return .double(n.doubleValue)
#else
return .int(n.intValue)
#endif

case .float32Type, .float64Type, .floatType, .doubleType, .cgFloatType:
return .double(n.doubleValue)
}

#endif

case let arr as [Any]:
return makeJSONArray(arr)
Expand Down
20 changes: 19 additions & 1 deletion Tests/FreddyTests/JSONDecodableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,27 @@
//

import XCTest
import Freddy
import Foundation
@testable import Freddy

class JSONDecodableTests: XCTestCase {

static var allTests : [(String, (JSONDecodableTests) -> () throws -> Void)] {
return [
("testThatJSONDecodableConformanceProducesInstance", testThatJSONDecodableConformanceProducesInstance),
("testJSONDecodableExtensionOnDouble", testJSONDecodableExtensionOnDouble),
("testJSONDecodableExtensionOnInt", testJSONDecodableExtensionOnInt),
("testJSONDecodableExtensionOnString", testJSONDecodableExtensionOnString),
("testJSONDecodableExtensionOnBool", testJSONDecodableExtensionOnBool),
("testThatJSONBoolIsDecodable", testThatJSONBoolIsDecodable),
("testThatJSONArrayIsDecodable", testThatJSONArrayIsDecodable),
("testThatJSONDictionaryIsDecodable", testThatJSONDictionaryIsDecodable),
("testThatArrayOfCanReturnArrayOfJSONDecodable", testThatArrayOfCanReturnArrayOfJSONDecodable),
("testThatDictionaryOfCanReturnDictionaryOfJSONDecodable", testThatDictionaryOfCanReturnDictionaryOfJSONDecodable),
("testThatNullIsDecodedToNilWhenRequestedAtTopLevel", testThatNullIsDecodedToNilWhenRequestedAtTopLevel),
("testThatAttemptingToDecodeNullThrowsWhenRequestedAtTopLevel", testThatAttemptingToDecodeNullThrowsWhenRequestedAtTopLevel),
]
}

private var mattJSON: JSON!
private var matt: Person!
Expand Down
13 changes: 13 additions & 0 deletions Tests/FreddyTests/JSONEncodableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ import XCTest
@testable import Freddy

class JSONEncodableTests: XCTestCase {

static var allTests : [(String, (JSONEncodableTests) -> () throws -> Void)] {
return [
("testThatJSONEncodableEncodesString", testThatJSONEncodableEncodesString),
("testThatJSONEncodableEncodesBool", testThatJSONEncodableEncodesBool),
("testThatJSONEncodableEncodesInt", testThatJSONEncodableEncodesInt),
("testThatJSONEncodableEncodesDouble", testThatJSONEncodableEncodesDouble),
("testThatJSONEncodableEncodesModelType", testThatJSONEncodableEncodesModelType),
("testThatJSONEncodableEncodesArray", testThatJSONEncodableEncodesArray),
("testThatJSONEncodableEncodesDictionary", testThatJSONEncodableEncodesDictionary),
("testThatJSONEncodableEncodesArrayOfModels", testThatJSONEncodableEncodesArrayOfModels),
]
}

func testThatJSONEncodableEncodesString() {
let matt = "Matt"
Expand Down
16 changes: 16 additions & 0 deletions Tests/FreddyTests/JSONEncodingDetectorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,25 @@
//

import XCTest
import Foundation
@testable import Freddy

class JSONEncodingDetectorTests: XCTestCase {

static var allTests : [(String, (JSONEncodingDetectorTests) -> () throws -> Void)] {
return [
("testUTF16LittleEndianDetection", testUTF16LittleEndianDetection),
("testUTF16LittleEndianWithBOMDetection", testUTF16LittleEndianWithBOMDetection),
("testUTF16BigEndianDetection", testUTF16BigEndianDetection),
("testUTF16BigEndianWithBOMDetection", testUTF16BigEndianWithBOMDetection),
("testUTF32LittleEndianDetection", testUTF32LittleEndianDetection),
("testUTF32LittleEndianWithBOMDetection", testUTF32LittleEndianWithBOMDetection),
("testUTF32BigEndianDetection", testUTF32BigEndianDetection),
("testUTF32BigEndianWithBOMDetection", testUTF32BigEndianWithBOMDetection),
("testUTF8Detection", testUTF8Detection),
("testUTF8WithBOMDetection", testUTF8WithBOMDetection),
]
}

let fixtures = JSONEncodingUTFTestFixtures()

Expand Down
11 changes: 10 additions & 1 deletion Tests/FreddyTests/JSONOptionalTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,19 @@
//

import XCTest
import Freddy
@testable import Freddy

class JSONOptionalTests: XCTestCase {

static var allTests : [(String, (JSONOptionalTests) -> () throws -> Void)] {
return [
("testThatJSONCanBeCreatedFromDoubleOptionals", testThatJSONCanBeCreatedFromDoubleOptionals),
("testThatJSONCanBeCreatedFromIntOptionals", testThatJSONCanBeCreatedFromIntOptionals),
("testThatJSONCanBeCreatedFromBoolOptionals", testThatJSONCanBeCreatedFromBoolOptionals),
("testThatJSONCanBeCreatedFromStringOptionals", testThatJSONCanBeCreatedFromStringOptionals),
]
}

func testThatJSONCanBeCreatedFromDoubleOptionals() {

let doubleWithValue: Double? = 123.45
Expand Down
45 changes: 44 additions & 1 deletion Tests/FreddyTests/JSONParserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
//

import XCTest
import Freddy
import Foundation
#if os(Linux)
// Can't seem to find this value in Glibc.
let DBL_EPSILON = 2.2204460492503131e-16
#endif
@testable import Freddy

private func ==(lhs: JSONParser.Error, rhs: JSONParser.Error) -> Bool {
switch (lhs, rhs) {
Expand Down Expand Up @@ -43,6 +48,44 @@ private func ==(lhs: JSONParser.Error, rhs: JSONParser.Error) -> Bool {
}

class JSONParserTests: XCTestCase {

static var allTests : [(String, (JSONParserTests) -> () throws -> Void)] {
return [
("testThatParserThrowsAnErrorForAnEmptyData", testThatParserThrowsAnErrorForAnEmptyData),
("testThatParserThrowsErrorForInsufficientData", testThatParserThrowsErrorForInsufficientData),
("testThatParserCompletesWithSingleZero", testThatParserCompletesWithSingleZero),
("testThatParserCompletesWithBOMAndSingleZero", testThatParserCompletesWithBOMAndSingleZero),
("testThatParserUnderstandsNull", testThatParserUnderstandsNull),
("testThatParserSkipsLeadingWhitespace", testThatParserSkipsLeadingWhitespace),
("testThatParserAllowsTrailingWhitespace", testThatParserAllowsTrailingWhitespace),
("testThatParserFailsWhenTrailingDataIsPresent", testThatParserFailsWhenTrailingDataIsPresent),
("testThatParserUnderstandsTrue", testThatParserUnderstandsTrue),
("testThatParserUnderstandsFalse", testThatParserUnderstandsFalse),
("testThatParserUnderstandsStringsWithoutEscapes", testThatParserUnderstandsStringsWithoutEscapes),
("testThatParserUnderstandsStringsWithEscapedCharacters", testThatParserUnderstandsStringsWithEscapedCharacters),
("testThatParserUnderstandsStringsWithEscapedUnicode", testThatParserUnderstandsStringsWithEscapedUnicode),
("testThatParserFailsWhenIncompleteDataIsPresent", testThatParserFailsWhenIncompleteDataIsPresent),
("testThatParserUnderstandsNumbers", testThatParserUnderstandsNumbers),
("testThatParserRejectsInvalidNumbers", testThatParserRejectsInvalidNumbers),
("testParserHandlingOfNumericOverflow", testParserHandlingOfNumericOverflow),
("testStringFloatingPointCanBeInt", testStringFloatingPointCanBeInt),
("testOverflowingIntResultsInStringWithNSJSONSerializationParser", testOverflowingIntResultsInStringWithNSJSONSerializationParser),
("testOverflowingIntResultsInStringWithFreddyParser", testOverflowingIntResultsInStringWithFreddyParser),
("testOverflowingInt32ResultsInIntWhenAppropriateWithFreddyParser", testOverflowingInt32ResultsInIntWhenAppropriateWithFreddyParser),
("testLargeIntResultsInStringOrIntWithFreddyParser", testLargeIntResultsInStringOrIntWithFreddyParser),
("testReturnsNilWhenDoubleValueExceedingIntMaxIsAccessedAsInt", testReturnsNilWhenDoubleValueExceedingIntMaxIsAccessedAsInt),
("testThatParserUnderstandsEmptyArrays", testThatParserUnderstandsEmptyArrays),
("testThatParserUnderstandsSingleItemArrays", testThatParserUnderstandsSingleItemArrays),
("testThatParserUnderstandsMultipleItemArrays", testThatParserUnderstandsMultipleItemArrays),
("testThatParserUnderstandsEmptyObjects", testThatParserUnderstandsEmptyObjects),
("testThatParserUnderstandsSingleItemObjects", testThatParserUnderstandsSingleItemObjects),
("testThatParserUnderstandsMultipleItemObjects", testThatParserUnderstandsMultipleItemObjects),
("testThatParserFailsForUnsupportedEncodings", testThatParserFailsForUnsupportedEncodings),
("testThatParserAcceptsUTF16SurrogatePairs", testThatParserAcceptsUTF16SurrogatePairs),
("testThatParserRejectsInvalidUTF16SurrogatePairs", testThatParserRejectsInvalidUTF16SurrogatePairs),
("testThatParserRejectsStringEndingInBackslash", testThatParserRejectsStringEndingInBackslash),
]
}

func testThatParserThrowsAnErrorForAnEmptyData() {

Expand Down
20 changes: 19 additions & 1 deletion Tests/FreddyTests/JSONSerializingTests.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
// Copyright (C) 2016 Big Nerd Ranch, Inc. Licensed under the MIT license WITHOUT ANY WARRANTY.

import XCTest
import Freddy
import Foundation
@testable import Freddy

class JSONSerializingTests: XCTestCase {

static var allTests : [(String, (JSONSerializingTests) -> () throws -> Void)] {
return [
("testThatJSONCanBeSerializedToNSData", testThatJSONCanBeSerializedToNSData),
("testThatJSONCanBeSerializedToString", testThatJSONCanBeSerializedToString),
("testThatJSONDataIsEqual", testThatJSONDataIsEqual),
("testThatJSONStringIsEqual", testThatJSONStringIsEqual),
("testThatJSONDataSerializationMakesEqualJSON", testThatJSONDataSerializationMakesEqualJSON),
("testThatJSONStringSerializationMakesEqualJSON", testThatJSONStringSerializationMakesEqualJSON),
("testThatJSONSerializationHandlesBoolsCorrectly", testThatJSONSerializationHandlesBoolsCorrectly),
("testThatJSONStringSerializationHandlesBoolsCorrectly", testThatJSONStringSerializationHandlesBoolsCorrectly),
("testThatSerializingCanSkipKeysWithNullValue", testThatSerializingCanSkipKeysWithNullValue),
("testThatSerializingUsingOptionalValuesIsPossible", testThatSerializingUsingOptionalValuesIsPossible),
("testThatSerializingUsingOptionalNilValuesIsPossible", testThatSerializingUsingOptionalNilValuesIsPossible),
]
}

let json = JSONFromFixture("sample.JSON")
let noWhiteSpaceData = dataFromFixture("sampleNoWhiteSpace.JSON")

Expand Down
Loading