// Copyright 2026 Noverberrain.
// SPDX-License-Identifier: Apache-2.0
///|
priv struct SortedSetBudget {
limit : Int32
mut used : Int32
}
///|
fn SortedSetBudget::check(
self : SortedSetBudget,
count : UInt64,
offset : Int32,
) -> Unit raise ParseError {
check_resource_limit("sorted-set-entries", offset, count, self.limit)
check_resource_limit(
"sorted-set-entries",
offset,
self.used.to_uint64() + count,
self.limit,
)
}
///|
fn SortedSetBudget::charge(
self : SortedSetBudget,
count : UInt64,
offset : Int32,
) -> Unit raise ParseError {
self.check(count, offset)
self.used += count.to_int()
}
///|
// Only decimal ASCII and explicit infinities; reject NaN, whitespace, NUL and
// implementation-specific syntax. Scratch score text is bounded independently.
fn parse_sorted_set_score(
raw : Bytes,
offset : Int32,
) -> Double raise ParseError {
let invalid = InvalidSortedSet(offset~, reason="invalid score")
if raw == b"inf" || raw == b"+inf" {
return 0x7ff0000000000000UL.reinterpret_as_double()
}
if raw == b"-inf" {
return 0xfff0000000000000UL.reinterpret_as_double()
}
if raw.is_empty() {
raise invalid
}
let chars : Array[Char] = []
for byte in raw {
let n = byte.to_int()
if !((n >= 48 && n <= 57) ||
n == 43 ||
n == 45 ||
n == 46 ||
n == 101 ||
n == 69) {
raise invalid
}
guard n.to_char() is Some(c) else { raise invalid }
chars.push(c)
}
let text = String::from_array(chars)
let score = @string.parse_double(text[:]) catch { _ => raise invalid }
if score.is_nan() || score.is_inf() {
raise invalid
}
score
}
///|
fn Reader::read_sorted_set_record(
self : Reader,
prefix : KeyPrefix,
database : UInt64,
max_string_bytes : Int32,
max_packed_bytes : Int32,
budget : DecodeBudget,
sorted_sets : SortedSetBudget,
) -> SortedSetRecord raise ParseError {
let key = self.read_string(
max_string_bytes,
budget,
allow_lzf=true,
resource="string-bytes",
)
let base = self.pos
let entries : Array[SortedSetEntry] = []
let seen : Map[Bytes, Unit] = Map([])
let (storage, packed_storage) : (SortedSetStorage, StringStorage?) = if prefix.object_type ==
b'\x05' {
let count = self.read_count()
sorted_sets.charge(count, base)
for _ in 0..