// 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.

///|
/// Extensible buffer.
///
/// It provides accumulative concatenation of bytes in linear time.
/// The capacity of buffer will automatically expand as necessary.
///
/// Note: StringBuilder is recommended for string concatenation in favor of
/// Buffer, since it is optimized for all targets.
/// # Usage
///
/// ```mbt nocheck
///   let buf = Buffer(size_hint=100)
///   buf.write_string_utf16le("Tes")
///   buf.write_char_utf16le('t')
///   inspect(
///     buf.contents(), 
///     content=(
///       
///   #|b"T\x00e\x00s\x00t\x00"
///
///     ),
///   )
/// ```
struct Buffer {
  mut data : FixedArray[Byte]
  mut len : Int
}

///|
/// Compute the next capacity without allocating. Since appends never shrink the
/// buffer, `required < len` means the required-size calculation overflowed.
fn buffer_growth_capacity(current : Int, len : Int, required : Int) -> Int {
  if required < len {
    abort("Buffer capacity overflow")
  }
  let start = if current <= 0 { 1 } else { current }
  let enough_space = for space = start {
    if space >= required {
      break space
    }
    let next = space * 2
    if next <= space {
      break required
    }
    continue next
  }
  enough_space
}

///|
/// Grow the buffer to at least `required`. Callers keep the capacity check on
/// their fast path and enter here only when growth or overflow handling is
/// needed. The buffer invariant `0 <= len <= data.length()` lets fixed-size
/// appends compare against the remaining capacity without overflowing.
fn Buffer::grow(self : Buffer, required : Int) -> Unit {
  let new_capacity = buffer_growth_capacity(
    self.data.length(),
    self.len,
    required,
  )
  let new_data = FixedArray::make_and_blit(
    self.data,
    allocate_len=new_capacity,
    init=b'\x00',
    len=self.len,
  )
  self.data = new_data
}

///|
/// Returns the number of bytes currently stored in the buffer.
///
/// Parameters:
///
/// * `buffer`: The buffer to get the length from.
///
/// Returns the length of the buffer in bytes.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_string_utf16le("Test")
///   inspect(buf.length(), content="8") // each char takes 2 bytes in UTF-16
/// }
/// ```
pub fn Buffer::length(self : Buffer) -> Int {
  self.len
}

///|
/// Returns whether the buffer is empty.
///
/// Parameters:
///
/// * `buffer` : The buffer to check.
///
/// Returns `true` if the buffer is empty (i.e., contains no bytes), `false`
/// otherwise.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   inspect(buf.is_empty(), content="true")
///   buf.write_string_utf16le("test")
///   inspect(buf.is_empty(), content="false")
/// }
/// ```
pub fn Buffer::is_empty(self : Buffer) -> Bool {
  self.len == 0
}

///|
/// Creates a new extensible buffer with specified initial capacity. If the
/// initial capacity is less than 1, the buffer will be initialized with capacity
/// 1.
///
/// Parameters:
///
/// * `size_hint` : Initial capacity of the buffer in bytes. Defaults to 0.
///
/// Returns a new buffer of type `Buffer`.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer(size_hint=10)
///   inspect(buf.length(), content="0")
///   buf.write_string_utf16le("test")
///   inspect(buf.length(), content="8")
/// }
/// ```
#deprecated("use `Buffer()` instead (with `Buffer` in scope via the prelude)")
pub fn new(size_hint? : Int = 0) -> Buffer {
  Buffer(size_hint~)
}

///|
/// Creates a new extensible buffer. Enables the constructor-call syntax
/// `Buffer()` (also `Buffer(size_hint=N)`) when `Buffer` is in scope, e.g.
/// via the prelude.
///
/// Parameters:
///
/// * `size_hint` : Initial capacity of the buffer in bytes. Defaults to 0.
///
/// Returns a new `Buffer`.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_string_utf16le("test")
///   inspect(buf.length(), content="8")
/// }
/// ```
pub fn Buffer::Buffer(size_hint? : Int = 0) -> Buffer {
  let initial = if size_hint < 1 { 1 } else { size_hint }
  let data = FixedArray::make(initial, b'\x00')
  { data, len: 0 }
}

