# DeviceAtlas Device API Interfacing from other languages # The DeviceAtlas APIs are using portable C features which makes it possible for easy integrations from within more modern languages. Indeed it open doors to new possibilities as these languages allow to develop web applications in the large sense, from websites to HTTP web servers while bring higher memory safety for comparable performances with C in general. ### How to ### Recent programming languages have various C types/features binding coverage, specific capacities like packed structs, bitfields, variadic arguments ... are not evenly supported among them ; however, the DeviceAtlas APIs' public interface is easy to use as easy to map. Depending on the language at hand, the manual mapping work is perfectly doable but it is, however, encouraged to use builtin tools as much as possible to eliminate subtle bugs while not being bound your code to specific APIs versions. For better results, the language ought to offer tight control in term of lifetime and memory management. #### Example - interfacing with Rust #### The DeviceAtlas APIs can be used in regard of the particular Rust's ownership model. As mentioned, it is perfectly feasible to map the APIs types and calls to Rust manually, there are easier and more reliable ways to be able to use those from within your code. The official Rust's bindgen crate can generate the proper FFI (Foreign Function Interface) automatically by adding it, as a build-dependency, to the relevant project's Cargo as follow: ```rust ... [build-dependencies] bindgen = "*" ... ``` Then completing the main related build.rs could be done this way: ```rust extern crate bindgen; fn main() { ... println!("cargo:rustc-link-lib=da"); let bind = bindgen::Builder::default() .header("<path/to/dac.h>").generate.expect("C header binding generation failure"); bind.write_to_file("src/deviceatlas.rs").expect("rust binding file generation failure"); ... } ``` In the end, from the Rust's code: ```rust use std::ffi::*; use std::ptr; include!("deviceatlas.rs"); ... extern "C" fn jsonread(ctx: *mut c_void, count: usize, buf: *mut c_char) -> usize { unsafe { fread( buf as *mut c_void, 1u64, count.try_into().unwrap(), ctx as *mut FILE, ) .try_into() .unwrap() } } extern "C" fn jsonseek(ctx: *mut c_void, off: off_t) -> da_status_t { unsafe { match fseek(ctx as *mut FILE, off, SEEK_SET as i32) { -1 => da_status_DA_SYS, _ => da_status_DA_OK, } } } ... /// For conveniency, it is possible to join the necessary atlas instance and its C'raw pointer /// in one droppable Rust type as follow pub struct deviceatlas { atlas: da_atlas_t, ptr: *mut c_void, } impl Drop for deviceatlas { fn drop(&mut self) { unsafe { da_atlas_close(&mut self.atlas); free(self.ptr); } } } ... let mut jsonpath = CString::new("/path/to/jsonfile").unwrap(); let mut fpmode = CString::new("r").unwrap(); let mut extra: [da_property_decl_t; 1] = mem::zeroed(); let mut refsz = mem::MaybeUninit::<usize>::uninit(); let mut dainst = deviceatlas { atlas: mem::zeroed(), ptr: mem::zeroed() }; let fp = fopen( jsonpath.as_ptr() as *const c_char, fpmode.as_ptr() as *const c_char, ); ... let mut status = da_atlas_compile( fp as *mut c_void, Some(jsonread), Some(jsonseek), &mut dainst.ptr, refsz.as_mut_ptr(), ); ... if status == da_status_DA_OK { status = da_atlas_open( &mut dainst.atlas, &mut extra as *mut da_property_decl_t, dainst.ptr, refsz.assume_init(), ); fclose(fp); ... let ev: Vec<da_evidence_t>; let mut refedv = mem::MaybeUninit::<da_deviceinfo_t>::uninit(); ev.push(da_evidence_t{ .key: da_atlas_header_evidence_id(&dainst.atlas, CString::new("user-agent").unwrap().as_ptr()), .value: CString::new("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.150").unwrap().as_ptr() }); ev.push(da_evidence_t{ .key: da_atlas_clientprop_evidence_id(&dainst.atlas), .value: CString::new("srendererRef:0756798863|sscreenWidthHeight:810/1080").unwrap().as_ptr() }); ... status = da_searchv(&mut dainst.atlas, refdev.as_mut_ptr(), ev.as_mut_ptr() as *mut da_evidence_t, ev.len()); ... let dev = refdev.assume_init(); ... for i in 0..dev.propcount { let mut refent = mem::MaybeUninit::<da_propent>::uninit(); let mut refval = mem::MaybeUninit::<usize>::uninit(); let prop = dev.proplist.add(i); match da_getpropval( &mut dev, *prop, refval.as_mut_ptr(), refent.as_mut_ptr(), ) { DA_OK => { let val = refval.assume_init(); let ent = refent.assume_init(); let nameptr = CStr::from_ptr(ent.name).to_str().unwrap(); print!("{}: ", nameptr); match ent.type_ { DA_TYPE_NUMBER | DA_TYPE_INTEGER | DA_TYPE_BOOLEAN => { println!("{}", val); } DA_TYPE_STRING => { let valptr = CStr::from_ptr(val as *const c_char).to_str().unwrap(); println!("{}", valptr); } _ => { eprintln!("not covered"); } } } _ => eprintln!("invalid id {}", *prop), } } ... da_close(&mut dev); } ``` Also, the APIs had been tested with alternative allocator crates such as jemalloc. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _ Copyright (c) DeviceAtlas Limited 2024. All Rights Reserved. _ _ https://deviceatlas.com _ <!-- HTML+JS for document formatting when opened in browser --> <div class="btn-group" id="main-menu" style="float:right"><a class="btn dropdown-toggle" data-toggle="dropdown" href="#">Menu<span class="caret"></span></a><ul class="dropdown-menu"><li><a href="README.html">Main</a></li><li><a href="README.DeviceApi.html">Device API Usage C</a></li><li><a href="README.ClientHints.html">Client Hints Support</a></li><li><a href="README.CarrierApi.html">Carrier Identification API</a></li><li><a href="README.Upgrade.html">Device API Upgrade</a></li><li><a href="README.Cpp.html">Device API C++</a></li><li><a href="README.Nginx.html">NGINX Module</a></li><li><a href="README.Apache2.html">Apache2 Module</a></li><li><a href="README.JsonConverter.html">Device Identification API C JSONConverter</a></li><li><a href="README.Scheduler.html">Device Identification API C file monitor and scheduler</a></li><li><a href="README.Go-DeviceApi.html">Device API Usage Go</a></li><li><a href="README.Go-Upgrade.html">Device API Upgrade Go</a></li><li><a href="README.Gpu.html">Device API and GPU/CPU interaction</a></li><li class="disabled"><a href="README.Interfacing.html">Device API Interfacing from other languages</a></li><li class="divider"></li><li><a href="./ApiDocs/index.html">C ApiDocs</a></li><li><a href="./ApiCppDocs/index.html">C ++ interface ApiDocs</a></li><li><a href="./Go-ApiDocs/carrier.html">Go ApiDocs Carrier</a></li><li><a href="./Go-ApiDocs/device.html">Go ApiDocs Device</a></li></ul></div>