@@ -574,7 +574,7 @@ fn count_matches(source: string, needle: string) -> map {
574574 let mut index: int = 0;
575575 let mut scanned: int = 0;
576576 let mut result: map = { ok: true, count: 0, code: "", message: "" };
577- while map_bool(result, "ok", false) && index + needle.length <= source.length {
577+ while map_bool(result, "ok", false) && can_scan( index, needle.length, source.length) {
578578 if source[index:(index + needle.length)] == needle {
579579 count = count + 1;
580580 index = index + needle.length;
@@ -601,27 +601,162 @@ fn count_matches(source: string, needle: string) -> map {
601601 result
602602}
603603
604- fn replace_text(source: string, old: string, new: string, limit: int) -> string {
605- let mut out: string = "";
606- let mut start: int = 0;
607- let mut index: int = 0;
608- let mut replaced: int = 0;
609- while index + old.length <= source.length {
610- if replaced == limit {
611- index = source.length;
612- } else {
613- if source[index:(index + old.length)] == old {
614- out = out + source[start:index] + new;
615- index = index + old.length;
616- start = index;
617- replaced = replaced + 1;
604+ fn can_scan(index: int, needle_len: int, source_len: int) -> bool {
605+ let mut result: bool = false;
606+ if index >= 0 {
607+ if needle_len >= 0 {
608+ if source_len >= 0 {
609+ if needle_len <= source_len {
610+ if index <= source_len - needle_len {
611+ result = true;
612+ }
613+ }
614+ }
615+ }
616+ }
617+ result
618+ }
619+
620+ fn chunk_bound(limit: int) -> map {
621+ let mut result: map = { ok: false, value: 0 };
622+ if limit >= 0 {
623+ if limit <= 2147483646 {
624+ result = { ok: true, value: limit * 2 + 1 };
625+ }
626+ }
627+ result
628+ }
629+
630+ fn join_string_chunks(chunks: array) -> string {
631+ let mut current: array = chunks.copy();
632+ while current.length > 1 {
633+ let mut next: array = [];
634+ let mut i: int = 0;
635+ let current_len: int = current.length;
636+ while i < current_len {
637+ if i + 1 < current_len {
638+ let left: string = current[i].copy();
639+ let right: string = current[i + 1].copy();
640+ let joined: string = left + right;
641+ next[next.length] = joined;
642+ i = i + 2;
618643 } else {
619- index = index + 1;
644+ let leftover: string = current[i].copy();
645+ next[next.length] = leftover;
646+ i = i + 1;
647+ }
648+ }
649+ current = next.copy();
650+ }
651+ let mut text: string = "";
652+ if current.length == 1 {
653+ text = current[0].copy();
654+ }
655+ text
656+ }
657+
658+ fn replace_text(source: string, old: string, new: string, limit: int) -> map {
659+ let mut result: map = { ok: true, text: "", code: "", message: "" };
660+ let bound: map = chunk_bound(limit);
661+ if map_bool(bound, "ok", false) == false {
662+ result = {
663+ ok: false,
664+ text: "",
665+ code: "internal_error",
666+ message: "replacement chunk bound overflow"
667+ };
668+ } else {
669+ let max_chunks: int = map_int(bound, "value", 0);
670+ let mut chunks: array = [];
671+ let mut start: int = 0;
672+ let mut index: int = 0;
673+ let mut replaced: int = 0;
674+ let mut scanned: int = 0;
675+ while map_bool(result, "ok", false) && can_scan(index, old.length, source.length) {
676+ if replaced == limit {
677+ index = source.length;
678+ } else {
679+ if source[index:(index + old.length)] == old {
680+ let prefix: string = source[start:index];
681+ if prefix.length > 0 {
682+ if chunks.length >= max_chunks {
683+ result = {
684+ ok: false,
685+ text: "",
686+ code: "internal_error",
687+ message: "replacement chunk bound exceeded"
688+ };
689+ } else {
690+ chunks[chunks.length] = prefix;
691+ }
692+ }
693+ if map_bool(result, "ok", false) {
694+ if new.length > 0 {
695+ if chunks.length >= max_chunks {
696+ result = {
697+ ok: false,
698+ text: "",
699+ code: "internal_error",
700+ message: "replacement chunk bound exceeded"
701+ };
702+ } else {
703+ chunks[chunks.length] = new.copy();
704+ }
705+ }
706+ }
707+ if map_bool(result, "ok", false) {
708+ index = index + old.length;
709+ start = index;
710+ replaced = replaced + 1;
711+ }
712+ } else {
713+ index = index + 1;
714+ }
715+ }
716+ scanned = scanned + 1;
717+ if scanned == 64 {
718+ scanned = 0;
719+ let checked: map = control_failure();
720+ if map_bool(checked, "ok", false) == false {
721+ result = {
722+ ok: false,
723+ text: "",
724+ code: types::map_string(checked, "code", "cancelled"),
725+ message: types::map_string(checked, "message", "")
726+ };
727+ }
728+ }
729+ }
730+ if map_bool(result, "ok", false) {
731+ let tail: string = source[start:source.length];
732+ if tail.length > 0 {
733+ if chunks.length >= max_chunks {
734+ result = {
735+ ok: false,
736+ text: "",
737+ code: "internal_error",
738+ message: "replacement chunk bound exceeded"
739+ };
740+ } else {
741+ chunks[chunks.length] = tail;
742+ }
743+ }
744+ }
745+ if map_bool(result, "ok", false) {
746+ let finished: map = control_failure();
747+ if map_bool(finished, "ok", false) == false {
748+ result = {
749+ ok: false,
750+ text: "",
751+ code: types::map_string(finished, "code", "cancelled"),
752+ message: types::map_string(finished, "message", "")
753+ };
754+ } else {
755+ result.text = join_string_chunks(chunks);
620756 }
621757 }
622758 }
623- out = out + source[start:source.length];
624- out
759+ result
625760}
626761
627762pub fn descriptor() -> map {
@@ -703,7 +838,7 @@ pub fn execute(context: map, arguments: map) -> map {
703838 result = fail_host(meta);
704839 } else {
705840 let file_type: string = types::map_string(meta, "file_type", "");
706- if file_type != "file " {
841+ if file_type == "directory" || file_type == "other " {
707842 result = fail("path_denied", "read-only open requires a regular file", unpublished());
708843 } else {
709844 let file_len: int = map_int(meta, "len", 0);
@@ -771,33 +906,38 @@ pub fn execute(context: map, arguments: map) -> map {
771906 if replace_all {
772907 limit = matches;
773908 }
774- let updated: string = replace_text(source, old_string, new_string, limit);
775- if utf8_len(updated) > max_patch_bytes {
776- result = fail("patch_too_large ", "result exceeds the configured patch budget" , unpublished());
909+ let replaced_text: map = replace_text(source, old_string, new_string, limit);
910+ if map_bool(replaced_text, "ok", false) == false {
911+ result = fail(types::map_string(replaced_text, "code ", "cancelled"), types::map_string(replaced_text, "message", "") , unpublished());
777912 } else {
778- let before_publish: map = control_failure( );
779- if map_bool(before_publish, "ok", false) == false {
780- result = fail(types::map_string(before_publish, "code ", "cancelled"), types::map_string(before_publish, "message", "") , unpublished());
913+ let updated: string = types::map_string(replaced_text, "text", "" );
914+ if utf8_len(updated) > max_patch_bytes {
915+ result = fail("patch_too_large ", "result exceeds the configured patch budget" , unpublished());
781916 } else {
782- let payload: bytes = bytes::from_utf8(updated);
783- let written: map = cap::fs_write_atomic(token, path, expected_hash, payload);
784- if map_bool(written, "ok", false) == false {
785- result = fail_host(written);
917+ let before_publish: map = control_failure();
918+ if map_bool(before_publish, "ok", false) == false {
919+ result = fail(types::map_string(before_publish, "code", "cancelled"), types::map_string(before_publish, "message", ""), unpublished());
786920 } else {
787- let preview: string = bounded_diff(path, source, updated, max_patch_preview_bytes);
788- let mut published: map = succeed(
789- preview,
790- {
791- publication: "published",
792- durable: map_bool(written, "durable", true),
793- staging_cleaned: map_bool(written, "staging_cleaned", true),
794- bytes: map_int(written, "len", utf8_len(updated)),
795- replacements: replacements
796- },
797- false,
798- []
799- );
800- result = shrink_result(published, max_output_bytes, token);
921+ let payload: bytes = bytes::from_utf8(updated);
922+ let written: map = cap::fs_write_atomic(token, path, expected_hash, payload);
923+ if map_bool(written, "ok", false) == false {
924+ result = fail_host(written);
925+ } else {
926+ let preview: string = bounded_diff(path, source, updated, max_patch_preview_bytes);
927+ let mut published: map = succeed(
928+ preview,
929+ {
930+ publication: "published",
931+ durable: map_bool(written, "durable", true),
932+ staging_cleaned: map_bool(written, "staging_cleaned", true),
933+ bytes: map_int(written, "len", utf8_len(updated)),
934+ replacements: replacements
935+ },
936+ false,
937+ []
938+ );
939+ result = shrink_result(published, max_output_bytes, token);
940+ }
801941 }
802942 }
803943 }
0 commit comments