├── .circleci └── config.yml ├── .gitattributes ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── demos ├── embed │ ├── .cargo │ │ └── config │ ├── Cargo.toml │ ├── README.md │ ├── embed.q │ └── src │ │ └── lib.rs └── ipc │ ├── Cargo.toml │ ├── README.md │ └── src │ └── main.rs └── src ├── api.rs ├── k.rs ├── kapi.rs ├── kbindings.rs ├── lib.rs └── types.rs /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | image: &default_image rustlang/rust:nightly 2 | 3 | version: 2 4 | jobs: 5 | build: 6 | docker: 7 | - image: *default_image 8 | steps: 9 | - checkout 10 | - run: 11 | name: Build package 12 | command: | 13 | cargo package 14 | publish: 15 | docker: 16 | - image: *default_image 17 | steps: 18 | - checkout 19 | - run: 20 | name: Publish package 21 | command: | 22 | cargo login $CRATES_KEY 23 | cargo publish 24 | 25 | workflows: 26 | version: 2 27 | build_publish: 28 | jobs: 29 | - build: 30 | filters: 31 | tags: 32 | only: /^v.*/ 33 | - publish: 34 | context: org-global 35 | requires: 36 | - build 37 | filters: 38 | tags: 39 | only: /^v.*/ 40 | branches: 41 | ignore: /.*/ 42 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | c/ 3 | target 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rkdb" 3 | version = "0.4.0" 4 | authors = ["Rahul Powar ", "Peter Parkanyi = 3.6. 55 | - macOS (with XCode >= 9.4.1) and Linux. 56 | - rustc >= 1.31.0 57 | 58 | ## License 59 | 60 | Based substantially on [adwhit](https://github.com/adwhit/krust) and licensed under the same MIT license. 61 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | extern crate syn; 2 | 3 | use std::env; 4 | use std::fs::File; 5 | use std::io::Read; 6 | use std::io::Write; 7 | use std::path::Path; 8 | 9 | pub const KDB_SYM_FILE: &'static str = "symbols.rs"; 10 | 11 | fn parse_fns(fns: &syn::ItemForeignMod) -> Vec { 12 | let mut functions = vec![]; 13 | for item in &fns.items { 14 | match item { 15 | syn::ForeignItem::Fn(ref item_fn) => { 16 | let mut sym: String = "_".to_owned(); 17 | let ident = item_fn.ident.to_string(); 18 | sym.push_str(&ident); 19 | 20 | functions.push(sym); 21 | } 22 | _ => (), 23 | } 24 | } 25 | 26 | functions 27 | } 28 | 29 | fn ksyms(fname: &str) -> Vec { 30 | let mut src = String::new(); 31 | let mut file = File::open(&fname).expect("Unable to open file"); 32 | file.read_to_string(&mut src).expect("Unable to read file"); 33 | 34 | let syntax = syn::parse_file(&src).expect("Unable to parse file"); 35 | 36 | syntax.items.iter().map(|item| match item { 37 | syn::Item::ForeignMod(ref item_fn) => parse_fns(item_fn), 38 | _ => vec![], 39 | }) 40 | .flat_map(|x| x) 41 | .collect() 42 | } 43 | 44 | fn sym_file() -> std::path::PathBuf { 45 | let out_dir = env::var("OUT_DIR").expect("OUT_DIR environment variable not provided"); 46 | 47 | Path::new(&out_dir).join(KDB_SYM_FILE) 48 | } 49 | 50 | fn main() { 51 | let base = env::current_dir().unwrap(); 52 | 53 | let src = base.join("src").join("k.rs"); 54 | 55 | let syms = ksyms(src.to_str().unwrap()); 56 | 57 | let dest_path = sym_file(); 58 | 59 | let mut f = File::create(&dest_path) 60 | .expect("Could not create file to store external KDB symbols"); 61 | 62 | writeln!(f, "#[allow(dead_code)]") 63 | .expect("Could not write to KDB symbol file header"); 64 | writeln!(f, "pub const SYMBOLS: [&'static str; {}] = {:?};", syms.len(), syms) 65 | .expect("Could not write to KDB symbol file"); 66 | 67 | f.flush() 68 | .expect("Could not flush write to KDB symbol"); 69 | 70 | let dir = base.join("src").join("c"); 71 | println!("cargo:rustc-link-search={}", dir.to_str().unwrap()); 72 | } 73 | -------------------------------------------------------------------------------- /demos/embed/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.'cfg(target_os="macos")'] 2 | rustflags = ["-Clink-args=-Wl,-U,_ktn -Wl,-U,_knk -Wl,-U,_ku -Wl,-U,_ka -Wl,-U,_kb -Wl,-U,_kg -Wl,-U,_kh -Wl,-U,_ki -Wl,-U,_kj -Wl,-U,_ke -Wl,-U,_kf -Wl,-U,_kc -Wl,-U,_ks -Wl,-U,_kd -Wl,-U,_kz -Wl,-U,_kt -Wl,-U,_ktj -Wl,-U,_kp -Wl,-U,_kpn -Wl,-U,_xT -Wl,-U,_xD -Wl,-U,_ktd -Wl,-U,_ss -Wl,-U,_sn -Wl,-U,_ymd -Wl,-U,_dj -Wl,-U,_setm -Wl,-U,_r1 -Wl,-U,_r0 -Wl,-U,_m9 -Wl,-U,_sd1 -Wl,-U,_sd0 -Wl,-U,_sd0x -Wl,-U,_k -Wl,-U,_dl -Wl,-U,_ja -Wl,-U,_js -Wl,-U,_jk -Wl,-U,_jv -Wl,-U,_krr -Wl,-U,_orr -Wl,-U,_dot -Wl,-U,_okx -Wl,-U,_b9 -Wl,-U,_d9"] 3 | -------------------------------------------------------------------------------- /demos/embed/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "embed" 3 | version = "0.1.0" 4 | authors = ["Alex Whitney "] 5 | 6 | [dependencies] 7 | num = "*" 8 | 9 | [dependencies.rkdb] 10 | path = "../.." 11 | 12 | [lib] 13 | name = "embed" 14 | crate-type = ["dylib"] 15 | -------------------------------------------------------------------------------- /demos/embed/README.md: -------------------------------------------------------------------------------- 1 | Embed Example 2 | ============= 3 | 4 | This crate will create an shared object that will add extra 5 | functions to q: 6 | 7 | * `sleep `: sleep thread for `` ms 8 | * `mean `: same as `avg` 9 | * `dictex`: example create dictionary 10 | * `tableex`: example create table 11 | * `printer `: print debug info about K object 12 | 13 | 14 | Usage 15 | ----- 16 | Ensure that the main rkdb crate is building correctly. 17 | 18 | Run `cargo build --release` to create `target/release/libembed.so`. 19 | 20 | Copy `libembed.so` to project root. 21 | 22 | Run `q embed.q`. The above functions will be defined in the root namespace. 23 | 24 | Note: Rust version and q version must be same 'bit' i.e. 64 bit. 25 | -------------------------------------------------------------------------------- /demos/embed/embed.q: -------------------------------------------------------------------------------- 1 | sleep: `libembed 2:(`ksleep;1) 2 | mean: `libembed 2:(`kmean;1) 3 | printer: `libembed 2:(`kprinter;1) 4 | dictex: `libembed 2:(`kdictex;1) 5 | tableex: `libembed 2:(`ktableex;1) 6 | -------------------------------------------------------------------------------- /demos/embed/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate rkdb; 2 | extern crate num; 3 | 4 | use std::{thread, time}; 5 | use rkdb::{ 6 | types::K, 7 | k::*, 8 | kbindings::* 9 | }; 10 | 11 | #[no_mangle] 12 | pub extern fn kprinter(k: *const K) -> *const K { 13 | println!("{:?}", KVal::new(k)); 14 | kvoid() 15 | } 16 | 17 | #[no_mangle] 18 | pub extern fn ksleep(k: *const K) -> *const K { 19 | match KVal::new(k) { 20 | KVal::Int(KData::Atom(&mut i)) => sleep(i as u32), 21 | KVal::Long(KData::Atom(&mut i)) => sleep(i as u32), 22 | _ => return kerror("Please supply an int") 23 | } 24 | kvoid() 25 | } 26 | 27 | fn sleep(i: u32) { 28 | if i < 1 { 29 | println!("Value must be greater than 0") 30 | } else { 31 | thread::sleep(time::Duration::from_millis(i as u64)) 32 | } 33 | } 34 | 35 | #[no_mangle] 36 | pub extern fn kmean(k: *const K) -> *const K { 37 | match KVal::new(k) { 38 | KVal::Long(KData::List(l)) => mean(l), 39 | KVal::Int(KData::List(l)) => mean(l), 40 | KVal::Float(KData::List(l)) => mean(l), 41 | KVal::Real(KData::List(l)) => mean(l), 42 | _ => kerror("nyi") 43 | } 44 | } 45 | 46 | fn mean(nums: &[N]) -> *const K { 47 | let sum = nums.iter().fold(N::zero(), |acc, &v| acc + v); 48 | unsafe { kf(sum.to_f64().unwrap() / nums.len() as f64) } 49 | } 50 | 51 | #[no_mangle] 52 | pub extern fn ksum(k: *const K) -> *const K { 53 | match KVal::new(k) { 54 | KVal::Long(KData::List(l)) => sum(l), 55 | KVal::Int(KData::List(l)) => sum(l), 56 | KVal::Float(KData::List(l)) => sum(l), 57 | KVal::Real(KData::List(l)) => sum(l), 58 | _ => kerror("nyi") 59 | } 60 | } 61 | 62 | fn sum(nums: &[N]) -> *const K { 63 | let sum = nums.iter().fold(N::zero(), |acc, &v| acc + v); 64 | unsafe { kf(sum.to_f64().unwrap()) } 65 | } 66 | 67 | #[no_mangle] 68 | pub extern fn kdictex(_: *const K) -> *const K { 69 | let mut a1 = intern_strings(vec!("Once", "Twice", "Thrice").iter().map(|s| s.to_string()).collect()); 70 | let mut a2 = vec!(1, 2, 3); 71 | let v1 = KVal::Symbol(KData::List(&mut a1)); 72 | let v2 = KVal::Int(KData::List(&mut a2)); 73 | kdict(&v1, &v2) 74 | } 75 | 76 | #[no_mangle] 77 | pub extern fn ktableex(_: *const K) -> *const K { 78 | let mut s1 = intern_strings(vec!("Once", "Twice", "Thrice").iter().map(|s| s.to_string()).collect()); 79 | let mut i1 = vec!(1, 2, 3); 80 | let mut i2 = vec!(2, 3, 4); 81 | let mut i3 = vec!(3.0, 4.1, 5.5); 82 | let k = KVal::Symbol(KData::List(&mut s1)); 83 | let v1 = KVal::Int(KData::List(&mut i1)); 84 | let v2 = KVal::Long(KData::List(&mut i2)); 85 | let v3 = KVal::Float(KData::List(&mut i3)); 86 | let v = KVal::Mixed(vec!(v1, v2, v3)); 87 | let d = KVal::Dict(Box::new(k), Box::new(v)); 88 | ktable(d) 89 | } 90 | -------------------------------------------------------------------------------- /demos/ipc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ipc" 3 | version = "0.1.0" 4 | authors = ["Alex Whitney "] 5 | 6 | [dependencies.rkdb] 7 | path = "../.." 8 | features = ["api"] 9 | -------------------------------------------------------------------------------- /demos/ipc/README.md: -------------------------------------------------------------------------------- 1 | IPC Example 2 | =========== 3 | 4 | This example will link the rkdb library and create a binary `ipc`. 5 | 6 | To run the demo, start a local q process serving on port 12001 then execute: 7 | 8 | ``` 9 | q -p 12001 10 | target/release/ipc 11 | ``` 12 | 13 | The program will contact the q process, run a query, and print the result. 14 | -------------------------------------------------------------------------------- /demos/ipc/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_patterns)] 2 | 3 | extern crate rkdb; 4 | 5 | use rkdb::{ 6 | api, 7 | kbindings::* 8 | }; 9 | 10 | // Attempts to connect to localhost port 12001, runs a query and prints result 11 | fn main() { 12 | let handle = match api::Handle::connect("localhost", 12001, "user") { 13 | Ok(h) => h, 14 | Err(e) => { println!("{}", e); std::process::exit(1) } 15 | }; 16 | let query = "([]a:til 10;b:reverse til 10;c:10?`4;d:{x#.Q.a}each til 10)"; 17 | let k = match handle.query(query) { 18 | Ok(h) => h, 19 | Err(e) => { println!("{}", e); std::process::exit(1) } 20 | }; 21 | handle.close(); 22 | let KOwned(k) = k; 23 | println!("{:?}", KVal::new(k)); 24 | } 25 | -------------------------------------------------------------------------------- /src/api.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fmt; 3 | use std::ffi; 4 | use std::io; 5 | use std::sync::mpsc; 6 | use std::convert; 7 | use kbindings; 8 | use kbindings::*; 9 | use kapi; 10 | 11 | #[derive(Debug)] 12 | pub struct KError { 13 | desc: String, 14 | pub kind: KErr 15 | } 16 | 17 | pub type KResult = Result; 18 | 19 | impl Error for KError { 20 | fn description(&self) -> &str { 21 | &self.desc 22 | } 23 | } 24 | 25 | impl fmt::Display for KError { 26 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 27 | write!(f, "Kind: {:?}, Desc: {}", self.kind, self.description()) 28 | } 29 | } 30 | 31 | impl KError { 32 | pub fn new(s: String, kind: KErr) -> Self { 33 | KError { 34 | desc: s, 35 | kind: kind 36 | } 37 | } 38 | } 39 | 40 | impl convert::From for KError { 41 | fn from(err: io::Error) -> KError { 42 | KError { 43 | desc: err.description().to_string(), 44 | kind: KErr::IOErr 45 | } 46 | } 47 | } 48 | 49 | impl convert::From for KError { 50 | fn from(err: mpsc::RecvError) -> KError { 51 | KError { 52 | desc: err.description().to_string(), 53 | kind: KErr::RecvErr 54 | } 55 | } 56 | } 57 | 58 | #[derive(Debug)] 59 | pub enum KErr { 60 | ConnectionFailed, 61 | AuthenticationFailed, 62 | QueryFailed, 63 | SocketClosed, 64 | SocketTimeout, 65 | IOErr, 66 | RecvErr, 67 | SendErr, 68 | EncodeFailed, 69 | DecodeFailed, 70 | CorruptData, 71 | BadConfig, 72 | Generic, 73 | WrongType 74 | } 75 | 76 | pub struct Handle { 77 | host: String, 78 | port: i32, 79 | username: String, 80 | handle: i32 81 | } 82 | 83 | impl Handle { 84 | pub fn connect(host: &str, port: i32, username: &str) -> Result> { 85 | let chost = try!(ffi::CString::new(host)); 86 | let cuser = try!(ffi::CString::new(username)); 87 | let handle = match unsafe { kapi::khpu(chost.as_ptr(), port, cuser.as_ptr()) } { 88 | h if h < 0 => return Err(Box::new(KError::new("Could not connect".to_string(), KErr::ConnectionFailed))), 89 | 0 => return Err(Box::new(KError::new("Wrong credentials".to_string(), KErr::AuthenticationFailed))), 90 | h => h 91 | }; 92 | Ok(Handle { 93 | host: host.to_string(), 94 | username: username.to_string(), 95 | port: port, 96 | handle: handle 97 | }) 98 | } 99 | 100 | pub fn query(&self, query: &str) -> Result> { 101 | let cquery = try!(ffi::CString::new(query)); 102 | let kptr = unsafe { kapi::k(self.handle, cquery.as_ptr(), kvoid()) }; 103 | if kptr.is_null() { 104 | return Err(Box::new(KError::new("Query failed".to_string(), KErr::QueryFailed))) 105 | } 106 | Ok(unsafe { KOwned(&*kptr)} ) 107 | } 108 | 109 | pub fn close(&self) { 110 | unsafe { kapi::kclose(self.handle) }; 111 | } 112 | } 113 | 114 | pub fn serialize(k: &KOwned) -> KResult { 115 | let ser = unsafe { &*kapi::b9(3, k.0) }; 116 | if kbindings::valid_stream(ser) { 117 | Ok(KOwned(ser)) 118 | } else { 119 | Err(KError::new("Invalid serialization".to_string(), KErr::EncodeFailed)) 120 | } 121 | } 122 | 123 | pub fn deserialize(ser: &KOwned) -> KResult { 124 | if kbindings::valid_stream(ser.0) { 125 | Ok(KOwned(kbindings::deserial(ser.0))) 126 | } else { 127 | Err(KError::new("Invalid deserialization".to_string(), KErr::DecodeFailed)) 128 | } 129 | } 130 | 131 | -------------------------------------------------------------------------------- /src/k.rs: -------------------------------------------------------------------------------- 1 | // for embedding 2 | use types::*; 3 | 4 | #[cfg(not(feature="api"))] 5 | extern "C" { 6 | pub fn ktn(arg1: I, arg2: J) -> *const K; // create list 7 | pub fn knk(arg1: I, ...) -> *const K; // create mixed list 8 | pub fn ku(arg1: U) -> *const K; // create guid 9 | pub fn ka(arg1: I) -> *const K; // create atom 10 | pub fn kb(arg1: I) -> *const K; // create boolean 11 | pub fn kg(arg1: I) -> *const K; // create byte 12 | pub fn kh(arg1: I) -> *const K; // create short 13 | pub fn ki(arg1: I) -> *const K; // create int 14 | pub fn kj(arg1: J) -> *const K; // create long 15 | pub fn ke(arg1: F) -> *const K; // create real 16 | pub fn kf(arg1: F) -> *const K; // create float 17 | pub fn kc(arg1: I) -> *const K; // create char 18 | pub fn ks(arg1: S) -> *const K; // create symbol 19 | pub fn kd(arg1: I) -> *const K; // create date 20 | pub fn kz(arg1: F) -> *const K; // create datetime 21 | pub fn kt(arg1: I) -> *const K; // create time 22 | pub fn ktj(arg1: I, arg2: J) -> *const K; // create timestamp 23 | pub fn kp(arg1: S) -> *const K; // create string 24 | pub fn kpn(arg1: S, arg2: J) -> *const K; // create string length n 25 | pub fn xT(arg1: *const K) -> *const K; // create table from dict 26 | pub fn xD(arg1: *const K, arg2: *const K) -> *const K; // create dict 27 | pub fn ktd(arg1: *const K) -> *const K; // simple table from keyed table 28 | 29 | pub fn ss(arg1: S) -> S; // intern a string 30 | pub fn sn(arg1: S, arg2: I) -> S; // intern n chars from string 31 | 32 | pub fn ymd(arg1: I, arg2: I, arg3: I) -> I; // encode year/month/day as int 33 | pub fn dj(arg1: I) -> I; // create date from int 34 | 35 | pub fn setm(arg1: I) -> I; 36 | 37 | pub fn r1(arg1: K) -> *const K; // increment ref count 38 | pub fn r0(arg1: *const K) -> V; // decrement ref count 39 | pub fn m9() -> V; // garbage collect (?) 40 | pub fn sd1(arg1: I, arg2: Option *const K>) -> *const K; // set callback 41 | pub fn sd0(arg1: I) -> V; // remove callback 42 | pub fn sd0x(arg1: I, arg2: I) -> V; 43 | pub fn k(arg1: I, arg2: S, ...) -> *const K; // local execution 44 | 45 | pub fn dl(f: *mut V, arg1: I) -> *const K; // dynamic link 46 | 47 | pub fn ja(arg1: *mut K, arg2: *mut V) -> *const K; // join atom to list 48 | pub fn js(arg1: *mut K, arg2: S) -> *const K; // join string to list 49 | pub fn jk(arg1: *mut K, arg2: K) -> *const K; // join k obj to list 50 | pub fn jv(k: *mut K, arg1: K) -> *const K; // join two lists 51 | 52 | pub fn krr(arg1: S) -> *const K; // raise error 53 | pub fn orr(arg1: S) -> *const K; // raise system error 54 | pub fn dot(arg1: K, arg2: K) -> *const K; // 'dot' function (apply-over) 55 | 56 | pub fn okx(arg1: *const K) -> I; // check byte stream valid 57 | pub fn b9(arg1: I, arg2: *const K) -> *const K; // serialize object 58 | pub fn d9(arg1: *const K) -> *const K; // deserialize byte stream 59 | } 60 | -------------------------------------------------------------------------------- /src/kapi.rs: -------------------------------------------------------------------------------- 1 | // the difference between the configurations is that api a) links to libkdb.a and b) includes ipc 2 | 3 | #[cfg(feature="api")] 4 | #[link(name="kdb")] 5 | 6 | extern "C" { 7 | pub fn ktn(arg1: I, arg2: J) -> *const K; // create list 8 | pub fn knk(arg1: I, ...) -> *const K; // create mixed list 9 | pub fn ku(arg1: U) -> *const K; // create guid 10 | pub fn ka(arg1: I) -> *const K; // create atom 11 | pub fn kb(arg1: I) -> *const K; // create boolean 12 | pub fn kg(arg1: I) -> *const K; // create byte 13 | pub fn kh(arg1: I) -> *const K; // create short 14 | pub fn ki(arg1: I) -> *const K; // create int 15 | pub fn kj(arg1: J) -> *const K; // create long 16 | pub fn ke(arg1: F) -> *const K; // create real 17 | pub fn kf(arg1: F) -> *const K; // create float 18 | pub fn kc(arg1: I) -> *const K; // create char 19 | pub fn ks(arg1: S) -> *const K; // create symbol 20 | pub fn kd(arg1: I) -> *const K; // create date 21 | pub fn kz(arg1: F) -> *const K; // create datetime 22 | pub fn kt(arg1: I) -> *const K; // create time 23 | pub fn ktj(arg1: I, arg2: J) -> *const K; // create timestamp 24 | pub fn kp(arg1: S) -> *const K; // create string 25 | pub fn kpn(arg1: S, arg2: J) -> *const K; // create string length n 26 | pub fn xT(arg1: *const K) -> *const K; // create table from dict 27 | pub fn xD(arg1: *const K, arg2: *const K) -> *const K; // create dict 28 | pub fn ktd(arg1: *const K) -> *const K; // simple table from keyed table 29 | 30 | pub fn ss(arg1: S) -> S; // intern a string 31 | pub fn sn(arg1: S, arg2: I) -> S; // intern n chars from string 32 | 33 | pub fn ymd(arg1: I, arg2: I, arg3: I) -> I; // encode year/month/day as int 34 | pub fn dj(arg1: I) -> I; // create date from int 35 | 36 | pub fn setm(arg1: I) -> I; 37 | 38 | // IPC 39 | pub fn khp(arg1: S, arg2: I) -> I; // connect to server 40 | pub fn khpu(arg1: S, arg2: I, arg3: S) -> I; // connect with username 41 | pub fn khpun(arg1: S, arg2: I, arg3: S, arg4: I) -> I; // connect with username, timeout 42 | pub fn kclose(arg1: I) -> V; // close socket 43 | pub fn k(arg1: I, arg2: S, ...) -> *const K; // remote execution 44 | 45 | pub fn r1(arg1: K) -> *const K; // increment ref count 46 | pub fn r0(arg1: *const K) -> V; // decrement ref count 47 | pub fn m9() -> V; // garbage collect (?) 48 | pub fn sd1(arg1: I, arg2: Option *const K>) -> *const K; // set callback 49 | pub fn sd0(arg1: I) -> V; // remove callback 50 | pub fn sd0x(arg1: I, arg2: I) -> V; 51 | 52 | pub fn dl(f: *mut V, arg1: I) -> *const K; // dynamic link 53 | 54 | pub fn ja(arg1: *mut K, arg2: *mut V) -> *const K; // join atom to list 55 | pub fn js(arg1: *mut K, arg2: S) -> *const K; // join string to list 56 | pub fn jk(arg1: *mut K, arg2: K) -> *const K; // join k obj to list 57 | pub fn jv(k: *mut K, arg1: K) -> *const K; // join two lists 58 | 59 | pub fn krr(arg1: S) -> *const K; // raise error 60 | pub fn orr(arg1: S) -> *const K; // raise system error 61 | pub fn dot(arg1: K, arg2: K) -> *const K; // 'dot' function (apply-over) 62 | 63 | pub fn okx(arg1: *const K) -> I; // check byte stream valid 64 | pub fn b9(arg1: I, arg2: *const K) -> *const K; // serialize object 65 | pub fn d9(arg1: *const K) -> *const K; // deserialize byte stream 66 | } -------------------------------------------------------------------------------- /src/kbindings.rs: -------------------------------------------------------------------------------- 1 | use std; 2 | use std::mem::{self, zeroed}; 3 | use std::slice; 4 | use std::ptr; 5 | use std::fmt; 6 | use std::ffi; 7 | use std::collections::HashMap; 8 | use std::sync::Mutex; 9 | 10 | use types::*; 11 | 12 | #[cfg(not(feature="api"))] 13 | use k::*; 14 | 15 | #[cfg(feature="api")] 16 | use kapi::*; 17 | 18 | lazy_static! { 19 | static ref ERR_STRS: Mutex> = { 20 | let m = HashMap::with_capacity(64); 21 | Mutex::new(m) 22 | }; 23 | } 24 | 25 | 26 | #[derive(Debug)] 27 | pub struct KOwned(pub &'static K); 28 | 29 | impl Drop for KOwned { 30 | fn drop(&mut self) { 31 | unsafe { r0(self.0) }; 32 | } 33 | } 34 | 35 | impl KOwned { 36 | pub fn new(bytes: &[u8]) -> KOwned { 37 | unsafe { 38 | // make a new, empty K struct holding a bytelist 39 | let k = ktn(4, bytes.len() as i64); 40 | let sx = (*k).fetch_slice::(); 41 | assert_eq!(bytes.len(), sx.len()); 42 | sx.clone_from_slice(bytes); 43 | KOwned(&*k) 44 | } 45 | } 46 | } 47 | 48 | 49 | // these are accessors for the (untagged) union 50 | impl K { 51 | #[inline] 52 | pub unsafe fn cast<'a, T: fmt::Debug>(&self) -> &'a mut T { 53 | let u = &self.union as *const u8; 54 | &mut *(u as *mut T) 55 | } 56 | 57 | pub unsafe fn cast_with_ptr_offset<'a, T>(&self) -> &'a mut T { 58 | let u = &self.union as *const u8; 59 | &mut *(u.add(mem::size_of::()) as *mut T) 60 | } 61 | 62 | #[inline] 63 | pub unsafe fn fetch_slice<'a, T:'a>(&self) -> &'a mut [T] { 64 | slice::from_raw_parts_mut(self.cast_with_ptr_offset(), *self.cast()) 65 | } 66 | } 67 | 68 | impl ::std::default::Default for K { 69 | fn default() -> Self { unsafe { zeroed() } } 70 | } 71 | 72 | #[derive(Debug)] 73 | pub enum KData<'a, T: 'a> { 74 | Atom(&'a mut T), 75 | List(&'a mut [T]) 76 | } 77 | 78 | impl<'a, T: 'a + fmt::Debug> KData<'a, T>{ 79 | #[inline] 80 | unsafe fn atom(k: &'a K) -> KData<'a, T> { 81 | KData::Atom(k.cast()) 82 | } 83 | 84 | #[inline] 85 | unsafe fn guid_atom(k: &'a K) -> KData<'a, T> { 86 | KData::Atom(k.cast_with_ptr_offset()) // while this is an atom, it is packed into a list of 1 87 | } 88 | 89 | #[inline] 90 | unsafe fn list(k: &'a K) -> KData<'a, T> { 91 | KData::List(k.fetch_slice()) 92 | } 93 | } 94 | 95 | 96 | #[derive(Debug)] 97 | pub enum KVal<'a> { 98 | Mixed(Vec>), 99 | Bool(KData<'a, bool>), 100 | Guid(KData<'a, [u8; 16]>), 101 | Byte(KData<'a, u8>), 102 | Short(KData<'a, i16>), 103 | Int(KData<'a, i32>), 104 | Long(KData<'a, i64>), 105 | Real(KData<'a, f32>), 106 | Float(KData<'a, f64>), 107 | Char(&'a i8), 108 | String(&'a str), 109 | Err(&'a str), 110 | Symbol(KData<'a, String>), 111 | Table(Box>), 112 | Dict(Box>, Box>), // Keys, Values 113 | Timestamp(KData<'a, i64>), 114 | Month(KData<'a, i32>), 115 | Date(KData<'a, i32>), 116 | Datetime(KData<'a, f64>), 117 | Timespan(KData<'a, i64>), 118 | Minute(KData<'a, i32>), 119 | Second(KData<'a, i32>), 120 | Time(KData<'a, i32>), 121 | Function, 122 | Unknown, 123 | } 124 | 125 | impl<'a> KVal<'a> { 126 | pub unsafe fn from_raw(k: *const K) -> KVal<'a> { 127 | Self::new(&*k) 128 | } 129 | 130 | pub fn new(k: &'a K) -> KVal<'a> { 131 | unsafe { 132 | match k.t { 133 | -1 => KVal::Bool(KData::atom(k)), 134 | -2 => KVal::Guid(KData::guid_atom(k)), 135 | -4 => KVal::Byte(KData::atom(k)), 136 | -5 => KVal::Short(KData::atom(k)), 137 | -6 => KVal::Int(KData::atom(k)), 138 | -7 => KVal::Long(KData::atom(k)), 139 | -8 => KVal::Real(KData::atom(k)), 140 | -9 => KVal::Float(KData::atom(k)), 141 | -10 => KVal::Char(k.cast()), 142 | -11 => KVal::Symbol(KData::atom(k)), 143 | -12 => KVal::Timestamp( KData::atom(k)), 144 | -13 => KVal::Month( KData::atom(k)), 145 | -14 => KVal::Date( KData::atom(k)), 146 | -15 => KVal::Datetime( KData::atom(k)), 147 | -16 => KVal::Timespan( KData::atom(k)), 148 | -17 => KVal::Minute( KData::atom(k)), 149 | -18 => KVal::Second( KData::atom(k)), 150 | -19 => KVal::Time( KData::atom(k)), 151 | -128 => { 152 | let err = { 153 | let mut ptr: [u8; 8] = [0; 8]; 154 | ptr.clone_from_slice(&(*k).union[0..8]); 155 | ffi::CStr::from_ptr(std::mem::transmute::<[u8; 8], *const i8>(ptr)) 156 | }; 157 | KVal::Err(err.to_str().unwrap()) 158 | } 159 | 0 => { 160 | let s: &[&K] = k.fetch_slice(); 161 | KVal::Mixed(s.iter().map(|&x| KVal::new(x)).collect()) 162 | }, 163 | 1 => KVal::Bool( KData::list(k)), 164 | 2 => KVal::Guid( KData::list(k)), 165 | 4 => KVal::Byte( KData::list(k)), 166 | 5 => KVal::Short( KData::list(k)), 167 | 6 => KVal::Int( KData::list(k)), 168 | 7 => KVal::Long( KData::list(k)), 169 | 8 => KVal::Real( KData::list(k)), 170 | 9 => KVal::Float( KData::list(k)), 171 | #[cfg(not(feature="unchecked_utf8"))] 172 | 10 => { 173 | let s = std::str::from_utf8(k.fetch_slice::()); 174 | KVal::String(s.unwrap()) 175 | }, 176 | #[cfg(feature="unchecked_utf8")] 177 | 10 => { 178 | let s = std::str::from_utf8_unchecked(k.fetch_slice::()); 179 | KVal::String(s) 180 | }, 181 | 11 => KVal::Symbol( KData::list(k)), 182 | 12 => KVal::Timestamp( KData::list(k)), 183 | 13 => KVal::Month( KData::list(k)), 184 | 14 => KVal::Date( KData::list(k)), 185 | 15 => KVal::Datetime( KData::list(k)), 186 | 16 => KVal::Timespan( KData::list(k)), 187 | 17 => KVal::Minute( KData::list(k)), 188 | 18 => KVal::Second( KData::list(k)), 189 | 19 => KVal::Time( KData::list(k)), 190 | 98 => KVal::Table( Box::new(KVal::new(k))), 191 | 99 => { 192 | let slice = k.fetch_slice::<&K>(); 193 | KVal::Dict( Box::new(KVal::new(slice[0])), 194 | Box::new(KVal::new(slice[1]))) 195 | } 196 | 100 => KVal::Function, 197 | _ => KVal::Unknown 198 | } 199 | } 200 | } 201 | 202 | pub fn to_k(&self) -> &K { 203 | match self { 204 | KVal::Mixed(ref arr) => kmixed(arr), 205 | KVal::Bool(KData::Atom(&mut v)) => kbool(v), 206 | KVal::Bool(KData::List(ref vals)) => klist::(1, vals), 207 | KVal::Byte(KData::Atom(&mut v)) => kbyte(v), 208 | KVal::Byte(KData::List(ref vals)) => klist::(4, vals), 209 | KVal::Short(KData::Atom(&mut v)) => kshort(v), 210 | KVal::Short(KData::List(ref vals)) => klist::(5, vals), 211 | KVal::Int(KData::Atom(&mut v)) => kint(v), 212 | KVal::Int(KData::List(ref vals)) => klist::(6, vals), 213 | KVal::Long(KData::Atom(&mut v)) => klong(v), 214 | KVal::Long(KData::List(ref vals)) => klist::(7, vals), 215 | KVal::Real(KData::Atom(&mut v)) => kreal(v), 216 | KVal::Real(KData::List(ref vals)) => klist::(8, vals), 217 | KVal::Float(KData::Atom(&mut v)) => kfloat(v), 218 | KVal::Float(KData::List(ref vals)) => klist::(9, vals), 219 | KVal::Symbol(KData::Atom(ref v)) => ksymbol(v), 220 | KVal::Symbol(KData::List(ref vals)) => klist::<*const i8>(11, &intern_strings(vals.to_vec())), 221 | KVal::Dict(k, v) => kdict(&k, &v), 222 | KVal::String(ref s) => kstring(s), 223 | ref unknown => { 224 | println!("{:?}", unknown); 225 | kerror("NYI") 226 | } 227 | } 228 | } 229 | 230 | } 231 | 232 | pub fn intern_strings(strs: Vec) -> Vec<*const i8> { 233 | strs.into_iter() 234 | .map(|s| ffi::CString::new(s).unwrap()) 235 | .map(|s| unsafe { ss(s.as_ptr()) }) 236 | .collect() 237 | } 238 | 239 | pub fn valid_stream(k: &K) -> bool { 240 | unsafe { okx(k) == 1 } 241 | } 242 | 243 | pub fn deserial(k: &K) -> &K { 244 | unsafe { &*d9(k) } 245 | } 246 | 247 | pub fn kerror(err: &str) -> &'static K { 248 | let mut map = ERR_STRS.lock().unwrap(); 249 | 250 | let msg = map.entry(err.to_string()).or_insert_with(|| ffi::CString::new(err).unwrap()); 251 | let ptr = msg.as_ptr(); 252 | 253 | // AFAICT, just returns a null pointer 254 | unsafe { &*krr(ptr) } 255 | } 256 | 257 | pub fn kbool(b: bool) -> &'static K { 258 | unsafe { &*kb( { if b { 1 } else { 0 } } ) } 259 | } 260 | 261 | pub fn kbyte(b: u8) -> &'static K { 262 | unsafe { &*kg(b as i32) } 263 | } 264 | 265 | pub fn kshort(h: i16) -> &'static K { 266 | unsafe { &*kh(h as i32) } 267 | } 268 | 269 | pub fn kint(i: i32) -> &'static K { 270 | unsafe { &*ki(i) } 271 | } 272 | 273 | pub fn klong(j: i64) -> &'static K { 274 | unsafe { &*kj(j) } 275 | } 276 | 277 | pub fn kreal(e: f32) -> &'static K { 278 | unsafe { &*ke(e as f64) } 279 | } 280 | 281 | pub fn kfloat(f: f64) -> &'static K { 282 | unsafe { &*kf(f) } 283 | } 284 | 285 | pub fn kchar(c: char) -> &'static K { 286 | unsafe { &*kc(c as i32) } 287 | } 288 | 289 | pub fn kstring(s: &str) -> &'static K { 290 | let c_str = ffi::CString::new(s).unwrap(); 291 | unsafe { &*kpn(c_str.as_ptr(), s.len() as i64) } 292 | } 293 | 294 | pub fn ksymbol(s: &str) -> &'static K { 295 | let c_str = ffi::CString::new(s).unwrap(); 296 | unsafe { &*(ks(c_str.as_ptr())) } 297 | } 298 | 299 | pub fn kvoid() -> *const K { 300 | ptr::null() 301 | } 302 | 303 | pub fn klist(ktype: i32, vals: &[T]) -> &'static K { 304 | unsafe { 305 | let k = ktn(ktype, vals.len() as i64); 306 | let sx = (*k).fetch_slice::(); 307 | assert_eq!(vals.len(), sx.len()); 308 | std::ptr::copy_nonoverlapping(vals.as_ptr(), sx.as_mut_ptr(), vals.len()); 309 | &*k 310 | } 311 | } 312 | 313 | pub fn kdict(keys: &KVal, vals: &KVal) -> &'static K { 314 | unsafe { &*xD(keys.to_k(), vals.to_k()) } 315 | } 316 | 317 | pub fn ktable(dict: KVal) -> &'static K { 318 | unsafe { &*xT(dict.to_k()) } 319 | } 320 | 321 | pub fn kmixed(vals: &[KVal]) -> &'static K { 322 | let (k, sx); 323 | unsafe { 324 | k = &*ktn(0, vals.len() as i64); 325 | sx = (*k).fetch_slice::<&K>(); 326 | } 327 | assert_eq!(vals.len(), sx.len()); 328 | for (ix, val) in vals.iter().enumerate() { 329 | sx[ix] = val.to_k() 330 | } 331 | k 332 | } 333 | 334 | /* TODO 335 | pub fn ks(arg1: S) -> *const K; // create symbol 336 | pub fn kd(arg1: I) -> *const K; // create date 337 | pub fn kz(arg1: F) -> *const K; // create datetime 338 | pub fn kt(arg1: I) -> *const K; // create time 339 | pub fn ku(arg1: U) -> *const K; // create guid 340 | pub fn ka(arg1: I) -> *const K; // create atom 341 | pub fn ktn(arg1: I, arg2: J) -> *const K; // create list 342 | pub fn knk(arg1: I, ...) -> *const K; // create mixed list 343 | pub fn ktj(arg1: I, arg2: J) -> *const K; // create timestamp 344 | pub fn kp(arg1: S) -> *const K; // create string 345 | pub fn kpn(arg1: S, arg2: J) -> *const K; // create string length n 346 | pub fn xT(arg1: K) -> *const K; // create table from dict 347 | pub fn xD(arg1: K, arg2: K) -> *const K; // create dict 348 | pub fn ktd(arg1: K) -> *const K; // simple table from keyed table 349 | */ 350 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate lazy_static; 3 | extern crate libc; 4 | extern crate num; 5 | extern crate nix; 6 | extern crate bitflags; 7 | 8 | include!(concat!(env!("OUT_DIR"), "/symbols.rs")); 9 | 10 | pub mod types; 11 | pub mod k; 12 | pub mod kapi; 13 | pub mod kbindings; 14 | 15 | 16 | #[cfg(feature = "api")] 17 | pub mod api; 18 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | use libc; 2 | use std::fmt; 3 | use std::mem::zeroed; 4 | 5 | pub type S = *const libc::c_char; 6 | pub type C = libc::c_char; 7 | pub type G = libc::c_uchar; 8 | pub type H = libc::c_short; 9 | pub type I = libc::c_int; 10 | pub type J = libc::c_longlong; 11 | pub type E = libc::c_float; 12 | pub type F = libc::c_double; 13 | pub type V = libc::c_void; 14 | 15 | 16 | #[repr(C)] 17 | pub struct K { 18 | pub m: libc::c_char, 19 | pub a: libc::c_char, 20 | pub t: libc::c_char, 21 | pub u: C, 22 | pub r: I, 23 | pub union: [u8; 16], 24 | } 25 | 26 | impl K { 27 | // Attributes seem to be a sequence as opposed to bit flags 28 | fn is_sorted(&self) -> bool { 29 | self.u == 0x1 30 | } 31 | 32 | fn is_unique(&self) -> bool { 33 | self.u == 0x2 34 | } 35 | 36 | fn is_parted(&self) -> bool { 37 | self.u == 0x3 38 | } 39 | 40 | fn is_grouped(&self) -> bool { 41 | self.u == 0x5 42 | } 43 | } 44 | 45 | impl fmt::Debug for K { 46 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 47 | let mut vs = Vec::new(); 48 | vs.push(format!("Type:{}, Attr:{}, RefCt:{} Addr:{:p}", 49 | self.t, self.u, self.r, self)); 50 | vs.push(format!("Sorted:{}, Unique:{}, Parted:{}, Grouped:{}", 51 | self.is_sorted(), self.is_unique(), self.is_parted(), self.is_grouped())); 52 | let mut s = String::new(); 53 | for v in self.union.iter() { 54 | s.push_str(&format!("{:02x}", v)) 55 | } 56 | vs.push(format!("Union: 0x{}", s)); 57 | f.write_str(&vs.join("\n")) 58 | } 59 | } 60 | 61 | #[repr(C)] 62 | pub struct U { 63 | pub g: [G; 16usize], 64 | } 65 | 66 | impl ::std::default::Default for U { 67 | fn default() -> Self { unsafe { zeroed() } } 68 | } 69 | --------------------------------------------------------------------------------