diff --git a/src/helpers/parseUtil.ts b/src/helpers/parseUtil.ts index f6b105a5b..14433d57a 100644 --- a/src/helpers/parseUtil.ts +++ b/src/helpers/parseUtil.ts @@ -63,6 +63,54 @@ export interface ParseContext { readonly parsedType: ZodParsedType; } +/** + * Processes a large input string in chunks and validates each chunk using a custom processor. + * Optimized for speed and memory efficiency with configurable chunk and batch sizes. + * + * @param {string} input - The input string to be processed in chunks. + * @param {number} chunkSize - The size of each chunk in characters. + * @param {number} batchSize - The number of chunks to process in one batch. + * @param {(chunk: string) => boolean} processor - A function to validate each chunk. Should return `true` if valid, `false` otherwise. + * @returns {boolean} - Returns `true` if all chunks pass validation, otherwise `false`. + * + * @example + * const isValid = processInChunks( + * largeString, + * 2048, // chunk size + * 4, // batch size + * (chunk) => /^[A-Za-z0-9+/]+={0,2}$/.test(chunk) + * ); + * console.log(isValid); // true or false + */ +export function processInChunks( + input: string, + chunkSize: number, + batchSize: number, + processor: (chunk: string) => boolean +): boolean { + // Handle single chunk case and length of 0 + if (input.length <= chunkSize) return processor(input); + + const totalChunks = Math.ceil(input.length / chunkSize); + + // Process chunks in batches + for (let i = 0; i < totalChunks; i += batchSize) { + const batchLimit = Math.min(i + batchSize, totalChunks); + + for (let j = i; j < batchLimit; j++) { + const startIndex = j * chunkSize; + const chunk = input.slice(startIndex, startIndex + chunkSize); + + // Stop processing on the first failure + if (!processor(chunk)) { + return false; + } + } + } + + return true; +} + export type ParseInput = { data: any; path: (string | number)[]; diff --git a/src/types.ts b/src/types.ts index f3730ae14..323dcd857 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,6 +19,7 @@ import { ParseReturnType, ParseStatus, SyncParseReturnType, + processInChunks, } from "./helpers/parseUtil"; import { partialUtil } from "./helpers/partialUtil"; import { Primitive } from "./helpers/typeAliases"; @@ -934,7 +935,7 @@ export class ZodString extends ZodType { status.dirty(); } } else if (check.kind === "base64") { - if (!base64Regex.test(input.data)) { + if(!processInChunks(input.data, 2048, 4, (chunk)=>base64Regex(chunk))) { ctx = this._getOrReturnCtx(input, ctx); addIssueToContext(ctx, { validation: "base64",