-
Notifications
You must be signed in to change notification settings - Fork 260
feat(NODE-4870): Support BigInt serialization #541
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
3a66fa9
feat(NODE-4870): Begin adding support for bigint serialization
W-A-James 2d6edf0
test(NODE-4870): Start updating tests
W-A-James d0c7688
fix(NODE-4870): remove unneeded imports
W-A-James 621cc19
test(NODE-4870): Add tests for bigint serialization
W-A-James d030f4e
test(NODE-4870): Add test for toBSON method
W-A-James 29fdd7b
style(NODE-4870): eslint
W-A-James 4539b2b
test(NODE-4870): Rename tests to better match language in design and …
W-A-James 8dbe23d
style(NODE-4870): eslint
W-A-James 7a06302
test(NODE-4870): Remove duplicated test and change test file naming s…
W-A-James ecb9de7
test(NODE-4870): Reorganize tests
W-A-James 39a5be8
test(NODE-4870): test cleanup
W-A-James 6b33750
test(NODE-4870): Test file rename and cleanup
W-A-James 4e94b20
style(NODE-4870): clean up imports
W-A-James 02c9cf2
test(NODE-4870): Add helper method to make debugging tests easier and…
W-A-James 1460722
fix(NODE-4870): Update if condition
W-A-James 52fe454
style(NODE-4870): remove unneeded import
W-A-James 50b619a
test(NODE-4870): Add test for array of BigInt and map of Bigint
W-A-James 6e3974a
test(NODE-4870): test nits
W-A-James a43b8f3
Merge branch 'main' into NODE-4870/support_bigint64_serialization
nbbeeken File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import { BSON } from '../register-bson'; | ||
import { bufferFromHexArray } from './tools/utils'; | ||
import { BSON_DATA_LONG } from '../../src/constants'; | ||
import { BSONDataView } from '../../src/utils/byte_utils'; | ||
|
||
describe('BSON BigInt serialization Support', function () { | ||
// Index for the data type byte of a BSON document with a | ||
// NOTE: These offsets only apply for documents with the shape {a : <n>} | ||
// where n is a BigInt | ||
type SerializedDocParts = { | ||
dataType: number; | ||
key: string; | ||
value: bigint; | ||
}; | ||
/** | ||
* NOTE: this function operates on serialized BSON documents with the shape { <key> : <n> } | ||
* where n is some int64. This function assumes that keys are properly encoded | ||
* with the necessary null byte at the end and only at the end of the key string | ||
*/ | ||
function getSerializedDocParts(serializedDoc: Uint8Array): SerializedDocParts { | ||
const DATA_TYPE_OFFSET = 4; | ||
const KEY_OFFSET = 5; | ||
|
||
const dataView = BSONDataView.fromUint8Array(serializedDoc); | ||
const keySlice = serializedDoc.slice(KEY_OFFSET); | ||
|
||
let keyLength = 0; | ||
while (keySlice[keyLength++] !== 0); | ||
|
||
const valueOffset = KEY_OFFSET + keyLength; | ||
const key = Buffer.from(serializedDoc.slice(KEY_OFFSET, KEY_OFFSET + keyLength)).toString( | ||
'utf8' | ||
); | ||
|
||
return { | ||
dataType: dataView.getInt8(DATA_TYPE_OFFSET), | ||
key: key.slice(0, keyLength - 1), | ||
value: dataView.getBigInt64(valueOffset, true) | ||
}; | ||
} | ||
|
||
it('serializes bigints with the correct BSON type', function () { | ||
const testDoc = { a: 0n }; | ||
const serializedDoc = getSerializedDocParts(BSON.serialize(testDoc)); | ||
expect(serializedDoc.dataType).to.equal(BSON_DATA_LONG); | ||
}); | ||
|
||
it('serializes bigints into little-endian byte order', function () { | ||
const testDoc = { a: 0x1234567812345678n }; | ||
const serializedDoc = getSerializedDocParts(BSON.serialize(testDoc)); | ||
const expectedResult = getSerializedDocParts( | ||
bufferFromHexArray([ | ||
'12', // int64 type | ||
'6100', // 'a' key with null terminator | ||
'7856341278563412' | ||
]) | ||
); | ||
|
||
expect(expectedResult.value).to.equal(serializedDoc.value); | ||
}); | ||
|
||
it('serializes a BigInt that can be safely represented as a Number', function () { | ||
const testDoc = { a: 0x23n }; | ||
const serializedDoc = getSerializedDocParts(BSON.serialize(testDoc)); | ||
const expectedResult = getSerializedDocParts( | ||
bufferFromHexArray([ | ||
'12', // int64 type | ||
'6100', // 'a' key with null terminator | ||
'2300000000000000' // little endian int64 | ||
]) | ||
); | ||
expect(serializedDoc).to.deep.equal(expectedResult); | ||
}); | ||
|
||
it('serializes a BigInt in the valid range [-2^63, 2^63 - 1]', function () { | ||
const testDoc = { a: 0xfffffffffffffff1n }; | ||
const serializedDoc = getSerializedDocParts(BSON.serialize(testDoc)); | ||
const expectedResult = getSerializedDocParts( | ||
bufferFromHexArray([ | ||
'12', // int64 | ||
'6100', // 'a' key with null terminator | ||
'f1ffffffffffffff' | ||
]) | ||
); | ||
expect(serializedDoc).to.deep.equal(expectedResult); | ||
}); | ||
|
||
it('wraps to negative on a BigInt that is larger than (2^63 -1)', function () { | ||
const maxIntPlusOne = { a: 2n ** 63n }; | ||
const serializedMaxIntPlusOne = getSerializedDocParts(BSON.serialize(maxIntPlusOne)); | ||
const expectedResultForMaxIntPlusOne = getSerializedDocParts( | ||
bufferFromHexArray([ | ||
'12', // int64 | ||
'6100', // 'a' key with null terminator | ||
'0000000000000080' | ||
]) | ||
); | ||
expect(serializedMaxIntPlusOne).to.deep.equal(expectedResultForMaxIntPlusOne); | ||
}); | ||
|
||
it('serializes BigInts at the edges of the valid range [-2^63, 2^63 - 1]', function () { | ||
const maxPositiveInt64 = { a: 2n ** 63n - 1n }; | ||
const serializedMaxPositiveInt64 = getSerializedDocParts(BSON.serialize(maxPositiveInt64)); | ||
const expectedSerializationForMaxPositiveInt64 = getSerializedDocParts( | ||
bufferFromHexArray([ | ||
'12', // int64 | ||
'6100', // 'a' key with null terminator | ||
'ffffffffffffff7f' | ||
]) | ||
); | ||
expect(serializedMaxPositiveInt64).to.deep.equal(expectedSerializationForMaxPositiveInt64); | ||
|
||
const minPositiveInt64 = { a: -(2n ** 63n) }; | ||
const serializedMinPositiveInt64 = getSerializedDocParts(BSON.serialize(minPositiveInt64)); | ||
const expectedSerializationForMinPositiveInt64 = getSerializedDocParts( | ||
bufferFromHexArray([ | ||
'12', // int64 | ||
'6100', // 'a' key with null terminator | ||
'0000000000000080' | ||
]) | ||
); | ||
expect(serializedMinPositiveInt64).to.deep.equal(expectedSerializationForMinPositiveInt64); | ||
}); | ||
|
||
it('truncates a BigInt that is larger than a 64-bit int', function () { | ||
const testDoc = { a: 2n ** 64n + 1n }; | ||
const serializedDoc = getSerializedDocParts(BSON.serialize(testDoc)); | ||
const expectedSerialization = getSerializedDocParts( | ||
bufferFromHexArray([ | ||
'12', //int64 | ||
'6100', // 'a' key with null terminator | ||
'0100000000000000' | ||
]) | ||
); | ||
expect(serializedDoc).to.deep.equal(expectedSerialization); | ||
}); | ||
|
||
it('serializes array of BigInts', function () { | ||
const testArr = { a: [1n] }; | ||
const serializedArr = BSON.serialize(testArr); | ||
const expectedSerialization = bufferFromHexArray([ | ||
'04', // array | ||
'6100', // 'a' key with null terminator | ||
bufferFromHexArray([ | ||
'12', // int64 | ||
'3000', // '0' key with null terminator | ||
'0100000000000000' // 1n (little-endian) | ||
]).toString('hex') | ||
]); | ||
expect(serializedArr).to.deep.equal(expectedSerialization); | ||
}); | ||
|
||
it('serializes Map with BigInt values', function () { | ||
const testMap = new Map(); | ||
testMap.set('a', 1n); | ||
const serializedMap = getSerializedDocParts(BSON.serialize(testMap)); | ||
const expectedSerialization = getSerializedDocParts( | ||
bufferFromHexArray([ | ||
'12', // int64 | ||
'6100', // 'a' key with null terminator | ||
'0100000000000000' | ||
]) | ||
); | ||
expect(serializedMap).to.deep.equal(expectedSerialization); | ||
}); | ||
}); |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { Long } from '../register-bson'; | ||
|
||
describe('Long', function () { | ||
it('accepts strings in the constructor', function () { | ||
expect(new Long('0').toString()).to.equal('0'); | ||
expect(new Long('00').toString()).to.equal('0'); | ||
expect(new Long('-1').toString()).to.equal('-1'); | ||
expect(new Long('-1', true).toString()).to.equal('18446744073709551615'); | ||
expect(new Long('123456789123456789').toString()).to.equal('123456789123456789'); | ||
expect(new Long('123456789123456789', true).toString()).to.equal('123456789123456789'); | ||
expect(new Long('13835058055282163712').toString()).to.equal('-4611686018427387904'); | ||
expect(new Long('13835058055282163712', true).toString()).to.equal('13835058055282163712'); | ||
}); | ||
|
||
it('accepts BigInts in Long constructor', function () { | ||
expect(new Long(0n).toString()).to.equal('0'); | ||
expect(new Long(-1n).toString()).to.equal('-1'); | ||
expect(new Long(-1n, true).toString()).to.equal('18446744073709551615'); | ||
expect(new Long(123456789123456789n).toString()).to.equal('123456789123456789'); | ||
expect(new Long(123456789123456789n, true).toString()).to.equal('123456789123456789'); | ||
expect(new Long(13835058055282163712n).toString()).to.equal('-4611686018427387904'); | ||
expect(new Long(13835058055282163712n, true).toString()).to.equal('13835058055282163712'); | ||
}); | ||
}); |
This file was deleted.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.