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
// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::ffi::CStr;
use std::ffi::CString;

use libc::EINVAL;
use serde::Deserialize;
use serde::Serialize;

use crate::descriptor::AsRawDescriptor;
use crate::descriptor::SafeDescriptor;
use crate::Error;
use crate::RawDescriptor;
use crate::Result;

/// A shared memory file descriptor and its size.
#[derive(Debug, Deserialize, Serialize)]
pub struct SharedMemory {
    #[serde(with = "crate::with_as_descriptor")]
    pub descriptor: SafeDescriptor,
    pub size: u64,
}

pub(crate) trait PlatformSharedMemory {
    fn new(debug_name: &CStr, size: u64) -> Result<SharedMemory>;
    fn from_safe_descriptor(descriptor: SafeDescriptor, size: u64) -> Result<SharedMemory>;
}

impl SharedMemory {
    /// Creates a new shared memory object of the given size.
    ///
    /// |name| is purely for debugging purposes. It does not need to be unique, and it does
    /// not affect any non-debugging related properties of the constructed shared memory.
    pub fn new<T: Into<Vec<u8>>>(debug_name: T, size: u64) -> Result<SharedMemory> {
        let debug_name = CString::new(debug_name).map_err(|_| super::Error::new(EINVAL))?;
        <SharedMemory as PlatformSharedMemory>::new(&debug_name, size)
    }

    /// Gets the size in bytes of the shared memory.
    ///
    /// The size returned here does not reflect changes by other interfaces or users of the shared
    /// memory file descriptor.
    pub fn size(&self) -> u64 {
        self.size
    }

    /// Creates a SharedMemory instance from a SafeDescriptor owning a reference to a
    /// shared memory descriptor. Ownership of the underlying descriptor is transferred to the
    /// new SharedMemory object.
    pub fn from_safe_descriptor(descriptor: SafeDescriptor, size: u64) -> Result<SharedMemory> {
        <SharedMemory as PlatformSharedMemory>::from_safe_descriptor(descriptor, size)
    }

    /// Clones the SharedMemory. The new SharedMemory will refer to the same
    /// underlying object as the original.
    pub fn try_clone(&self) -> Result<SharedMemory> {
        Ok(SharedMemory {
            descriptor: self.descriptor.try_clone()?,
            size: self.size,
        })
    }
}

/// USE THIS CAUTIOUSLY. On Windows, the returned handle is not a file handle and cannot be used as
/// if it were one. It is a handle to a the associated file mapping object and should only be used
/// for memory-mapping the file view.
impl AsRawDescriptor for SharedMemory {
    fn as_raw_descriptor(&self) -> RawDescriptor {
        self.descriptor.as_raw_descriptor()
    }
}

impl From<SharedMemory> for SafeDescriptor {
    fn from(sm: SharedMemory) -> SafeDescriptor {
        sm.descriptor
    }
}

impl audio_streams::shm_streams::SharedMemory for SharedMemory {
    type Error = Error;

    fn anon(size: u64) -> Result<Self> {
        SharedMemory::new("shm_streams", size)
    }

    fn size(&self) -> u64 {
        self.size()
    }

    #[cfg(any(target_os = "android", target_os = "linux"))]
    fn as_raw_fd(&self) -> RawDescriptor {
        self.as_raw_descriptor()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn new_1024() {
        let shm = SharedMemory::new("test", 1024).expect("failed to create shared memory");
        assert_eq!(shm.size(), 1024);
    }

    #[test]
    fn new_1028() {
        let shm = SharedMemory::new("name", 1028).expect("failed to create shared memory");
        assert_eq!(shm.size(), 1028);
    }

    #[test]
    fn new_too_huge() {
        SharedMemory::new("test", 0x8000_0000_0000_0000)
            .expect_err("8 exabyte shared memory creation should fail");
    }
}