blob: 27bac59454b776a2db9880800c08a3792926676d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*
*/
/*****************************************************************************/
/* */
/* proton.Data.Binary */
/* */
/*****************************************************************************/
/**
* Create a proton.Data.Binary. This constructor takes one or two parameters.
* The first parameter may serve different purposes depending on its type;
* If value is a number then it represents the size of the Binary data buffer,
* if it is a string then we copy the string to the buffer, if it is an Array
* or a TypedArray then we copy the data to the buffer. The optional second
* parameter is a pointer to the data in an internal data store. If start is
* not specified then the number of bytes specified in the first parameter
* will be allocated in the internal data store and start will point to the
* start of that block of data.
* @classdesc
* This class represents an AMQP Binary type. This class allows us to create and
* use raw binary data and map it efficiently between JavaScript client code and
* the underlying implementation, where all data is managed on a "virtual heap".
* <p>
* Client applications should generally not have to care about memory management
* as, for most common use cases, client applications would "transfer ownership"
* to a "container" which would then "own" the underlying data and free the data
* held by the {@link proton.Data.Binary}.
* <p>
* As an example one common use-case would be where client application creates a
* {@link proton.Data.Binary} specifying the required size. It would usually then
* call getBuffer() to access the underlying Uint8Array. At this point the client
* "owns" the data and so would have to call free() if it did nothing more with
* the Binary, however when {@link proton.Data.putBINARY} is called the ownership
* of the raw data on the virtual heap transfers from the Binary to the Data and
* the client no longer needs to call free() itself. In this case the putBINARY()
* call transfers ownership and can then safely call free() on the Binary.
* <p>
* Conversely a common use-case when receiving data is where a Binary may be
* created by {@link proton.Data#getBINARY}. In this case the Binary is simply a
* "view" onto the bytes owned by the Data instance. A client application can
* safely access the bytes from the view, but if it wishes to use the bytes beyond
* the scope of the Data instance (e.g. after the next {@link proton.Messenger#get}
* call then the client must explicitly *copy* the bytes into a new buffer, for
* example via copyBuffer().
* <p>
* Most of the {@link proton.Data} methods that take {@link proton.Data.Binary}
* as a parameter "consume" the underlying data and take responsibility for
* freeing it from the heap e.g. {@link proton.Data#putBINARY}, {@link proton.Data#decode}.
* For the methods that return a {@link proton.Data.Binary} the call to
* {@link proton.Data#getBINARY}, which is the most common case, returns a Binary
* that has a "view" of the underlying data that is actually owned by the Data
* instance and thus doesn't need to be explicitly freed on the Binary. The call
* to {@link proton.Data#encode} however returns a Binary that represents a *copy*
* of the underlying data, in this case (like a client calling new proton.Data.Binary)
* the client takes responsibility for freeing the data, unless of course it is
* subsequently passed to a method that will consume the data (putBINARY/decode).
* @constructor proton.Data.Binary
* @param {(number|string|Array|TypedArray)} value If value is a number then it
* represents the size of the Binary data buffer, if it is a string then
* we copy the string to the buffer, if it is an Array or a TypedArray
* then we copy the data to the buffer. N.B. although convenient do bear
* in mind that using a mechanism other than constructing with a simple
* size will result in some form of additional data copy.
* @param {number} start an optional pointer to the start of the Binary data buffer.
*/
Data['Binary'] = function(value, start) { // Data.Binary Constructor.
/**
* If the start pointer is specified then the underlying binary data is owned
* by another object, so we set the call to free to be a null function. If
* the start pointer is not passed then we allocate storage of the specified
* size on the emscripten heap and set the call to free to free the data from
* the emscripten heap.
*/
var size = value;
if (start) {
this['free'] = function() {};
} else { // Create Binary from Array, ArrayBuffer or TypedArray.
var hasArrayBuffer = (typeof ArrayBuffer === 'function');
if (Data.isArray(value) ||
(hasArrayBuffer && value instanceof ArrayBuffer) ||
(value.buffer && hasArrayBuffer && value.buffer instanceof ArrayBuffer)) {
value = new Uint8Array(value);
size = value.length;
start = _malloc(size); // Allocate storage from emscripten heap.
Module['HEAPU8'].set(value, start); // Copy the data to the emscripten heap.
} else if (Data.isString(value)) { // Create Binary from native string
value = unescape(encodeURIComponent(value)); // Create a C-like UTF representation.
size = value.length;
start = _malloc(size); // Allocate storage from emscripten heap.
for (var i = 0; i < size; i++) {
setValue(start + i, value.charCodeAt(i), 'i8', 1);
}
} else { // Create unpopulated Binary of specified size.
// If the type is not a number by this point then an unrecognised data
// type has been passed so we create a zero length Binary.
size = Data.isNumber(size) ? size : 0;
start = _malloc(size); // Allocate storage from emscripten heap.
}
this['free'] = function() {
_free(this.start);
this.size = 0;
this.start = 0;
// Set free to a null function to prevent possibility of a "double free".
this['free'] = function() {};
};
}
this.size = size;
this.start = start;
};
/**
* Get a Uint8Array view of the data. N.B. this is just a *view* of the data,
* which will go out of scope on the next call to {@link proton.Messenger.get}. If
* a client wants to retain the data then copy should be used to explicitly
* create a copy of the data which the client then owns to do with as it wishes.
* @method getBuffer
* @returns {Uint8Array} a new Uint8Array view of the data.
* @memberof! proton.Data.Binary#
*/
Data['Binary'].prototype['getBuffer'] = function() {
return new Uint8Array(HEAPU8.buffer, this.start, this.size);
};
/**
* Explicitly create a *copy* of the Binary, copying the underlying binary data too.
* @method copy
* @param {number} offset an optional offset into the underlying buffer from
* where we want to copy the data, default is zero.
* @param {number} n an optional number of bytes to copy, default is this.size - offset.
* @returns {proton.Data.Binary} a new {@link proton.Data.Binary} created by copying the underlying binary data.
* @memberof! proton.Data.Binary#
*/
Data['Binary'].prototype['copy'] = function(offset, n) {
offset = offset | 0;
n = n ? n : (this.size - offset);
if (offset >= this.size) {
offset = 0;
n = 0;
} else if ((offset + n) > this.size) {
n = this.size - offset; // Clamp length
}
var start = _malloc(n); // Allocate storage from emscripten heap.
_memcpy(start, this.start + offset, n); // Copy the raw data to new buffer.
return new Data['Binary'](n, start);
};
/**
* Converts the {@link proton.Data.Binary} to a string. This is clearly most
* useful when the binary data is actually a binary representation of a string
* such as a C style ASCII string.
* @method toString
* @memberof! proton.Data.Binary#
* @returns {string} the String form of a {@link proton.Data.Binary}.
*/
Data['Binary'].prototype.toString = Data['Binary'].prototype.valueOf = function() {
// Create a native JavaScript String from the start/size information.
return Pointer_stringify(this.start, this.size);
};