-
Notifications
You must be signed in to change notification settings - Fork 87
/
JSONSerializer.swift
executable file
·179 lines (148 loc) · 5.12 KB
/
JSONSerializer.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#if os(Linux)
import Glibc
#else
import Darwin.C
#endif
import CYAJL
import Venice
public struct JSONSerializerError : Error, CustomStringConvertible {
let reason: String
public var description: String {
return reason
}
}
public final class JSONSerializer {
private var ordering: Bool
private var buffer: String = ""
private var bufferSize: Int = 0
private var handle: yajl_gen?
public convenience init() {
self.init(ordering: false)
}
public init(ordering: Bool) {
self.ordering = ordering
self.handle = yajl_gen_alloc(nil)
}
deinit {
yajl_gen_free(handle)
}
public func serialize(_ json: JSON, bufferSize: Int = 4096, body: (UnsafeRawBufferPointer) throws -> Void) throws {
yajl_gen_reset(handle, nil)
self.bufferSize = bufferSize
try generate(json, body: body)
try write(body: body)
}
private func generate(_ json: JSON, body: (UnsafeRawBufferPointer) throws -> Void) throws {
switch json {
case .null:
try generateNull()
case .bool(let bool):
try generate(bool)
case .double(let double):
try generate(double)
case .int(let int):
try generate(int)
case .string(let string):
try generate(string)
case .array(let array):
try generate(array, body: body)
case .object(let object):
try generate(object, body: body)
}
try write(highwater: bufferSize, body: body)
}
private func generate(
_ dictionary: [String: JSON],
body: (UnsafeRawBufferPointer
) throws -> Void) throws {
var status = yajl_gen_status_ok
status = yajl_gen_map_open(handle)
try check(status: status)
if ordering {
for (key, value) in dictionary.sorted(by: { $0.0 < $1.0 }) {
try generate(key)
try generate(value, body: body)
}
} else {
for (key, value) in dictionary {
try generate(key)
try generate(value, body: body)
}
}
status = yajl_gen_map_close(handle)
try check(status: status)
}
private func generate(_ array: [JSON], body: (UnsafeRawBufferPointer) throws -> Void) throws {
var status = yajl_gen_status_ok
status = yajl_gen_array_open(handle)
try check(status: status)
for value in array {
try generate(value, body: body)
}
status = yajl_gen_array_close(handle)
try check(status: status)
}
private func generateNull() throws {
try check(status: yajl_gen_null(handle))
}
private func generate(_ string: String) throws {
let status: yajl_gen_status
if string.isEmpty {
status = yajl_gen_string(handle, nil, 0)
} else {
status = string.withCString { cStringPointer in
return cStringPointer.withMemoryRebound(to: UInt8.self, capacity: string.utf8.count) {
yajl_gen_string(self.handle, $0, string.utf8.count)
}
}
}
try check(status: status)
}
private func generate(_ bool: Bool) throws {
try check(status: yajl_gen_bool(handle, (bool) ? 1 : 0))
}
private func generate(_ double: Double) throws {
let string = double.description
let status = string.withCString { pointer in
return yajl_gen_number(self.handle, pointer, string.utf8.count)
}
try check(status: status)
}
private func generate(_ int: Int) throws {
try check(status: yajl_gen_integer(handle, Int64(int)))
}
private func check(status: yajl_gen_status) throws {
switch status {
case yajl_gen_keys_must_be_strings:
throw JSONSerializerError(reason: "Keys must be strings.")
case yajl_max_depth_exceeded:
throw JSONSerializerError(reason: "Max depth exceeded.")
case yajl_gen_in_error_state:
throw JSONSerializerError(reason: "In error state.")
case yajl_gen_invalid_number:
throw JSONSerializerError(reason: "Invalid number.")
case yajl_gen_no_buf:
throw JSONSerializerError(reason: "No buffer.")
case yajl_gen_invalid_string:
throw JSONSerializerError(reason: "Invalid string.")
case yajl_gen_status_ok:
break
case yajl_gen_generation_complete:
break
default:
throw JSONSerializerError(reason: "Unknown.")
}
}
private func write(highwater: Int = 0, body: (UnsafeRawBufferPointer) throws -> Void) throws {
var buffer: UnsafePointer<UInt8>? = nil
var bufferLength: Int = 0
guard yajl_gen_get_buf(handle, &buffer, &bufferLength) == yajl_gen_status_ok else {
throw JSONSerializerError(reason: "Could not get buffer.")
}
guard bufferLength >= highwater else {
return
}
try body(UnsafeRawBufferPointer(start: buffer, count: bufferLength))
yajl_gen_clear(handle)
}
}