Skip to content

Commit

Permalink
Merge branch 'master' of github.com:OpenKitten/Cheetah
Browse files Browse the repository at this point in the history
  • Loading branch information
Joannis committed Apr 8, 2017
2 parents 914edce + 6edd6ba commit 0f0eaec
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 81 deletions.
2 changes: 1 addition & 1 deletion Sources/Array.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public struct JSONArray: Value, InitializableSequence, ExpressibleByArrayLiteral

/// Initializes this JSON Array with a JSON String containing this array in JSON format
public init(from data: String, allowingComments: Bool = true) throws {
var parser = JSON(data.makeJSONBinary(), allowingComments: allowingComments)
var parser = JSON(data.utf8, allowingComments: allowingComments)
self = try parser.parse(rootLevel: true)
}

Expand Down
11 changes: 10 additions & 1 deletion Sources/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,17 @@ extension Optional where Wrapped == Value {
/// If this optional contains a JSONObject it'll return the Value at the provided key only if it exists.
///
/// Otherwise it will return nil
///
/// When setting, if the receiver is not already an object, it will be overwritten by a new object
public subscript(_ key: String) -> Value? {
return (self as? JSONObject)?[key]
get {
return (self as? JSONObject)?[key]
}
set {
var object = (self as? JSONObject) ?? [:]
object[key] = newValue
self = object
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/Object.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public struct JSONObject : Value, InitializableObject, ExpressibleByDictionaryLi

/// Initializes this Object from a JSON String
public init(from data: String, allowingComments: Bool = true) throws {
var parser = JSON(data.makeJSONBinary(), allowingComments: allowingComments)
var parser = JSON(data.utf8, allowingComments: allowingComments)
self = try parser.parse(rootLevel: true)
}

Expand Down
38 changes: 21 additions & 17 deletions Sources/Parser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ public struct JSON {
var position: Int = 0

/// Initializes this JSON context
internal init(_ data: [UInt8], allowingComments: Bool) {
self.data = data
internal init<S : Sequence>(_ data: S, allowingComments: Bool) where S.Iterator.Element == UInt8 {
self.data = Array(data)
}

/// Parses an escaped unicode character (as hexadecimal) at the current position
Expand Down Expand Up @@ -136,7 +136,7 @@ public struct JSON {

position += 1

var characters = ""
var characters = [UInt8]()

loop: while position < data.count {
if data[position] == SpecialCharacters.escape {
Expand All @@ -150,28 +150,28 @@ public struct JSON {
case 0x75:
position += 1
let unicodeScalar = try parseUnicode()
characters.append(Character(unicodeScalar))
characters.append(contentsOf: String(unicodeScalar).utf8)
case SpecialCharacters.stringQuotationMark:
characters.append(SpecialCharacters.stringQuotationMark.character)
characters.append(SpecialCharacters.stringQuotationMark)
position += 1
case SpecialCharacters.escape:
characters.append(SpecialCharacters.escape.character)
characters.append(SpecialCharacters.escape)
position += 1
case 0x2f: // `/`
characters.append(SpecialCharacters.slash.character)
characters.append(SpecialCharacters.slash)
position += 1
case 0x62: // `b`
throw JSONError.unsupported
case 0x66: // `f`
throw JSONError.unsupported
case 0x6e: // `n`
characters.append(SpecialCharacters.lineFeed.character)
characters.append(SpecialCharacters.lineFeed)
position += 1
case 0x72: // `r`
characters.append(SpecialCharacters.carriageReturn.character)
characters.append(SpecialCharacters.carriageReturn)
position += 1
case 0x74: // `t`
characters.append(SpecialCharacters.tab.character)
characters.append(SpecialCharacters.tab)
position += 1
default:
throw JSONError.invalidEscapedCharacter
Expand All @@ -182,9 +182,13 @@ public struct JSON {

try skipWhitespace()

return characters
guard let string = String(bytes: characters, encoding: .utf8) else {
throw JSONError.invalidString
}

return string
} else {
characters.append(data[position].character)
characters.append(data[position])
position += 1
}
}
Expand Down Expand Up @@ -535,11 +539,11 @@ public struct JSON {

/// Parses any value given a String
public static func parse(from data: String, allowingComments: Bool = true) throws -> Value {
return try parse(from: data.makeJSONBinary())
return try parse(from: data.utf8)
}

/// Parses any value given a String bytes buffer
public static func parse(from data: [UInt8], allowingComments: Bool = true) throws -> Value {
public static func parse<S : Sequence>(from data: S, allowingComments: Bool = true) throws -> Value where S.Iterator.Element == UInt8 {
var parser = JSON(data, allowingComments: allowingComments)
try parser.skipWhitespace()

Expand All @@ -559,23 +563,23 @@ public struct JSON {
case 0x30...0x39, SpecialCharacters.minus:
result = try parser.parseNumber()
case 0x6e: // `n`
guard parser.remaining(4), [UInt8](data[parser.position..<parser.position + 4]) == SpecialWords.null else {
guard parser.remaining(4), [UInt8](parser.data[parser.position..<parser.position + 4]) == SpecialWords.null else {
throw JSONError.unknownValue
}

parser.position += 4

result = Null()
case 0x74: // `t`
guard parser.remaining(4), [UInt8](data[parser.position..<parser.position + 4]) == SpecialWords.true else {
guard parser.remaining(4), [UInt8](parser.data[parser.position..<parser.position + 4]) == SpecialWords.true else {
throw JSONError.unknownValue
}

parser.position += 4

result = true
case 0x66: // `f`
guard parser.remaining(5), [UInt8](data[parser.position..<parser.position + 5]) == SpecialWords.false else {
guard parser.remaining(5), [UInt8](parser.data[parser.position..<parser.position + 5]) == SpecialWords.false else {
throw JSONError.unknownValue
}

Expand Down
68 changes: 12 additions & 56 deletions Sources/Value.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public enum JSONError : Error {
/// -
case invalidObject

/// -
case invalidString

/// -
case invalidArray

Expand Down Expand Up @@ -69,83 +72,36 @@ extension String: Value {
/// Serializes this String to a JSON String with quotes
public func serialize() -> [UInt8] {
var buffer: [UInt8] = [SpecialCharacters.stringQuotationMark]
buffer.append(contentsOf: [UInt8](self.utf8))
buffer.append(contentsOf: self.makeJSONBinary())
return buffer + [SpecialCharacters.stringQuotationMark]
}

/// Converts this String to JSON binary representation by escaping it
func makeJSONBinary() -> [UInt8] {
var buffer = [UInt8]()

let lowercasedRadix16table: [UInt8] = [0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66]

for char in self.unicodeScalars {
switch char.value {
for char in self.utf8 {
switch char {
case numericCast(SpecialCharacters.stringQuotationMark):
buffer.append(contentsOf: "\"".utf8)
buffer.append(contentsOf: "\\\"".utf8)
case numericCast(SpecialCharacters.escape):
buffer.append(contentsOf: "\\".utf8)
case numericCast(SpecialCharacters.tab):
buffer.append(contentsOf: "\t".utf8)
buffer.append(contentsOf: "\\t".utf8)
case numericCast(SpecialCharacters.lineFeed):
buffer.append(contentsOf: "\n".utf8)
buffer.append(contentsOf: "\\n".utf8)
case numericCast(SpecialCharacters.carriageReturn):
buffer.append(contentsOf: "\r".utf8)
case numericCast(SpecialCharacters.tab):
buffer.append(contentsOf: "\t".utf8)
buffer.append(contentsOf: "\\r".utf8)
case 0x00...0x1F:
buffer.append(contentsOf: "\\u".utf8)
let str = String(char.value, radix: 16, uppercase: true)
let str = String(char, radix: 16, uppercase: true)
if str.characters.count == 1 {
buffer.append(contentsOf: "000\(str)".utf8)
} else {
buffer.append(contentsOf: "00\(str)".utf8)
}
case 0x20...0xFF:
let character = UInt8(char.value)
buffer.append(character)
case 0x100..<UInt32(UInt16.max):
var character = UInt16(char.value)

buffer.append(SpecialCharacters.escape)
buffer.append(0x75)

buffer.append(lowercasedRadix16table[Int(character / 4096)])

character = character % 4096
buffer.append(lowercasedRadix16table[Int(character / 256)])

character = character % 256
buffer.append(lowercasedRadix16table[Int(character / 16)])

character = character % 16
buffer.append(lowercasedRadix16table[Int(character)])
default:
func append(_ character: inout UInt16) {
buffer.append(SpecialCharacters.escape)
buffer.append(0x75)

buffer.append(lowercasedRadix16table[Int(character / 4096)])

character = character % 4096
buffer.append(lowercasedRadix16table[Int(character / 256)])

character = character % 256
buffer.append(lowercasedRadix16table[Int(character / 16)])

character = character % 16
buffer.append(lowercasedRadix16table[Int(character)])
}

let characterValue = char.value - UInt32(UInt16.max) - 1

// Highest 10 + high surrogate
var character0 = UInt16(characterValue >> 10) + 55_296
// Highest lowest + low surrogate
var character1 = UInt16((characterValue << 22) >> 22) + 56320

append(&character0)
append(&character1)
buffer.append(char)
}
}

Expand Down
10 changes: 5 additions & 5 deletions Tests/CheetahTests/ParserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class ParsingTests: XCTestCase {
// MARK: - Null

func testNullParses() throws {
XCTAssert(try JSON.parse(from: "null") is Null)
XCTAssert(try JSON.parse(from: "null") is NSNull)
}

func testNullThrowsOnMismatch() {
Expand Down Expand Up @@ -60,7 +60,7 @@ class ParsingTests: XCTestCase {
func testArray_JustNull() throws {
let array = try JSONArray(from: "[ null ]")

XCTAssertEqual(array, [Null()])
XCTAssertEqual(array, [NSNull()])
}

func testArray_ZeroBegining() throws {
Expand All @@ -77,12 +77,12 @@ class ParsingTests: XCTestCase {

func testArray_NullsBoolsNums_Normal_Minimal_RootParser() throws {
XCTAssertEqual(try JSONArray(from: "[null,true,false,12,-10,-24.3,18.2e9]"),
[Null(), true, false, 12, -10, -24.3, 18200000000.0])
[NSNull(), true, false, 12, -10, -24.3, 18200000000.0])
}

func testArray_NullsBoolsNums_Normal_MuchWhitespace() throws {
XCTAssertEqual(try JSONArray(from: " \t[\n null ,true, \n-12.3 , false\r\n]\n "),
[Null(), true, -12.3, false])
[NSNull(), true, -12.3, false])
}

func testArray_NullsAndBooleans_Bad_MissingEnd() {
Expand Down Expand Up @@ -428,7 +428,7 @@ class ParsingTests: XCTestCase {
parse("{\t'hello': 'wor🇨🇿ld', \n\t 'val': 1234, 'many': [\n-12.32, null, 'yo'\r], 'emptyDict': {}, 'dict': {'arr':[]}, 'name': true}", to: [
"hello": "wor🇨🇿ld",
"val": 1234,
"many": [-12.32, Null(), "yo"] as JSONArray,
"many": [-12.32, NSNull(), "yo"] as JSONArray,
"emptyDict": [:] as JSONObject,
"dict": ["arr": [] as JSONArray] as JSONObject,
"name": true
Expand Down

0 comments on commit 0f0eaec

Please sign in to comment.