├── .babelrc
├── .gitignore
├── .npmignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── corrode.svg
├── docs-md
├── configuration.md
├── getting-started.md
└── overview.md
├── docs
├── CNAME
├── ast
│ └── source
│ │ ├── .external-ecmascript.js.json
│ │ ├── assert
│ │ └── index.js.json
│ │ ├── base.js.json
│ │ ├── index.js.json
│ │ ├── map
│ │ └── index.js.json
│ │ ├── utils
│ │ └── index.js.json
│ │ └── variable-stack.js.json
├── badge.svg
├── class
│ └── src
│ │ ├── base.js~CorrodeBase.html
│ │ ├── index.js~Corrode.html
│ │ └── variable-stack.js~VariableStack.html
├── coverage.json
├── css
│ ├── github.css
│ ├── identifiers.css
│ ├── manual.css
│ ├── prettify-tomorrow.css
│ ├── search.css
│ ├── source.css
│ ├── style.css
│ └── test.css
├── file
│ └── src
│ │ ├── assert
│ │ └── index.js.html
│ │ ├── base.js.html
│ │ ├── index.js.html
│ │ ├── map
│ │ └── index.js.html
│ │ ├── utils
│ │ └── index.js.html
│ │ └── variable-stack.js.html
├── function
│ └── index.html
├── identifiers.html
├── image
│ ├── badge.svg
│ ├── esdoc-logo-mini-black.png
│ ├── esdoc-logo-mini.png
│ ├── github.png
│ ├── manual-badge.svg
│ └── search.png
├── index.html
├── index.json
├── lint.json
├── manual
│ ├── CHANGELOG.html
│ ├── configuration.html
│ ├── getting-started.html
│ ├── index.html
│ └── overview.html
├── script
│ ├── inherited-summary.js
│ ├── inner-link.js
│ ├── manual.js
│ ├── patch-for-local.js
│ ├── prettify
│ │ ├── Apache-License-2.0.txt
│ │ └── prettify.js
│ ├── pretty-print.js
│ ├── search.js
│ ├── search_index.js
│ └── test-summary.js
├── source.html
├── test-file
│ └── test
│ │ ├── assert.test.js.html
│ │ ├── base-aborts.test.js.html
│ │ ├── base-edge-cases.test.js.html
│ │ ├── base-flags.test.js.html
│ │ ├── base-loop-anonymous.test.js.html
│ │ ├── base-loop-named.test.js.html
│ │ ├── base-primitves.test.js.html
│ │ ├── base-push-jobs.test.js.html
│ │ ├── base-skip.test.js.html
│ │ ├── base-stack.test.js.html
│ │ ├── base-tap.test.js.html
│ │ ├── corrode-extensions.test.js.html
│ │ ├── corrode-helper.test.js.html
│ │ ├── corrode-pointer.test.js.html
│ │ ├── corrode-position.test.js.html
│ │ ├── corrode-repeat.test.js.html
│ │ ├── corrode-terminated-buffer.test.js.html
│ │ ├── corrode-terminated-string.test.js.html
│ │ ├── map.test.js.html
│ │ ├── utils.test.js.html
│ │ └── variable-stack.test.js.html
├── test.html
└── variable
│ └── index.html
├── esdoc.json
├── examples
└── id3.js
├── package-lock.json
├── package.json
├── src
├── assert
│ └── index.js
├── base.js
├── index.js
├── map
│ └── index.js
├── utils
│ └── index.js
└── variable-stack.js
└── test
├── assert.test.js
├── base-aborts.test.js
├── base-edge-cases.test.js
├── base-flags.test.js
├── base-loop-anonymous.test.js
├── base-loop-named.test.js
├── base-primitves.test.js
├── base-push-jobs.test.js
├── base-skip.test.js
├── base-stack.test.js
├── base-tap.test.js
├── corrode-extensions.test.js
├── corrode-helper.test.js
├── corrode-pointer.test.js
├── corrode-position.test.js
├── corrode-repeat.test.js
├── corrode-terminated-buffer.test.js
├── corrode-terminated-string.test.js
├── fixtures
├── double-seq.bin
├── float-seq.bin
├── int16-seq.bin
├── int32-seq.bin
├── int64-seq.bin
├── int8-seq.bin
├── string-utf8.bin
└── vars.js
├── helpers
└── asserts.js
├── map.test.js
├── utils.test.js
└── variable-stack.test.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "es2015",
4 | "stage-0"
5 | ],
6 | "plugins": [
7 | "add-module-exports"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 |
40 | .DS_Store
41 | dist
42 | esdoc
43 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .nyc_output
2 | .travis.yml
3 | corrode.svg
4 | esdoc.json
5 |
6 | coverage
7 | esdoc
8 | test
9 | docs
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - stable
4 | - 5.12.0
5 | - 4.4.7
6 |
7 | install:
8 | - npm install
9 |
10 | script:
11 | - npm run coverall
12 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## corrode v1.0.2
4 | * Update ESDoc to v0.5.2 for better ES7 support
5 |
6 | ## corrode v1.0.1
7 | * Update lodash from 4.16.1 to 4.17.2
8 | * Update readable-stream from 2.1.5 to 2.2.2
9 | * Fix http links in npm-shrinkwrap.json
10 |
11 | ## corrode v1.0.0
12 | * Initial release. Any Changes prior to this version are merged into this one.
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Sebastian Langer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [](./LICENSE)
6 | [](https://www.npmjs.com/package/corrode)
7 | [](https://david-dm.org/screeny05/corrode)
8 | [](https://coveralls.io/github/screeny05/corrode)
9 | [](https://travis-ci.org/screeny05/corrode)
10 | [](https://corrode.scn.cx/)
11 |
12 | Corrode is a batteries-included library for reading binary data. It helps you converting that blob-mess into useable data.
13 |
14 | Use it to parse _that one_ obscure binary-file with the help of JavaScript.
15 |
16 | #### Install
17 | ```
18 | $ npm install --save corrode
19 | ```
20 |
21 | #### Tests
22 | ```
23 | $ npm test
24 | ```
25 |
26 | #### Offline Docs
27 | ```
28 | $ npm run docs
29 | $ open doc/index.html
30 | ```
31 |
32 |
33 | ## What's this?
34 | corrode provides standard read-actions like uint8-uint64 for big & little endian, strings, buffers and control-structures like loops, skipping, etc. for your buffers and files.
35 | Additionally you can use assertions to always be sure, the data you parse corresponds to a specified format.
36 | The parsing is done not by a configuration-object, but by imperative code, allowing for far greater flexibility.
37 |
38 | corrode is an abstraction on top of `TransformStream` and as such is pipeable to but also provides functions for more simple usage.
39 |
40 | This library is not only heavily inspired by [dissolve](https://github.com/deoxxa/dissolve), it in fact can be seen as a total rewrite with even more features.
41 | The code is written in ES7, fully documented and tested.
42 |
43 |
44 | ## Quick examples
45 | ```javascript
46 | const Corrode = require('corrode');
47 | const parser = new Corrode();
48 |
49 | parser
50 | .uint8('val_1')
51 | .uint32('val_2')
52 | .int16be('val_3')
53 | .tap(function(){
54 | console.log(this.vars.val_1 * this.vars.val_3);
55 | })
56 | .repeat('array', 5, function(){
57 | this
58 | .uint32('array_val_1')
59 | .string('array_val_4', 5);
60 | });
61 | ```
62 |
63 | #### Parsing a buffer
64 | ```javascript
65 | parser.fromBuffer(buffer, () => console.log(parser.vars));
66 | ```
67 |
68 | #### Parsing a filestream
69 | ```javascript
70 | var stream = fs.createReadStream(file);
71 | stream.pipe(parser);
72 | parser.on('finish', () => console.log(parser.vars));
73 | ```
74 |
75 | These are just some of the very basic operations supported by Corrode.
76 |
77 |
78 | ## Examples
79 | All examples can be found in the examples/-folder. Included:
80 | * ID3v2.3-Parser - strict, unforgiving parser for a subset of the standard used to store meta-data in mp3-files. It needs `npm i image-to-ascii temp` and can be run with `node examples/id3 test.mp3`.
81 |
82 | If you'd like to include your own examples, just open a PR. I'm more than happy to not have to think about existing complex structured binary data to parse myself.
83 |
84 |
85 | ## Documentation & API Reference
86 | * [Corrode Overview](https://corrode.scn.cx/manual/overview.html)
87 | * [API Reference](https://corrode.scn.cx/identifiers.html)
88 | * [Getting Started](https://corrode.scn.cx/manual/getting-started.html)
89 |
90 |
91 | ## Why use corrode over dissolve
92 | It solves most of the major shortcomings dissolve has:
93 | * EOF terminates corrode. If not explicitly asked not to do so it will give you all variables, without you having to fiddle with its intestines.
94 | * Loops get unwinded correctly.
95 | * Thoroughly tested.
96 | * As a js-library from 2016 it has all the swag you need.
97 |
98 |
99 | ## When not to use corrode
100 | * Your data is too complex - If you need to apply black magic on your data, to retrieve meaningful values, corrode currently may not support your use-case.
101 | * Your data is really simple - If you don't need to read structured data, but instead just numbers or strings you should simply use the built-in read-functions provided by `Buffer`.
102 |
103 | Not yet included are additions like bignum-support for int64 and additional non-node-standard-encodings.
104 |
105 | corrode also works in browsers. You will need a `setImmediate()` polyfill, but you're able to parse ArrayBuffers as usual.
106 |
107 |
108 | ## Used dependencies (3)
109 | The following dependencies are installed when installing corrode:
110 | * bl - used for buffering data, in case a job gets greedy or you don't want to auto-flush
111 | * readable-streams - ensures consistent and stable behaviour of the underlying Transform-Stream
112 | * lodash - several utility functions
113 |
114 |
115 | ## License
116 | This library is issued under the [MIT license](./LICENSE).
117 |
118 | The Logo is from The Noun Project, created by [Michael Senkow](https://thenounproject.com/mhsenkow/) and licensed under the [CC-BY-3.0](https://creativecommons.org/licenses/by/3.0/us/).
119 |
--------------------------------------------------------------------------------
/corrode.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs-md/configuration.md:
--------------------------------------------------------------------------------
1 | # Configuration
2 |
3 | Corrode provides some configuration-options. These are either defaults, safeguards or advanced-user-stuff.
4 |
5 | Corrode accepts an object as its first parameter, containing options.
6 |
7 | These options also get passed on to the TransformStream-constructor, so [those](https://nodejs.org/api/stream.html#stream_constructor_new_stream_writable_options) [options](https://nodejs.org/api/stream.html#stream_new_stream_readable_options) are also valid.
8 |
9 |
10 | ## `endianness`
11 | **default:** `'LE'`
12 |
13 | **accepts:** `'LE', 'BE'` (for little endian & big endian)
14 |
15 | If you use methods like `.uint8()` or `.int32()` this option determines what endianness corrode uses when reading your bytes.
16 |
17 | Of course using `.uint16be()` or `.doublele()` will overwrite this default, as you'd expect.
18 |
19 |
20 | ## `loopIdentifier`
21 | **default:** `Symbol(loop-variable)`
22 |
23 | **accepts:** Anything which can be used as an identifier in an object.
24 |
25 | Determines the identifier of the temporary variable which gets created when using `.loop()`.
26 |
27 |
28 | ## `encoding`
29 | **default:** `'utf8'`
30 |
31 | **accepts:** Any encoding `Buffer.prototype.toString` accepts. [Full list here](https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings).
32 |
33 | Determines which encoding to use for string-functions like `.string()` or `.terminatedString()`. Can be overwitten on a per-use-basis by the functions themselves.
34 |
35 |
36 | ## `finishJobsOnEOF`
37 | **default:** `true`
38 |
39 | **accepts:** `true, false`
40 |
41 | Determines whether or not to finish everything up, once corrode encounters the end of the stream being piped into corrode, or the end of the buffer.
42 |
43 | Set to `false` if you want to parse a file which was split in two or more parts. Or somethin along those lines.
44 |
45 | What this flag does in detail is to clean up all remaining jobs from the job list, which are read-related, once EOF is reached. Then the job list will be worked on, until there are no more jobs. Meaning all VariableStack-Layers have been popped, giving you access to all that sweet data of yours.
46 |
47 |
48 | ## `anonymousLoopDiscardDeep`
49 | **default:** `false`
50 |
51 | **accepts:** `true, false`
52 |
53 | Corrode provides the `discard()` function inside `.loop()`- and `.repeat()`-callbacks. If you use those anonymously (meaning you don't push an array onto the variable-stack by giving a string as the first parameter) and then call the `discard()` function, corrode will discard whatever data you read, and restore what's been there before.
54 |
55 | By default this is done in a shallow way, meaning that corrode will clone the users data before the callback is called shallowly by using `Object.assign()` and that shallow copy gets assigned again when calling `discard()`.
56 |
57 | This may lead to problems, when modifiyng objects within the curren VariableStack-layer. Those won't get replaced with their original version.
58 |
59 | To circumvent this problem you can set this option to true. This is not done, because probably no-one needs this, and it may be a huge performance-hit to clone an entire object, everytime the loop-callback is called.
60 |
61 |
62 | ## `strictObjectMode`
63 | **default:** `true`
64 |
65 | **accepts:** `true, false`
66 |
67 | When this option is set to true, corrode will prevent you from pushing into anything that's not an object. Meaning moves like this:
68 | ```javascript
69 | parser.uint8('val').tap('val', function(){});
70 | ```
71 | will throw an error. This way corrode provides a naive way of type-safety.
72 |
--------------------------------------------------------------------------------
/docs-md/overview.md:
--------------------------------------------------------------------------------
1 | # Overview
2 |
3 | ## Components
4 | Corrode consists of 5 components:
5 | 1. The [VariableStack](https://doc.esdoc.org/github.com/screeny05/corrode/class/src/variable-stack.js~VariableStack.html). It handles all things variables when reading data, pushing structures and looping bytes.
6 | 2. The [CorrodeBase](https://doc.esdoc.org/github.com/screeny05/corrode/class/src/base.js~CorrodeBase.html). This is the part of corrode, reading all bytes. It's the low-level part handling all the small things, preventing you from shooting yourself into the foot. Of course, it also enables you to do just that, if you explicitly ask for it.
7 | 3. The [Assertions](). Just like your assertion-library of choice these ensure you read correct data. If not it will abort mission.
8 | 4. The [Mappers](). Doing all the bulk parts of work for you, the mappers ensure your data is in the way you need it to be. Think of these like JavaScripts Array.prototype.map. Except for bits and bytes.
9 | 5. Corrode itself. This is the only thing you - as a developer - are directly in contact with. It connects The CorrodeBase with the Assertions and Mappers to provide a unified interface.
10 |
11 | For more info on each of those see the Reference.
12 |
13 | For info on ways to configure corrode to your liking, see Configuration.
14 |
15 | ## The Assertions
16 | Asserts help you to make sure, the buffer you're parsing is in the correct format.
17 | These assertions are like chai, throwing an error when an assertion doesn't hold.
18 |
19 | These functions won't modify your variables.
20 |
21 | All assertions can be called from a Corrode instance like this:
22 | ```javascript
23 | parser.assert.equal('var', 5);
24 | ```
25 |
26 |
27 | ## The Mappers
28 | These functions provide basic mapping-abilities for Corrode's VariableStack.
29 |
30 | Imagine them like this:
31 | ```javascript
32 | const parser = new Corrode();
33 | parser.uint8('value').map.double('value');
34 | ```
35 |
36 | Of course there's no existing mapping-function which doubles a value (yet?).
37 | But the concept is that they are functions receiving a value, processing it
38 | and saving a new value in the {@link VariableStack} in place of the old one.
39 |
40 | The imaginary code above would yield `{ value: 4 }`, parsing a buffer like this `[2]`.
41 |
42 | Note that all mappers don't check for existance, validity or other assumptions.
43 | You have to do that yourself with assertions.
44 |
45 |
46 | ## When will i be able to access the variables? (Extensions, Asserts & Mappers)
47 |
48 | ## Corrode#position / Corrode#skip issues (isSeeking)
49 |
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | corrode.scn.cx
--------------------------------------------------------------------------------
/docs/badge.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | document
13 | document
14 | 90%
15 | 90%
16 |
17 |
18 |
--------------------------------------------------------------------------------
/docs/coverage.json:
--------------------------------------------------------------------------------
1 | {
2 | "coverage": "90.83%",
3 | "expectCount": 120,
4 | "actualCount": 109,
5 | "files": {
6 | "src/assert/index.js": {
7 | "expectCount": 9,
8 | "actualCount": 9,
9 | "undocumentLines": []
10 | },
11 | "src/base.js": {
12 | "expectCount": 65,
13 | "actualCount": 61,
14 | "undocumentLines": [
15 | 6,
16 | 7,
17 | 8,
18 | 9
19 | ]
20 | },
21 | "src/index.js": {
22 | "expectCount": 18,
23 | "actualCount": 18,
24 | "undocumentLines": []
25 | },
26 | "src/map/index.js": {
27 | "expectCount": 10,
28 | "actualCount": 10,
29 | "undocumentLines": []
30 | },
31 | "src/utils/index.js": {
32 | "expectCount": 2,
33 | "actualCount": 2,
34 | "undocumentLines": []
35 | },
36 | "src/variable-stack.js": {
37 | "expectCount": 16,
38 | "actualCount": 9,
39 | "undocumentLines": [
40 | 1,
41 | 2,
42 | 3,
43 | 4,
44 | 5,
45 | 64,
46 | 65
47 | ]
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/docs/css/github.css:
--------------------------------------------------------------------------------
1 | /* github markdown */
2 | .github-markdown {
3 | font-size: 16px;
4 | }
5 |
6 | .github-markdown h1,
7 | .github-markdown h2,
8 | .github-markdown h3,
9 | .github-markdown h4,
10 | .github-markdown h5 {
11 | margin-top: 1em;
12 | margin-bottom: 16px;
13 | font-weight: bold;
14 | padding: 0;
15 | }
16 |
17 | .github-markdown h1:nth-of-type(1) {
18 | margin-top: 0;
19 | }
20 |
21 | .github-markdown h1 {
22 | font-size: 2em;
23 | padding-bottom: 0.3em;
24 | }
25 |
26 | .github-markdown h2 {
27 | font-size: 1.75em;
28 | padding-bottom: 0.3em;
29 | }
30 |
31 | .github-markdown h3 {
32 | font-size: 1.5em;
33 | }
34 |
35 | .github-markdown h4 {
36 | font-size: 1.25em;
37 | }
38 |
39 | .github-markdown h5 {
40 | font-size: 1em;
41 | }
42 |
43 | .github-markdown ul, .github-markdown ol {
44 | padding-left: 2em;
45 | }
46 |
47 | .github-markdown pre > code {
48 | font-size: 0.85em;
49 | }
50 |
51 | .github-markdown table {
52 | margin-bottom: 1em;
53 | border-collapse: collapse;
54 | border-spacing: 0;
55 | }
56 |
57 | .github-markdown table tr {
58 | background-color: #fff;
59 | border-top: 1px solid #ccc;
60 | }
61 |
62 | .github-markdown table th,
63 | .github-markdown table td {
64 | padding: 6px 13px;
65 | border: 1px solid #ddd;
66 | }
67 |
68 | .github-markdown table tr:nth-child(2n) {
69 | background-color: #f8f8f8;
70 | }
71 |
72 | .github-markdown hr {
73 | border-right: 0;
74 | border-bottom: 1px solid #e5e5e5;
75 | border-left: 0;
76 | border-top: 0;
77 | }
78 |
79 | /** badge(.svg) does not have border */
80 | .github-markdown img:not([src*=".svg"]) {
81 | max-width: 100%;
82 | box-shadow: 1px 1px 1px rgba(0,0,0,0.5);
83 | }
84 |
--------------------------------------------------------------------------------
/docs/css/identifiers.css:
--------------------------------------------------------------------------------
1 | .identifiers-wrap {
2 | display: flex;
3 | align-items: flex-start;
4 | }
5 |
6 | .identifier-dir-tree {
7 | background: #fff;
8 | border: solid 1px #ddd;
9 | border-radius: 0.25em;
10 | top: 52px;
11 | position: -webkit-sticky;
12 | position: sticky;
13 | max-height: calc(100vh - 155px);
14 | overflow-y: scroll;
15 | min-width: 200px;
16 | margin-left: 1em;
17 | }
18 |
19 | .identifier-dir-tree-header {
20 | padding: 0.5em;
21 | background-color: #fafafa;
22 | border-bottom: solid 1px #ddd;
23 | }
24 |
25 | .identifier-dir-tree-content {
26 | padding: 0 0.5em 0;
27 | }
28 |
29 | .identifier-dir-tree-content > div {
30 | padding-top: 0.25em;
31 | padding-bottom: 0.25em;
32 | }
33 |
34 | .identifier-dir-tree-content a {
35 | color: inherit;
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/docs/css/manual.css:
--------------------------------------------------------------------------------
1 | .github-markdown .manual-toc {
2 | padding-left: 0;
3 | }
4 |
5 | .manual-index .manual-cards {
6 | display: flex;
7 | flex-wrap: wrap;
8 | }
9 |
10 | .manual-index .manual-card-wrap {
11 | width: 280px;
12 | padding: 10px 20px 10px 0;
13 | box-sizing: border-box;
14 | }
15 |
16 | .manual-index .manual-card-wrap > h1 {
17 | margin: 0;
18 | font-size: 1em;
19 | font-weight: 600;
20 | padding: 0.2em 0 0.2em 0.5em;
21 | border-radius: 0.1em 0.1em 0 0;
22 | border: none;
23 | }
24 |
25 | .manual-index .manual-card-wrap > h1 span {
26 | color: #555;
27 | }
28 |
29 | .manual-index .manual-card {
30 | height: 200px;
31 | overflow: hidden;
32 | border: solid 1px rgba(230, 230, 230, 0.84);
33 | border-radius: 0 0 0.1em 0.1em;
34 | padding: 8px;
35 | position: relative;
36 | }
37 |
38 | .manual-index .manual-card > div {
39 | transform: scale(0.4);
40 | transform-origin: 0 0;
41 | width: 250%;
42 | }
43 |
44 | .manual-index .manual-card > a {
45 | position: absolute;
46 | top: 0;
47 | left: 0;
48 | width: 100%;
49 | height: 100%;
50 | background: rgba(210, 210, 210, 0.1);
51 | }
52 |
53 | .manual-index .manual-card > a:hover {
54 | background: none;
55 | }
56 |
57 | .manual-index .manual-badge {
58 | margin: 0;
59 | }
60 |
61 | .manual-index .manual-user-index {
62 | margin-bottom: 1em;
63 | border-bottom: solid 1px #ddd;
64 | }
65 |
66 | .manual-root .navigation {
67 | padding-left: 4px;
68 | margin-top: 4px;
69 | }
70 |
71 | .navigation .manual-toc-root > div {
72 | padding-left: 0.25em;
73 | padding-right: 0.75em;
74 | }
75 |
76 | .github-markdown .manual-toc-title a {
77 | color: inherit;
78 | }
79 |
80 | .manual-breadcrumb-list {
81 | font-size: 0.8em;
82 | margin-bottom: 1em;
83 | }
84 |
85 | .manual-toc-title a:hover {
86 | color: #039BE5;
87 | }
88 |
89 | .manual-toc li {
90 | margin: 0.75em 0;
91 | list-style-type: none;
92 | }
93 |
94 | .navigation .manual-toc [class^="indent-h"] a {
95 | color: #666;
96 | }
97 |
98 | .navigation .manual-toc .indent-h1 a {
99 | color: #555;
100 | font-weight: 600;
101 | display: block;
102 | }
103 |
104 | .manual-toc .indent-h1 {
105 | display: block;
106 | margin: 0.4em 0 0 0.25em;
107 | padding: 0.2em 0 0.2em 0.5em;
108 | border-radius: 0.1em;
109 | }
110 |
111 | .manual-root .navigation .manual-toc li:not(.indent-h1) {
112 | margin-top: 0.5em;
113 | }
114 |
115 | .manual-toc .indent-h2 {
116 | display: none;
117 | margin-left: 1.5em;
118 | }
119 | .manual-toc .indent-h3 {
120 | display: none;
121 | margin-left: 2.5em;
122 | }
123 | .manual-toc .indent-h4 {
124 | display: none;
125 | margin-left: 3.5em;
126 | }
127 | .manual-toc .indent-h5 {
128 | display: none;
129 | margin-left: 4.5em;
130 | }
131 |
132 | .manual-nav li {
133 | margin: 0.75em 0;
134 | }
135 |
--------------------------------------------------------------------------------
/docs/css/prettify-tomorrow.css:
--------------------------------------------------------------------------------
1 | /* Tomorrow Theme */
2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */
3 | /* Pretty printing styles. Used with prettify.js. */
4 | /* SPAN elements with the classes below are added by prettyprint. */
5 | /* plain text */
6 | .pln {
7 | color: #4d4d4c; }
8 |
9 | @media screen {
10 | /* string content */
11 | .str {
12 | color: #718c00; }
13 |
14 | /* a keyword */
15 | .kwd {
16 | color: #8959a8; }
17 |
18 | /* a comment */
19 | .com {
20 | color: #8e908c; }
21 |
22 | /* a type name */
23 | .typ {
24 | color: #4271ae; }
25 |
26 | /* a literal value */
27 | .lit {
28 | color: #f5871f; }
29 |
30 | /* punctuation */
31 | .pun {
32 | color: #4d4d4c; }
33 |
34 | /* lisp open bracket */
35 | .opn {
36 | color: #4d4d4c; }
37 |
38 | /* lisp close bracket */
39 | .clo {
40 | color: #4d4d4c; }
41 |
42 | /* a markup tag name */
43 | .tag {
44 | color: #c82829; }
45 |
46 | /* a markup attribute name */
47 | .atn {
48 | color: #f5871f; }
49 |
50 | /* a markup attribute value */
51 | .atv {
52 | color: #3e999f; }
53 |
54 | /* a declaration */
55 | .dec {
56 | color: #f5871f; }
57 |
58 | /* a variable name */
59 | .var {
60 | color: #c82829; }
61 |
62 | /* a function name */
63 | .fun {
64 | color: #4271ae; } }
65 | /* Use higher contrast and text-weight for printable form. */
66 | @media print, projection {
67 | .str {
68 | color: #060; }
69 |
70 | .kwd {
71 | color: #006;
72 | font-weight: bold; }
73 |
74 | .com {
75 | color: #600;
76 | font-style: italic; }
77 |
78 | .typ {
79 | color: #404;
80 | font-weight: bold; }
81 |
82 | .lit {
83 | color: #044; }
84 |
85 | .pun, .opn, .clo {
86 | color: #440; }
87 |
88 | .tag {
89 | color: #006;
90 | font-weight: bold; }
91 |
92 | .atn {
93 | color: #404; }
94 |
95 | .atv {
96 | color: #060; } }
97 | /* Style */
98 | /*
99 | pre.prettyprint {
100 | background: white;
101 | font-family: Consolas, Monaco, 'Andale Mono', monospace;
102 | font-size: 12px;
103 | line-height: 1.5;
104 | border: 1px solid #ccc;
105 | padding: 10px; }
106 | */
107 |
108 | /* Specify class=linenums on a pre to get line numbering */
109 | ol.linenums {
110 | margin-top: 0;
111 | margin-bottom: 0; }
112 |
113 | /* IE indents via margin-left */
114 | li.L0,
115 | li.L1,
116 | li.L2,
117 | li.L3,
118 | li.L4,
119 | li.L5,
120 | li.L6,
121 | li.L7,
122 | li.L8,
123 | li.L9 {
124 | /* */ }
125 |
126 | /* Alternate shading for lines */
127 | li.L1,
128 | li.L3,
129 | li.L5,
130 | li.L7,
131 | li.L9 {
132 | /* */ }
133 |
--------------------------------------------------------------------------------
/docs/css/search.css:
--------------------------------------------------------------------------------
1 | /* search box */
2 | .search-box {
3 | position: absolute;
4 | top: 10px;
5 | right: 50px;
6 | padding-right: 8px;
7 | padding-bottom: 10px;
8 | line-height: normal;
9 | font-size: 12px;
10 | }
11 |
12 | .search-box img {
13 | width: 20px;
14 | vertical-align: top;
15 | }
16 |
17 | .search-input {
18 | display: inline;
19 | visibility: hidden;
20 | width: 0;
21 | padding: 2px;
22 | height: 1.5em;
23 | outline: none;
24 | background: transparent;
25 | border: 1px #0af;
26 | border-style: none none solid none;
27 | vertical-align: bottom;
28 | }
29 |
30 | .search-input-edge {
31 | display: none;
32 | width: 1px;
33 | height: 5px;
34 | background-color: #0af;
35 | vertical-align: bottom;
36 | }
37 |
38 | .search-result {
39 | position: absolute;
40 | display: none;
41 | height: 600px;
42 | width: 100%;
43 | padding: 0;
44 | margin-top: 5px;
45 | margin-left: 24px;
46 | background: white;
47 | box-shadow: 1px 1px 4px rgb(0,0,0);
48 | white-space: nowrap;
49 | overflow-y: scroll;
50 | }
51 |
52 | .search-result-import-path {
53 | color: #aaa;
54 | font-size: 12px;
55 | }
56 |
57 | .search-result li {
58 | list-style: none;
59 | padding: 2px 4px;
60 | }
61 |
62 | .search-result li a {
63 | display: block;
64 | }
65 |
66 | .search-result li.selected {
67 | background: #ddd;
68 | }
69 |
70 | .search-result li.search-separator {
71 | background: rgb(37, 138, 175);
72 | color: white;
73 | }
74 |
75 | .search-box.active .search-input {
76 | visibility: visible;
77 | transition: width 0.2s ease-out;
78 | width: 300px;
79 | }
80 |
81 | .search-box.active .search-input-edge {
82 | display: inline-block;
83 | }
84 |
85 |
--------------------------------------------------------------------------------
/docs/css/source.css:
--------------------------------------------------------------------------------
1 | table.files-summary {
2 | width: 100%;
3 | margin: 10px 0;
4 | border-spacing: 0;
5 | border: 0;
6 | border-collapse: collapse;
7 | text-align: right;
8 | }
9 |
10 | table.files-summary tbody tr:hover {
11 | background: #eee;
12 | }
13 |
14 | table.files-summary td:first-child,
15 | table.files-summary td:nth-of-type(2) {
16 | text-align: left;
17 | }
18 |
19 | table.files-summary[data-use-coverage="false"] td.coverage {
20 | display: none;
21 | }
22 |
23 | table.files-summary thead {
24 | background: #fafafa;
25 | }
26 |
27 | table.files-summary td {
28 | border: solid 1px #ddd;
29 | padding: 4px 10px;
30 | vertical-align: top;
31 | }
32 |
33 | table.files-summary td.identifiers > span {
34 | display: block;
35 | margin-top: 4px;
36 | }
37 | table.files-summary td.identifiers > span:first-child {
38 | margin-top: 0;
39 | }
40 |
41 | table.files-summary .coverage-count {
42 | font-size: 12px;
43 | color: #aaa;
44 | display: inline-block;
45 | min-width: 40px;
46 | }
47 |
48 | .total-coverage-count {
49 | position: relative;
50 | bottom: 2px;
51 | font-size: 12px;
52 | color: #666;
53 | font-weight: 500;
54 | padding-left: 5px;
55 | }
56 |
--------------------------------------------------------------------------------
/docs/css/test.css:
--------------------------------------------------------------------------------
1 | table.test-summary thead {
2 | background: #fafafa;
3 | }
4 |
5 | table.test-summary thead .test-description {
6 | width: 50%;
7 | }
8 |
9 | table.test-summary {
10 | width: 100%;
11 | margin: 10px 0;
12 | border-spacing: 0;
13 | border: 0;
14 | border-collapse: collapse;
15 | }
16 |
17 | table.test-summary thead .test-count {
18 | width: 3em;
19 | }
20 |
21 | table.test-summary tbody tr:hover {
22 | background-color: #eee;
23 | }
24 |
25 | table.test-summary td {
26 | border: solid 1px #ddd;
27 | padding: 4px 10px;
28 | vertical-align: top;
29 | }
30 |
31 | table.test-summary td p {
32 | margin: 0;
33 | }
34 |
35 | table.test-summary tr.test-interface .toggle {
36 | display: inline-block;
37 | float: left;
38 | margin-right: 4px;
39 | cursor: pointer;
40 | font-size: 0.8em;
41 | padding-top: 0.25em;
42 | }
43 |
44 | table.test-summary tr.test-interface .toggle.opened:before {
45 | content: '▼';
46 | }
47 |
48 | table.test-summary tr.test-interface .toggle.closed:before {
49 | content: '▶';
50 | }
51 |
52 | table.test-summary .test-target > span {
53 | display: block;
54 | margin-top: 4px;
55 | }
56 | table.test-summary .test-target > span:first-child {
57 | margin-top: 0;
58 | }
59 |
--------------------------------------------------------------------------------
/docs/file/src/utils/index.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/utils/index.js | corrode
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
57 |
58 |
59 | src/utils/index.js
60 |
import { mapValues } from 'lodash';
61 |
62 | /**
63 | * bind each function in an object with a tap to a given context
64 | * @param {object} obj object with functions
65 | * @param {object} ctx context
66 | * @return {object} copy of object with each function wrapped in a tap
67 | */
68 | export function tapBindObject(obj, ctx){
69 | return mapValues(obj, fn => typeof fn === 'function' ? function(...args){
70 | return ctx.tap(fn.bind(ctx, ...args));
71 | } : fn);
72 | }
73 |
74 | /**
75 | * bind each function in an object to a given context
76 | * @param {object} obj object with functions
77 | * @param {object} ctx context
78 | * @return {object} copy of object obj with each function bound to ctx
79 | */
80 | export function bindObject(obj, ctx){
81 | return mapValues(obj, fn => typeof fn === 'function' ? fn.bind(ctx) : fn);
82 | }
83 |
84 |
85 |
86 |
87 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/docs/image/badge.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | document
13 | document
14 | @ratio@
15 | @ratio@
16 |
17 |
18 |
--------------------------------------------------------------------------------
/docs/image/esdoc-logo-mini-black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/screeny05/corrode/8ef6322ba5b2daee2c90b7f3799a018ed5cf9507/docs/image/esdoc-logo-mini-black.png
--------------------------------------------------------------------------------
/docs/image/esdoc-logo-mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/screeny05/corrode/8ef6322ba5b2daee2c90b7f3799a018ed5cf9507/docs/image/esdoc-logo-mini.png
--------------------------------------------------------------------------------
/docs/image/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/screeny05/corrode/8ef6322ba5b2daee2c90b7f3799a018ed5cf9507/docs/image/github.png
--------------------------------------------------------------------------------
/docs/image/manual-badge.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | manual
13 | manual
14 | @value@
15 | @value@
16 |
17 |
18 |
--------------------------------------------------------------------------------
/docs/image/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/screeny05/corrode/8ef6322ba5b2daee2c90b7f3799a018ed5cf9507/docs/image/search.png
--------------------------------------------------------------------------------
/docs/lint.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/docs/manual/CHANGELOG.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Manual | corrode
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
30 |
31 |
42 |
63 |
75 |
84 |
85 |
86 |
87 | Changelog corrode v1.0.2
88 | Update ESDoc to v0.5.2 for better ES7 support
89 |
90 |
corrode v1.0.1
91 | Update lodash from 4.16.1 to 4.17.2
92 | Update readable-stream from 2.1.5 to 2.2.2
93 | Fix http links in npm-shrinkwrap.json
94 |
95 |
corrode v1.0.0
96 | Initial release. Any Changes prior to this version are merged into this one.
97 |
98 |
99 |
100 |
101 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/docs/script/inherited-summary.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | function toggle(ev) {
3 | var button = ev.target;
4 | var parent = ev.target.parentElement;
5 | while(parent) {
6 | if (parent.tagName === 'TABLE' && parent.classList.contains('summary')) break;
7 | parent = parent.parentElement;
8 | }
9 |
10 | if (!parent) return;
11 |
12 | var tbody = parent.querySelector('tbody');
13 | if (button.classList.contains('opened')) {
14 | button.classList.remove('opened');
15 | button.classList.add('closed');
16 | tbody.style.display = 'none';
17 | } else {
18 | button.classList.remove('closed');
19 | button.classList.add('opened');
20 | tbody.style.display = 'block';
21 | }
22 | }
23 |
24 | var buttons = document.querySelectorAll('.inherited-summary thead .toggle');
25 | for (var i = 0; i < buttons.length; i++) {
26 | buttons[i].addEventListener('click', toggle);
27 | }
28 | })();
29 |
--------------------------------------------------------------------------------
/docs/script/inner-link.js:
--------------------------------------------------------------------------------
1 | // inner link(#foo) can not correctly scroll, because page has fixed header,
2 | // so, I manually scroll.
3 | (function(){
4 | var matched = location.hash.match(/errorLines=([\d,]+)/);
5 | if (matched) return;
6 |
7 | function adjust() {
8 | window.scrollBy(0, -55);
9 | var el = document.querySelector('.inner-link-active');
10 | if (el) el.classList.remove('inner-link-active');
11 |
12 | // ``[ ] . ' " @`` are not valid in DOM id. so must escape these.
13 | var id = location.hash.replace(/([\[\].'"@$])/g, '\\$1');
14 | var el = document.querySelector(id);
15 | if (el) el.classList.add('inner-link-active');
16 | }
17 |
18 | window.addEventListener('hashchange', adjust);
19 |
20 | if (location.hash) {
21 | setTimeout(adjust, 0);
22 | }
23 | })();
24 |
25 | (function(){
26 | var els = document.querySelectorAll('[href^="#"]');
27 | var href = location.href.replace(/#.*$/, ''); // remove existed hash
28 | for (var i = 0; i < els.length; i++) {
29 | var el = els[i];
30 | el.href = href + el.getAttribute('href'); // because el.href is absolute path
31 | }
32 | })();
33 |
--------------------------------------------------------------------------------
/docs/script/manual.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | var matched = location.pathname.match(/\/(manual\/.*\.html)$/);
3 | if (!matched) return;
4 |
5 | var currentName = matched[1];
6 | var cssClass = '.navigation .manual-toc li[data-link="' + currentName + '"]';
7 | var styleText = cssClass + '{ display: block; }\n';
8 | styleText += cssClass + '.indent-h1 a { color: #039BE5 }';
9 | var style = document.createElement('style');
10 | style.textContent = styleText;
11 | document.querySelector('head').appendChild(style);
12 | })();
13 |
--------------------------------------------------------------------------------
/docs/script/patch-for-local.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | if (location.protocol === 'file:') {
3 | var elms = document.querySelectorAll('a[href="./"]');
4 | for (var i = 0; i < elms.length; i++) {
5 | elms[i].href = './index.html';
6 | }
7 | }
8 | })();
9 |
--------------------------------------------------------------------------------
/docs/script/pretty-print.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | prettyPrint();
3 | var lines = document.querySelectorAll('.prettyprint.linenums li[class^="L"]');
4 | for (var i = 0; i < lines.length; i++) {
5 | lines[i].id = 'lineNumber' + (i + 1);
6 | }
7 |
8 | var matched = location.hash.match(/errorLines=([\d,]+)/);
9 | if (matched) {
10 | var lines = matched[1].split(',');
11 | for (var i = 0; i < lines.length; i++) {
12 | var id = '#lineNumber' + lines[i];
13 | var el = document.querySelector(id);
14 | el.classList.add('error-line');
15 | }
16 | return;
17 | }
18 |
19 | if (location.hash) {
20 | // ``[ ] . ' " @`` are not valid in DOM id. so must escape these.
21 | var id = location.hash.replace(/([\[\].'"@$])/g, '\\$1');
22 | var line = document.querySelector(id);
23 | if (line) line.classList.add('active');
24 | }
25 | })();
26 |
--------------------------------------------------------------------------------
/docs/script/search.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | var searchIndex = window.esdocSearchIndex;
3 | var searchBox = document.querySelector('.search-box');
4 | var input = document.querySelector('.search-input');
5 | var result = document.querySelector('.search-result');
6 | var selectedIndex = -1;
7 | var prevText;
8 |
9 | // active search box and focus when mouse enter on search box.
10 | searchBox.addEventListener('mouseenter', function(){
11 | searchBox.classList.add('active');
12 | input.focus();
13 | });
14 |
15 | // search with text when key is upped.
16 | input.addEventListener('keyup', function(ev){
17 | var text = ev.target.value.toLowerCase();
18 | if (!text) {
19 | result.style.display = 'none';
20 | result.innerHTML = '';
21 | return;
22 | }
23 |
24 | if (text === prevText) return;
25 | prevText = text;
26 |
27 | var html = {class: [], method: [], member: [], function: [], variable: [], typedef: [], external: [], file: [], test: [], testFile: []};
28 | var len = searchIndex.length;
29 | var kind;
30 | for (var i = 0; i < len; i++) {
31 | var pair = searchIndex[i];
32 | if (pair[0].indexOf(text) !== -1) {
33 | kind = pair[3];
34 | html[kind].push('' + pair[2] + ' ');
35 | }
36 | }
37 |
38 | var innerHTML = '';
39 | for (kind in html) {
40 | var list = html[kind];
41 | if (!list.length) continue;
42 | innerHTML += '' + kind + ' \n' + list.join('\n');
43 | }
44 | result.innerHTML = innerHTML;
45 | if (innerHTML) result.style.display = 'block';
46 | selectedIndex = -1;
47 | });
48 |
49 | // down, up and enter key are pressed, select search result.
50 | input.addEventListener('keydown', function(ev){
51 | if (ev.keyCode === 40) {
52 | // arrow down
53 | var current = result.children[selectedIndex];
54 | var selected = result.children[selectedIndex + 1];
55 | if (selected && selected.classList.contains('search-separator')) {
56 | var selected = result.children[selectedIndex + 2];
57 | selectedIndex++;
58 | }
59 |
60 | if (selected) {
61 | if (current) current.classList.remove('selected');
62 | selectedIndex++;
63 | selected.classList.add('selected');
64 | }
65 | } else if (ev.keyCode === 38) {
66 | // arrow up
67 | var current = result.children[selectedIndex];
68 | var selected = result.children[selectedIndex - 1];
69 | if (selected && selected.classList.contains('search-separator')) {
70 | var selected = result.children[selectedIndex - 2];
71 | selectedIndex--;
72 | }
73 |
74 | if (selected) {
75 | if (current) current.classList.remove('selected');
76 | selectedIndex--;
77 | selected.classList.add('selected');
78 | }
79 | } else if (ev.keyCode === 13) {
80 | // enter
81 | var current = result.children[selectedIndex];
82 | if (current) {
83 | var link = current.querySelector('a');
84 | if (link) location.href = link.href;
85 | }
86 | } else {
87 | return;
88 | }
89 |
90 | ev.preventDefault();
91 | });
92 |
93 | // select search result when search result is mouse over.
94 | result.addEventListener('mousemove', function(ev){
95 | var current = result.children[selectedIndex];
96 | if (current) current.classList.remove('selected');
97 |
98 | var li = ev.target;
99 | while (li) {
100 | if (li.nodeName === 'LI') break;
101 | li = li.parentElement;
102 | }
103 |
104 | if (li) {
105 | selectedIndex = Array.prototype.indexOf.call(result.children, li);
106 | li.classList.add('selected');
107 | }
108 | });
109 |
110 | // clear search result when body is clicked.
111 | document.body.addEventListener('click', function(ev){
112 | selectedIndex = -1;
113 | result.style.display = 'none';
114 | result.innerHTML = '';
115 | });
116 |
117 | })();
118 |
--------------------------------------------------------------------------------
/docs/script/test-summary.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | function toggle(ev) {
3 | var button = ev.target;
4 | var parent = ev.target.parentElement;
5 | while(parent) {
6 | if (parent.tagName === 'TR' && parent.classList.contains('test-interface')) break;
7 | parent = parent.parentElement;
8 | }
9 |
10 | if (!parent) return;
11 |
12 | var direction;
13 | if (button.classList.contains('opened')) {
14 | button.classList.remove('opened');
15 | button.classList.add('closed');
16 | direction = 'closed';
17 | } else {
18 | button.classList.remove('closed');
19 | button.classList.add('opened');
20 | direction = 'opened';
21 | }
22 |
23 | var targetDepth = parseInt(parent.dataset.testDepth, 10) + 1;
24 | var nextElement = parent.nextElementSibling;
25 | while (nextElement) {
26 | var depth = parseInt(nextElement.dataset.testDepth, 10);
27 | if (depth >= targetDepth) {
28 | if (direction === 'opened') {
29 | if (depth === targetDepth) nextElement.style.display = '';
30 | } else if (direction === 'closed') {
31 | nextElement.style.display = 'none';
32 | var innerButton = nextElement.querySelector('.toggle');
33 | if (innerButton && innerButton.classList.contains('opened')) {
34 | innerButton.classList.remove('opened');
35 | innerButton.classList.add('closed');
36 | }
37 | }
38 | } else {
39 | break;
40 | }
41 | nextElement = nextElement.nextElementSibling;
42 | }
43 | }
44 |
45 | var buttons = document.querySelectorAll('.test-summary tr.test-interface .toggle');
46 | for (var i = 0; i < buttons.length; i++) {
47 | buttons[i].addEventListener('click', toggle);
48 | }
49 |
50 | var topDescribes = document.querySelectorAll('.test-summary tr[data-test-depth="0"]');
51 | for (var i = 0; i < topDescribes.length; i++) {
52 | topDescribes[i].style.display = '';
53 | }
54 | })();
55 |
--------------------------------------------------------------------------------
/docs/test-file/test/base-aborts.test.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | test/base-aborts.test.js | corrode
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
57 |
58 |
59 | test/base-aborts.test.js
60 |
const { expect } = require('chai');
61 | const Base = require('../src/base');
62 |
63 | /** @test {CorrodeBase#jobLoop} */
64 | describe('CorrodeBase - Aborts', () => {
65 | beforeEach(function(){
66 | this.base = new Base();
67 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
68 | });
69 |
70 | it('correctly aborts too short int16', function(done){
71 | this.base.int16('val');
72 | this.eqArray([1], done, {});
73 | });
74 |
75 | it('correctly aborts too short int32', function(done){
76 | this.base.int32('val');
77 | this.eqArray([1, 2, 3], done, {});
78 | });
79 |
80 | it('correctly aborts too short int64', function(done){
81 | this.base.int64('val');
82 | this.eqArray([1, 2, 3, 4, 5, 6, 7], done, {});
83 | });
84 |
85 | it('correctly aborts too short float', function(done){
86 | this.base.float('val');
87 | this.eqArray([1, 2, 3], done, {});
88 | });
89 |
90 | it('correctly aborts too short double', function(done){
91 | this.base.double('val');
92 | this.eqArray([1, 2, 3, 4, 5, 6, 7], done, {});
93 | });
94 |
95 | it('correctly aborts too short string', function(done){
96 | this.base.string('foo', 2);
97 | this.eqArray([1], done, {});
98 | });
99 |
100 | it('correctly aborts too short buffer', function(done){
101 | this.base.buffer('foo', 2);
102 | this.eqArray([1], done, {});
103 | });
104 |
105 | it('correctly aborts too short skip', function(done){
106 | this.base
107 | .uint8('var_1')
108 | .skip(10)
109 | .uint8('var_2');
110 |
111 | this.eqArray([2, 0], done, {
112 | var_1: 2
113 | });
114 | });
115 | });
116 |
117 |
118 |
119 |
120 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/docs/test-file/test/base-skip.test.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | test/base-skip.test.js | corrode
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
57 |
58 |
59 | test/base-skip.test.js
60 |
const { expect } = require('chai');
61 | const Base = require('../src/base');
62 |
63 | /** @test {CorrodeBase#skip} */
64 | describe('CorrodeBase#skip', () => {
65 | beforeEach(function(){
66 | this.base = new Base();
67 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
68 | this.eqMultiArray = require('./helpers/asserts').eqMultiArray.bind(this);
69 | });
70 |
71 | it('allows us to skip content', function(done){
72 | this.base
73 | .uint8('var_1')
74 | .skip(2)
75 | .uint8('var_2')
76 | .skip(4)
77 | .uint8('var_3');
78 |
79 | this.eqArray([1, 0, 0, 2, 0, 0, 0, 0, 3], done, {
80 | var_1: 1,
81 | var_2: 2,
82 | var_3: 3
83 | });
84 | });
85 |
86 | /** @test {CorrodeBase#isSeeking} */
87 | it('prevents us from unskipping content with isSeeking = false', function(done){
88 | this.base
89 | .uint8('var_1')
90 | .skip(2)
91 | .uint8('var_2')
92 | .skip(-3)
93 | .uint8('var_3');
94 |
95 | expect(this.eqMultiArray.bind(this, [[1], [3], [0], [2]], done, {})).to.throw(RangeError);
96 |
97 | done();
98 | });
99 |
100 | /** @test {CorrodeBase#isSeeking} */
101 | it('allows us to unskip content with isSeeking = true', function(done){
102 | this.base.isSeeking = true;
103 |
104 | this.base
105 | .uint8('var_1')
106 | .skip(2)
107 | .uint8('var_2')
108 | .skip(-3)
109 | .uint8('var_3');
110 |
111 | this.eqArray([1, 3, 0, 2], done, {
112 | var_1: 1,
113 | var_2: 2,
114 | var_3: 3
115 | });
116 | });
117 |
118 | it('prevents us from unskipping too far', function(){
119 | this.base
120 | .uint8('var_1')
121 | .skip(-10);
122 |
123 | expect(this.eqArray.bind(this, [1, 2], () => {}, {})).to.throw(RangeError);
124 | });
125 | });
126 |
127 |
128 |
129 |
130 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/docs/test-file/test/corrode-helper.test.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | test/corrode-helper.test.js | corrode
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
57 |
58 |
59 | test/corrode-helper.test.js
60 |
const { expect } = require('chai');
61 | const Corrode = require('../src');
62 |
63 | /** @test {Corrode} */
64 | describe('Corrode - Helpers', () => {
65 | beforeEach(function(){
66 | this.base = new Corrode();
67 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
68 | });
69 |
70 | /**
71 | * coverage fix
72 | * @test {Corrode#debug}
73 | */
74 | it('debugs', function(done){
75 | let output = [];
76 | const orgConsoleLog = console.log;
77 | console.log = (...strings) => output = strings;
78 |
79 | this.base
80 | .loop('array', function(end, discard, i){
81 | this
82 | .uint8('values')
83 | .map.push();
84 | })
85 | .debug();
86 |
87 | this.eqArray([3, 5, 7], function(){
88 | expect(output).to.deep.equal([
89 | '{ array: [ 3, 5, 7 ] }'
90 | ]);
91 | console.log = orgConsoleLog;
92 | done();
93 | }, {
94 | array: [3, 5, 7],
95 | });
96 | });
97 |
98 | /** @test {Corrode#fromBuffer} */
99 | it('converts from buffer', function(){
100 | this.base
101 | .loop('array', function(end, discard, i){
102 | this
103 | .uint8('values')
104 | .map.push();
105 | })
106 | .fromBuffer(Buffer.from([0, 1, 2, 3, 4, 5]), vars => {
107 | expect(vars).to.deep.equal({
108 | array: [0, 1, 2, 3, 4, 5]
109 | });
110 | });
111 | });
112 | });
113 |
114 |
115 |
116 |
117 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/docs/test-file/test/corrode-pointer.test.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | test/corrode-pointer.test.js | corrode
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
57 |
58 |
59 | test/corrode-pointer.test.js
60 |
const { expect } = require('chai');
61 | const Corrode = require('../src');
62 |
63 | /** @test {Corrode#pointer} */
64 | describe('Corrode#pointer', () => {
65 | beforeEach(function(){
66 | this.base = new Corrode();
67 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
68 | });
69 |
70 | it('retrieves from array', function(done){
71 | this.base
72 | .loop('array', function(end, discard, i){
73 | if(i >= 3){
74 | return end();
75 | }
76 | this
77 | .uint8('values')
78 | .map.push();
79 | })
80 | .pointer('alphabet', ['a', 'b', 'c'], 'uint8')
81 | .pointer('numeric', 'array', 'uint8');
82 |
83 | this.eqArray([3, 5, 7, 1, 2], done, {
84 | array: [3, 5, 7],
85 | alphabet: 'b',
86 | numeric: 7
87 | });
88 | });
89 |
90 | it('retrieves from object', function(done){
91 | this.base
92 | .tap('obj', function(){
93 | this.loop(function(end, discard, i){
94 | if(i >= 3){
95 | return end();
96 | }
97 | this.uint8(i);
98 | });
99 | })
100 | .pointer('alphabet', { 0: 'a', 1: 'b', 2: 'c' }, 'uint8')
101 | .pointer('numeric', 'obj', 'uint8');
102 |
103 | this.eqArray([3, 5, 7, 1, 2], done, {
104 | obj: { 0: 3, 1: 5, 2: 7},
105 | alphabet: 'b',
106 | numeric: 7
107 | });
108 | });
109 |
110 | // why not?
111 | it('retrieves from string', function(done){
112 | this.base
113 | .terminatedString('string')
114 | .pointer('numeric', 'string', 'uint8');
115 |
116 | this.eqArray([0x68, 0x65, 0x6c, 0x6c, 0x6f, 0, 4], done, {
117 | string: 'hello',
118 | numeric: 'o'
119 | });
120 | });
121 | });
122 |
123 |
124 |
125 |
126 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/docs/test-file/test/corrode-position.test.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | test/corrode-position.test.js | corrode
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
57 |
58 |
59 | test/corrode-position.test.js
60 |
const { expect } = require('chai');
61 | const Corrode = require('../src');
62 |
63 | /** @test {Corrode#position} */
64 | describe('Corrode#position', () => {
65 | beforeEach(function(){
66 | this.base = new Corrode();
67 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
68 | });
69 |
70 | it('jumps to absolute positions', function(done){
71 | this.base.isSeeking = true;
72 |
73 | this.base
74 | .uint8('var_1')
75 | .position(0)
76 | .uint8('var_2')
77 | .position(3)
78 | .uint8('var_3')
79 | .position('var_3')
80 | .uint8('var_4')
81 |
82 | this.eqArray([0, 1, 2, 5, 4, 3, 7], done, {
83 | var_1: 0,
84 | var_2: 0,
85 | var_3: 5,
86 | var_4: 3
87 | });
88 | });
89 |
90 | it('prevents jumps to invalid negative positions', function(){
91 | this.base.isSeeking = true;
92 |
93 | this.base.position(-1);
94 |
95 | expect(this.eqArray.bind(this, [0], () => {}, {})).to.throw(RangeError);
96 | });
97 |
98 | it('allows jumps to future positive positions', function(done){
99 | this.base.isSeeking = true;
100 |
101 | this.base
102 | .uint8('var_1')
103 | .position(10)
104 | .uint8('var_2');
105 |
106 | this.eqArray([0, 1, 2, 3, 4, 5], done, {
107 | var_1: 0
108 | });
109 | });
110 | });
111 |
112 |
113 |
114 |
115 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/docs/test-file/test/utils.test.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | test/utils.test.js | corrode
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
57 |
58 |
59 | test/utils.test.js
60 |
const expect = require('chai').expect;
61 | const utils = require('../src/utils');
62 |
63 | describe('Utils', () => {
64 | beforeEach(function(){
65 | this.context = {
66 | fixture: 'fixture'
67 | };
68 | });
69 |
70 | /** @test {tapBindObject} */
71 | it('binds object with tap', function(){
72 | let boundFixture = utils.tapBindObject({
73 | fnFixture: function(argOne, argTwo){
74 | expect(argOne).to.equal('foo');
75 | expect(argTwo).to.equal('bar');
76 | }
77 | }, {
78 | tap: function(fn){
79 | return fn;
80 | },
81 | ...this.context
82 | });
83 |
84 | boundFixture.fnFixture('foo', 'bar')();
85 | });
86 |
87 | /** @test {bindObject} */
88 | it('binds object with data', function(){
89 | let obj = {
90 | fnFixture: function(){
91 | expect(this).to.not.be.empty;
92 | expect(this.fixture).to.equal('fixture');
93 | },
94 | fnEmpty: function(){
95 | expect(this).to.be.empty;
96 | }
97 | };
98 |
99 | let boundFixture = utils.bindObject(obj, this.context);
100 | boundFixture.fnFixture();
101 |
102 | let boundObj = utils.bindObject(obj, {});
103 | boundObj.fnEmpty();
104 | });
105 | });
106 |
107 |
108 |
109 |
110 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/esdoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "source": "./src",
3 | "destination": "./docs",
4 | "plugins": [{
5 | "name": "esdoc2-standard-plugin",
6 | "option": {
7 | "manual": {
8 | "files": [
9 | "./docs-md/overview.md",
10 | "./docs-md/getting-started.md",
11 | "./docs-md/configuration.md",
12 | "./CHANGELOG.md"
13 | ]
14 | },
15 | "test": {
16 | "source": "./test"
17 | }
18 | }
19 | }, {
20 | "name": "esdoc2-ecmascript-proposal-plugin",
21 | "option": {
22 | "classProperties": true,
23 | "objectRestSpread": true,
24 | "doExpressions": true,
25 | "functionBind": true,
26 | "functionSent": true,
27 | "asyncGenerators": true,
28 | "decorators": true,
29 | "exportExtensions": true,
30 | "dynamicImport": true
31 | }
32 | }, {
33 | "name": "esdoc2-coverage-plugin"
34 | }]
35 | }
36 |
--------------------------------------------------------------------------------
/examples/id3.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The most harsh, incomplete and unforgiving ID3v2.3-Parser, ever.
3 | *
4 | * includes image-extraction!
5 | * `npm i temp image-to-asci`
6 | * to enable it
7 | */
8 | const Corrode = require('../dist');
9 | const fs = require('fs');
10 | const path = require('path');
11 |
12 | const FILE_PATH = process.argv[process.argv.length - 1];
13 |
14 | const TAG_FLAG_UNSYNCHRONISATION = 0b10000000;
15 | const TAG_FLAG_EXTENDED_HEADER = 0b01000000;
16 | const TAG_FLAG_EXPERIMENTAL = 0b00100000;
17 | const TAG_FLAG_INVALID = 0b00011111;
18 | const TAG_SIZE_INVALID = 0b10000000;
19 | const TAG_SIZE_VALID = 0b01111111;
20 |
21 | const FRAME_FLAG_TAG_ALTER_PRESERVE = 0b1000000000000000;
22 | const FRAME_FLAG_FILE_ALTER_PRESERVE = 0b0100000000000000;
23 | const FRAME_FLAG_READ_ONLY = 0b0010000000000000;
24 | const FRAME_FLAG_COMPRESSION = 0b0000000010000000;
25 | const FRAME_FLAG_ENCRYPTION = 0b0000000001000000;
26 | const FRAME_FLAG_GROUPING_IDENTITY = 0b0000000000100000;
27 | const FRAME_FLAG_INVALID = 0b0001111100011111;
28 |
29 | const FRAME_TYPE_TEXT = /^(T[0-9A-WYZ]{3}|W[0-9A-WYZ]{3}|IPLS)/;
30 | const FRAME_TYPE_PICTURE = /^APIC$/;
31 | const FRAME_TYPE_EXPERIMENTAL = /^[XYZ]/;
32 |
33 | Corrode.addExtension('flagField', function flagField(src, flagsInvalidBitmask, flagsMap){
34 | if((this.vars[src] & flagsInvalidBitmask) > 0){
35 | throw new TypeError('ID3 File contains invalid flag, aborting mission.');
36 | }
37 |
38 | this.map.bitmask('src', flagsMap);
39 | });
40 |
41 | Corrode.addExtension('id3frameHeader', function id3frameHeader(){
42 | this
43 | .string('id', 4)
44 | .uint32be('size')
45 | .uint16('flags')
46 | .assert.bitmask('flags', FRAME_FLAG_INVALID, false)
47 | .map.bitmask('flags', {
48 | tagAlterPreserve: FRAME_FLAG_TAG_ALTER_PRESERVE,
49 | fileAlterPreserve: FRAME_FLAG_FILE_ALTER_PRESERVE,
50 | readOnly: FRAME_FLAG_READ_ONLY,
51 | compression: FRAME_FLAG_COMPRESSION,
52 | encryption: FRAME_FLAG_ENCRYPTION,
53 | groupingIdentity: FRAME_FLAG_GROUPING_IDENTITY
54 | })
55 | .tap(function(){
56 | this.vars.isExperimental = this.vars.id.match(FRAME_TYPE_EXPERIMENTAL) !== null;
57 | });
58 | });
59 |
60 | Corrode.addExtension('id3frameContent', function id3frameContent(header){
61 | if(header.id.match(FRAME_TYPE_TEXT)){
62 | this.string('content', header.size).map.callback('content', val => val.replace(/\u0000/g, ''));
63 | } else if(header.id.match(FRAME_TYPE_PICTURE)){
64 | this.tap('content', function(){
65 | this
66 | .uint8('encoding')
67 | .terminatedString('mimeType')
68 | .uint8('pictureType')
69 | .terminatedString('description')
70 | .tap(function(){
71 | var remainingSize = header.size - this.vars.mimeType.length - this.vars.description.length - 4;
72 | this.buffer('data', remainingSize);
73 | });
74 | });
75 | } else {
76 | this.buffer('content', header.size);
77 | }
78 |
79 | this.map.push('content');
80 | });
81 |
82 | let id3Parser = new Corrode();
83 |
84 | // header
85 | id3Parser
86 | .string('tag', 3)
87 | .assert.equal('tag', 'ID3')
88 | .uint8('version')
89 | .uint8('revision')
90 | .assert.equal('version', 3)
91 | .uint8('flags')
92 | .assert.bitmask('flags', TAG_FLAG_INVALID, false)
93 | .map.bitmask('flags', {
94 | unsynchronisation: TAG_FLAG_UNSYNCHRONISATION,
95 | extendedHeader: TAG_FLAG_EXTENDED_HEADER,
96 | experimental: TAG_FLAG_EXPERIMENTAL
97 | })
98 |
99 | // get tag-size
100 | id3Parser.tap('size', function(){
101 | this
102 | .uint8('size1')
103 | .uint8('size2')
104 | .uint8('size3')
105 | .uint8('size4')
106 | .assert.bitmask('size1', TAG_SIZE_INVALID, false)
107 | .assert.bitmask('size2', TAG_SIZE_INVALID, false)
108 | .assert.bitmask('size3', TAG_SIZE_INVALID, false)
109 | .assert.bitmask('size4', TAG_SIZE_INVALID, false)
110 | .tap(function(){
111 | const size1 = this.vars.size1 & TAG_SIZE_VALID;
112 | const size2 = this.vars.size2 & TAG_SIZE_VALID;
113 | const size3 = this.vars.size3 & TAG_SIZE_VALID;
114 | const size4 = this.vars.size4 & TAG_SIZE_VALID;
115 | this.vars = size4 | (size3 << 7) | (size2 << 14) | (size1 << 22);
116 | });
117 | });
118 |
119 | // extended header
120 | id3Parser.tap(function(){
121 | if(this.vars.flags.extendedHeader){
122 | throw new TypeError('ID3 contains extended header. Not supported, aborting mission.');
123 | }
124 | });
125 |
126 | // frames
127 | id3Parser.loop('frames', function(end, discard, i){
128 | this
129 | .ext.id3frameHeader('header')
130 | .tap(function(){
131 | if(this.vars.header.size === 0){
132 | return end(true);
133 | }
134 |
135 | this.ext.id3frameContent('content', this.vars.header);
136 | });
137 | });
138 |
139 |
140 | // run parser & print data
141 | fs.createReadStream(FILE_PATH)
142 | .pipe(id3Parser)
143 | .on('finish', () => {
144 | let id3info = id3Parser.vars.frames
145 | .map(frame => `${frame.header.id}: ${typeof frame.content === 'string' ? frame.content : 'Buffer'}`)
146 | .join('\n');
147 |
148 | console.log(id3info);
149 |
150 | // try printing images
151 | id3Parser.vars.frames
152 | .filter(frame => typeof frame.content.pictureType !== 'undefined')
153 | .forEach(frame => {
154 | try {
155 | var path = require('temp').path({ suffix: '.jpg' });
156 | require('fs').writeFileSync(path, frame.content.data);
157 | require('image-to-ascii')(path, { bg: true, width: 40 }, (err, conv) => console.log(err || conv));
158 | } catch(e){}
159 | });
160 | });
161 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "corrode",
3 | "version": "1.0.4",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "bl": {
8 | "version": "1.2.1",
9 | "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz",
10 | "integrity": "sha1-ysMo977kVzDUBLaSID/LWQ4XLV4=",
11 | "requires": {
12 | "readable-stream": "2.3.3"
13 | }
14 | },
15 | "core-util-is": {
16 | "version": "1.0.2",
17 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
18 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
19 | },
20 | "inherits": {
21 | "version": "2.0.3",
22 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
23 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
24 | },
25 | "isarray": {
26 | "version": "1.0.0",
27 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
28 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
29 | },
30 | "lodash": {
31 | "version": "4.17.5",
32 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
33 | "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw=="
34 | },
35 | "process-nextick-args": {
36 | "version": "1.0.7",
37 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
38 | "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
39 | },
40 | "readable-stream": {
41 | "version": "2.3.3",
42 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
43 | "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
44 | "requires": {
45 | "core-util-is": "1.0.2",
46 | "inherits": "2.0.3",
47 | "isarray": "1.0.0",
48 | "process-nextick-args": "1.0.7",
49 | "safe-buffer": "5.1.1",
50 | "string_decoder": "1.0.3",
51 | "util-deprecate": "1.0.2"
52 | }
53 | },
54 | "safe-buffer": {
55 | "version": "5.1.1",
56 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
57 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
58 | },
59 | "string_decoder": {
60 | "version": "1.0.3",
61 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
62 | "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
63 | "requires": {
64 | "safe-buffer": "5.1.1"
65 | }
66 | },
67 | "util-deprecate": {
68 | "version": "1.0.2",
69 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
70 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "corrode",
3 | "version": "1.0.6",
4 | "description": "A batteries-included library for reading binary data.",
5 | "main": "dist/index.js",
6 | "scripts": {
7 | "build": "babel src --out-dir dist --source-maps",
8 | "watch": "npm run build -- --watch",
9 | "test": "mocha test --compilers js:babel-register ./test",
10 | "lint": "eslint src",
11 | "docs": "esdoc2 -c esdoc.json",
12 | "coverage": "nyc --require babel-core/register --reporter=lcov mocha",
13 | "coverall": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
14 | "prepublish": "npm run build && npm run docs"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/screeny05/corrode.git"
19 | },
20 | "author": "Sebastian Langer ",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/screeny05/corrode/issues"
24 | },
25 | "homepage": "https://github.com/screeny05/corrode",
26 | "devDependencies": {
27 | "babel-cli": "^6.10.1",
28 | "babel-eslint": "^6.1.0",
29 | "babel-plugin-add-module-exports": "^0.2.1",
30 | "babel-preset-es2015": "^6.9.0",
31 | "babel-preset-stage-0": "^6.5.0",
32 | "babel-register": "^6.9.0",
33 | "buffer-safe": "^1.0.0",
34 | "chai": "^3.5.0",
35 | "coveralls": "^2.11.9",
36 | "esdoc2": "^2.1.3",
37 | "esdoc2-coverage-plugin": "^2.0.0",
38 | "esdoc2-ecmascript-proposal-plugin": "^2.0.0",
39 | "esdoc2-publish-html-plugin": "^2.0.1",
40 | "esdoc2-standard-plugin": "^2.1.0",
41 | "eslint": "^3.0.0",
42 | "eslint-config-xo": "^0.15.2",
43 | "eslint-plugin-babel": "^3.3.0",
44 | "istanbul": "^0.4.4",
45 | "lodash": "^4.13.1",
46 | "mocha": "^2.5.3",
47 | "nyc": "^6.6.1"
48 | },
49 | "dependencies": {
50 | "bl": "^1.1.2",
51 | "lodash": "^4.17.2",
52 | "readable-stream": "^2.2.2"
53 | },
54 | "eslintConfig": {
55 | "extends": "xo/esnext",
56 | "rules": {
57 | "indent": [
58 | "error",
59 | 4
60 | ],
61 | "keyword-spacing": 0,
62 | "space-before-function-paren": [
63 | 2,
64 | "never"
65 | ],
66 | "space-before-blocks": [
67 | 2,
68 | "never"
69 | ]
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/assert/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Asserts help you to make sure, the buffer you're parsing
3 | * is in the correct format. These assertions are like chai, throwing an error
4 | * when an assertion doesn't hold.
5 | *
6 | * These functions won't modify {@link Corrode#vars}.
7 | */
8 |
9 | import lodash from 'lodash';
10 |
11 | /**
12 | * assert strict equal single value
13 | * @param {string} name key of the value to test
14 | * @param {mixed} value comparision
15 | * @throws TypeError assertion-error
16 | */
17 | export function equal(name, value){
18 | if(this.vars[name] !== value){
19 | throw new TypeError(`Expected ${value}, found ${this.vars[name]} at ${name}`);
20 | }
21 | }
22 |
23 | /**
24 | * assert deep equality each value in Object|Array
25 | * @param {string} name key of the object|array to test
26 | * @param {mixed} testValue comparision, undefined for auto-detect
27 | * @throws TypeError assertion-error
28 | */
29 | export function allEqual(name, testValue){
30 | let values = this.vars[name];
31 | if(!Array.isArray(values)){
32 | values = lodash.values(values);
33 | }
34 |
35 | if(typeof testValue === 'undefined'){
36 | testValue = values[0];
37 | }
38 |
39 | const notEqualObjects = values.filter(varValue => varValue !== testValue);
40 | if(notEqualObjects.length !== 0){
41 | throw new TypeError(`Expected values in ${JSON.stringify(this.vars[name])} to all be ${testValue}`);
42 | }
43 | }
44 |
45 | /**
46 | * assert equality objects
47 | * @param {string} name key of the object to test
48 | * @param {object} value comparision
49 | * @throws TypeError assertion-error
50 | */
51 | export function deepEqual(name, value){
52 | const binaryValue = this.vars[name];
53 | if(!lodash.isEqual(binaryValue, value)){
54 | throw new TypeError(`Expected ${JSON.stringify(value)}, found ${JSON.stringify(binaryValue)}`);
55 | }
56 | }
57 |
58 | /**
59 | * assert array|object to contain item
60 | * @param {string} name key of the value to test
61 | * @param {array|object} arr comparision
62 | * @throws TypeError assertion-error
63 | */
64 | export function includes(name, arr){
65 | if(!lodash.includes(arr, this.vars[name])){
66 | throw new TypeError(`Expected ${JSON.stringify(arr)} to include ${this.vars[name]}`);
67 | }
68 | }
69 |
70 | /**
71 | * assert value to be within the bounds of an array
72 | * @param {string} name key of the number to test
73 | * @param {array} value comparision
74 | * @throws TypeError assertion-error
75 | */
76 | export function inBounds(name, value){
77 | const index = this.vars[name];
78 |
79 | if(index < 0 || index >= value.length){
80 | throw new TypeError(`Expected Array of ${value.length} items to be at least ${index} long`);
81 | }
82 | }
83 |
84 | /**
85 | * assert value via callback
86 | * @param {string} name key of the value to test
87 | * @param {function} fn callback
88 | * @param {string} testname optional test-name
89 | * @throws TypeError assertion-error
90 | */
91 | export function callback(name, fn, testname = fn.name){
92 | if(!fn(this.vars[name])){
93 | throw new TypeError(`Callback failed at ${testname}(${this.vars[name]})`);
94 | }
95 | }
96 |
97 | /**
98 | * assert array to be a given length
99 | * @param {string} name key of the value to test
100 | * @param {number|string} length comparision
101 | * @throws TypeError assertion-error
102 | */
103 | export function arrayLength(name, length){
104 | // try to get the length param from the vars if available
105 | if(typeof length === 'string' && this.vars[length] !== 'undefined'){
106 | length = this.vars[length];
107 | }
108 |
109 | if(typeof this.vars[name] === 'undefined' || this.vars[name].length !== length){
110 | throw new TypeError(`Expected array to have a length of ${length}, has ${this.vars[name].length}`);
111 | }
112 | }
113 |
114 | /**
115 | * asserts a variable exists in the first place
116 | * @param {string} name key of the value to test
117 | * @throws TypeError assertion-error
118 | */
119 | export function exists(name){
120 | if(typeof this.vars[name] === 'undefined'){
121 | throw new TypeError(`Expected var ${name} to exist`);
122 | }
123 | }
124 |
125 | /**
126 | * asserts a variable matches a given bitmask
127 | * @param {string} name key of the value to test
128 | * @param {number} mask bitmask to match
129 | * @param {boolean} assertMatch true: should match; false: shouldn't match
130 | * @throws TypeError assertion-error
131 | */
132 | export function bitmask(name, mask, assertMatch = true){
133 | const val = this.vars[name];
134 |
135 | if((val & mask) === mask === !assertMatch){
136 | throw new TypeError(`Expected var ${name} to ${assertMatch ? '' : 'not '}match bitmask (value: 0b${val.toString(2)} assert: 0b${mask.toString(2)})`);
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/map/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * These functions provide basic mapping-abilities for Corrode's VariableStack
3 | * {@link Corrode#vars}
4 | *
5 | * Imagine them like this:
6 | * ```
7 | * const parser = new Corrode();
8 | * parser.uint8('value').map.double('value');
9 | * ```
10 | *
11 | * Of course there's no real mapping-function which doubles a value.
12 | * But the concept is that they are functions receiving a value, processing it
13 | * and saving a new value in the {@link VariableStack} in place of the old one.
14 | *
15 | * The imaginary code above would yield `{ value: 4 }`, parsing a buffer like this `[2]`.
16 | *
17 | * There are two ways to create a mapper. Either by using the {bind} helper-function
18 | * which simply receives a value and returns one, or by defining the function yourself.
19 | *
20 | * The bind-utility only allows for simple functions with no additional parameters.
21 | * Our double-mapper would be a perfect example: `export const double = bind(val => val * 2)`.
22 | * These should be pure functions.
23 | *
24 | * The other way - defining your own mapper-function accepts deals with the {@link VariableStack}
25 | * at {@link Corrode#vars} by itself. This means: reads and writes from {@link Corrode#vars}. Because of that
26 | * they are inherently impure. A next step should be to move all mappers to pure functions.
27 | * (see Issue #28)
28 | *
29 | * Note that all mappers don't check for existance, validity or other assumptions.
30 | * You have to do that yourself with assertions.
31 | */
32 |
33 | /**
34 | * helper function to bind a mapper
35 | * mappers created with this utility accept two parameters:
36 | * name and src, with the src defaulting to name.
37 | * This way, we get a mapper which per-default takes the target as the source
38 | * but also accepts a different source.
39 | * @param {function(val: *)} fn map-function
40 | * @return {function} function ready to use in tap
41 | */
42 | const bind = function(fn){
43 | return function(name, src = name){
44 | this.vars[name] = fn(this.vars[src]);
45 | };
46 | };
47 |
48 | /**
49 | * replace a variable in the stack by a mapped version of itself
50 | * @param {string} name identifier of the variable to map
51 | * @param {function(val: *)} fn map-function
52 | * @example
53 | * parser.uint8('value').map.callback('value', val => (val - 1) * 2)
54 | *
55 | * // [21] => { value: 10 }
56 | */
57 | export function callback(name, fn){
58 | this.vars[name] = fn(this.vars[name]);
59 | }
60 |
61 | /**
62 | * retrieve a value from an accessable type (like array[0] or object['foo'])
63 | * @param {string} name identifier of the variable to map
64 | * @param {array|object|string} accessable accessable variable
65 | * @param {string} [src=name] identifier of the variable in {@link Corrode#vars} by which to access `accessable`
66 | * @example get from array
67 | * parser.uint8('accessor').map.get('accessor', ['A', 'B', 'C', 'D'])
68 | *
69 | * // [2] => { accessor: 'C' }
70 | *
71 | * @example get from object
72 | * parser.terminatedString('accessor').map.get('accessor', { foo: 'A', bar: 'B', qux: 'C' })
73 | *
74 | * // ['q', 'u', 'x', 0x00] => { accessor: 'C' }
75 | */
76 | export function get(name, accessable, src = name){
77 | this.vars[name] = accessable[this.vars[src]];
78 | }
79 |
80 | /**
81 | * retrieve a filtered array of objects from an array of objects, matching a specified attribute against a specified value
82 | * @param {string} name identifier of the variable, to write to {@link Corrode#vars}
83 | * @param {Array} array array, containing the objects to filter
84 | * @param {string} attr identifier of the attribute from an object of `array` to compare against
85 | * @param {string} [src=name] {@link Corrode#vars}-identifier to read from
86 | * @throws {Error} when no object can be found
87 | * @example
88 | * parser.uint8('matchAgainst').map.findAll('matchAgainst', [
89 | * { children: 1, name: 'foo' },
90 | * { children: 2, name: 'bar' },
91 | * { children: 2, name: 'qux' }
92 | * ], 'children')
93 | *
94 | * // [2] => { matchAgainst: [
95 | * // { children: 2, name: 'bar' },
96 | * // { children: 2, name: 'qux' }
97 | * // ]}
98 | *
99 | * // [1] => { matchAgainst: [
100 | * // { children: 1, name: 'foo' }
101 | * // ]}
102 | */
103 | export function findAll(name, array, attr, src = name){
104 | const filtered = array.filter(item => item[attr] === this.vars[src]);
105 | if(filtered.length === 0){
106 | throw new Error(`cannot find object in array with ${attr} === ${src}(${this.vars[src]})`);
107 | }
108 | this.vars[name] = filtered;
109 | }
110 |
111 | /**
112 | * retrieve the first object from an array of objects, matching a specified attribute against a specified value
113 | * like {@link findAll}, but returning only the first element
114 | * @param {string} name identifier of the variable, to write to {@link Corrode#vars}
115 | * @param {Array} array array, containing the objects to filter
116 | * @param {string} attr identifier of the attribute from an object of `array` to compare against
117 | * @param {string} [src=name] {@link Corrode#vars}-identifier to read from
118 | * @throws {Error} when no object can be found
119 | * @example
120 | * parser.uint8('matchAgainst').map.find('matchAgainst', [
121 | * { id: 1, name: 'foo' },
122 | * { id: 7, name: 'bar' },
123 | * { id: 4, name: 'qux' }
124 | * ], 'id')
125 | *
126 | * // [4] => { matchAgainst: { id: 4, name: 'qux' } }
127 | *
128 | * // [2] => Error cannot find object!
129 | */
130 | export function find(name, array, attr, src = name){
131 | findAll.call(this, name, array, attr, src);
132 | this.vars[name] = this.vars[name][0];
133 | }
134 |
135 | /**
136 | * replace {@link Corrode#vars} completely with a value from {@link Corrode#vars}
137 | * especially useful when pushing a variable further up in the stack
138 | *
139 | * @example push loop-variables up
140 | * parser.loop('array', function(){
141 | * this
142 | * .uint8('value')
143 | * .map.double()
144 | * .map.push('value');
145 | * });
146 | *
147 | * // [1, 2, 3, 4] => { array: [2, 4, 6, 8] }
148 | *
149 | * @example push values in an extension
150 | * Corrode.addExtension('doStuff', function(){
151 | * this
152 | * .uint32('address')
153 | * .tap(function(){
154 | * this.vars.address = `0x${this.vars.address.toString(16)}`;
155 | * })
156 | * .map.push('address');
157 | * });
158 | *
159 | * parser.ext.doStuff('hexAddress');
160 | *
161 | * // [245] => { hexAddress: '0xf5' }
162 | *
163 | * @param {string} [name='values'] identifier of the variable being used as replacement
164 | */
165 | export function push(name = 'values'){
166 | this.vars = this.vars[name];
167 | }
168 |
169 | /**
170 | * map a value by checking whether it has some bits set
171 | * @param {string} name identifier of the variable, to write to {@link Corrode#vars}
172 | * @param {Object|number} maskObject Object or number by which to check the bits of the variable to map
173 | * @example map via number
174 | * parser.uint8('bits').map.bitmask('bits', 0x80)
175 | *
176 | * // [0b10111110] => { bits: true }
177 | *
178 | * @example map via object
179 | * parser.uint8('bits').map.bitmask('bits', {
180 | * isCompressed: 0x80,
181 | * isReadOnly: 0x40
182 | * })
183 | *
184 | * // [0b10111110] => { bits: { isCompressed: true, isReadOnly: false } }
185 | */
186 | export function bitmask(name, maskObject){
187 | const bits = this.vars[name];
188 |
189 | // shortcut for single values
190 | if(typeof maskObject === 'number'){
191 | return this.vars[name] = (bits & maskObject) === maskObject;
192 | }
193 |
194 | const values = {};
195 | Object.keys(maskObject).forEach(maskName => {
196 | const mask = maskObject[maskName];
197 | values[maskName] = (bits & mask) === mask;
198 | });
199 | this.vars[name] = values;
200 | }
201 |
202 | /**
203 | * retrieve absolute value of a number
204 | * {@link Math.abs}
205 | * @type {function}
206 | * @example
207 | * this.int8('value').map.abs('value')
208 | *
209 | * // [-14] => { value: 14 }
210 | */
211 | export const abs = bind(Math.abs);
212 |
213 | /**
214 | * retrieve inverted number
215 | * @type {function}
216 | * @example
217 | * this.uint8('value').map.abs('value')
218 | *
219 | * // [27] => { value: -27 }
220 | */
221 | export const invert = bind(val => val * -1);
222 |
223 | /**
224 | * retrieve trimmed string
225 | * @type {function}
226 | * @example
227 | * this.terminatedString('value').map.trim('value')
228 | *
229 | * // [' ', '\t', 'f', 'o', 'b', 'r', '\n'] => { value: 'fobr' }
230 | */
231 | export const trim = bind(str => str.trim());
232 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | import { mapValues } from 'lodash';
2 |
3 | /**
4 | * bind each function in an object with a tap to a given context
5 | * @param {object} obj object with functions
6 | * @param {object} ctx context
7 | * @return {object} copy of object with each function wrapped in a tap
8 | */
9 | export function tapBindObject(obj, ctx){
10 | return mapValues(obj, fn => typeof fn === 'function' ? function(...args){
11 | return ctx.tap(fn.bind(ctx, ...args));
12 | } : fn);
13 | }
14 |
15 | /**
16 | * bind each function in an object to a given context
17 | * @param {object} obj object with functions
18 | * @param {object} ctx context
19 | * @return {object} copy of object obj with each function bound to ctx
20 | */
21 | export function bindObject(obj, ctx){
22 | return mapValues(obj, fn => typeof fn === 'function' ? fn.bind(ctx) : fn);
23 | }
24 |
--------------------------------------------------------------------------------
/src/variable-stack.js:
--------------------------------------------------------------------------------
1 | class VariableStackLayer {
2 | constructor(value = {}, isRoot = false, name = null){
3 | this.value = value;
4 | this.isRoot = isRoot;
5 | this.name = name;
6 | }
7 |
8 | isRoot = false;
9 | value = {};
10 | name = null;
11 | }
12 |
13 | /**
14 | * The VariableStack is a special kind of stack.
15 | * It allows corrode to do black magic like loops in loops and other crazy stuff.
16 | *
17 | * To enable this we define a stack as an object, containing other objects.
18 | * Seen this way it looks more like a TreeStructure which layers you can
19 | * push and pop as you like.
20 | *
21 | * The VariableStack starts as an "empty" object.
22 | * "empty" meaning, that each layer is an object consisting of two/three values:
23 | * * `isRoot` telling the user whether this layer is the uppermost one.
24 | * * `value` holding the value for this layer.
25 | * * `[name]` name of this layer (root-layer won't have one)
26 | *
27 | * Pushing a new layer means adding a new object to the `value`-object of
28 | * the current layer and setting the current layer to our newly created one.
29 | *
30 | * Actually, the object getting added to the `value`-object is not just any object.
31 | * It itself is an object like `{ isRoot: false, value: {} }`.
32 | *
33 | * The root-layer (layer-object where `isRoot === true`) is the lowest layer.
34 | * The current layer is the topmost one.
35 | *
36 | * @example
37 | * +--------------------------+ +-----------------+
38 | * | VariableStack#value: | | | current layer
39 | * | { | | {} |
40 | * | value_1: { foo: 'bar' }| | | isRoot: false
41 | * | } | +-----------------+
42 | * +------------+-------------+ | |
43 | * | | { |
44 | * + | foo: 'bar', | VariableStack
45 | * .push('value_1') | value_2: {} | #peek(1)
46 | * + +----------> | } |
47 | * | | | isRoot: false
48 | * v +----------> +-----------------+
49 | * +-----------+----------+ | |
50 | * |#value: { foo: 'bar' }| | { |
51 | * +-----------+----------+ | value_1: { | VariableStack
52 | * | | foo: 'bar', | #peek(2)
53 | * + | value_2: {} |
54 | * .push('value_2') | } | isRoot: true
55 | * + | } |
56 | * | | |
57 | * v +-----------------+
58 | * +----+-----+
59 | * |#value: {}|
60 | * +----------+
61 | *
62 | */
63 | export default class VariableStack {
64 | constructor(){
65 | this.top = this.stack[0];
66 | }
67 |
68 | /**
69 | * internal storage for the stack
70 | * @access public
71 | * @type {Array}
72 | */
73 | stack = [new VariableStackLayer({}, true)];
74 |
75 | /**
76 | * retrieve the top-layer
77 | * @return {Object} the current layer
78 | */
79 | top = null;
80 |
81 | /**
82 | * retrieve the value of the top.layer
83 | * @return {Object|*} the current value
84 | */
85 | get value(){
86 | return this.top.value;
87 | }
88 |
89 | /**
90 | * set the current value
91 | * this also updates the value in the parent-layer
92 | * @param {Object|*} val the new value
93 | */
94 | set value(val){
95 | if(!this.top.isRoot){
96 | this.peek()[this.top.name] = val;
97 | }
98 | this.top.value = val;
99 | }
100 |
101 | /**
102 | * get a layer below the current one
103 | * @param {number} layerCount how many layers deeper relative from the current
104 | * @return {Object} layer-object
105 | */
106 | peekLayer(layerCount = 1){
107 | if(layerCount > this.stack.length - 1){
108 | throw new ReferenceError(`can't retrieve layer ${layerCount}, stack is ${this.stack.length - 1} layers`);
109 | }
110 | return this.stack[this.stack.length - 1 - layerCount];
111 | }
112 |
113 | /**
114 | * get the value of a layer below the current one
115 | * @param {number} layerCount how many layers deeper relative from the current
116 | * @return {Object|*} value
117 | */
118 | peek(layerCount = 1){
119 | return this.peekLayer(layerCount).value;
120 | }
121 |
122 | /**
123 | * push a value onto the stack
124 | *
125 | * The value doesn't have to be a object, but only objects will properly support child-layers.
126 | * When pushing the new layer the current one will receive a reference to the pushed
127 | * object as a value at the given name.
128 | *
129 | * Note, that if you're pushing a non-object value this reference will not work,
130 | * as only arrays & objects are passed by reference. Instead the value in the
131 | * layer above will be updated, when the current layer's value will be set.
132 | *
133 | * If the value you want to push already exists at the current layer
134 | * VariableStack ignores your value and just re-uses the old one, so no
135 | * layer will be replaced.
136 | *
137 | * @example
138 | * varStack.push('foo');
139 | * varStack.value.bar = 'baz';
140 | * // varStack.value => { bar: 'baz' }
141 | * // varStack.peek() => { foo: { bar: 'baz' } }
142 | *
143 | * @param {string} name name of the new layer
144 | * @param {Object|*} [value={}] value-object of the new layer
145 | */
146 | push(name, value = {}){
147 | if(typeof this.value[name] === 'undefined'){
148 | // only push new value if there's no old one
149 | this.value[name] = value;
150 | } else {
151 | // otherwise re-push the current one
152 | value = this.value[name];
153 | }
154 |
155 | const index = this.stack.push(new VariableStackLayer(value, false, name));
156 | this.top = this.stack[index - 1];
157 | }
158 |
159 | /**
160 | * pop the current layer
161 | * @throws {ReferenceError} thrown if the layer to be popped is the root-layer
162 | */
163 | pop(){
164 | const popLayer = this.top;
165 | if(popLayer.isRoot){
166 | throw new ReferenceError('can\'t pop root layer');
167 | }
168 |
169 | this.stack.pop();
170 |
171 | this.top = this.stack[this.stack.length - 1];
172 |
173 | // reassure that the value in the layer above is right
174 | // (in case of non-object values)
175 | this.value[popLayer.name] = popLayer.value;
176 | }
177 |
178 | /**
179 | * pop all layers until the root-layer is reached
180 | */
181 | popAll(){
182 | while(!this.top.isRoot){
183 | this.pop();
184 | }
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/test/assert.test.js:
--------------------------------------------------------------------------------
1 | const expect = require('chai').expect;
2 | const fixture = require('./fixtures/vars');
3 | const assert = require('../src/assert');
4 | const utils = require('../src/utils');
5 |
6 | describe('Assert', () => {
7 | beforeEach(function(){
8 | this.fixture = fixture.clone();
9 |
10 | this.assert = function(name, ...args){
11 | let assertFn = utils.bindObject(assert, { vars: this.fixture });
12 | return assertFn[name].bind(assertFn, ...args);
13 | };
14 | });
15 |
16 | /** @test {equal} */
17 | it('asserts equal', function(){
18 | expect(this.assert('equal', 'string', 'fixture')).to.not.throw(TypeError);
19 | expect(this.assert('equal', 'string', 'wrong')).to.throw(TypeError);
20 | expect(this.assert('equal', 'number', 1337)).to.not.throw(TypeError);
21 | expect(this.assert('equal', 'number', 0)).to.throw(TypeError);
22 | });
23 |
24 | /** @test {allEqual} */
25 | it('asserts allEqual for objects', function(){
26 | expect(this.assert('allEqual', 'objectWithSameValues', 'fixture')).to.not.throw(TypeError);
27 | expect(this.assert('allEqual', 'objectWithSameValues', 'wrong')).to.throw(TypeError);
28 | expect(this.assert('allEqual', 'objectWithSameValues')).to.not.throw(TypeError);
29 | expect(this.assert('allEqual', 'object', 'wrong')).to.throw(TypeError);
30 | expect(this.assert('allEqual', 'object')).to.throw(TypeError);
31 | });
32 |
33 | /** @test {allEqual} */
34 | it('asserts allEqual for arrays', function(){
35 | expect(this.assert('allEqual', 'arrayWithSameValues', 'fixture')).to.not.throw(TypeError);
36 | expect(this.assert('allEqual', 'arrayWithSameValues', 'wrong')).to.throw(TypeError);
37 | expect(this.assert('allEqual', 'arrayWithSameValues')).to.not.throw(TypeError);
38 | expect(this.assert('allEqual', 'array', 'wrong')).to.throw(TypeError);
39 | expect(this.assert('allEqual', 'array')).to.throw(TypeError);
40 | });
41 |
42 | /** @test {deepEqual} */
43 | it('asserts deepEqual', function(){
44 | expect(this.assert('deepEqual', 'objectWithSameValues', this.fixture.objectWithSameValues)).to.not.throw(TypeError);
45 | expect(this.assert('deepEqual', 'objectWithSameValues', this.fixture.object)).to.throw(TypeError);
46 | expect(this.assert('deepEqual', 'object', this.fixture.object)).to.not.throw(TypeError);
47 | expect(this.assert('deepEqual', 'object', this.fixture.objectWithSameValues)).to.throw(TypeError);
48 | });
49 |
50 | /** @test {includes} */
51 | it('asserts includes for arrays', function(){
52 | expect(this.assert('includes', 'string', ['fixture'])).to.not.throw(TypeError);
53 | expect(this.assert('includes', 'string', ['wrong'])).to.throw(TypeError);
54 | });
55 |
56 | /** @test {includes} */
57 | it('asserts includes for objects', function(){
58 | expect(this.assert('includes', 'string', { child: 'fixture' })).to.not.throw(TypeError);
59 | expect(this.assert('includes', 'string', { child: 'wrong' })).to.throw(TypeError);
60 | });
61 |
62 | /** @test {inBounds} */
63 | it('asserts inBounds', function(){
64 | expect(this.assert('inBounds', 'negative', this.fixture.array)).to.throw(TypeError);
65 | expect(this.assert('inBounds', 'zero', this.fixture.array)).to.not.throw(TypeError);
66 | expect(this.assert('inBounds', 'one', this.fixture.array)).to.not.throw(TypeError);
67 | expect(this.assert('inBounds', 'two', this.fixture.array)).to.not.throw(TypeError);
68 | expect(this.assert('inBounds', 'three', this.fixture.array)).to.throw(TypeError);
69 | });
70 |
71 | /** @test {callback} */
72 | it('asserts via callback', function(){
73 | expect(this.assert('callback', 'string', val => true)).to.not.throw(TypeError);
74 | expect(this.assert('callback', 'string', val => false)).to.throw(TypeError);
75 | expect(this.assert('callback', 'string', val => true, 'custom')).to.not.throw(TypeError);
76 | expect(this.assert('callback', 'string', val => false, 'custom')).to.throw(TypeError);
77 | });
78 |
79 | /** @test {arrayLength} */
80 | it('asserts arrayLength', function(){
81 | expect(this.assert('arrayLength', 'array', 3)).to.not.throw(TypeError);
82 | expect(this.assert('arrayLength', 'array', -1)).to.throw(TypeError);
83 | expect(this.assert('arrayLength', 'array', 2)).to.throw(TypeError);
84 | expect(this.assert('arrayLength', 'array', 4)).to.throw(TypeError);
85 | });
86 |
87 | /** @test {exists} */
88 | it('asserts exists', function(){
89 | expect(this.assert('exists', 'array')).to.not.throw(TypeError);
90 | expect(this.assert('exists', 'string')).to.not.throw(TypeError);
91 | expect(this.assert('exists', 'number')).to.not.throw(TypeError);
92 | expect(this.assert('exists', 'wrong')).to.throw(TypeError);
93 | expect(this.assert('exists', -1)).to.throw(TypeError);
94 | });
95 |
96 | /** @test {bitmask} */
97 | it('asserts bitmask', function(){
98 | expect(this.assert('bitmask', 'bitmask1', this.fixture.bitmask1)).to.not.throw(TypeError);
99 | expect(this.assert('bitmask', 'bitmask1', this.fixture.bitmask2)).to.throw(TypeError);
100 | expect(this.assert('bitmask', 'bitmask2', this.fixture.bitmask1)).to.throw(TypeError);
101 | expect(this.assert('bitmask', 'bitmask2', this.fixture.bitmask2)).to.not.throw(TypeError);
102 | expect(this.assert('bitmask', 'bitmaskMatch', this.fixture.bitmask1)).to.not.throw(TypeError);
103 | expect(this.assert('bitmask', 'bitmaskMatch', this.fixture.bitmask2)).to.throw(TypeError);
104 | expect(this.assert('bitmask', 'bitmask1', this.fixture.bitmask1, false)).to.throw(TypeError);
105 | expect(this.assert('bitmask', 'bitmask1', this.fixture.bitmask2, false)).to.not.throw(TypeError);
106 | expect(this.assert('bitmask', 'bitmask2', this.fixture.bitmask1, false)).to.not.throw(TypeError);
107 | expect(this.assert('bitmask', 'bitmask2', this.fixture.bitmask2, false)).to.throw(TypeError);
108 | expect(this.assert('bitmask', 'bitmaskMatch', this.fixture.bitmask1, false)).to.throw(TypeError);
109 | expect(this.assert('bitmask', 'bitmaskMatch', this.fixture.bitmask2, false)).to.not.throw(TypeError);
110 | });
111 | });
112 |
--------------------------------------------------------------------------------
/test/base-aborts.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 | const Base = require('../src/base');
3 |
4 | /** @test {CorrodeBase#jobLoop} */
5 | describe('CorrodeBase - Aborts', () => {
6 | beforeEach(function(){
7 | this.base = new Base();
8 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
9 | });
10 |
11 | it('correctly aborts too short int16', function(done){
12 | this.base.int16('val');
13 | this.eqArray([1], done, {});
14 | });
15 |
16 | it('correctly aborts too short int32', function(done){
17 | this.base.int32('val');
18 | this.eqArray([1, 2, 3], done, {});
19 | });
20 |
21 | it('correctly aborts too short int64', function(done){
22 | this.base.int64('val');
23 | this.eqArray([1, 2, 3, 4, 5, 6, 7], done, {});
24 | });
25 |
26 | it('correctly aborts too short float', function(done){
27 | this.base.float('val');
28 | this.eqArray([1, 2, 3], done, {});
29 | });
30 |
31 | it('correctly aborts too short double', function(done){
32 | this.base.double('val');
33 | this.eqArray([1, 2, 3, 4, 5, 6, 7], done, {});
34 | });
35 |
36 | it('correctly aborts too short string', function(done){
37 | this.base.string('foo', 2);
38 | this.eqArray([1], done, {});
39 | });
40 |
41 | it('correctly aborts too short buffer', function(done){
42 | this.base.buffer('foo', 2);
43 | this.eqArray([1], done, {});
44 | });
45 |
46 | it('correctly aborts too short skip', function(done){
47 | this.base
48 | .uint8('var_1')
49 | .skip(10)
50 | .uint8('var_2');
51 |
52 | this.eqArray([2, 0], done, {
53 | var_1: 2
54 | });
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/test/base-edge-cases.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 | const Base = require('../src/base');
3 |
4 | /** @test {CorrodeBase} */
5 | describe('CorrodeBase - Edge Cases', () => {
6 | beforeEach(function(){
7 | this.base = new Base();
8 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
9 | this.eqMultiArray = require('./helpers/asserts').eqMultiArray.bind(this);
10 | });
11 |
12 | /** @test {CorrodeBase#jobLoop} */
13 | it('correctly overrides primitves', function(done){
14 | this.base
15 | .uint8('var')
16 | .uint8('var');
17 |
18 | this.eqArray([1, 2, 3], done, {
19 | var: 2
20 | });
21 | });
22 |
23 | /** @test {CorrodeBase#string} */
24 | it('considers strings as length as references to vars - strings', function(done){
25 | this.base
26 | .uint8('length')
27 | .string('string', 'length');
28 |
29 | this.eqArray([2, 0x21, 0x22], done, {
30 | length: 2,
31 | string: '!"'
32 | });
33 | });
34 |
35 | /** @test {CorrodeBase#buffer} */
36 | it('considers strings as length as references to vars - buffers', function(done){
37 | this.base
38 | .uint8('length')
39 | .buffer('buffer', 'length');
40 |
41 | this.eqArray([2, 0x21, 0x22], done, {
42 | length: 2,
43 | buffer: Buffer.from([0x21, 0x22])
44 | });
45 | });
46 |
47 | /** @test {CorrodeBase#skip} */
48 | it('considers strings as length as references to vars - skip', function(done){
49 | this.base
50 | .uint8('length')
51 | .skip('length')
52 | .uint8('var_1')
53 |
54 | this.eqArray([2, 1, 2, 3, 4], done, {
55 | length: 2,
56 | var_1: 3
57 | });
58 | });
59 |
60 | /** @test {CorrodeBase#string} */
61 | it('throws errors for invalid variables when using strings as length as references to vars - strings', function(){
62 | this.base
63 | .uint8('unknown')
64 | .string('string', 'length');
65 |
66 | expect(this.eqArray.bind(this, [2, 0x21, 0x22], () => {}, {})).to.throw(TypeError);
67 | });
68 |
69 | /** @test {CorrodeBase#buffer} */
70 | it('throws errors for invalid variables when using strings as length as references to vars - buffers', function(){
71 | this.base
72 | .uint8('unknown')
73 | .buffer('buffer', 'length');
74 |
75 | expect(this.eqArray.bind(this, [2, 0x21, 0x22], () => {}, {})).to.throw(TypeError);
76 | });
77 |
78 | /** @test {CorrodeBase#skip} */
79 | it('throws errors for invalid variables when using strings as length as references to vars - skip', function(){
80 | this.base
81 | .uint8('unknown')
82 | .skip('length')
83 | .uint8('var_1')
84 |
85 | expect(this.eqArray.bind(this, [2, 1, 2, 3, 4], () => {}, {})).to.throw(TypeError);
86 | });
87 |
88 | /**
89 | * @test {CorrodeBase#chunkOffset}
90 | * @test {CorrodeBase#streamOffset}
91 | */
92 | it('has the correct offset', function(done){
93 | this.base
94 | .uint8('var_1')
95 | .tap(function(){
96 | expect(this.chunkOffset).to.equal(1);
97 | expect(this.streamOffset).to.equal(1);
98 | })
99 | .uint8('var_2')
100 | .uint8('var_3')
101 | .tap(function(){
102 | expect(this.chunkOffset).to.equal(1);
103 | expect(this.streamOffset).to.equal(3);
104 | });
105 |
106 | this.eqMultiArray([[1, 2], [3], [4]], done, {
107 | var_1: 1,
108 | var_2: 2,
109 | var_3: 3
110 | });
111 | });
112 | });
113 |
--------------------------------------------------------------------------------
/test/base-flags.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 | const Base = require('../src/base');
3 |
4 | /** @test {CorrodeBase#options} */
5 | describe('CorrodeBase#options', () => {
6 | beforeEach(function(){
7 | this.base = new Base();
8 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
9 | this.eqMultiArray = require('./helpers/asserts').eqMultiArray.bind(this);
10 | });
11 |
12 | /** @test {CorrodeBase#options.finishJobsOnEOF} */
13 | it('correctly finishes primitive jobs on EOF', function(done){
14 | this.base
15 | .uint8('var_1')
16 | .uint8('var_2')
17 | .uint8('var_3')
18 | .uint8('var_4')
19 | .uint8('var_5');
20 |
21 | this.eqArray([1, 2, 3], done, vars => {
22 | expect(vars).to.deep.equal({
23 | var_1: 1,
24 | var_2: 2,
25 | var_3: 3
26 | });
27 | expect(this.base.jobs).to.be.empty;
28 | });
29 | });
30 |
31 | /** @test {CorrodeBase#options.finishJobsOnEOF} */
32 | it('correctly finishes tap jobs on EOF', function(done){
33 | this.base
34 | .uint8('var_1')
35 | .tap('struct', function(){
36 | this
37 | .uint8('var_2')
38 | .uint8('var_3')
39 | .uint8('var_4');
40 | });
41 |
42 | this.eqArray([1, 2, 3], done, vars => {
43 | expect(vars).to.deep.equal({
44 | var_1: 1,
45 | struct: {
46 | var_2: 2,
47 | var_3: 3
48 | }
49 | });
50 | expect(this.base.jobs).to.be.empty;
51 | });
52 | });
53 |
54 | /** @test {CorrodeBase#options.finishJobsOnEOF} */
55 | it('correctly finishes loop jobs on EOF', function(done){
56 | this.base
57 | .uint8('var_1')
58 | .loop('loop', function(){
59 | this
60 | .uint8('var_2')
61 | .uint8('var_3');
62 | });
63 |
64 | this.eqArray([1, 2, 3, 4, 5, 6], done, vars => {
65 | expect(vars).to.deep.equal({
66 | var_1: 1,
67 | loop: [{
68 | var_2: 2,
69 | var_3: 3
70 | }, {
71 | var_2: 4,
72 | var_3: 5
73 | }, {
74 | var_2: 6
75 | }]
76 | });
77 | expect(this.base.jobs).to.be.empty;
78 | });
79 | });
80 |
81 | /** @test {CorrodeBase#options.finishJobsOnEOF} */
82 | it('correctly finishes nested jobs on EOF', function(done){
83 | this.base
84 | .uint8('var_1')
85 | .loop('loop', function(){
86 | this
87 | .uint8('var_2')
88 | .uint8('var_3')
89 | .tap('innerStruct', function(){
90 | this
91 | .uint8('var_4')
92 | .loop('innerLoop', function(finish, discard, i){
93 | this
94 | .uint8('var_5')
95 | .uint8('var_6');
96 |
97 | if(i >= 1){
98 | finish();
99 | }
100 | });
101 | });
102 | });
103 |
104 | this.eqArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], done, {
105 | var_1: 1,
106 | loop: [{
107 | var_2: 2,
108 | var_3: 3,
109 | innerStruct: {
110 | var_4: 4,
111 | innerLoop: [{
112 | var_5: 5,
113 | var_6: 6
114 | }, {
115 | var_5: 7,
116 | var_6: 8
117 | }]
118 | }
119 | }, {
120 | var_2: 9,
121 | var_3: 10,
122 | innerStruct: {
123 | var_4: 11,
124 | innerLoop: [{
125 | var_5: 12,
126 | var_6: 13
127 | }, {
128 | var_5: 14
129 | }]
130 | }
131 | }]
132 | });
133 | });
134 |
135 | /** @test {CorrodeBase#options.finishJobsOnEOF} */
136 | it('correctly rests in the current state when finishJobsOnEOF is false - tap', function(done){
137 | this.base = new Base({ finishJobsOnEOF: false });
138 |
139 | this.base
140 | .uint8('var_1')
141 | .tap('struct', function(){
142 | this
143 | .uint8('var_2')
144 | .uint8('var_3')
145 | .uint8('var_4');
146 | });
147 |
148 | this.eqArray([1, 2, 3], done, () => {
149 | expect(this.base.varStack.top.isRoot).to.not.be.true;
150 | expect(this.base.varStack.stack.length).to.be.greaterThan(1);
151 | expect(this.base.jobs).to.not.be.empty;
152 | });
153 | });
154 |
155 | /** @test {CorrodeBase#options.finishJobsOnEOF} */
156 | it('correctly rests in the current state when finishJobsOnEOF is false - loop', function(done){
157 | this.base = new Base({ finishJobsOnEOF: false });
158 |
159 | this.base
160 | .uint8('var_1')
161 | .loop('loop', function(){
162 | this
163 | .uint8('var_2')
164 | .uint8('var_3')
165 | .uint8('var_4');
166 | });
167 |
168 | this.eqArray([1, 2, 3], done, () => {
169 | expect(this.base.varStack.peek()[this.base.options.loopIdentifier]).to.exist;
170 | expect(this.base.varStack.top.isRoot).to.not.be.true;
171 | expect(this.base.varStack.stack.length).to.be.greaterThan(1);
172 | expect(this.base.jobs).to.not.be.empty;
173 | });
174 | });
175 |
176 | /** @test {CorrodeBase#options.loopIdentifier} */
177 | it('should not be disturbed, when changing the loopIdentifier', function(done){
178 | this.base = new Base({ loopIdentifier: '__loop' });
179 |
180 | this.base.loop('loop', function(){
181 | this.uint8('var');
182 | });
183 |
184 | this.eqArray([1, 2, 3], done, {
185 | loop: [{
186 | var: 1
187 | }, {
188 | var: 2
189 | }, {
190 | var: 3
191 | }]
192 | });
193 | });
194 |
195 | /** @test {CorrodeBase#isSeeking} */
196 | it('flushes when isSeeking = false', function(done){
197 | this.base.loop('loop', function(){
198 | this.uint8('var');
199 | });
200 |
201 | this.eqMultiArray([[1], [2], [3], [4, 5], [6], [7], [8, 9], [0]], done, () => {
202 | expect(this.base.streamBuffer.length).to.equal(0);
203 | });
204 | });
205 |
206 | /** @test {CorrodeBase#isSeeking} */
207 | it('prevents flushes when isSeeking = true', function(done){
208 | this.base.isSeeking = true;
209 |
210 | this.base.loop('loop', function(){
211 | this.uint8('var');
212 | });
213 |
214 | this.eqMultiArray([[1], [2], [3], [4, 5], [6], [7], [8, 9], [0]], done, () => {
215 | expect(this.base.streamBuffer.length).to.equal(10);
216 | });
217 | });
218 |
219 | /** @test {CorrodeBase#isSeeking} */
220 | it('allows mixing of isSeeking-modes', function(done){
221 | this.base.loop('loop', function(end, discard, i){
222 | this.uint8('var');
223 |
224 | if(i >= 8){
225 | this.isSeeking = true;
226 | }
227 | });
228 |
229 | this.eqMultiArray([[1], [2], [3], [4], [5], [6], [7], [8], [9], [0]], done, () => {
230 | expect(this.base.streamBuffer.length).to.equal(2);
231 | });
232 | });
233 | });
234 |
--------------------------------------------------------------------------------
/test/base-loop-anonymous.test.js:
--------------------------------------------------------------------------------
1 | const expect = require('chai').expect;
2 | const Base = require('../src/base');
3 |
4 | /** @test {CorrodeBase#loop} */
5 | describe('CorrodeBase#loop - anonymous', () => {
6 | beforeEach(function(){
7 | this.base = new Base();
8 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
9 | });
10 |
11 |
12 | it('anonymous loop (overriding)', function(done){
13 | this.base.loop(function(finish, discard, i){
14 | this.uint8('var');
15 | this.vars.iterations = i + 1;
16 | });
17 |
18 | this.eqArray([1, 2, 3, 4, 5], done, {
19 | var: 5,
20 | iterations: 5
21 | });
22 | });
23 |
24 | it('anonymous loop (scope)', function(done){
25 | this.base.loop(function(){
26 | if(typeof this.vars.iterations === 'undefined'){
27 | this.vars.iterations = 0;
28 | }
29 | this.vars.iterations++;
30 |
31 | this.uint8('var');
32 | });
33 |
34 | this.eqArray([1, 2, 3], done, {
35 | iterations: 3,
36 | var: 3
37 | });
38 | });
39 |
40 | it('anonymous loop (no discard, no finish)', function(done){
41 | this.base.loop(function(finish, discard, i){
42 | this.vars['it_' + i] = i;
43 | this.uint8('var_' + i);
44 | });
45 |
46 | this.eqArray([1, 2, 3, 4, 5], done, {
47 | var_0: 1,
48 | var_1: 2,
49 | var_2: 3,
50 | var_3: 4,
51 | var_4: 5,
52 | it_0: 0,
53 | it_1: 1,
54 | it_2: 2,
55 | it_3: 3,
56 | it_4: 4
57 | });
58 | });
59 |
60 | it('anonymous loop (no discard, finish after)', function(done){
61 | this.base.loop(function(finish, discard, i){
62 | this.uint8('var_' + i);
63 | if(i >= 2){
64 | finish();
65 | }
66 | });
67 |
68 | this.eqArray([1, 2, 3, 4, 5], done, {
69 | var_0: 1,
70 | var_1: 2,
71 | var_2: 3
72 | });
73 | });
74 |
75 | it('anonymous loop (no discard, finish before)', function(done){
76 | this.base.loop(function(finish, discard, i){
77 | if(i >= 3){
78 | return finish();
79 | }
80 | this.uint8('var_' + i);
81 | });
82 |
83 | this.eqArray([1, 2, 3, 4, 5], done, {
84 | var_0: 1,
85 | var_1: 2,
86 | var_2: 3
87 | });
88 | });
89 |
90 | it('anonymous loop (discard before, no finish)', function(done){
91 | this.base.loop(function(finish, discard, i){
92 | if(i % 2 !== 0){
93 | discard();
94 | }
95 | this.uint8('var_' + i);
96 | });
97 |
98 | this.eqArray([0, 1, 2, 3, 4, 5, 6], done, {
99 | var_0: 0,
100 | var_2: 2,
101 | var_4: 4,
102 | var_6: 6
103 | });
104 | });
105 |
106 | it('anonymous loop (discard after, no finish)', function(done){
107 | this.base.loop(function(finish, discard, i){
108 | this
109 | .uint8('var_' + i)
110 | .tap(function(){
111 | if(this.vars['var_' + i] % 2 !== 0){
112 | discard();
113 | }
114 | });
115 | if(i % 3 === 0){
116 | discard();
117 | }
118 | });
119 |
120 | this.eqArray([0, 1, 2, 3, 4, 5, 6], done, {
121 | var_2: 2,
122 | var_4: 4
123 | });
124 | });
125 |
126 | /** @test {CorrodeBase#options.anonymousLoopDiscardDeep} */
127 | it('anonymous loop (discard deep, no finish)', function(done){
128 | this.base = new Base({ anonymousLoopDiscardDeep: true });
129 |
130 | this.base.loop(function(finish, discard, i){
131 | if(!this.vars['fix']){
132 | this.vars['fix'] = { iterations: 0, arr: [] };
133 | }
134 | this.vars.fix.iterations++;
135 | this.vars.fix.arr.push(i);
136 |
137 | this.uint8('var_' + i);
138 | if(i % 2 !== 0){
139 | discard();
140 | }
141 | });
142 |
143 | this.eqArray([0, 1, 2, 3, 4, 5, 6, 7], done, {
144 | fix: {
145 | iterations: 4,
146 | arr: [0, 2, 4, 6]
147 | },
148 | var_0: 0,
149 | var_2: 2,
150 | var_4: 4,
151 | var_6: 6
152 | });
153 | });
154 |
155 | /** @test {CorrodeBase#options.anonymousLoopDiscardDeep} */
156 | it('anonymous loop (discard shallow, no finish)', function(done){
157 | this.base.loop(function(finish, discard, i){
158 | if(!this.vars['fix']){
159 | this.vars['fix'] = { iterations: 0, arr: [] };
160 | }
161 | this.vars.fix.iterations++;
162 | this.vars.fix.arr.push(i);
163 |
164 | this.uint8('var_' + i);
165 | if(i % 2 !== 0){
166 | discard();
167 | }
168 | });
169 |
170 | this.eqArray([0, 1, 2, 3, 4, 5, 6, 7], done, {
171 | fix: {
172 | iterations: 8,
173 | arr: [0, 1, 2, 3, 4, 5, 6, 7]
174 | },
175 | var_0: 0,
176 | var_2: 2,
177 | var_4: 4,
178 | var_6: 6
179 | });
180 | });
181 |
182 | it('anonymous loop (discard, finish before)', function(done){
183 | this.base.loop(function(finish, discard, i){
184 | if(i >= 3){
185 | finish(true);
186 | }
187 | this.uint8('var_' + i);
188 | });
189 |
190 | this.eqArray([1, 2, 3, 4, 5], done, {
191 | var_0: 1,
192 | var_1: 2,
193 | var_2: 3
194 | });
195 | });
196 |
197 | it('anonymous loop (discard, finish after)', function(done){
198 | this.base.loop(function(finish, discard, i){
199 | this.uint8('var_' + i);
200 | if(i >= 3){
201 | finish(true);
202 | }
203 | });
204 |
205 | this.eqArray([1, 2, 3, 4, 5], done, {
206 | var_0: 1,
207 | var_1: 2,
208 | var_2: 3
209 | });
210 | });
211 | });
212 |
--------------------------------------------------------------------------------
/test/base-loop-named.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 | const Base = require('../src/base');
3 |
4 | /** @test {CorrodeBase#loop} */
5 | describe('CorrodeBase#loop - named', () => {
6 | beforeEach(function(){
7 | this.base = new Base();
8 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
9 | });
10 |
11 |
12 | it('named loop', function(done){
13 | this.base.loop('fix', function(){
14 | this.uint8('var');
15 | });
16 |
17 | this.eqArray([0, 1, 2], done, {
18 | fix: [{
19 | var: 0
20 | }, {
21 | var: 1
22 | }, {
23 | var: 2
24 | }]
25 | });
26 | });
27 |
28 | it('named loop (scope)', function(done){
29 | this.base
30 | .uint8('rootFix')
31 | .loop('fix', function(){
32 | if(typeof this.vars.fix === 'undefined'){
33 | this.vars.fix = -1;
34 | }
35 | this.vars.fix++;
36 | this.varStack.peek().rootFix++;
37 | this.uint8('var');
38 | });
39 |
40 | this.eqArray([0, 0, 1, 2], done, {
41 | rootFix: 3,
42 | fix: [{
43 | fix: 0,
44 | var: 0
45 | }, {
46 | fix: 0,
47 | var: 1
48 | }, {
49 | fix: 0,
50 | var: 2
51 | }]
52 | });
53 | });
54 |
55 | it('named loop (no discard, finish after)', function(done){
56 | this.base.loop('loop', function(finish, discard, i){
57 | this.uint8('var_' + i);
58 | if(i >= 2){
59 | finish();
60 | }
61 | });
62 |
63 | this.eqArray([0, 1, 2, 3, 4, 5], done, {
64 | loop: [{
65 | var_0: 0
66 | }, {
67 | var_1: 1
68 | }, {
69 | var_2: 2
70 | }]
71 | });
72 | });
73 |
74 | it('named loop (no discard, finish before)', function(done){
75 | this.base.loop('loop', function(finish, discard, i){
76 | if(i >= 3){
77 | return finish();
78 | }
79 | this.uint8('var_' + i);
80 | });
81 |
82 | this.eqArray([0, 1, 2, 3, 4, 5], done, {
83 | loop: [{
84 | var_0: 0
85 | }, {
86 | var_1: 1
87 | }, {
88 | var_2: 2
89 | }]
90 | });
91 | });
92 |
93 | it('named loop (discard before, no finish)', function(done){
94 | this.base.loop('loop', function(finish, discard, i){
95 | if(i % 2 !== 0){
96 | discard();
97 | }
98 | this.uint8('var_' + i);
99 | });
100 |
101 | this.eqArray([0, 1, 2, 3, 4, 5, 6], done, {
102 | loop: [{
103 | var_0: 0
104 | }, {
105 | var_2: 2
106 | }, {
107 | var_4: 4
108 | }, {
109 | var_6: 6
110 | }]
111 | });
112 | });
113 |
114 | it('named loop (discard after, no finish)', function(done){
115 | this.base
116 | .uint8('rootFix')
117 | .loop('loop', function(finish, discard, i){
118 | this
119 | .uint8('var_' + i)
120 | .tap(function(){
121 | if(this.vars['var_' + i] % 2 !== 0){
122 | discard();
123 | }
124 | });
125 | this.varStack.peek().rootFix++;
126 | if(i % 3 === 0){
127 | discard();
128 | }
129 | });
130 |
131 | this.eqArray([0, 0, 1, 2, 3, 4, 5, 6], done, {
132 | rootFix: 7,
133 | loop: [{
134 | var_2: 2
135 | }, {
136 | var_4: 4
137 | }]
138 | });
139 | });
140 |
141 | it('named loop (discard, finish before)', function(done){
142 | this.base.loop('loop', function(finish, discard, i){
143 | if(i >= 3){
144 | finish(true);
145 | }
146 | this.uint8('var_' + i);
147 | });
148 |
149 | this.eqArray([0, 1, 2, 3, 4, 5], done, {
150 | loop: [{
151 | var_0: 0
152 | }, {
153 | var_1: 1
154 | }, {
155 | var_2: 2
156 | }]
157 | });
158 | });
159 |
160 | it('named loop (discard, finish after)', function(done){
161 | this.base.loop('loop', function(finish, discard, i){
162 | this.uint8('var_' + i);
163 | if(i >= 3){
164 | finish(true);
165 | }
166 | });
167 |
168 | this.eqArray([0, 1, 2, 3, 4, 5], done, {
169 | loop: [{
170 | var_0: 0
171 | }, {
172 | var_1: 1
173 | }, {
174 | var_2: 2
175 | }]
176 | });
177 | });
178 |
179 | it('named loops replaced var', function(done){
180 | this.base.loop('loop', function(){
181 | this
182 | .uint8('value')
183 | .tap(function(){
184 | this.vars = this.vars.value;
185 | });
186 | });
187 |
188 | this.eqArray([0, 1, 2, 3, 4], done, {
189 | loop: [0, 1, 2, 3, 4]
190 | });
191 | });
192 | });
193 |
--------------------------------------------------------------------------------
/test/base-primitves.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 | const Base = require('../src/base');
3 |
4 | /** @test {CorrodeBase} */
5 | describe('CorrodeBase - Primitives', () => {
6 | beforeEach(function(){
7 | this.base = new Base();
8 | this.eqFile = require('./helpers/asserts').eqFile.bind(this);
9 | this.eqMultiArray = require('./helpers/asserts').eqMultiArray.bind(this);
10 | });
11 |
12 | /**
13 | * @test {CorrodeBase#int8}
14 | * @test {CorrodeBase#int8le}
15 | * @test {CorrodeBase#int8be}
16 | * @test {CorrodeBase#uint8}
17 | * @test {CorrodeBase#uint8le}
18 | * @test {CorrodeBase#uint8be}
19 | */
20 | it('reads int8', function(done){
21 | this.base
22 | .int8('int8')
23 | .int8le('int8le')
24 | .int8be('int8be')
25 | .uint8('uint8')
26 | .uint8le('uint8le')
27 | .uint8be('uint8be')
28 | .int8('int8n')
29 | .int8le('int8len')
30 | .int8be('int8ben');
31 |
32 | this.eqFile('int8-seq.bin', done, {
33 | int8: 2,
34 | int8le: 4,
35 | int8be: 6,
36 | uint8: 8,
37 | uint8le: 10,
38 | uint8be: 12,
39 | int8n: -14,
40 | int8len: -16,
41 | int8ben: -18
42 | });
43 | });
44 |
45 | /**
46 | * @test {CorrodeBase#int16}
47 | * @test {CorrodeBase#int16le}
48 | * @test {CorrodeBase#int16be}
49 | * @test {CorrodeBase#uint16}
50 | * @test {CorrodeBase#uint16le}
51 | * @test {CorrodeBase#uint16be}
52 | */
53 | it('reads int16', function(done){
54 | this.base
55 | .int16('int16')
56 | .int16le('int16le')
57 | .int16be('int16be')
58 | .uint16('uint16')
59 | .uint16le('uint16le')
60 | .uint16be('uint16be')
61 | .int16('int16n')
62 | .int16le('int16len')
63 | .int16be('int16ben');
64 |
65 | this.eqFile('int16-seq.bin', done, {
66 | int16: 2000,
67 | int16le: 4000,
68 | int16be: 6000,
69 | uint16: 34000,
70 | uint16le: 36000,
71 | uint16be: 38000,
72 | int16n: -30000,
73 | int16len: -31000,
74 | int16ben: -32000
75 | });
76 | });
77 |
78 | /**
79 | * @test {CorrodeBase#int32}
80 | * @test {CorrodeBase#int32le}
81 | * @test {CorrodeBase#int32be}
82 | * @test {CorrodeBase#uint32}
83 | * @test {CorrodeBase#uint32le}
84 | * @test {CorrodeBase#uint32be}
85 | */
86 | it('reads int32', function(done){
87 | this.base
88 | .int32('int32')
89 | .int32le('int32le')
90 | .int32be('int32be')
91 | .uint32('uint32')
92 | .uint32le('uint32le')
93 | .uint32be('uint32be')
94 | .int32('int32n')
95 | .int32le('int32len')
96 | .int32be('int32ben');
97 |
98 | this.eqFile('int32-seq.bin', done, {
99 | int32: 100000,
100 | int32le: 110000,
101 | int32be: 120000,
102 | uint32: 3000000000,
103 | uint32le: 3100000000,
104 | uint32be: 3200000000,
105 | int32n: -1000000000,
106 | int32len: -1100000000,
107 | int32ben: -1200000000
108 | });
109 | });
110 |
111 | /**
112 | * @see http://www.ecma-international.org/ecma-262/5.1/#sec-8.5
113 | * @test {CorrodeBase#int64}
114 | * @test {CorrodeBase#int64le}
115 | * @test {CorrodeBase#int64be}
116 | * @test {CorrodeBase#uint64}
117 | * @test {CorrodeBase#uint64le}
118 | * @test {CorrodeBase#uint64be}
119 | */
120 | it('reads int64', function(done){
121 | this.base
122 | .int64('int64')
123 | .int64le('int64le')
124 | .int64be('int64be')
125 | .uint64('uint64')
126 | .uint64le('uint64le')
127 | .uint64be('uint64be')
128 | .int64('int64n')
129 | .int64le('int64len')
130 | .int64be('int64ben');
131 |
132 | this.eqFile('int64-seq.bin', done, {
133 | int64: 100000,
134 | int64le: 110000,
135 | int64be: 120000,
136 | uint64: 3000000000,
137 | uint64le: 3100000000,
138 | uint64be: 3200000000,
139 | int64n: -1000000000,
140 | int64len: -1100000000,
141 | int64ben: -1200000000
142 | });
143 | });
144 |
145 | /**
146 | * @test {CorrodeBase#float}
147 | * @test {CorrodeBase#floatle}
148 | * @test {CorrodeBase#floatbe}
149 | */
150 | it('reads float', function(done){
151 | this.base
152 | .float('float')
153 | .floatle('floatle')
154 | .floatbe('floatbe')
155 | .floatle('floatlen')
156 | .floatbe('floatben');
157 |
158 | // floats are pretty unprecise
159 | this.eqFile('float-seq.bin', done, vars => {
160 | expect(vars.float).to.be.within(1.233, 1.235);
161 | expect(vars.floatle).to.be.within(5.677, 5.679);
162 | expect(vars.floatbe).to.be.within(9.1010, 9.1012);
163 | expect(vars.floatlen).to.be.within(-12.1315, -12.1313);
164 | expect(vars.floatben).to.be.within(-15.1618, -15.1616);
165 | });
166 | });
167 |
168 | /**
169 | * @test {CorrodeBase#double}
170 | * @test {CorrodeBase#doublele}
171 | * @test {CorrodeBase#doublebe}
172 | */
173 | it('reads double', function(done){
174 | this.base
175 | .double('double')
176 | .doublele('doublele')
177 | .doublebe('doublebe')
178 | .doublele('doublelen')
179 | .doublebe('doubleben');
180 |
181 | this.eqFile('double-seq.bin', done, {
182 | double: 1.234,
183 | doublele: 5.678,
184 | doublebe: 9.1011,
185 | doublelen: -12.1314,
186 | doubleben: -15.1617
187 | });
188 | });
189 |
190 | /** @test {Corrode#string} */
191 | it('reads utf8-strings', function(done){
192 | this.base.string('string', 16, 'utf8');
193 |
194 | this.eqFile('string-utf8.bin', done, {
195 | string: 'asdfghjklyxc𝌆'
196 | });
197 | });
198 |
199 | /** @test {Corrode#string} */
200 | it('reads strings, regardless of the underlying buffer', function(done){
201 | this.base
202 | .string('hi', 4)
203 | .string('lo', 4);
204 |
205 | this.eqMultiArray([[0x61], [0x62], [0x63, 0x64, 0x65], [0x66, 0x67], [0x68, 9, 10]], done, {
206 | hi: 'abcd',
207 | lo: 'efgh'
208 | });
209 | });
210 |
211 | /** @test {Corrode#buffer} */
212 | it('reads buffers', function(done){
213 | this.base
214 | .buffer('hi', 4)
215 | .buffer('lo', 4);
216 |
217 | this.eqFile('int64-seq.bin', done, {
218 | hi: Buffer.from([160, 134, 1, 0]),
219 | lo: Buffer.from([0, 0, 0, 0])
220 | });
221 | });
222 |
223 | /** @test {Corrode#buffer} */
224 | it('reads buffers, regardless of the underlying buffer', function(done){
225 | this.base
226 | .buffer('hi', 4)
227 | .buffer('lo', 4);
228 |
229 | this.eqMultiArray([[1], [2], [3, 4, 5], [6, 7], [8, 9, 10]], done, {
230 | hi: Buffer.from([1, 2, 3, 4]),
231 | lo: Buffer.from([5, 6, 7, 8])
232 | });
233 | });
234 | });
235 |
--------------------------------------------------------------------------------
/test/base-skip.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 | const Base = require('../src/base');
3 |
4 | /** @test {CorrodeBase#skip} */
5 | describe('CorrodeBase#skip', () => {
6 | beforeEach(function(){
7 | this.base = new Base();
8 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
9 | this.eqMultiArray = require('./helpers/asserts').eqMultiArray.bind(this);
10 | });
11 |
12 | it('allows us to skip content', function(done){
13 | this.base
14 | .uint8('var_1')
15 | .skip(2)
16 | .uint8('var_2')
17 | .skip(4)
18 | .uint8('var_3');
19 |
20 | this.eqArray([1, 0, 0, 2, 0, 0, 0, 0, 3], done, {
21 | var_1: 1,
22 | var_2: 2,
23 | var_3: 3
24 | });
25 | });
26 |
27 | /** @test {CorrodeBase#isSeeking} */
28 | it('prevents us from unskipping content with isSeeking = false', function(done){
29 | this.base
30 | .uint8('var_1')
31 | .skip(2)
32 | .uint8('var_2')
33 | .skip(-3)
34 | .uint8('var_3');
35 |
36 | expect(this.eqMultiArray.bind(this, [[1], [3], [0], [2]], done, {})).to.throw(RangeError);
37 |
38 | done();
39 | });
40 |
41 | /** @test {CorrodeBase#isSeeking} */
42 | it('allows us to unskip content with isSeeking = true', function(done){
43 | this.base.isSeeking = true;
44 |
45 | this.base
46 | .uint8('var_1')
47 | .skip(2)
48 | .uint8('var_2')
49 | .skip(-3)
50 | .uint8('var_3');
51 |
52 | this.eqArray([1, 3, 0, 2], done, {
53 | var_1: 1,
54 | var_2: 2,
55 | var_3: 3
56 | });
57 | });
58 |
59 | it('prevents us from unskipping too far', function(){
60 | this.base
61 | .uint8('var_1')
62 | .skip(-10);
63 |
64 | expect(this.eqArray.bind(this, [1, 2], () => {}, {})).to.throw(RangeError);
65 | });
66 | });
67 |
--------------------------------------------------------------------------------
/test/base-stack.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 | const Base = require('../src/base');
3 |
4 | /** @test {CorrodeBase#varStack} */
5 | describe('CorrodeBase#varStack', () => {
6 | beforeEach(function(){
7 | this.base = new Base();
8 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
9 | });
10 |
11 | /** @test {CorrodeBase#push} */
12 | it('pushes and pops', function(done){
13 | this.base
14 | .uint8('val_0')
15 | .push('child')
16 | .uint8('val_1')
17 | .push('child')
18 | .uint8('val_2')
19 | .pop()
20 | .uint8('val_3')
21 | .pop()
22 | .uint8('val_4');
23 |
24 | this.eqArray([0, 1, 2, 3, 4, 5], done, {
25 | val_0: 0,
26 | val_4: 4,
27 | child: {
28 | val_1: 1,
29 | val_3: 3,
30 | child: {
31 | val_2: 2,
32 | }
33 | }
34 | })
35 | });
36 |
37 | /** @test {CorrodeBase#pop} */
38 | it('denies popping the root-layer', function(){
39 | this.base.pop();
40 | expect(this.eqArray.bind(this, [], () => {}, {})).to.throw(ReferenceError);
41 | });
42 |
43 | /**
44 | * @test {CorrodeBase#push}
45 | * @test {CorrodeBase#pop}
46 | */
47 | it('pushes back into old layers', function(done){
48 | this.base
49 | .uint8('val_0')
50 | .push('child')
51 | .uint8('val_1')
52 | .pop()
53 | .uint8('val_2')
54 | .push('child')
55 | .uint8('val_3')
56 | .pop()
57 | .uint8('val_4');
58 |
59 | this.eqArray([0, 1, 2, 3, 4], done, {
60 | val_0: 0,
61 | val_2: 2,
62 | val_4: 4,
63 | child: {
64 | val_1: 1,
65 | val_3: 3
66 | }
67 | });
68 | });
69 |
70 | /**
71 | * @test {CorrodeBase#push}
72 | * @test {CorrodeBase#finishRemainingJobs}
73 | */
74 | it('automatically unpushes as it unwinds', function(done){
75 | this.base
76 | .uint8('var_0')
77 | .push('child')
78 | .uint8('var_1');
79 |
80 | this.eqArray([0, 1, 2], done, {
81 | var_0: 0,
82 | child: {
83 | var_1: 1
84 | }
85 | });
86 | });
87 |
88 | /**
89 | * @test {CorrodeBase#options.strictObjectMode}
90 | */
91 | it('denies pushing into non-object vars', function(){
92 | this.base
93 | .uint8('var_0')
94 | .push('var_0')
95 | .uint8('var_1');
96 |
97 | expect(this.eqArray.bind(this, [0, 1, 2], () => {}, {})).to.throw(TypeError);
98 | });
99 | });
100 |
--------------------------------------------------------------------------------
/test/base-tap.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 | const Base = require('../src/base');
3 |
4 | /** @test {CorrodeBase#tap} */
5 | describe('CorrodeBase#tap', () => {
6 | beforeEach(function(){
7 | this.base = new Base();
8 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
9 | });
10 |
11 | it('taps into the current state', function(done){
12 | this.base
13 | .uint8('var_1')
14 | .tap(function(){
15 | expect(this.vars.var_1).to.equal(1);
16 | });
17 |
18 | this.eqArray([1, 2, 3], done, {
19 | var_1: 1
20 | });
21 | });
22 |
23 | it('taps into a named var', function(done){
24 | this.base
25 | .uint8('var_1')
26 | .tap('struct', function(){
27 | this
28 | .uint8('var_2')
29 | .uint8('var_3');
30 | });
31 |
32 | this.eqArray([1, 2, 3], done, {
33 | var_1: 1,
34 | struct: {
35 | var_2: 2,
36 | var_3: 3
37 | }
38 | });
39 | });
40 |
41 | it('re-taps into existing objects', function(done){
42 | this.base
43 | .uint8('var_1')
44 | .tap('structure', function(){
45 | this
46 | .uint8('var_2')
47 | .uint8('var_3')
48 | })
49 | .uint8('var_4')
50 | .tap('structure', function(){
51 | this
52 | .uint8('var_5')
53 | .uint8('var_6');
54 | });
55 |
56 | this.eqArray([1, 2, 3, 4, 5, 6], done, {
57 | var_1: 1,
58 | var_4: 4,
59 | structure: {
60 | var_2: 2,
61 | var_3: 3,
62 | var_5: 5,
63 | var_6: 6
64 | }
65 | });
66 | });
67 |
68 | it('supports custom arguments in anonymous taps', function(done){
69 | this.base
70 | .uint8('var_1')
71 | .tap(function(subvar_1, subvar_2, subvar_3){
72 | expect(subvar_1).to.be.true;
73 | expect(subvar_2).to.be.false;
74 | expect(subvar_3).to.be.undefined;
75 | this.uint8('var_3');
76 | }, [true, false]);
77 |
78 | this.eqArray([1, 2, 3], done, {
79 | var_1: 1,
80 | var_3: 2
81 | });
82 | });
83 |
84 | it('supports custom arguments in named taps', function(done){
85 | this.base
86 | .uint8('var_1')
87 | .tap('structure', function(subvar_1, subvar_2, subvar_3){
88 | expect(subvar_1).to.be.true;
89 | expect(subvar_2).to.be.false;
90 | expect(subvar_3).to.be.undefined;
91 | this.uint8('var_3');
92 | }, [true, false]);
93 |
94 | this.eqArray([1, 2, 3], done, {
95 | var_1: 1,
96 | structure: {
97 | var_3: 2
98 | }
99 | });
100 | });
101 |
102 | /** @test {CorrodeBase#options.strictObjectMode} */
103 | it('does not allow tapping into other objects', function(){
104 | this.base
105 | .uint8('var_1')
106 | .tap('var_1', function(){
107 | this.uint8('var_2');
108 | })
109 | .uint8('var_3');
110 |
111 | expect(this.eqArray.bind(this, [1, 2, 3], () => {}, {})).to.throw(TypeError);
112 | });
113 |
114 | /** @test {CorrodeBase#options.strictObjectMode} */
115 | it('allows tapping into other objects when strictObjectMode is false', function(done){
116 | this.base = new Base({ strictObjectMode: false });
117 |
118 | this.base
119 | .uint8('var_1')
120 | .tap('var_1', function(){
121 | expect(this.vars).to.be.a.number;
122 | });
123 |
124 | this.eqArray([1, 2, 3], done, {
125 | var_1: 1
126 | });
127 | });
128 | });
129 |
--------------------------------------------------------------------------------
/test/corrode-extensions.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 | const Corrode = require('../src');
3 |
4 | /**
5 | * @test {Corrode#addExtension}
6 | * @test {Corrode#ext}
7 | */
8 | describe('Corrode#ext', () => {
9 | beforeEach(function(){
10 | this.base = new Corrode();
11 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
12 | });
13 |
14 |
15 | Corrode.addExtension('simpleRepeat', function(arg1, arg2){
16 | this
17 | .loop('values', function(end, discard, i){
18 | if(i >= 3){
19 | return end();
20 | }
21 |
22 | this
23 | .uint8(arg1)
24 | .uint8(arg2);
25 | })
26 | .map.push();
27 | });
28 |
29 |
30 | it('supports extensions', function(done){
31 | this.base.ext.simpleRepeat('extObj', 'val_1', 'val_2');
32 |
33 | this.eqArray([1, 2, 3, 4, 5, 6], done, {
34 | extObj: [{
35 | val_1: 1,
36 | val_2: 2
37 | }, {
38 | val_1: 3,
39 | val_2: 4
40 | }, {
41 | val_1: 5,
42 | val_2: 6
43 | }]
44 | });
45 | });
46 |
47 |
48 | Corrode.addExtension('simpleReturn', function(arg1, arg2){
49 | return this.varStack.peek()[arg1] + this.varStack.peek()[arg2];
50 | });
51 |
52 | it('supports extensions with return values and scope-access', function(done){
53 | this.base
54 | .uint8('val_1')
55 | .uint8('val_2')
56 | .ext.simpleReturn('extReturn', 'val_1', 'val_2');
57 |
58 | this.eqArray([2, 3, 4, 5, 6], done, {
59 | val_1: 2,
60 | val_2: 3,
61 | extReturn: 5
62 | });
63 | });
64 |
65 |
66 | Corrode.addExtension('mixReturnReadNonObject', function(){
67 | this.uint8('fix');
68 | return 4; // chosen by fair dice roll
69 | });
70 |
71 | it('supports extensions with return and read', function(){
72 | this.base
73 | .uint8('var_1')
74 | .ext.mixReturnReadNonObject('extObj');
75 |
76 | expect(this.eqArray.bind(this, [1, 2, 3], () => {}, {})).to.throw(TypeError);
77 | });
78 |
79 |
80 | Corrode.addExtension('mixReturnReadObject', function(){
81 | this.uint8('fix');
82 | return {
83 | objVal: 'foo'
84 | };
85 | });
86 |
87 | it('supports extensions with return and read', function(done){
88 | this.base
89 | .uint8('var_1')
90 | .ext.mixReturnReadObject('extObj');
91 |
92 | this.eqArray([1, 2, 3], done, {
93 | var_1: 1,
94 | extObj: {
95 | fix: 2,
96 | objVal: 'foo'
97 | }
98 | });
99 | });
100 |
101 |
102 | Corrode.addExtension('simpleRead', function(arg1, arg2){
103 | this
104 | .uint8(arg1)
105 | .uint8(arg2)
106 | .ext.simpleReturn('added', arg1, arg2);
107 | });
108 |
109 | Corrode.addExtension('callProxy', function(extName, ...args){
110 | this
111 | .ext[extName]('values', ...args)
112 | .map.push();
113 | });
114 |
115 | it('supports calling extensions within extensions', function(done){
116 | this.base
117 | .uint8('var_1')
118 | .ext.callProxy('proxyVal1', 'simpleRead', 'val_2', 'val_3')
119 | .ext.callProxy('proxyVal2', 'simpleRead', 'val_4', 'val_5');
120 |
121 | this.eqArray([1, 2, 3, 4, 5, 6], done, {
122 | var_1: 1,
123 | proxyVal1: {
124 | val_2: 2,
125 | val_3: 3,
126 | added: 5
127 | },
128 | proxyVal2: {
129 | val_4: 4,
130 | val_5: 5,
131 | added: 9
132 | }
133 | });
134 | });
135 | });
136 |
--------------------------------------------------------------------------------
/test/corrode-helper.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 | const Corrode = require('../src');
3 |
4 | /** @test {Corrode} */
5 | describe('Corrode - Helpers', () => {
6 | beforeEach(function(){
7 | this.base = new Corrode();
8 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
9 | });
10 |
11 | /**
12 | * coverage fix
13 | * @test {Corrode#debug}
14 | */
15 | it('debugs', function(done){
16 | let output = [];
17 | const orgConsoleLog = console.log;
18 | console.log = (...strings) => output = strings;
19 |
20 | this.base
21 | .loop('array', function(end, discard, i){
22 | this
23 | .uint8('values')
24 | .map.push();
25 | })
26 | .debug();
27 |
28 | this.eqArray([3, 5, 7], function(){
29 | expect(output).to.deep.equal([
30 | '{ array: [ 3, 5, 7 ] }'
31 | ]);
32 | console.log = orgConsoleLog;
33 | done();
34 | }, {
35 | array: [3, 5, 7],
36 | });
37 | });
38 |
39 | /** @test {Corrode#fromBuffer} */
40 | it('converts from buffer', function(){
41 | this.base
42 | .loop('array', function(end, discard, i){
43 | this
44 | .uint8('values')
45 | .map.push();
46 | })
47 | .fromBuffer(Buffer.from([0, 1, 2, 3, 4, 5]), vars => {
48 | expect(vars).to.deep.equal({
49 | array: [0, 1, 2, 3, 4, 5]
50 | });
51 | });
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/test/corrode-pointer.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 | const Corrode = require('../src');
3 |
4 | /** @test {Corrode#pointer} */
5 | describe('Corrode#pointer', () => {
6 | beforeEach(function(){
7 | this.base = new Corrode();
8 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
9 | });
10 |
11 | it('retrieves from array', function(done){
12 | this.base
13 | .loop('array', function(end, discard, i){
14 | if(i >= 3){
15 | return end();
16 | }
17 | this
18 | .uint8('values')
19 | .map.push();
20 | })
21 | .pointer('alphabet', ['a', 'b', 'c'], 'uint8')
22 | .pointer('numeric', 'array', 'uint8');
23 |
24 | this.eqArray([3, 5, 7, 1, 2], done, {
25 | array: [3, 5, 7],
26 | alphabet: 'b',
27 | numeric: 7
28 | });
29 | });
30 |
31 | it('retrieves from object', function(done){
32 | this.base
33 | .tap('obj', function(){
34 | this.loop(function(end, discard, i){
35 | if(i >= 3){
36 | return end();
37 | }
38 | this.uint8(i);
39 | });
40 | })
41 | .pointer('alphabet', { 0: 'a', 1: 'b', 2: 'c' }, 'uint8')
42 | .pointer('numeric', 'obj', 'uint8');
43 |
44 | this.eqArray([3, 5, 7, 1, 2], done, {
45 | obj: { 0: 3, 1: 5, 2: 7},
46 | alphabet: 'b',
47 | numeric: 7
48 | });
49 | });
50 |
51 | // why not?
52 | it('retrieves from string', function(done){
53 | this.base
54 | .terminatedString('string')
55 | .pointer('numeric', 'string', 'uint8');
56 |
57 | this.eqArray([0x68, 0x65, 0x6c, 0x6c, 0x6f, 0, 4], done, {
58 | string: 'hello',
59 | numeric: 'o'
60 | });
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/test/corrode-position.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 | const Corrode = require('../src');
3 |
4 | /** @test {Corrode#position} */
5 | describe('Corrode#position', () => {
6 | beforeEach(function(){
7 | this.base = new Corrode();
8 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
9 | });
10 |
11 | it('jumps to absolute positions', function(done){
12 | this.base.isSeeking = true;
13 |
14 | this.base
15 | .uint8('var_1')
16 | .position(0)
17 | .uint8('var_2')
18 | .position(3)
19 | .uint8('var_3')
20 | .position('var_3')
21 | .uint8('var_4')
22 |
23 | this.eqArray([0, 1, 2, 5, 4, 3, 7], done, {
24 | var_1: 0,
25 | var_2: 0,
26 | var_3: 5,
27 | var_4: 3
28 | });
29 | });
30 |
31 | it('prevents jumps to invalid negative positions', function(){
32 | this.base.isSeeking = true;
33 |
34 | this.base.position(-1);
35 |
36 | expect(this.eqArray.bind(this, [0], () => {}, {})).to.throw(RangeError);
37 | });
38 |
39 | it('allows jumps to future positive positions', function(done){
40 | this.base.isSeeking = true;
41 |
42 | this.base
43 | .uint8('var_1')
44 | .position(10)
45 | .uint8('var_2');
46 |
47 | this.eqArray([0, 1, 2, 3, 4, 5], done, {
48 | var_1: 0
49 | });
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/test/corrode-terminated-buffer.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 | const Corrode = require('../src');
3 |
4 | /** @test {Corrode#terminatedBuffer} */
5 | describe('Corrode#terminatedBuffer', () => {
6 | beforeEach(function(){
7 | this.base = new Corrode();
8 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
9 | this.eqMultiArray = require('./helpers/asserts').eqMultiArray.bind(this);
10 | });
11 |
12 | it('retrieves a terminated buffer', function(done){
13 | this.base
14 | .terminatedBuffer('buffer_1')
15 | .terminatedBuffer('buffer_2');
16 |
17 | this.eqArray([3, 5, 7, 0, 9, 1, 2, 4, 0, 6, 8], done, {
18 | buffer_1: Buffer.from([3, 5, 7]),
19 | buffer_2: Buffer.from([9, 1, 2, 4])
20 | });
21 | });
22 |
23 | it('retrieves a terminated buffer spanned over multiple buffers', function(done){
24 | this.base
25 | .terminatedBuffer('buffer_1')
26 | .terminatedBuffer('buffer_2');
27 |
28 | this.eqMultiArray([[2, 4, 6], [8], [0], [1, 3], [5, 7], [0, 0]], done, {
29 | buffer_1: Buffer.from([2, 4, 6, 8]),
30 | buffer_2: Buffer.from([1, 3, 5, 7])
31 | });
32 | });
33 |
34 | it('retrieves a terminated buffer spanned over multiple buffers with options', function(done){
35 | this.base
36 | .terminatedBuffer('buffer_1', 8, false)
37 | .terminatedBuffer('buffer_2', 3)
38 | .uint8('terminator')
39 | .terminatedBuffer('buffer_3', 'terminator');
40 |
41 | this.eqMultiArray([[2, 4, 6], [8], [0], [1, 3], [5, 7], [0, 0, 2, 5]], done, {
42 | buffer_1: Buffer.from([2, 4, 6, 8]),
43 | buffer_2: Buffer.from([0, 1]),
44 | terminator: 5,
45 | buffer_3: Buffer.from([7, 0, 0, 2])
46 | });
47 | });
48 |
49 | it('retrieves a terminated buffer in a complex parser', function(done){
50 | this.base
51 | .uint8('terminator_1')
52 | .loop('buffers', function(end, discard, i){
53 | if(i >= 3){
54 | return end();
55 | }
56 |
57 | this
58 | .uint8('prefix')
59 | .terminatedBuffer('buffer', this.varStack.peek().terminator_1)
60 | })
61 | .terminatedBuffer('buffer_1', 'terminator_1')
62 | .terminatedBuffer('buffer_2', 3);
63 |
64 | this.eqMultiArray([[2, 7, 4, 6], [8, 2], [7, 0], [1, 2, 7, 3], [5, 7, 2], [0, 0, 2, 5, 6, 3]], done, {
65 | terminator_1: 2,
66 | buffers: [{
67 | prefix: 7,
68 | buffer: Buffer.from([4, 6, 8])
69 | }, {
70 | prefix: 7,
71 | buffer: Buffer.from([0, 1])
72 | }, {
73 | prefix: 7,
74 | buffer: Buffer.from([3, 5, 7])
75 | }],
76 | buffer_1: Buffer.from([0, 0]),
77 | buffer_2: Buffer.from([5, 6])
78 | });
79 | });
80 | });
81 |
--------------------------------------------------------------------------------
/test/corrode-terminated-string.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 | const Corrode = require('../src');
3 |
4 | /** @test {Corrode#terminatedString} */
5 | describe('Corrode#terminatedString', () => {
6 | beforeEach(function(){
7 | this.base = new Corrode();
8 | this.eqArray = require('./helpers/asserts').eqArray.bind(this);
9 | this.eqMultiArray = require('./helpers/asserts').eqMultiArray.bind(this);
10 | });
11 |
12 | it('retrieves a terminated string', function(done){
13 | this.base
14 | .terminatedString('string_1')
15 | .terminatedString('string_2')
16 | .terminatedString('string_3');
17 |
18 | this.eqArray([0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00, 0x6f, 0x6f], done, {
19 | string_1: 'hello',
20 | string_2: ', world',
21 | string_3: ''
22 | });
23 | });
24 |
25 | it('retrieves a terminated string spanned over multiple buffers', function(done){
26 | this.base
27 | .terminatedString('string_1')
28 | .terminatedString('string_2')
29 | .terminatedString('string_3');
30 |
31 | this.eqMultiArray([[0x68, 0x65], [0x6c], [0x6c, 0x6f, 0x00], [0x2c], [0x20, 0x77], [0x6f, 0x72], [0x6c], [0x64], [0x00, 0x6f, 0x6f]], done, {
32 | string_1: 'hello',
33 | string_2: ', world',
34 | string_3: ''
35 | });
36 | });
37 |
38 | it('retrieves a terminated string with options', function(done){
39 | this.base
40 | .terminatedString('string_1', 0x2c)
41 | .terminatedString('string_2', 0x64, false)
42 | .terminatedString('string_3');
43 |
44 | this.eqMultiArray([[0x68, 0x65], [0x6c], [0x6c, 0x6f], [0x2c], [0x20, 0xe2, 0x9a, 0xa1, 0x20, 0x77], [0x6f, 0x72], [0x6c], [0x64], [0x00, 0x6f, 0x6f]], done, {
45 | string_1: 'hello',
46 | string_2: ' ⚡ world',
47 | string_3: ''
48 | });
49 | });
50 |
51 | it('accepts custom encoding', function(done){
52 | this.base.terminatedString('string', 0, true, 'ascii');
53 |
54 | this.eqArray([0xe2, 0x9a, 0xa1, 0x00], done, {
55 | string: 'b\u001a!'
56 | });
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/test/fixtures/double-seq.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/screeny05/corrode/8ef6322ba5b2daee2c90b7f3799a018ed5cf9507/test/fixtures/double-seq.bin
--------------------------------------------------------------------------------
/test/fixtures/float-seq.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/screeny05/corrode/8ef6322ba5b2daee2c90b7f3799a018ed5cf9507/test/fixtures/float-seq.bin
--------------------------------------------------------------------------------
/test/fixtures/int16-seq.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/screeny05/corrode/8ef6322ba5b2daee2c90b7f3799a018ed5cf9507/test/fixtures/int16-seq.bin
--------------------------------------------------------------------------------
/test/fixtures/int32-seq.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/screeny05/corrode/8ef6322ba5b2daee2c90b7f3799a018ed5cf9507/test/fixtures/int32-seq.bin
--------------------------------------------------------------------------------
/test/fixtures/int64-seq.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/screeny05/corrode/8ef6322ba5b2daee2c90b7f3799a018ed5cf9507/test/fixtures/int64-seq.bin
--------------------------------------------------------------------------------
/test/fixtures/int8-seq.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/screeny05/corrode/8ef6322ba5b2daee2c90b7f3799a018ed5cf9507/test/fixtures/int8-seq.bin
--------------------------------------------------------------------------------
/test/fixtures/string-utf8.bin:
--------------------------------------------------------------------------------
1 | asdfghjklyxc𝌆
2 |
--------------------------------------------------------------------------------
/test/fixtures/vars.js:
--------------------------------------------------------------------------------
1 | const getFixture = () => ({
2 | string: 'fixture',
3 | string2: 'fixture',
4 | number: 1337,
5 | object: {
6 | fixture: 'string',
7 | has: 'to',
8 | deep: ['equal', 'this', 1337],
9 | me: 1337
10 | },
11 | objectWithSameValues: {
12 | val1: 'fixture',
13 | val2: 'fixture',
14 | val3: 'fixture',
15 | val4: 'fixture',
16 | },
17 | arrayWithSameValues: ['fixture', 'fixture', 'fixture', 'fixture'],
18 | array: ['fixture', { object: 'foo' }, 1337],
19 | objectArray: [{ id: 1, name: 'foobar' }, { id: 2, name: 'lorem' }, { id: 2, name: 'ipsum' }, { id: 4, name: 'quxbaz' }],
20 | negative: -1,
21 | zero: 0,
22 | one: 1,
23 | two: 2,
24 | three: 3,
25 | id: 4,
26 | untrimmed: '\r\nfoobar ',
27 | trimmed: 'foobar',
28 | bitmask1: 0b10000000,
29 | bitmask2: 0b01000000,
30 | bitmaskMatch: 0b10111110
31 | });
32 |
33 | module.exports = getFixture();
34 | module.exports.clone = getFixture;
35 |
--------------------------------------------------------------------------------
/test/helpers/asserts.js:
--------------------------------------------------------------------------------
1 | require('buffer-safe');
2 | const fs = require('fs');
3 | const { expect } = require('chai');
4 |
5 | /**
6 | * parse a file and compare the result against a provided fixture
7 | * @param {string} file file-path
8 | * @param {Function} done mocha-callback
9 | * @param {object} obj expected result
10 | * @throws {Error} assertion error
11 | */
12 | module.exports.eqFile = function(file, done, obj){
13 | fs.createReadStream(__dirname + '/../fixtures/' + file)
14 | .pipe(this.base)
15 | .on('finish', () => {
16 | if(typeof obj === 'function'){
17 | obj.call(this, this.base.vars);
18 | } else {
19 | expect(this.base.vars).to.deep.equal(obj);
20 | }
21 | done();
22 | });
23 | };
24 |
25 | /**
26 | * parse an array and compare the result against a provided fixture
27 | * @param {Array} arr array getting converted to buffer and fed to corrode
28 | * @param {Function} done mocha-callback
29 | * @param {object} obj expected result
30 | * @throws {Error} assertion error
31 | */
32 | module.exports.eqArray = function(arr, done, obj){
33 | let arrMiddle = Math.floor(arr.length / 2);
34 | let arrFirst = arr.slice(0, arrMiddle);
35 | let arrSecond = arr.slice(arrMiddle);
36 | let arrData = [arrFirst, arrSecond];
37 |
38 | // uncomment this line to create a new buffer for each byte (may be overkill)
39 | //arrData = arr.map(val => [val]);
40 | module.exports.eqMultiArray.call(this, arrData, done, obj);
41 | };
42 |
43 |
44 | /**
45 | * parse an array of arrays and compare the result against a provided fixture
46 | * @param {Array} arr array getting converted to buffer and fed to corrode
47 | * @param {Function} done mocha-callback
48 | * @param {object} obj expected result
49 | * @throws {Error} assertion error
50 | */
51 | module.exports.eqMultiArray = function(arrs, done, obj){
52 | // i sometimes forget to do this
53 | if(typeof done !== 'function'){
54 | throw new Error('done not given');
55 | }
56 |
57 | arrs.forEach(arr => this.base.write(Buffer.from(arr)));
58 | this.base.end();
59 | this.base.on('finish', () => {
60 | if(typeof obj === 'function'){
61 | obj(this.base.vars);
62 | return done();
63 | }
64 | expect(this.base.vars).to.deep.equal(obj);
65 | done();
66 | });
67 | };
68 |
--------------------------------------------------------------------------------
/test/map.test.js:
--------------------------------------------------------------------------------
1 | const expect = require('chai').expect;
2 | const fixture = require('./fixtures/vars');
3 | const map = require('../src/map');
4 |
5 | describe('Map', () => {
6 | beforeEach(function(){
7 | this.fixture = fixture.clone();
8 |
9 | this.map = function(fn, on, ...args){
10 | map[fn].call({ vars: this.fixture }, on, ...args);
11 | return this.fixture[on];
12 | };
13 | });
14 |
15 | /** @test {map} */
16 | it('maps via callback', function(){
17 | expect(this.map('callback', 'number', val => val * 2)).to.equal(fixture.number * 2);
18 | });
19 |
20 | /** @test {get} */
21 | it('maps via get - array', function(){
22 | expect(this.map('get', 'zero', ['fixture'])).to.equal('fixture');
23 | expect(this.map('get', 'one', [0, {}])).to.be.object;
24 | expect(this.map('get', 'two', [0, 0, {}])).to.be.empty;
25 | });
26 |
27 | /** @test {get} */
28 | it('maps via get - object', function(){
29 | expect(this.map('get', 'string', { fixture: 'string' })).to.equal('string');
30 | expect(this.map('get', 'string2', { fixture: {} })).to.be.object;
31 | });
32 |
33 | /** @test {findAll} */
34 | it('maps via findAll', function(){
35 | expect(this.map('findAll', 'one', fixture.objectArray, 'id')).to.deep.equal([fixture.objectArray[0]]);
36 | expect(this.map('findAll', 'two', fixture.objectArray, 'id')).to.have.length.of(2);
37 | expect(this.map('findAll', 'id', fixture.objectArray, 'id')[0].name).to.equal('quxbaz');
38 | expect(this.map.bind(this, 'findAll', 'three', fixture.objectArray, 'id')).to.throw(Error);
39 | });
40 |
41 | /** @test {find} */
42 | it('maps via find', function(){
43 | expect(this.map('find', 'one', fixture.objectArray, 'id')).to.deep.equal(fixture.objectArray[0]);
44 | expect(this.map('find', 'id', fixture.objectArray, 'id').name).to.equal('quxbaz');
45 | expect(this.map.bind(this, 'find', 'three', fixture.objectArray, 'id')).to.throw(Error);
46 | });
47 |
48 | /** @test {abs} */
49 | it('maps via abs', function(){
50 | expect(this.map('abs', 'negative')).to.equal(1);
51 | expect(this.map('abs', 'two')).to.equal(2);
52 | });
53 |
54 | /** @test {invert} */
55 | it('maps via invert', function(){
56 | expect(this.map('invert', 'negative')).to.equal(1);
57 | expect(this.map('invert', 'three')).to.equal(-3);
58 | });
59 |
60 | /** @test {trim} */
61 | it('maps via trim', function(){
62 | expect(this.map('trim', 'untrimmed')).to.equal(fixture.trimmed);
63 | expect(this.map('trim', 'trimmed')).to.equal(fixture.trimmed);
64 | });
65 |
66 | /** @test {push} */
67 | it('pushes', function(){
68 | let that = { vars: fixture.clone() };
69 | map.push.call(that, 'object');
70 | expect(that.vars).to.deep.equal(fixture.object);
71 | });
72 |
73 | /** @test {bitmask} */
74 | it('maps via bitmask', function(){
75 | expect(this.map('bitmask', 'bitmaskMatch', {
76 | isX80: 0x80,
77 | isX40: 0x40,
78 | isX20: 0x20,
79 | isX10: 0x10,
80 | isX08: 0x08,
81 | isX04: 0x04,
82 | isX02: 0x02,
83 | isX01: 0x01
84 | })).to.deep.equal({
85 | isX80: true,
86 | isX40: false,
87 | isX20: true,
88 | isX10: true,
89 | isX08: true,
90 | isX04: true,
91 | isX02: true,
92 | isX01: false
93 | });
94 | });
95 |
96 | /** @test {bitmask} */
97 | it('maps single values via bitmask', function(){
98 | expect(this.map('bitmask', 'bitmaskMatch', 0x80)).to.be.true;
99 | });
100 | });
101 |
--------------------------------------------------------------------------------
/test/utils.test.js:
--------------------------------------------------------------------------------
1 | const expect = require('chai').expect;
2 | const utils = require('../src/utils');
3 |
4 | describe('Utils', () => {
5 | beforeEach(function(){
6 | this.context = {
7 | fixture: 'fixture'
8 | };
9 | });
10 |
11 | /** @test {tapBindObject} */
12 | it('binds object with tap', function(){
13 | let boundFixture = utils.tapBindObject({
14 | fnFixture: function(argOne, argTwo){
15 | expect(argOne).to.equal('foo');
16 | expect(argTwo).to.equal('bar');
17 | }
18 | }, {
19 | tap: function(fn){
20 | return fn;
21 | },
22 | ...this.context
23 | });
24 |
25 | boundFixture.fnFixture('foo', 'bar')();
26 | });
27 |
28 | /** @test {bindObject} */
29 | it('binds object with data', function(){
30 | let obj = {
31 | fnFixture: function(){
32 | expect(this).to.not.be.empty;
33 | expect(this.fixture).to.equal('fixture');
34 | },
35 | fnEmpty: function(){
36 | expect(this).to.be.empty;
37 | }
38 | };
39 |
40 | let boundFixture = utils.bindObject(obj, this.context);
41 | boundFixture.fnFixture();
42 |
43 | let boundObj = utils.bindObject(obj, {});
44 | boundObj.fnEmpty();
45 | });
46 | });
47 |
--------------------------------------------------------------------------------