| 1035 | } |
| 1036 | json_ws(p); |
| 1037 | if (**p == ',') (*p)++; |
| 1038 | json_ws(p); |
| 1039 | } |
| 1040 | if (**p != ']') return false; |
| 1041 | (*p)++; |
| 1042 | return true; |
| 1043 | } |
| 1044 | |
| 1045 | static bool stop_list_find_from(const stop_list *stops, const char *text, |
| 1046 | size_t from, size_t *pos, size_t *len) { |
| 1047 | if (!stops->len || !text) return false; |
| 1048 | bool found = false; |
| 1049 | size_t best_pos = 0, best_len = 0; |
| 1050 | for (int i = 0; i < stops->len; i++) { |
| 1051 | const char *p = strstr(text + from, stops->v[i]); |
| 1052 | if (!p) continue; |
| 1053 | size_t ppos = (size_t)(p - text); |
| 1054 | size_t plen = strlen(stops->v[i]); |
| 1055 | if (!found || ppos < best_pos) { |
| 1056 | found = true; |
| 1057 | best_pos = ppos; |
| 1058 | best_len = plen; |
| 1059 | } |
| 1060 | } |
| 1061 | if (!found) return false; |
| 1062 | *pos = best_pos; |
| 1063 | *len = best_len; |
| 1064 | return true; |
| 1065 | } |
| 1066 | |
| 1067 | static size_t stop_list_stream_safe_len(const stop_list *stops, size_t text_len) { |
| 1068 | /* Streaming cannot emit the last max_stop_len-1 bytes yet: a stop sequence |
| 1069 | * may start there and finish in the next token. The final flush releases |
| 1070 | * this small tail once generation ends without a stop hit. */ |
| 1071 | if (!stops->len || stops->max_len <= 1) return text_len; |
no test coverage detected