1use crossbeam::channel::{Receiver, Sender};
2use indexmap::IndexMap;
3use num_bigint::BigInt;
4use parking_lot::{Mutex, RwLock};
5use std::cmp::Ordering;
6use std::collections::VecDeque;
7use std::fmt;
8use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
9use std::sync::Arc;
10use std::sync::Barrier;
11
12use crate::ast::{Block, ClassDef, EnumDef, StructDef, SubSigParam};
13use crate::error::StrykeResult;
14use crate::nanbox;
15use crate::perl_decode::decode_utf8_or_latin1;
16use crate::perl_regex::PerlCompiledRegex;
17
18#[derive(Debug)]
20pub struct StrykeAsyncTask {
21 pub(crate) result: Arc<Mutex<Option<StrykeResult<StrykeValue>>>>,
22 pub(crate) join: Arc<Mutex<Option<std::thread::JoinHandle<()>>>>,
23}
24
25impl Clone for StrykeAsyncTask {
26 fn clone(&self) -> Self {
27 Self {
28 result: self.result.clone(),
29 join: self.join.clone(),
30 }
31 }
32}
33
34impl StrykeAsyncTask {
35 pub fn await_result(&self) -> StrykeResult<StrykeValue> {
37 if let Some(h) = self.join.lock().take() {
38 let _ = h.join();
39 }
40 self.result
41 .lock()
42 .clone()
43 .unwrap_or_else(|| Ok(StrykeValue::UNDEF))
44 }
45}
46
47pub trait StrykeIterator: Send + Sync {
52 fn next_item(&self) -> Option<StrykeValue>;
54
55 fn collect_all(&self) -> Vec<StrykeValue> {
57 let mut out = Vec::new();
58 while let Some(v) = self.next_item() {
59 out.push(v);
60 }
61 out
62 }
63}
64
65impl fmt::Debug for dyn StrykeIterator {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 f.write_str("PerlIterator")
68 }
69}
70
71pub struct FsWalkIterator {
73 stack: Mutex<Vec<(std::path::PathBuf, String)>>,
75 buf: Mutex<Vec<(String, bool)>>, pending_dirs: Mutex<Vec<(std::path::PathBuf, String)>>,
79 files_only: bool,
81}
82
83impl FsWalkIterator {
84 pub fn new(dir: &str, files_only: bool) -> Self {
86 Self {
87 stack: Mutex::new(vec![(std::path::PathBuf::from(dir), String::new())]),
88 buf: Mutex::new(Vec::new()),
89 pending_dirs: Mutex::new(Vec::new()),
90 files_only,
91 }
92 }
93
94 fn refill(&self) -> bool {
97 loop {
98 let mut stack = self.stack.lock();
99 let mut pending = self.pending_dirs.lock();
101 while let Some(d) = pending.pop() {
102 stack.push(d);
103 }
104 drop(pending);
105
106 let (base, rel) = match stack.pop() {
107 Some(v) => v,
108 None => return false,
109 };
110 drop(stack);
111
112 let entries = match std::fs::read_dir(&base) {
113 Ok(e) => e,
114 Err(_) => continue, };
116 let mut children: Vec<(std::ffi::OsString, String, bool, bool)> = Vec::new();
117 for entry in entries.flatten() {
118 let ft = match entry.file_type() {
119 Ok(ft) => ft,
120 Err(_) => continue,
121 };
122 let os_name = entry.file_name();
123 let name = match os_name.to_str() {
124 Some(n) => n.to_string(),
125 None => continue,
126 };
127 let child_rel = if rel.is_empty() {
128 name.clone()
129 } else {
130 format!("{rel}/{name}")
131 };
132 children.push((os_name, child_rel, ft.is_file(), ft.is_dir()));
133 }
134 children.sort_by(|a, b| a.0.cmp(&b.0));
135
136 let mut buf = self.buf.lock();
137 let mut pending = self.pending_dirs.lock();
138 let mut subdirs = Vec::new();
139 for (os_name, child_rel, is_file, is_dir) in children {
140 if is_dir {
141 if !self.files_only {
142 buf.push((child_rel.clone(), true));
143 }
144 subdirs.push((base.join(os_name), child_rel));
145 } else if is_file && self.files_only {
146 buf.push((child_rel, false));
147 }
148 }
149 for s in subdirs.into_iter().rev() {
150 pending.push(s);
151 }
152 buf.reverse();
153 if !buf.is_empty() {
154 return true;
155 }
156 }
158 }
159}
160
161impl StrykeIterator for FsWalkIterator {
162 fn next_item(&self) -> Option<StrykeValue> {
163 loop {
164 {
165 let mut buf = self.buf.lock();
166 if let Some((path, _)) = buf.pop() {
167 return Some(StrykeValue::string(path));
168 }
169 }
170 if !self.refill() {
171 return None;
172 }
173 }
174 }
175}
176
177pub struct RevIterator {
185 source: Arc<dyn StrykeIterator>,
187 drained: Mutex<Option<Vec<StrykeValue>>>,
189}
190
191impl RevIterator {
192 pub fn new(source: Arc<dyn StrykeIterator>) -> Self {
194 Self {
195 source,
196 drained: Mutex::new(None),
197 }
198 }
199}
200
201impl StrykeIterator for RevIterator {
202 fn next_item(&self) -> Option<StrykeValue> {
203 let mut g = self.drained.lock();
204 if g.is_none() {
205 let mut buf = Vec::new();
206 while let Some(v) = self.source.next_item() {
207 buf.push(v);
208 }
209 *g = Some(buf);
210 }
211 g.as_mut().and_then(|v| v.pop())
214 }
215}
216
217#[derive(Debug)]
219pub struct PerlGenerator {
220 pub(crate) block: Block,
221 pub(crate) pc: Mutex<usize>,
222 pub(crate) scope_started: Mutex<bool>,
223 pub(crate) exhausted: Mutex<bool>,
224}
225
226pub type PerlSet = IndexMap<String, StrykeValue>;
228
229#[derive(Debug, Clone)]
231pub struct PerlHeap {
232 pub items: Vec<StrykeValue>,
234 pub cmp: Arc<StrykeSub>,
236}
237
238#[derive(Debug)]
245pub struct MutexHandle {
246 pub held: parking_lot::Mutex<bool>,
248 pub condvar: parking_lot::Condvar,
250}
251
252impl MutexHandle {
253 pub fn new() -> Self {
255 Self {
256 held: parking_lot::Mutex::new(false),
257 condvar: parking_lot::Condvar::new(),
258 }
259 }
260}
261
262impl Default for MutexHandle {
263 fn default() -> Self {
264 Self::new()
265 }
266}
267
268#[derive(Debug)]
274pub struct SemaphoreHandle {
275 pub permits: parking_lot::Mutex<i64>,
277 pub limit: i64,
279 pub condvar: parking_lot::Condvar,
281}
282
283impl SemaphoreHandle {
284 pub fn new(n: i64) -> Self {
286 Self {
287 permits: parking_lot::Mutex::new(n),
288 limit: n,
289 condvar: parking_lot::Condvar::new(),
290 }
291 }
292}
293
294#[derive(Debug, Clone)]
302pub struct RemoteSlot {
303 pub host: String,
305 pub pe_path: String,
307}
308
309#[cfg(test)]
310mod cluster_parsing_tests {
311 use super::*;
312
313 fn s(v: &str) -> StrykeValue {
314 StrykeValue::string(v.to_string())
315 }
316
317 #[test]
318 fn parses_simple_host() {
319 let c = RemoteCluster::from_list_args(&[s("host1")]).expect("parse");
320 assert_eq!(c.slots.len(), 1);
321 assert_eq!(c.slots[0].host, "host1");
322 assert_eq!(c.slots[0].pe_path, "stryke");
323 }
324
325 #[test]
326 fn parses_host_with_slot_count() {
327 let c = RemoteCluster::from_list_args(&[s("host1:4")]).expect("parse");
328 assert_eq!(c.slots.len(), 4);
329 assert!(c.slots.iter().all(|s| s.host == "host1"));
330 }
331
332 #[test]
333 fn parses_user_at_host_with_slots() {
334 let c = RemoteCluster::from_list_args(&[s("alice@build1:2")]).expect("parse");
335 assert_eq!(c.slots.len(), 2);
336 assert_eq!(c.slots[0].host, "alice@build1");
337 }
338
339 #[test]
340 fn parses_host_slots_stryke_path_triple() {
341 let c =
342 RemoteCluster::from_list_args(&[s("build1:3:/usr/local/bin/stryke")]).expect("parse");
343 assert_eq!(c.slots.len(), 3);
344 assert!(c.slots.iter().all(|sl| sl.host == "build1"));
345 assert!(c
346 .slots
347 .iter()
348 .all(|sl| sl.pe_path == "/usr/local/bin/stryke"));
349 }
350
351 #[test]
352 fn parses_multiple_hosts_in_one_call() {
353 let c = RemoteCluster::from_list_args(&[s("host1:2"), s("host2:1")]).expect("parse");
354 assert_eq!(c.slots.len(), 3);
355 assert_eq!(c.slots[0].host, "host1");
356 assert_eq!(c.slots[1].host, "host1");
357 assert_eq!(c.slots[2].host, "host2");
358 }
359
360 #[test]
361 fn parses_hashref_slot_form() {
362 let mut h = indexmap::IndexMap::new();
363 h.insert("host".to_string(), s("data1"));
364 h.insert("slots".to_string(), StrykeValue::integer(2));
365 h.insert("stryke".to_string(), s("/opt/stryke"));
366 let c = RemoteCluster::from_list_args(&[StrykeValue::hash(h)]).expect("parse");
367 assert_eq!(c.slots.len(), 2);
368 assert_eq!(c.slots[0].host, "data1");
369 assert_eq!(c.slots[0].pe_path, "/opt/stryke");
370 }
371
372 #[test]
373 fn parses_trailing_tunables_hashref() {
374 let mut tun = indexmap::IndexMap::new();
375 tun.insert("timeout".to_string(), StrykeValue::integer(30));
376 tun.insert("retries".to_string(), StrykeValue::integer(2));
377 tun.insert("connect_timeout".to_string(), StrykeValue::integer(5));
378 let c = RemoteCluster::from_list_args(&[s("h1:1"), StrykeValue::hash(tun)]).expect("parse");
379 assert_eq!(c.slots.len(), 1);
381 assert_eq!(c.job_timeout_ms, 30_000);
382 assert_eq!(c.max_attempts, 3); assert_eq!(c.connect_timeout_ms, 5_000);
384 }
385
386 #[test]
387 fn defaults_when_no_tunables() {
388 let c = RemoteCluster::from_list_args(&[s("h1")]).expect("parse");
389 assert_eq!(c.job_timeout_ms, RemoteCluster::DEFAULT_JOB_TIMEOUT_MS);
390 assert_eq!(c.max_attempts, RemoteCluster::DEFAULT_MAX_ATTEMPTS);
391 assert_eq!(
392 c.connect_timeout_ms,
393 RemoteCluster::DEFAULT_CONNECT_TIMEOUT_MS
394 );
395 }
396
397 #[test]
398 fn rejects_empty_cluster() {
399 assert!(RemoteCluster::from_list_args(&[]).is_err());
400 }
401
402 #[test]
403 fn slot_count_minimum_one() {
404 let c = RemoteCluster::from_list_args(&[s("h1:0")]).expect("parse");
405 assert_eq!(c.slots.len(), 1);
408 }
409}
410
411#[derive(Debug, Clone)]
420pub struct RemoteCluster {
421 pub slots: Vec<RemoteSlot>,
423 pub job_timeout_ms: u64,
425 pub max_attempts: u32,
427 pub connect_timeout_ms: u64,
429}
430
431impl RemoteCluster {
432 pub const DEFAULT_JOB_TIMEOUT_MS: u64 = 60_000;
434 pub const DEFAULT_MAX_ATTEMPTS: u32 = 3;
436 pub const DEFAULT_CONNECT_TIMEOUT_MS: u64 = 10_000;
438
439 pub fn from_list_args(items: &[StrykeValue]) -> Result<Self, String> {
453 let mut slots: Vec<RemoteSlot> = Vec::new();
454 let mut job_timeout_ms = Self::DEFAULT_JOB_TIMEOUT_MS;
455 let mut max_attempts = Self::DEFAULT_MAX_ATTEMPTS;
456 let mut connect_timeout_ms = Self::DEFAULT_CONNECT_TIMEOUT_MS;
457
458 let (slot_items, tunables) = if let Some(last) = items.last() {
460 let h = last
461 .as_hash_map()
462 .or_else(|| last.as_hash_ref().map(|r| r.read().clone()));
463 if let Some(map) = h {
464 let known = |k: &str| {
465 matches!(k, "timeout" | "retries" | "connect_timeout" | "job_timeout")
466 };
467 if !map.is_empty() && map.keys().all(|k| known(k.as_str())) {
468 (&items[..items.len() - 1], Some(map))
469 } else {
470 (items, None)
471 }
472 } else {
473 (items, None)
474 }
475 } else {
476 (items, None)
477 };
478
479 if let Some(map) = tunables {
480 if let Some(v) = map.get("timeout").or_else(|| map.get("job_timeout")) {
481 job_timeout_ms = (v.to_number() * 1000.0) as u64;
482 }
483 if let Some(v) = map.get("retries") {
484 max_attempts = v.to_int().max(0) as u32 + 1;
486 }
487 if let Some(v) = map.get("connect_timeout") {
488 connect_timeout_ms = (v.to_number() * 1000.0) as u64;
489 }
490 }
491
492 for it in slot_items {
493 if let Some(map) = it
495 .as_hash_map()
496 .or_else(|| it.as_hash_ref().map(|r| r.read().clone()))
497 {
498 let host = map
499 .get("host")
500 .map(|v| v.to_string())
501 .ok_or_else(|| "cluster: hashref slot needs `host`".to_string())?;
502 let n = map.get("slots").map(|v| v.to_int().max(1)).unwrap_or(1) as usize;
503 let stryke = map
504 .get("stryke")
505 .or_else(|| map.get("pe_path"))
506 .map(|v| v.to_string())
507 .unwrap_or_else(|| "stryke".to_string());
508 for _ in 0..n {
509 slots.push(RemoteSlot {
510 host: host.clone(),
511 pe_path: stryke.clone(),
512 });
513 }
514 continue;
515 }
516
517 let s = it.to_string();
523 let (left, pe_path) = if let Some(idx) = s.find(':') {
525 let rest = &s[idx + 1..];
527 if let Some(jdx) = rest.find(':') {
528 let count_seg = &rest[..jdx];
530 if count_seg.parse::<usize>().is_ok() {
531 (
532 format!("{}:{}", &s[..idx], count_seg),
533 Some(rest[jdx + 1..].to_string()),
534 )
535 } else {
536 (s.clone(), None)
537 }
538 } else {
539 (s.clone(), None)
540 }
541 } else {
542 (s.clone(), None)
543 };
544 let pe_path = pe_path.unwrap_or_else(|| "stryke".to_string());
545
546 let (host, n) = if let Some((h, nstr)) = left.rsplit_once(':') {
549 if let Ok(n) = nstr.parse::<usize>() {
550 (h.to_string(), n.max(1))
551 } else {
552 (left.clone(), 1)
553 }
554 } else {
555 (left.clone(), 1)
556 };
557 for _ in 0..n {
558 slots.push(RemoteSlot {
559 host: host.clone(),
560 pe_path: pe_path.clone(),
561 });
562 }
563 }
564
565 if slots.is_empty() {
566 return Err("cluster: need at least one host".into());
567 }
568 Ok(RemoteCluster {
569 slots,
570 job_timeout_ms,
571 max_attempts,
572 connect_timeout_ms,
573 })
574 }
575}
576
577#[derive(Clone)]
579pub struct PerlBarrier(pub Arc<Barrier>);
580
581impl fmt::Debug for PerlBarrier {
582 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
583 f.write_str("Barrier")
584 }
585}
586
587#[derive(Debug, Clone)]
589pub struct CaptureResult {
590 pub stdout: String,
592 pub stderr: String,
594 pub exitcode: i64,
596}
597
598#[derive(Debug, Clone)]
600pub struct PerlDataFrame {
601 pub columns: Vec<String>,
603 pub cols: Vec<Vec<StrykeValue>>,
605 pub group_by: Option<String>,
607}
608
609impl PerlDataFrame {
610 #[inline]
612 pub fn nrows(&self) -> usize {
613 self.cols.first().map(|c| c.len()).unwrap_or(0)
614 }
615 #[inline]
617 pub fn ncols(&self) -> usize {
618 self.columns.len()
619 }
620 #[inline]
622 pub fn col_index(&self, name: &str) -> Option<usize> {
623 self.columns.iter().position(|c| c == name)
624 }
625}
626
627#[derive(Debug, Clone)]
629pub(crate) enum HeapObject {
630 Integer(i64),
631 BigInt(Arc<BigInt>),
634 Float(f64),
635 String(String),
636 Bytes(Arc<Vec<u8>>),
637 Array(Vec<StrykeValue>),
638 Hash(IndexMap<String, StrykeValue>),
639 ArrayRef(Arc<RwLock<Vec<StrykeValue>>>),
640 HashRef(Arc<RwLock<IndexMap<String, StrykeValue>>>),
641 ScalarRef(Arc<RwLock<StrykeValue>>),
642 CaptureCell(Arc<RwLock<StrykeValue>>),
646 ScalarBindingRef(String),
648 ArrayBindingRef(String),
650 HashBindingRef(String),
652 CodeRef(Arc<StrykeSub>),
653 Regex(Arc<PerlCompiledRegex>, String, String),
655 Blessed(Arc<BlessedRef>),
656 IOHandle(String),
657 Atomic(Arc<Mutex<StrykeValue>>),
658 Set(Arc<PerlSet>),
659 ChannelTx(Arc<Sender<StrykeValue>>),
660 ChannelRx(Arc<Receiver<StrykeValue>>),
661 AsyncTask(Arc<StrykeAsyncTask>),
662 Generator(Arc<PerlGenerator>),
663 Deque(Arc<Mutex<VecDeque<StrykeValue>>>),
664 Heap(Arc<Mutex<PerlHeap>>),
665 Mutex(Arc<MutexHandle>),
669 Semaphore(Arc<SemaphoreHandle>),
674 BloomFilter(Arc<Mutex<crate::sketches::BloomFilter>>),
677 HllSketch(Arc<Mutex<crate::sketches::HllSketch>>),
679 CmsSketch(Arc<Mutex<crate::sketches::CmsSketch>>),
681 TopKSketch(Arc<Mutex<crate::sketches::TopKSketch>>),
683 TDigestSketch(Arc<Mutex<crate::sketches::TDigestSketch>>),
685 RoaringBitmap(Arc<Mutex<crate::sketches::RoaringBitmapSketch>>),
687 RateLimiter(Arc<Mutex<crate::sketches::RateLimiterSketch>>),
689 HashRing(Arc<Mutex<crate::sketches::HashRingSketch>>),
691 SimHash(Arc<Mutex<crate::sketches::SimHashSketch>>),
693 MinHash(Arc<Mutex<crate::sketches::MinHashSketch>>),
695 IntervalTree(Arc<Mutex<crate::sketches::IntervalTreeSketch>>),
697 BkTree(Arc<Mutex<crate::sketches::BkTreeSketch>>),
699 Rope(Arc<Mutex<crate::sketches::RopeSketch>>),
701 KvStore(Arc<Mutex<crate::kvstore::KvStore>>),
703 Pipeline(Arc<Mutex<PipelineInner>>),
704 Capture(Arc<CaptureResult>),
705 Ppool(PerlPpool),
706 RemoteCluster(Arc<RemoteCluster>),
707 Barrier(PerlBarrier),
708 SqliteConn(Arc<Mutex<rusqlite::Connection>>),
709 StructInst(Arc<StructInstance>),
710 DataFrame(Arc<Mutex<PerlDataFrame>>),
711 EnumInst(Arc<EnumInstance>),
712 ClassInst(Arc<ClassInstance>),
713 Iterator(Arc<dyn StrykeIterator>),
715 ErrnoDual {
717 code: i32,
718 msg: String,
719 },
720}
721
722#[repr(transparent)]
724pub struct StrykeValue(pub(crate) u64);
725
726impl Default for StrykeValue {
727 fn default() -> Self {
728 Self::UNDEF
729 }
730}
731
732impl Clone for StrykeValue {
733 fn clone(&self) -> Self {
734 if nanbox::is_heap(self.0) {
735 let arc = self.heap_arc();
736 match &*arc {
737 HeapObject::Array(v) => {
738 StrykeValue::from_heap(Arc::new(HeapObject::Array(v.clone())))
739 }
740 HeapObject::Hash(h) => {
741 StrykeValue::from_heap(Arc::new(HeapObject::Hash(h.clone())))
742 }
743 HeapObject::String(s) => {
744 StrykeValue::from_heap(Arc::new(HeapObject::String(s.clone())))
745 }
746 HeapObject::Integer(n) => StrykeValue::integer(*n),
747 HeapObject::Float(f) => StrykeValue::float(*f),
748 _ => StrykeValue::from_heap(Arc::clone(&arc)),
749 }
750 } else {
751 StrykeValue(self.0)
752 }
753 }
754}
755
756impl StrykeValue {
757 #[inline]
760 pub fn dup_stack(&self) -> Self {
761 if nanbox::is_heap(self.0) {
762 let arc = self.heap_arc();
763 match &*arc {
764 HeapObject::Array(_) | HeapObject::Hash(_) => {
765 StrykeValue::from_heap(Arc::clone(&arc))
766 }
767 _ => self.clone(),
768 }
769 } else {
770 StrykeValue(self.0)
771 }
772 }
773
774 #[inline]
785 pub fn shallow_clone(&self) -> Self {
786 if nanbox::is_heap(self.0) {
787 StrykeValue::from_heap(self.heap_arc())
788 } else {
789 StrykeValue(self.0)
790 }
791 }
792}
793
794impl Drop for StrykeValue {
795 fn drop(&mut self) {
796 if nanbox::is_heap(self.0) {
797 unsafe {
798 let p = nanbox::decode_heap_ptr::<HeapObject>(self.0) as *mut HeapObject;
799 drop(Arc::from_raw(p));
800 }
801 }
802 }
803}
804
805impl fmt::Debug for StrykeValue {
806 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
807 write!(f, "{self}")
808 }
809}
810
811#[derive(Clone)]
814pub struct PerlPpool(pub(crate) Arc<crate::ppool::PpoolInner>);
815
816impl fmt::Debug for PerlPpool {
817 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
818 f.write_str("PerlPpool")
819 }
820}
821
822#[derive(Debug, Clone, PartialEq, Eq)]
825pub struct FibLikeRecAddPattern {
826 pub param: String,
828 pub base_k: i64,
830 pub left_k: i64,
832 pub right_k: i64,
834}
835#[derive(Debug, Clone)]
837pub struct StrykeSub {
838 pub name: String,
840 pub params: Vec<SubSigParam>,
842 pub body: Block,
844 pub closure_env: Option<Vec<(String, StrykeValue)>>,
846 pub prototype: Option<String>,
848 pub fib_like: Option<FibLikeRecAddPattern>,
851}
852
853#[derive(Debug, Clone)]
855pub enum PipelineOp {
856 Filter(Arc<StrykeSub>),
858 Map(Arc<StrykeSub>),
860 Tap(Arc<StrykeSub>),
862 Take(i64),
864 PMap { sub: Arc<StrykeSub>, progress: bool },
866 PGrep { sub: Arc<StrykeSub>, progress: bool },
868 PFor { sub: Arc<StrykeSub>, progress: bool },
870 PMapChunked {
872 chunk: i64,
873 sub: Arc<StrykeSub>,
874 progress: bool,
875 },
876 PSort {
878 cmp: Option<Arc<StrykeSub>>,
879 progress: bool,
880 },
881 PCache { sub: Arc<StrykeSub>, progress: bool },
883 PReduce { sub: Arc<StrykeSub>, progress: bool },
885 PReduceInit {
887 init: StrykeValue,
888 sub: Arc<StrykeSub>,
889 progress: bool,
890 },
891 PMapReduce {
893 map: Arc<StrykeSub>,
894 reduce: Arc<StrykeSub>,
895 progress: bool,
896 },
897}
898#[derive(Debug)]
900pub struct PipelineInner {
901 pub source: Vec<StrykeValue>,
903 pub ops: Vec<PipelineOp>,
905 pub has_scalar_terminal: bool,
907 pub par_stream: bool,
909 pub streaming: bool,
912 pub streaming_workers: usize,
914 pub streaming_buffer: usize,
916}
917#[derive(Debug)]
919pub struct BlessedRef {
920 pub class: String,
922 pub data: RwLock<StrykeValue>,
924 pub(crate) suppress_destroy_queue: AtomicBool,
926}
927
928impl BlessedRef {
929 pub(crate) fn new_blessed(class: String, data: StrykeValue) -> Self {
930 Self {
931 class,
932 data: RwLock::new(data),
933 suppress_destroy_queue: AtomicBool::new(false),
934 }
935 }
936
937 pub(crate) fn new_for_destroy_invocant(class: String, data: StrykeValue) -> Self {
939 Self {
940 class,
941 data: RwLock::new(data),
942 suppress_destroy_queue: AtomicBool::new(true),
943 }
944 }
945}
946
947impl Clone for BlessedRef {
948 fn clone(&self) -> Self {
949 Self {
950 class: self.class.clone(),
951 data: RwLock::new(self.data.read().clone()),
952 suppress_destroy_queue: AtomicBool::new(false),
953 }
954 }
955}
956
957impl Drop for BlessedRef {
958 fn drop(&mut self) {
959 if self.suppress_destroy_queue.load(AtomicOrdering::Acquire) {
960 return;
961 }
962 let inner = {
963 let mut g = self.data.write();
964 std::mem::take(&mut *g)
965 };
966 crate::pending_destroy::enqueue(self.class.clone(), inner);
967 }
968}
969
970#[derive(Debug)]
972pub struct StructInstance {
973 pub def: Arc<StructDef>,
975 pub values: RwLock<Vec<StrykeValue>>,
977}
978
979impl StructInstance {
980 pub fn new(def: Arc<StructDef>, values: Vec<StrykeValue>) -> Self {
982 Self {
983 def,
984 values: RwLock::new(values),
985 }
986 }
987
988 #[inline]
990 pub fn get_field(&self, idx: usize) -> Option<StrykeValue> {
991 self.values.read().get(idx).cloned()
992 }
993
994 #[inline]
996 pub fn set_field(&self, idx: usize, val: StrykeValue) {
997 if let Some(slot) = self.values.write().get_mut(idx) {
998 *slot = val;
999 }
1000 }
1001
1002 #[inline]
1004 pub fn get_values(&self) -> Vec<StrykeValue> {
1005 self.values.read().clone()
1006 }
1007}
1008
1009impl Clone for StructInstance {
1010 fn clone(&self) -> Self {
1011 Self {
1012 def: Arc::clone(&self.def),
1013 values: RwLock::new(self.values.read().clone()),
1014 }
1015 }
1016}
1017
1018#[derive(Debug)]
1020pub struct EnumInstance {
1021 pub def: Arc<EnumDef>,
1023 pub variant_idx: usize,
1025 pub data: StrykeValue,
1027}
1028
1029impl EnumInstance {
1030 pub fn new(def: Arc<EnumDef>, variant_idx: usize, data: StrykeValue) -> Self {
1032 Self {
1033 def,
1034 variant_idx,
1035 data,
1036 }
1037 }
1038 pub fn variant_name(&self) -> &str {
1040 &self.def.variants[self.variant_idx].name
1041 }
1042}
1043
1044impl Clone for EnumInstance {
1045 fn clone(&self) -> Self {
1046 Self {
1047 def: Arc::clone(&self.def),
1048 variant_idx: self.variant_idx,
1049 data: self.data.clone(),
1050 }
1051 }
1052}
1053
1054#[derive(Debug)]
1056pub struct ClassInstance {
1057 pub def: Arc<ClassDef>,
1059 pub values: RwLock<Vec<StrykeValue>>,
1061 pub isa_chain: Vec<String>,
1063}
1064
1065impl ClassInstance {
1066 pub fn new(def: Arc<ClassDef>, values: Vec<StrykeValue>) -> Self {
1068 Self {
1069 def,
1070 values: RwLock::new(values),
1071 isa_chain: Vec::new(),
1072 }
1073 }
1074 pub fn new_with_isa(
1076 def: Arc<ClassDef>,
1077 values: Vec<StrykeValue>,
1078 isa_chain: Vec<String>,
1079 ) -> Self {
1080 Self {
1081 def,
1082 values: RwLock::new(values),
1083 isa_chain,
1084 }
1085 }
1086
1087 #[inline]
1089 pub fn isa(&self, name: &str) -> bool {
1090 self.def.name == name || self.isa_chain.contains(&name.to_string())
1091 }
1092 #[inline]
1094 pub fn get_field(&self, idx: usize) -> Option<StrykeValue> {
1095 self.values.read().get(idx).cloned()
1096 }
1097 #[inline]
1099 pub fn set_field(&self, idx: usize, val: StrykeValue) {
1100 if let Some(slot) = self.values.write().get_mut(idx) {
1101 *slot = val;
1102 }
1103 }
1104 #[inline]
1106 pub fn get_values(&self) -> Vec<StrykeValue> {
1107 self.values.read().clone()
1108 }
1109
1110 pub fn get_field_by_name(&self, name: &str) -> Option<StrykeValue> {
1112 self.def
1113 .field_index(name)
1114 .and_then(|idx| self.get_field(idx))
1115 }
1116
1117 pub fn set_field_by_name(&self, name: &str, val: StrykeValue) -> bool {
1119 if let Some(idx) = self.def.field_index(name) {
1120 self.set_field(idx, val);
1121 true
1122 } else {
1123 false
1124 }
1125 }
1126}
1127
1128impl Clone for ClassInstance {
1129 fn clone(&self) -> Self {
1130 Self {
1131 def: Arc::clone(&self.def),
1132 values: RwLock::new(self.values.read().clone()),
1133 isa_chain: self.isa_chain.clone(),
1134 }
1135 }
1136}
1137
1138impl StrykeValue {
1139 pub const UNDEF: StrykeValue = StrykeValue(nanbox::encode_imm_undef());
1141
1142 #[inline]
1143 fn from_heap(arc: Arc<HeapObject>) -> StrykeValue {
1144 let ptr = Arc::into_raw(arc);
1145 StrykeValue(nanbox::encode_heap_ptr(ptr))
1146 }
1147
1148 #[inline]
1149 pub(crate) fn heap_arc(&self) -> Arc<HeapObject> {
1150 debug_assert!(nanbox::is_heap(self.0));
1151 unsafe {
1152 let p = nanbox::decode_heap_ptr::<HeapObject>(self.0);
1153 Arc::increment_strong_count(p);
1154 Arc::from_raw(p as *mut HeapObject)
1155 }
1156 }
1157
1158 #[inline]
1163 pub(crate) unsafe fn heap_ref(&self) -> &HeapObject {
1164 &*nanbox::decode_heap_ptr::<HeapObject>(self.0)
1165 }
1166
1167 #[inline]
1168 pub(crate) fn with_heap<R>(&self, f: impl FnOnce(&HeapObject) -> R) -> Option<R> {
1169 if !nanbox::is_heap(self.0) {
1170 return None;
1171 }
1172 Some(f(unsafe { self.heap_ref() }))
1174 }
1175
1176 #[inline]
1178 pub(crate) fn raw_bits(&self) -> u64 {
1179 self.0
1180 }
1181
1182 #[inline]
1184 pub(crate) fn from_raw_bits(bits: u64) -> Self {
1185 Self(bits)
1186 }
1187
1188 #[inline]
1190 pub fn is_integer_like(&self) -> bool {
1191 nanbox::as_imm_int32(self.0).is_some()
1192 || matches!(
1193 self.with_heap(|h| matches!(h, HeapObject::Integer(_) | HeapObject::BigInt(_))),
1194 Some(true)
1195 )
1196 }
1197
1198 #[inline]
1200 pub fn is_float_like(&self) -> bool {
1201 nanbox::is_raw_float_bits(self.0)
1202 || matches!(
1203 self.with_heap(|h| matches!(h, HeapObject::Float(_))),
1204 Some(true)
1205 )
1206 }
1207
1208 #[inline]
1210 pub fn is_string_like(&self) -> bool {
1211 matches!(
1212 self.with_heap(|h| matches!(h, HeapObject::String(_))),
1213 Some(true)
1214 )
1215 }
1216 #[inline]
1218 pub fn integer(n: i64) -> Self {
1219 if n >= i32::MIN as i64 && n <= i32::MAX as i64 {
1220 StrykeValue(nanbox::encode_imm_int32(n as i32))
1221 } else {
1222 Self::from_heap(Arc::new(HeapObject::Integer(n)))
1223 }
1224 }
1225
1226 pub fn bigint(n: BigInt) -> Self {
1229 use num_traits::ToPrimitive;
1230 if let Some(i) = n.to_i64() {
1231 return Self::integer(i);
1232 }
1233 Self::from_heap(Arc::new(HeapObject::BigInt(Arc::new(n))))
1234 }
1235
1236 pub fn as_bigint(&self) -> Option<Arc<BigInt>> {
1240 self.with_heap(|h| match h {
1241 HeapObject::BigInt(b) => Some(Arc::clone(b)),
1242 _ => None,
1243 })
1244 .flatten()
1245 }
1246
1247 pub fn to_bigint(&self) -> BigInt {
1250 if let Some(b) = self.as_bigint() {
1251 return (*b).clone();
1252 }
1253 if let Some(i) = self.as_integer() {
1254 return BigInt::from(i);
1255 }
1256 BigInt::from(self.to_number() as i64)
1257 }
1258 #[inline]
1260 pub fn float(f: f64) -> Self {
1261 if nanbox::float_needs_box(f) {
1262 Self::from_heap(Arc::new(HeapObject::Float(f)))
1263 } else {
1264 StrykeValue(f.to_bits())
1265 }
1266 }
1267 #[inline]
1269 pub fn string(s: String) -> Self {
1270 Self::from_heap(Arc::new(HeapObject::String(s)))
1271 }
1272 #[inline]
1274 pub fn bytes(b: Arc<Vec<u8>>) -> Self {
1275 Self::from_heap(Arc::new(HeapObject::Bytes(b)))
1276 }
1277 #[inline]
1279 pub fn array(v: Vec<StrykeValue>) -> Self {
1280 Self::from_heap(Arc::new(HeapObject::Array(v)))
1281 }
1282
1283 #[inline]
1285 pub fn iterator(it: Arc<dyn StrykeIterator>) -> Self {
1286 Self::from_heap(Arc::new(HeapObject::Iterator(it)))
1287 }
1288
1289 #[inline]
1291 pub fn is_iterator(&self) -> bool {
1292 if !nanbox::is_heap(self.0) {
1293 return false;
1294 }
1295 matches!(unsafe { self.heap_ref() }, HeapObject::Iterator(_))
1296 }
1297
1298 pub fn into_iterator(&self) -> Arc<dyn StrykeIterator> {
1300 if nanbox::is_heap(self.0) {
1301 if let HeapObject::Iterator(it) = &*self.heap_arc() {
1302 return Arc::clone(it);
1303 }
1304 }
1305 panic!("into_iterator on non-iterator value");
1306 }
1307 #[inline]
1309 pub fn hash(h: IndexMap<String, StrykeValue>) -> Self {
1310 Self::from_heap(Arc::new(HeapObject::Hash(h)))
1311 }
1312 #[inline]
1314 pub fn array_ref(a: Arc<RwLock<Vec<StrykeValue>>>) -> Self {
1315 Self::from_heap(Arc::new(HeapObject::ArrayRef(a)))
1316 }
1317 #[inline]
1319 pub fn hash_ref(h: Arc<RwLock<IndexMap<String, StrykeValue>>>) -> Self {
1320 Self::from_heap(Arc::new(HeapObject::HashRef(h)))
1321 }
1322 #[inline]
1324 pub fn scalar_ref(r: Arc<RwLock<StrykeValue>>) -> Self {
1325 Self::from_heap(Arc::new(HeapObject::ScalarRef(r)))
1326 }
1327 #[inline]
1329 pub fn capture_cell(r: Arc<RwLock<StrykeValue>>) -> Self {
1330 Self::from_heap(Arc::new(HeapObject::CaptureCell(r)))
1331 }
1332 #[inline]
1334 pub fn scalar_binding_ref(name: String) -> Self {
1335 Self::from_heap(Arc::new(HeapObject::ScalarBindingRef(name)))
1336 }
1337 #[inline]
1339 pub fn array_binding_ref(name: String) -> Self {
1340 Self::from_heap(Arc::new(HeapObject::ArrayBindingRef(name)))
1341 }
1342 #[inline]
1344 pub fn hash_binding_ref(name: String) -> Self {
1345 Self::from_heap(Arc::new(HeapObject::HashBindingRef(name)))
1346 }
1347 #[inline]
1349 pub fn code_ref(c: Arc<StrykeSub>) -> Self {
1350 Self::from_heap(Arc::new(HeapObject::CodeRef(c)))
1351 }
1352 #[inline]
1354 pub fn as_code_ref(&self) -> Option<Arc<StrykeSub>> {
1355 self.with_heap(|h| match h {
1356 HeapObject::CodeRef(sub) => Some(Arc::clone(sub)),
1357 _ => None,
1358 })
1359 .flatten()
1360 }
1361 #[inline]
1363 pub fn as_regex(&self) -> Option<Arc<PerlCompiledRegex>> {
1364 self.with_heap(|h| match h {
1365 HeapObject::Regex(re, _, _) => Some(Arc::clone(re)),
1366 _ => None,
1367 })
1368 .flatten()
1369 }
1370 #[inline]
1372 pub fn as_blessed_ref(&self) -> Option<Arc<BlessedRef>> {
1373 self.with_heap(|h| match h {
1374 HeapObject::Blessed(b) => Some(Arc::clone(b)),
1375 _ => None,
1376 })
1377 .flatten()
1378 }
1379
1380 #[inline]
1382 pub fn hash_get(&self, key: &str) -> Option<StrykeValue> {
1383 self.with_heap(|h| match h {
1384 HeapObject::Hash(h) => h.get(key).cloned(),
1385 _ => None,
1386 })
1387 .flatten()
1388 }
1389 #[inline]
1391 pub fn is_undef(&self) -> bool {
1392 nanbox::is_imm_undef(self.0)
1393 }
1394
1395 pub fn is_simple_scalar(&self) -> bool {
1400 if self.is_undef() {
1401 return true;
1402 }
1403 if !nanbox::is_heap(self.0) {
1404 return true; }
1406 matches!(
1407 unsafe { self.heap_ref() },
1408 HeapObject::Integer(_)
1409 | HeapObject::BigInt(_)
1410 | HeapObject::Float(_)
1411 | HeapObject::String(_)
1412 | HeapObject::Bytes(_)
1413 )
1414 }
1415
1416 #[inline]
1418 pub fn as_integer(&self) -> Option<i64> {
1419 if let Some(n) = nanbox::as_imm_int32(self.0) {
1420 return Some(n as i64);
1421 }
1422 if nanbox::is_raw_float_bits(self.0) {
1423 return None;
1424 }
1425 self.with_heap(|h| match h {
1426 HeapObject::Integer(n) => Some(*n),
1427 HeapObject::BigInt(b) => {
1428 use num_traits::ToPrimitive;
1429 b.to_i64()
1430 }
1431 _ => None,
1432 })
1433 .flatten()
1434 }
1435 #[inline]
1437 pub fn as_float(&self) -> Option<f64> {
1438 if nanbox::is_raw_float_bits(self.0) {
1439 return Some(f64::from_bits(self.0));
1440 }
1441 self.with_heap(|h| match h {
1442 HeapObject::Float(f) => Some(*f),
1443 _ => None,
1444 })
1445 .flatten()
1446 }
1447 #[inline]
1449 pub fn as_array_vec(&self) -> Option<Vec<StrykeValue>> {
1450 self.with_heap(|h| match h {
1451 HeapObject::Array(v) => Some(v.clone()),
1452 _ => None,
1453 })
1454 .flatten()
1455 }
1456
1457 pub fn map_flatten_outputs(&self, peel_array_ref: bool) -> Vec<StrykeValue> {
1461 if let Some(a) = self.as_array_vec() {
1462 return a;
1463 }
1464 if peel_array_ref {
1465 if let Some(r) = self.as_array_ref() {
1466 return r.read().clone();
1467 }
1468 }
1469 if self.is_iterator() {
1470 return self.into_iterator().collect_all();
1471 }
1472 vec![self.clone()]
1473 }
1474 #[inline]
1476 pub fn as_hash_map(&self) -> Option<IndexMap<String, StrykeValue>> {
1477 self.with_heap(|h| match h {
1478 HeapObject::Hash(h) => Some(h.clone()),
1479 _ => None,
1480 })
1481 .flatten()
1482 }
1483 #[inline]
1485 pub fn as_bytes_arc(&self) -> Option<Arc<Vec<u8>>> {
1486 self.with_heap(|h| match h {
1487 HeapObject::Bytes(b) => Some(Arc::clone(b)),
1488 _ => None,
1489 })
1490 .flatten()
1491 }
1492 pub fn length_value(&self, utf8: bool) -> i64 {
1498 if let Some(a) = self.as_array_vec() {
1499 a.len() as i64
1500 } else if let Some(h) = self.as_hash_map() {
1501 h.len() as i64
1502 } else if let Some(b) = self.as_bytes_arc() {
1503 b.len() as i64
1504 } else {
1505 let s = self.to_string();
1506 if utf8 {
1507 s.chars().count() as i64
1508 } else {
1509 s.len() as i64
1510 }
1511 }
1512 }
1513
1514 pub fn ord_value(&self) -> i64 {
1518 self.to_string().chars().next().map(|c| c as i64).unwrap_or(0)
1519 }
1520
1521 pub fn hex_value(&self) -> i64 {
1524 let s = self.to_string();
1525 let clean = s.trim().trim_start_matches("0x").trim_start_matches("0X");
1526 i64::from_str_radix(clean, 16).unwrap_or(0)
1527 }
1528
1529 pub fn oct_value(&self) -> i64 {
1533 let s = self.to_string();
1534 let s = s.trim();
1535 if s.starts_with("0x") || s.starts_with("0X") {
1536 i64::from_str_radix(&s[2..], 16).unwrap_or(0)
1537 } else if s.starts_with("0b") || s.starts_with("0B") {
1538 i64::from_str_radix(&s[2..], 2).unwrap_or(0)
1539 } else if s.starts_with("0o") || s.starts_with("0O") {
1540 i64::from_str_radix(&s[2..], 8).unwrap_or(0)
1541 } else {
1542 i64::from_str_radix(s.trim_start_matches('0'), 8).unwrap_or(0)
1543 }
1544 }
1545
1546 pub fn uc_value(&self) -> String {
1549 self.to_string().to_uppercase()
1550 }
1551
1552 pub fn lc_value(&self) -> String {
1555 self.to_string().to_lowercase()
1556 }
1557
1558 pub fn ucfirst_value(&self) -> String {
1561 let s = self.to_string();
1562 let mut chars = s.chars();
1563 match chars.next() {
1564 Some(c) => c.to_uppercase().to_string() + chars.as_str(),
1565 None => String::new(),
1566 }
1567 }
1568
1569 pub fn lcfirst_value(&self) -> String {
1572 let s = self.to_string();
1573 let mut chars = s.chars();
1574 match chars.next() {
1575 Some(c) => c.to_lowercase().to_string() + chars.as_str(),
1576 None => String::new(),
1577 }
1578 }
1579
1580 pub fn chr_value(&self) -> String {
1584 chr_from_codepoint(self.to_int())
1585 }
1586
1587 pub fn fc_value(&self) -> String {
1591 caseless::default_case_fold_str(&self.to_string())
1592 }
1593
1594 pub fn index_value(&self, sub: &StrykeValue) -> i64 {
1599 let s = self.to_string();
1600 let sub = sub.to_string();
1601 s.find(&sub).map(|i| i as i64).unwrap_or(-1)
1602 }
1603
1604 pub fn rindex_value(&self, sub: &StrykeValue) -> i64 {
1608 let s = self.to_string();
1609 let sub = sub.to_string();
1610 s.rfind(&sub).map(|i| i as i64).unwrap_or(-1)
1611 }
1612
1613 pub fn substr2_value(&self, off: i64) -> String {
1618 let s = self.to_string();
1619 let slen = s.len() as i64;
1620 let start = if off < 0 { (slen + off).max(0) } else { off }.min(slen) as usize;
1621 s.get(start..).unwrap_or("").to_string()
1622 }
1623
1624 pub fn repeat_value(&self, n: i64) -> String {
1628 self.to_string().repeat(n.max(0) as usize)
1629 }
1630
1631 pub fn substr3_value(&self, off: i64, len: i64) -> String {
1637 let s = self.to_string();
1638 let slen = s.len() as i64;
1639 let start = if off < 0 { (slen + off).max(0) } else { off }.min(slen) as usize;
1640 let end = if len < 0 {
1641 (slen + len).max(start as i64)
1642 } else {
1643 (start as i64 + len).min(slen)
1644 } as usize;
1645 s.get(start..end).unwrap_or("").to_string()
1646 }
1647 #[inline]
1649 pub fn as_async_task(&self) -> Option<Arc<StrykeAsyncTask>> {
1650 self.with_heap(|h| match h {
1651 HeapObject::AsyncTask(t) => Some(Arc::clone(t)),
1652 _ => None,
1653 })
1654 .flatten()
1655 }
1656 #[inline]
1658 pub fn as_generator(&self) -> Option<Arc<PerlGenerator>> {
1659 self.with_heap(|h| match h {
1660 HeapObject::Generator(g) => Some(Arc::clone(g)),
1661 _ => None,
1662 })
1663 .flatten()
1664 }
1665 #[inline]
1667 pub fn as_atomic_arc(&self) -> Option<Arc<Mutex<StrykeValue>>> {
1668 self.with_heap(|h| match h {
1669 HeapObject::Atomic(a) => Some(Arc::clone(a)),
1670 _ => None,
1671 })
1672 .flatten()
1673 }
1674 #[inline]
1676 pub fn as_io_handle_name(&self) -> Option<String> {
1677 self.with_heap(|h| match h {
1678 HeapObject::IOHandle(n) => Some(n.clone()),
1679 _ => None,
1680 })
1681 .flatten()
1682 }
1683 #[inline]
1685 pub fn as_sqlite_conn(&self) -> Option<Arc<Mutex<rusqlite::Connection>>> {
1686 self.with_heap(|h| match h {
1687 HeapObject::SqliteConn(c) => Some(Arc::clone(c)),
1688 _ => None,
1689 })
1690 .flatten()
1691 }
1692 #[inline]
1694 pub fn as_struct_inst(&self) -> Option<Arc<StructInstance>> {
1695 self.with_heap(|h| match h {
1696 HeapObject::StructInst(s) => Some(Arc::clone(s)),
1697 _ => None,
1698 })
1699 .flatten()
1700 }
1701 #[inline]
1703 pub fn as_enum_inst(&self) -> Option<Arc<EnumInstance>> {
1704 self.with_heap(|h| match h {
1705 HeapObject::EnumInst(e) => Some(Arc::clone(e)),
1706 _ => None,
1707 })
1708 .flatten()
1709 }
1710 #[inline]
1712 pub fn as_class_inst(&self) -> Option<Arc<ClassInstance>> {
1713 self.with_heap(|h| match h {
1714 HeapObject::ClassInst(c) => Some(Arc::clone(c)),
1715 _ => None,
1716 })
1717 .flatten()
1718 }
1719 #[inline]
1721 pub fn as_dataframe(&self) -> Option<Arc<Mutex<PerlDataFrame>>> {
1722 self.with_heap(|h| match h {
1723 HeapObject::DataFrame(d) => Some(Arc::clone(d)),
1724 _ => None,
1725 })
1726 .flatten()
1727 }
1728 #[inline]
1730 pub fn as_deque(&self) -> Option<Arc<Mutex<VecDeque<StrykeValue>>>> {
1731 self.with_heap(|h| match h {
1732 HeapObject::Deque(d) => Some(Arc::clone(d)),
1733 _ => None,
1734 })
1735 .flatten()
1736 }
1737 #[inline]
1739 pub fn as_heap_pq(&self) -> Option<Arc<Mutex<PerlHeap>>> {
1740 self.with_heap(|h| match h {
1741 HeapObject::Heap(h) => Some(Arc::clone(h)),
1742 _ => None,
1743 })
1744 .flatten()
1745 }
1746 #[inline]
1748 pub fn as_pipeline(&self) -> Option<Arc<Mutex<PipelineInner>>> {
1749 self.with_heap(|h| match h {
1750 HeapObject::Pipeline(p) => Some(Arc::clone(p)),
1751 _ => None,
1752 })
1753 .flatten()
1754 }
1755 #[inline]
1757 pub fn as_capture(&self) -> Option<Arc<CaptureResult>> {
1758 self.with_heap(|h| match h {
1759 HeapObject::Capture(c) => Some(Arc::clone(c)),
1760 _ => None,
1761 })
1762 .flatten()
1763 }
1764 #[inline]
1766 pub fn as_ppool(&self) -> Option<PerlPpool> {
1767 self.with_heap(|h| match h {
1768 HeapObject::Ppool(p) => Some(p.clone()),
1769 _ => None,
1770 })
1771 .flatten()
1772 }
1773 #[inline]
1775 pub fn as_remote_cluster(&self) -> Option<Arc<RemoteCluster>> {
1776 self.with_heap(|h| match h {
1777 HeapObject::RemoteCluster(c) => Some(Arc::clone(c)),
1778 _ => None,
1779 })
1780 .flatten()
1781 }
1782 #[inline]
1784 pub fn as_barrier(&self) -> Option<PerlBarrier> {
1785 self.with_heap(|h| match h {
1786 HeapObject::Barrier(b) => Some(b.clone()),
1787 _ => None,
1788 })
1789 .flatten()
1790 }
1791 #[inline]
1793 pub fn as_channel_tx(&self) -> Option<Arc<Sender<StrykeValue>>> {
1794 self.with_heap(|h| match h {
1795 HeapObject::ChannelTx(t) => Some(Arc::clone(t)),
1796 _ => None,
1797 })
1798 .flatten()
1799 }
1800 #[inline]
1802 pub fn as_channel_rx(&self) -> Option<Arc<Receiver<StrykeValue>>> {
1803 self.with_heap(|h| match h {
1804 HeapObject::ChannelRx(r) => Some(Arc::clone(r)),
1805 _ => None,
1806 })
1807 .flatten()
1808 }
1809 #[inline]
1811 pub fn as_scalar_ref(&self) -> Option<Arc<RwLock<StrykeValue>>> {
1812 self.with_heap(|h| match h {
1813 HeapObject::ScalarRef(r) => Some(Arc::clone(r)),
1814 _ => None,
1815 })
1816 .flatten()
1817 }
1818
1819 #[inline]
1821 pub fn as_capture_cell(&self) -> Option<Arc<RwLock<StrykeValue>>> {
1822 self.with_heap(|h| match h {
1823 HeapObject::CaptureCell(r) => Some(Arc::clone(r)),
1824 _ => None,
1825 })
1826 .flatten()
1827 }
1828
1829 #[inline]
1831 pub fn as_scalar_binding_name(&self) -> Option<String> {
1832 self.with_heap(|h| match h {
1833 HeapObject::ScalarBindingRef(s) => Some(s.clone()),
1834 _ => None,
1835 })
1836 .flatten()
1837 }
1838
1839 #[inline]
1841 pub fn as_array_binding_name(&self) -> Option<String> {
1842 self.with_heap(|h| match h {
1843 HeapObject::ArrayBindingRef(s) => Some(s.clone()),
1844 _ => None,
1845 })
1846 .flatten()
1847 }
1848
1849 #[inline]
1851 pub fn as_hash_binding_name(&self) -> Option<String> {
1852 self.with_heap(|h| match h {
1853 HeapObject::HashBindingRef(s) => Some(s.clone()),
1854 _ => None,
1855 })
1856 .flatten()
1857 }
1858 #[inline]
1860 pub fn as_array_ref(&self) -> Option<Arc<RwLock<Vec<StrykeValue>>>> {
1861 self.with_heap(|h| match h {
1862 HeapObject::ArrayRef(r) => Some(Arc::clone(r)),
1863 _ => None,
1864 })
1865 .flatten()
1866 }
1867 #[inline]
1869 pub fn as_hash_ref(&self) -> Option<Arc<RwLock<IndexMap<String, StrykeValue>>>> {
1870 self.with_heap(|h| match h {
1871 HeapObject::HashRef(r) => Some(Arc::clone(r)),
1872 _ => None,
1873 })
1874 .flatten()
1875 }
1876
1877 #[inline]
1879 pub fn is_mysync_deque_or_heap(&self) -> bool {
1880 matches!(
1881 self.with_heap(|h| matches!(h, HeapObject::Deque(_) | HeapObject::Heap(_))),
1882 Some(true)
1883 )
1884 }
1885 #[inline]
1887 pub fn regex(rx: Arc<PerlCompiledRegex>, pattern_src: String, flags: String) -> Self {
1888 Self::from_heap(Arc::new(HeapObject::Regex(rx, pattern_src, flags)))
1889 }
1890
1891 #[inline]
1893 pub fn regex_src_and_flags(&self) -> Option<(String, String)> {
1894 self.with_heap(|h| match h {
1895 HeapObject::Regex(_, pat, fl) => Some((pat.clone(), fl.clone())),
1896 _ => None,
1897 })
1898 .flatten()
1899 }
1900 #[inline]
1902 pub fn blessed(b: Arc<BlessedRef>) -> Self {
1903 Self::from_heap(Arc::new(HeapObject::Blessed(b)))
1904 }
1905 #[inline]
1907 pub fn io_handle(name: String) -> Self {
1908 Self::from_heap(Arc::new(HeapObject::IOHandle(name)))
1909 }
1910 #[inline]
1912 pub fn atomic(a: Arc<Mutex<StrykeValue>>) -> Self {
1913 Self::from_heap(Arc::new(HeapObject::Atomic(a)))
1914 }
1915 #[inline]
1917 pub fn set(s: Arc<PerlSet>) -> Self {
1918 Self::from_heap(Arc::new(HeapObject::Set(s)))
1919 }
1920 #[inline]
1922 pub fn channel_tx(tx: Arc<Sender<StrykeValue>>) -> Self {
1923 Self::from_heap(Arc::new(HeapObject::ChannelTx(tx)))
1924 }
1925 #[inline]
1927 pub fn channel_rx(rx: Arc<Receiver<StrykeValue>>) -> Self {
1928 Self::from_heap(Arc::new(HeapObject::ChannelRx(rx)))
1929 }
1930 #[inline]
1932 pub fn async_task(t: Arc<StrykeAsyncTask>) -> Self {
1933 Self::from_heap(Arc::new(HeapObject::AsyncTask(t)))
1934 }
1935 #[inline]
1937 pub fn generator(g: Arc<PerlGenerator>) -> Self {
1938 Self::from_heap(Arc::new(HeapObject::Generator(g)))
1939 }
1940 #[inline]
1942 pub fn deque(d: Arc<Mutex<VecDeque<StrykeValue>>>) -> Self {
1943 Self::from_heap(Arc::new(HeapObject::Deque(d)))
1944 }
1945 #[inline]
1947 pub fn heap(h: Arc<Mutex<PerlHeap>>) -> Self {
1948 Self::from_heap(Arc::new(HeapObject::Heap(h)))
1949 }
1950
1951 #[inline]
1953 pub fn mutex() -> Self {
1954 Self::from_heap(Arc::new(HeapObject::Mutex(Arc::new(MutexHandle::new()))))
1955 }
1956
1957 #[inline]
1960 pub fn semaphore(n: i64) -> Self {
1961 Self::from_heap(Arc::new(HeapObject::Semaphore(Arc::new(
1962 SemaphoreHandle::new(n),
1963 ))))
1964 }
1965
1966 #[inline]
1969 pub fn as_mutex(&self) -> Option<Arc<MutexHandle>> {
1970 self.with_heap(|h| match h {
1971 HeapObject::Mutex(m) => Some(Arc::clone(m)),
1972 _ => None,
1973 })
1974 .flatten()
1975 }
1976
1977 #[inline]
1979 pub fn as_semaphore(&self) -> Option<Arc<SemaphoreHandle>> {
1980 self.with_heap(|h| match h {
1981 HeapObject::Semaphore(s) => Some(Arc::clone(s)),
1982 _ => None,
1983 })
1984 .flatten()
1985 }
1986 #[inline]
1988 pub fn bloom_filter(b: Arc<Mutex<crate::sketches::BloomFilter>>) -> Self {
1989 Self::from_heap(Arc::new(HeapObject::BloomFilter(b)))
1990 }
1991 #[inline]
1993 pub fn as_bloom_filter(&self) -> Option<Arc<Mutex<crate::sketches::BloomFilter>>> {
1994 self.with_heap(|h| match h {
1995 HeapObject::BloomFilter(b) => Some(Arc::clone(b)),
1996 _ => None,
1997 })
1998 .flatten()
1999 }
2000 #[inline]
2002 pub fn hll_sketch(h: Arc<Mutex<crate::sketches::HllSketch>>) -> Self {
2003 Self::from_heap(Arc::new(HeapObject::HllSketch(h)))
2004 }
2005 #[inline]
2007 pub fn as_hll_sketch(&self) -> Option<Arc<Mutex<crate::sketches::HllSketch>>> {
2008 self.with_heap(|h| match h {
2009 HeapObject::HllSketch(s) => Some(Arc::clone(s)),
2010 _ => None,
2011 })
2012 .flatten()
2013 }
2014 #[inline]
2016 pub fn cms_sketch(c: Arc<Mutex<crate::sketches::CmsSketch>>) -> Self {
2017 Self::from_heap(Arc::new(HeapObject::CmsSketch(c)))
2018 }
2019 #[inline]
2021 pub fn as_cms_sketch(&self) -> Option<Arc<Mutex<crate::sketches::CmsSketch>>> {
2022 self.with_heap(|h| match h {
2023 HeapObject::CmsSketch(s) => Some(Arc::clone(s)),
2024 _ => None,
2025 })
2026 .flatten()
2027 }
2028 #[inline]
2030 pub fn topk_sketch(t: Arc<Mutex<crate::sketches::TopKSketch>>) -> Self {
2031 Self::from_heap(Arc::new(HeapObject::TopKSketch(t)))
2032 }
2033 #[inline]
2035 pub fn as_topk_sketch(&self) -> Option<Arc<Mutex<crate::sketches::TopKSketch>>> {
2036 self.with_heap(|h| match h {
2037 HeapObject::TopKSketch(s) => Some(Arc::clone(s)),
2038 _ => None,
2039 })
2040 .flatten()
2041 }
2042 #[inline]
2044 pub fn tdigest_sketch(t: Arc<Mutex<crate::sketches::TDigestSketch>>) -> Self {
2045 Self::from_heap(Arc::new(HeapObject::TDigestSketch(t)))
2046 }
2047 #[inline]
2049 pub fn as_tdigest_sketch(&self) -> Option<Arc<Mutex<crate::sketches::TDigestSketch>>> {
2050 self.with_heap(|h| match h {
2051 HeapObject::TDigestSketch(s) => Some(Arc::clone(s)),
2052 _ => None,
2053 })
2054 .flatten()
2055 }
2056 #[inline]
2058 pub fn roaring_bitmap(r: Arc<Mutex<crate::sketches::RoaringBitmapSketch>>) -> Self {
2059 Self::from_heap(Arc::new(HeapObject::RoaringBitmap(r)))
2060 }
2061 #[inline]
2063 pub fn as_roaring_bitmap(&self) -> Option<Arc<Mutex<crate::sketches::RoaringBitmapSketch>>> {
2064 self.with_heap(|h| match h {
2065 HeapObject::RoaringBitmap(s) => Some(Arc::clone(s)),
2066 _ => None,
2067 })
2068 .flatten()
2069 }
2070 #[inline]
2072 pub fn rate_limiter(r: Arc<Mutex<crate::sketches::RateLimiterSketch>>) -> Self {
2073 Self::from_heap(Arc::new(HeapObject::RateLimiter(r)))
2074 }
2075 #[inline]
2077 pub fn as_rate_limiter(&self) -> Option<Arc<Mutex<crate::sketches::RateLimiterSketch>>> {
2078 self.with_heap(|h| match h {
2079 HeapObject::RateLimiter(s) => Some(Arc::clone(s)),
2080 _ => None,
2081 })
2082 .flatten()
2083 }
2084 #[inline]
2086 pub fn hash_ring(r: Arc<Mutex<crate::sketches::HashRingSketch>>) -> Self {
2087 Self::from_heap(Arc::new(HeapObject::HashRing(r)))
2088 }
2089 #[inline]
2091 pub fn as_hash_ring(&self) -> Option<Arc<Mutex<crate::sketches::HashRingSketch>>> {
2092 self.with_heap(|h| match h {
2093 HeapObject::HashRing(s) => Some(Arc::clone(s)),
2094 _ => None,
2095 })
2096 .flatten()
2097 }
2098 #[inline]
2100 pub fn simhash(s: Arc<Mutex<crate::sketches::SimHashSketch>>) -> Self {
2101 Self::from_heap(Arc::new(HeapObject::SimHash(s)))
2102 }
2103 #[inline]
2105 pub fn as_simhash(&self) -> Option<Arc<Mutex<crate::sketches::SimHashSketch>>> {
2106 self.with_heap(|h| match h {
2107 HeapObject::SimHash(s) => Some(Arc::clone(s)),
2108 _ => None,
2109 })
2110 .flatten()
2111 }
2112 #[inline]
2114 pub fn minhash(m: Arc<Mutex<crate::sketches::MinHashSketch>>) -> Self {
2115 Self::from_heap(Arc::new(HeapObject::MinHash(m)))
2116 }
2117 #[inline]
2119 pub fn as_minhash(&self) -> Option<Arc<Mutex<crate::sketches::MinHashSketch>>> {
2120 self.with_heap(|h| match h {
2121 HeapObject::MinHash(s) => Some(Arc::clone(s)),
2122 _ => None,
2123 })
2124 .flatten()
2125 }
2126 #[inline]
2128 pub fn interval_tree(t: Arc<Mutex<crate::sketches::IntervalTreeSketch>>) -> Self {
2129 Self::from_heap(Arc::new(HeapObject::IntervalTree(t)))
2130 }
2131 #[inline]
2133 pub fn as_interval_tree(&self) -> Option<Arc<Mutex<crate::sketches::IntervalTreeSketch>>> {
2134 self.with_heap(|h| match h {
2135 HeapObject::IntervalTree(s) => Some(Arc::clone(s)),
2136 _ => None,
2137 })
2138 .flatten()
2139 }
2140 #[inline]
2142 pub fn bk_tree(t: Arc<Mutex<crate::sketches::BkTreeSketch>>) -> Self {
2143 Self::from_heap(Arc::new(HeapObject::BkTree(t)))
2144 }
2145 #[inline]
2147 pub fn as_bk_tree(&self) -> Option<Arc<Mutex<crate::sketches::BkTreeSketch>>> {
2148 self.with_heap(|h| match h {
2149 HeapObject::BkTree(s) => Some(Arc::clone(s)),
2150 _ => None,
2151 })
2152 .flatten()
2153 }
2154 #[inline]
2156 pub fn rope(r: Arc<Mutex<crate::sketches::RopeSketch>>) -> Self {
2157 Self::from_heap(Arc::new(HeapObject::Rope(r)))
2158 }
2159 #[inline]
2161 pub fn as_rope(&self) -> Option<Arc<Mutex<crate::sketches::RopeSketch>>> {
2162 self.with_heap(|h| match h {
2163 HeapObject::Rope(s) => Some(Arc::clone(s)),
2164 _ => None,
2165 })
2166 .flatten()
2167 }
2168 #[inline]
2170 pub fn kv_store(k: Arc<Mutex<crate::kvstore::KvStore>>) -> Self {
2171 Self::from_heap(Arc::new(HeapObject::KvStore(k)))
2172 }
2173 #[inline]
2175 pub fn as_kv_store(&self) -> Option<Arc<Mutex<crate::kvstore::KvStore>>> {
2176 self.with_heap(|h| match h {
2177 HeapObject::KvStore(s) => Some(Arc::clone(s)),
2178 _ => None,
2179 })
2180 .flatten()
2181 }
2182 #[inline]
2184 pub fn pipeline(p: Arc<Mutex<PipelineInner>>) -> Self {
2185 Self::from_heap(Arc::new(HeapObject::Pipeline(p)))
2186 }
2187 #[inline]
2189 pub fn capture(c: Arc<CaptureResult>) -> Self {
2190 Self::from_heap(Arc::new(HeapObject::Capture(c)))
2191 }
2192 #[inline]
2194 pub fn ppool(p: PerlPpool) -> Self {
2195 Self::from_heap(Arc::new(HeapObject::Ppool(p)))
2196 }
2197 #[inline]
2199 pub fn remote_cluster(c: Arc<RemoteCluster>) -> Self {
2200 Self::from_heap(Arc::new(HeapObject::RemoteCluster(c)))
2201 }
2202 #[inline]
2204 pub fn barrier(b: PerlBarrier) -> Self {
2205 Self::from_heap(Arc::new(HeapObject::Barrier(b)))
2206 }
2207 #[inline]
2209 pub fn sqlite_conn(c: Arc<Mutex<rusqlite::Connection>>) -> Self {
2210 Self::from_heap(Arc::new(HeapObject::SqliteConn(c)))
2211 }
2212 #[inline]
2214 pub fn struct_inst(s: Arc<StructInstance>) -> Self {
2215 Self::from_heap(Arc::new(HeapObject::StructInst(s)))
2216 }
2217 #[inline]
2219 pub fn enum_inst(e: Arc<EnumInstance>) -> Self {
2220 Self::from_heap(Arc::new(HeapObject::EnumInst(e)))
2221 }
2222 #[inline]
2224 pub fn class_inst(c: Arc<ClassInstance>) -> Self {
2225 Self::from_heap(Arc::new(HeapObject::ClassInst(c)))
2226 }
2227 #[inline]
2229 pub fn dataframe(df: Arc<Mutex<PerlDataFrame>>) -> Self {
2230 Self::from_heap(Arc::new(HeapObject::DataFrame(df)))
2231 }
2232
2233 #[inline]
2235 pub fn errno_dual(code: i32, msg: String) -> Self {
2236 Self::from_heap(Arc::new(HeapObject::ErrnoDual { code, msg }))
2237 }
2238
2239 #[inline]
2241 pub(crate) fn errno_dual_parts(&self) -> Option<(i32, String)> {
2242 if !nanbox::is_heap(self.0) {
2243 return None;
2244 }
2245 match unsafe { self.heap_ref() } {
2246 HeapObject::ErrnoDual { code, msg } => Some((*code, msg.clone())),
2247 _ => None,
2248 }
2249 }
2250
2251 #[inline]
2253 pub fn as_str(&self) -> Option<String> {
2254 if !nanbox::is_heap(self.0) {
2255 return None;
2256 }
2257 match unsafe { self.heap_ref() } {
2258 HeapObject::String(s) => Some(s.clone()),
2259 _ => None,
2260 }
2261 }
2262 #[inline]
2264 pub fn append_to(&self, buf: &mut String) {
2265 if nanbox::is_imm_undef(self.0) {
2266 return;
2267 }
2268 if let Some(n) = nanbox::as_imm_int32(self.0) {
2269 let mut b = itoa::Buffer::new();
2270 buf.push_str(b.format(n));
2271 return;
2272 }
2273 if nanbox::is_raw_float_bits(self.0) {
2274 buf.push_str(&format_float(f64::from_bits(self.0)));
2275 return;
2276 }
2277 match unsafe { self.heap_ref() } {
2278 HeapObject::String(s) => buf.push_str(s),
2279 HeapObject::ErrnoDual { msg, .. } => buf.push_str(msg),
2280 HeapObject::Bytes(b) => buf.push_str(&decode_utf8_or_latin1(b)),
2281 HeapObject::Atomic(arc) => arc.lock().append_to(buf),
2282 HeapObject::Set(s) => {
2283 buf.push('{');
2284 let mut first = true;
2285 for v in s.values() {
2286 if !first {
2287 buf.push(',');
2288 }
2289 first = false;
2290 v.append_to(buf);
2291 }
2292 buf.push('}');
2293 }
2294 HeapObject::ChannelTx(_) => buf.push_str("PCHANNEL::Tx"),
2295 HeapObject::ChannelRx(_) => buf.push_str("PCHANNEL::Rx"),
2296 HeapObject::AsyncTask(_) => buf.push_str("AsyncTask"),
2297 HeapObject::Generator(_) => buf.push_str("Generator"),
2298 HeapObject::Pipeline(_) => buf.push_str("Pipeline"),
2299 HeapObject::DataFrame(d) => {
2300 let g = d.lock();
2301 buf.push_str(&format!("DataFrame({}x{})", g.nrows(), g.ncols()));
2302 }
2303 HeapObject::Capture(_) => buf.push_str("Capture"),
2304 HeapObject::Ppool(_) => buf.push_str("Ppool"),
2305 HeapObject::RemoteCluster(_) => buf.push_str("Cluster"),
2306 HeapObject::Barrier(_) => buf.push_str("Barrier"),
2307 HeapObject::SqliteConn(_) => buf.push_str("SqliteConn"),
2308 HeapObject::StructInst(s) => buf.push_str(&s.def.name),
2309 _ => buf.push_str(&self.to_string()),
2310 }
2311 }
2312 #[inline]
2314 pub fn unwrap_atomic(&self) -> StrykeValue {
2315 if !nanbox::is_heap(self.0) {
2316 return self.clone();
2317 }
2318 match unsafe { self.heap_ref() } {
2319 HeapObject::Atomic(a) => a.lock().clone(),
2320 _ => self.clone(),
2321 }
2322 }
2323 #[inline]
2325 pub fn is_atomic(&self) -> bool {
2326 if !nanbox::is_heap(self.0) {
2327 return false;
2328 }
2329 matches!(unsafe { self.heap_ref() }, HeapObject::Atomic(_))
2330 }
2331 #[inline]
2333 pub fn is_true(&self) -> bool {
2334 if nanbox::is_imm_undef(self.0) {
2335 return false;
2336 }
2337 if let Some(n) = nanbox::as_imm_int32(self.0) {
2338 return n != 0;
2339 }
2340 if nanbox::is_raw_float_bits(self.0) {
2341 return f64::from_bits(self.0) != 0.0;
2342 }
2343 match unsafe { self.heap_ref() } {
2344 HeapObject::ErrnoDual { code, msg } => *code != 0 || !msg.is_empty(),
2345 HeapObject::String(s) => !s.is_empty() && s != "0",
2346 HeapObject::Bytes(b) => !b.is_empty(),
2347 HeapObject::BigInt(b) => {
2348 use num_traits::Zero;
2349 !b.is_zero()
2350 }
2351 HeapObject::Array(a) => !a.is_empty(),
2352 HeapObject::Hash(h) => !h.is_empty(),
2353 HeapObject::Atomic(arc) => arc.lock().is_true(),
2354 HeapObject::Set(s) => !s.is_empty(),
2355 HeapObject::Deque(d) => !d.lock().is_empty(),
2356 HeapObject::Heap(h) => !h.lock().items.is_empty(),
2357 HeapObject::Mutex(m) => *m.held.lock(),
2358 HeapObject::Semaphore(s) => *s.permits.lock() > 0,
2359 HeapObject::DataFrame(d) => d.lock().nrows() > 0,
2360 HeapObject::Pipeline(_) | HeapObject::Capture(_) => true,
2361 _ => true,
2362 }
2363 }
2364
2365 #[inline]
2368 pub(crate) fn concat_append_owned(self, rhs: &StrykeValue) -> StrykeValue {
2369 let mut s = self.into_string();
2370 rhs.append_to(&mut s);
2371 StrykeValue::string(s)
2372 }
2373
2374 #[inline]
2380 pub(crate) fn try_concat_repeat_inplace(&mut self, rhs: &str, n: usize) -> bool {
2381 if !nanbox::is_heap(self.0) || n == 0 {
2382 return n == 0 && nanbox::is_heap(self.0);
2384 }
2385 unsafe {
2386 if !matches!(self.heap_ref(), HeapObject::String(_)) {
2387 return false;
2388 }
2389 let raw = nanbox::decode_heap_ptr::<HeapObject>(self.0) as *mut HeapObject
2390 as *const HeapObject;
2391 let mut arc: Arc<HeapObject> = Arc::from_raw(raw);
2392 let did = if let Some(HeapObject::String(s)) = Arc::get_mut(&mut arc) {
2393 if !rhs.is_empty() {
2394 s.reserve(rhs.len().saturating_mul(n));
2395 for _ in 0..n {
2396 s.push_str(rhs);
2397 }
2398 }
2399 true
2400 } else {
2401 false
2402 };
2403 let restored = Arc::into_raw(arc);
2404 self.0 = nanbox::encode_heap_ptr(restored);
2405 did
2406 }
2407 }
2408
2409 #[inline]
2419 pub(crate) fn try_concat_append_inplace(&mut self, rhs: &StrykeValue) -> bool {
2420 if !nanbox::is_heap(self.0) {
2421 return false;
2422 }
2423 unsafe {
2427 if !matches!(self.heap_ref(), HeapObject::String(_)) {
2428 return false;
2429 }
2430 let raw = nanbox::decode_heap_ptr::<HeapObject>(self.0) as *mut HeapObject
2433 as *const HeapObject;
2434 let mut arc: Arc<HeapObject> = Arc::from_raw(raw);
2435 let did_append = if let Some(HeapObject::String(s)) = Arc::get_mut(&mut arc) {
2436 rhs.append_to(s);
2437 true
2438 } else {
2439 false
2440 };
2441 let restored = Arc::into_raw(arc);
2444 self.0 = nanbox::encode_heap_ptr(restored);
2445 did_append
2446 }
2447 }
2448 #[inline]
2450 pub fn into_string(self) -> String {
2451 let bits = self.0;
2452 std::mem::forget(self);
2453 if nanbox::is_imm_undef(bits) {
2454 return String::new();
2455 }
2456 if let Some(n) = nanbox::as_imm_int32(bits) {
2457 let mut buf = itoa::Buffer::new();
2458 return buf.format(n).to_owned();
2459 }
2460 if nanbox::is_raw_float_bits(bits) {
2461 return format_float(f64::from_bits(bits));
2462 }
2463 if nanbox::is_heap(bits) {
2464 unsafe {
2465 let arc =
2466 Arc::from_raw(nanbox::decode_heap_ptr::<HeapObject>(bits) as *mut HeapObject);
2467 match Arc::try_unwrap(arc) {
2468 Ok(HeapObject::String(s)) => return s,
2469 Ok(o) => return StrykeValue::from_heap(Arc::new(o)).to_string(),
2470 Err(arc) => {
2471 return match &*arc {
2472 HeapObject::String(s) => s.clone(),
2473 _ => StrykeValue::from_heap(Arc::clone(&arc)).to_string(),
2474 };
2475 }
2476 }
2477 }
2478 }
2479 String::new()
2480 }
2481 #[inline]
2483 pub fn as_str_or_empty(&self) -> String {
2484 if !nanbox::is_heap(self.0) {
2485 return String::new();
2486 }
2487 match unsafe { self.heap_ref() } {
2488 HeapObject::String(s) => s.clone(),
2489 HeapObject::ErrnoDual { msg, .. } => msg.clone(),
2490 _ => String::new(),
2491 }
2492 }
2493 #[inline]
2495 pub fn to_number(&self) -> f64 {
2496 if nanbox::is_imm_undef(self.0) {
2497 return 0.0;
2498 }
2499 if let Some(n) = nanbox::as_imm_int32(self.0) {
2500 return n as f64;
2501 }
2502 if nanbox::is_raw_float_bits(self.0) {
2503 return f64::from_bits(self.0);
2504 }
2505 match unsafe { self.heap_ref() } {
2506 HeapObject::Integer(n) => *n as f64,
2507 HeapObject::BigInt(b) => {
2508 use num_traits::ToPrimitive;
2509 b.to_f64().unwrap_or(f64::INFINITY)
2510 }
2511 HeapObject::Float(f) => *f,
2512 HeapObject::ErrnoDual { code, .. } => *code as f64,
2513 HeapObject::String(s) => parse_number(s),
2514 HeapObject::Bytes(b) => b.len() as f64,
2515 HeapObject::Array(a) => a.len() as f64,
2516 HeapObject::Atomic(arc) => arc.lock().to_number(),
2517 HeapObject::Set(s) => s.len() as f64,
2518 HeapObject::ChannelTx(_)
2519 | HeapObject::ChannelRx(_)
2520 | HeapObject::AsyncTask(_)
2521 | HeapObject::Generator(_) => 1.0,
2522 HeapObject::Deque(d) => d.lock().len() as f64,
2523 HeapObject::Heap(h) => h.lock().items.len() as f64,
2524 HeapObject::Mutex(m) => i64::from(*m.held.lock()) as f64,
2525 HeapObject::Semaphore(s) => *s.permits.lock() as f64,
2526 HeapObject::Pipeline(p) => p.lock().source.len() as f64,
2527 HeapObject::DataFrame(d) => d.lock().nrows() as f64,
2528 HeapObject::Capture(_)
2529 | HeapObject::Ppool(_)
2530 | HeapObject::RemoteCluster(_)
2531 | HeapObject::Barrier(_)
2532 | HeapObject::SqliteConn(_)
2533 | HeapObject::StructInst(_)
2534 | HeapObject::IOHandle(_) => 1.0,
2535 _ => 0.0,
2536 }
2537 }
2538 #[inline]
2540 pub fn to_int(&self) -> i64 {
2541 if nanbox::is_imm_undef(self.0) {
2542 return 0;
2543 }
2544 if let Some(n) = nanbox::as_imm_int32(self.0) {
2545 return n as i64;
2546 }
2547 if nanbox::is_raw_float_bits(self.0) {
2548 return f64::from_bits(self.0) as i64;
2549 }
2550 match unsafe { self.heap_ref() } {
2551 HeapObject::Integer(n) => *n,
2552 HeapObject::BigInt(b) => {
2553 use num_traits::ToPrimitive;
2554 b.to_i64().unwrap_or(i64::MAX)
2555 }
2556 HeapObject::Float(f) => *f as i64,
2557 HeapObject::ErrnoDual { code, .. } => *code as i64,
2558 HeapObject::String(s) => parse_number(s) as i64,
2559 HeapObject::Bytes(b) => b.len() as i64,
2560 HeapObject::Array(a) => a.len() as i64,
2561 HeapObject::Atomic(arc) => arc.lock().to_int(),
2562 HeapObject::Set(s) => s.len() as i64,
2563 HeapObject::ChannelTx(_)
2564 | HeapObject::ChannelRx(_)
2565 | HeapObject::AsyncTask(_)
2566 | HeapObject::Generator(_) => 1,
2567 HeapObject::Deque(d) => d.lock().len() as i64,
2568 HeapObject::Heap(h) => h.lock().items.len() as i64,
2569 HeapObject::Mutex(m) => i64::from(*m.held.lock()),
2570 HeapObject::Semaphore(s) => *s.permits.lock(),
2571 HeapObject::Pipeline(p) => p.lock().source.len() as i64,
2572 HeapObject::DataFrame(d) => d.lock().nrows() as i64,
2573 HeapObject::Capture(_)
2574 | HeapObject::Ppool(_)
2575 | HeapObject::RemoteCluster(_)
2576 | HeapObject::Barrier(_)
2577 | HeapObject::SqliteConn(_)
2578 | HeapObject::StructInst(_)
2579 | HeapObject::IOHandle(_) => 1,
2580 _ => 0,
2581 }
2582 }
2583 pub fn type_name(&self) -> String {
2585 if nanbox::is_imm_undef(self.0) {
2586 return "undef".to_string();
2587 }
2588 if nanbox::as_imm_int32(self.0).is_some() {
2589 return "INTEGER".to_string();
2590 }
2591 if nanbox::is_raw_float_bits(self.0) {
2592 return "FLOAT".to_string();
2593 }
2594 match unsafe { self.heap_ref() } {
2595 HeapObject::String(_) => "STRING".to_string(),
2596 HeapObject::Bytes(_) => "BYTES".to_string(),
2597 HeapObject::Array(_) => "ARRAY".to_string(),
2598 HeapObject::Hash(_) => "HASH".to_string(),
2599 HeapObject::ArrayRef(_) | HeapObject::ArrayBindingRef(_) => "ARRAY".to_string(),
2600 HeapObject::HashRef(_) | HeapObject::HashBindingRef(_) => "HASH".to_string(),
2601 HeapObject::ScalarRef(_)
2602 | HeapObject::ScalarBindingRef(_)
2603 | HeapObject::CaptureCell(_) => "SCALAR".to_string(),
2604 HeapObject::CodeRef(_) => "CODE".to_string(),
2605 HeapObject::Regex(_, _, _) => "Regexp".to_string(),
2606 HeapObject::Blessed(b) => b.class.clone(),
2607 HeapObject::IOHandle(_) => "GLOB".to_string(),
2608 HeapObject::Atomic(_) => "ATOMIC".to_string(),
2609 HeapObject::Set(_) => "Set".to_string(),
2610 HeapObject::ChannelTx(_) => "PCHANNEL::Tx".to_string(),
2611 HeapObject::ChannelRx(_) => "PCHANNEL::Rx".to_string(),
2612 HeapObject::AsyncTask(_) => "ASYNCTASK".to_string(),
2613 HeapObject::Generator(_) => "Generator".to_string(),
2614 HeapObject::Deque(_) => "Deque".to_string(),
2615 HeapObject::Heap(_) => "Heap".to_string(),
2616 HeapObject::Mutex(_) => "Mutex".to_string(),
2617 HeapObject::Semaphore(_) => "Semaphore".to_string(),
2618 HeapObject::BloomFilter(_) => "BloomFilter".to_string(),
2619 HeapObject::HllSketch(_) => "HllSketch".to_string(),
2620 HeapObject::CmsSketch(_) => "CmsSketch".to_string(),
2621 HeapObject::TopKSketch(_) => "TopKSketch".to_string(),
2622 HeapObject::TDigestSketch(_) => "TDigestSketch".to_string(),
2623 HeapObject::RoaringBitmap(_) => "RoaringBitmap".to_string(),
2624 HeapObject::RateLimiter(_) => "RateLimiter".to_string(),
2625 HeapObject::HashRing(_) => "HashRing".to_string(),
2626 HeapObject::SimHash(_) => "SimHash".to_string(),
2627 HeapObject::MinHash(_) => "MinHash".to_string(),
2628 HeapObject::IntervalTree(_) => "IntervalTree".to_string(),
2629 HeapObject::BkTree(_) => "BkTree".to_string(),
2630 HeapObject::Rope(_) => "Rope".to_string(),
2631 HeapObject::KvStore(_) => "KvStore".to_string(),
2632 HeapObject::Pipeline(_) => "Pipeline".to_string(),
2633 HeapObject::DataFrame(_) => "DataFrame".to_string(),
2634 HeapObject::Capture(_) => "Capture".to_string(),
2635 HeapObject::Ppool(_) => "Ppool".to_string(),
2636 HeapObject::RemoteCluster(_) => "Cluster".to_string(),
2637 HeapObject::Barrier(_) => "Barrier".to_string(),
2638 HeapObject::SqliteConn(_) => "SqliteConn".to_string(),
2639 HeapObject::StructInst(s) => s.def.name.to_string(),
2640 HeapObject::EnumInst(e) => e.def.name.to_string(),
2641 HeapObject::ClassInst(c) => c.def.name.to_string(),
2642 HeapObject::Iterator(_) => "Iterator".to_string(),
2643 HeapObject::ErrnoDual { .. } => "Errno".to_string(),
2644 HeapObject::Integer(_) => "INTEGER".to_string(),
2645 HeapObject::BigInt(_) => "INTEGER".to_string(),
2646 HeapObject::Float(_) => "FLOAT".to_string(),
2647 }
2648 }
2649 pub fn ref_type(&self) -> StrykeValue {
2651 if !nanbox::is_heap(self.0) {
2652 return StrykeValue::string(String::new());
2653 }
2654 match unsafe { self.heap_ref() } {
2655 HeapObject::ArrayRef(_) | HeapObject::ArrayBindingRef(_) => {
2656 StrykeValue::string("ARRAY".into())
2657 }
2658 HeapObject::HashRef(_) | HeapObject::HashBindingRef(_) => {
2659 StrykeValue::string("HASH".into())
2660 }
2661 HeapObject::ScalarRef(_) | HeapObject::ScalarBindingRef(_) => {
2662 StrykeValue::string("SCALAR".into())
2663 }
2664 HeapObject::CodeRef(_) => StrykeValue::string("CODE".into()),
2665 HeapObject::Regex(_, _, _) => StrykeValue::string("Regexp".into()),
2666 HeapObject::Atomic(_) => StrykeValue::string("ATOMIC".into()),
2667 HeapObject::Set(_) => StrykeValue::string("Set".into()),
2668 HeapObject::ChannelTx(_) => StrykeValue::string("PCHANNEL::Tx".into()),
2669 HeapObject::ChannelRx(_) => StrykeValue::string("PCHANNEL::Rx".into()),
2670 HeapObject::AsyncTask(_) => StrykeValue::string("ASYNCTASK".into()),
2671 HeapObject::Generator(_) => StrykeValue::string("Generator".into()),
2672 HeapObject::Deque(_) => StrykeValue::string("Deque".into()),
2673 HeapObject::Heap(_) => StrykeValue::string("Heap".into()),
2674 HeapObject::Mutex(_) => StrykeValue::string("Mutex".into()),
2675 HeapObject::Semaphore(_) => StrykeValue::string("Semaphore".into()),
2676 HeapObject::BloomFilter(_) => StrykeValue::string("BloomFilter".into()),
2677 HeapObject::HllSketch(_) => StrykeValue::string("HllSketch".into()),
2678 HeapObject::CmsSketch(_) => StrykeValue::string("CmsSketch".into()),
2679 HeapObject::TopKSketch(_) => StrykeValue::string("TopKSketch".into()),
2680 HeapObject::TDigestSketch(_) => StrykeValue::string("TDigestSketch".into()),
2681 HeapObject::RoaringBitmap(_) => StrykeValue::string("RoaringBitmap".into()),
2682 HeapObject::RateLimiter(_) => StrykeValue::string("RateLimiter".into()),
2683 HeapObject::HashRing(_) => StrykeValue::string("HashRing".into()),
2684 HeapObject::SimHash(_) => StrykeValue::string("SimHash".into()),
2685 HeapObject::MinHash(_) => StrykeValue::string("MinHash".into()),
2686 HeapObject::IntervalTree(_) => StrykeValue::string("IntervalTree".into()),
2687 HeapObject::BkTree(_) => StrykeValue::string("BkTree".into()),
2688 HeapObject::Rope(_) => StrykeValue::string("Rope".into()),
2689 HeapObject::KvStore(_) => StrykeValue::string("KvStore".into()),
2690 HeapObject::Pipeline(_) => StrykeValue::string("Pipeline".into()),
2691 HeapObject::DataFrame(_) => StrykeValue::string("DataFrame".into()),
2692 HeapObject::Capture(_) => StrykeValue::string("Capture".into()),
2693 HeapObject::Ppool(_) => StrykeValue::string("Ppool".into()),
2694 HeapObject::RemoteCluster(_) => StrykeValue::string("Cluster".into()),
2695 HeapObject::Barrier(_) => StrykeValue::string("Barrier".into()),
2696 HeapObject::SqliteConn(_) => StrykeValue::string("SqliteConn".into()),
2697 HeapObject::StructInst(s) => StrykeValue::string(s.def.name.clone()),
2698 HeapObject::EnumInst(e) => StrykeValue::string(e.def.name.clone()),
2699 HeapObject::ClassInst(c) => StrykeValue::string(c.def.name.clone()),
2700 HeapObject::Bytes(_) => StrykeValue::string("BYTES".into()),
2701 HeapObject::Blessed(b) => StrykeValue::string(b.class.clone()),
2702 _ => StrykeValue::string(String::new()),
2703 }
2704 }
2705 pub fn num_cmp(&self, other: &StrykeValue) -> Ordering {
2707 let a = self.to_number();
2708 let b = other.to_number();
2709 a.partial_cmp(&b).unwrap_or(Ordering::Equal)
2710 }
2711
2712 #[inline]
2714 pub fn str_eq(&self, other: &StrykeValue) -> bool {
2715 if nanbox::is_heap(self.0) && nanbox::is_heap(other.0) {
2716 if let (HeapObject::String(a), HeapObject::String(b)) =
2717 unsafe { (self.heap_ref(), other.heap_ref()) }
2718 {
2719 return a == b;
2720 }
2721 }
2722 self.to_string() == other.to_string()
2723 }
2724 pub fn str_cmp(&self, other: &StrykeValue) -> Ordering {
2726 if nanbox::is_heap(self.0) && nanbox::is_heap(other.0) {
2727 if let (HeapObject::String(a), HeapObject::String(b)) =
2728 unsafe { (self.heap_ref(), other.heap_ref()) }
2729 {
2730 return a.cmp(b);
2731 }
2732 }
2733 self.to_string().cmp(&other.to_string())
2734 }
2735
2736 pub fn struct_field_eq(&self, other: &StrykeValue) -> bool {
2738 if nanbox::is_imm_undef(self.0) && nanbox::is_imm_undef(other.0) {
2739 return true;
2740 }
2741 if let (Some(a), Some(b)) = (nanbox::as_imm_int32(self.0), nanbox::as_imm_int32(other.0)) {
2742 return a == b;
2743 }
2744 if nanbox::is_raw_float_bits(self.0) && nanbox::is_raw_float_bits(other.0) {
2745 return f64::from_bits(self.0) == f64::from_bits(other.0);
2746 }
2747 if !nanbox::is_heap(self.0) || !nanbox::is_heap(other.0) {
2748 return self.to_number() == other.to_number();
2749 }
2750 match (unsafe { self.heap_ref() }, unsafe { other.heap_ref() }) {
2751 (HeapObject::String(a), HeapObject::String(b)) => a == b,
2752 (HeapObject::Integer(a), HeapObject::Integer(b)) => a == b,
2753 (HeapObject::BigInt(a), HeapObject::BigInt(b)) => a == b,
2754 (HeapObject::BigInt(a), HeapObject::Integer(b))
2755 | (HeapObject::Integer(b), HeapObject::BigInt(a)) => a.as_ref() == &BigInt::from(*b),
2756 (HeapObject::Float(a), HeapObject::Float(b)) => a == b,
2757 (HeapObject::Array(a), HeapObject::Array(b)) => {
2758 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| x.struct_field_eq(y))
2759 }
2760 (HeapObject::ArrayRef(a), HeapObject::ArrayRef(b)) => {
2761 let ag = a.read();
2762 let bg = b.read();
2763 ag.len() == bg.len() && ag.iter().zip(bg.iter()).all(|(x, y)| x.struct_field_eq(y))
2764 }
2765 (HeapObject::Hash(a), HeapObject::Hash(b)) => {
2766 a.len() == b.len()
2767 && a.iter()
2768 .all(|(k, v)| b.get(k).is_some_and(|bv| v.struct_field_eq(bv)))
2769 }
2770 (HeapObject::HashRef(a), HeapObject::HashRef(b)) => {
2771 let ag = a.read();
2772 let bg = b.read();
2773 ag.len() == bg.len()
2774 && ag
2775 .iter()
2776 .all(|(k, v)| bg.get(k).is_some_and(|bv| v.struct_field_eq(bv)))
2777 }
2778 (HeapObject::StructInst(a), HeapObject::StructInst(b)) => {
2779 if a.def.name != b.def.name {
2780 false
2781 } else {
2782 let av = a.get_values();
2783 let bv = b.get_values();
2784 av.len() == bv.len()
2785 && av.iter().zip(bv.iter()).all(|(x, y)| x.struct_field_eq(y))
2786 }
2787 }
2788 _ => self.to_string() == other.to_string(),
2789 }
2790 }
2791
2792 pub fn deep_clone(&self) -> StrykeValue {
2794 if !nanbox::is_heap(self.0) {
2795 return self.clone();
2796 }
2797 match unsafe { self.heap_ref() } {
2798 HeapObject::Array(a) => StrykeValue::array(a.iter().map(|v| v.deep_clone()).collect()),
2799 HeapObject::ArrayRef(a) => {
2800 let cloned: Vec<StrykeValue> = a.read().iter().map(|v| v.deep_clone()).collect();
2801 StrykeValue::array_ref(Arc::new(RwLock::new(cloned)))
2802 }
2803 HeapObject::Hash(h) => {
2804 let mut cloned = IndexMap::new();
2805 for (k, v) in h.iter() {
2806 cloned.insert(k.clone(), v.deep_clone());
2807 }
2808 StrykeValue::hash(cloned)
2809 }
2810 HeapObject::HashRef(h) => {
2811 let mut cloned = IndexMap::new();
2812 for (k, v) in h.read().iter() {
2813 cloned.insert(k.clone(), v.deep_clone());
2814 }
2815 StrykeValue::hash_ref(Arc::new(RwLock::new(cloned)))
2816 }
2817 HeapObject::StructInst(s) => {
2818 let new_values = s.get_values().iter().map(|v| v.deep_clone()).collect();
2819 StrykeValue::struct_inst(Arc::new(StructInstance::new(
2820 Arc::clone(&s.def),
2821 new_values,
2822 )))
2823 }
2824 _ => self.clone(),
2825 }
2826 }
2827 pub fn to_list(&self) -> Vec<StrykeValue> {
2829 if nanbox::is_imm_undef(self.0) {
2830 return vec![];
2831 }
2832 if !nanbox::is_heap(self.0) {
2833 return vec![self.clone()];
2834 }
2835 match unsafe { self.heap_ref() } {
2836 HeapObject::Array(a) => a.clone(),
2837 HeapObject::Hash(h) => h
2838 .iter()
2839 .flat_map(|(k, v)| vec![StrykeValue::string(k.clone()), v.clone()])
2840 .collect(),
2841 HeapObject::Atomic(arc) => arc.lock().to_list(),
2842 HeapObject::Set(s) => s.values().cloned().collect(),
2843 HeapObject::Deque(d) => d.lock().iter().cloned().collect(),
2844 HeapObject::Iterator(it) => {
2845 let mut out = Vec::new();
2846 while let Some(v) = it.next_item() {
2847 out.push(v);
2848 }
2849 out
2850 }
2851 _ => vec![self.clone()],
2852 }
2853 }
2854 pub fn scalar_context(&self) -> StrykeValue {
2856 if !nanbox::is_heap(self.0) {
2857 return self.clone();
2858 }
2859 if let Some(arc) = self.as_atomic_arc() {
2860 return arc.lock().scalar_context();
2861 }
2862 match unsafe { self.heap_ref() } {
2863 HeapObject::Array(a) => StrykeValue::integer(a.len() as i64),
2864 HeapObject::Hash(h) => {
2865 if h.is_empty() {
2866 StrykeValue::integer(0)
2867 } else {
2868 StrykeValue::string(format!("{}/{}", h.len(), h.capacity()))
2869 }
2870 }
2871 HeapObject::Set(s) => StrykeValue::integer(s.len() as i64),
2872 HeapObject::Deque(d) => StrykeValue::integer(d.lock().len() as i64),
2873 HeapObject::Heap(h) => StrykeValue::integer(h.lock().items.len() as i64),
2874 HeapObject::Mutex(m) => StrykeValue::integer(i64::from(*m.held.lock())),
2875 HeapObject::Semaphore(s) => StrykeValue::integer(*s.permits.lock()),
2876 HeapObject::Pipeline(p) => StrykeValue::integer(p.lock().source.len() as i64),
2877 HeapObject::Capture(_)
2878 | HeapObject::Ppool(_)
2879 | HeapObject::RemoteCluster(_)
2880 | HeapObject::Barrier(_) => StrykeValue::integer(1),
2881 HeapObject::Generator(_) => StrykeValue::integer(1),
2882 _ => self.clone(),
2883 }
2884 }
2885}
2886
2887impl fmt::Display for StrykeValue {
2888 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2889 if nanbox::is_imm_undef(self.0) {
2890 return Ok(());
2891 }
2892 if let Some(n) = nanbox::as_imm_int32(self.0) {
2893 return write!(f, "{n}");
2894 }
2895 if nanbox::is_raw_float_bits(self.0) {
2896 return write!(f, "{}", format_float(f64::from_bits(self.0)));
2897 }
2898 match unsafe { self.heap_ref() } {
2899 HeapObject::Integer(n) => write!(f, "{n}"),
2900 HeapObject::BigInt(b) => write!(f, "{b}"),
2901 HeapObject::Float(val) => write!(f, "{}", format_float(*val)),
2902 HeapObject::ErrnoDual { msg, .. } => f.write_str(msg),
2903 HeapObject::String(s) => f.write_str(s),
2904 HeapObject::Bytes(b) => f.write_str(&decode_utf8_or_latin1(b)),
2905 HeapObject::Array(a) => {
2906 for v in a {
2907 write!(f, "{v}")?;
2908 }
2909 Ok(())
2910 }
2911 HeapObject::Hash(h) => write!(f, "{}/{}", h.len(), h.capacity()),
2912 HeapObject::ArrayRef(_) | HeapObject::ArrayBindingRef(_) => f.write_str("ARRAY(0x...)"),
2913 HeapObject::HashRef(_) | HeapObject::HashBindingRef(_) => f.write_str("HASH(0x...)"),
2914 HeapObject::ScalarRef(_)
2915 | HeapObject::ScalarBindingRef(_)
2916 | HeapObject::CaptureCell(_) => f.write_str("SCALAR(0x...)"),
2917 HeapObject::CodeRef(sub) => {
2918 let addr = Arc::as_ptr(sub) as usize;
2924 write!(f, "CODE(0x{:x})", addr)
2925 }
2926 HeapObject::Regex(_, src, _) => write!(f, "(?:{src})"),
2927 HeapObject::Blessed(b) => write!(f, "{}=HASH(0x...)", b.class),
2928 HeapObject::IOHandle(name) => f.write_str(name),
2929 HeapObject::Atomic(arc) => write!(f, "{}", arc.lock()),
2930 HeapObject::Set(s) => {
2931 f.write_str("{")?;
2932 if !s.is_empty() {
2933 let mut iter = s.values();
2934 if let Some(v) = iter.next() {
2935 write!(f, "{v}")?;
2936 }
2937 for v in iter {
2938 write!(f, ",{v}")?;
2939 }
2940 }
2941 f.write_str("}")
2942 }
2943 HeapObject::ChannelTx(_) => f.write_str("PCHANNEL::Tx"),
2944 HeapObject::ChannelRx(_) => f.write_str("PCHANNEL::Rx"),
2945 HeapObject::AsyncTask(_) => f.write_str("AsyncTask"),
2946 HeapObject::Generator(g) => write!(f, "Generator({} stmts)", g.block.len()),
2947 HeapObject::Deque(d) => write!(f, "Deque({})", d.lock().len()),
2948 HeapObject::Heap(h) => write!(f, "Heap({})", h.lock().items.len()),
2949 HeapObject::Mutex(m) => write!(f, "Mutex({})", *m.held.lock()),
2950 HeapObject::Semaphore(s) => {
2951 write!(f, "Semaphore({}/{})", *s.permits.lock(), s.limit)
2952 }
2953 HeapObject::BloomFilter(b) => {
2954 let g = b.lock();
2955 write!(
2956 f,
2957 "BloomFilter(n={}, bits={}, k={})",
2958 g.inserted(),
2959 g.bit_count(),
2960 g.k()
2961 )
2962 }
2963 HeapObject::HllSketch(s) => {
2964 let g = s.lock();
2965 write!(f, "HllSketch(p={}, m={})", g.precision(), g.registers_len())
2966 }
2967 HeapObject::CmsSketch(s) => {
2968 let g = s.lock();
2969 write!(f, "CmsSketch(w={}, d={})", g.width(), g.depth())
2970 }
2971 HeapObject::TopKSketch(s) => {
2972 let g = s.lock();
2973 write!(f, "TopKSketch(k={}, n={})", g.k(), g.size())
2974 }
2975 HeapObject::TDigestSketch(s) => {
2976 let g = s.lock();
2977 write!(f, "TDigestSketch(compression={})", g.compression())
2978 }
2979 HeapObject::RoaringBitmap(s) => {
2980 let g = s.lock();
2981 write!(f, "RoaringBitmap(n={})", g.len())
2982 }
2983 HeapObject::RateLimiter(s) => {
2984 let g = s.lock();
2985 let kind = if g.leaky { "leaky" } else { "token" };
2986 write!(
2987 f,
2988 "RateLimiter({}, cap={}, rate={}/s)",
2989 kind, g.capacity, g.rate_per_sec
2990 )
2991 }
2992 HeapObject::HashRing(s) => {
2993 let g = s.lock();
2994 write!(
2995 f,
2996 "HashRing(nodes={}, vnodes={})",
2997 g.node_count(),
2998 g.vnodes_per_node
2999 )
3000 }
3001 HeapObject::SimHash(s) => {
3002 let g = s.lock();
3003 write!(f, "SimHash(features={})", g.feature_count())
3004 }
3005 HeapObject::MinHash(s) => {
3006 let g = s.lock();
3007 write!(f, "MinHash(k={})", g.k())
3008 }
3009 HeapObject::IntervalTree(s) => {
3010 let g = s.lock();
3011 write!(f, "IntervalTree(n={})", g.len())
3012 }
3013 HeapObject::BkTree(s) => {
3014 let g = s.lock();
3015 write!(f, "BkTree(n={})", g.len())
3016 }
3017 HeapObject::Rope(s) => {
3018 let g = s.lock();
3019 write!(f, "Rope(len={}, bytes={})", g.len(), g.byte_len())
3020 }
3021 HeapObject::Pipeline(p) => {
3022 let g = p.lock();
3023 write!(f, "Pipeline({} ops)", g.ops.len())
3024 }
3025 HeapObject::Capture(c) => write!(f, "Capture(exit={})", c.exitcode),
3026 HeapObject::Ppool(_) => f.write_str("Ppool"),
3027 HeapObject::RemoteCluster(c) => write!(f, "Cluster({} slots)", c.slots.len()),
3028 HeapObject::Barrier(_) => f.write_str("Barrier"),
3029 HeapObject::SqliteConn(_) => f.write_str("SqliteConn"),
3030 HeapObject::StructInst(s) => {
3031 write!(f, "{}(", s.def.name)?;
3033 let values = s.values.read();
3034 for (i, field) in s.def.fields.iter().enumerate() {
3035 if i > 0 {
3036 f.write_str(", ")?;
3037 }
3038 write!(
3039 f,
3040 "{} => {}",
3041 field.name,
3042 values.get(i).cloned().unwrap_or(StrykeValue::UNDEF)
3043 )?;
3044 }
3045 f.write_str(")")
3046 }
3047 HeapObject::EnumInst(e) => {
3048 write!(f, "{}::{}", e.def.name, e.variant_name())?;
3050 if e.def.variants[e.variant_idx].ty.is_some() {
3051 write!(f, "({})", e.data)?;
3052 }
3053 Ok(())
3054 }
3055 HeapObject::ClassInst(c) => {
3056 write!(f, "{}(", c.def.name)?;
3058 let values = c.values.read();
3059 for (i, field) in c.def.fields.iter().enumerate() {
3060 if i > 0 {
3061 f.write_str(", ")?;
3062 }
3063 write!(
3064 f,
3065 "{} => {}",
3066 field.name,
3067 values.get(i).cloned().unwrap_or(StrykeValue::UNDEF)
3068 )?;
3069 }
3070 f.write_str(")")
3071 }
3072 HeapObject::DataFrame(d) => {
3073 let g = d.lock();
3074 write!(f, "DataFrame({} rows)", g.nrows())
3075 }
3076 HeapObject::Iterator(_) => f.write_str("Iterator"),
3077 HeapObject::KvStore(s) => {
3078 let g = s.lock();
3079 write!(f, "KvStore({} entries)", g.len())
3080 }
3081 }
3082 }
3083}
3084
3085pub fn set_member_key(v: &StrykeValue) -> String {
3087 if nanbox::is_imm_undef(v.0) {
3088 return "u:".to_string();
3089 }
3090 if let Some(n) = nanbox::as_imm_int32(v.0) {
3091 return format!("i:{n}");
3092 }
3093 if nanbox::is_raw_float_bits(v.0) {
3094 return format!("f:{}", f64::from_bits(v.0).to_bits());
3095 }
3096 match unsafe { v.heap_ref() } {
3097 HeapObject::String(s) => format!("s:{s}"),
3098 HeapObject::Bytes(b) => {
3099 use std::fmt::Write as _;
3100 let mut h = String::with_capacity(b.len() * 2);
3101 for &x in b.iter() {
3102 let _ = write!(&mut h, "{:02x}", x);
3103 }
3104 format!("by:{h}")
3105 }
3106 HeapObject::Array(a) => {
3107 let parts: Vec<_> = a.iter().map(set_member_key).collect();
3108 format!("a:{}", parts.join(","))
3109 }
3110 HeapObject::Hash(h) => {
3111 let mut keys: Vec<_> = h.keys().cloned().collect();
3112 keys.sort();
3113 let parts: Vec<_> = keys
3114 .iter()
3115 .map(|k| format!("{}={}", k, set_member_key(h.get(k).unwrap())))
3116 .collect();
3117 format!("h:{}", parts.join(","))
3118 }
3119 HeapObject::Set(inner) => {
3120 let mut keys: Vec<_> = inner.keys().cloned().collect();
3121 keys.sort();
3122 format!("S:{}", keys.join(","))
3123 }
3124 HeapObject::ArrayRef(a) => {
3125 let g = a.read();
3126 let parts: Vec<_> = g.iter().map(set_member_key).collect();
3127 format!("ar:{}", parts.join(","))
3128 }
3129 HeapObject::HashRef(h) => {
3130 let g = h.read();
3131 let mut keys: Vec<_> = g.keys().cloned().collect();
3132 keys.sort();
3133 let parts: Vec<_> = keys
3134 .iter()
3135 .map(|k| format!("{}={}", k, set_member_key(g.get(k).unwrap())))
3136 .collect();
3137 format!("hr:{}", parts.join(","))
3138 }
3139 HeapObject::Blessed(b) => {
3140 let d = b.data.read();
3141 format!("b:{}:{}", b.class, set_member_key(&d))
3142 }
3143 HeapObject::ScalarRef(_) | HeapObject::ScalarBindingRef(_) | HeapObject::CaptureCell(_) => {
3144 format!("sr:{v}")
3145 }
3146 HeapObject::ArrayBindingRef(n) => format!("abind:{n}"),
3147 HeapObject::HashBindingRef(n) => format!("hbind:{n}"),
3148 HeapObject::CodeRef(_) => format!("c:{v}"),
3149 HeapObject::Regex(_, src, _) => format!("r:{src}"),
3150 HeapObject::IOHandle(s) => format!("io:{s}"),
3151 HeapObject::Atomic(arc) => format!("at:{}", set_member_key(&arc.lock())),
3152 HeapObject::ChannelTx(tx) => format!("chtx:{:p}", Arc::as_ptr(tx)),
3153 HeapObject::ChannelRx(rx) => format!("chrx:{:p}", Arc::as_ptr(rx)),
3154 HeapObject::AsyncTask(t) => format!("async:{:p}", Arc::as_ptr(t)),
3155 HeapObject::Generator(g) => format!("gen:{:p}", Arc::as_ptr(g)),
3156 HeapObject::Deque(d) => format!("dq:{:p}", Arc::as_ptr(d)),
3157 HeapObject::Heap(h) => format!("hp:{:p}", Arc::as_ptr(h)),
3158 HeapObject::Mutex(m) => format!("mu:{:p}", Arc::as_ptr(m)),
3159 HeapObject::Semaphore(s) => format!("se:{:p}", Arc::as_ptr(s)),
3160 HeapObject::BloomFilter(b) => format!("bf:{:p}", Arc::as_ptr(b)),
3161 HeapObject::HllSketch(s) => format!("hll:{:p}", Arc::as_ptr(s)),
3162 HeapObject::CmsSketch(s) => format!("cms:{:p}", Arc::as_ptr(s)),
3163 HeapObject::TopKSketch(s) => format!("topk:{:p}", Arc::as_ptr(s)),
3164 HeapObject::TDigestSketch(s) => format!("td:{:p}", Arc::as_ptr(s)),
3165 HeapObject::RoaringBitmap(s) => format!("rb:{:p}", Arc::as_ptr(s)),
3166 HeapObject::RateLimiter(s) => format!("rl:{:p}", Arc::as_ptr(s)),
3167 HeapObject::HashRing(s) => format!("hr:{:p}", Arc::as_ptr(s)),
3168 HeapObject::SimHash(s) => format!("sh:{:p}", Arc::as_ptr(s)),
3169 HeapObject::MinHash(s) => format!("mh:{:p}", Arc::as_ptr(s)),
3170 HeapObject::IntervalTree(s) => format!("it:{:p}", Arc::as_ptr(s)),
3171 HeapObject::BkTree(s) => format!("bk:{:p}", Arc::as_ptr(s)),
3172 HeapObject::Rope(s) => format!("rp:{:p}", Arc::as_ptr(s)),
3173 HeapObject::Pipeline(p) => format!("pl:{:p}", Arc::as_ptr(p)),
3174 HeapObject::Capture(c) => format!("cap:{:p}", Arc::as_ptr(c)),
3175 HeapObject::Ppool(p) => format!("pp:{:p}", Arc::as_ptr(&p.0)),
3176 HeapObject::RemoteCluster(c) => format!("rcl:{:p}", Arc::as_ptr(c)),
3177 HeapObject::Barrier(b) => format!("br:{:p}", Arc::as_ptr(&b.0)),
3178 HeapObject::SqliteConn(c) => format!("sql:{:p}", Arc::as_ptr(c)),
3179 HeapObject::StructInst(s) => format!("st:{}:{:?}", s.def.name, s.values),
3180 HeapObject::EnumInst(e) => {
3181 format!("en:{}::{}:{}", e.def.name, e.variant_name(), e.data)
3182 }
3183 HeapObject::ClassInst(c) => format!("cl:{}:{:?}", c.def.name, c.values),
3184 HeapObject::DataFrame(d) => format!("df:{:p}", Arc::as_ptr(d)),
3185 HeapObject::KvStore(s) => format!("kv:{:p}", Arc::as_ptr(s)),
3186 HeapObject::Iterator(_) => "iter".to_string(),
3187 HeapObject::ErrnoDual { code, msg } => format!("e:{code}:{msg}"),
3188 HeapObject::Integer(n) => format!("i:{n}"),
3189 HeapObject::BigInt(b) => format!("bi:{b}"),
3190 HeapObject::Float(fl) => format!("f:{}", fl.to_bits()),
3191 }
3192}
3193
3194#[inline]
3205pub fn chr_from_codepoint(n: i64) -> String {
3210 char::from_u32(n as u32)
3211 .map(|c| c.to_string())
3212 .unwrap_or_default()
3213}
3214
3215pub fn perl_mod_i64(a: i64, b: i64) -> i64 {
3216 debug_assert_ne!(b, 0);
3217 let r = a.wrapping_rem(b);
3218 if r != 0 && (r ^ b) < 0 {
3221 r + b
3222 } else {
3223 r
3224 }
3225}
3226
3227#[inline]
3231pub fn perl_shl_i64(a: i64, b: i64) -> i64 {
3232 if !(0..64).contains(&b) {
3233 0
3234 } else {
3235 ((a as u64).wrapping_shl(b as u32)) as i64
3236 }
3237}
3238
3239#[inline]
3244pub fn perl_shr_i64(a: i64, b: i64) -> i64 {
3245 if b < 0 {
3246 0
3247 } else if b >= 64 {
3248 if a < 0 {
3249 -1
3250 } else {
3251 0
3252 }
3253 } else {
3254 a >> b
3255 }
3256}
3257
3258#[inline]
3262pub fn compat_mul(a: &StrykeValue, b: &StrykeValue) -> StrykeValue {
3263 if a.as_bigint().is_some() || b.as_bigint().is_some() {
3264 return StrykeValue::bigint(a.to_bigint() * b.to_bigint());
3265 }
3266 let (Some(x), Some(y)) = (a.as_integer(), b.as_integer()) else {
3267 return StrykeValue::float(a.to_number() * b.to_number());
3268 };
3269 if crate::compat_mode() || crate::bigint_pragma() {
3270 match x.checked_mul(y) {
3271 Some(r) => StrykeValue::integer(r),
3272 None => StrykeValue::bigint(BigInt::from(x) * BigInt::from(y)),
3273 }
3274 } else {
3275 StrykeValue::integer(x.wrapping_mul(y))
3276 }
3277}
3278#[inline]
3280pub fn compat_add(a: &StrykeValue, b: &StrykeValue) -> StrykeValue {
3281 if a.as_bigint().is_some() || b.as_bigint().is_some() {
3282 return StrykeValue::bigint(a.to_bigint() + b.to_bigint());
3283 }
3284 let (Some(x), Some(y)) = (a.as_integer(), b.as_integer()) else {
3285 return StrykeValue::float(a.to_number() + b.to_number());
3286 };
3287 if crate::compat_mode() || crate::bigint_pragma() {
3288 match x.checked_add(y) {
3289 Some(r) => StrykeValue::integer(r),
3290 None => StrykeValue::bigint(BigInt::from(x) + BigInt::from(y)),
3291 }
3292 } else {
3293 StrykeValue::integer(x.wrapping_add(y))
3294 }
3295}
3296#[inline]
3298pub fn compat_sub(a: &StrykeValue, b: &StrykeValue) -> StrykeValue {
3299 if a.as_bigint().is_some() || b.as_bigint().is_some() {
3300 return StrykeValue::bigint(a.to_bigint() - b.to_bigint());
3301 }
3302 let (Some(x), Some(y)) = (a.as_integer(), b.as_integer()) else {
3303 return StrykeValue::float(a.to_number() - b.to_number());
3304 };
3305 if crate::compat_mode() || crate::bigint_pragma() {
3306 match x.checked_sub(y) {
3307 Some(r) => StrykeValue::integer(r),
3308 None => StrykeValue::bigint(BigInt::from(x) - BigInt::from(y)),
3309 }
3310 } else {
3311 StrykeValue::integer(x.wrapping_sub(y))
3312 }
3313}
3314
3315#[inline]
3320pub fn compat_pow(a: &StrykeValue, b: &StrykeValue) -> StrykeValue {
3321 let (Some(base), Some(exp)) = (a.as_integer(), b.as_integer()) else {
3322 return StrykeValue::float(a.to_number().powf(b.to_number()));
3323 };
3324 let bigint_active = crate::compat_mode() || crate::bigint_pragma();
3325 if !bigint_active {
3326 return StrykeValue::float((base as f64).powf(exp as f64));
3329 }
3330 if exp < 0 {
3331 return StrykeValue::float((base as f64).powf(exp as f64));
3332 }
3333 use num_traits::Pow;
3334 let result = BigInt::from(base).pow(exp as u32);
3335 StrykeValue::bigint(result)
3336}
3337pub fn set_from_elements<I: IntoIterator<Item = StrykeValue>>(items: I) -> StrykeValue {
3339 let mut map = PerlSet::new();
3340 for v in items {
3341 let k = set_member_key(&v);
3342 map.insert(k, v);
3343 }
3344 StrykeValue::set(Arc::new(map))
3345}
3346
3347#[inline]
3349pub fn set_payload(v: &StrykeValue) -> Option<Arc<PerlSet>> {
3350 if !nanbox::is_heap(v.0) {
3351 return None;
3352 }
3353 match unsafe { v.heap_ref() } {
3354 HeapObject::Set(s) => Some(Arc::clone(s)),
3355 HeapObject::Atomic(a) => set_payload(&a.lock()),
3356 _ => None,
3357 }
3358}
3359pub fn set_union(a: &StrykeValue, b: &StrykeValue) -> Option<StrykeValue> {
3361 let ia = set_payload(a)?;
3362 let ib = set_payload(b)?;
3363 let mut m = (*ia).clone();
3364 for (k, v) in ib.iter() {
3365 m.entry(k.clone()).or_insert_with(|| v.clone());
3366 }
3367 Some(StrykeValue::set(Arc::new(m)))
3368}
3369pub fn set_intersection(a: &StrykeValue, b: &StrykeValue) -> Option<StrykeValue> {
3371 let ia = set_payload(a)?;
3372 let ib = set_payload(b)?;
3373 let mut m = PerlSet::new();
3374 for (k, v) in ia.iter() {
3375 if ib.contains_key(k) {
3376 m.insert(k.clone(), v.clone());
3377 }
3378 }
3379 Some(StrykeValue::set(Arc::new(m)))
3380}
3381fn parse_number(s: &str) -> f64 {
3382 let s = s.trim();
3383 if s.is_empty() {
3384 return 0.0;
3385 }
3386 {
3389 let bytes = s.as_bytes();
3390 let (sign, rest) = match bytes.first() {
3391 Some(b'+') => (1.0_f64, &s[1..]),
3392 Some(b'-') => (-1.0_f64, &s[1..]),
3393 _ => (1.0_f64, s),
3394 };
3395 if rest.eq_ignore_ascii_case("inf") || rest.eq_ignore_ascii_case("infinity") {
3396 return sign * f64::INFINITY;
3397 }
3398 if rest.eq_ignore_ascii_case("nan") {
3399 return f64::NAN;
3403 }
3404 }
3405 let mut end = 0;
3407 let bytes = s.as_bytes();
3408 if end < bytes.len() && (bytes[end] == b'+' || bytes[end] == b'-') {
3409 end += 1;
3410 }
3411 while end < bytes.len() && bytes[end].is_ascii_digit() {
3412 end += 1;
3413 }
3414 if end < bytes.len() && bytes[end] == b'.' {
3415 end += 1;
3416 while end < bytes.len() && bytes[end].is_ascii_digit() {
3417 end += 1;
3418 }
3419 }
3420 if end < bytes.len() && (bytes[end] == b'e' || bytes[end] == b'E') {
3421 end += 1;
3422 if end < bytes.len() && (bytes[end] == b'+' || bytes[end] == b'-') {
3423 end += 1;
3424 }
3425 while end < bytes.len() && bytes[end].is_ascii_digit() {
3426 end += 1;
3427 }
3428 }
3429 if end == 0 {
3430 return 0.0;
3431 }
3432 s[..end].parse::<f64>().unwrap_or(0.0)
3433}
3434
3435fn format_float(f: f64) -> String {
3436 if f.is_nan() {
3438 return "NaN".to_string();
3439 }
3440 if f.is_infinite() {
3441 return if f.is_sign_negative() {
3442 "-Inf".to_string()
3443 } else {
3444 "Inf".to_string()
3445 };
3446 }
3447 if f.fract() == 0.0 && f.abs() < 1e16 {
3448 format!("{}", f as i64)
3449 } else {
3450 let mut buf = [0u8; 64];
3452 unsafe {
3453 libc::snprintf(
3454 buf.as_mut_ptr() as *mut libc::c_char,
3455 buf.len(),
3456 c"%.15g".as_ptr(),
3457 f,
3458 );
3459 std::ffi::CStr::from_ptr(buf.as_ptr() as *const libc::c_char)
3460 .to_string_lossy()
3461 .into_owned()
3462 }
3463 }
3464}
3465
3466#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3468pub(crate) enum PerlListRangeIncOutcome {
3469 Continue,
3470 BecameNumeric,
3472}
3473
3474fn perl_str_looks_like_number_for_range(s: &str) -> bool {
3477 let t = s.trim();
3478 if t.is_empty() {
3479 return s.is_empty();
3480 }
3481 let b = t.as_bytes();
3482 let mut i = 0usize;
3483 if i < b.len() && (b[i] == b'+' || b[i] == b'-') {
3484 i += 1;
3485 }
3486 if i >= b.len() {
3487 return false;
3488 }
3489 let mut saw_digit = false;
3490 while i < b.len() && b[i].is_ascii_digit() {
3491 saw_digit = true;
3492 i += 1;
3493 }
3494 if i < b.len() && b[i] == b'.' {
3495 i += 1;
3496 while i < b.len() && b[i].is_ascii_digit() {
3497 saw_digit = true;
3498 i += 1;
3499 }
3500 }
3501 if !saw_digit {
3502 return false;
3503 }
3504 if i < b.len() && (b[i] == b'e' || b[i] == b'E') {
3505 i += 1;
3506 if i < b.len() && (b[i] == b'+' || b[i] == b'-') {
3507 i += 1;
3508 }
3509 let exp0 = i;
3510 while i < b.len() && b[i].is_ascii_digit() {
3511 i += 1;
3512 }
3513 if i == exp0 {
3514 return false;
3515 }
3516 }
3517 i == b.len()
3518}
3519
3520pub(crate) fn perl_list_range_pair_is_numeric(left: &StrykeValue, right: &StrykeValue) -> bool {
3522 if left.is_integer_like() || left.is_float_like() {
3523 return true;
3524 }
3525 if !left.is_undef() && !left.is_string_like() {
3526 return true;
3527 }
3528 if right.is_integer_like() || right.is_float_like() {
3529 return true;
3530 }
3531 if !right.is_undef() && !right.is_string_like() {
3532 return true;
3533 }
3534
3535 let left_ok = !left.is_undef();
3536 let right_ok = !right.is_undef();
3537 let left_pok = left.is_string_like();
3538 let left_pv = left.as_str_or_empty();
3539 let right_pv = right.as_str_or_empty();
3540
3541 let left_n = perl_str_looks_like_number_for_range(&left_pv);
3542 let right_n = perl_str_looks_like_number_for_range(&right_pv);
3543
3544 let left_zero_prefix =
3545 left_pok && left_pv.len() > 1 && left_pv.as_bytes().first() == Some(&b'0');
3546
3547 let clause5_left =
3548 (!left_ok && right_ok) || ((!left_ok || left_n) && left_pok && !left_zero_prefix);
3549 clause5_left && (!right_ok || right_n)
3550}
3551
3552pub(crate) fn perl_magic_string_increment_for_range(s: &mut String) -> PerlListRangeIncOutcome {
3554 if s.is_empty() {
3555 return PerlListRangeIncOutcome::BecameNumeric;
3556 }
3557 let b = s.as_bytes();
3558 let mut i = 0usize;
3559 while i < b.len() && b[i].is_ascii_alphabetic() {
3560 i += 1;
3561 }
3562 while i < b.len() && b[i].is_ascii_digit() {
3563 i += 1;
3564 }
3565 if i < b.len() {
3566 let n = parse_number(s) + 1.0;
3567 *s = format_float(n);
3568 return PerlListRangeIncOutcome::BecameNumeric;
3569 }
3570
3571 let bytes = unsafe { s.as_mut_vec() };
3572 let mut idx = bytes.len() - 1;
3573 loop {
3574 if bytes[idx].is_ascii_digit() {
3575 bytes[idx] += 1;
3576 if bytes[idx] <= b'9' {
3577 return PerlListRangeIncOutcome::Continue;
3578 }
3579 bytes[idx] = b'0';
3580 if idx == 0 {
3581 bytes.insert(0, b'1');
3582 return PerlListRangeIncOutcome::Continue;
3583 }
3584 idx -= 1;
3585 } else {
3586 bytes[idx] = bytes[idx].wrapping_add(1);
3587 if bytes[idx].is_ascii_alphabetic() {
3588 return PerlListRangeIncOutcome::Continue;
3589 }
3590 bytes[idx] = bytes[idx].wrapping_sub(b'z' - b'a' + 1);
3591 if idx == 0 {
3592 let c = bytes[0];
3593 bytes.insert(0, if c.is_ascii_digit() { b'1' } else { c });
3594 return PerlListRangeIncOutcome::Continue;
3595 }
3596 idx -= 1;
3597 }
3598 }
3599}
3600
3601pub(crate) fn perl_magic_string_decrement_for_range(s: &mut String) -> Option<()> {
3604 if s.is_empty() {
3605 return None;
3606 }
3607 let b = s.as_bytes();
3609 let mut i = 0usize;
3610 while i < b.len() && b[i].is_ascii_alphabetic() {
3611 i += 1;
3612 }
3613 while i < b.len() && b[i].is_ascii_digit() {
3614 i += 1;
3615 }
3616 if i < b.len() {
3617 return None; }
3619
3620 let bytes = unsafe { s.as_mut_vec() };
3621 let mut idx = bytes.len() - 1;
3622 loop {
3623 if bytes[idx].is_ascii_digit() {
3624 if bytes[idx] > b'0' {
3625 bytes[idx] -= 1;
3626 return Some(());
3627 }
3628 bytes[idx] = b'9';
3630 if idx == 0 {
3631 if bytes.len() == 1 {
3633 bytes[0] = b'0'; return None;
3635 }
3636 bytes.remove(0);
3637 return Some(());
3638 }
3639 idx -= 1;
3640 } else if bytes[idx].is_ascii_lowercase() {
3641 if bytes[idx] > b'a' {
3642 bytes[idx] -= 1;
3643 return Some(());
3644 }
3645 bytes[idx] = b'z';
3647 if idx == 0 {
3648 if bytes.len() == 1 {
3650 bytes[0] = b'a'; return None;
3652 }
3653 bytes.remove(0);
3654 return Some(());
3655 }
3656 idx -= 1;
3657 } else if bytes[idx].is_ascii_uppercase() {
3658 if bytes[idx] > b'A' {
3659 bytes[idx] -= 1;
3660 return Some(());
3661 }
3662 bytes[idx] = b'Z';
3664 if idx == 0 {
3665 if bytes.len() == 1 {
3666 bytes[0] = b'A'; return None;
3668 }
3669 bytes.remove(0);
3670 return Some(());
3671 }
3672 idx -= 1;
3673 } else {
3674 return None;
3675 }
3676 }
3677}
3678
3679fn perl_list_range_max_bound(right: &str) -> usize {
3680 if right.is_ascii() {
3681 right.len()
3682 } else {
3683 right.chars().count()
3684 }
3685}
3686
3687fn perl_list_range_cur_bound(cur: &str, right_is_ascii: bool) -> usize {
3688 if right_is_ascii {
3689 cur.len()
3690 } else {
3691 cur.chars().count()
3692 }
3693}
3694
3695fn perl_list_range_expand_string_magic(from: StrykeValue, to: StrykeValue) -> Vec<StrykeValue> {
3696 let mut cur = from.into_string();
3697 let right = to.into_string();
3698 let right_ascii = right.is_ascii();
3699 let max_bound = perl_list_range_max_bound(&right);
3700 let mut out = Vec::new();
3701 let mut guard = 0usize;
3702 loop {
3703 guard += 1;
3704 if guard > 50_000_000 {
3705 break;
3706 }
3707 let cur_bound = perl_list_range_cur_bound(&cur, right_ascii);
3708 if cur_bound > max_bound {
3709 break;
3710 }
3711 out.push(StrykeValue::string(cur.clone()));
3712 if cur == right {
3713 break;
3714 }
3715 match perl_magic_string_increment_for_range(&mut cur) {
3716 PerlListRangeIncOutcome::Continue => {}
3717 PerlListRangeIncOutcome::BecameNumeric => break,
3718 }
3719 }
3720 out
3721}
3722
3723pub(crate) fn perl_list_range_expand(from: StrykeValue, to: StrykeValue) -> Vec<StrykeValue> {
3725 if perl_list_range_pair_is_numeric(&from, &to) {
3726 let i = from.to_int();
3727 let j = to.to_int();
3728 if j >= i {
3729 (i..=j).map(StrykeValue::integer).collect()
3730 } else {
3731 Vec::new()
3732 }
3733 } else {
3734 perl_list_range_expand_string_magic(from, to)
3735 }
3736}
3737
3738fn is_roman_numeral(s: &str) -> bool {
3744 if s.is_empty() {
3745 return false;
3746 }
3747 let upper = s.to_ascii_uppercase();
3748 upper
3749 .chars()
3750 .all(|c| matches!(c, 'I' | 'V' | 'X' | 'L' | 'C' | 'D' | 'M'))
3751}
3752
3753fn is_ipv4(s: &str) -> bool {
3755 let parts: Vec<&str> = s.split('.').collect();
3756 parts.len() == 4 && parts.iter().all(|p| p.parse::<u8>().is_ok())
3757}
3758
3759fn ipv4_to_u32(s: &str) -> Option<u32> {
3761 let parts: Vec<u8> = s.split('.').filter_map(|p| p.parse().ok()).collect();
3762 if parts.len() != 4 {
3763 return None;
3764 }
3765 Some(
3766 ((parts[0] as u32) << 24)
3767 | ((parts[1] as u32) << 16)
3768 | ((parts[2] as u32) << 8)
3769 | (parts[3] as u32),
3770 )
3771}
3772
3773fn u32_to_ipv4(n: u32) -> String {
3775 format!(
3776 "{}.{}.{}.{}",
3777 (n >> 24) & 0xFF,
3778 (n >> 16) & 0xFF,
3779 (n >> 8) & 0xFF,
3780 n & 0xFF
3781 )
3782}
3783
3784fn ipv4_range_stepped(from: &str, to: &str, step: i64) -> Vec<StrykeValue> {
3786 let Some(start) = ipv4_to_u32(from) else {
3787 return vec![];
3788 };
3789 let Some(end) = ipv4_to_u32(to) else {
3790 return vec![];
3791 };
3792 let mut out = Vec::new();
3793 if step > 0 {
3794 let mut cur = start as i64;
3795 while cur <= end as i64 {
3796 out.push(StrykeValue::string(u32_to_ipv4(cur as u32)));
3797 cur += step;
3798 }
3799 } else {
3800 let mut cur = start as i64;
3801 while cur >= end as i64 {
3802 out.push(StrykeValue::string(u32_to_ipv4(cur as u32)));
3803 cur += step;
3804 }
3805 }
3806 out
3807}
3808
3809fn is_ipv6(s: &str) -> bool {
3812 s.parse::<std::net::Ipv6Addr>().is_ok()
3813}
3814
3815fn is_hex_source_literal(s: &str) -> bool {
3820 let bytes = s.as_bytes();
3821 bytes.len() > 2
3822 && bytes[0] == b'0'
3823 && (bytes[1] == b'x' || bytes[1] == b'X')
3824 && bytes[2..].iter().all(|b| b.is_ascii_hexdigit())
3825}
3826
3827fn hex_range_stepped(from: &str, to: &str, step: i64) -> Vec<StrykeValue> {
3834 let from_body = &from[2..];
3835 let to_body = &to[2..];
3836 let Ok(start) = i64::from_str_radix(from_body, 16) else {
3837 return vec![];
3838 };
3839 let Ok(end) = i64::from_str_radix(to_body, 16) else {
3840 return vec![];
3841 };
3842 let prefix = &from[..2];
3843 let width = from_body.len().max(to_body.len());
3844 let upper = from_body.bytes().any(|b| b.is_ascii_uppercase())
3845 || to_body.bytes().any(|b| b.is_ascii_uppercase());
3846 let mut out = Vec::new();
3847 let format_one = |n: i64, width: usize, upper: bool, prefix: &str| -> String {
3848 if upper {
3849 format!("{}{:0>w$X}", prefix, n, w = width)
3850 } else {
3851 format!("{}{:0>w$x}", prefix, n, w = width)
3852 }
3853 };
3854 if step > 0 {
3855 if start > end {
3856 return out;
3857 }
3858 let mut cur = start;
3859 while cur <= end {
3860 out.push(StrykeValue::string(format_one(cur, width, upper, prefix)));
3861 if (end - cur) < step {
3862 break;
3863 }
3864 cur += step;
3865 }
3866 } else if step < 0 {
3867 if start < end {
3868 return out;
3869 }
3870 let mut cur = start;
3871 while cur >= end {
3872 out.push(StrykeValue::string(format_one(cur, width, upper, prefix)));
3873 if (cur - end) < (-step) {
3874 break;
3875 }
3876 cur += step;
3877 }
3878 }
3879 out
3880}
3881
3882fn ipv6_range_stepped(from: &str, to: &str, step: i64) -> Vec<StrykeValue> {
3883 let Ok(start) = from.parse::<std::net::Ipv6Addr>() else {
3884 return vec![];
3885 };
3886 let Ok(end) = to.parse::<std::net::Ipv6Addr>() else {
3887 return vec![];
3888 };
3889 let s = u128::from(start);
3890 let e = u128::from(end);
3891 let mut out = Vec::new();
3892 if step > 0 {
3893 if s > e {
3894 return out; }
3896 let step = step as u128;
3897 let mut cur = s;
3898 loop {
3899 out.push(StrykeValue::string(
3900 std::net::Ipv6Addr::from(cur).to_string(),
3901 ));
3902 if cur == e || e.saturating_sub(cur) < step {
3903 break;
3904 }
3905 cur += step;
3906 }
3907 } else if step < 0 {
3908 if s < e {
3909 return out; }
3911 let step = (-step) as u128;
3912 let mut cur = s;
3913 loop {
3914 out.push(StrykeValue::string(
3915 std::net::Ipv6Addr::from(cur).to_string(),
3916 ));
3917 if cur == e || cur.saturating_sub(e) < step {
3918 break;
3919 }
3920 cur -= step;
3921 }
3922 }
3923 out
3924}
3925
3926fn is_iso_date(s: &str) -> bool {
3928 if s.len() != 10 {
3929 return false;
3930 }
3931 let parts: Vec<&str> = s.split('-').collect();
3932 parts.len() == 3
3933 && parts[0].len() == 4
3934 && parts[0].parse::<u16>().is_ok()
3935 && parts[1].len() == 2
3936 && parts[1]
3937 .parse::<u8>()
3938 .map(|m| (1..=12).contains(&m))
3939 .unwrap_or(false)
3940 && parts[2].len() == 2
3941 && parts[2]
3942 .parse::<u8>()
3943 .map(|d| (1..=31).contains(&d))
3944 .unwrap_or(false)
3945}
3946
3947fn is_year_month(s: &str) -> bool {
3949 if s.len() != 7 {
3950 return false;
3951 }
3952 let parts: Vec<&str> = s.split('-').collect();
3953 parts.len() == 2
3954 && parts[0].len() == 4
3955 && parts[0].parse::<u16>().is_ok()
3956 && parts[1].len() == 2
3957 && parts[1]
3958 .parse::<u8>()
3959 .map(|m| (1..=12).contains(&m))
3960 .unwrap_or(false)
3961}
3962
3963fn parse_iso_date(s: &str) -> Option<(i32, u32, u32)> {
3965 let parts: Vec<&str> = s.split('-').collect();
3966 if parts.len() != 3 {
3967 return None;
3968 }
3969 Some((
3970 parts[0].parse().ok()?,
3971 parts[1].parse().ok()?,
3972 parts[2].parse().ok()?,
3973 ))
3974}
3975
3976fn parse_year_month(s: &str) -> Option<(i32, u32)> {
3978 let parts: Vec<&str> = s.split('-').collect();
3979 if parts.len() != 2 {
3980 return None;
3981 }
3982 Some((parts[0].parse().ok()?, parts[1].parse().ok()?))
3983}
3984
3985fn days_in_month(year: i32, month: u32) -> u32 {
3987 match month {
3988 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
3989 4 | 6 | 9 | 11 => 30,
3990 2 => {
3991 if (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 {
3992 29
3993 } else {
3994 28
3995 }
3996 }
3997 _ => 30,
3998 }
3999}
4000
4001fn add_days(mut year: i32, mut month: u32, mut day: u32, mut delta: i64) -> (i32, u32, u32) {
4003 if delta > 0 {
4004 while delta > 0 {
4005 let dim = days_in_month(year, month);
4006 let remaining = dim - day;
4007 if delta <= remaining as i64 {
4008 day += delta as u32;
4009 break;
4010 }
4011 delta -= (remaining + 1) as i64;
4012 day = 1;
4013 month += 1;
4014 if month > 12 {
4015 month = 1;
4016 year += 1;
4017 }
4018 }
4019 } else {
4020 while delta < 0 {
4021 if (-delta) < day as i64 {
4022 day = (day as i64 + delta) as u32;
4023 break;
4024 }
4025 delta += day as i64;
4026 month -= 1;
4027 if month == 0 {
4028 month = 12;
4029 year -= 1;
4030 }
4031 day = days_in_month(year, month);
4032 }
4033 }
4034 (year, month, day)
4035}
4036
4037fn iso_date_range_stepped(from: &str, to: &str, step: i64) -> Vec<StrykeValue> {
4039 let Some((mut y, mut m, mut d)) = parse_iso_date(from) else {
4040 return vec![];
4041 };
4042 let Some((ey, em, ed)) = parse_iso_date(to) else {
4043 return vec![];
4044 };
4045 let mut out = Vec::new();
4046 let mut guard = 0;
4047 if step > 0 {
4048 while (y, m, d) <= (ey, em, ed) && guard < 50_000 {
4049 out.push(StrykeValue::string(format!("{:04}-{:02}-{:02}", y, m, d)));
4050 (y, m, d) = add_days(y, m, d, step);
4051 guard += 1;
4052 }
4053 } else {
4054 while (y, m, d) >= (ey, em, ed) && guard < 50_000 {
4055 out.push(StrykeValue::string(format!("{:04}-{:02}-{:02}", y, m, d)));
4056 (y, m, d) = add_days(y, m, d, step);
4057 guard += 1;
4058 }
4059 }
4060 out
4061}
4062
4063fn add_months(mut year: i32, mut month: u32, delta: i64) -> (i32, u32) {
4065 let total = (year as i64 * 12 + month as i64 - 1) + delta;
4066 year = (total / 12) as i32;
4067 month = ((total % 12) + 1) as u32;
4068 if month == 0 {
4069 month = 12;
4070 year -= 1;
4071 }
4072 (year, month)
4073}
4074
4075fn year_month_range_stepped(from: &str, to: &str, step: i64) -> Vec<StrykeValue> {
4077 let Some((mut y, mut m)) = parse_year_month(from) else {
4078 return vec![];
4079 };
4080 let Some((ey, em)) = parse_year_month(to) else {
4081 return vec![];
4082 };
4083 let mut out = Vec::new();
4084 let mut guard = 0;
4085 if step > 0 {
4086 while (y, m) <= (ey, em) && guard < 50_000 {
4087 out.push(StrykeValue::string(format!("{:04}-{:02}", y, m)));
4088 (y, m) = add_months(y, m, step);
4089 guard += 1;
4090 }
4091 } else {
4092 while (y, m) >= (ey, em) && guard < 50_000 {
4093 out.push(StrykeValue::string(format!("{:04}-{:02}", y, m)));
4094 (y, m) = add_months(y, m, step);
4095 guard += 1;
4096 }
4097 }
4098 out
4099}
4100
4101fn is_time_hhmm(s: &str) -> bool {
4103 if s.len() != 5 {
4104 return false;
4105 }
4106 let parts: Vec<&str> = s.split(':').collect();
4107 parts.len() == 2
4108 && parts[0].len() == 2
4109 && parts[0].parse::<u8>().map(|h| h < 24).unwrap_or(false)
4110 && parts[1].len() == 2
4111 && parts[1].parse::<u8>().map(|m| m < 60).unwrap_or(false)
4112}
4113
4114fn parse_time_hhmm(s: &str) -> Option<i32> {
4116 let parts: Vec<&str> = s.split(':').collect();
4117 if parts.len() != 2 {
4118 return None;
4119 }
4120 let h: i32 = parts[0].parse().ok()?;
4121 let m: i32 = parts[1].parse().ok()?;
4122 Some(h * 60 + m)
4123}
4124
4125fn minutes_to_hhmm(mins: i32) -> String {
4127 let h = (mins / 60) % 24;
4128 let m = mins % 60;
4129 format!("{:02}:{:02}", h, m)
4130}
4131
4132fn time_range_stepped(from: &str, to: &str, step: i64) -> Vec<StrykeValue> {
4134 let Some(start) = parse_time_hhmm(from) else {
4135 return vec![];
4136 };
4137 let Some(end) = parse_time_hhmm(to) else {
4138 return vec![];
4139 };
4140 let mut out = Vec::new();
4141 let mut guard = 0;
4142 if step > 0 {
4143 let mut cur = start;
4144 while cur <= end && guard < 50_000 {
4145 out.push(StrykeValue::string(minutes_to_hhmm(cur)));
4146 cur += step as i32;
4147 guard += 1;
4148 }
4149 } else {
4150 let mut cur = start;
4151 while cur >= end && guard < 50_000 {
4152 out.push(StrykeValue::string(minutes_to_hhmm(cur)));
4153 cur += step as i32;
4154 guard += 1;
4155 }
4156 }
4157 out
4158}
4159
4160const WEEKDAYS: [&str; 7] = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
4161const WEEKDAYS_FULL: [&str; 7] = [
4162 "Monday",
4163 "Tuesday",
4164 "Wednesday",
4165 "Thursday",
4166 "Friday",
4167 "Saturday",
4168 "Sunday",
4169];
4170
4171fn weekday_index(s: &str) -> Option<usize> {
4173 let lower = s.to_ascii_lowercase();
4174 for (i, &d) in WEEKDAYS.iter().enumerate() {
4175 if d.to_ascii_lowercase() == lower {
4176 return Some(i);
4177 }
4178 }
4179 for (i, &d) in WEEKDAYS_FULL.iter().enumerate() {
4180 if d.to_ascii_lowercase() == lower {
4181 return Some(i);
4182 }
4183 }
4184 None
4185}
4186
4187fn weekday_range_stepped(from: &str, to: &str, step: i64) -> Vec<StrykeValue> {
4189 let Some(start) = weekday_index(from) else {
4190 return vec![];
4191 };
4192 let Some(end) = weekday_index(to) else {
4193 return vec![];
4194 };
4195 let full = from.len() > 3;
4196 let names = if full { &WEEKDAYS_FULL } else { &WEEKDAYS };
4197 let mut out = Vec::new();
4198 if step > 0 {
4199 let mut cur = start as i64;
4200 let target = if end >= start {
4201 end as i64
4202 } else {
4203 end as i64 + 7
4204 };
4205 while cur <= target {
4206 out.push(StrykeValue::string(names[(cur % 7) as usize].to_string()));
4207 cur += step;
4208 }
4209 } else {
4210 let mut cur = start as i64;
4211 let target = if end <= start {
4212 end as i64
4213 } else {
4214 end as i64 - 7
4215 };
4216 while cur >= target {
4217 out.push(StrykeValue::string(
4218 names[((cur % 7 + 7) % 7) as usize].to_string(),
4219 ));
4220 cur += step;
4221 }
4222 }
4223 out
4224}
4225
4226const MONTHS: [&str; 12] = [
4227 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
4228];
4229const MONTHS_FULL: [&str; 12] = [
4230 "January",
4231 "February",
4232 "March",
4233 "April",
4234 "May",
4235 "June",
4236 "July",
4237 "August",
4238 "September",
4239 "October",
4240 "November",
4241 "December",
4242];
4243
4244fn month_name_index(s: &str) -> Option<usize> {
4246 let lower = s.to_ascii_lowercase();
4247 for (i, &m) in MONTHS.iter().enumerate() {
4248 if m.to_ascii_lowercase() == lower {
4249 return Some(i);
4250 }
4251 }
4252 for (i, &m) in MONTHS_FULL.iter().enumerate() {
4253 if m.to_ascii_lowercase() == lower {
4254 return Some(i);
4255 }
4256 }
4257 None
4258}
4259
4260fn month_name_range_stepped(from: &str, to: &str, step: i64) -> Vec<StrykeValue> {
4262 let Some(start) = month_name_index(from) else {
4263 return vec![];
4264 };
4265 let Some(end) = month_name_index(to) else {
4266 return vec![];
4267 };
4268 let full = from.len() > 3;
4269 let names = if full { &MONTHS_FULL } else { &MONTHS };
4270 let mut out = Vec::new();
4271 if step > 0 {
4272 let mut cur = start as i64;
4273 let target = if end >= start {
4274 end as i64
4275 } else {
4276 end as i64 + 12
4277 };
4278 while cur <= target {
4279 out.push(StrykeValue::string(names[(cur % 12) as usize].to_string()));
4280 cur += step;
4281 }
4282 } else {
4283 let mut cur = start as i64;
4284 let target = if end <= start {
4285 end as i64
4286 } else {
4287 end as i64 - 12
4288 };
4289 while cur >= target {
4290 out.push(StrykeValue::string(
4291 names[((cur % 12 + 12) % 12) as usize].to_string(),
4292 ));
4293 cur += step;
4294 }
4295 }
4296 out
4297}
4298
4299fn is_float_pair(from: &str, to: &str) -> bool {
4301 fn is_float(s: &str) -> bool {
4302 s.contains('.')
4303 && !s.contains(':')
4304 && s.matches('.').count() == 1
4305 && s.parse::<f64>().is_ok()
4306 }
4307 is_float(from) && is_float(to)
4308}
4309
4310fn float_range_stepped(from: &str, to: &str, step: f64) -> Vec<StrykeValue> {
4312 let Ok(start) = from.parse::<f64>() else {
4313 return vec![];
4314 };
4315 let Ok(end) = to.parse::<f64>() else {
4316 return vec![];
4317 };
4318 let mut out = Vec::new();
4319 let mut guard = 0;
4320 if step > 0.0 {
4322 let mut i = 0i64;
4323 loop {
4324 let cur = start + (i as f64) * step;
4325 if cur > end + step.abs() * f64::EPSILON * 10.0 || guard >= 50_000 {
4326 break;
4327 }
4328 let rounded = (cur * 1e12).round() / 1e12;
4330 out.push(StrykeValue::float(rounded));
4331 i += 1;
4332 guard += 1;
4333 }
4334 } else if step < 0.0 {
4335 let mut i = 0i64;
4336 loop {
4337 let cur = start + (i as f64) * step;
4338 if cur < end - step.abs() * f64::EPSILON * 10.0 || guard >= 50_000 {
4339 break;
4340 }
4341 let rounded = (cur * 1e12).round() / 1e12;
4342 out.push(StrykeValue::float(rounded));
4343 i += 1;
4344 guard += 1;
4345 }
4346 }
4347 out
4348}
4349
4350fn roman_to_int(s: &str) -> Option<i64> {
4352 let upper = s.to_ascii_uppercase();
4353 let mut result = 0i64;
4354 let mut prev = 0i64;
4355 for c in upper.chars().rev() {
4356 let val = match c {
4357 'I' => 1,
4358 'V' => 5,
4359 'X' => 10,
4360 'L' => 50,
4361 'C' => 100,
4362 'D' => 500,
4363 'M' => 1000,
4364 _ => return None,
4365 };
4366 if val < prev {
4367 result -= val;
4368 } else {
4369 result += val;
4370 }
4371 prev = val;
4372 }
4373 if result > 0 {
4374 Some(result)
4375 } else {
4376 None
4377 }
4378}
4379
4380fn int_to_roman(mut n: i64, lowercase: bool) -> Option<String> {
4382 if n <= 0 || n > 3999 {
4383 return None;
4384 }
4385 let numerals = [
4386 (1000, "M"),
4387 (900, "CM"),
4388 (500, "D"),
4389 (400, "CD"),
4390 (100, "C"),
4391 (90, "XC"),
4392 (50, "L"),
4393 (40, "XL"),
4394 (10, "X"),
4395 (9, "IX"),
4396 (5, "V"),
4397 (4, "IV"),
4398 (1, "I"),
4399 ];
4400 let mut result = String::new();
4401 for (val, sym) in numerals {
4402 while n >= val {
4403 result.push_str(sym);
4404 n -= val;
4405 }
4406 }
4407 if lowercase {
4408 Some(result.to_ascii_lowercase())
4409 } else {
4410 Some(result)
4411 }
4412}
4413
4414fn roman_range_stepped(from: &str, to: &str, step: i64) -> Vec<StrykeValue> {
4416 let Some(start) = roman_to_int(from) else {
4417 return vec![];
4418 };
4419 let Some(end) = roman_to_int(to) else {
4420 return vec![];
4421 };
4422 let lowercase = from
4423 .chars()
4424 .next()
4425 .map(|c| c.is_ascii_lowercase())
4426 .unwrap_or(false);
4427
4428 let mut out = Vec::new();
4429 if step > 0 {
4430 let mut cur = start;
4431 while cur <= end {
4432 if let Some(r) = int_to_roman(cur, lowercase) {
4433 out.push(StrykeValue::string(r));
4434 }
4435 cur += step;
4436 }
4437 } else {
4438 let mut cur = start;
4439 while cur >= end {
4440 if let Some(r) = int_to_roman(cur, lowercase) {
4441 out.push(StrykeValue::string(r));
4442 }
4443 cur += step; }
4445 }
4446 out
4447}
4448
4449pub(crate) fn perl_list_range_expand_stepped(
4452 from: StrykeValue,
4453 to: StrykeValue,
4454 step_val: StrykeValue,
4455) -> Vec<StrykeValue> {
4456 let from_str = from.to_string();
4457 let to_str = to.to_string();
4458
4459 let is_float_range = is_float_pair(&from_str, &to_str);
4461
4462 let step_float = step_val.as_float().unwrap_or(step_val.to_int() as f64);
4464 let step_int = step_val.to_int();
4465
4466 if step_int == 0 && step_float == 0.0 {
4467 return vec![];
4468 }
4469
4470 if is_float_range {
4472 return float_range_stepped(&from_str, &to_str, step_float);
4473 }
4474
4475 if perl_list_range_pair_is_numeric(&from, &to) {
4477 let i = from.to_int();
4478 let j = to.to_int();
4479 if step_int > 0 {
4480 (i..=j)
4481 .step_by(step_int as usize)
4482 .map(StrykeValue::integer)
4483 .collect()
4484 } else {
4485 std::iter::successors(Some(i), |&x| {
4486 let next = x + step_int;
4487 if next >= j {
4488 Some(next)
4489 } else {
4490 None
4491 }
4492 })
4493 .map(StrykeValue::integer)
4494 .collect()
4495 }
4496 } else {
4497 if is_hex_source_literal(&from_str) && is_hex_source_literal(&to_str) {
4504 return hex_range_stepped(&from_str, &to_str, step_int);
4505 }
4506
4507 if is_ipv4(&from_str) && is_ipv4(&to_str) {
4509 return ipv4_range_stepped(&from_str, &to_str, step_int);
4510 }
4511
4512 if is_ipv6(&from_str) && is_ipv6(&to_str) {
4516 return ipv6_range_stepped(&from_str, &to_str, step_int);
4517 }
4518
4519 if is_iso_date(&from_str) && is_iso_date(&to_str) {
4521 return iso_date_range_stepped(&from_str, &to_str, step_int);
4522 }
4523
4524 if is_year_month(&from_str) && is_year_month(&to_str) {
4526 return year_month_range_stepped(&from_str, &to_str, step_int);
4527 }
4528
4529 if is_time_hhmm(&from_str) && is_time_hhmm(&to_str) {
4531 return time_range_stepped(&from_str, &to_str, step_int);
4532 }
4533
4534 if weekday_index(&from_str).is_some() && weekday_index(&to_str).is_some() {
4536 return weekday_range_stepped(&from_str, &to_str, step_int);
4537 }
4538
4539 if month_name_index(&from_str).is_some() && month_name_index(&to_str).is_some() {
4541 return month_name_range_stepped(&from_str, &to_str, step_int);
4542 }
4543
4544 if is_roman_numeral(&from_str) && is_roman_numeral(&to_str) {
4546 return roman_range_stepped(&from_str, &to_str, step_int);
4547 }
4548
4549 perl_list_range_expand_string_magic_stepped(from, to, step_int)
4551 }
4552}
4553
4554pub(crate) fn perl_slice_endpoint_to_strict_int(
4558 v: &StrykeValue,
4559 where_: &str,
4560) -> Result<i64, String> {
4561 if let Some(n) = v.as_integer() {
4562 return Ok(n);
4563 }
4564 if let Some(f) = v.as_float() {
4565 if f.is_finite() && f.fract() == 0.0 && f >= i64::MIN as f64 && f <= i64::MAX as f64 {
4566 return Ok(f as i64);
4567 }
4568 return Err(format!(
4569 "array slice {}: non-integer float endpoint {}",
4570 where_, f
4571 ));
4572 }
4573 let s = v.as_str_or_empty();
4574 if !s.is_empty() {
4575 if let Ok(n) = s.trim().parse::<i64>() {
4576 return Ok(n);
4577 }
4578 return Err(format!(
4579 "array slice {}: non-integer string endpoint {:?}",
4580 where_, s
4581 ));
4582 }
4583 Err(format!(
4584 "array slice {}: endpoint must be an integer (got non-numeric value)",
4585 where_
4586 ))
4587}
4588
4589pub(crate) fn compute_array_slice_indices(
4599 arr_len: i64,
4600 from: &StrykeValue,
4601 to: &StrykeValue,
4602 step: &StrykeValue,
4603) -> Result<Vec<i64>, String> {
4604 let step_i = if step.is_undef() {
4605 1i64
4606 } else {
4607 perl_slice_endpoint_to_strict_int(step, "step")?
4608 };
4609 if step_i == 0 {
4610 return Err("array slice step cannot be 0".into());
4611 }
4612
4613 let normalize = |i: i64| -> i64 {
4614 if i < 0 {
4615 i + arr_len
4616 } else {
4617 i
4618 }
4619 };
4620
4621 let any_undef = from.is_undef() || to.is_undef();
4627
4628 let from_raw = if from.is_undef() {
4629 if step_i > 0 {
4630 0
4631 } else {
4632 arr_len - 1
4633 }
4634 } else {
4635 perl_slice_endpoint_to_strict_int(from, "start")?
4636 };
4637
4638 let to_raw = if to.is_undef() {
4639 if step_i > 0 {
4640 arr_len - 1
4641 } else {
4642 0
4643 }
4644 } else {
4645 perl_slice_endpoint_to_strict_int(to, "stop")?
4646 };
4647
4648 let mut out = Vec::new();
4649 if arr_len == 0 {
4650 return Ok(out);
4651 }
4652
4653 let (from_i, to_i) = if any_undef {
4654 (normalize(from_raw), normalize(to_raw))
4655 } else {
4656 (from_raw, to_raw)
4657 };
4658
4659 if step_i > 0 {
4660 let mut i = from_i;
4661 while i <= to_i {
4662 out.push(if any_undef { i } else { normalize(i) });
4663 i += step_i;
4664 }
4665 } else {
4666 let mut i = from_i;
4667 while i >= to_i {
4668 out.push(if any_undef { i } else { normalize(i) });
4669 i += step_i; }
4671 }
4672 Ok(out)
4673}
4674
4675pub(crate) fn compute_hash_slice_keys(
4680 from: &StrykeValue,
4681 to: &StrykeValue,
4682 step: &StrykeValue,
4683) -> Result<Vec<String>, String> {
4684 if from.is_undef() || to.is_undef() {
4685 return Err(
4686 "hash slice range requires both endpoints (open-ended forms not allowed)".into(),
4687 );
4688 }
4689 let step_val = if step.is_undef() {
4690 StrykeValue::integer(1)
4691 } else {
4692 step.clone()
4693 };
4694 let expanded = perl_list_range_expand_stepped(from.clone(), to.clone(), step_val);
4695 Ok(expanded.into_iter().map(|v| v.to_string()).collect())
4696}
4697
4698fn perl_list_range_expand_string_magic_stepped(
4699 from: StrykeValue,
4700 to: StrykeValue,
4701 step: i64,
4702) -> Vec<StrykeValue> {
4703 if step == 0 {
4704 return vec![];
4705 }
4706 let mut cur = from.into_string();
4707 let right = to.into_string();
4708
4709 if step > 0 {
4710 let step = step as usize;
4712 let right_ascii = right.is_ascii();
4713 let max_bound = perl_list_range_max_bound(&right);
4714 let mut out = Vec::new();
4715 let mut guard = 0usize;
4716 let mut idx = 0usize;
4717 loop {
4718 guard += 1;
4719 if guard > 50_000_000 {
4720 break;
4721 }
4722 let cur_bound = perl_list_range_cur_bound(&cur, right_ascii);
4723 if cur_bound > max_bound {
4724 break;
4725 }
4726 if idx.is_multiple_of(step) {
4727 out.push(StrykeValue::string(cur.clone()));
4728 }
4729 if cur == right {
4730 break;
4731 }
4732 match perl_magic_string_increment_for_range(&mut cur) {
4733 PerlListRangeIncOutcome::Continue => {}
4734 PerlListRangeIncOutcome::BecameNumeric => break,
4735 }
4736 idx += 1;
4737 }
4738 out
4739 } else {
4740 let step = (-step) as usize;
4742 let mut out = Vec::new();
4743 let mut guard = 0usize;
4744 let mut idx = 0usize;
4745 loop {
4746 guard += 1;
4747 if guard > 50_000_000 {
4748 break;
4749 }
4750 if idx.is_multiple_of(step) {
4751 out.push(StrykeValue::string(cur.clone()));
4752 }
4753 if cur == right {
4754 break;
4755 }
4756 if cur < right {
4758 break;
4759 }
4760 match perl_magic_string_decrement_for_range(&mut cur) {
4761 Some(()) => {}
4762 None => break, }
4764 idx += 1;
4765 }
4766 out
4767 }
4768}
4769
4770impl PerlDataFrame {
4771 pub fn row_hashref(&self, row: usize) -> StrykeValue {
4773 let mut m = IndexMap::new();
4774 for (i, col) in self.columns.iter().enumerate() {
4775 m.insert(
4776 col.clone(),
4777 self.cols[i].get(row).cloned().unwrap_or(StrykeValue::UNDEF),
4778 );
4779 }
4780 StrykeValue::hash_ref(Arc::new(RwLock::new(m)))
4781 }
4782}
4783
4784#[cfg(test)]
4785mod tests {
4786 use super::StrykeValue;
4787 use crate::perl_regex::PerlCompiledRegex;
4788 use indexmap::IndexMap;
4789 use parking_lot::RwLock;
4790 use std::cmp::Ordering;
4791 use std::sync::Arc;
4792
4793 #[test]
4794 fn undef_is_false() {
4795 assert!(!StrykeValue::UNDEF.is_true());
4796 }
4797
4798 #[test]
4799 fn string_zero_is_false() {
4800 assert!(!StrykeValue::string("0".into()).is_true());
4801 assert!(StrykeValue::string("00".into()).is_true());
4802 }
4803
4804 #[test]
4805 fn empty_string_is_false() {
4806 assert!(!StrykeValue::string(String::new()).is_true());
4807 }
4808
4809 #[test]
4810 fn integer_zero_is_false_nonzero_true() {
4811 assert!(!StrykeValue::integer(0).is_true());
4812 assert!(StrykeValue::integer(-1).is_true());
4813 }
4814
4815 #[test]
4816 fn float_zero_is_false_nonzero_true() {
4817 assert!(!StrykeValue::float(0.0).is_true());
4818 assert!(StrykeValue::float(0.1).is_true());
4819 }
4820
4821 #[test]
4822 fn num_cmp_orders_float_against_integer() {
4823 assert_eq!(
4824 StrykeValue::float(2.5).num_cmp(&StrykeValue::integer(3)),
4825 Ordering::Less
4826 );
4827 }
4828
4829 #[test]
4830 fn to_int_parses_leading_number_from_string() {
4831 assert_eq!(StrykeValue::string("42xyz".into()).to_int(), 42);
4832 assert_eq!(StrykeValue::string(" -3.7foo".into()).to_int(), -3);
4833 }
4834
4835 #[test]
4836 fn num_cmp_orders_as_numeric() {
4837 assert_eq!(
4838 StrykeValue::integer(2).num_cmp(&StrykeValue::integer(11)),
4839 Ordering::Less
4840 );
4841 assert_eq!(
4842 StrykeValue::string("2foo".into()).num_cmp(&StrykeValue::string("11".into())),
4843 Ordering::Less
4844 );
4845 }
4846
4847 #[test]
4848 fn str_cmp_orders_as_strings() {
4849 assert_eq!(
4850 StrykeValue::string("2".into()).str_cmp(&StrykeValue::string("11".into())),
4851 Ordering::Greater
4852 );
4853 }
4854
4855 #[test]
4856 fn str_eq_heap_strings_fast_path() {
4857 let a = StrykeValue::string("hello".into());
4858 let b = StrykeValue::string("hello".into());
4859 assert!(a.str_eq(&b));
4860 assert!(!a.str_eq(&StrykeValue::string("hell".into())));
4861 }
4862
4863 #[test]
4864 fn str_eq_fallback_matches_stringified_equality() {
4865 let n = StrykeValue::integer(42);
4866 let s = StrykeValue::string("42".into());
4867 assert!(n.str_eq(&s));
4868 assert!(!StrykeValue::integer(1).str_eq(&StrykeValue::string("2".into())));
4869 }
4870
4871 #[test]
4872 fn str_cmp_heap_strings_fast_path() {
4873 assert_eq!(
4874 StrykeValue::string("a".into()).str_cmp(&StrykeValue::string("b".into())),
4875 Ordering::Less
4876 );
4877 }
4878
4879 #[test]
4880 fn scalar_context_array_and_hash() {
4881 let a = StrykeValue::array(vec![StrykeValue::integer(1), StrykeValue::integer(2)])
4882 .scalar_context();
4883 assert_eq!(a.to_int(), 2);
4884 let mut h = IndexMap::new();
4885 h.insert("a".into(), StrykeValue::integer(1));
4886 let sc = StrykeValue::hash(h).scalar_context();
4887 assert!(sc.is_string_like());
4888 }
4889
4890 #[test]
4891 fn to_list_array_hash_and_scalar() {
4892 assert_eq!(
4893 StrykeValue::array(vec![StrykeValue::integer(7)])
4894 .to_list()
4895 .len(),
4896 1
4897 );
4898 let mut h = IndexMap::new();
4899 h.insert("k".into(), StrykeValue::integer(1));
4900 let list = StrykeValue::hash(h).to_list();
4901 assert_eq!(list.len(), 2);
4902 let one = StrykeValue::integer(99).to_list();
4903 assert_eq!(one.len(), 1);
4904 assert_eq!(one[0].to_int(), 99);
4905 }
4906
4907 #[test]
4908 fn type_name_and_ref_type_for_core_kinds() {
4909 assert_eq!(StrykeValue::integer(0).type_name(), "INTEGER");
4910 assert_eq!(StrykeValue::UNDEF.ref_type().to_string(), "");
4911 assert_eq!(
4912 StrykeValue::array_ref(Arc::new(RwLock::new(vec![])))
4913 .ref_type()
4914 .to_string(),
4915 "ARRAY"
4916 );
4917 }
4918
4919 #[test]
4920 fn display_undef_is_empty_integer_is_decimal() {
4921 assert_eq!(StrykeValue::UNDEF.to_string(), "");
4922 assert_eq!(StrykeValue::integer(-7).to_string(), "-7");
4923 }
4924
4925 #[test]
4926 fn empty_array_is_false_nonempty_is_true() {
4927 assert!(!StrykeValue::array(vec![]).is_true());
4928 assert!(StrykeValue::array(vec![StrykeValue::integer(0)]).is_true());
4929 }
4930
4931 #[test]
4932 fn to_number_undef_and_non_numeric_refs_are_zero() {
4933 use super::StrykeSub;
4934
4935 assert_eq!(StrykeValue::UNDEF.to_number(), 0.0);
4936 assert_eq!(
4937 StrykeValue::code_ref(Arc::new(StrykeSub {
4938 name: "f".into(),
4939 params: vec![],
4940 body: vec![],
4941 closure_env: None,
4942 prototype: None,
4943 fib_like: None,
4944 }))
4945 .to_number(),
4946 0.0
4947 );
4948 }
4949
4950 #[test]
4951 fn append_to_builds_string_without_extra_alloc_for_int_and_string() {
4952 let mut buf = String::new();
4953 StrykeValue::integer(-12).append_to(&mut buf);
4954 StrykeValue::string("ab".into()).append_to(&mut buf);
4955 assert_eq!(buf, "-12ab");
4956 let mut u = String::new();
4957 StrykeValue::UNDEF.append_to(&mut u);
4958 assert!(u.is_empty());
4959 }
4960
4961 #[test]
4962 fn append_to_atomic_delegates_to_inner() {
4963 use parking_lot::Mutex;
4964 let a = StrykeValue::atomic(Arc::new(Mutex::new(StrykeValue::string("z".into()))));
4965 let mut buf = String::new();
4966 a.append_to(&mut buf);
4967 assert_eq!(buf, "z");
4968 }
4969
4970 #[test]
4971 fn unwrap_atomic_reads_inner_other_variants_clone() {
4972 use parking_lot::Mutex;
4973 let a = StrykeValue::atomic(Arc::new(Mutex::new(StrykeValue::integer(9))));
4974 assert_eq!(a.unwrap_atomic().to_int(), 9);
4975 assert_eq!(StrykeValue::integer(3).unwrap_atomic().to_int(), 3);
4976 }
4977
4978 #[test]
4979 fn is_atomic_only_true_for_atomic_variant() {
4980 use parking_lot::Mutex;
4981 assert!(StrykeValue::atomic(Arc::new(Mutex::new(StrykeValue::UNDEF))).is_atomic());
4982 assert!(!StrykeValue::integer(0).is_atomic());
4983 }
4984
4985 #[test]
4986 fn as_str_only_on_string_variant() {
4987 assert_eq!(
4988 StrykeValue::string("x".into()).as_str(),
4989 Some("x".to_string())
4990 );
4991 assert_eq!(StrykeValue::integer(1).as_str(), None);
4992 }
4993
4994 #[test]
4995 fn as_str_or_empty_defaults_non_string() {
4996 assert_eq!(StrykeValue::string("z".into()).as_str_or_empty(), "z");
4997 assert_eq!(StrykeValue::integer(1).as_str_or_empty(), "");
4998 }
4999
5000 #[test]
5001 fn to_int_truncates_float_toward_zero() {
5002 assert_eq!(StrykeValue::float(3.9).to_int(), 3);
5003 assert_eq!(StrykeValue::float(-2.1).to_int(), -2);
5004 }
5005
5006 #[test]
5007 fn to_number_array_is_length() {
5008 assert_eq!(
5009 StrykeValue::array(vec![StrykeValue::integer(1), StrykeValue::integer(2)]).to_number(),
5010 2.0
5011 );
5012 }
5013
5014 #[test]
5015 fn scalar_context_empty_hash_is_zero() {
5016 let h = IndexMap::new();
5017 assert_eq!(StrykeValue::hash(h).scalar_context().to_int(), 0);
5018 }
5019
5020 #[test]
5021 fn scalar_context_nonhash_nonarray_clones() {
5022 let v = StrykeValue::integer(8);
5023 assert_eq!(v.scalar_context().to_int(), 8);
5024 }
5025
5026 #[test]
5027 fn display_float_integer_like_omits_decimal() {
5028 assert_eq!(StrykeValue::float(4.0).to_string(), "4");
5029 }
5030
5031 #[test]
5032 fn display_array_concatenates_element_displays() {
5033 let a = StrykeValue::array(vec![
5034 StrykeValue::integer(1),
5035 StrykeValue::string("b".into()),
5036 ]);
5037 assert_eq!(a.to_string(), "1b");
5038 }
5039
5040 #[test]
5041 fn display_code_ref_is_perl_style_hex_address() {
5042 use super::StrykeSub;
5045 let c = StrykeValue::code_ref(Arc::new(StrykeSub {
5046 name: "foo".into(),
5047 params: vec![],
5048 body: vec![],
5049 closure_env: None,
5050 prototype: None,
5051 fib_like: None,
5052 }));
5053 let s = c.to_string();
5054 assert!(s.starts_with("CODE(0x"), "got {:?}", s);
5055 assert!(s.ends_with(')'), "got {:?}", s);
5056 }
5057
5058 #[test]
5059 fn display_regex_shows_non_capturing_prefix() {
5060 let r = StrykeValue::regex(
5061 PerlCompiledRegex::compile("x+").unwrap(),
5062 "x+".into(),
5063 "".into(),
5064 );
5065 assert_eq!(r.to_string(), "(?:x+)");
5066 }
5067
5068 #[test]
5069 fn display_iohandle_is_name() {
5070 assert_eq!(
5071 StrykeValue::io_handle("STDOUT".into()).to_string(),
5072 "STDOUT"
5073 );
5074 }
5075
5076 #[test]
5077 fn ref_type_blessed_uses_class_name() {
5078 let b = StrykeValue::blessed(Arc::new(super::BlessedRef::new_blessed(
5079 "Pkg".into(),
5080 StrykeValue::UNDEF,
5081 )));
5082 assert_eq!(b.ref_type().to_string(), "Pkg");
5083 }
5084
5085 #[test]
5086 fn blessed_drop_enqueues_pending_destroy() {
5087 let v = StrykeValue::blessed(Arc::new(super::BlessedRef::new_blessed(
5088 "Z".into(),
5089 StrykeValue::integer(7),
5090 )));
5091 drop(v);
5092 let q = crate::pending_destroy::take_queue();
5093 assert_eq!(q.len(), 1);
5094 assert_eq!(q[0].0, "Z");
5095 assert_eq!(q[0].1.to_int(), 7);
5096 }
5097
5098 #[test]
5099 fn type_name_iohandle_is_glob() {
5100 assert_eq!(StrykeValue::io_handle("FH".into()).type_name(), "GLOB");
5101 }
5102
5103 #[test]
5104 fn empty_hash_is_false() {
5105 assert!(!StrykeValue::hash(IndexMap::new()).is_true());
5106 }
5107
5108 #[test]
5109 fn hash_nonempty_is_true() {
5110 let mut h = IndexMap::new();
5111 h.insert("k".into(), StrykeValue::UNDEF);
5112 assert!(StrykeValue::hash(h).is_true());
5113 }
5114
5115 #[test]
5116 fn num_cmp_equal_integers() {
5117 assert_eq!(
5118 StrykeValue::integer(5).num_cmp(&StrykeValue::integer(5)),
5119 Ordering::Equal
5120 );
5121 }
5122
5123 #[test]
5124 fn str_cmp_compares_lexicographic_string_forms() {
5125 assert_eq!(
5127 StrykeValue::integer(2).str_cmp(&StrykeValue::integer(10)),
5128 Ordering::Greater
5129 );
5130 }
5131
5132 #[test]
5133 fn to_list_undef_empty() {
5134 assert!(StrykeValue::UNDEF.to_list().is_empty());
5135 }
5136
5137 #[test]
5138 fn unwrap_atomic_nested_atomic() {
5139 use parking_lot::Mutex;
5140 let inner = StrykeValue::atomic(Arc::new(Mutex::new(StrykeValue::integer(2))));
5141 let outer = StrykeValue::atomic(Arc::new(Mutex::new(inner)));
5142 assert_eq!(outer.unwrap_atomic().to_int(), 2);
5143 }
5144
5145 #[test]
5146 fn errno_dual_parts_extracts_code_and_message() {
5147 let v = StrykeValue::errno_dual(-2, "oops".into());
5148 assert_eq!(v.errno_dual_parts(), Some((-2, "oops".into())));
5149 }
5150
5151 #[test]
5152 fn errno_dual_parts_none_for_plain_string() {
5153 assert!(StrykeValue::string("hi".into())
5154 .errno_dual_parts()
5155 .is_none());
5156 }
5157
5158 #[test]
5159 fn errno_dual_parts_none_for_integer() {
5160 assert!(StrykeValue::integer(1).errno_dual_parts().is_none());
5161 }
5162
5163 #[test]
5164 fn errno_dual_numeric_context_uses_code_string_uses_msg() {
5165 let v = StrykeValue::errno_dual(5, "five".into());
5166 assert_eq!(v.to_int(), 5);
5167 assert_eq!(v.to_string(), "five");
5168 }
5169
5170 #[test]
5171 fn list_range_alpha_joins_like_perl() {
5172 use super::perl_list_range_expand;
5173 let v = perl_list_range_expand(
5174 StrykeValue::string("a".into()),
5175 StrykeValue::string("z".into()),
5176 );
5177 let s: String = v.iter().map(|x| x.to_string()).collect();
5178 assert_eq!(s, "abcdefghijklmnopqrstuvwxyz");
5179 }
5180
5181 #[test]
5182 fn list_range_numeric_string_endpoints() {
5183 use super::perl_list_range_expand;
5184 let v = perl_list_range_expand(
5185 StrykeValue::string("9".into()),
5186 StrykeValue::string("11".into()),
5187 );
5188 assert_eq!(v.len(), 3);
5189 assert_eq!(
5190 v.iter().map(|x| x.to_int()).collect::<Vec<_>>(),
5191 vec![9, 10, 11]
5192 );
5193 }
5194
5195 #[test]
5196 fn list_range_leading_zero_is_string_mode() {
5197 use super::perl_list_range_expand;
5198 let v = perl_list_range_expand(
5199 StrykeValue::string("01".into()),
5200 StrykeValue::string("05".into()),
5201 );
5202 assert_eq!(v.len(), 5);
5203 assert_eq!(
5204 v.iter().map(|x| x.to_string()).collect::<Vec<_>>(),
5205 vec!["01", "02", "03", "04", "05"]
5206 );
5207 }
5208
5209 #[test]
5210 fn list_range_empty_to_letter_one_element() {
5211 use super::perl_list_range_expand;
5212 let v = perl_list_range_expand(
5213 StrykeValue::string(String::new()),
5214 StrykeValue::string("c".into()),
5215 );
5216 assert_eq!(v.len(), 1);
5217 assert_eq!(v[0].to_string(), "");
5218 }
5219
5220 #[test]
5221 fn magic_string_inc_z_wraps_aa() {
5222 use super::{perl_magic_string_increment_for_range, PerlListRangeIncOutcome};
5223 let mut s = "z".to_string();
5224 assert_eq!(
5225 perl_magic_string_increment_for_range(&mut s),
5226 PerlListRangeIncOutcome::Continue
5227 );
5228 assert_eq!(s, "aa");
5229 }
5230
5231 #[test]
5232 fn test_boxed_numeric_stringification() {
5233 let large_int = 10_000_000_000i64;
5235 let v_int = StrykeValue::integer(large_int);
5236 assert_eq!(v_int.to_string(), "10000000000");
5237
5238 let v_inf = StrykeValue::float(f64::INFINITY);
5240 assert_eq!(v_inf.to_string(), "Inf");
5241 }
5242
5243 #[test]
5244 fn magic_string_inc_nine_to_ten() {
5245 use super::{perl_magic_string_increment_for_range, PerlListRangeIncOutcome};
5246 let mut s = "9".to_string();
5247 assert_eq!(
5248 perl_magic_string_increment_for_range(&mut s),
5249 PerlListRangeIncOutcome::Continue
5250 );
5251 assert_eq!(s, "10");
5252 }
5253}