├── .gitignore ├── Cargo.toml ├── README.md ├── bindgen ├── Cargo.toml └── src │ └── lib.rs ├── bindgen_macros ├── Cargo.toml └── src │ └── lib.rs └── examples ├── Cargo.toml ├── encrypt_decrypt ├── .gitignore ├── Cargo.toml ├── README.md ├── get_sql.sh └── src │ └── lib.rs ├── src ├── concat.rs ├── concat3.rs ├── contains.rs └── reverse_blob.rs └── wasmedge ├── README.md ├── get_exchange_rate ├── .cargo │ └── config.toml ├── Cargo.toml ├── README.md ├── gen_libsql_udf.sh └── src │ └── main.rs └── wasi_nn_udf ├── .cargo └── config.toml ├── Cargo.toml ├── README.md ├── gen_demo_sql.sh ├── gen_insert_image_sql.sh ├── gen_libsql_udf.sh ├── input.jpg ├── mobilenet.pt └── src ├── imagenet_classes.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "bindgen", 4 | "bindgen_macros", 5 | "examples", 6 | "examples/encrypt_decrypt", 7 | "examples/wasmedge/*", 8 | ] 9 | 10 | [profile.release] 11 | opt-level = 's' 12 | lto = true 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | 3 | This repository contains a helper library for coding 4 | WebAssembly-powered user-defined functions for libSQL. 5 | 6 | Marking a native Rust function with `#[libsql_bindgen]` macro 7 | and compiling it to `wasm32-unknown-unknown` target 8 | is enough to produce a user-defined function definition 9 | callable directly from libSQL. 10 | 11 | A generic example: 12 | https://github.com/psarna/libsql_bindgen/blob/master/examples/encrypt_decrypt/src/lib.rs 13 | 14 | Try it yourself: 15 | ``` 16 | cd examples/encrypt_decrypt 17 | ./get_sql.sh encrypt 18 | ./get_sql.sh decrypt 19 | ``` 20 | 21 | There are also [advanced examples that require WasmEdge](examples/wasmedge) to run: 22 | 23 | * [Call external HTTPS web services from a UDF](examples/wasmedge/get_exchange_rate) 24 | * [Classify image fields using a PyTorch model](examples/wasmedge/wasi_nn_udf) 25 | 26 | 27 | This repository is the foundation of [libSQL generate](https://github.com/psarna/libsql_generate) and http://bindgen.libsql.org/ 28 | -------------------------------------------------------------------------------- /bindgen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libsql_bindgen" 3 | version = "0.3.1" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | keywords = ["webassembly", "libsql", "sqlite", "wasm", "udf"] 7 | description = "Rust library for writing WebAssembly-powered user-defined functions for libSQL" 8 | repository = "https://github.com/psarna/libsql_bindgen" 9 | readme = "../README.md" 10 | 11 | [dependencies] 12 | libsql_bindgen_macros = { version = "0.2.4", path = "../bindgen_macros"} 13 | 14 | -------------------------------------------------------------------------------- /bindgen/src/lib.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::{c_char, c_void, CStr}; 2 | 3 | pub use libsql_bindgen_macros::libsql_bindgen; 4 | 5 | static SQLITE_TEXT: u8 = 3; 6 | static SQLITE_BLOB: u8 = 4; 7 | static SQLITE_NULL: u8 = 5; 8 | 9 | // Type translation from Wasm pointers to strings, blobs and nulls 10 | pub trait FromLibSQL { 11 | fn from_libsql_type(wasm_ptr: i32) -> Self; 12 | } 13 | 14 | // Type translation from strings, blobs and nulls to Wasm pointers 15 | pub trait IntoLibSQL { 16 | fn into_libsql_type(self) -> i32; 17 | } 18 | 19 | impl FromLibSQL for &str { 20 | fn from_libsql_type(wasm_ptr: i32) -> Self { 21 | let raw_ptr = wasm_ptr as *const c_char; 22 | unsafe { 23 | if *raw_ptr != SQLITE_TEXT as c_char { 24 | "???" 25 | } else { 26 | match CStr::from_ptr(raw_ptr.offset(1)).to_str() { 27 | Ok(s) => s, 28 | Err(_) => "!!!", 29 | } 30 | } 31 | } 32 | } 33 | } 34 | 35 | impl IntoLibSQL for &str { 36 | fn into_libsql_type(self) -> i32 { 37 | let mut mem: Vec = vec![0; self.len() + 2]; 38 | mem[0] = SQLITE_TEXT; 39 | mem[1..=self.len()].copy_from_slice(self.as_bytes()); 40 | mem[self.len() + 1] = 0; 41 | let ptr = mem.as_ptr() as i32; 42 | std::mem::forget(mem); 43 | ptr 44 | } 45 | } 46 | 47 | impl FromLibSQL for String { 48 | fn from_libsql_type(wasm_ptr: i32) -> Self { 49 | <&str>::from_libsql_type(wasm_ptr).to_owned() 50 | } 51 | } 52 | 53 | impl IntoLibSQL for String { 54 | fn into_libsql_type(self) -> i32 { 55 | self.as_str().into_libsql_type() 56 | } 57 | } 58 | 59 | impl FromLibSQL for &mut [u8] { 60 | fn from_libsql_type(wasm_ptr: i32) -> Self { 61 | let raw_ptr = wasm_ptr as *const c_void; 62 | if unsafe { *(raw_ptr as *const c_char) != SQLITE_BLOB as c_char } { 63 | &mut [] 64 | } else { 65 | let size = unsafe { 66 | u32::from_be_bytes( 67 | core::slice::from_raw_parts(raw_ptr.offset(1) as *const u8, 4) 68 | .try_into() 69 | .unwrap_or([0_u8, 0_u8, 0_u8, 0_u8]), 70 | ) 71 | }; 72 | unsafe { core::slice::from_raw_parts_mut(raw_ptr.offset(5) as *mut u8, size as usize) } 73 | } 74 | } 75 | } 76 | 77 | impl IntoLibSQL for Vec { 78 | fn into_libsql_type(self) -> i32 { 79 | let mut mem: Vec = vec![0; self.len() + 5]; 80 | mem[0] = SQLITE_BLOB; 81 | mem[1..5].copy_from_slice(&u32::to_be_bytes(self.len() as u32)); 82 | mem[5..].copy_from_slice(&self); 83 | let ptr = mem.as_ptr() as i32; 84 | std::mem::forget(mem); 85 | ptr 86 | } 87 | } 88 | 89 | impl FromLibSQL for Option { 90 | fn from_libsql_type(wasm_ptr: i32) -> Self { 91 | let raw_ptr = wasm_ptr as *const c_char; 92 | unsafe { 93 | if *raw_ptr == SQLITE_NULL as c_char { 94 | None 95 | } else { 96 | Some(::from_libsql_type(wasm_ptr)) 97 | } 98 | } 99 | } 100 | } 101 | 102 | #[no_mangle] 103 | pub unsafe extern "C" fn libsql_malloc(size: usize) -> usize { 104 | let buffer = Vec::::with_capacity(size); 105 | let ptr = Vec::leak(buffer); 106 | ptr.as_ptr() as usize 107 | } 108 | -------------------------------------------------------------------------------- /bindgen_macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "libsql_bindgen_macros" 4 | version = "0.2.4" 5 | license = "MIT OR Apache-2.0" 6 | keywords = ["webassembly", "libsql", "sqlite", "wasm", "udf"] 7 | description = "Rust macros for writing WebAssembly-powered user-defined functions for libSQL" 8 | repository = "https://github.com/psarna/libsql_bindgen" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | proc-macro2 = "1.0" 14 | quote = "1.0" 15 | syn = { version = "1.0", features = ["full"] } 16 | 17 | [lib] 18 | proc-macro = true 19 | -------------------------------------------------------------------------------- /bindgen_macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use proc_macro2::Span; 3 | use quote::quote; 4 | use quote::ToTokens; 5 | use std::borrow::Borrow; 6 | use std::borrow::BorrowMut; 7 | use syn::{parse, parse_str, FnArg, ItemFn, Pat, Stmt}; 8 | 9 | fn gen_libsql_type(ty: &syn::Type) -> Box { 10 | match ty.to_owned().into_token_stream().to_string().as_str() { 11 | "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" | "bool" | "char" => { 12 | Box::new(syn::Type::from(parse_str::("i64").unwrap())) 13 | } 14 | "f32" | "f64" => Box::new(syn::Type::from(parse_str::("f64").unwrap())), 15 | _ => Box::new(syn::Type::from(parse_str::("i32").unwrap())), 16 | } 17 | } 18 | 19 | fn gen_from_expr(ty: &syn::Type, id: &str) -> String { 20 | let type_str = ty.to_owned().into_token_stream().to_string(); 21 | match type_str.as_str() { 22 | "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" | "bool" | "char" => { 23 | format!("{} as {}", id, type_str) 24 | } 25 | "f32" | "f64" => format!("{} as f64", id), 26 | _ => format!("<{}>::from_libsql_type({})", type_str, id), 27 | } 28 | } 29 | 30 | fn gen_into_expr(return_type_str: &str, expr: syn::Expr) -> syn::Expr { 31 | let expr_str = expr.into_token_stream().to_string(); 32 | parse_str::(&match return_type_str { 33 | "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" | "bool" | "char" => { 34 | format!("{} as i64", expr_str) 35 | } 36 | "f32" | "f64" => format!("{} as f64", expr_str), 37 | _ => format!("{}.into_libsql_type()", expr_str), 38 | }) 39 | .unwrap() 40 | } 41 | 42 | /* libSQL bindgen transforms a native Rust function into a form compilable to Wasm 43 | ** and immediately usable as a libSQL user-defined function. 44 | ** It follows the following ABI rules for libSQL types (INTEGER, FLOAT, STRING, BLOB, NULL): 45 | ** 1. integers are passed as i64 46 | ** 2. floats are passed as f64 47 | ** 3. Strings, blobs and nulls are pointers, 48 | ** with the first byte indicating the type - SQLITE_TEXT, SQLITE_BLOB or SQLITE_NULL 49 | ** and then: 50 | ** - strings are encoded as null-terminated strings 51 | ** - blobs are encoded as [4 bytes of size information][data] 52 | ** - nulls are encoded as nothing, because the type byte already indicates it's null 53 | */ 54 | #[proc_macro_attribute] 55 | pub fn libsql_bindgen(_attrs: TokenStream, item: TokenStream) -> TokenStream { 56 | let input = match parse::(item) { 57 | Ok(i) => i, 58 | Err(_) => { 59 | return TokenStream::from( 60 | syn::Error::new( 61 | Span::call_site(), 62 | "libsql_bindgen operates on function definitions only", 63 | ) 64 | .to_compile_error(), 65 | ) 66 | } 67 | }; 68 | 69 | let mut native_sig = input.sig.clone(); 70 | let mut generated_sig = input.sig.clone(); 71 | native_sig.ident = syn::Ident::new( 72 | format!("__libsql_native_{}", generated_sig.ident).as_str(), 73 | Span::call_site(), 74 | ); 75 | // Translate parameter types 76 | for raw_param in &mut generated_sig.inputs { 77 | if let &mut FnArg::Typed(ref mut param) = raw_param { 78 | param.ty = gen_libsql_type(¶m.ty); 79 | if let &mut Pat::Ident(ref mut id) = param.pat.borrow_mut() { 80 | id.mutability = Option::None; 81 | } 82 | } 83 | } 84 | // Translate the return type 85 | let mut return_type_str = String::new(); 86 | if let &syn::ReturnType::Type(_, ref ty) = &generated_sig.output { 87 | return_type_str = ty.to_owned().into_token_stream().to_string(); 88 | generated_sig.output = 89 | syn::ReturnType::Type(syn::token::RArrow::default(), gen_libsql_type(ty)); 90 | } 91 | 92 | // Copy the native function body 93 | let native_block = &input.block; 94 | let mut generated_block = syn::Block { 95 | brace_token: input.block.brace_token, 96 | stmts: Vec::::new(), 97 | }; 98 | 99 | // Generate the exported function body 100 | let mut raw_ret_expr = 101 | parse_str::(&format!("{}()", &native_sig.ident.to_string())).unwrap(); 102 | if let &mut syn::Expr::Call(ref mut call) = &mut raw_ret_expr { 103 | for raw_param in &native_sig.inputs { 104 | if let &FnArg::Typed(ref param) = raw_param { 105 | if let &Pat::Ident(ref id) = param.pat.borrow() { 106 | let from_expr = 107 | parse_str::(&gen_from_expr(¶m.ty, &id.ident.to_string())) 108 | .unwrap(); 109 | call.args.push(from_expr); 110 | } 111 | } 112 | } 113 | } 114 | let ret_expr = gen_into_expr(&return_type_str, raw_ret_expr); 115 | generated_block.stmts.push(Stmt::Expr(ret_expr)); 116 | 117 | TokenStream::from(quote! { 118 | #native_sig 119 | #native_block 120 | 121 | #[no_mangle] 122 | pub #generated_sig 123 | #generated_block 124 | }) 125 | } 126 | -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "examples" 4 | publish = false 5 | version = "0.0.0" 6 | 7 | [dependencies] 8 | libsql_bindgen = { path = "../bindgen" } 9 | 10 | [[example]] 11 | name = "concat" 12 | path = "src/concat.rs" 13 | 14 | [[example]] 15 | name = "concat3" 16 | path = "src/concat3.rs" 17 | 18 | 19 | [[example]] 20 | name = "contains" 21 | path = "src/contains.rs" 22 | 23 | [[example]] 24 | name = "reverse_blob" 25 | path = "src/reverse_blob.rs" 26 | -------------------------------------------------------------------------------- /examples/encrypt_decrypt/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | /libsql-target 4 | -------------------------------------------------------------------------------- /examples/encrypt_decrypt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libsql_encrypt_decrypt" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | libsql_bindgen = { version = "0", path = "../../bindgen" } 10 | magic-crypt = "3.1" 11 | 12 | [lib] 13 | crate-type = ["cdylib"] 14 | -------------------------------------------------------------------------------- /examples/encrypt_decrypt/README.md: -------------------------------------------------------------------------------- 1 | # libSQL user-defined functions: encrypt-decrypt 2 | 3 | **_NOTE:_** This experimental example is based on top of pull request https://github.com/libsql/libsql/pull/45 - it's not in upstream libSQL at the time of writing this post! 4 | 5 | This short example shows how to write simple encryption/decryption routines in Rust, compile them to WebAssembly and finally register them as user-defined functions in libSQL command-line interface. 6 | 7 | ## Initial setup 8 | ### Install cargo 9 | Support for user-defined functions is currently implemented on top of [Wasmtime](https://github.com/bytecodealliance/wasmtime). Our roadmap includes evaluating [WasmEdge](https://github.com/WasmEdge/WasmEdge) and [Wasmer](https://github.com/wasmerio/wasmer) as well. 10 | 11 | In order to be able to build the runtime, follow the official instructions: https://doc.rust-lang.org/cargo/getting-started/installation.html 12 | 13 | Once done, libSQL can be compiled with WebAssembly functions support enabled. 14 | 15 | ### Compile libSQL with support for WebAssembly functions 16 | Support for WebAssembly-powered user-defined functions is currently experimental and opt-in. It can be enabled by compiling libSQL with the `--enable-wasm-runtime` configure flag. In order to compile libSQL shell with WebAssembly support, the following snippet can be used: 17 | 18 | ```sh 19 | ./configure --enable-wasm-runtime 20 | make -j8 sqlite3 21 | ``` 22 | 23 | Remember that this configuration option is only present at https://github.com/libsql/libsql/pull/45 right now. 24 | 25 | Once compiled, the shell binary will depend on `libwblibsql.so` library produced in `.libs/` directory. 26 | In order to be able to run the shell, either move the binary to a known library path (e.g. `/usr/lib`), 27 | or use the following env variable (Linux-only): 28 | ```sh 29 | LD_LIBRARY_PATH=.libs ./sqlite3 30 | ``` 31 | 32 | # Compile user-defined functions to WebAssembly 33 | 34 | ## libsql\_bindgen macro 35 | libSQL supports running WebAssembly functions with specific type translation: 36 | - INTEGER and DOUBLE are passed as is 37 | - TEXT is passed as a pointer to the following structure: 38 | `[1 byte of type information][null-terminated string]` 39 | - BLOB is passed as a pointer to the following structure: 40 | `[1 byte of type information][4 bytes of big endian size][data]` 41 | - NULL is passed as a pointer to the following structure: 42 | `[1 byte of type information]` 43 | 44 | In order to automatically translate between native Rust types and libSQL types, 45 | one can use the [libsql\_bindgen](https://crates.io/crates/libsql_bindgen) crate 46 | and its [#[libsql\_bindgen]](https://docs.rs/libsql_bindgen/latest/libsql_bindgen/attr.libsql_bindgen.html) macro. Here's an example: 47 | https://github.com/psarna/libsql_bindgen/blob/3e215e270773d101440c8d0e93b730e2107a4dd3/examples/encrypt_decrypt/src/lib.rs#L4-L8 48 | 49 | Full implementation of encryption/decryption is not much longer and can be found here: 50 | https://github.com/psarna/libsql_bindgen/blob/3e215e270773d101440c8d0e93b730e2107a4dd3/examples/encrypt_decrypt/src/lib.rs#L1-L15 51 | 52 | Even though the code operates on native Rust type - `String`, the generated WebAssembly output will correctly translate it to pointers to structures mentioned above. 53 | 54 | Finally, please note that even though this code relies on an external crate - magic-crypt - it's still fully compilable to standalone WebAssembly, and can run directly in libSQL! 55 | 56 | ## get\_sql.sh script 57 | 58 | A convenience script is available for producing an SQL snippet which creates given user-defined function. The script takes one parameter - the exported function name - and outputs a single file named `create_.sql`, which can be run in libSQL shell to register the function. 59 | 60 | This example contains the implementation of two functions - `encrypt` and `decrypt`. Their SQL can be generated as follows: 61 | ```sh 62 | ./get_sql.sh encrypt 63 | ./get_sql.sh decrypt 64 | ls libsql-target/*.sql 65 | ``` 66 | On success, the following files should appear: 67 | ```sh 68 | [sarna@sarna-pc encrypt_decrypt]$ ls -lsh libsql-target/*.sql 69 | 816K -rw-r--r-- 1 sarna sarna 813K Nov 2 11:21 libsql-target/create_decrypt.sql 70 | 816K -rw-r--r-- 1 sarna sarna 813K Nov 2 12:34 libsql-target/create_encrypt.sql 71 | ``` 72 | 73 | Feel free to inspect these files - it's a regular `CREATE FUNCTION` statement, but it *will* be quite large due to the size of Wasm binary. 74 | 75 | > **_NOTE:_** The binary is large mostly due to containing lots of Rust standard library inside. We plan to significantly reduce the output size, either by aggresive optimization and dropping unused symbols, or by shipping the Rust runtime as a separate, deduplicated Wasm module. 76 | 77 | ## Use the functions! 78 | Once the SQL commands are generated, they can be used to dynamically register the functions - either in libSQL shell, or via any other driver. 79 | 80 | Here's an example shell snippet: 81 | ```sql 82 | sqlite3 # remember that this binary needs to be compiled with Wasm support 83 | 84 | 85 | .init_wasm_func_table -- sets up a table for storing functions, it's enough to invoke it once per session 86 | .read libsql-target/create_encrypt.sql 87 | .read libsql-target/create_decrypt.sql 88 | 89 | CREATE TABLE secrets(secret); 90 | INSERT INTO secrets (secret) VALUES (encrypt('my secret value: 1', 's3cretp4ss')); 91 | INSERT INTO secrets (secret) VALUES (encrypt('my even more secret value: 2', 's3cretp4ss')); 92 | INSERT INTO secrets (secret) VALUES (encrypt('classified value: 3', 's3cretp4ss')); 93 | 94 | .mode column 95 | SELECT secret, decrypt(secret, 'wrong-pass') from secrets; 96 | 97 | secret decrypt(secret, 'wrong-pass') 98 | -------------------------------------------- ----------------------------- 99 | IyTvoTEnh9a/f6+pac3rLPToP9DkWqS7CEW8tan3mbQ= [ACCESS DENIED] 100 | bUQ4fEe6hPnsMx8ABOZO97CMr/wouGTByfUCEmFVZTs= [ACCESS DENIED] 101 | o+m1w7UdoxBZxLumNW0VoMKSMFaC4o8N5uknAQZ/yFY= [ACCESS DENIED] 102 | 103 | SELECT secret, decrypt(secret, 's3cretp4ss') from secrets; 104 | secret decrypt(secret, 's3cretp4ss') 105 | -------------------------------------------- ----------------------------- 106 | IyTvoTEnh9a/f6+pac3rLPToP9DkWqS7CEW8tan3mbQ= my secret value: 1 107 | bUQ4fEe6hPnsMx8ABOZO97CMr/wouGTByfUCEmFVZTs= my even more secret value: 2 108 | o+m1w7UdoxBZxLumNW0VoMKSMFaC4o8N5uknAQZ/yFY= classified value: 3 109 | 110 | SELECT secret, decrypt(secret, 's3cretp4ss') from secrets; 111 | 112 | ``` 113 | 114 | -------------------------------------------------------------------------------- /examples/encrypt_decrypt/get_sql.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [ "$#" -ne 1 ]; then 6 | echo "Usage: $0 " 7 | exit 1 8 | fi 9 | 10 | LIBSQL_EXPORTED_FUNC=$1 11 | LIBSQL_COMPILED_WASM=libsql-target/wasm32-unknown-unknown/release/libsql_encrypt_decrypt.wasm 12 | LIBSQL_OPTIMIZED_WASM=libsql-target/libsql_encrypt_decrypt_optimized.wasm 13 | LIBSQL_TARGET_FILE=libsql-target/create_${LIBSQL_EXPORTED_FUNC}.sql 14 | 15 | CARGO_TARGET_DIR=libsql-target cargo build --release --target wasm32-unknown-unknown 16 | wasm-opt -Os $LIBSQL_COMPILED_WASM -o $LIBSQL_OPTIMIZED_WASM || : 17 | echo ".init_wasm_func_table -- only needed for shell" > $LIBSQL_TARGET_FILE 18 | echo "DROP FUNCTION IF EXISTS $1;" >> $LIBSQL_TARGET_FILE 19 | echo "CREATE FUNCTION $1 LANGUAGE wasm AS '" >> $LIBSQL_TARGET_FILE 20 | wasm2wat $LIBSQL_OPTIMIZED_WASM | sed "s/'/''/g" >> $LIBSQL_TARGET_FILE 21 | echo "';" >> $LIBSQL_TARGET_FILE 22 | 23 | if ! grep "export \"$LIBSQL_EXPORTED_FUNC\"" $LIBSQL_TARGET_FILE; then 24 | echo "Error: function $LIBSQL_EXPORTED_FUNC not exported from the WebAssembly module" 25 | exit 1 26 | fi 27 | if ! grep "export \"memory\"" $LIBSQL_TARGET_FILE; then 28 | echo "Error: memory not exported from the WebAssembly module" 29 | exit 1 30 | fi 31 | -------------------------------------------------------------------------------- /examples/encrypt_decrypt/src/lib.rs: -------------------------------------------------------------------------------- 1 | use libsql_bindgen::*; 2 | use magic_crypt::{new_magic_crypt, MagicCryptTrait}; 3 | 4 | #[libsql_bindgen::libsql_bindgen] 5 | pub fn encrypt(data: String, key: String) -> String { 6 | let mc = new_magic_crypt!(key, 256); 7 | mc.encrypt_str_to_base64(data) 8 | } 9 | 10 | #[libsql_bindgen::libsql_bindgen] 11 | pub fn decrypt(data: String, key: String) -> String { 12 | let mc = new_magic_crypt!(key, 256); 13 | mc.decrypt_base64_to_string(data) 14 | .unwrap_or("[ACCESS DENIED]".to_owned()) 15 | } 16 | -------------------------------------------------------------------------------- /examples/src/concat.rs: -------------------------------------------------------------------------------- 1 | use libsql_bindgen::*; 2 | 3 | #[libsql_bindgen::libsql_bindgen] 4 | pub fn concat(s1: String, s2: String) -> String { 5 | let mut ret = s1.clone(); 6 | ret += &s2; 7 | ret 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /examples/src/concat3.rs: -------------------------------------------------------------------------------- 1 | use libsql_bindgen::*; 2 | 3 | #[libsql_bindgen::libsql_bindgen] 4 | pub fn concat3(s1: String, s2: String, s3: String) -> String { 5 | let mut ret = s1.clone(); 6 | ret += &s2; 7 | ret += &s3; 8 | ret 9 | } 10 | 11 | fn main() {} 12 | -------------------------------------------------------------------------------- /examples/src/contains.rs: -------------------------------------------------------------------------------- 1 | use libsql_bindgen::*; 2 | 3 | #[libsql_bindgen::libsql_bindgen] 4 | pub fn contains(s1: String, s2: String) -> bool { 5 | s1.contains(&s2) 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /examples/src/reverse_blob.rs: -------------------------------------------------------------------------------- 1 | use libsql_bindgen::*; 2 | 3 | #[libsql_bindgen::libsql_bindgen] 4 | pub fn reverse_blob(blob: &mut [u8]) -> Vec { 5 | let mut r = blob.to_vec(); 6 | r.reverse(); 7 | r 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /examples/wasmedge/README.md: -------------------------------------------------------------------------------- 1 | # WasmEdge examples 2 | 3 | [WasmEdge](https://github.com/WasmEdge/WasmEdge) is a high-performance WebAssembly (Wasm) runtime [optimized for cloud-native applications](https://wasmedge.org/docs/develop/wasmedge/features). When [embedded in libsql](https://wasmedge.org/docs/embed/use-case/libsql#prerequisites), it can support complex Wasm UDF functions with advanced features. In this folder, we will give two examples that leverage WasmEdge-specific features. 4 | 5 | The [Get exchange rate](get_exchange_rate/) example shows how a libsql UDF can make external web service calls. In this case, it makes an HTTPS request to get the latest exchange rate between any two pair of currencies. You can then use the exchange rate in your queries. 6 | 7 | The [WASI NN](wasi_nn_udf/) example shows how a libsql UDF can read a binary image stored in a blob fields, and then use a PyTorch model, stored inside the UDF, to classify the content in the image. The UDF outputs the text label of the image classification. 8 | -------------------------------------------------------------------------------- /examples/wasmedge/get_exchange_rate/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-wasi" 3 | rustflags = ["-C", "target-feature=+bulk-memory"] 4 | 5 | [target.wasm32-wasi] 6 | runner = "wasmedge" 7 | -------------------------------------------------------------------------------- /examples/wasmedge/get_exchange_rate/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "get_exchange_rate" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | http_req_wasi = { version = "0.10", git = "https://github.com/second-state/http_req.git", branch = "rustls" } 10 | libsql_bindgen = { version = "0", path = "../../../bindgen" } 11 | serde = { version = "1", features = ["derive"] } 12 | serde_json = "1" 13 | -------------------------------------------------------------------------------- /examples/wasmedge/get_exchange_rate/README.md: -------------------------------------------------------------------------------- 1 | ## How to use: 2 | 3 | [Install WasmEdge](https://wasmedge.org/book/en/quick_start/install.html) and then install the HTTPS plugin as follows. 4 | 5 | ```bash 6 | # Download and extract the plugin 7 | wget "https://github.com/second-state/wasmedge_rustls_plugin/releases/download/0.1.0/WasmEdge-plugin-wasmedge_rustls-0.1.0-alpha-ubuntu20.04_x86_64.tar" 8 | 9 | tar -xf "WasmEdge-plugin-wasmedge_rustls-0.1.0-alpha-ubuntu20.04_x86_64.tar" 10 | 11 | # Install the plugin if your wasmedge is installed in ~/.wasmedge 12 | cp libwasmedge_rustls.so ~/.wasmedge/plugin/ 13 | 14 | # Install the plugin if your wasmedge is installed in /usr/local 15 | cp libwasmedge_rustls.so /usr/local/lib/wasmedge/ 16 | ``` 17 | 18 | Make sure that you [build libsql with WasmEdge support](https://wasmedge.org/docs/embed/use-case/libsql#prerequisites). 19 | 20 | ```bash 21 | git clone https://github.com/libsql/libsql 22 | cd libsql 23 | ./configure --enable-wasm-runtime-wasmedge 24 | make 25 | ``` 26 | 27 | Build the [GET EXCHANGE RATE](src/main.rs) example. 28 | 29 | ```bash 30 | cargo wasi build --release 31 | ``` 32 | 33 | Create sql file for libsql and run libsql 34 | ```bash 35 | ./gen_libsql_udf.sh 36 | 37 | libsql 38 | ``` 39 | 40 | Execute in libsql 41 | ```sql 42 | > .init_wasm_func_table 43 | > .read create_get_exchange_rate_udf.sql 44 | > select get_exchange_rate('USD','CNY'); 45 | ``` 46 | -------------------------------------------------------------------------------- /examples/wasmedge/get_exchange_rate/gen_libsql_udf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | FUNC_NAME='get_exchange_rate' 3 | echo "DROP FUNCTION IF EXISTS ${FUNC_NAME};" > create_${FUNC_NAME}_udf.sql 4 | echo -n "CREATE FUNCTION ${FUNC_NAME} LANGUAGE wasm AS X'" >> create_${FUNC_NAME}_udf.sql 5 | xxd -p ../../../target/wasm32-wasi/release/get_exchange_rate.wasm | tr -d "\n" >> create_${FUNC_NAME}_udf.sql 6 | echo "';" >> create_${FUNC_NAME}_udf.sql -------------------------------------------------------------------------------- /examples/wasmedge/get_exchange_rate/src/main.rs: -------------------------------------------------------------------------------- 1 | use libsql_bindgen::*; 2 | // use serde 3 | fn main() {} 4 | 5 | #[libsql_bindgen::libsql_bindgen] 6 | pub fn get_exchange_rate(from: String, to: String) -> f32 { 7 | let rate = get_online_exchange_rate(&from, &to) 8 | .map(|api| api.data.rate) 9 | .unwrap_or(0.0); 10 | rate 11 | } 12 | 13 | #[derive(serde::Deserialize)] 14 | struct APIResult { 15 | #[serde(default)] 16 | data: Rate, 17 | } 18 | #[derive(serde::Deserialize, Default)] 19 | struct Rate { 20 | rate: f32, 21 | } 22 | 23 | fn get_online_exchange_rate(from: &str, to: &str) -> Option { 24 | let mut writer = Vec::new(); 25 | let uri = format!( 26 | "https://api.it120.cc/gooking/forex/rate?fromCode={}&toCode={}", 27 | to, from, 28 | ); 29 | let _ = http_req::request::get(uri, &mut writer).ok()?; 30 | let r = serde_json::from_slice::(&writer).ok()?; 31 | Some(r) 32 | } 33 | -------------------------------------------------------------------------------- /examples/wasmedge/wasi_nn_udf/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-wasi" 3 | rustflags = ["-C", "target-feature=+bulk-memory"] 4 | -------------------------------------------------------------------------------- /examples/wasmedge/wasi_nn_udf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasi_nn_udf" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | libsql_bindgen = { version = "0", path = "../../../bindgen" } 10 | image = { version = "0.24", default-features = false, features = [ 11 | "jpeg", 12 | "png", 13 | ] } 14 | wasi-nn = { version = "0.2.1" } 15 | -------------------------------------------------------------------------------- /examples/wasmedge/wasi_nn_udf/README.md: -------------------------------------------------------------------------------- 1 | ## How to use: 2 | 3 | [Install WasmEdge](https://wasmedge.org/book/en/quick_start/install.html) and then install the WasiNN plugin as follows. 4 | 5 | ```bash 6 | # Install PyTorch 7 | PYTORCH_VERSION="1.8.2" 8 | curl -s -L -O --remote-name-all "https://download.pytorch.org/libtorch/lts/1.8/cpu/libtorch-cxx11-abi-shared-with-deps-${PYTORCH_VERSION}%2Bcpu.zip" 9 | unzip -q "libtorch-cxx11-abi-shared-with-deps-${PYTORCH_VERSION}%2Bcpu.zip" 10 | rm -f "libtorch-cxx11-abi-shared-with-deps-${PYTORCH_VERSION}%2Bcpu.zip" 11 | export LD_LIBRARY_PATH=$(pwd)/libtorch/lib:${LD_LIBRARY_PATH} 12 | export Torch_DIR=$(pwd)/libtorch 13 | 14 | # Download and extract the plugin 15 | wget "https://github.com/WasmEdge/WasmEdge/releases/download/0.12.0-alpha.2/WasmEdge-plugin-wasi_nn-pytorch-0.12.0-alpha.2-ubuntu20.04_x86_64.tar.gz" 16 | 17 | tar -xzf "WasmEdge-plugin-wasi_nn-pytorch-0.12.0-alpha.2-ubuntu20.04_x86_64.tar.gz" 18 | 19 | # Install the plugin if your wasmedge is installed in ~/.wasmedge 20 | cp libwasmedgePluginWasiNN.so ~/.wasmedge/plugin/ 21 | 22 | # Install the plugin if your wasmedge is installed in /usr/local 23 | cp libwasmedgePluginWasiNN.so /usr/local/lib/wasmedge/ 24 | ``` 25 | 26 | Make sure that you [build libsql with WasmEdge support](https://wasmedge.org/docs/embed/use-case/libsql#prerequisites). 27 | 28 | ```bash 29 | git clone https://github.com/libsql/libsql 30 | cd libsql 31 | ./configure --enable-wasm-runtime-wasmedge 32 | make 33 | ``` 34 | 35 | Build the [WASI NN UDF](src/main.rs) example. 36 | 37 | ```bash 38 | cargo wasi build --release 39 | ``` 40 | 41 | Create test.sql file for libsql and run libsql 42 | ```bash 43 | ./gen_demo_sql.sh 44 | 45 | libsql 46 | ``` 47 | 48 | Execute in libsql 49 | ```sql 50 | > .read test.sql 51 | ``` 52 | 53 | ### Note 54 | 55 | The [gen_demo_sql.sh](gen_demo_sql.sh) script 56 | 57 | * first converts the image file to a tensor file. This is not strictly necessary as the UDF itself can perform this conversion. 58 | * then calls [gen_insert_image_sql.sh](gen_insert_image_sql.sh) to create a SQL file that inserts the tensor file into a database table as a blob. 59 | * then creates a database table with the above mentioned blob field, and calls the generated SQL file to insert the blob. 60 | * then calls [gen_libsql_udf.sh](gen_libsql_udf.sh) to create the UDF. The PyTorch model is embedded in the UDF. 61 | * finally, uses the UDF to classify the blob in a SQL query. 62 | -------------------------------------------------------------------------------- /examples/wasmedge/wasi_nn_udf/gen_demo_sql.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # AOT wasm 4 | wasmedgec ../../../target/wasm32-wasi/release/wasi_nn_udf.wasm wasi_nn_udf.aot.wasm 5 | 6 | # create libsql udf 7 | ./gen_libsql_udf.sh ../../../target/wasm32-wasi/release/wasi_nn_udf.wasm 8 | 9 | # image to tensor 10 | wasmedge --dir=.:. wasi_nn_udf.aot.wasm ./input.jpg ./input.tensor 11 | 12 | # create insert sql script 13 | ./gen_insert_image_sql.sh images img_blob input.tensor insert_data.sql 14 | 15 | # create test sql script 16 | SQL_NAME=${1:-"test.sql"} 17 | echo ".init_wasm_func_table" > $SQL_NAME 18 | echo ".read $PWD/create_classify_udf.sql" >> $SQL_NAME 19 | echo "CREATE TABLE images (id INTEGER PRIMARY KEY AUTOINCREMENT,img_blob BLOB);" >> $SQL_NAME 20 | echo ".read $PWD/insert_data.sql" >> $SQL_NAME 21 | echo "select classify(img_blob) from images where id = 1;" >> $SQL_NAME -------------------------------------------------------------------------------- /examples/wasmedge/wasi_nn_udf/gen_insert_image_sql.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # example: ./gen_insert_image_sql.sh images img_blob input.jpg insert_data.sql 3 | 4 | echo -n "INSERT INTO $1 ($2) VALUES(X'" >> $4 5 | xxd -p $3 | tr -d "\n" >> $4 6 | echo "');" >> $4 -------------------------------------------------------------------------------- /examples/wasmedge/wasi_nn_udf/gen_libsql_udf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | FUNC_NAME='classify' 3 | echo "DROP FUNCTION IF EXISTS ${FUNC_NAME};" > create_${FUNC_NAME}_udf.sql 4 | echo -n "CREATE FUNCTION ${FUNC_NAME} LANGUAGE wasm AS X'" >> create_${FUNC_NAME}_udf.sql 5 | xxd -p $1 | tr -d "\n" >> create_${FUNC_NAME}_udf.sql 6 | echo "';" >> create_${FUNC_NAME}_udf.sql -------------------------------------------------------------------------------- /examples/wasmedge/wasi_nn_udf/input.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libsql/libsql_bindgen/1131ddfc56a48dc897d1e8eb49d5657e1bc00744/examples/wasmedge/wasi_nn_udf/input.jpg -------------------------------------------------------------------------------- /examples/wasmedge/wasi_nn_udf/mobilenet.pt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libsql/libsql_bindgen/1131ddfc56a48dc897d1e8eb49d5657e1bc00744/examples/wasmedge/wasi_nn_udf/mobilenet.pt -------------------------------------------------------------------------------- /examples/wasmedge/wasi_nn_udf/src/imagenet_classes.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2019 Google LLC. All Rights Reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * ============================================================================= 16 | */ 17 | 18 | /* The code in this file is adapted from https://github.com/tensorflow/tfjs-models/blob/master/mobilenet/src/imagenet_classes.ts */ 19 | 20 | pub const IMAGENET_CLASSES: [&str; 1000] = [ 21 | "tench, Tinca tinca", 22 | "goldfish, Carassius auratus", 23 | "great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias", 24 | "tiger shark, Galeocerdo cuvieri", 25 | "hammerhead, hammerhead shark", 26 | "electric ray, crampfish, numbfish, torpedo", 27 | "stingray", 28 | "cock", 29 | "hen", 30 | "ostrich, Struthio camelus", 31 | "brambling, Fringilla montifringilla", 32 | "goldfinch, Carduelis carduelis", 33 | "house finch, linnet, Carpodacus mexicanus", 34 | "junco, snowbird", 35 | "indigo bunting, indigo finch, indigo bird, Passerina cyanea", 36 | "robin, American robin, Turdus migratorius", 37 | "bulbul", 38 | "jay", 39 | "magpie", 40 | "chickadee", 41 | "water ouzel, dipper", 42 | "kite", 43 | "bald eagle, American eagle, Haliaeetus leucocephalus", 44 | "vulture", 45 | "great grey owl, great gray owl, Strix nebulosa", 46 | "European fire salamander, Salamandra salamandra", 47 | "common newt, Triturus vulgaris", 48 | "eft", 49 | "spotted salamander, Ambystoma maculatum", 50 | "axolotl, mud puppy, Ambystoma mexicanum", 51 | "bullfrog, Rana catesbeiana", 52 | "tree frog, tree-frog", 53 | "tailed frog, bell toad, ribbed toad, tailed toad, Ascaphus trui", 54 | "loggerhead, loggerhead turtle, Caretta caretta", 55 | "leatherback turtle, leatherback, leathery turtle, Dermochelys coriacea", 56 | "mud turtle", 57 | "terrapin", 58 | "box turtle, box tortoise", 59 | "banded gecko", 60 | "common iguana, iguana, Iguana iguana", 61 | "American chameleon, anole, Anolis carolinensis", 62 | "whiptail, whiptail lizard", 63 | "agama", 64 | "frilled lizard, Chlamydosaurus kingi", 65 | "alligator lizard", 66 | "Gila monster, Heloderma suspectum", 67 | "green lizard, Lacerta viridis", 68 | "African chameleon, Chamaeleo chamaeleon", 69 | "Komodo dragon, Komodo lizard, dragon lizard, giant lizard, Varanus komodoensis", 70 | "African crocodile, Nile crocodile, Crocodylus niloticus", 71 | "American alligator, Alligator mississipiensis", 72 | "triceratops", 73 | "thunder snake, worm snake, Carphophis amoenus", 74 | "ringneck snake, ring-necked snake, ring snake", 75 | "hognose snake, puff adder, sand viper", 76 | "green snake, grass snake", 77 | "king snake, kingsnake", 78 | "garter snake, grass snake", 79 | "water snake", 80 | "vine snake", 81 | "night snake, Hypsiglena torquata", 82 | "boa constrictor, Constrictor constrictor", 83 | "rock python, rock snake, Python sebae", 84 | "Indian cobra, Naja naja", 85 | "green mamba", 86 | "sea snake", 87 | "horned viper, cerastes, sand viper, horned asp, Cerastes cornutus", 88 | "diamondback, diamondback rattlesnake, Crotalus adamanteus", 89 | "sidewinder, horned rattlesnake, Crotalus cerastes", 90 | "trilobite", 91 | "harvestman, daddy longlegs, Phalangium opilio", 92 | "scorpion", 93 | "black and gold garden spider, Argiope aurantia", 94 | "barn spider, Araneus cavaticus", 95 | "garden spider, Aranea diademata", 96 | "black widow, Latrodectus mactans", 97 | "tarantula", 98 | "wolf spider, hunting spider", 99 | "tick", 100 | "centipede", 101 | "black grouse", 102 | "ptarmigan", 103 | "ruffed grouse, partridge, Bonasa umbellus", 104 | "prairie chicken, prairie grouse, prairie fowl", 105 | "peacock", 106 | "quail", 107 | "partridge", 108 | "African grey, African gray, Psittacus erithacus", 109 | "macaw", 110 | "sulphur-crested cockatoo, Kakatoe galerita, Cacatua galerita", 111 | "lorikeet", 112 | "coucal", 113 | "bee eater", 114 | "hornbill", 115 | "hummingbird", 116 | "jacamar", 117 | "toucan", 118 | "drake", 119 | "red-breasted merganser, Mergus serrator", 120 | "goose", 121 | "black swan, Cygnus atratus", 122 | "tusker", 123 | "echidna, spiny anteater, anteater", 124 | "platypus, duckbill, duckbilled platypus, duck-billed platypus, Ornithorhynchus anatinus", 125 | "wallaby, brush kangaroo", 126 | "koala, koala bear, kangaroo bear, native bear, Phascolarctos cinereus", 127 | "wombat", 128 | "jelly fish", 129 | "sea anemone, anemone", 130 | "brain coral", 131 | "flatworm, platyhelminth", 132 | "nematode, nematode worm, roundworm", 133 | "conch", 134 | "snail", 135 | "slug", 136 | "sea slug, nudibranch", 137 | "chiton, coat-of-mail shell, sea cradle, polyplacophore", 138 | "chambered nautilus, pearly nautilus, nautilus", 139 | "Dungeness crab, Cancer magister", 140 | "rock crab, Cancer irroratus", 141 | "fiddler crab", 142 | "king crab, Alaska crab, Alaskan king crab, Alaska king crab, Paralithodes camtschatica", 143 | "American lobster, Northern lobster, Maine lobster, Homarus americanus", 144 | "spiny lobster, langouste, rock lobster, crawfish, crayfish, sea crawfish", 145 | "crayfish, crawfish, crawdad, crawdaddy", 146 | "hermit crab", 147 | "isopod", 148 | "white stork, Ciconia ciconia", 149 | "black stork, Ciconia nigra", 150 | "spoonbill", 151 | "flamingo", 152 | "little blue heron, Egretta caerulea", 153 | "American egret, great white heron, Egretta albus", 154 | "bittern", 155 | "crane", 156 | "limpkin, Aramus pictus", 157 | "European gallinule, Porphyrio porphyrio", 158 | "American coot, marsh hen, mud hen, water hen, Fulica americana", 159 | "bustard", 160 | "ruddy turnstone, Arenaria interpres", 161 | "red-backed sandpiper, dunlin, Erolia alpina", 162 | "redshank, Tringa totanus", 163 | "dowitcher", 164 | "oystercatcher, oyster catcher", 165 | "pelican", 166 | "king penguin, Aptenodytes patagonica", 167 | "albatross, mollymawk", 168 | "grey whale, gray whale, devilfish, Eschrichtius gibbosus, Eschrichtius robustus", 169 | "killer whale, killer, orca, grampus, sea wolf, Orcinus orca", 170 | "dugong, Dugong dugon", 171 | "sea lion", 172 | "Chihuahua", 173 | "Japanese spaniel", 174 | "Maltese dog, Maltese terrier, Maltese", 175 | "Pekinese, Pekingese, Peke", 176 | "Shih-Tzu", 177 | "Blenheim spaniel", 178 | "papillon", 179 | "toy terrier", 180 | "Rhodesian ridgeback", 181 | "Afghan hound, Afghan", 182 | "basset, basset hound", 183 | "beagle", 184 | "bloodhound, sleuthhound", 185 | "bluetick", 186 | "black-and-tan coonhound", 187 | "Walker hound, Walker foxhound", 188 | "English foxhound", 189 | "redbone", 190 | "borzoi, Russian wolfhound", 191 | "Irish wolfhound", 192 | "Italian greyhound", 193 | "whippet", 194 | "Ibizan hound, Ibizan Podenco", 195 | "Norwegian elkhound, elkhound", 196 | "otterhound, otter hound", 197 | "Saluki, gazelle hound", 198 | "Scottish deerhound, deerhound", 199 | "Weimaraner", 200 | "Staffordshire bullterrier, Staffordshire bull terrier", 201 | "American Staffordshire terrier, Staffordshire terrier, American pit bull terrier, pit bull terrier", 202 | "Bedlington terrier", 203 | "Border terrier", 204 | "Kerry blue terrier", 205 | "Irish terrier", 206 | "Norfolk terrier", 207 | "Norwich terrier", 208 | "Yorkshire terrier", 209 | "wire-haired fox terrier", 210 | "Lakeland terrier", 211 | "Sealyham terrier, Sealyham", 212 | "Airedale, Airedale terrier", 213 | "cairn, cairn terrier", 214 | "Australian terrier", 215 | "Dandie Dinmont, Dandie Dinmont terrier", 216 | "Boston bull, Boston terrier", 217 | "miniature schnauzer", 218 | "giant schnauzer", 219 | "standard schnauzer", 220 | "Scotch terrier, Scottish terrier, Scottie", 221 | "Tibetan terrier, chrysanthemum dog", 222 | "silky terrier, Sydney silky", 223 | "soft-coated wheaten terrier", 224 | "West Highland white terrier", 225 | "Lhasa, Lhasa apso", 226 | "flat-coated retriever", 227 | "curly-coated retriever", 228 | "golden retriever", 229 | "Labrador retriever", 230 | "Chesapeake Bay retriever", 231 | "German short-haired pointer", 232 | "vizsla, Hungarian pointer", 233 | "English setter", 234 | "Irish setter, red setter", 235 | "Gordon setter", 236 | "Brittany spaniel", 237 | "clumber, clumber spaniel", 238 | "English springer, English springer spaniel", 239 | "Welsh springer spaniel", 240 | "cocker spaniel, English cocker spaniel, cocker", 241 | "Sussex spaniel", 242 | "Irish water spaniel", 243 | "kuvasz", 244 | "schipperke", 245 | "groenendael", 246 | "malinois", 247 | "briard", 248 | "kelpie", 249 | "komondor", 250 | "Old English sheepdog, bobtail", 251 | "Shetland sheepdog, Shetland sheep dog, Shetland", 252 | "collie", 253 | "Border collie", 254 | "Bouvier des Flandres, Bouviers des Flandres", 255 | "Rottweiler", 256 | "German shepherd, German shepherd dog, German police dog, alsatian", 257 | "Doberman, Doberman pinscher", 258 | "miniature pinscher", 259 | "Greater Swiss Mountain dog", 260 | "Bernese mountain dog", 261 | "Appenzeller", 262 | "EntleBucher", 263 | "boxer", 264 | "bull mastiff", 265 | "Tibetan mastiff", 266 | "French bulldog", 267 | "Great Dane", 268 | "Saint Bernard, St Bernard", 269 | "Eskimo dog, husky", 270 | "malamute, malemute, Alaskan malamute", 271 | "Siberian husky", 272 | "dalmatian, coach dog, carriage dog", 273 | "affenpinscher, monkey pinscher, monkey dog", 274 | "basenji", 275 | "pug, pug-dog", 276 | "Leonberg", 277 | "Newfoundland, Newfoundland dog", 278 | "Great Pyrenees", 279 | "Samoyed, Samoyede", 280 | "Pomeranian", 281 | "chow, chow chow", 282 | "keeshond", 283 | "Brabancon griffon", 284 | "Pembroke, Pembroke Welsh corgi", 285 | "Cardigan, Cardigan Welsh corgi", 286 | "toy poodle", 287 | "miniature poodle", 288 | "standard poodle", 289 | "Mexican hairless", 290 | "timber wolf, grey wolf, gray wolf, Canis lupus", 291 | "white wolf, Arctic wolf, Canis lupus tundrarum", 292 | "red wolf, maned wolf, Canis rufus, Canis niger", 293 | "coyote, prairie wolf, brush wolf, Canis latrans", 294 | "dingo, warrigal, warragal, Canis dingo", 295 | "dhole, Cuon alpinus", 296 | "African hunting dog, hyena dog, Cape hunting dog, Lycaon pictus", 297 | "hyena, hyaena", 298 | "red fox, Vulpes vulpes", 299 | "kit fox, Vulpes macrotis", 300 | "Arctic fox, white fox, Alopex lagopus", 301 | "grey fox, gray fox, Urocyon cinereoargenteus", 302 | "tabby, tabby cat", 303 | "tiger cat", 304 | "Persian cat", 305 | "Siamese cat, Siamese", 306 | "Egyptian cat", 307 | "cougar, puma, catamount, mountain lion, painter, panther, Felis concolor", 308 | "lynx, catamount", 309 | "leopard, Panthera pardus", 310 | "snow leopard, ounce, Panthera uncia", 311 | "jaguar, panther, Panthera onca, Felis onca", 312 | "lion, king of beasts, Panthera leo", 313 | "tiger, Panthera tigris", 314 | "cheetah, chetah, Acinonyx jubatus", 315 | "brown bear, bruin, Ursus arctos", 316 | "American black bear, black bear, Ursus americanus, Euarctos americanus", 317 | "ice bear, polar bear, Ursus Maritimus, Thalarctos maritimus", 318 | "sloth bear, Melursus ursinus, Ursus ursinus", 319 | "mongoose", 320 | "meerkat, mierkat", 321 | "tiger beetle", 322 | "ladybug, ladybeetle, lady beetle, ladybird, ladybird beetle", 323 | "ground beetle, carabid beetle", 324 | "long-horned beetle, longicorn, longicorn beetle", 325 | "leaf beetle, chrysomelid", 326 | "dung beetle", 327 | "rhinoceros beetle", 328 | "weevil", 329 | "fly", 330 | "bee", 331 | "ant, emmet, pismire", 332 | "grasshopper, hopper", 333 | "cricket", 334 | "walking stick, walkingstick, stick insect", 335 | "cockroach, roach", 336 | "mantis, mantid", 337 | "cicada, cicala", 338 | "leafhopper", 339 | "lacewing, lacewing fly", 340 | "dragonfly, darning needle, devil\"s darning needle, sewing needle, snake feeder, snake doctor, mosquito hawk, skeeter hawk", 341 | "damselfly", 342 | "admiral", 343 | "ringlet, ringlet butterfly", 344 | "monarch, monarch butterfly, milkweed butterfly, Danaus plexippus", 345 | "cabbage butterfly", 346 | "sulphur butterfly, sulfur butterfly", 347 | "lycaenid, lycaenid butterfly", 348 | "starfish, sea star", 349 | "sea urchin", 350 | "sea cucumber, holothurian", 351 | "wood rabbit, cottontail, cottontail rabbit", 352 | "hare", 353 | "Angora, Angora rabbit", 354 | "hamster", 355 | "porcupine, hedgehog", 356 | "fox squirrel, eastern fox squirrel, Sciurus niger", 357 | "marmot", 358 | "beaver", 359 | "guinea pig, Cavia cobaya", 360 | "sorrel", 361 | "zebra", 362 | "hog, pig, grunter, squealer, Sus scrofa", 363 | "wild boar, boar, Sus scrofa", 364 | "warthog", 365 | "hippopotamus, hippo, river horse, Hippopotamus amphibius", 366 | "ox", 367 | "water buffalo, water ox, Asiatic buffalo, Bubalus bubalis", 368 | "bison", 369 | "ram, tup", 370 | "bighorn, bighorn sheep, cimarron, Rocky Mountain bighorn, Rocky Mountain sheep, Ovis canadensis", 371 | "ibex, Capra ibex", 372 | "hartebeest", 373 | "impala, Aepyceros melampus", 374 | "gazelle", 375 | "Arabian camel, dromedary, Camelus dromedarius", 376 | "llama", 377 | "weasel", 378 | "mink", 379 | "polecat, fitch, foulmart, foumart, Mustela putorius", 380 | "black-footed ferret, ferret, Mustela nigripes", 381 | "otter", 382 | "skunk, polecat, wood pussy", 383 | "badger", 384 | "armadillo", 385 | "three-toed sloth, ai, Bradypus tridactylus", 386 | "orangutan, orang, orangutang, Pongo pygmaeus", 387 | "gorilla, Gorilla gorilla", 388 | "chimpanzee, chimp, Pan troglodytes", 389 | "gibbon, Hylobates lar", 390 | "siamang, Hylobates syndactylus, Symphalangus syndactylus", 391 | "guenon, guenon monkey", 392 | "patas, hussar monkey, Erythrocebus patas", 393 | "baboon", 394 | "macaque", 395 | "langur", 396 | "colobus, colobus monkey", 397 | "proboscis monkey, Nasalis larvatus", 398 | "marmoset", 399 | "capuchin, ringtail, Cebus capucinus", 400 | "howler monkey, howler", 401 | "titi, titi monkey", 402 | "spider monkey, Ateles geoffroyi", 403 | "squirrel monkey, Saimiri sciureus", 404 | "Madagascar cat, ring-tailed lemur, Lemur catta", 405 | "indri, indris, Indri indri, Indri brevicaudatus", 406 | "Indian elephant, Elephas maximus", 407 | "African elephant, Loxodonta africana", 408 | "lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens", 409 | "giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca", 410 | "barracouta, snoek", 411 | "eel", 412 | "coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch", 413 | "rock beauty, Holocanthus tricolor", 414 | "anemone fish", 415 | "sturgeon", 416 | "gar, garfish, garpike, billfish, Lepisosteus osseus", 417 | "lionfish", 418 | "puffer, pufferfish, blowfish, globefish", 419 | "abacus", 420 | "abaya", 421 | "academic gown, academic robe, judge\"s robe", 422 | "accordion, piano accordion, squeeze box", 423 | "acoustic guitar", 424 | "aircraft carrier, carrier, flattop, attack aircraft carrier", 425 | "airliner", 426 | "airship, dirigible", 427 | "altar", 428 | "ambulance", 429 | "amphibian, amphibious vehicle", 430 | "analog clock", 431 | "apiary, bee house", 432 | "apron", 433 | "ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin", 434 | "assault rifle, assault gun", 435 | "backpack, back pack, knapsack, packsack, rucksack, haversack", 436 | "bakery, bakeshop, bakehouse", 437 | "balance beam, beam", 438 | "balloon", 439 | "ballpoint, ballpoint pen, ballpen, Biro", 440 | "Band Aid", 441 | "banjo", 442 | "bannister, banister, balustrade, balusters, handrail", 443 | "barbell", 444 | "barber chair", 445 | "barbershop", 446 | "barn", 447 | "barometer", 448 | "barrel, cask", 449 | "barrow, garden cart, lawn cart, wheelbarrow", 450 | "baseball", 451 | "basketball", 452 | "bassinet", 453 | "bassoon", 454 | "bathing cap, swimming cap", 455 | "bath towel", 456 | "bathtub, bathing tub, bath, tub", 457 | "beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon", 458 | "beacon, lighthouse, beacon light, pharos", 459 | "beaker", 460 | "bearskin, busby, shako", 461 | "beer bottle", 462 | "beer glass", 463 | "bell cote, bell cot", 464 | "bib", 465 | "bicycle-built-for-two, tandem bicycle, tandem", 466 | "bikini, two-piece", 467 | "binder, ring-binder", 468 | "binoculars, field glasses, opera glasses", 469 | "birdhouse", 470 | "boathouse", 471 | "bobsled, bobsleigh, bob", 472 | "bolo tie, bolo, bola tie, bola", 473 | "bonnet, poke bonnet", 474 | "bookcase", 475 | "bookshop, bookstore, bookstall", 476 | "bottlecap", 477 | "bow", 478 | "bow tie, bow-tie, bowtie", 479 | "brass, memorial tablet, plaque", 480 | "brassiere, bra, bandeau", 481 | "breakwater, groin, groyne, mole, bulwark, seawall, jetty", 482 | "breastplate, aegis, egis", 483 | "broom", 484 | "bucket, pail", 485 | "buckle", 486 | "bulletproof vest", 487 | "bullet train, bullet", 488 | "butcher shop, meat market", 489 | "cab, hack, taxi, taxicab", 490 | "caldron, cauldron", 491 | "candle, taper, wax light", 492 | "cannon", 493 | "canoe", 494 | "can opener, tin opener", 495 | "cardigan", 496 | "car mirror", 497 | "carousel, carrousel, merry-go-round, roundabout, whirligig", 498 | "carpenter\"s kit, tool kit", 499 | "carton", 500 | "car wheel", 501 | "cash machine, cash dispenser, automated teller machine, automatic teller machine, automated teller, automatic teller, ATM", 502 | "cassette", 503 | "cassette player", 504 | "castle", 505 | "catamaran", 506 | "CD player", 507 | "cello, violoncello", 508 | "cellular telephone, cellular phone, cellphone, cell, mobile phone", 509 | "chain", 510 | "chainlink fence", 511 | "chain mail, ring mail, mail, chain armor, chain armour, ring armor, ring armour", 512 | "chain saw, chainsaw", 513 | "chest", 514 | "chiffonier, commode", 515 | "chime, bell, gong", 516 | "china cabinet, china closet", 517 | "Christmas stocking", 518 | "church, church building", 519 | "cinema, movie theater, movie theatre, movie house, picture palace", 520 | "cleaver, meat cleaver, chopper", 521 | "cliff dwelling", 522 | "cloak", 523 | "clog, geta, patten, sabot", 524 | "cocktail shaker", 525 | "coffee mug", 526 | "coffeepot", 527 | "coil, spiral, volute, whorl, helix", 528 | "combination lock", 529 | "computer keyboard, keypad", 530 | "confectionery, confectionary, candy store", 531 | "container ship, containership, container vessel", 532 | "convertible", 533 | "corkscrew, bottle screw", 534 | "cornet, horn, trumpet, trump", 535 | "cowboy boot", 536 | "cowboy hat, ten-gallon hat", 537 | "cradle", 538 | "crane", 539 | "crash helmet", 540 | "crate", 541 | "crib, cot", 542 | "Crock Pot", 543 | "croquet ball", 544 | "crutch", 545 | "cuirass", 546 | "dam, dike, dyke", 547 | "desk", 548 | "desktop computer", 549 | "dial telephone, dial phone", 550 | "diaper, nappy, napkin", 551 | "digital clock", 552 | "digital watch", 553 | "dining table, board", 554 | "dishrag, dishcloth", 555 | "dishwasher, dish washer, dishwashing machine", 556 | "disk brake, disc brake", 557 | "dock, dockage, docking facility", 558 | "dogsled, dog sled, dog sleigh", 559 | "dome", 560 | "doormat, welcome mat", 561 | "drilling platform, offshore rig", 562 | "drum, membranophone, tympan", 563 | "drumstick", 564 | "dumbbell", 565 | "Dutch oven", 566 | "electric fan, blower", 567 | "electric guitar", 568 | "electric locomotive", 569 | "entertainment center", 570 | "envelope", 571 | "espresso maker", 572 | "face powder", 573 | "feather boa, boa", 574 | "file, file cabinet, filing cabinet", 575 | "fireboat", 576 | "fire engine, fire truck", 577 | "fire screen, fireguard", 578 | "flagpole, flagstaff", 579 | "flute, transverse flute", 580 | "folding chair", 581 | "football helmet", 582 | "forklift", 583 | "fountain", 584 | "fountain pen", 585 | "four-poster", 586 | "freight car", 587 | "French horn, horn", 588 | "frying pan, frypan, skillet", 589 | "fur coat", 590 | "garbage truck, dustcart", 591 | "gasmask, respirator, gas helmet", 592 | "gas pump, gasoline pump, petrol pump, island dispenser", 593 | "goblet", 594 | "go-kart", 595 | "golf ball", 596 | "golfcart, golf cart", 597 | "gondola", 598 | "gong, tam-tam", 599 | "gown", 600 | "grand piano, grand", 601 | "greenhouse, nursery, glasshouse", 602 | "grille, radiator grille", 603 | "grocery store, grocery, food market, market", 604 | "guillotine", 605 | "hair slide", 606 | "hair spray", 607 | "half track", 608 | "hammer", 609 | "hamper", 610 | "hand blower, blow dryer, blow drier, hair dryer, hair drier", 611 | "hand-held computer, hand-held microcomputer", 612 | "handkerchief, hankie, hanky, hankey", 613 | "hard disc, hard disk, fixed disk", 614 | "harmonica, mouth organ, harp, mouth harp", 615 | "harp", 616 | "harvester, reaper", 617 | "hatchet", 618 | "holster", 619 | "home theater, home theatre", 620 | "honeycomb", 621 | "hook, claw", 622 | "hoopskirt, crinoline", 623 | "horizontal bar, high bar", 624 | "horse cart, horse-cart", 625 | "hourglass", 626 | "iPod", 627 | "iron, smoothing iron", 628 | "jack-o\"-lantern", 629 | "jean, blue jean, denim", 630 | "jeep, landrover", 631 | "jersey, T-shirt, tee shirt", 632 | "jigsaw puzzle", 633 | "jinrikisha, ricksha, rickshaw", 634 | "joystick", 635 | "kimono", 636 | "knee pad", 637 | "knot", 638 | "lab coat, laboratory coat", 639 | "ladle", 640 | "lampshade, lamp shade", 641 | "laptop, laptop computer", 642 | "lawn mower, mower", 643 | "lens cap, lens cover", 644 | "letter opener, paper knife, paperknife", 645 | "library", 646 | "lifeboat", 647 | "lighter, light, igniter, ignitor", 648 | "limousine, limo", 649 | "liner, ocean liner", 650 | "lipstick, lip rouge", 651 | "Loafer", 652 | "lotion", 653 | "loudspeaker, speaker, speaker unit, loudspeaker system, speaker system", 654 | "loupe, jeweler\"s loupe", 655 | "lumbermill, sawmill", 656 | "magnetic compass", 657 | "mailbag, postbag", 658 | "mailbox, letter box", 659 | "maillot", 660 | "maillot, tank suit", 661 | "manhole cover", 662 | "maraca", 663 | "marimba, xylophone", 664 | "mask", 665 | "matchstick", 666 | "maypole", 667 | "maze, labyrinth", 668 | "measuring cup", 669 | "medicine chest, medicine cabinet", 670 | "megalith, megalithic structure", 671 | "microphone, mike", 672 | "microwave, microwave oven", 673 | "military uniform", 674 | "milk can", 675 | "minibus", 676 | "miniskirt, mini", 677 | "minivan", 678 | "missile", 679 | "mitten", 680 | "mixing bowl", 681 | "mobile home, manufactured home", 682 | "Model T", 683 | "modem", 684 | "monastery", 685 | "monitor", 686 | "moped", 687 | "mortar", 688 | "mortarboard", 689 | "mosque", 690 | "mosquito net", 691 | "motor scooter, scooter", 692 | "mountain bike, all-terrain bike, off-roader", 693 | "mountain tent", 694 | "mouse, computer mouse", 695 | "mousetrap", 696 | "moving van", 697 | "muzzle", 698 | "nail", 699 | "neck brace", 700 | "necklace", 701 | "nipple", 702 | "notebook, notebook computer", 703 | "obelisk", 704 | "oboe, hautboy, hautbois", 705 | "ocarina, sweet potato", 706 | "odometer, hodometer, mileometer, milometer", 707 | "oil filter", 708 | "organ, pipe organ", 709 | "oscilloscope, scope, cathode-ray oscilloscope, CRO", 710 | "overskirt", 711 | "oxcart", 712 | "oxygen mask", 713 | "packet", 714 | "paddle, boat paddle", 715 | "paddlewheel, paddle wheel", 716 | "padlock", 717 | "paintbrush", 718 | "pajama, pyjama, pj\"s, jammies", 719 | "palace", 720 | "panpipe, pandean pipe, syrinx", 721 | "paper towel", 722 | "parachute, chute", 723 | "parallel bars, bars", 724 | "park bench", 725 | "parking meter", 726 | "passenger car, coach, carriage", 727 | "patio, terrace", 728 | "pay-phone, pay-station", 729 | "pedestal, plinth, footstall", 730 | "pencil box, pencil case", 731 | "pencil sharpener", 732 | "perfume, essence", 733 | "Petri dish", 734 | "photocopier", 735 | "pick, plectrum, plectron", 736 | "pickelhaube", 737 | "picket fence, paling", 738 | "pickup, pickup truck", 739 | "pier", 740 | "piggy bank, penny bank", 741 | "pill bottle", 742 | "pillow", 743 | "ping-pong ball", 744 | "pinwheel", 745 | "pirate, pirate ship", 746 | "pitcher, ewer", 747 | "plane, carpenter\"s plane, woodworking plane", 748 | "planetarium", 749 | "plastic bag", 750 | "plate rack", 751 | "plow, plough", 752 | "plunger, plumber\"s helper", 753 | "Polaroid camera, Polaroid Land camera", 754 | "pole", 755 | "police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria", 756 | "poncho", 757 | "pool table, billiard table, snooker table", 758 | "pop bottle, soda bottle", 759 | "pot, flowerpot", 760 | "potter\"s wheel", 761 | "power drill", 762 | "prayer rug, prayer mat", 763 | "printer", 764 | "prison, prison house", 765 | "projectile, missile", 766 | "projector", 767 | "puck, hockey puck", 768 | "punching bag, punch bag, punching ball, punchball", 769 | "purse", 770 | "quill, quill pen", 771 | "quilt, comforter, comfort, puff", 772 | "racer, race car, racing car", 773 | "racket, racquet", 774 | "radiator", 775 | "radio, wireless", 776 | "radio telescope, radio reflector", 777 | "rain barrel", 778 | "recreational vehicle, RV, R.V.", 779 | "reel", 780 | "reflex camera", 781 | "refrigerator, icebox", 782 | "remote control, remote", 783 | "restaurant, eating house, eating place, eatery", 784 | "revolver, six-gun, six-shooter", 785 | "rifle", 786 | "rocking chair, rocker", 787 | "rotisserie", 788 | "rubber eraser, rubber, pencil eraser", 789 | "rugby ball", 790 | "rule, ruler", 791 | "running shoe", 792 | "safe", 793 | "safety pin", 794 | "saltshaker, salt shaker", 795 | "sandal", 796 | "sarong", 797 | "sax, saxophone", 798 | "scabbard", 799 | "scale, weighing machine", 800 | "school bus", 801 | "schooner", 802 | "scoreboard", 803 | "screen, CRT screen", 804 | "screw", 805 | "screwdriver", 806 | "seat belt, seatbelt", 807 | "sewing machine", 808 | "shield, buckler", 809 | "shoe shop, shoe-shop, shoe store", 810 | "shoji", 811 | "shopping basket", 812 | "shopping cart", 813 | "shovel", 814 | "shower cap", 815 | "shower curtain", 816 | "ski", 817 | "ski mask", 818 | "sleeping bag", 819 | "slide rule, slipstick", 820 | "sliding door", 821 | "slot, one-armed bandit", 822 | "snorkel", 823 | "snowmobile", 824 | "snowplow, snowplough", 825 | "soap dispenser", 826 | "soccer ball", 827 | "sock", 828 | "solar dish, solar collector, solar furnace", 829 | "sombrero", 830 | "soup bowl", 831 | "space bar", 832 | "space heater", 833 | "space shuttle", 834 | "spatula", 835 | "speedboat", 836 | "spider web, spider\"s web", 837 | "spindle", 838 | "sports car, sport car", 839 | "spotlight, spot", 840 | "stage", 841 | "steam locomotive", 842 | "steel arch bridge", 843 | "steel drum", 844 | "stethoscope", 845 | "stole", 846 | "stone wall", 847 | "stopwatch, stop watch", 848 | "stove", 849 | "strainer", 850 | "streetcar, tram, tramcar, trolley, trolley car", 851 | "stretcher", 852 | "studio couch, day bed", 853 | "stupa, tope", 854 | "submarine, pigboat, sub, U-boat", 855 | "suit, suit of clothes", 856 | "sundial", 857 | "sunglass", 858 | "sunglasses, dark glasses, shades", 859 | "sunscreen, sunblock, sun blocker", 860 | "suspension bridge", 861 | "swab, swob, mop", 862 | "sweatshirt", 863 | "swimming trunks, bathing trunks", 864 | "swing", 865 | "switch, electric switch, electrical switch", 866 | "syringe", 867 | "table lamp", 868 | "tank, army tank, armored combat vehicle, armoured combat vehicle", 869 | "tape player", 870 | "teapot", 871 | "teddy, teddy bear", 872 | "television, television system", 873 | "tennis ball", 874 | "thatch, thatched roof", 875 | "theater curtain, theatre curtain", 876 | "thimble", 877 | "thresher, thrasher, threshing machine", 878 | "throne", 879 | "tile roof", 880 | "toaster", 881 | "tobacco shop, tobacconist shop, tobacconist", 882 | "toilet seat", 883 | "torch", 884 | "totem pole", 885 | "tow truck, tow car, wrecker", 886 | "toyshop", 887 | "tractor", 888 | "trailer truck, tractor trailer, trucking rig, rig, articulated lorry, semi", 889 | "tray", 890 | "trench coat", 891 | "tricycle, trike, velocipede", 892 | "trimaran", 893 | "tripod", 894 | "triumphal arch", 895 | "trolleybus, trolley coach, trackless trolley", 896 | "trombone", 897 | "tub, vat", 898 | "turnstile", 899 | "typewriter keyboard", 900 | "umbrella", 901 | "unicycle, monocycle", 902 | "upright, upright piano", 903 | "vacuum, vacuum cleaner", 904 | "vase", 905 | "vault", 906 | "velvet", 907 | "vending machine", 908 | "vestment", 909 | "viaduct", 910 | "violin, fiddle", 911 | "volleyball", 912 | "waffle iron", 913 | "wall clock", 914 | "wallet, billfold, notecase, pocketbook", 915 | "wardrobe, closet, press", 916 | "warplane, military plane", 917 | "washbasin, handbasin, washbowl, lavabo, wash-hand basin", 918 | "washer, automatic washer, washing machine", 919 | "water bottle", 920 | "water jug", 921 | "water tower", 922 | "whiskey jug", 923 | "whistle", 924 | "wig", 925 | "window screen", 926 | "window shade", 927 | "Windsor tie", 928 | "wine bottle", 929 | "wing", 930 | "wok", 931 | "wooden spoon", 932 | "wool, woolen, woollen", 933 | "worm fence, snake fence, snake-rail fence, Virginia fence", 934 | "wreck", 935 | "yawl", 936 | "yurt", 937 | "web site, website, internet site, site", 938 | "comic book", 939 | "crossword puzzle, crossword", 940 | "street sign", 941 | "traffic light, traffic signal, stoplight", 942 | "book jacket, dust cover, dust jacket, dust wrapper", 943 | "menu", 944 | "plate", 945 | "guacamole", 946 | "consomme", 947 | "hot pot, hotpot", 948 | "trifle", 949 | "ice cream, icecream", 950 | "ice lolly, lolly, lollipop, popsicle", 951 | "French loaf", 952 | "bagel, beigel", 953 | "pretzel", 954 | "cheeseburger", 955 | "hotdog, hot dog, red hot", 956 | "mashed potato", 957 | "head cabbage", 958 | "broccoli", 959 | "cauliflower", 960 | "zucchini, courgette", 961 | "spaghetti squash", 962 | "acorn squash", 963 | "butternut squash", 964 | "cucumber, cuke", 965 | "artichoke, globe artichoke", 966 | "bell pepper", 967 | "cardoon", 968 | "mushroom", 969 | "Granny Smith", 970 | "strawberry", 971 | "orange", 972 | "lemon", 973 | "fig", 974 | "pineapple, ananas", 975 | "banana", 976 | "jackfruit, jak, jack", 977 | "custard apple", 978 | "pomegranate", 979 | "hay", 980 | "carbonara", 981 | "chocolate sauce, chocolate syrup", 982 | "dough", 983 | "meat loaf, meatloaf", 984 | "pizza, pizza pie", 985 | "potpie", 986 | "burrito", 987 | "red wine", 988 | "espresso", 989 | "cup", 990 | "eggnog", 991 | "alp", 992 | "bubble", 993 | "cliff, drop, drop-off", 994 | "coral reef", 995 | "geyser", 996 | "lakeside, lakeshore", 997 | "promontory, headland, head, foreland", 998 | "sandbar, sand bar", 999 | "seashore, coast, seacoast, sea-coast", 1000 | "valley, vale", 1001 | "volcano", 1002 | "ballplayer, baseball player", 1003 | "groom, bridegroom", 1004 | "scuba diver", 1005 | "rapeseed", 1006 | "daisy", 1007 | "yellow lady\"s slipper, yellow lady-slipper, Cypripedium calceolus, Cypripedium parviflorum", 1008 | "corn", 1009 | "acorn", 1010 | "hip, rose hip, rosehip", 1011 | "buckeye, horse chestnut, conker", 1012 | "coral fungus", 1013 | "agaric", 1014 | "gyromitra", 1015 | "stinkhorn, carrion fungus", 1016 | "earthstar", 1017 | "hen-of-the-woods, hen of the woods, Polyporus frondosus, Grifola frondosa", 1018 | "bolete", 1019 | "ear, spike, capitulum", 1020 | "toilet tissue, toilet paper, bathroom tissue" 1021 | ]; 1022 | -------------------------------------------------------------------------------- /examples/wasmedge/wasi_nn_udf/src/main.rs: -------------------------------------------------------------------------------- 1 | use libsql_bindgen::*; 2 | use wasi_nn::NnErrno; 3 | mod imagenet_classes; 4 | 5 | fn main() { 6 | let mut args = std::env::args().into_iter(); 7 | let _ = args.next(); 8 | let input = args.next().unwrap(); 9 | let output = args.next().unwrap(); 10 | 11 | let image = std::fs::read(&input).unwrap(); 12 | let tensor_data = image_to_pytorch_tensor(&image, 224, 224).unwrap(); 13 | let tensor_ptr = tensor_data.as_ptr() as *const u8; 14 | let tensor_len = tensor_data.len() * std::mem::size_of::(); 15 | let tensor_data = unsafe { std::slice::from_raw_parts(tensor_ptr, tensor_len) }; 16 | std::fs::write(&output, tensor_data).unwrap(); 17 | } 18 | 19 | #[derive(Debug, PartialEq)] 20 | struct InferenceResult(usize, f32); 21 | 22 | #[libsql_bindgen::libsql_bindgen] 23 | fn classify(tensor: &mut [u8]) -> String { 24 | if tensor.len() != 602112 { 25 | return format!("err: tensor data length error"); 26 | } 27 | match pytorch_classify(&tensor) { 28 | Ok(s) => s, 29 | Err(e) => format!("err: {:?}", e), 30 | } 31 | } 32 | 33 | fn image_to_pytorch_tensor(buffer: &[u8], height: u32, width: u32) -> image::ImageResult> { 34 | let img = image::load_from_memory(&buffer)?.to_rgb32f(); 35 | let resized = 36 | image::imageops::resize(&img, height, width, ::image::imageops::FilterType::Triangle); 37 | 38 | let mut flat_img = resized.to_vec(); 39 | let flat_img_len = flat_img.len() / 3; 40 | 41 | for (i, rgb) in resized.pixels().enumerate() { 42 | flat_img[i] = (rgb.0[0] - 0.485) / 0.229; 43 | flat_img[i + flat_img_len] = (rgb.0[1] - 0.456) / 0.224; 44 | flat_img[i + flat_img_len * 2] = (rgb.0[2] - 0.406) / 0.225; 45 | } 46 | 47 | Ok(flat_img) 48 | } 49 | 50 | fn sort_results(buffer: &[f32]) -> Vec { 51 | let mut results: Vec = buffer 52 | .iter() 53 | .enumerate() 54 | .map(|(c, p)| InferenceResult(c, *p)) 55 | .collect(); 56 | results.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); 57 | results 58 | } 59 | 60 | fn pytorch_classify(tensor_data: &[u8]) -> Result { 61 | let weights = include_bytes!("../mobilenet.pt"); 62 | unsafe { 63 | let graph = wasi_nn::load( 64 | &[weights], 65 | wasi_nn::GRAPH_ENCODING_PYTORCH, 66 | wasi_nn::EXECUTION_TARGET_CPU, 67 | )?; 68 | 69 | let context = wasi_nn::init_execution_context(graph)?; 70 | let tensor = wasi_nn::Tensor { 71 | dimensions: &[1, 3, 224, 224], 72 | type_: wasi_nn::TENSOR_TYPE_F32, 73 | data: &tensor_data, 74 | }; 75 | 76 | wasi_nn::set_input(context, 0, tensor)?; 77 | wasi_nn::compute(context)?; 78 | let mut output_buffer = vec![0f32; 1000]; 79 | wasi_nn::get_output( 80 | context, 81 | 0, 82 | &mut output_buffer[..] as *mut [f32] as *mut u8, 83 | (output_buffer.len() * std::mem::size_of::()) as u32, 84 | )?; 85 | 86 | let results = sort_results(&output_buffer); 87 | Ok(imagenet_classes::IMAGENET_CLASSES[results[0].0].to_string()) 88 | } 89 | } 90 | --------------------------------------------------------------------------------