@@ -902,26 +902,62 @@ std::vector<int64_t> nth_child(ErlNifEnv *env, ExLazyHTML ex_lazy_html) {
902902}
903903FINE_NIF (nth_child, ERL_NIF_DIRTY_JOB_CPU_BOUND );
904904
905- std::string text (ErlNifEnv *env, ExLazyHTML ex_lazy_html) {
906- auto document = ex_lazy_html.resource ->document_ref ->document ;
905+ void node_text (lxb_dom_node_t *node, std::string &content,
906+ std::optional<std::string> &separator) {
907+ // We use an explicit stack instead of recursion to avoid stack
908+ // overflow on deeply nested trees.
909+ struct StackFrame {
910+ lxb_dom_node_t *next_child;
911+ };
907912
908- auto content = std::string () ;
913+ std::vector<StackFrame> stack ;
909914
910- for (auto node : ex_lazy_html.resource ->nodes ) {
911- if (node->type == LXB_DOM_NODE_TYPE_ELEMENT ||
912- node->type == LXB_DOM_NODE_TYPE_TEXT ) {
913- size_t size;
914- auto text = lxb_dom_node_text_content (node, &size);
915- if (text == NULL ) {
916- throw std::runtime_error (" failed to get element text content" );
915+ auto current = node;
916+
917+ while (true ) {
918+ if (current->type == LXB_DOM_NODE_TYPE_TEXT ) {
919+ auto character_data = lxb_dom_interface_character_data (current);
920+ if (character_data->data .length > 0 ) {
921+ if (separator.has_value () && !content.empty ()) {
922+ content.append (separator.value ());
923+ }
924+ content.append (reinterpret_cast <char *>(character_data->data .data ),
925+ character_data->data .length );
926+ }
927+ } else if (current->type == LXB_DOM_NODE_TYPE_ELEMENT ) {
928+ auto first_child = template_aware_first_child (current);
929+ if (first_child != nullptr ) {
930+ stack.push_back ({lxb_dom_node_next (first_child)});
931+ current = first_child;
932+ continue ;
933+ }
934+ }
935+
936+ // Advance to the next sibling, or pop frames until we find one.
937+ while (!stack.empty ()) {
938+ auto &frame = stack.back ();
939+
940+ if (frame.next_child != nullptr ) {
941+ current = frame.next_child ;
942+ frame.next_child = lxb_dom_node_next (current);
943+ break ;
917944 }
918- auto text_guard = ScopeGuard ([&]() {
919- lxb_dom_document_destroy_text (lxb_dom_interface_document (document),
920- text);
921- });
922945
923- content. append ( reinterpret_cast < char *>(text), size );
946+ stack. pop_back ( );
924947 }
948+
949+ if (stack.empty ()) {
950+ return ;
951+ }
952+ }
953+ }
954+
955+ std::string text (ErlNifEnv *env, ExLazyHTML ex_lazy_html,
956+ std::optional<std::string> separator) {
957+ auto content = std::string ();
958+
959+ for (auto node : ex_lazy_html.resource ->nodes ) {
960+ node_text (node, content, separator);
925961 }
926962
927963 return content;
0 commit comments