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