corecdtl 0.1.6 → 0.1.8

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.
@@ -1,161 +0,0 @@
1
- //===----------------------------------------------------------------------===//
2
- // RouteBuilder - Builder Implementation
3
- //===----------------------------------------------------------------------===//
4
-
5
- #include "route.h"
6
-
7
- #include <algorithm>
8
- #include <map>
9
- #include <string>
10
-
11
- using namespace RouteBuilder;
12
-
13
- namespace {
14
-
15
- constexpr char kParamMarker = ':'; ///< marker for parameters in the pattern
16
- constexpr char kWildcardMarker = '*'; ///< marker for wildcard all path names in the pattern
17
- constexpr int kMaxPacked = 8; ///< maximum bytes to pack into a u64 for comparison
18
-
19
- // Pack up to 8 bytes from `str` into a u64. If len < 8, fill high bytes with 0xFF
20
- // to maintain inequality for short vs longer sequences.
21
- inline static uint64_t packU64Safe(const char* str, int len) {
22
- if (len <= 0) return 0;
23
- if (len > 8) len = 8;
24
- uint64_t val = 0;
25
- for (int i = 0; i < len; ++i) {
26
- uint8_t c = static_cast<uint8_t>(str[i]);
27
- val |= (uint64_t)c << (8 * i);
28
- }
29
- for (int i = len; i < 8; ++i) {
30
- val |= (uint64_t)0xFF << (8 * i);
31
- }
32
- return val;
33
- }
34
-
35
- // Helper factory functions to make nodes
36
- std::shared_ptr<RouteNode> makeParamNode() {
37
- auto n = std::make_shared<RouteNode>();
38
- n->is_param = true;
39
- n->is_wildcard = false;
40
- return n;
41
- }
42
-
43
- std::shared_ptr<RouteNode> makeStaticNode() {
44
- auto n = std::make_shared<RouteNode>();
45
- n->is_param = false;
46
- n->is_wildcard = false;
47
- return n;
48
- }
49
-
50
- std::shared_ptr<RouteNode> makeWildcardNode() {
51
- auto n = std::make_shared<RouteNode>();
52
- n->is_wildcard = true;
53
- n->is_param = false;
54
- return n;
55
- }
56
-
57
- // offset -> index into the url char array
58
- static void buildSubRouteTree(std::shared_ptr<RouteNode> node, const std::vector<Endpoint>& eps, int offset) {
59
- if (!node) return;
60
- if (eps.empty()) return;
61
-
62
-
63
- for (auto &ep : eps) {
64
- if (ep.url[offset] == '\0') {
65
- // conflict resolution: if multiple endpoints end here, pick one or handle collision
66
- node->vptr_table_index = ep.vptr_table_index;
67
- // not removing; it's OK to leave them (they represent same path)
68
- }
69
- }
70
-
71
- std::vector<Endpoint> param_eps;
72
- std::vector<Endpoint> static_eps;
73
- Endpoint wildcard_ep;
74
-
75
- for (auto &ep : eps) {
76
- char c = ep.url[offset];
77
- if (c == '\0') continue;
78
-
79
- if (c == kWildcardMarker) wildcard_ep = ep;
80
- else if (c == kParamMarker && ep.url[offset + 1] == '/') param_eps.push_back(ep);
81
- else static_eps.push_back(ep);
82
- }
83
-
84
- // 3) Handle static prefix group (build common prefix up to MAX_VALUE_EXP)
85
- if (!static_eps.empty()) {
86
- // Build common prefix character-by-character
87
- std::string prefix;
88
- for (int p = 0; p < kMaxPacked; ++p) {
89
- char ch = static_eps[0].url[offset + p];
90
- if (ch == '\0' || ch == kParamMarker || ch == kWildcardMarker) break;
91
-
92
- bool all_match = true;
93
- for (auto &se : static_eps) {
94
- if (se.url[offset + p] != ch) { all_match = false; break; }
95
- }
96
- if (!all_match) break;
97
- prefix.push_back(ch);
98
- }
99
-
100
- if (!prefix.empty()) {
101
- auto static_node = makeStaticNode();
102
- static_node->value_length = static_cast<size_t>(prefix.size());
103
- static_node->value = packU64Safe(prefix.c_str(), static_node->value_length);
104
- node->children.push_back(static_node);
105
-
106
- // recursion on static group with advanced offset
107
- int next_offset = offset + static_node->value_length;
108
- // pass by reference: note static_eps is a copy local vector
109
- buildSubRouteTree(static_node, static_eps, next_offset);
110
- } else {
111
- // No common multi-char prefix: group by first char and create one child per first character
112
- // This reduces worst-case behavior: split by single char
113
- // collect buckets
114
- std::map<char, std::vector<Endpoint>> buckets;
115
- for (auto &se : static_eps) buckets[se.url[offset]].push_back(se);
116
- for (auto &kv : buckets) {
117
- char first = kv.first;
118
- std::string onechar(1, first);
119
- auto child = makeStaticNode();
120
- child->value_length = 1;
121
- child->value = packU64Safe(onechar.c_str(), 1);
122
- node->children.push_back(child);
123
- buildSubRouteTree(child, kv.second, offset + 1);
124
- }
125
- }
126
- }
127
-
128
- // 4) Handle param group (there should be at most one param child, but if multiple different param names,
129
- // we might need to disambiguate; here we take the first's metadata)
130
- if (!param_eps.empty()) {
131
- auto param_node = makeParamNode();
132
- // Use the first endpoint's param meta
133
- if (!param_eps[0].params.empty()) {
134
- param_node->param_name = param_eps[0].params[0].name;
135
- param_node->param_type = param_eps[0].params[0].type;
136
- }
137
- node->children.push_back(param_node);
138
- // skip the ":/" marker -> offset + 2
139
- buildSubRouteTree(param_node, param_eps, offset + 2);
140
- }
141
-
142
- // 5) Wildcard route (terminal matcher)
143
- // Only one wildcard is allowed per route level.
144
- // Wildcard consumes the rest of the URL and must NOT recurse further.
145
- // It acts as a final fallback when no static or param routes match.
146
- if (wildcard_ep) {
147
- auto wildcard_node = makeWildcardNode();
148
- wildcard_node->vptr_table_index = wildcard_ep.vptr_table_index;
149
- node->children.push_back(wildcard_node);
150
- }
151
- }
152
- }
153
-
154
- // Public API
155
- std::shared_ptr<RouteNode> RouteBuilder::buildRouteTree(const std::vector<Endpoint>& eps) noexcept {
156
- if (eps.size() == 0) return nullptr;
157
-
158
- auto root = std::make_shared<RouteNode>();
159
- buildSubRouteTree(root, eps, 0);
160
- return root;
161
- }
@@ -1,266 +0,0 @@
1
- //===----------------------------------------------------------------------===//
2
- // RouteBuilder - Matcher Implementation
3
- //===----------------------------------------------------------------------===//
4
-
5
- #include "route.h"
6
-
7
- #include <cassert>
8
- #include <cstring>
9
- #include <memory>
10
-
11
- using namespace RouteBuilder;
12
-
13
- namespace {
14
-
15
- //===------------------------------------------------------------------===//
16
- // Helper Functions
17
- //===------------------------------------------------------------------===//
18
-
19
- [[gnu::always_inline]] constexpr static uint64_t packedU64FromString(const char* __restrict src, size_t start, size_t end) {
20
- size_t len = end - start;
21
- if (len > 8) len = 8;
22
-
23
- uint64_t value = 0;
24
-
25
- switch(len) {
26
- case 8: value |= (uint64_t)(uint8_t)src[start+7] << 56; [[fallthrough]];
27
- case 7: value |= (uint64_t)(uint8_t)src[start+6] << 48; [[fallthrough]];
28
- case 6: value |= (uint64_t)(uint8_t)src[start+5] << 40; [[fallthrough]];
29
- case 5: value |= (uint64_t)(uint8_t)src[start+4] << 32; [[fallthrough]];
30
- case 4: value |= (uint64_t)(uint8_t)src[start+3] << 24; [[fallthrough]];
31
- case 3: value |= (uint64_t)(uint8_t)src[start+2] << 16; [[fallthrough]];
32
- case 2: value |= (uint64_t)(uint8_t)src[start+1] << 8; [[fallthrough]];
33
- case 1: value |= (uint64_t)(uint8_t)src[start+0]; break;
34
- default: break;
35
- }
36
-
37
- switch(len) {
38
- case 0: value |= (uint64_t)0xFF << 0; [[fallthrough]];
39
- case 1: value |= (uint64_t)0xFF << 8; [[fallthrough]];
40
- case 2: value |= (uint64_t)0xFF << 16; [[fallthrough]];
41
- case 3: value |= (uint64_t)0xFF << 24; [[fallthrough]];
42
- case 4: value |= (uint64_t)0xFF << 32; [[fallthrough]];
43
- case 5: value |= (uint64_t)0xFF << 40; [[fallthrough]];
44
- case 6: value |= (uint64_t)0xFF << 48; [[fallthrough]];
45
- case 7: value |= (uint64_t)0xFF << 56; break;
46
- default: break;
47
- }
48
-
49
- return value;
50
- }
51
-
52
- inline static bool nodeStaticMatches(const std::shared_ptr<RouteNode>& node,
53
- const char* __restrict url,
54
- uint32_t* offset) {
55
- uint64_t value_buffer = packedU64FromString(url, *offset, *offset + node->value_length);
56
-
57
- return (value_buffer == node->value);
58
- }
59
-
60
- inline static constexpr char hex_to_char(char h) noexcept {
61
- return (h >= '0' && h <= '9') ? (h - '0')
62
- : (h >= 'A' && h <= 'F') ? (h - 'A' + 10)
63
- : (h >= 'a' && h <= 'f') ? (h - 'a' + 10)
64
- : 0;
65
- }
66
-
67
- inline static std::string url_decode(const char* __restrict start, const char* __restrict end) {
68
- std::string out;
69
- out.reserve(end - start);
70
-
71
- const char* p = start;
72
- while (p < end) {
73
- char c = *p;
74
- if (c == '%') {
75
- if (p + 2 < end) {
76
- char hi = hex_to_char(*(p + 1));
77
- char lo = hex_to_char(*(p + 2));
78
- out.push_back((hi << 4) | lo);
79
- p += 3;
80
- continue;
81
- }
82
- } else if (c == '+') {
83
- out.push_back(' ');
84
- ++p;
85
- continue;
86
- }
87
-
88
- out.push_back(c);
89
- ++p;
90
- }
91
- return out;
92
- }
93
-
94
- inline static bool parse_query_params(
95
- Napi::Env env,
96
- const char* __restrict url,
97
- uint32_t* __restrict offset,
98
- Napi::Object* query_params,
99
- uint32_t query_limit
100
- ) noexcept
101
- {
102
- const char* p = url + *offset;
103
-
104
- if (*p == '?') p++;
105
-
106
- uint32_t scanned = 0;
107
-
108
- const char* key_start = p;
109
- const char* val_start = nullptr;
110
-
111
- while (*p != '\0') {
112
-
113
- // ---- LAST POINT CONTROLLERS ----
114
- if (*p == ' ' || *p == '\r' || *p == '\n' || *p == '#') {
115
- break;
116
- }
117
-
118
- scanned++;
119
-
120
- if (scanned > query_limit) {
121
- return false; // QUERY LIMIT EXCEEDED
122
- }
123
-
124
- if (*p == '=') {
125
- val_start = p + 1;
126
- }
127
- else if (*p == '&') {
128
-
129
- const char* key_end = val_start ? (val_start - 1) : p;
130
- const char* val_end = val_start ? p : p;
131
-
132
- auto key = url_decode(key_start, key_end);
133
- auto value = val_start ? url_decode(val_start, val_end) : "";
134
-
135
- query_params->Set(key, value);
136
-
137
- key_start = p + 1;
138
- val_start = nullptr;
139
- }
140
-
141
- p++;
142
- }
143
-
144
- if (key_start && key_start < p) {
145
- const char* key_end = val_start ? (val_start - 1) : p;
146
- const char* val_end = val_start ? p : p;
147
-
148
- auto key = url_decode(key_start, key_end);
149
- auto value = val_start ? url_decode(val_start, val_end) : "";
150
-
151
- query_params->Set(key, value);
152
- }
153
-
154
- *offset += scanned;
155
-
156
- return true;
157
- }
158
-
159
- } // namespace
160
-
161
-
162
- //===----------------------------------------------------------------------===//
163
- // matchUrl Implementation
164
- //===----------------------------------------------------------------------===//
165
- int RouteBuilder::matchUrl(
166
- Napi::Env env,
167
- const std::shared_ptr<RouteNode>& root,
168
- const char* url,
169
- size_t urlLen,
170
- uint32_t* offset,
171
- Napi::Array* path_params,
172
- Napi::Object* query_params,
173
- uint32_t query_limit
174
- ) noexcept
175
- {
176
- auto node = root;
177
-
178
- if (!node) return -1;
179
-
180
- if (!node->is_param && node->value_length > 0) {
181
- if (!nodeStaticMatches(node, url, offset)) {
182
- return -1;
183
- }
184
- *offset += node->value_length;
185
- }
186
-
187
- uint32_t path_index = 0;
188
- bool matched = false;
189
-
190
- while (true) {
191
- matched = false;
192
-
193
- #pragma clang loop vectorize(disable)
194
- #pragma clang loop unroll(disable)
195
- for (auto& child : node->children) {
196
- if (!child) continue;
197
- // Line caches Opt
198
- // __builtin_prefetch(child.get(), 0, 1);
199
-
200
- if (child->is_param) [[unlikely]] {
201
- const char* p = url + *offset;
202
- size_t start = *offset;
203
-
204
- while (*p && *p != '/' && *p != '?' && *p != ' ') p++;
205
- __builtin_assume(p >= url && p <= url + urlLen);
206
-
207
- size_t param_len = p - (url + start);
208
- std::string param_value(url + start, param_len);
209
-
210
- path_params->Set(path_index++, Napi::String::New(env, param_value));
211
-
212
- *offset += param_len;
213
-
214
- node = child;
215
- matched = true;
216
-
217
- if (__builtin_expect((child->vptr_table_index != -1) && (url[*offset] == ' ' || url[*offset] == '?'), 1)) {
218
- if (url[*offset] == '?') {
219
- if (!parse_query_params(env, url, offset, query_params, query_limit)) return -2;
220
- *offset += 1;
221
- }
222
- return child->vptr_table_index;
223
- }
224
- *offset += 1;
225
- break;
226
- }
227
- else [[likely]] {
228
- if (child->is_wildcard) [[unlikely]] {
229
- #pragma clang loop vectorize(disable)
230
- #pragma clang loop unroll(disable)
231
- while (url[*offset] != ' ') {
232
- if (*offset > 1000) return -3;
233
- if (url[*offset] == '?') {
234
- if(!parse_query_params(env, url, offset, query_params, query_limit)) return -2;
235
- *offset += 1;
236
- break;
237
- }
238
- *offset += 1;
239
- }
240
- return child->vptr_table_index;
241
- }
242
-
243
- if (nodeStaticMatches(child, url, offset)) {
244
- *offset += child->value_length;
245
-
246
- node = child;
247
- matched = true;
248
-
249
- if (child->vptr_table_index != -1 && (url[*offset] == ' ' || url[*offset] == '?')) {
250
- // Parse Query
251
- if (url[*offset] == '?') {
252
- if(!parse_query_params(env, url, offset, query_params, query_limit)) return -2;
253
- *offset += 1;
254
- }
255
- return child->vptr_table_index;
256
- }
257
- break;
258
- }
259
- }
260
- }
261
-
262
- if (!matched) break;
263
- }
264
-
265
- return -1;
266
- }
@@ -1,20 +0,0 @@
1
- #include <iostream>
2
-
3
- #include "route.h"
4
-
5
- void RouteBuilder::printRouteTree(const std::shared_ptr<RouteNode>& node, int depth) noexcept {
6
- if (!node) return;
7
- for (int i = 0; i < depth; ++i) std::cout << " ";
8
- if (node->is_param) {
9
- std::cout << "PARAM(" << node->param_name << ")";
10
- } else if (node->value_length > 0) {
11
- std::cout << "STATIC(len=" << node->value_length << ", hex=" << std::hex << node->value << std::dec << ")";
12
- } else {
13
- std::cout << "ROOT";
14
- }
15
- if (node->vptr_table_index != -1)
16
- std::cout << " -> ENDPOINT_IDX=" << node->vptr_table_index;
17
- std::cout << "\n";
18
-
19
- for (auto &c : node->children) printRouteTree(c, depth + 1);
20
- }
package/native/main.cpp DELETED
@@ -1,54 +0,0 @@
1
- #include <napi.h>
2
-
3
- #include "http_scanner.h"
4
- #include <asset_parser.h>
5
- #include <cpool.h>
6
- #include <asset_parser.h>
7
-
8
- inline const char* scan_url(
9
- const char* __restrict curl,
10
- uint32_t* __restrict offset
11
- ) {
12
- uint32_t i = *offset;
13
- const char* url_start = curl + i;
14
-
15
- while (true) {
16
- char c = curl[i];
17
-
18
- if (c == ' ' || c == '?') {
19
- *offset = i;
20
- return url_start;
21
- }
22
- ++i;
23
- }
24
- }
25
-
26
- Napi::Value ScanUrl(const Napi::CallbackInfo& info) {
27
- Napi::Env env = info.Env();
28
-
29
- if (info.Length() < 2 || !info[0].IsBuffer() || !info[1].IsNumber()) [[unlikely]] {
30
- Napi::TypeError::New(env, "Expected (curl: Buffer, offset: number)").ThrowAsJavaScriptException();
31
- return env.Undefined();
32
- }
33
-
34
- auto buf = info[0].As<Napi::Buffer<char>>();
35
- const char* curl = buf.Data();
36
- uint32_t offset = info[1].As<Napi::Number>().Uint32Value();
37
-
38
- const char* url = scan_url(curl, &offset);
39
-
40
- return Napi::String::New(env, url);
41
- }
42
-
43
-
44
- Napi::Object Init(Napi::Env env, Napi::Object exports) {
45
- exports.Set("HttpCore", HttpCore::GetClass(env));
46
- exports.Set("PublicAssetParser", PublicAssetParser::GetClass(env));
47
-
48
- exports.Set("scanUrl", Napi::Function::New(env, ScanUrl));
49
-
50
- exports.Set("CPool", CPool::GetClass(env));
51
- return exports;
52
- }
53
-
54
- NODE_API_MODULE(hypernode, Init)