Skip to content

Commit c0d7223

Browse files
committed
fix: avoid regexp when checking long base64 data uris
1 parent 48aca4f commit c0d7223

File tree

2 files changed

+33
-9
lines changed

2 files changed

+33
-9
lines changed

packages/app/src/lib/utils/getTypeAndExtension.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,29 @@ test('getTypeAndExtension', () => {
2929
outputType: ClapOutputType.VIDEO,
3030
})
3131
})
32+
33+
/**
34+
* Related to the `Maximum call stack size exceeded` issue
35+
* when using RegExp, now with string/array manipulation is
36+
* much faster and the `stack` error solved; I wasn't able to easily
37+
* replicate the stack size error in vitest env, seems happening only
38+
* in Next env; so only a "performance" test is done.
39+
*
40+
* Issue: https://github.com/jbilcke-hf/clapper/issues/72
41+
*/
42+
test('getTypeAndExtension should be fast for long uris', () => {
43+
const startTime = Date.now()
44+
const longBase64String = 'a'.repeat(500_000_000)
45+
const dataUri = `data:image/png;base64,${longBase64String}`
46+
const result = getTypeAndExtension(dataUri)
47+
expect(result).toStrictEqual({
48+
assetFileFormat: 'image/png',
49+
category: 'image',
50+
extension: 'png',
51+
outputType: ClapOutputType.IMAGE,
52+
})
53+
const endTime = Date.now()
54+
const duration = endTime - startTime
55+
// Original regexp approach was running around ~350ms; new one is around ~70ms
56+
expect(duration).toBeLessThan(200)
57+
})

packages/app/src/lib/utils/getTypeAndExtension.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ClapOutputType } from '@aitube/clap'
22

33
/**
4-
* break a base64 string into sub-components
4+
* break a base64 data uri string into sub-components
55
*/
66
export function getTypeAndExtension(base64: string = ''): {
77
// category eg. video, audio, text
@@ -15,17 +15,15 @@ export function getTypeAndExtension(base64: string = ''): {
1515

1616
outputType: ClapOutputType
1717
} {
18-
// Regular expression to extract the MIME type and the base64 data
19-
const matches = base64.match(/^data:([A-Za-z-+0-9/]+);base64,(.+)$/)
20-
21-
if (!matches || matches.length !== 3) {
22-
throw new Error('Invalid base64 string')
18+
if (!base64.startsWith('data:') || !base64.includes('base64,')) {
19+
throw new Error('Invalid base64 data uri provided.')
2320
}
2421

25-
const assetFileFormat = matches[1] || ''
22+
const base64Index = base64.indexOf('base64,')
23+
const mimeType = base64.slice(5, base64Index - 1)
2624

2725
// this should be enough for most media formats (jpeg, png, webp, mp4)
28-
const [category, extension] = assetFileFormat.split('/')
26+
const [category, extension] = mimeType.split('/')
2927

3028
let outputType = ClapOutputType.TEXT
3129

@@ -39,7 +37,7 @@ export function getTypeAndExtension(base64: string = ''): {
3937

4038
return {
4139
category,
42-
assetFileFormat,
40+
assetFileFormat: mimeType,
4341
extension,
4442
outputType,
4543
}

0 commit comments

Comments
 (0)