Bitwise Calculator — Rust source
Perform AND, OR, XOR, NOT, shifts and rotates on 8/16/32/64-bit values with exact bigint math. Enter operands in binary, octal, decimal or hex and read the result in every base plus a live bit grid. Runs 100% in your browser.
This is the Rust implementation — the same logic the interactive tool runs, in a shareable, citable form.
// =============================================================================
// bitwise.rs — CosmoDev polyglot showcase port of the `bitwise` tool
// -----------------------------------------------------------------------------
// Language : Rust (standard library only)
// Source : ported from src/lib/bitwise.ts (the canonical, live TypeScript lib)
// License : display source — part of CosmoDev's polyglot tool pages
// (dev.cosmolabs.org). Shown verbatim alongside the JS/TS/Go/PHP/Python ports.
// -----------------------------------------------------------------------------
// Pure, deterministic bitwise calculator. Zero deps. Operands are interpreted
// as width-bit two's-complement values: any integer is normalized to the
// half-open range [0, 2^width) before an operation, and every result is masked
// back into that range — so the returned integer is always the unsigned
// bit-pattern of the width-bit result.
//
// Rust note: the TS source uses arbitrary-precision `bigint`. The Rust stdlib
// has no bignum type, so signed parsing/formatting uses `i128` and masked
// results use `u128`. This is exact for every supported width (8/16/32/64) and
// for all shift intermediates (a 64-bit value shifted left by up to 63 needs at
// most 127 bits). Only literals beyond ~10^38 diverge from the TS lib — far
// outside any realistic bitwise-calculator input.
// =============================================================================
/// Numeric radix used for parsing and formatting.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Base {
Bin,
Oct,
Dec,
Hex,
}
impl Base {
/// Positional radix (2/8/10/16).
fn radix(self) -> u32 {
match self {
Base::Bin => 2,
Base::Oct => 8,
Base::Dec => 10,
Base::Hex => 16,
}
}
/// Lowercase digit alphabet valid for this base.
fn digits(self) -> &'static str {
match self {
Base::Bin => "01",
Base::Oct => "01234567",
Base::Dec => "0123456789",
Base::Hex => "0123456789abcdef",
}
}
/// Lowercase name, for error messages that match the TS lib.
fn lower(self) -> &'static str {
match self {
Base::Bin => "bin",
Base::Oct => "oct",
Base::Dec => "dec",
Base::Hex => "hex",
}
}
}
/// Bit-width of the virtual register (8/16/32/64).
pub type Width = u32;
/// Bitwise operation. `Not` is unary on `a`; the rest are binary, with `b` as
/// the shift/rotate count for the shift/rotate ops.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Op {
And,
Or,
Xor,
Not,
Shl,
Shr,
Rol,
Ror,
}
/// Parse error: an empty value, an out-of-base digit, or an out-of-range literal.
#[derive(Debug, PartialEq, Eq)]
pub struct ParseError(pub String);
impl std::fmt::Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::error::Error for ParseError {}
/// Parse a numeric string in `base` into a signed integer. Strips 0x/0b/0o
/// prefixes and an optional leading sign. Errors on empty input, any
/// out-of-base digit, or a literal that overflows i128. The raw signed value is
/// returned (no width normalization); callers fold it into a field via
/// `normalize` / `bitwise`.
pub fn parse(value: &str, base: Base) -> Result<i128, ParseError> {
let trimmed = value.trim();
if trimmed.is_empty() || trimmed == "-" {
return Err(ParseError(format!("Empty {} value", base.lower())));
}
// Peel off an optional leading '-' so negative literals parse correctly.
let (negative, body) = match trimmed.strip_prefix('-') {
Some(rest) => (true, rest),
None => (false, trimmed),
};
// Strip each base prefix in turn (0x, then 0b, then 0o), case-insensitive —
// mirrors the reference's chained leading-prefix removal. Matching against
// the lowercased copy keeps an uppercase mantissa like "0xFF" valid.
let lowered = body.to_ascii_lowercase();
let mut digits = lowered.as_str();
for prefix in ["0x", "0b", "0o"] {
if let Some(rest) = digits.strip_prefix(prefix) {
digits = rest;
}
}
if digits.is_empty() {
return Err(ParseError(format!("Empty {} value", base.lower())));
}
let allowed = base.digits();
let radix = base.radix() as i128;
let mut acc: i128 = 0;
for ch in digits.chars() {
let digit = match allowed.find(ch) {
Some(i) => i as i128,
None => {
return Err(ParseError(format!(
"Invalid digit '{}' for base {}",
ch, base.lower()
)))
}
};
// Checked arithmetic: an over-wide literal reports a clean error
// instead of panicking on overflow.
acc = acc
.checked_mul(radix)
.and_then(|v| v.checked_add(digit))
.ok_or_else(|| ParseError(format!("Value out of range for base {}", base.lower())))?;
}
Ok(if negative { -acc } else { acc })
}
/// Mask for a `width`-bit field: 2^width - 1. (Valid for width <= 127.)
pub fn mask(width: Width) -> u128 {
(1u128 << width) - 1
}
/// Normalize any signed value to its unsigned width-bit two's-complement value,
/// i.e. into the half-open range [0, 2^width). The double-modulo form
/// `((n % m) + m) % m` maps negative dividends into the canonical positive
/// bucket (Rust's `%` is truncated, so the add + second mod is required).
pub fn normalize(n: i128, width: Width) -> u128 {
let m: i128 = 1 << width; // 2^width (fits i128 for width <= 126)
(((n % m) + m) % m) as u128
}
/// Format `n` in `base`, zero-padded to at least `width` digits. Negatives
/// carry a leading '-' and format their magnitude. (The third arg is a minimum
/// digit count — the binary rendering of a width-bit value needs exactly
/// `width` digits.)
pub fn format(n: i128, base: Base, width: usize) -> String {
if n < 0 {
// Format the magnitude and prepend the sign.
return format!("-{}", format(-n, base, width));
}
let alphabet = base.digits();
let radix = base.radix() as u128;
let v = n as u128;
// Manual base conversion keeps us stdlib-only and yields lowercase digits
// matching the TS `bigint.toString(radix)` output.
let mut bytes: Vec<u8> = if v == 0 {
vec![b'0']
} else {
let mut buf = Vec::new();
let mut t = v;
while t > 0 {
let d = (t % radix) as usize;
buf.push(alphabet.as_bytes()[d]);
t /= radix;
}
buf.reverse();
buf
};
while bytes.len() < width {
bytes.insert(0, b'0');
}
String::from_utf8(bytes).unwrap()
}
/// Apply a width-bit operation. `a` is the (unary) operand for `not`; `b` is
/// the second operand for binary ops and the shift/rotate count for
/// shl/shr/rol/ror. Both operands are normalized to width-bit two's complement
/// first; the result is masked to `width` bits.
///
/// For shl/shr we short-circuit when the shift count meets or exceeds `width`:
/// every significant bit is shifted out, so the masked result is zero. This
/// also avoids shifting a u128 by >= 128 (undefined behavior in Rust).
pub fn bitwise(op: Op, a: i128, b: i128, width: Width) -> u128 {
let m = mask(width);
let x = normalize(a, width);
let y = normalize(b, width);
let w = width as u128;
match op {
Op::And => x & y,
Op::Or => x | y,
Op::Xor => x ^ y,
Op::Not => (!x) & m,
Op::Shl => {
if y >= w {
0
} else {
(x << y) & m
}
}
Op::Shr => {
// x is normalized non-negative → logical (zero-filling) shift.
if y >= w {
0
} else {
x >> y
}
}
Op::Rol | Op::Ror => {
let shift = y % w; // rotate amount wraps within width
if shift == 0 {
return x;
}
// A right-rotate by `shift` is a left-rotate by (width - shift).
let s = if op == Op::Rol { shift } else { w - shift };
((x << s) | (x >> (w - s))) & m
}
}
}
/// Fixed-width binary string of `width` bits (MSB first).
pub fn to_bits(n: i128, width: Width) -> String {
format(normalize(n, width) as i128, Base::Bin, width as usize)
}
/// Indices of set bits (LSB = index 0), normalized to `width`. Stops at the
/// highest set bit.
pub fn flags(n: i128, width: Width) -> Vec<u32> {
let mut v = normalize(n, width);
let mut out = Vec::new();
let mut i = 0u32;
while v > 0 {
if v & 1 != 0 {
out.push(i);
}
v >>= 1;
i += 1;
}
out
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →