Skip to content

[PoC] misc/io_utils.rs: add rust impl of atomic file save #15934

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions meson.build
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
project('mpv',
'c',
['c', 'rust'],
license: ['GPL2+', 'LGPL2.1+'],
version: files('./MPV_VERSION'),
meson_version: '>=1.3.0',
Expand All @@ -11,6 +11,8 @@ project('mpv',
'cpp_std=c++20',
'cpp_eh=default',
'warning_level=2',
'rust_std=2021',
'build.rust_std=2021',
]
)

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

tempfile_dep = dependency('tempfile', fallback : ['tempfile', 'tempfile_dep'])
libmpv_rs = static_library('libmpv_rs',
'misc/io_utils.rs',
rust_abi: 'c',
dependencies: tempfile_dep
)

libmpv = library('mpv', sources, dependencies: dependencies, gnu_symbol_visibility: 'hidden',
link_args: cc.get_supported_link_arguments(['-Wl,-Bsymbolic']),
version: client_api_version, install: get_option('libmpv'),
Expand All @@ -1777,7 +1786,7 @@ if get_option('libmpv')
install_headers(headers, subdir: 'mpv')

# Allow projects to build with libmpv by cloning into ./subprojects/mpv
libmpv_dep = declare_dependency(link_with: libmpv)
libmpv_dep = declare_dependency(link_with: [libmpv, libmpv_rs])
meson.override_dependency('mpv', libmpv_dep)
endif

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

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

if win32 and get_option('win32-subsystem') != 'console'
wrapper_sources= 'osdep/win32-console-wrapper.c'
Expand Down
29 changes: 0 additions & 29 deletions misc/io_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,32 +56,3 @@ int mp_mkostemps(char *template, int suffixlen, int flags)
errno = EEXIST;
return -1;
}

bool mp_save_to_file(const char *filepath, const void *data, size_t size)
{
assert(filepath && data && size);

bool result = false;
char *tmp = talloc_asprintf(NULL, "%sXXXXXX", filepath);
int fd = mp_mkostemps(tmp, 0, O_CLOEXEC);
if (fd < 0)
goto done;
FILE *cache = fdopen(fd, "wb");
if (!cache) {
close(fd);
unlink(tmp);
goto done;
}
size_t written = fwrite(data, size, 1, cache);
int ret = fclose(cache);
if (written > 0 && !ret) {
ret = rename(tmp, filepath);
result = !ret;
} else {
unlink(tmp);
}

done:
talloc_free(tmp);
return result;
}
61 changes: 61 additions & 0 deletions misc/io_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* I/O utility functions
*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/

use std::ffi::c_char;
use std::ffi::CStr;
use std::io::Write;
use std::path::Path;
use tempfile::NamedTempFile;

#[no_mangle]
pub extern "C" fn mp_save_to_file(filepath: *const c_char, data: *const u8, size: usize) -> bool
{
if filepath.is_null() || data.is_null() || size == 0 {
return false;
}

let c_str = unsafe { CStr::from_ptr(filepath) };
let target_path = match c_str.to_str() {
Ok(path) => Path::new(path),
Err(_) => return false,
};

let dir = match target_path.parent() {
Some(dir) => dir,
None => return false,
};

let filename = match target_path.file_name() {
Some(name) => name,
None => return false,
};

let mut temp_file = match NamedTempFile::with_prefix_in(filename, dir) {
Ok(file) => file,
Err(_) => return false,
};

// Write data to temporary file
let written = temp_file.write(unsafe { std::slice::from_raw_parts(data, size) });
if written.is_err() {
return false;
}

temp_file.persist(&target_path).is_ok()
}
6 changes: 6 additions & 0 deletions subprojects/cfg_if.wrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[wrap-file]
directory = cfg-if-1.0.0
source_url = https://crates.io/api/v1/crates/cfg-if/1.0.0/download
source_filename = cfg-if-1.0.0.tar.gz
source_hash = baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd
patch_directory = cfg_if
6 changes: 6 additions & 0 deletions subprojects/fastrand.wrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[wrap-file]
directory = fastrand-2.3.0
source_url = https://crates.io/api/v1/crates/fastrand/2.3.0/download
source_filename = fastrand-2.3.0.tar.gz
source_hash = 37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be
patch_directory = fastrand
6 changes: 6 additions & 0 deletions subprojects/once_cell.wrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[wrap-file]
directory = once_cell-1.20.3
source_url = https://crates.io/api/v1/crates/once_cell/1.20.3/download
source_filename = once_cell-1.20.3.tar.gz
source_hash = 945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e
patch_directory = once_cell
18 changes: 18 additions & 0 deletions subprojects/packagefiles/cfg_if/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
project(
'cfg_if',
'rust',
version: '1.0.0',
license: 'MIT OR Apache-2.0',
)

