├── Gruntfile.js ├── README.md ├── dist └── yar.min.js ├── example ├── example.html └── server.php ├── lib ├── cstruct │ └── cstruct.js └── stringview │ └── stringview.js ├── package.json └── src └── yar.js /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt){ 2 | grunt.initConfig({ 3 | uglify:{ 4 | app:{ 5 | files: { 6 | 'dist/yar.min.js':[ 7 | './src/stringview.js', 8 | './src/yar.js' 9 | ] 10 | } 11 | } 12 | }, 13 | clean : { 14 | spm : ['.build','build','dist'] 15 | } 16 | 17 | }); 18 | 19 | grunt.loadNpmTasks('grunt-contrib-clean'); 20 | grunt.loadNpmTasks('grunt-contrib-uglify'); 21 | 22 | 23 | grunt.registerTask('build',['uglify:app']) 24 | 25 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ###Intro 2 | --- 3 | 这是一个[Yar(一个轻量高效率的RPC框架)](https://github.com/laruence/yar)的浏览器版本调用客户端 4 | 利用HTML5的 ArrayBuffer实现二进制传输。 5 | 6 | 当前尚处于开发阶段,请勿用于生产环境 7 | 8 | 9 | ###Install 10 | --- 11 | 12 | git clone https://github.com/weixinhost/yar-javascript-client 13 | 14 | ###Example 15 | --- 16 | ```HTML 17 | 23 | ``` 24 | 25 | ###CStruct 26 | 在 lib/cstruct 是一个使用js语言c变量/数组/结构体的二进制操作。未来将增加nodejs的支持 27 | 28 | ###Todo 29 | --- 30 | - 支援JQuery进行Ajax请求 31 | - 支援并发请求 32 | - 浏览器兼容 33 | - CStruct 支援 nodejs 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /dist/yar.min.js: -------------------------------------------------------------------------------- 1 | !function(a){function b(){this.struct=new CStruct.CStruct({id:new CStruct.CVal("uint32",0),version:new CStruct.CVal("uint16",1),magic_number:new CStruct.CVal("uint32",2162158688),reserved:new CStruct.CVal("uint32",0),provider:new CStruct.CArray(32),token:new CStruct.CArray(32),body_len:new CStruct.CVal("uint32",0),pack:new CStruct.CVal("string","JSON\x00\x00\x00\x00"),body:new CStruct.CVal("string","")},["id","version","magic_number","reserved","provider","token","body_len","pack","body"])}function c(a){this.address=a}b.prototype.create=function(a){return"object"==typeof a&&(a=JSON.stringify(a)),this.struct.get("body_len").val=a.length,this.struct.get("body").val=a,this.struct.get("id").val=123456789,this.struct.raw()},c.prototype.call=function(a,c,d){var e={i:123456789,m:""+a,p:c||[]},f=new b;f.id=e.id;var g=f.create(e),h=new XMLHttpRequest;h.onreadystatechange=function(){if(4==h.readyState){var a=h.responseText,b=a.substr(90,a.length-89);b=JSON.parse(b),"undefined"!=typeof b.e,"function"==typeof d&&d(b.r)}},h.open("POST",this.address,!0),h.send(g)},a.YarClient=c}(window,void 0); -------------------------------------------------------------------------------- /example/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Yar.js Test 6 | 7 | 8 | 9 | 10 | 11 | 18 | 19 | -------------------------------------------------------------------------------- /example/server.php: -------------------------------------------------------------------------------- 1 | handle(); 20 | -------------------------------------------------------------------------------- /lib/cstruct/cstruct.js: -------------------------------------------------------------------------------- 1 | /**** 2 | * 使用js描述c语言变量的内存布局 3 | * 4 | */ 5 | 6 | (function(global,undefined){ 7 | function CVal(type,val){ 8 | this.type = type; 9 | this.val = val; 10 | } 11 | 12 | CVal.prototype.TYPE_DEFINE = { 13 | 14 | 'int8':1, 15 | 'uint8':1, 16 | 17 | 'int16':2, 18 | 'uint16':2, 19 | 20 | 'int32':4, 21 | 'uint32':4, 22 | 23 | 'float32':4, 24 | 'float64':8, 25 | 'double':8, 26 | 27 | 'string':-1, //for dynamic string like char * 28 | 'raw':-1 //is packed data 29 | 30 | }; 31 | 32 | 33 | CVal.prototype.raw = function(view,offset){ 34 | var _view = null; 35 | switch(this.type){ 36 | case 'string' :{ 37 | _view = new StringView(this.val,this.STRING_ENCODING,0,this.val.length); 38 | var _len = _view.bufferView.byteLength; 39 | for(var i=0;i<_len;i++){ 40 | view.setUint8(offset,_view.bufferView[i]); 41 | offset+=1; 42 | } 43 | return offset; 44 | }break; 45 | 46 | case 'raw':{ //todo 47 | return offset; 48 | }break; 49 | 50 | default :{ 51 | var _len = this.TYPE_DEFINE[this.type]; 52 | var _buffer = new ArrayBuffer(_len); 53 | _view = new DataView(_buffer); 54 | if(_len){ 55 | switch(this.type){ 56 | case 'int8' :{ 57 | view.setInt8(offset,parseInt(this.val)); 58 | }break; 59 | case 'uint8' :{ 60 | view.setUint8(offset,parseInt(this.val)); 61 | }break; 62 | case 'int16' :{ 63 | view.setInt16(offset,parseInt(this.val)); 64 | }break; 65 | case 'uint16' :{ 66 | view.setUint16(offset,parseInt(this.val)); 67 | }break; 68 | case 'int32' :{ 69 | view.setInt32(offset,parseInt(this.val)); 70 | }break; 71 | case 'uint32' :{ 72 | view.setUint32(offset,parseInt(this.val)); 73 | }break; 74 | case 'float32' :{ 75 | view.setFloat32(offset,parseFloat(this.val)); 76 | }break; 77 | case 'double' : 78 | case 'float64' :{ 79 | view.setFloat64(offset,parseFloat(this.val)); 80 | }break; 81 | } 82 | offset += _len; 83 | } 84 | } 85 | } 86 | 87 | return offset; 88 | } 89 | 90 | CVal.prototype.byte_length = function(){ 91 | var _len = 0; 92 | switch(this.type){ 93 | case 'string' :{ 94 | var _view = new StringView(this.val,this.STRING_ENCODING,0,this.val.length); 95 | _len = _view.bufferView.byteLength; 96 | }break; 97 | 98 | case 'raw' :{ 99 | return (this.val.length); 100 | }break; 101 | 102 | default :{ 103 | _len = this.TYPE_DEFINE[this.type]; 104 | } 105 | } 106 | return _len; 107 | } 108 | 109 | CVal.prototype.STRING_ENCODING ='UTF-8'; 110 | 111 | 112 | function CArray(length){ 113 | this.length = length; 114 | this.data = []; 115 | for(var i=0;i -9007199254740992 && nVal < 9007199254740992 && Math.floor(nVal) === nVal; 8 | }; 9 | } 10 | 11 | 12 | function StringView (vInput, sEncoding /* optional (default: UTF-8) */, nOffset /* optional */, nLength /* optional */) { 13 | 14 | var fTAView, aWhole, aRaw, fPutOutptCode, fGetOutptChrSize, nInptLen, nStartIdx = isFinite(nOffset) ? nOffset : 0, nTranscrType = 15; 15 | 16 | if (sEncoding) { this.encoding = sEncoding.toString(); } 17 | 18 | encSwitch: switch (this.encoding) { 19 | case "UTF-8": 20 | fPutOutptCode = StringView.putUTF8CharCode; 21 | fGetOutptChrSize = StringView.getUTF8CharLength; 22 | fTAView = Uint8Array; 23 | break encSwitch; 24 | case "UTF-16": 25 | fPutOutptCode = StringView.putUTF16CharCode; 26 | fGetOutptChrSize = StringView.getUTF16CharLength; 27 | fTAView = Uint16Array; 28 | break encSwitch; 29 | case "UTF-32": 30 | fTAView = Uint32Array; 31 | nTranscrType &= 14; 32 | break encSwitch; 33 | default: 34 | /* case "ASCII", or case "BinaryString" or unknown cases */ 35 | fTAView = Uint8Array; 36 | nTranscrType &= 14; 37 | } 38 | 39 | typeSwitch: switch (typeof vInput) { 40 | case "string": 41 | /* the input argument is a primitive string: a new buffer will be created. */ 42 | nTranscrType &= 7; 43 | break typeSwitch; 44 | case "object": 45 | classSwitch: switch (vInput.constructor) { 46 | case StringView: 47 | /* the input argument is a stringView: a new buffer will be created. */ 48 | nTranscrType &= 3; 49 | break typeSwitch; 50 | case String: 51 | /* the input argument is an objectified string: a new buffer will be created. */ 52 | nTranscrType &= 7; 53 | break typeSwitch; 54 | case ArrayBuffer: 55 | /* the input argument is an arrayBuffer: the buffer will be shared. */ 56 | aWhole = new fTAView(vInput); 57 | nInptLen = this.encoding === "UTF-32" ? 58 | vInput.byteLength >>> 2 59 | : this.encoding === "UTF-16" ? 60 | vInput.byteLength >>> 1 61 | : 62 | vInput.byteLength; 63 | aRaw = nStartIdx === 0 && (!isFinite(nLength) || nLength === nInptLen) ? 64 | aWhole 65 | : new fTAView(vInput, nStartIdx, !isFinite(nLength) ? nInptLen - nStartIdx : nLength); 66 | 67 | break typeSwitch; 68 | case Uint32Array: 69 | case Uint16Array: 70 | case Uint8Array: 71 | /* the input argument is a typedArray: the buffer, and possibly the array itself, will be shared. */ 72 | fTAView = vInput.constructor; 73 | nInptLen = vInput.length; 74 | aWhole = vInput.byteOffset === 0 && vInput.length === ( 75 | fTAView === Uint32Array ? 76 | vInput.buffer.byteLength >>> 2 77 | : fTAView === Uint16Array ? 78 | vInput.buffer.byteLength >>> 1 79 | : 80 | vInput.buffer.byteLength 81 | ) ? vInput : new fTAView(vInput.buffer); 82 | aRaw = nStartIdx === 0 && (!isFinite(nLength) || nLength === nInptLen) ? 83 | vInput 84 | : vInput.subarray(nStartIdx, isFinite(nLength) ? nStartIdx + nLength : nInptLen); 85 | 86 | break typeSwitch; 87 | default: 88 | /* the input argument is an array or another serializable object: a new typedArray will be created. */ 89 | aWhole = new fTAView(vInput); 90 | nInptLen = aWhole.length; 91 | aRaw = nStartIdx === 0 && (!isFinite(nLength) || nLength === nInptLen) ? 92 | aWhole 93 | : aWhole.subarray(nStartIdx, isFinite(nLength) ? nStartIdx + nLength : nInptLen); 94 | } 95 | break typeSwitch; 96 | default: 97 | /* the input argument is a number, a boolean or a function: a new typedArray will be created. */ 98 | aWhole = aRaw = new fTAView(Number(vInput) || 0); 99 | 100 | } 101 | 102 | if (nTranscrType < 8) { 103 | 104 | var vSource, nOutptLen, nCharStart, nCharEnd, nEndIdx, fGetInptChrSize, fGetInptChrCode; 105 | 106 | if (nTranscrType & 4) { /* input is string */ 107 | 108 | vSource = vInput; 109 | nOutptLen = nInptLen = vSource.length; 110 | nTranscrType ^= this.encoding === "UTF-32" ? 0 : 2; 111 | /* ...or...: nTranscrType ^= Number(this.encoding !== "UTF-32") << 1; */ 112 | nStartIdx = nCharStart = nOffset ? Math.max((nOutptLen + nOffset) % nOutptLen, 0) : 0; 113 | nEndIdx = nCharEnd = (Number.isInteger(nLength) ? Math.min(Math.max(nLength, 0) + nStartIdx, nOutptLen) : nOutptLen) - 1; 114 | 115 | } else { /* input is stringView */ 116 | 117 | vSource = vInput.rawData; 118 | nInptLen = vInput.makeIndex(); 119 | nStartIdx = nCharStart = nOffset ? Math.max((nInptLen + nOffset) % nInptLen, 0) : 0; 120 | nOutptLen = Number.isInteger(nLength) ? Math.min(Math.max(nLength, 0), nInptLen - nCharStart) : nInptLen; 121 | nEndIdx = nCharEnd = nOutptLen + nCharStart; 122 | 123 | if (vInput.encoding === "UTF-8") { 124 | fGetInptChrSize = StringView.getUTF8CharLength; 125 | fGetInptChrCode = StringView.loadUTF8CharCode; 126 | } else if (vInput.encoding === "UTF-16") { 127 | fGetInptChrSize = StringView.getUTF16CharLength; 128 | fGetInptChrCode = StringView.loadUTF16CharCode; 129 | } else { 130 | nTranscrType &= 1; 131 | } 132 | 133 | } 134 | 135 | if (nOutptLen === 0 || nTranscrType < 4 && vSource.encoding === this.encoding && nCharStart === 0 && nOutptLen === nInptLen) { 136 | 137 | /* the encoding is the same, the length too and the offset is 0... or the input is empty! */ 138 | 139 | nTranscrType = 7; 140 | 141 | } 142 | 143 | conversionSwitch: switch (nTranscrType) { 144 | 145 | case 0: 146 | 147 | /* both the source and the new StringView have a fixed-length encoding... */ 148 | 149 | aWhole = new fTAView(nOutptLen); 150 | for (var nOutptIdx = 0; nOutptIdx < nOutptLen; aWhole[nOutptIdx] = vSource[nStartIdx + nOutptIdx++]); 151 | break conversionSwitch; 152 | 153 | case 1: 154 | 155 | /* the source has a fixed-length encoding but the new StringView has a variable-length encoding... */ 156 | 157 | /* mapping... */ 158 | 159 | nOutptLen = 0; 160 | 161 | for (var nInptIdx = nStartIdx; nInptIdx < nEndIdx; nInptIdx++) { 162 | nOutptLen += fGetOutptChrSize(vSource[nInptIdx]); 163 | } 164 | 165 | aWhole = new fTAView(nOutptLen); 166 | 167 | /* transcription of the source... */ 168 | 169 | for (var nInptIdx = nStartIdx, nOutptIdx = 0; nOutptIdx < nOutptLen; nInptIdx++) { 170 | nOutptIdx = fPutOutptCode(aWhole, vSource[nInptIdx], nOutptIdx); 171 | } 172 | 173 | break conversionSwitch; 174 | 175 | case 2: 176 | 177 | /* the source has a variable-length encoding but the new StringView has a fixed-length encoding... */ 178 | 179 | /* mapping... */ 180 | 181 | nStartIdx = 0; 182 | 183 | var nChrCode; 184 | 185 | for (nChrIdx = 0; nChrIdx < nCharStart; nChrIdx++) { 186 | nChrCode = fGetInptChrCode(vSource, nStartIdx); 187 | nStartIdx += fGetInptChrSize(nChrCode); 188 | } 189 | 190 | aWhole = new fTAView(nOutptLen); 191 | 192 | /* transcription of the source... */ 193 | 194 | for (var nInptIdx = nStartIdx, nOutptIdx = 0; nOutptIdx < nOutptLen; nInptIdx += fGetInptChrSize(nChrCode), nOutptIdx++) { 195 | nChrCode = fGetInptChrCode(vSource, nInptIdx); 196 | aWhole[nOutptIdx] = nChrCode; 197 | } 198 | 199 | break conversionSwitch; 200 | 201 | case 3: 202 | 203 | /* both the source and the new StringView have a variable-length encoding... */ 204 | 205 | /* mapping... */ 206 | 207 | nOutptLen = 0; 208 | 209 | var nChrCode; 210 | 211 | for (var nChrIdx = 0, nInptIdx = 0; nChrIdx < nCharEnd; nInptIdx += fGetInptChrSize(nChrCode)) { 212 | nChrCode = fGetInptChrCode(vSource, nInptIdx); 213 | if (nChrIdx === nCharStart) { nStartIdx = nInptIdx; } 214 | if (++nChrIdx > nCharStart) { nOutptLen += fGetOutptChrSize(nChrCode); } 215 | } 216 | 217 | aWhole = new fTAView(nOutptLen); 218 | 219 | /* transcription... */ 220 | 221 | for (var nInptIdx = nStartIdx, nOutptIdx = 0; nOutptIdx < nOutptLen; nInptIdx += fGetInptChrSize(nChrCode)) { 222 | nChrCode = fGetInptChrCode(vSource, nInptIdx); 223 | nOutptIdx = fPutOutptCode(aWhole, nChrCode, nOutptIdx); 224 | } 225 | 226 | break conversionSwitch; 227 | 228 | case 4: 229 | 230 | /* DOMString to ASCII or BinaryString or other unknown encodings */ 231 | 232 | aWhole = new fTAView(nOutptLen); 233 | 234 | /* transcription... */ 235 | 236 | for (var nIdx = 0; nIdx < nOutptLen; nIdx++) { 237 | aWhole[nIdx] = vSource.charCodeAt(nIdx) & 0xff; 238 | } 239 | 240 | break conversionSwitch; 241 | 242 | case 5: 243 | 244 | /* DOMString to UTF-8 or to UTF-16 */ 245 | 246 | /* mapping... */ 247 | 248 | nOutptLen = 0; 249 | 250 | for (var nMapIdx = 0; nMapIdx < nInptLen; nMapIdx++) { 251 | if (nMapIdx === nCharStart) { nStartIdx = nOutptLen; } 252 | nOutptLen += fGetOutptChrSize(vSource.charCodeAt(nMapIdx)); 253 | if (nMapIdx === nCharEnd) { nEndIdx = nOutptLen; } 254 | } 255 | 256 | aWhole = new fTAView(nOutptLen); 257 | 258 | /* transcription... */ 259 | 260 | for (var nOutptIdx = 0, nChrIdx = 0; nOutptIdx < nOutptLen; nChrIdx++) { 261 | nOutptIdx = fPutOutptCode(aWhole, vSource.charCodeAt(nChrIdx), nOutptIdx); 262 | } 263 | 264 | break conversionSwitch; 265 | 266 | case 6: 267 | 268 | /* DOMString to UTF-32 */ 269 | 270 | aWhole = new fTAView(nOutptLen); 271 | 272 | /* transcription... */ 273 | 274 | for (var nIdx = 0; nIdx < nOutptLen; nIdx++) { 275 | aWhole[nIdx] = vSource.charCodeAt(nIdx); 276 | } 277 | 278 | break conversionSwitch; 279 | 280 | case 7: 281 | 282 | aWhole = new fTAView(nOutptLen ? vSource : 0); 283 | break conversionSwitch; 284 | 285 | } 286 | 287 | aRaw = nTranscrType > 3 && (nStartIdx > 0 || nEndIdx < aWhole.length - 1) ? aWhole.subarray(nStartIdx, nEndIdx) : aWhole; 288 | 289 | } 290 | 291 | this.buffer = aWhole.buffer; 292 | this.bufferView = aWhole; 293 | this.rawData = aRaw; 294 | 295 | Object.freeze(this); 296 | 297 | } 298 | 299 | /* CONSTRUCTOR'S METHODS */ 300 | 301 | StringView.loadUTF8CharCode = function (aChars, nIdx) { 302 | 303 | var nLen = aChars.length, nPart = aChars[nIdx]; 304 | 305 | return nPart > 251 && nPart < 254 && nIdx + 5 < nLen ? 306 | /* (nPart - 252 << 32) is not possible in ECMAScript! So...: */ 307 | /* six bytes */ (nPart - 252) * 1073741824 + (aChars[nIdx + 1] - 128 << 24) + (aChars[nIdx + 2] - 128 << 18) + (aChars[nIdx + 3] - 128 << 12) + (aChars[nIdx + 4] - 128 << 6) + aChars[nIdx + 5] - 128 308 | : nPart > 247 && nPart < 252 && nIdx + 4 < nLen ? 309 | /* five bytes */ (nPart - 248 << 24) + (aChars[nIdx + 1] - 128 << 18) + (aChars[nIdx + 2] - 128 << 12) + (aChars[nIdx + 3] - 128 << 6) + aChars[nIdx + 4] - 128 310 | : nPart > 239 && nPart < 248 && nIdx + 3 < nLen ? 311 | /* four bytes */(nPart - 240 << 18) + (aChars[nIdx + 1] - 128 << 12) + (aChars[nIdx + 2] - 128 << 6) + aChars[nIdx + 3] - 128 312 | : nPart > 223 && nPart < 240 && nIdx + 2 < nLen ? 313 | /* three bytes */ (nPart - 224 << 12) + (aChars[nIdx + 1] - 128 << 6) + aChars[nIdx + 2] - 128 314 | : nPart > 191 && nPart < 224 && nIdx + 1 < nLen ? 315 | /* two bytes */ (nPart - 192 << 6) + aChars[nIdx + 1] - 128 316 | : 317 | /* one byte */ nPart; 318 | 319 | }; 320 | 321 | StringView.putUTF8CharCode = function (aTarget, nChar, nPutAt) { 322 | 323 | var nIdx = nPutAt; 324 | 325 | if (nChar < 0x80 /* 128 */) { 326 | /* one byte */ 327 | aTarget[nIdx++] = nChar; 328 | } else if (nChar < 0x800 /* 2048 */) { 329 | /* two bytes */ 330 | aTarget[nIdx++] = 0xc0 /* 192 */ + (nChar >>> 6); 331 | aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */); 332 | } else if (nChar < 0x10000 /* 65536 */) { 333 | /* three bytes */ 334 | aTarget[nIdx++] = 0xe0 /* 224 */ + (nChar >>> 12); 335 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */); 336 | aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */); 337 | } else if (nChar < 0x200000 /* 2097152 */) { 338 | /* four bytes */ 339 | aTarget[nIdx++] = 0xf0 /* 240 */ + (nChar >>> 18); 340 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 12) & 0x3f /* 63 */); 341 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */); 342 | aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */); 343 | } else if (nChar < 0x4000000 /* 67108864 */) { 344 | /* five bytes */ 345 | aTarget[nIdx++] = 0xf8 /* 248 */ + (nChar >>> 24); 346 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 18) & 0x3f /* 63 */); 347 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 12) & 0x3f /* 63 */); 348 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */); 349 | aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */); 350 | } else /* if (nChar <= 0x7fffffff) */ { /* 2147483647 */ 351 | /* six bytes */ 352 | aTarget[nIdx++] = 0xfc /* 252 */ + /* (nChar >>> 32) is not possible in ECMAScript! So...: */ (nChar / 1073741824); 353 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 24) & 0x3f /* 63 */); 354 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 18) & 0x3f /* 63 */); 355 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 12) & 0x3f /* 63 */); 356 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */); 357 | aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */); 358 | } 359 | return nIdx; 360 | }; 361 | 362 | StringView.getUTF8CharLength = function (nChar) { 363 | return nChar < 0x80 ? 1 : nChar < 0x800 ? 2 : nChar < 0x10000 ? 3 : nChar < 0x200000 ? 4 : nChar < 0x4000000 ? 5 : 6; 364 | }; 365 | 366 | StringView.loadUTF16CharCode = function (aChars, nIdx) { 367 | 368 | /* UTF-16 to DOMString decoding algorithm */ 369 | var nFrstChr = aChars[nIdx]; 370 | 371 | return nFrstChr > 0xD7BF /* 55231 */ && nIdx + 1 < aChars.length ? 372 | (nFrstChr - 0xD800 /* 55296 */ << 10) + aChars[nIdx + 1] + 0x2400 /* 9216 */ 373 | : nFrstChr; 374 | 375 | }; 376 | 377 | StringView.putUTF16CharCode = function (aTarget, nChar, nPutAt) { 378 | 379 | var nIdx = nPutAt; 380 | 381 | if (nChar < 0x10000 /* 65536 */) { 382 | /* one element */ 383 | aTarget[nIdx++] = nChar; 384 | } else { 385 | /* two elements */ 386 | aTarget[nIdx++] = 0xD7C0 /* 55232 */ + (nChar >>> 10); 387 | aTarget[nIdx++] = 0xDC00 /* 56320 */ + (nChar & 0x3FF /* 1023 */); 388 | } 389 | 390 | return nIdx; 391 | 392 | }; 393 | 394 | StringView.getUTF16CharLength = function (nChar) { 395 | return nChar < 0x10000 ? 1 : 2; 396 | }; 397 | 398 | /* Array of bytes to base64 string decoding */ 399 | 400 | StringView.b64ToUint6 = function (nChr) { 401 | 402 | return nChr > 64 && nChr < 91 ? 403 | nChr - 65 404 | : nChr > 96 && nChr < 123 ? 405 | nChr - 71 406 | : nChr > 47 && nChr < 58 ? 407 | nChr + 4 408 | : nChr === 43 ? 409 | 62 410 | : nChr === 47 ? 411 | 63 412 | : 413 | 0; 414 | 415 | }; 416 | 417 | StringView.uint6ToB64 = function (nUint6) { 418 | 419 | return nUint6 < 26 ? 420 | nUint6 + 65 421 | : nUint6 < 52 ? 422 | nUint6 + 71 423 | : nUint6 < 62 ? 424 | nUint6 - 4 425 | : nUint6 === 62 ? 426 | 43 427 | : nUint6 === 63 ? 428 | 47 429 | : 430 | 65; 431 | 432 | }; 433 | 434 | /* Base64 string to array encoding */ 435 | 436 | StringView.bytesToBase64 = function (aBytes) { 437 | 438 | var sB64Enc = ""; 439 | 440 | for (var nMod3, nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) { 441 | nMod3 = nIdx % 3; 442 | if (nIdx > 0 && (nIdx * 4 / 3) % 76 === 0) { sB64Enc += "\r\n"; } 443 | nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24); 444 | if (nMod3 === 2 || aBytes.length - nIdx === 1) { 445 | sB64Enc += String.fromCharCode(StringView.uint6ToB64(nUint24 >>> 18 & 63), StringView.uint6ToB64(nUint24 >>> 12 & 63), StringView.uint6ToB64(nUint24 >>> 6 & 63), StringView.uint6ToB64(nUint24 & 63)); 446 | nUint24 = 0; 447 | } 448 | } 449 | 450 | return sB64Enc.replace(/A(?=A$|$)/g, "="); 451 | 452 | }; 453 | 454 | 455 | StringView.base64ToBytes = function (sBase64, nBlockBytes) { 456 | 457 | var 458 | sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length, 459 | nOutLen = nBlockBytes ? Math.ceil((nInLen * 3 + 1 >>> 2) / nBlockBytes) * nBlockBytes : nInLen * 3 + 1 >>> 2, aBytes = new Uint8Array(nOutLen); 460 | 461 | for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) { 462 | nMod4 = nInIdx & 3; 463 | nUint24 |= StringView.b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4; 464 | if (nMod4 === 3 || nInLen - nInIdx === 1) { 465 | for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) { 466 | aBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255; 467 | } 468 | nUint24 = 0; 469 | } 470 | } 471 | 472 | return aBytes; 473 | 474 | }; 475 | 476 | StringView.makeFromBase64 = function (sB64Inpt, sEncoding, nByteOffset, nLength) { 477 | 478 | return new StringView(sEncoding === "UTF-16" || sEncoding === "UTF-32" ? StringView.base64ToBytes(sB64Inpt, sEncoding === "UTF-16" ? 2 : 4).buffer : StringView.base64ToBytes(sB64Inpt), sEncoding, nByteOffset, nLength); 479 | 480 | }; 481 | 482 | /* DEFAULT VALUES */ 483 | 484 | StringView.prototype.encoding = "UTF-8"; /* Default encoding... */ 485 | 486 | /* INSTANCES' METHODS */ 487 | 488 | StringView.prototype.makeIndex = function (nChrLength, nStartFrom) { 489 | 490 | var 491 | 492 | aTarget = this.rawData, nChrEnd, nRawLength = aTarget.length, 493 | nStartIdx = nStartFrom || 0, nIdxEnd = nStartIdx, nStopAtChr = isNaN(nChrLength) ? Infinity : nChrLength; 494 | 495 | if (nChrLength + 1 > aTarget.length) { throw new RangeError("StringView.prototype.makeIndex - The offset can\'t be major than the length of the array - 1."); } 496 | 497 | switch (this.encoding) { 498 | 499 | case "UTF-8": 500 | 501 | var nPart; 502 | 503 | for (nChrEnd = 0; nIdxEnd < nRawLength && nChrEnd < nStopAtChr; nChrEnd++) { 504 | nPart = aTarget[nIdxEnd]; 505 | nIdxEnd += nPart > 251 && nPart < 254 && nIdxEnd + 5 < nRawLength ? 6 506 | : nPart > 247 && nPart < 252 && nIdxEnd + 4 < nRawLength ? 5 507 | : nPart > 239 && nPart < 248 && nIdxEnd + 3 < nRawLength ? 4 508 | : nPart > 223 && nPart < 240 && nIdxEnd + 2 < nRawLength ? 3 509 | : nPart > 191 && nPart < 224 && nIdxEnd + 1 < nRawLength ? 2 510 | : 1; 511 | } 512 | 513 | break; 514 | 515 | case "UTF-16": 516 | 517 | for (nChrEnd = nStartIdx; nIdxEnd < nRawLength && nChrEnd < nStopAtChr; nChrEnd++) { 518 | nIdxEnd += aTarget[nIdxEnd] > 0xD7BF /* 55231 */ && nIdxEnd + 1 < aTarget.length ? 2 : 1; 519 | } 520 | 521 | break; 522 | 523 | default: 524 | 525 | nIdxEnd = nChrEnd = isFinite(nChrLength) ? nChrLength : nRawLength - 1; 526 | 527 | } 528 | 529 | if (nChrLength) { return nIdxEnd; } 530 | 531 | return nChrEnd; 532 | 533 | }; 534 | 535 | StringView.prototype.toBase64 = function (bWholeBuffer) { 536 | 537 | return StringView.bytesToBase64( 538 | bWholeBuffer ? 539 | ( 540 | this.bufferView.constructor === Uint8Array ? 541 | this.bufferView 542 | : 543 | new Uint8Array(this.buffer) 544 | ) 545 | : this.rawData.constructor === Uint8Array ? 546 | this.rawData 547 | : 548 | new Uint8Array(this.buffer, this.rawData.byteOffset, this.rawData.length << (this.rawData.constructor === Uint16Array ? 1 : 2)) 549 | ); 550 | 551 | }; 552 | 553 | StringView.prototype.subview = function (nCharOffset /* optional */, nCharLength /* optional */) { 554 | 555 | var 556 | 557 | nChrLen, nCharStart, nStrLen, bVariableLen = this.encoding === "UTF-8" || this.encoding === "UTF-16", 558 | nStartOffset = nCharOffset, nStringLength, nRawLen = this.rawData.length; 559 | 560 | if (nRawLen === 0) { 561 | return new StringView(this.buffer, this.encoding); 562 | } 563 | 564 | nStringLength = bVariableLen ? this.makeIndex() : nRawLen; 565 | nCharStart = nCharOffset ? Math.max((nStringLength + nCharOffset) % nStringLength, 0) : 0; 566 | nStrLen = Number.isInteger(nCharLength) ? Math.max(nCharLength, 0) + nCharStart > nStringLength ? nStringLength - nCharStart : nCharLength : nStringLength; 567 | 568 | if (nCharStart === 0 && nStrLen === nStringLength) { return this; } 569 | 570 | if (bVariableLen) { 571 | nStartOffset = this.makeIndex(nCharStart); 572 | nChrLen = this.makeIndex(nStrLen, nStartOffset) - nStartOffset; 573 | } else { 574 | nStartOffset = nCharStart; 575 | nChrLen = nStrLen - nCharStart; 576 | } 577 | 578 | if (this.encoding === "UTF-16") { 579 | nStartOffset <<= 1; 580 | } else if (this.encoding === "UTF-32") { 581 | nStartOffset <<= 2; 582 | } 583 | 584 | return new StringView(this.buffer, this.encoding, nStartOffset, nChrLen); 585 | 586 | }; 587 | 588 | StringView.prototype.forEachChar = function (fCallback, oThat, nChrOffset, nChrLen) { 589 | 590 | var aSource = this.rawData, nRawEnd, nRawIdx; 591 | 592 | if (this.encoding === "UTF-8" || this.encoding === "UTF-16") { 593 | 594 | var fGetInptChrSize, fGetInptChrCode; 595 | 596 | if (this.encoding === "UTF-8") { 597 | fGetInptChrSize = StringView.getUTF8CharLength; 598 | fGetInptChrCode = StringView.loadUTF8CharCode; 599 | } else if (this.encoding === "UTF-16") { 600 | fGetInptChrSize = StringView.getUTF16CharLength; 601 | fGetInptChrCode = StringView.loadUTF16CharCode; 602 | } 603 | 604 | nRawIdx = isFinite(nChrOffset) ? this.makeIndex(nChrOffset) : 0; 605 | nRawEnd = isFinite(nChrLen) ? this.makeIndex(nChrLen, nRawIdx) : aSource.length; 606 | 607 | for (var nChrCode, nChrIdx = 0; nRawIdx < nRawEnd; nChrIdx++) { 608 | nChrCode = fGetInptChrCode(aSource, nRawIdx); 609 | fCallback.call(oThat || null, nChrCode, nChrIdx, nRawIdx, aSource); 610 | nRawIdx += fGetInptChrSize(nChrCode); 611 | } 612 | 613 | } else { 614 | 615 | nRawIdx = isFinite(nChrOffset) ? nChrOffset : 0; 616 | nRawEnd = isFinite(nChrLen) ? nChrLen + nRawIdx : aSource.length; 617 | 618 | for (nRawIdx; nRawIdx < nRawEnd; nRawIdx++) { 619 | fCallback.call(oThat || null, aSource[nRawIdx], nRawIdx, nRawIdx, aSource); 620 | } 621 | 622 | } 623 | 624 | }; 625 | 626 | StringView.prototype.valueOf = StringView.prototype.toString = function () { 627 | 628 | if (this.encoding !== "UTF-8" && this.encoding !== "UTF-16") { 629 | /* ASCII, UTF-32 or BinaryString to DOMString */ 630 | return String.fromCharCode.apply(null, this.rawData); 631 | } 632 | 633 | var fGetCode, fGetIncr, sView = ""; 634 | 635 | if (this.encoding === "UTF-8") { 636 | fGetIncr = StringView.getUTF8CharLength; 637 | fGetCode = StringView.loadUTF8CharCode; 638 | } else if (this.encoding === "UTF-16") { 639 | fGetIncr = StringView.getUTF16CharLength; 640 | fGetCode = StringView.loadUTF16CharCode; 641 | } 642 | 643 | for (var nChr, nLen = this.rawData.length, nIdx = 0; nIdx < nLen; nIdx += fGetIncr(nChr)) { 644 | nChr = fGetCode(this.rawData, nIdx); 645 | sView += String.fromCharCode(nChr); 646 | } 647 | 648 | return sView; 649 | 650 | }; 651 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yar-javascript-client", 3 | "version": "1.0.0", 4 | "description": "Yar RPC FrameWork Client for browser", 5 | "main": "yar.js", 6 | "directories": { 7 | "doc": "docs" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/weixinhost/yar-javascript-client" 12 | }, 13 | "author": "misko_lee", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "grunt": "~0.4.1", 17 | "grunt-cmd-concat": "^0.2.7", 18 | "grunt-cmd-transport": "^0.4.1", 19 | "grunt-contrib-clean": "^0.5.0", 20 | "grunt-contrib-cssmin": "^0.10.0", 21 | "grunt-contrib-jshint": "~0.6.0", 22 | "grunt-contrib-nodeunit": "~0.2.0", 23 | "grunt-contrib-uglify": "~0.2.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/yar.js: -------------------------------------------------------------------------------- 1 | 2 | (function(window,undefined){ 3 | 4 | function yar_exception(message){ 5 | return 'Yar Exception:'+(message || 'unknow exception'); 6 | } 7 | 8 | function random_id(){ 9 | return 1000000 + (parseInt(Math.random() * 10000000)); 10 | } 11 | 12 | function str_byte_length(str){ 13 | var doubleByteChars = str.match(/[^\x00-\xfe]/ig); 14 | return str.length + (doubleByteChars == null ? 0 : doubleByteChars.length); 15 | } 16 | 17 | function Yar_Header(){ 18 | 19 | this.struct = new CStruct.CStruct( 20 | { 21 | id : new CStruct.CVal('uint32',0), 22 | version : new CStruct.CVal('uint16',1), 23 | magic_number : new CStruct.CVal('uint32',0x80DFEC60), 24 | reserved : new CStruct.CVal('uint32',0), 25 | provider : new CStruct.CArray(32), 26 | token : new CStruct.CArray(32), 27 | body_len : new CStruct.CVal('uint32',0), 28 | pack : new CStruct.CVal('string','JSON\0\0\0\0'), 29 | body : new CStruct.CVal('string','') 30 | }, 31 | ['id','version','magic_number','reserved','provider','token','body_len','pack','body'] 32 | ); 33 | 34 | 35 | 36 | } 37 | 38 | Yar_Header.prototype.create = function(body){ 39 | if('object' == typeof body){ 40 | body = JSON.stringify(body); 41 | } 42 | this.struct.get('body_len').val = body.length; 43 | this.struct.get('body').val = body; 44 | this.struct.get('id').val =123456789; 45 | return this.struct.raw(); 46 | } 47 | 48 | function YarClient(server){ 49 | this.address = server; 50 | } 51 | 52 | YarClient.prototype.call = function(method,parmters,callback){ 53 | var _request = { 54 | i:123456789, 55 | 'm':(''+method), 56 | 'p':parmters || [] 57 | }; 58 | var _header = new Yar_Header(); 59 | _header.id = _request['id']; 60 | var _send = _header.create(_request); 61 | var exchange=new XMLHttpRequest(); 62 | exchange.onreadystatechange = function () { 63 | if (exchange.readyState == 4) { 64 | var _str = exchange.responseText; 65 | var response = _str.substr(90,_str.length - 89); 66 | response = JSON.parse(response); 67 | if('object' != typeof response){ 68 | // throw yar_exception("yar protocol parse error!"); 69 | } 70 | if('undefined' != typeof response['e']){ 71 | // throw yar_exception(response['e']); 72 | } 73 | if('function' == typeof callback){ 74 | callback(response['r']); 75 | } 76 | } 77 | } 78 | exchange.open("POST",this.address,true); 79 | exchange.send(_send); 80 | } 81 | 82 | window.YarClient = YarClient; 83 | })(window,undefined); --------------------------------------------------------------------------------