use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicI64, Ordering};
use gsm_map::operations::mo_forward_sm::MoForwardSmArg;
use gsm_map::operations::mt_forward_sm::MtForwardSmArg;
use gsm_map::operations::sri_sm::{RoutingInfoForSmArg, RoutingInfoForSmRes};
use gsm_map::types::{op_codes, LocationInfoWithLmsi, SmRpDa, SmRpOa};
use sccp::{GlobalTitle, SccpAddress, SubsystemNumber, UnitData};
use tcap::{Begin, Component, Invoke, OperationCode, TcapMessage};
static LIVE: AtomicI64 = AtomicI64::new(0);
struct Counting;
unsafe impl GlobalAlloc for Counting {
unsafe fn alloc(&self, l: Layout) -> *mut u8 {
let p = System.alloc(l);
if !p.is_null() {
LIVE.fetch_add(l.size() as i64, Ordering::Relaxed);
}
p
}
unsafe fn dealloc(&self, p: *mut u8, l: Layout) {
System.dealloc(p, l);
LIVE.fetch_sub(l.size() as i64, Ordering::Relaxed);
}
unsafe fn alloc_zeroed(&self, l: Layout) -> *mut u8 {
let p = System.alloc_zeroed(l);
if !p.is_null() {
LIVE.fetch_add(l.size() as i64, Ordering::Relaxed);
}
p
}
unsafe fn realloc(&self, ptr: *mut u8, l: Layout, new_size: usize) -> *mut u8 {
let p = System.realloc(ptr, l, new_size);
if !p.is_null() {
LIVE.fetch_add(new_size as i64 - l.size() as i64, Ordering::Relaxed);
}
p
}
}
#[global_allocator]
static ALLOC: Counting = Counting;
fn live() -> i64 {
LIVE.load(Ordering::Relaxed)
}
fn sample_msisdn() -> Vec<u8> {
vec![0x91, 0x51, 0x55, 0x10, 0x00, 0x99, 0xF9]
}
fn sample_sc_addr() -> Vec<u8> {
vec![0x91, 0x51, 0x55, 0x10, 0x00]
}
fn sample_imsi() -> Vec<u8> {
vec![0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01]
}
fn sample_tpdu() -> Vec<u8> {
let mut ui = vec![
0x04, 0x0B, 0x91, 0x51, 0x55, 0x10, 0x00, 0x99, 0xF9, 0x00, 0x00,
];
ui.extend_from_slice(&[0xAB; 19]);
ui
}
fn codec_cycle(iters: usize) {
let sri_arg = RoutingInfoForSmArg {
msisdn: sample_msisdn().into(),
sm_rp_pri: true,
service_centre_address: sample_sc_addr().into(),
gprs_support_indicator: None,
sm_rp_mti: None,
sm_rp_smea: None,
};
let sri_res = RoutingInfoForSmRes {
imsi: sample_imsi().into(),
location_info_with_lmsi: LocationInfoWithLmsi {
network_node_number: vec![0x91, 0x51, 0x55, 0x10, 0x12, 0x34, 0x56].into(),
lmsi: Some(vec![0x00, 0x00, 0x00, 0x01].into()),
gprs_node_indicator: None,
additional_number: None,
},
};
let mo = MoForwardSmArg {
sm_rp_da: SmRpDa::ServiceCentreAddressDa(sample_sc_addr().into()),
sm_rp_oa: SmRpOa::MsIsdn(sample_msisdn().into()),
sm_rp_ui: sample_tpdu().into(),
imsi: None,
};
let mt = MtForwardSmArg {
sm_rp_da: SmRpDa::Imsi(sample_imsi().into()),
sm_rp_oa: SmRpOa::ServiceCentreAddressOa(sample_sc_addr().into()),
sm_rp_ui: sample_tpdu().into(),
more_messages_to_send: Some(()),
};
for _ in 0..iters {
let a = rasn::ber::encode(&sri_arg).unwrap();
std::hint::black_box(rasn::ber::decode::<RoutingInfoForSmArg>(&a).unwrap());
let r = rasn::ber::encode(&sri_res).unwrap();
std::hint::black_box(rasn::ber::decode::<RoutingInfoForSmRes>(&r).unwrap());
let o = rasn::ber::encode(&mo).unwrap();
std::hint::black_box(rasn::ber::decode::<MoForwardSmArg>(&o).unwrap());
let t = rasn::ber::encode(&mt).unwrap();
std::hint::black_box(rasn::ber::decode::<MtForwardSmArg>(&t).unwrap());
}
}
fn called_hlr() -> SccpAddress {
SccpAddress::with_gt(
GlobalTitle::Gt0100 {
translation_type: 0,
numbering_plan: 1,
encoding_scheme: 1,
nature_of_address: 4,
digits: "15550100123".to_string(),
},
Some(SubsystemNumber::Hlr),
)
}
fn calling_gmsc() -> SccpAddress {
SccpAddress::with_gt(
GlobalTitle::Gt0100 {
translation_type: 0,
numbering_plan: 1,
encoding_scheme: 1,
nature_of_address: 4,
digits: "15550100999".to_string(),
},
Some(SubsystemNumber::Msc),
)
}
fn stack_cycle(iters: usize) {
let arg = RoutingInfoForSmArg {
msisdn: sample_msisdn().into(),
sm_rp_pri: true,
service_centre_address: sample_sc_addr().into(),
gprs_support_indicator: None,
sm_rp_mti: None,
sm_rp_smea: None,
};
for _ in 0..iters {
let param = rasn::ber::encode(&arg).unwrap();
let invoke = Invoke {
invoke_id: 1,
linked_id: None,
operation_code: OperationCode::Local(op_codes::SEND_ROUTING_INFO_FOR_SM),
parameter: Some(rasn::types::Any::new(param)),
};
let begin = Begin {
otid: vec![0x00, 0x00, 0x00, 0x01].into(),
dialogue_portion: None,
components: Some(vec![Component::Invoke(invoke)]),
};
let tcap_bytes = tcap::encode(&TcapMessage::Begin(begin)).unwrap();
let udt = UnitData::new(called_hlr(), calling_gmsc(), tcap_bytes);
let wire = udt.encode().unwrap();
let udt = UnitData::decode(&wire).unwrap();
let msg = tcap::decode(&udt.data).unwrap();
if let TcapMessage::Begin(b) = msg {
let comps = b.components.unwrap();
if let Component::Invoke(inv) = &comps[0] {
let p = inv.parameter.as_ref().unwrap().as_bytes();
std::hint::black_box(rasn::ber::decode::<RoutingInfoForSmArg>(p).unwrap());
}
}
}
}
fn report(phase: &str, base: i64) -> i64 {
let growth = live() - base;
println!(" {phase}: live = {} bytes (Δ {:+})", live(), growth);
growth
}
fn main() {
const ITERS: usize = 100_000;
const CYCLES: usize = 10;
const BUDGET: i64 = 64 * 1024;
println!("[codec] {CYCLES} x {ITERS} BER encode+decode round-trips (SRI-SM arg/res + MO/MT-ForwardSM)");
codec_cycle(ITERS); let codec_base = live();
for c in 1..=CYCLES {
codec_cycle(ITERS);
report(&format!("cycle {c:>2}/{CYCLES}"), codec_base);
}
let codec_growth = live() - codec_base;
println!("\n[full stack] {CYCLES} x {ITERS} MAP → TCAP → SCCP encode + decode round-trips");
stack_cycle(ITERS); let stack_base = live();
for c in 1..=CYCLES {
stack_cycle(ITERS);
report(&format!("cycle {c:>2}/{CYCLES}"), stack_base);
}
let stack_growth = live() - stack_base;
println!();
let mut ok = true;
if codec_growth > BUDGET {
eprintln!("FAIL: codec live bytes grew {codec_growth} (> {BUDGET})");
ok = false;
}
if stack_growth > BUDGET {
eprintln!("FAIL: full-stack live bytes grew {stack_growth} (> {BUDGET})");
ok = false;
}
if !ok {
std::process::exit(1);
}
println!("PASS: codec Δ {codec_growth} ≤ {BUDGET}; full-stack Δ {stack_growth} ≤ {BUDGET}");
}