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
// include-flate // Copyright (C) SOFe // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! A variant of `include_bytes!`/`include_str!` with compile-time deflation and runtime lazy inflation. //! //! ## Why? //! `include_bytes!`/`include_str!` are great for embedding resources into an executable/library //! without involving the complex logistics of maintaining an assets manager. //! However, they are copied as-is into the artifact, leading to unnecessarily large binary size. //! This library automatically compresses the resources and lazily decompresses them at runtime, //! allowing smaller binary sizes. //! //! Nevertheless, this inevitably leads to wasting RAM to store both the compressed and decompressed data, //! which might be undesirable if the data are too large. //! An actual installer is still required if the binary involves too many resources that do not need to be kept in RAM all time. use libflate::deflate; /// The low-level macros used by this crate. pub use include_flate_codegen_exports as codegen; #[doc(hidden)] pub use lazy_static::lazy_static; /// This macro is like [`include_bytes!`][1] or [`include_str!`][2], but compresses at compile time /// and lazily decompresses at runtime. /// /// # Parameters /// The macro can be used like this: /// ```ignore /// flate!($meta $vis static $name: $type from $file); /// ``` /// /// - `$meta` is zero or more `#[...]` attributes that can be applied on the static parameters of /// `lazy_static`. For the actual semantics of the meta attributes, please refer to /// [`lazy_static`][3] documentation. /// - `$vis` is a visibility modifier (e.g. `pub`, `pub(crate)`) or empty. /// - `$name` is the name of the static variable.. /// - `$type` can be either `[u8]` or `str`. However, the actual type created would dereference /// into `Vec<u8>` and `String` (although they are `AsRef<[u8]>` and `AsRef<str>`) respectively. /// - `$file` is either an absolute path or a path relative to the current /// [`CARGO_MANIFEST_DIR`][4]. Note that **this is distinct from the behaviour of the builtin /// `include_bytes!`/`include_str!` macros** — `includle_bytes!`/`include_str!` paths are /// relative to the current source file, while `flate!` paths are relative to `CARGO_MANIFEST_DIR`. /// /// # Returns /// The macro expands to a [`lazy_static`][3] call, which lazily inflates the compressed bytes. /// /// # Compile errors /// - If the input format is incorrect /// - If the referenced file does not exist or is not readable /// - If `$type` is `str` but the file is not fully valid UTF-8 /// /// # Algorithm /// Compression and decompression use the DEFLATE algorithm from [`libflate`][5]. /// /// # Examples /// Below are some basic examples. For actual compiled examples, see the [`tests`][6] directory. /// /// ```ignore /// // This declares a `static VAR_NAME: impl Deref<Vec<u8>>` /// flate!(static VAR_NAME: [u8] from "binary-file.dat"); /// /// // This declares a `static VAR_NAME: impl Deref<String>` /// flate!(static VAR_NAME: str from "text-file.txt"); /// /// // Visibility modifiers can be added in the front /// flate!(pub static VAR_NAME: str from "public-file.txt"); /// /// // Meta attributes can also be added /// flate!(#[allow(unused)] /// #[doc = "Example const"] /// pub static VAR_NAME: str from "file.txt"); /// ``` /// /// [1]: https://doc.rust-lang.org/std/macro.include_bytes.html /// [2]: https://doc.rust-lang.org/std/macro.include_str.html /// [3]: https://docs.rs/lazy_static/1.3.0/lazy_static/ /// [4]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates /// [5]: https://docs.rs/libflate/0.1.26/libflate/ /// [6]: https://github.com/SOF3/include-flate/tree/master/tests #[macro_export] macro_rules! flate { ($(#[$meta:meta])* $(pub $(($($vis:tt)+))?)? static $name:ident: [u8] from $path:literal) => { $crate::lazy_static! { $(#[$meta])* $(pub $(($($vis)+))?)? static ref $name: ::std::vec::Vec<u8> = $crate::decode($crate::codegen::deflate_file!($path)); } }; ($(#[$meta:meta])* $(pub $(($($vis:tt)+))?)? static $name:ident: str from $path:literal) => { $crate::lazy_static! { $(#[$meta])* $(pub $(($($vis)+))?)? static ref $name: ::std::string::String = $crate::decode_string($crate::codegen::deflate_utf8_file!($path)); } }; } #[doc(hidden)] pub fn decode(bytes: &[u8]) -> Vec<u8> { use std::io::{Cursor, Read}; let mut dec = deflate::Decoder::new(Cursor::new(bytes)); let mut ret = Vec::new(); dec.read_to_end(&mut ret) .expect("Compiled DEFLATE buffer was corrupted"); ret } #[doc(hidden)] pub fn decode_string(bytes: &[u8]) -> String { // We should have checked for utf8 correctness in encode_utf8_file! String::from_utf8(decode(bytes)) .expect("flate_str has malformed UTF-8 despite checked at compile time") }