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
use std::mem;
use std::ops::{Deref, DerefMut};
use std::os::raw::c_void;
use std::slice;

use block::{Block, ConcreteBlock};

use objc::runtime::Object;
use objc::{class, msg_send, sel, sel_impl};
use objc_id::Id;

use crate::foundation::{id, to_bool, NSUInteger, BOOL, NO, YES};

/// Wrapper for a retained `NSData` object.
///
/// Supports constructing a new `NSData` from a `Vec<u8>`, wrapping and retaining an existing
/// pointer from the Objective-C side, and turning an `NSData` into a `Vec<u8>`.
///
/// This is an intentionally limited API.
#[derive(Debug)]
pub struct NSData(pub Id<Object>);

impl NSData {
    /// Given a vector of bytes, creates, retains, and returns a wrapped `NSData`.
    ///
    /// This method is borrowed straight out of [objc-foundation](objc-foundation) by the amazing
    /// Steven Sheldon, and just tweaked slightly to fit the desired API semantics here.
    ///
    /// [objc-foundation]: https://crates.io/crates/objc-foundation
    pub fn new(bytes: Vec<u8>) -> Self {
        let capacity = bytes.capacity();

        let dealloc = ConcreteBlock::new(move |bytes: *mut c_void, len: usize| unsafe {
            // Recreate the Vec and let it drop
            let _ = Vec::from_raw_parts(bytes as *mut u8, len, capacity);
        });
        let dealloc = dealloc.copy();
        let dealloc: &Block<(*mut c_void, usize), ()> = &dealloc;

        let mut bytes = bytes;
        let bytes_ptr = bytes.as_mut_ptr() as *mut c_void;

        unsafe {
            let obj: id = msg_send![class!(NSData), alloc];
            let obj: id = msg_send![obj, initWithBytesNoCopy:bytes_ptr
                                                             length:bytes.len()
                                                        deallocator:dealloc];
            mem::forget(bytes);
            NSData(Id::from_ptr(obj))
        }
    }

    /// Given a slice of bytes, creates, retains, and returns a wrapped `NSData`.
    ///
    /// This method is borrowed straight out of [objc-foundation](objc-foundation) by the amazing
    /// Steven Sheldon, and just tweaked slightly to fit the desired API semantics here.
    ///
    /// [objc-foundation]: https://crates.io/crates/objc-foundation
    pub fn with_slice(bytes: &[u8]) -> Self {
        let bytes_ptr = bytes.as_ptr() as *mut c_void;

        unsafe {
            let obj: id = msg_send![class!(NSData), dataWithBytes:bytes_ptr length:bytes.len()];
            NSData(Id::from_ptr(obj))
        }
    }

    /// Given a (presumably) `NSData`, wraps and retains it.
    pub fn retain(data: id) -> Self {
        NSData(unsafe { Id::from_ptr(data) })
    }

    /// If we're vended an NSData from a method (e.g, a push notification token) we might want to
    /// wrap it while we figure out what to do with it. This does that.
    pub fn from_retained(data: id) -> Self {
        NSData(unsafe { Id::from_retained_ptr(data) })
    }

    /// A helper method for determining if a given `NSObject` is an `NSData`.
    pub fn is(obj: id) -> bool {
        let result: BOOL = unsafe { msg_send![obj, isKindOfClass: class!(NSData)] };

        to_bool(result)
    }

    /// Returns the length of the underlying `NSData` bytes.
    pub fn len(&self) -> usize {
        unsafe {
            let x: NSUInteger = msg_send![&*self.0, length];
            x as usize
        }
    }

    /// Returns a reference to the underlying bytes for the wrapped `NSData`.
    ///
    /// This, like `NSData::new()`, is cribbed from [objc-foundation](objc-foundation).
    ///
    /// [objc-foundation](https://crates.io/crates/objc-foundation)
    pub fn bytes(&self) -> &[u8] {
        let ptr: *const c_void = unsafe { msg_send![&*self.0, bytes] };

        // The bytes pointer may be null for length zero
        let (ptr, len) = if ptr.is_null() {
            (0x1 as *const u8, 0)
        } else {
            (ptr as *const u8, self.len())
        };

        unsafe { slice::from_raw_parts(ptr, len) }
    }

    /// Creates a new Vec, copies the NSData (safely, but quickly) bytes into that Vec, and
    /// consumes the NSData enabling it to release (provided nothing in Cocoa is using it).
    ///
    // A point of discussion: I think this is the safest way to handle it, however I could be
    // wrong - it's a bit defensive but I can't think of a way to reliably return an owned set of
    // this data without messing up the Objective-C side of things. Thankfully this isn't used too
    // often, but still... open to ideas.
    pub fn into_vec(self) -> Vec<u8> {
        let mut data = Vec::new();

        let bytes = self.bytes();
        data.resize(bytes.len(), 0);
        data.copy_from_slice(bytes);

        data
    }
}

impl From<NSData> for id {
    /// Consumes and returns the underlying `NSData`.
    fn from(mut data: NSData) -> Self {
        &mut *data.0
    }
}

impl Deref for NSData {
    type Target = Object;

    /// Derefs to the underlying Objective-C Object.
    fn deref(&self) -> &Object {
        &*self.0
    }
}

impl DerefMut for NSData {
    /// Derefs to the underlying Objective-C Object.
    fn deref_mut(&mut self) -> &mut Object {
        &mut *self.0
    }
}