Index: lib/typedObjects/utils.js |
=================================================================== |
--- a/lib/typedObjects/utils.js |
+++ b/lib/typedObjects/utils.js |
@@ -82,28 +82,66 @@ let getViewsForType = exports.getViewsFo |
index = (viewTypes.push(viewType) | 0) - 1; |
views.push([]); |
} |
result.push(views[index]); |
} |
return result; |
}; |
+ /** |
+ * Creates a wrapper function for a setter that will call the watcher function |
+ * with the new value of the property before executing the actual setter. |
+ */ |
+function watchSetter(/**Function*/ setter, /**Function*/ watcher) /**Function*/ |
+{ |
+ return function(value) |
+ { |
+ setter.call(this, watcher.call(this, value)); |
+ } |
+} |
+ |
+/** |
+ * Creates a parameter-less wrapper function around a getter that will get |
+ * bufferIndex and byteOffset parameters from object properties. |
+ */ |
+function wrapGetter(/**Function*/ getter) /**Function*/ |
+{ |
+ return function() |
+ { |
+ return getter.call(this, this.bufferIndex, this.byteOffset); |
+ } |
+} |
+ |
+/** |
+ * Creates a wrapper function around a setter with value as the only parameter, |
+ * the bufferIndex and byteOffset parameters will be retrieved from object |
+ * properties. |
+ */ |
+function wrapSetter(/**Function*/ setter) /**Function*/ |
+{ |
+ return function(value) |
+ { |
+ return setter.call(this, this.bufferIndex, this.byteOffset, value); |
+ } |
+} |
+ |
/** |
* Defines properties with given name and type on an object. |
* |
* @param obj object to define properties on |
* @param properties object mapping property names to their respective types |
* @param viewTypes see getViewsForType() |
* @param views see getViewsForType() |
* @param [offset] byte array offset at which the properties should start |
- * @param [cleanupValues] array of property/value combinations to be set when the object is created or destroyed |
+ * @param [watchers] map of watcher functions to be called when a particular property is being set |
+ * @param [initialValues] array of property/value combinations to be set when the object is created or destroyed |
* @return new start offset for additional properties |
*/ |
-exports.defineProperties = function defineProperties(obj, properties, viewTypes, views, offset, cleanupValues) |
+exports.defineProperties = function defineProperties(obj, properties, viewTypes, views, offset, watchers, initialValues) |
{ |
offset = offset | 0; |
let propList = []; |
for (let name in properties) |
propList.push([name, properties[name]]); |
// Put larger properties first to make sure alignment requirements are met. |
@@ -115,23 +153,125 @@ exports.defineProperties = function defi |
// Generates getters and setters for each property. |
let descriptors = {}; |
for (let i = 0, l = propList.length | 0; i < l; i++) |
{ |
let [name, type] = propList[i]; |
let viewParams = getViewsForType(type, viewTypes, views); |
descriptors[name] = { |
- get: type.createGetter.apply(type, [offset].concat(viewParams)), |
- set: type.createSetter.apply(type, [offset].concat(viewParams)), |
+ get: wrapGetter(type.createGetter.apply(type, [offset].concat(viewParams))), |
+ set: wrapSetter(type.createSetter.apply(type, [offset].concat(viewParams))), |
configurable: false, |
enumerable: true |
}; |
+ |
+ if (watchers && typeof watchers[name] == "function") |
+ descriptors[name].set = watchSetter(descriptors[name].set, watchers[name]); |
+ |
offset += type.referenceLength; |
- if (cleanupValues && typeof type.cleanupValue != "undefined") |
- cleanupValues.push([name, type.cleanupValue]); |
+ if (initialValues && typeof type.initialValue != "undefined") |
+ initialValues.push([name, type.initialValue]); |
} |
// Define properties |
Object.defineProperties(obj, descriptors); |
return offset; |
}; |
+ |
+/** |
+ * Creates a new array buffer and adds the necessary views. |
+ * |
+ * @param {Integer} byteSize bytes to allocate for the buffer |
+ * @param {Array} buffers existing buffers (will be modified) |
+ * @param {Array} viewTypes view types for the buffers |
+ * @param {Array} views existing buffer views (will be modified) |
+ * @result {Integer} index of the buffer created |
+ */ |
+let addBuffer = exports.addBuffer = function(byteSize, buffers, viewTypes, views) |
+{ |
+ let buffer = new ArrayBuffer(byteSize | 0); |
+ buffers.push(buffer); |
+ for (let i = 0, l = viewTypes.length | 0; i < l; i++) |
+ views[i].push(new viewTypes[i](buffer)); |
+ return (buffers.length | 0) - 1; |
+} |
+ |
+/** |
+ * Releases an array buffer. |
+ * |
+ * @param {Integer} bufferIndex index of the buffer to be released. |
+ * @param {Array} buffers existing buffers (will be modified) |
+ * @param {Array} views existing buffer views (will be modified) |
+ */ |
+exports.removeBuffer = function(bufferIndex, buffers, views) |
+{ |
+ delete buffers[bufferIndex]; |
+ for (let i = 0, l = views.length | 0; i < l; i++) |
+ delete views[i][bufferIndex]; |
+} |
+ |
+/** |
+ * Allocates a new fixed-size element. It will return the first available free |
+ * block or create a new buffer if the existing ones have no space left. |
+ * |
+ * @param {TypedReference} firstFree head of the linked list pointing to unallocated elements |
+ * @param {Integer} byteLength size of an element |
+ * @param {Integer} bufferSize number of elements in a buffer |
+ * @param {Array} buffers existing buffers (might be modified in necessary) |
+ * @param {Array} viewTypes view types for the buffers |
+ * @param {Array} views existing buffer views (might be modified if necessary) |
+ * @result {Array} [bufferIndex, byteOffset] parameters of the newly allocated block |
+ */ |
+exports.alloc = function(firstFree, byteLength, bufferSize, buffers, viewTypes, views) |
+{ |
+ let bufferIndex = firstFree.bufferIndex | 0; |
+ let byteOffset = firstFree.byteOffset | 0; |
+ if (bufferIndex >= 0) |
+ { |
+ // There is still a free spot, simply move on firstFree reference |
+ [firstFree.bufferIndex, firstFree.byteOffset] = |
+ [firstFree.targetBufferIndex, firstFree.targetByteOffset]; |
+ } |
+ else |
+ { |
+ byteLength = byteLength | 0; |
+ bufferSize = bufferSize | 0; |
+ |
+ // Create new buffer and use the first element of it |
+ bufferIndex = addBuffer(byteLength * bufferSize, buffers, viewTypes, views); |
+ byteOffset = 0; |
+ |
+ // Mark last element of the new buffer as the last free spot |
+ firstFree.bufferIndex = bufferIndex; |
+ firstFree.byteOffset = (bufferSize - 1) * byteLength; |
+ firstFree.targetBufferIndex = -1; |
+ |
+ // Make each remaining element of the new buffer point to the next one |
+ for (let i = bufferSize - 2; i >= 1; i--) |
+ { |
+ let nextByteOffset = firstFree.byteOffset; |
+ firstFree.byteOffset = nextByteOffset - byteLength; |
+ firstFree.targetBufferIndex = bufferIndex; |
+ firstFree.targetByteOffset = nextByteOffset; |
+ } |
+ } |
+ return [bufferIndex, byteOffset]; |
+}; |
+ |
+/** |
+ * Releases the block at given offset so that it can be allocated again. |
+ * |
+ * @param {TypedReference} firstFree head of the linked list pointing to unallocated elements |
+ * @param {Integer} bufferIndex buffer index of the block to be released |
+ * @param {Integer} byteOffset byte offset o fthe block to be released |
+ */ |
+exports.dealloc = function(firstFree, bufferIndex, byteOffset) |
+{ |
+ let oldFreeBufferIndex = firstFree.bufferIndex | 0; |
+ let oldFreeByteOffset = firstFree.byteOffset | 0; |
+ |
+ firstFree.bufferIndex = bufferIndex | 0; |
+ firstFree.byteOffset = byteOffset | 0; |
+ firstFree.targetBufferIndex = oldFreeBufferIndex; |
+ firstFree.targetByteOffset = oldFreeByteOffset; |
+} |