diff --git a/lib/mac/bindings.js b/lib/mac/bindings.js index 8304bd73..a0276b9d 100644 --- a/lib/mac/bindings.js +++ b/lib/mac/bindings.js @@ -150,7 +150,57 @@ blenoBindings.on('kCBMsgId17', function(args) { }); blenoBindings.addService = function AddGATTService(service) { - var attributeId = this._attributes.length ? this._attributes.length - 1 : 1; + var attributeId; + var attributeCount = 1 + service.characteristics.length; + var attributes = this._attributes; + + /** + * The goal is to find an attribute range that can host the full service, which in most cases will be the next available push + * position. In the edge case, where our attribute consumes more Id's than the standard permits (0xFFFF), we must find + * a place within our attributes array that has been vacated or throw an error. + */ + if(!attributes.length){ + atrributeId = 1; +} else if((attributes.length - 1 + attributeCount) >= 0xFFFF){ + // perform search to find somewhere within our attributes array that contains a hole that can host the attributeCount + var consecutiveCount = 0; + var groupedOpenings = {}; + for(var i = 0, ii = attributes.length; i++){ + var attribute = attributes[i]; + if(!attribute.kCBMsgArgAttributeID){ + consecutiveCount++; + } else if(attribute.kCBMsgArgAttributeID && consecutiveCount){ + + // If we have found this amount of openings before, don't mark in our group so we use the first available. + if(!groupedOpenings[consecutiveCount].start){ + var start = i - 1 - consecutiveCount; + var end = i + consecutiveCount - 1; + groupedOpenings[consecutiveCount] = { start: start, end: end}; + + if(consecutiveCount === attributeCount){ + break; + } + } + consecutiveCount = 0; + } + } + + if(groupedOpenings[attributeCount].start){ + attributeId = groupedOpenings[attributeCount].start; + } else { + for(var openingCount in groupedOpenings){ + if(parseInt(openingCount, 10) > attributeCount){ + attributeId = groupedOpenings[openingCount].start; + break; + } + } + } + + if(!attributeId){ + throw new Error('Not enough space to add service.'); + } + } + var arg = { kCBMsgArgAttributeID: attributeId, kCBMsgArgAttributeIDs: [], @@ -225,6 +275,7 @@ blenoBindings.addService = function AddGATTService(service) { kCBMsgArgUUID: new Buffer(characteristic.uuid, 'hex') }; this._attributes[attributeId] = characteristic; + characteristic.attributeId = attributeId; for (var k = 0; k < characteristic.descriptors.length; k++) { var descriptor = characteristic.descriptors[k]; @@ -241,17 +292,24 @@ blenoBindings.addService = function AddGATTService(service) { }; blenoBindings.removeService = function RemoveGATTService(service){ - var attributeId = this._attributes[service.attributeId]; + var attributes = this._attributes; + var attributeId = service.attributeId; + var attribute = attributes[attributeId]; + for(var i = 0, ii = attribute.characteristics.length; i < ii; i++){ + var characteristic = attribute.characteristics[i]; + attributes[characteristic.attributeId] = {}; + } + attributes[attributeId] = {}; var messageBody = { kCBMsgArgAttributeID: attributeId }; - this._attributes[service.attributeId] = {}; this.sendCBMsg(11, attributeId); -} +}; blenoBindings.removeAllServices = function RemoveAllGATTServices(){ + this._attributes = []; this.sendCBMsg(12, null); -} +}; blenoBindings.setServices = function SetGATTServices(services) { this.sendCBMsg(12, null);