Struct Blob

pub struct Blob<'a> { /* private fields */ }
Expand description

ECMA-335 binary blob heap providing indexed access to variable-length data.

The Blob struct represents the #Blob metadata heap according to ECMA-335 Section II.24.2.4. It provides safe, zero-copy access to binary data referenced by metadata table entries such as method signatures, field types, custom attributes, and constant values.

§Blob Heap Properties

§Indexed Access Model

Blobs are accessed by offset rather than sequential iteration:

  • Metadata tables store blob heap offsets as indices
  • Each offset points to a size-prefixed binary chunk
  • Size encoding uses compressed unsigned integers
  • Random access enables efficient metadata parsing

§Size Encoding Details

The blob size prefix uses ECMA-335 compressed format:

Format           | Size Range        | Encoding
-----------------|-------------------|------------------
1-byte           | 0-127 bytes      | 0bbbbbbb
2-byte           | 128-16,383 bytes | 10bbbbbb xxxxxxxx
4-byte           | 16,384+ bytes    | 110bbbbb xxxxxxxx yyyyyyyy zzzzzzzz

§Memory Layout

Offset | Content
-------|------------------
0      | 0x00 (null blob)
1      | Size₁ | Data₁
N      | Size₂ | Data₂
M      | Size₃ | Data₃

§Blob Content Categories

§Signature Blobs

Method, field, and property signatures:

  • Calling conventions and parameter information
  • Type specifications and generic parameters
  • Custom modifiers and constraints

§Custom Attribute Blobs

Constructor arguments and named parameters:

  • Primitive arrays and enum values
  • Complex nested data structures
  • String and object references

§Constant Value Blobs

Default values for fields and parameters:

  • Primitive type constants
  • String literals and null values
  • Binary data for complex constants

§Safety and Error Handling

The Blob struct provides comprehensive safety guarantees:

  • Bounds checking for all offset and size calculations
  • Validation of compressed size encoding format
  • Protection against integer overflow in size calculations
  • Graceful handling of truncated or malformed data

§Examples

§Creating and Accessing Blobs

use dotscope::metadata::streams::Blob;

// Create blob heap with sample data
let heap_data = &[
    0x00,                           // Required null blob at offset 0
    0x04, 0x01, 0x02, 0x03, 0x04,  // 4-byte blob at offset 1
    0x02, 0xFF, 0xFE,              // 2-byte blob at offset 6
];

let blob_heap = Blob::from(heap_data)?;

// Access specific blobs by offset (as referenced by metadata tables)
let first_blob = blob_heap.get(1)?;
assert_eq!(first_blob, &[0x01, 0x02, 0x03, 0x04]);

let second_blob = blob_heap.get(6)?;
assert_eq!(second_blob, &[0xFF, 0xFE]);

§Handling Large Blobs

use dotscope::metadata::streams::Blob;

// Large blob with 2-byte size encoding
let mut heap_data = vec![0x00];                    // Null blob
heap_data.push(0x81);                             // Size: 10000001 (2-byte format)
heap_data.push(0x00);                             // Size: 00000000 (total = 128)
heap_data.extend(std::iter::repeat(0xAA).take(128)); // 128 bytes of data

let blob_heap = Blob::from(&heap_data)?;
let large_blob = blob_heap.get(1)?;
assert_eq!(large_blob.len(), 128);
assert!(large_blob.iter().all(|&b| b == 0xAA));

§Iterating Through All Blobs

use dotscope::metadata::streams::Blob;

let heap_data = &[0x00, 0x03, 0x41, 0x42, 0x43, 0x01, 0x44];
let blob_heap = Blob::from(heap_data)?;

for result in &blob_heap {
    let (offset, data) = result?;
    println!("Blob at offset {}: {:02X?}", offset, data);
}

§ECMA-335 Compliance

This implementation fully supports ECMA-335 requirements:

  • Correct handling of all three size encoding formats
  • Mandatory null blob at offset 0
  • Proper bounds checking and error handling
  • Support for maximum blob sizes (up to 2^29 bytes)

§See Also

Implementations§

§

impl<'a> Blob<'a>

