Skip to content

Instantly share code, notes, and snippets.

@ChunMinChang
Created October 2, 2018 16:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ChunMinChang/22a30f214c97609d72f17d80740b8506 to your computer and use it in GitHub Desktop.
Save ChunMinChang/22a30f214c97609d72f17d80740b8506 to your computer and use it in GitHub Desktop.
C, C++, Rust Examples to call C-compatible Query APIs

C, C++, Rust Examples to call C Query APIs

Basic implmentation for Query APIs

see here

With String Handle

  • String handle: string_ref.h, string_ref.c
  • Query APIs: device.h, device.c, device.cpp
  • Sample code
    • C: sample.c
    • C++: sample.cpp
    • Rust: sample.rs, ext.rs, sys.rs
// Makefile

# Sample in C:
# gcc -shared -fPIC string_ref.c -o libstringref.so
# gcc -shared -fPIC device.c libstringref.so -o libdevice.so
# gcc sample.c libdevice.so libstringref.so -o sample-c
# ./sample-c

# Sample in C++:
g++ -shared -fPIC string_ref.c -o libstringref.so
g++ -std=c++11 -shared -fPIC device.cpp libstringref.so -o libdevice.so
g++ -std=c++11 sample.cpp libdevice.so libstringref.so -o sample-cpp
./sample-cpp

# Samples in Rust:
rustc sample.rs -L.
LD_LIBRARY_PATH=. RUST_BACKTRACE=1 ./sample

Static library

# Sample in C (static library):
gcc -c string_ref.c -o string_ref.o
ar rcs libstringref.a string_ref.o
gcc -c device.c -o device.o
ar rcs libdevice.a device.o
gcc sample.c -L. -ldevice -lstringref -o sample-c
./sample-c

# Samples in Rust:
rustc sample.rs -L.
LD_LIBRARY_PATH=. RUST_BACKTRACE=1 ./sample

Wrapping String Hanlde Class String as a String Class

  • String handle: string_ref.h, string_ref.c
  • Query APIs: device.h, device_w_string.cpp
  • Sample code
    • C++: sample_w_string.cpp
    • Rust: sample_w_string.rs, ext.rs, sys.rs
// Makefile

# Sample in C++ with string wrapper:
g++ -shared -fPIC string_ref.c -o libstringref.so
g++ -std=c++11 -shared -fPIC device_w_string.cpp libstringref.so -o libdevice.so
g++ -std=c++11 sample_w_string.cpp libdevice.so libstringref.so -o sample-cpp
./sample-cpp

# Samples in Rust:
rustc sample_w_string.rs -L. -o sample
LD_LIBRARY_PATH=. RUST_BACKTRACE=1 ./sample

Apply a Byte-size Trick to Query Data

  • String handle: string_ref.h, string_ref.c
  • Query APIs: device.h, device_w_string.cpp
  • Sample code
    • C++: sample_w_size_trick.cpp
    • Rust: sample_w_size_trick.rs, ext.rs, sys.rs
// Makefile

# Sample in C++ with byte size trick:
g++ -shared -fPIC string_ref.c -o libstringref.so
g++ -std=c++11 -shared -fPIC device_w_string.cpp libstringref.so -o libdevice.so
g++ -std=c++11 sample_w_size_trick.cpp libdevice.so libstringref.so -o sample-cpp
./sample-cpp