///|
/// Create a buffer from a bytes.
pub fn from_bytes(bytes : BytesView) -> Buffer {
  let val_len = bytes.length()
  let buf = Buffer(size_hint=val_len)
  // Inline write_bytes because the exact capacity is known.
  // SAFETY: known bytes size
  buf.data.blit_from_bytes(0, bytes.data(), bytes.start_offset(), val_len)
  buf.len = val_len
  buf
}

///|
/// Create a buffer from an array.
pub fn from_array(arr : ArrayView[Byte]) -> Buffer {
  let buf = Buffer(size_hint=arr.length())
  for byte in arr {
    // Inline write_byte because the exact capacity is known.
    // SAFETY: known array size
    buf.data[buf.len] = byte
    buf.len += 1
  }
  buf
}

///|
/// Create a buffer from an iterator.
pub fn from_iter(iter : Iter[Byte]) -> Buffer {
  let buf = Buffer()
  for byte in iter; capacity = buf.data.length() {
    // Inline write_byte and keep growth off the fast path.
    let capacity = if buf.len == capacity {
      buf.grow(capacity + 1)
      buf.data.length()
    } else {
      capacity
    }
    buf.data[buf.len] = byte
    buf.len += 1
    continue capacity
  }
  buf
}

///|
/// Writes a UTF-16LE encoded string into the buffer. The buffer will
/// automatically grow if needed to accommodate the string.
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `string` : The string to be written.
/// This Logger impl currently writes UTF-16LE bytes for compatibility. After a
/// breaking-change window, Buffer may implement Logger again with UTF-8
/// semantics.
#deprecated("Buffer's current Logger impl writes UTF-16LE bytes; use StringBuilder or explicit Buffer encoders. A future breaking release may restore it with UTF-8 semantics.", skip_current_package=true)
pub impl Logger for Buffer

///|
pub impl Logger for Buffer with fn write_string(self, value) {
  let required = self.len + value.length() * 2
  if required > self.data.length() || required < self.len {
    self.grow(required)
  }
  self.data.blit_from_string(self.len, value, 0, value.length())
  self.len += value.length() * 2
}

///|
/// Writes an unsigned 64-bit integer into the buffer in big-endian format (most
/// significant byte first).
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `value` : The unsigned 64-bit integer to be written.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_uint64_be(0xAABBCCDD11223344)
///   // Bytes are written in big-endian order
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"\xaa\xbb\xcc\xdd\x11\x223D"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_uint64_be(self : Buffer, value : UInt64) -> Unit {
  if self.data.length() - self.len < 8 {
    self.grow(self.len + 8)
  }
  let offset = self.len
  self.data[offset] = (value >> 56).to_byte()
  self.data[offset + 1] = (value >> 48).to_byte()
  self.data[offset + 2] = (value >> 40).to_byte()
  self.data[offset + 3] = (value >> 32).to_byte()
  self.data[offset + 4] = (value >> 24).to_byte()
  self.data[offset + 5] = (value >> 16).to_byte()
  self.data[offset + 6] = (value >> 8).to_byte()
  self.data[offset + 7] = value.to_byte()
  self.len += 8
}

///|
/// Writes an unsigned 64-bit integer to the buffer in little-endian byte order.
/// Each byte is written sequentially from least significant to most significant.
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `value` : The UInt64 value to be written.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_uint64_le(0x0123456789ABCDEF)
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"\xef\xcd\xab\x89gE#\x01"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_uint64_le(self : Buffer, value : UInt64) -> Unit {
  if self.data.length() - self.len < 8 {
    self.grow(self.len + 8)
  }
  let offset = self.len
  self.data[offset] = value.to_byte()
  self.data[offset + 1] = (value >> 8).to_byte()
  self.data[offset + 2] = (value >> 16).to_byte()
  self.data[offset + 3] = (value >> 24).to_byte()
  self.data[offset + 4] = (value >> 32).to_byte()
  self.data[offset + 5] = (value >> 40).to_byte()
  self.data[offset + 6] = (value >> 48).to_byte()
  self.data[offset + 7] = (value >> 56).to_byte()
  self.len += 8
}

