-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontinuable.lua
More file actions
268 lines (215 loc) · 6.16 KB
/
Copy pathcontinuable.lua
File metadata and controls
268 lines (215 loc) · 6.16 KB
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
local native = require('uv_native')
local Object = require('core').Object
local function noop() end
local uv = {}
local Queue = Object:extend()
uv.Queue = Queue
function Queue:initialize()
self.first = 1
self.last = 0
self.length = 0
end
function Queue:push(item)
self.last = self.last + 1
self.length = self.length + 1
self[self.last] = item
end
function Queue:shift()
-- Ignore the call if the queue is empty. Return
if self.length == 0 then
return
end
-- Get the first item
local item = self[self.first]
self[self.first] = nil
self.length = self.length - 1
if self.first == self.last then
-- If it was the last item, reset the queue
self:initialize()
else
-- Otherwise enqueue the next item.
self.first = self.first + 1
end
return item
end
local ReadableStream = Object:extend()
uv.ReadableStream = ReadableStream
-- If there are more than this many buffered input chunks, readStop the source
ReadableStream.highWaterMark = 1
-- If there are less than this many buffered chunks, readStart the source
ReadableStream.lowWaterMark = 1
function ReadableStream:initialize()
self.inputQueue = Queue:new()
self.readerQueue = Queue:new()
end
function ReadableStream:read() return function (callback)
self.readerQueue:push(callback)
self:processReaders()
end end
function ReadableStream:processReaders()
while self.inputQueue.length > 0 and self.readerQueue.length > 0 do
local chunk = self.inputQueue:shift()
local reader = self.readerQueue:shift()
reader(nil, chunk)
end
local watermark = self.inputQueue.length - self.readerQueue.length
if watermark > self.highWaterMark and not self.paused then
self.paused = true
self:pause()
elseif watermark < self.lowWaterMark and self.paused then
self.paused = false
self:resume()
end
end
local fs = {}
uv.fs = fs
function fs.open(path, flags, mode) return function (callback)
-- TODO: register this resource with the resource cleaner
native.fsOpen(path, flags, mode or tonumber("666", 8), callback or noop)
end end
function fs.read(fd, offset, size) return function (callback)
native.fsRead(fd, offset, size, callback or noop)
end end
function fs.write(fd, offset, chunk) return function (callback)
native.fsWrite(fd, offset, chunk, callback or noop)
end end
function fs.close(fd) return function (callback)
-- TODO: free this resource from the resource cleaner
native.fsClose(fd, callback or noop)
end end
function fs.stat(path) return function (callback)
native.fsStat(path, callback or noop)
end end
function fs.fstat(fd) return function (callback)
native.fsFstat(fd, callback or noop)
end end
function fs.lstat(path) return function (callback)
native.fsLstat(path, callback or noop)
end end
function fs.readdir(path) return function (callback)
native.fsReaddir(path, callback or noop)
end end
fs.ReadStream = Object:extend()
fs.ReadStream.chunkSize = 65536
function fs.ReadStream:initialize(fd)
self.fd = fd
self.offset = 0
end
function fs.ReadStream:read() return function (callback)
fs.read(self.fd, self.offset, self.chunkSize)(function (err, chunk)
-- In case of error, close the fd and emit the error
if err then
fs.close(self.fd)()
return callback(err)
end
local length = #chunk
-- In case of data, move the offset and emit the chunk.
if length > 0 then
self.offset = self.offset + length
return callback(nil, chunk)
end
-- Otherwise, it's EOF. Close the fd and emit end.
fs.close(self.fd)()
callback()
end)
end end
fs.WriteStream = Object:extend()
function fs.WriteStream:initialize(fd)
self.fd = fd
self.offset = 0
end
function fs.WriteStream:write(chunk) return function (callback)
-- on eof, close the file
if not chunk then
return fs.close(self.fd)(callback)
end
-- Otherwise write the chunk
fs.write(self.fd, self.offset, chunk)(function (err, bytesWritten)
-- On error, close the file and emit the error
if err then
fs.close(self.fd)()
return callback(err)
end
-- TODO: if bytesWritten is ever less than #chunk, we need to retry
self.offset = self.offset + bytesWritten
callback()
end)
end end
local handle = {}
uv.handle = handle
function handle:close() return function (callback)
native.close(self, callback)
end end
function handle:setHandler(name, handler)
native.setHandler(self, name, handler)
end
local stream = setmetatable({}, {__index = handle})
uv.stream = stream
function stream:write(chunk) return function (callback)
return native.write(self, chunk, callback)
end end
function stream:shutdown() return function (callback)
native.shutdown(self, callback)
end end
function stream:readStart()
return native.readStart(self)
end
function stream:readStop()
return native.readStop(self)
end
function stream:listen(onConnection)
return native.listen(self, onConnection)
end
function stream:accept(client)
return native.accept(self, client)
end
stream.Stream = ReadableStream:extend()
function stream.Stream:initialize(handle)
self.handle = handle
-- Readable stuff
ReadableStream.initialize(self)
uv.handle.setHandler(handle, "data", function (chunk)
self.inputQueue:push(chunk)
self:processReaders()
end)
uv.handle.setHandler(handle, "end", function ()
self.inputQueue:push()
self:processReaders()
end)
uv.stream.readStart(handle)
end
function stream.Stream:pause()
uv.stream.readStop(self.handle)
end
function stream.Stream:resume()
uv.stream.readStart(self.handle)
end
function stream.Stream:write(chunk)
if chunk then
return uv.stream.write(self.handle, chunk)
end
return uv.stream.shutdown(self.handle)
end
local tcp = setmetatable({}, {__index=stream})
uv.tcp = tcp
function tcp:bind(host, port)
return native.tcpBind(self, host, port)
end
function tcp.new()
return native.newTcp()
end
function tcp:getsockname()
return native.tcpGetsockname(self)
end
function tcp.createServer(host, port, onConnection)
local server = tcp.new()
tcp.bind(server, host, port)
tcp.listen(server, function ()
local client = tcp.new()
tcp.accept(server, client)
onConnection(tcp.Stream:new(client))
end)
return server
end
uv.fiber = require('./fiber.lua')
return uv