1use std::{cell::Cell, marker::PhantomData};23use crate::error::{Error, ErrorKind};45struct StackLimit {6 max_stack_size: Cell<usize>,7 current_depth: Cell<usize>,8}910#[cfg(feature = "nightly")]11#[thread_local]12static STACK_LIMIT: StackLimit = StackLimit {13 max_stack_size: Cell::new(200),14 current_depth: Cell::new(0),15};16#[cfg(not(feature = "nightly"))]17thread_local! {18 static STACK_LIMIT: StackLimit = StackLimit {19 max_stack_size: Cell::new(200),20 current_depth: Cell::new(0),21 };22}2324pub struct StackOverflowError;25impl From<StackOverflowError> for ErrorKind {26 fn from(_: StackOverflowError) -> Self {27 ErrorKind::StackOverflow28 }29}30impl From<StackOverflowError> for Error {31 fn from(_: StackOverflowError) -> Self {32 ErrorKind::StackOverflow.into()33 }34}353637pub struct StackDepthGuard(PhantomData<()>);38impl Drop for StackDepthGuard {39 #[cfg(feature = "nightly")]40 fn drop(&mut self) {41 STACK_LIMIT42 .current_depth43 .set(STACK_LIMIT.current_depth.get() - 1)44 }45 #[cfg(not(feature = "nightly"))]46 fn drop(&mut self) {47 STACK_LIMIT.with(|limit| limit.current_depth.set(limit.current_depth.get() - 1));48 }49}505152pub fn check_depth() -> Result<StackDepthGuard, StackOverflowError> {53 fn internal(limit: &StackLimit) -> Result<StackDepthGuard, StackOverflowError> {54 let current = limit.current_depth.get();55 if current < limit.max_stack_size.get() {56 limit.current_depth.set(current + 1);57 Ok(StackDepthGuard(PhantomData))58 } else {59 Err(StackOverflowError)60 }61 }62 #[cfg(feature = "nightly")]63 {64 internal(&STACK_LIMIT)65 }66 #[cfg(not(feature = "nightly"))]67 {68 STACK_LIMIT.with(internal)69 }70}7172pub struct StackDepthLimitOverrideGuard {73 old_limit: usize,74}75impl Drop for StackDepthLimitOverrideGuard {76 #[cfg(feature = "nightly")]77 fn drop(&mut self) {78 STACK_LIMIT.max_stack_size.set(self.old_limit)79 }80 #[cfg(not(feature = "nightly"))]81 fn drop(&mut self) {82 STACK_LIMIT.with(|limit| limit.max_stack_size.set(self.old_limit));83 }84}8586pub fn limit_stack_depth(depth_limit: usize) -> StackDepthLimitOverrideGuard {87 fn internal(limit: &StackLimit, depth_limit: usize) -> StackDepthLimitOverrideGuard {88 let old_limit = limit.max_stack_size.get();89 let current_depth = limit.current_depth.get();9091 limit.max_stack_size.set(current_depth + depth_limit);92 StackDepthLimitOverrideGuard { old_limit }93 }94 #[cfg(feature = "nightly")]95 {96 internal(&STACK_LIMIT, depth_limit)97 }98 #[cfg(not(feature = "nightly"))]99 {100 STACK_LIMIT.with(|limit| internal(limit, depth_limit))101 }102}103104105106107pub fn set_stack_depth_limit(depth_limit: usize) {108 std::mem::forget(limit_stack_depth(depth_limit));109}