blob: 19e49286f247a79ebe57718e924eb2a9046fe303 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
var vanadium = require('vanadium');
module.exports = {
NamedResource: NamedResource,
listChildren: listChildren,
prefixRangeLimit: prefixRangeLimit,
stringToUTF8Bytes: stringToUTF8Bytes,
escape: escape,
unescape: unescape
};
/**
* NamedResource is a base class for Syncbase layers with names.
* @param {string} parentFullName Full name of parent layer.
* @param {string} name Relative name for this layer.
* @param {string} fullName Full name for this layer.
*/
function NamedResource(parentFullName, name, fullName) {
if (!(this instanceof NamedResource)) {
return new NamedResource(parentFullName, name, fullName);
}
/**
* @property _parentFullName
* @private
* @type {string}
*/
Object.defineProperty(this, '_parentFullName', {
value: parentFullName,
writable: false,
enumerable: false
});
/**
* @property name
* @type {string}
*/
Object.defineProperty(this, 'name', {
value: name,
writable: false,
enumerable: true
});
/**
* @property fullName
* @type {string}
*/
Object.defineProperty(this, 'fullName', {
value: fullName,
writable: false,
enumerable: true
});
}
/**
* listChildren returns the relative names of all children of parentFullName.
* @private
*/
function listChildren(ctx, parentFullName, cb) {
var rt = vanadium.runtimeForContext(ctx);
var globPattern = vanadium.naming.join(parentFullName, '*');
var childNames = [];
var streamErr = null;
var stream = rt.getNamespace().glob(ctx, globPattern, function(err) {
if (err) {
return cb(err);
}
if (streamErr) {
return cb(streamErr);
}
cb(null, childNames);
}).stream;
stream.on('data', function(globResult) {
var fullName = globResult.name;
var escName = vanadium.naming.basename(fullName);
// Component names within object names are always escaped. See comment in
// server/nosql/dispatcher.go for explanation.
// If unescape throws an exception, there's a bug in the Syncbase server.
// Glob should return names with escaped components.
childNames.push(unescape(escName));
});
stream.on('error', function(err) {
console.error('Stream error: ' + JSON.stringify(err));
streamErr = streamErr || err.error;
});
}
/**
* prefixRangeLimit modifies the input bytes to be the limit of the row range
* for the given prefix.
* TODO(sadovsky): Why do we modify the input bytes, rather than returning a new
* byte array?
* @private
* @param {Uint8Array} bytes Integer ArrayBuffer to modify.
*/
function prefixRangeLimit(bytes) {
// bytes can be thought of as a base-256 number. The code below effectively
// adds 1 to this number, then chops off any trailing \x00 bytes. If the input
// string consists entirely of \xff bytes, we return an empty string.
while (bytes.length > 0) {
var last = bytes.length - 1;
if (bytes[last] === 255) {
bytes = bytes.slice(0, last); // chop off trailing \x00
} else {
bytes[last] += 1; // add 1
return; // no carry
}
}
}
/**
* stringToUTF8Bytes converts a JavaScript string to a array of bytes
* representing the string in UTF8 format.
* @private
* @param {string} str String to convert to UTF8 bytes.
* @return {Uint8Array} UTF8 bytes.
*/
function stringToUTF8Bytes(str) {
var utf8String = unescape(encodeURIComponent(str)); //jshint ignore:line
var bytes = new Uint8Array(utf8String.length);
for (var i = 0; i < utf8String.length; i++) {
bytes[i] = utf8String.charCodeAt(i);
}
return bytes;
}
/**
* escape escapes a component name for use in a Syncbase object name. In
* particular, it replaces bytes "%" and "/" with the "%" character followed by
* the byte's two-digit hex code. Clients using the client library need not
* escape names themselves; the client library does so on their behalf.
* @param {string} s String to escape.
* @return {string} Escaped string.
*/
function escape(s) {
return vanadium.naming.encodeAsNameElement(s);
}
/**
* unescape applies the inverse of escape. Throws exception if the given string
* is not a valid escaped string.
* @param {string} s String to unescape.
* @return {string} Unescaped string.
*/
function unescape(s) {
return vanadium.naming.decodeFromNameElement(s);
}