From 073563dd29d793741ed03f9dafa375408dad8ce4 Mon Sep 17 00:00:00 2001 From: Gordon Woodhull Date: Mon, 18 Apr 2016 15:10:31 -0400 Subject: [PATCH] merge kevin jin's fixes to rserve.js NA/null -> cscheid/rserve-js#32 handle connection error -> cscheid/rserve-js#31 --- htdocs/lib/js/rserve.js | 146 +++++++++++++++++++++++++++++----------- 1 file changed, 107 insertions(+), 39 deletions(-) diff --git a/htdocs/lib/js/rserve.js b/htdocs/lib/js/rserve.js index 3345960266..584cab458d 100644 --- a/htdocs/lib/js/rserve.js +++ b/htdocs/lib/js/rserve.js @@ -59,20 +59,20 @@ Rserve.Robj = { if (_.isUndefined(this.attributes)) { return values; } else { - // FIXME: there is no reason why names should be the first or only - // attribute, so the code should really look - // for "names" and not cry if it doesn't exist + // FIXME: there is no reason why names should be the first or only + // attribute, so the code should really look + // for "names" and not cry if it doesn't exist if (this.attributes.value[0].name == "names") { var keys = this.attributes.value[0].value.value; var result = {}; _.each(keys, function(key, i) { - result[key] = values[i]; + result[key] = values[i]; }); return result; - } - // FIXME: how can we pass other important attributes - // like "class" ? - return values; + } + // FIXME: how can we pass other important attributes + // like "class" ? + return values; } } }), @@ -88,11 +88,11 @@ Rserve.Robj = { if (_.isUndefined(this.attributes)) { return values; } else { - // FIXME: lang doens't have "names" attribute since - // names are sent as tags (langs are pairlists) - // so this seems superfluous (it is dangerous - // if lang ever had attributes since there is - // no reason to fail in that case) + // FIXME: lang doens't have "names" attribute since + // names are sent as tags (langs are pairlists) + // so this seems superfluous (it is dangerous + // if lang ever had attributes since there is + // no reason to fail in that case) if(this.attributes.value[0].name!="names") throw "expected names here"; var keys = this.attributes.value[0].value.value; @@ -202,6 +202,26 @@ Rserve.Robj = { // Simple constants and functions are defined here, // in correspondence with Rserve's Rsrv.h +function custom_nan(int32_words) { + // DataView.getFloat64() canonicalizes any NaNs with custom bit patterns, so + // instead use what is essentially a C union in JavaScript. Unfortunately, + // that means we have to deal with endianness. + var buf = new ArrayBuffer(8); + var intRep = new Int32Array(buf); + var floatRep = new Float64Array(buf); + + // Server may be little endian. + intRep[0] = int32_words[1]; + intRep[1] = int32_words[0]; + if (isNaN(floatRep[0])) + return floatRep[0]; + + // Server may be big endian. + intRep[0] = int32_words[0]; + intRep[1] = int32_words[1]; + return floatRep[0]; +} + Rserve.Rsrv = { PAR_TYPE: function(x) { return x & 255; }, PAR_LEN: function(x) { return x >>> 8; }, @@ -313,10 +333,23 @@ Rserve.Rsrv = { BOOL_FALSE : 0, BOOL_NA : 2, + // See Arith.h in R. + INTEGER_NA : (2147483647 + 1) | 0, + STRING_NA : 0xff, + DOUBLE_NA : custom_nan([ 0x7ff00000, 0x000007a2 ]), + GET_XT: function(x) { return x & 63; }, GET_DT: function(x) { return x & 63; }, HAS_ATTR: function(x) { return (x & Rserve.Rsrv.XT_HAS_ATTR) > 0; }, IS_LARGE: function(x) { return (x & Rserve.Rsrv.XT_LARGE) > 0; }, + IS_DOUBLE_NA: function(x) { + var view = new DataView(new ArrayBuffer(16)); + view.setFloat64(0, x); + view.setFloat64(8, Rserve.Rsrv.DOUBLE_NA); + // Any operations (not involving NaN) still produce NA, but they convert + // the signaling NaN to quiet NaN by toggling the highest mantissa bit. + return (view.getInt32(0) & ~0x00080000) === view.getInt32(8) && view.getInt32(4) === view.getInt32(12); + }, // # FIXME A WHOLE LOT OF MACROS HERE WHICH ARE PROBABLY IMPORTANT // ############################################################################## @@ -406,12 +439,16 @@ function read(m) read_int_vector: function(length) { var old_offset = this.offset; this.offset += length; - return this.msg.make(Int32Array, old_offset, length); + return _.map(this.msg.make(Int32Array, old_offset, length), function(v) { + return v !== Rserve.Rsrv.INTEGER_NA ? v : null; + }); }, read_double_vector: function(length) { var old_offset = this.offset; this.offset += length; - return this.msg.make(Float64Array, old_offset, length); + return _.map(this.msg.make(Float64Array, old_offset, length), function(v) { + return !Rserve.Rsrv.IS_DOUBLE_NA(v) ? v : null; + }); }, ////////////////////////////////////////////////////////////////////// @@ -430,7 +467,10 @@ function read(m) var current_str = ""; for (var i=0; i