///|
/// Writes a 64-bit integer into the buffer in big-endian format, where the most
/// significant byte is written first.
///
/// Parameters:
///
/// * `buffer` : The buffer to write into.
/// * `value` : The 64-bit integer to be written.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_int64_be(0x0102030405060708L)
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"\x01\x02\x03\x04\x05\x06\x07\x08"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_int64_be(self : Buffer, value : Int64) -> Unit {
  self.write_uint64_be(value.reinterpret_as_uint64())
}

///|
/// Writes a 64-bit signed integer to the buffer in little-endian byte order.
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `value` : The 64-bit signed integer to write.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_int64_le(-1L)
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"\xff\xff\xff\xff\xff\xff\xff\xff"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_int64_le(self : Buffer, value : Int64) -> Unit {
  self.write_uint64_le(value.reinterpret_as_uint64())
}

///|
/// Writes a 32-bit unsigned integer into the buffer in big-endian format (most
/// significant byte first).
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `value` : The unsigned integer value to write.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_uint_be(0x12345678)
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"\x124Vx"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_uint_be(self : Buffer, value : UInt) -> Unit {
  if self.data.length() - self.len < 4 {
    self.grow(self.len + 4)
  }
  let offset = self.len
  self.data[offset] = (value >> 24).to_byte()
  self.data[offset + 1] = (value >> 16).to_byte()
  self.data[offset + 2] = (value >> 8).to_byte()
  self.data[offset + 3] = value.to_byte()
  self.len += 4
}

///|
/// Writes a 32-bit unsigned integer into the buffer in little-endian format. The
/// integer is split into 4 bytes and written in order from least significant to
/// most significant byte.
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `value` : A 32-bit unsigned integer to be written.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_uint_le(0x12345678)
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"xV4\x12"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_uint_le(self : Buffer, value : UInt) -> Unit {
  if self.data.length() - self.len < 4 {
    self.grow(self.len + 4)
  }
  let offset = self.len
  self.data[offset] = value.to_byte()
  self.data[offset + 1] = (value >> 8).to_byte()
  self.data[offset + 2] = (value >> 16).to_byte()
  self.data[offset + 3] = (value >> 24).to_byte()
  self.len += 4
}

///|
/// Writes a 32-bit integer to the buffer in big-endian format. Big-endian means
/// the most significant byte is written first.
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `value` : The 32-bit integer to be written.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_int_be(0x12345678)
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"\x124Vx"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_int_be(self : Buffer, value : Int) -> Unit {
  self.write_uint_be(value.reinterpret_as_uint())
}

///|
/// Writes a 32-bit integer into the buffer in little-endian format. The integer
/// is first reinterpreted as an unsigned integer, then written as 4 bytes where
/// the least significant byte is written first.
///
/// Parameters:
///
/// * `buffer` : The buffer to write into.
/// * `value` : The integer value to be written.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_int_le(-1)
///   inspect(buf.contents(), content="b\"\\xff\\xff\\xff\\xff\"")
/// }
/// ```
pub fn Buffer::write_int_le(self : Buffer, value : Int) -> Unit {
  self.write_uint_le(value.reinterpret_as_uint())
}

///|
/// Writes a 16-bit unsigned integer into the buffer in big-endian format (most
/// significant byte first).
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `value` : The unsigned 16-bit integer value to write.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_uint16_be(0x1234)
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"\x124"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_uint16_be(self : Buffer, value : UInt16) -> Unit {
  if self.data.length() - self.len < 2 {
    self.grow(self.len + 2)
  }
  let offset = self.len
  self.data[offset] = (value.to_int() >> 8).to_byte()
  self.data[offset + 1] = value.to_byte()
  self.len += 2
}

