Skip to content

Commit d3e377a

Browse files
committed
misc/io_utils.rs: add rust impl of atomic file save
1 parent e32beaa commit d3e377a

File tree

15 files changed

+260
-32
lines changed

15 files changed

+260
-32
lines changed

meson.build

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
project('mpv',
2-
'c',
2+
['c', 'rust'],
33
license: ['GPL2+', 'LGPL2.1+'],
44
version: files('./MPV_VERSION'),
55
meson_version: '>=1.3.0',
@@ -11,6 +11,8 @@ project('mpv',
1111
'cpp_std=c++20',
1212
'cpp_eh=default',
1313
'warning_level=2',
14+
'rust_std=2021',
15+
'build.rust_std=2021',
1416
]
1517
)
1618

@@ -1761,6 +1763,13 @@ major = client_h_define.split('|')[0].split('<<')[0].strip('() ')
17611763
minor = client_h_define.split('|')[1].strip('() ')
17621764
client_api_version = major + '.' + minor + '.0'
17631765

1766+
tempfile_dep = dependency('tempfile', fallback : ['tempfile', 'tempfile_dep'])
1767+
libmpv_rs = static_library('libmpv_rs',
1768+
'misc/io_utils.rs',
1769+
rust_abi: 'c',
1770+
dependencies: tempfile_dep
1771+
)
1772+
17641773
libmpv = library('mpv', sources, dependencies: dependencies, gnu_symbol_visibility: 'hidden',
17651774
link_args: cc.get_supported_link_arguments(['-Wl,-Bsymbolic']),
17661775
version: client_api_version, install: get_option('libmpv'),
@@ -1777,7 +1786,7 @@ if get_option('libmpv')
17771786
install_headers(headers, subdir: 'mpv')
17781787

17791788
# Allow projects to build with libmpv by cloning into ./subprojects/mpv
1780-
libmpv_dep = declare_dependency(link_with: libmpv)
1789+
libmpv_dep = declare_dependency(link_with: [libmpv, libmpv_rs])
17811790
meson.override_dependency('mpv', libmpv_dep)
17821791
endif
17831792

@@ -1810,7 +1819,7 @@ if get_option('cplayer')
18101819
install_data('etc/mpv-symbolic.svg', install_dir: join_paths(hicolor_dir, 'symbolic', 'apps'))
18111820

18121821
mpv = executable('mpv', main_fn_source, objects: libmpv.extract_all_objects(recursive: true), dependencies: dependencies,
1813-
win_subsystem: get_option('win32-subsystem'), install: true)
1822+
link_with: libmpv_rs, win_subsystem: get_option('win32-subsystem'), install: true)
18141823

18151824
if win32 and get_option('win32-subsystem') != 'console'
18161825
wrapper_sources= 'osdep/win32-console-wrapper.c'

misc/io_utils.c

-29
Original file line numberDiff line numberDiff line change
@@ -56,32 +56,3 @@ int mp_mkostemps(char *template, int suffixlen, int flags)
5656
errno = EEXIST;
5757
return -1;
5858
}
59-
60-
bool mp_save_to_file(const char *filepath, const void *data, size_t size)
61-
{
62-
assert(filepath && data && size);
63-
64-
bool result = false;
65-
char *tmp = talloc_asprintf(NULL, "%sXXXXXX", filepath);
66-
int fd = mp_mkostemps(tmp, 0, O_CLOEXEC);
67-
if (fd < 0)
68-
goto done;
69-
FILE *cache = fdopen(fd, "wb");
70-
if (!cache) {
71-
close(fd);
72-
unlink(tmp);
73-
goto done;
74-
}
75-
size_t written = fwrite(data, size, 1, cache);
76-
int ret = fclose(cache);
77-
if (written > 0 && !ret) {
78-
ret = rename(tmp, filepath);
79-
result = !ret;
80-
} else {
81-
unlink(tmp);
82-
}
83-
84-
done:
85-
talloc_free(tmp);
86-
return result;
87-
}

