Skip to content

Commit 21086a6

Browse files
authored
Merge pull request #330 from linux-on-ibm-z/master-endian
Add support for big endian platforms
2 parents 57fe40c + d92f6e7 commit 21086a6

File tree

9 files changed

+70
-40
lines changed

9 files changed

+70
-40
lines changed

index.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { EventEmitter } from 'events';
22
import { setExternals, setNativeFunctions, Dbi, version } from './native.js';
3-
import { arch, tmpdir, platform } from 'os';
3+
import { arch, tmpdir, platform, endianness } from 'os';
44
import fs from 'fs';
55
import { Encoder as MsgpackrEncoder } from 'msgpackr';
66
import { WeakLRUCache } from 'weak-lru-cache';
@@ -14,7 +14,7 @@ setExternals({
1414
if (process.getMaxListeners() < process.listenerCount('exit') + 8)
1515
process.setMaxListeners(process.listenerCount('exit') + 8);
1616
process.on('exit', callback);
17-
},
17+
}, isLittleEndian: endianness() == 'LE'
1818
});
1919
export { toBufferKey as keyValueToBuffer, compareKeys, compareKeys as compareKey, fromBufferKey as bufferToKeyValue } from 'ordered-binary';
2020
export { ABORT, IF_EXISTS, asBinary } from './write.js';
@@ -25,8 +25,25 @@ import { levelup } from './level.js';
2525
export { clearKeptObjects, version } from './native.js';
2626
import { nativeAddon } from './native.js';
2727
export let { noop } = nativeAddon;
28-
export const TIMESTAMP_PLACEHOLDER = new Uint8Array([1,1,1,1,0,0,0,0]);
29-
export const DIRECT_WRITE_PLACEHOLDER = new Uint8Array([1,1,1,2,0,0,0,0]);
28+
export const TIMESTAMP_PLACEHOLDER = (() => {
29+
if (endianness() == 'BE') {
30+
return new Uint8Array([0,0,0,0,1,1,1,1]);
31+
} else {
32+
return new Uint8Array([1,1,1,1,0,0,0,0]);
33+
}
34+
})();
35+
export const DIRECT_WRITE_PLACEHOLDER = (() => {
36+
if (endianness() == 'BE') {
37+
return new Uint8Array([0,0,0,0,2,1,1,1]);
38+
} else {
39+
return new Uint8Array([1,1,1,2,0,0,0,0]);
40+
}
41+
})();
42+
export function setSpecialWriteValue(destArray, placeholder, uint32Value) {
43+
destArray.set(placeholder);
44+
let uint32 = new Uint32Array(destArray.buffer, 0, 2);
45+
endianness() == 'BE' ? uint32[0] = uint32Value : uint32[1] = uint32Value;
46+
}
3047
export { open, openAsClass, getLastVersion, allDbs, getLastTxnId } from './open.js';
3148
import { toBufferKey as keyValueToBuffer, compareKeys as compareKey, fromBufferKey as bufferToKeyValue } from 'ordered-binary';
3249
import { open, openAsClass, getLastVersion } from './open.js';

keys.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { getAddress, orderedBinary } from './native.js';
1+
import { getAddress, orderedBinary, isLittleEndian } from './native.js';
22

