Skip to content

Commit 7e54fc7

Browse files
authored
feat: Added native CRC32 option. (#126)
Signed-off-by: Paolo Insogna <[email protected]>
1 parent e15b2ed commit 7e54fc7

File tree

4 files changed

+205
-26
lines changed

4 files changed

+205
-26
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"scule": "^1.3.0"
5151
},
5252
"optionalDependencies": {
53+
"@node-rs/crc32": "^1.10.6",
5354
"lz4-napi": "^2.9.0",
5455
"snappy": "^7.3.3"
5556
},
@@ -81,4 +82,4 @@
8182
"engines": {
8283
"node": ">= 20.19.4 || >= 22.18.0 || >= 24.6.0"
8384
}
84-
}
85+
}

pnpm-lock.yaml

Lines changed: 152 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/protocol/crc32c.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Based on the work from: https://github.com/tulios/kafkajs/blob/master/src/protocol/recordBatch/crc32C/crc32C.js
2-
2+
import { createRequire } from 'node:module'
33
import { DynamicBuffer } from './dynamic-buffer.ts'
44

55
/* prettier-ignore */
@@ -70,7 +70,24 @@ const CRC: number[] = [
7070
0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351
7171
]
7272

73-
export function crc32c (data: Buffer | Uint8Array | DynamicBuffer): number {
73+
export function loadNativeCRC32C (): typeof jsCRC32C | null {
74+
try {
75+
const require = createRequire(import.meta.url)
76+
const { crc32c: nativeImplementation } = require('@node-rs/crc32')
77+
78+
return function nativeCRC32C (data: Buffer | Uint8Array | DynamicBuffer): number {
79+
const bytes: Uint8Array = DynamicBuffer.isDynamicBuffer(data)
80+
? ((data as DynamicBuffer).buffer as Uint8Array)
81+
: new Uint8Array(data as Uint8Array)
82+
83+
return nativeImplementation(bytes)
84+
}
85+
} catch (error) {
86+
return null
87+
}
88+
}
89+
90+
export function jsCRC32C (data: Buffer | Uint8Array | DynamicBuffer): number {
7491
const bytes: Uint8Array = DynamicBuffer.isDynamicBuffer(data)
7592
? ((data as DynamicBuffer).buffer as Uint8Array)
7693
: new Uint8Array(data as Uint8Array)
@@ -83,3 +100,5 @@ export function crc32c (data: Buffer | Uint8Array | DynamicBuffer): number {
83100

84101
return (crc ^ 0xffffffff) >>> 0
85102
}
103+
104+
export const crc32c = loadNativeCRC32C() ?? jsCRC32C

test/protocol/crc32.test.ts

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { deepStrictEqual } from 'node:assert'
22
import test from 'node:test'
3-
import { crc32c, DynamicBuffer } from '../../src/index.ts'
3+
import { DynamicBuffer, jsCRC32C, loadNativeCRC32C } from '../../src/index.ts'
44

55
// Samples copied from https://github.com/tulios/kafkajs/blob/55b0b416308b9e597a5a6b97b0a6fd6b846255dc/src/protocol/recordBatch/crc32c/fixtures/samples.js
66
const samples = [
@@ -68,29 +68,36 @@ const java = Buffer.from([
6868
57, 100, 102, 102, 99, 52, 97, 97, 99, 57, 2, 2, 97, 2, 98
6969
])
7070

71-
test('perform crc32c computations', () => {
72-
const longString =
73-
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi mollis cursus metus vel tristique. Proin congue massa massa, a malesuada dolor ullamcorper a. Nulla eget leo vel orci venenatis placerat. Donec semper condimentum justo, vel sollicitudin dolor consequat id. Nunc sed aliquet felis, eget congue nisi. Mauris eu justo suscipit, elementum turpis ut, molestie tellus. Mauris ornare rutrum fringilla. Nulla dignissim luctus pretium. Nullam nec eros hendrerit sapien pellentesque sollicitudin. Integer eget ligula dui. Mauris nec cursus nibh. Nunc interdum elementum leo, eu sagittis eros sodales nec. Duis dictum nulla sed tincidunt malesuada. Quisque in vulputate sapien. Sed sit amet tellus a est porta rhoncus sed eu metus. Mauris non pulvinar nisl, volutpat luctus enim. Suspendisse est nisi, sagittis at risus quis, ultricies rhoncus sem. Donec ullamcorper purus eget sapien facilisis, eu eleifend felis viverra. Suspendisse elit neque, semper aliquet neque sed, egestas tempus leo. Duis condimentum turpis duis.'
74-
const buffer = Buffer.from(longString)
75-
deepStrictEqual(crc32c(new DynamicBuffer(buffer)), 1796588439)
76-
})
71+
const implementations = {
72+
Javascript: jsCRC32C,
73+
Native: loadNativeCRC32C()!
74+
}
7775

78-
test('match the java crc32c code', () => {
79-
deepStrictEqual(crc32c(new DynamicBuffer(java)), 818496390)
80-
deepStrictEqual(crc32c(java), 818496390)
81-
})
76+
for (const [name, implementation] of Object.entries(implementations)) {
77+
test(`perform crc32c computations (${name})`, () => {
78+
const longString =
79+
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi mollis cursus metus vel tristique. Proin congue massa massa, a malesuada dolor ullamcorper a. Nulla eget leo vel orci venenatis placerat. Donec semper condimentum justo, vel sollicitudin dolor consequat id. Nunc sed aliquet felis, eget congue nisi. Mauris eu justo suscipit, elementum turpis ut, molestie tellus. Mauris ornare rutrum fringilla. Nulla dignissim luctus pretium. Nullam nec eros hendrerit sapien pellentesque sollicitudin. Integer eget ligula dui. Mauris nec cursus nibh. Nunc interdum elementum leo, eu sagittis eros sodales nec. Duis dictum nulla sed tincidunt malesuada. Quisque in vulputate sapien. Sed sit amet tellus a est porta rhoncus sed eu metus. Mauris non pulvinar nisl, volutpat luctus enim. Suspendisse est nisi, sagittis at risus quis, ultricies rhoncus sem. Donec ullamcorper purus eget sapien facilisis, eu eleifend felis viverra. Suspendisse elit neque, semper aliquet neque sed, egestas tempus leo. Duis condimentum turpis duis.'
80+
const buffer = Buffer.from(longString)
81+
deepStrictEqual(implementation(new DynamicBuffer(buffer)), 1796588439)
82+
})
8283

83-
test('samples', () => {
84-
for (const sample of samples) {
85-
const buffer = Buffer.from(sample.input)
86-
deepStrictEqual(crc32c(new DynamicBuffer(buffer)), sample.output)
87-
}
88-
})
84+
test(`match the java crc32c code (${name})`, () => {
85+
deepStrictEqual(implementation(new DynamicBuffer(java)), 818496390)
86+
deepStrictEqual(implementation(java), 818496390)
87+
})
88+
89+
test(`samples (${name})`, () => {
90+
for (const sample of samples) {
91+
const buffer = Buffer.from(sample.input)
92+
deepStrictEqual(implementation(new DynamicBuffer(buffer)), sample.output)
93+
}
94+
})
8995

90-
test('empty', () => {
91-
deepStrictEqual(crc32c(new DynamicBuffer(Buffer.alloc(0))), 0)
92-
})
96+
test(`empty (${name})`, () => {
97+
deepStrictEqual(implementation(new DynamicBuffer(Buffer.alloc(0))), 0)
98+
})
9399

94-
test('unicode null', () => {
95-
deepStrictEqual(crc32c(new DynamicBuffer(Buffer.from('\u0000'))), 1383945041)
96-
})
100+
test(`unicode null (${name})`, () => {
101+
deepStrictEqual(implementation(new DynamicBuffer(Buffer.from('\u0000'))), 1383945041)
102+
})
103+
}

0 commit comments

Comments
 (0)