Skip to content

No Cast() or MHFromBytes() utility functions #56

Description

@sumanjeet0012

Go provides Cast(buf []byte) (Multihash, error) for validating and casting raw bytes to a Multihash, and MHFromBytes(buf []byte) (int, Multihash, error) for reading a multihash from the beginning of a buffer with bytes-consumed tracking. Python has decode() but no direct equivalents.

Problem

Go's multihash.go:

// Cast casts a buffer onto a multihash, and returns an error if it does not work.
func Cast(buf []byte) (Multihash, error) {
    _, err := Decode(buf)
    if err != nil { return nil, err }
    return Multihash(buf), nil
}

// MHFromBytes reads a multihash from the given byte buffer,
// returning the number of bytes read and the multihash.
func MHFromBytes(buf []byte) (int, Multihash, error) {
    nr, _, _, err := readMultihashFromBuf(buf)
    if err != nil { return 0, nil, err }
    return nr, Multihash(buf[:nr]), nil
}

MHFromBytes is particularly useful when parsing a stream that contains a multihash followed by other data — it tells you exactly how many bytes the multihash consumed.

Python's decode() always consumes the entire input and doesn't return bytes consumed.

Proposed Solution

Add both functions:

def cast(buf: bytes) -> bytes:
    """Validate and return multihash bytes. Raises ValueError if invalid."""
    decode(buf)  # Validates
    return buf

def from_bytes_with_length(buf: bytes) -> tuple[int, "Multihash"]:
    """Read a multihash from the beginning of buf.

    Returns (bytes_consumed, Multihash).
    Unlike decode(), this handles buffers with trailing data.
    """
    buffer = BytesIO(buf)
    code = varint.decode_stream(buffer)
    if not is_valid_code(code):
        raise ValueError(f"Unsupported hash code {code}")
    length = varint.decode_stream(buffer)
    digest = buffer.read(length)
    if len(digest) != length:
        raise ValueError(f"Insufficient data: expected {length} bytes, got {len(digest)}")
    bytes_consumed = buffer.tell()
    name = constants.CODE_HASHES.get(code, code)
    return bytes_consumed, Multihash(code=code, name=name, length=length, digest=digest)

Export from __init__.py and add tests.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions