From 010a36543896b121b99aa43f0330874139db0b63 Mon Sep 17 00:00:00 2001 From: "Klaus L. Hougesen" Date: Wed, 4 Dec 2024 13:00:03 +0100 Subject: [PATCH 1/3] Update types.ts --- src/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types.ts b/src/types.ts index f3730ae14..7ab3480d6 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"; From ad0a7e1411294b4b804e2cc827bb53af1cbf00e0 Mon Sep 17 00:00:00 2001 From: "Klaus L. Hougesen" Date: Wed, 4 Dec 2024 13:04:50 +0100 Subject: [PATCH 2/3] Updated base64 in types.ts to use chunk processing --- src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.ts b/src/types.ts index 7ab3480d6..323dcd857 100644 --- a/src/types.ts +++ b/src/types.ts @@ -935,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", From 9ac9eddb81021202e804cd012ae068305e963858 Mon Sep 17 00:00:00 2001 From: "Klaus L. Hougesen" Date: Wed, 4 Dec 2024 13:06:59 +0100 Subject: [PATCH 3/3] add chunkparser to parseUtils When processing large strings zod will cause overflows and inefficient handling of items - especially base64 strings can be large at times --- src/helpers/parseUtil.ts | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) 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)[];