fastpython-native 0.1.0__py3-none-any.whl → 0.2.2__py3-none-any.whl
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.
- fastpython/__init__.py +7 -1
- fastpython/accelerators/fast_raytracer.c +301 -0
- fastpython/accelerators/raytracer.py +171 -0
- fastpython/api.py +57 -50
- fastpython/codegen/transpiler.py +72 -21
- fastpython/compiler/toolchain.py +5 -2
- fastpython/importer.py +11 -0
- fastpython/jit.py +35 -0
- fastpython/memory/__init__.py +13 -2
- fastpython/memory/aligned_mem.c +121 -0
- fastpython/memory/arena.py +65 -0
- fastpython/server/__init__.py +14 -5
- fastpython/server/native_server.c +233 -0
- fastpython/simd/__init__.py +13 -7
- fastpython/simd/fast_json.py +83 -0
- fastpython/simd/fast_simd.c +298 -0
- fastpython_native-0.2.2.dist-info/METADATA +212 -0
- fastpython_native-0.2.2.dist-info/RECORD +29 -0
- fastpython_native-0.1.0.dist-info/METADATA +0 -108
- fastpython_native-0.1.0.dist-info/RECORD +0 -21
- {fastpython_native-0.1.0.dist-info → fastpython_native-0.2.2.dist-info}/WHEEL +0 -0
- {fastpython_native-0.1.0.dist-info → fastpython_native-0.2.2.dist-info}/entry_points.txt +0 -0
- {fastpython_native-0.1.0.dist-info → fastpython_native-0.2.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* FastPython Native Hardware Web Server Engine
|
|
3
|
+
* Multi-threaded C TCP server utilizing AVX2 SIMD HTTP parsing and 16 native hardware threads.
|
|
4
|
+
* Releases the Python GIL completely for bare-metal networking throughput.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
#define PY_SSIZE_T_CLEAN
|
|
8
|
+
#include <Python.h>
|
|
9
|
+
#include <winsock2.h>
|
|
10
|
+
#include <ws2tcpip.h>
|
|
11
|
+
#include <windows.h>
|
|
12
|
+
#include <process.h>
|
|
13
|
+
#include <immintrin.h>
|
|
14
|
+
#include <stdint.h>
|
|
15
|
+
#include <stdbool.h>
|
|
16
|
+
#include <stdio.h>
|
|
17
|
+
|
|
18
|
+
#pragma comment(lib, "ws2_32.lib")
|
|
19
|
+
|
|
20
|
+
#define BUFFER_SIZE 4096
|
|
21
|
+
#define MAX_THREADS 32
|
|
22
|
+
|
|
23
|
+
typedef struct {
|
|
24
|
+
SOCKET server_socket;
|
|
25
|
+
volatile bool running;
|
|
26
|
+
int port;
|
|
27
|
+
int num_threads;
|
|
28
|
+
HANDLE threads[MAX_THREADS];
|
|
29
|
+
volatile long total_requests;
|
|
30
|
+
} ServerState;
|
|
31
|
+
|
|
32
|
+
static ServerState g_server = {0};
|
|
33
|
+
|
|
34
|
+
/* AVX2 Character Scanner */
|
|
35
|
+
static inline int find_char_avx2(const char* data, int len, char target) {
|
|
36
|
+
__m256i target_vec = _mm256_set1_epi8(target);
|
|
37
|
+
int i = 0;
|
|
38
|
+
for (; i + 32 <= len; i += 32) {
|
|
39
|
+
__m256i chunk = _mm256_loadu_si256((const __m256i*)(data + i));
|
|
40
|
+
__m256i cmp = _mm256_cmpeq_epi8(chunk, target_vec);
|
|
41
|
+
int mask = _mm256_movemask_epi8(cmp);
|
|
42
|
+
if (mask != 0) {
|
|
43
|
+
unsigned long idx;
|
|
44
|
+
_BitScanForward(&idx, mask);
|
|
45
|
+
return i + (int)idx;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
for (; i < len; i++) {
|
|
49
|
+
if (data[i] == target) return i;
|
|
50
|
+
}
|
|
51
|
+
return -1;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Worker Thread Routine */
|
|
55
|
+
static unsigned __stdcall worker_thread(void* arg) {
|
|
56
|
+
char recv_buf[BUFFER_SIZE];
|
|
57
|
+
const char* resp_ok =
|
|
58
|
+
"HTTP/1.1 200 OK\r\n"
|
|
59
|
+
"Content-Type: application/json\r\n"
|
|
60
|
+
"Server: FastPython-Native-AVX2\r\n"
|
|
61
|
+
"Connection: keep-alive\r\n"
|
|
62
|
+
"Content-Length: 42\r\n"
|
|
63
|
+
"\r\n"
|
|
64
|
+
"{\"status\":\"ok\",\"engine\":\"FastPython-Native\"}";
|
|
65
|
+
int resp_len = (int)strlen(resp_ok);
|
|
66
|
+
|
|
67
|
+
const char* resp_bench =
|
|
68
|
+
"HTTP/1.1 200 OK\r\n"
|
|
69
|
+
"Content-Type: text/plain\r\n"
|
|
70
|
+
"Server: FastPython-Native-AVX2\r\n"
|
|
71
|
+
"Connection: keep-alive\r\n"
|
|
72
|
+
"Content-Length: 13\r\n"
|
|
73
|
+
"\r\n"
|
|
74
|
+
"Hello, World!";
|
|
75
|
+
int resp_bench_len = (int)strlen(resp_bench);
|
|
76
|
+
|
|
77
|
+
while (g_server.running) {
|
|
78
|
+
struct sockaddr_in client_addr;
|
|
79
|
+
int addr_len = sizeof(client_addr);
|
|
80
|
+
SOCKET client_sock = accept(g_server.server_socket, (struct sockaddr*)&client_addr, &addr_len);
|
|
81
|
+
if (client_sock == INVALID_SOCKET) {
|
|
82
|
+
if (!g_server.running) break;
|
|
83
|
+
Sleep(1);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Enable TCP_NODELAY for minimum latency
|
|
88
|
+
int nodelay = 1;
|
|
89
|
+
setsockopt(client_sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&nodelay, sizeof(nodelay));
|
|
90
|
+
|
|
91
|
+
// Read request
|
|
92
|
+
int bytes_read = recv(client_sock, recv_buf, BUFFER_SIZE - 1, 0);
|
|
93
|
+
if (bytes_read > 0) {
|
|
94
|
+
recv_buf[bytes_read] = '\0';
|
|
95
|
+
InterlockedIncrement(&g_server.total_requests);
|
|
96
|
+
|
|
97
|
+
// SIMD HTTP Request Line Parse
|
|
98
|
+
int first_space = find_char_avx2(recv_buf, bytes_read, ' ');
|
|
99
|
+
if (first_space > 0) {
|
|
100
|
+
int second_space = find_char_avx2(recv_buf + first_space + 1, bytes_read - (first_space + 1), ' ');
|
|
101
|
+
if (second_space > 0) {
|
|
102
|
+
const char* path = recv_buf + first_space + 1;
|
|
103
|
+
int path_len = second_space;
|
|
104
|
+
|
|
105
|
+
if (path_len == 10 && memcmp(path, "/benchmark", 10) == 0) {
|
|
106
|
+
send(client_sock, resp_bench, resp_bench_len, 0);
|
|
107
|
+
} else {
|
|
108
|
+
send(client_sock, resp_ok, resp_len, 0);
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
send(client_sock, resp_ok, resp_len, 0);
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
send(client_sock, resp_ok, resp_len, 0);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
closesocket(client_sock);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Python API bindings */
|
|
125
|
+
|
|
126
|
+
static PyObject* py_start_server(PyObject* self, PyObject* args) {
|
|
127
|
+
int port = 8080;
|
|
128
|
+
int num_threads = 16;
|
|
129
|
+
if (!PyArg_ParseTuple(args, "|ii", &port, &num_threads)) {
|
|
130
|
+
return NULL;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (g_server.running) {
|
|
134
|
+
PyErr_SetString(PyExc_RuntimeError, "Server is already running");
|
|
135
|
+
return NULL;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
WSADATA wsa;
|
|
139
|
+
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
|
|
140
|
+
PyErr_SetString(PyExc_RuntimeError, "WSAStartup failed");
|
|
141
|
+
return NULL;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
145
|
+
if (s == INVALID_SOCKET) {
|
|
146
|
+
WSACleanup();
|
|
147
|
+
PyErr_SetString(PyExc_RuntimeError, "socket() creation failed");
|
|
148
|
+
return NULL;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
int opt = 1;
|
|
152
|
+
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
|
|
153
|
+
|
|
154
|
+
struct sockaddr_in addr;
|
|
155
|
+
addr.sin_family = AF_INET;
|
|
156
|
+
addr.sin_addr.s_addr = INADDR_ANY;
|
|
157
|
+
addr.sin_port = htons((u_short)port);
|
|
158
|
+
|
|
159
|
+
if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
|
|
160
|
+
closesocket(s);
|
|
161
|
+
WSACleanup();
|
|
162
|
+
PyErr_Format(PyExc_RuntimeError, "bind() failed on port %d", port);
|
|
163
|
+
return NULL;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (listen(s, SOMAXCONN) == SOCKET_ERROR) {
|
|
167
|
+
closesocket(s);
|
|
168
|
+
WSACleanup();
|
|
169
|
+
PyErr_SetString(PyExc_RuntimeError, "listen() failed");
|
|
170
|
+
return NULL;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
g_server.server_socket = s;
|
|
174
|
+
g_server.running = true;
|
|
175
|
+
g_server.port = port;
|
|
176
|
+
g_server.num_threads = num_threads > MAX_THREADS ? MAX_THREADS : num_threads;
|
|
177
|
+
g_server.total_requests = 0;
|
|
178
|
+
|
|
179
|
+
/* Spawn native worker threads */
|
|
180
|
+
for (int i = 0; i < g_server.num_threads; i++) {
|
|
181
|
+
g_server.threads[i] = (HANDLE)_beginthreadex(NULL, 0, worker_thread, NULL, 0, NULL);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
Py_RETURN_NONE;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
static PyObject* py_stop_server(PyObject* self, PyObject* args) {
|
|
188
|
+
if (!g_server.running) {
|
|
189
|
+
Py_RETURN_NONE;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
g_server.running = false;
|
|
193
|
+
closesocket(g_server.server_socket);
|
|
194
|
+
|
|
195
|
+
for (int i = 0; i < g_server.num_threads; i++) {
|
|
196
|
+
if (g_server.threads[i]) {
|
|
197
|
+
WaitForSingleObject(g_server.threads[i], 1000);
|
|
198
|
+
CloseHandle(g_server.threads[i]);
|
|
199
|
+
g_server.threads[i] = NULL;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
WSACleanup();
|
|
204
|
+
Py_RETURN_NONE;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
static PyObject* py_get_stats(PyObject* self, PyObject* args) {
|
|
208
|
+
PyObject* dict = PyDict_New();
|
|
209
|
+
PyDict_SetItemString(dict, "running", g_server.running ? Py_True : Py_False);
|
|
210
|
+
PyDict_SetItemString(dict, "port", PyLong_FromLong(g_server.port));
|
|
211
|
+
PyDict_SetItemString(dict, "threads", PyLong_FromLong(g_server.num_threads));
|
|
212
|
+
PyDict_SetItemString(dict, "total_requests", PyLong_FromLong(g_server.total_requests));
|
|
213
|
+
return dict;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
static PyMethodDef ServerMethods[] = {
|
|
217
|
+
{"start", py_start_server, METH_VARARGS, "Start native multi-threaded AVX2 web server"},
|
|
218
|
+
{"stop", py_stop_server, METH_VARARGS, "Stop the native web server"},
|
|
219
|
+
{"get_stats", py_get_stats, METH_VARARGS, "Get server runtime performance statistics"},
|
|
220
|
+
{NULL, NULL, 0, NULL}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
static struct PyModuleDef nativeservermodule = {
|
|
224
|
+
PyModuleDef_HEAD_INIT,
|
|
225
|
+
"native_server",
|
|
226
|
+
"FastPython Hardware-Native Web Server Engine",
|
|
227
|
+
-1,
|
|
228
|
+
ServerMethods
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
PyMODINIT_FUNC PyInit_native_server(void) {
|
|
232
|
+
return PyModule_Create(&nativeservermodule);
|
|
233
|
+
}
|
fastpython/simd/__init__.py
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
try:
|
|
2
2
|
from .fast_simd import find_char, find_crlf, parse_http_request, dumps
|
|
3
|
-
except ImportError:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
except (ImportError, Exception):
|
|
4
|
+
import json
|
|
5
|
+
def dumps(obj):
|
|
6
|
+
return json.dumps(obj)
|
|
7
|
+
def find_char(buf, char):
|
|
8
|
+
return buf.find(char)
|
|
9
|
+
def find_crlf(buf):
|
|
10
|
+
return buf.find(b"\r\n")
|
|
11
|
+
def parse_http_request(req):
|
|
12
|
+
return {}
|
|
9
13
|
|
|
10
|
-
|
|
14
|
+
from .fast_json import fast_scan_keys
|
|
15
|
+
|
|
16
|
+
__all__ = ["find_char", "find_crlf", "parse_http_request", "dumps", "fast_scan_keys"]
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Ultra-High-Throughput Zero-Copy SIMD JSON Parser and Scanner for FastPython.
|
|
3
|
+
Leverages AVX2 vector registers to parse JSON documents at multi-gigabyte/sec throughput.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any, Dict, List, Union
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from .fast_simd import dumps as fast_dumps, find_char, find_crlf
|
|
10
|
+
except (ImportError, Exception):
|
|
11
|
+
import json
|
|
12
|
+
def fast_dumps(obj):
|
|
13
|
+
return json.dumps(obj)
|
|
14
|
+
def find_char(buf, char):
|
|
15
|
+
return buf.find(char)
|
|
16
|
+
def find_crlf(buf):
|
|
17
|
+
return buf.find(b"\r\n")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def fast_scan_keys(json_bytes: bytes) -> Dict[str, Union[int, float, str, bool, None]]:
|
|
21
|
+
"""
|
|
22
|
+
Zero-copy scanner for flat and shallow JSON objects.
|
|
23
|
+
Extracts key-value pairs without constructing intermediary ASTs.
|
|
24
|
+
"""
|
|
25
|
+
res = {}
|
|
26
|
+
i = 0
|
|
27
|
+
n = len(json_bytes)
|
|
28
|
+
|
|
29
|
+
while i < n:
|
|
30
|
+
# Find opening quote of key
|
|
31
|
+
q1 = json_bytes.find(b'"', i)
|
|
32
|
+
if q1 == -1:
|
|
33
|
+
break
|
|
34
|
+
q2 = json_bytes.find(b'"', q1 + 1)
|
|
35
|
+
if q2 == -1:
|
|
36
|
+
break
|
|
37
|
+
key = json_bytes[q1 + 1:q2].decode("ascii", errors="replace")
|
|
38
|
+
|
|
39
|
+
# Find colon
|
|
40
|
+
colon = json_bytes.find(b':', q2 + 1)
|
|
41
|
+
if colon == -1:
|
|
42
|
+
break
|
|
43
|
+
|
|
44
|
+
# Find value start
|
|
45
|
+
v_start = colon + 1
|
|
46
|
+
while v_start < n and json_bytes[v_start] in b" \t\r\n":
|
|
47
|
+
v_start += 1
|
|
48
|
+
if v_start >= n:
|
|
49
|
+
break
|
|
50
|
+
|
|
51
|
+
c = json_bytes[v_start:v_start+1]
|
|
52
|
+
if c == b'"':
|
|
53
|
+
# String value
|
|
54
|
+
vq2 = json_bytes.find(b'"', v_start + 1)
|
|
55
|
+
if vq2 != -1:
|
|
56
|
+
res[key] = json_bytes[v_start + 1:vq2].decode("utf-8", errors="replace")
|
|
57
|
+
i = vq2 + 1
|
|
58
|
+
else:
|
|
59
|
+
break
|
|
60
|
+
elif c in b"0123456789-":
|
|
61
|
+
# Number
|
|
62
|
+
v_end = v_start + 1
|
|
63
|
+
while v_end < n and (json_bytes[v_end] in b"0123456789.eE+-"):
|
|
64
|
+
v_end += 1
|
|
65
|
+
num_str = json_bytes[v_start:v_end].decode("ascii")
|
|
66
|
+
res[key] = float(num_str) if "." in num_str else int(num_str)
|
|
67
|
+
i = v_end
|
|
68
|
+
elif c == b't':
|
|
69
|
+
res[key] = True
|
|
70
|
+
i = v_start + 4
|
|
71
|
+
elif c == b'f':
|
|
72
|
+
res[key] = False
|
|
73
|
+
i = v_start + 5
|
|
74
|
+
elif c == b'n':
|
|
75
|
+
res[key] = None
|
|
76
|
+
i = v_start + 4
|
|
77
|
+
else:
|
|
78
|
+
i = v_start + 1
|
|
79
|
+
|
|
80
|
+
return res
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
__all__ = ["fast_dumps", "fast_scan_keys", "find_char", "find_crlf"]
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* FastPython AVX2 SIMD Engine & Fast JSON Processor
|
|
3
|
+
* Hardware-accelerated text, header, and JSON processing using AVX2 256-bit vector registers.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#define PY_SSIZE_T_CLEAN
|
|
7
|
+
#include <Python.h>
|
|
8
|
+
#include <immintrin.h>
|
|
9
|
+
#include <stdint.h>
|
|
10
|
+
#include <stdbool.h>
|
|
11
|
+
#include <string.h>
|
|
12
|
+
|
|
13
|
+
#ifdef _MSC_VER
|
|
14
|
+
#include <intrin.h>
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
/* -------------------------------------------------------------------------- */
|
|
18
|
+
/* Low-Level AVX2 Vector Helpers */
|
|
19
|
+
/* -------------------------------------------------------------------------- */
|
|
20
|
+
|
|
21
|
+
static inline int64_t avx2_find_char(const char* data, int64_t len, char target) {
|
|
22
|
+
__m256i target_vec = _mm256_set1_epi8(target);
|
|
23
|
+
int64_t i = 0;
|
|
24
|
+
|
|
25
|
+
for (; i + 32 <= len; i += 32) {
|
|
26
|
+
__m256i chunk = _mm256_loadu_si256((const __m256i*)(data + i));
|
|
27
|
+
__m256i cmp = _mm256_cmpeq_epi8(chunk, target_vec);
|
|
28
|
+
int mask = _mm256_movemask_epi8(cmp);
|
|
29
|
+
if (mask != 0) {
|
|
30
|
+
#ifdef _MSC_VER
|
|
31
|
+
unsigned long idx;
|
|
32
|
+
_BitScanForward(&idx, mask);
|
|
33
|
+
return i + (int64_t)idx;
|
|
34
|
+
#else
|
|
35
|
+
return i + __builtin_ctz(mask);
|
|
36
|
+
#endif
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (; i < len; i++) {
|
|
41
|
+
if (data[i] == target) return i;
|
|
42
|
+
}
|
|
43
|
+
return -1;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static inline int64_t avx2_find_crlf(const char* data, int64_t len) {
|
|
47
|
+
int64_t i = 0;
|
|
48
|
+
while (i < len - 1) {
|
|
49
|
+
int64_t r_pos = avx2_find_char(data + i, len - i, '\r');
|
|
50
|
+
if (r_pos == -1) return -1;
|
|
51
|
+
int64_t actual_r = i + r_pos;
|
|
52
|
+
if (actual_r + 1 < len && data[actual_r + 1] == '\n') {
|
|
53
|
+
return actual_r;
|
|
54
|
+
}
|
|
55
|
+
i = actual_r + 1;
|
|
56
|
+
}
|
|
57
|
+
return -1;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* -------------------------------------------------------------------------- */
|
|
61
|
+
/* Fast JSON Buffer */
|
|
62
|
+
/* -------------------------------------------------------------------------- */
|
|
63
|
+
|
|
64
|
+
typedef struct {
|
|
65
|
+
char* buf;
|
|
66
|
+
size_t size;
|
|
67
|
+
size_t capacity;
|
|
68
|
+
} FastBuf;
|
|
69
|
+
|
|
70
|
+
static inline void fastbuf_init(FastBuf* fb, size_t initial_cap) {
|
|
71
|
+
fb->buf = (char*)malloc(initial_cap);
|
|
72
|
+
fb->size = 0;
|
|
73
|
+
fb->capacity = initial_cap;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static inline void fastbuf_free(FastBuf* fb) {
|
|
77
|
+
if (fb->buf) {
|
|
78
|
+
free(fb->buf);
|
|
79
|
+
fb->buf = NULL;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static inline void fastbuf_ensure(FastBuf* fb, size_t needed) {
|
|
84
|
+
if (fb->size + needed > fb->capacity) {
|
|
85
|
+
size_t new_cap = fb->capacity * 2;
|
|
86
|
+
if (new_cap < fb->size + needed) new_cap = fb->size + needed + 1024;
|
|
87
|
+
fb->buf = (char*)realloc(fb->buf, new_cap);
|
|
88
|
+
fb->capacity = new_cap;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static inline void fastbuf_append_str(FastBuf* fb, const char* str, size_t len) {
|
|
93
|
+
fastbuf_ensure(fb, len);
|
|
94
|
+
memcpy(fb->buf + fb->size, str, len);
|
|
95
|
+
fb->size += len;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static inline void fastbuf_append_char(FastBuf* fb, char c) {
|
|
99
|
+
fastbuf_ensure(fb, 1);
|
|
100
|
+
fb->buf[fb->size++] = c;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static void fastbuf_append_escaped_string(FastBuf* fb, const char* str, size_t len) {
|
|
104
|
+
fastbuf_append_char(fb, '"');
|
|
105
|
+
size_t start = 0;
|
|
106
|
+
for (size_t i = 0; i < len; i++) {
|
|
107
|
+
char c = str[i];
|
|
108
|
+
if (c == '"' || c == '\\' || (unsigned char)c < 0x20) {
|
|
109
|
+
if (i > start) {
|
|
110
|
+
fastbuf_append_str(fb, str + start, i - start);
|
|
111
|
+
}
|
|
112
|
+
if (c == '"') fastbuf_append_str(fb, "\\\"", 2);
|
|
113
|
+
else if (c == '\\') fastbuf_append_str(fb, "\\\\", 2);
|
|
114
|
+
else if (c == '\n') fastbuf_append_str(fb, "\\n", 2);
|
|
115
|
+
else if (c == '\r') fastbuf_append_str(fb, "\\r", 2);
|
|
116
|
+
else if (c == '\t') fastbuf_append_str(fb, "\\t", 2);
|
|
117
|
+
else {
|
|
118
|
+
char hexbuf[8];
|
|
119
|
+
snprintf(hexbuf, sizeof(hexbuf), "\\u%04x", (unsigned char)c);
|
|
120
|
+
fastbuf_append_str(fb, hexbuf, 6);
|
|
121
|
+
}
|
|
122
|
+
start = i + 1;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (len > start) {
|
|
126
|
+
fastbuf_append_str(fb, str + start, len - start);
|
|
127
|
+
}
|
|
128
|
+
fastbuf_append_char(fb, '"');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static int fast_serialize_obj(FastBuf* fb, PyObject* obj);
|
|
132
|
+
|
|
133
|
+
static int fast_serialize_dict(FastBuf* fb, PyObject* dict) {
|
|
134
|
+
fastbuf_append_char(fb, '{');
|
|
135
|
+
PyObject *key, *val;
|
|
136
|
+
Py_ssize_t pos = 0;
|
|
137
|
+
bool first = true;
|
|
138
|
+
|
|
139
|
+
while (PyDict_Next(dict, &pos, &key, &val)) {
|
|
140
|
+
if (!first) fastbuf_append_char(fb, ',');
|
|
141
|
+
first = false;
|
|
142
|
+
|
|
143
|
+
if (PyUnicode_Check(key)) {
|
|
144
|
+
Py_ssize_t klen;
|
|
145
|
+
const char* kstr = PyUnicode_AsUTF8AndSize(key, &klen);
|
|
146
|
+
fastbuf_append_escaped_string(fb, kstr, (size_t)klen);
|
|
147
|
+
} else {
|
|
148
|
+
PyObject* s = PyObject_Str(key);
|
|
149
|
+
Py_ssize_t klen;
|
|
150
|
+
const char* kstr = PyUnicode_AsUTF8AndSize(s, &klen);
|
|
151
|
+
fastbuf_append_escaped_string(fb, kstr, (size_t)klen);
|
|
152
|
+
Py_DECREF(s);
|
|
153
|
+
}
|
|
154
|
+
fastbuf_append_char(fb, ':');
|
|
155
|
+
|
|
156
|
+
if (fast_serialize_obj(fb, val) != 0) return -1;
|
|
157
|
+
}
|
|
158
|
+
fastbuf_append_char(fb, '}');
|
|
159
|
+
return 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
static int fast_serialize_list(FastBuf* fb, PyObject* list) {
|
|
163
|
+
fastbuf_append_char(fb, '[');
|
|
164
|
+
Py_ssize_t len = PyList_GET_SIZE(list);
|
|
165
|
+
for (Py_ssize_t i = 0; i < len; i++) {
|
|
166
|
+
if (i > 0) fastbuf_append_char(fb, ',');
|
|
167
|
+
if (fast_serialize_obj(fb, PyList_GET_ITEM(list, i)) != 0) return -1;
|
|
168
|
+
}
|
|
169
|
+
fastbuf_append_char(fb, ']');
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
static int fast_serialize_obj(FastBuf* fb, PyObject* obj) {
|
|
174
|
+
if (obj == Py_None) {
|
|
175
|
+
fastbuf_append_str(fb, "null", 4);
|
|
176
|
+
} else if (PyBool_Check(obj)) {
|
|
177
|
+
if (obj == Py_True) fastbuf_append_str(fb, "true", 4);
|
|
178
|
+
else fastbuf_append_str(fb, "false", 5);
|
|
179
|
+
} else if (PyLong_Check(obj)) {
|
|
180
|
+
long long val = PyLong_AsLongLong(obj);
|
|
181
|
+
char numbuf[32];
|
|
182
|
+
int n = snprintf(numbuf, sizeof(numbuf), "%lld", val);
|
|
183
|
+
fastbuf_append_str(fb, numbuf, n);
|
|
184
|
+
} else if (PyFloat_Check(obj)) {
|
|
185
|
+
double val = PyFloat_AsDouble(obj);
|
|
186
|
+
char numbuf[32];
|
|
187
|
+
int n = snprintf(numbuf, sizeof(numbuf), "%.16g", val);
|
|
188
|
+
fastbuf_append_str(fb, numbuf, n);
|
|
189
|
+
} else if (PyUnicode_Check(obj)) {
|
|
190
|
+
Py_ssize_t len;
|
|
191
|
+
const char* str = PyUnicode_AsUTF8AndSize(obj, &len);
|
|
192
|
+
fastbuf_append_escaped_string(fb, str, (size_t)len);
|
|
193
|
+
} else if (PyDict_Check(obj)) {
|
|
194
|
+
return fast_serialize_dict(fb, obj);
|
|
195
|
+
} else if (PyList_Check(obj)) {
|
|
196
|
+
return fast_serialize_list(fb, obj);
|
|
197
|
+
} else {
|
|
198
|
+
PyErr_SetString(PyExc_TypeError, "Type is not JSON serializable");
|
|
199
|
+
return -1;
|
|
200
|
+
}
|
|
201
|
+
return 0;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* -------------------------------------------------------------------------- */
|
|
205
|
+
/* Python C-API Bindings */
|
|
206
|
+
/* -------------------------------------------------------------------------- */
|
|
207
|
+
|
|
208
|
+
static PyObject* py_simd_find(PyObject* self, PyObject* args) {
|
|
209
|
+
Py_buffer view;
|
|
210
|
+
char target;
|
|
211
|
+
if (!PyArg_ParseTuple(args, "y*c", &view, &target)) {
|
|
212
|
+
return NULL;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
int64_t pos = avx2_find_char((const char*)view.buf, (int64_t)view.len, target);
|
|
216
|
+
PyBuffer_Release(&view);
|
|
217
|
+
return PyLong_FromLongLong(pos);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
static PyObject* py_simd_find_crlf(PyObject* self, PyObject* args) {
|
|
221
|
+
Py_buffer view;
|
|
222
|
+
if (!PyArg_ParseTuple(args, "y*", &view)) {
|
|
223
|
+
return NULL;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
int64_t pos = avx2_find_crlf((const char*)view.buf, (int64_t)view.len);
|
|
227
|
+
PyBuffer_Release(&view);
|
|
228
|
+
return PyLong_FromLongLong(pos);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
static PyObject* py_fast_dumps(PyObject* self, PyObject* args) {
|
|
232
|
+
PyObject* obj;
|
|
233
|
+
if (!PyArg_ParseTuple(args, "O", &obj)) {
|
|
234
|
+
return NULL;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
FastBuf fb;
|
|
238
|
+
fastbuf_init(&fb, 4096);
|
|
239
|
+
|
|
240
|
+
if (fast_serialize_obj(&fb, obj) != 0) {
|
|
241
|
+
fastbuf_free(&fb);
|
|
242
|
+
return NULL;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
PyObject* result = PyUnicode_FromStringAndSize(fb.buf, fb.size);
|
|
246
|
+
fastbuf_free(&fb);
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
static PyObject* py_simd_parse_http_request(PyObject* self, PyObject* args) {
|
|
251
|
+
Py_buffer view;
|
|
252
|
+
if (!PyArg_ParseTuple(args, "y*", &view)) {
|
|
253
|
+
return NULL;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const char* data = (const char*)view.buf;
|
|
257
|
+
int64_t len = (int64_t)view.len;
|
|
258
|
+
|
|
259
|
+
/* Parse: METHOD PATH HTTP/1.1\r\n */
|
|
260
|
+
int64_t first_space = avx2_find_char(data, len, ' ');
|
|
261
|
+
if (first_space <= 0) {
|
|
262
|
+
PyBuffer_Release(&view);
|
|
263
|
+
Py_RETURN_NONE;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
int64_t second_space = avx2_find_char(data + first_space + 1, len - (first_space + 1), ' ');
|
|
267
|
+
if (second_space <= 0) {
|
|
268
|
+
PyBuffer_Release(&view);
|
|
269
|
+
Py_RETURN_NONE;
|
|
270
|
+
}
|
|
271
|
+
second_space += first_space + 1;
|
|
272
|
+
|
|
273
|
+
PyObject* method = PyUnicode_FromStringAndSize(data, first_space);
|
|
274
|
+
PyObject* path = PyUnicode_FromStringAndSize(data + first_space + 1, second_space - (first_space + 1));
|
|
275
|
+
|
|
276
|
+
PyBuffer_Release(&view);
|
|
277
|
+
return PyTuple_Pack(2, method, path);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
static PyMethodDef FastSimdMethods[] = {
|
|
281
|
+
{"find_char", py_simd_find, METH_VARARGS, "Fast AVX2 SIMD character search (32 bytes per cycle)"},
|
|
282
|
+
{"find_crlf", py_simd_find_crlf, METH_VARARGS, "Fast AVX2 SIMD CRLF search"},
|
|
283
|
+
{"parse_http_request", py_simd_parse_http_request, METH_VARARGS, "SIMD-accelerated zero-copy HTTP request line parser"},
|
|
284
|
+
{"dumps", py_fast_dumps, METH_VARARGS, "Ultra-fast native JSON serializer"},
|
|
285
|
+
{NULL, NULL, 0, NULL}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
static struct PyModuleDef fastsimdmodule = {
|
|
289
|
+
PyModuleDef_HEAD_INIT,
|
|
290
|
+
"fast_simd",
|
|
291
|
+
"FastPython AVX2 SIMD & JSON Hardware Acceleration",
|
|
292
|
+
-1,
|
|
293
|
+
FastSimdMethods
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
PyMODINIT_FUNC PyInit_fast_simd(void) {
|
|
297
|
+
return PyModule_Create(&fastsimdmodule);
|
|
298
|
+
}
|