Add initial ol.structs.Buffer

This commit is contained in:
Tom Payne
2013-03-19 06:31:02 +01:00
parent 1833ec15e2
commit 9047e98889
2 changed files with 416 additions and 0 deletions

137
src/ol/structs/buffer.js Normal file
View File

@@ -0,0 +1,137 @@
goog.provide('ol.structs.Buffer');
goog.require('ol.structs.IntegerSet');
/**
* @constructor
* @param {Array.<number>=} opt_arr Array.
* @param {number=} opt_used Used.
* @param {boolean=} opt_dirty Dirty.
*/
ol.structs.Buffer = function(opt_arr, opt_used, opt_dirty) {
/**
* @private
* @type {Array.<number>}
*/
this.arr_ = goog.isDef(opt_arr) ? opt_arr : [];
/**
* @private
* @type {ol.structs.IntegerSet}
*/
this.dirtySet_ = new ol.structs.IntegerSet();
/**
* @private
* @type {ol.structs.IntegerSet}
*/
this.freeSet_ = new ol.structs.IntegerSet();
var used = goog.isDef(opt_used) ? opt_used : this.arr_.length;
if (used < this.arr_.length) {
this.freeSet_.addRange(used, this.arr_.length);
}
if (opt_dirty && used !== 0) {
this.dirtySet_.addRange(0, used);
}
};
/**
* @param {Array.<number>} values Values.
* @return {number} Index.
*/
ol.structs.Buffer.prototype.add = function(values) {
var size = values.length;
goog.asserts.assert(size > 0);
var index = this.freeSet_.findRange(size);
goog.asserts.assert(index != -1); // FIXME
this.freeSet_.removeRange(index, index + size);
var i;
for (i = 0; i < size; ++i) {
this.arr_[index + i] = values[i];
}
this.dirtySet_.addRange(index, index + size);
return index;
};
/**
* @param {function(this: T, number, number)} f Callback.
* @param {T=} opt_obj The object to be used as the value of 'this' within f.
* @template T
*/
ol.structs.Buffer.prototype.forEachRange = function(f, opt_obj) {
if (this.arr_.length !== 0) {
this.freeSet_.forEachRangeInverted(0, this.arr_.length, f, opt_obj);
}
};
/**
* @return {Array.<number>} Array.
*/
ol.structs.Buffer.prototype.getArray = function() {
return this.arr_;
};
/**
* @return {number} Count.
*/
ol.structs.Buffer.prototype.getCount = function() {
return this.arr_.length - this.freeSet_.getSize();
};
/**
* @return {ol.structs.IntegerSet} Dirty set.
*/
ol.structs.Buffer.prototype.getDirtySet = function() {
return this.dirtySet_;
};
/**
* @return {ol.structs.IntegerSet} Free set.
*/
ol.structs.Buffer.prototype.getFreeSet = function() {
return this.freeSet_;
};
/**
* @param {number} index Index.
* @param {number} size Size.
*/
ol.structs.Buffer.prototype.remove = function(index, size) {
this.freeSet_.addRange(index, index + size);
this.dirtySet_.removeRange(index, index + size);
};
/**
* @param {number} index Index.
* @param {Array.<number>} values Values.
*/
ol.structs.Buffer.prototype.set = function(index, values) {
var arr = this.arr_;
var n = values.length;
goog.asserts.assert(0 <= index && index + n <= arr.length);
for (i = 0; i < n; ++i) {
arr[index + i] = values[i];
}
this.dirtySet_.addRange(index, index + n);
};
/**
* Marks the buffer as being clean.
*/
ol.structs.Buffer.prototype.setClean = function() {
this.dirtySet_.clear();
};

View File

