Skip to content
This repository was archived by the owner on Mar 18, 2022. It is now read-only.

Commit 77915c3

Browse files
committed
feat: export Rust functions via CFFI
1 parent 264ff93 commit 77915c3

File tree

7 files changed

+422
-58
lines changed

7 files changed

+422
-58
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ name = "redisless"
1111
crate-type = ["dylib"]
1212

1313
[dependencies]
14+
crossbeam-channel = "0.5.1"

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,12 @@ redisless.stop_server()
5454
- [ ] Atomic operations
5555
- [ ] Multi-threaded
5656
- [ ] Memory off-load and disk persistence
57-
- [ ] Cluster mode with replication
57+
- [ ] Cluster mode with replication (RAFT)
5858

5959
# Supported clients
60-
- [ ] Python
60+
- [ ] NodeJS
6161
- [ ] Golang
62+
- [ ] Python
6263
- [ ] Java
6364

6465
# Performance

clients/python/requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
cffi == 1.14.5
2+
redis == 3.5.3

clients/python/src/main.py

+5-17
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,10 @@
11
#!/usr/bin/env python
2-
from cffi import FFI
2+
import time
33

4+
from clients.python.src.redisless import RedisLess
45

56
if __name__ == '__main__':
6-
ffi = FFI()
7-
ffi.cdef("""
8-
typedef void* redisless;
9-
10-
redisless redisless_new();
11-
12-
void redisless_set(redisless, char *key, char *value);
13-
*const char redisless_get(redisless, char *key);
14-
""")
15-
16-
C = ffi.dlopen('../../../target/release/libredisless.dylib')
17-
18-
redisless = C.redisless_new()
19-
print(redisless)
20-
C.redisless_set(redisless, b'key', b'value')
21-
# print(redisless.get('key'))
7+
redisless = RedisLess()
8+
redisless.start_server()
9+
redisless.stop_server()
2210

clients/python/src/redisless.py

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from cffi import FFI
2+
3+
4+
class RedisLess(object):
5+
"""
6+
RedisLess
7+
"""
8+
9+
def __init__(self):
10+
ffi = FFI()
11+
ffi.cdef("""
12+
typedef void* redisless;
13+
typedef void* server;
14+
15+
redisless redisless_new();
16+
server redisless_server_new(redisless);
17+
void redisless_server_start(server);
18+
void redisless_server_stop(server);
19+
""")
20+
21+
self._C = ffi.dlopen('../../../target/release/libredisless.dylib')
22+
self._redisless = self._C.redisless_new()
23+
self._redisless_server = self._C.redisless_server_new(self._redisless)
24+
25+
def start_server(self):
26+
"""
27+
Start local embedded RedisLess instance
28+
:return:
29+
"""
30+
self._C.redisless_server_start(self._redisless_server)
31+
32+
def stop_server(self):
33+
"""
34+
Stop local embedded RedisLess instance
35+
:return:
36+
"""
37+
self._C.redisless_server_stop(self._redisless_server)

src/lib.rs

+101-39
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
use crossbeam_channel::{unbounded, Receiver, Sender};
12
use std::collections::HashMap;
23
use std::ffi::{CStr, CString};
3-
use std::os::raw::c_char;
4-
use std::ptr;
4+
use std::io::Error;
5+
use std::net::{TcpListener, TcpStream, ToSocketAddrs};
6+
use std::sync::{Arc, Mutex};
7+
use std::thread;
8+
9+
mod redis_protocol;
510

611
#[repr(C)]
712
pub struct RedisLess {
@@ -49,52 +54,109 @@ impl RedisLess {
4954
}
5055
}
5156