33
const REUSE_BUFFER_MODE = 512;
44
const writeUint32Key = (key, target, start) => {
5-
(target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length))).setUint32(start, key, true);
5+
(target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length))).setUint32(start, key, isLittleEndian);
66
return start + 4;
77
};
88
const readUint32Key = (target, start) => {
9-
return (target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length))).getUint32(start, true);
9+
return (target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length))).getUint32(start, isLittleEndian);
1010
};
1111
const writeBufferKey = (key, target, start) => {
1212
target.set(key, start);
@@ -70,7 +70,7 @@ function allocateSaveBuffer() {
7070
saveDataAddress = saveBuffer.buffer.address;
7171
// TODO: Conditionally only do this for key sequences?
7272
saveDataView.setUint32(savePosition, 0xffffffff);
73-
saveDataView.setFloat64(savePosition + 4, saveDataAddress, true); // save a pointer from the old buffer to the new address for the sake of the prefetch sequences
73+
saveDataView.setFloat64(savePosition + 4, saveDataAddress, isLittleEndian); // save a pointer from the old buffer to the new address for the sake of the prefetch sequences
7474
saveDataView = saveBuffer.dataView || (saveBuffer.dataView = new DataView(saveBuffer.buffer, saveBuffer.byteOffset, saveBuffer.byteLength));
7575
savePosition = 0;
7676
}
@@ -103,7 +103,7 @@ export function saveKey(key, writeKey, saveTo, maxKeySize, flags) {
103103
return saveKey(key, writeKey, saveTo, maxKeySize);
104104
}
105105
if (saveTo) {
106-
saveDataView.setUint32(start, flags ? length | flags : length, true); // save the length
106+
saveDataView.setUint32(start, flags ? length | flags : length, isLittleEndian); // save the length
107107
saveTo.saveBuffer = saveBuffer;
108108
savePosition = (savePosition + 12) & 0xfffffc;
109109
return start + saveDataAddress;

native.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export let Env,
1616
fs,
1717
os,
1818
onExit,
19+
isLittleEndian,
1920
tmpdir,
2021
lmdbError,
2122
path,
@@ -131,4 +132,5 @@ export function setExternals(externals) {
131132
tmpdir = externals.tmpdir;
132133
os = externals.os;
133134
onExit = externals.onExit;
135+
isLittleEndian = externals.isLittleEndian;
134136
}

open.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Compression, getAddress, arch, fs, path as pathModule, lmdbError, EventEmitter, MsgpackrEncoder, Env,
2-
Dbi, tmpdir, os, nativeAddon, version } from './native.js';
2+
Dbi, tmpdir, os, nativeAddon, version, isLittleEndian } from './native.js';
33
import { CachingStore, setGetLastVersion } from './caching.js';
44
import { addReadMethods, makeReusableBuffer } from './read.js';
55
import { addWriteMethods } from './write.js';
@@ -410,14 +410,14 @@ export function openAsClass(path, options) {
410410
}
411411

412412
export function getLastVersion() {
413-
return keyBytesView.getFloat64(16, true);
413+
return keyBytesView.getFloat64(16, isLittleEndian);
414414
}
415415
export function setLastVersion(version) {
416-
return keyBytesView.setFloat64(16, version, true);
416+
return keyBytesView.setFloat64(16, version, isLittleEndian);
417417
}
418418

419419
export function getLastTxnId() {
420-
return keyBytesView.getUint32(32, true);
420+
return keyBytesView.getUint32(32, isLittleEndian);
421421
}
422422

423423
const KEY_BUFFER_SIZE = 4096;

