From b68f5a034f4803756a2ce69ffaf303ec0504bcda Mon Sep 17 00:00:00 2001
From: Cyle Witruk <236413682+cylewitruk-stacks@users.noreply.github.com>
Date: Sun, 22 Mar 2026 22:28:05 +0100
Subject: [PATCH 01/12] rust 1.94 fmt
---
.../vm/analysis/type_checker/v2_05/tests/contracts.rs | 4 ++--
stacks-common/src/util/secp256k1/native.rs | 9 ++++-----
stacks-common/src/util/secp256k1/wasm.rs | 7 +++----
stacks-node/src/tests/neon_integrations.rs | 4 ++--
stacks-node/src/tests/signer/multiversion.rs | 5 ++++-
stacks-node/src/tests/stackerdb.rs | 3 ++-
stackslib/src/chainstate/tests/mod.rs | 3 +--
stackslib/src/clarity_vm/database/ephemeral.rs | 3 +--
stackslib/src/clarity_vm/database/marf.rs | 3 +--
stackslib/src/net/api/getsortition.rs | 4 ++--
stackslib/src/net/api/poststackerdbchunk.rs | 3 +--
stackslib/src/net/chat.rs | 3 +--
stackslib/src/net/codec.rs | 3 +--
stackslib/src/net/connection.rs | 3 +--
stackslib/src/net/http/response.rs | 3 ++-
stackslib/src/net/inv/epoch2x.rs | 3 +--
stackslib/src/net/mod.rs | 7 ++++---
stackslib/src/net/p2p.rs | 7 +++----
stackslib/src/net/poll.rs | 5 ++---
19 files changed, 38 insertions(+), 44 deletions(-)
diff --git a/clarity/src/vm/analysis/type_checker/v2_05/tests/contracts.rs b/clarity/src/vm/analysis/type_checker/v2_05/tests/contracts.rs
index 2f52025543f..9670c1d0091 100644
--- a/clarity/src/vm/analysis/type_checker/v2_05/tests/contracts.rs
+++ b/clarity/src/vm/analysis/type_checker/v2_05/tests/contracts.rs
@@ -14,9 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-use assert_json_diff::assert_json_eq;
+use assert_json_diff::{self, assert_json_eq};
+use serde_json;
use stacks_common::types::StacksEpochId;
-use {assert_json_diff, serde_json};
use crate::vm::ClarityVersion;
use crate::vm::analysis::contract_interface_builder::build_contract_interface;
diff --git a/stacks-common/src/util/secp256k1/native.rs b/stacks-common/src/util/secp256k1/native.rs
index e6b7452061d..49d969311b5 100644
--- a/stacks-common/src/util/secp256k1/native.rs
+++ b/stacks-common/src/util/secp256k1/native.rs
@@ -14,15 +14,15 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-use ::secp256k1;
use ::secp256k1::ecdsa::{
RecoverableSignature as LibSecp256k1RecoverableSignature, RecoveryId as LibSecp256k1RecoveryID,
Signature as LibSecp256k1Signature,
};
pub use ::secp256k1::Error;
use ::secp256k1::{
- constants as LibSecp256k1Constants, Error as LibSecp256k1Error, Message as LibSecp256k1Message,
- PublicKey as LibSecp256k1PublicKey, Secp256k1, SecretKey as LibSecp256k1PrivateKey,
+ self, constants as LibSecp256k1Constants, Error as LibSecp256k1Error,
+ Message as LibSecp256k1Message, PublicKey as LibSecp256k1PublicKey, Secp256k1,
+ SecretKey as LibSecp256k1PrivateKey,
};
use serde::de::{Deserialize, Error as de_Error};
use serde::Serialize;
@@ -441,8 +441,7 @@ pub fn secp256k1_verify(
#[cfg(test)]
mod tests {
use rand::RngCore as _;
- use secp256k1;
- use secp256k1::{PublicKey as LibSecp256k1PublicKey, Secp256k1};
+ use secp256k1::{self, PublicKey as LibSecp256k1PublicKey, Secp256k1};
use super::*;
use crate::util::get_epoch_time_ms;
diff --git a/stacks-common/src/util/secp256k1/wasm.rs b/stacks-common/src/util/secp256k1/wasm.rs
index 55b5266bfd5..03414641485 100644
--- a/stacks-common/src/util/secp256k1/wasm.rs
+++ b/stacks-common/src/util/secp256k1/wasm.rs
@@ -14,15 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-use ::libsecp256k1;
use ::libsecp256k1::curve::Scalar;
pub use ::libsecp256k1::Error;
-#[cfg(not(feature = "wasm-deterministic"))]
-use ::libsecp256k1::{Error as LibSecp256k1Error, Message as LibSecp256k1Message};
use ::libsecp256k1::{
- PublicKey as LibSecp256k1PublicKey, RecoveryId as LibSecp256k1RecoveryId,
+ self, PublicKey as LibSecp256k1PublicKey, RecoveryId as LibSecp256k1RecoveryId,
SecretKey as LibSecp256k1PrivateKey, Signature as LibSecp256k1Signature, ECMULT_GEN_CONTEXT,
};
+#[cfg(not(feature = "wasm-deterministic"))]
+use ::libsecp256k1::{Error as LibSecp256k1Error, Message as LibSecp256k1Message};
use serde::de::{Deserialize, Error as de_Error};
use serde::Serialize;
diff --git a/stacks-node/src/tests/neon_integrations.rs b/stacks-node/src/tests/neon_integrations.rs
index d7d1e4b2cea..0477953404e 100644
--- a/stacks-node/src/tests/neon_integrations.rs
+++ b/stacks-node/src/tests/neon_integrations.rs
@@ -287,8 +287,8 @@ pub mod test_observer {
use stacks::net::api::postblock_proposal::BlockValidateResponse;
use stacks::util::hash::hex_bytes;
use stacks_common::types::chainstate::StacksBlockId;
- use warp::Filter;
- use {tokio, warp};
+ use tokio;
+ use warp::{self, Filter};
use crate::event_dispatcher::{MinedBlockEvent, MinedMicroblockEvent, MinedNakamotoBlockEvent};
use crate::Config;
diff --git a/stacks-node/src/tests/signer/multiversion.rs b/stacks-node/src/tests/signer/multiversion.rs
index acb5a20bda6..aff6dbc0ee4 100644
--- a/stacks-node/src/tests/signer/multiversion.rs
+++ b/stacks-node/src/tests/signer/multiversion.rs
@@ -20,19 +20,22 @@ use libsigner::v0::messages::{
SignerMessageMetadata,
};
use libsigner::v0::signer_state::{MinerState, ReplayTransactionSet, SignerStateMachine};
+use libsigner_v3_3_0_0_5;
use libsigner_v3_3_0_0_5::v0::messages::SignerMessage as OldSignerMessage;
+use signer_v3_3_0_0_5_0;
use signer_v3_3_0_0_5_0::v0::signer_state::SUPPORTED_SIGNER_PROTOCOL_VERSION as OldSupportedVersion;
use stacks::chainstate::stacks::StacksTransaction;
use stacks::util::hash::{Hash160, Sha512Trunc256Sum};
use stacks::util::secp256k1::{MessageSignature, Secp256k1PrivateKey};
use stacks_common::types::chainstate::{ConsensusHash, StacksBlockId};
+use stacks_common_v3_3_0_0_5;
use stacks_common_v3_3_0_0_5::codec::StacksMessageCodec as OldStacksMessageCodec;
use stacks_signer::runloop::{RewardCycleInfo, State, StateInfo};
use stacks_signer::v0::signer_state::{
LocalStateMachine, SUPPORTED_SIGNER_PROTOCOL_VERSION as NewSupportedVersion,
};
use stacks_signer::v0::SpawnedSigner;
-use {libsigner_v3_3_0_0_5, signer_v3_3_0_0_5_0, stacks_common_v3_3_0_0_5, stacks_v3_3_0_0_5};
+use stacks_v3_3_0_0_5;
use super::SpawnedSignerTrait;
use crate::stacks_common::codec::StacksMessageCodec;
diff --git a/stacks-node/src/tests/stackerdb.rs b/stacks-node/src/tests/stackerdb.rs
index 2fc840d4f5b..491506208fd 100644
--- a/stacks-node/src/tests/stackerdb.rs
+++ b/stacks-node/src/tests/stackerdb.rs
@@ -17,12 +17,13 @@
use std::{env, thread};
use clarity::vm::types::QualifiedContractIdentifier;
+use reqwest;
+use serde_json;
use stacks::chainstate::stacks::StacksPrivateKey;
use stacks::config::{EventKeyType, InitialBalance};
use stacks::libstackerdb::{StackerDBChunkAckData, StackerDBChunkData};
use stacks_common::types::chainstate::StacksAddress;
use stacks_common::util::hash::Sha512Trunc256Sum;
-use {reqwest, serde_json};
use crate::burnchains::bitcoin::core_controller::BitcoinCoreController;
use crate::burnchains::BurnchainController;
diff --git a/stackslib/src/chainstate/tests/mod.rs b/stackslib/src/chainstate/tests/mod.rs
index 7c86e8fc838..792400bb0a5 100644
--- a/stackslib/src/chainstate/tests/mod.rs
+++ b/stackslib/src/chainstate/tests/mod.rs
@@ -35,8 +35,7 @@ use clarity::vm::costs::ExecutionCost;
use clarity::vm::database::STXBalance;
use clarity::vm::types::*;
use clarity::vm::ContractName;
-use rand;
-use rand::{thread_rng, Rng};
+use rand::{self, thread_rng, Rng};
use stacks_common::address::*;
use stacks_common::deps_common::bitcoin::network::serialize::BitcoinHash;
use stacks_common::types::StacksEpochId;
diff --git a/stackslib/src/clarity_vm/database/ephemeral.rs b/stackslib/src/clarity_vm/database/ephemeral.rs
index 448afeb7ab2..3a9aff56dc3 100644
--- a/stackslib/src/clarity_vm/database/ephemeral.rs
+++ b/stackslib/src/clarity_vm/database/ephemeral.rs
@@ -23,8 +23,7 @@ use clarity::vm::database::sqlite::{
use clarity::vm::database::{ClarityBackingStore, SpecialCaseHandler, SqliteConnection};
use clarity::vm::errors::{RuntimeError, VmExecutionError, VmInternalError};
use clarity::vm::types::QualifiedContractIdentifier;
-use rusqlite;
-use rusqlite::Connection;
+use rusqlite::{self, Connection};
use stacks_common::codec::StacksMessageCodec;
use stacks_common::types::chainstate::{BlockHeaderHash, StacksBlockId, TrieHash};
use stacks_common::types::sqlite::NO_PARAMS;
diff --git a/stackslib/src/clarity_vm/database/marf.rs b/stackslib/src/clarity_vm/database/marf.rs
index 9f11720b7a9..9f8a2d3993d 100644
--- a/stackslib/src/clarity_vm/database/marf.rs
+++ b/stackslib/src/clarity_vm/database/marf.rs
@@ -26,8 +26,7 @@ use clarity::vm::database::sqlite::{
use clarity::vm::database::{ClarityBackingStore, SpecialCaseHandler, SqliteConnection};
use clarity::vm::errors::{IncomparableError, RuntimeError, VmExecutionError, VmInternalError};
use clarity::vm::types::QualifiedContractIdentifier;
-use rusqlite;
-use rusqlite::Connection;
+use rusqlite::{self, Connection};
use stacks_common::codec::StacksMessageCodec;
use stacks_common::types::chainstate::{BlockHeaderHash, StacksBlockId, TrieHash};
diff --git a/stackslib/src/net/api/getsortition.rs b/stackslib/src/net/api/getsortition.rs
index 365b6588cd9..d79faf711dc 100644
--- a/stackslib/src/net/api/getsortition.rs
+++ b/stackslib/src/net/api/getsortition.rs
@@ -15,14 +15,14 @@
use clarity::types::chainstate::VRFSeed;
use regex::{Captures, Regex};
-use serde::Serialize;
+use serde::{self, Serialize};
+use serde_json;
use stacks_common::types::chainstate::{
BlockHeaderHash, BurnchainHeaderHash, ConsensusHash, SortitionId, StacksBlockId,
};
use stacks_common::types::net::PeerHost;
use stacks_common::util::hash::Hash160;
use stacks_common::util::serde_serializers::{prefix_hex, prefix_opt_hex};
-use {serde, serde_json};
use crate::chainstate::burn::db::sortdb::SortitionDB;
use crate::chainstate::burn::BlockSnapshot;
diff --git a/stackslib/src/net/api/poststackerdbchunk.rs b/stackslib/src/net/api/poststackerdbchunk.rs
index dc3981268cf..3d2ea15c300 100644
--- a/stackslib/src/net/api/poststackerdbchunk.rs
+++ b/stackslib/src/net/api/poststackerdbchunk.rs
@@ -18,8 +18,7 @@ use clarity::vm::representations::{CONTRACT_NAME_REGEX_STRING, STANDARD_PRINCIPA
use clarity::vm::types::QualifiedContractIdentifier;
use libstackerdb::{StackerDBChunkAckData, StackerDBChunkData};
use regex::{Captures, Regex};
-use serde_json;
-use serde_json::json;
+use serde_json::{self, json};
use stacks_common::codec::MAX_MESSAGE_LEN;
use stacks_common::types::net::PeerHost;
use stacks_common::util::secp256k1::MessageSignature;
diff --git a/stackslib/src/net/chat.rs b/stackslib/src/net/chat.rs
index 84bb94dbc8e..5ad242fbfee 100644
--- a/stackslib/src/net/chat.rs
+++ b/stackslib/src/net/chat.rs
@@ -20,8 +20,7 @@ use std::net::SocketAddr;
use std::{cmp, mem};
use clarity::vm::types::QualifiedContractIdentifier;
-use rand;
-use rand::{thread_rng, Rng};
+use rand::{self, thread_rng, Rng};
use stacks_common::types::net::PeerAddress;
use stacks_common::types::StacksPublicKeyBuffer;
use stacks_common::util::hash::to_hex;
diff --git a/stackslib/src/net/codec.rs b/stackslib/src/net/codec.rs
index a94ddce6898..3e2b5eeba85 100644
--- a/stackslib/src/net/codec.rs
+++ b/stackslib/src/net/codec.rs
@@ -21,8 +21,7 @@ use std::io::Read;
use clarity::vm::types::QualifiedContractIdentifier;
use clarity::vm::ContractName;
-use rand;
-use rand::Rng;
+use rand::{self, Rng};
use sha2::{Digest, Sha512_256};
use stacks_common::bitvec::BitVec;
use stacks_common::codec::{
diff --git a/stackslib/src/net/connection.rs b/stackslib/src/net/connection.rs
index 60367f66608..b3382666522 100644
--- a/stackslib/src/net/connection.rs
+++ b/stackslib/src/net/connection.rs
@@ -1550,8 +1550,7 @@ mod test {
use std::sync::{Arc, Mutex};
use std::{io, thread};
- use rand;
- use rand::RngCore;
+ use rand::{self, RngCore};
use stacks_common::util::secp256k1::*;
use stacks_common::util::*;
diff --git a/stackslib/src/net/http/response.rs b/stackslib/src/net/http/response.rs
index 1428352e3e8..7cf3ec8d58c 100644
--- a/stackslib/src/net/http/response.rs
+++ b/stackslib/src/net/http/response.rs
@@ -18,6 +18,8 @@ use std::collections::{BTreeMap, HashSet};
use std::fmt;
use std::io::{Read, Write};
+use serde;
+use serde_json;
use stacks_common::codec::{Error as CodecError, StacksMessageCodec};
use stacks_common::deps_common::httparse;
use stacks_common::util::chunked_encoding::{
@@ -25,7 +27,6 @@ use stacks_common::util::chunked_encoding::{
};
use stacks_common::util::hash::to_hex;
use stacks_common::util::pipe::PipeWrite;
-use {serde, serde_json};
use crate::net::http::common::{
HttpReservedHeader, HTTP_PREAMBLE_MAX_ENCODED_SIZE, HTTP_PREAMBLE_MAX_NUM_HEADERS,
diff --git a/stackslib/src/net/inv/epoch2x.rs b/stackslib/src/net/inv/epoch2x.rs
index 312b67a8c05..03f3c81b119 100644
--- a/stackslib/src/net/inv/epoch2x.rs
+++ b/stackslib/src/net/inv/epoch2x.rs
@@ -18,9 +18,8 @@ use std::cmp;
use std::collections::{HashMap, HashSet};
use p2p::DropSource;
-use rand;
use rand::seq::SliceRandom;
-use rand::thread_rng;
+use rand::{self, thread_rng};
use stacks_common::types::chainstate::{BlockHeaderHash, PoxId};
use stacks_common::util::get_epoch_time_secs;
diff --git a/stackslib/src/net/mod.rs b/stackslib/src/net/mod.rs
index 0c788d76157..86d9baf4b0e 100644
--- a/stackslib/src/net/mod.rs
+++ b/stackslib/src/net/mod.rs
@@ -23,6 +23,7 @@ use clarity::vm::errors::{ClarityTypeError, VmExecutionError};
use clarity::vm::types::{PrincipalData, QualifiedContractIdentifier};
use libstackerdb::{Error as libstackerdb_error, StackerDBChunkData};
use p2p::{DropReason, DropSource};
+use rusqlite;
use serde::{Deserialize, Serialize};
use stacks_common::bitvec::BitVec;
use stacks_common::codec::{Error as codec_error, StacksMessageCodec};
@@ -34,7 +35,7 @@ use stacks_common::types::StacksPublicKeyBuffer;
use stacks_common::util::get_epoch_time_secs;
use stacks_common::util::hash::{Hash160, Sha256Sum};
use stacks_common::util::secp256k1::{MessageSignature, Secp256k1PublicKey};
-use {rusqlite, url};
+use url;
use self::dns::*;
use crate::burnchains::{Error as burnchain_error, Txid};
@@ -2250,14 +2251,14 @@ pub mod test {
use clarity::types::sqlite::NO_PARAMS;
use clarity::vm::costs::ExecutionCost;
use clarity::vm::types::*;
- use rand::RngCore;
+ use mio;
+ use rand::{self, RngCore};
use stacks_common::codec::StacksMessageCodec;
use stacks_common::deps_common::bitcoin::network::serialize::BitcoinHash;
use stacks_common::types::StacksEpochId;
use stacks_common::util::hash::*;
use stacks_common::util::secp256k1::*;
use stacks_common::util::vrf::*;
- use {mio, rand};
use super::*;
use crate::burnchains::bitcoin::indexer::BitcoinIndexer;
diff --git a/stackslib/src/net/p2p.rs b/stackslib/src/net/p2p.rs
index a4d7d99ce34..8d9344987ea 100644
--- a/stackslib/src/net/p2p.rs
+++ b/stackslib/src/net/p2p.rs
@@ -21,7 +21,7 @@ use std::sync::mpsc::{sync_channel, Receiver, SyncSender, TryRecvError, TrySendE
use std::thread::JoinHandle;
use clarity::vm::types::QualifiedContractIdentifier;
-use mio::net as mio_net;
+use mio::{self, net as mio_net};
use rand::prelude::*;
use rand::thread_rng;
use stacks_common::consts::{FIRST_BURNCHAIN_CONSENSUS_HASH, FIRST_STACKS_BLOCK_HASH};
@@ -31,7 +31,7 @@ use stacks_common::types::StacksEpochId;
use stacks_common::util::hash::to_hex;
use stacks_common::util::secp256k1::Secp256k1PublicKey;
use stacks_common::util::{get_epoch_time_ms, get_epoch_time_secs};
-use {mio, url};
+use url;
use crate::burnchains::db::{BurnchainDB, BurnchainHeaderReader};
use crate::burnchains::{Burnchain, BurnchainView};
@@ -5568,8 +5568,7 @@ mod test {
use std::{thread, time};
use clarity::util::sleep_ms;
- use rand;
- use rand::RngCore;
+ use rand::{self, RngCore};
use stacks_common::types::chainstate::BurnchainHeaderHash;
use super::*;
diff --git a/stackslib/src/net/poll.rs b/stackslib/src/net/poll.rs
index 2589fed0801..ff4abfd7b3e 100644
--- a/stackslib/src/net/poll.rs
+++ b/stackslib/src/net/poll.rs
@@ -20,10 +20,9 @@ use std::net::{Shutdown, SocketAddr};
use std::time::Duration;
use std::{io, time};
-use mio::{net as mio_net, PollOpt, Ready, Token};
-use rand::RngCore;
+use mio::{self, net as mio_net, PollOpt, Ready, Token};
+use rand::{self, RngCore};
use stacks_common::util::sleep_ms;
-use {mio, rand};
use crate::net::Error as net_error;
From cc76c236268e6bec1f9c7cbad4956f0ffa141c8a Mon Sep 17 00:00:00 2001
From: Cyle Witruk <236413682+cylewitruk-stacks@users.noreply.github.com>
Date: Sun, 22 Mar 2026 22:46:21 +0100
Subject: [PATCH 02/12] new ResidentBytes trait and impls for memory footprint
tracking
---
clarity-types/src/lib.rs | 1 +
clarity-types/src/resident_bytes.rs | 553 ++++++++++++++++++++++++++
clarity-types/src/types/mod.rs | 5 +
clarity/src/vm/callables.rs | 12 +
clarity/src/vm/contexts.rs | 17 +
clarity/src/vm/contracts.rs | 272 +++++++++++++
clarity/src/vm/database/structures.rs | 25 ++
clarity/src/vm/types/signatures.rs | 7 +
stacks-common/src/util/macros.rs | 5 +
9 files changed, 897 insertions(+)
create mode 100644 clarity-types/src/resident_bytes.rs
diff --git a/clarity-types/src/lib.rs b/clarity-types/src/lib.rs
index 6910c9f4d0a..0a4d38f1de5 100644
--- a/clarity-types/src/lib.rs
+++ b/clarity-types/src/lib.rs
@@ -28,6 +28,7 @@ pub mod diagnostic;
pub mod errors;
pub mod execution_cost;
pub mod representations;
+pub mod resident_bytes;
pub mod token;
pub mod types;
diff --git a/clarity-types/src/resident_bytes.rs b/clarity-types/src/resident_bytes.rs
new file mode 100644
index 00000000000..d3f4acd751f
--- /dev/null
+++ b/clarity-types/src/resident_bytes.rs
@@ -0,0 +1,553 @@
+// Copyright (C) 2026 Stacks Open Internet Foundation
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
+use std::mem::size_of;
+use std::sync::Arc;
+
+use crate::representations::{
+ ClarityName, ContractName, SymbolicExpression, SymbolicExpressionType, TraitDefinition,
+};
+use crate::types::signatures::{
+ BufferLength, CallableSubtype, ListTypeData, SequenceSubtype, StringSubtype, StringUTF8Length,
+ TupleTypeSignature, TypeSignature,
+};
+use crate::types::{
+ ASCIIData, BuffData, CallableData, CharType, FunctionIdentifier, ListData, OptionalData,
+ PrincipalData, QualifiedContractIdentifier, ResponseData, SequenceData, StandardPrincipalData,
+ TraitIdentifier, TupleData, UTF8Data, Value,
+};
+
+// Rust's BTreeMap uses B=6 (hardcoded): each node holds up to CAPACITY = 2*B-1 = 11 entries:
+// * LeafNode layout: parent ptr (8) + parent_idx (2) + len (2) + padding (~4) + keys:
+// [MaybeUninit; 11] + vals: [MaybeUninit; 11].
+// * Allocator header adds ~16 bytes per node.
+// * Total per-node overhead (metadata + allocator): ~32 bytes. Average fill factor ~2/3 → ~7
+// entries per node.
+
+/// Maximum entries per BTree node (B=6 in Rust's implementation → 2*B-1 = 11).
+const BTREE_NODE_CAPACITY: usize = 11;
+/// Estimated average entries per node in a steady-state B-tree (~2/3 fill).
+const BTREE_AVERAGE_FILL: usize = 7;
+/// Per-node overhead: metadata (parent ptr + idx + len + padding) + allocator header.
+const BTREE_NODE_OVERHEAD: usize = 32;
+/// Estimated overhead for Arc: strong + weak counts + allocation header.
+const ARC_OVERHEAD: usize = 16;
+
+/// Reports the approximate in-memory footprint of an instance, in bytes.
+///
+/// See module-level documentation for the two-method design.
+pub trait ResidentBytes {
+ /// Total in-memory footprint: inline `size_of` + heap allocations.
+ ///
+ /// This is the method callers should use. It has a provided default
+ /// implementation; implementors only need to implement [`heap_bytes`].
+ fn resident_bytes(&self) -> usize {
+ std::mem::size_of_val(self) + self.heap_bytes()
+ }
+
+ /// Heap allocations only, beyond the inline `size_of`.
+ ///
+ /// Container types call this on their children to avoid double-counting
+ /// inline sizes that are already part of the container's backing allocation.
+ fn heap_bytes(&self) -> usize;
+}
+
+impl ResidentBytes for String {
+ fn heap_bytes(&self) -> usize {
+ self.capacity()
+ }
+}
+
+impl ResidentBytes for Vec {
+ fn heap_bytes(&self) -> usize {
+ // Backing array: capacity slots (inline size per slot)
+ let backing = self.capacity() * size_of::();
+ // Children's heap allocations
+ let children: usize = self.iter().map(|v| v.heap_bytes()).sum();
+ backing + children
+ }
+}
+
+impl ResidentBytes for Box {
+ fn heap_bytes(&self) -> usize {
+ // Box heap-allocates the pointee: its inline size + its own heap
+ size_of::() + (**self).heap_bytes()
+ }
+}
+
+impl ResidentBytes for Option {
+ fn heap_bytes(&self) -> usize {
+ match self {
+ // For Some, the T is inline in the Option — only count T's heap
+ Some(v) => v.heap_bytes(),
+ None => 0,
+ }
+ }
+}
+
+impl ResidentBytes for Arc {
+ fn heap_bytes(&self) -> usize {
+ // Arc heap-allocates: header (strong + weak counts, ~16 bytes) + T inline + T's heap
+ 16 + size_of::() + (**self).heap_bytes()
+ }
+}
+
+impl ResidentBytes for HashMap {
+ fn heap_bytes(&self) -> usize {
+ // Backing array: capacity * (entry inline size + ~1 byte control metadata)
+ let backing = self.capacity() * (size_of::<(K, V)>() + 1);
+ // Children's heap allocations (only for occupied entries)
+ let children: usize = self
+ .iter()
+ .map(|(k, v)| k.heap_bytes() + v.heap_bytes())
+ .sum();
+ backing + children
+ }
+}
+
+impl ResidentBytes for BTreeMap {
+ fn heap_bytes(&self) -> usize {
+ if self.is_empty() {
+ return 0; // Empty BTreeMaps do not allocate on the heap.
+ }
+
+ // Node count from average fill, not max capacity
+ let nodes = self.len().div_ceil(BTREE_AVERAGE_FILL);
+ // Keys and values are separate arrays in the node, not (K, V) tuples
+ let node_size = BTREE_NODE_OVERHEAD
+ + (BTREE_NODE_CAPACITY * size_of::())
+ + (BTREE_NODE_CAPACITY * size_of::());
+ let structural = nodes * node_size;
+
+ let children: usize = self
+ .iter()
+ .map(|(k, v)| k.heap_bytes() + v.heap_bytes())
+ .sum();
+ structural + children
+ }
+}
+
+impl ResidentBytes for BTreeSet {
+ fn heap_bytes(&self) -> usize {
+ if self.is_empty() {
+ return 0;
+ }
+
+ // BTreeSet is backed by BTreeMap — vals array is zero-size
+ let nodes = self.len().div_ceil(BTREE_AVERAGE_FILL);
+ let node_size = BTREE_NODE_OVERHEAD + (BTREE_NODE_CAPACITY * size_of::());
+ let structural = nodes * node_size;
+ let children: usize = self.iter().map(|v| v.heap_bytes()).sum();
+ structural + children
+ }
+}
+
+impl ResidentBytes for HashSet {
+ fn heap_bytes(&self) -> usize {
+ let backing = self.capacity() * (size_of::() + 1);
+ let children: usize = self.iter().map(|v| v.heap_bytes()).sum();
+ backing + children
+ }
+}
+
+impl ResidentBytes for (A, B) {
+ fn heap_bytes(&self) -> usize {
+ self.0.heap_bytes() + self.1.heap_bytes()
+ }
+}
+
+// Primitive types: no heap allocation
+impl ResidentBytes for bool {
+ fn heap_bytes(&self) -> usize {
+ 0
+ }
+}
+impl ResidentBytes for u8 {
+ fn heap_bytes(&self) -> usize {
+ 0
+ }
+}
+impl ResidentBytes for u32 {
+ fn heap_bytes(&self) -> usize {
+ 0
+ }
+}
+impl ResidentBytes for u64 {
+ fn heap_bytes(&self) -> usize {
+ 0
+ }
+}
+impl ResidentBytes for u128 {
+ fn heap_bytes(&self) -> usize {
+ 0
+ }
+}
+impl ResidentBytes for i128 {
+ fn heap_bytes(&self) -> usize {
+ 0
+ }
+}
+
+impl ResidentBytes for Value {
+ fn heap_bytes(&self) -> usize {
+ match self {
+ Value::Int(_) | Value::UInt(_) | Value::Bool(_) => 0,
+ Value::Sequence(data) => data.heap_bytes(),
+ Value::Principal(data) => data.heap_bytes(),
+ Value::Tuple(data) => data.heap_bytes(),
+ Value::Optional(data) => data.heap_bytes(),
+ Value::Response(data) => data.heap_bytes(),
+ Value::CallableContract(data) => data.heap_bytes(),
+ }
+ }
+}
+
+impl ResidentBytes for SequenceData {
+ fn heap_bytes(&self) -> usize {
+ match self {
+ SequenceData::Buffer(buf) => buf.heap_bytes(),
+ SequenceData::List(list) => list.heap_bytes(),
+ SequenceData::String(char_type) => char_type.heap_bytes(),
+ }
+ }
+}
+
+impl ResidentBytes for BuffData {
+ fn heap_bytes(&self) -> usize {
+ self.data.heap_bytes()
+ }
+}
+
+impl ResidentBytes for ListData {
+ fn heap_bytes(&self) -> usize {
+ self.data.heap_bytes() + self.type_signature.heap_bytes()
+ }
+}
+
+impl ResidentBytes for CharType {
+ fn heap_bytes(&self) -> usize {
+ match self {
+ CharType::ASCII(data) => data.heap_bytes(),
+ CharType::UTF8(data) => data.heap_bytes(),
+ }
+ }
+}
+
+impl ResidentBytes for ASCIIData {
+ fn heap_bytes(&self) -> usize {
+ self.data.heap_bytes()
+ }
+}
+
+impl ResidentBytes for UTF8Data {
+ fn heap_bytes(&self) -> usize {
+ // Vec>: outer vec backing + each inner vec's backing
+ let outer = self.data.capacity() * size_of::>();
+ let inner: usize = self.data.iter().map(|v| v.capacity()).sum();
+ outer + inner
+ }
+}
+
+impl ResidentBytes for TupleData {
+ fn heap_bytes(&self) -> usize {
+ self.type_signature.heap_bytes() + self.data_map.heap_bytes()
+ }
+}
+
+impl ResidentBytes for OptionalData {
+ fn heap_bytes(&self) -> usize {
+ self.data.heap_bytes()
+ }
+}
+
+impl ResidentBytes for ResponseData {
+ fn heap_bytes(&self) -> usize {
+ self.data.heap_bytes()
+ }
+}
+
+impl ResidentBytes for CallableData {
+ fn heap_bytes(&self) -> usize {
+ self.contract_identifier.heap_bytes() + self.trait_identifier.heap_bytes()
+ }
+}
+
+impl ResidentBytes for PrincipalData {
+ fn heap_bytes(&self) -> usize {
+ match self {
+ PrincipalData::Standard(data) => data.heap_bytes(),
+ PrincipalData::Contract(data) => data.heap_bytes(),
+ }
+ }
+}
+
+impl ResidentBytes for StandardPrincipalData {
+ fn heap_bytes(&self) -> usize {
+ 0 // Fixed-size: u8 + [u8; 20], no heap allocation
+ }
+}
+
+impl ResidentBytes for QualifiedContractIdentifier {
+ fn heap_bytes(&self) -> usize {
+ self.issuer.heap_bytes() + self.name.heap_bytes()
+ }
+}
+
+impl ResidentBytes for ClarityName {
+ fn heap_bytes(&self) -> usize {
+ self.heap_capacity()
+ }
+}
+
+impl ResidentBytes for ContractName {
+ fn heap_bytes(&self) -> usize {
+ self.heap_capacity()
+ }
+}
+
+impl ResidentBytes for TraitIdentifier {
+ fn heap_bytes(&self) -> usize {
+ self.name.heap_bytes() + self.contract_identifier.heap_bytes()
+ }
+}
+
+impl ResidentBytes for FunctionIdentifier {
+ fn heap_bytes(&self) -> usize {
+ self.heap_capacity()
+ }
+}
+
+impl ResidentBytes for TypeSignature {
+ fn heap_bytes(&self) -> usize {
+ match self {
+ TypeSignature::NoType
+ | TypeSignature::IntType
+ | TypeSignature::UIntType
+ | TypeSignature::BoolType
+ | TypeSignature::PrincipalType => 0,
+ TypeSignature::SequenceType(subtype) => subtype.heap_bytes(),
+ TypeSignature::TupleType(tuple_sig) => tuple_sig.heap_bytes(),
+ TypeSignature::OptionalType(inner) => inner.heap_bytes(),
+ TypeSignature::ResponseType(inner) => inner.heap_bytes(),
+ TypeSignature::CallableType(subtype) => subtype.heap_bytes(),
+ TypeSignature::ListUnionType(set) => set.heap_bytes(),
+ TypeSignature::TraitReferenceType(trait_id) => trait_id.heap_bytes(),
+ }
+ }
+}
+
+impl ResidentBytes for TupleTypeSignature {
+ fn heap_bytes(&self) -> usize {
+ // TupleTypeSignature wraps Arc>. get_type_map()
+ // returns &BTreeMap — count Arc overhead + map header + contents.
+ let map_header = size_of::>();
+ ARC_OVERHEAD + map_header + self.get_type_map().heap_bytes()
+ }
+}
+
+impl ResidentBytes for SequenceSubtype {
+ fn heap_bytes(&self) -> usize {
+ match self {
+ SequenceSubtype::BufferType(len) => len.heap_bytes(),
+ SequenceSubtype::ListType(list) => list.heap_bytes(),
+ SequenceSubtype::StringType(string) => string.heap_bytes(),
+ }
+ }
+}
+
+impl ResidentBytes for ListTypeData {
+ fn heap_bytes(&self) -> usize {
+ // max_len: u32 (no heap), entry_type: Box
+ size_of::() + self.get_list_item_type().heap_bytes()
+ }
+}
+
+impl ResidentBytes for StringSubtype {
+ fn heap_bytes(&self) -> usize {
+ 0 // Both variants (ASCII, UTF8) contain only u32 newtypes
+ }
+}
+
+impl ResidentBytes for BufferLength {
+ fn heap_bytes(&self) -> usize {
+ 0 // u32 newtype
+ }
+}
+
+impl ResidentBytes for StringUTF8Length {
+ fn heap_bytes(&self) -> usize {
+ 0 // u32 newtype
+ }
+}
+
+impl ResidentBytes for CallableSubtype {
+ fn heap_bytes(&self) -> usize {
+ match self {
+ CallableSubtype::Principal(id) => id.heap_bytes(),
+ CallableSubtype::Trait(trait_id) => trait_id.heap_bytes(),
+ }
+ }
+}
+
+impl ResidentBytes for SymbolicExpression {
+ fn heap_bytes(&self) -> usize {
+ // Only production fields: expr + id.
+ // developer-mode fields (span, pre_comments, end_line_comment, post_comments)
+ // are excluded for deterministic sizing between dev and prod builds.
+ self.expr.heap_bytes()
+ // id is u64 — no heap allocation
+ }
+}
+
+impl ResidentBytes for SymbolicExpressionType {
+ fn heap_bytes(&self) -> usize {
+ match self {
+ SymbolicExpressionType::AtomValue(value)
+ | SymbolicExpressionType::LiteralValue(value) => value.heap_bytes(),
+ SymbolicExpressionType::Atom(name) => name.heap_bytes(),
+ SymbolicExpressionType::List(exprs) => exprs.heap_bytes(),
+ SymbolicExpressionType::Field(trait_id) => trait_id.heap_bytes(),
+ SymbolicExpressionType::TraitReference(name, defn) => {
+ name.heap_bytes() + defn.heap_bytes()
+ }
+ }
+ }
+}
+
+impl ResidentBytes for TraitDefinition {
+ fn heap_bytes(&self) -> usize {
+ match self {
+ TraitDefinition::Defined(id) | TraitDefinition::Imported(id) => id.heap_bytes(),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_primitive_values_include_inline_size() {
+ // resident_bytes() includes size_of::() even for scalar variants
+ let int_size = Value::Int(42).resident_bytes();
+ assert!(
+ int_size >= size_of::(),
+ "Int resident_bytes ({int_size}) should be >= size_of::()"
+ );
+ assert_eq!(Value::Int(42).heap_bytes(), 0);
+ }
+
+ #[test]
+ fn test_u64_resident_bytes() {
+ let v: u64 = 42;
+ assert_eq!(v.resident_bytes(), 8);
+ assert_eq!(v.heap_bytes(), 0);
+ }
+
+ #[test]
+ fn test_string_resident_bytes() {
+ let s = String::from("hello world");
+ assert!(s.resident_bytes() >= size_of::() + 11);
+ assert!(s.heap_bytes() >= 11);
+ }
+
+ #[test]
+ fn test_vec_resident_bytes() {
+ let v: Vec = vec![1, 2, 3, 4, 5];
+ // heap: capacity * size_of::() = 5 * 8 = 40 bytes (no child heap)
+ assert!(v.heap_bytes() >= 40);
+ // total: size_of::>() + heap
+ assert!(v.resident_bytes() >= size_of::>() + 40);
+ }
+
+ #[test]
+ fn test_hashmap_resident_bytes() {
+ let mut m: HashMap = HashMap::new();
+ m.insert("key1".into(), 1);
+ m.insert("key2".into(), 2);
+ // Should include: HashMap header + backing array + key string heaps
+ assert!(m.resident_bytes() > size_of::>());
+ }
+
+ #[test]
+ fn test_optional_none() {
+ let opt: Option> = None;
+ assert_eq!(opt.heap_bytes(), 0);
+ // resident_bytes includes size_of::