///|
/// Writes a 16-bit unsigned integer into the buffer in little-endian format. The
/// integer is split into 2 bytes and written in order from least significant to
/// most significant byte.
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `value` : A 16-bit unsigned integer to be written.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_uint16_le(0x1234)
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"4\x12"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_uint16_le(self : Buffer, value : UInt16) -> Unit {
  if self.data.length() - self.len < 2 {
    self.grow(self.len + 2)
  }
  let offset = self.len
  self.data[offset] = value.to_byte()
  self.data[offset + 1] = (value.to_int() >> 8).to_byte()
  self.len += 2
}

///|
/// Writes a 16-bit integer to the buffer in big-endian format. Big-endian means
/// the most significant byte is written first.
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `value` : The 16-bit integer to be written.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_int16_be(0x1234)
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"\x124"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_int16_be(self : Buffer, value : Int16) -> Unit {
  if self.data.length() - self.len < 2 {
    self.grow(self.len + 2)
  }
  let offset = self.len
  self.data[offset] = (value.to_int() >> 8).to_byte()
  self.data[offset + 1] = value.to_byte()
  self.len += 2
}

///|
/// Writes a 16-bit integer into the buffer in little-endian format. The integer
/// is written as 2 bytes where the least significant byte is written first.
///
/// Parameters:
///
/// * `buffer` : The buffer to write into.
/// * `value` : The 16-bit integer value to be written.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_int16_le(-1)
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"\xff\xff"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_int16_le(self : Buffer, value : Int16) -> Unit {
  if self.data.length() - self.len < 2 {
    self.grow(self.len + 2)
  }
  let offset = self.len
  self.data[offset] = value.to_byte()
  self.data[offset + 1] = (value.to_int() >> 8).to_byte()
  self.len += 2
}

///|
/// Writes an IEEE 754 double-precision floating-point number into the buffer in
/// big-endian format (most significant byte first).
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `value` : The double-precision floating-point number to be written.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_double_be(1.0)
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"?\xf0\x00\x00\x00\x00\x00\x00"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_double_be(self : Buffer, value : Double) -> Unit {
  self.write_uint64_be(value.reinterpret_as_uint64())
}

///|
/// Writes a double-precision floating-point number into the buffer in
/// little-endian format.
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `value` : The double-precision floating-point number to write.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_double_le(3.14)
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"\x1f\x85\xebQ\xb8\x1e\x09@"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_double_le(self : Buffer, value : Double) -> Unit {
  self.write_uint64_le(value.reinterpret_as_uint64())
}

///|
/// Writes a 32-bit floating-point number to the buffer in big-endian byte order.
/// The float value is first reinterpreted as a 32-bit unsigned integer before
/// writing.
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `value` : The floating-point number to be written.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_float_be(3.14)
///   // In big-endian format, 3.14 is represented as [0x40, 0x48, 0xF5, 0xC3]
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"@H\xf5\xc3"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_float_be(self : Buffer, value : Float) -> Unit {
  self.write_uint_be(value.reinterpret_as_uint())
}

///|
/// Writes a Float value into the buffer in little-endian format. The float value
/// is converted to its binary representation and written as four bytes.
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `value` : The Float value to be written.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_float_le(3.14)
///   // The bytes are written in little-endian format
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"\xc3\xf5H@"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_float_le(self : Buffer, value : Float) -> Unit {
  self.write_uint_le(value.reinterpret_as_uint())
}

///|
/// Writes a string representation of any value that implements the `Show` trait
/// into the buffer.
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `value` : Any value that implements the `Show` trait. The value will be
/// converted to a string using its `to_string` method before being written to
/// the buffer.
#deprecated("Buffer::write_object writes UTF-16LE bytes; use write_utf8 or explicit Buffer encoders. A future breaking release may restore it with UTF-8 semantics.", skip_current_package=true)
pub fn Buffer::write_object(self : Buffer, value : &Show) -> Unit {
  self.write_string_utf16le(value.to_string())
}

