├── .gitignore ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── JSON │ ├── Constants.swift │ ├── Decoding │ ├── DecodingError.swift │ ├── JSON+Decoder.swift │ ├── JSON+withScopedDecoder.swift │ ├── KeyedDecodingContainer.swift │ ├── SingleValueDecodingContainer.swift │ └── UnkeyedDecodingContainer.swift │ ├── Encoding │ ├── JSON+Encoder.swift │ ├── JSON+withScopedEncoder.swift │ ├── KeyedEncodingContainer.swift │ ├── SingleValueEncodingContainer.swift │ └── UnkeyedEncodingContainer.swift │ ├── JSON+Value │ ├── Accessors.swift │ ├── Characters.swift │ ├── Error.swift │ ├── Hex.swift │ ├── JSONValueInitializable.swift │ ├── Value+Array.swift │ ├── Value+Number.swift │ ├── Value+Object.swift │ ├── Value+String.swift │ └── Value.swift │ └── JSON.swift ├── Tests ├── Codable │ ├── Decoder │ │ └── main.swift │ ├── Encoder │ │ └── main.swift │ ├── JSON.decode │ │ └── main.swift │ ├── JSON.encode │ │ └── main.swift │ ├── ScopedCoders │ │ └── main.swift │ ├── UnkeyedDecodingContainer │ │ └── main.swift │ └── UnkeyedEncodingContainer │ │ └── main.swift └── JSON │ ├── Value+DynamicLookup │ └── main.swift │ ├── Value+InputStream │ └── main.swift │ ├── Value+OutputStream │ └── main.swift │ └── Value │ └── main.swift └── run_tests /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /.swiftpm 4 | /Packages 5 | /*.xcodeproj 6 | /Package.resolved 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.9 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "JSON", 6 | platforms: [ 7 | .iOS(.v16), 8 | .macOS(.v13), 9 | ], 10 | products: [ 11 | .library( 12 | name: "JSON", 13 | targets: ["JSON"]), 14 | ], 15 | dependencies: [ 16 | .package(name: "Platform"), 17 | .package(name: "Codable"), 18 | .package(name: "Stream"), 19 | .package(name: "Test"), 20 | ], 21 | targets: [ 22 | .target( 23 | name: "JSON", 24 | dependencies: [ 25 | .product(name: "Platform", package: "platform"), 26 | .product(name: "Codable", package: "codable"), 27 | .product(name: "Stream", package: "stream"), 28 | ], 29 | swiftSettings: swift6), 30 | ] 31 | ) 32 | 33 | // MARK: - tests 34 | 35 | testTarget("Codable") { test in 36 | test("Decoder") 37 | test("Encoder") 38 | test("JSON.decode") 39 | test("JSON.encode") 40 | test("ScopedCoders") 41 | test("UnkeyedDecodingContainer") 42 | test("UnkeyedEncodingContainer") 43 | } 44 | 45 | testTarget("JSON") { test in 46 | test("Value") 47 | test("Value+DynamicLookup") 48 | test("Value+InputStream") 49 | test("Value+OutputStream") 50 | } 51 | 52 | func testTarget(_ target: String, task: ((String) -> Void) -> Void) { 53 | task { test in addTest(target: target, name: test) } 54 | } 55 | 56 | func addTest(target: String, name: String) { 57 | package.targets.append( 58 | .executableTarget( 59 | name: "Tests/\(target)/\(name)", 60 | dependencies: [ 61 | .target(name: "JSON"), 62 | .product(name: "Stream", package: "stream"), 63 | .product(name: "Test", package: "test"), 64 | ], 65 | path: "Tests/\(target)/\(name)", 66 | swiftSettings: swift6)) 67 | } 68 | 69 | let swift6: [SwiftSetting] = [ 70 | .enableUpcomingFeature("ConciseMagicFile"), 71 | .enableUpcomingFeature("ForwardTrailingClosures"), 72 | .enableUpcomingFeature("ExistentialAny"), 73 | .enableUpcomingFeature("StrictConcurrency"), 74 | .enableUpcomingFeature("ImplicitOpenExistentials"), 75 | .enableUpcomingFeature("BareSlashRegexLiterals"), 76 | ] 77 | 78 | // MARK: - custom package source 79 | 80 | #if canImport(ObjectiveC) 81 | import Darwin.C 82 | #else 83 | import Glibc 84 | #endif 85 | 86 | extension Package.Dependency { 87 | enum Source: String { 88 | case local, remote, github 89 | 90 | static var `default`: Self { .github } 91 | 92 | var baseUrl: String { 93 | switch self { 94 | case .local: return "../" 95 | case .remote: return "https://swiftstack.io/" 96 | case .github: return "https://github.com/swiftstack/" 97 | } 98 | } 99 | 100 | func url(for name: String) -> String { 101 | return self == .local 102 | ? baseUrl + name.lowercased() 103 | : baseUrl + name.lowercased() + ".git" 104 | } 105 | } 106 | 107 | static func package(name: String) -> Package.Dependency { 108 | guard let pointer = getenv("SWIFTSTACK") else { 109 | return .package(name: name, source: .default) 110 | } 111 | guard let source = Source(rawValue: String(cString: pointer)) else { 112 | fatalError("Invalid source. Use local, remote or github") 113 | } 114 | return .package(name: name, source: source) 115 | } 116 | 117 | static func package(name: String, source: Source) -> Package.Dependency { 118 | return source == .local 119 | ? .package(name: name, path: source.url(for: name)) 120 | : .package(url: source.url(for: name), branch: "dev") 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSON 2 | 3 | Fastest JSON implementation written in Swift. 4 | 5 | ## Package.swift 6 | 7 | ```swift 8 | .package(url: "https://github.com/swiftstack/json.git", .branch("dev")) 9 | ``` 10 | 11 | ## Usage 12 | 13 | ### Convenience API 14 | 15 | ```swift 16 | let bytes = JSON.encode(Model()) 17 | let model = JSON.decode(Model.self, from: bytes) 18 | ``` 19 | 20 | ### Streaming API 21 | 22 | ```swift 23 | let output = OutputByteStream() 24 | JSON.encode(Model(), to: output) 25 | 26 | let input = InputByteStream(output.bytes) 27 | let model = JSON.decode(Model.self, from: input) 28 | ``` 29 | 30 | ### Encoder / Decoder + Streaming API 31 | 32 | ```swift 33 | let bytes = JSONEncoder().encode(Model()) 34 | JSONEncoder().encode(Model(), to: stream) 35 | ... 36 | ``` 37 | 38 | ### JSON Value 39 | 40 | ```swift 41 | public struct JSON { 42 | public enum Value { 43 | case null 44 | case bool(Bool) 45 | case number(Number) 46 | case string(String) 47 | case array([JSON.Value]) 48 | case object([String : JSON.Value]) 49 | 50 | public enum Number { 51 | case int(Int) 52 | case uint(UInt) 53 | case double(Double) 54 | } 55 | } 56 | } 57 | ``` 58 | 59 | ## Performance 60 | `{"message":"Hello, World!"}`
61 | 62 | JSON.JSONEncoder: 934 644 tasks/sec
63 | Foundation.JSONEncoder: 92 619 tasks/sec
64 | 65 | JSON.JSONDecoder: 236 062 tasks/sec
66 | Foundation.JSONDecoder: 226 515 tasks/sec
67 | 68 | `{"message":"\u3053\u3093\u306B\u3061\u306F\u4E16\u754C\uFF01"}`
69 | 70 | JSON.JSONDecoder: 179 440 tasks/sec
71 | Foundation.JSONDecoder: 88 614 tasks/sec
72 | -------------------------------------------------------------------------------- /Sources/JSON/Constants.swift: -------------------------------------------------------------------------------- 1 | extension Array where Element == UInt8 { 2 | static let null = [UInt8]("null".utf8) 3 | static let `true` = [UInt8]("true".utf8) 4 | static let `false` = [UInt8]("false".utf8) 5 | } 6 | -------------------------------------------------------------------------------- /Sources/JSON/Decoding/DecodingError.swift: -------------------------------------------------------------------------------- 1 | extension DecodingError.Context { 2 | static func description(_ string: String) -> DecodingError.Context { 3 | return DecodingError.Context(codingPath: [], debugDescription: string) 4 | } 5 | 6 | static func incompatible(with value: JSON.Value) -> DecodingError.Context { 7 | return .description("incompatible with \(value)") 8 | } 9 | 10 | static func incompatible( 11 | with value: JSON.Value, for key: T 12 | ) -> DecodingError.Context { 13 | return .description("incompatible with \(value) for \(key)") 14 | } 15 | 16 | static func unexpectedNull() -> DecodingError.Context { 17 | return .description("unexpected null") 18 | } 19 | } 20 | 21 | extension DecodingError.Context: ExpressibleByStringLiteral { 22 | public init(stringLiteral value: String) { 23 | self.init(codingPath: [], debugDescription: value) 24 | } 25 | } 26 | 27 | extension DecodingError.Context: ExpressibleByNilLiteral { 28 | public init(nilLiteral: ()) { 29 | self.init(codingPath: [], debugDescription: "") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/JSON/Decoding/JSON+Decoder.swift: -------------------------------------------------------------------------------- 1 | import Stream 2 | 3 | extension JSON { 4 | public class Decoder: Swift.Decoder { 5 | public var codingPath: [CodingKey] { [] } 6 | public var userInfo: [CodingUserInfoKey: Any] { [:] } 7 | 8 | let json: JSON.Value 9 | let options: Options 10 | 11 | public struct Options { 12 | public let parseNullAsOptional: Bool 13 | 14 | public static var `default` = Options(parseNullAsOptional: true) 15 | } 16 | 17 | public init(_ json: JSON.Value, options: Options = .default) throws { 18 | self.json = json 19 | self.options = options 20 | } 21 | 22 | // NOTE: should be internal, use withScopedDecoder 23 | func close() throws { 24 | // consume the rest of stream 25 | } 26 | 27 | public func container( 28 | keyedBy type: Key.Type 29 | ) throws -> KeyedDecodingContainer { 30 | guard case .object(let dictionary) = json else { 31 | throw DecodingError 32 | .typeMismatch([String: JSON.Value].self, nil) 33 | } 34 | let container = JSONKeyedDecodingContainer(dictionary, options) 35 | return KeyedDecodingContainer(container) 36 | } 37 | 38 | public func unkeyedContainer() throws -> UnkeyedDecodingContainer { 39 | guard case .array(let array) = json else { 40 | throw DecodingError.typeMismatch([JSON.Value].self, nil) 41 | } 42 | return JSONUnkeyedDecodingContainer(array, options) 43 | } 44 | 45 | public func singleValueContainer( 46 | ) throws -> SingleValueDecodingContainer { 47 | return JSONSingleValueDecodingContainer(json, options) 48 | } 49 | } 50 | } 51 | 52 | // testable 53 | extension JSON.Decoder { 54 | convenience init( 55 | _ stream: InputByteStream, 56 | options: Options = .default 57 | ) async throws { 58 | let value = try await JSON.Value.decode(from: stream) 59 | try self.init(value, options: options) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/JSON/Decoding/JSON+withScopedDecoder.swift: -------------------------------------------------------------------------------- 1 | import Stream 2 | 3 | extension JSON { 4 | // FIXME: currently pointless, designed for future lazy reading 5 | public static func withScopedDecoder( 6 | using reader: StreamReader, 7 | options: JSON.Decoder.Options = .default, 8 | _ body: (Decoder) throws -> T 9 | ) async throws -> T { 10 | let json = try await JSON.Value.decode(from: reader) 11 | let decoder = try Decoder(json, options: options) 12 | let result = try body(decoder) 13 | try decoder.close() 14 | return result 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/JSON/Decoding/KeyedDecodingContainer.swift: -------------------------------------------------------------------------------- 1 | struct JSONKeyedDecodingContainer 2 | : KeyedDecodingContainerProtocol { 3 | var codingPath: [CodingKey] { 4 | return [] 5 | } 6 | var allKeys: [K] { 7 | return object.keys.compactMap(Key.init) 8 | } 9 | 10 | let object: [String: JSON.Value] 11 | let options: JSON.Decoder.Options 12 | 13 | init(_ object: [String: JSON.Value], _ options: JSON.Decoder.Options) { 14 | self.object = object 15 | self.options = options 16 | } 17 | 18 | func contains(_ key: K) -> Bool { 19 | return object[key.stringValue] != nil 20 | } 21 | 22 | @inline(__always) 23 | private func inlinedDecodeIfPresent( 24 | _ type: T.Type, forKey key: K 25 | ) throws -> T? { 26 | guard let object = object[key.stringValue] else { 27 | return nil 28 | } 29 | 30 | if options.parseNullAsOptional && object == .null { 31 | return nil 32 | } 33 | 34 | guard let value = T(object) else { 35 | throw DecodingError.typeMismatch( 36 | type, .incompatible(with: object, for: key)) 37 | } 38 | 39 | return value 40 | } 41 | 42 | @inline(__always) 43 | private func inlinedDecode( 44 | _ type: T.Type, forKey key: K 45 | ) throws -> T { 46 | guard let object = object[key.stringValue] else { 47 | throw DecodingError.keyNotFound(key, nil) 48 | } 49 | 50 | guard let value = T(object) else { 51 | throw DecodingError.typeMismatch( 52 | type, .incompatible(with: object, for: key)) 53 | } 54 | 55 | return value 56 | } 57 | 58 | func decodeNil(forKey key: K) throws -> Bool { 59 | guard let object = object[key.stringValue] else { 60 | throw DecodingError.keyNotFound(key, nil) 61 | } 62 | return object == .null 63 | } 64 | 65 | func decode(_ type: Bool.Type, forKey key: K) throws -> Bool { 66 | return try inlinedDecode(type, forKey: key) 67 | } 68 | 69 | func decode(_ type: Int.Type, forKey key: K) throws -> Int { 70 | return try inlinedDecode(type, forKey: key) 71 | } 72 | 73 | func decode(_ type: Int8.Type, forKey key: K) throws -> Int8 { 74 | return try inlinedDecode(type, forKey: key) 75 | } 76 | 77 | func decode(_ type: Int16.Type, forKey key: K) throws -> Int16 { 78 | return try inlinedDecode(type, forKey: key) 79 | } 80 | 81 | func decode(_ type: Int32.Type, forKey key: K) throws -> Int32 { 82 | return try inlinedDecode(type, forKey: key) 83 | } 84 | 85 | func decode(_ type: Int64.Type, forKey key: K) throws -> Int64 { 86 | return try inlinedDecode(type, forKey: key) 87 | } 88 | 89 | func decode(_ type: UInt.Type, forKey key: K) throws -> UInt { 90 | return try inlinedDecode(type, forKey: key) 91 | } 92 | 93 | func decode(_ type: UInt8.Type, forKey key: K) throws -> UInt8 { 94 | return try inlinedDecode(type, forKey: key) 95 | } 96 | 97 | func decode(_ type: UInt16.Type, forKey key: K) throws -> UInt16 { 98 | return try inlinedDecode(type, forKey: key) 99 | } 100 | 101 | func decode(_ type: UInt32.Type, forKey key: K) throws -> UInt32 { 102 | return try inlinedDecode(type, forKey: key) 103 | } 104 | 105 | func decode(_ type: UInt64.Type, forKey key: K) throws -> UInt64 { 106 | return try inlinedDecode(type, forKey: key) 107 | } 108 | 109 | func decode(_ type: Float.Type, forKey key: K) throws -> Float { 110 | return try inlinedDecode(type, forKey: key) 111 | } 112 | 113 | func decode(_ type: Double.Type, forKey key: K) throws -> Double { 114 | return try inlinedDecode(type, forKey: key) 115 | } 116 | 117 | func decode(_ type: String.Type, forKey key: K) throws -> String { 118 | return try inlinedDecode(type, forKey: key) 119 | } 120 | 121 | func decode( 122 | _ type: T.Type, 123 | forKey key: K 124 | ) throws -> T where T: Decodable { 125 | guard let value = object[key.stringValue] else { 126 | throw DecodingError.keyNotFound(key, nil) 127 | } 128 | let decoder = try JSON.Decoder(value, options: options) 129 | return try T(from: decoder) 130 | } 131 | 132 | func decodeIfPresent(_ type: Bool.Type, forKey key: K) throws -> Bool? { 133 | return try inlinedDecodeIfPresent(type, forKey: key) 134 | } 135 | 136 | func decodeIfPresent(_ type: Int.Type, forKey key: K) throws -> Int? { 137 | return try inlinedDecodeIfPresent(type, forKey: key) 138 | } 139 | 140 | func decodeIfPresent(_ type: Int8.Type, forKey key: K) throws -> Int8? { 141 | return try inlinedDecodeIfPresent(type, forKey: key) 142 | } 143 | 144 | func decodeIfPresent(_ type: Int16.Type, forKey key: K) throws -> Int16? { 145 | return try inlinedDecodeIfPresent(type, forKey: key) 146 | } 147 | 148 | func decodeIfPresent(_ type: Int32.Type, forKey key: K) throws -> Int32? { 149 | return try inlinedDecodeIfPresent(type, forKey: key) 150 | } 151 | 152 | func decodeIfPresent(_ type: Int64.Type, forKey key: K) throws -> Int64? { 153 | return try inlinedDecodeIfPresent(type, forKey: key) 154 | } 155 | 156 | func decodeIfPresent(_ type: UInt.Type, forKey key: K) throws -> UInt? { 157 | return try inlinedDecodeIfPresent(type, forKey: key) 158 | } 159 | 160 | func decodeIfPresent(_ type: UInt8.Type, forKey key: K) throws -> UInt8? { 161 | return try inlinedDecodeIfPresent(type, forKey: key) 162 | } 163 | 164 | func decodeIfPresent(_ type: UInt16.Type, forKey key: K) throws -> UInt16? { 165 | return try inlinedDecodeIfPresent(type, forKey: key) 166 | } 167 | 168 | func decodeIfPresent(_ type: UInt32.Type, forKey key: K) throws -> UInt32? { 169 | return try inlinedDecodeIfPresent(type, forKey: key) 170 | } 171 | 172 | func decodeIfPresent(_ type: UInt64.Type, forKey key: K) throws -> UInt64? { 173 | return try inlinedDecodeIfPresent(type, forKey: key) 174 | } 175 | 176 | func decodeIfPresent(_ type: Float.Type, forKey key: K) throws -> Float? { 177 | return try inlinedDecodeIfPresent(type, forKey: key) 178 | } 179 | 180 | func decodeIfPresent(_ type: Double.Type, forKey key: K) throws -> Double? { 181 | return try inlinedDecodeIfPresent(type, forKey: key) 182 | } 183 | 184 | func decodeIfPresent(_ type: String.Type, forKey key: K) throws -> String? { 185 | return try inlinedDecodeIfPresent(type, forKey: key) 186 | } 187 | 188 | func decodeIfPresent( 189 | _ type: T.Type, 190 | forKey key: K 191 | ) throws -> T? where T: Decodable { 192 | guard let object = object[key.stringValue] else { 193 | return nil 194 | } 195 | if options.parseNullAsOptional && object == .null { 196 | return nil 197 | } 198 | let decoder = try JSON.Decoder(object, options: options) 199 | return try T(from: decoder) 200 | } 201 | 202 | func nestedContainer( 203 | keyedBy type: NestedKey.Type, 204 | forKey key: K 205 | ) throws -> KeyedDecodingContainer { 206 | guard let nested = object[key.stringValue] else { 207 | throw DecodingError.keyNotFound(key, nil) 208 | } 209 | guard case .object(let object) = nested else { 210 | throw DecodingError.typeMismatch([String: JSON.Value].self, nil) 211 | } 212 | let container = JSONKeyedDecodingContainer(object, options) 213 | return KeyedDecodingContainer(container) 214 | } 215 | 216 | func nestedUnkeyedContainer( 217 | forKey key: K 218 | ) throws -> UnkeyedDecodingContainer { 219 | guard let nested = object[key.stringValue] else { 220 | throw DecodingError.keyNotFound(key, nil) 221 | } 222 | guard case .array(let array) = nested else { 223 | throw DecodingError.typeMismatch([JSON.Value].self, nil) 224 | } 225 | return JSONUnkeyedDecodingContainer(array, options) 226 | } 227 | 228 | func superDecoder() throws -> Swift.Decoder { 229 | return try JSON.Decoder(.object(object), options: options) 230 | } 231 | 232 | func superDecoder(forKey key: K) throws -> Swift.Decoder { 233 | guard let nested = object[key.stringValue] else { 234 | throw DecodingError.keyNotFound(key, nil) 235 | } 236 | return try JSON.Decoder(nested, options: options) 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /Sources/JSON/Decoding/SingleValueDecodingContainer.swift: -------------------------------------------------------------------------------- 1 | struct JSONSingleValueDecodingContainer: SingleValueDecodingContainer { 2 | var codingPath: [CodingKey] { 3 | return [] 4 | } 5 | 6 | let value: JSON.Value 7 | let options: JSON.Decoder.Options 8 | 9 | init(_ value: JSON.Value, _ options: JSON.Decoder.Options) { 10 | self.value = value 11 | self.options = options 12 | } 13 | 14 | @inline(__always) 15 | private func inlinedDecode( 16 | _ type: T.Type 17 | ) throws -> T { 18 | guard let value = T(self.value) else { 19 | throw DecodingError.typeMismatch( 20 | type, .incompatible(with: self.value)) 21 | } 22 | return value 23 | } 24 | 25 | func decodeNil() -> Bool { 26 | return value == .null 27 | } 28 | 29 | func decode(_ type: Bool.Type) throws -> Bool { 30 | return try inlinedDecode(type) 31 | } 32 | 33 | func decode(_ type: Int.Type) throws -> Int { 34 | return try inlinedDecode(type) 35 | } 36 | 37 | func decode(_ type: Int8.Type) throws -> Int8 { 38 | return try inlinedDecode(type) 39 | } 40 | 41 | func decode(_ type: Int16.Type) throws -> Int16 { 42 | return try inlinedDecode(type) 43 | } 44 | 45 | func decode(_ type: Int32.Type) throws -> Int32 { 46 | return try inlinedDecode(type) 47 | } 48 | 49 | func decode(_ type: Int64.Type) throws -> Int64 { 50 | return try inlinedDecode(type) 51 | } 52 | 53 | func decode(_ type: UInt.Type) throws -> UInt { 54 | return try inlinedDecode(type) 55 | } 56 | 57 | func decode(_ type: UInt8.Type) throws -> UInt8 { 58 | return try inlinedDecode(type) 59 | } 60 | 61 | func decode(_ type: UInt16.Type) throws -> UInt16 { 62 | return try inlinedDecode(type) 63 | } 64 | 65 | func decode(_ type: UInt32.Type) throws -> UInt32 { 66 | return try inlinedDecode(type) 67 | } 68 | 69 | func decode(_ type: UInt64.Type) throws -> UInt64 { 70 | return try inlinedDecode(type) 71 | } 72 | 73 | func decode(_ type: Float.Type) throws -> Float { 74 | return try inlinedDecode(type) 75 | } 76 | 77 | func decode(_ type: Double.Type) throws -> Double { 78 | return try inlinedDecode(type) 79 | } 80 | 81 | func decode(_ type: String.Type) throws -> String { 82 | return try inlinedDecode(type) 83 | } 84 | 85 | func decode(_ type: T.Type) throws -> T where T: Decodable { 86 | let decoder = try JSON.Decoder(value, options: options) 87 | return try T(from: decoder) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Sources/JSON/Decoding/UnkeyedDecodingContainer.swift: -------------------------------------------------------------------------------- 1 | class JSONUnkeyedDecodingContainer: UnkeyedDecodingContainer { 2 | var codingPath: [CodingKey] { 3 | return [] 4 | } 5 | 6 | var currentIndex: Int = 0 7 | let array: [JSON.Value] 8 | let options: JSON.Decoder.Options 9 | 10 | init(_ array: [JSON.Value], _ options: JSON.Decoder.Options) { 11 | self.array = array 12 | self.options = options 13 | } 14 | 15 | var count: Int? { 16 | return array.count 17 | } 18 | 19 | var isAtEnd: Bool { 20 | return currentIndex == array.count 21 | } 22 | 23 | @inline(__always) 24 | private func inlinedDecodeIfPresent( 25 | _ type: T.Type 26 | ) throws -> T? { 27 | guard let value = T(array[currentIndex]) else { 28 | if options.parseNullAsOptional, array[currentIndex] == .null { 29 | currentIndex += 1 30 | return nil 31 | } 32 | throw DecodingError.typeMismatch( 33 | type, .incompatible(with: array[currentIndex])) 34 | } 35 | currentIndex += 1 36 | return value 37 | } 38 | 39 | func decodeIfPresent(_ type: Bool.Type) throws -> Bool? { 40 | return try inlinedDecodeIfPresent(type) 41 | } 42 | 43 | func decodeIfPresent(_ type: Int.Type) throws -> Int? { 44 | return try inlinedDecodeIfPresent(type) 45 | } 46 | 47 | func decodeIfPresent(_ type: Int8.Type) throws -> Int8? { 48 | return try inlinedDecodeIfPresent(type) 49 | } 50 | 51 | func decodeIfPresent(_ type: Int16.Type) throws -> Int16? { 52 | return try inlinedDecodeIfPresent(type) 53 | } 54 | 55 | func decodeIfPresent(_ type: Int32.Type) throws -> Int32? { 56 | return try inlinedDecodeIfPresent(type) 57 | } 58 | 59 | func decodeIfPresent(_ type: Int64.Type) throws -> Int64? { 60 | return try inlinedDecodeIfPresent(type) 61 | } 62 | 63 | func decodeIfPresent(_ type: UInt.Type) throws -> UInt? { 64 | return try inlinedDecodeIfPresent(type) 65 | } 66 | 67 | func decodeIfPresent(_ type: UInt8.Type) throws -> UInt8? { 68 | return try inlinedDecodeIfPresent(type) 69 | } 70 | 71 | func decodeIfPresent(_ type: UInt16.Type) throws -> UInt16? { 72 | return try inlinedDecodeIfPresent(type) 73 | } 74 | 75 | func decodeIfPresent(_ type: UInt32.Type) throws -> UInt32? { 76 | return try inlinedDecodeIfPresent(type) 77 | } 78 | 79 | func decodeIfPresent(_ type: UInt64.Type) throws -> UInt64? { 80 | return try inlinedDecodeIfPresent(type) 81 | } 82 | 83 | func decodeIfPresent(_ type: Float.Type) throws -> Float? { 84 | return try inlinedDecodeIfPresent(type) 85 | } 86 | 87 | func decodeIfPresent(_ type: Double.Type) throws -> Double? { 88 | return try inlinedDecodeIfPresent(type) 89 | } 90 | 91 | func decodeIfPresent(_ type: String.Type) throws -> String? { 92 | return try inlinedDecodeIfPresent(type) 93 | } 94 | 95 | func decodeIfPresent( 96 | _ type: T.Type 97 | ) throws -> T? where T: Decodable { 98 | let decoder = try JSON.Decoder(array[currentIndex], options: options) 99 | let value = try T(from: decoder) 100 | currentIndex += 1 101 | return value 102 | } 103 | 104 | func nestedContainer( 105 | keyedBy type: NestedKey.Type 106 | ) throws -> KeyedDecodingContainer { 107 | guard case .object(let object) = array[currentIndex] else { 108 | throw DecodingError.typeMismatch([String: JSON.Value].self, nil) 109 | } 110 | currentIndex += 1 111 | let container = JSONKeyedDecodingContainer(object, options) 112 | return KeyedDecodingContainer(container) 113 | } 114 | 115 | func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { 116 | guard case .array(let array) = array[currentIndex] else { 117 | throw DecodingError.typeMismatch([JSON.Value].self, nil) 118 | } 119 | currentIndex += 1 120 | return JSONUnkeyedDecodingContainer(array, options) 121 | } 122 | 123 | func superDecoder() throws -> Swift.Decoder { 124 | return self 125 | } 126 | } 127 | 128 | // FIXME: 😞 129 | extension JSONUnkeyedDecodingContainer: Swift.Decoder { 130 | var userInfo: [CodingUserInfoKey: Any] { 131 | return [:] 132 | } 133 | 134 | func container( 135 | keyedBy type: Key.Type 136 | ) throws -> KeyedDecodingContainer { 137 | return try nestedContainer(keyedBy: type) 138 | } 139 | 140 | func unkeyedContainer() throws -> UnkeyedDecodingContainer { 141 | return try nestedUnkeyedContainer() 142 | } 143 | 144 | func singleValueContainer() throws -> SingleValueDecodingContainer { 145 | return self 146 | } 147 | } 148 | 149 | extension JSONUnkeyedDecodingContainer: SingleValueDecodingContainer { 150 | func decodeNil() -> Bool { 151 | guard case .null = array[currentIndex] else { 152 | return false 153 | } 154 | return true 155 | } 156 | 157 | func decode(_ type: Int.Type) throws -> Int { 158 | guard let value = try decodeIfPresent(Int.self) else { 159 | throw DecodingError.typeMismatch(type, .unexpectedNull()) 160 | } 161 | return value 162 | } 163 | 164 | @inline(__always) 165 | func decode(_ type: T.Type) throws -> T { 166 | guard let value = try decodeIfPresent(T.self) else { 167 | throw DecodingError.typeMismatch(type, .unexpectedNull()) 168 | } 169 | return value 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /Sources/JSON/Encoding/JSON+Encoder.swift: -------------------------------------------------------------------------------- 1 | import Stream 2 | import Codable 3 | 4 | extension JSON { 5 | public class Encoder: Swift.Encoder { 6 | public var codingPath: [CodingKey] { 7 | return [] 8 | } 9 | public var userInfo: [CodingUserInfoKey: Any] { 10 | return [:] 11 | } 12 | 13 | // FIXME: [Concurrency] should be async StreamWriter 14 | let storage: OutputByteStream 15 | 16 | // FIXME: [Concurrency] should be async StreamWriter 17 | init(_ writer: OutputByteStream) { 18 | self.storage = writer 19 | } 20 | 21 | func close() throws { 22 | try closeContainers(downTo: 0) 23 | } 24 | 25 | enum ContainerType { 26 | case keyed 27 | case unkeyed 28 | case single 29 | } 30 | 31 | var openedContainers = ContiguousArray() 32 | 33 | func openContainer(_ type: ContainerType) throws { 34 | switch type { 35 | case .keyed: storage.write(.curlyBracketOpen) 36 | case .unkeyed: storage.write(.squareBracketOpen) 37 | case .single: break 38 | } 39 | openedContainers.append(type) 40 | } 41 | 42 | func closeContainer() throws { 43 | if let type = openedContainers.popLast() { 44 | switch type { 45 | case .keyed: storage.write(.curlyBracketClose) 46 | case .unkeyed: storage.write(.squareBracketClose) 47 | case .single: break 48 | } 49 | } 50 | } 51 | 52 | func closeContainers(downTo index: Int) throws { 53 | precondition(openedContainers.count >= index, "invalid stack") 54 | guard openedContainers.count > index else { 55 | return 56 | } 57 | while openedContainers.count > index { 58 | try closeContainer() 59 | } 60 | } 61 | 62 | public func container( 63 | keyedBy type: Key.Type 64 | ) -> KeyedEncodingContainer { 65 | do { 66 | try openContainer(.keyed) 67 | let container = JSONKeyedEncodingContainer(self) 68 | return KeyedEncodingContainer(container) 69 | } catch { 70 | return KeyedEncodingContainer(KeyedEncodingError(error)) 71 | } 72 | } 73 | 74 | public func unkeyedContainer() -> UnkeyedEncodingContainer { 75 | do { 76 | try openContainer(.unkeyed) 77 | return JSONUnkeyedEncodingContainer(self) 78 | } catch { 79 | return EncodingError(error) 80 | } 81 | } 82 | 83 | public func singleValueContainer() -> SingleValueEncodingContainer { 84 | do { 85 | try openContainer(.single) 86 | return JSONSingleValueEncodingContainer(self) 87 | } catch { 88 | return EncodingError(error) 89 | } 90 | } 91 | } 92 | } 93 | 94 | extension JSON.Encoder { 95 | func encodeNil() throws { 96 | storage.write(.null) 97 | } 98 | 99 | func encode(_ value: Bool) throws { 100 | storage.write(value ? .true : .false) 101 | } 102 | 103 | func encode(_ value: Int) throws { 104 | storage.write(String(describing: value)) 105 | } 106 | 107 | func encode(_ value: Int8) throws { 108 | storage.write(String(describing: value)) 109 | } 110 | 111 | func encode(_ value: Int16) throws { 112 | storage.write(String(describing: value)) 113 | } 114 | 115 | func encode(_ value: Int32) throws { 116 | storage.write(String(describing: value)) 117 | } 118 | 119 | func encode(_ value: Int64) throws { 120 | storage.write(String(describing: value)) 121 | } 122 | 123 | func encode(_ value: UInt) throws { 124 | storage.write(String(describing: value)) 125 | } 126 | 127 | func encode(_ value: UInt8) throws { 128 | storage.write(String(describing: value)) 129 | } 130 | 131 | func encode(_ value: UInt16) throws { 132 | storage.write(String(describing: value)) 133 | } 134 | 135 | func encode(_ value: UInt32) throws { 136 | storage.write(String(describing: value)) 137 | } 138 | 139 | func encode(_ value: UInt64) throws { 140 | storage.write(String(describing: value)) 141 | } 142 | 143 | func encode(_ value: Float) throws { 144 | storage.write(String(describing: value)) 145 | } 146 | 147 | func encode(_ value: Double) throws { 148 | storage.write(String(describing: value)) 149 | } 150 | 151 | func encode(_ value: String) throws { 152 | storage.write(.doubleQuote) 153 | 154 | for scalar in value.unicodeScalars { 155 | switch scalar { 156 | case "\"": 157 | storage.write(.backslash) 158 | storage.write(.doubleQuote) 159 | case "\\": 160 | storage.write(.backslash) 161 | storage.write(.backslash) 162 | case "\n": 163 | storage.write(.backslash) 164 | storage.write(.n) 165 | case "\r": 166 | storage.write(.backslash) 167 | storage.write(.r) 168 | case "\t": 169 | storage.write(.backslash) 170 | storage.write(.t) 171 | case "\u{8}": 172 | storage.write(.backslash) 173 | storage.write(.b) 174 | case "\u{c}": 175 | storage.write(.backslash) 176 | storage.write(.f) 177 | case "\u{0}"..."\u{f}": 178 | storage.write("\\u000") 179 | storage.write(String(scalar.value, radix: 16)) 180 | case "\u{10}"..."\u{1f}": 181 | storage.write("\\u00") 182 | storage.write(String(scalar.value, radix: 16)) 183 | default: 184 | guard let utf8 = UTF8.encode(scalar) else { 185 | throw JSON.Error.invalidJSON 186 | } 187 | utf8.forEach(storage.write) 188 | } 189 | } 190 | 191 | storage.write(.doubleQuote) 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /Sources/JSON/Encoding/JSON+withScopedEncoder.swift: -------------------------------------------------------------------------------- 1 | import Stream 2 | 3 | extension JSON { 4 | public static func withScopedEncoder( 5 | using writer: StreamWriter, 6 | // FIXME: [Concurrency] should be async 7 | _ body: (Encoder) throws -> T 8 | ) async throws -> T { 9 | let stream = OutputByteStream() 10 | let encoder = Encoder(stream) 11 | let result = try body(encoder) 12 | try encoder.close() 13 | try await writer.write(stream.bytes) 14 | return result 15 | } 16 | 17 | public static func withScopedEncoder( 18 | using stream: OutputByteStream, 19 | _ body: (Encoder) throws -> T 20 | ) throws -> T { 21 | let encoder = Encoder(stream) 22 | let result = try body(encoder) 23 | try encoder.close() 24 | return result 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/JSON/Encoding/KeyedEncodingContainer.swift: -------------------------------------------------------------------------------- 1 | import Stream 2 | import Codable 3 | 4 | struct JSONKeyedEncodingContainer 5 | : KeyedEncodingContainerProtocol { 6 | typealias Key = K 7 | 8 | var codingPath: [CodingKey] { 9 | return [] 10 | } 11 | 12 | let encoder: JSON.Encoder 13 | let nestingLevel: Int 14 | 15 | init(_ encoder: JSON.Encoder) { 16 | self.encoder = encoder 17 | nestingLevel = encoder.openedContainers.count 18 | } 19 | 20 | var hasValues = false 21 | mutating func writeCommaIfNeeded() throws { 22 | guard _slowPath(hasValues) else { 23 | hasValues = true 24 | return 25 | } 26 | encoder.storage.write(.comma) 27 | } 28 | 29 | mutating func writeKey(_ key: String) throws { 30 | encoder.storage.write(.doubleQuote) 31 | encoder.storage.write(key) 32 | encoder.storage.write(.doubleQuote) 33 | encoder.storage.write(.colon) 34 | } 35 | 36 | var hasNested = false 37 | mutating func closeNestedIfNeeded() throws { 38 | if hasNested { 39 | try encoder.closeContainers(downTo: nestingLevel) 40 | hasNested = false 41 | } 42 | } 43 | 44 | mutating func encodeNil(forKey key: K) throws { 45 | try closeNestedIfNeeded() 46 | try writeCommaIfNeeded() 47 | try writeKey(key.stringValue) 48 | encoder.storage.write(.null) 49 | } 50 | 51 | mutating func encode(_ value: Bool, forKey key: K) throws { 52 | try closeNestedIfNeeded() 53 | try writeCommaIfNeeded() 54 | try writeKey(key.stringValue) 55 | try encoder.encode(value) 56 | } 57 | 58 | mutating func encode(_ value: Int, forKey key: K) throws { 59 | try closeNestedIfNeeded() 60 | try writeCommaIfNeeded() 61 | try writeKey(key.stringValue) 62 | try encoder.encode(value) 63 | } 64 | 65 | mutating func encode(_ value: Int8, forKey key: K) throws { 66 | try closeNestedIfNeeded() 67 | try writeCommaIfNeeded() 68 | try writeKey(key.stringValue) 69 | try encoder.encode(value) 70 | } 71 | 72 | mutating func encode(_ value: Int16, forKey key: K) throws { 73 | try closeNestedIfNeeded() 74 | try writeCommaIfNeeded() 75 | try writeKey(key.stringValue) 76 | try encoder.encode(value) 77 | } 78 | 79 | mutating func encode(_ value: Int32, forKey key: K) throws { 80 | try closeNestedIfNeeded() 81 | try writeCommaIfNeeded() 82 | try writeKey(key.stringValue) 83 | try encoder.encode(value) 84 | } 85 | 86 | mutating func encode(_ value: Int64, forKey key: K) throws { 87 | try closeNestedIfNeeded() 88 | try writeCommaIfNeeded() 89 | try writeKey(key.stringValue) 90 | try encoder.encode(value) 91 | } 92 | 93 | mutating func encode(_ value: UInt, forKey key: K) throws { 94 | try closeNestedIfNeeded() 95 | try writeCommaIfNeeded() 96 | try writeKey(key.stringValue) 97 | try encoder.encode(value) 98 | } 99 | 100 | mutating func encode(_ value: UInt8, forKey key: K) throws { 101 | try closeNestedIfNeeded() 102 | try writeCommaIfNeeded() 103 | try writeKey(key.stringValue) 104 | try encoder.encode(value) 105 | } 106 | 107 | mutating func encode(_ value: UInt16, forKey key: K) throws { 108 | try closeNestedIfNeeded() 109 | try writeCommaIfNeeded() 110 | try writeKey(key.stringValue) 111 | try encoder.encode(value) 112 | } 113 | 114 | mutating func encode(_ value: UInt32, forKey key: K) throws { 115 | try closeNestedIfNeeded() 116 | try writeCommaIfNeeded() 117 | try writeKey(key.stringValue) 118 | try encoder.encode(value) 119 | } 120 | 121 | mutating func encode(_ value: UInt64, forKey key: K) throws { 122 | try closeNestedIfNeeded() 123 | try writeCommaIfNeeded() 124 | try writeKey(key.stringValue) 125 | try encoder.encode(value) 126 | } 127 | 128 | mutating func encode(_ value: Float, forKey key: K) throws { 129 | try closeNestedIfNeeded() 130 | try writeCommaIfNeeded() 131 | try writeKey(key.stringValue) 132 | try encoder.encode(value) 133 | } 134 | 135 | mutating func encode(_ value: Double, forKey key: K) throws { 136 | try closeNestedIfNeeded() 137 | try writeCommaIfNeeded() 138 | try writeKey(key.stringValue) 139 | try encoder.encode(value) 140 | } 141 | 142 | mutating func encode(_ value: String, forKey key: K) throws { 143 | try closeNestedIfNeeded() 144 | try writeCommaIfNeeded() 145 | try writeKey(key.stringValue) 146 | try encoder.encode(value) 147 | } 148 | 149 | mutating func encode( 150 | _ value: T, forKey key: K 151 | ) throws where T: Encodable { 152 | try closeNestedIfNeeded() 153 | try writeCommaIfNeeded() 154 | try writeKey(key.stringValue) 155 | hasNested = true 156 | try value.encode(to: encoder) 157 | } 158 | 159 | mutating func nestedContainer( 160 | keyedBy keyType: NestedKey.Type, 161 | forKey key: K 162 | ) -> KeyedEncodingContainer { 163 | do { 164 | try closeNestedIfNeeded() 165 | try writeCommaIfNeeded() 166 | hasNested = true 167 | return encoder.container(keyedBy: keyType) 168 | } catch { 169 | return KeyedEncodingContainer(KeyedEncodingError(error)) 170 | } 171 | } 172 | 173 | mutating func nestedUnkeyedContainer( 174 | forKey key: K 175 | ) -> UnkeyedEncodingContainer { 176 | do { 177 | try closeNestedIfNeeded() 178 | try writeCommaIfNeeded() 179 | try writeKey(key.stringValue) 180 | hasNested = true 181 | return encoder.unkeyedContainer() 182 | } catch { 183 | return EncodingError(error) 184 | } 185 | } 186 | 187 | mutating func superEncoder() -> Swift.Encoder { 188 | do { 189 | try closeNestedIfNeeded() 190 | try writeCommaIfNeeded() 191 | hasNested = true 192 | return encoder 193 | } catch { 194 | return EncodingError(error) 195 | } 196 | } 197 | 198 | mutating func superEncoder(forKey key: K) -> Swift.Encoder { 199 | do { 200 | try closeNestedIfNeeded() 201 | try writeCommaIfNeeded() 202 | try writeKey(key.stringValue) 203 | hasNested = true 204 | return encoder 205 | } catch { 206 | return EncodingError(error) 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /Sources/JSON/Encoding/SingleValueEncodingContainer.swift: -------------------------------------------------------------------------------- 1 | import Stream 2 | 3 | struct JSONSingleValueEncodingContainer: SingleValueEncodingContainer { 4 | var codingPath: [CodingKey] { 5 | return [] 6 | } 7 | 8 | let encoder: JSON.Encoder 9 | 10 | init(_ encoder: JSON.Encoder) { 11 | self.encoder = encoder 12 | } 13 | 14 | mutating func encodeNil() throws { 15 | try encoder.encodeNil() 16 | } 17 | 18 | mutating func encode(_ value: Bool) throws { 19 | try encoder.encode(value) 20 | } 21 | 22 | mutating func encode(_ value: Int) throws { 23 | try encoder.encode(value) 24 | } 25 | 26 | mutating func encode(_ value: Int8) throws { 27 | try encoder.encode(value) 28 | } 29 | 30 | mutating func encode(_ value: Int16) throws { 31 | try encoder.encode(value) 32 | } 33 | 34 | mutating func encode(_ value: Int32) throws { 35 | try encoder.encode(value) 36 | } 37 | 38 | mutating func encode(_ value: Int64) throws { 39 | try encoder.encode(value) 40 | } 41 | 42 | mutating func encode(_ value: UInt) throws { 43 | try encoder.encode(value) 44 | } 45 | 46 | mutating func encode(_ value: UInt8) throws { 47 | try encoder.encode(value) 48 | } 49 | 50 | mutating func encode(_ value: UInt16) throws { 51 | try encoder.encode(value) 52 | } 53 | 54 | mutating func encode(_ value: UInt32) throws { 55 | try encoder.encode(value) 56 | } 57 | 58 | mutating func encode(_ value: UInt64) throws { 59 | try encoder.encode(value) 60 | } 61 | 62 | mutating func encode(_ value: Float) throws { 63 | try encoder.encode(value) 64 | } 65 | 66 | mutating func encode(_ value: Double) throws { 67 | try encoder.encode(value) 68 | } 69 | 70 | mutating func encode(_ value: String) throws { 71 | try encoder.encode(value) 72 | } 73 | 74 | mutating func encode(_ value: T) throws where T: Encodable { 75 | try value.encode(to: encoder) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Sources/JSON/Encoding/UnkeyedEncodingContainer.swift: -------------------------------------------------------------------------------- 1 | import Stream 2 | import Codable 3 | 4 | struct JSONUnkeyedEncodingContainer: UnkeyedEncodingContainer { 5 | var codingPath: [CodingKey] { 6 | return [] 7 | } 8 | 9 | let encoder: JSON.Encoder 10 | let nestingLevel: Int 11 | var count: Int 12 | 13 | init(_ encoder: JSON.Encoder) { 14 | self.encoder = encoder 15 | self.nestingLevel = encoder.openedContainers.count 16 | self.count = 0 17 | } 18 | 19 | var hasValues = false 20 | mutating func writeCommaIfNeeded() throws { 21 | guard _slowPath(hasValues) else { 22 | hasValues = true 23 | return 24 | } 25 | encoder.storage.write(",") 26 | } 27 | 28 | var hasNested = false 29 | mutating func closeNestedIfNeeded() throws { 30 | if hasNested { 31 | try encoder.closeContainers(downTo: nestingLevel) 32 | hasNested = false 33 | } 34 | } 35 | 36 | mutating func encodeNil() throws { 37 | try closeNestedIfNeeded() 38 | try writeCommaIfNeeded() 39 | try encoder.encodeNil() 40 | count += 1 41 | } 42 | 43 | mutating func encode(_ value: Bool) throws { 44 | try closeNestedIfNeeded() 45 | try writeCommaIfNeeded() 46 | try encoder.encode(value) 47 | count += 1 48 | } 49 | 50 | mutating func encode(_ value: Int) throws { 51 | try closeNestedIfNeeded() 52 | try writeCommaIfNeeded() 53 | try encoder.encode(value) 54 | count += 1 55 | } 56 | 57 | mutating func encode(_ value: Int8) throws { 58 | try closeNestedIfNeeded() 59 | try writeCommaIfNeeded() 60 | try encoder.encode(value) 61 | count += 1 62 | } 63 | 64 | mutating func encode(_ value: Int16) throws { 65 | try closeNestedIfNeeded() 66 | try writeCommaIfNeeded() 67 | try encoder.encode(value) 68 | count += 1 69 | } 70 | 71 | mutating func encode(_ value: Int32) throws { 72 | try closeNestedIfNeeded() 73 | try writeCommaIfNeeded() 74 | try encoder.encode(value) 75 | count += 1 76 | } 77 | 78 | mutating func encode(_ value: Int64) throws { 79 | try closeNestedIfNeeded() 80 | try writeCommaIfNeeded() 81 | try encoder.encode(value) 82 | count += 1 83 | } 84 | 85 | mutating func encode(_ value: UInt) throws { 86 | try closeNestedIfNeeded() 87 | try writeCommaIfNeeded() 88 | try encoder.encode(value) 89 | count += 1 90 | } 91 | 92 | mutating func encode(_ value: UInt8) throws { 93 | try closeNestedIfNeeded() 94 | try writeCommaIfNeeded() 95 | try encoder.encode(value) 96 | count += 1 97 | } 98 | 99 | mutating func encode(_ value: UInt16) throws { 100 | try closeNestedIfNeeded() 101 | try writeCommaIfNeeded() 102 | try encoder.encode(value) 103 | count += 1 104 | } 105 | 106 | mutating func encode(_ value: UInt32) throws { 107 | try closeNestedIfNeeded() 108 | try writeCommaIfNeeded() 109 | try encoder.encode(value) 110 | count += 1 111 | } 112 | 113 | mutating func encode(_ value: UInt64) throws { 114 | try closeNestedIfNeeded() 115 | try writeCommaIfNeeded() 116 | try encoder.encode(value) 117 | count += 1 118 | } 119 | 120 | mutating func encode(_ value: Float) throws { 121 | try closeNestedIfNeeded() 122 | try writeCommaIfNeeded() 123 | try encoder.encode(value) 124 | count += 1 125 | } 126 | 127 | mutating func encode(_ value: Double) throws { 128 | try closeNestedIfNeeded() 129 | try writeCommaIfNeeded() 130 | try encoder.encode(value) 131 | count += 1 132 | } 133 | 134 | mutating func encode(_ value: String) throws { 135 | try closeNestedIfNeeded() 136 | try writeCommaIfNeeded() 137 | try encoder.encode(value) 138 | count += 1 139 | } 140 | 141 | mutating func encode(_ value: T) throws where T: Encodable { 142 | try closeNestedIfNeeded() 143 | try writeCommaIfNeeded() 144 | hasNested = true 145 | try value.encode(to: encoder) 146 | count += 1 147 | } 148 | 149 | mutating func nestedContainer( 150 | keyedBy keyType: NestedKey.Type 151 | ) -> KeyedEncodingContainer { 152 | do { 153 | try closeNestedIfNeeded() 154 | try writeCommaIfNeeded() 155 | hasNested = true 156 | count += 1 157 | return encoder.container(keyedBy: keyType) 158 | } catch { 159 | return KeyedEncodingContainer(KeyedEncodingError(error)) 160 | } 161 | } 162 | 163 | mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { 164 | do { 165 | try closeNestedIfNeeded() 166 | try writeCommaIfNeeded() 167 | hasNested = true 168 | count += 1 169 | return encoder.unkeyedContainer() 170 | } catch { 171 | return EncodingError(error) 172 | } 173 | } 174 | 175 | mutating func superEncoder() -> Swift.Encoder { 176 | do { 177 | try closeNestedIfNeeded() 178 | try writeCommaIfNeeded() 179 | hasNested = true 180 | return encoder 181 | } catch { 182 | return EncodingError(error) 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /Sources/JSON/JSON+Value/Accessors.swift: -------------------------------------------------------------------------------- 1 | extension JSON.Value { 2 | public var booleanValue: Bool? { 3 | switch self { 4 | case .bool(let value): return value 5 | default: return nil 6 | } 7 | } 8 | 9 | public var integerValue: Int? { 10 | switch self { 11 | case .number(.int(let value)): return value 12 | case .number(.uint(let value)) where value < UInt(Int.max): 13 | return Int(value) 14 | default: return nil 15 | } 16 | } 17 | 18 | public var unsignedValue: UInt? { 19 | switch self { 20 | case .number(.uint(let value)): return value 21 | case .number(.int(let value)) where value >= 0: return UInt(value) 22 | default: return nil 23 | } 24 | } 25 | 26 | public var doubleValue: Double? { 27 | switch self { 28 | case .number(.double(let value)): return value 29 | case .number(.int(let value)): return Double(value) 30 | case .number(.uint(let value)): return Double(value) 31 | default: return nil 32 | } 33 | } 34 | 35 | public var stringValue: String? { 36 | switch self { 37 | case .string(let value): return value 38 | default: return nil 39 | } 40 | } 41 | 42 | public subscript(index: Int) -> JSON.Value? { 43 | switch self { 44 | case .array(let array) where index < array.count: return array[index] 45 | default: return nil 46 | } 47 | } 48 | 49 | public subscript(key: String) -> JSON.Value? { 50 | switch self { 51 | case .object(let object): return object[key] 52 | default: return nil 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/JSON/JSON+Value/Characters.swift: -------------------------------------------------------------------------------- 1 | extension Set where Element == UInt8 { 2 | static let controls: Set = [.cr, .lf, .tab] 3 | static var whitespaces: Set = [.whitespace, .cr, .lf, .tab] 4 | } 5 | -------------------------------------------------------------------------------- /Sources/JSON/JSON+Value/Error.swift: -------------------------------------------------------------------------------- 1 | extension JSON { 2 | public enum Error: Swift.Error { 3 | case invalidJSON 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /Sources/JSON/JSON+Value/Hex.swift: -------------------------------------------------------------------------------- 1 | extension Int { 2 | init(hex bytes: T) where T.Element == UInt8 { 3 | var result = 0 4 | for byte in bytes { 5 | result = result &* 16 &+ Int(hexTable[Int(byte)]) 6 | } 7 | self = result 8 | } 9 | } 10 | 11 | private let hexTable: [UInt8] = [ 12 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 13 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 14 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 15 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 16 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 17 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 18 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 19 | 0x08, 0x09, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 20 | 0x80, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x80, 21 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 22 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 23 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 24 | 0x80, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x80, 25 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 26 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 27 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 28 | 29 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 30 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 31 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 32 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 33 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 34 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 35 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 36 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 37 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 38 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 39 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 40 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 41 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 42 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 43 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 44 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 45 | ] 46 | -------------------------------------------------------------------------------- /Sources/JSON/JSON+Value/JSONValueInitializable.swift: -------------------------------------------------------------------------------- 1 | public protocol JSONValueInitializable { 2 | init?(_ value: JSON.Value) 3 | } 4 | 5 | extension Bool: JSONValueInitializable { 6 | public init?(_ value: JSON.Value) { 7 | guard case .bool(let value) = value else { 8 | return nil 9 | } 10 | self = value 11 | } 12 | } 13 | 14 | extension Int: JSONValueInitializable { 15 | public init?(_ value: JSON.Value) { 16 | guard case .number(let number) = value else { 17 | return nil 18 | } 19 | switch number { 20 | case .int(let value): self = value 21 | case .uint(let value) where value <= Int.max: self = Int(value) 22 | default: return nil 23 | } 24 | } 25 | } 26 | 27 | extension Int8: JSONValueInitializable { 28 | public init?(_ value: JSON.Value) { 29 | guard case .number(let number) = value else { 30 | return nil 31 | } 32 | switch number { 33 | case .int(let value) where value <= Int8.max: self = Int8(value) 34 | case .uint(let value) where value <= Int8.max: self = Int8(value) 35 | default: return nil 36 | } 37 | } 38 | } 39 | 40 | extension Int16: JSONValueInitializable { 41 | public init?(_ value: JSON.Value) { 42 | guard case .number(let number) = value else { 43 | return nil 44 | } 45 | switch number { 46 | case .int(let value) where value <= Int16.max: self = Int16(value) 47 | case .uint(let value) where value <= Int16.max: self = Int16(value) 48 | default: return nil 49 | } 50 | } 51 | } 52 | 53 | extension Int32: JSONValueInitializable { 54 | public init?(_ value: JSON.Value) { 55 | guard case .number(let number) = value else { 56 | return nil 57 | } 58 | switch number { 59 | case .int(let value) where value <= Int32.max: self = Int32(value) 60 | case .uint(let value) where value <= Int32.max: self = Int32(value) 61 | default: return nil 62 | } 63 | } 64 | } 65 | 66 | extension Int64: JSONValueInitializable { 67 | public init?(_ value: JSON.Value) { 68 | guard case .number(let number) = value else { 69 | return nil 70 | } 71 | switch number { 72 | case .int(let value) where value <= Int64.max: self = Int64(value) 73 | case .uint(let value) where value <= Int64.max: self = Int64(value) 74 | default: return nil 75 | } 76 | } 77 | } 78 | 79 | extension UInt: JSONValueInitializable { 80 | public init?(_ value: JSON.Value) { 81 | guard case .number(let number) = value else { 82 | return nil 83 | } 84 | switch number { 85 | case .int(let value) where value >= 0: self = UInt(value) 86 | case .uint(let value): self = value 87 | default: return nil 88 | } 89 | } 90 | } 91 | 92 | extension UInt8: JSONValueInitializable { 93 | public init?(_ value: JSON.Value) { 94 | guard case .number(let number) = value else { 95 | return nil 96 | } 97 | switch number { 98 | case .int(let value) where value >= 0 && value <= UInt8.max: 99 | self = UInt8(value) 100 | case .uint(let value) where value <= UInt8.max: 101 | self = UInt8(value) 102 | default: return nil 103 | } 104 | } 105 | } 106 | 107 | extension UInt16: JSONValueInitializable { 108 | public init?(_ value: JSON.Value) { 109 | guard case .number(let number) = value else { 110 | return nil 111 | } 112 | switch number { 113 | case .int(let value) where value >= 0 && value <= UInt16.max: 114 | self = UInt16(value) 115 | case .uint(let value) where value <= UInt16.max: 116 | self = UInt16(value) 117 | default: return nil 118 | } 119 | } 120 | } 121 | 122 | extension UInt32: JSONValueInitializable { 123 | public init?(_ value: JSON.Value) { 124 | guard case .number(let number) = value else { 125 | return nil 126 | } 127 | switch number { 128 | case .int(let value) where value >= 0 && value <= UInt32.max: 129 | self = UInt32(value) 130 | case .uint(let value) where value <= UInt32.max: 131 | self = UInt32(value) 132 | default: return nil 133 | } 134 | } 135 | } 136 | 137 | extension UInt64: JSONValueInitializable { 138 | public init?(_ value: JSON.Value) { 139 | guard case .number(let number) = value else { 140 | return nil 141 | } 142 | switch number { 143 | case .int(let value) where value >= 0 && value <= UInt64.max: 144 | self = UInt64(value) 145 | case .uint(let value) where value <= UInt64.max: 146 | self = UInt64(value) 147 | default: return nil 148 | } 149 | } 150 | } 151 | 152 | extension Float: JSONValueInitializable { 153 | public init?(_ value: JSON.Value) { 154 | guard case .number(let number) = value else { 155 | return nil 156 | } 157 | // TODO: check maximum value without losing precision 158 | switch number { 159 | case .double(let value): 160 | self = Float(value) 161 | case .int(let value): 162 | self = Float(value) 163 | case .uint(let value): 164 | self = Float(value) 165 | } 166 | } 167 | } 168 | 169 | extension Double: JSONValueInitializable { 170 | public init?(_ value: JSON.Value) { 171 | guard case .number(let number) = value else { 172 | return nil 173 | } 174 | // TODO: check maximum value without losing precision 175 | switch number { 176 | case .double(let value): 177 | self = value 178 | case .int(let value): 179 | self = Double(value) 180 | case .uint(let value): 181 | self = Double(value) 182 | } 183 | } 184 | } 185 | 186 | extension String: JSONValueInitializable { 187 | public init?(_ value: JSON.Value) { 188 | guard case .string(let value) = value else { 189 | return nil 190 | } 191 | self = value 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /Sources/JSON/JSON+Value/Value+Array.swift: -------------------------------------------------------------------------------- 1 | import Stream 2 | 3 | extension Array where Element == JSON.Value { 4 | public static func decode(from stream: StreamReader) async throws -> Self { 5 | guard try await stream.consume(.squareBracketOpen) else { 6 | throw JSON.Error.invalidJSON 7 | } 8 | var result = [JSON.Value]() 9 | loop: while true { 10 | try await stream.consume(set: .whitespaces) 11 | 12 | switch try await stream.peek() { 13 | case .squareBracketClose: 14 | try await stream.consume(count: 1) 15 | break loop 16 | case .comma: 17 | try await stream.consume(count: 1) 18 | default: 19 | result.append(try await JSON.Value.decode(from: stream)) 20 | } 21 | } 22 | return result 23 | } 24 | 25 | public func encode(to stream: StreamWriter) async throws { 26 | try await stream.write(.squareBracketOpen) 27 | var needComma = false 28 | for value in self { 29 | switch needComma { 30 | case true: try await stream.write(.comma) 31 | case false: needComma = true 32 | } 33 | try await value.encode(to: stream) 34 | } 35 | try await stream.write(.squareBracketClose) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/JSON/JSON+Value/Value+Number.swift: -------------------------------------------------------------------------------- 1 | import Stream 2 | import Platform 3 | 4 | extension JSON.Value.Number { 5 | public static func decode(from stream: StreamReader) async throws -> Self { 6 | let isNegative = try await stream.consume(.hyphen) ? true : false 7 | var isInteger = true 8 | 9 | var string = [UInt8]() 10 | 11 | try await stream.read(while: isDigit) { bytes in 12 | string.append(contentsOf: bytes) 13 | } 14 | 15 | if (try? await stream.consume(.dot)) ?? false { 16 | isInteger = false 17 | string.append(.dot) 18 | try await stream.read(while: isDigit) { bytes in 19 | string.append(contentsOf: bytes) 20 | } 21 | } 22 | string.append(0) 23 | 24 | let casted = unsafeBitCast(string, to: [Int8].self) 25 | 26 | switch isNegative { 27 | case true: 28 | switch isInteger { 29 | case true: return .int(-strtol(casted, nil, 10)) 30 | case false: return .double(-strtod(casted, nil)) 31 | } 32 | case false: 33 | switch isInteger { 34 | case true: return .uint(strtoul(casted, nil, 10)) 35 | case false: return .double(strtod(casted, nil)) 36 | } 37 | } 38 | } 39 | 40 | public func encode(to stream: StreamWriter) async throws { 41 | switch self { 42 | case .int(let value): try await stream.write(String(value)) 43 | case .uint(let value): try await stream.write(String(value)) 44 | case .double(let value): try await stream.write(String(value)) 45 | } 46 | } 47 | 48 | private static func isDigit(_ byte: UInt8) -> Bool { 49 | byte >= .zero && byte <= .nine 50 | } 51 | } 52 | 53 | extension JSON.Value.Number: Equatable { 54 | public static func == ( 55 | lhs: JSON.Value.Number, 56 | rhs: JSON.Value.Number 57 | ) -> Bool { 58 | switch (lhs, rhs) { 59 | case let (.int(lhs), .int(rhs)): return lhs == rhs 60 | case let (.uint(lhs), .uint(rhs)): return lhs == rhs 61 | case let (.double(lhs), .double(rhs)): return lhs == rhs 62 | default: return false 63 | } 64 | } 65 | } 66 | 67 | extension JSON.Value.Number: CustomStringConvertible { 68 | public var description: String { 69 | switch self { 70 | case .int(let int): return int.description 71 | case .uint(let uint): return uint.description 72 | case .double(let double): return double.description 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Sources/JSON/JSON+Value/Value+Object.swift: -------------------------------------------------------------------------------- 1 | import Stream 2 | 3 | extension Dictionary where Key == String, Value == JSON.Value { 4 | public static func decode(from stream: StreamReader) async throws -> Self { 5 | guard try await stream.consume(.curlyBracketOpen) else { 6 | throw JSON.Error.invalidJSON 7 | } 8 | 9 | var result = [String: JSON.Value]() 10 | loop: while true { 11 | try await stream.consume(set: .whitespaces) 12 | 13 | switch try await stream.peek() { 14 | case .curlyBracketClose: 15 | try await stream.consume(count: 1) 16 | break loop 17 | case .doubleQuote: 18 | let key = try await String.decode(from: stream) 19 | try await stream.consume(set: .whitespaces) 20 | guard try await stream.consume(.colon) else { 21 | throw JSON.Error.invalidJSON 22 | } 23 | try await stream.consume(set: .whitespaces) 24 | result[key] = try await JSON.Value.decode(from: stream) 25 | case .comma: 26 | try await stream.consume(count: 1) 27 | default: 28 | throw JSON.Error.invalidJSON 29 | } 30 | } 31 | return result 32 | } 33 | 34 | public func encode(to stream: StreamWriter) async throws { 35 | try await stream.write(.curlyBracketOpen) 36 | for (key, value) in self { 37 | try await stream.write(.doubleQuote) 38 | try await stream.write(key) 39 | try await stream.write(.doubleQuote) 40 | try await stream.write(.colon) 41 | try await value.encode(to: stream) 42 | } 43 | try await stream.write(.curlyBracketClose) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/JSON/JSON+Value/Value+String.swift: -------------------------------------------------------------------------------- 1 | import Stream 2 | 3 | extension String { 4 | static func decode(from stream: StreamReader) async throws -> Self { 5 | guard try await stream.consume(.doubleQuote) else { 6 | throw JSON.Error.invalidJSON 7 | } 8 | 9 | var result: [UInt8] = [] 10 | 11 | func readEscaped() async throws { 12 | switch try await stream.read(UInt8.self) { 13 | case .doubleQuote: result.append(.doubleQuote) 14 | case .n: result.append(.lf) 15 | case .r: result.append(.cr) 16 | case .t: result.append(.tab) 17 | case .backslash: result.append(.backslash) 18 | case .u: try await readUnicodeScalar() 19 | default: throw JSON.Error.invalidJSON 20 | } 21 | } 22 | 23 | func readUnicodeScalar() async throws { 24 | let code = try await stream.read(count: 4) { buffer in 25 | return Int(hex: buffer) 26 | } 27 | guard 28 | let scalar = Unicode.Scalar(code), 29 | let encoded = UTF8.encode(scalar) 30 | else { 31 | throw JSON.Error.invalidJSON 32 | } 33 | 34 | result.append(contentsOf: encoded) 35 | } 36 | 37 | loop: while true { 38 | let byte = try await stream.read(UInt8.self) 39 | switch byte { 40 | case .doubleQuote: break loop 41 | case .backslash: try await readEscaped() 42 | case _ where !byte.isControl: result.append(byte) 43 | default: throw JSON.Error.invalidJSON 44 | } 45 | } 46 | 47 | return String(decoding: result, as: UTF8.self) 48 | } 49 | } 50 | 51 | extension UInt8 { 52 | var isControl: Bool { Set.controls.contains(self) } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/JSON/JSON+Value/Value.swift: -------------------------------------------------------------------------------- 1 | import Stream 2 | 3 | extension JSON.Value { 4 | public static func decode(from stream: StreamReader) async throws -> Self { 5 | try await stream.consume(set: .whitespaces) 6 | 7 | func consume(_ value: [UInt8]) async throws { 8 | guard try await stream.consume(sequence: value) else { 9 | throw JSON.Error.invalidJSON 10 | } 11 | } 12 | 13 | switch try await stream.peek() { 14 | case .curlyBracketOpen: 15 | return .object(try await [String: JSON.Value].decode(from: stream)) 16 | 17 | case .squareBracketOpen: 18 | return .array(try await [JSON.Value].decode(from: stream)) 19 | 20 | case .n: 21 | try await consume(.null) 22 | return .null 23 | 24 | case .t: 25 | try await consume(.true) 26 | return .bool(true) 27 | 28 | case .f: 29 | try await consume(.false) 30 | return .bool(false) 31 | 32 | case (.zero)...(.nine), .hyphen: 33 | return .number(try await Number.decode(from: stream)) 34 | 35 | case .doubleQuote: 36 | return .string(try await String.decode(from: stream)) 37 | 38 | default: 39 | throw JSON.Error.invalidJSON 40 | } 41 | } 42 | 43 | public func encode(to stream: StreamWriter) async throws { 44 | switch self { 45 | case .null: 46 | try await stream.write(.null) 47 | case .bool(let value): 48 | try await stream.write(value ? .true : .false) 49 | case .number(let number): 50 | try await number.encode(to: stream) 51 | case .string(let string): 52 | try await stream.write(.doubleQuote) 53 | try await stream.write(string) 54 | try await stream.write(.doubleQuote) 55 | case .array(let values): 56 | try await values.encode(to: stream) 57 | case .object(let object): 58 | try await object.encode(to: stream) 59 | } 60 | } 61 | } 62 | 63 | extension JSON.Value: Equatable { 64 | public static func == (lhs: JSON.Value, rhs: JSON.Value) -> Bool { 65 | switch (lhs, rhs) { 66 | case (.null, .null): return true 67 | case let (.bool(lhs), .bool(rhs)): return lhs == rhs 68 | case let (.number(lhs), .number(rhs)): return lhs == rhs 69 | case let (.string(lhs), .string(rhs)): return lhs == rhs 70 | case let (.array(lhs), .array(rhs)): return lhs == rhs 71 | case let (.object(lhs), .object(rhs)): return lhs == rhs 72 | default: return false 73 | } 74 | } 75 | } 76 | 77 | extension JSON.Value: CustomStringConvertible { 78 | public var description: String { 79 | switch self { 80 | case .null: return "null" 81 | case .bool(let value): return value.description 82 | case .number(let value): return value.description 83 | case .string(let value): return "\"\(value)\"" 84 | case .array(let value): return value.description 85 | case .object(let value): return value.description 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Sources/JSON/JSON.swift: -------------------------------------------------------------------------------- 1 | import Stream 2 | 3 | public struct JSON { 4 | @dynamicMemberLookup 5 | public enum Value { 6 | case null 7 | case bool(Bool) 8 | case number(Number) 9 | case string(String) 10 | case array([JSON.Value]) 11 | case object([String: JSON.Value]) 12 | 13 | public enum Number { 14 | case int(Int) 15 | case uint(UInt) 16 | case double(Double) 17 | } 18 | 19 | public subscript(dynamicMember member: String) -> Value? { 20 | get { 21 | switch self { 22 | case .object(let object): return object[member] 23 | default: return nil 24 | } 25 | } 26 | set { 27 | switch self { 28 | case .object(var object): 29 | object[member] = newValue 30 | self = .object(object) 31 | default: 32 | switch newValue { 33 | case .some(let value): 34 | self = .object([member: value]) 35 | case .none: 36 | self = .object([:]) 37 | } 38 | } 39 | } 40 | } 41 | } 42 | } 43 | 44 | // MARK: generic 45 | 46 | extension JSON { 47 | public static func encode( 48 | _ value: Model, 49 | to stream: StreamWriter 50 | ) async throws { 51 | try await withScopedEncoder(using: stream) { encoder in 52 | try value.encode(to: encoder) 53 | } 54 | } 55 | 56 | public static func decode( 57 | _ type: Model.Type, 58 | from stream: StreamReader, 59 | options: Decoder.Options = .default 60 | ) async throws -> Model { 61 | try await withScopedDecoder( 62 | using: stream, 63 | options: options 64 | ) { decoder in 65 | try Model(from: decoder) 66 | } 67 | } 68 | } 69 | 70 | // MARK: type-erased 71 | 72 | extension JSON { 73 | public static func encode( 74 | encodable value: Encodable, 75 | to stream: StreamWriter 76 | ) async throws { 77 | try await withScopedEncoder(using: stream) { encoder in 78 | try value.encode(to: encoder) 79 | } 80 | } 81 | 82 | public static func decode( 83 | decodable type: Decodable.Type, 84 | from stream: StreamReader, 85 | options: Decoder.Options = .default 86 | ) async throws -> Decodable { 87 | try await withScopedDecoder( 88 | using: stream, 89 | options: options 90 | ) { decoder in 91 | try type.init(from: decoder) 92 | } 93 | } 94 | } 95 | 96 | // MARK: [UInt8] 97 | 98 | extension JSON { 99 | public static func encode(_ value: T) throws -> [UInt8] { 100 | // FIXME: [Concurrency] 101 | let stream = OutputByteStream() 102 | let encoder = Encoder(stream) 103 | try value.encode(to: encoder) 104 | try encoder.close() 105 | return stream.bytes 106 | } 107 | 108 | public static func decode( 109 | _ type: T.Type, 110 | from json: [UInt8], 111 | options: Decoder.Options = .default 112 | ) async throws -> T { 113 | // FIXME: [Concurrency] should be sync 114 | try await decode(type, from: InputByteStream(json), options: options) 115 | } 116 | 117 | public static func encode(encodable value: Encodable) throws -> [UInt8] { 118 | // FIXME: [Concurrency] 119 | let stream = OutputByteStream() 120 | let encoder = Encoder(stream) 121 | try value.encode(to: encoder) 122 | try encoder.close() 123 | return stream.bytes 124 | } 125 | 126 | public static func decode( 127 | decodable type: Decodable.Type, 128 | from json: [UInt8], 129 | options: Decoder.Options = .default 130 | ) async throws -> Decodable { 131 | // FIXME: [Concurrency] should be sync 132 | try await decode(decodable: type, 133 | from: InputByteStream(json), 134 | options: options) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Tests/Codable/Decoder/main.swift: -------------------------------------------------------------------------------- 1 | import Test 2 | import Stream 3 | 4 | @testable import JSON 5 | 6 | test("KeyedContainer") { 7 | let stream = InputByteStream(""" 8 | {"answer":42} 9 | """) 10 | let decoder = try await JSON.Decoder(stream) 11 | enum Keys: CodingKey { 12 | case answer 13 | } 14 | let container = try decoder.container(keyedBy: Keys.self) 15 | let answer = try container.decode(Int.self, forKey: .answer) 16 | expect(answer == 42) 17 | } 18 | 19 | test("UnkeyedContainer") { 20 | let stream = InputByteStream("[1,[2],[3],4]") 21 | let decoder = try await JSON.Decoder(stream) 22 | var container = try decoder.unkeyedContainer() 23 | let int1 = try container.decode(Int.self) 24 | var nested1 = try container.nestedUnkeyedContainer() 25 | let int2 = try nested1.decode(Int.self) 26 | var nested2 = try container.nestedUnkeyedContainer() 27 | let int3 = try nested2.decode(Int.self) 28 | let int4 = try container.decode(Int.self) 29 | expect(int1 == 1) 30 | expect(int2 == 2) 31 | expect(int3 == 3) 32 | expect(int4 == 4) 33 | } 34 | 35 | test("SingleValueContainer") { 36 | let stream = InputByteStream("true") 37 | let decoder = try await JSON.Decoder(stream) 38 | let container = try decoder.singleValueContainer() 39 | let bool = try container.decode(Bool.self) 40 | expect(bool == true) 41 | } 42 | 43 | await run() 44 | -------------------------------------------------------------------------------- /Tests/Codable/Encoder/main.swift: -------------------------------------------------------------------------------- 1 | import Test 2 | import Stream 3 | 4 | @testable import JSON 5 | 6 | test("KeyedContainer") { 7 | let expected = """ 8 | {"answer":42} 9 | """ 10 | let output = OutputByteStream() 11 | let encoder = JSON.Encoder(output) 12 | enum Keys: CodingKey { 13 | case answer 14 | } 15 | var container = encoder.container(keyedBy: Keys.self) 16 | try container.encode(42, forKey: .answer) 17 | try encoder.close() 18 | expect(output.stringValue == expected) 19 | } 20 | 21 | test("UnkeyedContainer") { 22 | let expected = "[1,[2],[3],4]" 23 | let output = OutputByteStream() 24 | let encoder = JSON.Encoder(output) 25 | var container = encoder.unkeyedContainer() 26 | try container.encode(1) 27 | var nested1 = container.nestedUnkeyedContainer() 28 | try nested1.encode(2) 29 | var nested2 = container.nestedUnkeyedContainer() 30 | try nested2.encode(3) 31 | try container.encode(4) 32 | try encoder.close() 33 | expect(output.stringValue == expected) 34 | } 35 | 36 | test("SingleValueContainer") { 37 | let expected = "true" 38 | let output = OutputByteStream() 39 | let encoder = JSON.Encoder(output) 40 | var container = encoder.singleValueContainer() 41 | try container.encode(true) 42 | expect(output.stringValue == expected) 43 | } 44 | 45 | await run() 46 | -------------------------------------------------------------------------------- /Tests/Codable/JSON.decode/main.swift: -------------------------------------------------------------------------------- 1 | import Test 2 | import Stream 3 | 4 | @testable import JSON 5 | 6 | test("Keyed") { 7 | let json = InputByteStream(#"{"answer":42,"hello":"Hello, World!"}"#) 8 | struct Model: Decodable { 9 | let answer: Int 10 | let hello: String 11 | } 12 | let model = try await JSON.decode(Model.self, from: json) 13 | expect(model.answer == 42) 14 | expect(model.hello == "Hello, World!") 15 | } 16 | 17 | test("DecodeEscaped") { 18 | let json = InputByteStream(""" 19 | { 20 | "answer":42, 21 | "hello":"Hello, World!" 22 | } 23 | """) 24 | struct Model: Decodable { 25 | let answer: Int 26 | let hello: String 27 | } 28 | let model = try await JSON.decode(Model.self, from: json) 29 | expect(model.answer == 42) 30 | expect(model.hello == "Hello, World!") 31 | } 32 | 33 | test("DecodeEscapedUnicode") { 34 | let json = InputByteStream( 35 | #"{"hello":"\u3053\u3093\u306b\u3061\u306f"}"#) 36 | struct Model: Decodable { 37 | let hello: String 38 | } 39 | let model = try await JSON.decode(Model.self, from: json) 40 | expect(model.hello == "こんにちは") 41 | } 42 | 43 | test("KeyedNested") { 44 | let json = InputByteStream( 45 | #"{"answer":42,"nested":{"hello":"Hello, World!"}}"#) 46 | struct Model: Decodable { 47 | struct Nested: Decodable { 48 | let hello: String 49 | } 50 | let answer: Int 51 | let nested: Nested 52 | } 53 | let object = try await JSON.decode(Model.self, from: json) 54 | expect(object.answer == 42) 55 | expect(object.nested.hello == "Hello, World!") 56 | } 57 | 58 | test("KeyedNestedInTheMiddle") { 59 | let json = InputByteStream( 60 | #"{"nested":{"hello":"Hello, World!"},"answer":42}"#) 61 | struct Model: Decodable { 62 | struct Nested: Decodable { 63 | let hello: String 64 | } 65 | let nested: Nested 66 | let answer: Int 67 | } 68 | let object = try await JSON.decode(Model.self, from: json) 69 | expect(object.nested.hello == "Hello, World!") 70 | expect(object.answer == 42) 71 | } 72 | 73 | test("NestedArrayInTheMiddle") { 74 | let json = InputByteStream( 75 | #"{"nested":{"array":[1,2]},"answer":42}"#) 76 | struct Model: Decodable { 77 | struct Nested: Decodable { 78 | let array: [Int] 79 | } 80 | let nested: Nested 81 | let answer: Int 82 | } 83 | let object = try await JSON.decode(Model.self, from: json) 84 | expect(object.nested.array == [1, 2]) 85 | expect(object.answer == 42) 86 | } 87 | 88 | test("NestedArraysInTheMiddle") { 89 | let json = InputByteStream( 90 | #"{"nested":{"array":[[1,2],[3,4]]},"answer":42}"#) 91 | struct Model: Decodable { 92 | struct Nested: Decodable { 93 | let array: [[Int]] 94 | } 95 | let nested: Nested 96 | let answer: Int 97 | } 98 | let object = try await JSON.decode(Model.self, from: json) 99 | expect(object.nested.array.first ?? [] == [1, 2]) 100 | expect(object.nested.array.last ?? [] == [3, 4]) 101 | expect(object.answer == 42) 102 | } 103 | 104 | test("Unkeyed") { 105 | let json = InputByteStream("[1,2,3]") 106 | let object = try await JSON.decode([Int].self, from: json) 107 | expect(object == [1, 2, 3]) 108 | } 109 | 110 | test("UnkeyedOfUnkeyed") { 111 | let json = InputByteStream("[[1,2],[3,4]]") 112 | let object = try await JSON.decode([[Int]].self, from: json) 113 | expect(object.first ?? [] == [1, 2]) 114 | expect(object.last ?? [] == [3, 4]) 115 | } 116 | 117 | test("Enum") { 118 | let json = InputByteStream(#"{"single":1,"array":[1,2,3]}"#) 119 | enum Number: Int, Decodable { 120 | case one = 1 121 | case two 122 | case three 123 | } 124 | struct Model: Decodable { 125 | let single: Number 126 | let array: [Number] 127 | } 128 | let object = try await JSON.decode(Model.self, from: json) 129 | expect(object.single == .one) 130 | expect(object.array == [.one, .two, .three]) 131 | } 132 | 133 | test("Decodable") { 134 | let json = InputByteStream(#"{"answer":42,"hello":"Hello, World!"}"#) 135 | struct Model: Decodable { 136 | let answer: Int 137 | let hello: String 138 | } 139 | let type: Decodable.Type = Model.self 140 | let decodable = try await JSON.decode(decodable: type, from: json) 141 | guard let object = decodable as? Model else { 142 | fail() 143 | return 144 | } 145 | expect(object.answer == 42) 146 | expect(object.hello == "Hello, World!") 147 | } 148 | 149 | await run() 150 | -------------------------------------------------------------------------------- /Tests/Codable/JSON.encode/main.swift: -------------------------------------------------------------------------------- 1 | import Test 2 | 3 | @testable import JSON 4 | 5 | test("Keyed") { 6 | let expected = #"{"answer":42,"hello":"Hello, World"}"# 7 | struct Model: Encodable { 8 | let answer: Int = 42 9 | let hello: String = "Hello, World" 10 | } 11 | let bytes = try JSON.encode(Model()) 12 | let json = String(decoding: bytes, as: UTF8.self) 13 | expect(json == expected) 14 | } 15 | 16 | test("KeyedNested") { 17 | let expected = #"{"answer":42,"nested":{"hello":"Hello, World"}}"# 18 | struct Model: Encodable { 19 | struct Nested: Encodable { 20 | let hello = "Hello, World" 21 | } 22 | let answer: Int = 42 23 | let nested = Nested() 24 | } 25 | let bytes = try JSON.encode(Model()) 26 | let json = String(decoding: bytes, as: UTF8.self) 27 | expect(json == expected) 28 | } 29 | 30 | test("KeyedInTheMiddle") { 31 | let expected = #"{"nested":{"hello":"Hello, World"},"answer":42}"# 32 | struct Model: Encodable { 33 | struct Nested: Encodable { 34 | let hello = "Hello, World" 35 | } 36 | let nested = Nested() 37 | let answer: Int = 42 38 | } 39 | let bytes = try JSON.encode(Model()) 40 | let json = String(decoding: bytes, as: UTF8.self) 41 | expect(json == expected) 42 | } 43 | 44 | test("NestedInTheMiddle") { 45 | let expected = #"{"nested":{"array":[1,2]},"answer":42}"# 46 | struct Model: Encodable { 47 | struct Nested: Encodable { 48 | let array: [Int] = [1, 2] 49 | } 50 | let nested = Nested() 51 | let answer: Int = 42 52 | } 53 | let bytes = try JSON.encode(Model()) 54 | let json = String(decoding: bytes, as: UTF8.self) 55 | expect(json == expected) 56 | } 57 | 58 | test("NestedArrayInTheMiddle") { 59 | let expected = #"{"nested":{"array":[[1,2],[3,4]]},"answer":42}"# 60 | struct Model: Encodable { 61 | struct Nested: Encodable { 62 | let array: [[Int]] = [[1, 2], [3, 4]] 63 | } 64 | let nested = Nested() 65 | let answer: Int = 42 66 | } 67 | let bytes = try JSON.encode(Model()) 68 | let json = String(decoding: bytes, as: UTF8.self) 69 | expect(json == expected) 70 | } 71 | 72 | test("Unkeyed") { 73 | let bytes = try JSON.encode([1, 2, 3]) 74 | let json = String(decoding: bytes, as: UTF8.self) 75 | expect(json == "[1,2,3]") 76 | } 77 | 78 | test("UnkeyedOfUnkeyed") { 79 | let bytes = try JSON.encode([[1, 2], [3, 4]]) 80 | let json = String(decoding: bytes, as: UTF8.self) 81 | expect(json == "[[1,2],[3,4]]") 82 | } 83 | 84 | test("Enum") { 85 | let expected = #"{"single":1,"array":[1,2,3]}"# 86 | enum Number: Int, Encodable { 87 | case one = 1 88 | case two 89 | case three 90 | } 91 | struct Model: Encodable { 92 | let single: Number = .one 93 | let array: [Number] = [.one, .two, .three] 94 | } 95 | let bytes = try JSON.encode(Model()) 96 | let json = String(decoding: bytes, as: UTF8.self) 97 | expect(json == expected) 98 | } 99 | 100 | test("Encodable") { 101 | let expected = #"{"answer":42,"hello":"Hello, World"}"# 102 | struct Model: Encodable { 103 | let answer: Int = 42 104 | let hello: String = "Hello, World" 105 | } 106 | let encodable = Model() as Encodable 107 | let bytes = try JSON.encode(encodable: encodable) 108 | let json = String(decoding: bytes, as: UTF8.self) 109 | expect(json == expected) 110 | } 111 | 112 | await run() 113 | -------------------------------------------------------------------------------- /Tests/Codable/ScopedCoders/main.swift: -------------------------------------------------------------------------------- 1 | import Test 2 | import Stream 3 | // don't use @testable 4 | import JSON 5 | 6 | test("withScopedEncoder") { 7 | try JSON.withScopedEncoder(using: OutputByteStream()) { _ in 8 | } 9 | } 10 | 11 | test("withScopedDecoder") { 12 | try await JSON.withScopedDecoder(using: InputByteStream("null")) { _ in 13 | } 14 | } 15 | 16 | await run() 17 | -------------------------------------------------------------------------------- /Tests/Codable/UnkeyedDecodingContainer/main.swift: -------------------------------------------------------------------------------- 1 | import Test 2 | import Stream 3 | 4 | @testable import JSON 5 | 6 | test("Container") { 7 | let decoder = try await JSON.Decoder(InputByteStream("[1,2]")) 8 | var unkeyedContainer = try decoder.unkeyedContainer() 9 | expect(unkeyedContainer.count == 2) 10 | expect(unkeyedContainer.isAtEnd == false) 11 | 12 | let int1 = try unkeyedContainer.decode(Int.self) 13 | let int2 = try unkeyedContainer.decode(Int.self) 14 | expect(unkeyedContainer.isAtEnd == true) 15 | expect(int1 == 1) 16 | expect(int2 == 2) 17 | } 18 | 19 | test("NestedContainer") { 20 | let decoder = try await JSON.Decoder(InputByteStream("[[1],[2]]")) 21 | var unkeyedContainer = try decoder.unkeyedContainer() 22 | expect(unkeyedContainer.count == 2) 23 | expect(unkeyedContainer.isAtEnd == false) 24 | 25 | var nested1 = try unkeyedContainer.nestedUnkeyedContainer() 26 | expect(nested1.count == 1) 27 | expect(nested1.isAtEnd == false) 28 | let int1 = try nested1.decode(Int.self) 29 | expect(int1 == 1) 30 | expect(nested1.isAtEnd == true) 31 | 32 | var nested2 = try unkeyedContainer.nestedUnkeyedContainer() 33 | expect(nested2.count == 1) 34 | expect(nested2.isAtEnd == false) 35 | let int2 = try nested2.decode(Int.self) 36 | expect(int2 == 2) 37 | expect(nested2.isAtEnd == true) 38 | 39 | expect(unkeyedContainer.isAtEnd == true) 40 | } 41 | 42 | await run() 43 | -------------------------------------------------------------------------------- /Tests/Codable/UnkeyedEncodingContainer/main.swift: -------------------------------------------------------------------------------- 1 | import Test 2 | import Stream 3 | 4 | @testable import JSON 5 | 6 | test("Container") { 7 | let output = OutputByteStream() 8 | let encoder = JSON.Encoder(output) 9 | var unkeyedContainer = encoder.unkeyedContainer() 10 | try unkeyedContainer.encode(1) 11 | try unkeyedContainer.encode(2) 12 | try encoder.close() 13 | expect(output.stringValue == "[1,2]") 14 | } 15 | 16 | test("NestedContainer") { 17 | let output = OutputByteStream() 18 | let encoder = JSON.Encoder(output) 19 | var unkeyedContainer = encoder.unkeyedContainer() 20 | var nested1 = unkeyedContainer.nestedUnkeyedContainer() 21 | try nested1.encode(1) 22 | var nested2 = unkeyedContainer.nestedUnkeyedContainer() 23 | try nested2.encode(2) 24 | try encoder.close() 25 | expect(output.stringValue == "[[1],[2]]") 26 | } 27 | 28 | test("Null") { 29 | let output = OutputByteStream() 30 | let encoder = JSON.Encoder(output) 31 | var unkeyedContainer = encoder.unkeyedContainer() 32 | try unkeyedContainer.encodeNil() 33 | try unkeyedContainer.encodeNil() 34 | try encoder.close() 35 | expect(output.stringValue == "[null,null]") 36 | } 37 | 38 | await run() 39 | -------------------------------------------------------------------------------- /Tests/JSON/Value+DynamicLookup/main.swift: -------------------------------------------------------------------------------- 1 | import Test 2 | 3 | @testable import JSON 4 | 5 | test("Get") { 6 | let value = JSON.Value.object(["key": .string("value")]) 7 | expect(value.key == .string("value")) 8 | } 9 | 10 | test("Set") { 11 | var value = JSON.Value.null 12 | value.key = .string("value") 13 | expect(value == .object(["key": .string("value")])) 14 | } 15 | 16 | await run() 17 | -------------------------------------------------------------------------------- /Tests/JSON/Value+InputStream/main.swift: -------------------------------------------------------------------------------- 1 | import Test 2 | import Stream 3 | 4 | @testable import JSON 5 | 6 | test("Null") { 7 | let null = try await JSON.Value.decode(from: InputByteStream("null")) 8 | expect(null == .null) 9 | } 10 | 11 | test("Bool") { 12 | let jsonTrue = try await JSON.Value.decode(from: InputByteStream("true")) 13 | expect(jsonTrue == .bool(true)) 14 | 15 | let jsonFalse = try await JSON.Value.decode(from: InputByteStream("false")) 16 | expect(jsonFalse == .bool(false)) 17 | } 18 | 19 | test("Number") { 20 | let uint = try await JSON.Value.decode(from: InputByteStream("42")) 21 | expect(uint == .number(.uint(42))) 22 | 23 | let int = try await JSON.Value.decode(from: InputByteStream("-42")) 24 | expect(int == .number(.int(-42))) 25 | 26 | let double = try await JSON.Value.decode(from: InputByteStream("-42.42")) 27 | expect(double == .number(.double(-42.42))) 28 | } 29 | 30 | test("String") { 31 | let json = InputByteStream("\"string\"") 32 | let string = try await JSON.Value.decode(from: json) 33 | expect(string == .string("string")) 34 | } 35 | 36 | test("EsapedString") { 37 | let escapedJson = InputByteStream("\"string\\r\\n\"") 38 | let escapedString = try await JSON.Value.decode(from: escapedJson) 39 | expect(escapedString == .string("string\r\n")) 40 | } 41 | 42 | test("InvalidEscapedString") { 43 | let invalidJson = InputByteStream("\"string\r\n\"") 44 | await expect(throws: JSON.Error.invalidJSON) { 45 | try await JSON.Value.decode(from: invalidJson) 46 | } 47 | } 48 | 49 | test("EscapedUnicodeString") { 50 | let escapedUnicodeJson = InputByteStream( 51 | #""\u3053\u3093\u306b\u3061\u306f""#) 52 | let escapedUnicode = try await JSON.Value.decode(from: escapedUnicodeJson) 53 | expect(escapedUnicode == .string("こんにちは")) 54 | } 55 | 56 | test("EmptyObject") { 57 | let empty = try await JSON.Value.decode(from: InputByteStream("{}")) 58 | expect(empty == .object([:])) 59 | } 60 | 61 | test("SimpleObject") { 62 | let stream = InputByteStream(#"{"key":"value"}"#) 63 | let simple = try await JSON.Value.decode(from: stream) 64 | expect(simple == .object(["key": .string("value")])) 65 | } 66 | 67 | test("NestedObject") { 68 | let stream = InputByteStream(#"{"o":{"k":"v"}}"#) 69 | let nested = try await JSON.Value.decode(from: stream) 70 | expect(nested == .object(["o": .object(["k": .string("v")])])) 71 | } 72 | 73 | test("WhitespaceInObject") { 74 | let whitespace = try await JSON.Value.decode(from: InputByteStream( 75 | #"{"key" : "value"}"#)) 76 | expect(whitespace == .object(["key": .string("value")])) 77 | } 78 | 79 | test("WhitespacBetweenObjects") { 80 | let separator = try await JSON.Value.decode(from: InputByteStream( 81 | #"{"k1":"v1", "k2":"v2"}"#)) 82 | let expected: JSON.Value = .object([ 83 | "k1": .string("v1"), 84 | "k2": .string("v2")]) 85 | expect(separator == expected) 86 | } 87 | 88 | test("Array") { 89 | let empty = try await JSON.Value.decode(from: InputByteStream("[]")) 90 | expect(empty == .array([])) 91 | 92 | let simple = try await JSON.Value.decode(from: InputByteStream("[1,2]")) 93 | expect(simple == .array([.number(.uint(1)), .number(.uint(2))])) 94 | 95 | let strings = try await JSON.Value.decode(from: InputByteStream( 96 | #"["one", "two"]"#)) 97 | expect(strings == .array([.string("one"), .string("two")])) 98 | } 99 | 100 | test("Nested") { 101 | let objectInArray = try await JSON.Value.decode(from: InputByteStream( 102 | #"["one", 2, {"key": false}]"#)) 103 | expect(objectInArray == .array([ 104 | .string("one"), 105 | .number(.uint(2)), 106 | .object(["key": .bool(false)])])) 107 | 108 | let arrayInObject = try await JSON.Value.decode(from: InputByteStream( 109 | #"{"values" : [1,true]}"#)) 110 | expect(arrayInObject == .object( 111 | ["values": .array([.number(.uint(1)), .bool(true)])])) 112 | } 113 | 114 | await run() 115 | -------------------------------------------------------------------------------- /Tests/JSON/Value+OutputStream/main.swift: -------------------------------------------------------------------------------- 1 | import Test 2 | import Stream 3 | 4 | @testable import JSON 5 | 6 | test("Null") { 7 | let stream = OutputByteStream() 8 | let value: JSON.Value = .null 9 | try await value.encode(to: stream) 10 | expect(stream.stringValue == "null") 11 | } 12 | 13 | test("Bool") { 14 | var stream = OutputByteStream() 15 | let jsonTrue: JSON.Value = .bool(true) 16 | try await jsonTrue.encode(to: stream) 17 | expect(stream.stringValue == "true") 18 | 19 | stream = OutputByteStream() 20 | let jsonFalse: JSON.Value = .bool(false) 21 | try await jsonFalse.encode(to: stream) 22 | expect(stream.stringValue == "false") 23 | } 24 | 25 | test("Number") { 26 | var stream = OutputByteStream() 27 | let uint: JSON.Value = .number(.uint(42)) 28 | try await uint.encode(to: stream) 29 | expect(stream.stringValue == "42") 30 | 31 | stream = OutputByteStream() 32 | let int: JSON.Value = .number(.int(-42)) 33 | try await int.encode(to: stream) 34 | expect(stream.stringValue == "-42") 35 | 36 | stream = OutputByteStream() 37 | let double: JSON.Value = .number(.double(-42.42)) 38 | try await double.encode(to: stream) 39 | expect(stream.stringValue == "-42.42") 40 | } 41 | 42 | test("String") { 43 | var stream = OutputByteStream() 44 | let string: JSON.Value = .string("string") 45 | try await string.encode(to: stream) 46 | expect(stream.stringValue == "\"string\"") 47 | 48 | stream = OutputByteStream() 49 | let escapedJson: JSON.Value = .string("string\r\n") 50 | try await escapedJson.encode(to: stream) 51 | expect(stream.stringValue == "\"string\r\n\"") 52 | 53 | stream = OutputByteStream() 54 | let escapedUnicode: JSON.Value = .string("こんにちは") 55 | try await escapedUnicode.encode(to: stream) 56 | // TODO: Do we need to escape? 57 | expect(stream.stringValue == "\"こんにちは\"") 58 | } 59 | 60 | test("Object") { 61 | var stream = OutputByteStream() 62 | let empty: JSON.Value = .object([:]) 63 | try await empty.encode(to: stream) 64 | expect(stream.stringValue == "{}") 65 | 66 | stream = OutputByteStream() 67 | let simple: JSON.Value = .object(["key": .string("value")]) 68 | try await simple.encode(to: stream) 69 | expect(stream.stringValue == #"{"key":"value"}"#) 70 | 71 | stream = OutputByteStream() 72 | let nested: JSON.Value = .object(["o": .object(["k": .string("v")])]) 73 | try await nested.encode(to: stream) 74 | expect(stream.stringValue == #"{"o":{"k":"v"}}"#) 75 | } 76 | 77 | test("Array") { 78 | var stream = OutputByteStream() 79 | let empty: JSON.Value = .array([]) 80 | try await empty.encode(to: stream) 81 | expect(stream.stringValue == "[]") 82 | 83 | stream = OutputByteStream() 84 | let simple: JSON.Value = .array([.number(.uint(1)), .number(.uint(2))]) 85 | try await simple.encode(to: stream) 86 | expect(stream.stringValue == "[1,2]") 87 | 88 | stream = OutputByteStream() 89 | let strings: JSON.Value = .array([.string("one"), .string("two")]) 90 | try await strings.encode(to: stream) 91 | expect(stream.stringValue == #"["one","two"]"#) 92 | } 93 | 94 | test("Nested") { 95 | var stream = OutputByteStream() 96 | let objectInArray: JSON.Value = .array([ 97 | .string("one"), 98 | .number(.uint(2)), 99 | .object(["key": .bool(false)])]) 100 | try await objectInArray.encode(to: stream) 101 | expect(stream.stringValue == #"["one",2,{"key":false}]"#) 102 | 103 | stream = OutputByteStream() 104 | let arrayInObject: JSON.Value = .object( 105 | ["values": .array([.number(.uint(1)), .bool(true)])]) 106 | try await arrayInObject.encode(to: stream) 107 | expect(stream.stringValue == #"{"values":[1,true]}"#) 108 | } 109 | 110 | await run() 111 | -------------------------------------------------------------------------------- /Tests/JSON/Value/main.swift: -------------------------------------------------------------------------------- 1 | import Test 2 | 3 | @testable import JSON 4 | 5 | test("Object") { 6 | let value = JSON.Value.object(["key": .string("value")]) 7 | expect(value["key"] == .string("value")) 8 | } 9 | 10 | test("Array") { 11 | let value = JSON.Value.array([.number(.int(42))]) 12 | expect(value[0] == .number(.int(42))) 13 | } 14 | 15 | test("Boolean") { 16 | let value = JSON.Value.bool(true) 17 | expect(value.booleanValue == true) 18 | } 19 | 20 | test("Integer") { 21 | let value = JSON.Value.number(.int(42)) 22 | expect(value.integerValue == 42) 23 | expect(value.unsignedValue == 42) 24 | } 25 | 26 | test("Unsigned") { 27 | let value = JSON.Value.number(.uint(42)) 28 | expect(value.unsignedValue == 42) 29 | expect(value.integerValue == 42) 30 | } 31 | 32 | test("Double") { 33 | let value = JSON.Value.number(.double(40.2)) 34 | expect(value.doubleValue == 40.2) 35 | } 36 | 37 | test("String") { 38 | let value = JSON.Value.string("value") 39 | expect(value.stringValue == "value") 40 | } 41 | 42 | await run() 43 | -------------------------------------------------------------------------------- /run_tests: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | swift build 6 | 7 | export DYLD_LIBRARY_PATH=/Library/Developer/Toolchains/swift-latest.xctoolchain/usr/lib/swift/macosx 8 | 9 | .build/debug/Tests/Codable/Decoder 10 | .build/debug/Tests/Codable/Encoder 11 | .build/debug/Tests/Codable/JSON.decode 12 | .build/debug/Tests/Codable/JSON.encode 13 | .build/debug/Tests/Codable/ScopedCoders 14 | .build/debug/Tests/Codable/UnkeyedDecodingContainer 15 | .build/debug/Tests/Codable/UnkeyedEncodingContainer 16 | 17 | .build/debug/Tests/JSON/Value 18 | .build/debug/Tests/JSON/Value+DynamicLookup 19 | .build/debug/Tests/JSON/Value+InputStream 20 | .build/debug/Tests/JSON/Value+OutputStream 21 | --------------------------------------------------------------------------------