presidium-db-client-server 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ArrayEventEmitter.js +21 -0
- package/AsyncIterator.js +38 -0
- package/BinaryEntriesStream.js +75 -0
- package/BinaryStream.js +54 -0
- package/DatabaseHandler.js +292 -0
- package/DatabaseServer.js +101 -0
- package/DiskHashTableClient.js +224 -0
- package/DiskSortedHashTableClient.js +321 -0
- package/README.md +54 -2
- package/TextEntriesStream.js +73 -0
- package/TextStream.js +55 -0
- package/buffersToEntry.js +44 -0
- package/handleServerResponse.js +72 -0
- package/package.json +45 -4
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presidium DB Client and Server may be freely distributed under the CFOSS license.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const stream = require("stream")
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @name ArrayEventEmitter
|
|
9
|
+
*
|
|
10
|
+
* @docs
|
|
11
|
+
* ```specscript
|
|
12
|
+
* module EventEmitter "./specscript/EventEmitter.ss"
|
|
13
|
+
*
|
|
14
|
+
* ArrayEventEmitter(array Array<string|Buffer>) -> EventEmitter
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
function ArrayEventEmitter(array) {
|
|
18
|
+
return stream.Readable.from(array)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = ArrayEventEmitter
|
package/AsyncIterator.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presidium DB Client and Server may be freely distributed under the CFOSS license.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const AsyncIterator = {}
|
|
6
|
+
|
|
7
|
+
function sleep(time) {
|
|
8
|
+
return new Promise(resolve => setTimeout(resolve, time))
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @name fromEvents
|
|
13
|
+
*
|
|
14
|
+
* @docs
|
|
15
|
+
* ```specscript
|
|
16
|
+
* module EventEmitter "./specscript/EventEmitter.ss"
|
|
17
|
+
*
|
|
18
|
+
* fromEvents(events EventEmitter) -> AsyncIterator
|
|
19
|
+
* fromEvents(events EventEmitter, dataEventName string) -> AsyncIterator
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
async function* fromEvents(events, dataEventName = "data") {
|
|
23
|
+
let buffer = []
|
|
24
|
+
events.on(dataEventName, data => buffer.push(data))
|
|
25
|
+
let streaming = true
|
|
26
|
+
events.on("end", () => (streaming = false))
|
|
27
|
+
while (streaming || buffer.length > 0) {
|
|
28
|
+
while (buffer.length > 0) {
|
|
29
|
+
yield* buffer
|
|
30
|
+
buffer = []
|
|
31
|
+
}
|
|
32
|
+
await sleep(1)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
AsyncIterator.fromEvents = fromEvents
|
|
37
|
+
|
|
38
|
+
module.exports = AsyncIterator
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presidium DB Client and Server may be freely distributed under the CFOSS license.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const EventEmitter = require("events")
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @name BinaryEntriesStream
|
|
9
|
+
*
|
|
10
|
+
* @docs
|
|
11
|
+
* ```coffeescript [specscript]
|
|
12
|
+
* module stream 'https://nodejs.org/docs/latest-v26.x/api/stream.html'
|
|
13
|
+
* module events 'https://nodejs.org/docs/latest-v26.x/api/events.html'
|
|
14
|
+
*
|
|
15
|
+
* BinaryEntriesStream(
|
|
16
|
+
* readable stream.Readable,
|
|
17
|
+
* entryLength number,
|
|
18
|
+
* delimiter string
|
|
19
|
+
* ) -> binaryEntriesStream events.EventEmitter
|
|
20
|
+
*
|
|
21
|
+
* binaryEntriesStream.on("entry", (entry Array<Buffer>, keyEncoding string) -> ())
|
|
22
|
+
*
|
|
23
|
+
* binaryEntriesStream.on("end", () -> ())
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
class BinaryEntriesStream extends EventEmitter {
|
|
27
|
+
constructor(readable, entryLength, delimiter, encoding) {
|
|
28
|
+
super()
|
|
29
|
+
|
|
30
|
+
delimiter ??= ""
|
|
31
|
+
encoding ??= "utf8"
|
|
32
|
+
|
|
33
|
+
let buffer = Buffer.from("")
|
|
34
|
+
let entry = []
|
|
35
|
+
|
|
36
|
+
readable.on('data', (chunk, encoding) => {
|
|
37
|
+
if (typeof chunk == 'string') {
|
|
38
|
+
buffer = Buffer.concat([buffer, Buffer.from(chunk, encoding)])
|
|
39
|
+
} else {
|
|
40
|
+
buffer = Buffer.concat([buffer, chunk])
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let delimiterIndex = buffer.indexOf(delimiter)
|
|
44
|
+
while (delimiterIndex > -1) {
|
|
45
|
+
const slice = buffer.slice(0, delimiterIndex)
|
|
46
|
+
entry.push(slice)
|
|
47
|
+
if (entry.length == entryLength) {
|
|
48
|
+
this.emit("entry", entry, encoding)
|
|
49
|
+
entry = []
|
|
50
|
+
}
|
|
51
|
+
buffer = buffer.slice(delimiterIndex + 1)
|
|
52
|
+
delimiterIndex = buffer.indexOf(delimiter)
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
readable.on('end', () => {
|
|
57
|
+
if (buffer.length > 0) {
|
|
58
|
+
entry.push(buffer)
|
|
59
|
+
}
|
|
60
|
+
if (entry.length == entryLength) {
|
|
61
|
+
this.emit("entry", entry, encoding)
|
|
62
|
+
this.emit("end")
|
|
63
|
+
entry = []
|
|
64
|
+
} else if (entry.length > 0) {
|
|
65
|
+
const error = new Error("last entry")
|
|
66
|
+
error.entry = entry
|
|
67
|
+
this.emit("error", error)
|
|
68
|
+
} else {
|
|
69
|
+
this.emit("end")
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = BinaryEntriesStream
|
package/BinaryStream.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presidium DB Client and Server may be freely distributed under the CFOSS license.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const EventEmitter = require("events")
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @name BinaryStream
|
|
9
|
+
*
|
|
10
|
+
* @docs
|
|
11
|
+
* ```coffeescript [specscript]
|
|
12
|
+
* module stream 'https://nodejs.org/docs/latest-v26.x/api/stream.html'
|
|
13
|
+
* module events 'https://nodejs.org/docs/latest-v26.x/api/events.html'
|
|
14
|
+
*
|
|
15
|
+
* BinaryStream(readable stream.Readable, delimiter string) -> binaryStream events.EventEmitter
|
|
16
|
+
*
|
|
17
|
+
* textStream.on("data", chunk Buffer -> ())
|
|
18
|
+
*
|
|
19
|
+
* textStream.on("end", () -> ())
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
class BinaryStream extends EventEmitter {
|
|
23
|
+
constructor(readable, delimiter) {
|
|
24
|
+
super()
|
|
25
|
+
|
|
26
|
+
delimiter ??= ""
|
|
27
|
+
|
|
28
|
+
let buffer = Buffer.from("")
|
|
29
|
+
readable.on('data', (chunk, encoding) => {
|
|
30
|
+
if (typeof chunk == 'string') {
|
|
31
|
+
buffer = Buffer.concat([buffer, Buffer.from(chunk, encoding)])
|
|
32
|
+
} else {
|
|
33
|
+
buffer = Buffer.concat([buffer, chunk])
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let delimiterIndex = buffer.indexOf(delimiter)
|
|
37
|
+
while (delimiterIndex > -1) {
|
|
38
|
+
const slice = buffer.slice(0, delimiterIndex)
|
|
39
|
+
this.emit("data", slice)
|
|
40
|
+
buffer = buffer.slice(delimiterIndex + 1)
|
|
41
|
+
delimiterIndex = buffer.indexOf(delimiter)
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
readable.on('end', () => {
|
|
46
|
+
if (buffer.length > 0) {
|
|
47
|
+
this.emit("data", buffer)
|
|
48
|
+
}
|
|
49
|
+
this.emit("end")
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = BinaryStream
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presidium DB Client and Server may be freely distributed under the CFOSS license.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
require("rubico/global")
|
|
6
|
+
const DiskHashTable = require("presidium/DiskHashTable")
|
|
7
|
+
const DiskSortedHashTable = require("presidium/DiskSortedHashTable")
|
|
8
|
+
const Readable = require("presidium/Readable")
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @name DatabaseHandler
|
|
12
|
+
*
|
|
13
|
+
* @docs
|
|
14
|
+
* ```specscript
|
|
15
|
+
* module DiskHashTable "https://presidium.services/docs/DiskHashTable"
|
|
16
|
+
* module HTTP "https://presidium.services/docs/HTTP"
|
|
17
|
+
*
|
|
18
|
+
* DatabaseHandler(options {
|
|
19
|
+
* databasePath: string,
|
|
20
|
+
* manifestTableName: string,
|
|
21
|
+
* manifestTableInitialLength: number,
|
|
22
|
+
* manifestTableItemSize: number,
|
|
23
|
+
* defaultTableInitialLength: number,
|
|
24
|
+
* defaultTableItemSize: number,
|
|
25
|
+
* manifestHt: DiskHashTable,
|
|
26
|
+
* hts: Map<tableName string, DiskHashTable>,
|
|
27
|
+
* }) -> databaseHandler HTTP.AsyncHandler
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
function DatabaseHandler(options) {
|
|
31
|
+
const {
|
|
32
|
+
databasePath,
|
|
33
|
+
manifestTableName,
|
|
34
|
+
manifestTableInitialLength,
|
|
35
|
+
manifestTableItemSize,
|
|
36
|
+
defaultTableInitialLength,
|
|
37
|
+
defaultTableItemSize,
|
|
38
|
+
manifestHt,
|
|
39
|
+
hts,
|
|
40
|
+
} = options
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @name getHt
|
|
44
|
+
*
|
|
45
|
+
* @docs
|
|
46
|
+
* ```specscript
|
|
47
|
+
* module HTTP "https://presidium.services/docs/HTTP"
|
|
48
|
+
*
|
|
49
|
+
* getHt(tableName string, request HTTP.Request) -> ht DiskHashTable|DiskSortedHashTable
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
async function getHt(tableName, request, manifestHt) {
|
|
53
|
+
const htConfigString = await manifestHt.get(tableName)
|
|
54
|
+
let ht
|
|
55
|
+
if (htConfigString) {
|
|
56
|
+
const config = JSON.parse(htConfigString)
|
|
57
|
+
if (request.headers["client-name"] == "DiskHashTableClient") {
|
|
58
|
+
ht = new DiskHashTable(config)
|
|
59
|
+
await ht.init()
|
|
60
|
+
hts.set(tableName, ht)
|
|
61
|
+
} else if (request.headers["client-name"] == "DiskSortedHashTableClient") {
|
|
62
|
+
ht = new DiskSortedHashTable(config)
|
|
63
|
+
await ht.init()
|
|
64
|
+
hts.set(tableName, ht)
|
|
65
|
+
} else {
|
|
66
|
+
const error = new Error("Forbidden: Client-Name")
|
|
67
|
+
error.code = 403
|
|
68
|
+
throw error
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
if (request.headers["client-name"] == "DiskHashTableClient") {
|
|
72
|
+
const config = {
|
|
73
|
+
storagePath: `${databasePath}/${tableName}/storage`,
|
|
74
|
+
headerPath: `${databasePath}/${tableName}/header`,
|
|
75
|
+
initialLength: defaultTableInitialLength,
|
|
76
|
+
itemSize: defaultTableItemSize,
|
|
77
|
+
}
|
|
78
|
+
ht = new DiskHashTable(config)
|
|
79
|
+
await manifestHt.set(tableName, JSON.stringify(config))
|
|
80
|
+
await ht.init()
|
|
81
|
+
hts.set(tableName, ht)
|
|
82
|
+
} else if (request.headers["client-name"] == "DiskSortedHashTableClient") {
|
|
83
|
+
const config = {
|
|
84
|
+
storagePath: `${databasePath}/${tableName}/storage`,
|
|
85
|
+
headerPath: `${databasePath}/${tableName}/header`,
|
|
86
|
+
initialLength: defaultTableInitialLength,
|
|
87
|
+
itemSize: defaultTableItemSize,
|
|
88
|
+
sortValueType: request.headers["sort-value-type"],
|
|
89
|
+
degree: Number(request.headers["degree"]),
|
|
90
|
+
}
|
|
91
|
+
ht = new DiskSortedHashTable(config)
|
|
92
|
+
await manifestHt.set(tableName, JSON.stringify(config))
|
|
93
|
+
await ht.init()
|
|
94
|
+
hts.set(tableName, ht)
|
|
95
|
+
} else {
|
|
96
|
+
const error = new Error("Forbidden: Client-Name")
|
|
97
|
+
error.code = 403
|
|
98
|
+
throw error
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return ht
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return async function databaseHandler(request, response) {
|
|
105
|
+
const { pathname } = new URL(`http://*${request.url}`)
|
|
106
|
+
request.path = decodeURIComponent(
|
|
107
|
+
pathname == "/" ? pathname
|
|
108
|
+
: pathname.endsWith("/") ? pathname.slice(0, -1)
|
|
109
|
+
: pathname
|
|
110
|
+
)
|
|
111
|
+
request.pathSegments = request.path.slice(1).split("/")
|
|
112
|
+
request.pathDepth = request.pathSegments.length
|
|
113
|
+
|
|
114
|
+
if (request.pathDepth == 2) {
|
|
115
|
+
const tableName = request.pathSegments[0]
|
|
116
|
+
const fname = request.pathSegments[1]
|
|
117
|
+
const ht = hts.get(tableName)
|
|
118
|
+
?? await getHt(tableName, request, manifestHt)
|
|
119
|
+
|
|
120
|
+
await switchCase(
|
|
121
|
+
fname,
|
|
122
|
+
|
|
123
|
+
eq("clear"), async () => {
|
|
124
|
+
await ht.clear()
|
|
125
|
+
response.writeHead(200, {
|
|
126
|
+
"Content-Type": "text/plain",
|
|
127
|
+
})
|
|
128
|
+
response.end("OK")
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
eq("set"), async () => {
|
|
132
|
+
const { key, value, sortValue } = await Readable.JSON(request)
|
|
133
|
+
await ht.set(key, value, sortValue)
|
|
134
|
+
response.writeHead(200, {
|
|
135
|
+
"Content-Type": "text/plain",
|
|
136
|
+
})
|
|
137
|
+
response.end("OK")
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
eq("get"), async () => {
|
|
141
|
+
const { key } = await Readable.JSON(request)
|
|
142
|
+
const value = await ht.get(key)
|
|
143
|
+
if (value === undefined) {
|
|
144
|
+
response.writeHead(499, {
|
|
145
|
+
"Content-Type": "text/plain",
|
|
146
|
+
})
|
|
147
|
+
response.end("Resource Not Found")
|
|
148
|
+
} else {
|
|
149
|
+
response.writeHead(200, {
|
|
150
|
+
"Content-Type": "text/plain",
|
|
151
|
+
})
|
|
152
|
+
response.end(value)
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
eq("getBinary"), async () => {
|
|
157
|
+
const { key } = await Readable.JSON(request)
|
|
158
|
+
const value = await ht.getBinary(key)
|
|
159
|
+
response.writeHead(200, {
|
|
160
|
+
"Content-Type": "application/octet-stream",
|
|
161
|
+
})
|
|
162
|
+
response.end(value)
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
eq("delete"), async () => {
|
|
166
|
+
const { key } = await Readable.JSON(request)
|
|
167
|
+
const didDelete = await ht.delete(key)
|
|
168
|
+
response.writeHead(200, {
|
|
169
|
+
"Content-Type": "text/plain",
|
|
170
|
+
})
|
|
171
|
+
response.end(String(didDelete))
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
eq("count"), async () => {
|
|
175
|
+
const count = ht.count()
|
|
176
|
+
response.writeHead(200, {
|
|
177
|
+
"Content-Type": "text/plain",
|
|
178
|
+
})
|
|
179
|
+
response.end(String(count))
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
eq("iterator"), async () => {
|
|
183
|
+
const options = await Readable.JSON(request)
|
|
184
|
+
response.writeHead(200, {
|
|
185
|
+
"Content-Type": "application/octet-stream",
|
|
186
|
+
"Content-Encoding": "utf8",
|
|
187
|
+
})
|
|
188
|
+
const count = ht.count()
|
|
189
|
+
for await (const value of ht.iterator(options)) {
|
|
190
|
+
response.write(value)
|
|
191
|
+
response.write("")
|
|
192
|
+
}
|
|
193
|
+
response.end()
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
eq("entriesIterator"), async () => {
|
|
197
|
+
const options = await Readable.JSON(request)
|
|
198
|
+
response.writeHead(200, {
|
|
199
|
+
"Content-Type": "application/octet-stream",
|
|
200
|
+
"Content-Encoding": "utf8",
|
|
201
|
+
})
|
|
202
|
+
const count = ht.count()
|
|
203
|
+
for await (const [key, value] of ht.entriesIterator(options)) {
|
|
204
|
+
response.write(key)
|
|
205
|
+
response.write("")
|
|
206
|
+
response.write(value)
|
|
207
|
+
response.write("")
|
|
208
|
+
}
|
|
209
|
+
response.end()
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
eq("forwardIterator"), async () => {
|
|
213
|
+
const options = await Readable.JSON(request)
|
|
214
|
+
response.writeHead(200, {
|
|
215
|
+
"Content-Type": "application/octet-stream",
|
|
216
|
+
"Content-Encoding": "utf8",
|
|
217
|
+
})
|
|
218
|
+
const count = ht.count()
|
|
219
|
+
for await (const value of ht.forwardIterator(options)) {
|
|
220
|
+
response.write(value)
|
|
221
|
+
response.write("")
|
|
222
|
+
}
|
|
223
|
+
response.end()
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
eq("reverseIterator"), async () => {
|
|
227
|
+
const options = await Readable.JSON(request)
|
|
228
|
+
response.writeHead(200, {
|
|
229
|
+
"Content-Type": "application/octet-stream",
|
|
230
|
+
"Content-Encoding": "utf8",
|
|
231
|
+
})
|
|
232
|
+
const count = ht.count()
|
|
233
|
+
for await (const value of ht.reverseIterator(options)) {
|
|
234
|
+
response.write(value)
|
|
235
|
+
response.write("")
|
|
236
|
+
}
|
|
237
|
+
response.end()
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
eq("forwardEntriesIterator"), async () => {
|
|
241
|
+
const options = await Readable.JSON(request)
|
|
242
|
+
response.writeHead(200, {
|
|
243
|
+
"Content-Type": "application/octet-stream",
|
|
244
|
+
"Content-Encoding": "utf8",
|
|
245
|
+
})
|
|
246
|
+
const count = ht.count()
|
|
247
|
+
for await (const [key, value, sortValue] of ht.forwardEntriesIterator(options)) {
|
|
248
|
+
response.write(key)
|
|
249
|
+
response.write("")
|
|
250
|
+
response.write(value)
|
|
251
|
+
response.write("")
|
|
252
|
+
response.write(String(sortValue))
|
|
253
|
+
response.write("")
|
|
254
|
+
}
|
|
255
|
+
response.end()
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
eq("reverseEntriesIterator"), async () => {
|
|
259
|
+
const options = await Readable.JSON(request)
|
|
260
|
+
response.writeHead(200, {
|
|
261
|
+
"Content-Type": "application/octet-stream",
|
|
262
|
+
"Content-Encoding": "utf8",
|
|
263
|
+
})
|
|
264
|
+
const count = ht.count()
|
|
265
|
+
for await (const [key, value, sortValue] of ht.reverseEntriesIterator(options)) {
|
|
266
|
+
response.write(key)
|
|
267
|
+
response.write("")
|
|
268
|
+
response.write(value)
|
|
269
|
+
response.write("")
|
|
270
|
+
response.write(String(sortValue))
|
|
271
|
+
response.write("")
|
|
272
|
+
}
|
|
273
|
+
response.end()
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
async () => {
|
|
277
|
+
const error = new Error("Forbidden: Hash Table Method")
|
|
278
|
+
error.code = "403"
|
|
279
|
+
throw error
|
|
280
|
+
},
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
} else {
|
|
284
|
+
response.writeHead(404, {
|
|
285
|
+
"Content-Type": "text/plain",
|
|
286
|
+
})
|
|
287
|
+
response.end("Not Found")
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
module.exports = DatabaseHandler
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presidium DB Client and Server may be freely distributed under the CFOSS license.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const HTTP = require("presidium/HTTP")
|
|
6
|
+
const DiskHashTable = require("presidium/DiskHashTable")
|
|
7
|
+
const DatabaseHandler = require("./DatabaseHandler")
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @name DatabaseServer
|
|
11
|
+
*
|
|
12
|
+
* @docs
|
|
13
|
+
* ```specscript
|
|
14
|
+
* new DatabaseServer(options {
|
|
15
|
+
* databasePath: string,
|
|
16
|
+
* manifestTableName: string,
|
|
17
|
+
* manifestTableInitialLength: number,
|
|
18
|
+
* manifestTableItemSize: number,
|
|
19
|
+
* defaultTableInitialLength: number,
|
|
20
|
+
* defaultTableItemSize: number,
|
|
21
|
+
* defaultDiskSortedHashTableDegree: number,
|
|
22
|
+
* }) -> server DatabaseServer
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
class DatabaseServer {
|
|
26
|
+
constructor(options) {
|
|
27
|
+
this.databasePath = options.databasePath
|
|
28
|
+
this.manifestTableName = options.manifestTableName ?? "Manifest"
|
|
29
|
+
this.manifestTableInitialLength = options.manifestTableInitialLength ?? 1048576
|
|
30
|
+
this.manifestTableItemSize = options.manifestTableItemSize ?? 1024
|
|
31
|
+
this.defaultTableInitialLength = options.defaultTableInitialLength ?? 1048576
|
|
32
|
+
this.defaultTableItemSize = options.defaultTableItemSize ?? 1024
|
|
33
|
+
this.defaultDiskSortedHashTableDegree = options.defaultDiskSortedHashTableDegree ?? 2
|
|
34
|
+
this.server = null
|
|
35
|
+
this.hts = new Map()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @name init
|
|
40
|
+
*
|
|
41
|
+
* @docs
|
|
42
|
+
* ```specscript
|
|
43
|
+
* init() -> Promise<>
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
async init() {
|
|
47
|
+
const manifestHt = new DiskHashTable({
|
|
48
|
+
storagePath: `${this.databasePath}/${this.manifestTableName}/storage`,
|
|
49
|
+
headerPath: `${this.databasePath}/${this.manifestTableName}/header`,
|
|
50
|
+
initialLength: this.manifestTableInitialLength,
|
|
51
|
+
itemSize: this.manifestTableItemSize,
|
|
52
|
+
})
|
|
53
|
+
await manifestHt.init()
|
|
54
|
+
this.manifestHt = manifestHt
|
|
55
|
+
|
|
56
|
+
const databaseHandler = DatabaseHandler({
|
|
57
|
+
databasePath: this.databasePath,
|
|
58
|
+
manifestTableName: this.manifestTableName,
|
|
59
|
+
manifestTableInitialLength: this.manifestTableInitialLength,
|
|
60
|
+
manifestTableItemSize: this.manifestTableItemSize,
|
|
61
|
+
defaultTableInitialLength: this.defaultTableInitialLength,
|
|
62
|
+
defaultTableItemSize: this.defaultTableItemSize,
|
|
63
|
+
defaultDiskSortedHashTableDegree: this.defaultDiskSortedHashTableDegree,
|
|
64
|
+
manifestHt: this.manifestHt,
|
|
65
|
+
hts: this.hts,
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
this.server = HTTP.Server(databaseHandler)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @name listen
|
|
73
|
+
*
|
|
74
|
+
* @docs
|
|
75
|
+
* ```specscript
|
|
76
|
+
* listen(port number) -> ()
|
|
77
|
+
* listen(port number, callback function) -> ()
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
listen(port, callback) {
|
|
81
|
+
this.server.listen(port, callback)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @name close
|
|
86
|
+
*
|
|
87
|
+
* @docs
|
|
88
|
+
* ```specscript
|
|
89
|
+
* close() -> ()
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
close() {
|
|
93
|
+
this.manifestHt.close()
|
|
94
|
+
for (const ht of this.hts.values()) {
|
|
95
|
+
ht.close()
|
|
96
|
+
}
|
|
97
|
+
this.server.close()
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = DatabaseServer
|