# Samples in Rust:
rustc sample_w_size_trick.rs -L. -o sample # with byte size trick
LD_LIBRARY_PATH=. RUST_BACKTRACE=1 ./sample
#include "device.h"
#include <assert.h> // assert
#include <string.h> // memcpy
// Private APIs
// ============================================================================
void begin (void) __attribute__((constructor)); // Run before main.
void end (void) __attribute__((destructor)); // Run after main.
const int MAX_TYPE_NUM = MANUFACTURER + 1;
typedef struct {
DeviceId id;
DeviceVersion version;
DeviceString name;
DeviceString manufacturer;
} Device;
Device* get_or_create_device() {
static Device instance = { 123, 4.5, NULL, NULL }; // default setting.
return &instance;
}
void begin (void) {}
void end (void) {
Device* device = get_or_create_device();
release_string(device->name);
release_string(device->manufacturer);
}
size_t get_device_data_size(DataType type) {
assert(type < MAX_TYPE_NUM);
static size_t sizes[MAX_TYPE_NUM] = {
sizeof(DeviceId), // ID
sizeof(DeviceVersion), // VERSION
sizeof(DeviceString), // NAME
sizeof(DeviceString) // MANUFACTURER
};
return sizes[type];
}
void* get_device_data_source(DataType type) {
assert(type < MAX_TYPE_NUM);
Device* device = get_or_create_device();
void* sources[MAX_TYPE_NUM] = {
&device->id, // ID
&device->version, // VERSION
&device->name, // NAME
&device->manufacturer // MANUFACTURER
};
return sources[type];
// Notice that the following code doesn't work:
// ---------------------------------------------
// if (type == ID) {
// DeviceId id = device->id;
// return (void*) &id;
// } else if (type == VERSION)
// DeviceVersion version = device->version;
// return (void*) &version;
// } else if (type == ...) {
// ...
// ---------------------------------------------
// `Device{Id, Version, ...} x = device->{id, version, ...}` means that
// we copy the value of `device->*` to a local variable `x`. This local
// variable `x` will be destroyed after finish this function call. Thus,
// if we return its address, this address will point to a garbage memory
// that was used to store the `x`, which is destroyed after return.
}
typedef enum {
GET,
SET
} Action;
Status process_string_data_source(Action action,
DataType type,
void** source,
void** dest) {
assert(type < MAX_TYPE_NUM);
// Process the string data only.
if (type != NAME && type != MANUFACTURER) {
return OK;
}
// Always clone a string as the source to make sure the original string won't
// be changed.
StringRef* ref_ptr = (StringRef*) (*source);
if (!*ref_ptr) {
return NO_DATA;
}
StringRef new_ref = new_string_by_clone(*ref_ptr);
*source = &new_ref;
// Release the original string before writing a new one into its memory.
if (action == SET) {
StringRef* ref_ptr = (StringRef*) (*dest);
if (*ref_ptr) {
release_string(*ref_ptr);
}
}
return OK;
}
Status set_or_get_device_data(Action action,
DataType type,
size_t size,
void* data) {
if (type > MAX_TYPE_NUM) {
return BAD_TYPE;
}
if (!data) {
return BAD_POINTER;
}
size_t device_data_size = get_device_data_size(type);
if (device_data_size <= 0 ||
size < device_data_size) {
return BAD_SIZE;
}
void* device_data = get_device_data_source(type);
assert(device_data);
void* source = NULL;
void* dest = NULL;
if (action == GET) {
source = device_data;
dest = data;
} else {
source = data;
dest = device_data;
}
Status s = process_string_data_source(action, type, &source, &dest);
if (s != OK) {
return s;
}
memcpy(dest, source, device_data_size);
return OK;
}
// Public APIs
// ============================================================================
Status get_device_data(DataType type, size_t size, void* data) {
return set_or_get_device_data(GET, type, size, data);
}
Status set_device_data(DataType type, size_t size, const void* data) {
return set_or_get_device_data(SET, type, size, (void*) data);
}
#include "device.h"
#include <cassert> // assert
#include <cstring> // memcpy
// Private APIs
// ============================================================================
const int MAX_TYPE_NUM = MANUFACTURER + 1;
typedef enum {
GET,
SET
} Action;
class Device final
{
public:
static Device& get_instance() {
static Device instance(123, 4.5, "Mobile 📱", "🍍 Pineapple");
// static Device instance(123, 4.5, nullptr, "🍍 Pineapple");
// static Device instance(123, 4.5, "Mobile 📱", nullptr);
// static Device instance(123, 4.5, nullptr, nullptr);
return instance;
}
static size_t data_size(DataType type) {
assert(type < MAX_TYPE_NUM);
static size_t sizes[MAX_TYPE_NUM] = {
sizeof(DeviceId), // ID
sizeof(DeviceVersion), // VERSION
sizeof(DeviceString), // NAME
sizeof(DeviceString) // MANUFACTURER
};
return sizes[type];
}
static void* get_data_source(DataType type) {
assert(type < MAX_TYPE_NUM);
Device& device = get_instance();
void* sources[MAX_TYPE_NUM] = {
&device._id,
&device._version,
&device._name,
&device._manufacturer
};
return sources[type];
// Notice that the following code doesn't work:
// ---------------------------------------------
// if (type == ID) {
// DeviceId id = device._id;
// return (void*) &id;
// } else if (type == VERSION)
// DeviceVersion version = device._version;
// return (void*) &version;
// } else if (type == ...) {
// ...
// ---------------------------------------------
// `Device{Id, Version, ...} x = device.{_id, _version, ...}` means that
// we copy the value of `device._*` to a local variable `x`. This local
// variable `x` will be destroyed after finish this function call. Thus,
// if we return its address, this address will point to a garbage memory
// that was used to store the `x`, which is destroyed after return.
}
private:
Device(DeviceId id,
DeviceVersion version,
const char* name,
const char* manufacturer)
: _id(id)
, _version(version)
, _name(name
? new_string_by_clone_raw(name)
: nullptr)
, _manufacturer(manufacturer
? new_string_by_clone_raw(manufacturer)
: nullptr)
{
}
Device(Device const&) = delete; // Disable copy constructor.
void operator=(Device const&) = delete; // Disable assignment.
~Device() {
if (_name) {
release_string(_name);
}
if (_manufacturer) {
release_string(_manufacturer);
}
}
DeviceId _id;
DeviceVersion _version;
DeviceString _name;
DeviceString _manufacturer;
};
Status process_string_data_source(Action action,
DataType type,
void** source,
void** dest) {
assert(type < MAX_TYPE_NUM);
// Process the string data only.
if (type != NAME && type != MANUFACTURER) {
return OK;
}
// Always clone a string as the source to make sure no one can touch our own
// string.
StringRef* ref_ptr = (StringRef*) (*source);
if (!*ref_ptr) {
return NO_DATA;
}
StringRef new_ref = new_string_by_clone(*ref_ptr);
*source = &new_ref;
// Release the original string before writing a new one into its memory.
if (action == SET) {
StringRef* ref_ptr = (StringRef*) (*dest);
if (*ref_ptr) {
release_string(*ref_ptr);
}
}
return OK;
}
Status set_or_get_device_data(Action action,
DataType type,
size_t size,
void* data) {
if (type > MAX_TYPE_NUM) {
return BAD_TYPE;
}
if (!data) {
return BAD_POINTER;
}
size_t device_data_size = Device::data_size(type);
if (device_data_size <= 0 ||
size < device_data_size) {
return BAD_SIZE;
}
void* device_data = Device::get_data_source(type);
assert(device_data);
void* source = nullptr;
void* dest = nullptr;
if (action == GET) {
source = device_data;
dest = data;
} else {
source = data;
dest = device_data;
}
Status s = process_string_data_source(action, type, &source, &dest);
if (s != OK) {
return s;
}
memcpy(dest, source, device_data_size);
return OK;
}
// Public APIs
// ============================================================================
Status get_device_data(DataType type, size_t size, void* data) {
return set_or_get_device_data(GET, type, size, data);
}
Status set_device_data(DataType type, size_t size, const void* data) {
return set_or_get_device_data(SET, type, size, (void*) data);
}
#ifndef EXT_H
#define EXT_H
#include "string_ref.h"
#include <stdint.h> // uint32_t
#include <stdlib.h> // size_t
typedef enum {
OK = 0,
BAD_POINTER = 1,
BAD_SIZE = 2,
BAD_TYPE = 3,
NO_DATA = 4,
} Status;
typedef enum {
ID,
VERSION,
NAME,
MANUFACTURER
} DataType;
typedef uint32_t DeviceId;
typedef float DeviceVersion;
typedef StringRef DeviceString;
#ifdef __cplusplus
extern "C"
{
#endif
Status get_device_data(DataType type, size_t size, void* data);
Status set_device_data(DataType type, size_t size, const void* data);
#ifdef __cplusplus
}
#endif
#endif // EXT_H
#include "device.h"
#include <cassert> // assert
#include <cstring> // memcpy
#include <utility> // std::move
// Private APIs
// ============================================================================
const int MAX_TYPE_NUM = MANUFACTURER + 1;
// A String Wrapper around DeviceString.
class String final
{
public:
String(DeviceString s = nullptr)
: _string(s ? new_string_by_clone(s) : nullptr)
{
}
String(const char* s)
: _string(s ? new_string_by_clone_raw(s) : nullptr)
{
}
// Copy constructor: Do deep copy for inner data.
String(const String& other) = delete;
// String(const String& other)
// : _string(other.clone_device_string())
// {
// }
// Move constructor: Transfer ownership of the inner data.
String(String&& other)
: _string(other._string)
{
other._string = nullptr;
}
~String() {
reset();
}
bool empty() {
return !_string;
}
DeviceString clone_device_string() const {
return _string ? new_string_by_clone(_string) : nullptr;
}
// Disable copy assignmenet.
String& operator=(const String& other) = delete;
// Copy assignmenet:
// String& operator=(const String& other) {
// if (&other == this) {
// return *this;
// }
// reset();
// _string = other.clone_device_string();
// return *this;
// }
// Move assignment:
String& operator=(String&& other) {
if (&other == this) {
return *this;
}
reset();
_string = other._string;
other._string = nullptr;
return *this;
}
private:
void reset() {
release_string(_string);
_string = nullptr;
}
DeviceString _string;
};
class Device final
{
public:
static Device& get_instance() {
static Device instance(123, 4.5, "Mobile 📱", "🍍 Pineapple");
// static Device instance(123, 4.5, nullptr, "🍍 Pineapple");
// static Device instance(123, 4.5, "Mobile 📱", nullptr);
// static Device instance(123, 4.5, nullptr, nullptr);
return instance;
}
static size_t data_size(DataType type) {
assert(type < MAX_TYPE_NUM);
static size_t sizes[MAX_TYPE_NUM] = {
sizeof(DeviceId), // ID
sizeof(DeviceVersion), // VERSION
sizeof(DeviceString), // NAME
sizeof(DeviceString) // MANUFACTURER
};
return sizes[type];
}
template<typename T>
static Status get_data(DataType type, T& data);
template<typename T>
static Status set_data(DataType type, T& data);
private:
Device(DeviceId id,
DeviceVersion version,
const char* name,
const char* manufacturer)
: _id(id)
, _version(version)
, _name(name)
, _manufacturer(manufacturer)
{
}
Device(Device&& other) = delete; // Disable move constructor.
Device(Device const&) = delete; // Disable copy constructor.
void operator=(Device const&) = delete; // Disable assignment.
~Device() {}
DeviceId _id;
DeviceVersion _version;
String _name;
String _manufacturer;
};
template<>
/* static */ Status
Device::get_data<DeviceId>(DataType type, DeviceId& data) {
assert(type == ID);
Device& device = get_instance();
data = device._id;
return OK;
}
template<>
/* static */ Status
Device::get_data<DeviceVersion>(DataType type, DeviceVersion& data) {
assert(type == VERSION);
Device& device = get_instance();
data = device._version;
return OK;
}
template<>
/* static */ Status
Device::get_data<DeviceString>(DataType type, DeviceString& data) {
assert(type == NAME || type == MANUFACTURER);
Device& device = get_instance();
String s;
if (type == NAME) {
s = std::move(device._name);
} else {
s = std::move(device._manufacturer);
}
if (s.empty()) {
return NO_DATA;
}
// Deep copy the inner DeviceString data of device._* to the output
// DeviceString data.
data = s.clone_device_string();
return OK;
}
template<>
/* static */ Status
Device::set_data<DeviceId>(DataType type, DeviceId& data) {
assert(type == ID);
Device& device = get_instance();
device._id = data;
return OK;
}
template<>
/* static */ Status
Device::set_data<DeviceVersion>(DataType type, DeviceVersion& data) {
assert(type == VERSION);
Device& device = get_instance();
device._version = data;
return OK;
}
template<>
/* static */ Status
Device::set_data<DeviceString>(DataType type, DeviceString& data) {
assert(type == NAME || type == MANUFACTURER);
if (!data) {
return NO_DATA;
}
Device& device = get_instance();
// device._* = String(data):
// 1. Deep copy the input DeviceString data and assign the copied
// DeviceString data to the inner DeviceString data of the temp created
// String.
// 2. Deep copy the whole temp String data to device._*
if (type == NAME) {
device._name = String(data);
} else {
device._manufacturer = String(data);
}
return OK;
}
Status get_device_type_data(DataType type, void* data) {
assert(type < MAX_TYPE_NUM);
if (type == ID) {
DeviceId* id = (DeviceId*) data;
return Device::get_data<DeviceId>(type, *id);
}
if (type == VERSION) {
DeviceVersion* version = (DeviceVersion*) data;
return Device::get_data<DeviceVersion>(type, *version);
}
if (type == NAME || type == MANUFACTURER) {
DeviceString* str = (DeviceString*) data;
return Device::get_data<DeviceString>(type, *str);
}
assert(false && "type error!");
}
Status set_device_type_data(DataType type, void* data) {
assert(type < MAX_TYPE_NUM);
if (type == ID) {
DeviceId* id = (DeviceId*) data;
return Device::set_data<DeviceId>(type, *id);
}
if (type == VERSION) {
DeviceVersion* version = (DeviceVersion*) data;
return Device::set_data<DeviceVersion>(type, *version);
}
if (type == NAME || type == MANUFACTURER) {
DeviceString* str = (DeviceString*) data;
return Device::set_data<DeviceString>(type, *str);
}
assert(false && "type error!");
}
// Public APIs
// ============================================================================
Status get_device_data(DataType type, size_t size, void* data) {
if (type > MAX_TYPE_NUM) {
return BAD_TYPE;
}
if (!data) {
return BAD_POINTER;
}
size_t device_data_size = Device::data_size(type);
if (device_data_size <= 0 ||
size < device_data_size) {
return BAD_SIZE;
}
return get_device_type_data(type, data);
}
Status set_device_data(DataType type, size_t size, const void* data) {
if (type > MAX_TYPE_NUM) {
return BAD_TYPE;
}
if (!data) {
return BAD_POINTER;
}
size_t device_data_size = Device::data_size(type);
if (device_data_size <= 0 ||
size < device_data_size) {
return BAD_SIZE;
}
return set_device_type_data(type, (void*) data);
}
use super::sys;
use std::ffi::CString;
use std::mem; // For mem::{uninitialized(), size_of()}
use std::os::raw::c_void;
use std::ptr;
#[derive(Debug)]
pub enum Error {
BadPointer,
BadSize,
BadType,
NoData,
}
// To convert a sys::Status to a Error.
impl From<sys::Status> for Error {
fn from(status: sys::Status) -> Error {
match status {
sys::BAD_POINTER => Error::BadPointer,
sys::BAD_SIZE => Error::BadSize,
sys::BAD_TYPE => Error::BadType,
sys::NO_DATA => Error::NoData,
s => panic!("Unknown status: {}", s),
}
}
}
pub enum DataType {
Id,
Version,
Name,
Manufacturer
}
// To convert a DataType to a sys::DataType.
impl From<DataType> for sys::DataType {
fn from(data_type: DataType) -> sys::DataType {
match data_type {
DataType::Id => sys::ID,
DataType::Version => sys::VERSION,
DataType::Name => sys::NAME,
DataType::Manufacturer => sys::MANUFACTURER
}
}
}
// Public APIs:
pub fn get_data<T>(data_type: DataType) -> Result<T, Error> {
let mut data: T = unsafe { mem::uninitialized() };
let size = mem::size_of::<T>();
let status = unsafe {
sys::get_device_data(
data_type.into(), // Convert DataType into sys::DataType.
size,
&mut data as *mut T as *mut c_void,
)
};
convert_to_result(status)?;
Ok(data)
}
pub fn set_data<T>(data_type: DataType, data: &T) -> Result<(), Error> {
let size = mem::size_of::<T>();
let status = unsafe {
sys::set_device_data(
data_type.into(), // Convert DataType into sys::DataType.
size,
data as *const T as *const c_void,
)
};
convert_to_result(status)
}
pub fn reset_string(string: &sys::DeviceString) {
release_string(string);
}
pub fn create_string(string: &str) -> sys::DeviceString {
let c_string = CString::new(string).unwrap();
unsafe {
sys::new_string_by_clone_raw(c_string.as_c_str().as_ptr())
}
}
pub fn to_rust_string(string: &sys::DeviceString) -> String {
if string.is_null() {
return String::new();
}
let bytes = get_string_bytes(string);
string_from_bytes(bytes)
}
// Private APIs:
fn convert_to_result(status: sys::Status) -> Result<(), Error> {
match status {
sys::OK => Ok(()),
e => Err(e.into()), // Convert sys::Status into Error.
}
}
fn get_string_bytes(string: &sys::DeviceString) -> Vec<u8> {
if string.is_null() {
return Vec::<u8>::new();
}
let mut size = unsafe {
let mut s: usize = 0;
sys::get_string_bytes(
*string,
&mut s,
ptr::null_mut(),
);
s
};
if size <= 0 {
return Vec::<u8>::new();
}
let mut buffer = vec![b'\x00'; size as usize];
unsafe {
sys::get_string_bytes(
*string,
&mut size,
buffer.as_mut_ptr(),
);
}
return buffer;
}
fn string_from_bytes(buffer: Vec<u8>) -> String {
String::from_utf8(buffer).unwrap_or(String::new())
}
fn release_string(string: &sys::DeviceString) {
unsafe {
sys::release_string(*string);
}
}
all:
# Sample in C(static library):
# gcc -c string_ref.c -o string_ref.o
# ar rcs libstringref.a string_ref.o
# gcc -c device.c -o device.o
# ar rcs libdevice.a device.o
# gcc sample.c -L. -ldevice -lstringref -o sample-c
# ./sample-c
# Sample in C:
# gcc -shared -fPIC string_ref.c -o libstringref.so
# gcc -shared -fPIC device.c libstringref.so -o libdevice.so
# gcc sample.c libdevice.so libstringref.so -o sample-c
# ./sample-c
# Sample in C++:
# g++ -shared -fPIC string_ref.c -o libstringref.so
# g++ -std=c++11 -shared -fPIC device.cpp libstringref.so -o libdevice.so
# g++ -std=c++11 sample.cpp libdevice.so libstringref.so -o sample-cpp
# ./sample-cpp
# Sample in C++ with string wrapper:
# g++ -shared -fPIC string_ref.c -o libstringref.so
# # g++ -std=c++11 -shared -fPIC device.cpp libstringref.so -o libdevice.so
# g++ -std=c++11 -shared -fPIC device_w_string.cpp libstringref.so -o libdevice.so
# g++ -std=c++11 sample_w_string.cpp libdevice.so libstringref.so -o sample-cpp
# ./sample-cpp
# Sample in C++ with byte size trick:
g++ -shared -fPIC string_ref.c -o libstringref.so
# g++ -std=c++11 -shared -fPIC device.cpp libstringref.so -o libdevice.so
g++ -std=c++11 -shared -fPIC device_w_string.cpp libstringref.so -o libdevice.so
g++ -std=c++11 sample_w_size_trick.cpp libdevice.so libstringref.so -o sample-cpp
./sample-cpp
# Samples in Rust:
# rustc sample.rs -L.
# rustc sample_w_string.rs -L. -o sample # with String
rustc sample_w_size_trick.rs -L. -o sample # with byte size trick
LD_LIBRARY_PATH=. RUST_BACKTRACE=1 ./sample
clean:
# rm string_ref.o device.o libstringref.a libdevice.a
# rm sample-c # C version executable.
rm sample-cpp # C++ version executable.
rm sample
rm libstringref.so
rm libdevice.so
#include "device.h"
#include <assert.h> // assert
#include <stdio.h> // printf
#include <stdlib.h> // calloc, free
Status get_id(DeviceId* id) {
return get_device_data(ID, sizeof(*id), id);
}
Status set_id(DeviceId* id) {
return set_device_data(ID, sizeof(*id), id);
}
Status get_version(DeviceVersion* version) {
return get_device_data(VERSION, sizeof(*version), version);
}
Status set_version(DeviceVersion* version) {
return set_device_data(VERSION, sizeof(*version), version);
}
Status get_name(DeviceString* name) {
return get_device_data(NAME, sizeof(*name), name);
}
Status set_name(DeviceString* name) {
return set_device_data(NAME, sizeof(*name), name);
}
Status get_manufacturer(DeviceString* manufacturer) {
return get_device_data(MANUFACTURER, sizeof(*manufacturer), manufacturer);
}
Status set_manufacturer(DeviceString* manufacturer) {
return set_device_data(MANUFACTURER, sizeof(*manufacturer), manufacturer);
}
void reset_string(DeviceString* str) {
assert(str);
release_string(*str);
*str = NULL;
}
void replace_string_from_raw(DeviceString* str, const char* c_str) {
assert(str);
reset_string(str);
assert(c_str);
*str = new_string_by_clone_raw(c_str);
}
typedef Status (*get_string_func)(DeviceString* str);
Status replace_string_by_func(DeviceString* str, get_string_func func) {
assert(str);
reset_string(str);
return func(str);
}
// Avoid to name this struct to Device, in case compiler confuse this struct
// with the Device struct in device.c.
typedef struct {
DeviceId id;
DeviceVersion version;
DeviceString name;
DeviceString manufacturer;
} DeviceData;
void get_device(DeviceData* device) {
Status s = OK;
s = get_id(&device->id);
assert(s == OK);
s = get_version(&device->version);
assert(s == OK);
s = replace_string_by_func(&device->name, get_name);
assert(s == OK || s == NO_DATA); // Get `NO_DATA` when string is empty.
s = replace_string_by_func(&device->manufacturer, get_manufacturer);
assert(s == OK || s == NO_DATA); // Get `NO_DATA` when string is empty.
}
void set_device(DeviceData* device) {
Status s = OK;
s = set_id(&device->id);
assert(s == OK);
s = set_version(&device->version);
assert(s == OK);
s = set_name(&device->name);
assert(s == OK);
s = set_manufacturer(&device->manufacturer);
assert(s == OK);
}
const char* get_raw_string(DeviceString str) {
if (!str) {
return NULL;
}
size_t size = 0;
get_string_bytes(str, &size, NULL);
uint8_t* buffer = (uint8_t*) calloc(1, size);
get_string_bytes(str, &size, buffer);
return (const char*) buffer;
}
void destroy_raw_string(const char* str) {
free((void*) str); // It's ok to free a NULL pointer.
}
void show_device(DeviceData* device) {
const char* name = get_raw_string(device->name);
const char* manufacturer = get_raw_string(device->manufacturer);
printf("id: %d, version: %f, name: %s, manufacturer: %s\n",
device->id,
device->version,
name,
manufacturer);
destroy_raw_string(name);
destroy_raw_string(manufacturer);
}
void destroy_device(DeviceData* device) {
if (!device) {
return;
}
reset_string(&device->name);
reset_string(&device->manufacturer);
}
int main() {
// Set name and manufacturer to NULL in case we free a random memory
// when calling replace_string.
DeviceData device = { 0, 0.0, NULL, NULL };
get_device(&device);
show_device(&device);
device.id = 607;
device.version = 8.9;
replace_string_from_raw(&device.name, "Robot 🤖");
replace_string_from_raw(&device.manufacturer, "🎃 jack o'lantern");
set_device(&device);
get_device(&device);
show_device(&device);
destroy_device(&device);
return 0;
}
#include "device.h"
#include <cassert> // assert
#include <cstdlib> // free
#include <iostream> // std::{cout, endl}
#include <memory> // std::unique_ptr
class Utils final
{
public:
static Status get_id(DeviceId& id) {
return get_data(ID, id);
}
static Status set_id(DeviceId& id) {
return set_data(ID, id);
}
static Status get_version(DeviceVersion& version) {
return get_data(VERSION, version);
}
static Status set_version(DeviceVersion& version) {
return set_data(VERSION, version);
}
static Status get_name(DeviceString& name) {
return get_data(NAME, name);
}
static Status set_name(DeviceString& name) {
return set_data(NAME, name);
}
static Status get_manufacturer(DeviceString& manufacturer) {
return get_data(MANUFACTURER, manufacturer);
}
static Status set_manufacturer(DeviceString& manufacturer) {
return set_data(MANUFACTURER, manufacturer);
}
private:
template<typename T>
static Status get_data(DataType type, T& data) {
return get_device_data(type, sizeof(T), &data);
}
template<typename T>
static Status set_data(DataType type, T& data) {
return set_device_data(type, sizeof(T), &data);
}
};
const char* get_raw_string(DeviceString str) {
if (!str) {
return NULL;
}
size_t size = 0;
get_string_bytes(str, &size, NULL);
uint8_t* buffer = (uint8_t*) calloc(1, size);
get_string_bytes(str, &size, buffer);
return (const char*) buffer;
}
void destroy_raw_string(const char* str) {
free((void*) str); // It's ok to free a NULL pointer.
}
// Avoid to name this class to Device, in case compiler confuse this class
// with the Device class in device.cpp.
class DeviceData final
{
public:
static DeviceData& get_instance() {
static DeviceData instance = get_device();
return instance;
}
void set_id(DeviceId id) {
_id = id;
Status s = Utils::set_id(_id);
assert(s == OK);
}
void set_version(DeviceVersion version) {
_version = version;
Status s = Utils::set_version(_version);
assert(s == OK);
}
void set_name(const char* name) {
assert(name);
replace_string_from_raw(_name, name);
Status s = Utils::set_name(_name);
assert(s == OK);
}
void set_manufacturer(const char* manufacturer) {
assert(manufacturer);
replace_string_from_raw(_manufacturer, manufacturer);
Status s = Utils::set_manufacturer(_manufacturer);
assert(s == OK);
}
void show() {
std::unique_ptr<const char, decltype(&destroy_raw_string)>
name(get_raw_string(_name), destroy_raw_string);
std::unique_ptr<const char, decltype(&destroy_raw_string)>
manufacturer(get_raw_string(_manufacturer), destroy_raw_string);
// If name or manufacturer is nullptr, then we print "(empty)", or
// `std::cout << ptr, whwere ptr = nullptr` will cause a segment fault.
std::cout << "id: " << _id
<< ", version: " << _version
<< ", name: " << (name ? name.get() : "(empty)")
<< ", manufacturer: " << (manufacturer ? manufacturer.get() : "(empty)")
<< std::endl;
}
private:
DeviceData(DeviceId id,
DeviceVersion version,
DeviceString& name,
DeviceString& manufacturer)
: _id(id)
, _version(version)
, _name(name)
, _manufacturer(manufacturer)
{}
~DeviceData() {
reset_string(_name);
reset_string(_manufacturer);
}
static DeviceData get_device() {
DeviceId id = 0;
DeviceVersion version = 0.0;
// Set name and manufacturer to nullptr in case we free a random memory
// when calling replace_string.
DeviceString name = nullptr;
DeviceString manufacturer = nullptr;
replace_data(id, version, name, manufacturer);
return DeviceData(id, version, name, manufacturer);
}
static void replace_data(DeviceId& id,
DeviceVersion& version,
DeviceString& name,
DeviceString& manufacturer) {
Status s = OK;
s = Utils::get_id(id);
assert(s == OK);
s = Utils::get_version(version);
assert(s == OK);
s = replace_string_by_func(name, Utils::get_name);
assert(s == OK || s == NO_DATA); // Get `NO_DATA` when string is empty.
s = replace_string_by_func(manufacturer, Utils::get_manufacturer);
assert(s == OK || s == NO_DATA); // Get `NO_DATA` when string is empty.
}
typedef Status (get_string_func)(DeviceString& str);
static Status replace_string_by_func(DeviceString& str, get_string_func func) {
reset_string(str);
return func(str);
}
static void replace_string_from_raw(DeviceString& str, const char* c_str) {
reset_string(str);
str = new_string_by_clone_raw(c_str);
}
static void reset_string(DeviceString& str) {
release_string(str);
str = nullptr;
}
DeviceId _id;
DeviceVersion _version;
DeviceString _name;
DeviceString _manufacturer;
};
int main() {
DeviceData& device = DeviceData::get_instance();
device.show();
device.set_id(607);
device.set_version(8.9);
device.set_name("Robot 🤖");
device.set_manufacturer("🎃 jack o'lantern");
device.show();
return 0;
}
// Contains all the native types and APIs in external library.
mod sys;
// An adapter layer for the external library.
mod ext;
mod utils {
use super::{ext, sys};
use std::ptr;
#[derive(Debug)]
pub enum Error {
Ext(ext::Error),
}
// To convert a network::Error to a Error.
impl From<ext::Error> for Error {
fn from(e: ext::Error) -> Error {
Error::Ext(e)
}
}
pub struct Device {
id: sys::DeviceId,
version: sys::DeviceVersion,
name: sys::DeviceString,
manufacturer: sys::DeviceString,
}
impl Device {
pub fn new() -> Result<Self, Error> {
let id = get_id()?;
let version = get_version()?;
let name = get_name().unwrap_or(ptr::null_mut());
let manufacturer = get_manufacturer().unwrap_or(ptr::null_mut());
Ok(Device {
id,
version,
name,
manufacturer,
})
}
pub fn set_id(&mut self, id: sys::DeviceId) -> Result<(), Error> {
self.id = id;
set_id(self.id)
}
pub fn set_version(&mut self, version: sys::DeviceVersion) -> Result<(), Error> {
self.version = version;
set_version(self.version)
}
pub fn set_name(&mut self, name: &str) -> Result<(), Error> {
ext::reset_string(&self.name);
self.name = ext::create_string(name);
set_name(&self.name)
}
pub fn set_manufacturer(&mut self, manufacturer: &str) -> Result<(), Error> {
ext::reset_string(&self.manufacturer);
self.manufacturer = ext::create_string(manufacturer);
set_manufacturer(&self.name)
}
pub fn show(&self) {
println!("id: {}, version: {}, name: {}, manufacturer: {}",
self.id, self.version,
ext::to_rust_string(&self.name),
ext::to_rust_string(&self.manufacturer));
}
}
impl Drop for Device {
fn drop(&mut self) {
ext::reset_string(&mut self.name);
ext::reset_string(&mut self.manufacturer);
}
}
fn get_id() -> Result<sys::DeviceId, Error> {
ext::get_data::<sys::DeviceId>(ext::DataType::Id)
.map_err(|e| e.into())
}
fn set_id(id: sys::DeviceId) -> Result<(), Error> {
ext::set_data(ext::DataType::Id, &id)
.map_err(|e| e.into())
}
fn get_version() -> Result<sys::DeviceVersion, Error> {
ext::get_data::<sys::DeviceVersion>(ext::DataType::Version)
.map_err(|e| e.into())
}
fn set_version(version: sys::DeviceVersion) -> Result<(), Error> {
ext::set_data(ext::DataType::Version, &version)
.map_err(|e| e.into())
}
fn get_name() -> Result<sys::DeviceString, Error> {
get_string(ext::DataType::Name)
}
fn set_name(name: &sys::DeviceString) -> Result<(), Error> {
set_string(ext::DataType::Name, name)
}
fn get_manufacturer() -> Result<sys::DeviceString, Error> {
get_string(ext::DataType::Manufacturer)
}
fn set_manufacturer(manufacturer: &sys::DeviceString) -> Result<(), Error> {
set_string(ext::DataType::Manufacturer, manufacturer)
}
fn get_string(data_type: ext::DataType) -> Result<sys::DeviceString, Error> {
ext::get_data::<sys::DeviceString>(data_type)
.map_err(|e| e.into())
}
fn set_string(data_type: ext::DataType, string: &sys::DeviceString) -> Result<(), Error> {
ext::set_data::<sys::DeviceString>(data_type, string)
.map_err(|e| e.into())
}
}
fn main() {
let mut device = utils::Device::new().unwrap();
device.show();
device.set_id(607).unwrap();
device.set_version(8.9).unwrap();
device.set_name("Robot 🤖").unwrap();
device.set_manufacturer("🎃 jack o'lantern").unwrap();
device.show();
}
#include "device.h"
#include <cassert> // assert
#include <cstdlib> // free
#include <iostream> // std::{cout, endl}
#include <utility> // std::move
const char* get_raw_string(DeviceString str) {
if (!str) {
return NULL;
}
size_t size = 0;
get_string_bytes(str, &size, NULL);
uint8_t* buffer = (uint8_t*) calloc(1, size);
get_string_bytes(str, &size, buffer);
return (const char*) buffer;
}
void destroy_raw_string(const char* str) {
free((void*) str); // It's ok to free a NULL pointer.
}
// A string Wrapper around DeviceString.
// Avoid to name this class to String, in case compiler confuse this class
// with the String class in device_w_string.cpp.
class DevString final
{
public:
DevString()
: _string(nullptr)
{
}
DevString(const char* s)
: _string(s ? new_string_by_clone_raw(s) : nullptr)
{
}
// Disable copy constructor.
DevString(const DevString& other) = delete;
// Copy constructor: Do deep copy for inner data.
// DevString(const DevString& other)
// : _string(other.clone_device_string())
// {
// }
// Move constructor: Transfer ownership of the inner data.
DevString(DevString&& other)
: _string(other._string)
{
other._string = nullptr;
}
~DevString() {
reset();
}
void reset() {
release_string(_string);
_string = nullptr;
}
// Disable copy assignmenet.
DevString& operator=(const DevString& other) = delete;
// Copy assignmenet:
// DevString& operator=(const DevString& other) {
// if (&other == this) {
// return *this;
// }
// reset();
// _string = other.clone_device_string();
// return *this;
// }
// Move assignment:
DevString& operator=(DevString&& other) {
if (&other == this) {
return *this;
}
reset();
_string = other._string;
other._string = nullptr;
return *this;
}
friend std::ostream& operator << (std::ostream &out, const DevString &str) {
if (str._string) {
const char* s = get_raw_string(str._string);
// stream buffer will copy the data.
out << s;
destroy_raw_string(s);
} else {
out << "(empty)";
}
return out;
}
private:
// DeviceString clone_device_string() const {
// return _string ? new_string_by_clone(_string) : nullptr;
// }
DeviceString _string;
};
class Utils final
{
public:
static Status get_id(DeviceId& id) {
return get_data(ID, id);
}
static Status set_id(DeviceId& id) {
return set_data(ID, id);
}
static Status get_version(DeviceVersion& version) {
return get_data(VERSION, version);
}
static Status set_version(DeviceVersion& version) {
return set_data(VERSION, version);
}
static Status get_name(DevString& name) {
name.reset();
return operate_string(NAME, name, get_data);
}
static Status set_name(DevString& name) {
return operate_string(NAME, name, set_data);
}
static Status get_manufacturer(DevString& manufacturer) {
manufacturer.reset();
return operate_string(MANUFACTURER, manufacturer, get_data);
}
static Status set_manufacturer(DevString& manufacturer) {
return operate_string(MANUFACTURER, manufacturer, set_data);
}
private:
typedef Status (*StringOperatoration)(DataType type, DevString& str);
static Status operate_string(DataType type,
DevString& str,
StringOperatoration op) {
assert(sizeof(str) == sizeof(DeviceString));
return op(type, str);
}
template<typename T>
static Status get_data(DataType type, T& data) {
return get_device_data(type, sizeof(T), &data);
}
template<typename T>
static Status set_data(DataType type, T& data) {
return set_device_data(type, sizeof(T), &data);
}
};
// Avoid to name this class to Device, in case compiler confuse this class
// with the Device class in device.cpp.
class DeviceData final
{
public:
static DeviceData& get_instance() {
static DeviceData instance = std::move(get_device());
return instance;
}
void set_id(DeviceId id) {
_id = id;
Status s = Utils::set_id(_id);
assert(s == OK);
}
void set_version(DeviceVersion version) {
_version = version;
Status s = Utils::set_version(_version);
assert(s == OK);
}
void set_name(const char* name) {
assert(name);
_name = DevString(name);
Status s = Utils::set_name(_name);
assert(s == OK);
}
void set_manufacturer(const char* manufacturer) {
assert(manufacturer);
_manufacturer = DevString(manufacturer);
Status s = Utils::set_manufacturer(_manufacturer);
assert(s == OK);
}
void show() {
std::cout << "id: " << _id
<< ", version: " << _version
<< ", name: " << _name
<< ", manufacturer: " << _manufacturer
<< std::endl;
}
private:
DeviceData(DeviceId& id,
DeviceVersion& version,
DevString& name,
DevString& manufacturer)
: _id(id)
, _version(version)
, _name(std::move(name))
, _manufacturer(std::move(manufacturer))
{
}
// Move constructor: Transfer ownership of the inner data.
DeviceData(DeviceData&& other)
: _id(other._id)
, _version(other._version)
, _name(std::move(other._name))
, _manufacturer(std::move(other._manufacturer))
{
}
// Disable copy constructor.
DeviceData(const DeviceData& other) = delete;
~DeviceData() {}
static DeviceData get_device() {
DeviceId id = 0;
DeviceVersion version = 0.0;
DevString name;
DevString manufacturer;
refresh_data(id, version, name, manufacturer);
return DeviceData(id, version, name, manufacturer);
}
static void refresh_data(DeviceId& id,
DeviceVersion& version,
DevString& name,
DevString& manufacturer) {
Status s = OK;
s = Utils::get_id(id);
assert(s == OK);
s = Utils::get_version(version);
assert(s == OK);
s = Utils::get_name(name);
assert(s == OK || s == NO_DATA);
s = Utils::get_manufacturer(manufacturer);
assert(s == OK || s == NO_DATA);
}
DeviceId _id;
DeviceVersion _version;
DevString _name;
DevString _manufacturer;
};
int main() {
DeviceData& device = DeviceData::get_instance();
device.show();
device.set_id(607);
device.set_version(8.9);
device.set_name("Robot 🤖");
device.set_manufacturer("🎃 jack o'lantern");
device.show();
return 0;
}
// Contains all the native types and APIs in external library.
mod sys;
// An adapter layer for the external library.
mod ext;
mod utils {
use super::{ext, sys};
use std::fmt;
#[derive(Debug)]
pub enum Error {
Ext(ext::Error),
}
// To convert a network::Error to a Error.
impl From<ext::Error> for Error {
fn from(e: ext::Error) -> Error {
Error::Ext(e)
}
}
pub struct Device {
id: DeviceIdWrapper,
version: DeviceVersionWrapper,
name: DeviceStringWrapper,
manufacturer: DeviceStringWrapper,
}
impl Device {
pub fn new() -> Result<Self, Error> {
let id = get_id()?;
let version = get_version()?;
let name = get_name()?;
let manufacturer = get_manufacturer()?;
Ok(Device {
id,
version,
name,
manufacturer,
})
}
pub fn set_id(&mut self, id: sys::DeviceId) -> Result<(), Error> {
self.id = DeviceIdWrapper::from(id);
set_id(&self.id)
}
pub fn set_version(&mut self, version: sys::DeviceVersion) -> Result<(), Error> {
self.version = DeviceVersionWrapper::from(version);
set_version(&self.version)
}
pub fn set_name(&mut self, name: &str) -> Result<(), Error> {
self.name = DeviceStringWrapper::from(name);
set_name(&self.name)
}
pub fn set_manufacturer(&mut self, manufacturer: &str) -> Result<(), Error> {
self.manufacturer = DeviceStringWrapper::from(manufacturer);
set_manufacturer(&self.manufacturer)
}
pub fn show(&self) {
println!("id: {}, version: {}, name: {}, manufacturer: {}",
self.id, self.version, self.name, self.manufacturer);
}
}
struct DeviceIdWrapper(sys::DeviceId);
impl fmt::Display for DeviceIdWrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<sys::DeviceId> for DeviceIdWrapper {
fn from(id: sys::DeviceId) -> Self {
DeviceIdWrapper(id)
}
}
struct DeviceVersionWrapper(sys::DeviceVersion);
impl fmt::Display for DeviceVersionWrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<sys::DeviceVersion> for DeviceVersionWrapper {
fn from(version: sys::DeviceVersion) -> Self {
DeviceVersionWrapper(version)
}
}
struct DeviceStringWrapper(sys::DeviceString);
impl Drop for DeviceStringWrapper {
fn drop(&mut self) {
ext::reset_string(&self.0);
}
}
impl fmt::Display for DeviceStringWrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let string = ext::to_rust_string(&self.0);
write!(f, "{}", string)
}
}
impl<'a> From<&'a str> for DeviceStringWrapper {
fn from(string: &'a str) -> Self {
DeviceStringWrapper(ext::create_string(string))
}
}
fn get_id() -> Result<DeviceIdWrapper, Error> {
ext::get_data::<DeviceIdWrapper>(ext::DataType::Id)
.map_err(|e| e.into())
}
fn set_id(id: &DeviceIdWrapper) -> Result<(), Error> {
ext::set_data(ext::DataType::Id, &id)
.map_err(|e| e.into())
}
fn get_version() -> Result<DeviceVersionWrapper, Error> {
ext::get_data::<DeviceVersionWrapper>(ext::DataType::Version)
.map_err(|e| e.into())
}
fn set_version(version: &DeviceVersionWrapper) -> Result<(), Error> {
ext::set_data(ext::DataType::Version, &version)
.map_err(|e| e.into())
}
fn get_name() -> Result<DeviceStringWrapper, Error> {
get_string(ext::DataType::Name)
}
fn set_name(name: &DeviceStringWrapper) -> Result<(), Error> {
set_string(ext::DataType::Name, name)
}
fn get_manufacturer() -> Result<DeviceStringWrapper, Error> {
get_string(ext::DataType::Manufacturer)
}
fn set_manufacturer(manufacturer: &DeviceStringWrapper) -> Result<(), Error> {
set_string(ext::DataType::Manufacturer, manufacturer)
}
fn get_string(data_type: ext::DataType) -> Result<DeviceStringWrapper, Error> {
ext::get_data::<DeviceStringWrapper>(data_type)
.map_err(|e| e.into())
}
fn set_string(data_type: ext::DataType, string: &DeviceStringWrapper) -> Result<(), Error> {
ext::set_data(data_type, string)
.map_err(|e| e.into())
}
}
fn main() {
let mut device = utils::Device::new().unwrap();
device.show();
device.set_id(607).unwrap();
device.set_version(8.9).unwrap();
device.set_name("Robot 🤖").unwrap();
device.set_manufacturer("🎃 jack o'lantern").unwrap();
device.show();
}
#include "device.h"
#include <cassert> // assert
#include <cstdlib> // free
#include <iostream> // std::{cout, endl}
#include <utility> // std::move
class Utils final
{
public:
static Status get_id(DeviceId& id) {
return get_data(ID, id);
}
static Status set_id(DeviceId& id) {
return set_data(ID, id);
}
static Status get_version(DeviceVersion& version) {
return get_data(VERSION, version);
}
static Status set_version(DeviceVersion& version) {
return set_data(VERSION, version);
}
static Status get_name(DeviceString& name) {
return get_data(NAME, name);
}
static Status set_name(DeviceString& name) {
return set_data(NAME, name);
}
static Status get_manufacturer(DeviceString& manufacturer) {
return get_data(MANUFACTURER, manufacturer);
}
static Status set_manufacturer(DeviceString& manufacturer) {
return set_data(MANUFACTURER, manufacturer);
}
private:
template<typename T>
static Status get_data(DataType type, T& data) {
return get_device_data(type, sizeof(T), &data);
}
template<typename T>
static Status set_data(DataType type, T& data) {
return set_device_data(type, sizeof(T), &data);
}
};
const char* get_raw_string(DeviceString str) {
if (!str) {
return NULL;
}
size_t size = 0;
get_string_bytes(str, &size, NULL);
uint8_t* buffer = (uint8_t*) calloc(1, size);
get_string_bytes(str, &size, buffer);
return (const char*) buffer;
}
void destroy_raw_string(const char* str) {
free((void*) str); // It's ok to free a NULL pointer.
}
// A string Wrapper around DeviceString.
// Avoid to name this class to String, in case compiler confuse this class
// with the String class in device_w_string.cpp.
class DevString final
{
public:
DevString(DeviceString s = nullptr)
: _string(s ? new_string_by_clone(s) : nullptr)
{
}
DevString(const char* s) {
assert(s);
_string = new_string_by_clone_raw(s);
}
// Disable copy constructor.
DevString(const DevString& other) = delete;
// Copy constructor: Do deep copy for inner data.
// DevString(const DevString& other)
// : _string(other.clone_device_string())
// {
// }
// Move constructor: Transfer ownership of the inner data.
DevString(DevString&& other)
: _string(other._string)
{
other._string = nullptr;
}
~DevString() {
reset();
}
DeviceString& get_device_string() {
assert(_string);
return _string;
}
void reset() {
release_string(_string);
_string = nullptr;
}
// Disable copy assignmenet.
DevString& operator=(const DevString& other) = delete;
// Copy assignmenet:
// DevString& operator=(const DevString& other) {
// if (&other == this) {
// return *this;
// }
// reset();
// _string = other.clone_device_string();
// return *this;
// }
// Move assignment:
DevString& operator=(DevString&& other) {
if (&other == this) {
return *this;
}
reset();
_string = other._string;
other._string = nullptr;
return *this;
}
friend std::ostream& operator << (std::ostream &out, const DevString &str) {
if (str._string) {
const char* s = get_raw_string(str._string);
// stream buffer will copy the data.
out << s;
destroy_raw_string(s);
} else {
out << "(empty)";
}
return out;
}
private:
DeviceString clone_device_string() const {
return _string ? new_string_by_clone(_string) : nullptr;
}
DeviceString _string;
};
// Avoid to name this class to Device, in case compiler confuse this class
// with the Device class in device.cpp.
class DeviceData final
{
public:
static DeviceData& get_instance() {
static DeviceData instance = std::move(get_device());
return instance;
}
void set_id(DeviceId id) {
_id = id;
Status s = Utils::set_id(_id);
assert(s == OK);
}
void set_version(DeviceVersion version) {
_version = version;
Status s = Utils::set_version(_version);
assert(s == OK);
}
void set_name(const char* name) {
assert(name);
_name = DevString(name);
Status s = Utils::set_name(_name.get_device_string());
assert(s == OK);
}
void set_manufacturer(const char* manufacturer) {
assert(manufacturer);
_manufacturer = DevString(manufacturer);
Status s = Utils::set_manufacturer(_manufacturer.get_device_string());
assert(s == OK);
}
void show() {
std::cout << "id: " << _id
<< ", version: " << _version
<< ", name: " << _name
<< ", manufacturer: " << _manufacturer
<< std::endl;
}
private:
DeviceData(DeviceId id,
DeviceVersion version,
DeviceString& name,
DeviceString& manufacturer)
: _id(id)
, _version(version)
, _name(name)
, _manufacturer(manufacturer)
{
// DevString constructor will clone the DeviceString to its inner data,
// so we need to release them by ourselves.
reset_string(name);
reset_string(manufacturer);
}
// Move constructor: Transfer ownership of the inner data.
DeviceData(DeviceData&& other)
: _id(other._id)
, _version(other._version)
, _name(std::move(other._name))
, _manufacturer(std::move(other._manufacturer))
{
}
// Disable copy constructor.
DeviceData(const DeviceData& other) = delete;
~DeviceData() {}
static DeviceData get_device() {
DeviceId id = 0;
DeviceVersion version = 0.0;
// Set name and manufacturer to nullptr in case we free a random memory
// when calling replace_string.
DeviceString name = nullptr;
DeviceString manufacturer = nullptr;
refresh_data(id, version, name, manufacturer);
return DeviceData(id, version, name, manufacturer);
}
static void refresh_data(DeviceId& id,
DeviceVersion& version,
DeviceString& name,
DeviceString& manufacturer) {
Status s = OK;
s = Utils::get_id(id);
assert(s == OK);
s = Utils::get_version(version);
assert(s == OK);
s = replace_string_by_func(name, Utils::get_name);
assert(s == OK || s == NO_DATA); // Get `NO_DATA` when string is empty.
s = replace_string_by_func(manufacturer, Utils::get_manufacturer);
assert(s == OK || s == NO_DATA); // Get `NO_DATA` when string is empty.
}
typedef Status (*StringFunc)(DeviceString& str);
static Status replace_string_by_func(DeviceString& str, StringFunc func) {
reset_string(str);
return func(str);
}
static void reset_string(DeviceString& str) {
if (str) {
release_string(str);
}
str = nullptr;
}
DeviceId _id;
DeviceVersion _version;
DevString _name;
DevString _manufacturer;
};
int main() {
DeviceData& device = DeviceData::get_instance();
device.show();
device.set_id(607);
device.set_version(8.9);
device.set_name("Robot 🤖");
device.set_manufacturer("🎃 jack o'lantern");
device.show();
return 0;
}
// Contains all the native types and APIs in external library.
mod sys;
// An adapter layer for the external library.
mod ext;
mod utils {
use super::{ext, sys};
use std::ptr;
#[derive(Debug)]
pub enum Error {
Ext(ext::Error),
}
// To convert a network::Error to a Error.
impl From<ext::Error> for Error {
fn from(e: ext::Error) -> Error {
Error::Ext(e)
}
}
pub struct Device {
id: sys::DeviceId,
version: sys::DeviceVersion,
name: String,
manufacturer: String,
}
impl Device {
pub fn new() -> Result<Self, Error> {
let id = get_id()?;
let version = get_version()?;
let name_ref = get_name().unwrap_or(ptr::null_mut());
let name = ext::to_rust_string(&name_ref);
let manufacturer_ref = get_manufacturer().unwrap_or(ptr::null_mut());
let manufacturer = ext::to_rust_string(&manufacturer_ref);
Ok(Device {
id,
version,
name,
manufacturer,
})
}
pub fn set_id(&mut self, id: sys::DeviceId) -> Result<(), Error> {
self.id = id;
set_id(self.id)
}
pub fn set_version(&mut self, version: sys::DeviceVersion) -> Result<(), Error> {
self.version = version;
set_version(self.version)
}
pub fn set_name(&mut self, name: &str) -> Result<(), Error> {
self.name = String::from(name);
let dev_str = ext::create_string(self.name.as_str());
let result = set_name(&dev_str);
ext::reset_string(&dev_str);
result
}
pub fn set_manufacturer(&mut self, manufacturer: &str) -> Result<(), Error> {
self.manufacturer = String::from(manufacturer);
let dev_str = ext::create_string(self.manufacturer.as_str());
let result = set_manufacturer(&dev_str);
ext::reset_string(&dev_str);
result
}
pub fn show(&self) {
println!("id: {}, version: {}, name: {}, manufacturer: {}",
self.id, self.version, self.name, self.manufacturer);
}
}
fn get_id() -> Result<sys::DeviceId, Error> {
ext::get_data::<sys::DeviceId>(ext::DataType::Id)
.map_err(|e| e.into())
}
fn set_id(id: sys::DeviceId) -> Result<(), Error> {
ext::set_data(ext::DataType::Id, &id)
.map_err(|e| e.into())
}
fn get_version() -> Result<sys::DeviceVersion, Error> {
ext::get_data::<sys::DeviceVersion>(ext::DataType::Version)
.map_err(|e| e.into())
}
fn set_version(version: sys::DeviceVersion) -> Result<(), Error> {
ext::set_data(ext::DataType::Version, &version)
.map_err(|e| e.into())
}
fn get_name() -> Result<sys::DeviceString, Error> {
get_string(ext::DataType::Name)
}
fn set_name(name: &sys::DeviceString) -> Result<(), Error> {
set_string(ext::DataType::Name, name)
}
fn get_manufacturer() -> Result<sys::DeviceString, Error> {
get_string(ext::DataType::Manufacturer)
}
fn set_manufacturer(manufacturer: &sys::DeviceString) -> Result<(), Error> {
set_string(ext::DataType::Manufacturer, manufacturer)
}
fn get_string(data_type: ext::DataType) -> Result<sys::DeviceString, Error> {
ext::get_data::<sys::DeviceString>(data_type)
.map_err(|e| e.into())
}
fn set_string(data_type: ext::DataType, string: &sys::DeviceString) -> Result<(), Error> {
ext::set_data(data_type, string)
.map_err(|e| e.into())
}
}
fn main() {
let mut device = utils::Device::new().unwrap();
device.show();
device.set_id(607).unwrap();
device.set_version(8.9).unwrap();
device.set_name("Robot 🤖").unwrap();
device.set_manufacturer("🎃 jack o'lantern").unwrap();
device.show();
}
#include "string_ref.h"
#include <assert.h> // assert
#include <stdio.h> // fprintf, stderr
#include <stdlib.h> // calloc, free
#include <string.h> // memcpy, strcpy, strlen
#define DEBUG 0 // Set 1 to log the debugging messages.
#define LOG(...) DEBUG && fprintf(stderr, __VA_ARGS__)
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
// To make it simple, we just use `char*` a string's inner data.
// In real world, one character in a string may require mutiple bytes,
// and the string may have different encoding formats.
typedef struct {
char* data; // uint8_t or uint16_t, depending on format ...
// Other string info: string length, encoding format, ..., etc.
} String;
// Private APIs
// ============================================================================
StringRef get_ref_from_string(String* s) {
assert(s);
return (StringRef) s;
}
String* get_string_from_ref(StringRef string_ref) {
assert(string_ref);
return (String*) string_ref;
}
char* clone_c_string(const char* c_string) {
if (!c_string) {
return NULL;
}
size_t length = strlen(c_string) + 1; // + 1 for '\0'.
size_t char_size = sizeof(char);
size_t str_size = length * char_size;
char* clone = (char*) calloc(1, str_size);
assert(clone);
strcpy(clone, c_string);
return clone;
}
// Public APIs
// ============================================================================
StringRef new_string_by_clone(StringRef string_ref) {
if (!string_ref) {
return NULL;
}
String* string = get_string_from_ref(string_ref);
const char* c_string = (const char*) string->data;
if (!c_string) {
return NULL;
}
return new_string_by_clone_raw(c_string);
}
StringRef new_string_by_clone_raw(const char* c_string) {
if (!c_string) {
return NULL;
}
String* string = (String*) calloc(1, sizeof(String));
assert(string);
string->data = clone_c_string(c_string);
LOG("[%s] allocate String @ %p\n", __func__, string);
return get_ref_from_string(string);
}
void release_string(StringRef string_ref) {
if (!string_ref) {
return;
}
String* string = get_string_from_ref(string_ref);
if (string->data) {
free(string->data);
}
free(string);
LOG("[%s] deallocate String @ %p\n", __func__, string);
}
void get_string_bytes(StringRef string_ref, size_t* size, uint8_t* buffer) {
if (string_ref == NULL) {
return;
}
String* string = get_string_from_ref(string_ref);
const char* c_string = (const char*) string->data;
// Calculate byte size until '\0'.
size_t str_size = strlen(c_string) + 1; // + 1 for '\0'.
if (buffer == NULL) {
*size = str_size;
return;
}
size_t copy_size = MIN(*size, str_size);
memcpy((void*) buffer, (const void *) c_string, copy_size);
}
#ifndef STRING_REF_H
#define STRING_REF_H
#include <stddef.h> // size_t
#include <stdint.h> // uint8_t
// Handle class for our string implementation.
typedef void* StringRef;
#ifdef __cplusplus
extern "C"
{
#endif
// Copy the whole string.
// Return NULL if string_ref is NULL.
StringRef new_string_by_clone(StringRef string_ref);
// Create a new string by copying the raw C string data.
// Return NULL if string_ref is NULL.
StringRef new_string_by_clone_raw(const char* c_string);
// Free the string and dellocate its underlying data.
// Do nothing if string_ref is NULL.
void release_string(StringRef string_ref);
// Copy the underlying bytes of the string from string_ref to the buffer.
// 1. Do nothing if string_ref is NULL.
// 2. Otherwise,
// a. if the buffer is NULL, then size will be assigned to the size of the
// string, including nul-terminator('\0').
// b. if the buffer is not NULL, we will copy N bytes into the buffer,
// where N = min(string's size, size). The nul-terminator('\0') will
// only be copied into the buffer when size is greater than the string's
// size.
void get_string_bytes(StringRef string_ref, size_t* size, uint8_t* buffer);
#ifdef __cplusplus
}
#endif
#endif // STRING_REF_H
mod device {
use std::os::raw::c_void;
pub type Status = i32;
pub const OK: Status = 0;
pub const BAD_POINTER: Status = 1;
pub const BAD_SIZE: Status = 2;
pub const BAD_TYPE: Status = 3;
pub const NO_DATA: Status = 4;
pub type DataType = i32;
pub const ID: DataType = 0;
pub const VERSION: DataType = 1;
pub const NAME: DataType = 2;
pub const MANUFACTURER: DataType = 3;
pub type DeviceId = u32;
pub type DeviceVersion = f32;
pub type DeviceString = super::string_ref::StringRef;
#[link(name = "device")] // libdevice
extern "C" {
pub fn get_device_data(data_type: DataType, size: usize, data: *mut c_void) -> Status;
pub fn set_device_data(data_type: DataType, size: usize, data: *const c_void) -> Status;
}
}
mod string_ref {
use std::os::raw::{c_char, c_void};
pub type StringRef = *mut c_void;
#[link(name = "stringref")] // libstringref
extern "C" {
pub fn new_string_by_clone(string_ref: StringRef) -> StringRef;
pub fn new_string_by_clone_raw(c_string: *const c_char) -> StringRef;
pub fn get_string_bytes(string_ref: StringRef, size: *mut usize, buffer: *mut u8);
pub fn release_string(string_ref: StringRef);
}
}
pub use self::device::*;
pub use self::string_ref::*;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment