protobuf.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. /* ProtocolBuffer client 0.1.0*/
  2. /**
  3. * pomelo-protobuf
  4. * @author <zhang0935@gmail.com>
  5. */
  6. /**
  7. * Protocol buffer root
  8. * In browser, it will be window.protbuf
  9. */
  10. (function (exports, global){
  11. var Protobuf = exports;
  12. Protobuf.init = function(opts){
  13. //On the serverside, use serverProtos to encode messages send to client
  14. Protobuf.encoder.init(opts.encoderProtos);
  15. //On the serverside, user clientProtos to decode messages receive from clients
  16. Protobuf.decoder.init(opts.decoderProtos);
  17. };
  18. Protobuf.encode = function(key, msg){
  19. return Protobuf.encoder.encode(key, msg);
  20. };
  21. Protobuf.decode = function(key, msg){
  22. return Protobuf.decoder.decode(key, msg);
  23. };
  24. // exports to support for components
  25. module.exports = Protobuf;
  26. if(typeof(window) != "undefined") {
  27. window.protobuf = Protobuf;
  28. }
  29. })(typeof(window) == "undefined" ? module.exports : (this.protobuf = {}), this);
  30. /**
  31. * constants
  32. */
  33. (function (exports, global){
  34. var constants = exports.constants = {};
  35. constants.TYPES = {
  36. uInt32 : 0,
  37. sInt32 : 0,
  38. int32 : 0,
  39. double : 1,
  40. string : 2,
  41. message : 2,
  42. float : 5
  43. };
  44. })('undefined' !== typeof protobuf ? protobuf : module.exports, this);
  45. /**
  46. * util module
  47. */
  48. (function (exports, global){
  49. var Util = exports.util = {};
  50. Util.isSimpleType = function(type){
  51. return ( type === 'uInt32' ||
  52. type === 'sInt32' ||
  53. type === 'int32' ||
  54. type === 'uInt64' ||
  55. type === 'sInt64' ||
  56. type === 'float' ||
  57. type === 'double' );
  58. };
  59. })('undefined' !== typeof protobuf ? protobuf : module.exports, this);
  60. /**
  61. * codec module
  62. */
  63. (function (exports, global){
  64. var Codec = exports.codec = {};
  65. var buffer = new ArrayBuffer(8);
  66. var float32Array = new Float32Array(buffer);
  67. var float64Array = new Float64Array(buffer);
  68. var uInt8Array = new Uint8Array(buffer);
  69. Codec.encodeUInt32 = function(n){
  70. var n = parseInt(n);
  71. if(isNaN(n) || n < 0){
  72. return null;
  73. }
  74. var result = [];
  75. do{
  76. var tmp = n % 128;
  77. var next = Math.floor(n/128);
  78. if(next !== 0){
  79. tmp = tmp + 128;
  80. }
  81. result.push(tmp);
  82. n = next;
  83. }while(n !== 0);
  84. return result;
  85. };
  86. Codec.encodeSInt32 = function(n){
  87. var n = parseInt(n);
  88. if(isNaN(n)){
  89. return null;
  90. }
  91. n = n<0?(Math.abs(n)*2-1):n*2;
  92. return Codec.encodeUInt32(n);
  93. };
  94. Codec.decodeUInt32 = function(bytes){
  95. var n = 0;
  96. for(var i = 0; i < bytes.length; i++){
  97. var m = parseInt(bytes[i]);
  98. n = n + ((m & 0x7f) * Math.pow(2,(7*i)));
  99. if(m < 128){
  100. return n;
  101. }
  102. }
  103. return n;
  104. };
  105. Codec.decodeSInt32 = function(bytes){
  106. var n = this.decodeUInt32(bytes);
  107. var flag = ((n%2) === 1)?-1:1;
  108. n = ((n%2 + n)/2)*flag;
  109. return n;
  110. };
  111. Codec.encodeFloat = function(float){
  112. float32Array[0] = float;
  113. return uInt8Array;
  114. };
  115. Codec.decodeFloat = function(bytes, offset){
  116. if(!bytes || bytes.length < (offset + 4)){
  117. return null;
  118. }
  119. for(var i = 0; i < 4; i++){
  120. uInt8Array[i] = bytes[offset + i];
  121. }
  122. return float32Array[0];
  123. };
  124. Codec.encodeDouble = function(double){
  125. float64Array[0] = double;
  126. return uInt8Array.subarray(0, 8);
  127. };
  128. Codec.decodeDouble = function(bytes, offset){
  129. if(!bytes || bytes.length < (offset + 8)){
  130. return null;
  131. }
  132. for(var i = 0; i < 8; i++){
  133. uInt8Array[i] = bytes[offset + i];
  134. }
  135. return float64Array[0];
  136. };
  137. Codec.encodeStr = function(bytes, offset, str){
  138. for(var i = 0; i < str.length; i++){
  139. var code = str.charCodeAt(i);
  140. var codes = encode2UTF8(code);
  141. for(var j = 0; j < codes.length; j++){
  142. bytes[offset] = codes[j];
  143. offset++;
  144. }
  145. }
  146. return offset;
  147. };
  148. /**
  149. * Decode string from utf8 bytes
  150. */
  151. Codec.decodeStr = function(bytes, offset, length){
  152. var array = [];
  153. var end = offset + length;
  154. while(offset < end){
  155. var code = 0;
  156. if(bytes[offset] < 128){
  157. code = bytes[offset];
  158. offset += 1;
  159. }else if(bytes[offset] < 224){
  160. code = ((bytes[offset] & 0x3f)<<6) + (bytes[offset+1] & 0x3f);
  161. offset += 2;
  162. }else{
  163. code = ((bytes[offset] & 0x0f)<<12) + ((bytes[offset+1] & 0x3f)<<6) + (bytes[offset+2] & 0x3f);
  164. offset += 3;
  165. }
  166. array.push(code);
  167. }
  168. var str = '';
  169. for(var i = 0; i < array.length;){
  170. str += String.fromCharCode.apply(null, array.slice(i, i + 10000));
  171. i += 10000;
  172. }
  173. return str;
  174. };
  175. /**
  176. * Return the byte length of the str use utf8
  177. */
  178. Codec.byteLength = function(str){
  179. if(typeof(str) !== 'string'){
  180. return -1;
  181. }
  182. var length = 0;
  183. for(var i = 0; i < str.length; i++){
  184. var code = str.charCodeAt(i);
  185. length += codeLength(code);
  186. }
  187. return length;
  188. };
  189. /**
  190. * Encode a unicode16 char code to utf8 bytes
  191. */
  192. function encode2UTF8(charCode){
  193. if(charCode <= 0x7f){
  194. return [charCode];
  195. }else if(charCode <= 0x7ff){
  196. return [0xc0|(charCode>>6), 0x80|(charCode & 0x3f)];
  197. }else{
  198. return [0xe0|(charCode>>12), 0x80|((charCode & 0xfc0)>>6), 0x80|(charCode & 0x3f)];
  199. }
  200. }
  201. function codeLength(code){
  202. if(code <= 0x7f){
  203. return 1;
  204. }else if(code <= 0x7ff){
  205. return 2;
  206. }else{
  207. return 3;
  208. }
  209. }
  210. })('undefined' !== typeof protobuf ? protobuf : module.exports, this);
  211. /**
  212. * encoder module
  213. */
  214. (function (exports, global){
  215. var protobuf = exports;
  216. var MsgEncoder = exports.encoder = {};
  217. var codec = protobuf.codec;
  218. var constant = protobuf.constants;
  219. var util = protobuf.util;
  220. MsgEncoder.init = function(protos){
  221. this.protos = protos || {};
  222. };
  223. MsgEncoder.encode = function(route, msg){
  224. //Get protos from protos map use the route as key
  225. var protos = this.protos[route];
  226. //Check msg
  227. if(!checkMsg(msg, protos)){
  228. return null;
  229. }
  230. //Set the length of the buffer 2 times bigger to prevent overflow
  231. var length = codec.byteLength(JSON.stringify(msg));
  232. //Init buffer and offset
  233. var buffer = new ArrayBuffer(length);
  234. var uInt8Array = new Uint8Array(buffer);
  235. var offset = 0;
  236. if(!!protos){
  237. offset = encodeMsg(uInt8Array, offset, protos, msg);
  238. if(offset > 0){
  239. return uInt8Array.subarray(0, offset);
  240. }
  241. }
  242. return null;
  243. };
  244. /**
  245. * Check if the msg follow the defination in the protos
  246. */
  247. function checkMsg(msg, protos){
  248. if(!protos){
  249. return false;
  250. }
  251. for(var name in protos){
  252. var proto = protos[name];
  253. //All required element must exist
  254. switch(proto.option){
  255. case 'required' :
  256. if(typeof(msg[name]) === 'undefined'){
  257. console.warn('no property exist for required! name: %j, proto: %j, msg: %j', name, proto, msg);
  258. return false;
  259. }
  260. case 'optional' :
  261. if(typeof(msg[name]) !== 'undefined'){
  262. var message = protos.__messages[proto.type] || MsgEncoder.protos['message ' + proto.type];
  263. if(!!message && !checkMsg(msg[name], message)){
  264. console.warn('inner proto error! name: %j, proto: %j, msg: %j', name, proto, msg);
  265. return false;
  266. }
  267. }
  268. break;
  269. case 'repeated' :
  270. //Check nest message in repeated elements
  271. var message = protos.__messages[proto.type] || MsgEncoder.protos['message ' + proto.type];
  272. if(!!msg[name] && !!message){
  273. for(var i = 0; i < msg[name].length; i++){
  274. if(!checkMsg(msg[name][i], message)){
  275. return false;
  276. }
  277. }
  278. }
  279. break;
  280. }
  281. }
  282. return true;
  283. }
  284. function encodeMsg(buffer, offset, protos, msg){
  285. for(var name in msg){
  286. if(!!protos[name]){
  287. var proto = protos[name];
  288. switch(proto.option){
  289. case 'required' :
  290. case 'optional' :
  291. offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));
  292. offset = encodeProp(msg[name], proto.type, offset, buffer, protos);
  293. break;
  294. case 'repeated' :
  295. if(msg[name].length > 0){
  296. offset = encodeArray(msg[name], proto, offset, buffer, protos);
  297. }
  298. break;
  299. }
  300. }
  301. }
  302. return offset;
  303. }
  304. function encodeProp(value, type, offset, buffer, protos){
  305. switch(type){
  306. case 'uInt32':
  307. offset = writeBytes(buffer, offset, codec.encodeUInt32(value));
  308. break;
  309. case 'int32' :
  310. case 'sInt32':
  311. offset = writeBytes(buffer, offset, codec.encodeSInt32(value));
  312. break;
  313. case 'float':
  314. writeBytes(buffer, offset, codec.encodeFloat(value));
  315. offset += 4;
  316. break;
  317. case 'double':
  318. writeBytes(buffer, offset, codec.encodeDouble(value));
  319. offset += 8;
  320. break;
  321. case 'string':
  322. var length = codec.byteLength(value);
  323. //Encode length
  324. offset = writeBytes(buffer, offset, codec.encodeUInt32(length));
  325. //write string
  326. codec.encodeStr(buffer, offset, value);
  327. offset += length;
  328. break;
  329. default :
  330. var message = protos.__messages[type] || MsgEncoder.protos['message ' + type];
  331. if(!!message){
  332. //Use a tmp buffer to build an internal msg
  333. var tmpBuffer = new ArrayBuffer(codec.byteLength(JSON.stringify(value))*2);
  334. var length = 0;
  335. length = encodeMsg(tmpBuffer, length, message, value);
  336. //Encode length
  337. offset = writeBytes(buffer, offset, codec.encodeUInt32(length));
  338. //contact the object
  339. for(var i = 0; i < length; i++){
  340. buffer[offset] = tmpBuffer[i];
  341. offset++;
  342. }
  343. }
  344. break;
  345. }
  346. return offset;
  347. }
  348. /**
  349. * Encode reapeated properties, simple msg and object are decode differented
  350. */
  351. function encodeArray(array, proto, offset, buffer, protos){
  352. var i = 0;
  353. if(util.isSimpleType(proto.type)){
  354. offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));
  355. offset = writeBytes(buffer, offset, codec.encodeUInt32(array.length));
  356. for(i = 0; i < array.length; i++){
  357. offset = encodeProp(array[i], proto.type, offset, buffer);
  358. }
  359. }else{
  360. for(i = 0; i < array.length; i++){
  361. offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));
  362. offset = encodeProp(array[i], proto.type, offset, buffer, protos);
  363. }
  364. }
  365. return offset;
  366. }
  367. function writeBytes(buffer, offset, bytes){
  368. for(var i = 0; i < bytes.length; i++, offset++){
  369. buffer[offset] = bytes[i];
  370. }
  371. return offset;
  372. }
  373. function encodeTag(type, tag){
  374. var value = constant.TYPES[type]||2;
  375. return codec.encodeUInt32((tag<<3)|value);
  376. }
  377. })('undefined' !== typeof protobuf ? protobuf : module.exports, this);
  378. /**
  379. * decoder module
  380. */
  381. (function (exports, global){
  382. var protobuf = exports;
  383. var MsgDecoder = exports.decoder = {};
  384. var codec = protobuf.codec;
  385. var util = protobuf.util;
  386. var buffer;
  387. var offset = 0;
  388. MsgDecoder.init = function(protos){
  389. this.protos = protos || {};
  390. };
  391. MsgDecoder.setProtos = function(protos){
  392. if(!!protos){
  393. this.protos = protos;
  394. }
  395. };
  396. MsgDecoder.decode = function(route, buf){
  397. var protos = this.protos[route];
  398. buffer = buf;
  399. offset = 0;
  400. if(!!protos){
  401. return decodeMsg({}, protos, buffer.length);
  402. }
  403. return null;
  404. };
  405. function decodeMsg(msg, protos, length){
  406. while(offset<length){
  407. var head = getHead();
  408. var type = head.type;
  409. var tag = head.tag;
  410. var name = protos.__tags[tag];
  411. switch(protos[name].option){
  412. case 'optional' :
  413. case 'required' :
  414. msg[name] = decodeProp(protos[name].type, protos);
  415. break;
  416. case 'repeated' :
  417. if(!msg[name]){
  418. msg[name] = [];
  419. }
  420. decodeArray(msg[name], protos[name].type, protos);
  421. break;
  422. }
  423. }
  424. return msg;
  425. }
  426. /**
  427. * Test if the given msg is finished
  428. */
  429. function isFinish(msg, protos){
  430. return (!protos.__tags[peekHead().tag]);
  431. }
  432. /**
  433. * Get property head from protobuf
  434. */
  435. function getHead(){
  436. var tag = codec.decodeUInt32(getBytes());
  437. return {
  438. type : tag&0x7,
  439. tag : tag>>3
  440. };
  441. }
  442. /**
  443. * Get tag head without move the offset
  444. */
  445. function peekHead(){
  446. var tag = codec.decodeUInt32(peekBytes());
  447. return {
  448. type : tag&0x7,
  449. tag : tag>>3
  450. };
  451. }
  452. function decodeProp(type, protos){
  453. switch(type){
  454. case 'uInt32':
  455. return codec.decodeUInt32(getBytes());
  456. case 'int32' :
  457. case 'sInt32' :
  458. return codec.decodeSInt32(getBytes());
  459. case 'float' :
  460. var float = codec.decodeFloat(buffer, offset);
  461. offset += 4;
  462. return float;
  463. case 'double' :
  464. var double = codec.decodeDouble(buffer, offset);
  465. offset += 8;
  466. return double;
  467. case 'string' :
  468. var length = codec.decodeUInt32(getBytes());
  469. var str = codec.decodeStr(buffer, offset, length);
  470. offset += length;
  471. return str;
  472. default :
  473. var message = protos && (protos.__messages[type] || MsgDecoder.protos['message ' + type]);
  474. if(!!message){
  475. var length = codec.decodeUInt32(getBytes());
  476. var msg = {};
  477. decodeMsg(msg, message, offset+length);
  478. return msg;
  479. }
  480. break;
  481. }
  482. }
  483. function decodeArray(array, type, protos){
  484. if(util.isSimpleType(type)){
  485. var length = codec.decodeUInt32(getBytes());
  486. for(var i = 0; i < length; i++){
  487. array.push(decodeProp(type));
  488. }
  489. }else{
  490. array.push(decodeProp(type, protos));
  491. }
  492. }
  493. function getBytes(flag){
  494. var bytes = [];
  495. var pos = offset;
  496. flag = flag || false;
  497. var b;
  498. do{
  499. b = buffer[pos];
  500. bytes.push(b);
  501. pos++;
  502. }while(b >= 128);
  503. if(!flag){
  504. offset = pos;
  505. }
  506. return bytes;
  507. }
  508. function peekBytes(){
  509. return getBytes(true);
  510. }
  511. })('undefined' !== typeof protobuf ? protobuf : module.exports, this);