@@ -0,0 +1,279 @@
goog.provide('ol.test.structs.Buffer');
describe('ol.structs.Buffer', function() {
describe('constructor', function() {
describe('without an argument', function() {
var b;
beforeEach(function() {
b = new ol.structs.Buffer();
});
it('constructs an empty instance', function() {
expect(b.getArray()).to.be.empty();
expect(b.getCount()).to.be(0);
});
});
describe('with a single array argument', function() {
var b;
beforeEach(function() {
b = new ol.structs.Buffer([0, 1, 2, 3]);
});
it('constructs a populated instance', function() {
expect(b.getArray()).to.equalArray([0, 1, 2, 3]);
});
});
});
describe('with an empty instance', function() {
var b;
beforeEach(function() {
b = new ol.structs.Buffer();
});
describe('forEachRange', function() {
it('does not call the callback', function() {
var callback = sinon.spy();
b.forEachRange(callback);
expect(callback).not.to.be.called();
});
});
describe('getArray', function() {
it('returns an empty array', function() {
expect(b.getArray()).to.be.empty();
});
});
describe('getCount', function() {
it('returns 0', function() {
expect(b.getCount()).to.be(0);
});
});
describe('getDirtySet', function() {
it('returns an empty set', function() {
expect(b.getDirtySet().isEmpty()).to.be(true);
});
});
});
describe('with an empty instance with spare capacity', function() {
var b;
beforeEach(function() {
b = new ol.structs.Buffer(new Array(4), 0);
});
describe('add', function() {
it('allows elements to be added', function() {
expect(b.add([0, 1, 2, 3])).to.be(0);
expect(b.getArray()).to.equalArray([0, 1, 2, 3]);
});
});
describe('forEachRange', function() {
it('does not call the callback', function() {
var callback = sinon.spy();
b.forEachRange(callback);
expect(callback).not.to.be.called();
});
});
describe('getCount', function() {
it('returns 0', function() {
expect(b.getCount()).to.be(0);
});
});
});
describe('with an instance with no spare capacity', function() {
var b;
beforeEach(function() {
b = new ol.structs.Buffer([0, 1, 2, 3]);
});
describe('add', function() {
it('throws an exception', function() {
expect(function() {
b.add([4, 5]);
}).to.throwException();
});
});
describe('forEachRange', function() {
it('calls the callback', function() {
var callback = sinon.spy();
b.forEachRange(callback);
expect(callback.calledOnce).to.be(true);
expect(callback.args[0]).to.equalArray([0, 4]);
});
});
describe('getCount', function() {
it('returns the expected value', function() {
expect(b.getCount()).to.be(4);
});
});
describe('getDirtySet', function() {
it('returns an empty set', function() {
expect(b.getDirtySet().isEmpty()).to.be(true);
});
});
describe('remove', function() {
it('allows items to be removes', function() {
expect(function() {
b.remove(2, 4);
}).to.not.throwException();
});
});
describe('set', function() {
it('updates the items', function() {
b.set(2, [5, 6]);
expect(b.getArray()).to.equalArray([0, 1, 5, 6]);
});
it('marks the set items as dirty', function() {
b.set(2, [5, 6]);
var dirtySet = b.getDirtySet();
expect(dirtySet.isEmpty()).to.be(false);
expect(dirtySet.getArray()).to.equalArray([2, 4]);
});
});
});
describe('with an instance with spare capacity', function() {
var b;
beforeEach(function() {
var arr = [0, 1, 2, 3];
arr.length = 8;
b = new ol.structs.Buffer(arr, 4);
});
describe('add', function() {
it('allows more items to be added', function() {
expect(b.add([4, 5, 6, 7])).to.be(4);
expect(b.getArray()).to.equalArray([0, 1, 2, 3, 4, 5, 6, 7]);
});
});
describe('forEachRange', function() {
it('calls the callback with the expected values', function() {
var callback = sinon.spy();
b.forEachRange(callback);
expect(callback.calledOnce).to.be(true);
expect(callback.args[0]).to.equalArray([0, 4]);
});
});
describe('getCount', function() {
it('returns the expected value', function() {
expect(b.getCount()).to.be(4);
});
});
describe('getDirtySet', function() {
it('returns an empty set', function() {
expect(b.getDirtySet().isEmpty()).to.be(true);
});
});
describe('getFreeSet', function() {
it('returns the expected set', function() {
var freeSet = b.getFreeSet();
expect(freeSet.isEmpty()).to.be(false);
expect(freeSet.getArray()).to.equalArray([4, 8]);
});
});
});
describe('usage tests', function() {
it('allows multiple adds and removes', function() {
var b = new ol.structs.Buffer(new Array(8), 0);
expect(b.add([0, 1])).to.be(0);
expect(b.getArray()).to.equalArray([
0, 1,
undefined, undefined,
undefined, undefined,
undefined, undefined
]);
expect(b.getCount()).to.be(2);
expect(b.add([2, 3, 4, 5])).to.be(2);
expect(b.getCount()).to.be(6);
expect(b.add([6, 7])).to.be(6);
expect(b.getCount()).to.be(8);
expect(b.getArray()).to.equalArray([0, 1, 2, 3, 4, 5, 6, 7]);
b.remove(2, 2);
expect(b.getCount()).to.be(6);
expect(b.add([8, 9])).to.be(2);
expect(b.getArray()).to.equalArray([0, 1, 8, 9, 4, 5, 6, 7]);
b.remove(1, 1);
b.remove(4, 4);
expect(b.add([10, 11, 12])).to.be(4);
expect(b.getArray()).to.equalArray([0, 1, 8, 9, 10, 11, 12, 7]);
expect(b.add([13])).to.be(1);
expect(b.getArray()).to.equalArray([0, 13, 8, 9, 10, 11, 12, 7]);
});
});
});
goog.require('ol.structs.Buffer');