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