diff --git a/README.md b/README.md index 2d367f5..744b491 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,8 @@ milo.setActiveCallbacks(parser, milo.CALLBACK_ACTIVE_ON_DATA) buffer.set(message, 0) milo.parse(parser, ptr, message.length) +// Use milo.parseWithError to get the consumed characters plus an error flag in a single call. + // Cleanup used resources. milo.destroy(parser) milo.dealloc(ptr, message.length) diff --git a/docs/js.md b/docs/js.md index 36eee7f..1862eb5 100644 --- a/docs/js.md +++ b/docs/js.md @@ -135,6 +135,23 @@ Parses `data` up to `limit` characters. It returns the number of consumed characters. +#### `parseWithError(parser, data, limit)` + +Parses `data` up to `limit` characters. + +It returns an object containing the number of consumed characters and whether the parser errored: + +```javascript +{ + consumed: 0, + errored: false +} +``` + +Internally this wraps the `parse_with_error` WebAssembly export, which returns a signed 32-bit integer: `consumed` on success, and `-(consumed + 1)` on error. The extra `+ 1` allows representing errors that happen after consuming zero bytes. + +Since the raw return value is a signed 32-bit integer, `parseWithError` supports up to `2_147_483_646` consumed bytes per call when an error is reported. + #### `reset(parser)` Resets a parser. The second parameters specifies if to also reset the diff --git a/parser/src/wasm.rs b/parser/src/wasm.rs index 82b0473..5507278 100644 --- a/parser/src/wasm.rs +++ b/parser/src/wasm.rs @@ -70,6 +70,20 @@ pub fn parse(parser: *mut c_void, data: *const c_uchar, limit: usize) -> usize { unsafe { (*(parser as *mut Parser)).parse(data, limit) } } +/// Parses a slice of characters. It returns consumed bytes, or -(consumed + 1) +/// if the parser errored. +#[unsafe(no_mangle)] +pub fn parse_with_error(parser: *mut c_void, data: *const c_uchar, limit: usize) -> i32 { + let parser = unsafe { &mut *(parser as *mut Parser) }; + let consumed = parser.parse(data, limit) as i32; + + if parser.error_code == crate::ERROR_NONE { + consumed + } else { + -(consumed + 1) + } +} + /// Pauses the parser. It will have to be resumed via `resume`. #[unsafe(no_mangle)] pub fn pause(parser: *mut c_void) { unsafe { (*(parser as *mut Parser)).pause() } } diff --git a/parser/src/wasm/template.js b/parser/src/wasm/template.js index d19a0ca..e8d5126 100644 --- a/parser/src/wasm/template.js +++ b/parser/src/wasm/template.js @@ -30,6 +30,16 @@ function parse (parser, data, limit) { return this.parse(parser, data, limit) >>> 0 } +function parseWithError (parser, data, limit) { + const result = this.parse_with_error(parser, data, limit) | 0 + const errored = result < 0 + + return { + consumed: errored ? -result - 1 : result, + errored + } +} + function fail (parser, code, description) { const len = description.length const ptr = this.alloc(len) @@ -102,6 +112,7 @@ export function setup (env = {}) { create: create.bind(wasm), destroy: destroy.bind(wasm), parse: parse.bind(wasm), + parseWithError: parseWithError.bind(wasm), fail: fail.bind(wasm), hasDebug: hasDebug.bind(wasm), clear: wasm.clear,