jszhuyin
Version:
Smart Chinese Zhuyin Input Method in JavaScript.
162 lines (131 loc) • 4.14 kB
JavaScript
;
// This file builds an in-memory key-value storage blob/buffer used
// by BinStorage.
var DatabaseBuilder = function DatabaseBuilder() {
// The array that holds the structured data.
// Itself is the table0.
this.data = [];
};
DatabaseBuilder.prototype.onprogress = null;
DatabaseBuilder.prototype.put = function bsb_put(key, value) {
var table = this.data;
for (var i = 0; i < key.length; i++) {
var code = key[i];
if (!table[code]) {
table[code] = [];
}
table = table[code];
}
table[0] = value;
};
DatabaseBuilder.prototype.get = function bsb_get(key) {
var table = this.data;
for (var i = 0; i < key.length; i++) {
var code = key[i];
if (!table[code]) {
return undefined;
}
table = table[code];
}
return table[0];
};
DatabaseBuilder.prototype.getBlob = function bsb_getBlob() {
/*
* The binary data structure are as follows:
* Each table gets 4 blocks in the resulting file and a header.
* BLOCK0: Header. Fixed length at 4 bytes.
* 0-1 byte: Uint16, element length of BLOCK2 and BLOCK3.
* 2-3 byte: Uint16, element length of BLOCK1.
* BLOCK1: Content of this table (JSZhuyinDataPack arraybuffer).
* BLOCK2: Keys of sub-tables. Each key is in Uint16.
* BLOCK3: Address to other tables. Each address is in Uint32.
*/
var blobParts = [];
var blobLength = 0;
var self = this;
var appendTableToBlob = function appendTableToBlob(table) {
if (typeof self.onprogress === 'function') {
self.onprogress();
}
var keyTable = [];
var ptrTable = [];
var i;
for (i = 1; i < table.length; i++) {
if (!table[i]) {
continue;
}
keyTable.push(i);
ptrTable.push(table[i]);
}
var contentBuf = table[0];
var blockIndex = blobLength;
// Create a buffer, containing two Uint16 numbers.
var headerView = new DataView(new ArrayBuffer(4));
// The first number would always the # of items of the keyTable.
headerView.setUint16(0, keyTable.length, true);
// The second number would be the # of items of the contentBuf.
if (contentBuf) {
headerView.setUint16(2, contentBuf.byteLength >> 1, true);
}
blobParts.push(headerView);
blobLength += headerView.byteLength;
if (contentBuf) {
blobParts.push(contentBuf);
blobLength += contentBuf.byteLength;
}
if (keyTable.length) {
var keyView = new DataView(new ArrayBuffer(keyTable.length << 1));
keyTable.forEach(function(key, i) {
keyView.setUint16(i << 1, key, true);
});
blobParts.push(keyView);
blobLength += keyView.byteLength;
}
if (ptrTable.length) {
var ptrView = new DataView(new ArrayBuffer(ptrTable.length << 2));
blobParts.push(ptrView);
blobLength += ptrView.byteLength;
ptrTable.forEach(function(table, i) {
var ptr = appendTableToBlob(table);
if (ptr > 0xffffffff) {
throw new Error('Table pointer address exceeds maximum.');
}
ptrView.setUint32(i << 2, ptr, true);
});
}
return blockIndex;
};
appendTableToBlob(this.data);
var supportsBlobConstructor = (function(){
var blob;
try {
blob = new Blob([]);
} catch (e) {
return false;
}
return !!blob;
})();
if (supportsBlobConstructor) {
// Blob in browsers
return new Blob(blobParts,
{ type: 'application/octet-stream', ending: 'transparent' });
} else if (typeof Buffer === 'function') {
var buf = Buffer.alloc(blobLength);
var i = 0;
for (var j = 0; j < blobParts.length; j++) {
var view = new Uint8Array(
(blobParts[j].constructor === ArrayBuffer) ?
blobParts[j] : blobParts[j].buffer);
var bufPart = Buffer.from(view);
bufPart.copy(buf, i);
i += bufPart.length;
}
return buf;
} else {
throw new Error('No binary constructor available on this platform.');
}
};
// Export as a CommonJS module if we are loaded as one.
if (typeof module === 'object' && module.exports) {
module.exports = DatabaseBuilder;
}