//! `Bytes`, an immutable buffer of bytes, and the functions that make one.
//!
//! The bytes of a `Bytes` value carry no encoding, where a `String`'s are
//! always UTF-8, and nothing converts between the two implicitly.
//! `bytes_from_string` and `bytes_from_ints` make a `Bytes`, and its methods
//! measure, index, slice, join and search it, all counting in bytes; none
//! changes it. `to_string` turns bytes that are valid UTF-8 back into a
//! `String`. `Bytes` implements `Eq`: `a.eq(b)` compares two buffers byte for
//! byte.
//!
//! Making `Bytes` from a string and from integers, measuring, slicing,
//! joining and searching them, and turning them back into a `String`:
//!
//! ```
//! let greeting = bytes_from_string("hi!")
//! assert_eq(greeting.len(), 3)
//! assert_eq(greeting.byte_at(0).unwrap(), 104) // 'h'
//! assert_eq(greeting.slice(1, 99).to_string().unwrap(), "i!") // bounds are clamped
//!
//! let line = greeting.concat(bytes_from_ints([10])) // a line feed
//! assert(line.eq(bytes_from_string("hi!\n")), "equal byte for byte")
//! assert_eq(line.index_of(bytes_from_string("!")).unwrap(), 2)
//!
//! // Bytes carry no encoding: these two are not valid UTF-8.
//! let raw = bytes_from_ints([0xff, 0xfe])
//! assert(raw.to_string().is_none(), "not UTF-8, so no String")
//! ```
// Nova standard library — byte buffers.
//
// Compiled as an implicit module and glob-imported into every user module, so
// these names need no `import` (see docs/adr/0004-stdlib-compile-model.md).
//
// `Bytes` is an immutable byte buffer. It shares `String`'s representation --
// a `{len, ptr}` leaf header, and every `Bytes` value is one object with its
// bytes inline after it -- and differs in carrying no encoding guarantee.
// Nothing converts between them implicitly.
//
// This module follows `std/strings`' structure: a small intrinsic core with the
// searching and bounds checking done here in Nova, rather than one intrinsic per
// method.
impl Bytes {
// The number of bytes. Not a character count: `Bytes` has no encoding.
/// Returns the number of bytes.
pub fn len(self) -> Int { bytes_len(self) }
// These bytes as a `String`, or `None` if they are not valid UTF-8.
//
// `Option` rather than `Result<String, IoError>`: there is exactly one way
// this fails, so an error kind would carry no information, and a byte type
// should not depend on an I/O error type.
/// Returns the bytes as a `String`, or `None` if they are not valid UTF-8.
pub fn to_string(self) -> Option<String> {
if bytes_is_utf8(self) { Some(bytes_to_string_unchecked(self)) } else { None }
}
// The byte at `i`, or `None` if `i` is out of range.
//
// `Option` rather than an abort, matching `String::char_at`; a negative
// index is `None` for the same reason.
/// Returns the byte at index `i`, as an `Int` from 0 to 255, or `None` if
/// `i` is negative or not less than `len()`.
pub fn byte_at(self, i: Int) -> Option<Int> {
if i < 0 { return None }
if i >= self.len() { return None }
Some(bytes_at(self, i))
}
// The bytes in `start..end`, clamped to this buffer. Deliberately NOT
// what `String::slice` does -- that one panics on an out-of-range bound.
// A `Bytes` length often comes from disk rather than from a caller who
// already knows it, so clamping (rather than aborting the process) is
// the better failure mode here. Recorded, accepted divergence -- see the
// design spec's dated note -- not a claim that `String::slice` agrees.
/// Returns the bytes from index `start` up to, but not including, `end`.
///
/// Unlike `String::slice`, it never panics: each bound is clamped to
/// `0..=len()`, and a `start` at or after `end` gives empty bytes.
pub fn slice(self, start: Int, end: Int) -> Bytes { bytes_slice(self, start, end) }
// These bytes followed by `other`'s.
/// Returns these bytes followed by `other`'s.
pub fn concat(self, other: Bytes) -> Bytes { bytes_concat(self, other) }
// Each byte as an `Int` in `0..=255`.
/// Returns each byte as an `Int` from 0 to 255, in order.
pub fn to_ints(self) -> [Int] { bytes_to_ints(self) }
// The index of `needle`'s first occurrence, or `None`.
//
// Searched here in Nova over an `[Int]` rather than by an intrinsic -- one
// runtime call per operand instead of one per byte compared. That is the
// shape `String::index_of` used over `str_chars` until 2026-10-02, when it
// moved to a runtime byte search.
/// Returns the index at which `needle` first occurs, or `None` if it does
/// not occur.
///
/// An empty `needle` occurs at index 0.
pub fn index_of(self, needle: Bytes) -> Option<Int> {
let h = self.to_ints()
let n = needle.to_ints()
if n.len() == 0 { return Some(0) }
if n.len() > h.len() { return None }
let last = h.len() - n.len()
let mut at = 0
while at <= last {
let mut k = 0
let mut same = true
while k < n.len() {
if h[at + k] != n[k] { same = false }
k = k + 1
}
if same { return Some(at) }
at = at + 1
}
None
}
// Whether `needle` occurs in these bytes.
/// Returns `true` if `needle` occurs in these bytes. An empty `needle`
/// always does.
pub fn contains(self, needle: Bytes) -> Bool { self.index_of(needle).is_some() }
}
// A `Bytes` holding `s`'s UTF-8 bytes.
//
// A free function rather than a method on `String`, so `std/strings` needs no
// knowledge of `Bytes`.
//
// Calls `bytes_from_string_intrinsic`, not a builtin of this same name: this
// module is itself a `STD_MODULES` entry, so `Builtin::STD_ONLY` -- including
// the builtin backing this very function -- is seeded into this module's own
// scope before this function is collected (`nova_resolver::resolve_program`'s
// pass order). A builtin named `bytes_from_string` here would make this
// definition collide with itself: `insert_value` reports `E0002 duplicate
// definition` ("is a compiler builtin") rather than silently compiling the
// call below into a recursive call, and rather than silently letting this
// definition shadow the builtin -- there is no such shadowing path for a
// module's own item colliding with something already in its own scope. See
// `Builtin::BytesFromString`'s doc comment (`nova-resolver`) and the Task 2
// report for the verification.
/// Returns the UTF-8 bytes of `s`.
pub fn bytes_from_string(s: String) -> Bytes { bytes_from_string_intrinsic(s) }
// A `Bytes` holding each `Int` in `ints` as one byte.
//
// Aborts if any element is outside `0..=255`: that is a caller error, and a
// `Result` here would infect every construction path.
//
// Calls `bytes_from_ints_intrinsic`, not a builtin of this same name, for the
// same reason `bytes_from_string` above calls `bytes_from_string_intrinsic`:
// this module is a `STD_MODULES` entry, so a builtin named `bytes_from_ints`
// would collide with this very definition (`E0002`) rather than being called
// by it. See `Builtin::BytesFromInts`'s doc comment (`nova-resolver`).
/// Returns a `Bytes` holding each `Int` of `ints` as one byte, in order.
///
/// Panics if any element is outside `0..=255`.
pub fn bytes_from_ints(ints: [Int]) -> Bytes { bytes_from_ints_intrinsic(ints) }
impl Eq for Bytes {
fn eq(self, other: Bytes) -> Bool { bytes_eq(self, other) }
}