///|
/// Writes the `Show` representation of any value into the buffer as UTF-8
/// encoded bytes.
///
/// Parameters:
///
/// * `self` : The buffer to write to.
/// * `value` : Any value that implements the `Show` trait. The value is
/// converted to a string via `Show::to_string` and then encoded as UTF-8.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_utf8(42)
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"42"
///     ),
///   )
/// }
/// ```
#alias(write_bytes_interpolation)
pub fn[T : Show] Buffer::write_utf8(self : Buffer, value : T) -> Unit {
  self.write_string_utf8(value.to_string())
}

///|
/// Writes a sequence of bytes into the buffer.
///
/// Parameters:
///
/// * `buffer` : An extensible buffer to write into.
/// * `bytes` : The sequence of bytes to be written.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_bytes(b"Test")
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"Test"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_bytes(self : Buffer, value : BytesView) -> Unit {
  self.write_bytesview(value)
}

///|
/// Writes a sequence of bytes from a BytesView into the buffer.
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `value` : The View containing the bytes to write.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   let view = b"Test"[1:3]
///   buf.write_bytesview(view)
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"es"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_bytesview(self : Buffer, value : BytesView) -> Unit {
  let val_len = value.length()
  let required = self.len + val_len
  if required > self.data.length() || required < self.len {
    self.grow(required)
  }
  self.data.blit_from_bytes(
    self.len,
    value.data(),
    value.start_offset(),
    value.length(),
  )
  self.len += val_len
}

///|
/// Write a char into buffer as UTF8.
pub fn Buffer::write_char_utf8(buf : Self, value : Char) -> Unit {
  let code = value.to_uint()
  match code {
    _..<0x80 => {
      if buf.len >= buf.data.length() {
        buf.grow(buf.len + 1)
      }
      buf.data[buf.len] = ((code & 0x7F) | 0x00).to_byte()
      buf.len += 1
    }
    _..<0x0800 => {
      if buf.data.length() - buf.len < 2 {
        buf.grow(buf.len + 2)
      }
      buf.data[buf.len] = (((code >> 6) & 0x1F) | 0xC0).to_byte()
      buf.data[buf.len + 1] = ((code & 0x3F) | 0x80).to_byte()
      buf.len += 2
    }
    _..<0x010000 => {
      if buf.data.length() - buf.len < 3 {
        buf.grow(buf.len + 3)
      }
      buf.data[buf.len] = (((code >> 12) & 0x0F) | 0xE0).to_byte()
      buf.data[buf.len + 1] = (((code >> 6) & 0x3F) | 0x80).to_byte()
      buf.data[buf.len + 2] = ((code & 0x3F) | 0x80).to_byte()
      buf.len += 3
    }
    _..<0x110000 => {
      if buf.data.length() - buf.len < 4 {
        buf.grow(buf.len + 4)
      }
      buf.data[buf.len] = (((code >> 18) & 0x07) | 0xF0).to_byte()
      buf.data[buf.len + 1] = (((code >> 12) & 0x3F) | 0x80).to_byte()
      buf.data[buf.len + 2] = (((code >> 6) & 0x3F) | 0x80).to_byte()
      buf.data[buf.len + 3] = ((code & 0x3F) | 0x80).to_byte()
      buf.len += 4
    }
    _ => abort("Char out of range")
  }
}

