Skip to content

Add hex_bytes! macro to interpret string literals as hex strings #14573

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

Closed
wants to merge 1 commit into from
Closed
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
20 changes: 20 additions & 0 deletions src/libstd/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,26 @@ pub mod builtin {
#[macro_export]
macro_rules! bytes( ($($e:expr),*) => ({ /* compiler built-in */ }) )

/// Decode a literal string as a hex-encoded byte array
///
/// This macro takes any number of comma-separated literal strings,
/// yielding an expression of type `&'static [u8]` which is the
/// concatenation (left to right) of all the literals interpreted as
/// as hex-encoding of the bytes.
///
/// This extension currently only supports string literals.
///
/// # Example
///
/// ```
/// let rust = hex_bytes!("ABcd", "01");
/// assert_eq!(rust[0], 0xab as u8);
/// assert_eq!(rust[1], 0xcd as u8);
/// assert_eq!(rust[2], 0x01 as u8);
/// ```
#[macro_export]
macro_rules! hex_bytes( ($($e:expr),*) => ({ /* compiler built-in */ }) )

/// Concatenate identifiers into one identifier.
///
/// This macro takes any number of comma-separated identifiers, and
Expand Down
3 changes: 3 additions & 0 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,9 @@ pub fn syntax_expander_table() -> SyntaxEnv {
syntax_expanders.insert(intern("bytes"),
builtin_normal_expander(
ext::bytes::expand_syntax_ext));
syntax_expanders.insert(intern("hex_bytes"),
builtin_normal_expander(
ext::hex_bytes::expand_syntax_ext));
syntax_expanders.insert(intern("concat_idents"),
builtin_normal_expander(
ext::concat_idents::expand_syntax_ext));
Expand Down
87 changes: 87 additions & 0 deletions src/libsyntax/ext/hex_bytes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/* The compiler code necessary to support the hex_bytes! extension. */

use ast;
use codemap::Span;
use ext::base::*;
use ext::base;
use ext::build::AstBuilder;


pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> Box<base::MacResult> {
// Gather all argument expressions
let exprs = match get_exprs_from_tts(cx, sp, tts) {
None => return DummyResult::expr(sp),
Some(e) => e,
};
let mut bytes = Vec::new();
let mut err = false;

for expr in exprs.iter() {
match expr.node {
// expression is a literal
ast::ExprLit(lit) => match lit.node {
// Only string literals are supported, it is not clear what to do in other
// cases. Probably what the user wanted was to use bytes! so recommend
// that instead
ast::LitStr(ref s, _) => {
let mut iter = s.get().chars();
loop {
let first = iter.next();
// If no first character, we're done
if first.is_none() {
break;
}
// If no second, there was an odd read
let second = iter.next();
if second.is_none() {
cx.span_err(expr.span, "uneven number of hex digits in hex_bytes!");
err = true;
break;
}
// If we have both, we are golden
let (hi, lo) = (first.unwrap().to_digit(16), second.unwrap().to_digit(16));
if hi.is_none() || lo.is_none() {
cx.span_err(expr.span, "invalid character in hex_bytes!");
err = true;
break;
}
// If we have both, -and- they successfully decoded
bytes.push(cx.expr_u8(expr.span, (hi.unwrap() * 16 + lo.unwrap()) as u8));
}
}
_ => {
cx.span_err(expr.span, "non-string in hex_bytes!. Perhaps you meant bytes!?");
err = true;
}
},
_ => {
cx.span_err(expr.span, "non-literal in hex_bytes!");
err = true;
}
}
}

// For some reason using quote_expr!() here aborts if we threw an error.
// I'm assuming that the end of the recursive parse tricks the compiler
// into thinking this is a good time to stop. But we'd rather keep going.
if err {
// Since the compiler will stop after the macro expansion phase anyway, we
// don't need type info, so we can just return a DummyResult
return DummyResult::expr(sp);
}

let e = cx.expr_vec_slice(sp, bytes);
let e = quote_expr!(cx, { static BYTES: &'static [u8] = $e; BYTES});
MacExpr::new(e)
}
1 change: 1 addition & 0 deletions src/libsyntax/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ pub mod ext {
pub mod format;
pub mod env;
pub mod bytes;
pub mod hex_bytes;
pub mod concat;
pub mod concat_idents;
pub mod log_syntax;
Expand Down