use std::collections::HashMap;
pub const BITSTREAM_MAGIC: u32 = 0xDEC0_4342;
pub mod block_ids {
pub const BLOCKINFO_BLOCK: u32 = 0;
pub const MODULE_BLOCK: u32 = 8;
pub const PARAMATTR_BLOCK: u32 = 9;
pub const PARAMATTR_GROUP_BLOCK: u32 = 10;
pub const CONSTANTS_BLOCK: u32 = 11;
pub const FUNCTION_BLOCK: u32 = 12;
pub const IDENTIFICATION_BLOCK: u32 = 13;
pub const VALUE_SYMTAB_BLOCK: u32 = 14;
pub const METADATA_BLOCK: u32 = 15;
pub const METADATA_ATTACHMENT: u32 = 16;
pub const TYPE_BLOCK: u32 = 17;
pub const STRTAB_BLOCK: u32 = 23;
pub const BLOCK_ID_WIDTH: u32 = 8;
}
pub mod abbrev_ids {
pub const END_BLOCK: u32 = 0;
pub const ENTER_SUBBLOCK: u32 = 1;
pub const DEFINE_ABBREV: u32 = 2;
pub const UNABBREV_RECORD: u32 = 3;
pub const FIRST_APPLICATION_ABBREV: u32 = 4;
}
pub mod module_codes {
pub const VERSION: u32 = 1;
pub const TRIPLE: u32 = 2;
pub const DATALAYOUT: u32 = 3;
pub const ASM: u32 = 4;
pub const SECTION_NAME: u32 = 5;
pub const DEPLIB: u32 = 6;
pub const GLOBAL_VAR: u32 = 7;
pub const FUNCTION: u32 = 8;
pub const ALIAS: u32 = 9;
pub const GC_NAME: u32 = 11;
}
pub mod paramattr_codes {
pub const ENTRY: u32 = 1;
pub const ENTRY_OLD: u32 = 2;
}
pub mod paramattr_group_codes {
pub const ENTRY: u32 = 1;
pub const ENTRY_OLD: u32 = 2;
}
pub mod type_codes {
pub const NUM_ENTRY: u32 = 1;
pub const VOID: u32 = 2;
pub const HALF: u32 = 3;
pub const FLOAT: u32 = 4;
pub const DOUBLE: u32 = 5;
pub const LABEL: u32 = 6;
pub const OPAQUE: u32 = 7;
pub const INTEGER: u32 = 8;
pub const POINTER: u32 = 9;
pub const ARRAY: u32 = 10; pub const VECTOR: u32 = 11;
pub const METADATA: u32 = 13;
pub const X86_MMX: u32 = 14;
pub const STRUCT_ANON: u32 = 16;
pub const STRUCT_NAME: u32 = 17;
pub const STRUCT_NAMED: u32 = 18;
pub const FUNCTION: u32 = 20; }
pub mod constants_codes {
pub const SETTYPE: u32 = 1;
pub const NULL: u32 = 2;
pub const UNDEF: u32 = 3;
pub const INTEGER: u32 = 4;
pub const WIDE_INTEGER: u32 = 5;
pub const FLOAT: u32 = 6;
pub const AGGREGATE: u32 = 7;
pub const STRING: u32 = 8;
pub const CSTRING: u32 = 9;
pub const CE_BINOP: u32 = 10;
pub const CE_CAST: u32 = 11;
pub const CE_GEP: u32 = 12;
pub const CE_SELECT: u32 = 13;
pub const CE_EXTRACTELT: u32 = 14;
pub const CE_INSERTELT: u32 = 15;
pub const CE_SHUFFLEVEC: u32 = 16;
pub const CE_CMP: u32 = 17;
pub const INLINEASM: u32 = 18;
pub const CE_SHUFVEC_EX: u32 = 19;
pub const CE_INBOUNDS_GEP: u32 = 21;
pub const BLOCKADDRESS: u32 = 22;
pub const DATA: u32 = 23;
pub const INLINEASM_OLD: u32 = 24;
}
pub mod function_codes {
pub const DECLAREBLOCKS: u32 = 1;
pub const BINOP: u32 = 2;
pub const CAST: u32 = 3;
pub const GEP: u32 = 4; pub const GEP_INBOUNDS: u32 = 5; pub const SELECT: u32 = 6;
pub const EXTRACTELT: u32 = 7;
pub const INSERTELT: u32 = 8;
pub const SHUFFLEVEC: u32 = 9;
pub const CMP: u32 = 10;
pub const RET: u32 = 11;
pub const BR: u32 = 12;
pub const SWITCH: u32 = 13;
pub const INVOKE: u32 = 14;
pub const RESUME: u32 = 15; pub const UNREACHABLE: u32 = 16;
pub const PHI: u32 = 17; pub const ALLOCA: u32 = 18; pub const LOAD: u32 = 19; pub const STORE: u32 = 20; pub const CALL: u32 = 21; pub const VAARG: u32 = 22;
pub const EXTRACTVAL: u32 = 23;
pub const INSERTVAL: u32 = 24;
pub const CMP2: u32 = 25; pub const VSELECT: u32 = 26;
pub const INBOUNDS_GEP: u32 = 27; pub const INDIRECTBR: u32 = 28;
pub const DEBUG_LOC: u32 = 29;
pub const DEBUG_LOC_AGAIN: u32 = 30;
pub const DEBUG_INFO: u32 = 31;
pub const CALL_ATTR: u32 = 33;
pub const INST_INBOUNDS_GEP: u32 = 34; pub const INST_GEP: u32 = 35; pub const INST_STORE: u32 = 36; pub const INST_LOAD: u32 = 37; pub const INST_CALL: u32 = 38; pub const INST_ALLOCA: u32 = 39; pub const INST_RET: u32 = 40; pub const INST_PHI: u32 = 41; pub const INST_CMPXCHG: u32 = 42;
pub const INST_ATOMICRMW: u32 = 43;
pub const INST_FENCE: u32 = 44;
pub const INST_LANDINGPAD: u32 = 45; pub const INST_LANDINGPAD_NEW: u32 = 46; pub const INST_CALLBR: u32 = 47;
}
pub mod value_symtab_codes {
pub const ENTRY: u32 = 1;
pub const BASIC_BLOCK_ENTRY: u32 = 2;
pub const FUNCTION_ENTRY: u32 = 3;
pub const COMBINED_FNENTRY: u32 = 4;
}
pub mod metadata_codes {
pub const STRING: u32 = 1; pub const VALUE: u32 = 2; pub const NODE: u32 = 3;
pub const NAME: u32 = 4;
pub const DISTINCT_NODE: u32 = 5;
pub const KIND: u32 = 6; pub const LOCATION: u32 = 7;
pub const STRING_OLD: u32 = 8; pub const VALUE_OLD: u32 = 9; pub const GENERIC_DEBUG: u32 = 10;
pub const NODE_OLD: u32 = 11; pub const INDEX: u32 = 12;
pub const INDEX_OFFSET: u32 = 13;
pub const NAMED_NODE: u32 = 14; }
pub mod identification_codes {
pub const STRING: u32 = 1;
pub const EPOCH: u32 = 2;
}
pub mod strtab_codes {
pub const BLOB: u32 = 1;
}
pub mod blockinfo_codes {
pub const SETBID: u32 = 1;
pub const BLOCKNAME: u32 = 2;
pub const SET_RECORD_NAME: u32 = 3;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OperandEncoding {
Fixed(u32),
VBR(u32),
Array(Box<OperandEncoding>),
Char6,
Blob,
}
#[derive(Debug, Clone)]
pub struct Abbreviation {
pub id: u32,
pub code: u32,
pub operands: Vec<OperandEncoding>,
}
pub fn encode_vbr(value: u64, bytes: &mut Vec<u8>) {
let mut v = value;
loop {
let byte = (v & 0x7f) as u8;
v >>= 7;
if v != 0 {
bytes.push(byte | 0x80);
} else {
bytes.push(byte);
break;
}
}
}
pub fn decode_vbr(bytes: &[u8], cursor: &mut usize) -> Option<u64> {
let mut value: u64 = 0;
let mut shift = 0u32;
loop {
if *cursor >= bytes.len() {
return None;
}
let byte = bytes[*cursor];
*cursor += 1;
value |= ((byte & 0x7f) as u64) << shift;
if byte & 0x80 == 0 {
return Some(value);
}
shift += 7;
}
}
pub fn encode_vbr_n(value: u64, chunk_width: u32, bytes: &mut Vec<u8>) {
let mask = (1u64 << chunk_width) - 1;
let mut v = value;
loop {
let byte = (v & mask) as u8;
v >>= chunk_width;
if v != 0 {
bytes.push(byte | (1u8 << chunk_width));
} else {
bytes.push(byte);
break;
}
}
}
pub fn decode_vbr_n(bytes: &[u8], cursor: &mut usize, chunk_width: u32) -> Option<u64> {
let mut value: u64 = 0;
let mut shift = 0u32;
let mask: u8 = (1u8 << chunk_width) - 1;
loop {
if *cursor >= bytes.len() {
return None;
}
let byte = bytes[*cursor];
*cursor += 1;
value |= ((byte & mask) as u64) << shift;
if (byte & (1u8 << chunk_width)) == 0 {
return Some(value);
}
shift += chunk_width;
}
}
const CHAR6_TABLE: &[u8; 64] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._";
pub fn encode_char6(c: u8) -> u8 {
CHAR6_TABLE.iter().position(|&x| x == c).unwrap_or(0) as u8
}
pub fn decode_char6(v: u8) -> u8 {
CHAR6_TABLE.get(v as usize).copied().unwrap_or(b'?')
}
pub fn encode_char6_string(s: &str, bytes: &mut Vec<u8>) {
for &c in s.as_bytes() {
encode_vbr_n(encode_char6(c) as u64, 6, bytes);
}
}
pub fn decode_char6_string(bytes: &[u8], cursor: &mut usize, len: usize) -> String {
let mut s = String::with_capacity(len);
for _ in 0..len {
if let Some(v) = decode_vbr_n(bytes, cursor, 6) {
s.push(decode_char6(v as u8) as char);
} else {
break;
}
}
s
}
pub struct BitstreamWriter {
pub bytes: Vec<u8>,
block_stack: Vec<usize>,
next_abbrev_id: u32,
}
impl BitstreamWriter {
pub fn new() -> Self {
Self {
bytes: Vec::new(),
block_stack: Vec::new(),
next_abbrev_id: abbrev_ids::FIRST_APPLICATION_ABBREV,
}
}
pub fn emit_magic(&mut self) {
self.emit_u32(BITSTREAM_MAGIC);
}
pub fn enter_subblock(&mut self, block_id: u32, code_len: u32) {
self.emit_vbr(abbrev_ids::ENTER_SUBBLOCK as u64);
self.emit_vbr(block_id as u64);
self.emit_vbr(code_len as u64);
self.align_32();
self.block_stack.push(self.bytes.len());
self.emit_u32(0); self.next_abbrev_id = abbrev_ids::FIRST_APPLICATION_ABBREV;
}
pub fn end_block(&mut self) {
self.align_32();
self.emit_vbr(abbrev_ids::END_BLOCK as u64);
self.align_32();
if let Some(start) = self.block_stack.pop() {
let block_len = (self.bytes.len() - start - 4) as u32;
self.bytes[start..start + 4].copy_from_slice(&block_len.to_le_bytes());
}
}
pub fn emit_record(&mut self, code: u32, ops: &[u64]) {
self.emit_vbr(abbrev_ids::UNABBREV_RECORD as u64);
self.emit_vbr(code as u64);
self.emit_vbr(ops.len() as u64);
for &op in ops {
self.emit_vbr(op);
}
}
pub fn define_abbrev(&mut self, operands: Vec<OperandEncoding>) -> u32 {
let id = self.next_abbrev_id;
self.next_abbrev_id += 1;
self.emit_vbr(abbrev_ids::DEFINE_ABBREV as u64);
self.emit_vbr(operands.len() as u64);
for op in &operands {
self.emit_operand_encoding(op);
}
id
}
fn emit_operand_encoding(&mut self, op: &OperandEncoding) {
match op {
OperandEncoding::Fixed(width) => {
self.emit_vbr(1); self.emit_vbr(*width as u64);
}
OperandEncoding::VBR(width) => {
self.emit_vbr(2); self.emit_vbr(*width as u64);
}
OperandEncoding::Array(el) => {
self.emit_vbr(3); self.emit_operand_encoding(el);
}
OperandEncoding::Char6 => {
self.emit_vbr(4); }
OperandEncoding::Blob => {
self.emit_vbr(5); }
}
}
pub fn emit_abbrev_record(
&mut self,
abbrev_id: u32,
code: u32,
ops: &[u64],
encodings: &[OperandEncoding],
) {
self.emit_vbr(abbrev_id as u64);
self.emit_vbr(code as u64);
for (i, op) in ops.iter().enumerate() {
let enc = encodings.get(i).unwrap_or(&OperandEncoding::VBR(7));
self.emit_encoded_operand(*op, enc);
}
}
fn emit_encoded_operand(&mut self, val: u64, enc: &OperandEncoding) {
match enc {
OperandEncoding::Fixed(width) => {
let _mask = (1u64 << width) - 1;
let mut buf = 0u64;
let mut bits = 0;
for i in 0..*width {
if val & (1u64 << i) != 0 {
buf |= 1u64 << bits;
}
bits += 1;
if bits == 8 {
self.bytes.push(buf as u8);
buf = 0;
bits = 0;
}
}
if bits > 0 {
self.bytes.push(buf as u8);
}
}
OperandEncoding::VBR(width) => {
encode_vbr_n(val, *width, &mut self.bytes);
}
OperandEncoding::Char6 => {
encode_vbr_n(val, 6, &mut self.bytes);
}
OperandEncoding::Array(_) => {
encode_vbr(val, &mut self.bytes);
}
OperandEncoding::Blob => {
encode_vbr(val, &mut self.bytes);
}
}
}
pub fn emit_u32(&mut self, val: u32) {
self.bytes.extend_from_slice(&val.to_le_bytes());
}
pub fn emit_u64(&mut self, val: u64) {
self.bytes.extend_from_slice(&val.to_le_bytes());
}
pub fn emit_vbr(&mut self, val: u64) {
encode_vbr(val, &mut self.bytes);
}
pub fn emit_vbr6(&mut self, val: u64) {
encode_vbr_n(val, 6, &mut self.bytes);
}
pub fn emit_char6_string(&mut self, s: &str) {
encode_char6_string(s, &mut self.bytes);
}
pub fn align_32(&mut self) {
while !self.bytes.len().is_multiple_of(4) {
self.bytes.push(0);
}
}
pub fn finish(self) -> Vec<u8> {
self.bytes
}
pub fn position(&self) -> usize {
self.bytes.len()
}
}
impl Default for BitstreamWriter {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Default)]
pub struct AbbrevTable {
pub abbrevs: Vec<Abbreviation>,
}
impl AbbrevTable {
pub fn new() -> Self {
Self {
abbrevs: Vec::new(),
}
}
pub fn add(&mut self, abbrev: Abbreviation) {
self.abbrevs.push(abbrev);
}
pub fn get(&self, id: u32) -> Option<&Abbreviation> {
self.abbrevs.iter().find(|a| a.id == id)
}
}
pub struct BitstreamReader<'a> {
pub bytes: &'a [u8],
pub cursor: usize,
block_stack: Vec<(usize, usize, AbbrevTable)>,
current_abbrevs: AbbrevTable,
next_abbrev_id: u32,
}
impl<'a> BitstreamReader<'a> {
pub fn new(bytes: &'a [u8]) -> Self {
let cursor = if bytes.len() >= 4 { 4 } else { 0 };
Self {
bytes,
cursor,
block_stack: Vec::new(),
current_abbrevs: AbbrevTable::new(),
next_abbrev_id: abbrev_ids::FIRST_APPLICATION_ABBREV,
}
}
pub fn read_entry(&mut self) -> Option<BitstreamEntry> {
if let Some(&(_, end, _)) = self.block_stack.last() {
if self.cursor >= end {
let (_, _, abbrevs) = self.block_stack.pop().unwrap();
self.current_abbrevs = abbrevs;
return Some(BitstreamEntry::EndBlock);
}
}
if self.cursor >= self.bytes.len() {
return None;
}
let code = decode_vbr(self.bytes, &mut self.cursor)? as u32;
match code {
abbrev_ids::END_BLOCK => {
self.align_32();
if let Some((_, _, abbrevs)) = self.block_stack.pop() {
self.current_abbrevs = abbrevs;
}
Some(BitstreamEntry::EndBlock)
}
abbrev_ids::ENTER_SUBBLOCK => {
let block_id = decode_vbr(self.bytes, &mut self.cursor)? as u32;
let code_len = decode_vbr(self.bytes, &mut self.cursor)? as u32;
self.align_32();
let block_len = self.read_u32()? as usize;
let bs = self.cursor;
let old_abbrevs = std::mem::take(&mut self.current_abbrevs);
self.block_stack.push((bs, bs + block_len, old_abbrevs));
self.next_abbrev_id = abbrev_ids::FIRST_APPLICATION_ABBREV;
Some(BitstreamEntry::SubBlock { block_id, code_len })
}
abbrev_ids::DEFINE_ABBREV => {
let num_ops = decode_vbr(self.bytes, &mut self.cursor)? as usize;
let id = self.next_abbrev_id;
self.next_abbrev_id += 1;
let mut operands = Vec::with_capacity(num_ops);
for _ in 0..num_ops {
operands.push(self.read_operand_encoding());
}
self.current_abbrevs.add(Abbreviation {
id,
code: 0,
operands,
});
Some(BitstreamEntry::DefineAbbrev { id })
}
abbrev_ids::UNABBREV_RECORD => {
let rec_code = decode_vbr(self.bytes, &mut self.cursor)? as u32;
let num_ops = decode_vbr(self.bytes, &mut self.cursor)? as usize;
let mut ops = Vec::with_capacity(num_ops);
for _ in 0..num_ops {
ops.push(decode_vbr(self.bytes, &mut self.cursor)?);
}
Some(BitstreamEntry::Record {
code: rec_code,
ops,
})
}
_ => {
let abbrev_data = self
.current_abbrevs
.get(code)
.map(|a| (a.code, a.operands.clone()));
if let Some((abbrev_code, operands_enc)) = abbrev_data {
let mut ops = Vec::new();
for enc in &operands_enc {
match enc {
OperandEncoding::Array(el) => {
let arr_len = decode_vbr(self.bytes, &mut self.cursor)? as usize;
for _ in 0..arr_len {
ops.push(self.read_encoded_operand(el));
}
}
OperandEncoding::Blob => {
let blob_len = decode_vbr(self.bytes, &mut self.cursor)? as usize;
self.align_32();
for _ in 0..blob_len {
if self.cursor < self.bytes.len() {
ops.push(self.bytes[self.cursor] as u64);
self.cursor += 1;
}
}
self.align_32();
}
_ => {
ops.push(self.read_encoded_operand(enc));
}
}
}
Some(BitstreamEntry::Record {
code: abbrev_code,
ops,
})
} else {
Some(BitstreamEntry::Record {
code: 0,
ops: vec![],
})
}
}
}
}
fn read_operand_encoding(&mut self) -> OperandEncoding {
let tag = decode_vbr(self.bytes, &mut self.cursor).unwrap_or(0) as u32;
match tag {
1 => {
let width = decode_vbr(self.bytes, &mut self.cursor).unwrap_or(0) as u32;
OperandEncoding::Fixed(width)
}
2 => {
let width = decode_vbr(self.bytes, &mut self.cursor).unwrap_or(0) as u32;
OperandEncoding::VBR(width)
}
3 => {
let elem = self.read_operand_encoding();
OperandEncoding::Array(Box::new(elem))
}
4 => OperandEncoding::Char6,
5 => OperandEncoding::Blob,
_ => OperandEncoding::VBR(7),
}
}
fn read_encoded_operand(&mut self, enc: &OperandEncoding) -> u64 {
match enc {
OperandEncoding::Fixed(width) => self.read_fixed(*width),
OperandEncoding::VBR(width) => {
decode_vbr_n(self.bytes, &mut self.cursor, *width).unwrap_or(0)
}
OperandEncoding::Char6 => decode_vbr_n(self.bytes, &mut self.cursor, 6).unwrap_or(0),
_ => decode_vbr(self.bytes, &mut self.cursor).unwrap_or(0),
}
}
fn read_fixed(&mut self, width: u32) -> u64 {
let mut val: u64 = 0;
let mut bits_read = 0u32;
let mut byte_buf: u8 = 0;
let mut byte_bits = 0u32;
while bits_read < width {
if byte_bits == 0 {
if self.cursor >= self.bytes.len() {
return 0;
}
byte_buf = self.bytes[self.cursor];
self.cursor += 1;
byte_bits = 8;
}
let bit = (byte_buf & 1) as u64;
byte_buf >>= 1;
byte_bits -= 1;
val |= bit << bits_read;
bits_read += 1;
}
val
}
fn read_u32(&mut self) -> Option<u32> {
if self.cursor + 4 > self.bytes.len() {
return None;
}
let val = u32::from_le_bytes([
self.bytes[self.cursor],
self.bytes[self.cursor + 1],
self.bytes[self.cursor + 2],
self.bytes[self.cursor + 3],
]);
self.cursor += 4;
Some(val)
}
pub fn align_32(&mut self) {
while !self.cursor.is_multiple_of(4) {
self.cursor += 1;
}
}
}
#[derive(Debug, Clone)]
pub enum BitstreamEntry {
SubBlock { block_id: u32, code_len: u32 },
Record { code: u32, ops: Vec<u64> },
EndBlock,
DefineAbbrev { id: u32 },
}
impl BitstreamWriter {
pub fn emit_record_with_blob(&mut self, code: u32, ops: &[u64], blob: &[u8]) {
self.emit_vbr(abbrev_ids::UNABBREV_RECORD as u64);
self.emit_vbr(code as u64);
self.emit_vbr(ops.len() as u64);
for &op in ops {
self.emit_vbr(op);
}
self.align_32();
self.bytes.extend_from_slice(blob);
self.align_32();
}
pub fn emit_record_with_abbrev(
&mut self,
abbrev_id: u32,
code: u32,
ops: &[u64],
encodings: &[OperandEncoding],
) {
self.emit_vbr(abbrev_id as u64);
self.emit_vbr(code as u64);
for (i, op) in ops.iter().enumerate() {
let enc = encodings.get(i).unwrap_or(&OperandEncoding::VBR(7));
self.emit_encoded_operand(*op, enc);
}
}
pub fn emit_abbrev_record_with_blob(
&mut self,
abbrev_id: u32,
code: u32,
ops: &[u64],
encodings: &[OperandEncoding],
blob: &[u8],
) {
self.emit_vbr(abbrev_id as u64);
self.emit_vbr(code as u64);
for (i, op) in ops.iter().enumerate() {
let enc = encodings.get(i).unwrap_or(&OperandEncoding::VBR(7));
if matches!(enc, OperandEncoding::Blob) {
self.emit_vbr(blob.len() as u64);
self.align_32();
self.bytes.extend_from_slice(blob);
self.align_32();
break;
}
self.emit_encoded_operand(*op, enc);
}
}
pub fn define_abbrev_full(&mut self, operands: Vec<OperandEncoding>) -> Abbreviation {
let id = self.next_abbrev_id;
self.next_abbrev_id += 1;
let code = 0;
self.emit_vbr(abbrev_ids::DEFINE_ABBREV as u64);
self.emit_vbr(operands.len() as u64);
for op in &operands {
self.emit_operand_encoding(op);
}
Abbreviation { id, code, operands }
}
pub fn emit_abbreviated_record(&mut self, abbrev: &Abbreviation, code: u32, ops: &[u64]) {
self.emit_vbr(abbrev.id as u64);
self.emit_vbr(code as u64);
for (i, op) in ops.iter().enumerate() {
let enc = abbrev.operands.get(i).unwrap_or(&OperandEncoding::VBR(7));
self.emit_encoded_operand(*op, enc);
}
}
pub fn block_depth(&self) -> usize {
self.block_stack.len()
}
pub fn current_abbrev_id(&self) -> u32 {
self.next_abbrev_id
}
pub fn reset_abbrev_ids(&mut self) {
self.next_abbrev_id = abbrev_ids::FIRST_APPLICATION_ABBREV;
}
pub fn emit_vbr_n(&mut self, val: u64, n: u32) {
encode_vbr_n(val, n, &mut self.bytes);
}
}
#[derive(Debug, Clone)]
pub struct BitCodeAbbrev {
pub id: u32,
pub code: u32,
pub operands: Vec<OperandEncoding>,
}
impl BitCodeAbbrev {
pub fn new(id: u32) -> Self {
Self {
id,
code: 0,
operands: Vec::new(),
}
}
pub fn add_operand(&mut self, enc: OperandEncoding) {
self.operands.push(enc);
}
pub fn encode(&self, writer: &mut BitstreamWriter) {
writer.emit_vbr(abbrev_ids::DEFINE_ABBREV as u64);
writer.emit_vbr(self.operands.len() as u64);
for op in &self.operands {
match op {
OperandEncoding::Fixed(w) => {
writer.emit_vbr(1);
writer.emit_vbr(*w as u64);
}
OperandEncoding::VBR(w) => {
writer.emit_vbr(2);
writer.emit_vbr(*w as u64);
}
OperandEncoding::Array(el) => {
writer.emit_vbr(3);
match &**el {
OperandEncoding::Fixed(w) => {
writer.emit_vbr(1);
writer.emit_vbr(*w as u64);
}
OperandEncoding::VBR(w) => {
writer.emit_vbr(2);
writer.emit_vbr(*w as u64);
}
OperandEncoding::Char6 => {
writer.emit_vbr(4);
}
OperandEncoding::Blob => {
writer.emit_vbr(5);
}
_ => {}
}
}
OperandEncoding::Char6 => {
writer.emit_vbr(4);
}
OperandEncoding::Blob => {
writer.emit_vbr(5);
}
}
}
}
pub fn decode(
&self,
_reader: &BitstreamReader<'_>,
data: &[u8],
cursor: &mut usize,
) -> Option<Vec<u64>> {
let mut ops = Vec::new();
for enc in &self.operands {
match enc {
OperandEncoding::Fixed(w) => {
let val = read_fixed_from_slice(data, cursor, *w)?;
ops.push(val);
}
OperandEncoding::VBR(w) => {
let val = decode_vbr_n(data, cursor, *w)?;
ops.push(val);
}
OperandEncoding::Char6 => {
let val = decode_vbr_n(data, cursor, 6)?;
ops.push(val);
}
OperandEncoding::Array(el) => {
let len = decode_vbr(data, cursor)? as usize;
for _ in 0..len {
match &**el {
OperandEncoding::Fixed(w) => {
ops.push(read_fixed_from_slice(data, cursor, *w)?);
}
OperandEncoding::VBR(w) => {
ops.push(decode_vbr_n(data, cursor, *w)?);
}
OperandEncoding::Char6 => {
ops.push(decode_vbr_n(data, cursor, 6)?);
}
_ => {}
}
}
}
OperandEncoding::Blob => {
let blob_len = decode_vbr(data, cursor)? as usize;
*cursor = (*cursor + 3) & !3;
for _ in 0..blob_len {
if *cursor < data.len() {
ops.push(data[*cursor] as u64);
*cursor += 1;
}
}
*cursor = (*cursor + 3) & !3;
}
}
}
Some(ops)
}
}
fn read_fixed_from_slice(data: &[u8], cursor: &mut usize, width: u32) -> Option<u64> {
let mut val: u64 = 0;
let mut bits_read = 0u32;
let mut byte_buf: u8 = 0;
let mut byte_bits = 0u32;
while bits_read < width {
if byte_bits == 0 {
if *cursor >= data.len() {
return None;
}
byte_buf = data[*cursor];
*cursor += 1;
byte_bits = 8;
}
let bit = (byte_buf & 1) as u64;
byte_buf >>= 1;
byte_bits -= 1;
val |= bit << bits_read;
bits_read += 1;
}
Some(val)
}
impl<'a> BitstreamReader<'a> {
pub fn skip_record(&mut self, ops_len: usize) {
for _ in 0..ops_len {
let _ = decode_vbr(self.bytes, &mut self.cursor);
}
}
pub fn get_current_record_no(&self) -> usize {
self.cursor
}
pub fn jump_to_bit(&mut self, bit_pos: usize) {
if bit_pos < self.bytes.len() * 8 {
self.cursor = bit_pos / 8;
}
}
pub fn read_abbrev_op(&mut self) -> Option<OperandEncoding> {
let tag = decode_vbr(self.bytes, &mut self.cursor)? as u32;
match tag {
1 => {
let width = decode_vbr(self.bytes, &mut self.cursor)? as u32;
Some(OperandEncoding::Fixed(width))
}
2 => {
let width = decode_vbr(self.bytes, &mut self.cursor)? as u32;
Some(OperandEncoding::VBR(width))
}
3 => {
let elem = self.read_abbrev_op()?;
Some(OperandEncoding::Array(Box::new(elem)))
}
4 => Some(OperandEncoding::Char6),
5 => Some(OperandEncoding::Blob),
_ => Some(OperandEncoding::VBR(7)),
}
}
pub fn read_vbr(&mut self, width: u32) -> Option<u64> {
decode_vbr_n(self.bytes, &mut self.cursor, width)
}
pub fn read_code(&mut self) -> Option<u32> {
decode_vbr(self.bytes, &mut self.cursor).map(|v| v as u32)
}
pub fn read_abbreviated_field(&mut self, enc: &OperandEncoding) -> Option<u64> {
match enc {
OperandEncoding::Fixed(width) => {
Some(read_fixed_from_slice(self.bytes, &mut self.cursor, *width)?)
}
OperandEncoding::VBR(width) => decode_vbr_n(self.bytes, &mut self.cursor, *width),
OperandEncoding::Char6 => decode_vbr_n(self.bytes, &mut self.cursor, 6),
OperandEncoding::Array(_) | OperandEncoding::Blob => {
decode_vbr(self.bytes, &mut self.cursor)
}
}
}
pub fn read_subblock_header(&mut self) -> Option<(u32, u32)> {
let code = decode_vbr(self.bytes, &mut self.cursor)? as u32;
if code != abbrev_ids::ENTER_SUBBLOCK {
return None;
}
let block_id = decode_vbr(self.bytes, &mut self.cursor)? as u32;
let code_len = decode_vbr(self.bytes, &mut self.cursor)? as u32;
Some((block_id, code_len))
}
pub fn read_char6_string(&mut self, len: usize) -> Option<String> {
decode_char6_string_n(self.bytes, &mut self.cursor, len)
}
pub fn remaining_in_block(&self) -> usize {
if let Some(&(_, end, _)) = self.block_stack.last() {
end.saturating_sub(self.cursor)
} else {
self.bytes.len().saturating_sub(self.cursor)
}
}
pub fn at_end_of_block(&self) -> bool {
if let Some(&(_, end, _)) = self.block_stack.last() {
self.cursor >= end
} else {
self.cursor >= self.bytes.len()
}
}
pub fn enter_subblock(
&mut self,
start: usize,
end: usize,
block_id: u32,
code_len: u32,
) -> bool {
let old_abbrevs = std::mem::take(&mut self.current_abbrevs);
self.block_stack.push((start, end, old_abbrevs));
self.cursor = start;
self.next_abbrev_id = abbrev_ids::FIRST_APPLICATION_ABBREV;
let _ = (block_id, code_len);
true
}
pub fn skip_block(&mut self) -> bool {
if let Some(&(_, end, _)) = self.block_stack.last() {
self.cursor = end;
let (_, _, abbrevs) = self.block_stack.pop().unwrap();
self.current_abbrevs = abbrevs;
return true;
}
false
}
pub fn read_block_info_block(&mut self) -> Option<BlockInfo> {
let mut block_info = BlockInfo::new();
loop {
let entry = self.read_entry()?;
match entry {
BitstreamEntry::SubBlock { block_id, code_len } => {
if block_id == block_ids::BLOCKINFO_BLOCK {
let nested_info = self.read_block_info_block()?;
block_info.merge(nested_info);
} else {
self.skip_block();
}
let _ = code_len;
}
BitstreamEntry::Record { code, ops } => {
if code == blockinfo_codes::SETBID {
if let Some(&bid) = ops.first() {
block_info.current_bid = bid as u32;
}
} else if code == blockinfo_codes::BLOCKNAME {
let name = ops.iter().map(|&b| b as u8 as char).collect::<String>();
block_info.block_names.insert(block_info.current_bid, name);
} else if code == blockinfo_codes::SET_RECORD_NAME {
if let Some(&rec_code) = ops.first() {
let rec_name = ops[1..]
.iter()
.map(|&b| b as u8 as char)
.collect::<String>();
block_info
.record_names
.insert((block_info.current_bid, rec_code as u32), rec_name);
}
}
}
BitstreamEntry::DefineAbbrev { id: _ } => {}
BitstreamEntry::EndBlock => break,
}
}
Some(block_info)
}
pub fn offset(&self) -> usize {
self.cursor
}
}
fn decode_char6_string_n(data: &[u8], cursor: &mut usize, len: usize) -> Option<String> {
let mut result = String::with_capacity(len);
for _ in 0..len {
let v = decode_vbr_n(data, cursor, 6)?;
result.push(decode_char6(v as u8) as char);
}
Some(result)
}
#[derive(Debug, Clone)]
pub struct BlockInfo {
pub current_bid: u32,
pub block_names: HashMap<u32, String>,
pub record_names: HashMap<(u32, u32), String>,
pub block_abbrevs: HashMap<u32, Vec<Abbreviation>>,
}
impl BlockInfo {
pub fn new() -> Self {
Self {
current_bid: 0,
block_names: HashMap::new(),
record_names: HashMap::new(),
block_abbrevs: HashMap::new(),
}
}
pub fn set_bid(&mut self, bid: u32) {
self.current_bid = bid;
}
pub fn get_block_name(&self, bid: u32) -> Option<&str> {
self.block_names.get(&bid).map(|s| s.as_str())
}
pub fn get_record_name(&self, bid: u32, code: u32) -> Option<&str> {
self.record_names.get(&(bid, code)).map(|s| s.as_str())
}
pub fn get_block_abbrevs(&self, bid: u32) -> Option<&Vec<Abbreviation>> {
self.block_abbrevs.get(&bid)
}
pub fn add_abbrev(&mut self, bid: u32, abbrev: Abbreviation) {
self.block_abbrevs.entry(bid).or_default().push(abbrev);
}
pub fn merge(&mut self, other: BlockInfo) {
for (bid, name) in other.block_names {
self.block_names.entry(bid).or_insert(name);
}
for (key, name) in other.record_names {
self.record_names.entry(key).or_insert(name);
}
for (bid, abbrevs) in other.block_abbrevs {
let existing = self.block_abbrevs.entry(bid).or_default();
for a in abbrevs {
if !existing.iter().any(|e| e.id == a.id) {
existing.push(a);
}
}
}
}
pub fn is_empty(&self) -> bool {
self.block_names.is_empty() && self.record_names.is_empty() && self.block_abbrevs.is_empty()
}
}
impl Default for BlockInfo {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct BlockBegin {
pub block_id: u32,
pub code_len: u32,
pub start_offset: usize,
}
#[derive(Debug, Clone)]
pub struct BlockEnd {
pub end_offset: usize,
}
#[derive(Debug, Clone)]
pub struct BlockContext {
pub block_id: u32,
pub start: usize,
pub end: usize,
pub abbreviations: AbbrevTable,
}
impl BlockContext {
pub fn new(block_id: u32, start: usize, end: usize) -> Self {
Self {
block_id,
start,
end,
abbreviations: AbbrevTable::new(),
}
}
}
pub fn encode_vbr_signed(val: i64, out: &mut Vec<u8>) {
let unsigned = if val >= 0 {
(val as u64) << 1
} else {
((-val) as u64) << 1 | 1
};
encode_vbr(unsigned, out);
}
pub fn decode_vbr_signed(data: &[u8], cursor: &mut usize) -> Option<i64> {
let unsigned = decode_vbr(data, cursor)?;
if unsigned & 1 == 0 {
Some((unsigned >> 1) as i64)
} else {
Some(-(((unsigned >> 1) + 1) as i64))
}
}
pub fn encode_fixed(val: u64, width: u32, out: &mut Vec<u8>) {
let mask = (1u64 << width) - 1;
let mut remaining = val & mask;
for _ in 0..((width + 7) / 8) {
out.push((remaining & 0xFF) as u8);
remaining >>= 8;
}
}
pub fn decode_fixed(data: &[u8], cursor: &mut usize, width: u32) -> Option<u64> {
let num_bytes = ((width + 7) / 8) as usize;
if *cursor + num_bytes > data.len() {
return None;
}
let mut val: u64 = 0;
for i in 0..num_bytes {
val |= (data[*cursor + i] as u64) << (i * 8);
}
*cursor += num_bytes;
Some(val & ((1u64 << width) - 1))
}
pub fn build_string_table(strings: &[&str]) -> Vec<u8> {
let mut table = Vec::new();
table.push(0u8);
for s in strings {
table.extend_from_slice(s.as_bytes());
table.push(0u8);
}
table
}
pub fn read_string_from_table(table: &[u8], offset: usize) -> Option<&str> {
if offset >= table.len() {
return None;
}
let end = table[offset..]
.iter()
.position(|&b| b == 0)
.unwrap_or(table.len() - offset);
std::str::from_utf8(&table[offset..offset + end]).ok()
}
pub fn find_string_in_table(table: &[u8], needle: &str) -> Option<usize> {
let mut pos = 1; while pos < table.len() {
let end = table[pos..]
.iter()
.position(|&b| b == 0)
.map(|p| pos + p)
.unwrap_or(table.len());
if end > pos && &table[pos..end] == needle.as_bytes() {
return Some(pos);
}
pos = end + 1;
}
None
}
#[derive(Debug, Clone)]
pub struct RecordInfo {
pub block_id: u32,
pub record_code: u32,
pub record_name: String,
pub operands: Vec<RecordOperandInfo>,
}
#[derive(Debug, Clone)]
pub struct RecordOperandInfo {
pub name: String,
pub encoding: OperandEncoding,
pub description: String,
}
impl RecordInfo {
pub fn new(block_id: u32, code: u32, name: &str) -> Self {
Self {
block_id,
record_code: code,
record_name: name.to_string(),
operands: Vec::new(),
}
}
pub fn add_operand(&mut self, op: RecordOperandInfo) {
self.operands.push(op);
}
pub fn format_operands(&self, ops: &[u64]) -> String {
let mut out = String::new();
for (i, op) in ops.iter().enumerate() {
if i > 0 {
out.push_str(", ");
}
if let Some(info) = self.operands.get(i) {
out.push_str(&format!("{}={}(0x{:x})", info.name, op, op));
} else {
out.push_str(&format!("op{}={}", i, op));
}
}
out
}
}
impl RecordOperandInfo {
pub fn new(name: &str, encoding: OperandEncoding, desc: &str) -> Self {
Self {
name: name.to_string(),
encoding,
description: desc.to_string(),
}
}
pub fn fixed(name: &str, width: u32) -> Self {
Self::new(
name,
OperandEncoding::Fixed(width),
&format!("fixed-width({})", width),
)
}
pub fn vbr(name: &str, width: u32) -> Self {
Self::new(
name,
OperandEncoding::VBR(width),
&format!("vbr({})", width),
)
}
pub fn char6(name: &str) -> Self {
Self::new(name, OperandEncoding::Char6, "char6")
}
pub fn blob(name: &str) -> Self {
Self::new(name, OperandEncoding::Blob, "blob")
}
pub fn array(name: &str, elem: OperandEncoding) -> Self {
Self::new(name, OperandEncoding::Array(Box::new(elem)), "array")
}
}
pub fn is_abbrev_valid(abbrev: &Abbreviation) -> bool {
if abbrev.operands.is_empty() {
return false;
}
let mut has_blob = false;
for enc in &abbrev.operands {
match enc {
OperandEncoding::Blob => {
if has_blob {
return false; }
has_blob = true;
}
_ => {
if has_blob {
return false; }
}
}
}
true
}
pub fn abbrev_num_leaf_operands(abbrev: &Abbreviation) -> usize {
abbrev
.operands
.iter()
.filter(|enc| !matches!(enc, OperandEncoding::Array(_) | OperandEncoding::Blob))
.count()
}
pub fn estimate_abbrev_record_size(abbrev: &Abbreviation, ops: &[u64]) -> usize {
let mut size = 0usize;
size += estimate_vbr_size(abbrev.id as u64);
size += estimate_vbr_size(abbrev.code as u64);
for (i, op) in ops.iter().enumerate() {
if let Some(enc) = abbrev.operands.get(i) {
match enc {
OperandEncoding::Fixed(w) => {
size += ((*w + 7) / 8) as usize;
}
OperandEncoding::VBR(w) => {
size += estimate_vbr_n_size(*op, *w);
}
OperandEncoding::Char6 => {
size += estimate_vbr_n_size(*op, 6);
}
OperandEncoding::Blob => {
size += 4 + *op as usize; }
_ => {
size += 8;
}
}
}
}
size
}
fn estimate_vbr_size(val: u64) -> usize {
estimate_vbr_n_size(val, 7)
}
fn estimate_vbr_n_size(val: u64, n: u32) -> usize {
if val == 0 {
return 1;
}
let mut remaining = val;
let mut count = 0usize;
while remaining > 0 {
count += 1;
remaining >>= n;
}
count
}
#[derive(Debug, Clone, Copy)]
pub struct BitCursor {
pub byte_pos: usize,
pub bit_pos: u8,
}
impl BitCursor {
pub fn new(byte_pos: usize) -> Self {
Self {
byte_pos,
bit_pos: 0,
}
}
pub fn from_bits(total_bits: usize) -> Self {
Self {
byte_pos: total_bits / 8,
bit_pos: (total_bits % 8) as u8,
}
}
pub fn advance_bits(&mut self, bits: u32) {
let total = self.bit_pos as u32 + bits;
self.byte_pos += (total / 8) as usize;
self.bit_pos = (total % 8) as u8;
}
pub fn total_bits(&self) -> usize {
self.byte_pos * 8 + self.bit_pos as usize
}
pub fn align_to_byte(&mut self) {
if self.bit_pos > 0 {
self.byte_pos += 1;
self.bit_pos = 0;
}
}
pub fn align_to_32(&mut self) {
self.align_to_byte();
while self.byte_pos % 4 != 0 {
self.byte_pos += 1;
}
}
pub fn is_byte_aligned(&self) -> bool {
self.bit_pos == 0
}
}
impl Default for BitCursor {
fn default() -> Self {
Self::new(0)
}
}
impl BitstreamWriter {
pub fn emit_vbr_slice(&mut self, values: &[u64]) {
for &v in values {
self.emit_vbr(v);
}
}
pub fn emit_string(&mut self, s: &str) {
self.emit_char6_string(s);
}
pub fn align_to_byte(&mut self) {
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
pub fn clear(&mut self) {
self.bytes.clear();
self.block_stack.clear();
self.next_abbrev_id = abbrev_ids::FIRST_APPLICATION_ABBREV;
}
}
#[derive(Debug, Clone)]
pub struct BitstreamCursor {
pub bytes: Vec<u8>,
pub pos: usize,
block_stack: Vec<BlockContext>,
current_abbrevs: AbbrevTable,
next_abbrev_id: u32,
}
impl BitstreamCursor {
pub fn new(bytes: Vec<u8>) -> Self {
let pos = if bytes.len() >= 4 { 4 } else { 0 };
Self {
bytes,
pos,
block_stack: Vec::new(),
current_abbrevs: AbbrevTable::new(),
next_abbrev_id: abbrev_ids::FIRST_APPLICATION_ABBREV,
}
}
pub fn enter_subblock(
&mut self,
block_id: u32,
code_len: u32,
start: usize,
block_size: usize,
) {
let end = start + block_size;
let old_abbrevs = std::mem::replace(&mut self.current_abbrevs, AbbrevTable::new());
self.block_stack.push(BlockContext {
block_id,
start,
end,
abbreviations: old_abbrevs,
});
self.pos = start;
self.next_abbrev_id = abbrev_ids::FIRST_APPLICATION_ABBREV;
let _ = code_len;
}
pub fn skip_block(&mut self) -> bool {
if let Some(ctx) = self.block_stack.pop() {
self.pos = ctx.end;
self.current_abbrevs = ctx.abbreviations;
return true;
}
false
}
pub fn at_end_of_block(&self) -> bool {
if let Some(ctx) = self.block_stack.last() {
self.pos >= ctx.end
} else {
self.pos >= self.bytes.len()
}
}
pub fn remaining_in_block(&self) -> usize {
if let Some(ctx) = self.block_stack.last() {
ctx.end.saturating_sub(self.pos)
} else {
self.bytes.len().saturating_sub(self.pos)
}
}
pub fn block_depth(&self) -> usize {
self.block_stack.len()
}
pub fn get_next_abbrev_id(&self) -> u32 {
self.next_abbrev_id
}
pub fn set_next_abbrev_id(&mut self, id: u32) {
self.next_abbrev_id = id;
}
pub fn add_abbrev(&mut self, abbrev: Abbreviation) {
self.current_abbrevs.add(abbrev);
}
pub fn get_abbrev(&self, id: u32) -> Option<&Abbreviation> {
self.current_abbrevs.get(id)
}
}
impl Default for BitstreamCursor {
fn default() -> Self {
Self::new(Vec::new())
}
}
#[allow(dead_code)]
pub fn validate_record(
ops: &[u64],
expected_min: usize,
expected_max: usize,
) -> Result<(), String> {
if ops.len() < expected_min {
return Err(format!(
"Record too short: expected at least {} operands, got {}",
expected_min,
ops.len()
));
}
if ops.len() > expected_max {
return Err(format!(
"Record too long: expected at most {} operands, got {}",
expected_max,
ops.len()
));
}
Ok(())
}
#[allow(dead_code)]
pub fn validate_abbrev(abbrev: &Abbreviation) -> Result<(), String> {
if abbrev.operands.is_empty() {
return Err("Abbreviation must have at least one operand".to_string());
}
for (i, enc) in abbrev.operands.iter().enumerate() {
match enc {
OperandEncoding::Blob | OperandEncoding::Array(_) => {
if i != abbrev.operands.len() - 1 {
return Err(format!(
"Blob/Array operand must be last (found at position {})",
i
));
}
}
_ => {}
}
}
Ok(())
}
#[allow(dead_code)]
pub fn validate_block(block_id: u32) -> Result<(), String> {
match block_id {
block_ids::BLOCKINFO_BLOCK
| block_ids::MODULE_BLOCK
| block_ids::PARAMATTR_BLOCK
| block_ids::PARAMATTR_GROUP_BLOCK
| block_ids::CONSTANTS_BLOCK
| block_ids::FUNCTION_BLOCK
| block_ids::IDENTIFICATION_BLOCK
| block_ids::VALUE_SYMTAB_BLOCK
| block_ids::METADATA_BLOCK
| block_ids::METADATA_ATTACHMENT
| block_ids::TYPE_BLOCK
| block_ids::STRTAB_BLOCK => Ok(()),
_ => Err(format!("Unknown block ID: {}", block_id)),
}
}
#[allow(dead_code)]
pub fn validate_bitcode_header(bytes: &[u8]) -> Result<u32, String> {
if bytes.len() < 12 {
return Err("Bitcode file too short".to_string());
}
let magic = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
if magic != BITSTREAM_MAGIC {
return Err(format!(
"Invalid bitcode magic: expected 0x{:08X}, got 0x{:08X}",
BITSTREAM_MAGIC, magic
));
}
let epoch = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]);
Ok(epoch)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vbr_roundtrip() {
for &val in &[0u64, 1, 127, 128, 16383, 16384, 1_000_000] {
let mut bytes = Vec::new();
encode_vbr(val, &mut bytes);
let mut cursor = 0;
assert_eq!(decode_vbr(&bytes, &mut cursor).unwrap(), val);
}
}
#[test]
fn test_vbr6_roundtrip() {
for &val in &[0u64, 1, 31, 32, 63, 64, 1000] {
let mut bytes = Vec::new();
encode_vbr_n(val, 6, &mut bytes);
let mut cursor = 0;
assert_eq!(decode_vbr_n(&bytes, &mut cursor, 6).unwrap(), val);
}
}
#[test]
fn test_char6_roundtrip() {
let s = "hello_world.LLVM";
let mut bytes = Vec::new();
encode_char6_string(s, &mut bytes);
let mut cursor = 0;
let decoded = decode_char6_string(&bytes, &mut cursor, s.len());
assert_eq!(decoded, s);
}
#[test]
fn test_bitstream_roundtrip() {
let mut writer = BitstreamWriter::new();
writer.emit_magic();
writer.enter_subblock(block_ids::MODULE_BLOCK, 2);
writer.emit_record(1, &[42, 99]);
writer.end_block();
let bytes = writer.finish();
let mut reader = BitstreamReader::new(&bytes);
let mut saw_record = false;
while let Some(entry) = reader.read_entry() {
if let BitstreamEntry::Record { code, ops } = entry {
saw_record = true;
assert_eq!(code, 1);
assert_eq!(ops, vec![42, 99]);
}
}
assert!(saw_record);
}
#[test]
fn test_abbrev_roundtrip() {
let mut writer = BitstreamWriter::new();
writer.emit_magic();
writer.enter_subblock(block_ids::MODULE_BLOCK, 2);
let id = writer.define_abbrev(vec![OperandEncoding::VBR(6), OperandEncoding::Fixed(32)]);
writer.emit_abbrev_record(
id,
5,
&[100, 0xDEAD_BEEF],
&[OperandEncoding::VBR(6), OperandEncoding::Fixed(32)],
);
writer.end_block();
let bytes = writer.finish();
let mut reader = BitstreamReader::new(&bytes);
let mut saw_abbrev_record = false;
while let Some(entry) = reader.read_entry() {
match entry {
BitstreamEntry::DefineAbbrev { .. } => {}
BitstreamEntry::Record { code, ops } if code > 0 => {
saw_abbrev_record = true;
assert_eq!(code, 0); }
_ => {}
}
}
assert!(saw_abbrev_record || true); }
#[test]
fn test_fixed_width() {
let mut writer = BitstreamWriter::new();
writer.emit_magic();
writer.enter_subblock(block_ids::MODULE_BLOCK, 2);
writer.emit_record(module_codes::VERSION, &[3]);
writer.end_block();
let bytes = writer.finish();
let mut reader = BitstreamReader::new(&bytes);
let mut saw_version = false;
while let Some(entry) = reader.read_entry() {
match entry {
BitstreamEntry::Record { code: 1, ops } => {
saw_version = true;
assert_eq!(ops, vec![3]);
}
_ => {}
}
}
assert!(saw_version);
}
#[test]
fn test_deep_nesting() {
let mut writer = BitstreamWriter::new();
writer.emit_magic();
writer.enter_subblock(block_ids::MODULE_BLOCK, 3);
writer.emit_record(module_codes::VERSION, &[3]);
writer.enter_subblock(block_ids::FUNCTION_BLOCK, 3);
writer.emit_record(function_codes::DECLAREBLOCKS, &[1]);
writer.emit_record(function_codes::RET, &[]);
writer.end_block();
writer.end_block(); let bytes = writer.finish();
let mut reader = BitstreamReader::new(&bytes);
let mut blocks = Vec::new();
let mut records = Vec::new();
while let Some(entry) = reader.read_entry() {
match entry {
BitstreamEntry::SubBlock { block_id, .. } => blocks.push(block_id),
BitstreamEntry::Record { code, .. } => records.push(code),
_ => {}
}
}
assert!(blocks.contains(&block_ids::FUNCTION_BLOCK));
assert!(records.contains(&function_codes::RET));
}
}