///|
// Pkl's binary value encoding is MessagePack with small tagged arrays for
// values that MessagePack cannot represent directly. The tag values mirror
// Apple Pkl's PklBinaryCode enum.
let pklbinary_object_code : Int = 0x01
///|
let pklbinary_map_code : Int = 0x02
///|
let pklbinary_mapping_code : Int = 0x03
///|
let pklbinary_list_code : Int = 0x04
///|
let pklbinary_listing_code : Int = 0x05
///|
let pklbinary_set_code : Int = 0x06
///|
let pklbinary_duration_code : Int = 0x07
///|
let pklbinary_datasize_code : Int = 0x08
///|
let pklbinary_pair_code : Int = 0x09
///|
let pklbinary_intseq_code : Int = 0x0a
///|
let pklbinary_regex_code : Int = 0x0b
///|
let pklbinary_class_code : Int = 0x0c
///|
let pklbinary_typealias_code : Int = 0x0d
///|
let pklbinary_function_code : Int = 0x0e
///|
let pklbinary_bytes_code : Int = 0x0f
///|
let pklbinary_property_code : Int = 0x10
///|
let pklbinary_entry_code : Int = 0x11
///|
let pklbinary_element_code : Int = 0x12
///|
fn pklbinary_write_u8(buf : @buffer.Buffer, value : Int) -> Unit {
buf.write_byte(value.to_byte())
}
///|
fn pklbinary_write_array_header(buf : @buffer.Buffer, length : Int) -> Unit {
if length <= 15 {
pklbinary_write_u8(buf, 0x90 | length)
} else if length <= 0xffff {
pklbinary_write_u8(buf, 0xdc)
buf.write_uint16_be(length.to_uint16())
} else {
pklbinary_write_u8(buf, 0xdd)
buf.write_uint_be(length.reinterpret_as_uint())
}
}
///|
fn pklbinary_write_map_header(buf : @buffer.Buffer, length : Int) -> Unit {
if length <= 15 {
pklbinary_write_u8(buf, 0x80 | length)
} else if length <= 0xffff {
pklbinary_write_u8(buf, 0xde)
buf.write_uint16_be(length.to_uint16())
} else {
pklbinary_write_u8(buf, 0xdf)
buf.write_uint_be(length.reinterpret_as_uint())
}
}
///|
fn pklbinary_write_int(buf : @buffer.Buffer, value : Int64) -> Unit {
if value >= 0L {
if value <= 0x7fL {
pklbinary_write_u8(buf, value.to_int())
} else if value <= 0xffL {
pklbinary_write_u8(buf, 0xcc)
pklbinary_write_u8(buf, value.to_int())
} else if value <= 0xffffL {
pklbinary_write_u8(buf, 0xcd)
buf.write_uint16_be(value.to_uint16())
} else if value <= 0xffffffffL {
pklbinary_write_u8(buf, 0xce)
buf.write_uint_be(value.to_int().reinterpret_as_uint())
} else {
pklbinary_write_u8(buf, 0xcf)
buf.write_uint64_be(value.reinterpret_as_uint64())
}
} else if value >= -32L {
pklbinary_write_u8(buf, (value & 0xffL).to_int())
} else if value >= -128L {
pklbinary_write_u8(buf, 0xd0)
pklbinary_write_u8(buf, (value & 0xffL).to_int())
} else if value >= -32768L {
pklbinary_write_u8(buf, 0xd1)
buf.write_int16_be(Int16::from_int64(value))
} else if value >= -2147483648L {
pklbinary_write_u8(buf, 0xd2)
buf.write_int_be(value.to_int())
} else {
pklbinary_write_u8(buf, 0xd3)
buf.write_int64_be(value)
}
}
///|
fn pklbinary_write_string(buf : @buffer.Buffer, value : String) -> Unit {
let encoded = @utf8.encode(value[:])
let length = encoded.length()
if length <= 31 {
pklbinary_write_u8(buf, 0xa0 | length)
} else if length <= 0xff {
pklbinary_write_u8(buf, 0xd9)
pklbinary_write_u8(buf, length)
} else if length <= 0xffff {
pklbinary_write_u8(buf, 0xda)
buf.write_uint16_be(length.to_uint16())
} else {
pklbinary_write_u8(buf, 0xdb)
buf.write_uint_be(length.reinterpret_as_uint())
}
buf.write_bytes(encoded[:])
}
///|
fn pklbinary_write_binary(buf : @buffer.Buffer, bytes : Bytes) -> Unit {
let length = bytes.length()
if length <= 0xff {
pklbinary_write_u8(buf, 0xc4)
pklbinary_write_u8(buf, length)
} else if length <= 0xffff {
pklbinary_write_u8(buf, 0xc5)
buf.write_uint16_be(length.to_uint16())
} else {
pklbinary_write_u8(buf, 0xc6)
buf.write_uint_be(length.reinterpret_as_uint())
}
buf.write_bytes(bytes[:])
}
///|
fn pklbinary_write_tagged_list_header(
buf : @buffer.Buffer,
code : Int,
payload_length : Int,
) -> Unit {
pklbinary_write_array_header(buf, payload_length + 1)
pklbinary_write_int(buf, code.to_int64())
}
///|
fn pklbinary_class_display_name(
class_name : String,
cache : Array[ValueBinding],
) -> String {
if class_name == "module" || class_name == "Module" {
match lookup_value(cache, "@__module_name") {
Some(StringValue(name)) if name.length() > 0 => name
_ => "module"
}
} else if class_name == "Dynamic" || is_stdlib_class_name(class_name) {
class_name
} else {
renderer_qualified_user_name(class_name, cache)
}
}
///|
fn pklbinary_class_module_uri(
class_name : String,
cache : Array[ValueBinding],
) -> String {
if class_name == "module" || class_name == "Module" {
match lookup_value(cache, "@__module_path") {
Some(StringValue(path)) => normalize_pklbinary_module_uri(path)
_ => ""
}
} else if class_name == "Dynamic" || is_stdlib_class_name(class_name) {
"pkl:base"
} else {
match lookup_value(cache, "@__module_path") {
Some(StringValue(path)) => normalize_pklbinary_module_uri(path)
_ => ""
}
}
}
///|
fn pklbinary_write_reflect_value(
buf : @buffer.Buffer,
members : Array[ValueMember],
cache : Array[ValueBinding],
) -> Bool {
let code = match reflect_kind(members) {
Some("Class") => pklbinary_class_code
Some("TypeAlias") => pklbinary_typealias_code
_ => return false
}
let name = match lookup_member(members, "__qualified_name") {
Some(StringValue(value)) => value
_ =>
match lookup_member(members, "name") {
Some(StringValue(value)) => value
_ => ""
}
}
let uri = match lookup_member(members, "moduleUri") {
Some(StringValue(value)) => normalize_pklbinary_module_uri(value)
_ =>
match lookup_member(members, "uri") {
Some(StringValue(value)) => normalize_pklbinary_module_uri(value)
_ => {
let simple_name = match lookup_member(members, "simpleName") {
Some(StringValue(value)) => value
_ => name
}
if (code == pklbinary_class_code && is_stdlib_class_name(simple_name)) ||
(
code == pklbinary_typealias_code &&
is_stdlib_type_alias_name(simple_name)
) {
"pkl:base"
} else {
match lookup_value(cache, "@__module_path") {
Some(StringValue(value)) => normalize_pklbinary_module_uri(value)
_ => ""
}
}
}
}
}
pklbinary_write_tagged_list_header(buf, code, 2)
pklbinary_write_string(buf, name)
pklbinary_write_string(buf, uri)
true
}
///|
/// Apple Pkl serializes inherited renderer properties after evaluation.
/// Synthetic stdlib classes do not run the VM's external class initializer,
/// so materialize the canonical `PcfRenderer` defaults at this boundary.
fn pklbinary_visible_object_members(
class_name : String,
members : Array[ValueMember],
) -> Array[ValueMember] {
let visible = visible_members(members)
if class_name != "PcfRenderer" || visible.length() > 0 {
return visible
}
[
{
name: "converters",
value: MappingValue([]),
source: None,
annotations: [],
},
{
name: "convertPropertyTransformers",
value: MappingValue([]),
source: None,
annotations: [],
},
{
name: "extension",
value: StringValue("pcf"),
source: None,
annotations: [],
},
{ name: "indent", value: StringValue(" "), source: None, annotations: [] },
{
name: "omitNullProperties",
value: BoolValue(false),
source: None,
annotations: [],
},
{
name: "useCustomStringDelimiters",
value: BoolValue(false),
source: None,
annotations: [],
},
]
}
///|
fn pklbinary_write_object_member(
buf : @buffer.Buffer,
field : ValueMember,
element_index : Int64,
cache : Array[ValueBinding],
) -> Unit {
if field.name.has_prefix("@subscript$") {
match field.value {
ObjectValue(pair_members) =>
match
(
lookup_member(pair_members, "@key"),
lookup_member(pair_members, "@value"),
) {
(Some(key), Some(value)) => {
pklbinary_write_tagged_list_header(buf, pklbinary_entry_code, 2)
pklbinary_write_value(buf, key, cache)
pklbinary_write_value(buf, value, cache)
}
_ => ()
}
_ => ()
}
} else if field.name.has_prefix("@element$") {
pklbinary_write_tagged_list_header(buf, pklbinary_element_code, 2)
pklbinary_write_int(buf, element_index)
pklbinary_write_value(buf, field.value, cache)
} else {
pklbinary_write_tagged_list_header(buf, pklbinary_property_code, 2)
pklbinary_write_string(buf, strip_member_visibility_prefix(field.name))
pklbinary_write_value(buf, field.value, cache)
}
}
///|
fn pklbinary_write_value(
buf : @buffer.Buffer,
value : Value,
cache : Array[ValueBinding],
) -> Unit {
match value {
ThunkValue(_) => pklbinary_write_value(buf, force_eval_thunk(value), cache)
IntValue(number) => pklbinary_write_int(buf, number)
FloatValue(number) => {
pklbinary_write_u8(buf, 0xcb)
buf.write_double_be(number)
}
BoolValue(flag) => pklbinary_write_u8(buf, if flag { 0xc3 } else { 0xc2 })
StringValue(text) => pklbinary_write_string(buf, text)
NullValue => pklbinary_write_u8(buf, 0xc0)
DurationValue(number, unit) => {
pklbinary_write_tagged_list_header(buf, pklbinary_duration_code, 2)
pklbinary_write_u8(buf, 0xcb)
buf.write_double_be(number)
pklbinary_write_string(buf, unit)
}
DataSizeValue(number, unit) => {
pklbinary_write_tagged_list_header(buf, pklbinary_datasize_code, 2)
pklbinary_write_u8(buf, 0xcb)
buf.write_double_be(number)
pklbinary_write_string(buf, unit)
}
BytesValue(bytes) => {
pklbinary_write_tagged_list_header(buf, pklbinary_bytes_code, 1)
pklbinary_write_binary(buf, bytes)
}
RegexValue(pattern) => {
pklbinary_write_tagged_list_header(buf, pklbinary_regex_code, 1)
pklbinary_write_string(buf, pattern)
}
PairValue(first, second) => {
pklbinary_write_tagged_list_header(buf, pklbinary_pair_code, 2)
pklbinary_write_value(buf, first, cache)
pklbinary_write_value(buf, second, cache)
}
IntSeqValue(start, end_, step) => {
pklbinary_write_tagged_list_header(buf, pklbinary_intseq_code, 3)
pklbinary_write_int(buf, start)
pklbinary_write_int(buf, end_)
pklbinary_write_int(buf, step)
}
FunctionValue(_, _, _, _, _) =>
pklbinary_write_tagged_list_header(buf, pklbinary_function_code, 0)
ListValue(elements) => {
pklbinary_write_tagged_list_header(buf, pklbinary_list_code, 1)
pklbinary_write_array_header(buf, elements.length())
for element in elements {
pklbinary_write_value(buf, element, cache)
}
}
ListingValue(elements) | DefaultedListingValue(_, elements, _) => {
pklbinary_write_tagged_list_header(buf, pklbinary_listing_code, 1)
pklbinary_write_array_header(buf, elements.length())
for element in elements {
pklbinary_write_value(buf, element, cache)
}
}
SetValue(elements) => {
pklbinary_write_tagged_list_header(buf, pklbinary_set_code, 1)
pklbinary_write_array_header(buf, elements.length())
for element in elements {
pklbinary_write_value(buf, element, cache)
}
}
MapValue(entries) => {
pklbinary_write_tagged_list_header(buf, pklbinary_map_code, 1)
pklbinary_write_map_header(buf, entries.length())
for entry in entries {
pklbinary_write_value(buf, entry.key, cache)
pklbinary_write_value(buf, entry.value, cache)
}
}
MappingValue(entries) | DefaultedMappingValue(_, entries, _) => {
pklbinary_write_tagged_list_header(buf, pklbinary_mapping_code, 1)
pklbinary_write_map_header(buf, entries.length())
for entry in entries {
pklbinary_write_value(buf, entry.key, cache)
pklbinary_write_value(buf, entry.value, cache)
}
}
ObjectValue(members) =>
if !pklbinary_write_reflect_value(buf, members, cache) {
let class_name = match find_object_class_tag(members) {
Some(name) => name
None => "Dynamic"
}
let visible = pklbinary_visible_object_members(class_name, members)
pklbinary_write_tagged_list_header(buf, pklbinary_object_code, 3)
pklbinary_write_string(
buf,
pklbinary_class_display_name(class_name, cache),
)
pklbinary_write_string(
buf,
pklbinary_class_module_uri(class_name, cache),
)
pklbinary_write_array_header(buf, visible.length())
let mut element_index = 0L
for field in visible {
pklbinary_write_object_member(buf, field, element_index, cache)
if field.name.has_prefix("@element$") {
element_index = element_index + 1L
}
}
}
DeferredImportValue(uri) => pklbinary_write_string(buf, uri)
}
}
///|
fn render_value_as_pklbinary_with_cache(
value : Value,
cache : Array[ValueBinding],
) -> Bytes {
let buf = @buffer.Buffer()
pklbinary_write_value(buf, value, cache)
buf.to_bytes()
}
///|
/// Render a value using Pkl's MessagePack-based binary format while keeping
/// user class and module metadata anchored to the containing module.
pub fn render_value_as_pklbinary_with_module(
value : Value,
module_name : String,
module_uri : String,
) -> Bytes {
let cache : Array[ValueBinding] = [
{ name: "@__module_name", value: StringValue(module_name) },
{ name: "@__module_path", value: StringValue(module_uri) },
]
render_value_as_pklbinary_with_cache(value, cache)
}
///|
/// Render a module projection. Values reconstructed from
/// `output.value = module` do not retain their hidden class marker, so
/// restore it before writing the Pkl binary object envelope.
pub fn render_module_as_pklbinary(
value : Value,
module_name : String,
module_uri : String,
) -> Bytes {
let module_value = match value {
ObjectValue(members) =>
ObjectValue(tag_object_with_class(members, "module"))
other => other
}
render_value_as_pklbinary_with_module(module_value, module_name, module_uri)
}