// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
fn BytesView::bytes(self : BytesView) -> Bytes = "%bytesview.bytes"
///|
fn BytesView::start(self : BytesView) -> Int = "%bytesview.start"
///|
fn BytesView::len(self : BytesView) -> Int = "%bytesview.len"
///|
fn BytesView::make(b : Bytes, start : Int, len : Int) -> BytesView = "%bytesview.make"
///|
/// Returns the number of bytes in the view.
///
/// Parameters:
///
/// * `bytes_view` : The view of a byte sequence.
///
/// Returns an integer representing the length of the view.
///
/// Example:
///
/// ```mbt check
/// test {
/// let bytes = b"\x00\x01\x02\x03\x04"
/// let view = bytes[2:4]
/// inspect(view.length(), content="2")
/// }
/// ```
#intrinsic("%bytesview.length")
pub fn BytesView::length(self : BytesView) -> Int {
self.len()
}
///|
/// Returns whether the bytes view is empty.
///
/// Example:
///
/// ```mbt check
/// test {
/// let view = b"\x00\x01"[1:1]
/// inspect(view.is_empty(), content="true")
/// let view = b"\x00\x01"[0:1]
/// inspect(view.is_empty(), content="false")
/// }
/// ```
pub fn BytesView::is_empty(self : BytesView) -> Bool {
self.length() == 0
}
///|
/// Retrieves a byte from the view at the specified index.
///
/// Parameters:
///
/// * `self` : The bytes view to retrieve the byte from.
/// * `index` : The position in the view from which to retrieve the byte.
///
/// Returns the byte at the specified index if the index is valid.
///
/// Example:
///
/// ```mbt check
/// test {
/// let bytes = b"\x01\x02\x03\x04\x05"
/// let view = bytes[1:4] // view contains [0x02, 0x03, 0x04]
/// inspect(view[1], content="b'\\x03'")
/// }
/// ```
#intrinsic("%bytesview.get")
#alias("_[_]")
pub fn BytesView::at(self : BytesView, index : Int) -> Byte {
guard index >= 0 && index < self.length() else {
index_out_of_bounds(self.length(), index)
}
self.bytes().unsafe_get(self.start() + index)
}
///|
/// Retrieves a byte from the view at the specified index.
///
/// Parameters:
///
/// * `self` : The bytes view to retrieve the byte from.
/// * `index` : The position in the view from which to retrieve the byte.
///
/// Returns the byte at the specified index, or None if the index is out of bounds.
///
/// Example:
///
/// ```mbt check
/// test {
/// let bytes = b"\x01\x02\x03\x04\x05"
/// let view = bytes[1:4]
/// let result = view.get(1)
/// debug_inspect(
/// result,
/// content=(
/// #|Some(0x03)
/// ),
/// )
/// let bytes = b"\x01\x02\x03\x04\x05"
/// let view = bytes[1:4]
/// let result = view.get(5)
/// debug_inspect(result, content="None")
/// }
/// ```
#intrinsic("%bytesview.get_opt")
pub fn BytesView::get(self : BytesView, index : Int) -> Byte? {
guard index >= 0 && index < self.length() else { None }
Some(self.bytes().unsafe_get(self.start() + index))
}
///|
/// Retrieves a byte at the specified index from a bytes view without performing
/// bounds checking.
///
/// Parameters:
///
/// * `self` : The bytes view to retrieve the byte from.
/// * `index` : The position in the view from which to retrieve the byte. The
/// index is relative to the start of the view, not the underlying bytes.
///
/// Returns a single byte from the specified position in the view.
///
/// Throws a panic if the index is out of bounds (less than 0 or greater than or
/// equal to the length of the view).
///
/// Example:
///
/// ```mbt check
/// test {
/// let bytes = b"\x01\x02\x03\x04\x05"
/// let view = bytes[2:4] // view contains [0x03, 0x04]
/// inspect(view.unsafe_get(0), content="b'\\x03'")
/// }
/// ```
///
#intrinsic("%bytesview.unsafe_get")
#internal(unsafe, "Panic if index is out of bounds")
#doc(hidden)
pub fn BytesView::unsafe_get(self : BytesView, index : Int) -> Byte {
self.bytes().unsafe_get(self.start() + index)
}
///|
/// Creates a new `View` from the given `Bytes`.
///
/// # Example
///
/// ```mbt check
/// test {
/// let bs = b"\x00\x01\x02\x03\x04\x05"
/// let bv = bs[1:4]
/// inspect(bv.length(), content="3")
/// @test.assert_eq(bv[0], b'\x01')
/// @test.assert_eq(bv[1], b'\x02')
/// @test.assert_eq(bv[2], b'\x03')
/// }
/// ```
#intrinsic("%bytes.view")
#alias("_[_:_]")
#alias(sub, deprecated="Use _[_:_] instead")
pub fn Bytes::view(self : Bytes, start? : Int = 0, end? : Int) -> BytesView {
let len = self.length()
let end = match end {
Some(end) | (None with end = len) => end
}
guard start >= 0 && start <= end && end <= len else {
abort("Invalid index for View")
}
BytesView::make(self, start, end - start)
}
///|
/// Returns a view of the bytes between `start` and `end`, or `None` if the range
/// is invalid. Unlike `Bytes::view` (a.k.a. `b[start:end]`), this variant does
/// not abort on out-of-bounds indices, making it suitable for composition with
/// pattern matching:
///
/// ```mbt check
/// test {
/// let bs = b"\x00\x01\x02\x03\x04\x05"
/// if bs.get_view(start=1) is Some([b'\x01', b'\x02', ..]) {
/// ()
/// } else {
/// abort("unreachable")
/// }
/// debug_inspect(bs.get_view(start=10), content="None")
/// }
/// ```
pub fn Bytes::get_view(
self : Bytes,
start? : Int = 0,
end? : Int,
) -> BytesView? {
let len = self.length()
let end = match end {
Some(end) | (None with end = len) => end
}
guard start >= 0 && start <= end && end <= len else { None }
Some(BytesView::make(self, start, end - start))
}
///|
/// Returns a sub-view of the view between `start` and `end`, or `None` if the
/// range is invalid. The optional variant of `BytesView::view` (a.k.a.
/// `bv[start:end]`).
pub fn BytesView::get_view(
self : BytesView,
start? : Int = 0,
end? : Int,
) -> BytesView? {
let len = self.length()
let end = match end {
Some(end) | (None with end = len) => end
}
guard start >= 0 && start <= end && end <= len else { None }
Some(BytesView::make(self.bytes(), self.start() + start, end - start))
}
///|
/// Creates a new `View` from the given `View`.
///
/// # Example
///
/// ```mbt check
/// test {
/// let bv = b"\x00\x01\x02\x03\x04\x05"[:]
/// let bv2 = bv[1:4]
/// inspect(bv2.length(), content="3")
/// @test.assert_eq(bv2[1], b'\x02')
/// }
/// ```
#intrinsic("%bytesview.view")
#alias("_[_:_]")
#alias(sub, deprecated="Use _[_:_] instead")
pub fn BytesView::view(
self : BytesView,
start? : Int = 0,
end? : Int,
) -> BytesView {
let len = self.length()
let end = match end {
Some(end) | (None with end = len) => end
}
guard start >= 0 && start <= end && end <= len else {
abort("Invalid index for View")
}
BytesView::make(self.bytes(), self.start() + start, end - start)
}
///|
/// Returns an iterator over the `View`.
///
/// # Example
///
/// ```mbt check
/// test {
/// let bv = b"\x00\x01\x02\x03\x04\x05"[:]
/// let mut sum = 0
/// bv.iter().each(x => sum += x.to_int())
/// inspect(sum, content="15")
/// }
/// ```
#alias(iterator, deprecated)
pub fn BytesView::iter(self : BytesView) -> Iter[Byte] {
let mut i = 0
let len = self.length()
Iter::new(
fn() {
guard i < len else { None }
let result = self.unsafe_get(i)
i += 1
Some(result)
},
size_hint=len,
)
}
///|
/// Returns an iterator over the `View` with index.
///
/// Example:
///
/// ```mbt check
/// test {
/// let buf = StringBuilder(size_hint=5)
/// let keys = []
/// let it = b"abcde"[:].iter2()
/// while it.next() is Some((i, x)) {
/// buf.write_string(x.to_string())
/// keys.push(i)
/// }
/// inspect(buf, content="b'\\x61'b'\\x62'b'\\x63'b'\\x64'b'\\x65'")
/// debug_inspect(keys, content="[0, 1, 2, 3, 4]")
/// }
/// ```
#alias(iterator2, deprecated)
pub fn BytesView::iter2(self : BytesView) -> Iter2[Int, Byte] {
let mut i = 0
let len = self.length()
Iter2::new(
fn() {
guard i < len else { None }
let result = (i, self.unsafe_get(i))
i += 1
Some(result)
},
size_hint=len,
)
}
///|
pub impl Show for BytesView with fn output(self, logger) {
logger.write_string("b\"")
for byte in self {
if byte is (b' '..=b'~') && byte != b'"' && byte != b'\\' {
logger.write_char(byte.to_char())
} else {
logger.write_string("\\x")
logger.write_string(byte.to_hex())
}
}
logger.write_string("\"")
}
///|
pub impl Show for Bytes with fn output(self, logger) {
BytesView::output(self, logger)
}
///|
/// Compares two views for equality. Returns true only if both views
/// have the same length and contain identical bytes in the same order.
///
/// Parameters:
///
/// * `self` : The first view to compare.
/// * `other` : The second view to compare.
///
/// Returns `true` if the byte sequences are equal, `false` otherwise.
///
/// Example:
/// ```mbt check
/// test {
/// let bytes = b"abcabc"
/// inspect(bytes[0:3] == bytes[3:6], content="true")
/// inspect(bytes[0:3] == bytes[2:5], content="false")
/// inspect(bytes[0:4] == bytes[3:6], content="false")
/// }
/// ```
pub impl Eq for BytesView with fn equal(self, other) -> Bool {
let len = self.length()
guard len == other.length() else { return false }
self
.bytes()
.unsafe_range_equal(
other.bytes(),
self_off=self.start(),
other_off=other.start(),
len~,
)
}
///|
/// Compares a `BytesView` to a `Bytes` byte-for-byte.
///
/// This is the cross-type equivalent of `==` and avoids materializing a fresh
/// `Bytes` (or a wrapping `BytesView`) when probing an owned-`Bytes`-keyed
/// container with a view-shaped key.
///
/// Returns `true` if the lengths match and every byte in `self` equals the
/// byte at the same index in `other`.
///
/// Example:
/// ```mbt check
/// test {
/// let buf = b"prefix_hello_suffix"
/// inspect(buf[7:12].equal_to_bytes(b"hello"), content="true")
/// inspect(buf[7:12].equal_to_bytes(b"world"), content="false")
/// }
/// ```
pub fn BytesView::equal_to_bytes(self : BytesView, other : Bytes) -> Bool {
let len = self.length()
guard len == other.length() else { return false }
self
.bytes()
.unsafe_range_equal(other, self_off=self.start(), other_off=0, len~)
}
///|
/// Compares two views based on shortlex order. First compares the lengths of
/// the views, then compares bytes pairwise until a difference is found or
/// all bytes have been compared.
///
/// Parameters:
///
/// * `self` : The first view to compare.
/// * `other` : The second byte sequence to compare.
///
/// Returns an integer indicating the relative order:
///
/// * A negative value if `self` is less than `other`
/// * Zero if `self` equals `other`
/// * A positive value if `self` is greater than `other`
///
/// Example:
///
/// ```mbt check
/// test {
/// let bytes = b"abcabc"
/// inspect(bytes[0:3].compare(bytes[3:6]), content="0") // abc = abc
/// inspect(bytes[0:3].compare(bytes[2:5]), content="-1") // abc < cab
/// inspect(bytes[1:4].compare(bytes[3:6]), content="1") // bca > abc
/// inspect(bytes[0:3].compare(bytes[0:4]), content="-1") // abc < abca
/// inspect(bytes[1:5].compare(bytes[2:5]), content="1") // bcab > cab
/// }
/// ```
pub impl Compare for BytesView with fn compare(self, other) -> Int {
let self_len = self.length()
let other_len = other.length()
let cmp = self_len.compare(other_len)
guard cmp == 0 else { return cmp }
self.lexical_compare(other)
}
///|
/// Performs a lexicographical comparison of two byte views.
///
/// This method returns the lexicographical ordering by byte value. Unlike the
/// `Compare` trait implementation which uses shortlex order (shorter views come
/// first), this method compares based purely on byte values until a difference
/// is found.
///
/// # Returns
///
/// - A negative integer if `self` is lexicographically less than `other`
/// - Zero if `self` is lexicographically equal to `other`
/// - A positive integer if `self` is lexicographically greater than `other`
///
/// # Example
///
/// ```mbt check
/// test {
/// inspect(b"\x01\x02"[:].lexical_compare(b"\x01\x02\x03"), content="-1")
/// inspect(b"\x01\x02\x03"[:].lexical_compare(b"\x01\x02"), content="1")
/// inspect(b"\x01\x02\x03"[:].lexical_compare(b"\x01\x02\x03"), content="0")
/// inspect(b"\x01\x02\x03"[:].lexical_compare(b"\x01\x02\x04"), content="-1")
/// }
/// ```
pub fn BytesView::lexical_compare(self : BytesView, other : BytesView) -> Int {
self.bytesview_lexical_compare_impl(other)
}
///|
#cfg(any(target="native", target="wasm"))
fn BytesView::bytesview_lexical_compare_impl(
self : BytesView,
other : BytesView,
) -> Int {
let self_len = self.length()
let other_len = other.length()
let min_len = if self_len < other_len { self_len } else { other_len }
// Avoid v128 setup for the common case where long inputs differ immediately.
if min_len >= 16 {
let cmp = self.unsafe_get(0).compare(other.unsafe_get(0))
if cmp != 0 {
return cmp
}
let cmp = self.unsafe_get(1).compare(other.unsafe_get(1))
if cmp != 0 {
return cmp
}
}
let self_bytes = unsafe_from_bytes(self.bytes())
let other_bytes = unsafe_from_bytes(other.bytes())
let self_start = self.start()
let other_start = other.start()
let mut i = 0
let block_limit = min_len - min_len % 16
while i < block_limit {
let a = v128_load(self_bytes, self_start + i)
let b = v128_load(other_bytes, other_start + i)
let diff_bits = i8x16_bitmask(i8x16_eq(a, b)) ^ 0xFFFF
if diff_bits != 0 {
let lane = diff_bits.ctz()
return self_bytes
.unsafe_get(self_start + i + lane)
.compare(other_bytes.unsafe_get(other_start + i + lane))
}
i += 16
}
while i < min_len {
let cmp = self.unsafe_get(i).compare(other.unsafe_get(i))
if cmp != 0 {
return cmp
}
i += 1
}
self_len.compare(other_len)
}
///|
#cfg(not(any(target="native", target="wasm")))
fn BytesView::bytesview_lexical_compare_impl(
self : BytesView,
other : BytesView,
) -> Int {
let self_len = self.length()
let other_len = other.length()
let min_len = if self_len < other_len { self_len } else { other_len }
for i in 0.. Bytes {
self.bytes()
}
///|
/// Retrieves the start index of the view.
pub fn BytesView::start_offset(self : BytesView) -> Int {
self.start()
}
///|
/// Return a `Bytes` value containing exactly this view's range.
///
/// If the view spans the full underlying bytes, this returns the original value
/// without copying; otherwise it allocates and copies.
///
/// Example:
///
/// ```mbt check
/// test {
/// let b = b"hello"
/// inspect(b[1:4].to_owned().length(), content="3")
/// }
/// ```
#alias(to_bytes, deprecated="Use `to_owned` to allocate an owned `Bytes` from a `BytesView`")
pub fn BytesView::to_owned(self : BytesView) -> Bytes {
if self.length() == self.bytes().length() {
// If the view covers the entire bytes, return the original bytes to avoid copying
return self.bytes()
}
let bytes = FixedArray::make(self.length(), (0 : Byte))
bytes.blit_from_bytes(0, self.bytes(), self.start_offset(), self.length())
unsafe_to_bytes(bytes)
}
///|
pub impl ToJson for BytesView with fn to_json(self) -> Json {
let sb = StringBuilder()
for byte in self {
if byte is (b' '..=b'~') && byte != b'"' && byte != b'\\' {
sb.write_char(byte.to_char())
} else {
sb.write_string("\\x")
sb.write_string(byte.to_hex())
}
}
Json::string(sb.to_string())
}
///|
/// Converts a `Bytes` value to a JSON representation.
/// The representation is picked for easier debugging.
/// Printable ASCII characters (from space to tilde, excluding '"' and '\') are output as-is.
/// All other bytes are represented as \xHH, where HH is the two-digit hexadecimal value of the byte.
pub impl ToJson for Bytes with fn to_json(self : Bytes) -> Json {
BytesView::to_json(self)
}