read.js

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
notifyUserCallbacks,
2424
attemptLock,
2525
unlock,
26+
isLittleEndian
2627
} from './native.js';
2728
import { saveKey } from './keys.js';
2829
const IF_EXISTS = 3.542694326329068e-103;
@@ -108,11 +109,11 @@ export function addReadMethods(
108109
);
109110
if (rc == -30000)
110111
// int32 overflow, read uint32
111-
rc = this.lastSize = keyBytesView.getUint32(0, true);
112+
rc = this.lastSize = keyBytesView.getUint32(0, isLittleEndian);
112113
else if (rc == -30001) {
113114
// shared buffer
114-
this.lastSize = keyBytesView.getUint32(0, true);
115-
let bufferId = keyBytesView.getUint32(4, true);
115+
this.lastSize = keyBytesView.getUint32(0, isLittleEndian);
116+
let bufferId = keyBytesView.getUint32(4, isLittleEndian);
116117
let bytes = getMMapBuffer(bufferId, this.lastSize);
117118
return asSafeBuffer ? Buffer.from(bytes) : bytes;
118119
} else throw lmdbError(rc);
@@ -624,7 +625,7 @@ export function addReadMethods(
624625
keyBytesView.setFloat64(
625626
START_ADDRESS_POSITION,
626627
startAddress,
627-
true,
628+
isLittleEndian,
628629
);
629630
endAddress = saveKey(
630631
options.end,
@@ -645,7 +646,7 @@ export function addReadMethods(
645646
keyBytesView.setFloat64(
646647
START_ADDRESS_POSITION,
647648
startAddress,
648-
true,
649+
isLittleEndian,
649650
);
650651
endAddress = saveKey(
651652
options.end,
@@ -736,8 +737,8 @@ export function addReadMethods(
736737
}
737738
if (includeValues) {
738739
let value;
739-
lastSize = keyBytesView.getUint32(0, true);
740-
let bufferId = keyBytesView.getUint32(4, true);
740+
lastSize = keyBytesView.getUint32(0, isLittleEndian);
741+
let bufferId = keyBytesView.getUint32(4, isLittleEndian);
741742
let bytes;
742743
if (bufferId) {
743744
bytes = getMMapBuffer(bufferId, lastSize);
@@ -850,9 +851,9 @@ export function addReadMethods(
850851
return; //undefined
851852
}
852853
return this.lastSize;
853-
this.lastSize = keyBytesView.getUint32(0, true);
854-
let bufferIndex = keyBytesView.getUint32(12, true);
855-
lastOffset = keyBytesView.getUint32(8, true);
854+
this.lastSize = keyBytesView.getUint32(0, isLittleEndian);
855+
let bufferIndex = keyBytesView.getUint32(12, isLittleEndian);
856+
lastOffset = keyBytesView.getUint32(8, isLittleEndian);
856857
let buffer = buffers[bufferIndex];
857858
let startOffset;
858859
if (
@@ -1001,7 +1002,7 @@ export function addReadMethods(
10011002
if (!buffer) {
10021003
buffer = mmaps[bufferId] = getSharedBuffer(bufferId, env.address);
10031004
}
1004-
let offset = keyBytesView.getUint32(8, true);
1005+
let offset = keyBytesView.getUint32(8, isLittleEndian);
10051006
return new Uint8Array(buffer, offset, size);
10061007
}
10071008
function renewReadTxn(store) {
@@ -1152,7 +1153,7 @@ export function recordReadInstruction(
11521153
uint32Instructions[(start >> 2) + 3] = length; // save the length
11531154
uint32Instructions[(start >> 2) + 2] = dbi;
11541155
savePosition = (savePosition + 12) & 0xfffffc;
1155-
instructionsDataView.setFloat64(start, txnAddress, true);
1156+
instructionsDataView.setFloat64(start, txnAddress, isLittleEndian);
11561157
let callbackId = addReadCallback(() => {
11571158
let position = start >> 2;
11581159
let rc = thisInstructions[position];

src/env.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ using namespace Napi;
1212
#include <v8.h>
1313
#endif
1414

15+
#if defined (__linux)
16+
#include <endian.h> // For __BYTE_ORDER, __BIG_ENDIAN
17+
#if __BYTE_ORDER == __BIG_ENDIAN
18+
#define BE64_TO_HOST(x) (x)
19+
#else
20+
#define BE64_TO_HOST(x) bswap_64((x))
21+
#endif
22+
#else
23+
#define BE64_TO_HOST(x) bswap_64((x))
24+
#endif
25+
1526
MDB_txn* ExtendedEnv::prefetchTxns[20];
1627
pthread_mutex_t* ExtendedEnv::prefetchTxnsLock;
1728
env_tracking_t* EnvWrap::envTracking = EnvWrap::initTracking();
@@ -1080,10 +1091,10 @@ ExtendedEnv::~ExtendedEnv() {
10801091
uint64_t ExtendedEnv::getNextTime() {
10811092
uint64_t next_time_int = next_time_double();
10821093
if (next_time_int == lastTime) next_time_int++;
1083-
return bswap_64(lastTime = next_time_int);
1094+
return BE64_TO_HOST(lastTime = next_time_int);
10841095
}
10851096
uint64_t ExtendedEnv::getLastTime() {
1086-
return bswap_64(lastTime);
1097+
return BE64_TO_HOST(lastTime);
10871098
}
10881099

10891100
NAPI_FUNCTION(getUserSharedBuffer) {

src/ordered-binary.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,12 @@ int compareFast(const MDB_val *a, const MDB_val *b) {
3434
bVal = *((uint8_t*) dataB);
3535
} else {
3636
aVal = ntohl(*dataA);
37+
#if __BYTE_ORDER == __BIG_ENDIAN
38+
bVal = remaining == 2 ? *dataB & 0xffff0000 : *dataB & 0xffffff00;
39+
#else
3740
bVal = remaining == 2 ? (*((uint8_t*) dataB) << 24) + (*((uint8_t*) dataB + 1) << 16) :
3841
ntohl(*dataB & 0x00ffffff);
42+
#endif
3943
}
4044
if (aVal > bVal)
4145
return 1;

test/index.test.js

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
} from '../node-index.js';
3131
import { openAsClass } from '../open.js';
3232
import { RangeIterable } from '../util/RangeIterable.js';
33+
import { setSpecialWriteValue } from '../index.js';
3334
const require = createRequire(import.meta.url);
3435
// we don't always test CJS because it messes up debugging in webstorm (and I am not about to give the awesomeness
3536
// that is webstorm debugging)
@@ -1510,8 +1511,7 @@ describe('lmdb-js', function () {
15101511
}),
15111512
);
15121513
let value = Buffer.alloc(16, 3);
1513-
value.set(TIMESTAMP_PLACEHOLDER);
1514-
value[4] = 0;
1514+
setSpecialWriteValue(value, TIMESTAMP_PLACEHOLDER, 0);
15151515
await dbBinary.put(1, value, {
15161516
instructedWrite: true,
15171517
});
@@ -1523,8 +1523,7 @@ describe('lmdb-js', function () {
15231523
should.equal(returnedValue[9], 3);
15241524

15251525
value = Buffer.alloc(16, 3);
1526-
value.set(TIMESTAMP_PLACEHOLDER);
1527-
value[4] = 1; // assign previous
1526+
setSpecialWriteValue(value, TIMESTAMP_PLACEHOLDER, 1); // assign previous
15281527

15291528
await dbBinary.put(1, value, {
15301529
instructedWrite: true,
@@ -1653,8 +1652,7 @@ describe('lmdb-js', function () {
16531652
let returnedValue = dbBinary.get(1);
16541653
should.equal(returnedValue[2], 4);
16551654
value = Buffer.alloc(12, 3);
1656-
value.set(DIRECT_WRITE_PLACEHOLDER);
1657-
value[4] = 2;
1655+
setSpecialWriteValue(value, DIRECT_WRITE_PLACEHOLDER, 2);
16581656
value.set([1, 2, 3, 4], 8);
16591657

16601658
await dbBinary.put(1, value, {
@@ -1667,8 +1665,7 @@ describe('lmdb-js', function () {
16671665

16681666
// this should always trigger the full put operation
16691667
value = Buffer.alloc(18, 3);
1670-
value.set(DIRECT_WRITE_PLACEHOLDER);
1671-
value[4] = 2;
1668+
setSpecialWriteValue(value, DIRECT_WRITE_PLACEHOLDER, 2);
16721669
value.set([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 8);
16731670

16741671
await dbBinary.put(1, value, {
@@ -1697,8 +1694,7 @@ describe('lmdb-js', function () {
16971694
let returnedValue = dbBinary.get(1);
16981695
let updated_byte = i % 200;
16991696
value = Buffer.alloc(32, updated_byte);
1700-
value.set(DIRECT_WRITE_PLACEHOLDER);
1701-
value[4] = 2;
1697+
setSpecialWriteValue(value, DIRECT_WRITE_PLACEHOLDER, 2);
17021698
let promise = dbBinary.put(1, value, {
17031699
instructedWrite: true,
17041700
});
@@ -1754,8 +1750,7 @@ describe('lmdb-js', function () {
17541750
let returnedValue = dbBinary.get(1);
17551751
let updated_byte = i % 200;
17561752
value = Buffer.alloc(16, updated_byte);
1757-
value.set(DIRECT_WRITE_PLACEHOLDER);
1758-
value[4] = 2;
1753+
setSpecialWriteValue(value, DIRECT_WRITE_PLACEHOLDER, 2);
17591754
let promise = dbBinary.put(1, value, {
17601755
instructedWrite: true,
17611756
});

test/threads.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ if (isMainThread) {
5858

5959
setTimeout(() => {
6060
worker.terminate();
61-
}, 100);
61+
}, 500);
6262
if (messages.length === workerCount) {
6363
db.close();
6464
for (var i = 0; i < messages.length; i++) {

0 commit comments

Comments
 (0)