Skip to main content

wasmer_package/
utils.rs

1#![allow(
2    clippy::result_large_err,
3    reason = "WasmerPackageError is large, but not often used"
4)]
5
6use bytes::{Buf, Bytes};
7use std::{
8    fs::File,
9    io::{BufRead, BufReader, Read, Seek},
10    path::Path,
11};
12use wasmer_types::Features;
13use webc::{Container, ContainerError, Version};
14
15use crate::package::{Package, WasmerPackageError};
16
17/// Check if something looks like a `*.tar.gz` file.
18fn is_tarball(mut file: impl Read + Seek) -> bool {
19    /// Magic bytes for a `*.tar.gz` file according to
20    /// [Wikipedia](https://en.wikipedia.org/wiki/List_of_file_signatures).
21    const TAR_GZ_MAGIC_BYTES: [u8; 2] = [0x1F, 0x8B];
22
23    let mut buffer = [0_u8; 2];
24    let result = match file.read_exact(&mut buffer) {
25        Ok(_) => buffer == TAR_GZ_MAGIC_BYTES,
26        Err(_) => false,
27    };
28
29    let _ = file.rewind();
30
31    result
32}
33
34pub fn from_disk(path: impl AsRef<Path>) -> Result<Container, WasmerPackageError> {
35    let path = path.as_ref();
36
37    if path.is_dir() {
38        return parse_dir(path);
39    }
40
41    let mut f = File::open(path).map_err(|error| ContainerError::Open {
42        error,
43        path: path.to_path_buf(),
44    })?;
45
46    if is_tarball(&mut f) {
47        return parse_tarball(BufReader::new(f));
48    }
49
50    match webc::detect(&mut f) {
51        Ok(Version::V2) => parse_v2_mmap(f).map_err(Into::into),
52        Ok(Version::V3) => parse_v3_mmap(f).map_err(Into::into),
53        Ok(other) => {
54            // fall back to the allocating generic version
55            let mut buffer = Vec::new();
56            f.rewind()
57                .and_then(|_| f.read_to_end(&mut buffer))
58                .map_err(|error| ContainerError::Read {
59                    path: path.to_path_buf(),
60                    error,
61                })?;
62
63            Container::from_bytes_and_version(buffer.into(), other).map_err(Into::into)
64        }
65        Err(e) => Err(ContainerError::Detect(e).into()),
66    }
67}
68
69/// Check if the data looks like a webc.
70pub fn is_container(bytes: &[u8]) -> bool {
71    is_tarball(std::io::Cursor::new(bytes)) || webc::detect(bytes).is_ok()
72}
73
74pub fn from_bytes(bytes: impl Into<Bytes>) -> Result<Container, WasmerPackageError> {
75    let bytes: Bytes = bytes.into();
76
77    if is_tarball(std::io::Cursor::new(&bytes)) {
78        return parse_tarball(bytes.reader());
79    }
80
81    let version = webc::detect(bytes.as_ref())?;
82    Container::from_bytes_and_version(bytes, version).map_err(Into::into)
83}
84
85#[allow(clippy::result_large_err)]
86fn parse_tarball(reader: impl BufRead) -> Result<Container, WasmerPackageError> {
87    let pkg = Package::from_tarball(reader)?;
88    Ok(Container::new(pkg))
89}
90
91#[allow(clippy::result_large_err)]
92fn parse_dir(path: &Path) -> Result<Container, WasmerPackageError> {
93    let wasmer_toml = path.join("wasmer.toml");
94    let pkg = Package::from_manifest(wasmer_toml)?;
95    Ok(Container::new(pkg))
96}
97
98#[allow(clippy::result_large_err)]
99fn parse_v2_mmap(f: File) -> Result<Container, ContainerError> {
100    // Note: OwnedReader::from_file() will automatically try to
101    // use a memory-mapped file when possible.
102    let webc = webc::v2::read::OwnedReader::from_file(f)?;
103    Ok(Container::new(webc))
104}
105
106#[allow(clippy::result_large_err)]
107fn parse_v3_mmap(f: File) -> Result<Container, ContainerError> {
108    // Note: OwnedReader::from_file() will automatically try to
109    // use a memory-mapped file when possible.
110    let webc = webc::v3::read::OwnedReader::from_file(f)?;
111    Ok(Container::new(webc))
112}
113
114/// Convert a `Features` object to a list of WebAssembly feature strings
115/// that can be used in annotations.
116///
117/// This maps each enabled feature to its corresponding string identifier
118/// used in the WebAssembly ecosystem.
119pub fn features_to_wasm_annotations(features: &Features) -> Vec<String> {
120    let mut feature_strings = Vec::new();
121
122    if features.simd {
123        feature_strings.push("simd".to_string());
124    }
125    if features.bulk_memory {
126        feature_strings.push("bulk-memory".to_string());
127    }
128    if features.reference_types {
129        feature_strings.push("reference-types".to_string());
130    }
131    if features.multi_value {
132        feature_strings.push("multi-value".to_string());
133    }
134    if features.threads {
135        feature_strings.push("threads".to_string());
136    }
137    if features.exceptions {
138        feature_strings.push("exception-handling".to_string());
139    }
140    if features.memory64 {
141        feature_strings.push("memory64".to_string());
142    }
143    if features.wide_arithmetic {
144        feature_strings.push("wide-arithmetic".to_string());
145    }
146    if features.tail_call {
147        feature_strings.push("tail-call".to_string());
148    }
149    if features.multi_memory {
150        feature_strings.push("multi-memory".to_string());
151    }
152    // Note: We don't currently include module_linking or extended_const in the feature strings.
153
154    feature_strings
155}
156
157/// Create a `Features` object from a list of WebAssembly feature strings.
158///
159/// This is the inverse of `features_to_wasm_annotations`, mapping string identifiers
160/// back to Features settings.
161pub fn wasm_annotations_to_features(feature_strings: &[String]) -> Features {
162    let mut features = Features::default();
163
164    // Initialize with default values
165    features
166        .simd(false)
167        .bulk_memory(false)
168        .reference_types(false)
169        .multi_value(false)
170        .threads(false)
171        .exceptions(false)
172        .memory64(false);
173
174    // Set features based on the string values
175    for feature in feature_strings {
176        match feature.as_str() {
177            "simd" => {
178                features.simd(true);
179            }
180            "bulk-memory" => {
181                features.bulk_memory(true);
182            }
183            "reference-types" => {
184                features.reference_types(true);
185            }
186            "multi-value" => {
187                features.multi_value(true);
188            }
189            "threads" => {
190                features.threads(true);
191            }
192            "exception-handling" => {
193                features.exceptions(true);
194            }
195            "memory64" => {
196                features.memory64(true);
197            }
198            "relaxed-simd" => {
199                features.relaxed_simd(true);
200            }
201            "wide-arithmetic" => {
202                features.wide_arithmetic(true);
203            }
204            "extended-const" => {
205                features.extended_const(true);
206            }
207            "tail-call" => {
208                features.tail_call(true);
209            }
210            "multi-memory" => {
211                features.multi_memory(true);
212            }
213            // Ignore unrecognized features
214            _ => {}
215        }
216    }
217
218    features
219}