lib = static_library(
'cfg_if',
'src/lib.rs',
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
native: true,
)

cfg_if_dep = declare_dependency(
link_with: [lib],
)
24 changes: 24 additions & 0 deletions subprojects/packagefiles/fastrand/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
project(
'fastrand',
'rust',
version: '2.3.0',
license: 'MIT OR Apache-2.0',
)

rust_args = [
'--cfg', 'feature="std"',
'--cfg', 'feature="alloc"',
]

lib = static_library(
'fastrand',
'src/lib.rs',
rust_args: rust_args,
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
native: true,
)

fastrand_dep = declare_dependency(
link_with: [lib],
)
23 changes: 23 additions & 0 deletions subprojects/packagefiles/once_cell/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
project(
'once_cell',
'rust',
version: '1.20.3',
license: 'MIT OR Apache-2.0',
)

rust_args = [
'--cfg', 'feature="std"',
]

lib = static_library(
'once_cell',
'src/lib.rs',
rust_args: rust_args,
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
native: true,
)

once_cell_dep = declare_dependency(
link_with: [lib],
)
39 changes: 39 additions & 0 deletions subprojects/packagefiles/tempfile/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
project(
'tempfile',
'rust',
version: '3.17.1',
license: 'MIT OR Apache-2.0',
)

rust_args = [
'-A', 'unused_variables',
'--cfg', 'feature="std"',
]

cfg_if_dep = dependency('cfg_if', fallback: ['cfg_if', 'cfg_if_dep'])
once_cell_dep = dependency('once_cell', fallback: ['once_cell', 'once_cell_dep'])
fastrand_dep = dependency('fastrand', fallback: ['fastrand', 'fastrand_dep'])

dependencies = [
cfg_if_dep,
fastrand_dep,
once_cell_dep,
]

if host_machine.system() == 'windows'
dependencies += dependency('windows_sys', fallback: ['windows_sys', 'windows_sys_dep'])
endif

lib = static_library(
'tempfile',
'src/lib.rs',
rust_args: rust_args,
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
native: true,
dependencies: dependencies,
)

tempfile_dep = declare_dependency(
link_with: [lib],
)
29 changes: 29 additions & 0 deletions subprojects/packagefiles/windows_sys/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
project(
'windows_sys',
'rust',
version: '0.59.0',
license: 'MIT OR Apache-2.0',
)

rust_args = [
'--cfg', 'feature="Win32"',
'--cfg', 'feature="Win32_Storage"',
'--cfg', 'feature="Win32_Foundation"',
'--cfg', 'feature="Win32_Storage_FileSystem"',
]

windows_targets_dep = dependency('windows_targets', fallback: ['windows_targets', 'windows_targets_dep'])

lib = static_library(
'windows_sys',
'src/lib.rs',
rust_args: rust_args,
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
native: true,
dependencies: [windows_targets_dep],
)

windows_sys_dep = declare_dependency(
link_with: [lib],
)
18 changes: 18 additions & 0 deletions subprojects/packagefiles/windows_targets/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
project(
'windows_targets',
'rust',
version: '0.53.0',
license: 'MIT OR Apache-2.0',
)

lib = static_library(
'windows_targets',
'src/lib.rs',
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
native: true,
)

windows_targets_dep = declare_dependency(
link_with: [lib],
)
6 changes: 6 additions & 0 deletions subprojects/tempfile.wrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[wrap-file]
directory = tempfile-3.17.1
source_url = https://crates.io/api/v1/crates/tempfile/3.17.1/download
source_filename = tempfile-3.17.1.tar.gz
source_hash = 22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230
patch_directory = tempfile
6 changes: 6 additions & 0 deletions subprojects/windows_sys.wrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[wrap-file]
directory = windows-sys-0.59.0
source_url = https://crates.io/api/v1/crates/windows-sys/0.59.0/download
source_filename = windows-sys-0.59.0.tar.gz
source_hash = 1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b
patch_directory = windows_sys
6 changes: 6 additions & 0 deletions subprojects/windows_targets.wrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[wrap-file]
directory = windows-targets-0.53.0
source_url = https://crates.io/api/v1/crates/windows-targets/0.53.0/download
source_filename = windows-targets-0.53.0.tar.gz
source_hash = b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b
patch_directory = windows_targets
Loading