Skip to content

Commit b4142cf

Browse files
Add Flatbuffers support to NodeJS bindings (#6338)
1 parent 06719be commit b4142cf

File tree

10 files changed

+238
-38
lines changed

10 files changed

+238
-38
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Unreleased
22
- Changes from 5.26.0
33
- API:
4+
- ADDED: Add Flatbuffers support to NodeJS bindings. [#6338](https://github.com/Project-OSRM/osrm-backend/pull/6338)
45
- CHANGED: Add `data_version` field to responses of all services. [#5387](https://github.com/Project-OSRM/osrm-backend/pull/5387)
56
- FIXED: Use Boost.Beast to parse HTTP request. [#6294](https://github.com/Project-OSRM/osrm-backend/pull/6294)
67
- FIXED: Fix inefficient osrm-routed connection handling [#6113](https://github.com/Project-OSRM/osrm-backend/pull/6113)

docs/http.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -956,7 +956,7 @@ The object is used to describe the waypoint on a route.
956956
The default response format is `json`, but OSRM supports binary [`flatbuffers`](https://google.github.io/flatbuffers/) format, which
957957
is much faster in serialization/deserialization, comparing to `json`.
958958

959-
The format itself is described in message descriptors, located at `include/engine/api/flatbuffers directory`. Those descriptors could
959+
The format itself is described in message descriptors, located at `include/engine/api/flatbuffers` directory. Those descriptors could
960960
be compiled to provide protocol parsers in Go/Javascript/Typescript/Java/Dart/C#/Python/Lobster/Lua/Rust/PHP/Kotlin. Precompiled
961961
protocol parser for C++ is supplied with OSRM.
962962

docs/nodejs/api.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Returns the fastest route between two or more coordinates while visiting the way
6464
- `options.approaches` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)?** Keep waypoints on curb side. Can be `null` (unrestricted, default) or `curb`.
6565
`null`/`true`/`false`
6666
- `options.waypoints` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)?** Indices to coordinates to treat as waypoints. If not supplied, all coordinates are waypoints. Must include first and last coordinate index.
67+
- `options.format` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** Which output format to use, either `json`, or [`flatbuffers`](https://github.com/Project-OSRM/osrm-backend/tree/master/include/engine/api/flatbuffers).
6768
- `options.snapping` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** Which edges can be snapped to, either `default`, or `any`. `default` only snaps to edges marked by the profile as `is_startpoint`, `any` will allow snapping to any edge in the routing graph.
6869
- `options.skip_waypoints` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Removes waypoints from the response. Waypoints are still calculated, but not serialized. Could be useful in case you are interested in some other part of response and do not want to transfer waste data. (optional, default `false`)
6970
- `callback` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)**
@@ -99,6 +100,7 @@ Note: `coordinates` in the general options only supports a single `{longitude},{
99100
- `options.number` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** Number of nearest segments that should be returned.
100101
Must be an integer greater than or equal to `1`. (optional, default `1`)
101102
- `options.approaches` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)?** Keep waypoints on curb side. Can be `null` (unrestricted, default) or `curb`.
103+
- `options.format` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** Which output format to use, either `json`, or [`flatbuffers`](https://github.com/Project-OSRM/osrm-backend/tree/master/include/engine/api/flatbuffers).
102104
- `options.snapping` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** Which edges can be snapped to, either `default`, or `any`. `default` only snaps to edges marked by the profile as `is_startpoint`, `any` will allow snapping to any edge in the routing graph.
103105
- `callback` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)**
104106

@@ -334,12 +336,15 @@ specific behaviours.
334336

335337
- `plugin_config` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?** Object literal containing parameters for the trip query.
336338
- `plugin_config.format` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** The format of the result object to various API calls.
337-
Valid options are `object` (default), which returns a
338-
standard Javascript object, as described above, and `json_buffer`, which will return a NodeJS
339-
**[Buffer](https://nodejs.org/api/buffer.html)** object, containing a JSON string. The latter has
340-
the advantage that it can be immediately serialized to disk/sent over the network, and the
341-
generation of the string is performed outside the main NodeJS event loop. This option is ignored
342-
by the `tile` plugin.
339+
Valid options are `object` (default if `options.format` is
340+
`json`), which returns a standard Javascript object, as described above, and `buffer`(default if
341+
`options.format` is `flatbuffers`), which will return a NodeJS
342+
**[Buffer](https://nodejs.org/api/buffer.html)** object, containing a JSON string or Flatbuffers
343+
object. The latter has the advantage that it can be immediately serialized to disk/sent over the
344+
network, and the generation of the string is performed outside the main NodeJS event loop. This
345+
option is ignored by the `tile` plugin. Also note that `options.format` set to `flatbuffers`
346+
cannot be used with `plugin_config.format` set to `object`. `json_buffer` is deprecated alias for
347+
`buffer`.
343348

344349
**Examples**
345350

@@ -351,7 +356,7 @@ var options = {
351356
[13.374481201171875, 52.506191342034576]
352357
]
353358
};
354-
osrm.route(options, { format: "json_buffer" }, function(err, response) {
359+
osrm.route(options, { format: "buffer" }, function(err, response) {
355360
if (err) throw err;
356361
console.log(response.toString("utf-8"));
357362
});

include/nodejs/node_osrm_support.hpp

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
#define OSRM_BINDINGS_NODE_SUPPORT_HPP
33

44
#include "nodejs/json_v8_renderer.hpp"
5-
#include "util/json_renderer.hpp"
6-
5+
#include "engine/api/flatbuffers/fbresult_generated.h"
76
#include "osrm/approach.hpp"
87
#include "osrm/bearing.hpp"
98
#include "osrm/coordinate.hpp"
@@ -18,6 +17,7 @@
1817
#include "osrm/table_parameters.hpp"
1918
#include "osrm/tile_parameters.hpp"
2019
#include "osrm/trip_parameters.hpp"
20+
#include "util/json_renderer.hpp"
2121

2222
#include <boost/assert.hpp>
2323
#include <boost/optional.hpp>
@@ -26,6 +26,7 @@
2626
#include <iostream>
2727
#include <iterator>
2828
#include <sstream>
29+
#include <stdexcept>
2930
#include <string>
3031
#include <vector>
3132

@@ -46,7 +47,7 @@ using table_parameters_ptr = std::unique_ptr<osrm::TableParameters>;
4647

4748
struct PluginParameters
4849
{
49-
bool renderJSONToBuffer = false;
50+
bool renderToBuffer = false;
5051
};
5152

5253
using ObjectOrString = typename mapbox::util::variant<osrm::json::Object, std::string>;
@@ -96,6 +97,17 @@ inline void ParseResult(const osrm::Status &result_status, osrm::json::Object &r
9697
}
9798

9899
inline void ParseResult(const osrm::Status & /*result_status*/, const std::string & /*unused*/) {}
100+
inline void ParseResult(const osrm::Status &result_status,
101+
const flatbuffers::FlatBufferBuilder &fbs_builder)
102+
{
103+
auto fbs_result = osrm::engine::api::fbresult::GetFBResult(fbs_builder.GetBufferPointer());
104+
105+
if (result_status == osrm::Status::Error)
106+
{
107+
BOOST_ASSERT(fbs_result->code());
108+
throw std::logic_error(fbs_result->code()->message()->c_str());
109+
}
110+
}
99111

100112
inline engine_config_ptr argumentsToEngineConfig(const Nan::FunctionCallbackInfo<v8::Value> &args)
101113
{
@@ -725,6 +737,36 @@ inline bool argumentsToParameter(const Nan::FunctionCallbackInfo<v8::Value> &arg
725737
}
726738
}
727739

740+
if (Nan::Has(obj, Nan::New("format").ToLocalChecked()).FromJust())
741+
{
742+
v8::Local<v8::Value> format =
743+
Nan::Get(obj, Nan::New("format").ToLocalChecked()).ToLocalChecked();
744+
if (format.IsEmpty())
745+
{
746+
return false;
747+
}
748+
749+
if (!format->IsString())
750+
{
751+
Nan::ThrowError("format must be a string: \"json\" or \"flatbuffers\"");
752+
return false;
753+
}
754+
755+
std::string format_str = *Nan::Utf8String(format);
756+
if (format_str == "json")
757+
{
758+
params->format = osrm::engine::api::BaseParameters::OutputFormatType::JSON;
759+
}
760+
else if (format_str == "flatbuffers")
761+
{
762+
params->format = osrm::engine::api::BaseParameters::OutputFormatType::FLATBUFFERS;
763+
}
764+
else
765+
{
766+
Nan::ThrowError("format must be a string: \"json\" or \"flatbuffers\"");
767+
return false;
768+
}
769+
}
728770
return true;
729771
}
730772

@@ -885,17 +927,18 @@ inline bool parseCommonParameters(const v8::Local<v8::Object> &obj, ParamType &p
885927
return true;
886928
}
887929

888-
inline PluginParameters
889-
argumentsToPluginParameters(const Nan::FunctionCallbackInfo<v8::Value> &args)
930+
inline PluginParameters argumentsToPluginParameters(
931+
const Nan::FunctionCallbackInfo<v8::Value> &args,
932+
const boost::optional<osrm::engine::api::BaseParameters::OutputFormatType> &output_format = {})
890933
{
891934
if (args.Length() < 3 || !args[1]->IsObject())
892935
{
893-
return {};
936+
// output to buffer by default for Flatbuffers
937+
return {output_format == osrm::engine::api::BaseParameters::OutputFormatType::FLATBUFFERS};
894938
}
895939
v8::Local<v8::Object> obj = Nan::To<v8::Object>(args[1]).ToLocalChecked();
896940
if (Nan::Has(obj, Nan::New("format").ToLocalChecked()).FromJust())
897941
{
898-
899942
v8::Local<v8::Value> format =
900943
Nan::Get(obj, Nan::New("format").ToLocalChecked()).ToLocalChecked();
901944
if (format.IsEmpty())
@@ -905,7 +948,7 @@ argumentsToPluginParameters(const Nan::FunctionCallbackInfo<v8::Value> &args)
905948

906949
if (!format->IsString())
907950
{
908-
Nan::ThrowError("format must be a string: \"object\" or \"json_buffer\"");
951+
Nan::ThrowError("format must be a string: \"object\" or \"buffer\"");
909952
return {};
910953
}
911954

@@ -914,20 +957,35 @@ argumentsToPluginParameters(const Nan::FunctionCallbackInfo<v8::Value> &args)
914957

915958
if (format_str == "object")
916959
{
960+
if (output_format == osrm::engine::api::BaseParameters::OutputFormatType::FLATBUFFERS)
961+
{
962+
Nan::ThrowError("Flatbuffers result can only output to buffer.");
963+
return {true};
964+
}
917965
return {false};
918966
}
967+
else if (format_str == "buffer")
968+
{
969+
return {true};
970+
}
919971
else if (format_str == "json_buffer")
920972
{
973+
if (output_format &&
974+
output_format != osrm::engine::api::BaseParameters::OutputFormatType::JSON)
975+
{
976+
Nan::ThrowError("Deprecated `json_buffer` can only be used with JSON format");
977+
}
921978
return {true};
922979
}
923980
else
924981
{
925-
Nan::ThrowError("format must be a string: \"object\" or \"json_buffer\"");
982+
Nan::ThrowError("format must be a string: \"object\" or \"buffer\"");
926983
return {};
927984
}
928985
}
929986

930-
return {};
987+
// output to buffer by default for Flatbuffers
988+
return {output_format == osrm::engine::api::BaseParameters::OutputFormatType::FLATBUFFERS};
931989
}
932990

933991
inline route_parameters_ptr

src/nodejs/node_osrm.cpp

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include <exception>
1212
#include <sstream>
13+
#include <stdexcept>
1314
#include <type_traits>
1415
#include <utility>
1516

@@ -128,8 +129,7 @@ inline void async(const Nan::FunctionCallbackInfo<v8::Value> &info,
128129
auto params = argsToParams(info, requires_multiple_coordinates);
129130
if (!params)
130131
return;
131-
132-
auto pluginParams = argumentsToPluginParameters(info);
132+
auto pluginParams = argumentsToPluginParameters(info, params->format);
133133

134134
BOOST_ASSERT(params->IsValid());
135135

@@ -156,20 +156,41 @@ inline void async(const Nan::FunctionCallbackInfo<v8::Value> &info,
156156
void Execute() override
157157
try
158158
{
159-
osrm::engine::api::ResultT r;
160-
r = osrm::util::json::Object();
161-
const auto status = ((*osrm).*(service))(*params, r);
162-
auto json_result = r.get<osrm::json::Object>();
163-
ParseResult(status, json_result);
164-
if (pluginParams.renderJSONToBuffer)
159+
switch (
160+
params->format.value_or(osrm::engine::api::BaseParameters::OutputFormatType::JSON))
165161
{
166-
std::ostringstream buf;
167-
osrm::util::json::render(buf, json_result);
168-
result = buf.str();
162+
case osrm::engine::api::BaseParameters::OutputFormatType::JSON:
163+
{
164+
osrm::engine::api::ResultT r;
165+
r = osrm::util::json::Object();
166+
const auto status = ((*osrm).*(service))(*params, r);
167+
auto &json_result = r.get<osrm::json::Object>();
168+
ParseResult(status, json_result);
169+
if (pluginParams.renderToBuffer)
170+
{
171+
std::ostringstream buf;
172+
osrm::util::json::render(buf, json_result);
173+
result = buf.str();
174+
}
175+
else
176+
{
177+
result = json_result;
178+
}
169179
}
170-
else
180+
break;
181+
case osrm::engine::api::BaseParameters::OutputFormatType::FLATBUFFERS:
171182
{
172-
result = json_result;
183+
osrm::engine::api::ResultT r = flatbuffers::FlatBufferBuilder();
184+
const auto status = ((*osrm).*(service))(*params, r);
185+
const auto &fbs_result = r.get<flatbuffers::FlatBufferBuilder>();
186+
ParseResult(status, fbs_result);
187+
BOOST_ASSERT(pluginParams.renderToBuffer);
188+
std::string result_str(
189+
reinterpret_cast<const char *>(fbs_result.GetBufferPointer()),
190+
fbs_result.GetSize());
191+
result = std::move(result_str);
192+
}
193+
break;
173194
}
174195
}
175196
catch (const std::exception &e)
@@ -299,6 +320,7 @@ inline void asyncForTiles(const Nan::FunctionCallbackInfo<v8::Value> &info,
299320
* @param {Array} [options.approaches] Keep waypoints on curb side. Can be `null` (unrestricted, default) or `curb`.
300321
* `null`/`true`/`false`
301322
* @param {Array} [options.waypoints] Indices to coordinates to treat as waypoints. If not supplied, all coordinates are waypoints. Must include first and last coordinate index.
323+
* @param {String} [options.format] Which output format to use, either `json`, or [`flatbuffers`](https://github.com/Project-OSRM/osrm-backend/tree/master/include/engine/api/flatbuffers).
302324
* @param {String} [options.snapping] Which edges can be snapped to, either `default`, or `any`. `default` only snaps to edges marked by the profile as `is_startpoint`, `any` will allow snapping to any edge in the routing graph.
303325
* @param {Boolean} [options.skip_waypoints=false] Removes waypoints from the response. Waypoints are still calculated, but not serialized. Could be useful in case you are interested in some other part of response and do not want to transfer waste data.
304326
* @param {Function} callback
@@ -340,6 +362,7 @@ NAN_METHOD(Engine::route) //
340362
* @param {Number} [options.number=1] Number of nearest segments that should be returned.
341363
* Must be an integer greater than or equal to `1`.
342364
* @param {Array} [options.approaches] Keep waypoints on curb side. Can be `null` (unrestricted, default) or `curb`.
365+
* @param {String} [options.format] Which output format to use, either `json`, or [`flatbuffers`](https://github.com/Project-OSRM/osrm-backend/tree/master/include/engine/api/flatbuffers).
343366
* @param {String} [options.snapping] Which edges can be snapped to, either `default`, or `any`. `default` only snaps to edges marked by the profile as `is_startpoint`, `any` will allow snapping to any edge in the routing graph.
344367
* @param {Function} callback
345368
*
@@ -606,12 +629,15 @@ NAN_METHOD(Engine::trip) //
606629
* @name Configuration
607630
* @param {Object} [plugin_config] - Object literal containing parameters for the trip query.
608631
* @param {String} [plugin_config.format] The format of the result object to various API calls.
609-
* Valid options are `object` (default), which returns a
610-
* standard Javascript object, as described above, and `json_buffer`, which will return a NodeJS
611-
* **[Buffer](https://nodejs.org/api/buffer.html)** object, containing a JSON string. The latter has
612-
* the advantage that it can be immediately serialized to disk/sent over the network, and the
613-
* generation of the string is performed outside the main NodeJS event loop. This option is ignored
614-
* by the `tile` plugin.
632+
* Valid options are `object` (default if `options.format` is
633+
* `json`), which returns a standard Javascript object, as described above, and `buffer`(default if
634+
* `options.format` is `flatbuffers`), which will return a NodeJS
635+
* **[Buffer](https://nodejs.org/api/buffer.html)** object, containing a JSON string or Flatbuffers
636+
* object. The latter has the advantage that it can be immediately serialized to disk/sent over the
637+
* network, and the generation of the string is performed outside the main NodeJS event loop. This
638+
* option is ignored by the `tile` plugin. Also note that `options.format` set to `flatbuffers`
639+
* cannot be used with `plugin_config.format` set to `object`. `json_buffer` is deprecated alias for
640+
* `buffer`.
615641
*
616642
* @example
617643
* var osrm = new OSRM('network.osrm');
@@ -621,7 +647,7 @@ NAN_METHOD(Engine::trip) //
621647
* [13.374481201171875, 52.506191342034576]
622648
* ]
623649
* };
624-
* osrm.route(options, { format: "json_buffer" }, function(err, response) {
650+
* osrm.route(options, { format: "buffer" }, function(err, response) {
625651
* if (err) throw err;
626652
* console.log(response.toString("utf-8"));
627653
* });

test/nodejs/match.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,24 @@ var data_path = require('./constants').data_path;
44
var mld_data_path = require('./constants').mld_data_path;
55
var three_test_coordinates = require('./constants').three_test_coordinates;
66
var two_test_coordinates = require('./constants').two_test_coordinates;
7+
const flatbuffers = require('../../features/support/flatbuffers').flatbuffers;
8+
const FBResult = require('../../features/support/fbresult_generated').osrm.engine.api.fbresult.FBResult;
9+
10+
11+
test('match: match in Monaco with flatbuffers format', function(assert) {
12+
assert.plan(2);
13+
var osrm = new OSRM(data_path);
14+
var options = {
15+
coordinates: three_test_coordinates,
16+
timestamps: [1424684612, 1424684616, 1424684620],
17+
format: 'flatbuffers'
18+
};
19+
osrm.match(options, function(err, response) {
20+
assert.ifError(err);
21+
const fb = FBResult.getRootAsFBResult(new flatbuffers.ByteBuffer(response));
22+
assert.equal(fb.routesLength(), 1);
23+
});
24+
});
725

826
test('match: match in Monaco', function(assert) {
927
assert.plan(5);

0 commit comments

Comments
 (0)