Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streaming handlers chunked #241

Merged
merged 3 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions c-api/c-tests/src/test_element_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,10 @@ static int loltest_write_all_callback_after(lol_html_streaming_sink_t *sink, voi
const char *after = "&after";
const char emoji[] = {0xf0,0x9f,0x98,0x82};
return lol_html_streaming_sink_write_str(sink, after, strlen(after), false) ||
lol_html_streaming_sink_write_str(sink, emoji, 4, false);
lol_html_streaming_sink_write_utf8_chunk(sink, &emoji[0], 1, false) ||
lol_html_streaming_sink_write_utf8_chunk(sink, &emoji[1], 1, false) ||
lol_html_streaming_sink_write_utf8_chunk(sink, &emoji[2], 1, false) ||
lol_html_streaming_sink_write_utf8_chunk(sink, &emoji[3], 1, false);
}

static int loltest_write_all_callback_prepend(lol_html_streaming_sink_t *sink, void *user_data) {
Expand All @@ -275,8 +278,8 @@ static int loltest_write_all_callback_prepend(lol_html_streaming_sink_t *sink, v

const char *prepend1 = "<!--pre";
const char *prepend2 = "pend-->";
return lol_html_streaming_sink_write_str(sink, prepend1, strlen(prepend1), true) ||
lol_html_streaming_sink_write_str(sink, prepend2, strlen(prepend2), true);
return lol_html_streaming_sink_write_utf8_chunk(sink, prepend1, strlen(prepend1), true) ||
lol_html_streaming_sink_write_utf8_chunk(sink, prepend2, strlen(prepend2), true);
}

static int loltest_write_all_callback_append(lol_html_streaming_sink_t *sink, void *user_data) {
Expand Down
18 changes: 17 additions & 1 deletion c-api/include/lol_html.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ typedef struct lol_html_CStreamingHandler {
void *user_data;
// Called when the handler is supposed to produce its output. Return `0` for success.
// The `sink` argument is guaranteed non-`NULL`. It is valid only for the duration of this call, and can only be used on the same thread.
// The sink is for [`lol_html_streaming_sink_write_str`].
// The sink is for [`lol_html_streaming_sink_write_str`] and [`lol_html_streaming_sink_write_utf8_chunk`].
// `user_data` comes from this struct.
//
// `write_all_callback` must not be `NULL`.
Expand Down Expand Up @@ -1019,6 +1019,22 @@ int lol_html_streaming_sink_write_str(lol_html_streaming_sink_t *sink,
size_t string_utf8_len,
bool is_html);

// [`StreamingHandlerSink::write_utf8_chunk`]
//
// Writes as much of the given UTF-8 fragment as possible, converting the encoding and HTML-escaping if `is_html` is `false`.
//
// The `bytes_utf8` doesn't need to be a complete UTF-8 string, as long as consecutive calls to this function create a valid UTF-8 string.
// Any incomplete UTF-8 sequence at the end of the content is buffered and flushed as soon as it's completed.
//
// Other functions like [`lol_html_streaming_sink_write_str`] should not be called after a
// `lol_html_streaming_sink_write_utf8_chunk` call with an incomplete UTF-8 sequence.
//
// Returns `0` on success, and `-1` if it wasn't valid UTF-8.
// All pointers must be non-`NULL`.
int lol_html_streaming_sink_write_utf8_chunk(lol_html_streaming_sink_t *sink,
const char *bytes_utf8,
size_t bytes_utf8_len,
bool is_html);

#if defined(__cplusplus)
} // extern C
Expand Down
33 changes: 32 additions & 1 deletion c-api/src/streaming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,37 @@ pub unsafe extern "C" fn lol_html_streaming_sink_write_str(
0
}

/// [`StreamingHandlerSink::write_utf8_chunk`]
///
/// Writes as much of the given UTF-8 fragment as possible, converting the encoding and HTML-escaping if `is_html` is `false`.
///
/// The `bytes_utf8` doesn't need to be a complete UTF-8 string, as long as consecutive calls to this function create a valid UTF-8 string.
/// Any incomplete UTF-8 sequence at the end of the content is buffered and flushed as soon as it's completed.
///
/// Other functions like [`lol_html_streaming_sink_write_str`] should not be called after a
/// `lol_html_streaming_sink_write_utf8_chunk` call with an incomplete UTF-8 sequence.
///
/// Returns `0` on success, and `-1` if it wasn't valid UTF-8.
/// All pointers must be non-`NULL`.
#[no_mangle]
pub unsafe extern "C" fn lol_html_streaming_sink_write_utf8_chunk(
sink: *mut CStreamingHandlerSink<'_>,
bytes_utf8: *const c_char,
bytes_utf8_len: size_t,
is_html: bool,
) -> c_int {
let sink = to_ref_mut!(sink);
let content = to_bytes!(bytes_utf8, bytes_utf8_len);
let is_html = if is_html {
ContentType::Html
} else {
ContentType::Text
};

unwrap_or_ret_err_code! { sink.write_utf8_chunk(content, is_html) };
0
}

/// Safety: the user data and the callbacks must be safe to use from a different thread (e.g. can't rely on thread-local storage).
/// It doesn't have to be `Sync`, it will be used only by one thread at a time.
///
Expand All @@ -37,7 +68,7 @@ pub struct CStreamingHandler {
pub user_data: *mut c_void,
/// Called when the handler is supposed to produce its output. Return `0` for success.
/// The `sink` argument is guaranteed non-`NULL`. It is valid only for the duration of this call, and can only be used on the same thread.
/// The sink is for [`lol_html_streaming_sink_write_str`].
/// The sink is for [`lol_html_streaming_sink_write_str`] and [`lol_html_streaming_sink_write_utf8_chunk`].
/// `user_data` comes from this struct.
/// `write_all_callback` must not be `NULL`.
pub write_all_callback: Option<
Expand Down
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ pub mod send {
pub mod errors {
pub use super::memory::MemoryLimitExceededError;
pub use super::parser::ParsingAmbiguityError;
pub use super::rewritable_units::{AttributeNameError, CommentTextError, TagNameError};
pub use super::rewritable_units::{
AttributeNameError, CommentTextError, TagNameError, Utf8Error,
};
pub use super::rewriter::RewritingError;
pub use super::selectors_vm::SelectorError;
}
Expand Down
2 changes: 1 addition & 1 deletion src/rewritable_units/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1215,7 +1215,7 @@ mod tests {
{
let output = rewrite_element(&html, enc, "span", |el| {
el.streaming_prepend(streaming!(|s| {
s.write_str("<prepended>", ContentType::Html);
s.write_utf8_chunk(b"<prepended>", ContentType::Html)?;
Ok(())
}));
el.append("<appended>", ContentType::Html);
Expand Down
2 changes: 1 addition & 1 deletion src/rewritable_units/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub use self::document_end::*;
pub use self::element::*;
pub use self::mutations::{ContentType, StreamingHandler};
pub(crate) use self::mutations::{Mutations, StringChunk};
pub use self::text_encoder::StreamingHandlerSink;
pub use self::text_encoder::{StreamingHandlerSink, Utf8Error};
pub use self::tokens::*;

/// Data that can be attached to a rewritable unit by a user and shared between content handler
Expand Down
Loading
Loading