57+
fn handle_request(redisless: &RedisLess, mut stream: &TcpStream) {
58+
// TODO
59+
}
60+
61+
#[repr(C)]
62+
pub struct Server {
63+
redisless: Arc<RedisLess>,
64+
send_state_ch: Sender<ServerState>,
65+
recv_state_ch: Receiver<ServerState>,
66+
}
67+
68+
enum ServerState {
69+
Start,
70+
Stop,
71+
}
72+
73+
impl Server {
74+
pub fn new(redisless: RedisLess) -> Self {
75+
let (send_state_ch, recv_state_ch) = unbounded::<ServerState>();
76+
77+
let s = Server {
78+
redisless: Arc::new(redisless),
79+
send_state_ch,
80+
recv_state_ch,
81+
};
82+
83+
// TODO export conf
84+
s._init_configuration("0.0.0.0:16379");
85+
86+
s
87+
}
88+
89+
pub fn _init_configuration<A: Into<String>>(&self, addr: A) {
90+
let addr = Arc::new(addr.into());
91+
let redisless = self.redisless.clone();
92+
let recv = self.recv_state_ch.clone();
93+
94+
thread::spawn(move || {
95+
for server_state in recv {
96+
let addr = addr.clone();
97+
let redisless = redisless.clone();
98+
99+
match server_state {
100+
ServerState::Start => {
101+
let _ = thread::spawn(move || match TcpListener::bind(addr.as_str()) {
102+
Ok(listener) => {
103+
// listen incoming requests
104+
for stream in listener.incoming() {
105+
let redisless = redisless.clone();
106+
107+
let _ = thread::spawn(move || match stream {
108+
Ok(tcp_stream) => loop {
109+
handle_request(redisless.as_ref(), &tcp_stream);
110+
},
111+
Err(err) => {
112+
println!("{:?}", err);
113+
}
114+
});
115+
}
116+
}
117+
Err(err) => {
118+
println!("{:?}", err);
119+
}
120+
});
121+
}
122+
ServerState::Stop => {
123+
// TODO implement
124+
}
125+
}
126+
}
127+
});
128+
}
129+
130+
/// start server
131+
pub fn start(&self) {
132+
self.send_state_ch.send(ServerState::Start);
133+
}
134+
135+
/// stop server
136+
pub fn stop(&self) {
137+
self.send_state_ch.send(ServerState::Stop);
138+
}
139+
}
140+
52141
#[no_mangle]
53142
pub extern "C" fn redisless_new() -> *mut RedisLess {
54143
Box::into_raw(Box::new(RedisLess::new()))
55144
}
56145

57146
#[no_mangle]
58-
pub extern "C" fn redisless_set(
59-
redisless: &mut RedisLess,
60-
key: *const c_char,
61-
value: *const c_char,
62-
) {
63-
if key.is_null() || value.is_null() {
64-
return;
65-
}
66-
67-
let key_str = unsafe { CStr::from_ptr(key) };
68-
let value_str = unsafe { CStr::from_ptr(value) };
147+
pub extern "C" fn redisless_server_new(redisless: RedisLess) -> *const Server {
148+
Box::into_raw(Box::new(Server::new(redisless)))
149+
}
69150

70-
if let Ok(key) = key_str.to_str() {
71-
if let Ok(value) = value_str.to_str() {
72-
redisless.set(key.as_bytes(), value.as_bytes());
73-
}
74-
}
151+
#[no_mangle]
152+
pub extern "C" fn redisless_server_start(server: &Server) {
153+
server.start();
75154
}
76155

77-
// #[no_mangle]
78-
// pub extern "C" fn redisless_get(redisless: &mut RedisLess, key: *const c_char) -> *const c_char {
79-
// if key.is_null() {
80-
// return ptr::null();
81-
// }
82-
//
83-
// let key_str = unsafe { CStr::from_ptr(key) };
84-
//
85-
// if let Ok(key) = key_str.to_str() {
86-
// return match redisless.get(key.as_bytes()) {
87-
// Some(x) => {
88-
// let ptr = CString::new("").unwrap();
89-
// let ptr = ptr.as_ptr();
90-
// unsafe { *ptr };
91-
// }
92-
// None => ptr::null(),
93-
// };
94-
// }
95-
//
96-
// ptr::null()
97-
// }
156+
#[no_mangle]
157+
pub extern "C" fn redisless_server_stop(server: &Server) {
158+
server.stop();
159+
}
98160

99161
#[cfg(test)]
100162
mod tests {

0 commit comments

Comments
 (0)