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