corecdtl 0.1.6 → 0.1.7
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/dist/hypernode.js +1 -1
- package/package.json +8 -6
- package/prebuilds/darwin-arm64/corecdtl.node +0 -0
- package/prebuilds/linux-x64/corecdtl.node +0 -0
- package/prebuilds/win32-x64/corecdtl.node +0 -0
- package/binding.gyp +0 -42
- package/native/http/core/http_core.cpp +0 -395
- package/native/http/core/http_core.h +0 -90
- package/native/http/core/http_scanner.cpp +0 -1502
- package/native/http/core/http_scanner.h +0 -474
- package/native/http/cpool/cpool.cpp +0 -284
- package/native/http/cpool/cpool.h +0 -39
- package/native/http/parser/asset_meta.cpp +0 -31
- package/native/http/parser/asset_meta.h +0 -53
- package/native/http/parser/asset_parser.cpp +0 -52
- package/native/http/parser/asset_parser.h +0 -20
- package/native/http/routes/route.h +0 -85
- package/native/http/routes/route_builder.cpp +0 -161
- package/native/http/routes/route_matching.cpp +0 -266
- package/native/http/routes/route_print.cpp +0 -20
- package/native/main.cpp +0 -54
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
#include "cpool.h"
|
|
2
|
-
#include <iostream>
|
|
3
|
-
#include <stdexcept>
|
|
4
|
-
|
|
5
|
-
Napi::Function CPool::GetClass(Napi::Env env) {
|
|
6
|
-
return DefineClass(env, "CPool", {
|
|
7
|
-
InstanceMethod("initializePool", &CPool::InitializePool),
|
|
8
|
-
InstanceMethod("registerObj", &CPool::RegisterObj),
|
|
9
|
-
InstanceMethod("allocate", &CPool::Allocate),
|
|
10
|
-
InstanceMethod("free", &CPool::Free),
|
|
11
|
-
InstanceMethod("resizePool", &CPool::ResizePool),
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
CPool::CPool(const Napi::CallbackInfo& info)
|
|
16
|
-
: Napi::ObjectWrap<CPool>(info) {
|
|
17
|
-
m_activeSize = 0;
|
|
18
|
-
m_currentSize = 0;
|
|
19
|
-
m_retiredCount = 0;
|
|
20
|
-
m_shrinking = false;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
CPool::~CPool() {
|
|
24
|
-
for (auto &e : m_poolEntries) {
|
|
25
|
-
if (!e.jsRef.IsEmpty()) e.jsRef.Unref();
|
|
26
|
-
}
|
|
27
|
-
m_poolEntries.clear();
|
|
28
|
-
m_freeStack.clear();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
__attribute__((always_inline))
|
|
32
|
-
void CPool::pushFreeIndex(int idx) {
|
|
33
|
-
m_freeStack.push_back(idx);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
__attribute__((always_inline))
|
|
37
|
-
int CPool::popFreeIndex() {
|
|
38
|
-
if (m_freeStack.empty()) [[unlikely]] return -1;
|
|
39
|
-
int idx = m_freeStack.back();
|
|
40
|
-
m_freeStack.pop_back();
|
|
41
|
-
return idx;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
Napi::Value CPool::InitializePool(const Napi::CallbackInfo& info) {
|
|
45
|
-
Napi::Env env = info.Env();
|
|
46
|
-
if (info.Length() < 1 || !info[0].IsNumber()) {
|
|
47
|
-
Napi::TypeError::New(env, "Pool size must be a number").ThrowAsJavaScriptException();
|
|
48
|
-
return env.Null();
|
|
49
|
-
}
|
|
50
|
-
size_t newSize = info[0].As<Napi::Number>().Uint32Value();
|
|
51
|
-
if (newSize == 0) {
|
|
52
|
-
Napi::Error::New(env, "Pool size must be > 0").ThrowAsJavaScriptException();
|
|
53
|
-
return env.Null();
|
|
54
|
-
}
|
|
55
|
-
if (m_currentSize != 0) {
|
|
56
|
-
Napi::Error::New(env, "Pool already initialized").ThrowAsJavaScriptException();
|
|
57
|
-
return env.Null();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
m_poolEntries.resize(newSize);
|
|
62
|
-
m_freeStack.reserve(newSize);
|
|
63
|
-
} catch (const std::bad_alloc&) {
|
|
64
|
-
Napi::Error::New(env, "Allocation failed").ThrowAsJavaScriptException();
|
|
65
|
-
return env.Null();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
m_currentSize = newSize;
|
|
69
|
-
m_activeSize = newSize;
|
|
70
|
-
m_retiredCount = 0;
|
|
71
|
-
m_shrinking = false;
|
|
72
|
-
|
|
73
|
-
// Initially all slots are free for registration and allocation.
|
|
74
|
-
for (size_t i = 0; i < (size_t)newSize; ++i) {
|
|
75
|
-
m_poolEntries[i].inUse = false;
|
|
76
|
-
pushFreeIndex((int)i);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return env.Undefined();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
Napi::Value CPool::RegisterObj(const Napi::CallbackInfo& info) {
|
|
83
|
-
Napi::Env env = info.Env();
|
|
84
|
-
if (info.Length() < 1 || !info[0].IsObject()) {
|
|
85
|
-
Napi::TypeError::New(env, "RegisterObj expects an object").ThrowAsJavaScriptException();
|
|
86
|
-
return env.Null();
|
|
87
|
-
}
|
|
88
|
-
if (m_currentSize == 0) {
|
|
89
|
-
Napi::Error::New(env, "Pool not initialized").ThrowAsJavaScriptException();
|
|
90
|
-
return env.Null();
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Find an index that currently has no jsRef assigned.
|
|
94
|
-
int found = -1;
|
|
95
|
-
for (size_t i = 0; i < m_currentSize; ++i) {
|
|
96
|
-
if (m_poolEntries[i].jsRef.IsEmpty()) {
|
|
97
|
-
found = (int)i;
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
if (found == -1) {
|
|
102
|
-
Napi::Error::New(env, "No free registration slot").ThrowAsJavaScriptException();
|
|
103
|
-
return env.Null();
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
Napi::Object obj = info[0].As<Napi::Object>();
|
|
107
|
-
m_poolEntries[found].jsRef = Napi::Persistent(obj);
|
|
108
|
-
// keep it weak in sense we managed lifetime; do not call Ref here to avoid keeping V8 alive unnecessarily
|
|
109
|
-
// but persistent already increases refcount; if you want to manage GC, call Ref/Unref appropriately.
|
|
110
|
-
|
|
111
|
-
return Napi::Number::New(env, found);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
Napi::Value CPool::Allocate(const Napi::CallbackInfo& info) {
|
|
115
|
-
Napi::Env env = info.Env();
|
|
116
|
-
|
|
117
|
-
if (m_activeSize == 0) [[unlikely]] {
|
|
118
|
-
return env.Null();
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// if shrinking and retired region exists, still can allocate from active region only
|
|
122
|
-
int idx = popFreeIndex();
|
|
123
|
-
if (idx == -1) [[unlikely]] {
|
|
124
|
-
// no free slot
|
|
125
|
-
return env.Null();
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Safety: If popped index is >= activeSize (retired area) -> put back and fail
|
|
129
|
-
if ((size_t)idx >= m_activeSize) [[unlikely]] {
|
|
130
|
-
// returned index belongs to retired area, push it back and fail allocation
|
|
131
|
-
pushFreeIndex(idx);
|
|
132
|
-
return env.Null();
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
PoolEntry& entry = m_poolEntries[idx];
|
|
136
|
-
entry.inUse = true;
|
|
137
|
-
|
|
138
|
-
// Return the JS object or null if not registered
|
|
139
|
-
if (entry.jsRef.IsEmpty()) [[likely]] {
|
|
140
|
-
// Not registered JS object — still return null (caller must handle)
|
|
141
|
-
return env.Null();
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return entry.jsRef.Value();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
Napi::Value CPool::Free(const Napi::CallbackInfo& info) {
|
|
148
|
-
Napi::Env env = info.Env();
|
|
149
|
-
if (info.Length() < 1 || !info[0].IsNumber()) [[unlikely]] {
|
|
150
|
-
Napi::TypeError::New(env, "Free expects index number").ThrowAsJavaScriptException();
|
|
151
|
-
return env.Null();
|
|
152
|
-
}
|
|
153
|
-
int idx = info[0].As<Napi::Number>().Int32Value();
|
|
154
|
-
if (idx < 0 || (size_t)idx >= m_currentSize) [[unlikely]] {
|
|
155
|
-
Napi::RangeError::New(env, "Index out of range").ThrowAsJavaScriptException();
|
|
156
|
-
return env.Null();
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
PoolEntry& entry = m_poolEntries[idx];
|
|
160
|
-
if (!entry.inUse) [[unlikely]] {
|
|
161
|
-
// double free - ignore silently (or optionally log)
|
|
162
|
-
return env.Undefined();
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
entry.inUse = false;
|
|
166
|
-
|
|
167
|
-
// If this index is in retired area, we must Unref the jsRef and decrease retired count.
|
|
168
|
-
if ((size_t)idx >= m_activeSize) [[unlikely]] {
|
|
169
|
-
// unref persistent ref if set
|
|
170
|
-
if (!entry.jsRef.IsEmpty()) {
|
|
171
|
-
entry.jsRef.Unref();
|
|
172
|
-
entry.jsRef = Napi::ObjectReference();
|
|
173
|
-
}
|
|
174
|
-
if (m_retiredCount > 0) {
|
|
175
|
-
--m_retiredCount;
|
|
176
|
-
if (m_retiredCount == 0 && m_shrinking) {
|
|
177
|
-
finalizeShrinkIfNeeded(env);
|
|
178
|
-
return env.Undefined();
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
// we don't push retired indices back into freeStack because retired area will be shrunk away
|
|
182
|
-
return env.Undefined();
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// normal free -> push back to free list
|
|
186
|
-
pushFreeIndex(idx);
|
|
187
|
-
return env.Undefined();
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
void CPool::finalizeShrinkIfNeeded(Napi::Env env) {
|
|
191
|
-
// physically release retired area
|
|
192
|
-
if (!m_shrinking) return;
|
|
193
|
-
for (size_t i = m_activeSize; i < m_currentSize; ++i) {
|
|
194
|
-
if (!m_poolEntries[i].jsRef.IsEmpty()) {
|
|
195
|
-
m_poolEntries[i].jsRef.Unref();
|
|
196
|
-
m_poolEntries[i].jsRef = Napi::ObjectReference();
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
m_poolEntries.resize(m_activeSize);
|
|
200
|
-
m_currentSize = m_activeSize;
|
|
201
|
-
// rebuild freeStack to contain only indices < activeSize that are free
|
|
202
|
-
m_freeStack.clear();
|
|
203
|
-
m_freeStack.reserve(m_activeSize);
|
|
204
|
-
for (size_t i = 0; i < m_activeSize; ++i) {
|
|
205
|
-
if (!m_poolEntries[i].inUse) pushFreeIndex((int)i);
|
|
206
|
-
}
|
|
207
|
-
m_shrinking = false;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
Napi::Value CPool::ResizePool(const Napi::CallbackInfo& info) {
|
|
211
|
-
Napi::Env env = info.Env();
|
|
212
|
-
if (info.Length() < 1 || !info[0].IsNumber()) {
|
|
213
|
-
Napi::TypeError::New(env, "ResizePool expects a number").ThrowAsJavaScriptException();
|
|
214
|
-
return env.Null();
|
|
215
|
-
}
|
|
216
|
-
size_t newSize = info[0].As<Napi::Number>().Uint32Value();
|
|
217
|
-
if (newSize == 0) {
|
|
218
|
-
Napi::Error::New(env, "new size must be > 0").ThrowAsJavaScriptException();
|
|
219
|
-
return env.Null();
|
|
220
|
-
}
|
|
221
|
-
if (newSize == m_activeSize) return env.Undefined();
|
|
222
|
-
|
|
223
|
-
if (newSize > m_currentSize) {
|
|
224
|
-
// expand physical vector
|
|
225
|
-
try {
|
|
226
|
-
size_t old = m_currentSize;
|
|
227
|
-
m_poolEntries.resize(newSize);
|
|
228
|
-
// initialize new slots as free and push them to freeStack if they are within active area
|
|
229
|
-
for (size_t i = old; i < newSize; ++i) {
|
|
230
|
-
m_poolEntries[i].inUse = false;
|
|
231
|
-
}
|
|
232
|
-
// add new indices to freeStack for the extended active area
|
|
233
|
-
for (size_t i = old; i < newSize; ++i) {
|
|
234
|
-
pushFreeIndex((int)i);
|
|
235
|
-
}
|
|
236
|
-
m_currentSize = newSize;
|
|
237
|
-
m_activeSize = newSize;
|
|
238
|
-
} catch (const std::bad_alloc&) {
|
|
239
|
-
Napi::Error::New(env, "allocation failed").ThrowAsJavaScriptException();
|
|
240
|
-
return env.Null();
|
|
241
|
-
}
|
|
242
|
-
} else {
|
|
243
|
-
// shrinking -> mark activeSize and if there are in-use slots in retired area mark retiring
|
|
244
|
-
size_t retiredStart = newSize;
|
|
245
|
-
size_t activeInRetired = 0;
|
|
246
|
-
for (size_t i = retiredStart; i < m_currentSize; ++i) {
|
|
247
|
-
if (m_poolEntries[i].inUse) activeInRetired++;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
m_activeSize = newSize;
|
|
251
|
-
|
|
252
|
-
if (activeInRetired == 0) {
|
|
253
|
-
// safe to immediately shrink: unref jsRefs and resize
|
|
254
|
-
for (size_t i = retiredStart; i < m_currentSize; ++i) {
|
|
255
|
-
if (!m_poolEntries[i].jsRef.IsEmpty()) {
|
|
256
|
-
m_poolEntries[i].jsRef.Unref();
|
|
257
|
-
m_poolEntries[i].jsRef = Napi::ObjectReference();
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
m_poolEntries.resize(m_activeSize);
|
|
261
|
-
m_currentSize = m_activeSize;
|
|
262
|
-
// rebuild freeStack
|
|
263
|
-
m_freeStack.clear();
|
|
264
|
-
m_freeStack.reserve(m_activeSize);
|
|
265
|
-
for (size_t i = 0; i < m_activeSize; ++i) {
|
|
266
|
-
if (!m_poolEntries[i].inUse) pushFreeIndex((int)i);
|
|
267
|
-
}
|
|
268
|
-
} else {
|
|
269
|
-
// there are active entries in retired area -> mark for shrink
|
|
270
|
-
m_retiredCount = activeInRetired;
|
|
271
|
-
m_shrinking = true;
|
|
272
|
-
// remove retired indices from freeStack if any (they shouldn't be free)
|
|
273
|
-
std::vector<int> newStack;
|
|
274
|
-
newStack.reserve(m_freeStack.size());
|
|
275
|
-
for (int idx : m_freeStack) {
|
|
276
|
-
if ((size_t)idx < m_activeSize) newStack.push_back(idx);
|
|
277
|
-
}
|
|
278
|
-
m_freeStack.swap(newStack);
|
|
279
|
-
// retired indices remain until freed; when freed, Free() will decrement m_retiredCount and finalize
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return env.Undefined();
|
|
284
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
#include <napi.h>
|
|
3
|
-
#include <vector>
|
|
4
|
-
#include <stack>
|
|
5
|
-
#include <functional>
|
|
6
|
-
|
|
7
|
-
struct PoolEntry {
|
|
8
|
-
Napi::ObjectReference jsRef; // persistent JS object
|
|
9
|
-
bool inUse = false; // true when allocated
|
|
10
|
-
// optionally other meta fields...
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
class CPool : public Napi::ObjectWrap<CPool> {
|
|
14
|
-
public:
|
|
15
|
-
static Napi::Function GetClass(Napi::Env env);
|
|
16
|
-
CPool(const Napi::CallbackInfo& info);
|
|
17
|
-
~CPool();
|
|
18
|
-
|
|
19
|
-
// NAPI methods
|
|
20
|
-
Napi::Value InitializePool(const Napi::CallbackInfo& info);
|
|
21
|
-
Napi::Value RegisterObj(const Napi::CallbackInfo& info);
|
|
22
|
-
Napi::Value Allocate(const Napi::CallbackInfo& info);
|
|
23
|
-
Napi::Value Free(const Napi::CallbackInfo& info);
|
|
24
|
-
Napi::Value ResizePool(const Napi::CallbackInfo& info);
|
|
25
|
-
|
|
26
|
-
private:
|
|
27
|
-
// core data
|
|
28
|
-
std::vector<PoolEntry> m_poolEntries;
|
|
29
|
-
std::vector<int> m_freeStack; // indices of free entries (LIFO)
|
|
30
|
-
size_t m_activeSize = 0; // visible active capacity
|
|
31
|
-
size_t m_currentSize = 0; // physical vector size
|
|
32
|
-
size_t m_retiredCount = 0; // number of in-use entries in retired zone
|
|
33
|
-
bool m_shrinking = false; // indicates shrink process in progress
|
|
34
|
-
|
|
35
|
-
// helpers
|
|
36
|
-
void pushFreeIndex(int idx);
|
|
37
|
-
int popFreeIndex(); // -1 if none
|
|
38
|
-
void finalizeShrinkIfNeeded(Napi::Env env);
|
|
39
|
-
};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
#include <unordered_map>
|
|
3
|
-
#include "asset_meta.h"
|
|
4
|
-
|
|
5
|
-
namespace Asset {
|
|
6
|
-
size_t UrlHash::operator()(const UrlKey& k) const noexcept {
|
|
7
|
-
// FNV-1a 64-bit
|
|
8
|
-
size_t h = 1469598103934665603ULL;
|
|
9
|
-
for (size_t i = 0; i < k.len; ++i) {
|
|
10
|
-
h ^= static_cast<uint8_t>(k.data[i]);
|
|
11
|
-
h *= 1099511628211ULL;
|
|
12
|
-
}
|
|
13
|
-
return h;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
bool UrlEq::operator()(const UrlKey& a, const UrlKey& b) const noexcept {
|
|
17
|
-
return a == b;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
AssetIndex::AssetIndex() = default;
|
|
21
|
-
|
|
22
|
-
void AssetIndex::add(const char* path, uint32_t len, AssetMeta* meta) {
|
|
23
|
-
index.emplace(UrlKey{ path, (size_t)len }, meta);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
AssetMeta* AssetIndex::find(const char* path, uint32_t len) const noexcept {
|
|
27
|
-
UrlKey key{ path, len };
|
|
28
|
-
auto it = index.find(key);
|
|
29
|
-
return it == index.end() ? nullptr : it->second;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
#include <cstdint>
|
|
3
|
-
#include <cstring>
|
|
4
|
-
#include <unordered_map>
|
|
5
|
-
|
|
6
|
-
namespace Asset {
|
|
7
|
-
enum class CacheKind : uint8_t {
|
|
8
|
-
RAM,
|
|
9
|
-
MMAP,
|
|
10
|
-
SENDFILE
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
struct UrlKey {
|
|
14
|
-
const char* data;
|
|
15
|
-
size_t len;
|
|
16
|
-
|
|
17
|
-
bool operator==(const UrlKey& other) const {
|
|
18
|
-
if (len != other.len) return false;
|
|
19
|
-
return std::memcmp(data, other.data, len) == 0;
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
struct UrlHash {
|
|
24
|
-
size_t operator()(const UrlKey& k) const noexcept;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
struct UrlEq {
|
|
28
|
-
bool operator()(const UrlKey& a, const UrlKey& b) const noexcept;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
struct AssetMeta {
|
|
33
|
-
const char* path;
|
|
34
|
-
uint32_t pathLen;
|
|
35
|
-
int fd;
|
|
36
|
-
uint64_t size;
|
|
37
|
-
uint64_t mtime;
|
|
38
|
-
CacheKind kind;
|
|
39
|
-
void* data;
|
|
40
|
-
uint64_t dataLen;
|
|
41
|
-
uint64_t etag;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
class AssetIndex {
|
|
45
|
-
public:
|
|
46
|
-
using MapType = std::unordered_map<UrlKey, AssetMeta*, UrlHash, UrlEq>;
|
|
47
|
-
AssetIndex();
|
|
48
|
-
void add(const char* path, uint32_t len, AssetMeta* meta);
|
|
49
|
-
AssetMeta* find(const char* path, uint32_t len) const noexcept;
|
|
50
|
-
private:
|
|
51
|
-
MapType index;
|
|
52
|
-
};
|
|
53
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
#include "asset_parser.h"
|
|
2
|
-
#include "asset_meta.h"
|
|
3
|
-
#include <iostream>
|
|
4
|
-
|
|
5
|
-
using namespace Asset;
|
|
6
|
-
|
|
7
|
-
__attribute__((always_inline))
|
|
8
|
-
CacheKind decideCacheKind(uint64_t size) {
|
|
9
|
-
if (size <= 64 * 1024)
|
|
10
|
-
return Asset::CacheKind::RAM;
|
|
11
|
-
if (size <= 2 * 1024 * 1024)
|
|
12
|
-
return Asset::CacheKind::MMAP;
|
|
13
|
-
return Asset::CacheKind::SENDFILE;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
Napi::Function PublicAssetParser::GetClass(Napi::Env env) {
|
|
17
|
-
return DefineClass(env, "PublicAssetParser", {
|
|
18
|
-
InstanceMethod("setAssetRoute", &PublicAssetParser::SetAssetRoute),
|
|
19
|
-
InstanceMethod("handlePublicAsset", &PublicAssetParser::HandlePublicAsset)
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
PublicAssetParser::PublicAssetParser(const Napi::CallbackInfo& info)
|
|
24
|
-
: Napi::ObjectWrap<PublicAssetParser>(info),
|
|
25
|
-
assetRouteLen(0) {}
|
|
26
|
-
|
|
27
|
-
void PublicAssetParser::SetAssetRoute(const Napi::CallbackInfo& info) {
|
|
28
|
-
const std::string route = info[0].As<Napi::String>();
|
|
29
|
-
assetRouteName = route;
|
|
30
|
-
assetRouteLen = route.length();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
Napi::Value PublicAssetParser::HandlePublicAsset(
|
|
34
|
-
const Napi::CallbackInfo& info
|
|
35
|
-
) {
|
|
36
|
-
Napi::Env env = info.Env();
|
|
37
|
-
|
|
38
|
-
const char* buf = info[0].As<Napi::Buffer<char>>().Data();
|
|
39
|
-
size_t startOffset = info[1].As<Napi::Number>().Uint32Value();
|
|
40
|
-
|
|
41
|
-
size_t i = startOffset + assetRouteLen;
|
|
42
|
-
size_t begin = i;
|
|
43
|
-
|
|
44
|
-
while (buf[i] != '?' && buf[i] != ' ' && buf[i] != '\0') {
|
|
45
|
-
++i;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
size_t pathLen = i - begin;
|
|
49
|
-
const char* path = buf + begin;
|
|
50
|
-
|
|
51
|
-
return Napi::String::New(env, path, pathLen);
|
|
52
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
#include <napi.h>
|
|
3
|
-
#include <string>
|
|
4
|
-
#include "asset_meta.h"
|
|
5
|
-
|
|
6
|
-
class PublicAssetParser : public Napi::ObjectWrap<PublicAssetParser> {
|
|
7
|
-
public:
|
|
8
|
-
static Napi::Function GetClass(Napi::Env env);
|
|
9
|
-
|
|
10
|
-
PublicAssetParser(const Napi::CallbackInfo& info);
|
|
11
|
-
~PublicAssetParser() = default;
|
|
12
|
-
|
|
13
|
-
void SetAssetRoute(const Napi::CallbackInfo& info);
|
|
14
|
-
Napi::Value HandlePublicAsset(const Napi::CallbackInfo& info);
|
|
15
|
-
|
|
16
|
-
private:
|
|
17
|
-
std::string assetRouteName;
|
|
18
|
-
size_t assetRouteLen;
|
|
19
|
-
Asset::AssetIndex assetIndex;
|
|
20
|
-
};
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
#include <cstdint>
|
|
3
|
-
#include <vector>
|
|
4
|
-
#include <napi.h>
|
|
5
|
-
|
|
6
|
-
using namespace std;
|
|
7
|
-
|
|
8
|
-
namespace RouteBuilder {
|
|
9
|
-
enum ParamType { kString = 1, kNumber = 2 };
|
|
10
|
-
/// Endpoint parameter metadata.
|
|
11
|
-
struct EndpointParam {
|
|
12
|
-
std::string name;
|
|
13
|
-
ParamType type = ParamType::kString;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/// Endpoint description used by the builder.
|
|
18
|
-
struct Endpoint {
|
|
19
|
-
const char* url; ///< Null-terminated URL pattern (e.g. "users/:id/")
|
|
20
|
-
std::vector<EndpointParam> params;
|
|
21
|
-
int vptr_table_index = -1;
|
|
22
|
-
|
|
23
|
-
explicit operator bool() const {
|
|
24
|
-
return vptr_table_index != -1;
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/// Route trie node layout. We purposefully separate 'hot' data from
|
|
29
|
-
/// 'cold' data: small fixed-size fields come first for cache locality.
|
|
30
|
-
struct alignas(64) RouteNode {
|
|
31
|
-
// HOT data
|
|
32
|
-
uint64_t value = 0; ///< Packed up to 8 characters for fast comparison
|
|
33
|
-
size_t value_length = 0; ///< Number of meaningful bytes in `value`
|
|
34
|
-
int vptr_table_index = -1; ///< Handler index if this node terminates an endpoint
|
|
35
|
-
ParamType param_type = ParamType::kString;
|
|
36
|
-
char _padding[7] = {};
|
|
37
|
-
bool is_param = false;
|
|
38
|
-
bool is_wildcard = false;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// COLD data
|
|
42
|
-
std::string param_name; ///< Name of the parameter (if is_param)
|
|
43
|
-
std::vector<std::shared_ptr<RouteNode>> children;///< Child nodes
|
|
44
|
-
std::weak_ptr<RouteNode> parent; ///< Optional parent pointer
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// static_assert(sizeof(RouteNode) % 64 == 0, "RouteNode must be 64-byte aligned multiple");
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// Public API
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* @brief Build a route tree from a Endpoints Eps.
|
|
56
|
-
*
|
|
57
|
-
* The caller retains ownership of Endpoint; the function copies endpoint
|
|
58
|
-
* descriptors as needed. Returns nullptr if table empty.
|
|
59
|
-
*/
|
|
60
|
-
std::shared_ptr<RouteNode> buildRouteTree(const std::vector<Endpoint>& eps) noexcept;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* @brief Match a URL against a previously-built route tree.
|
|
65
|
-
*
|
|
66
|
-
* The returned unique_ptr holds the match result. Use vptr_table_index to
|
|
67
|
-
* dispatch to the appropriate handler.
|
|
68
|
-
*/
|
|
69
|
-
int matchUrl(
|
|
70
|
-
Napi::Env env,
|
|
71
|
-
const std::shared_ptr<RouteNode>& root,
|
|
72
|
-
const char* url,
|
|
73
|
-
size_t urlLen,
|
|
74
|
-
uint32_t* offset,
|
|
75
|
-
Napi::Array* pathParams,
|
|
76
|
-
Napi::Object* queryParams,
|
|
77
|
-
uint32_t query_limit
|
|
78
|
-
) noexcept;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* @brief Debug helper: print the route tree (human-readable).
|
|
83
|
-
*/
|
|
84
|
-
void printRouteTree(const std::shared_ptr<RouteNode>& node, int depth = 0) noexcept;
|
|
85
|
-
} // namespace RouteBuilder
|