verilization_bindings_c_api_core/
lib.rs

1//! Defines the C API for use in bindings.
2//! Most notably for WebAssembly.
3
4use verilization_compiler::{lang, model, parser, load_all_models, VError, MemoryOutputHandler};
5use lang::{GeneratorError, Language, LanguageRegistry, LanguageHandler};
6
7use std::ffi::{c_void, OsString};
8use std::collections::HashMap;
9
10
11
12/// Represents a string with a length followed by the UTF-8 data.
13#[repr(C)]
14pub struct APIString {
15    length: usize,
16    data: [u8; 0],
17}
18
19impl APIString {
20    unsafe fn allocate(s: &str) -> *mut APIString {
21        let ptr = verilization_mem_alloc(std::mem::size_of::<APIString>() + s.len());
22        let api_str = ptr as *mut APIString;
23        (*api_str).length = s.len();
24        std::ptr::copy_nonoverlapping(s.as_ptr(), (*api_str).data.as_mut_ptr(), s.len());
25        api_str
26    }
27
28    fn to_str<'a>(&'a self) -> Option<&'a str> {
29        let data = &self.data as *const u8;
30        unsafe { std::str::from_utf8(std::slice::from_raw_parts(data, self.length)).ok() }
31    }
32}
33
34/// Represents a Result<T, String>.
35/// If is_error is true, then the error field of data is inhabited.
36/// Otherwise, the value field is inhabited.
37#[repr(C)]
38pub struct APIResult<T> {
39    is_error: usize,
40    data: APIResultPtr<T>,
41}
42
43/// Represents either a value or an error message.
44#[repr(C)]
45pub union APIResultPtr<T> {
46    error: *mut APIString,
47    value: *mut T,
48}
49
50/// An option defined by the name of the option and the value.
51#[repr(C)]
52pub struct LanguageOption {
53    name: *mut APIString,
54    value: *mut APIString,
55}
56
57/// An output file. Contains the file name and the content.
58#[repr(C)]
59pub struct OutputFileEntry {
60    name: *mut APIString,
61    length: usize,
62    content: *mut u8,
63}
64
65/// A map of output files.
66#[repr(C)]
67pub struct OutputFileMap {
68    length: usize,
69    entries: [OutputFileEntry; 0],
70}
71
72impl OutputFileMap {
73    unsafe fn allocate(map: &HashMap<String, Vec<u8>>) -> *mut OutputFileMap {
74        let ptr = verilization_mem_alloc(std::mem::size_of::<OutputFileMap>() + map.len() * std::mem::size_of::<OutputFileEntry>()) as *mut OutputFileMap;
75        (*ptr).length = map.len();
76
77        let entries = std::slice::from_raw_parts_mut((*ptr).entries.as_mut_ptr(), map.len());
78        for (index, (name, data)) in map.iter().enumerate() {
79            let entry: &mut OutputFileEntry = &mut entries[index];
80            entry.name = APIString::allocate(name);
81            entry.length = data.len();
82
83            let buffer = verilization_mem_alloc(data.len());
84            entry.content = buffer;
85            std::ptr::copy_nonoverlapping(data.as_ptr(), buffer, data.len());
86        }
87
88        ptr
89    }
90}
91
92
93/// Allocates a block of memory.
94///
95/// Used to allocate a block of memory for values passed to verilization.
96/// This is useful when hosting verilization as a WASM module.
97#[no_mangle]
98pub unsafe extern "C" fn verilization_mem_alloc(size: usize) -> *mut u8 {
99    std::alloc::alloc(std::alloc::Layout::from_size_align(size, std::mem::size_of::<*mut c_void>()).unwrap())
100}
101
102/// Free a block of memory.
103///
104/// Used to free a block of memory allocated by `verilization_mem_alloc`.
105/// Some values returned by functions should be freed using this function as well.
106/// The size must be the same size used to allocate the memory.
107#[no_mangle]
108pub unsafe extern "C" fn verilization_mem_free(size: usize, ptr: *mut u8) {
109    std::alloc::dealloc(ptr, std::alloc::Layout::from_size_align(size, std::mem::size_of::<*mut c_void>()).unwrap())
110}
111
112/// Parses verilization source files.
113///
114/// This function accepts an C-style array of strings. These strings contain the *content* of the files to be parsed.
115/// A success result should be released using `verilization_destroy`.
116/// An error result should be released using verilization_mem_free.
117#[no_mangle]
118pub unsafe extern "C" fn verilization_parse(nfiles: usize, files: *const *const APIString, result: *mut APIResult<model::Verilization>) {
119    let files = std::slice::from_raw_parts(files, nfiles);
120
121    *result = match verilization_parse_impl(files) {
122        Ok(model) => APIResult {
123            is_error: 0,
124            data: APIResultPtr {
125                value: Box::into_raw(Box::new(model)),
126            },
127        },
128        Err(err) => APIResult {
129            is_error: 1,
130            data: APIResultPtr {
131                error: APIString::allocate(&format!("{:?}", err)),
132            },
133        },
134    }
135}
136
137unsafe fn verilization_parse_impl(files: &[*const APIString]) -> Result<model::Verilization, VError> {
138    let models = files.iter().map(|content| {
139        let content = content.as_ref().expect("Pointer was null").to_str().expect("Invalid String");
140        let (_, model) = parser::parse_model(content)?;
141        let model = model()?;
142        Ok(model)
143    });
144
145    load_all_models(models)
146}
147
148/// Destroys a verilization model.
149#[no_mangle]
150pub unsafe extern "C" fn verilization_destroy(verilization: *mut model::Verilization) {
151    Box::from_raw(verilization);
152}
153
154/// Generates source to handle a file format defined by a verilizaiton model.
155///
156/// Generates a file map containing the files generated.
157/// The languge is a string indicating the language of the generated code.
158/// The options are a C-style array of the language options.
159/// These options are the same as the -o: flags (without the -o: prefix) to the command line interface.
160/// The result and all dependent pointers must be freed using verilization_mem_free.
161pub unsafe fn verilization_generate_impl<Registry: LanguageRegistry>(verilization: *const model::Verilization, language: *const APIString, noptions: usize, options: *const LanguageOption, result: *mut APIResult<OutputFileMap>, registry: &Registry) {
162    *result = match verilization_generate_impl_result(verilization, language, noptions, options, registry) {
163        Ok(map) => APIResult {
164            is_error: 0,
165            data: APIResultPtr {
166                value: map,
167            },
168        },
169        Err(err) => APIResult {
170            is_error: 1,
171            data: APIResultPtr {
172                error: APIString::allocate(&format!("{:?}", err)),
173            },
174        },
175    }
176}
177
178
179unsafe fn verilization_generate_impl_result<Registry: LanguageRegistry>(verilization: *const model::Verilization, language: *const APIString, noptions: usize, options: *const LanguageOption, registry: &Registry) -> Result<*mut OutputFileMap, GeneratorError> {
180    let verilization = verilization.as_ref().expect("Verilization pointer is null");
181    let language = language.as_ref().expect("Language string is null").to_str().expect("Language is invalid text");
182    let options = std::slice::from_raw_parts(options, noptions)
183        .iter()
184        .map(|option| {
185            let name = option.name.as_ref().expect("Option name is null").to_str().expect("Invalid option name text");
186            let value = option.value.as_ref().expect("Option value is null").to_str().expect("Invalid option value text");
187            (name, value)
188        })
189        .collect::<Vec<_>>();
190
191    let mut output = MemoryOutputHandler {
192        files: HashMap::new(),
193    };
194
195    match registry.handle_language(language, &mut VerilizationGenerateLang { verilization: verilization, options: options, output: &mut output, }) {
196        Some(result) => result?,
197        None =>Err(GeneratorError::UnknownLanguage(String::from(language)))?,
198    }
199
200    Ok(OutputFileMap::allocate(&output.files))
201}
202
203struct VerilizationGenerateLang<'a, Output> {
204    verilization: &'a model::Verilization,
205    options: Vec<(&'a str, &'a str)>,
206    output: &'a mut Output,
207}
208
209impl <'a, Output: for<'output> lang::OutputHandler<'output>> LanguageHandler for VerilizationGenerateLang<'a, Output> {
210    type Result = Result<(), GeneratorError>;
211
212    fn run<Lang: Language>(&mut self) -> Self::Result {
213        let mut lang_options = Lang::empty_options();
214        for (name, value) in &self.options {
215            Lang::add_option(&mut lang_options, name, OsString::from(value))?;
216        }
217        let lang_options = Lang::finalize_options(lang_options)?;
218    
219        Lang::generate(self.verilization, lang_options, self.output)
220    }
221}
222
223