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: &HashMap<String, Value>) -> Result<(i32, HashMap<String, Value>), 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 let mut final_vars = HashMap::new();
185 for i in 0..len {
186 let slot = &slots_slice[i];
187 let value = match slot.tag {
188 Tag::Int => Value::Int(slot.data as i64),
189 Tag::Float => Value::Float(f64::from_bits(slot.data)),
190 Tag::Ptr => Value::Ptr(slot.data as *mut u8),
191 Tag::Null => Value::Null,
192 };
193 final_vars.insert(vars_vec[i].0.clone(), value);
194 }
195
196 Ok((exit_code, final_vars))
197 }
198}
199
200#[unsafe(no_mangle)]
201pub extern "C" fn lasm_write(fd: i64, ptr: i64, len: i64) -> i64 {
202 if fd == 1 {
203 unsafe {
204 let slice = std::slice::from_raw_parts(ptr as *const u8, len as usize);
205 if let Ok(s) = std::str::from_utf8(slice) {
206 print!("{}", s);
207 use std::io::Write;
208 let _ = std::io::stdout().flush();
209 return len;
210 }
211 }
212 }
213 #[cfg(unix)]
214 unsafe {
215 return libc::write(fd as i32, ptr as *const libc::c_void, len as usize) as i64;
216 }
217 #[cfg(windows)]
218 unsafe {
219 return libc::write(fd as i32, ptr as *const libc::c_void, len as u32) as i64;
220 }
221 #[cfg(not(any(unix, windows)))]
222 {
223 -1
224 }
225}
226
227#[unsafe(no_mangle)]
228pub extern "C" fn lasm_read(fd: i64, ptr: i64, len: i64) -> i64 {
229 #[cfg(unix)]
230 unsafe {
231 return libc::read(fd as i32, ptr as *mut libc::c_void, len as usize) as i64;
232 }
233 #[cfg(windows)]
234 unsafe {
235 return libc::read(fd as i32, ptr as *mut libc::c_void, len as u32) as i64;
236 }
237 #[cfg(not(any(unix, windows)))]
238 {
239 -1
240 }
241}
242
243#[unsafe(no_mangle)]
244pub extern "C" fn write_float(value: f64) -> i64 {
245 print!("{}", value);
246 0
247}
248
249#[unsafe(no_mangle)]
250pub extern "C" fn lasm_malloc(size: i64) -> i64 {
251 use libc::malloc;
252 if size <= 0 {
253 return 0;
254 }
255 unsafe {
256 malloc(size as usize) as i64
257 }
258}
259
260#[unsafe(no_mangle)]
261pub extern "C" fn lasm_free(ptr: i64) {
262 use libc::free;
263 if ptr == 0 {
264 return;
265 }
266 unsafe {
267 free(ptr as *mut libc::c_void);
268 }
269}
270
271#[unsafe(no_mangle)]
272pub extern "C" fn lasm_sleep(milliseconds: i64) {
273 use std::thread;
274 use std::time::Duration;
275 thread::sleep(Duration::from_millis(milliseconds as u64));
276}
277
278#[unsafe(no_mangle)]
279pub extern "C" fn lasm_time() -> i64 {
280 use std::time::{SystemTime, UNIX_EPOCH};
281 let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
282 (now.as_secs() as i64 * 1_000_000_000) + now.subsec_nanos() as i64
283}
284
285#[unsafe(no_mangle)]
286pub extern "C" fn lasm_rand(seed: i64) -> i64 {
287 let mut s = seed as u32;
289 s = ((s as u64 * 1103515245u64 + 12345) % (1u64 << 31)) as u32;
290 let value = s as f64 / (1u32 << 31) as f64;
291 value.to_bits() as i64
292}
293
294#[unsafe(no_mangle)]
295pub extern "C" fn lasm_atoi(ptr: i64) -> i64 {
296 use std::ffi::CStr;
297 if ptr == 0 {
298 return 0;
299 }
300 unsafe {
301 let c_str = CStr::from_ptr(ptr as *const i8);
302 if let Ok(s) = c_str.to_str() {
303 s.parse().unwrap_or(0)
304 } else {
305 0
306 }
307 }
308}
309
310#[unsafe(no_mangle)]
311pub extern "C" fn lasm_system(cmd_ptr: i64) -> i64 {
312 use std::ffi::CStr;
313 use std::process::Command;
314
315 #[cfg(unix)]
316 unsafe {
317 let cmd_c = CStr::from_ptr(cmd_ptr as *const i8);
318 if let Ok(cmd_s) = cmd_c.to_str() {
319 match Command::new("sh").arg("-c").arg(cmd_s).status() {
320 Ok(status) => status.code().unwrap_or(-1) as i64,
321 Err(_) => -1,
322 }
323 } else {
324 -1
325 }
326 }
327 #[cfg(windows)]
328 unsafe {
329 let cmd_c = CStr::from_ptr(cmd_ptr as *const i8);
330 if let Ok(cmd_s) = cmd_c.to_str() {
331 match Command::new("cmd").arg("/C").arg(cmd_s).status() {
332 Ok(status) => status.code().unwrap_or(-1) as i64,
333 Err(_) => -1,
334 }
335 } else {
336 -1
337 }
338 }
339}
340
341#[unsafe(no_mangle)]
342pub extern "C" fn lasm_ends_with(str_ptr: i64, suffix_ptr: i64) -> i64 {
343 use std::ffi::CStr;
344 unsafe {
345 let str_c = CStr::from_ptr(str_ptr as *const i8);
346 let suffix_c = CStr::from_ptr(suffix_ptr as *const i8);
347
348 if let (Ok(str_s), Ok(suffix_s)) = (str_c.to_str(), suffix_c.to_str()) {
349 if str_s.ends_with(suffix_s) { 1 } else { 0 }
350 } else {
351 0
352 }
353 }
354}
355
356#[unsafe(no_mangle)]
357pub extern "C" fn lasm_trim(str_ptr: i64, out_ptr: i64) -> i64 {
358 use std::ffi::CStr;
359 unsafe {
360 let str_c = CStr::from_ptr(str_ptr as *const i8);
361 if let Ok(s) = str_c.to_str() {
362 let trimmed = s.trim();
363 let bytes = trimmed.as_bytes();
364 let len = bytes.len().min(255);
365 std::ptr::copy_nonoverlapping(bytes.as_ptr(), out_ptr as *mut u8, len);
366 *(out_ptr as *mut u8).add(len) = 0;
367 len as i64
368 } else {
369 0
370 }
371 }
372}
373
374#[unsafe(no_mangle)]
375pub extern "C" fn lasm_trim_start(str_ptr: i64, out_ptr: i64) -> i64 {
376 use std::ffi::CStr;
377 unsafe {
378 let str_c = CStr::from_ptr(str_ptr as *const i8);
379 if let Ok(s) = str_c.to_str() {
380 let trimmed = s.trim_start();
381 let bytes = trimmed.as_bytes();
382 let len = bytes.len().min(255);
383 std::ptr::copy_nonoverlapping(bytes.as_ptr(), out_ptr as *mut u8, len);
384 *(out_ptr as *mut u8).add(len) = 0;
385 len as i64
386 } else {
387 0
388 }
389 }
390}
391
392#[unsafe(no_mangle)]
393pub extern "C" fn lasm_trim_end(str_ptr: i64, out_ptr: i64) -> i64 {
394 use std::ffi::CStr;
395 unsafe {
396 let str_c = CStr::from_ptr(str_ptr as *const i8);
397 if let Ok(s) = str_c.to_str() {
398 let trimmed = s.trim_end();
399 let bytes = trimmed.as_bytes();
400 let len = bytes.len().min(255);
401 std::ptr::copy_nonoverlapping(bytes.as_ptr(), out_ptr as *mut u8, len);
402 *(out_ptr as *mut u8).add(len) = 0;
403 len as i64
404 } else {
405 0
406 }
407 }
408}
409
410#[unsafe(no_mangle)]
411pub extern "C" fn lasm_streq(str1_ptr: i64, str2_ptr: i64) -> i64 {
412 use std::ffi::CStr;
413 unsafe {
414 let str1_c = CStr::from_ptr(str1_ptr as *const i8);
415 let str2_c = CStr::from_ptr(str2_ptr as *const i8);
416 if let (Ok(s1), Ok(s2)) = (str1_c.to_str(), str2_c.to_str()) {
417 if s1 == s2 { 1 } else { 0 }
418 } else {
419 0
420 }
421 }
422}
423
424#[unsafe(no_mangle)]
425pub extern "C" fn lasm_isws(str_ptr: i64) -> i64 {
426 use std::ffi::CStr;
427 unsafe {
428 let str_c = CStr::from_ptr(str_ptr as *const i8);
429 if let Ok(s) = str_c.to_str() {
430 if s.trim().is_empty() { 1 } else { 0 }
431 } else {
432 0
433 }
434 }
435}
436
437#[unsafe(no_mangle)]
438#[allow(unused_variables)]
439pub extern "C" fn lasm_syscall(number: i64, arg0: i64, arg1: i64, arg2: i64, arg3: i64, arg4: i64, arg5: i64) -> i64 {
440 #[cfg(target_os = "macos")]
441 {
442 unsafe { libc::syscall(number as i32, arg0, arg1, arg2, arg3, arg4, arg5) as i64 }
443 }
444
445 #[cfg(target_os = "linux")]
446 {
447 unsafe { libc::syscall(number, arg0, arg1, arg2, arg3, arg4, arg5) }
448 }
449 #[cfg(windows)]
450 {
451 match number {
452 1 => { if arg0 == 1 { let buf = arg1 as *const u8;
455 let count = arg2 as usize;
456 unsafe {
457 let slice = std::slice::from_raw_parts(buf, count);
458 if let Ok(s) = std::str::from_utf8(slice) {
459 print!("{}", s);
460 std::io::Write::flush(&mut std::io::stdout()).ok();
461 count as i64
462 } else {
463 -1
464 }
465 }
466 } else {
467 -1
468 }
469 }
470 0 => { if arg0 == 0 { let buf = arg1 as *mut u8;
473 let count = arg2 as usize;
474 unsafe {
475 let mut input = String::new();
476 if std::io::stdin().read_line(&mut input).is_ok() {
477 let bytes = input.as_bytes();
478 let len = bytes.len().min(count);
479 std::ptr::copy_nonoverlapping(bytes.as_ptr(), buf, len);
480 len as i64
481 } else {
482 -1
483 }
484 }
485 } else {
486 -1
487 }
488 }
489 _ => -1
490 }
491 }
492 #[cfg(not(any(unix, windows)))]
493 {
494 -1
495 }
496}
497
498#[unsafe(no_mangle)]
499pub extern "C" fn lasm_print_int(value: i64) {
500 print!("{}", value);
501 use std::io::Write;
502 let _ = std::io::stdout().flush();
503}
504
505#[unsafe(no_mangle)]
506pub extern "C" fn lasm_print_float(bits: i64) {
507 let value = f64::from_bits(bits as u64);
508 print!("{}", value);
509 use std::io::Write;
510 let _ = std::io::stdout().flush();
511}
512
513#[unsafe(no_mangle)]
514pub extern "C" fn lasm_print_str(ptr: i64) {
515 use std::ffi::CStr;
516 if ptr == 0 {
517 return;
518 }
519 unsafe {
520 let c_str = CStr::from_ptr(ptr as *const i8);
521 if let Ok(s) = c_str.to_str() {
522 print!("{}", s);
523 }
524 }
525 use std::io::Write;
526 let _ = std::io::stdout().flush();
527}
528
529#[unsafe(no_mangle)]
530pub extern "C" fn lasm_itoa(value: i64, buf: i64, size: i64) -> i64 {
531 if buf == 0 || size <= 0 {
532 return 0;
533 }
534 let s = format!("{}", value);
535 let bytes = s.as_bytes();
536 let len = bytes.len().min(size as usize - 1);
537 unsafe {
538 std::ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, len);
539 *(buf as *mut u8).add(len) = 0;
540 }
541 len as i64
542}
543
544#[unsafe(no_mangle)]
545pub extern "C" fn lasm_fadd(a_bits: i64, b_bits: i64) -> i64 {
546 let a = f64::from_bits(a_bits as u64);
547 let b = f64::from_bits(b_bits as u64);
548 (a + b).to_bits() as i64
549}
550
551#[unsafe(no_mangle)]
552pub extern "C" fn lasm_fsub(a_bits: i64, b_bits: i64) -> i64 {
553 let a = f64::from_bits(a_bits as u64);
554 let b = f64::from_bits(b_bits as u64);
555 (a - b).to_bits() as i64
556}
557
558#[unsafe(no_mangle)]
559pub extern "C" fn lasm_fmul(a_bits: i64, b_bits: i64) -> i64 {
560 let a = f64::from_bits(a_bits as u64);
561 let b = f64::from_bits(b_bits as u64);
562 (a * b).to_bits() as i64
563}
564
565#[unsafe(no_mangle)]
566pub extern "C" fn lasm_fdiv(a_bits: i64, b_bits: i64) -> i64 {
567 let a = f64::from_bits(a_bits as u64);
568 let b = f64::from_bits(b_bits as u64);
569 (a / b).to_bits() as i64
570}
571
572#[unsafe(no_mangle)]
573pub extern "C" fn lasm_itof(value: i64) -> i64 {
574 (value as f64).to_bits() as i64
575}
576
577#[unsafe(no_mangle)]
578pub extern "C" fn lasm_ftoi(bits: i64) -> i64 {
579 let f = f64::from_bits(bits as u64);
580 f as i64
581}
582
583#[unsafe(no_mangle)]
584pub extern "C" fn lasm_fmod(a_bits: i64, b_bits: i64) -> i64 {
585 let a = f64::from_bits(a_bits as u64);
586 let b = f64::from_bits(b_bits as u64);
587 (a % b).to_bits() as i64
588}
589
590#[unsafe(no_mangle)]
591pub extern "C" fn lasm_atof(ptr: i64) -> i64 {
592 use std::ffi::CStr;
593 if ptr == 0 {
594 return 0.0f64.to_bits() as i64;
595 }
596 unsafe {
597 let c_str = CStr::from_ptr(ptr as *const i8);
598 if let Ok(s) = c_str.to_str() {
599 if let Ok(f) = s.parse::<f64>() {
600 f.to_bits() as i64
601 } else {
602 0.0f64.to_bits() as i64
603 }
604 } else {
605 0.0f64.to_bits() as i64
606 }
607 }
608}
609
610#[unsafe(no_mangle)]
611pub extern "C" fn lasm_ftoa(bits: i64, buf: i64, size: i64) -> i64 {
612 if buf == 0 || size <= 0 {
613 return 0;
614 }
615 let f = f64::from_bits(bits as u64);
616 let s = format!("{}", f);
617 let bytes = s.as_bytes();
618 let len = bytes.len().min(size as usize - 1);
619 unsafe {
620 std::ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, len);
621 *(buf as *mut u8).add(len) = 0;
622 }
623 len as i64
624}
625
626#[unsafe(no_mangle)]
627pub extern "C" fn lasm_memcpy(dst: i64, src: i64, len: i64) {
628 if dst == 0 || src == 0 || len <= 0 {
629 return;
630 }
631 unsafe {
632 std::ptr::copy_nonoverlapping(src as *const u8, dst as *mut u8, len as usize);
633 }
634}
635
636#[unsafe(no_mangle)]
637pub extern "C" fn lasm_memset(dst: i64, val: i64, len: i64) {
638 if dst == 0 || len <= 0 {
639 return;
640 }
641 unsafe {
642 std::ptr::write_bytes(dst as *mut u8, val as u8, len as usize);
643 }
644}
645
646#[unsafe(no_mangle)]
647pub extern "C" fn lasm_fmt_time(total_nanos: i64, fmt_ptr: i64, buffer: i64) -> i64 {
648 use std::ffi::CStr;
649 use std::time::{SystemTime, UNIX_EPOCH};
650
651 if buffer == 0 || fmt_ptr == 0 {
652 return 0;
653 }
654
655 let fmt_str = unsafe {
656 match CStr::from_ptr(fmt_ptr as *const i8).to_str() {
657 Ok(s) => s,
658 Err(_) => "%Y-%m-%d %H:%M:%S",
659 }
660 };
661
662 let total_seconds = if total_nanos == 0 {
663 let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
664 now.as_secs()
665 } else {
666 (total_nanos / 1_000_000_000) as u64
667 };
668
669 let _nanoseconds = if total_nanos == 0 {
670 let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
671 now.subsec_nanos()
672 } else {
673 (total_nanos % 1_000_000_000) as u32
674 };
675
676 let days_since_epoch = total_seconds / 86400;
677 let mut year = 1970;
678 let mut days = days_since_epoch;
679
680 loop {
681 let is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
682 let days_in_year = if is_leap { 366 } else { 365 };
683 if days >= days_in_year {
684 days -= days_in_year;
685 year += 1;
686 } else {
687 break;
688 }
689 }
690
691 let month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
692 let is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
693 let mut month = 1;
694 let mut day = days + 1;
695
696 for (i, &days_in_month) in month_days.iter().enumerate() {
697 let actual_days = if i == 1 && is_leap { days_in_month + 1 } else { days_in_month };
698 if day > actual_days {
699 day -= actual_days;
700 month += 1;
701 } else {
702 break;
703 }
704 }
705
706 let seconds_in_day = total_seconds % 86400;
707 let hour = seconds_in_day / 3600;
708 let minute = (seconds_in_day % 3600) / 60;
709 let second = seconds_in_day % 60;
710
711 let mut result = String::new();
712 let mut chars = fmt_str.chars().peekable();
713
714 while let Some(ch) = chars.next() {
715 if ch == '%' {
716 if let Some(spec) = chars.next() {
717 match spec {
718 'Y' => result.push_str(&format!("{:04}", year)),
719 'm' => result.push_str(&format!("{:02}", month)),
720 'd' => result.push_str(&format!("{:02}", day)),
721 'H' => result.push_str(&format!("{:02}", hour)),
722 'M' => result.push_str(&format!("{:02}", minute)),
723 'S' => result.push_str(&format!("{:02}", second)),
724 '%' => result.push('%'),
725 _ => { result.push('%'); result.push(spec); }
726 }
727 } else {
728 result.push('%');
729 }
730 } else {
731 result.push(ch);
732 }
733 }
734
735 let bytes = result.as_bytes();
736 let len = bytes.len().min(255);
737
738 unsafe {
739 std::ptr::copy_nonoverlapping(bytes.as_ptr(), buffer as *mut u8, len);
740 *(buffer as *mut u8).add(len) = 0;
741 }
742
743 len as i64
744}
745
746#[unsafe(no_mangle)]
747pub extern "C" fn lasm_starts_with(str_ptr: i64, prefix_ptr: i64) -> i64 {
748 use std::ffi::CStr;
749 if str_ptr == 0 || prefix_ptr == 0 {
750 return 0;
751 }
752 unsafe {
753 let str_cstr = CStr::from_ptr(str_ptr as *const i8);
754 let prefix_cstr = CStr::from_ptr(prefix_ptr as *const i8);
755 if let (Ok(s), Ok(p)) = (str_cstr.to_str(), prefix_cstr.to_str()) {
756 if s.starts_with(p) { 1 } else { 0 }
757 } else {
758 0
759 }
760 }
761}
762
763#[unsafe(no_mangle)]
764pub extern "C" fn lasm_str_len(str_ptr: i64) -> i64 {
765 use std::ffi::CStr;
766 if str_ptr == 0 {
767 return 0;
768 }
769 unsafe {
770 CStr::from_ptr(str_ptr as *const i8).to_bytes().len() as i64
771 }
772}
773
774#[unsafe(no_mangle)]
775pub extern "C" fn lasm_strcmp(str1_ptr: i64, str2_ptr: i64) -> i64 {
776 if str1_ptr == 0 || str2_ptr == 0 {
777 return if str1_ptr == str2_ptr { 0 } else { 1 };
778 }
779 unsafe {
780 let s1 = CStr::from_ptr(str1_ptr as *const i8).to_bytes();
781 let s2 = CStr::from_ptr(str2_ptr as *const i8).to_bytes();
782 s1.cmp(s2) as i64
783 }
784}
785
786#[unsafe(no_mangle)]
787pub extern "C" fn lasm_strcpy(dst: i64, src: i64) -> i64 {
788 if dst == 0 || src == 0 {
789 return 0;
790 }
791 unsafe {
792 let src_str = CStr::from_ptr(src as *const i8).to_bytes();
793 let dst_slice = std::slice::from_raw_parts_mut(dst as *mut u8, src_str.len() + 1);
794 dst_slice[..src_str.len()].copy_from_slice(src_str);
795 dst_slice[src_str.len()] = 0;
796 dst
797 }
798}
799
800#[unsafe(no_mangle)]
801pub extern "C" fn lasm_flush(fd: i64) {
802 if fd == 1 {
803 use std::io::Write;
804 let _ = std::io::stdout().flush();
805 } else if fd == 2 {
806 use std::io::Write;
807 let _ = std::io::stderr().flush();
808 }
809}