pub static AERON_IPC_STREAM: &std::ffi::CStr =
unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(b"aeron:ipc\0") };
unsafe impl Send for AeronCountersReader {}
unsafe impl Send for AeronSubscription {}
unsafe impl Send for AeronPublication {}
unsafe impl Send for AeronCounter {}
fn publication_position_to_result(position: i64) -> Result<i64, AeronCError> {
if position < 0 {
Err(AeronCError::from_code(position as i32))
} else {
Ok(position)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AeronStatus {
Disconnected,
Connected,
BackPressured,
Closed,
}
impl AeronStatus {
pub fn from_error(error: &AeronCError) -> Option<Self> {
match error.kind() {
AeronErrorType::PublicationBackPressured => Some(Self::BackPressured),
AeronErrorType::PublicationClosed => Some(Self::Closed),
_ => None,
}
}
}
impl AeronCnc {
#[inline]
pub fn read_on_partial_stack(
aeron_dir: &std::ffi::CString,
mut handler: impl FnMut(&mut AeronCnc),
) -> Result<(), AeronCError> {
let cnc = ManagedCResource::initialise(move |cnc| unsafe {
aeron_cnc_init(cnc, aeron_dir.as_ptr(), 0)
})?;
let mut cnc = Self {
inner: CResource::Borrowed(cnc),
};
handler(&mut cnc);
unsafe { aeron_cnc_close(cnc.get_inner()) };
Ok(())
}
#[deprecated(since = "0.1.122", note = "Use `new_on_heap` instead")]
#[inline]
pub fn new(aeron_dir: &str) -> Result<AeronCnc, AeronCError> {
Self::new_on_heap(aeron_dir)
}
#[inline]
pub fn new_on_heap(aeron_dir: &str) -> Result<AeronCnc, AeronCError> {
let c_string = std::ffi::CString::new(aeron_dir).map_err(|_| AeronCError::from_code(-1))?;
let resource = ManagedCResource::new(
move |cnc| unsafe { aeron_cnc_init(cnc, c_string.as_ptr(), 0) },
Some(Box::new(move |cnc| unsafe {
aeron_cnc_close(*cnc);
0
})),
false,
None,
)?;
let result = Self {
inner: CResource::OwnedOnHeap(std::rc::Rc::new(resource)),
};
Ok(result)
}
#[doc = " Gets the timestamp of the last heartbeat sent to the media driver from any client.\n\n @param aeron_cnc to query\n @return last heartbeat timestamp in ms."]
#[inline]
pub fn get_to_driver_heartbeat_ms(&self) -> Result<i64, AeronCError> {
unsafe {
let timestamp = aeron_cnc_to_driver_heartbeat(self.get_inner());
if timestamp >= 0 {
return Ok(timestamp);
} else {
return Err(AeronCError::from_code(timestamp as i32));
}
}
}
}
impl AeronHeader {
pub fn image(&self) -> Option<AeronImage> {
let ptr = self.context();
if ptr.is_null() {
None
} else {
Some(AeronImage::from(ptr as *mut aeron_image_t))
}
}
#[inline]
pub fn session_id(&self) -> Option<i32> {
self.get_values().ok().map(|v| v.frame().session_id())
}
#[inline]
pub fn stream_id(&self) -> Option<i32> {
self.get_values().ok().map(|v| v.frame().stream_id())
}
#[inline]
pub fn reserved_value(&self) -> Option<i64> {
self.get_values().ok().map(|v| v.frame().reserved_value())
}
#[inline]
pub fn term_id(&self) -> Option<i32> {
self.get_values().ok().map(|v| v.frame().term_id())
}
#[inline]
pub fn term_offset(&self) -> Option<i32> {
self.get_values().ok().map(|v| v.frame().term_offset())
}
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum AeronSystemCounterType {
BytesSent = 0,
BytesReceived = 1,
ReceiverProxyFails = 2,
SenderProxyFails = 3,
ConductorProxyFails = 4,
NakMessagesSent = 5,
NakMessagesReceived = 6,
StatusMessagesSent = 7,
StatusMessagesReceived = 8,
HeartbeatsSent = 9,
HeartbeatsReceived = 10,
RetransmitsSent = 11,
FlowControlUnderRuns = 12,
FlowControlOverRuns = 13,
InvalidPackets = 14,
Errors = 15,
ShortSends = 16,
FreeFails = 17,
SenderFlowControlLimits = 18,
UnblockedPublications = 19,
UnblockedCommands = 20,
PossibleTtlAsymmetry = 21,
ControllableIdleStrategy = 22,
LossGapFills = 23,
ClientTimeouts = 24,
ResolutionChanges = 25,
ConductorMaxCycleTime = 26,
ConductorCycleTimeThresholdExceeded = 27,
SenderMaxCycleTime = 28,
SenderCycleTimeThresholdExceeded = 29,
ReceiverMaxCycleTime = 30,
ReceiverCycleTimeThresholdExceeded = 31,
NameResolverMaxTime = 32,
NameResolverTimeThresholdExceeded = 33,
AeronVersion = 34,
BytesCurrentlyMapped = 35,
RetransmittedBytes = 36,
RetransmitOverflow = 37,
ErrorFramesReceived = 38,
ErrorFramesSent = 39,
DummyLast = 40,
}
impl std::convert::TryFrom<i32> for AeronSystemCounterType {
type Error = AeronCError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
if value < 0 {
return Err(AeronCError::from_code(value));
}
match value as u32 {
0 => Ok(AeronSystemCounterType::BytesSent),
1 => Ok(AeronSystemCounterType::BytesReceived),
2 => Ok(AeronSystemCounterType::ReceiverProxyFails),
3 => Ok(AeronSystemCounterType::SenderProxyFails),
4 => Ok(AeronSystemCounterType::ConductorProxyFails),
5 => Ok(AeronSystemCounterType::NakMessagesSent),
6 => Ok(AeronSystemCounterType::NakMessagesReceived),
7 => Ok(AeronSystemCounterType::StatusMessagesSent),
8 => Ok(AeronSystemCounterType::StatusMessagesReceived),
9 => Ok(AeronSystemCounterType::HeartbeatsSent),
10 => Ok(AeronSystemCounterType::HeartbeatsReceived),
11 => Ok(AeronSystemCounterType::RetransmitsSent),
12 => Ok(AeronSystemCounterType::FlowControlUnderRuns),
13 => Ok(AeronSystemCounterType::FlowControlOverRuns),
14 => Ok(AeronSystemCounterType::InvalidPackets),
15 => Ok(AeronSystemCounterType::Errors),
16 => Ok(AeronSystemCounterType::ShortSends),
17 => Ok(AeronSystemCounterType::FreeFails),
18 => Ok(AeronSystemCounterType::SenderFlowControlLimits),
19 => Ok(AeronSystemCounterType::UnblockedPublications),
20 => Ok(AeronSystemCounterType::UnblockedCommands),
21 => Ok(AeronSystemCounterType::PossibleTtlAsymmetry),
22 => Ok(AeronSystemCounterType::ControllableIdleStrategy),
23 => Ok(AeronSystemCounterType::LossGapFills),
24 => Ok(AeronSystemCounterType::ClientTimeouts),
25 => Ok(AeronSystemCounterType::ResolutionChanges),
26 => Ok(AeronSystemCounterType::ConductorMaxCycleTime),
27 => Ok(AeronSystemCounterType::ConductorCycleTimeThresholdExceeded),
28 => Ok(AeronSystemCounterType::SenderMaxCycleTime),
29 => Ok(AeronSystemCounterType::SenderCycleTimeThresholdExceeded),
30 => Ok(AeronSystemCounterType::ReceiverMaxCycleTime),
31 => Ok(AeronSystemCounterType::ReceiverCycleTimeThresholdExceeded),
32 => Ok(AeronSystemCounterType::NameResolverMaxTime),
33 => Ok(AeronSystemCounterType::NameResolverTimeThresholdExceeded),
34 => Ok(AeronSystemCounterType::AeronVersion),
35 => Ok(AeronSystemCounterType::BytesCurrentlyMapped),
36 => Ok(AeronSystemCounterType::RetransmittedBytes),
37 => Ok(AeronSystemCounterType::RetransmitOverflow),
38 => Ok(AeronSystemCounterType::ErrorFramesReceived),
39 => Ok(AeronSystemCounterType::ErrorFramesSent),
40 => Ok(AeronSystemCounterType::DummyLast),
_ => Err(AeronCError::from_code(-1)),
}
}
}
impl AeronCncMetadata {
#[inline]
pub fn load_from_file(aeron_dir: &str) -> Result<Self, AeronCError> {
let aeron_dir =
std::ffi::CString::new(aeron_dir).map_err(|_| AeronCError::from_code(-1))?;
let mapped_file = std::rc::Rc::new(std::cell::RefCell::new(aeron_mapped_file_t {
addr: std::ptr::null_mut(),
length: 0,
}));
let mapped_file2 = std::rc::Rc::clone(&mapped_file);
let resource = ManagedCResource::new(
move |ctx| {
let result = unsafe {
aeron_cnc_map_file_and_load_metadata(
aeron_dir.as_ptr(),
mapped_file.borrow_mut().deref_mut() as *mut aeron_mapped_file_t,
ctx,
)
};
if result == aeron_cnc_load_result_t::AERON_CNC_LOAD_SUCCESS {
1
} else {
-1
}
},
Some(Box::new(move |ctx| unsafe {
aeron_unmap(mapped_file2.borrow_mut().deref_mut() as *mut aeron_mapped_file_t)
})),
false,
None,
)?;
let result = Self {
inner: CResource::OwnedOnHeap(std::rc::Rc::new(resource)),
};
Ok(result)
}
#[inline]
pub fn read_from_file(
aeron_dir: &std::ffi::CString,
mut handler: impl FnMut(Self),
) -> Result<(), AeronCError> {
let mut mapped_file = aeron_mapped_file_t {
addr: std::ptr::null_mut(),
length: 0,
};
let ctx = ManagedCResource::initialise(move |ctx| {
let result = unsafe {
aeron_cnc_map_file_and_load_metadata(
aeron_dir.as_ptr(),
&mut mapped_file as *mut aeron_mapped_file_t,
ctx,
)
};
if result == aeron_cnc_load_result_t::AERON_CNC_LOAD_SUCCESS {
1
} else {
-1
}
})?;
let result = Self {
inner: CResource::Borrowed(ctx),
};
handler(result);
unsafe { aeron_unmap(&mut mapped_file as *mut aeron_mapped_file_t) };
Ok(())
}
}
impl AeronSubscription {
pub fn async_add_destination(
&mut self,
client: &Aeron,
destination: &std::ffi::CStr,
) -> Result<AeronAsyncDestination, AeronCError> {
AeronAsyncDestination::aeron_subscription_async_add_destination(client, self, destination)
}
pub fn add_destination(
&mut self,
client: &Aeron,
destination: &std::ffi::CStr,
timeout: std::time::Duration,
) -> Result<(), AeronCError> {
let result = self.async_add_destination(client, destination)?;
if result
.aeron_subscription_async_destination_poll()
.unwrap_or_default()
> 0
{
return Ok(());
}
let time = std::time::Instant::now();
while time.elapsed() < timeout {
if result
.aeron_subscription_async_destination_poll()
.unwrap_or_default()
> 0
{
return Ok(());
}
#[cfg(debug_assertions)]
std::thread::sleep(std::time::Duration::from_millis(10));
}
log::error!("failed async poll for {:?} {:?}", destination, self);
Err(AeronErrorType::TimedOut.into())
}
#[inline]
pub fn for_each_fragment<F>(&self, max_per_poll: usize, f: F) -> Result<i32, AeronCError>
where
F: FnMut(&[u8], AeronHeader),
{
self.poll_once(f, max_per_poll)
}
}
impl AeronExclusivePublication {
pub fn async_add_destination(
&mut self,
client: &Aeron,
destination: &std::ffi::CStr,
) -> Result<AeronAsyncDestination, AeronCError> {
AeronAsyncDestination::aeron_exclusive_publication_async_add_destination(
client,
self,
destination,
)
}
pub fn add_destination(
&mut self,
client: &Aeron,
destination: &std::ffi::CStr,
timeout: std::time::Duration,
) -> Result<(), AeronCError> {
let result = self.async_add_destination(client, destination)?;
if result
.aeron_subscription_async_destination_poll()
.unwrap_or_default()
> 0
{
return Ok(());
}
let time = std::time::Instant::now();
while time.elapsed() < timeout {
if result
.aeron_subscription_async_destination_poll()
.unwrap_or_default()
> 0
{
return Ok(());
}
#[cfg(debug_assertions)]
std::thread::sleep(std::time::Duration::from_millis(10));
}
log::error!("failed async poll for {:?} {:?}", destination, self);
Err(AeronErrorType::TimedOut.into())
}
}
impl AeronPublication {
pub fn async_add_destination(
&mut self,
client: &Aeron,
destination: &std::ffi::CStr,
) -> Result<AeronAsyncDestination, AeronCError> {
AeronAsyncDestination::aeron_publication_async_add_destination(client, self, destination)
}
pub fn add_destination(
&mut self,
client: &Aeron,
destination: &std::ffi::CStr,
timeout: std::time::Duration,
) -> Result<(), AeronCError> {
let result = self.async_add_destination(client, destination)?;
if result
.aeron_subscription_async_destination_poll()
.unwrap_or_default()
> 0
{
return Ok(());
}
let time = std::time::Instant::now();
while time.elapsed() < timeout {
if result
.aeron_subscription_async_destination_poll()
.unwrap_or_default()
> 0
{
return Ok(());
}
#[cfg(debug_assertions)]
std::thread::sleep(std::time::Duration::from_millis(10));
}
log::error!("failed async poll for {:?} {:?}", destination, self);
Err(AeronErrorType::TimedOut.into())
}
}
impl std::str::FromStr for AeronUriStringBuilder {
type Err = AeronCError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let builder = AeronUriStringBuilder::default();
let s = std::ffi::CString::new(s).map_err(|_| AeronCError::from_code(-1))?;
builder.init_on_string(&s)?;
Ok(builder)
}
}
impl Default for AeronUriStringBuilder {
fn default() -> Self {
let r_constructor = ManagedCResource::new(
move |ctx_field| {
let inst: aeron_uri_string_builder_t = unsafe { std::mem::zeroed() };
let inner_ptr: *mut aeron_uri_string_builder_t = Box::into_raw(Box::new(inst));
unsafe { *ctx_field = inner_ptr };
0
},
Some(Box::new(move |ctx_field| unsafe {
aeron_uri_string_builder_close(*ctx_field)
})),
true,
Some(|ctx| unsafe { (*ctx).closed }),
)
.expect("should not happen");
Self {
inner: CResource::OwnedOnHeap(std::rc::Rc::new(r_constructor)),
}
}
}
impl AeronCError {
pub fn get_last_err_message(&self) -> &str {
Aeron::errmsg()
}
}
impl std::fmt::Debug for AeronCError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AeronCError")
.field("code", &self.code)
.field("kind", &self.kind())
.field("lastError", &self.get_last_err_message())
.finish()
}
}
impl std::fmt::Display for AeronCError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Aeron error {}: {:?} [lastError={}]",
self.code,
self.kind(),
self.get_last_err_message()
)
}
}
impl std::error::Error for AeronCError {}
const PARSE_CSTR_ERROR_CODE: i32 = -132131;
impl AeronUriStringBuilder {
#[inline]
#[doc = "Initialize a new AeronUriStringBuilder. If already initialized, it will close the previous builder to prevent memory leaks."]
pub fn init_new(&self) -> Result<i32, AeronCError> {
if let Some(inner) = self.inner.as_owned() {
if !inner.close_already_called.get() {
let _ = self.close();
inner.close_already_called.set(false);
}
}
let result = unsafe {
#[cfg(feature = "log-c-bindings")]
log::info!("aeron_uri_string_builder_init_new(self.get_inner())");
aeron_uri_string_builder_init_new(self.get_inner())
};
if result < 0 {
Err(AeronCError::from_code(result))
} else {
Ok(result)
}
}
#[inline]
#[doc = "Initialize AeronUriStringBuilder with an existing URI string. If already initialized, it will close the previous builder."]
pub fn init_on_string(&self, uri: &std::ffi::CStr) -> Result<i32, AeronCError> {
if let Some(inner) = self.inner.as_owned() {
if !inner.close_already_called.get() {
let _ = self.close();
inner.close_already_called.set(false);
}
}
let result = unsafe {
#[cfg(feature = "log-c-bindings")]
log::info!("aeron_uri_string_builder_init_on_string(self.get_inner(), uri)");
aeron_uri_string_builder_init_on_string(self.get_inner(), uri.as_ptr())
};
if result < 0 {
Err(AeronCError::from_code(result))
} else {
Ok(result)
}
}
#[inline]
pub fn build(&self, max_str_length: usize) -> Result<String, AeronCError> {
let mut result = String::with_capacity(max_str_length);
self.build_into(&mut result)?;
Ok(result)
}
pub fn put_string(&self, key: &std::ffi::CStr, value: &str) -> Result<&Self, AeronCError> {
let value = std::ffi::CString::new(value)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put(&key, &value)?;
Ok(self)
}
pub fn put_strings(&self, key: &str, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CString::new(key)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
let value = std::ffi::CString::new(value)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put(&key, &value)?;
Ok(self)
}
pub fn media(&self, value: Media) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_STRING_BUILDER_MEDIA_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value.as_str())?;
Ok(self)
}
pub fn control_mode(&self, value: ControlMode) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_UDP_CHANNEL_CONTROL_MODE_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value.as_str())?;
Ok(self)
}
pub fn prefix(&self, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_STRING_BUILDER_PREFIX_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value)?;
Ok(self)
}
pub fn initial_term_id(&self, value: i32) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_INITIAL_TERM_ID_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int32(key, value)?;
Ok(self)
}
pub fn term_id(&self, value: i32) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_TERM_ID_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int32(key, value)?;
Ok(self)
}
pub fn term_offset(&self, value: i32) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_TERM_OFFSET_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int32(key, value)?;
Ok(self)
}
pub fn alias(&self, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_ALIAS_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value)?;
Ok(self)
}
pub fn term_length(&self, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_TERM_LENGTH_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value)?;
Ok(self)
}
pub fn linger_timeout(&self, value: i64) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_LINGER_TIMEOUT_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int64(key, value)?;
Ok(self)
}
pub fn mtu_length(&self, value: i32) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_MTU_LENGTH_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int32(key, value)?;
Ok(self)
}
pub fn ttl(&self, value: i32) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_UDP_CHANNEL_TTL_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int32(key, value)?;
Ok(self)
}
pub fn sparse_term(&self, value: bool) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_SPARSE_TERM_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, if value { "true" } else { "false" })?;
Ok(self)
}
pub fn reliable(&self, value: bool) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_UDP_CHANNEL_RELIABLE_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, if value { "true" } else { "false" })?;
Ok(self)
}
pub fn eos(&self, value: bool) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_EOS_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, if value { "true" } else { "false" })?;
Ok(self)
}
pub fn tether(&self, value: bool) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_TETHER_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, if value { "true" } else { "false" })?;
Ok(self)
}
pub fn tags(&self, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_TAGS_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value)?;
Ok(self)
}
pub fn endpoint(&self, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_UDP_CHANNEL_ENDPOINT_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value)?;
Ok(self)
}
pub fn interface(&self, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_UDP_CHANNEL_INTERFACE_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value)?;
Ok(self)
}
pub fn control(&self, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_UDP_CHANNEL_CONTROL_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value)?;
Ok(self)
}
pub fn session_id(&self, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_SESSION_ID_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value)?;
Ok(self)
}
pub fn group(&self, value: bool) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_GROUP_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, if value { "true" } else { "false" })?;
Ok(self)
}
pub fn rejoin(&self, value: bool) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_REJOIN_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, if value { "true" } else { "false" })?;
Ok(self)
}
pub fn fc(&self, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_FC_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value)?;
Ok(self)
}
pub fn gtag(&self, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_GTAG_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value)?;
Ok(self)
}
pub fn cc(&self, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_CC_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value)?;
Ok(self)
}
pub fn spies_simulate_connection(&self, value: bool) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_SPIES_SIMULATE_CONNECTION_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, if value { "true" } else { "false" })?;
Ok(self)
}
pub fn ats(&self, value: bool) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_ATS_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, if value { "true" } else { "false" })?;
Ok(self)
}
pub fn socket_sndbuf(&self, value: i32) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_SOCKET_SNDBUF_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int32(key, value)?;
Ok(self)
}
pub fn socket_rcvbuf(&self, value: i32) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_SOCKET_RCVBUF_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int32(key, value)?;
Ok(self)
}
pub fn receiver_window(&self, value: i32) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_RECEIVER_WINDOW_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int32(key, value)?;
Ok(self)
}
pub fn media_rcv_timestamp_offset(&self, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_MEDIA_RCV_TIMESTAMP_OFFSET_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value)?;
Ok(self)
}
pub fn channel_rcv_timestamp_offset(&self, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_CHANNEL_RCV_TIMESTAMP_OFFSET_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value)?;
Ok(self)
}
pub fn channel_snd_timestamp_offset(&self, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_CHANNEL_SND_TIMESTAMP_OFFSET_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value)?;
Ok(self)
}
pub fn timestamp_offset_reserved(&self, value: &str) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_TIMESTAMP_OFFSET_RESERVED)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_string(key, value)?;
Ok(self)
}
pub fn response_correlation_id(&self, value: i64) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_RESPONSE_CORRELATION_ID_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int64(key, value)?;
Ok(self)
}
pub fn nak_delay(&self, value: i64) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_NAK_DELAY_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int64(key, value)?;
Ok(self)
}
pub fn untethered_window_limit_timeout(&self, value: i64) -> Result<&Self, AeronCError> {
let key =
std::ffi::CStr::from_bytes_until_nul(AERON_URI_UNTETHERED_WINDOW_LIMIT_TIMEOUT_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int64(key, value)?;
Ok(self)
}
pub fn untethered_resting_timeout(&self, value: i64) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_UNTETHERED_RESTING_TIMEOUT_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int64(key, value)?;
Ok(self)
}
pub fn max_resend(&self, value: i32) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_MAX_RESEND_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int32(key, value)?;
Ok(self)
}
pub fn stream_id(&self, value: i32) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_STREAM_ID_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int32(key, value)?;
Ok(self)
}
pub fn publication_window(&self, value: i32) -> Result<&Self, AeronCError> {
let key = std::ffi::CStr::from_bytes_until_nul(AERON_URI_PUBLICATION_WINDOW_KEY)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.put_int32(key, value)?;
Ok(self)
}
#[inline]
pub fn build_into(&self, dst: &mut String) -> Result<(), AeronCError> {
self.sprint_into(dst)?;
Ok(())
}
#[inline]
pub fn remove(&self, key: &std::ffi::CStr) -> Result<i32, AeronCError> {
unsafe {
let result =
aeron_uri_string_builder_put(self.get_inner(), key.as_ptr(), std::ptr::null());
if result < 0 {
Err(AeronCError::from_code(result))
} else {
Ok(result)
}
}
}
#[inline]
pub fn remove_str(&self, key: &str) -> Result<i32, AeronCError> {
let key = std::ffi::CString::new(key)
.map_err(|_| AeronCError::from_code(PARSE_CSTR_ERROR_CODE))?;
self.remove(&key)
}
}
impl AeronCountersReader {
#[inline]
#[doc = "Get the label for a counter."]
#[doc = ""]
#[doc = " \n**param** counters_reader that contains the counter"]
#[doc = " \n**param** counter_id to find"]
#[doc = " \n**param** buffer to store the counter in."]
#[doc = " \n**param** buffer_length length of the output buffer"]
#[doc = " \n**return** -1 on failure, number of characters copied to buffer on success."]
pub fn get_counter_label(
&self,
counter_id: i32,
max_length: usize,
) -> Result<String, AeronCError> {
let mut result = String::with_capacity(max_length);
self.get_counter_label_into(counter_id, &mut result)?;
Ok(result)
}
#[inline]
#[doc = "Get the label for a counter."]
pub fn get_counter_label_into(
&self,
counter_id: i32,
dst: &mut String,
) -> Result<(), AeronCError> {
unsafe {
let capacity = dst.capacity();
let vec = dst.as_mut_vec();
vec.set_len(capacity);
let written =
self.counter_label(counter_id, vec.as_mut_ptr() as *mut _, capacity)? as usize;
vec.set_len(std::cmp::min(written, capacity));
}
Ok(())
}
#[inline]
#[doc = "Get the key for a counter."]
pub fn get_counter_key(&self, counter_id: i32) -> Result<Vec<u8>, AeronCError> {
let mut dst = Vec::new();
self.get_counter_key_into(counter_id, &mut dst)?;
Ok(dst)
}
#[inline]
#[doc = "Get the key for a counter."]
pub fn get_counter_key_into(
&self,
counter_id: i32,
dst: &mut Vec<u8>,
) -> Result<(), AeronCError> {
let mut key_ptr: *mut u8 = std::ptr::null_mut();
unsafe {
let result = bindings::aeron_counters_reader_metadata_key(
self.get_inner(),
counter_id,
&mut key_ptr,
);
if result < 0 || key_ptr.is_null() {
return Err(AeronCError::from_code(result));
}
loop {
let val = *key_ptr.add(dst.len());
if val == 0 {
break;
} else {
dst.push(val);
}
}
Ok(())
}
}
#[inline]
pub fn get_counter_value(&self, counter_id: i32) -> i64 {
unsafe { *self.addr(counter_id) }
}
}
impl Aeron {
pub fn new_blocking(
context: &AeronContext,
timeout: std::time::Duration,
) -> Result<Self, AeronCError> {
if let Ok(aeron) = Aeron::new(&context) {
return Ok(aeron);
}
let time = std::time::Instant::now();
while time.elapsed() < timeout {
if let Ok(aeron) = Aeron::new(&context) {
return Ok(aeron);
}
#[cfg(debug_assertions)]
std::thread::sleep(std::time::Duration::from_millis(10));
}
log::error!("failed to create aeron client for {:?}", context);
Err(AeronErrorType::TimedOut.into())
}
}
impl AeronFragmentHandlerCallback for AeronFragmentAssembler {
fn handle_aeron_fragment_handler(&mut self, buffer: &[u8], header: AeronHeader) -> () {
unsafe {
aeron_fragment_assembler_handler(
self.get_inner() as *mut _,
buffer.as_ptr(),
buffer.len(),
header.get_inner(),
)
}
}
}
impl AeronControlledFragmentHandlerCallback for AeronControlledFragmentAssembler {
fn handle_aeron_controlled_fragment_handler(
&mut self,
buffer: &[u8],
header: AeronHeader,
) -> aeron_controlled_fragment_handler_action_t {
unsafe {
aeron_controlled_fragment_assembler_handler(
self.get_inner() as *mut _,
buffer.as_ptr(),
buffer.len(),
header.get_inner(),
)
}
}
}
impl<T: AeronFragmentHandlerCallback> Handler<T> {
pub fn leak_with_fragment_assembler(
handler: T,
) -> Result<(Handler<AeronFragmentAssembler>, Handler<T>), AeronCError> {
let handler = Handler::leak(handler);
Ok((
Handler::leak(AeronFragmentAssembler::new(Some(&handler))?),
handler,
))
}
}
impl<T: AeronControlledFragmentHandlerCallback> Handler<T> {
pub fn leak_with_controlled_fragment_assembler(
handler: T,
) -> Result<(Handler<AeronControlledFragmentAssembler>, Handler<T>), AeronCError> {
let handler = Handler::leak(handler);
Ok((
Handler::leak(AeronControlledFragmentAssembler::new(Some(&handler))?),
handler,
))
}
}
impl AeronBufferClaim {
#[inline]
pub fn data_mut(&self) -> &mut [u8] {
debug_assert!(!self.data.is_null());
unsafe { std::slice::from_raw_parts_mut(self.data, self.length) }
}
#[inline]
pub fn frame_header_mut(&self) -> &mut aeron_header_values_frame_t {
debug_assert!(!self.frame_header.is_null());
unsafe { &mut *self.frame_header.cast::<aeron_header_values_frame_t>() }
}
}
pub struct AeronClaim {
claim: AeronBufferClaim,
position: i64,
finalised: bool,
}
impl AeronClaim {
#[inline]
pub fn data(&mut self) -> &mut [u8] {
self.claim.data()
}
#[inline]
pub fn len(&self) -> usize {
self.claim.length()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.claim.length() == 0
}
#[inline]
pub fn position(&self) -> i64 {
self.position
}
pub fn commit(mut self) -> Result<i64, AeronCError> {
self.finalised = true;
self.claim.commit()?;
Ok(self.position)
}
pub fn abort(mut self) -> Result<(), AeronCError> {
self.finalised = true;
self.claim.abort()?;
Ok(())
}
}
impl Drop for AeronClaim {
fn drop(&mut self) {
if !self.finalised {
let _ = self.claim.abort();
}
}
}
impl AeronPublication {
#[inline]
pub fn offer_result<H: AeronReservedValueSupplierCallback>(
&self,
buffer: &[u8],
reserved_value_supplier: Option<&Handler<H>>,
) -> Result<i64, AeronCError> {
publication_position_to_result(self.offer::<H>(buffer, reserved_value_supplier))
}
#[inline]
pub fn offer_result_simple(&self, buffer: &[u8]) -> Result<i64, AeronCError> {
self.offer_result::<AeronReservedValueSupplierLogger>(
buffer,
Handlers::no_reserved_value_supplier_handler(),
)
}
#[inline]
pub fn try_claim_result(
&self,
length: usize,
buffer_claim: &AeronBufferClaim,
) -> Result<i64, AeronCError> {
publication_position_to_result(self.try_claim(length, buffer_claim))
}
pub fn try_claim_owned(&self, length: usize) -> Result<AeronClaim, AeronCError> {
let claim = AeronBufferClaim::new_zeroed_on_stack();
let position = publication_position_to_result(self.try_claim(length, &claim))?;
Ok(AeronClaim {
claim,
position,
finalised: false,
})
}
#[inline]
pub fn status(&self) -> AeronStatus {
if self.is_closed() {
AeronStatus::Closed
} else if self.is_connected() {
AeronStatus::Connected
} else {
AeronStatus::Disconnected
}
}
}
impl AeronExclusivePublication {
#[inline]
pub fn offer_result<H: AeronReservedValueSupplierCallback>(
&self,
buffer: &[u8],
reserved_value_supplier: Option<&Handler<H>>,
) -> Result<i64, AeronCError> {
publication_position_to_result(self.offer::<H>(buffer, reserved_value_supplier))
}
#[inline]
pub fn offer_result_simple(&self, buffer: &[u8]) -> Result<i64, AeronCError> {
self.offer_result::<AeronReservedValueSupplierLogger>(
buffer,
Handlers::no_reserved_value_supplier_handler(),
)
}
#[inline]
pub fn try_claim_result(
&self,
length: usize,
buffer_claim: &AeronBufferClaim,
) -> Result<i64, AeronCError> {
publication_position_to_result(self.try_claim(length, buffer_claim))
}
pub fn try_claim_owned(&self, length: usize) -> Result<AeronClaim, AeronCError> {
let claim = AeronBufferClaim::new_zeroed_on_stack();
let position = publication_position_to_result(self.try_claim(length, &claim))?;
Ok(AeronClaim {
claim,
position,
finalised: false,
})
}
#[inline]
pub fn status(&self) -> AeronStatus {
if self.is_closed() {
AeronStatus::Closed
} else if self.is_connected() {
AeronStatus::Connected
} else {
AeronStatus::Disconnected
}
}
}
impl AeronSubscription {
#[inline]
pub fn status(&self) -> AeronStatus {
if self.is_closed() {
AeronStatus::Closed
} else if self.is_connected() {
AeronStatus::Connected
} else {
AeronStatus::Disconnected
}
}
}
impl AeronImage {
#[inline]
pub fn poll_instrumented<AeronFragmentHandlerHandlerImpl: AeronFragmentHandlerCallback>(
&self,
handler: Option<&Handler<AeronFragmentHandlerHandlerImpl>>,
fragment_limit: usize,
) -> Result<i32, AeronCError> {
self.poll(handler, fragment_limit)
}
#[inline]
pub fn poll_once_instrumented<
AeronFragmentHandlerHandlerImpl: FnMut(&[u8], AeronHeader) -> (),
>(
&self,
handler: AeronFragmentHandlerHandlerImpl,
fragment_limit: usize,
) -> Result<i32, AeronCError> {
self.poll_once(handler, fragment_limit)
}
}
pub struct AeronErrorLogger;
impl AeronErrorHandlerCallback for AeronErrorLogger {
fn handle_aeron_error_handler(&mut self, error_code: std::ffi::c_int, msg: &str) -> () {
log::error!("aeron error {}: {}", error_code, msg);
}
}
unsafe impl Send for AeronErrorLogger {}
unsafe impl Sync for AeronErrorLogger {}
pub struct FnMutMessageHandler {
func: fn(*mut (), &[u8], AeronHeader),
ctx: *mut (),
}
impl AeronFragmentHandlerCallback for FnMutMessageHandler {
fn handle_aeron_fragment_handler(&mut self, buffer: &[u8], header: AeronHeader) -> () {
self.call(buffer, header);
}
}
impl FnMutMessageHandler {
pub fn new() -> Self {
Self {
func: Self::noop,
ctx: std::ptr::null_mut(),
}
}
#[inline]
pub fn set<T>(&mut self, ctx: &mut T, func: fn(&mut T, &[u8], AeronHeader)) -> &mut Self {
self.func = Self::wrap::<T>(func);
self.ctx = ctx as *mut T as *mut ();
self
}
#[inline(always)]
pub fn call(&mut self, msg: &[u8], header: AeronHeader) {
(self.func)(self.ctx, msg, header);
}
#[inline]
fn wrap<T>(f: fn(&mut T, &[u8], AeronHeader)) -> fn(*mut (), &[u8], AeronHeader) {
unsafe { std::mem::transmute(f) }
}
fn noop(_: *mut (), _: &[u8], _: AeronHeader) {
}
}
pub struct AeronFragmentClosureAssembler {
assembler: AeronFragmentAssembler,
handler: Handler<FnMutMessageHandler>,
assembler_handler: Handler<AeronFragmentAssembler>,
}
impl AeronFragmentClosureAssembler {
pub fn new() -> Result<Self, AeronCError> {
let handler = Handler::leak(FnMutMessageHandler::new());
Ok(Self {
assembler: AeronFragmentAssembler::new(Some(&handler))?,
handler,
assembler_handler: Handler {
raw_ptr: std::ptr::null_mut(),
should_drop: false,
},
})
}
pub fn process<T>(
&mut self,
ctx: &mut T,
func: fn(&mut T, &[u8], AeronHeader),
) -> Option<&Handler<AeronFragmentAssembler>> {
self.handler.set(ctx, func);
self.assembler_handler.raw_ptr = &mut self.assembler as *mut _;
Some(&self.assembler_handler)
}
}
impl Drop for AeronFragmentClosureAssembler {
fn drop(&mut self) {
self.handler.release();
}
}
pub struct FnMutControlledMessageHandler {
func: fn(*mut (), &[u8], AeronHeader) -> aeron_controlled_fragment_handler_action_t,
ctx: *mut (),
}
impl FnMutControlledMessageHandler {
pub fn new() -> Self {
Self {
func: Self::noop,
ctx: std::ptr::null_mut(),
}
}
#[inline]
pub fn set<T>(
&mut self,
ctx: &mut T,
func: fn(&mut T, &[u8], AeronHeader) -> aeron_controlled_fragment_handler_action_t,
) -> &mut Self {
self.func = Self::wrap::<T>(func);
self.ctx = ctx as *mut T as *mut ();
self
}
#[inline(always)]
pub fn call(
&mut self,
msg: &[u8],
header: AeronHeader,
) -> aeron_controlled_fragment_handler_action_t {
(self.func)(self.ctx, msg, header)
}
#[inline]
fn wrap<T>(
f: fn(&mut T, &[u8], AeronHeader) -> aeron_controlled_fragment_handler_action_t,
) -> fn(*mut (), &[u8], AeronHeader) -> aeron_controlled_fragment_handler_action_t {
unsafe { std::mem::transmute(f) }
}
fn noop(_: *mut (), _: &[u8], _: AeronHeader) -> aeron_controlled_fragment_handler_action_t {
bindings::aeron_controlled_fragment_handler_action_en::AERON_ACTION_CONTINUE
}
}
impl AeronControlledFragmentHandlerCallback for FnMutControlledMessageHandler {
fn handle_aeron_controlled_fragment_handler(
&mut self,
buffer: &[u8],
header: AeronHeader,
) -> aeron_controlled_fragment_handler_action_t {
self.call(buffer, header)
}
}
pub struct AeronControlledFragmentClosureAssembler {
assembler: AeronControlledFragmentAssembler,
handler: Handler<FnMutControlledMessageHandler>,
assembler_handler: Handler<AeronControlledFragmentAssembler>,
}
impl AeronControlledFragmentClosureAssembler {
pub fn new() -> Result<Self, AeronCError> {
let handler = Handler::leak(FnMutControlledMessageHandler::new());
Ok(Self {
assembler: AeronControlledFragmentAssembler::new(Some(&handler))?,
handler,
assembler_handler: Handler {
raw_ptr: std::ptr::null_mut(),
should_drop: false,
},
})
}
pub fn process<T>(
&mut self,
ctx: &mut T,
func: fn(&mut T, &[u8], AeronHeader) -> aeron_controlled_fragment_handler_action_t,
) -> Option<&Handler<AeronControlledFragmentAssembler>> {
self.handler.set(ctx, func);
self.assembler_handler.raw_ptr = &mut self.assembler as *mut _;
Some(&self.assembler_handler)
}
}
impl Drop for AeronControlledFragmentClosureAssembler {
fn drop(&mut self) {
self.handler.release();
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AeronStatusTracker {
last_status: Option<AeronStatus>,
}
impl AeronStatusTracker {
#[must_use]
pub const fn new() -> Self {
Self { last_status: None }
}
pub fn observe(&mut self, status: AeronStatus) -> Option<AeronStatus> {
if self.last_status != Some(status) {
self.last_status = Some(status);
Some(status)
} else {
None
}
}
pub fn reset(&mut self) {
self.last_status = None;
}
#[must_use]
pub const fn last_status(&self) -> Option<AeronStatus> {
self.last_status
}
}
impl Default for AeronStatusTracker {
fn default() -> Self {
Self::new()
}
}
pub fn validate_endpoint_for_aeron_udp(endpoint: &str) -> Result<(), AeronCError> {
if endpoint.is_empty() {
return Err(AeronCError::from_code(-1));
}
if endpoint.contains('?') || endpoint.contains('=') || endpoint.contains('/') {
return Err(AeronCError::from_code(-1));
}
if endpoint.starts_with('[') {
let end_bracket = endpoint
.find(']')
.ok_or_else(|| AeronCError::from_code(-1))?;
if end_bracket == 1 {
return Err(AeronCError::from_code(-1));
}
let host_part = &endpoint[1..end_bracket];
let after_bracket = &endpoint[end_bracket..];
let colon_offset = after_bracket
.find(':')
.ok_or_else(|| AeronCError::from_code(-1))?;
let colon_pos = end_bracket + colon_offset;
if colon_offset != 1 || colon_pos + 1 >= endpoint.len() {
return Err(AeronCError::from_code(-1));
}
let port_str = &endpoint[colon_pos + 1..];
validate_ipv6(host_part)?;
validate_port(port_str)?;
return Ok(());
}
let colon_pos = endpoint
.rfind(':')
.ok_or_else(|| AeronCError::from_code(-1))?;
if colon_pos == 0 || colon_pos + 1 >= endpoint.len() {
return Err(AeronCError::from_code(-1));
}
let host = &endpoint[..colon_pos];
let port_str = &endpoint[colon_pos + 1..];
validate_host(host)?;
validate_port(port_str)?;
Ok(())
}
fn validate_ipv6(addr: &str) -> Result<(), AeronCError> {
if addr.is_empty() {
return Err(AeronCError::from_code(-1));
}
let has_colon = addr.bytes().any(|b| b == b':');
if !has_colon {
return Err(AeronCError::from_code(-1));
}
for ch in addr.bytes() {
let is_hex = ch.is_ascii_hexdigit();
let is_colon = ch == b':';
if !(is_hex || is_colon) {
return Err(AeronCError::from_code(-1));
}
}
Ok(())
}
fn validate_host(host: &str) -> Result<(), AeronCError> {
if host.is_empty() {
return Err(AeronCError::from_code(-1));
}
let colon_count = host.bytes().filter(|&b| b == b':').count();
if colon_count > 1 {
return Err(AeronCError::from_code(-1));
}
if colon_count == 1 {
if host.contains("::") || host.starts_with(':') || host.ends_with(':') {
return Err(AeronCError::from_code(-1));
}
}
for ch in host.bytes() {
let is_alnum = ch.is_ascii_alphanumeric();
let is_safe = matches!(ch, b'-' | b'.' | b':');
if !(is_alnum || is_safe) {
return Err(AeronCError::from_code(-1));
}
}
Ok(())
}
fn validate_port(port: &str) -> Result<(), AeronCError> {
if port.is_empty() {
return Err(AeronCError::from_code(-1));
}
if !port.bytes().all(|b| b.is_ascii_digit()) {
return Err(AeronCError::from_code(-1));
}
let port_num = port
.parse::<u32>()
.map_err(|_| AeronCError::from_code(-1))?;
if port_num > 65535 {
return Err(AeronCError::from_code(-1));
}
Ok(())
}
#[cfg(test)]
mod aeron_custom_tests {
use super::*;
#[test]
fn position_to_result_positive_is_ok_position() {
assert_eq!(super::publication_position_to_result(0), Ok(0));
assert_eq!(super::publication_position_to_result(12_345), Ok(12_345));
}
#[test]
fn position_to_result_back_pressure_is_minus_two() {
let err = super::publication_position_to_result(-2).expect_err("-2 is an error");
assert!(err.is_back_pressured());
assert_eq!(err.kind(), AeronErrorType::PublicationBackPressured);
}
#[test]
fn position_to_result_closed_is_minus_four() {
let err = super::publication_position_to_result(-4).expect_err("-4 is an error");
assert_eq!(err.kind(), AeronErrorType::PublicationClosed);
}
#[test]
fn position_to_result_not_connected_is_minus_one() {
let err = super::publication_position_to_result(-1).expect_err("-1 is an error");
assert_eq!(err.code, -1);
assert_eq!(err.kind(), AeronErrorType::GenericError);
}
#[test]
fn position_to_result_unknown_negative_preserves_code() {
let err = super::publication_position_to_result(-99).expect_err("-99 is an error");
assert_eq!(err.code, -99);
assert_eq!(err.kind(), AeronErrorType::Unknown(-99));
}
#[test]
fn status_from_error_maps_back_pressure_and_closed() {
assert_eq!(
AeronStatus::from_error(&AeronCError::from_code(-2)),
Some(AeronStatus::BackPressured)
);
assert_eq!(
AeronStatus::from_error(&AeronCError::from_code(-4)),
Some(AeronStatus::Closed)
);
}
#[test]
fn status_from_error_returns_none_for_non_status_errors() {
assert_eq!(AeronStatus::from_error(&AeronCError::from_code(-5)), None);
}
#[test]
fn defused_claim_drops_without_invoking_ffi() {
let claim = AeronClaim {
claim: AeronBufferClaim::default(),
position: 7,
finalised: true,
};
assert_eq!(claim.position(), 7);
drop(claim);
}
#[test]
fn status_tracker_emits_on_first_observation() {
let mut tracker = AeronStatusTracker::new();
assert_eq!(
tracker.observe(AeronStatus::Disconnected),
Some(AeronStatus::Disconnected)
);
}
#[test]
fn status_tracker_emits_on_transition() {
let mut tracker = AeronStatusTracker::new();
tracker.observe(AeronStatus::Disconnected);
assert_eq!(
tracker.observe(AeronStatus::Connected),
Some(AeronStatus::Connected)
);
}
#[test]
fn status_tracker_does_not_emit_on_same_status() {
let mut tracker = AeronStatusTracker::new();
tracker.observe(AeronStatus::Connected);
assert_eq!(tracker.observe(AeronStatus::Connected), None);
assert_eq!(tracker.observe(AeronStatus::Connected), None);
}
#[test]
fn status_tracker_emits_again_after_reset() {
let mut tracker = AeronStatusTracker::new();
tracker.observe(AeronStatus::Connected);
assert_eq!(tracker.observe(AeronStatus::Connected), None);
tracker.reset();
assert_eq!(
tracker.observe(AeronStatus::Connected),
Some(AeronStatus::Connected)
);
}
#[test]
fn status_tracker_records_closed_transition() {
let mut tracker = AeronStatusTracker::new();
tracker.observe(AeronStatus::Connected);
assert_eq!(
tracker.observe(AeronStatus::Closed),
Some(AeronStatus::Closed)
);
assert_eq!(tracker.observe(AeronStatus::Closed), None);
}
#[test]
fn status_tracker_tracks_all_states() {
let mut tracker = AeronStatusTracker::new();
assert_eq!(
tracker.observe(AeronStatus::Disconnected),
Some(AeronStatus::Disconnected)
);
assert_eq!(
tracker.observe(AeronStatus::Connected),
Some(AeronStatus::Connected)
);
assert_eq!(
tracker.observe(AeronStatus::BackPressured),
Some(AeronStatus::BackPressured)
);
assert_eq!(
tracker.observe(AeronStatus::Connected),
Some(AeronStatus::Connected)
);
assert_eq!(
tracker.observe(AeronStatus::Closed),
Some(AeronStatus::Closed)
);
}
#[test]
fn validate_endpoint_accepts_hostname_with_port() {
assert!(validate_endpoint_for_aeron_udp("localhost:40123").is_ok());
assert!(validate_endpoint_for_aeron_udp("localhost:0").is_ok());
assert!(validate_endpoint_for_aeron_udp("my-host.example.com:65535").is_ok());
}
#[test]
fn validate_endpoint_accepts_ipv4_with_port() {
assert!(validate_endpoint_for_aeron_udp("127.0.0.1:8080").is_ok());
assert!(validate_endpoint_for_aeron_udp("192.168.1.1:0").is_ok());
assert!(validate_endpoint_for_aeron_udp("10.0.0.1:65535").is_ok());
}
#[test]
fn validate_endpoint_accepts_ipv6_bracketed_with_port() {
assert!(validate_endpoint_for_aeron_udp("[::1]:8080").is_ok());
assert!(validate_endpoint_for_aeron_udp("[fe80::1]:9000").is_ok());
assert!(validate_endpoint_for_aeron_udp("[2001:db8::1]:0").is_ok());
assert!(validate_endpoint_for_aeron_udp("[2001:db8::1]:65535").is_ok());
}
#[test]
fn validate_endpoint_rejects_empty() {
assert!(validate_endpoint_for_aeron_udp("").is_err());
}
#[test]
fn validate_endpoint_rejects_missing_port() {
assert!(validate_endpoint_for_aeron_udp("localhost").is_err());
assert!(validate_endpoint_for_aeron_udp("127.0.0.1").is_err());
assert!(validate_endpoint_for_aeron_udp("[::1]").is_err());
assert!(validate_endpoint_for_aeron_udp("[::1]:").is_err());
assert!(validate_endpoint_for_aeron_udp(":8080").is_err()); }
#[test]
fn validate_endpoint_rejects_port_out_of_range() {
assert!(validate_endpoint_for_aeron_udp("localhost:65536").is_err());
assert!(validate_endpoint_for_aeron_udp("localhost:99999").is_err());
assert!(validate_endpoint_for_aeron_udp("[::1]:65536").is_err());
}
#[test]
fn validate_endpoint_rejects_non_digit_port() {
assert!(validate_endpoint_for_aeron_udp("localhost:abc").is_err());
assert!(validate_endpoint_for_aeron_udp("localhost:80a0").is_err());
assert!(validate_endpoint_for_aeron_udp("localhost:-1").is_err());
assert!(validate_endpoint_for_aeron_udp("localhost:+8080").is_err());
}
#[test]
fn validate_endpoint_rejects_unsafe_characters() {
assert!(validate_endpoint_for_aeron_udp("localhost?foo:8080").is_err());
assert!(validate_endpoint_for_aeron_udp("localhost=bar:8080").is_err());
assert!(validate_endpoint_for_aeron_udp("localhost/path:8080").is_err());
assert!(validate_endpoint_for_aeron_udp("local_host:8080").is_err()); assert!(validate_endpoint_for_aeron_udp("local host:8080").is_err()); assert!(validate_endpoint_for_aeron_udp("local$host:8080").is_err()); }
#[test]
fn validate_endpoint_rejects_malformed_ipv6() {
assert!(validate_endpoint_for_aeron_udp("[::1:8080").is_err());
assert!(validate_endpoint_for_aeron_udp("[]:8080").is_err());
assert!(validate_endpoint_for_aeron_udp("[::1]").is_err());
assert!(validate_endpoint_for_aeron_udp("[::1]x:8080").is_err());
assert!(validate_endpoint_for_aeron_udp("[::1]8080").is_err());
}
#[test]
fn validate_endpoint_rejects_colon_in_wrong_position() {
assert!(validate_endpoint_for_aeron_udp("::1:8080").is_err());
assert!(validate_endpoint_for_aeron_udp(":8080").is_err());
}
#[test]
fn validate_endpoint_accepts_hyphenated_hostname() {
assert!(validate_endpoint_for_aeron_udp("my-host-name:8080").is_ok());
assert!(validate_endpoint_for_aeron_udp("host-1:40123").is_ok());
}
#[test]
fn validate_endpoint_accepts_dotted_hostname() {
assert!(validate_endpoint_for_aeron_udp("host.sub.example.com:8080").is_ok());
assert!(validate_endpoint_for_aeron_udp("a.b.c.d:40123").is_ok());
}
}