///|
priv enum NativeTextLength {
PayloadBytesExcludingTerminator
BufferBytesIncludingTerminator
}
///|
fn native_text_buffer_length(
context : String,
required : Int,
length : NativeTextLength,
) -> Int raise NativeError {
match length {
PayloadBytesExcludingTerminator =>
if required < 0 || required == 2147483647 {
raise InvalidPayload(
context~,
message="native query returned an invalid required length",
)
} else {
required + 1
}
BufferBytesIncludingTerminator =>
if required <= 0 {
raise InvalidPayload(
context~,
message="native query returned an invalid required length",
)
} else {
required
}
}
}
///|
fn native_text_payload_length(
context : String,
required : Int,
length : NativeTextLength,
) -> Int raise NativeError {
match length {
PayloadBytesExcludingTerminator =>
if required < 0 {
raise InvalidPayload(
context~,
message="native query returned an invalid payload length",
)
} else {
required
}
BufferBytesIncludingTerminator =>
if required <= 0 {
raise InvalidPayload(
context~,
message="native query returned an invalid buffer length",
)
} else {
required - 1
}
}
}
///|
fn read_native_text(
context : String,
length : NativeTextLength,
query : (FixedArray[Byte], Int, Ref[Int]) -> Int,
) -> String? raise NativeError {
let required = Ref(0)
let mut buffer = FixedArray::make(1, b'\x00')
let mut buffer_len = 0
for ;; {
required.val = 0
let status = query(buffer, buffer_len, required)
if status == proton_event_none {
return None
}
if status == proton_err_buffer_too_small {
let next_len = native_text_buffer_length(context, required.val, length)
if next_len <= buffer_len {
raise InvalidPayload(
context~,
message="native query did not increase the required buffer length",
)
}
buffer = FixedArray::make(next_len, b'\x00')
buffer_len = next_len
continue
}
if status < 0 {
raise native_error(status)
}
if status != 0 {
raise InvalidPayload(
context~,
message="native query returned unexpected status " + status.to_string(),
)
}
let payload_len = native_text_payload_length(context, required.val, length)
if payload_len == 0 {
return Some("")
}
if payload_len >= buffer_len || buffer[payload_len] != b'\x00' {
raise InvalidPayload(
context~,
message="native query returned an invalid text buffer",
)
}
return Some(@ffi.from_cstr(Bytes::from_array(buffer[0:payload_len + 1])))
}
}
///|
fn require_native_text(
context : String,
length : NativeTextLength,
query : (FixedArray[Byte], Int, Ref[Int]) -> Int,
) -> String raise NativeError {
match read_native_text(context, length, query) {
Some(text) => text
None =>
raise InvalidPayload(context~, message="native query returned no value")
}
}