misc/io_utils.rs

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* I/O utility functions
3+
*
4+
* This file is part of mpv.
5+
*
6+
* mpv is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 2.1 of the License, or (at your option) any later version.
10+
*
11+
* mpv is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public
17+
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
use std::ffi::c_char;
21+
use std::ffi::CStr;
22+
use std::io::Write;
23+
use std::path::Path;
24+
use tempfile::NamedTempFile;
25+
26+
#[no_mangle]
27+
pub extern "C" fn mp_save_to_file(filepath: *const c_char, data: *const u8, size: usize) -> bool
28+
{
29+
if filepath.is_null() || data.is_null() || size == 0 {
30+
return false;
31+
}
32+
33+
let c_str = unsafe { CStr::from_ptr(filepath) };
34+
let target_path = match c_str.to_str() {
35+
Ok(path) => Path::new(path),
36+
Err(_) => return false,
37+
};
38+
39+
let dir = match target_path.parent() {
40+
Some(dir) => dir,
41+
None => return false,
42+
};
43+
44+
let filename = match target_path.file_name() {
45+
Some(name) => name,
46+
None => return false,
47+
};
48+
49+
let mut temp_file = match NamedTempFile::with_prefix_in(filename, dir) {
50+
Ok(file) => file,
51+
Err(_) => return false,
52+
};
53+
54+
// Write data to temporary file
55+
let written = temp_file.write(unsafe { std::slice::from_raw_parts(data, size) });
56+
if written.is_err() {
57+
return false;
58+
}
59+
60+
temp_file.persist(&target_path).is_ok()
61+
}

subprojects/cfg_if.wrap

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[wrap-file]
2+
directory = cfg-if-1.0.0
3+
source_url = https://crates.io/api/v1/crates/cfg-if/1.0.0/download
4+
source_filename = cfg-if-1.0.0.tar.gz
5+
source_hash = baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd
6+
patch_directory = cfg_if

subprojects/fastrand.wrap

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[wrap-file]
2+
directory = fastrand-2.3.0
3+
source_url = https://crates.io/api/v1/crates/fastrand/2.3.0/download
4+
source_filename = fastrand-2.3.0.tar.gz
5+
source_hash = 37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be
6+
patch_directory = fastrand