///|
/// Write a char into buffer as UTF16LE.
pub fn Buffer::write_char_utf16le(buf : Self, value : Char) -> Unit {
  let code = value.to_uint()
  if code < 0x10000 {
    if buf.data.length() - buf.len < 2 {
      buf.grow(buf.len + 2)
    }
    buf.data[buf.len + 0] = (code & 0xFF).to_byte()
    buf.data[buf.len + 1] = (code >> 8).to_byte()
    buf.len += 2
  } else if code < 0x110000 {
    let cp = code - 0x10000
    let high = (cp >> 10) | 0xD800
    let low = (cp & 0x3FF) | 0xDC00
    if buf.data.length() - buf.len < 4 {
      buf.grow(buf.len + 4)
    }
    buf.data[buf.len + 0] = (high & 0xFF).to_byte()
    buf.data[buf.len + 1] = (high >> 8).to_byte()
    buf.data[buf.len + 2] = (low & 0xFF).to_byte()
    buf.data[buf.len + 3] = (low >> 8).to_byte()
    buf.len += 4
  } else {
    abort("Char out of range")
  }
}

///|
/// Write a char into buffer as UTF16BE.
pub fn Buffer::write_char_utf16be(buf : Self, value : Char) -> Unit {
  let code = value.to_uint()
  if code < 0x10000 {
    if buf.data.length() - buf.len < 2 {
      buf.grow(buf.len + 2)
    }
    buf.data[buf.len + 0] = (code >> 8).to_byte()
    buf.data[buf.len + 1] = (code & 0xFF).to_byte()
    buf.len += 2
  } else if code < 0x110000 {
    if buf.data.length() - buf.len < 4 {
      buf.grow(buf.len + 4)
    }
    let cp = code - 0x10000
    let high = (cp >> 10) | 0xD800
    let low = (cp & 0x3FF) | 0xDC00
    buf.data[buf.len + 0] = (high >> 8).to_byte()
    buf.data[buf.len + 1] = (high & 0xFF).to_byte()
    buf.data[buf.len + 2] = (low >> 8).to_byte()
    buf.data[buf.len + 3] = (low & 0xFF).to_byte()
    buf.len += 4
  } else {
    abort("Char out of range")
  }
}

///|
/// Write a UTF-8 encoded string view into the buffer.
///
/// Parameters:
///
/// - `buf`: destination buffer.
/// - `string`: string view to encode as UTF-8 bytes.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_string_utf8("Hi")
///   inspect(buf.contents().length(), content="2")
/// }
/// ```
pub fn Buffer::write_string_utf8(buf : Self, string : StringView) -> Unit {
  for ch in string {
    buf.write_char_utf8(ch)
  }
}

///|
/// Write a string view into the buffer as UTF-16 little-endian code units.
///
/// Parameters:
///
/// - `buf`: destination buffer.
/// - `string`: string view to encode in UTF-16LE.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_string_utf16le("A")
///   inspect(buf.contents().length(), content="2")
/// }
/// ```
#alias(write_stringview, deprecated="use write_string_utf16le instead")
pub fn Buffer::write_string_utf16le(buf : Self, string : StringView) -> Unit {
  let len = string.length()
  let required = buf.len + len * 2
  if required > buf.data.length() || required < buf.len {
    buf.grow(required)
  }
  for code_unit in string.code_units(); j = buf.len {
    let c = code_unit.to_int().reinterpret_as_uint()
    buf.data[j] = (c & 0xff).to_byte()
    buf.data[j + 1] = (c >> 8).to_byte()
    continue j + 2
  }
  buf.len += len * 2
}

///|
/// Write a string view into the buffer as UTF-16 big-endian code units.
///
/// Parameters:
///
/// - `buf`: destination buffer.
/// - `string`: string view to encode in UTF-16BE.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_string_utf16be("A")
///   inspect(buf.contents().length(), content="2")
/// }
/// ```
pub fn Buffer::write_string_utf16be(buf : Self, string : StringView) -> Unit {
  let len = string.length()
  let required = buf.len + len * 2
  if required > buf.data.length() || required < buf.len {
    buf.grow(required)
  }
  for code_unit in string.code_units(); j = buf.len {
    let c = code_unit.to_int().reinterpret_as_uint()
    buf.data[j + 1] = (c & 0xff).to_byte()
    buf.data[j] = (c >> 8).to_byte()
    continue j + 2
  }
  buf.len += len * 2
}

