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 constants = require('../protocol/constants');
|
||||
var Coin = require('../primitives/coin');
|
||||
var Output = require('../primitives/output');
|
||||
var BufferReader = require('../utils/reader');
|
||||
var BufferWriter = require('../utils/writer');
|
||||
var compressor = require('./compress');
|
||||
@ -109,7 +110,7 @@ Coins.prototype.add = function add(coin) {
|
||||
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) {
|
||||
if (index >= this.outputs.length)
|
||||
return false;
|
||||
|
||||
return this.outputs[index] != null;
|
||||
};
|
||||
|
||||
@ -129,31 +133,17 @@ Coins.prototype.has = function has(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)
|
||||
return;
|
||||
return null;
|
||||
|
||||
if (coin instanceof CompressedCoin)
|
||||
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;
|
||||
return coin.toCoin(this, index);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -164,10 +154,12 @@ Coins.prototype.count = function count(index) {
|
||||
|
||||
Coins.prototype.spend = function spend(index) {
|
||||
var coin = this.get(index);
|
||||
|
||||
if (!coin)
|
||||
return;
|
||||
|
||||
this.outputs[index] = null;
|
||||
|
||||
return coin;
|
||||
};
|
||||
|
||||
@ -178,10 +170,11 @@ Coins.prototype.spend = function spend(index) {
|
||||
|
||||
Coins.prototype.size = function size() {
|
||||
var index = -1;
|
||||
var i;
|
||||
var i, output;
|
||||
|
||||
for (i = this.outputs.length - 1; i >= 0; i--) {
|
||||
if (this.outputs[i]) {
|
||||
output = this.outputs[i];
|
||||
if (output) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
@ -220,15 +213,15 @@ Coins.prototype.size = function size() {
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Coins.prototype.toRaw = function toRaw(writer) {
|
||||
Coins.prototype.toRaw = function toRaw() {
|
||||
var p = new BufferWriter();
|
||||
var height = this.height;
|
||||
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.
|
||||
if (length === 0)
|
||||
return writer;
|
||||
return null;
|
||||
|
||||
// Varint version: hopefully some smartass
|
||||
// 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
|
||||
// allocating a buffer. We mark the spents
|
||||
// after rendering the final buffer.
|
||||
flen = Math.ceil(length / 8);
|
||||
p.writeVarint(flen);
|
||||
fstart = p.written;
|
||||
p.fill(0, flen);
|
||||
len = Math.ceil(length / 8);
|
||||
p.writeVarint(len);
|
||||
start = p.written;
|
||||
p.fill(0, len);
|
||||
|
||||
// Write the compressed outputs.
|
||||
for (i = 0; i < length; i++) {
|
||||
output = this.outputs[i];
|
||||
|
||||
if (!output)
|
||||
continue;
|
||||
|
||||
// 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.
|
||||
if (output instanceof CompressedCoin) {
|
||||
p.writeBytes(output.toRaw());
|
||||
continue;
|
||||
}
|
||||
|
||||
compress.script(output.script, p);
|
||||
p.writeVarint(output.value);
|
||||
output.toRaw(p);
|
||||
}
|
||||
|
||||
// Render the buffer with all
|
||||
@ -293,16 +277,14 @@ Coins.prototype.toRaw = function toRaw(writer) {
|
||||
// Mark the spents in the spent field.
|
||||
// This is essentially a NOP for new coins.
|
||||
for (i = 0; i < length; i++) {
|
||||
if (this.outputs[i])
|
||||
output = this.outputs[i];
|
||||
|
||||
if (output)
|
||||
continue;
|
||||
|
||||
bit = i % 8;
|
||||
oct = (i - bit) / 8;
|
||||
p[fstart + oct] |= 1 << (7 - bit);
|
||||
}
|
||||
|
||||
if (writer) {
|
||||
writer.writeBytes(p);
|
||||
return writer;
|
||||
p[start + oct] |= 1 << (7 - bit);
|
||||
}
|
||||
|
||||
return p;
|
||||
@ -318,7 +300,7 @@ Coins.prototype.toRaw = function toRaw(writer) {
|
||||
Coins.prototype.fromRaw = function fromRaw(data, hash, index) {
|
||||
var p = new BufferReader(data);
|
||||
var pos = 0;
|
||||
var fstart, flen, bit, oct;
|
||||
var start, len, bit, oct;
|
||||
var bits, coin, spent;
|
||||
|
||||
this.version = p.readVarint();
|
||||
@ -334,15 +316,15 @@ Coins.prototype.fromRaw = function fromRaw(data, hash, index) {
|
||||
|
||||
// Mark the start of the spent field and
|
||||
// seek past it to avoid reading a buffer.
|
||||
flen = p.readVarint();
|
||||
fstart = p.offset;
|
||||
p.seek(flen);
|
||||
len = p.readVarint();
|
||||
start = p.offset;
|
||||
p.seek(len);
|
||||
|
||||
while (p.left()) {
|
||||
// Read a single bit out of the spent field.
|
||||
bit = pos % 8;
|
||||
oct = (pos - bit) / 8;
|
||||
spent = (p.data[fstart + oct] >>> (7 - bit)) & 1;
|
||||
spent = (data[start + oct] >>> (7 - bit)) & 1;
|
||||
|
||||
// Already spent.
|
||||
if (spent) {
|
||||
@ -353,7 +335,7 @@ Coins.prototype.fromRaw = function fromRaw(data, hash, index) {
|
||||
|
||||
// Store the offset and size
|
||||
// in the compressed coin object.
|
||||
coin = CompressedCoin.fromRaw(p);
|
||||
coin = CoinEntry.fromRaw(p);
|
||||
|
||||
this.outputs.push(coin);
|
||||
pos++;
|
||||
@ -374,7 +356,7 @@ Coins.parseCoin = function parseCoin(data, hash, index) {
|
||||
var p = new BufferReader(data);
|
||||
var coin = new Coin();
|
||||
var pos = 0;
|
||||
var fstart, flen, bit, oct;
|
||||
var start, len, bit, oct;
|
||||
var spent, bits;
|
||||
|
||||
coin.version = p.readVarint();
|
||||
@ -392,20 +374,20 @@ Coins.parseCoin = function parseCoin(data, hash, index) {
|
||||
|
||||
// Mark the start of the spent field and
|
||||
// seek past it to avoid reading a buffer.
|
||||
flen = p.readVarint();
|
||||
fstart = p.offset;
|
||||
p.seek(flen);
|
||||
len = p.readVarint();
|
||||
start = p.offset;
|
||||
p.seek(len);
|
||||
|
||||
while (p.left()) {
|
||||
// Read a single bit out of the spent field.
|
||||
bit = pos % 8;
|
||||
oct = (pos - bit) / 8;
|
||||
spent = (p.data[fstart + oct] >>> (7 - bit)) & 1;
|
||||
spent = (data[start + oct] >>> (7 - bit)) & 1;
|
||||
|
||||
// We found our coin.
|
||||
if (pos === index) {
|
||||
if (spent)
|
||||
return;
|
||||
return null;
|
||||
decompress.script(p, coin.script);
|
||||
coin.value = p.readVarint();
|
||||
return coin;
|
||||
@ -417,10 +399,12 @@ Coins.parseCoin = function parseCoin(data, hash, index) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip passed the compressed coin.
|
||||
// Skip past the compressed coin.
|
||||
skipCoin(p);
|
||||
pos++;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -456,7 +440,7 @@ Coins.prototype.fromTX = function fromTX(tx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.outputs.push(Coin.fromTX(tx, i));
|
||||
this.outputs.push(CoinEntry.fromTX(tx, i));
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -491,10 +475,11 @@ Coins.fromTX = function fromTX(tx) {
|
||||
* @param {Buffer} raw
|
||||
*/
|
||||
|
||||
function CompressedCoin(offset, size, raw) {
|
||||
this.offset = offset;
|
||||
this.size = size;
|
||||
this.raw = raw;
|
||||
function CoinEntry() {
|
||||
this.offset = 0;
|
||||
this.size = 0;
|
||||
this.raw = null;
|
||||
this.output = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -504,9 +489,9 @@ function CompressedCoin(offset, size, raw) {
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
CompressedCoin.prototype.toCoin = function toCoin(coins, index) {
|
||||
var p = new BufferReader(this.raw);
|
||||
CoinEntry.prototype.toCoin = function toCoin(coins, index) {
|
||||
var coin = new Coin();
|
||||
var p;
|
||||
|
||||
// Load in all necessary properties
|
||||
// from the parent Coins object.
|
||||
@ -516,6 +501,14 @@ CompressedCoin.prototype.toCoin = function toCoin(coins, index) {
|
||||
coin.hash = coins.hash;
|
||||
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.
|
||||
p.seek(this.offset);
|
||||
|
||||
@ -532,20 +525,65 @@ CompressedCoin.prototype.toCoin = function toCoin(coins, index) {
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
CompressedCoin.prototype.toRaw = function toRaw() {
|
||||
return this.raw.slice(this.offset, this.offset + this.size);
|
||||
CoinEntry.prototype.toRaw = function toRaw(p) {
|
||||
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.
|
||||
* @param {BufferReader} p
|
||||
* @returns {CompressedCoin}
|
||||
* @returns {CoinEntry}
|
||||
*/
|
||||
|
||||
CompressedCoin.fromRaw = function fromRaw(p) {
|
||||
var offset = p.offset;
|
||||
var size = skipCoin(p);
|
||||
return new CompressedCoin(offset, size, p.data);
|
||||
CoinEntry.fromRaw = function fromRaw(p) {
|
||||
var coin = new CoinEntry();
|
||||
coin.offset = p.offset;
|
||||
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.
|
||||
p.readVarint();
|
||||
p.skipVarint();
|
||||
|
||||
return p.offset - start;
|
||||
}
|
||||
|
||||
@ -423,6 +423,30 @@ encoding.readVarint = function readVarint(data, off, big) {
|
||||
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.
|
||||
* @param {Buffer} dst
|
||||
|
||||
@ -476,6 +476,17 @@ BufferReader.prototype.readVarint = function readVarint(big) {
|
||||
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).
|
||||
* @param {Boolean?} big - Whether to read as a big number.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user