cstr-0.2.12/.cargo_vcs_info.json0000644000000001360000000000100120710ustar { "git": { "sha1": "306279e3fcd669293da795880f59a01f706eebd7" }, "path_in_vcs": "" }cstr-0.2.12/.github/workflows/ci.yaml000064400000000000000000000036151046102023000155420ustar 00000000000000on: [push, pull_request] name: CI jobs: check: name: Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - uses: actions-rs/cargo@v1 with: command: check args: --tests build_no_std: name: Build on no_std runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable target: thumbv7em-none-eabi override: true - uses: actions-rs/cargo@v1 with: command: build args: >- --verbose --target thumbv7em-none-eabi --manifest-path tests/test_no_std/Cargo.toml test: name: Test Suite runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - uses: actions-rs/cargo@v1 with: command: test fmt: name: Rustfmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - run: rustup component add rustfmt - uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check clippy: name: Clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - run: rustup component add clippy - uses: actions-rs/cargo@v1 with: command: clippy args: --tests -- -D warnings cstr-0.2.12/.gitignore000064400000000000000000000000601046102023000126450ustar 00000000000000 /target/ /macros/target/ **/*.rs.bk Cargo.lock cstr-0.2.12/Cargo.toml0000644000000017360000000000100100760ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.64" name = "cstr" version = "0.2.12" authors = ["Xidorn Quan "] description = "Macro for building static CStr reference" readme = "README.md" keywords = [ "macro", "cstr", ] license = "MIT" repository = "https://github.com/upsuper/cstr" [lib] proc-macro = true [dependencies.proc-macro2] version = "1" [dependencies.quote] version = "1" [dev-dependencies.trybuild] version = "1.0.30" [badges.travis-ci] branch = "master" repository = "upsuper/cstr" cstr-0.2.12/Cargo.toml.orig000064400000000000000000000007371046102023000135570ustar 00000000000000[package] name = "cstr" version = "0.2.12" authors = ["Xidorn Quan "] description = "Macro for building static CStr reference" repository = "https://github.com/upsuper/cstr" license = "MIT" keywords = ["macro", "cstr"] readme = "README.md" edition = "2021" rust-version = "1.64" [lib] proc-macro = true [badges] travis-ci = { repository = "upsuper/cstr", branch = "master" } [dependencies] proc-macro2 = "1" quote = "1" [dev-dependencies] trybuild = "1.0.30" cstr-0.2.12/LICENSE000064400000000000000000000020441046102023000116660ustar 00000000000000Copyright (c) 2018-2020 Xidorn Quan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cstr-0.2.12/README.md000064400000000000000000000024421046102023000121420ustar 00000000000000# cstr [![CI](https://github.com/upsuper/cstr/workflows/CI/badge.svg)](https://github.com/upsuper/cstr/actions) [![Crates.io](https://img.shields.io/crates/v/cstr.svg)](https://crates.io/crates/cstr) [![Docs](https://docs.rs/cstr/badge.svg)](https://docs.rs/cstr) **This crate has been deprecated. Rust 1.77.0 stabilized [C-string literals][c-string-literal]. From that version, `c"abc"` can be used in place of `cstr!("abc")` provided by this crate. This new feature gives more concise code and faster compilation. Hence, this crate will no longer be maintained.** [c-string-literal]: https://blog.rust-lang.org/2024/03/21/Rust-1.77.0.html#c-string-literals A macro for getting `&'static CStr` from literal or identifier. This macro checks whether the given literal is valid for `CStr` at compile time, and returns a static reference of `CStr`. This macro can be used to initialize constants. ## Example ```rust use cstr::cstr; use std::ffi::CStr; let test = cstr!(b"hello\xff"); assert_eq!(test, CStr::from_bytes_with_nul(b"hello\xff\0").unwrap()); let test = cstr!("hello"); assert_eq!(test, CStr::from_bytes_with_nul(b"hello\0").unwrap()); let test = cstr!(hello); assert_eq!(test, CStr::from_bytes_with_nul(b"hello\0").unwrap()); ``` cstr-0.2.12/src/lib.rs000064400000000000000000000043441046102023000125710ustar 00000000000000//! **This crate has been deprecated. //! Rust 1.77.0 stabilized [C-string literals][c-string-literal]. //! From that version, `c"abc"` can be used in place of `cstr!("abc")` provided by this crate. //! This new feature gives more concise code and faster compilation. //! Hence, this crate will no longer be maintained.** //! //! [c-string-literal]: https://blog.rust-lang.org/2024/03/21/Rust-1.77.0.html#c-string-literals //! //! A macro for getting `&'static CStr` from literal or identifier. //! //! This macro checks whether the given literal is valid for `CStr` //! at compile time, and returns a static reference of `CStr`. //! //! This macro can be used to initialize constants. //! //! ## Example //! //! ``` //! use cstr::cstr; //! use std::ffi::CStr; //! //! let test = cstr!(b"hello\xff"); //! assert_eq!(test, CStr::from_bytes_with_nul(b"hello\xff\0").unwrap()); //! let test = cstr!("hello"); //! assert_eq!(test, CStr::from_bytes_with_nul(b"hello\0").unwrap()); //! let test = cstr!(hello); //! assert_eq!(test, CStr::from_bytes_with_nul(b"hello\0").unwrap()); //! ``` // While this isn't necessary when using Cargo >= 1.42, omitting it actually requires path-less // `--extern proc_macro` to be passed to `rustc` when building this crate. Some tools may not do // this correctly. So it's added as a precaution. extern crate proc_macro; use crate::parse::parse_input; use proc_macro::TokenStream as RawTokenStream; use proc_macro2::{Literal, Span, TokenStream}; use quote::{quote, quote_spanned}; use std::ffi::CString; mod parse; struct Error(Span, &'static str); #[proc_macro] pub fn cstr(input: RawTokenStream) -> RawTokenStream { let tokens = match build_byte_str(input.into()) { Ok(s) => quote!(unsafe { ::core::ffi::CStr::from_bytes_with_nul_unchecked(#s) }), Err(Error(span, msg)) => quote_spanned!(span => compile_error!(#msg)), }; tokens.into() } fn build_byte_str(input: TokenStream) -> Result { let (bytes, span) = parse_input(input)?; match CString::new(bytes) { Ok(s) => { let mut lit = Literal::byte_string(s.as_bytes_with_nul()); lit.set_span(span); Ok(lit) } Err(_) => Err(Error(span, "nul byte found in the literal")), } } cstr-0.2.12/src/parse.rs000064400000000000000000000157161046102023000131420ustar 00000000000000use crate::Error; use proc_macro2::{Delimiter, Ident, Literal, Span, TokenStream, TokenTree}; use std::char; macro_rules! unexpected_content { () => { "expected one of: byte string literal, string literal, identifier" }; } pub(crate) fn parse_input(mut input: TokenStream) -> Result<(Vec, Span), Error> { loop { let mut tokens = input.into_iter(); let token = match tokens.next() { Some(token) => token, None => { return Err(Error( Span::call_site(), concat!("unexpected end of input, ", unexpected_content!()), )) } }; let span = token.span(); let result = match token { // Unwrap any empty group which may be created from macro expansion. TokenTree::Group(group) if group.delimiter() == Delimiter::None => Err(group), TokenTree::Literal(literal) => match parse_literal(literal) { Ok(result) => Ok(result), Err(msg) => return Err(Error(span, msg)), }, TokenTree::Ident(ident) => Ok(parse_ident(ident)), _ => return Err(Error(span, unexpected_content!())), }; if let Some(token) = tokens.next() { return Err(Error(token.span(), "unexpected token")); } match result { Ok(result) => return Ok((result, span)), Err(group) => input = group.stream(), } } } fn parse_literal(literal: Literal) -> Result, &'static str> { let s = literal.to_string(); let s = s.as_bytes(); match s[0] { b'"' => Ok(parse_cooked_content(s)), b'r' => Ok(parse_raw_content(&s[1..])), b'b' => match s[1] { b'"' => Ok(parse_cooked_content(&s[1..])), b'r' => Ok(parse_raw_content(&s[2..])), _ => Err(unexpected_content!()), }, _ => Err(unexpected_content!()), } } fn all_pounds(bytes: &[u8]) -> bool { bytes.iter().all(|b| *b == b'#') } /// Parses raw string / bytes content after `r` prefix. fn parse_raw_content(s: &[u8]) -> Vec { let q_start = s.iter().position(|b| *b == b'"').unwrap(); let q_end = s.iter().rposition(|b| *b == b'"').unwrap(); assert!(all_pounds(&s[0..q_start])); assert!(all_pounds(&s[q_end + 1..q_end + q_start + 1])); Vec::from(&s[q_start + 1..q_end]) } /// Parses the cooked string / bytes content within quotes. fn parse_cooked_content(mut s: &[u8]) -> Vec { s = &s[1..s.iter().rposition(|b| *b == b'"').unwrap()]; let mut result = Vec::new(); while !s.is_empty() { match s[0] { b'\\' => {} b'\r' => { assert_eq!(s[1], b'\n'); result.push(b'\n'); s = &s[2..]; continue; } b => { result.push(b); s = &s[1..]; continue; } } let b = s[1]; s = &s[2..]; match b { b'x' => { let (b, rest) = backslash_x(s); result.push(b); s = rest; } b'u' => { let (c, rest) = backslash_u(s); result.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes()); s = rest; } b'n' => result.push(b'\n'), b'r' => result.push(b'\r'), b't' => result.push(b'\t'), b'\\' => result.push(b'\\'), b'0' => result.push(b'\0'), b'\'' => result.push(b'\''), b'"' => result.push(b'"'), b'\r' | b'\n' => { let next = s.iter().position(|b| { let ch = char::from_u32(u32::from(*b)).unwrap(); !ch.is_whitespace() }); match next { Some(pos) => s = &s[pos..], None => s = b"", } } b => panic!("unexpected byte {:?} after \\", b), } } result } fn backslash_x(s: &[u8]) -> (u8, &[u8]) { let ch = hex_to_u8(s[0]) * 0x10 + hex_to_u8(s[1]); (ch, &s[2..]) } fn hex_to_u8(b: u8) -> u8 { match b { b'0'..=b'9' => b - b'0', b'a'..=b'f' => b - b'a' + 10, b'A'..=b'F' => b - b'A' + 10, _ => unreachable!("unexpected non-hex character {:?} after \\x", b), } } fn backslash_u(s: &[u8]) -> (char, &[u8]) { assert_eq!(s[0], b'{'); let end = s[1..].iter().position(|b| *b == b'}').unwrap(); let mut ch = 0; for b in &s[1..=end] { ch *= 0x10; ch += u32::from(hex_to_u8(*b)); } (char::from_u32(ch).unwrap(), &s[end + 2..]) } fn parse_ident(ident: Ident) -> Vec { ident.to_string().into_bytes() } #[cfg(test)] mod tests { use super::*; use std::str::FromStr; // Tests below were modified from // https://github.com/dtolnay/syn/blob/cd5fdc0f530f822446fccaf831669cd0cf4a0fc9/tests/test_lit.rs fn lit(s: &str) -> Vec { match TokenStream::from_str(s) .unwrap() .into_iter() .next() .unwrap() { TokenTree::Literal(lit) => parse_literal(lit).unwrap(), _ => panic!(), } } #[test] fn strings() { #[track_caller] fn test_string(s: &str, value: &[u8]) { assert_eq!(lit(s), value); } test_string("\"a\"", b"a"); test_string("\"\\n\"", b"\n"); test_string("\"\\r\"", b"\r"); test_string("\"\\t\"", b"\t"); test_string("\"🐕\"", b"\xf0\x9f\x90\x95"); // NOTE: This is an emoji test_string("\"\\\"\"", b"\""); test_string("\"'\"", b"'"); test_string("\"\"", b""); test_string("\"\\u{1F415}\"", b"\xf0\x9f\x90\x95"); test_string( "\"contains\nnewlines\\\nescaped newlines\"", b"contains\nnewlinesescaped newlines", ); test_string("r\"raw\nstring\\\nhere\"", b"raw\nstring\\\nhere"); test_string("\"...\"q", b"..."); test_string("r\"...\"q", b"..."); test_string("r##\"...\"##q", b"..."); } #[test] fn byte_strings() { #[track_caller] fn test_byte_string(s: &str, value: &[u8]) { assert_eq!(lit(s), value); } test_byte_string("b\"a\"", b"a"); test_byte_string("b\"\\n\"", b"\n"); test_byte_string("b\"\\r\"", b"\r"); test_byte_string("b\"\\t\"", b"\t"); test_byte_string("b\"\\\"\"", b"\""); test_byte_string("b\"'\"", b"'"); test_byte_string("b\"\"", b""); test_byte_string( "b\"contains\nnewlines\\\nescaped newlines\"", b"contains\nnewlinesescaped newlines", ); test_byte_string("br\"raw\nstring\\\nhere\"", b"raw\nstring\\\nhere"); test_byte_string("b\"...\"q", b"..."); test_byte_string("br\"...\"q", b"..."); test_byte_string("br##\"...\"##q", b"..."); } } cstr-0.2.12/tests/clippy_lints.rs000064400000000000000000000004271046102023000151050ustar 00000000000000use cstr::cstr; use std::ffi::CStr; #[test] #[deny(clippy::transmute_ptr_to_ref)] fn deny_transmute_ptr_to_ref() { let s: &'static CStr = cstr!("foo\u{4e00}bar"); let expected = b"foo\xe4\xb8\x80bar\0"; assert_eq!(s, CStr::from_bytes_with_nul(expected).unwrap()); } cstr-0.2.12/tests/compile_fail/empty.rs000064400000000000000000000000671046102023000161550ustar 00000000000000use cstr::cstr; fn main() { let _foo = cstr!(); } cstr-0.2.12/tests/compile_fail/empty.stderr000064400000000000000000000004531046102023000170330ustar 00000000000000error: unexpected end of input, expected one of: byte string literal, string literal, identifier --> $DIR/empty.rs:4:16 | 4 | let _foo = cstr!(); | ^^^^^^^ | = note: this error originates in the macro `cstr` (in Nightly builds, run with -Z macro-backtrace for more info) cstr-0.2.12/tests/compile_fail/interior-nul.rs000064400000000000000000000001011046102023000174330ustar 00000000000000use cstr::cstr; fn main() { let _foo = cstr!("foo\0bar"); } cstr-0.2.12/tests/compile_fail/interior-nul.stderr000064400000000000000000000002221046102023000203160ustar 00000000000000error: nul byte found in the literal --> $DIR/interior-nul.rs:4:22 | 4 | let _foo = cstr!("foo\0bar"); | ^^^^^^^^^^ cstr-0.2.12/tests/compile_fail/non-str.rs000064400000000000000000000001571046102023000164170ustar 00000000000000use cstr::cstr; fn main() { let _foo = cstr!(1); let _foo = cstr!(("a")); let _foo = cstr!(&1); } cstr-0.2.12/tests/compile_fail/non-str.stderr000064400000000000000000000007451046102023000173010ustar 00000000000000error: expected one of: byte string literal, string literal, identifier --> $DIR/non-str.rs:4:22 | 4 | let _foo = cstr!(1); | ^ error: expected one of: byte string literal, string literal, identifier --> $DIR/non-str.rs:5:22 | 5 | let _foo = cstr!(("a")); | ^^^^^ error: expected one of: byte string literal, string literal, identifier --> $DIR/non-str.rs:6:22 | 6 | let _foo = cstr!(&1); | ^ cstr-0.2.12/tests/compile_fail/trash-after.rs000064400000000000000000000001041046102023000172270ustar 00000000000000use cstr::cstr; fn main() { let _foo = cstr!("foo" + "bar"); } cstr-0.2.12/tests/compile_fail/trash-after.stderr000064400000000000000000000002041046102023000201070ustar 00000000000000error: unexpected token --> $DIR/trash-after.rs:4:28 | 4 | let _foo = cstr!("foo" + "bar"); | ^ cstr-0.2.12/tests/compile_test.rs000064400000000000000000000002241046102023000150560ustar 00000000000000#[test] fn compile_test() { let t = trybuild::TestCases::new(); t.pass("tests/pass/*.rs"); t.compile_fail("tests/compile_fail/*.rs"); } cstr-0.2.12/tests/pass/byte_str_lit.rs000064400000000000000000000002601046102023000160400ustar 00000000000000use cstr::cstr; use std::ffi::CStr; fn main() { let foo: &'static CStr = cstr!(b"foo\xffbar"); assert_eq!(foo, CStr::from_bytes_with_nul(b"foo\xffbar\0").unwrap()); } cstr-0.2.12/tests/pass/const.rs000064400000000000000000000004141046102023000144640ustar 00000000000000use cstr::cstr; use std::ffi::CStr; const FOO: &CStr = cstr!(b"foo\xffbar"); static BAR: &CStr = cstr!("bar"); fn main() { assert_eq!(FOO, CStr::from_bytes_with_nul(b"foo\xffbar\0").unwrap()); assert_eq!(BAR, CStr::from_bytes_with_nul(b"bar\0").unwrap()); } cstr-0.2.12/tests/pass/ident.rs000064400000000000000000000004331046102023000144420ustar 00000000000000use cstr::cstr; use std::ffi::CStr; fn main() { let foo: &'static CStr = cstr!(foobar); assert_eq!(foo, CStr::from_bytes_with_nul(b"foobar\0").unwrap()); let foo: &'static CStr = cstr!(r#foobar); assert_eq!(foo, CStr::from_bytes_with_nul(b"r#foobar\0").unwrap()); } cstr-0.2.12/tests/pass/macro.rs000064400000000000000000000006621046102023000144440ustar 00000000000000use cstr::cstr; use std::ffi::CStr; macro_rules! cstr_expr { ($s:expr) => { cstr!($s) }; } macro_rules! cstr_literal { ($s:literal) => { cstr!($s) }; } fn main() { let foo: &'static CStr = cstr_expr!("foo"); assert_eq!(foo, CStr::from_bytes_with_nul(b"foo\0").unwrap()); let bar: &'static CStr = cstr_literal!("bar"); assert_eq!(bar, CStr::from_bytes_with_nul(b"bar\0").unwrap()); } cstr-0.2.12/tests/pass/str_lit.rs000064400000000000000000000003301046102023000150130ustar 00000000000000use cstr::cstr; use std::ffi::CStr; fn main() { let foo: &'static CStr = cstr!("foo\u{4e00}bar"); let expected = b"foo\xe4\xb8\x80bar\0"; assert_eq!(foo, CStr::from_bytes_with_nul(expected).unwrap()); }