///|
/// Parameters:
///
/// * `self` : The buffer to write to.
/// * `str` : The source string from which the substring will be taken.
/// * `offset` : The starting position in the source string (inclusive). Must be
/// non-negative.
/// * `count` : The number of characters to write. Must be non-negative and
/// `offset + count` must not exceed the length of the source string.
pub impl Logger for Buffer with fn write_view(self : Buffer, value : StringView) -> Unit {
  let required = self.len + value.length() * 2
  if required > self.data.length() || required < self.len {
    self.grow(required)
  }
  self.data.blit_from_string(
    self.len,
    value.data(),
    value.start_offset(),
    value.length(),
  )
  self.len += value.length() * 2
}

///|
/// Writes a UTF-16LE encoded character into the buffer. Automatically grows the
/// buffer if necessary.
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `char` : The character to be written.
pub impl Logger for Buffer with fn write_char(self : Buffer, value : Char) -> Unit {
  if self.data.length() - self.len < 4 {
    self.grow(self.len + 4)
  }
  let inc = self.data.set_utf16le_char(self.len, value)
  self.len += inc
}

///|
/// Writes a single byte to the end of the buffer. The buffer will automatically
/// grow if necessary to accommodate the new byte.
///
/// Parameters:
///
/// * `buffer` : The buffer to write to.
/// * `byte` : The byte value to be written.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_byte(b'\x41')
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"A"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_byte(self : Buffer, value : Byte) -> Unit {
  if self.len >= self.data.length() {
    self.grow(self.len + 1)
  }
  self.data[self.len] = value
  self.len += 1
}

///|
/// Writes bytes from an iterator to the buffer. 
///
/// Parameters:
///
/// * `self` : The buffer to write to.
/// * `iter` : An iterator yielding bytes to write.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   let bytes = b"Hello"
///   buf.write_iter(bytes.iter())
///   inspect(
///     buf.contents(),
///     content=(
///       #|b"Hello"
///     ),
///   )
/// }
/// ```
pub fn Buffer::write_iter(self : Buffer, iter : Iter[Byte]) -> Unit {
  for byte in iter {
    self.write_byte(byte)
  }
}

///|
/// Resets the buffer to an empty state by setting the internal offset to 0.
/// This makes the buffer appear empty without actually clearing the underlying data.
///
/// Parameters:
///
/// * `self` : The buffer to be reset.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_string_utf16le("Hello")
///   inspect(buf.length(), content="10")
///   buf.reset()
///   inspect(buf.length(), content="0")
///   inspect(buf.is_empty(), content="true")
/// }
/// ```
///
pub fn Buffer::reset(self : Buffer) -> Unit {
  self.len = 0
}

///|
/// Returns a copy of the buffer's contents as a `Bytes` object. The returned
/// bytes will have the same length as the buffer.
///
/// Parameters:
///
/// * `buffer` : The buffer whose contents will be converted to bytes.
///
/// Returns a `Bytes` object containing a copy of the buffer's contents.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_string_utf16le("Test")
///   let bytes = buf.to_bytes()
///   inspect(bytes.length(), content="8") //utf16
/// }
/// ```
#alias(contents)
pub fn Buffer::to_bytes(self : Buffer) -> Bytes {
  Bytes::from_array(self.data[0:self.len])
}

///|
/// Return a read-only byte view over the current buffer contents.
///
/// The view length equals `self.length()`. It shares underlying storage with
/// the buffer, so later writes to the buffer may affect subsequent reads.
///
/// Example:
///
/// ```mbt check
/// test {
///   let buf = Buffer()
///   buf.write_byte(b'A')
///   let v = buf.view()
///   inspect(v.length(), content="1")
///   inspect(v[0], content="b'\\x41'")
/// }
/// ```
pub fn Buffer::view(self : Buffer) -> ArrayView[Byte] {
  self.data[0:self.len]
}

///|
pub impl Show for Buffer with fn output(self, logger) {
  logger.write_string(self.contents().to_unchecked_string())
}