index.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. var ProtoBuf = require("protobufjs"),
  2. ByteBuffer = ProtoBuf.ByteBuffer, // ProtoBuf.js uses and also exposes ByteBuffer.js
  3. Long = ProtoBuf.Long; // as well as Long.js (not used in this example)
  4. // Option 1: Loading the .proto file directly
  5. var builder = ProtoBuf.loadProtoFile("./json.proto"), // Creates the Builder
  6. JS = builder.build("js"); // Returns just the 'js' namespace if that's all we need
  7. // Option 2: Loading the .json file generated through 'proto2js json.proto > json.json'
  8. var root = ProtoBuf.loadJsonFile("./json.json").build(), // Here we make the Builder return the root namespace
  9. JS = root.js; // then we reference 'js' inside. Both is possible.
  10. // Option 3: Loading the module generated through 'proto2js json.proto -commonjs=js > json.js'
  11. var JS = require("./json.js"); // Returns what is specified with -commonjs[=XX] (omitted=root)
  12. // `JS` now contains the js namespace from json.proto: Value, Array and Object
  13. // This is how we use these classes:
  14. /**
  15. * Converts a JSON-like structure to JS-Namespace values.
  16. * @param {*} val JSON
  17. * @returns {!JS.Value} JS-Namespace value
  18. * @inner
  19. */
  20. function _protoify(val) {
  21. switch (typeof val) {
  22. case 'number':
  23. if (val%1 === 0 && val >= (0x80000000|0) && val <= (0x7fffffff|0))
  24. return new JS.Value(val); // sets the first field declared in .js.Value
  25. else
  26. return new JS.Value(null, val); // sets the second field
  27. case 'string':
  28. return new JS.Value({ 'string': val }); // uses object notation instead
  29. case 'boolean':
  30. return new JS.Value({ 'boolean': val });
  31. case 'object':
  32. if (val === null)
  33. return new JS.Value({ 'null': true });
  34. if (Object.prototype.toString.call(val) === "[object Array]") {
  35. var arr = new JS.Array();
  36. for (var i=0; i<val.length; ++i)
  37. arr['values'][i] = _protoify(val[i]);
  38. return new JS.Value({ 'array': arr });
  39. }
  40. var obj = new JS.Object();
  41. for (var key in val)
  42. if (val.hasOwnProperty(key))
  43. obj['keys'].push(_protoify(key)),
  44. obj['values'].push(_protoify(val[key]));
  45. return new JS.Value({ 'object': obj });
  46. case 'undefined':
  47. return new JS.Value(); // undefined
  48. default:
  49. throw Error("Unsupported type: "+(typeof val)); // symbol, function
  50. }
  51. }
  52. /**
  53. * Converts JS-Namespace values to JSON.
  54. * @param {!JS.Value} value JS value
  55. * @returns {*} JSON
  56. * @inner
  57. */
  58. function _jsonify(value) {
  59. if (value.type === null)
  60. return undefined;
  61. switch (value.type) {
  62. case 'null':
  63. return null;
  64. case 'array':
  65. return (function() {
  66. var values = value['array']['values'],
  67. i = 0,
  68. k = values.length,
  69. arr = new Array(k);
  70. for (; i<k; ++i)
  71. arr[i] = _jsonify(values[i]);
  72. return arr;
  73. })();
  74. case 'object':
  75. return (function() {
  76. var keys = value['object']['keys'],
  77. values = value['object']['values'],
  78. i = 0,
  79. k = keys.length,
  80. obj = {};
  81. for (; i<k; ++i)
  82. obj[keys[i]['string'] /* is a JS.Value, here always a string */] = _jsonify(values[i]);
  83. return obj;
  84. })();
  85. default:
  86. return value[value.type];
  87. }
  88. }
  89. // And this is how we actually encode and decode them:
  90. /**
  91. * A temporary Buffer to speed up encoding.
  92. * @type {!ByteBuffer}
  93. * @inner
  94. */
  95. var tempBuffer = ByteBuffer.allocate(1024);
  96. /**
  97. * Converts a JSON structure to a Buffer.
  98. * @param {*} json JSON
  99. * @returns {!Buffer|!ArrayBuffer}
  100. * @expose
  101. */
  102. module.exports = function(json) {
  103. return _protoify(json) // Returns the root JS.Value
  104. .encode(tempBuffer).flip() // Encodes it to a ByteBuffer, here: reusing tempBuffer forever
  105. // The non-tempBuffer alternative is just doing .encode()
  106. .toBuffer(); // Converts it to a Buffer. In the browser, this returns an ArrayBuffer. To return an
  107. // ArrayBuffer explicitly both under node.js and in the browser, use .toArrayBuffer().
  108. // Performance note: This just returns a slice on the ByteBuffer's backing .buffer
  109. };
  110. /**
  111. * Converts a Buffer to a JSON structure.
  112. * @param {!Buffer|!ArrayBuffer} proto Buffer
  113. * @returns {*} JSON
  114. * @expose
  115. */
  116. module.exports.parse = function(proto) {
  117. return _jsonify( // Processes JS-namespace objects
  118. JS.Value.decode(proto) // Decodes the JS.Value from a ByteBuffer, a Buffer, an ArrayBuffer, an Uint8Array, ...
  119. );
  120. };
  121. /**
  122. * Performs maintenance.
  123. * @expose
  124. */
  125. module.exports.performMaintenance = function() {
  126. if (tempBuffer.capacity() > 2048)
  127. tempBuffer = ByteBuffer.allocate(1024);
  128. // In case this module is running inside of a daemon, we'd just call this
  129. // method every now and then to discard the tempBuffer if it becomes too
  130. // large. This is just an example on how to reuse ByteBuffers effectively.
  131. // You may consider something like this for the performance benefit, which
  132. // is decreasing the memory allocation footprint of your app.
  133. };
  134. // Have a nice day!