UNPKG

azure-storage-legacy

Version:

Microsoft Azure Storage Client Library for node for back compat with older versions of node sdk

144 lines (130 loc) 3.88 kB
// // Copyright (c) Microsoft and contributors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // // See the License for the specific language governing permissions and // limitations under the License. // /** * Chunked memory pool allocator. * It could dramatically reduce the memory usage. * However, it can't dramatically reduce the CPU time since GC in v8 is very efficient. */ function ChunkAllocator(chunkSize, maxCount) { this._pool = []; this._requests = []; //There is no need to track all allocated buffer for now. this._inuse = 0; this._chunkSize = chunkSize; this._maxCount = maxCount || 10; this._extendMemoryPool(); } /** * Asynchronously require a buffer. * Caller should be aware of that the content of buffer is random since the Buffer.fill is Time-consumed opreation. * @param {function(error, buffer)} callback */ ChunkAllocator.prototype.getBuffer = function(size, callback) { var buffer = this._getBufferFromPool(size); if (buffer) { //There is no need to run this callback in nextTick since it's an internal api for now. //TODO do some benchmarkes about nextTick; this._inuse++; callback(null, buffer); } else { this._requests.push(callback); } }; /** * Synchronously require a buffer * Caller should be aware of that the content of buffer is random since the Buffer.fill is Time-consumed opreation. */ ChunkAllocator.prototype.getBufferSync = function(size) { var buffer = this._getBufferFromPool(size); if (buffer === null) { //It means the total memory could larger than this._chunkSize * this._maxCount. buffer = this._allocateBuffer(size); } this._inuse++; return buffer; }; /** * Get buffer from the current memory pool. */ ChunkAllocator.prototype._getBufferFromPool = function(size) { if(size !== this._chunkSize) { //Create a buffer outside of the memory pool. return this._allocateBuffer(size); } else if(this._pool.length === 0) { this._extendMemoryPool(); } if(this._pool.length !== 0) { return this._pool.pop(); } else { return null; } }; /** * Extend the memory pool */ ChunkAllocator.prototype._extendMemoryPool = function() { var total = this._pool.length + this._inuse; if(total >= this._maxCount || total < 0) return; var nextSize = Math.min(total * 2, this._maxCount); if(nextSize === 0) { nextSize = 1; } var increment = nextSize - total; this._addChunk(increment); }; /** * Add more chunks into memory pool */ ChunkAllocator.prototype._addChunk = function(increment) { for(var i = 0; i < increment; i++) { var buffer = this._allocateBuffer(this._chunkSize); this._pool.push(buffer); } }; /** * Release buffer */ ChunkAllocator.prototype.releaseBuffer = function(buffer) { if(buffer.length !== this._chunkSize) { //Directly delete the buffer if bufferSize is invalid and wait for GC. buffer = null; return; } if (this._requests.length) { //Wake up the buffer request. var bufferRequest = this._requests.shift(); bufferRequest(null, buffer); return; } if (this._pool.length < this._maxCount) { this._pool.push(buffer); } else { //The pool is full and wait for GC buffer = null; } this._inuse--; if(this._inuse < 0) { this._inuse = 0; } }; /** * Allocate a new buffer */ ChunkAllocator.prototype._allocateBuffer = function(size) { var buffer = new Buffer(size); return buffer; }; module.exports = ChunkAllocator;