1use crate::data_buffer::DataBuffer;
2use crate::database::kvs::KeyValueStore;
3use crate::database::undo::UndoEntry;
4use crate::database::Database;
5use crate::file_metadata::FileMetadata;
6use crate::progress::ProgressCallback;
7use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
8use crate::string::{BnString, IntoCStr};
9use binaryninjacore_sys::{
10 BNCollaborationFreeSnapshotIdList, BNFreeSnapshot, BNFreeSnapshotList, BNGetSnapshotChildren,
11 BNGetSnapshotDatabase, BNGetSnapshotFileContents, BNGetSnapshotFileContentsHash,
12 BNGetSnapshotFirstParent, BNGetSnapshotId, BNGetSnapshotName, BNGetSnapshotParents,
13 BNGetSnapshotUndoData, BNGetSnapshotUndoEntries, BNGetSnapshotUndoEntriesWithProgress,
14 BNIsSnapshotAutoSave, BNNewSnapshotReference, BNReadSnapshotData,
15 BNReadSnapshotDataWithProgress, BNSetSnapshotName, BNSnapshot, BNSnapshotHasAncestor,
16 BNSnapshotHasContents, BNSnapshotHasUndo, BNSnapshotStoreData,
17};
18use std::ffi::c_void;
19use std::fmt;
20use std::fmt::{Debug, Display, Formatter};
21use std::ptr::NonNull;
22
23pub struct Snapshot {
24 pub(crate) handle: NonNull<BNSnapshot>,
25}
26
27impl Snapshot {
28 pub(crate) unsafe fn from_raw(handle: NonNull<BNSnapshot>) -> Self {
29 Self { handle }
30 }
31
32 pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNSnapshot>) -> Ref<Self> {
33 Ref::new(Self { handle })
34 }
35
36 pub fn database(&self) -> Ref<Database> {
38 unsafe {
39 Database::ref_from_raw(
40 NonNull::new(BNGetSnapshotDatabase(self.handle.as_ptr())).unwrap(),
41 )
42 }
43 }
44
45 pub fn id(&self) -> SnapshotId {
47 SnapshotId(unsafe { BNGetSnapshotId(self.handle.as_ptr()) })
48 }
49
50 pub fn name(&self) -> String {
52 unsafe { BnString::into_string(BNGetSnapshotName(self.handle.as_ptr())) }
53 }
54
55 pub fn set_name(&self, value: &str) {
57 let value_raw = value.to_cstr();
58 let value_ptr = value_raw.as_ptr();
59 unsafe { BNSetSnapshotName(self.handle.as_ptr(), value_ptr) }
60 }
61
62 pub fn is_auto_save(&self) -> bool {
64 unsafe { BNIsSnapshotAutoSave(self.handle.as_ptr()) }
65 }
66
67 pub fn has_contents(&self) -> bool {
69 unsafe { BNSnapshotHasContents(self.handle.as_ptr()) }
70 }
71
72 pub fn has_undo(&self) -> bool {
74 unsafe { BNSnapshotHasUndo(self.handle.as_ptr()) }
75 }
76
77 pub fn first_parent(&self) -> Option<Snapshot> {
79 let result = unsafe { BNGetSnapshotFirstParent(self.handle.as_ptr()) };
80 NonNull::new(result).map(|s| unsafe { Snapshot::from_raw(s) })
81 }
82
83 pub fn parents(&self) -> Array<Snapshot> {
85 let mut count = 0;
86 let result = unsafe { BNGetSnapshotParents(self.handle.as_ptr(), &mut count) };
87 assert!(!result.is_null());
88 unsafe { Array::new(result, count, ()) }
89 }
90
91 pub fn children(&self) -> Array<Snapshot> {
93 let mut count = 0;
94 let result = unsafe { BNGetSnapshotChildren(self.handle.as_ptr(), &mut count) };
95 assert!(!result.is_null());
96 unsafe { Array::new(result, count, ()) }
97 }
98
99 pub fn file_contents(&self) -> Option<DataBuffer> {
101 self.has_contents().then(|| unsafe {
102 let result = BNGetSnapshotFileContents(self.handle.as_ptr());
103 assert!(!result.is_null());
104 DataBuffer::from_raw(result)
105 })
106 }
107
108 pub fn file_contents_hash(&self) -> Option<DataBuffer> {
110 self.has_contents().then(|| unsafe {
111 let result = BNGetSnapshotFileContentsHash(self.handle.as_ptr());
112 assert!(!result.is_null());
113 DataBuffer::from_raw(result)
114 })
115 }
116
117 pub fn undo_entries(&self, file: &FileMetadata) -> Array<UndoEntry> {
119 assert!(self.has_undo());
120 let mut count = 0;
121 let result =
122 unsafe { BNGetSnapshotUndoEntries(self.handle.as_ptr(), file.handle, &mut count) };
123 assert!(!result.is_null());
124 unsafe { Array::new(result, count, ()) }
125 }
126
127 pub fn undo_entries_with_progress<P: ProgressCallback>(
128 &self,
129 file: &FileMetadata,
130 mut progress: P,
131 ) -> Array<UndoEntry> {
132 assert!(self.has_undo());
133 let mut count = 0;
134
135 let result = unsafe {
136 BNGetSnapshotUndoEntriesWithProgress(
137 self.handle.as_ptr(),
138 file.handle,
139 &mut progress as *mut P as *mut c_void,
140 Some(P::cb_progress_callback),
141 &mut count,
142 )
143 };
144
145 assert!(!result.is_null());
146 unsafe { Array::new(result, count, ()) }
147 }
148
149 pub fn read_data(&self) -> Ref<KeyValueStore> {
151 let result = unsafe { BNReadSnapshotData(self.handle.as_ptr()) };
152 unsafe { KeyValueStore::ref_from_raw(NonNull::new(result).unwrap()) }
153 }
154
155 pub fn read_data_with_progress<P: ProgressCallback>(
156 &self,
157 mut progress: P,
158 ) -> Ref<KeyValueStore> {
159 let result = unsafe {
160 BNReadSnapshotDataWithProgress(
161 self.handle.as_ptr(),
162 &mut progress as *mut P as *mut c_void,
163 Some(P::cb_progress_callback),
164 )
165 };
166
167 unsafe { KeyValueStore::ref_from_raw(NonNull::new(result).unwrap()) }
168 }
169
170 pub fn undo_data(&self) -> DataBuffer {
171 let result = unsafe { BNGetSnapshotUndoData(self.handle.as_ptr()) };
172 assert!(!result.is_null());
173 DataBuffer::from_raw(result)
174 }
175
176 pub fn store_data(&self, data: &KeyValueStore) -> bool {
177 unsafe {
178 BNSnapshotStoreData(
179 self.handle.as_ptr(),
180 data.handle.as_ptr(),
181 std::ptr::null_mut(),
182 None,
183 )
184 }
185 }
186
187 pub fn store_data_with_progress<P: ProgressCallback>(
188 &self,
189 data: &KeyValueStore,
190 mut progress: P,
191 ) -> bool {
192 unsafe {
193 BNSnapshotStoreData(
194 self.handle.as_ptr(),
195 data.handle.as_ptr(),
196 &mut progress as *mut P as *mut c_void,
197 Some(P::cb_progress_callback),
198 )
199 }
200 }
201
202 pub fn has_ancestor(self, other: &Snapshot) -> bool {
204 unsafe { BNSnapshotHasAncestor(self.handle.as_ptr(), other.handle.as_ptr()) }
205 }
206}
207
208impl Debug for Snapshot {
209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210 f.debug_struct("Snapshot")
211 .field("id", &self.id())
212 .field("name", &self.name())
213 .field("is_auto_save", &self.is_auto_save())
214 .field("has_contents", &self.has_contents())
215 .field("has_undo", &self.has_undo())
216 .field("children", &self.children().to_vec())
218 .finish()
219 }
220}
221
222impl ToOwned for Snapshot {
223 type Owned = Ref<Self>;
224
225 fn to_owned(&self) -> Self::Owned {
226 unsafe { RefCountable::inc_ref(self) }
227 }
228}
229
230unsafe impl RefCountable for Snapshot {
231 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
232 Ref::new(Self {
233 handle: NonNull::new(BNNewSnapshotReference(handle.handle.as_ptr())).unwrap(),
234 })
235 }
236
237 unsafe fn dec_ref(handle: &Self) {
238 BNFreeSnapshot(handle.handle.as_ptr());
239 }
240}
241
242impl CoreArrayProvider for Snapshot {
243 type Raw = *mut BNSnapshot;
244 type Context = ();
245 type Wrapped<'a> = Guard<'a, Snapshot>;
246}
247
248unsafe impl CoreArrayProviderInner for Snapshot {
249 unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
250 BNFreeSnapshotList(raw, count);
251 }
252
253 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
254 let raw_ptr = NonNull::new(*raw).unwrap();
255 Guard::new(Self::from_raw(raw_ptr), context)
256 }
257}
258
259#[repr(transparent)]
260#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
261pub struct SnapshotId(pub i64);
262
263impl Display for SnapshotId {
264 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
265 f.write_fmt(format_args!("{}", self.0))
266 }
267}
268
269impl CoreArrayProvider for SnapshotId {
270 type Raw = i64;
271 type Context = ();
272 type Wrapped<'a> = SnapshotId;
273}
274
275unsafe impl CoreArrayProviderInner for SnapshotId {
276 unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
277 BNCollaborationFreeSnapshotIdList(raw, count)
278 }
279
280 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
281 SnapshotId(*raw)
282 }
283}