coins: optimize.
This commit is contained in:
parent
9bc92abb41
commit
c033f5d465
@ -10,6 +10,7 @@ var util = require('../utils/util');
|
|||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var constants = require('../protocol/constants');
|
var constants = require('../protocol/constants');
|
||||||
var Coin = require('../primitives/coin');
|
var Coin = require('../primitives/coin');
|
||||||
|
var Output = require('../primitives/output');
|
||||||
var BufferReader = require('../utils/reader');
|
var BufferReader = require('../utils/reader');
|
||||||
var BufferWriter = require('../utils/writer');
|
var BufferWriter = require('../utils/writer');
|
||||||
var compressor = require('./compress');
|
var compressor = require('./compress');
|
||||||
@ -109,7 +110,7 @@ Coins.prototype.add = function add(coin) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.outputs[coin.index] = coin;
|
this.outputs[coin.index] = CoinEntry.fromCoin(coin);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,6 +120,9 @@ Coins.prototype.add = function add(coin) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Coins.prototype.has = function has(index) {
|
Coins.prototype.has = function has(index) {
|
||||||
|
if (index >= this.outputs.length)
|
||||||
|
return false;
|
||||||
|
|
||||||
return this.outputs[index] != null;
|
return this.outputs[index] != null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -129,31 +133,17 @@ Coins.prototype.has = function has(index) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Coins.prototype.get = function get(index) {
|
Coins.prototype.get = function get(index) {
|
||||||
var coin = this.outputs[index];
|
var coin;
|
||||||
|
|
||||||
|
if (index >= this.outputs.length)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
coin = this.outputs[index];
|
||||||
|
|
||||||
if (!coin)
|
if (!coin)
|
||||||
return;
|
return null;
|
||||||
|
|
||||||
if (coin instanceof CompressedCoin)
|
return coin.toCoin(this, index);
|
||||||
coin = coin.toCoin(this, index);
|
|
||||||
|
|
||||||
return coin;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Count unspent coins.
|
|
||||||
* @returns {Number}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Coins.prototype.count = function count(index) {
|
|
||||||
var total = 0;
|
|
||||||
var i;
|
|
||||||
|
|
||||||
for (i = 0; i < this.outputs.length; i++) {
|
|
||||||
if (this.outputs[i])
|
|
||||||
total++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return total;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -164,10 +154,12 @@ Coins.prototype.count = function count(index) {
|
|||||||
|
|
||||||
Coins.prototype.spend = function spend(index) {
|
Coins.prototype.spend = function spend(index) {
|
||||||
var coin = this.get(index);
|
var coin = this.get(index);
|
||||||
|
|
||||||
if (!coin)
|
if (!coin)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.outputs[index] = null;
|
this.outputs[index] = null;
|
||||||
|
|
||||||
return coin;
|
return coin;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -178,10 +170,11 @@ Coins.prototype.spend = function spend(index) {
|
|||||||
|
|
||||||
Coins.prototype.size = function size() {
|
Coins.prototype.size = function size() {
|
||||||
var index = -1;
|
var index = -1;
|
||||||
var i;
|
var i, output;
|
||||||
|
|
||||||
for (i = this.outputs.length - 1; i >= 0; i--) {
|
for (i = this.outputs.length - 1; i >= 0; i--) {
|
||||||
if (this.outputs[i]) {
|
output = this.outputs[i];
|
||||||
|
if (output) {
|
||||||
index = i;
|
index = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -220,15 +213,15 @@ Coins.prototype.size = function size() {
|
|||||||
* @returns {Buffer}
|
* @returns {Buffer}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Coins.prototype.toRaw = function toRaw(writer) {
|
Coins.prototype.toRaw = function toRaw() {
|
||||||
var p = new BufferWriter();
|
var p = new BufferWriter();
|
||||||
var height = this.height;
|
var height = this.height;
|
||||||
var length = this.size();
|
var length = this.size();
|
||||||
var i, output, bits, fstart, flen, bit, oct;
|
var i, output, bits, start, len, bit, oct;
|
||||||
|
|
||||||
// Return nothing if we're fully spent.
|
// Return nothing if we're fully spent.
|
||||||
if (length === 0)
|
if (length === 0)
|
||||||
return writer;
|
return null;
|
||||||
|
|
||||||
// Varint version: hopefully some smartass
|
// Varint version: hopefully some smartass
|
||||||
// miner doesn't start mining `-1` versions.
|
// miner doesn't start mining `-1` versions.
|
||||||
@ -262,28 +255,19 @@ Coins.prototype.toRaw = function toRaw(writer) {
|
|||||||
// Fill the spent field with zeroes to avoid
|
// Fill the spent field with zeroes to avoid
|
||||||
// allocating a buffer. We mark the spents
|
// allocating a buffer. We mark the spents
|
||||||
// after rendering the final buffer.
|
// after rendering the final buffer.
|
||||||
flen = Math.ceil(length / 8);
|
len = Math.ceil(length / 8);
|
||||||
p.writeVarint(flen);
|
p.writeVarint(len);
|
||||||
fstart = p.written;
|
start = p.written;
|
||||||
p.fill(0, flen);
|
p.fill(0, len);
|
||||||
|
|
||||||
|
// Write the compressed outputs.
|
||||||
for (i = 0; i < length; i++) {
|
for (i = 0; i < length; i++) {
|
||||||
output = this.outputs[i];
|
output = this.outputs[i];
|
||||||
|
|
||||||
if (!output)
|
if (!output)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// If we read this coin from the db and
|
output.toRaw(p);
|
||||||
// didn't use it, it's still in its
|
|
||||||
// compressed form. Just write it back
|
|
||||||
// as a buffer for speed.
|
|
||||||
if (output instanceof CompressedCoin) {
|
|
||||||
p.writeBytes(output.toRaw());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
compress.script(output.script, p);
|
|
||||||
p.writeVarint(output.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render the buffer with all
|
// Render the buffer with all
|
||||||
@ -293,16 +277,14 @@ Coins.prototype.toRaw = function toRaw(writer) {
|
|||||||
// Mark the spents in the spent field.
|
// Mark the spents in the spent field.
|
||||||
// This is essentially a NOP for new coins.
|
// This is essentially a NOP for new coins.
|
||||||
for (i = 0; i < length; i++) {
|
for (i = 0; i < length; i++) {
|
||||||
if (this.outputs[i])
|
output = this.outputs[i];
|
||||||
|
|
||||||
|
if (output)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bit = i % 8;
|
bit = i % 8;
|
||||||
oct = (i - bit) / 8;
|
oct = (i - bit) / 8;
|
||||||
p[fstart + oct] |= 1 << (7 - bit);
|
p[start + oct] |= 1 << (7 - bit);
|
||||||
}
|
|
||||||
|
|
||||||
if (writer) {
|
|
||||||
writer.writeBytes(p);
|
|
||||||
return writer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
@ -318,7 +300,7 @@ Coins.prototype.toRaw = function toRaw(writer) {
|
|||||||
Coins.prototype.fromRaw = function fromRaw(data, hash, index) {
|
Coins.prototype.fromRaw = function fromRaw(data, hash, index) {
|
||||||
var p = new BufferReader(data);
|
var p = new BufferReader(data);
|
||||||
var pos = 0;
|
var pos = 0;
|
||||||
var fstart, flen, bit, oct;
|
var start, len, bit, oct;
|
||||||
var bits, coin, spent;
|
var bits, coin, spent;
|
||||||
|
|
||||||
this.version = p.readVarint();
|
this.version = p.readVarint();
|
||||||
@ -334,15 +316,15 @@ Coins.prototype.fromRaw = function fromRaw(data, hash, index) {
|
|||||||
|
|
||||||
// Mark the start of the spent field and
|
// Mark the start of the spent field and
|
||||||
// seek past it to avoid reading a buffer.
|
// seek past it to avoid reading a buffer.
|
||||||
flen = p.readVarint();
|
len = p.readVarint();
|
||||||
fstart = p.offset;
|
start = p.offset;
|
||||||
p.seek(flen);
|
p.seek(len);
|
||||||
|
|
||||||
while (p.left()) {
|
while (p.left()) {
|
||||||
// Read a single bit out of the spent field.
|
// Read a single bit out of the spent field.
|
||||||
bit = pos % 8;
|
bit = pos % 8;
|
||||||
oct = (pos - bit) / 8;
|
oct = (pos - bit) / 8;
|
||||||
spent = (p.data[fstart + oct] >>> (7 - bit)) & 1;
|
spent = (data[start + oct] >>> (7 - bit)) & 1;
|
||||||
|
|
||||||
// Already spent.
|
// Already spent.
|
||||||
if (spent) {
|
if (spent) {
|
||||||
@ -353,7 +335,7 @@ Coins.prototype.fromRaw = function fromRaw(data, hash, index) {
|
|||||||
|
|
||||||
// Store the offset and size
|
// Store the offset and size
|
||||||
// in the compressed coin object.
|
// in the compressed coin object.
|
||||||
coin = CompressedCoin.fromRaw(p);
|
coin = CoinEntry.fromRaw(p);
|
||||||
|
|
||||||
this.outputs.push(coin);
|
this.outputs.push(coin);
|
||||||
pos++;
|
pos++;
|
||||||
@ -374,7 +356,7 @@ Coins.parseCoin = function parseCoin(data, hash, index) {
|
|||||||
var p = new BufferReader(data);
|
var p = new BufferReader(data);
|
||||||
var coin = new Coin();
|
var coin = new Coin();
|
||||||
var pos = 0;
|
var pos = 0;
|
||||||
var fstart, flen, bit, oct;
|
var start, len, bit, oct;
|
||||||
var spent, bits;
|
var spent, bits;
|
||||||
|
|
||||||
coin.version = p.readVarint();
|
coin.version = p.readVarint();
|
||||||
@ -392,20 +374,20 @@ Coins.parseCoin = function parseCoin(data, hash, index) {
|
|||||||
|
|
||||||
// Mark the start of the spent field and
|
// Mark the start of the spent field and
|
||||||
// seek past it to avoid reading a buffer.
|
// seek past it to avoid reading a buffer.
|
||||||
flen = p.readVarint();
|
len = p.readVarint();
|
||||||
fstart = p.offset;
|
start = p.offset;
|
||||||
p.seek(flen);
|
p.seek(len);
|
||||||
|
|
||||||
while (p.left()) {
|
while (p.left()) {
|
||||||
// Read a single bit out of the spent field.
|
// Read a single bit out of the spent field.
|
||||||
bit = pos % 8;
|
bit = pos % 8;
|
||||||
oct = (pos - bit) / 8;
|
oct = (pos - bit) / 8;
|
||||||
spent = (p.data[fstart + oct] >>> (7 - bit)) & 1;
|
spent = (data[start + oct] >>> (7 - bit)) & 1;
|
||||||
|
|
||||||
// We found our coin.
|
// We found our coin.
|
||||||
if (pos === index) {
|
if (pos === index) {
|
||||||
if (spent)
|
if (spent)
|
||||||
return;
|
return null;
|
||||||
decompress.script(p, coin.script);
|
decompress.script(p, coin.script);
|
||||||
coin.value = p.readVarint();
|
coin.value = p.readVarint();
|
||||||
return coin;
|
return coin;
|
||||||
@ -417,10 +399,12 @@ Coins.parseCoin = function parseCoin(data, hash, index) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip passed the compressed coin.
|
// Skip past the compressed coin.
|
||||||
skipCoin(p);
|
skipCoin(p);
|
||||||
pos++;
|
pos++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -456,7 +440,7 @@ Coins.prototype.fromTX = function fromTX(tx) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.outputs.push(Coin.fromTX(tx, i));
|
this.outputs.push(CoinEntry.fromTX(tx, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
@ -491,10 +475,11 @@ Coins.fromTX = function fromTX(tx) {
|
|||||||
* @param {Buffer} raw
|
* @param {Buffer} raw
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function CompressedCoin(offset, size, raw) {
|
function CoinEntry() {
|
||||||
this.offset = offset;
|
this.offset = 0;
|
||||||
this.size = size;
|
this.size = 0;
|
||||||
this.raw = raw;
|
this.raw = null;
|
||||||
|
this.output = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -504,9 +489,9 @@ function CompressedCoin(offset, size, raw) {
|
|||||||
* @returns {Coin}
|
* @returns {Coin}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CompressedCoin.prototype.toCoin = function toCoin(coins, index) {
|
CoinEntry.prototype.toCoin = function toCoin(coins, index) {
|
||||||
var p = new BufferReader(this.raw);
|
|
||||||
var coin = new Coin();
|
var coin = new Coin();
|
||||||
|
var p;
|
||||||
|
|
||||||
// Load in all necessary properties
|
// Load in all necessary properties
|
||||||
// from the parent Coins object.
|
// from the parent Coins object.
|
||||||
@ -516,6 +501,14 @@ CompressedCoin.prototype.toCoin = function toCoin(coins, index) {
|
|||||||
coin.hash = coins.hash;
|
coin.hash = coins.hash;
|
||||||
coin.index = index;
|
coin.index = index;
|
||||||
|
|
||||||
|
if (this.output) {
|
||||||
|
coin.script = this.output.script;
|
||||||
|
coin.value = this.output.value;
|
||||||
|
return coin;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = new BufferReader(this.raw);
|
||||||
|
|
||||||
// Seek to the coin's offset.
|
// Seek to the coin's offset.
|
||||||
p.seek(this.offset);
|
p.seek(this.offset);
|
||||||
|
|
||||||
@ -532,20 +525,65 @@ CompressedCoin.prototype.toCoin = function toCoin(coins, index) {
|
|||||||
* @returns {Buffer}
|
* @returns {Buffer}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CompressedCoin.prototype.toRaw = function toRaw() {
|
CoinEntry.prototype.toRaw = function toRaw(p) {
|
||||||
return this.raw.slice(this.offset, this.offset + this.size);
|
var raw;
|
||||||
|
|
||||||
|
if (this.output) {
|
||||||
|
compress.script(this.output.script, p);
|
||||||
|
p.writeVarint(this.output.value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(this.raw);
|
||||||
|
|
||||||
|
// If we read this coin from the db and
|
||||||
|
// didn't use it, it's still in its
|
||||||
|
// compressed form. Just write it back
|
||||||
|
// as a buffer for speed.
|
||||||
|
raw = this.raw.slice(this.offset, this.offset + this.size);
|
||||||
|
|
||||||
|
p.writeBytes(raw);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiate compressed coin from reader.
|
* Instantiate compressed coin from reader.
|
||||||
* @param {BufferReader} p
|
* @param {BufferReader} p
|
||||||
* @returns {CompressedCoin}
|
* @returns {CoinEntry}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CompressedCoin.fromRaw = function fromRaw(p) {
|
CoinEntry.fromRaw = function fromRaw(p) {
|
||||||
var offset = p.offset;
|
var coin = new CoinEntry();
|
||||||
var size = skipCoin(p);
|
coin.offset = p.offset;
|
||||||
return new CompressedCoin(offset, size, p.data);
|
coin.size = skipCoin(p);
|
||||||
|
coin.raw = p.data;
|
||||||
|
return coin;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate compressed coin from tx.
|
||||||
|
* @param {TX} tx
|
||||||
|
* @param {Number} index
|
||||||
|
* @returns {CoinEntry}
|
||||||
|
*/
|
||||||
|
|
||||||
|
CoinEntry.fromTX = function fromTX(tx, index) {
|
||||||
|
var coin = new CoinEntry();
|
||||||
|
coin.output = tx.outputs[index];
|
||||||
|
return coin;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate compressed coin from coin.
|
||||||
|
* @param {Coin} coin
|
||||||
|
* @returns {CoinEntry}
|
||||||
|
*/
|
||||||
|
|
||||||
|
CoinEntry.fromCoin = function fromCoin(coin) {
|
||||||
|
var entry = new CoinEntry();
|
||||||
|
entry.output = new Output();
|
||||||
|
entry.output.script = coin.script;
|
||||||
|
entry.output.value = coin.value;
|
||||||
|
return entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -572,7 +610,7 @@ function skipCoin(p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Skip past the value.
|
// Skip past the value.
|
||||||
p.readVarint();
|
p.skipVarint();
|
||||||
|
|
||||||
return p.offset - start;
|
return p.offset - start;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -423,6 +423,30 @@ encoding.readVarint = function readVarint(data, off, big) {
|
|||||||
return { size: size, value: value };
|
return { size: size, value: value };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a varint size.
|
||||||
|
* @param {Buffer} data
|
||||||
|
* @param {Number} off
|
||||||
|
* @returns {Number}
|
||||||
|
*/
|
||||||
|
|
||||||
|
encoding.skipVarint = function skipVarint(data, off) {
|
||||||
|
off = off >>> 0;
|
||||||
|
|
||||||
|
assert(off < data.length);
|
||||||
|
|
||||||
|
switch (data[off]) {
|
||||||
|
case 0xff:
|
||||||
|
return 9;
|
||||||
|
case 0xfe:
|
||||||
|
return 5;
|
||||||
|
case 0xfd:
|
||||||
|
return 3;
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a varint.
|
* Write a varint.
|
||||||
* @param {Buffer} dst
|
* @param {Buffer} dst
|
||||||
|
|||||||
@ -476,6 +476,17 @@ BufferReader.prototype.readVarint = function readVarint(big) {
|
|||||||
return result.value;
|
return result.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skip past a varint.
|
||||||
|
* @returns {Number}
|
||||||
|
*/
|
||||||
|
|
||||||
|
BufferReader.prototype.skipVarint = function skipVarint() {
|
||||||
|
var size = encoding.skipVarint(this.data, this.offset);
|
||||||
|
assert(this.offset + size <= this.data.length);
|
||||||
|
this.offset += size;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a varint (type 2).
|
* Read a varint (type 2).
|
||||||
* @param {Boolean?} big - Whether to read as a big number.
|
* @param {Boolean?} big - Whether to read as a big number.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user