// Copyright 2025 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.
///|
/// Internal "small" style collection types.
///
/// Ported from `fontations/skrifa/src/collections.rs` (Apache-2.0 OR MIT).
///
/// NOTE: Upstream uses const-generics for inline capacity. In MoonBit we keep
/// `inline_cap` as a runtime parameter; this preserves behavior while still
/// allowing the inline/heap split used by later ports.
priv enum Storage[T] {
Inline(FixedArray[T], Int)
Heap(Array[T])
}
///|
/// A growable vector type with a logical "inline" storage mode.
///
/// Unlike upstream, `inline_cap` is a runtime value.
priv struct SmallVec[T] {
storage : Storage[T]
inline_cap : Int
}
///|
fn[T : Default] SmallVec::SmallVec(inline_cap : Int) -> SmallVec[T] {
let buf = FixedArray::make(inline_cap, T::default())
{ storage: Inline(buf, 0), inline_cap }
}
///|
fn[T] SmallVec::with_len(len : Int, value : T, inline_cap : Int) -> SmallVec[T] {
if len <= inline_cap {
let buf = FixedArray::make(inline_cap, value)
{ storage: Inline(buf, len), inline_cap }
} else {
let vec = Array::make(len, value)
{ storage: Heap(vec), inline_cap }
}
}
///|
fn[T] SmallVec::len(self : SmallVec[T]) -> Int {
match self.storage {
Inline(_buf, len) => len
Heap(vec) => vec.length()
}
}
///|
fn[T] SmallVec::is_inline(self : SmallVec[T]) -> Bool {
match self.storage {
Inline(_, _) => true
Heap(_) => false
}
}
///|
fn[T] SmallVec::as_view(self : SmallVec[T]) -> ArrayView[T] {
match self.storage {
Inline(buf, len) => buf[0:len]
Heap(vec) => vec.op_as_view()
}
}
///|
fn[T] SmallVec::to_array(self : SmallVec[T]) -> Array[T] {
match self.storage {
Inline(buf, len) => {
let out = Array::new(capacity=len)
for i in 0.. vec.copy()
}
}
///|
fn[T] SmallVec::clear(self : SmallVec[T]) -> SmallVec[T] {
match self.storage {
Inline(buf, _len) =>
{ storage: Inline(buf, 0), inline_cap: self.inline_cap }
Heap(vec) => {
vec.clear()
{ storage: Heap(vec), inline_cap: self.inline_cap }
}
}
}
///|
fn[T] SmallVec::try_reserve(
self : SmallVec[T],
additional : Int,
) -> (SmallVec[T], Bool) {
match self.storage {
Inline(buf, len) => {
let need = len + additional
if need > self.inline_cap {
let vec = Array::new(capacity=need)
for i in 0.. {
vec.reserve_capacity(vec.length() + additional)
({ storage: Heap(vec), inline_cap: self.inline_cap }, true)
}
}
}
///|
fn[T] SmallVec::push(self : SmallVec[T], value : T) -> SmallVec[T] {
match self.storage {
Inline(buf, len) =>
if len + 1 > self.inline_cap {
let vec = Array::new(capacity=len + 1)
for i in 0.. {
vec.push(value)
{ storage: Heap(vec), inline_cap: self.inline_cap }
}
}
}
///|
fn[T] SmallVec::pop(self : SmallVec[T]) -> (SmallVec[T], T?) {
match self.storage {
Inline(buf, len) =>
if len > 0 {
let v = buf.at(len - 1)
(
{ storage: Inline(buf, len - 1), inline_cap: self.inline_cap },
Some(v),
)
} else {
(self, None)
}
Heap(vec) => {
let v = vec.pop()
({ storage: Heap(vec), inline_cap: self.inline_cap }, v)
}
}
}
///|
fn[T] SmallVec::truncate(self : SmallVec[T], new_len : Int) -> SmallVec[T] {
match self.storage {
Inline(buf, len) => {
let next_len = if new_len < len { new_len } else { len }
{ storage: Inline(buf, next_len), inline_cap: self.inline_cap }
}
Heap(vec) => {
vec.truncate(new_len)
{ storage: Heap(vec), inline_cap: self.inline_cap }
}
}
}
///|
fn[T] SmallVec::get(self : SmallVec[T], i : Int) -> T? {
match self.storage {
Inline(buf, len) => if i >= 0 && i < len { Some(buf.at(i)) } else { None }
Heap(vec) => vec.get(i)
}
}
///|
fn[T] SmallVec::set(self : SmallVec[T], i : Int, v : T) -> SmallVec[T] {
match self.storage {
Inline(buf, len) => {
if i >= 0 && i < len {
buf.set(i, v)
} else {
// Keep behavior simple: ignore out-of-range writes (only used in tests).
i |> ignore
}
{ storage: Inline(buf, len), inline_cap: self.inline_cap }
}
Heap(vec) => {
if i >= 0 && i < vec.length() {
vec.set(i, v)
} else {
i |> ignore
}
{ storage: Heap(vec), inline_cap: self.inline_cap }
}
}
}
///|
test "choose_inline" {
let vec = SmallVec::with_len(4, 0, 4)
inspect(vec.is_inline(), content="true")
inspect(vec.len(), content="4")
}
///|
test "choose_heap" {
let vec = SmallVec::with_len(5, 0, 4)
inspect(vec.is_inline(), content="false")
inspect(vec.len(), content="5")
}
///|
test "store_and_read_inline" {
let mut vec = SmallVec::with_len(8, 0, 8)
for i in 0..