subprojects/once_cell.wrap

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[wrap-file]
2+
directory = once_cell-1.20.3
3+
source_url = https://crates.io/api/v1/crates/once_cell/1.20.3/download
4+
source_filename = once_cell-1.20.3.tar.gz
5+
source_hash = 945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e
6+
patch_directory = once_cell
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
project(
2+
'cfg_if',
3+
'rust',
4+
version: '1.0.0',
5+
license: 'MIT OR Apache-2.0',
6+
)
7+
8+
lib = static_library(
9+
'cfg_if',
10+
'src/lib.rs',
11+
override_options: ['rust_std=2021', 'build.rust_std=2021'],
12+
rust_abi: 'rust',
13+
native: true,
14+
)
15+
16+
cfg_if_dep = declare_dependency(
17+
link_with: [lib],
18+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
project(
2+
'fastrand',
3+
'rust',
4+
version: '2.3.0',
5+
license: 'MIT OR Apache-2.0',
6+
)
7+
8+
rust_args = [
9+
'--cfg', 'feature="std"',
10+
'--cfg', 'feature="alloc"',
11+
]
12+
13+
lib = static_library(
14+
'fastrand',
15+
'src/lib.rs',
16+
rust_args: rust_args,
17+
override_options: ['rust_std=2021', 'build.rust_std=2021'],
18+
rust_abi: 'rust',
19+
native: true,
20+
)
21+
22+
fastrand_dep = declare_dependency(
23+
link_with: [lib],
24+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
project(
2+
'once_cell',
3+
'rust',
4+
version: '1.20.3',
5+
license: 'MIT OR Apache-2.0',
6+
)
7+
8+
rust_args = [
9+
'--cfg', 'feature="std"',
10+
]
11+
12+
lib = static_library(
13+
'once_cell',
14+
'src/lib.rs',
15+
rust_args: rust_args,
16+
override_options: ['rust_std=2021', 'build.rust_std=2021'],
17+
rust_abi: 'rust',
18+
native: true,
19+
)
20+
21+
once_cell_dep = declare_dependency(
22+
link_with: [lib],
23+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
project(
2+
'tempfile',
3+
'rust',
4+
version: '3.17.1',
5+
license: 'MIT OR Apache-2.0',
6+
)
7+
8+
rust_args = [
9+
'-A', 'unused_variables',
10+
'--cfg', 'feature="std"',
11+
]
12+
13+
cfg_if_dep = dependency('cfg_if', fallback: ['cfg_if', 'cfg_if_dep'])
14+
once_cell_dep = dependency('once_cell', fallback: ['once_cell', 'once_cell_dep'])
15+
fastrand_dep = dependency('fastrand', fallback: ['fastrand', 'fastrand_dep'])
16+
17+
dependencies = [
18+
cfg_if_dep,
19+
fastrand_dep,
20+
once_cell_dep,
21+
]
22+
23+
if host_machine.system() == 'windows'
24+
dependencies += dependency('windows_sys', fallback: ['windows_sys', 'windows_sys_dep'])
25+
endif
26+
27+
lib = static_library(
28+
'tempfile',
29+
'src/lib.rs',
30+
rust_args: rust_args,
31+
override_options: ['rust_std=2021', 'build.rust_std=2021'],
32+
rust_abi: 'rust',
33+
native: true,
34+
dependencies: dependencies,
35+
)
36+
37+
tempfile_dep = declare_dependency(
38+
link_with: [lib],
39+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
project(
2+
'windows_sys',
3+
'rust',
4+
version: '0.59.0',
5+
license: 'MIT OR Apache-2.0',
6+
)
7+
8+
rust_args = [
9+
'--cfg', 'feature="Win32"',
10+
'--cfg', 'feature="Win32_Storage"',
11+
'--cfg', 'feature="Win32_Foundation"',
12+
'--cfg', 'feature="Win32_Storage_FileSystem"',
13+
]
14+
15+
windows_targets_dep = dependency('windows_targets', fallback: ['windows_targets', 'windows_targets_dep'])
16+
17+
lib = static_library(
18+
'windows_sys',
19+
'src/lib.rs',
20+
rust_args: rust_args,
21+
override_options: ['rust_std=2021', 'build.rust_std=2021'],
22+
rust_abi: 'rust',
23+
native: true,
24+
dependencies: [windows_targets_dep],
25+
)
26+
27+
windows_sys_dep = declare_dependency(
28+
link_with: [lib],
29+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
project(
2+
'windows_targets',
3+
'rust',
4+
version: '0.53.0',
5+
license: 'MIT OR Apache-2.0',
6+
)
7+
8+
lib = static_library(
9+
'windows_targets',
10+
'src/lib.rs',
11+
override_options: ['rust_std=2021', 'build.rust_std=2021'],
12+
rust_abi: 'rust',
13+
native: true,
14+
)
15+
16+
windows_targets_dep = declare_dependency(
17+
link_with: [lib],
18+
)

subprojects/tempfile.wrap

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[wrap-file]
2+
directory = tempfile-3.17.1
3+
source_url = https://crates.io/api/v1/crates/tempfile/3.17.1/download
4+
source_filename = tempfile-3.17.1.tar.gz
5+
source_hash = 22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230
6+
patch_directory = tempfile

subprojects/windows_sys.wrap

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[wrap-file]
2+
directory = windows-sys-0.59.0
3+
source_url = https://crates.io/api/v1/crates/windows-sys/0.59.0/download
4+
source_filename = windows-sys-0.59.0.tar.gz
5+
source_hash = 1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b
6+
patch_directory = windows_sys

subprojects/windows_targets.wrap

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[wrap-file]
2+
directory = windows-targets-0.53.0
3+
source_url = https://crates.io/api/v1/crates/windows-targets/0.53.0/download
4+
source_filename = windows-targets-0.53.0.tar.gz
5+
source_hash = b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b
6+
patch_directory = windows_targets

0 commit comments

Comments
 (0)