1use std::collections::HashMap;
2#[cfg(feature = "dbg")]
3use std::fmt::Debug;
4use super::{LasmError, Value};
5use std::ffi::{CString, CStr};
6
7#[cfg(feature = "llvm")]
8use inkwell::execution_engine::{JitFunction, ExecutionEngine};
9
10#[cfg(feature = "cranelift")]
11use {cranelift_jit::JITModule, cranelift_module::FuncId};
12
13#[repr(u8)]
15#[derive(Clone, Copy, Debug)]
16pub enum Tag {
17 Int = 0,
18 Float = 1,
19 Ptr = 2,
20 Null = 3,
21}
22
23#[repr(C)]
25#[derive(Clone, Copy, Debug)]
26pub struct Slot {
27 pub tag: Tag,
28 pub _pad: [u8; 7],
29 pub data: u64,
30}
31
32#[cfg(feature = "dbg")]
34#[derive(Debug, Clone, PartialEq)]
35pub struct DebugInfo {
36 #[cfg(feature = "llvm")]
37 pub llvm_ir: String,
38 #[cfg(feature = "cranelift")]
39 pub cranelift_ir: String,
40 #[cfg(feature = "llvm")]
41 pub var_indices: HashMap<String, usize>,
42}
43
44#[cfg(not(feature = "dbg"))]
46#[repr(transparent)]
47#[derive(Clone, Copy)]
48pub struct DebugInfo;
49
50pub struct LasmFunction {
52 #[cfg(feature = "llvm")]
53 jit_fn: JitFunction<'static, unsafe extern "C" fn(*mut Slot, usize) -> i32>,
54 #[cfg(feature = "llvm")]
55 _ee: ExecutionEngine<'static>,
56 #[cfg(feature = "llvm")]
57 _string_storage: Vec<Box<Vec<u8>>>,
58
59 #[cfg(feature = "cranelift")]
60 jit_module: JITModule,
61 #[cfg(feature = "cranelift")]
62 func_id: FuncId,
63
64 #[allow(dead_code)]
65 dbg_info: Option<DebugInfo>,
66
67 variables: Vec<String>,
68}
69
70impl LasmFunction {
71 #[cfg(feature = "llvm")]
73 pub(crate) fn new_llvm(
74 jit_fn: JitFunction<'static, unsafe extern "C" fn(*mut Slot, usize) -> i32>,
75 ee: ExecutionEngine<'static>,
76 variables: Vec<String>,
77 string_storage: Vec<Box<Vec<u8>>>,
78 ) -> Self {
79 Self {
80 jit_fn,
81 _ee: ee,
82 _string_storage: string_storage,
83 variables,
84 dbg_info: None,
85 }
86 }
87
88 #[cfg(feature = "cranelift")]
90 pub(crate) fn new_cranelift(jit_module: JITModule, func_id: FuncId, variables: Vec<String>) -> Self {
91 Self { jit_module, func_id, variables, dbg_info: None }
92 }
93
94 #[cfg(feature = "dbg")]
96 pub(crate) fn with_debug_info(mut self, dbg_info: DebugInfo) -> Self {
97 self.dbg_info = Some(dbg_info);
98 self
99 }
100
101 pub fn get_id(&self) -> usize {
104 #[cfg(feature = "llvm")]
105 {
106 (unsafe { self.jit_fn.as_raw() }) as *const () as usize
107 }
108 #[cfg(feature = "cranelift")]
109 {
110 self.func_id.as_u32() as usize
111 }
112 }
113
114 #[cfg(feature = "dbg")]
116 pub fn debug_info(&self) -> Option<&DebugInfo> {
117 self.dbg_info.as_ref()
118 }
119
120 #[cfg(feature = "dbg")]
123 pub fn get_ir(&self) -> Option<&str> {
124 self.dbg_info.as_ref().map(|info| {
125 #[cfg(feature = "llvm")]
126 { info.llvm_ir.as_str() }
127 #[cfg(feature = "cranelift")]
128 { info.cranelift_ir.as_str() }
129 })
130 }
131
132 #[cfg(not(feature = "dbg"))]
134 pub fn get_ir(&self) -> Option<&str> {
135 None
136 }
137
138 pub(crate) fn call(&self, variables: &mut HashMap<String, Value>) -> Result<i32, LasmError> {
140 let mut vars_vec: Vec<(String, Value)> = self.variables.iter().map(|var| {
141 let value = variables.get(var).cloned().unwrap_or(Value::Null);
142 (var.clone(), value)
143 }).collect();
144 vars_vec.sort_by(|a, b| a.0.cmp(&b.0));
145
146 let mut slots = Vec::with_capacity(vars_vec.len());
147 for (_, value) in &vars_vec {
148 let slot = match value {
149 Value::Int(i) => Slot { tag: Tag::Int, _pad: [0; 7], data: *i as u64 },
150 Value::Float(f) => Slot { tag: Tag::Float, _pad: [0; 7], data: f.to_bits() },
151 Value::String(s) => {
152 let c_str = CString::new(s.clone())
153 .map_err(|_| LasmError::Runtime("Invalid string".to_string()))?;
154 let ptr = c_str.as_ptr() as *mut u8;
155 std::mem::forget(c_str);
156 Slot { tag: Tag::Ptr, _pad: [0; 7], data: ptr as u64 }
157 }
158 Value::Ptr(p) => Slot { tag: Tag::Ptr, _pad: [0; 7], data: *p as u64 },
159 Value::Null => Slot { tag: Tag::Null, _pad: [0; 7], data: 0 },
160 };
161 slots.push(slot);
162 }
163
164 let mut slot_array = slots.into_boxed_slice();
165 let ptr = slot_array.as_mut_ptr();
166 let len = slot_array.len();
167 std::mem::forget(slot_array);
168
169 let exit_code = {
170 #[cfg(feature = "llvm")]
171 {
172 unsafe { self.jit_fn.call(ptr, len) }
173 }
174
175 #[cfg(feature = "cranelift")]
176 {
177 let code = self.jit_module.get_finalized_function(self.func_id);
178 let func: extern "C" fn(*mut Slot, usize) -> i32 = unsafe { std::mem::transmute(code) };
179 func(ptr, len)
180 }
181 };
182
183 let slots_slice = unsafe { std::slice::from_raw_parts(ptr, len) };
184 for i in 0..len {
185 let slot = &slots_slice[i];
186 let value = match slot.tag {
187 Tag::Int => Value::Int(slot.data as i64),
188 Tag::Float => Value::Float(f64::from_bits(slot.data)),
189 Tag::Ptr => Value::Ptr(slot.data as *mut u8),
190 Tag::Null => Value::Null,
191 };
192 variables.insert(vars_vec[i].0.clone(), value);
193 }
194
195 Ok(exit_code)
196 }
197}
198
199#[unsafe(no_mangle)]
200pub extern "C" fn lasm_write(fd: i64, ptr: i64, len: i64) -> i64 {
201 if fd == 1 {
202 unsafe {
203 let slice = std::slice::from_raw_parts(ptr as *const u8, len as usize);
204 if let Ok(s) = std::str::from_utf8(slice) {
205 print!("{}", s);
206 use std::io::Write;
207 let _ = std::io::stdout().flush();
208 return len;
209 }
210 }
211 }
212 #[cfg(unix)]
213 unsafe {
214 return libc::write(fd as i32, ptr as *const libc::c_void, len as usize) as i64;
215 }
216 #[cfg(windows)]
217 unsafe {
218 return libc::write(fd as i32, ptr as *const libc::c_void, len as u32) as i64;
219 }
220 #[cfg(not(any(unix, windows)))]
221 {
222 -1
223 }
224}
225
226#[unsafe(no_mangle)]
227pub extern "C" fn lasm_read(fd: i64, ptr: i64, len: i64) -> i64 {
228 #[cfg(unix)]
229 unsafe {
230 return libc::read(fd as i32, ptr as *mut libc::c_void, len as usize) as i64;
231 }
232 #[cfg(windows)]
233 unsafe {
234 return libc::read(fd as i32, ptr as *mut libc::c_void, len as u32) as i64;
235 }
236 #[cfg(not(any(unix, windows)))]
237 {
238 -1
239 }
240}
241
242#[unsafe(no_mangle)]
243pub extern "C" fn write_float(value: f64) -> i64 {
244 print!("{}", value);
245 0
246}
247
248#[unsafe(no_mangle)]
249pub extern "C" fn lasm_malloc(size: i64) -> i64 {
250 use libc::malloc;
251 if size <= 0 {
252 return 0;
253 }
254 unsafe {
255 malloc(size as usize) as i64
256 }
257}
258
259#[unsafe(no_mangle)]
260pub extern "C" fn lasm_free(ptr: i64) {
261 use libc::free;
262 if ptr == 0 {
263 return;
264 }
265 unsafe {
266 free(ptr as *mut libc::c_void);
267 }
268}
269
270#[unsafe(no_mangle)]
271pub extern "C" fn lasm_sleep(milliseconds: i64) {
272 use std::thread;
273 use std::time::Duration;
274 thread::sleep(Duration::from_millis(milliseconds as u64));
275}
276
277#[unsafe(no_mangle)]
278pub extern "C" fn lasm_time() -> i64 {
279 use std::time::{SystemTime, UNIX_EPOCH};
280 let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
281 (now.as_secs() as i64 * 1_000_000_000) + now.subsec_nanos() as i64
282}
283
284#[unsafe(no_mangle)]
285pub extern "C" fn lasm_rand(seed: i64) -> i64 {
286 let mut s = seed as u32;
288 s = ((s as u64 * 1103515245u64 + 12345) % (1u64 << 31)) as u32;
289 let value = s as f64 / (1u32 << 31) as f64;
290 value.to_bits() as i64
291}
292
293#[unsafe(no_mangle)]
294pub extern "C" fn lasm_atoi(ptr: i64) -> i64 {
295 use std::ffi::CStr;
296 if ptr == 0 {
297 return 0;
298 }
299 unsafe {
300 let c_str = CStr::from_ptr(ptr as *const i8);
301 if let Ok(s) = c_str.to_str() {
302 s.parse().unwrap_or(0)
303 } else {
304 0
305 }
306 }
307}
308
309#[unsafe(no_mangle)]
310pub extern "C" fn lasm_system(cmd_ptr: i64) -> i64 {
311 use std::ffi::CStr;
312 use std::process::Command;
313
314 #[cfg(unix)]
315 unsafe {
316 let cmd_c = CStr::from_ptr(cmd_ptr as *const i8);
317 if let Ok(cmd_s) = cmd_c.to_str() {
318 match Command::new("sh").arg("-c").arg(cmd_s).status() {
319 Ok(status) => status.code().unwrap_or(-1) as i64,
320 Err(_) => -1,
321 }
322 } else {
323 -1
324 }
325 }
326 #[cfg(windows)]
327 unsafe {
328 let cmd_c = CStr::from_ptr(cmd_ptr as *const i8);
329 if let Ok(cmd_s) = cmd_c.to_str() {
330 match Command::new("cmd").arg("/C").arg(cmd_s).status() {
331 Ok(status) => status.code().unwrap_or(-1) as i64,
332 Err(_) => -1,
333 }
334 } else {
335 -1
336 }
337 }
338}
339
340#[unsafe(no_mangle)]
341pub extern "C" fn lasm_ends_with(str_ptr: i64, suffix_ptr: i64) -> i64 {
342 use std::ffi::CStr;
343 unsafe {
344 let str_c = CStr::from_ptr(str_ptr as *const i8);
345 let suffix_c = CStr::from_ptr(suffix_ptr as *const i8);
346
347 if let (Ok(str_s), Ok(suffix_s)) = (str_c.to_str(), suffix_c.to_str()) {
348 if str_s.ends_with(suffix_s) { 1 } else { 0 }
349 } else {
350 0
351 }
352 }
353}
354
355#[unsafe(no_mangle)]
356pub extern "C" fn lasm_trim(str_ptr: i64, out_ptr: i64) -> i64 {
357 use std::ffi::CStr;
358 unsafe {
359 let str_c = CStr::from_ptr(str_ptr as *const i8);
360 if let Ok(s) = str_c.to_str() {
361 let trimmed = s.trim();
362 let bytes = trimmed.as_bytes();
363 let len = bytes.len().min(255);
364 std::ptr::copy_nonoverlapping(bytes.as_ptr(), out_ptr as *mut u8, len);
365 *(out_ptr as *mut u8).add(len) = 0;
366 len as i64
367 } else {
368 0
369 }
370 }
371}
372
373#[unsafe(no_mangle)]
374pub extern "C" fn lasm_trim_start(str_ptr: i64, out_ptr: i64) -> i64 {
375 use std::ffi::CStr;
376 unsafe {
377 let str_c = CStr::from_ptr(str_ptr as *const i8);
378 if let Ok(s) = str_c.to_str() {
379 let trimmed = s.trim_start();
380 let bytes = trimmed.as_bytes();
381 let len = bytes.len().min(255);
382 std::ptr::copy_nonoverlapping(bytes.as_ptr(), out_ptr as *mut u8, len);
383 *(out_ptr as *mut u8).add(len) = 0;
384 len as i64
385 } else {
386 0
387 }
388 }
389}
390
391#[unsafe(no_mangle)]
392pub extern "C" fn lasm_trim_end(str_ptr: i64, out_ptr: i64) -> i64 {
393 use std::ffi::CStr;
394 unsafe {
395 let str_c = CStr::from_ptr(str_ptr as *const i8);
396 if let Ok(s) = str_c.to_str() {
397 let trimmed = s.trim_end();
398 let bytes = trimmed.as_bytes();
399 let len = bytes.len().min(255);
400 std::ptr::copy_nonoverlapping(bytes.as_ptr(), out_ptr as *mut u8, len);
401 *(out_ptr as *mut u8).add(len) = 0;
402 len as i64
403 } else {
404 0
405 }
406 }
407}
408
409#[unsafe(no_mangle)]
410pub extern "C" fn lasm_streq(str1_ptr: i64, str2_ptr: i64) -> i64 {
411 use std::ffi::CStr;
412 unsafe {
413 let str1_c = CStr::from_ptr(str1_ptr as *const i8);
414 let str2_c = CStr::from_ptr(str2_ptr as *const i8);
415 if let (Ok(s1), Ok(s2)) = (str1_c.to_str(), str2_c.to_str()) {
416 if s1 == s2 { 1 } else { 0 }
417 } else {
418 0
419 }
420 }
421}
422
423#[unsafe(no_mangle)]
424pub extern "C" fn lasm_isws(str_ptr: i64) -> i64 {
425 use std::ffi::CStr;
426 unsafe {
427 let str_c = CStr::from_ptr(str_ptr as *const i8);
428 if let Ok(s) = str_c.to_str() {
429 if s.trim().is_empty() { 1 } else { 0 }
430 } else {
431 0
432 }
433 }
434}
435
436#[unsafe(no_mangle)]
437#[allow(unused_variables)]
438pub extern "C" fn lasm_syscall(number: i64, arg0: i64, arg1: i64, arg2: i64, arg3: i64, arg4: i64, arg5: i64) -> i64 {
439 #[cfg(target_os = "macos")]
440 {
441 unsafe { libc::syscall(number as i32, arg0, arg1, arg2, arg3, arg4, arg5) as i64 }
442 }
443
444 #[cfg(target_os = "linux")]
445 {
446 unsafe { libc::syscall(number, arg0, arg1, arg2, arg3, arg4, arg5) }
447 }
448 #[cfg(windows)]
449 {
450 match number {
451 1 => { if arg0 == 1 { let buf = arg1 as *const u8;
454 let count = arg2 as usize;
455 unsafe {
456 let slice = std::slice::from_raw_parts(buf, count);
457 if let Ok(s) = std::str::from_utf8(slice) {
458 print!("{}", s);
459 std::io::Write::flush(&mut std::io::stdout()).ok();
460 count as i64
461 } else {
462 -1
463 }
464 }
465 } else {
466 -1
467 }
468 }
469 0 => { if arg0 == 0 { let buf = arg1 as *mut u8;
472 let count = arg2 as usize;
473 unsafe {
474 let mut input = String::new();
475 if std::io::stdin().read_line(&mut input).is_ok() {
476 let bytes = input.as_bytes();
477 let len = bytes.len().min(count);
478 std::ptr::copy_nonoverlapping(bytes.as_ptr(), buf, len);
479 len as i64
480 } else {
481 -1
482 }
483 }
484 } else {
485 -1
486 }
487 }
488 _ => -1
489 }
490 }
491 #[cfg(not(any(unix, windows)))]
492 {
493 -1
494 }
495}
496
497#[unsafe(no_mangle)]
498pub extern "C" fn lasm_print_int(value: i64) {
499 print!("{}", value);
500 use std::io::Write;
501 let _ = std::io::stdout().flush();
502}
503
504#[unsafe(no_mangle)]
505pub extern "C" fn lasm_print_float(bits: i64) {
506 let value = f64::from_bits(bits as u64);
507 print!("{}", value);
508 use std::io::Write;
509 let _ = std::io::stdout().flush();
510}
511
512#[unsafe(no_mangle)]
513pub extern "C" fn lasm_print_str(ptr: i64) {
514 use std::ffi::CStr;
515 if ptr == 0 {
516 return;
517 }
518 unsafe {
519 let c_str = CStr::from_ptr(ptr as *const i8);
520 if let Ok(s) = c_str.to_str() {
521 print!("{}", s);
522 }
523 }
524 use std::io::Write;
525 let _ = std::io::stdout().flush();
526}
527
528#[unsafe(no_mangle)]
529pub extern "C" fn lasm_itoa(value: i64, buf: i64, size: i64) -> i64 {
530 if buf == 0 || size <= 0 {
531 return 0;
532 }
533 let s = format!("{}", value);
534 let bytes = s.as_bytes();
535 let len = bytes.len().min(size as usize - 1);
536 unsafe {
537 std::ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, len);
538 *(buf as *mut u8).add(len) = 0;
539 }
540 len as i64
541}
542
543#[unsafe(no_mangle)]
544pub extern "C" fn lasm_fadd(a_bits: i64, b_bits: i64) -> i64 {
545 let a = f64::from_bits(a_bits as u64);
546 let b = f64::from_bits(b_bits as u64);
547 (a + b).to_bits() as i64
548}
549
550#[unsafe(no_mangle)]
551pub extern "C" fn lasm_fsub(a_bits: i64, b_bits: i64) -> i64 {
552 let a = f64::from_bits(a_bits as u64);
553 let b = f64::from_bits(b_bits as u64);
554 (a - b).to_bits() as i64
555}
556
557#[unsafe(no_mangle)]
558pub extern "C" fn lasm_fmul(a_bits: i64, b_bits: i64) -> i64 {
559 let a = f64::from_bits(a_bits as u64);
560 let b = f64::from_bits(b_bits as u64);
561 (a * b).to_bits() as i64
562}
563
564#[unsafe(no_mangle)]
565pub extern "C" fn lasm_fdiv(a_bits: i64, b_bits: i64) -> i64 {
566 let a = f64::from_bits(a_bits as u64);
567 let b = f64::from_bits(b_bits as u64);
568 (a / b).to_bits() as i64
569}
570
571#[unsafe(no_mangle)]
572pub extern "C" fn lasm_itof(value: i64) -> i64 {
573 (value as f64).to_bits() as i64
574}
575
576#[unsafe(no_mangle)]
577pub extern "C" fn lasm_ftoi(bits: i64) -> i64 {
578 let f = f64::from_bits(bits as u64);
579 f as i64
580}
581
582#[unsafe(no_mangle)]
583pub extern "C" fn lasm_fmod(a_bits: i64, b_bits: i64) -> i64 {
584 let a = f64::from_bits(a_bits as u64);
585 let b = f64::from_bits(b_bits as u64);
586 (a % b).to_bits() as i64
587}
588
589#[unsafe(no_mangle)]
590pub extern "C" fn lasm_atof(ptr: i64) -> i64 {
591 use std::ffi::CStr;
592 if ptr == 0 {
593 return 0.0f64.to_bits() as i64;
594 }
595 unsafe {
596 let c_str = CStr::from_ptr(ptr as *const i8);
597 if let Ok(s) = c_str.to_str() {
598 if let Ok(f) = s.parse::<f64>() {
599 f.to_bits() as i64
600 } else {
601 0.0f64.to_bits() as i64
602 }
603 } else {
604 0.0f64.to_bits() as i64
605 }
606 }
607}
608
609#[unsafe(no_mangle)]
610pub extern "C" fn lasm_ftoa(bits: i64, buf: i64, size: i64) -> i64 {
611 if buf == 0 || size <= 0 {
612 return 0;
613 }
614 let f = f64::from_bits(bits as u64);
615 let s = format!("{}", f);
616 let bytes = s.as_bytes();
617 let len = bytes.len().min(size as usize - 1);
618 unsafe {
619 std::ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, len);
620 *(buf as *mut u8).add(len) = 0;
621 }
622 len as i64
623}
624
625#[unsafe(no_mangle)]
626pub extern "C" fn lasm_memcpy(dst: i64, src: i64, len: i64) {
627 if dst == 0 || src == 0 || len <= 0 {
628 return;
629 }
630 unsafe {
631 std::ptr::copy_nonoverlapping(src as *const u8, dst as *mut u8, len as usize);
632 }
633}
634
635#[unsafe(no_mangle)]
636pub extern "C" fn lasm_memset(dst: i64, val: i64, len: i64) {
637 if dst == 0 || len <= 0 {
638 return;
639 }
640 unsafe {
641 std::ptr::write_bytes(dst as *mut u8, val as u8, len as usize);
642 }
643}
644
645#[unsafe(no_mangle)]
646pub extern "C" fn lasm_fmt_time(total_nanos: i64, fmt_ptr: i64, buffer: i64) -> i64 {
647 use std::ffi::CStr;
648 use std::time::{SystemTime, UNIX_EPOCH};
649
650 if buffer == 0 || fmt_ptr == 0 {
651 return 0;
652 }
653
654 let fmt_str = unsafe {
655 match CStr::from_ptr(fmt_ptr as *const i8).to_str() {
656 Ok(s) => s,
657 Err(_) => "%Y-%m-%d %H:%M:%S",
658 }
659 };
660
661 let total_seconds = if total_nanos == 0 {
662 let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
663 now.as_secs()
664 } else {
665 (total_nanos / 1_000_000_000) as u64
666 };
667
668 let _nanoseconds = if total_nanos == 0 {
669 let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
670 now.subsec_nanos()
671 } else {
672 (total_nanos % 1_000_000_000) as u32
673 };
674
675 let days_since_epoch = total_seconds / 86400;
676 let mut year = 1970;
677 let mut days = days_since_epoch;
678
679 loop {
680 let is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
681 let days_in_year = if is_leap { 366 } else { 365 };
682 if days >= days_in_year {
683 days -= days_in_year;
684 year += 1;
685 } else {
686 break;
687 }
688 }
689
690 let month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
691 let is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
692 let mut month = 1;
693 let mut day = days + 1;
694
695 for (i, &days_in_month) in month_days.iter().enumerate() {
696 let actual_days = if i == 1 && is_leap { days_in_month + 1 } else { days_in_month };
697 if day > actual_days {
698 day -= actual_days;
699 month += 1;
700 } else {
701 break;
702 }
703 }
704
705 let seconds_in_day = total_seconds % 86400;
706 let hour = seconds_in_day / 3600;
707 let minute = (seconds_in_day % 3600) / 60;
708 let second = seconds_in_day % 60;
709
710 let mut result = String::new();
711 let mut chars = fmt_str.chars().peekable();
712
713 while let Some(ch) = chars.next() {
714 if ch == '%' {
715 if let Some(spec) = chars.next() {
716 match spec {
717 'Y' => result.push_str(&format!("{:04}", year)),
718 'm' => result.push_str(&format!("{:02}", month)),
719 'd' => result.push_str(&format!("{:02}", day)),
720 'H' => result.push_str(&format!("{:02}", hour)),
721 'M' => result.push_str(&format!("{:02}", minute)),
722 'S' => result.push_str(&format!("{:02}", second)),
723 '%' => result.push('%'),
724 _ => { result.push('%'); result.push(spec); }
725 }
726 } else {
727 result.push('%');
728 }
729 } else {
730 result.push(ch);
731 }
732 }
733
734 let bytes = result.as_bytes();
735 let len = bytes.len().min(255);
736
737 unsafe {
738 std::ptr::copy_nonoverlapping(bytes.as_ptr(), buffer as *mut u8, len);
739 *(buffer as *mut u8).add(len) = 0;
740 }
741
742 len as i64
743}
744
745#[unsafe(no_mangle)]
746pub extern "C" fn lasm_starts_with(str_ptr: i64, prefix_ptr: i64) -> i64 {
747 use std::ffi::CStr;
748 if str_ptr == 0 || prefix_ptr == 0 {
749 return 0;
750 }
751 unsafe {
752 let str_cstr = CStr::from_ptr(str_ptr as *const i8);
753 let prefix_cstr = CStr::from_ptr(prefix_ptr as *const i8);
754 if let (Ok(s), Ok(p)) = (str_cstr.to_str(), prefix_cstr.to_str()) {
755 if s.starts_with(p) { 1 } else { 0 }
756 } else {
757 0
758 }
759 }
760}
761
762#[unsafe(no_mangle)]
763pub extern "C" fn lasm_str_len(str_ptr: i64) -> i64 {
764 use std::ffi::CStr;
765 if str_ptr == 0 {
766 return 0;
767 }
768 unsafe {
769 CStr::from_ptr(str_ptr as *const i8).to_bytes().len() as i64
770 }
771}
772
773#[unsafe(no_mangle)]
774pub extern "C" fn lasm_strcmp(str1_ptr: i64, str2_ptr: i64) -> i64 {
775 if str1_ptr == 0 || str2_ptr == 0 {
776 return if str1_ptr == str2_ptr { 0 } else { 1 };
777 }
778 unsafe {
779 let s1 = CStr::from_ptr(str1_ptr as *const i8).to_bytes();
780 let s2 = CStr::from_ptr(str2_ptr as *const i8).to_bytes();
781 s1.cmp(s2) as i64
782 }
783}
784
785#[unsafe(no_mangle)]
786pub extern "C" fn lasm_strcpy(dst: i64, src: i64) -> i64 {
787 if dst == 0 || src == 0 {
788 return 0;
789 }
790 unsafe {
791 let src_str = CStr::from_ptr(src as *const i8).to_bytes();
792 let dst_slice = std::slice::from_raw_parts_mut(dst as *mut u8, src_str.len() + 1);
793 dst_slice[..src_str.len()].copy_from_slice(src_str);
794 dst_slice[src_str.len()] = 0;
795 dst
796 }
797}
798
799#[unsafe(no_mangle)]
800pub extern "C" fn lasm_flush(fd: i64) {
801 if fd == 1 {
802 use std::io::Write;
803 let _ = std::io::stdout().flush();
804 } else if fd == 2 {
805 use std::io::Write;
806 let _ = std::io::stderr().flush();
807 }
808}