pub fn from(data: &'a [u8]) -> Result<Blob<'a>>

Creates a new Blob heap accessor from raw metadata bytes.

Validates that the provided data represents a well-formed ECMA-335 #Blob heap with the required null blob at offset 0. The blob heap stores variable-length binary data referenced by metadata table entries.

§Parameters
  • data - Raw bytes containing the complete #Blob heap data
§Returns

A Blob instance providing safe access to the heap contents, or an error if the data format is invalid.

§Errors

Returns crate::Error in the following cases:

  • Empty data: The heap must contain at least one byte
  • Missing null blob: First byte must be 0x00 per ECMA-335 requirements
  • Invalid format: Data doesn’t conform to blob heap structure
§Examples
§Valid Blob Heap
use dotscope::metadata::streams::Blob;

let valid_data = &[
    0x00,                    // Required null blob at offset 0
    0x04, 0x01, 0x02, 0x03, 0x04,  // 4-byte blob
    0x02, 0xFF, 0xFE,        // 2-byte blob
];

let blob_heap = Blob::from(valid_data)?;
assert_eq!(blob_heap.get(1)?, &[0x01, 0x02, 0x03, 0x04]);
§Invalid Blob Heap
use dotscope::metadata::streams::Blob;

// Missing required null byte at offset 0
let invalid_data = &[0x01, 0x02, 0x03];
assert!(Blob::from(invalid_data).is_err());

// Empty data
let empty_data = &[];
assert!(Blob::from(empty_data).is_err());
§Safety

This method performs minimal validation for performance. Individual blob access via get provides comprehensive bounds checking.

§See Also
  • get: Access individual blobs by offset
  • iter: Iterate over all blobs in the heap
  • ECMA-335 II.24.2.4: Blob heap specification

pub fn get(&self, index: usize) -> Result<&'a [u8]>

Retrieves a blob from the heap by its offset.

Returns a zero-copy view of the binary data stored at the specified offset. The offset typically comes from metadata table entries that reference blob heap data such as method signatures, custom attributes, or constant values.

§Parameters
  • index - Byte offset within the blob heap (from metadata table references)
§Returns

A slice containing the blob data at the specified offset, or an error if the offset is invalid or the blob is malformed.

§Errors

Returns crate::Error in the following cases:

  • Out of bounds: index exceeds the heap size
  • Invalid size encoding: Blob size header is malformed
  • Truncated data: Blob extends beyond heap boundaries
  • Integer overflow: Size calculations exceed platform limits
§Examples
§Basic Blob Access
use dotscope::metadata::streams::Blob;

let data = &[
    0x00,                           // Null blob at offset 0
    0x03, 0x41, 0x42, 0x43,        // "ABC" at offset 1
    0x81, 0x02,                     // 2-byte size encoding for 258 bytes
    // ... 258 bytes of data would follow
];

let blob_heap = Blob::from(&data[..6])?; // Truncated for example

// Access null blob (always empty)
let null_blob = blob_heap.get(0)?;
assert_eq!(null_blob, &[]);

// Access first real blob
let first_blob = blob_heap.get(1)?;
assert_eq!(first_blob, b"ABC");
§Error Handling
use dotscope::metadata::streams::Blob;

let data = &[0x00, 0x02, 0x41]; // Blob claims 2 bytes but only 1 available
let blob_heap = Blob::from(data)?;

// This will fail due to truncated data
assert!(blob_heap.get(1).is_err());

// Out of bounds access
assert!(blob_heap.get(100).is_err());
§Size Encoding Details

The blob size is encoded using ECMA-335 compressed unsigned integers:

EncodingFirst Byte PatternSize Range
1-byte0bbbbbbb0-127
2-byte10bbbbbb128-16,383
4-byte110bbbbb16,384+
§See Also

pub fn iter(&self) -> BlobIterator<'_>

Returns an iterator over all blobs in the heap.

Provides sequential access to every blob stored in the heap, yielding both the offset and binary data for each entry. This is useful for comprehensive analysis, validation, or debugging of blob heap contents.

§Returns

A BlobIterator that yields Result<(usize, &[u8])> tuples containing:

  • Offset: Byte position of the blob within the heap
  • Data: Zero-copy slice of the blob’s binary content
§Iteration Behavior
  • Sequential access: Blobs returned in heap order (not offset order)
  • Skips null blob: Iterator starts at offset 1, skipping the null blob at 0
  • Error handling: Returns errors for malformed blobs but continues iteration
  • Zero-copy: Each blob is a direct slice reference to heap data
§Examples
§Basic Iteration
use dotscope::metadata::streams::Blob;

let data = &[
    0x00,                    // Null blob (skipped by iterator)
    0x03, 0x41, 0x42, 0x43, // "ABC" blob at offset 1
    0x02, 0x44, 0x45,       // "DE" blob at offset 5
    0x00,                   // Empty blob at offset 8
];

let blob_heap = Blob::from(data)?;

for result in blob_heap.iter() {
    let (offset, blob_data) = result?;
    println!("Blob at offset {}: {} bytes", offset, blob_data.len());
}
§Error Handling During Iteration
use dotscope::metadata::streams::Blob;

let data = &[0x00, 0x05, 0x41, 0x42]; // Claims 5 bytes but only 2 available
let blob_heap = Blob::from(data)?;

for result in blob_heap.iter() {
    match result {
        Ok((offset, blob_data)) => {
            println!("Valid blob at {}: {:02X?}", offset, blob_data);
        }
        Err(e) => {
            eprintln!("Malformed blob: {}", e);
            break; // Stop on first error
        }
    }
}
§Collecting All Valid Blobs
use dotscope::metadata::streams::Blob;

let data = &[0x00, 0x02, 0x41, 0x42, 0x01, 0x43];
let blob_heap = Blob::from(data)?;

let blobs: Result<Vec<_>, _> = blob_heap.iter().collect();
let blobs = blobs?;

assert_eq!(blobs.len(), 2);
assert_eq!(blobs[0], (1, &[0x41, 0x42][..]));
assert_eq!(blobs[1], (4, &[0x43][..]));
§Error Recovery

If a malformed blob is encountered, the iterator returns an error but can potentially continue with subsequent blobs if the heap structure allows recovery. This design enables partial processing of corrupted metadata.

§See Also

Trait Implementations§

§

impl<'a> IntoIterator for &'a Blob<'a>

§

type Item = Result<(usize, &'a [u8]), Error>

The type of the elements being iterated over.
§

type IntoIter = BlobIterator<'a>

Which kind of iterator are we turning this into?
§

fn into_iter(self) -> Self::IntoIter

Creates an iterator from a value. Read more

Auto Trait Implementations§

§

impl<'a> Freeze for Blob<'a>

§

impl<'a> RefUnwindSafe for Blob<'a>

§

impl<'a> Send for Blob<'a>

§

impl<'a> Sync for Blob<'a>

§

impl<'a> Unpin for Blob<'a>

§

impl<'a> UnwindSafe for Blob<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.