1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
//! Utilities for working with serde.
//!
//! Because serde is the underlying library used for data passing between
//! processes procspawn provides various utilities that help with common
//! operations.
use ipc_channel::ipc::IpcSharedMemory;
use serde::{de::Deserializer, de::Error, de::Visitor, ser::Serializer};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::sync::atomic::{AtomicBool, Ordering};
thread_local! {
static IN_PROCSPAWN: AtomicBool = AtomicBool::new(false);
}
struct ResetProcspawn(bool);
impl Drop for ResetProcspawn {
fn drop(&mut self) {
IN_PROCSPAWN.with(|in_procspawn| in_procspawn.swap(self.0, Ordering::Relaxed));
}
}
/// Internal helper to mark all serde calls that go across processes
/// so that serializers can respond to it.
pub fn with_ipc_mode<F: FnOnce() -> R, R>(f: F) -> R {
let old = IN_PROCSPAWN.with(|in_procspawn| in_procspawn.swap(true, Ordering::Relaxed));
let _dropper = ResetProcspawn(old);
f()
}
/// Checks if serde is in IPC mode.
///
/// This can be used to customize the serialization behavior of custom
/// types for IPC specific purposes. This is useful when a type has a regular
/// serialization/deserialization behavior but you want to use a cheaper one
/// when procspawn is used.
///
/// An example of this can be a type that abstracts over an mmap. It might want
/// to serialize the raw bytes under normal circumstances but for IPC purposes
/// might want to instead serialize the underlying file path and reopen the
/// mmap on the other side.
///
/// This function returns `true` whenever procspawn is attempting to serialize
/// and deserialize but never anytime else. Internally this is implemented as a
/// thread local.
pub fn in_ipc_mode() -> bool {
IN_PROCSPAWN.with(|in_procspawn| in_procspawn.load(Ordering::Relaxed))
}
/// A read-only byte buffer for sending between processes.
///
/// The buffer behind the scenes uses shared memory which is faster send
/// between processes than to serialize the raw bytes directly. It is however
/// read-only.
pub struct Shmem {
shmem: IpcSharedMemory,
}
impl fmt::Debug for Shmem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
struct ByteRepr<'a>(&'a Shmem);
impl<'a> fmt::Debug for ByteRepr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "b\"")?;
for &byte in self.0.as_bytes() {
if (32..127).contains(&byte) {
write!(f, "{}", byte)?;
} else {
write!(f, "\\x{:02x}", byte)?;
}
}
write!(f, "b\"")?;
Ok(())
}
}
f.debug_tuple("Shmem").field(&ByteRepr(self)).finish()
}
}
impl Shmem {
/// Creates a buffer from some bytes.
pub fn from_bytes(bytes: &[u8]) -> Shmem {
Shmem {
shmem: IpcSharedMemory::from_bytes(bytes),
}
}
/// Returns the bytes inside.
pub fn as_bytes(&self) -> &[u8] {
&self.shmem
}
}
impl std::ops::Deref for Shmem {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
&self.shmem
}
}
impl Serialize for Shmem {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if in_ipc_mode() {
self.shmem.serialize(serializer)
} else {
serializer.serialize_bytes(self.as_bytes())
}
}
}
struct ShmemVisitor;
impl<'de> Visitor<'de> for ShmemVisitor {
type Value = Shmem;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a borrowed byte array")
}
fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
where
E: Error,
{
Ok(Shmem {
shmem: IpcSharedMemory::from_bytes(v),
})
}
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: Error,
{
Ok(Shmem {
shmem: IpcSharedMemory::from_bytes(v.as_bytes()),
})
}
}
impl<'de> Deserialize<'de> for Shmem {
fn deserialize<D>(deserializer: D) -> Result<Shmem, D::Error>
where
D: Deserializer<'de>,
{
if in_ipc_mode() {
Ok(Shmem {
shmem: IpcSharedMemory::deserialize(deserializer)?,
})
} else {
deserializer.deserialize_bytes(ShmemVisitor)
}
}
}
#[cfg(feature = "json")]
pub use crate::json::Json;