├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── appveyor.yml ├── binding.gyp ├── code-of-conduct.md ├── docs ├── compile.js ├── gh-pages.sh ├── images │ ├── apple-touch-icon-114x114.png │ ├── apple-touch-icon-72x72.png │ ├── apple-touch-icon.png │ ├── favicon.ico │ └── ref.pxm ├── index.jade ├── scripts │ ├── jquery-3.5.1.min.js │ └── main.js └── stylesheets │ ├── base.css │ ├── hightlight.css │ ├── layout.css │ └── skeleton.css ├── include └── ref-napi.h ├── lib ├── get-paths.js └── ref.js ├── package.json ├── src └── binding.cc └── test ├── address.js ├── alloc.js ├── bool.js ├── char.js ├── coerce.js ├── int64.js ├── iojs3issue.js ├── isNull.js ├── object.js ├── pointer.js ├── ref-deref.js ├── reinterpret.js ├── reinterpretUntilZeros.js ├── string.js ├── types.js ├── uint64.js └── utf16le.bin /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | node_modules 15 | npm-debug.log 16 | .nyc_output/ 17 | coverage 18 | build 19 | node-addon-api 20 | 21 | prebuilds/ 22 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | .nyc_output/ 15 | coverage 16 | build 17 | node-addon-api 18 | 19 | core 20 | vgcore.* 21 | 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | env: 4 | - CXX=g++-4.9 5 | - CXX=clang++ 6 | 7 | addons: 8 | apt: 9 | sources: 10 | - ubuntu-toolchain-r-test 11 | packages: 12 | - g++-4.9 13 | 14 | language: node_js 15 | 16 | os: 17 | - linux 18 | - osx 19 | 20 | osx_image: xcode10 21 | 22 | node_js: 23 | - "10" 24 | - "12" 25 | - "14" 26 | 27 | jobs: 28 | include: 29 | - os: linux 30 | arch: arm64 31 | node_js: 12 32 | env: CXX=g++-4.9 33 | exclude: 34 | - os: osx 35 | env: CXX=g++-4.9 36 | - os: linux 37 | env: CXX=clang++ 38 | 39 | install: 40 | - npm install --build-from-source 41 | 42 | after_success: 43 | - npm install coveralls 44 | - nyc report --reporter=text-lcov | ./node_modules/.bin/coveralls 45 | 46 | before_deploy: 47 | - ARCHIVE_NAME="${TRAVIS_TAG:-latest}-$TRAVIS_OS_NAME-`uname -m`.tar" 48 | - npm run prebuild --v8_enable_pointer_compression=false --v8_enable_31bit_smis_on_64bit_arch=false 49 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ARCH=ia32 npm run prebuild --v8_enable_pointer_compression=false --v8_enable_31bit_smis_on_64bit_arch=false; fi 50 | - tar --create --verbose --file="$ARCHIVE_NAME" --directory "$TRAVIS_BUILD_DIR/prebuilds" . 51 | 52 | deploy: 53 | provider: releases 54 | draft: false 55 | prerelease: true 56 | file: "$ARCHIVE_NAME" 57 | skip_cleanup: true 58 | on: 59 | tags: true 60 | node: 12 61 | api_key: $PREBUILD_GITHUB_TOKEN 62 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 1.4.3 / 2020-01-16 2 | ================== 3 | 4 | * [[`33d46e71db`](https://github.com/node-ffi-napi/ref-napi/commit/33d46e71db)] - Update to jquery-3.4.1.min.js (Alan Ayoub) [#23](https://github.com/node-ffi-napi/ref-napi/pull/23) 5 | * [[`4b8f542c16`](https://github.com/node-ffi-napi/ref-napi/commit/4b8f542c16)] - **chore**: update marked to version 0.7.0 (greenkeeper[bot]) [#17](https://github.com/node-ffi-napi/ref-napi/pull/17) 6 | * [[`7137ebcfa1`](https://github.com/node-ffi-napi/ref-napi/commit/7137ebcfa1)] - **chore**: update node-addon-api to version 2.0.0 (greenkeeper[bot]) [#21](https://github.com/node-ffi-napi/ref-napi/pull/21) 7 | 8 | 1.4.2 / 2019-01-07 9 | ================== 10 | 11 | * [[`ed19a20370`](https://github.com/node-ffi-napi/ref-napi/commit/ed19a20370)] - Fix readUInt64 so that it returns positive numbers when high order bit is set (Paul Elder) [#20](https://github.com/node-ffi-napi/ref-napi/pull/20) 12 | 13 | 1.4.1 / 2019-01-07 14 | ================== 15 | 16 | * [[`f1827f3bdd`](https://github.com/node-ffi-napi/ref-napi/commit/f1827f3bdd)] - Update node-addon-api dependency to ^1.6.2 (Anna Henningsen) 17 | * [[`bf16e880fb`](https://github.com/node-ffi-napi/ref-napi/commit/bf16e880fb)] - chore(package): update marked to version 0.6.0 (#11) (greenkeeper[bot]) 18 | * [[`bb92877339`](https://github.com/node-ffi-napi/ref-napi/commit/bb92877339)] - ***Revert*** "Update debug to 4.0.0" (Anna Henningsen) 19 | * [[`6fa4022605`](https://github.com/node-ffi-napi/ref-napi/commit/6fa4022605)] - Update debug to 4.0.0 (Anna Henningsen) 20 | * [[`3c98424bee`](https://github.com/node-ffi-napi/ref-napi/commit/3c98424bee)] - chore(package): update marked to version 0.5.0 (#7) (greenkeeper[bot]) 21 | * [[`c9523cd587`](https://github.com/node-ffi-napi/ref-napi/commit/c9523cd587)] - chore(package): update nyc to version 12.0.1 (#6) (greenkeeper[bot]) 22 | * [[`3f3f1b8efa`](https://github.com/node-ffi-napi/ref-napi/commit/3f3f1b8efa)] - chore(package): update marked to version 0.4.0 (#5) (greenkeeper[bot]) 23 | * [[`1c2a878c7a`](https://github.com/node-ffi-napi/ref-napi/commit/1c2a878c7a)] - chore(package): update mocha to version 5.0.0 (#2) (greenkeeper[bot]) 24 | * [[`0b35b34000`](https://github.com/node-ffi-napi/ref-napi/commit/0b35b34000)] - Explicitly test Node v4 (Anna Henningsen) 25 | * [[`74bbaa4cb6`](https://github.com/node-ffi-napi/ref-napi/commit/74bbaa4cb6)] - docs(readme): add Greenkeeper badge (greenkeeper[bot]) 26 | * [[`bba04d75ce`](https://github.com/node-ffi-napi/ref-napi/commit/bba04d75ce)] - chore(package): update dependencies (greenkeeper[bot]) 27 | * [[`0a77d1edde`](https://github.com/node-ffi-napi/ref-napi/commit/0a77d1edde)] - Bump node-addon-api to ^1.1.0 (Anna Henningsen) 28 | * [[`b8bac0aea0`](https://github.com/node-ffi-napi/ref-napi/commit/b8bac0aea0)] - Use full package name in README heading (Anna Henningsen) 29 | * [[`31bd8454a7`](https://github.com/node-ffi-napi/ref-napi/commit/31bd8454a7)] - Use matching branch name for badges in README.md (Anna Henningsen) 30 | * [[`46b0db449b`](https://github.com/node-ffi-napi/ref-napi/commit/46b0db449b)] - fix up package name in package.json (Anna Henningsen) 31 | 32 | 1.4.0 / 2017-11-19 33 | ================== 34 | 35 | * [[`0cd2129acf`](https://github.com/node-ffi-napi/ref/commit/0cd2129acf)] - Update information to new repository (Anna Henningsen) 36 | * [[`27171b2a96`](https://github.com/node-ffi-napi/ref/commit/27171b2a96)] - 🎉 Port code to N-API (Anna Henningsen) 37 | * [[`d23f403ab2`](https://github.com/node-ffi-napi/ref/commit/d23f403ab2)] - Add missing LICENSE file to match package.json (Anna Henningsen) 38 | * [[`9523598530`](https://github.com/node-ffi-napi/ref/commit/9523598530)] - Add Code of Conduct (Anna Henningsen) 39 | * [[`f0790af04d`](https://github.com/node-ffi-napi/ref/commit/f0790af04d)] - Refactor tests to ES6 (Anna Henningsen) 40 | * [[`924437f94b`](https://github.com/node-ffi-napi/ref/commit/924437f94b)] - fix `readPointer()` docs (#90) (John Cupitt) 41 | * [[`1029203b46`](https://github.com/node-ffi-napi/ref/commit/1029203b46)] - don't fail in Node.js "6.0.0 && \< 6.9.1" (#89) (btsimonh) 42 | * [[`4903350866`](https://github.com/node-ffi-napi/ref/commit/4903350866)] - Update docs about ref.address() being unsafe on 64-bit platforms (#88) (Yaacov Tarko) 43 | 44 | 1.3.5 / 2017-01-27 45 | ================== 46 | 47 | * [[`a1b8216fe7`](https://github.com/TooTallNate/ref/commit/a1b8216fe7)] - fix `util.inspect()` overriding on Node v7 (Nathan Rajlich) 48 | * [[`0e26fcf81e`](https://github.com/TooTallNate/ref/commit/0e26fcf81e)] - **appveyor**: drop node v2 and v3 (Nathan Rajlich) 49 | * [[`9e9078a4e1`](https://github.com/TooTallNate/ref/commit/9e9078a4e1)] - **travis**: drop node v1 and v2 (Nathan Rajlich) 50 | * [[`615016ac1a`](https://github.com/TooTallNate/ref/commit/615016ac1a)] - **test**: fix `util.inspect()` test (Nathan Rajlich) 51 | * [[`e1fe604c05`](https://github.com/TooTallNate/ref/commit/e1fe604c05)] - **test**: fix tests after V8 6.0 GC changes (Michaël Zasso, #85) 52 | 53 | 1.3.4 / 2017-01-27 54 | ================== 55 | 56 | * [[`32637be7e4`](https://github.com/TooTallNate/ref/commit/32637be7e4)] - CI stuffs (Nathan Rajlich) 57 | * [[`55716fd9e3`](https://github.com/TooTallNate/ref/commit/55716fd9e3)] - always use `defineProperty()` for `name` (Nathan Rajlich) 58 | * [[`786b73941e`](https://github.com/TooTallNate/ref/commit/786b73941e)] - **refType**: force name to writable before updating it (Joel Martin) (#67) 59 | 60 | 1.3.3 / 2016-11-03 61 | ================== 62 | 63 | * [[`3f0a2d4775`](https://github.com/TooTallNate/ref/commit/3f0a2d4775)] - rename History.md to CHANGELOG.md (Nathan Rajlich) 64 | * [[`30fe405ae5`](https://github.com/TooTallNate/ref/commit/30fe405ae5)] - Merge pull request #62 from mhertsch/master (Nathan Rajlich) 65 | * [[`6fdb4b7b23`](https://github.com/TooTallNate/ref/commit/6fdb4b7b23)] - Replaced ForceSet with Nan::ForceSet to remove deprecation warnings when using node 6.9.1 (Michael Hertsch) 66 | * [[`000b2a7889`](https://github.com/TooTallNate/ref/commit/000b2a7889)] - **travis**: test node v6 (Nathan Rajlich) 67 | 68 | 1.3.2 / 2016-01-10 69 | ================== 70 | 71 | * int64: fix failing OS X tests now 72 | * int64: better error handling for Linux 73 | 74 | 1.3.1 / 2015-12-02 75 | ================== 76 | 77 | * writeUInt64: better error checking for `WriteUInt64()` 78 | * writeUInt64: allow hex or octal input string values 79 | 80 | 1.3.0 / 2015-12-02 81 | ================== 82 | 83 | * writeInt64: better error checking for `WriteInt64()` 84 | * writeInt64: allow hex or octal input string values 85 | * appveyor: test node v5 86 | * travis: test node v5 87 | 88 | 1.2.0 / 2015-10-08 89 | ================== 90 | 91 | * force Buffer length to 0 if NULL is read (#42, @saneki) 92 | 93 | 1.1.3 / 2015-09-23 94 | ================== 95 | 96 | * appveyor: remove v1 97 | * speed increase by avoiding `JSON.stringify()` constant call (#39, @dan-tull) 98 | 99 | 1.1.2 / 2015-09-19 100 | ================== 101 | 102 | * CString: allow a Buffer to be used as backing store in `set()` (https://github.com/node-ffi/node-ffi/issues/169) 103 | * travis, appveyor: test Node.js v4 104 | 105 | 1.1.1 / 2015-09-14 106 | ================== 107 | 108 | * remove unused WrapPointer overload (fix compile warning) 109 | * appveyor: better Windows testing 110 | 111 | 1.1.0 / 2015-08-26 112 | ================== 113 | 114 | * appveyor: 2.5 + 3 added 115 | * appveyor: attempt to fix v0.8 and v0.10 116 | * int64 conversion works with debug mode iojs runtime 117 | * persistent size fixed 118 | * better automated testing 119 | * package: update "weak" to v1 120 | * package: add "MIT" license field 121 | * NAN 2.0 support (#33, @unbornchikken) 122 | 123 | 1.0.2 / 2015-05-09 124 | ================== 125 | 126 | * package: update "nan" to v1.8.4 (#30, @mafintosh) 127 | * README: use SVG for appveyor badge 128 | 129 | 1.0.1 / 2015-03-22 130 | ================== 131 | 132 | * package: update "nan" to v1.7.0 133 | * appveyor: test node v0.12, don't test v0.11 134 | * travis: test node v0.12, don't test v0.11 135 | * README: add link to Known Types page 136 | 137 | 1.0.0 / 2015-01-20 138 | ================== 139 | 140 | * bumping to v1.0.0 for better semver semantics 141 | * travis: don't test node v0.8.x 142 | 143 | 0.3.5 / 2015-01-18 144 | ================== 145 | 146 | * src: add SET_SIZEOF and SET_ALIGNOF macros 147 | 148 | 0.3.4 / 2015-01-18 149 | ================== 150 | 151 | * package: update "nan" to v1.5.1 152 | * travis: don't test node v0.6.x 153 | * use `v8::Object::ForceSet` instead of `v8::Object:Set` (#20, @sarangsapre) 154 | 155 | 0.3.3 / 2014-12-29 156 | ================== 157 | 158 | * package: allow any "debug" v2 159 | * add support for `Buffer#reinterpret()` with `offset` (#18, @deepak1556) 160 | 161 | 0.3.2 / 2014-06-19 162 | ================== 163 | 164 | * src: fix comment typo 165 | * src: define our own `kMaxLength` constant 166 | 167 | 0.3.1 / 2014-06-09 168 | ================== 169 | 170 | * src: allow Buffers returned from `reinterpretUntilZeros()` up to `kMaxLength` bytes 171 | * test: move the reinterpretUntilZeros() tests to their own file 172 | * test: fix `Buffer#inspect()` test on Windows 173 | 174 | 0.3.0 / 2014-06-08 175 | ================== 176 | 177 | * ref: use `hexAddress()` for the Buffer inspect() override 178 | * ref: add `hexAddress()` function 179 | * src: use `NanEscapableScope` where appropriate 180 | * src: use `uintptr_t` to ensure a positive address 181 | * src: better _snprintf_s #define macro (#12, @fjhub) 182 | * package: update "debug" to v1.0.1 183 | 184 | 0.2.3 / 2014-06-04 185 | ================== 186 | 187 | * package: update "nan" to v1.2.0 188 | * src: remove commented code 189 | 190 | 0.2.2 / 2014-06-01 191 | ================== 192 | 193 | * package: update "nan" to v1.1.2 194 | * travis: remove IRC notifications from Travis 195 | 196 | 0.2.1 / 2014-05-27 197 | ================== 198 | 199 | * package: pin dev dependency versions 200 | * package: use explicit nan commit with LLVM fix 201 | * README: use https for Travis URL 202 | * travis: test node v0.6.x 203 | 204 | 0.2.0 / 2014-05-26 205 | ================== 206 | 207 | * binding: use `rvagg/nan` for node v0.8, v0.10, and v0.11 compat 208 | * package: update "nan" to v1.1.0 209 | * package: remove "engines" section 210 | * README: add appveyor test badge 211 | * README: use .svg for Travis badge 212 | * add appveyor.yml file 213 | * .travis: don't test node v0.9.x 214 | * package: beautify 215 | * add a `persistent` option to writeObject() 216 | * make passing `ref.NULL` to allocCString() work as expected 217 | * docs: document the "length" parameter of ref.readPointer() 218 | 219 | 0.1.3 / 2012-09-25 220 | ================== 221 | 222 | * fix compiler warnings on Windows 223 | 224 | 0.1.2 / 2012-09-02 225 | ================== 226 | 227 | * allow an offset as the third argument to the "reinterpret" functions 228 | 229 | 0.1.1 / 2012-08-03 230 | ================== 231 | 232 | * prevent multiple instances of ref from chaining inspects in "overwriteInspect" 233 | 234 | 0.1.0 / 2012-07-22 235 | ================== 236 | 237 | * initial release of the documentation (http://tootallnate.github.com/ref) 238 | * binding: make "endianness" and "NULL" be 'frozen' 239 | * lib: make derefType() throw an Error when given a "type" with indirection 1 240 | * augment the Buffer#inspect() function to print out the memory address as well 241 | 242 | 0.0.20 / 2012-06-27 243 | =================== 244 | 245 | * rename the `Utf8String` type to `CString` (#5) 246 | * make `Utf8String` an alias to `CString` and deprecated 247 | * more work on docs (not yet ready) 248 | 249 | 0.0.19 / 2012-06-25 250 | ================== 251 | 252 | * use node-bindings 253 | 254 | 0.0.18 / 2012-06-21 255 | =================== 256 | 257 | * add the non-native-endian read+write int64 functions 258 | * starting on some real (inline) documentation 259 | 260 | 0.0.17 / 2012-06-05 261 | =================== 262 | 263 | * allow the "bool" type to write arbitrary number values (0-255) to it 264 | * Utf8String: return JS `null` when reading a pointer pointing to NULL 265 | * make `reinterpret()` and `reinterpretUntilZeros()` throw an Error when given a NULL pointer 266 | * make `ref.get()` and `ref.set()` coerce their optional type when given 267 | * some more tests 268 | 269 | 0.0.16 / 2012-06-01 270 | =================== 271 | 272 | * use Object.create() and Object.getPrototypeOf() for `refType()` and `derefType()` 273 | * remove `cloneType()` 274 | * make reading from a NULL pointer always throw an Error: 275 | * readCString() 276 | * readPointer() 277 | * readObject() 278 | * readInt64() 279 | * readUInt64() 280 | 281 | 0.0.15 / 2012-05-31 282 | =================== 283 | 284 | * fix possible segmentation fault with `readObject()` 285 | * fix possible segmentation fault with `readPointer()` 286 | 287 | 0.0.14 / 2012-05-31 288 | =================== 289 | 290 | * fix possible segmentation fault with `readCString()` 291 | 292 | 0.0.13 / 2012-05-30 293 | =================== 294 | 295 | * make `refType()` coerce string types properly 296 | * make the `bool` type inherit from a proper fixed type (like `uint8`) 297 | 298 | 0.0.12 / 2012-05-30 299 | =================== 300 | 301 | * make the "char" and "uchar" types accept JS String values 302 | * make the synonym types (i.e. longlong is a synonym for int64) be distinct objects, rather than simple JS references 303 | * fix coersion of a string value of "Object" 304 | * added the `reinterpretUntilZeros()` function 305 | 306 | 0.0.11 / 2012-05-17 307 | =================== 308 | 309 | * always do string type coersion, like on `alloc()` 310 | * add a "bool" type, which works with JS `true`/`false` values 311 | 312 | 0.0.10 / 2012-05-15 313 | =================== 314 | 315 | * fix compiler error on Solaris 316 | * fix compiler errors on Windows 317 | 318 | 0.0.9 / 2012-05-13 319 | ================== 320 | 321 | * allow `ref.alloc()` to not have a value being set with it 322 | * add the `coerceType()` function (get a proper "type" instance from a string) 323 | * add the Utf8String type back over from node-ffi 324 | * conditionally extend SlowBuffer.prototype for node >= v0.7.9 325 | 326 | 0.0.8 / 2012-05-12 327 | ================== 328 | 329 | * make the `void` type "set()" function be a no-op instead of throwing 330 | * added some more test cases 331 | 332 | 0.0.7 / 2012-05-09 333 | ================== 334 | 335 | * added the `reinterpret()` function 336 | 337 | 0.0.6 / 2012-05-09 338 | ================== 339 | 340 | * add `alignof` mappings for the types 341 | * add an `Object` type 342 | * set the `alignment` property on the built-in types 343 | 344 | 0.0.5 / 2012-05-09 345 | ================== 346 | 347 | * quickly add get() and set() functions 348 | * use the `PRId64` and `PRIu64` snprintf types 349 | 350 | 0.0.4 / 2012-05-08 351 | ================== 352 | 353 | * README improvements; some API documentation 354 | * removed some leftover debugging statements 355 | 356 | 0.0.3 / 2012-05-08 357 | ================== 358 | 359 | * added `readCString()` function (to `Buffer.prototype` as well) 360 | * added `writeCString()` function (to `Buffer.prototype` as well) 361 | * added an `allocCString()` function 362 | * removed the `Utf8String` type; moved it to node-ffi 363 | * made `ref.NULL` be a 'void' type 364 | 365 | 0.0.2 / 2012-05-05 366 | ================== 367 | 368 | * Added missing includes for Linux, etc. 369 | 370 | 0.0.1 / 2012-05-04 371 | ================== 372 | 373 | * Initial release 374 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012 Nathan Rajlich 4 | Copyright (c) 2017 Anna Henningsen (N-API port) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ref-napi 2 | ======== 3 | ### Turn Buffer instances into "pointers" 4 | 5 | [![Greenkeeper badge](https://badges.greenkeeper.io/node-ffi-napi/ref-napi.svg)](https://greenkeeper.io/) 6 | 7 | [![NPM Version](https://img.shields.io/npm/v/ref-napi.svg?style=flat)](https://npmjs.org/package/ref-napi) 8 | [![NPM Downloads](https://img.shields.io/npm/dm/ref-napi.svg?style=flat)](https://npmjs.org/package/ref-napi) 9 | [![Build Status](https://travis-ci.org/node-ffi-napi/ref-napi.svg?style=flat&branch=latest)](https://travis-ci.org/node-ffi-napi/ref-napi?branch=latest) 10 | [![Coverage Status](https://coveralls.io/repos/node-ffi-napi/ref-napi/badge.svg?branch=latest)](https://coveralls.io/r/node-ffi-napi/ref-napi?branch=latest) 11 | [![Dependency Status](https://david-dm.org/node-ffi-napi/ref-napi.svg?style=flat)](https://david-dm.org/node-ffi-napi/ref-napi) 12 | 13 | This module is inspired by the old `Pointer` class from node-ffi, but with the 14 | intent of using Node's fast `Buffer` instances instead of a slow C++ `Pointer` 15 | class. These two concepts were previously very similar, but now this module 16 | brings over the functionality that Pointers had and Buffers are missing, so 17 | now Buffers are a lot more powerful. 18 | 19 | ### Features: 20 | 21 | * Get the memory address of any `Buffer` instance 22 | * Read/write references to JavaScript Objects into `Buffer` instances 23 | * Read/write `Buffer` instances' memory addresses to other `Buffer` instances 24 | * Read/write `int64_t` and `uint64_t` data values (Numbers or Strings) 25 | * A "type" convention, so that you can specify a buffer as an `int *`, 26 | and reference/dereference at will. 27 | * Offers a buffer instance representing the `NULL` pointer 28 | 29 | 30 | Installation 31 | ------------ 32 | 33 | Install with `npm`: 34 | 35 | ``` bash 36 | $ npm install ref-napi 37 | ``` 38 | 39 | 40 | Examples 41 | -------- 42 | 43 | #### referencing and derefencing 44 | 45 | ``` js 46 | var ref = require('ref-napi') 47 | 48 | // so we can all agree that a buffer with the int value written 49 | // to it could be represented as an "int *" 50 | var buf = new Buffer(4) 51 | buf.writeInt32LE(12345, 0) 52 | 53 | // first, what is the memory address of the buffer? 54 | console.log(buf.hexAddress()) // ← '7FA89D006FD8' 55 | 56 | // using `ref`, you can set the "type", and gain magic abilities! 57 | buf.type = ref.types.int 58 | 59 | // now we can dereference to get the "meaningful" value 60 | console.log(buf.deref()) // ← 12345 61 | 62 | 63 | // you can also get references to the original buffer if you need it. 64 | // this buffer could be thought of as an "int **" 65 | var one = buf.ref() 66 | 67 | // and you can dereference all the way back down to an int 68 | console.log(one.deref().deref()) // ← 12345 69 | ``` 70 | 71 | See the [full API Docs][docs] for more examples. 72 | 73 | 74 | The "type" interface 75 | -------------------- 76 | 77 | You can easily define your own "type" objects at attach to `Buffer` instances. 78 | It just needs to be a regular JavaScript Object that contains the following 79 | properties: 80 | 81 | | **Name** | **Data Type** | **Description** 82 | |:--------------|:---------------------------------|:---------------------------------- 83 | | `size` | Number | The size in bytes required to hold this type. 84 | | `indirection` | Number | The current level of indirection of the buffer. Usually this would be _1_, and gets incremented on Buffers from `ref()` calls. A value of less than or equal to _0_ is invalid. 85 | | `get` | Function (buffer, offset) | The function to invoke when dereferencing this type when the indirection level is _1_. 86 | | `set` | Function (buffer, offset, value) | The function to invoke when setting a value to a buffer instance. 87 | | `name` | String | _(optional)_ The name to use during debugging for this type. 88 | | `alignment` | Number | _(optional)_ The alignment of this type when placed in a struct. Defaults to the type's `size`. 89 | 90 | Be sure to check out the Wiki page of ["Known 91 | Types"](https://github.com/TooTallNate/ref/wiki/Known-%22types%22), for the list 92 | of built-in ref types, as well as known external type implementations. 93 | 94 | For example, you could define a "bigint" type that dereferences into a 95 | [`bigint`](https://github.com/substack/node-bigint) instance: 96 | 97 | ``` js 98 | var ref = require('ref-napi') 99 | var bigint = require('bigint') 100 | 101 | // define the "type" instance according to the spec 102 | var BigintType = { 103 | size: ref.sizeof.int64 104 | , indirection: 1 105 | , get: function (buffer, offset) { 106 | // return a bigint instance from the buffer 107 | return bigint.fromBuffer(buffer) 108 | } 109 | , set: function (buffer, offset, value) { 110 | // 'value' would be a bigint instance 111 | var val = value.toString() 112 | return ref.writeInt64(buffer, offset || 0, val) 113 | } 114 | } 115 | 116 | // now we can create instances of the type from existing buffers. 117 | // "buf" is some Buffer instance returned from some external data 118 | // source, which should contain "bigint" binary data. 119 | buf.type = BigintType 120 | 121 | // and now you can create "bigint" instances using this generic "types" API 122 | var val = buf.deref() 123 | .add('1234') 124 | .sqrt() 125 | .shiftLeft(5) 126 | ``` 127 | 128 | Build the docs 129 | -------------- 130 | 131 | Install the dev dependencies: 132 | 133 | ``` bash 134 | $ npm install 135 | ``` 136 | 137 | Generate the docs: 138 | 139 | ``` bash 140 | $ npm run docs 141 | ``` 142 | 143 | Incompatible packages 144 | -------------------- 145 | 146 | The [`ref-struct-napi`](https://www.npmjs.com/package/ref-struct-napi) and 147 | [`ref-array-napi`](https://www.npmjs.com/package/ref-array-napi) packages 148 | have names that sound like they are compatible with this module. 149 | 150 | They are not, and your application will experience crashes if you use 151 | them together with `ref-napi`. 152 | Use [`ref-struct-di`](https://www.npmjs.com/package/ref-struct-di) 153 | or [`ref-array-di`](https://www.npmjs.com/package/ref-array-di) instead. 154 | 155 | License 156 | ------- 157 | 158 | (The MIT License) 159 | 160 | Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net> 161 | Copyright (c) 2017 Anna Henningsen <anna@addaleax.net> (N-API port) 162 | 163 | Permission is hereby granted, free of charge, to any person obtaining 164 | a copy of this software and associated documentation files (the 165 | 'Software'), to deal in the Software without restriction, including 166 | without limitation the rights to use, copy, modify, merge, publish, 167 | distribute, sublicense, and/or sell copies of the Software, and to 168 | permit persons to whom the Software is furnished to do so, subject to 169 | the following conditions: 170 | 171 | The above copyright notice and this permission notice shall be 172 | included in all copies or substantial portions of the Software. 173 | 174 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 175 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 176 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 177 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 178 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 179 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 180 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 181 | 182 | [docs]: http://tootallnate.github.com/ref 183 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # http://www.appveyor.com/docs/appveyor-yml 2 | 3 | # Test against these versions of Node.js. 4 | environment: 5 | # Test against these versions of Node.js and io.js 6 | matrix: 7 | # node.js 8 | - nodejs_version: "10" 9 | - nodejs_version: "12" 10 | - nodejs_version: "14" 11 | 12 | platform: 13 | - x86 14 | - x64 15 | 16 | # Install scripts. (runs after repo cloning) 17 | install: 18 | - python -V 19 | - set PYTHON=python 20 | - ps: Install-Product node $env:nodejs_version $env:platform 21 | - node -p process.arch 22 | - node -p process.version 23 | - npm install --build-from-source 24 | 25 | # Post-install test scripts. 26 | test_script: 27 | # Output useful info for debugging. 28 | - node --version 29 | - npm --version 30 | # run tests 31 | - npm test 32 | 33 | after_test: 34 | - ps: If ($env:nodejs_version -eq "12") { npm run prebuild --v8_enable_pointer_compression=false --v8_enable_31bit_smis_on_64bit_arch=false } 35 | 36 | # Don't actually build. 37 | build: off 38 | 39 | # Set build version format here instead of in the admin panel. 40 | version: "{build}" 41 | 42 | artifacts: 43 | - path: prebuilds 44 | name: $(APPVEYOR_REPO_TAG_NAME)-win-$(PLATFORM) 45 | type: zip 46 | 47 | deploy: 48 | - provider: GitHub 49 | artifact: /.*\.zip/ 50 | draft: false 51 | prerelease: true 52 | auth_token: $(PREBUILD_GITHUB_TOKEN) 53 | on: 54 | appveyor_repo_tag: true 55 | nodejs_version: "12" 56 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [{ 3 | 'target_name': 'binding', 4 | 'sources': [ 'src/binding.cc' ], 5 | 'include_dirs': [ 6 | " tags 60 | t.description = t.description.substring(3, t.description.length - 4) 61 | } 62 | }) 63 | doc.returnType = doc.tags.filter(function (t) { 64 | return t.type == 'return' 65 | })[0] 66 | doc.paramTypes = doc.tags.filter(function (t) { 67 | return t.type == 'param' 68 | }) 69 | }) 70 | 71 | // sort 72 | exports = exports.sort(sort) 73 | extensions = extensions.sort(sort) 74 | 75 | // parse and highlight the Markdown descriptions 76 | ;[exports, types, extensions].forEach(function (docs) { 77 | docs.forEach(function (doc) { 78 | var desc = doc.description 79 | desc.full = markdown(desc.full) 80 | desc.summary = markdown(desc.summary) 81 | desc.body = markdown(desc.body) 82 | }) 83 | }) 84 | 85 | // get a reference to the ref export doc object for every Buffer extension doc 86 | extensions.forEach(function (doc) { 87 | doc.ref = exports.filter(function (ref) { 88 | return ref.name === doc.name 89 | })[0] 90 | }) 91 | 92 | fs.readFile(__dirname + '/index.jade', 'utf8', function (err, template) { 93 | if (err) throw err 94 | 95 | template = jade.compile(template) 96 | var html = template({ 97 | exports: sections[0] 98 | , types: sections[1] 99 | , extensions: sections[2] 100 | , package: require('../package.json') 101 | , markdown: markdown 102 | , highlight: highlight 103 | }) 104 | 105 | fs.writeFile(__dirname + '/index.html', html, function (err) { 106 | if (err) throw err 107 | }) 108 | }) 109 | }) 110 | 111 | 112 | /** 113 | * Sorts an array of dox objects by doc.name. If the first letter is an '_' then 114 | * it is considered "private" and gets sorted at the bottom. 115 | */ 116 | 117 | function sort (a, b) { 118 | var aname = a.name 119 | var bname = b.name 120 | var aprivate = a.isPrivate 121 | var bprivate = b.isPrivate 122 | if (aprivate && !bprivate) { 123 | return 1 124 | } 125 | if (bprivate && !aprivate) { 126 | return -1 127 | } 128 | return aname > bname ? 1 : -1 129 | } 130 | 131 | /** 132 | * Parses Markdown into highlighted HTML. 133 | */ 134 | 135 | function markdown (code) { 136 | if (!code) return code 137 | return marked(code, { 138 | gfm: true 139 | , highlight: highlight 140 | }) 141 | } 142 | 143 | /** 144 | * Add syntax highlighting HTML to the given `code` block. 145 | * `lang` defaults to "javascript" if no valid name is given. 146 | */ 147 | 148 | function highlight (code, lang) { 149 | if (hljs.listLanguages().indexOf(lang) === -1) { 150 | lang = 'javascript' 151 | } 152 | return hljs.highlight(lang, code).value 153 | } 154 | -------------------------------------------------------------------------------- /docs/gh-pages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DIR=`dirname $0` 4 | TMP_DOC_DIR="/tmp/ref_docs" 5 | 6 | npm run docs \ 7 | && rm -rf $TMP_DOC_DIR \ 8 | && mkdir -p $TMP_DOC_DIR \ 9 | && cp -Lrf {"$DIR/index.html","$DIR/images","$DIR/scripts","$DIR/stylesheets"} $TMP_DOC_DIR \ 10 | && git checkout gh-pages \ 11 | && cp -Lrf $TMP_DOC_DIR/* . \ 12 | && echo "done" 13 | -------------------------------------------------------------------------------- /docs/images/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-ffi-napi/ref-napi/a7f62a4caa3e0c32aeb66c015641b0bc01ba03ab/docs/images/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /docs/images/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-ffi-napi/ref-napi/a7f62a4caa3e0c32aeb66c015641b0bc01ba03ab/docs/images/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /docs/images/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-ffi-napi/ref-napi/a7f62a4caa3e0c32aeb66c015641b0bc01ba03ab/docs/images/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-ffi-napi/ref-napi/a7f62a4caa3e0c32aeb66c015641b0bc01ba03ab/docs/images/favicon.ico -------------------------------------------------------------------------------- /docs/images/ref.pxm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-ffi-napi/ref-napi/a7f62a4caa3e0c32aeb66c015641b0bc01ba03ab/docs/images/ref.pxm -------------------------------------------------------------------------------- /docs/index.jade: -------------------------------------------------------------------------------- 1 | doctype 5 2 | html(lang="en") 3 | head 4 | meta(charset="utf-8") 5 | title "ref" documentation v#{package.version} 6 | meta(name="description", content=package.description) 7 | meta(name="keywords", content=['node.js', package.name].concat(package.keywords).join(', ')) 8 | meta(name="author", content="Nathan Rajlich") 9 | meta(name="viewport", content="width=device-width, initial-scale=1, maximum-scale=1") 10 | 11 | link(rel="stylesheet", href="stylesheets/hightlight.css") 12 | link(rel="stylesheet", href="stylesheets/base.css") 13 | link(rel="stylesheet", href="stylesheets/skeleton.css") 14 | link(rel="stylesheet", href="stylesheets/layout.css") 15 | 16 | link(rel="shortcut icon", href="images/favicon.ico") 17 | link(rel="apple-touch-icon", href="images/apple-touch-icon.png") 18 | link(rel="apple-touch-icon", sizes="72x72", href="images/apple-touch-icon-72x72.png") 19 | link(rel="apple-touch-icon", sizes="114x114", href="images/apple-touch-icon-114x114.png") 20 | 21 | body 22 | .container 23 | .columns.three.logo 24 | a(href="") 25 | h1 #{package.name} 26 | span.pointer * 27 | span.version v#{package.version} 28 | .columns.thirteen.subtitle 29 | h3= package.description 30 | .columns.sixteen.intro 31 | h4 What is ref? 32 | p 33 | code ref 34 | | is a native addon for 35 | a(href="http://nodejs.org") Node.js 36 | | that aids in doing C programming in JavaScript, by extending 37 | | the built-in 38 | a(href="http://nodejs.org/api/buffer.html") 39 | code Buffer 40 | | class 41 | | with some fancy additions like: 42 | ul 43 | li Getting the memory address of a Buffer 44 | li Checking the endianness of the processor 45 | li Checking if a Buffer represents the NULL pointer 46 | li Reading and writing "pointers" with Buffers 47 | li Reading and writing C Strings (NULL-terminated) 48 | li Reading and writing JavaScript Object references 49 | li Reading and writing 50 | strong int64_t 51 | | and 52 | strong uint64_t 53 | | values 54 | li A "type" convention to define the contents of a Buffer 55 | p 56 | | There is indeed a lot of 57 | em meat 58 | | to 59 | code ref 60 | | , but it all fits together in one way or another in the end. 61 | br 62 | | For simplicity, 63 | code ref 64 | | 's API can be broken down into 3 sections: 65 | a.nav.columns.five(href='#exports') 66 | h4 ref exports 67 | p 68 | | All the static versions of 69 | code ref 70 | | 's functions and default "types" available on the exports returned from 71 | code require('ref') 72 | | . 73 | a.nav.columns.five(href='#types') 74 | h4 "type" system 75 | p 76 | | The 77 | em "type" 78 | | system allows you to define a "type" on any Buffer instance, and then 79 | | use generic 80 | code ref() 81 | | and 82 | code deref() 83 | | functions to reference and dereference values. 84 | a.nav.columns.five(href='#extensions') 85 | h4 Buffer extensions 86 | p 87 | code Buffer.prototype 88 | | gets extended with some convenience functions. These all just mirror 89 | | their static counterpart, using the Buffer's 90 | code this 91 | | variable as the 92 | code buffer 93 | | variable. 94 | 95 | 96 | hr 97 | .columns.eight.section.exports 98 | a(name="exports") 99 | a(href="#exports") 100 | h2 ref exports 101 | .columns.sixteen.intro 102 | p 103 | | This section documents all the functions exported from 104 | code require('ref') 105 | | . 106 | each doc in exports 107 | if (!doc.ignore) 108 | .columns.sixteen.section 109 | a(name='exports-' + doc.name) 110 | a(href='#exports-' + doc.name) 111 | isFunc = doc.type == 'method' || doc.isPrivate 112 | h3 113 | | ref.#{doc.name} 114 | if (isFunc) 115 | | ( 116 | each param, i in doc.paramTypes 117 | span.param #{param.types.join('|')} #{param.name} 118 | if (i + 1 < doc.paramTypes.length) 119 | | , 120 | | ) 121 | if (doc.returnType) 122 | | 123 | span.rtn → #{doc.returnType.types.join('|')} 124 | if (!isFunc) 125 | | 126 | span.rtn ⇒ #{doc.type} 127 | if (doc.paramTypes.length > 0 || doc.returnType) 128 | ul 129 | each param in doc.paramTypes 130 | li #{param.name} - !{param.description} 131 | if (doc.returnType) 132 | li 133 | strong Return: 134 | | !{doc.returnType.description} 135 | != (doc && doc.description.full || '').replace(/\
/g, ' ') 136 | 137 | 138 | hr 139 | .columns.eight.section.types 140 | a(name="types") 141 | a(href="#types") 142 | h2 143 | em "type" 144 | | system 145 | .columns.sixteen.intro.types 146 | p 147 | | A "type" in 148 | code ref 149 | | is simply an plain 'ol JavaScript Object, with a set 150 | | of expected properties attached that implement the logic for getting 151 | | & setting values on a given 152 | code Buffer 153 | | instance. 154 | p 155 | | To attach a "type" to a Buffer instance, you simply attach the "type" 156 | | object to the Buffer's type property. 157 | code ref 158 | | comes with a set of commonly used types which are described in this 159 | | section. 160 | h4 Creating your own "type" 161 | p 162 | | It's trivial to create your own "type" that reads and writes your 163 | | own custom datatype/class to and from Buffer instances using 164 | code ref 165 | | 's unified API. 166 | br 167 | | To create your own "type", simply create a JavaScript Object with 168 | | the following properties defined: 169 | table 170 | tr 171 | th Name 172 | th Data Type 173 | th Description 174 | tr 175 | td: code size 176 | td: code Number 177 | td The size in bytes required to hold this datatype. 178 | tr 179 | td: code indirection 180 | td: code Number 181 | td 182 | | The current level of indirection of the buffer. When defining 183 | | your own "types", just set this value to 1. 184 | tr 185 | td: code get 186 | td: code Function 187 | td 188 | | The function to invoke when 189 | a(href="#exports-get") 190 | code ref.get() 191 | | is invoked on a buffer of this type. 192 | tr 193 | td: code set 194 | td: code Function 195 | td 196 | | The function to invoke when 197 | a(href="#exports-set") 198 | code ref.set() 199 | | is invoked on a buffer of this type. 200 | tr 201 | td: code name 202 | td: code String 203 | td 204 | em (Optional) 205 | | The name to use during debugging for this datatype. 206 | tr 207 | td: code alignment 208 | td: code Number 209 | td 210 | em (Optional) 211 | | The alignment of this datatype when placed inside a struct. 212 | | Defaults to the type's 213 | code size 214 | | . 215 | h4 The built-in "types" 216 | p 217 | | Here is the list of 218 | code ref 219 | | 's built-in "type" Objects. All these built-in "types" can be found 220 | | on the 221 | code ref.types 222 | | export Object. All the built-in types use "native endianness" when 223 | | multi-byte datatypes are involved. 224 | each doc in types 225 | if (!doc.ignore) 226 | .columns.sixteen.section 227 | a(name='types-' + doc.name) 228 | a(href='#types-' + doc.name) 229 | h3 types.#{doc.name} 230 | != doc.description.full.replace(/\
/g, ' ') 231 | 232 | 233 | hr 234 | .columns.eight.section.exports 235 | a(name="extensions") 236 | a(href="#extensions") 237 | h2 Buffer extensions 238 | .columns.sixteen.intro 239 | p 240 | code Buffer.prototype 241 | | gets extended with some convenience functions that you can use in 242 | | your modules and/or applications. 243 | each doc in extensions 244 | if (!doc.ignore) 245 | .columns.sixteen.section 246 | a(name='extensions-' + doc.name) 247 | a(href='#extensions-' + doc.name) 248 | h3 Buffer##{doc.name}() 249 | if (doc.name === 'inspect') 250 | != doc.description.full.replace(/\
/g, ' ') 251 | else 252 | p 253 | | Shorthand for 254 | a(href='#exports-' + doc.name) 255 | code ref.#{doc.name}(this, …) 256 | | . 257 | != (doc.ref && doc.ref.description.full || '').replace(/\
/g, ' ') 258 | 259 | 260 | .ribbon 261 | a(href="https://github.com/TooTallNate/ref", rel="me") Fork me on GitHub 262 | 263 | script(src="scripts/jquery-3.5.1.min.js") 264 | script(src="scripts/main.js") 265 | -------------------------------------------------------------------------------- /docs/scripts/main.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Animated scroll to named anchors. 4 | */ 5 | 6 | $(document.body).on('click', 'a', function () { 7 | var href = $(this).attr('href') 8 | if (href[0] === '#') { 9 | scrollTo(href) 10 | window.history && history.pushState({}, name, href) 11 | return false 12 | } 13 | }) 14 | 15 | function scrollTo (hash) { 16 | var name = hash.substring(1) 17 | var target = $('a[name="' + name + '"]') 18 | if (target.length) { 19 | $('html, body').animate({ scrollTop: target.offset().top } 20 | , { duration: 500, easing: 'swing'}) 21 | } else { 22 | console.log('documentation not written: %s', name) 23 | } 24 | return target 25 | } 26 | -------------------------------------------------------------------------------- /docs/stylesheets/base.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skeleton V1.2 3 | * Copyright 2011, Dave Gamache 4 | * www.getskeleton.com 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 6/20/2012 8 | */ 9 | 10 | 11 | /* Table of Content 12 | ================================================== 13 | #Reset & Basics 14 | #Basic Styles 15 | #Site Styles 16 | #Typography 17 | #Links 18 | #Lists 19 | #Images 20 | #Buttons 21 | #Forms 22 | #Misc */ 23 | 24 | 25 | /* #Reset & Basics (Inspired by E. Meyers) 26 | ================================================== */ 27 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { 28 | margin: 0; 29 | padding: 0; 30 | border: 0; 31 | font-size: 100%; 32 | font: inherit; 33 | vertical-align: baseline; } 34 | article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { 35 | display: block; } 36 | body { 37 | line-height: 1; } 38 | ol, ul { 39 | list-style: none; } 40 | blockquote, q { 41 | quotes: none; } 42 | blockquote:before, blockquote:after, 43 | q:before, q:after { 44 | content: ''; 45 | content: none; } 46 | table { 47 | border-collapse: collapse; 48 | border-spacing: 0; } 49 | 50 | 51 | /* #Basic Styles 52 | ================================================== */ 53 | body { 54 | background: #fff; 55 | font: 14px/21px "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; 56 | color: #444; 57 | -webkit-font-smoothing: antialiased; /* Fix for webkit rendering */ 58 | -webkit-text-size-adjust: 100%; 59 | } 60 | 61 | 62 | /* #Typography 63 | ================================================== */ 64 | h1, h2, h3, h4, h5, h6 { 65 | color: #181818; 66 | font-family: "Georgia", "Times New Roman", serif; 67 | font-weight: normal; } 68 | h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { font-weight: inherit; } 69 | h1 { font-size: 46px; line-height: 50px; margin-bottom: 14px;} 70 | h2 { font-size: 35px; line-height: 40px; margin-bottom: 10px; } 71 | h3 { font-size: 28px; line-height: 34px; margin-bottom: 8px; } 72 | h4 { font-size: 21px; line-height: 30px; margin-bottom: 4px; } 73 | h5 { font-size: 17px; line-height: 24px; } 74 | h6 { font-size: 14px; line-height: 21px; } 75 | .subheader { color: #777; } 76 | 77 | p { margin: 0 0 20px 0; } 78 | p img { margin: 0; } 79 | p.lead { font-size: 21px; line-height: 27px; color: #777; } 80 | 81 | em { font-style: italic; } 82 | strong { font-weight: bold; color: #333; } 83 | small { font-size: 80%; } 84 | 85 | /* Blockquotes */ 86 | blockquote, blockquote p { font-size: 17px; line-height: 24px; color: #777; font-style: italic; } 87 | blockquote { margin: 0 0 20px; padding: 9px 20px 0 19px; border-left: 1px solid #ddd; } 88 | blockquote cite { display: block; font-size: 12px; color: #555; } 89 | blockquote cite:before { content: "\2014 \0020"; } 90 | blockquote cite a, blockquote cite a:visited, blockquote cite a:visited { color: #555; } 91 | 92 | hr { border: solid #ddd; border-width: 1px 0 0; clear: both; margin: 10px 0 30px; height: 0; } 93 | 94 | 95 | /* #Links 96 | ================================================== */ 97 | a, a:visited { color: #333; text-decoration: underline; outline: 0; } 98 | a:hover, a:focus { color: #000; } 99 | p a, p a:visited { line-height: inherit; } 100 | 101 | 102 | /* #Lists 103 | ================================================== */ 104 | ul, ol { margin-bottom: 20px; } 105 | ul { list-style: none outside; } 106 | ol { list-style: decimal; } 107 | ol, ul.square, ul.circle, ul.disc { margin-left: 30px; } 108 | ul.square { list-style: square outside; } 109 | ul.circle { list-style: circle outside; } 110 | ul.disc { list-style: disc outside; } 111 | ul ul, ul ol, 112 | ol ol, ol ul { margin: 4px 0 5px 30px; font-size: 90%; } 113 | ul ul li, ul ol li, 114 | ol ol li, ol ul li { margin-bottom: 6px; } 115 | li { line-height: 18px; margin-bottom: 12px; } 116 | ul.large li { line-height: 21px; } 117 | li p { line-height: 21px; } 118 | 119 | /* #Images 120 | ================================================== */ 121 | 122 | img.scale-with-grid { 123 | max-width: 100%; 124 | height: auto; } 125 | 126 | 127 | /* #Buttons 128 | ================================================== */ 129 | 130 | .button, 131 | button, 132 | input[type="submit"], 133 | input[type="reset"], 134 | input[type="button"] { 135 | background: #eee; /* Old browsers */ 136 | background: #eee -moz-linear-gradient(top, rgba(255,255,255,.2) 0%, rgba(0,0,0,.2) 100%); /* FF3.6+ */ 137 | background: #eee -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.2)), color-stop(100%,rgba(0,0,0,.2))); /* Chrome,Safari4+ */ 138 | background: #eee -webkit-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* Chrome10+,Safari5.1+ */ 139 | background: #eee -o-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* Opera11.10+ */ 140 | background: #eee -ms-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* IE10+ */ 141 | background: #eee linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* W3C */ 142 | border: 1px solid #aaa; 143 | border-top: 1px solid #ccc; 144 | border-left: 1px solid #ccc; 145 | -moz-border-radius: 3px; 146 | -webkit-border-radius: 3px; 147 | border-radius: 3px; 148 | color: #444; 149 | display: inline-block; 150 | font-size: 11px; 151 | font-weight: bold; 152 | text-decoration: none; 153 | text-shadow: 0 1px rgba(255, 255, 255, .75); 154 | cursor: pointer; 155 | margin-bottom: 20px; 156 | line-height: normal; 157 | padding: 8px 10px; 158 | font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; } 159 | 160 | .button:hover, 161 | button:hover, 162 | input[type="submit"]:hover, 163 | input[type="reset"]:hover, 164 | input[type="button"]:hover { 165 | color: #222; 166 | background: #ddd; /* Old browsers */ 167 | background: #ddd -moz-linear-gradient(top, rgba(255,255,255,.3) 0%, rgba(0,0,0,.3) 100%); /* FF3.6+ */ 168 | background: #ddd -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.3)), color-stop(100%,rgba(0,0,0,.3))); /* Chrome,Safari4+ */ 169 | background: #ddd -webkit-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* Chrome10+,Safari5.1+ */ 170 | background: #ddd -o-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* Opera11.10+ */ 171 | background: #ddd -ms-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* IE10+ */ 172 | background: #ddd linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* W3C */ 173 | border: 1px solid #888; 174 | border-top: 1px solid #aaa; 175 | border-left: 1px solid #aaa; } 176 | 177 | .button:active, 178 | button:active, 179 | input[type="submit"]:active, 180 | input[type="reset"]:active, 181 | input[type="button"]:active { 182 | border: 1px solid #666; 183 | background: #ccc; /* Old browsers */ 184 | background: #ccc -moz-linear-gradient(top, rgba(255,255,255,.35) 0%, rgba(10,10,10,.4) 100%); /* FF3.6+ */ 185 | background: #ccc -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.35)), color-stop(100%,rgba(10,10,10,.4))); /* Chrome,Safari4+ */ 186 | background: #ccc -webkit-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* Chrome10+,Safari5.1+ */ 187 | background: #ccc -o-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* Opera11.10+ */ 188 | background: #ccc -ms-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* IE10+ */ 189 | background: #ccc linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* W3C */ } 190 | 191 | .button.full-width, 192 | button.full-width, 193 | input[type="submit"].full-width, 194 | input[type="reset"].full-width, 195 | input[type="button"].full-width { 196 | width: 100%; 197 | padding-left: 0 !important; 198 | padding-right: 0 !important; 199 | text-align: center; } 200 | 201 | /* Fix for odd Mozilla border & padding issues */ 202 | button::-moz-focus-inner, 203 | input::-moz-focus-inner { 204 | border: 0; 205 | padding: 0; 206 | } 207 | 208 | 209 | /* #Forms 210 | ================================================== */ 211 | 212 | form { 213 | margin-bottom: 20px; } 214 | fieldset { 215 | margin-bottom: 20px; } 216 | input[type="text"], 217 | input[type="password"], 218 | input[type="email"], 219 | textarea, 220 | select { 221 | border: 1px solid #ccc; 222 | padding: 6px 4px; 223 | outline: none; 224 | -moz-border-radius: 2px; 225 | -webkit-border-radius: 2px; 226 | border-radius: 2px; 227 | font: 13px "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; 228 | color: #777; 229 | margin: 0; 230 | width: 210px; 231 | max-width: 100%; 232 | display: block; 233 | margin-bottom: 20px; 234 | background: #fff; } 235 | select { 236 | padding: 0; } 237 | input[type="text"]:focus, 238 | input[type="password"]:focus, 239 | input[type="email"]:focus, 240 | textarea:focus { 241 | border: 1px solid #aaa; 242 | color: #444; 243 | -moz-box-shadow: 0 0 3px rgba(0,0,0,.2); 244 | -webkit-box-shadow: 0 0 3px rgba(0,0,0,.2); 245 | box-shadow: 0 0 3px rgba(0,0,0,.2); } 246 | textarea { 247 | min-height: 60px; } 248 | label, 249 | legend { 250 | display: block; 251 | font-weight: bold; 252 | font-size: 13px; } 253 | select { 254 | width: 220px; } 255 | input[type="checkbox"] { 256 | display: inline; } 257 | label span, 258 | legend span { 259 | font-weight: normal; 260 | font-size: 13px; 261 | color: #444; } 262 | 263 | /* #Misc 264 | ================================================== */ 265 | .remove-bottom { margin-bottom: 0 !important; } 266 | .half-bottom { margin-bottom: 10px !important; } 267 | .add-bottom { margin-bottom: 20px !important; } 268 | -------------------------------------------------------------------------------- /docs/stylesheets/hightlight.css: -------------------------------------------------------------------------------- 1 | ../../node_modules/highlight.js/src/styles/solarized_light.css -------------------------------------------------------------------------------- /docs/stylesheets/layout.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skeleton V1.2 3 | * Copyright 2011, Dave Gamache 4 | * www.getskeleton.com 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 6/20/2012 8 | */ 9 | 10 | /* Table of Content 11 | ================================================== 12 | #Site Styles 13 | #Page Styles 14 | #Media Queries 15 | #Font-Face */ 16 | 17 | /* #Site Styles 18 | ================================================== */ 19 | 20 | /* #Page Styles 21 | ================================================== */ 22 | 23 | /* #Media Queries 24 | ================================================== */ 25 | 26 | /* Smaller than standard 960 (devices and browsers) */ 27 | @media only screen and (max-width: 959px) {} 28 | 29 | /* Tablet Portrait size to standard 960 (devices and browsers) */ 30 | @media only screen and (min-width: 768px) and (max-width: 959px) {} 31 | 32 | /* All Mobile Sizes (devices and browser) */ 33 | @media only screen and (max-width: 767px) {} 34 | 35 | /* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ 36 | @media only screen and (min-width: 480px) and (max-width: 767px) {} 37 | 38 | /* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */ 39 | @media only screen and (max-width: 479px) {} 40 | 41 | 42 | /* #Font-Face 43 | ================================================== */ 44 | /* This is the proper syntax for an @font-face file 45 | Just create a "fonts" folder at the root, 46 | copy your FontName into code below and remove 47 | comment brackets */ 48 | 49 | /* @font-face { 50 | font-family: 'FontName'; 51 | src: url('../fonts/FontName.eot'); 52 | src: url('../fonts/FontName.eot?iefix') format('eot'), 53 | url('../fonts/FontName.woff') format('woff'), 54 | url('../fonts/FontName.ttf') format('truetype'), 55 | url('../fonts/FontName.svg#webfontZam02nTh') format('svg'); 56 | font-weight: normal; 57 | font-style: normal; } 58 | */ 59 | 60 | 61 | /* `ref` stuff */ 62 | code, pre { 63 | background-color: #fcf8f2; 64 | border: solid 1px rgba(68, 68, 68, 0.3); 65 | border-radius: 2px; 66 | padding: 1px 2px; 67 | -webkit-transition-property: background-color; 68 | -webkit-transition-duration: 0.5s; 69 | -webkit-transition-delay: 0s; 70 | } 71 | 72 | pre { 73 | padding: 10px; 74 | } 75 | 76 | pre code { 77 | background-color: transparent; 78 | border: none; 79 | padding: 0px; 80 | } 81 | 82 | th { 83 | font-weight: bold; 84 | } 85 | 86 | a { 87 | text-decoration: none; 88 | } 89 | 90 | p { 91 | margin: 10px 0; 92 | } 93 | 94 | p a { 95 | text-decoration: underline; 96 | } 97 | 98 | ul { 99 | list-style: disc; 100 | margin-left: 40px; 101 | } 102 | 103 | h3 { 104 | color: #777; 105 | } 106 | 107 | .container { 108 | margin: 100px auto; 109 | } 110 | 111 | .logo h1 .pointer { 112 | color: #dbe8d9; 113 | -webkit-transition-property: color; 114 | -webkit-transition-duration: 0.5s; 115 | -webkit-transition-delay: 0s; 116 | } 117 | 118 | .logo h1:hover .pointer { 119 | color: #444; 120 | } 121 | 122 | .logo h1 .version { 123 | color: #aaa; 124 | font-size: 0.3em; 125 | position: absolute; 126 | top: 12px; 127 | left: 70px; 128 | } 129 | 130 | .types table, .types tr { 131 | border: solid 1px #444; 132 | } 133 | 134 | .types td, .types th { 135 | border: solid 1px #444; 136 | padding: 4px; 137 | } 138 | 139 | .param, .rtn { 140 | display: inline-block; 141 | font-size: 0.8em; 142 | font-style: italic; 143 | color: #444; 144 | } 145 | 146 | a.nav { 147 | border: solid 1px transparent; 148 | border-radius: 2px; 149 | padding: 3px; 150 | margin-bottom: 20px; 151 | text-decoration: none; 152 | background-color: transparent; 153 | -webkit-transition-property: background-color, border-color; 154 | -webkit-transition-duration: 0.5s; 155 | -webkit-transition-delay: 0s; 156 | } 157 | 158 | a.nav:hover { 159 | background-color: #fcf8f2; 160 | border-color: rgba(68, 68, 68, 0.3); 161 | } 162 | 163 | a.nav:hover code { 164 | background-color: #dbe8d9; 165 | } 166 | 167 | .subtitle h3 { 168 | margin-top: 18px; 169 | } 170 | 171 | .section { 172 | margin-top: 40px; 173 | } 174 | 175 | 176 | /* GitHub Ribbon 177 | * http://unindented.org/articles/2009/10/github-ribbon-using-css-transforms */ 178 | .ribbon { 179 | background-color: #fcf8f2; 180 | overflow: hidden; 181 | /* top right corner */ 182 | position: fixed; 183 | right: -3em; 184 | top: 2.5em; 185 | /* 45 deg ccw rotation */ 186 | -moz-transform: rotate(45deg); 187 | -webkit-transform: rotate(45deg); 188 | /* shadow */ 189 | -moz-box-shadow: 0 0 0.5em #888; 190 | -webkit-box-shadow: 0 0 0.5em #888; 191 | /* hover transition */ 192 | -webkit-transition-property: background-color; 193 | -webkit-transition-duration: 0.3s; 194 | -webkit-transition-delay: 0s; 195 | } 196 | 197 | .ribbon a { 198 | border: solid 1px rgba(68, 68, 68, 0.4); 199 | color: #444; 200 | display: block; 201 | font: bold 81.25% 'Helvetiva Neue', Helvetica, Arial, sans-serif; 202 | margin: 0.05em 0 0.075em 0; 203 | padding: 0.5em 3.5em; 204 | text-align: center; 205 | text-decoration: none; 206 | /* shadow */ 207 | text-shadow: 0 0 0.5em #888; 208 | } 209 | 210 | .ribbon:hover { 211 | background-color: #dbe8d9; 212 | } 213 | -------------------------------------------------------------------------------- /docs/stylesheets/skeleton.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skeleton V1.2 3 | * Copyright 2011, Dave Gamache 4 | * www.getskeleton.com 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 6/20/2012 8 | */ 9 | 10 | 11 | /* Table of Contents 12 | ================================================== 13 | #Base 960 Grid 14 | #Tablet (Portrait) 15 | #Mobile (Portrait) 16 | #Mobile (Landscape) 17 | #Clearing */ 18 | 19 | 20 | 21 | /* #Base 960 Grid 22 | ================================================== */ 23 | 24 | .container { position: relative; width: 960px; margin: 0 auto; padding: 0; } 25 | .container .column, 26 | .container .columns { float: left; display: inline; margin-left: 10px; margin-right: 10px; } 27 | .row { margin-bottom: 20px; } 28 | 29 | /* Nested Column Classes */ 30 | .column.alpha, .columns.alpha { margin-left: 0; } 31 | .column.omega, .columns.omega { margin-right: 0; } 32 | 33 | /* Base Grid */ 34 | .container .one.column, 35 | .container .one.columns { width: 40px; } 36 | .container .two.columns { width: 100px; } 37 | .container .three.columns { width: 160px; } 38 | .container .four.columns { width: 220px; } 39 | .container .five.columns { width: 280px; } 40 | .container .six.columns { width: 340px; } 41 | .container .seven.columns { width: 400px; } 42 | .container .eight.columns { width: 460px; } 43 | .container .nine.columns { width: 520px; } 44 | .container .ten.columns { width: 580px; } 45 | .container .eleven.columns { width: 640px; } 46 | .container .twelve.columns { width: 700px; } 47 | .container .thirteen.columns { width: 760px; } 48 | .container .fourteen.columns { width: 820px; } 49 | .container .fifteen.columns { width: 880px; } 50 | .container .sixteen.columns { width: 940px; } 51 | 52 | .container .one-third.column { width: 300px; } 53 | .container .two-thirds.column { width: 620px; } 54 | 55 | /* Offsets */ 56 | .container .offset-by-one { padding-left: 60px; } 57 | .container .offset-by-two { padding-left: 120px; } 58 | .container .offset-by-three { padding-left: 180px; } 59 | .container .offset-by-four { padding-left: 240px; } 60 | .container .offset-by-five { padding-left: 300px; } 61 | .container .offset-by-six { padding-left: 360px; } 62 | .container .offset-by-seven { padding-left: 420px; } 63 | .container .offset-by-eight { padding-left: 480px; } 64 | .container .offset-by-nine { padding-left: 540px; } 65 | .container .offset-by-ten { padding-left: 600px; } 66 | .container .offset-by-eleven { padding-left: 660px; } 67 | .container .offset-by-twelve { padding-left: 720px; } 68 | .container .offset-by-thirteen { padding-left: 780px; } 69 | .container .offset-by-fourteen { padding-left: 840px; } 70 | .container .offset-by-fifteen { padding-left: 900px; } 71 | 72 | 73 | 74 | /* #Tablet (Portrait) 75 | ================================================== */ 76 | 77 | /* Note: Design for a width of 768px */ 78 | 79 | @media only screen and (min-width: 768px) and (max-width: 959px) { 80 | .container { width: 768px; } 81 | .container .column, 82 | .container .columns { margin-left: 10px; margin-right: 10px; } 83 | .column.alpha, .columns.alpha { margin-left: 0; margin-right: 10px; } 84 | .column.omega, .columns.omega { margin-right: 0; margin-left: 10px; } 85 | .alpha.omega { margin-left: 0; margin-right: 0; } 86 | 87 | .container .one.column, 88 | .container .one.columns { width: 28px; } 89 | .container .two.columns { width: 76px; } 90 | .container .three.columns { width: 124px; } 91 | .container .four.columns { width: 172px; } 92 | .container .five.columns { width: 220px; } 93 | .container .six.columns { width: 268px; } 94 | .container .seven.columns { width: 316px; } 95 | .container .eight.columns { width: 364px; } 96 | .container .nine.columns { width: 412px; } 97 | .container .ten.columns { width: 460px; } 98 | .container .eleven.columns { width: 508px; } 99 | .container .twelve.columns { width: 556px; } 100 | .container .thirteen.columns { width: 604px; } 101 | .container .fourteen.columns { width: 652px; } 102 | .container .fifteen.columns { width: 700px; } 103 | .container .sixteen.columns { width: 748px; } 104 | 105 | .container .one-third.column { width: 236px; } 106 | .container .two-thirds.column { width: 492px; } 107 | 108 | /* Offsets */ 109 | .container .offset-by-one { padding-left: 48px; } 110 | .container .offset-by-two { padding-left: 96px; } 111 | .container .offset-by-three { padding-left: 144px; } 112 | .container .offset-by-four { padding-left: 192px; } 113 | .container .offset-by-five { padding-left: 240px; } 114 | .container .offset-by-six { padding-left: 288px; } 115 | .container .offset-by-seven { padding-left: 336px; } 116 | .container .offset-by-eight { padding-left: 384px; } 117 | .container .offset-by-nine { padding-left: 432px; } 118 | .container .offset-by-ten { padding-left: 480px; } 119 | .container .offset-by-eleven { padding-left: 528px; } 120 | .container .offset-by-twelve { padding-left: 576px; } 121 | .container .offset-by-thirteen { padding-left: 624px; } 122 | .container .offset-by-fourteen { padding-left: 672px; } 123 | .container .offset-by-fifteen { padding-left: 720px; } 124 | } 125 | 126 | 127 | /* #Mobile (Portrait) 128 | ================================================== */ 129 | 130 | /* Note: Design for a width of 320px */ 131 | 132 | @media only screen and (max-width: 767px) { 133 | .container { width: 300px; } 134 | .container .columns, 135 | .container .column { margin: 0; } 136 | 137 | .container .one.column, 138 | .container .one.columns, 139 | .container .two.columns, 140 | .container .three.columns, 141 | .container .four.columns, 142 | .container .five.columns, 143 | .container .six.columns, 144 | .container .seven.columns, 145 | .container .eight.columns, 146 | .container .nine.columns, 147 | .container .ten.columns, 148 | .container .eleven.columns, 149 | .container .twelve.columns, 150 | .container .thirteen.columns, 151 | .container .fourteen.columns, 152 | .container .fifteen.columns, 153 | .container .sixteen.columns, 154 | .container .one-third.column, 155 | .container .two-thirds.column { width: 300px; } 156 | 157 | /* Offsets */ 158 | .container .offset-by-one, 159 | .container .offset-by-two, 160 | .container .offset-by-three, 161 | .container .offset-by-four, 162 | .container .offset-by-five, 163 | .container .offset-by-six, 164 | .container .offset-by-seven, 165 | .container .offset-by-eight, 166 | .container .offset-by-nine, 167 | .container .offset-by-ten, 168 | .container .offset-by-eleven, 169 | .container .offset-by-twelve, 170 | .container .offset-by-thirteen, 171 | .container .offset-by-fourteen, 172 | .container .offset-by-fifteen { padding-left: 0; } 173 | 174 | } 175 | 176 | 177 | /* #Mobile (Landscape) 178 | ================================================== */ 179 | 180 | /* Note: Design for a width of 480px */ 181 | 182 | @media only screen and (min-width: 480px) and (max-width: 767px) { 183 | .container { width: 420px; } 184 | .container .columns, 185 | .container .column { margin: 0; } 186 | 187 | .container .one.column, 188 | .container .one.columns, 189 | .container .two.columns, 190 | .container .three.columns, 191 | .container .four.columns, 192 | .container .five.columns, 193 | .container .six.columns, 194 | .container .seven.columns, 195 | .container .eight.columns, 196 | .container .nine.columns, 197 | .container .ten.columns, 198 | .container .eleven.columns, 199 | .container .twelve.columns, 200 | .container .thirteen.columns, 201 | .container .fourteen.columns, 202 | .container .fifteen.columns, 203 | .container .sixteen.columns, 204 | .container .one-third.column, 205 | .container .two-thirds.column { width: 420px; } 206 | } 207 | 208 | 209 | /* #Clearing 210 | ================================================== */ 211 | 212 | /* Self Clearing Goodness */ 213 | .container:after { content: "\0020"; display: block; height: 0; clear: both; visibility: hidden; } 214 | 215 | /* Use clearfix class on parent to clear nested columns, 216 | or wrap each row of columns in a
*/ 217 | .clearfix:before, 218 | .clearfix:after, 219 | .row:before, 220 | .row:after { 221 | content: '\0020'; 222 | display: block; 223 | overflow: hidden; 224 | visibility: hidden; 225 | width: 0; 226 | height: 0; } 227 | .row:after, 228 | .clearfix:after { 229 | clear: both; } 230 | .row, 231 | .clearfix { 232 | zoom: 1; } 233 | 234 | /* You can also use a
to clear columns */ 235 | .clear { 236 | clear: both; 237 | display: block; 238 | overflow: hidden; 239 | visibility: hidden; 240 | width: 0; 241 | height: 0; 242 | } 243 | -------------------------------------------------------------------------------- /include/ref-napi.h: -------------------------------------------------------------------------------- 1 | #ifndef REF_NAPI_H 2 | #define REF_NAPI_H 3 | 4 | #if !defined(NAPI_VERSION) || NAPI_VERSION < 6 5 | #include 6 | #endif 7 | #include "napi.h" 8 | 9 | // The definitions in this file are intended to be used by node-ffi-napi. 10 | 11 | namespace RefNapi { 12 | 13 | class Instance { 14 | public: 15 | virtual napi_value WrapPointer(char* ptr, size_t length) = 0; 16 | virtual char* GetBufferData(napi_value val) = 0; 17 | virtual void RegisterArrayBuffer(napi_value val) = 0; 18 | }; 19 | 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /lib/get-paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const symbol = require('get-symbol-from-current-process-h'); 3 | exports.includeRaw = [path.resolve(__dirname, '..', 'include')].concat(symbol.includeRaw); 4 | exports.include = exports.includeRaw.map(x => `"${x}"`.replace(/\\/g, '\\\\')).join(' '); 5 | exports.gyp = ''; 6 | -------------------------------------------------------------------------------- /lib/ref.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const inspect = require('util').inspect; 4 | const debug = require('debug')('ref'); 5 | const os = require('os'); 6 | const path = require('path'); 7 | 8 | exports = module.exports = require('node-gyp-build')(path.join(__dirname, '..')); 9 | 10 | exports.endianness = os.endianness(); 11 | 12 | /** 13 | * A `Buffer` that references the C NULL pointer. That is, its memory address 14 | * points to 0. Its `length` is 0 because accessing any data from this buffer 15 | * would cause a _segmentation fault_. 16 | * 17 | * ``` 18 | * console.log(ref.NULL); 19 | * 20 | * ``` 21 | * 22 | * @name NULL 23 | * @type Buffer 24 | */ 25 | 26 | /** 27 | * A string that represents the native endianness of the machine's processor. 28 | * The possible values are either `"LE"` or `"BE"`. 29 | * 30 | * ``` 31 | * console.log(ref.endianness); 32 | * 'LE' 33 | * ``` 34 | * 35 | * @name endianness 36 | * @type String 37 | */ 38 | 39 | /** 40 | * Accepts a `Buffer` instance and returns the memory address of the buffer 41 | * instance. Returns a JavaScript Number, which can't hold 64-bit integers, 42 | * so this function is unsafe on 64-bit systems. 43 | * ``` 44 | * console.log(ref.address(new Buffer(1))); 45 | * 4320233616 46 | * 47 | * console.log(ref.address(ref.NULL))); 48 | * 0 49 | * ``` 50 | * 51 | * @param {Buffer} buffer The buffer to get the memory address of. 52 | * @return {Number} The memory address the buffer instance. 53 | * @name address 54 | * @type method 55 | */ 56 | 57 | /** 58 | * Accepts a `Buffer` instance and returns _true_ if the buffer represents the 59 | * NULL pointer, _false_ otherwise. 60 | * 61 | * ``` 62 | * console.log(ref.isNull(new Buffer(1))); 63 | * false 64 | * 65 | * console.log(ref.isNull(ref.NULL)); 66 | * true 67 | * ``` 68 | * 69 | * @param {Buffer} buffer The buffer to check for NULL. 70 | * @return {Boolean} true or false. 71 | * @name isNull 72 | * @type method 73 | */ 74 | 75 | /** 76 | * Reads a JavaScript Object that has previously been written to the given 77 | * _buffer_ at the given _offset_. 78 | * 79 | * ``` 80 | * var obj = { foo: 'bar' }; 81 | * var buf = ref.alloc('Object', obj); 82 | * 83 | * var obj2 = ref.readObject(buf, 0); 84 | * console.log(obj === obj2); 85 | * true 86 | * ``` 87 | * 88 | * @param {Buffer} buffer The buffer to read an Object from. 89 | * @param {Number} offset The offset to begin reading from. 90 | * @return {Object} The Object that was read from _buffer_. 91 | * @name readObject 92 | * @type method 93 | */ 94 | 95 | /** 96 | * Reads a Buffer instance from the given _buffer_ at the given _offset_. 97 | * The _size_ parameter specifies the `length` of the returned Buffer instance, 98 | * which defaults to __0__. 99 | * 100 | * ``` 101 | * var buf = new Buffer('hello world'); 102 | * var pointer = ref.alloc('pointer', buf); 103 | * 104 | * var buf2 = ref.readPointer(pointer, 0, buf.length); 105 | * console.log(buf2.toString()); 106 | * 'hello world' 107 | * ``` 108 | * 109 | * @param {Buffer} buffer The buffer to read a Buffer from. 110 | * @param {Number} offset The offset to begin reading from. 111 | * @param {Number} length (optional) The length of the returned Buffer. Defaults to 0. 112 | * @return {Buffer} The Buffer instance that was read from _buffer_. 113 | * @name readPointer 114 | * @type method 115 | */ 116 | 117 | /** 118 | * Returns a JavaScript String read from _buffer_ at the given _offset_. The 119 | * C String is read until the first NULL byte, which indicates the end of the 120 | * String. 121 | * 122 | * This function can read beyond the `length` of a Buffer. 123 | * 124 | * ``` 125 | * var buf = new Buffer('hello\0world\0'); 126 | * 127 | * var str = ref.readCString(buf, 0); 128 | * console.log(str); 129 | * 'hello' 130 | * ``` 131 | * 132 | * @param {Buffer} buffer The buffer to read a Buffer from. 133 | * @param {Number} offset The offset to begin reading from. 134 | * @return {String} The String that was read from _buffer_. 135 | * @name readCString 136 | * @type method 137 | */ 138 | 139 | /** 140 | * Returns a big-endian signed 64-bit int read from _buffer_ at the given 141 | * _offset_. 142 | * 143 | * If the returned value will fit inside a JavaScript Number without losing 144 | * precision, then a Number is returned, otherwise a String is returned. 145 | * 146 | * ``` 147 | * var buf = ref.alloc('int64'); 148 | * ref.writeInt64BE(buf, 0, '9223372036854775807'); 149 | * 150 | * var val = ref.readInt64BE(buf, 0) 151 | * console.log(val) 152 | * '9223372036854775807' 153 | * ``` 154 | * 155 | * @param {Buffer} buffer The buffer to read a Buffer from. 156 | * @param {Number} offset The offset to begin reading from. 157 | * @return {Number|String} The Number or String that was read from _buffer_. 158 | * @name readInt64BE 159 | * @type method 160 | */ 161 | 162 | /** 163 | * Returns a little-endian signed 64-bit int read from _buffer_ at the given 164 | * _offset_. 165 | * 166 | * If the returned value will fit inside a JavaScript Number without losing 167 | * precision, then a Number is returned, otherwise a String is returned. 168 | * 169 | * ``` 170 | * var buf = ref.alloc('int64'); 171 | * ref.writeInt64LE(buf, 0, '9223372036854775807'); 172 | * 173 | * var val = ref.readInt64LE(buf, 0) 174 | * console.log(val) 175 | * '9223372036854775807' 176 | * ``` 177 | * 178 | * @param {Buffer} buffer The buffer to read a Buffer from. 179 | * @param {Number} offset The offset to begin reading from. 180 | * @return {Number|String} The Number or String that was read from _buffer_. 181 | * @name readInt64LE 182 | * @type method 183 | */ 184 | 185 | /** 186 | * Returns a big-endian unsigned 64-bit int read from _buffer_ at the given 187 | * _offset_. 188 | * 189 | * If the returned value will fit inside a JavaScript Number without losing 190 | * precision, then a Number is returned, otherwise a String is returned. 191 | * 192 | * ``` 193 | * var buf = ref.alloc('uint64'); 194 | * ref.writeUInt64BE(buf, 0, '18446744073709551615'); 195 | * 196 | * var val = ref.readUInt64BE(buf, 0) 197 | * console.log(val) 198 | * '18446744073709551615' 199 | * ``` 200 | * 201 | * @param {Buffer} buffer The buffer to read a Buffer from. 202 | * @param {Number} offset The offset to begin reading from. 203 | * @return {Number|String} The Number or String that was read from _buffer_. 204 | * @name readUInt64BE 205 | * @type method 206 | */ 207 | 208 | /** 209 | * Returns a little-endian unsigned 64-bit int read from _buffer_ at the given 210 | * _offset_. 211 | * 212 | * If the returned value will fit inside a JavaScript Number without losing 213 | * precision, then a Number is returned, otherwise a String is returned. 214 | * 215 | * ``` 216 | * var buf = ref.alloc('uint64'); 217 | * ref.writeUInt64LE(buf, 0, '18446744073709551615'); 218 | * 219 | * var val = ref.readUInt64LE(buf, 0) 220 | * console.log(val) 221 | * '18446744073709551615' 222 | * ``` 223 | * 224 | * @param {Buffer} buffer The buffer to read a Buffer from. 225 | * @param {Number} offset The offset to begin reading from. 226 | * @return {Number|String} The Number or String that was read from _buffer_. 227 | * @name readUInt64LE 228 | * @type method 229 | */ 230 | 231 | /** 232 | * Writes the _input_ Number or String as a big-endian signed 64-bit int into 233 | * _buffer_ at the given _offset_. 234 | * 235 | * ``` 236 | * var buf = ref.alloc('int64'); 237 | * ref.writeInt64BE(buf, 0, '9223372036854775807'); 238 | * ``` 239 | * 240 | * @param {Buffer} buffer The buffer to write to. 241 | * @param {Number} offset The offset to begin writing from. 242 | * @param {Number|String} input This String or Number which gets written. 243 | * @name writeInt64BE 244 | * @type method 245 | */ 246 | 247 | /** 248 | * Writes the _input_ Number or String as a little-endian signed 64-bit int into 249 | * _buffer_ at the given _offset_. 250 | * 251 | * ``` 252 | * var buf = ref.alloc('int64'); 253 | * ref.writeInt64LE(buf, 0, '9223372036854775807'); 254 | * ``` 255 | * 256 | * @param {Buffer} buffer The buffer to write to. 257 | * @param {Number} offset The offset to begin writing from. 258 | * @param {Number|String} input This String or Number which gets written. 259 | * @name writeInt64LE 260 | * @type method 261 | */ 262 | 263 | /** 264 | * Writes the _input_ Number or String as a big-endian unsigned 64-bit int into 265 | * _buffer_ at the given _offset_. 266 | * 267 | * ``` 268 | * var buf = ref.alloc('uint64'); 269 | * ref.writeUInt64BE(buf, 0, '18446744073709551615'); 270 | * ``` 271 | * 272 | * @param {Buffer} buffer The buffer to write to. 273 | * @param {Number} offset The offset to begin writing from. 274 | * @param {Number|String} input This String or Number which gets written. 275 | * @name writeUInt64BE 276 | * @type method 277 | */ 278 | 279 | /** 280 | * Writes the _input_ Number or String as a little-endian unsigned 64-bit int 281 | * into _buffer_ at the given _offset_. 282 | * 283 | * ``` 284 | * var buf = ref.alloc('uint64'); 285 | * ref.writeUInt64LE(buf, 0, '18446744073709551615'); 286 | * ``` 287 | * 288 | * @param {Buffer} buffer The buffer to write to. 289 | * @param {Number} offset The offset to begin writing from. 290 | * @param {Number|String} input This String or Number which gets written. 291 | * @name writeUInt64LE 292 | * @type method 293 | */ 294 | 295 | /** 296 | * Returns a new clone of the given "type" object, with its 297 | * `indirection` level incremented by **1**. 298 | * 299 | * Say you wanted to create a type representing a `void *`: 300 | * 301 | * ``` 302 | * var voidPtrType = ref.refType(ref.types.void); 303 | * ``` 304 | * 305 | * @param {Object|String} type The "type" object to create a reference type from. Strings get coerced first. 306 | * @return {Object} The new "type" object with its `indirection` incremented by 1. 307 | */ 308 | 309 | exports.refType = function refType (type) { 310 | const _type = exports.coerceType(type); 311 | const rtn = Object.create(_type); 312 | rtn.indirection++; 313 | if (_type.name) { 314 | Object.defineProperty(rtn, 'name', { 315 | value: _type.name + '*', 316 | configurable: true, 317 | enumerable: true, 318 | writable: true 319 | }); 320 | } 321 | return rtn; 322 | } 323 | 324 | /** 325 | * Returns a new clone of the given "type" object, with its 326 | * `indirection` level decremented by 1. 327 | * 328 | * @param {Object|String} type The "type" object to create a dereference type from. Strings get coerced first. 329 | * @return {Object} The new "type" object with its `indirection` decremented by 1. 330 | */ 331 | 332 | exports.derefType = function derefType (type) { 333 | const _type = exports.coerceType(type); 334 | if (_type.indirection === 1) { 335 | throw new Error('Cannot create deref\'d type for type with indirection 1'); 336 | } 337 | let rtn = Object.getPrototypeOf(_type); 338 | if (rtn.indirection !== _type.indirection - 1) { 339 | // slow case 340 | rtn = Object.create(_type); 341 | rtn.indirection--; 342 | } 343 | return rtn; 344 | } 345 | 346 | /** 347 | * Coerces a "type" object from a String or an actual "type" object. String values 348 | * are looked up from the `ref.types` Object. So: 349 | * 350 | * * `"int"` gets coerced into `ref.types.int`. 351 | * * `"int *"` gets translated into `ref.refType(ref.types.int)` 352 | * * `ref.types.int` gets translated into `ref.types.int` (returns itself) 353 | * 354 | * Throws an Error if no valid "type" object could be determined. Most `ref` 355 | * functions use this function under the hood, so anywhere a "type" object is 356 | * expected, a String may be passed as well, including simply setting the 357 | * `buffer.type` property. 358 | * 359 | * ``` 360 | * var type = ref.coerceType('int **'); 361 | * 362 | * console.log(type.indirection); 363 | * 3 364 | * ``` 365 | * 366 | * @param {Object|String} type The "type" Object or String to coerce. 367 | * @return {Object} A "type" object 368 | */ 369 | 370 | exports.coerceType = function coerceType (type) { 371 | let rtn = type; 372 | if (typeof rtn === 'string') { 373 | rtn = exports.types[type]; 374 | if (rtn) return rtn; 375 | 376 | // strip whitespace 377 | rtn = type.replace(/\s+/g, '').toLowerCase(); 378 | if (rtn === 'pointer') { 379 | // legacy "pointer" being used :( 380 | rtn = exports.refType(exports.types.void); // void * 381 | } else if (rtn === 'string') { 382 | rtn = exports.types.CString; // special char * type 383 | } else { 384 | var refCount = 0; 385 | rtn = rtn.replace(/\*/g, function() { 386 | refCount++; 387 | return ''; 388 | }); 389 | // allow string names to be passed in 390 | rtn = exports.types[rtn]; 391 | if (refCount > 0) { 392 | if (!(rtn && 'size' in rtn && 'indirection' in rtn)) { 393 | throw new TypeError('could not determine a proper "type" from: ' + inspect(type)); 394 | } 395 | for (let i = 0; i < refCount; i++) { 396 | rtn = exports.refType(rtn); 397 | } 398 | } 399 | } 400 | } 401 | if (!(rtn && 'size' in rtn && 'indirection' in rtn)) { 402 | throw new TypeError('could not determine a proper "type" from: ' + inspect(type)); 403 | } 404 | return rtn; 405 | } 406 | 407 | /** 408 | * Returns the "type" property of the given Buffer. 409 | * Creates a default type for the buffer when none exists. 410 | * 411 | * @param {Buffer} buffer The Buffer instance to get the "type" object from. 412 | * @return {Object} The "type" object from the given Buffer. 413 | */ 414 | 415 | exports.getType = function getType (buffer) { 416 | if (!buffer.type) { 417 | debug('WARN: no "type" found on buffer, setting default "type"', buffer); 418 | buffer.type = {}; 419 | buffer.type.size = buffer.length; 420 | buffer.type.indirection = 1; 421 | buffer.type.get = function get () { 422 | throw new Error('unknown "type"; cannot get()'); 423 | }; 424 | buffer.type.set = function set () { 425 | throw new Error('unknown "type"; cannot set()'); 426 | }; 427 | } 428 | return exports.coerceType(buffer.type); 429 | } 430 | 431 | /** 432 | * Calls the `get()` function of the Buffer's current "type" (or the 433 | * passed in _type_ if present) at the given _offset_. 434 | * 435 | * This function handles checking the "indirection" level and returning a 436 | * proper "dereferenced" Bufffer instance when necessary. 437 | * 438 | * @param {Buffer} buffer The Buffer instance to read from. 439 | * @param {Number} offset (optional) The offset on the Buffer to start reading from. Defaults to 0. 440 | * @param {Object|String} type (optional) The "type" object to use when reading. Defaults to calling `getType()` on the buffer. 441 | * @return {?} Whatever value the "type" used when reading returns. 442 | */ 443 | 444 | exports.get = function get (buffer, offset, type) { 445 | if (!offset) { 446 | offset = 0; 447 | } 448 | if (type) { 449 | type = exports.coerceType(type); 450 | } else { 451 | type = exports.getType(buffer); 452 | } 453 | debug('get(): (offset: %d)', offset, buffer); 454 | assert(type.indirection > 0, `"indirection" level must be at least 1, saw ${type.indirection}`); 455 | if (type.indirection === 1) { 456 | // need to check "type" 457 | return type.get(buffer, offset); 458 | } else { 459 | // need to create a deref'd Buffer 460 | const size = type.indirection === 2 ? type.size : exports.sizeof.pointer; 461 | const reference = exports.readPointer(buffer, offset, size); 462 | reference.type = exports.derefType(type); 463 | return reference; 464 | } 465 | } 466 | 467 | /** 468 | * Calls the `set()` function of the Buffer's current "type" (or the 469 | * passed in _type_ if present) at the given _offset_. 470 | * 471 | * This function handles checking the "indirection" level writing a pointer rather 472 | * than calling the `set()` function if the indirection is greater than 1. 473 | * 474 | * @param {Buffer} buffer The Buffer instance to write to. 475 | * @param {Number} offset The offset on the Buffer to start writing to. 476 | * @param {?} value The value to write to the Buffer instance. 477 | * @param {Object|String} type (optional) The "type" object to use when reading. Defaults to calling `getType()` on the buffer. 478 | */ 479 | 480 | exports.set = function set (buffer, offset, value, type) { 481 | if (!offset) { 482 | offset = 0; 483 | } 484 | if (type) { 485 | type = exports.coerceType(type); 486 | } else { 487 | type = exports.getType(buffer); 488 | } 489 | debug('set(): (offset: %d)', offset, buffer, value); 490 | assert(type.indirection >= 1, '"indirection" level must be at least 1'); 491 | if (type.indirection === 1) { 492 | type.set(buffer, offset, value); 493 | } else { 494 | exports.writePointer(buffer, offset, value); 495 | } 496 | } 497 | 498 | 499 | /** 500 | * Returns a new Buffer instance big enough to hold `type`, 501 | * with the given `value` written to it. 502 | * 503 | * ``` js 504 | * var intBuf = ref.alloc(ref.types.int) 505 | * var int_with_4 = ref.alloc(ref.types.int, 4) 506 | * ``` 507 | * 508 | * @param {Object|String} type The "type" object to allocate. Strings get coerced first. 509 | * @param {?} value (optional) The initial value set on the returned Buffer, using _type_'s `set()` function. 510 | * @return {Buffer} A new Buffer instance with it's `type` set to "type", and (optionally) "value" written to it. 511 | */ 512 | 513 | exports.alloc = function alloc (_type, value) { 514 | var type = exports.coerceType(_type); 515 | debug('allocating Buffer for type with "size"', type.size); 516 | let size; 517 | if (type.indirection === 1) { 518 | size = type.size; 519 | } else { 520 | size = exports.sizeof.pointer; 521 | } 522 | const buffer = Buffer.alloc(size); 523 | buffer.type = type; 524 | if (arguments.length >= 2) { 525 | debug('setting value on allocated buffer', value); 526 | exports.set(buffer, 0, value, type); 527 | } 528 | return buffer; 529 | } 530 | 531 | /** 532 | * Returns a new `Buffer` instance with the given String written to it with the 533 | * given encoding (defaults to __'utf8'__). The buffer is 1 byte longer than the 534 | * string itself, and is NUL terminated. 535 | * 536 | * ``` 537 | * var buf = ref.allocCString('hello world'); 538 | * 539 | * console.log(buf.toString()); 540 | * 'hello world\u0000' 541 | * ``` 542 | * 543 | * @param {String} string The JavaScript string to be converted to a C string. 544 | * @param {String} encoding (optional) The encoding to use for the C string. Defaults to __'utf8'__. 545 | * @return {Buffer} The new `Buffer` instance with the specified String wrtten to it, and a trailing NUL byte. 546 | */ 547 | 548 | exports.allocCString = function allocCString (string, encoding) { 549 | if (null == string || (Buffer.isBuffer(string) && exports.isNull(string))) { 550 | return exports.NULL; 551 | } 552 | const size = Buffer.byteLength(string, encoding) + 1; 553 | const buffer = Buffer.allocUnsafe(size); 554 | exports.writeCString(buffer, 0, string, encoding); 555 | buffer.type = charPtrType; 556 | return buffer; 557 | } 558 | 559 | /** 560 | * Writes the given string as a C String (NULL terminated) to the given buffer 561 | * at the given offset. "encoding" is optional and defaults to __'utf8'__. 562 | * 563 | * Unlike `readCString()`, this function requires the buffer to actually have the 564 | * proper length. 565 | * 566 | * @param {Buffer} buffer The Buffer instance to write to. 567 | * @param {Number} offset The offset of the buffer to begin writing at. 568 | * @param {String} string The JavaScript String to write that will be written to the buffer. 569 | * @param {String} encoding (optional) The encoding to read the C string as. Defaults to __'utf8'__. 570 | */ 571 | 572 | exports.writeCString = function writeCString (buffer, offset, string, encoding) { 573 | assert(Buffer.isBuffer(buffer), 'expected a Buffer as the first argument'); 574 | assert.strictEqual('string', typeof string, 'expected a "string" as the third argument'); 575 | if (!offset) { 576 | offset = 0; 577 | } 578 | if (!encoding) { 579 | encoding = 'utf8'; 580 | } 581 | const size = buffer.length - offset - 1; 582 | const len = buffer.write(string, offset, size, encoding); 583 | buffer.writeUInt8(0, offset + len); // NUL terminate 584 | } 585 | 586 | exports['readInt64' + exports.endianness] = exports.readInt64; 587 | exports['readUInt64' + exports.endianness] = exports.readUInt64; 588 | exports['writeInt64' + exports.endianness] = exports.writeInt64; 589 | exports['writeUInt64' + exports.endianness] = exports.writeUInt64; 590 | 591 | var opposite = exports.endianness == 'LE' ? 'BE' : 'LE'; 592 | var int64temp = Buffer.alloc(exports.sizeof.int64); 593 | var uint64temp = Buffer.alloc(exports.sizeof.uint64); 594 | 595 | exports['readInt64' + opposite] = function (buffer, offset) { 596 | for (let i = 0; i < exports.sizeof.int64; i++) { 597 | int64temp[i] = buffer[offset + exports.sizeof.int64 - i - 1]; 598 | } 599 | return exports.readInt64(int64temp, 0); 600 | } 601 | exports['readUInt64' + opposite] = function (buffer, offset) { 602 | for (let i = 0; i < exports.sizeof.uint64; i++) { 603 | uint64temp[i] = buffer[offset + exports.sizeof.uint64 - i - 1]; 604 | } 605 | return exports.readUInt64(uint64temp, 0); 606 | } 607 | exports['writeInt64' + opposite] = function (buffer, offset, value) { 608 | exports.writeInt64(int64temp, 0, value); 609 | for (let i = 0; i < exports.sizeof.int64; i++) { 610 | buffer[offset + i] = int64temp[exports.sizeof.int64 - i - 1]; 611 | } 612 | } 613 | exports['writeUInt64' + opposite] = function (buffer, offset, value) { 614 | exports.writeUInt64(uint64temp, 0, value); 615 | for (let i = 0; i < exports.sizeof.uint64; i++) { 616 | buffer[offset + i] = uint64temp[exports.sizeof.uint64 - i - 1]; 617 | } 618 | } 619 | 620 | /** 621 | * `ref()` accepts a Buffer instance and returns a new Buffer 622 | * instance that is "pointer" sized and has its data pointing to the given 623 | * Buffer instance. Essentially the created Buffer is a "reference" to the 624 | * original pointer, equivalent to the following C code: 625 | * 626 | * ``` c 627 | * char *buf = buffer; 628 | * char **ref = &buf; 629 | * ``` 630 | * 631 | * @param {Buffer} buffer A Buffer instance to create a reference to. 632 | * @return {Buffer} A new Buffer instance pointing to _buffer_. 633 | */ 634 | 635 | exports.ref = function ref (buffer) { 636 | debug('creating a reference to buffer', buffer); 637 | var type = exports.refType(exports.getType(buffer)); 638 | return exports.alloc(type, buffer); 639 | } 640 | 641 | /** 642 | * Accepts a Buffer instance and attempts to "dereference" it. 643 | * That is, first it checks the `indirection` count of _buffer_'s "type", and if 644 | * it's greater than __1__ then it merely returns another Buffer, but with one 645 | * level less `indirection`. 646 | * 647 | * When _buffer_'s indirection is at __1__, then it checks for `buffer.type` 648 | * which should be an Object with its own `get()` function. 649 | * 650 | * ``` 651 | * var buf = ref.alloc('int', 6); 652 | * 653 | * var val = ref.deref(buf); 654 | * console.log(val); 655 | * 6 656 | * ``` 657 | * 658 | * 659 | * @param {Buffer} buffer A Buffer instance to dereference. 660 | * @return {?} The returned value after dereferencing _buffer_. 661 | */ 662 | 663 | exports.deref = function deref (buffer) { 664 | debug('dereferencing buffer', buffer); 665 | return exports.get(buffer); 666 | } 667 | 668 | const kAttachedRefs = Symbol('attached'); 669 | 670 | /** 671 | * Attaches _object_ to _buffer_ such that it prevents _object_ from being garbage 672 | * collected until _buffer_ does. 673 | * 674 | * @param {Buffer} buffer A Buffer instance to attach _object_ to. 675 | * @param {Object|Buffer} object An Object or Buffer to prevent from being garbage collected until _buffer_ does. 676 | * @api private 677 | */ 678 | 679 | exports._attach = function _attach (buf, obj) { 680 | if (!buf[kAttachedRefs]) { 681 | buf[kAttachedRefs] = []; 682 | } 683 | buf[kAttachedRefs].push(obj); 684 | } 685 | 686 | /** 687 | * @param {Buffer} buffer 688 | * @param {Number} offset 689 | * @param {Object} object 690 | * @name _writeObject 691 | * @api private 692 | */ 693 | 694 | /** 695 | * Writes a pointer to _object_ into _buffer_ at the specified _offset. 696 | * 697 | * This function "attaches" _object_ to _buffer_ to prevent it from being garbage 698 | * collected. 699 | * 700 | * ``` 701 | * var buf = ref.alloc('Object'); 702 | * ref.writeObject(buf, 0, { foo: 'bar' }); 703 | * 704 | * ``` 705 | * 706 | * @param {Buffer} buffer A Buffer instance to write _object_ to. 707 | * @param {Number} offset The offset on the Buffer to start writing at. 708 | * @param {Object} object The Object to be written into _buffer_. 709 | */ 710 | 711 | exports.writeObject = function writeObject (buf, offset, obj) { 712 | debug('writing Object to buffer', buf, offset, obj); 713 | exports._writeObject(buf, offset, obj); 714 | exports._attach(buf, obj); 715 | } 716 | 717 | 718 | /** 719 | * Same as `ref.writePointer()`, except that this version does not attach 720 | * _pointer_ to _buffer_, which is potentially unsafe if the garbage collector 721 | * runs. 722 | * 723 | * @param {Buffer} buffer A Buffer instance to write _pointer to. 724 | * @param {Number} offset The offset on the Buffer to start writing at. 725 | * @param {Buffer} pointer The Buffer instance whose memory address will be written to _buffer_. 726 | * @name _writePointer 727 | * @api private 728 | */ 729 | 730 | /** 731 | * Writes the memory address of _pointer_ to _buffer_ at the specified _offset_. 732 | * 733 | * This function "attaches" _object_ to _buffer_ to prevent it from being garbage 734 | * collected. 735 | * 736 | * ``` 737 | * var someBuffer = new Buffer('whatever'); 738 | * var buf = ref.alloc('pointer'); 739 | * ref.writePointer(buf, 0, someBuffer); 740 | * ``` 741 | * 742 | * @param {Buffer} buffer A Buffer instance to write _pointer to. 743 | * @param {Number} offset The offset on the Buffer to start writing at. 744 | * @param {Buffer} pointer The Buffer instance whose memory address will be written to _buffer_. 745 | */ 746 | 747 | exports.writePointer = function writePointer (buf, offset, ptr) { 748 | debug('writing pointer to buffer', buf, offset, ptr); 749 | // Passing true as a fourth parameter does an a stronger 750 | // version of attach which ensures ptr is only collected after 751 | // the finalizer for buf has run. See 752 | // https://github.com/node-ffi-napi/ref-napi/issues/54 753 | // for why this is necessary 754 | exports._writePointer(buf, offset, ptr, true); 755 | }; 756 | 757 | /** 758 | * Same as `ref.reinterpret()`, except that this version does not attach 759 | * _buffer_ to the returned Buffer, which is potentially unsafe if the 760 | * garbage collector runs. 761 | * 762 | * @param {Buffer} buffer A Buffer instance to base the returned Buffer off of. 763 | * @param {Number} size The `length` property of the returned Buffer. 764 | * @param {Number} offset The offset of the Buffer to begin from. 765 | * @return {Buffer} A new Buffer instance with the same memory address as _buffer_, and the requested _size_. 766 | * @name _reinterpret 767 | * @api private 768 | */ 769 | 770 | /** 771 | * Returns a new Buffer instance with the specified _size_, with the same memory 772 | * address as _buffer_. 773 | * 774 | * This function "attaches" _buffer_ to the returned Buffer to prevent it from 775 | * being garbage collected. 776 | * 777 | * @param {Buffer} buffer A Buffer instance to base the returned Buffer off of. 778 | * @param {Number} size The `length` property of the returned Buffer. 779 | * @param {Number} offset The offset of the Buffer to begin from. 780 | * @return {Buffer} A new Buffer instance with the same memory address as _buffer_, and the requested _size_. 781 | */ 782 | 783 | exports.reinterpret = function reinterpret (buffer, size, offset) { 784 | debug('reinterpreting buffer to "%d" bytes', size); 785 | const rtn = exports._reinterpret(buffer, size, offset || 0); 786 | exports._attach(rtn, buffer); 787 | return rtn; 788 | } 789 | 790 | /** 791 | * Same as `ref.reinterpretUntilZeros()`, except that this version does not 792 | * attach _buffer_ to the returned Buffer, which is potentially unsafe if the 793 | * garbage collector runs. 794 | * 795 | * @param {Buffer} buffer A Buffer instance to base the returned Buffer off of. 796 | * @param {Number} size The number of sequential, aligned `NULL` bytes that are required to terminate the buffer. 797 | * @param {Number} offset The offset of the Buffer to begin from. 798 | * @return {Buffer} A new Buffer instance with the same memory address as _buffer_, and a variable `length` that is terminated by _size_ NUL bytes. 799 | * @name _reinterpretUntilZeros 800 | * @api private 801 | */ 802 | 803 | /** 804 | * Accepts a `Buffer` instance and a number of `NULL` bytes to read from the 805 | * pointer. This function will scan past the boundary of the Buffer's `length` 806 | * until it finds `size` number of aligned `NULL` bytes. 807 | * 808 | * This is useful for finding the end of NUL-termintated array or C string. For 809 | * example, the `readCString()` function _could_ be implemented like: 810 | * 811 | * ``` 812 | * function readCString (buf) { 813 | * return ref.reinterpretUntilZeros(buf, 1).toString('utf8') 814 | * } 815 | * ``` 816 | * 817 | * This function "attaches" _buffer_ to the returned Buffer to prevent it from 818 | * being garbage collected. 819 | * 820 | * @param {Buffer} buffer A Buffer instance to base the returned Buffer off of. 821 | * @param {Number} size The number of sequential, aligned `NULL` bytes are required to terminate the buffer. 822 | * @param {Number} offset The offset of the Buffer to begin from. 823 | * @return {Buffer} A new Buffer instance with the same memory address as _buffer_, and a variable `length` that is terminated by _size_ NUL bytes. 824 | */ 825 | 826 | exports.reinterpretUntilZeros = function reinterpretUntilZeros (buffer, size, offset) { 827 | debug('reinterpreting buffer to until "%d" NULL (0) bytes are found', size); 828 | var rtn = exports._reinterpretUntilZeros(buffer, size, offset || 0); 829 | exports._attach(rtn, buffer); 830 | return rtn; 831 | }; 832 | 833 | 834 | // the built-in "types" 835 | const types = exports.types = {}; 836 | 837 | /** 838 | * The `void` type. 839 | * 840 | * @section types 841 | */ 842 | 843 | types.void = { 844 | size: 0, 845 | indirection: 1, 846 | get: function get (buf, offset) { 847 | debug('getting `void` type (returns `null`)'); 848 | return null; 849 | }, 850 | set: function set (buf, offset, val) { 851 | debug('setting `void` type (no-op)'); 852 | } 853 | }; 854 | 855 | /** 856 | * The `int8` type. 857 | */ 858 | 859 | types.int8 = { 860 | size: exports.sizeof.int8, 861 | indirection: 1, 862 | get: function get (buf, offset) { 863 | return buf.readInt8(offset || 0); 864 | }, 865 | set: function set (buf, offset, val) { 866 | if (typeof val === 'string') { 867 | val = val.charCodeAt(0); 868 | } 869 | return buf.writeInt8(val, offset || 0); 870 | } 871 | }; 872 | 873 | /** 874 | * The `uint8` type. 875 | */ 876 | 877 | types.uint8 = { 878 | size: exports.sizeof.uint8, 879 | indirection: 1, 880 | get: function get (buf, offset) { 881 | return buf.readUInt8(offset || 0); 882 | }, 883 | set: function set (buf, offset, val) { 884 | if (typeof val === 'string') { 885 | val = val.charCodeAt(0); 886 | } 887 | return buf.writeUInt8(val, offset || 0); 888 | } 889 | }; 890 | 891 | /** 892 | * The `int16` type. 893 | */ 894 | 895 | types.int16 = { 896 | size: exports.sizeof.int16, 897 | indirection: 1, 898 | get: function get (buf, offset) { 899 | return buf['readInt16' + exports.endianness](offset || 0); 900 | }, 901 | set: function set (buf, offset, val) { 902 | return buf['writeInt16' + exports.endianness](val, offset || 0); 903 | } 904 | } 905 | 906 | /** 907 | * The `uint16` type. 908 | */ 909 | 910 | types.uint16 = { 911 | size: exports.sizeof.uint16, 912 | indirection: 1, 913 | get: function get (buf, offset) { 914 | return buf['readUInt16' + exports.endianness](offset || 0); 915 | }, 916 | set: function set (buf, offset, val) { 917 | return buf['writeUInt16' + exports.endianness](val, offset || 0); 918 | } 919 | } 920 | 921 | /** 922 | * The `int32` type. 923 | */ 924 | 925 | types.int32 = { 926 | size: exports.sizeof.int32, 927 | indirection: 1, 928 | get: function get (buf, offset) { 929 | return buf['readInt32' + exports.endianness](offset || 0); 930 | }, 931 | set: function set (buf, offset, val) { 932 | return buf['writeInt32' + exports.endianness](val, offset || 0); 933 | } 934 | } 935 | 936 | /** 937 | * The `uint32` type. 938 | */ 939 | 940 | types.uint32 = { 941 | size: exports.sizeof.uint32, 942 | indirection: 1, 943 | get: function get (buf, offset) { 944 | return buf['readUInt32' + exports.endianness](offset || 0); 945 | }, 946 | set: function set (buf, offset, val) { 947 | return buf['writeUInt32' + exports.endianness](val, offset || 0); 948 | } 949 | } 950 | 951 | /** 952 | * The `int64` type. 953 | */ 954 | 955 | types.int64 = { 956 | size: exports.sizeof.int64, 957 | indirection: 1, 958 | get: function get (buf, offset) { 959 | return buf['readInt64' + exports.endianness](offset || 0); 960 | }, 961 | set: function set (buf, offset, val) { 962 | return buf['writeInt64' + exports.endianness](val, offset || 0); 963 | } 964 | } 965 | 966 | /** 967 | * The `uint64` type. 968 | */ 969 | 970 | types.uint64 = { 971 | size: exports.sizeof.uint64, 972 | indirection: 1, 973 | get: function get (buf, offset) { 974 | return buf['readUInt64' + exports.endianness](offset || 0); 975 | }, 976 | set: function set (buf, offset, val) { 977 | return buf['writeUInt64' + exports.endianness](val, offset || 0); 978 | } 979 | } 980 | 981 | /** 982 | * The `float` type. 983 | */ 984 | 985 | types.float = { 986 | size: exports.sizeof.float, 987 | indirection: 1, 988 | get: function get (buf, offset) { 989 | return buf['readFloat' + exports.endianness](offset || 0); 990 | }, 991 | set: function set (buf, offset, val) { 992 | return buf['writeFloat' + exports.endianness](val, offset || 0); 993 | } 994 | } 995 | 996 | /** 997 | * The `double` type. 998 | */ 999 | 1000 | types.double = { 1001 | size: exports.sizeof.double, 1002 | indirection: 1, 1003 | get: function get (buf, offset) { 1004 | return buf['readDouble' + exports.endianness](offset || 0) 1005 | }, 1006 | set: function set (buf, offset, val) { 1007 | return buf['writeDouble' + exports.endianness](val, offset || 0) 1008 | } 1009 | } 1010 | 1011 | /** 1012 | * The `Object` type. This can be used to read/write regular JS Objects 1013 | * into raw memory. 1014 | */ 1015 | 1016 | types.Object = { 1017 | size: exports.sizeof.Object, 1018 | indirection: 1, 1019 | get: function get (buf, offset) { 1020 | return buf.readObject(offset || 0); 1021 | }, 1022 | set: function set (buf, offset, val) { 1023 | return buf.writeObject(val, offset || 0); 1024 | } 1025 | } 1026 | 1027 | /** 1028 | * The `CString` (a.k.a `"string"`) type. 1029 | * 1030 | * CStrings are a kind of weird thing. We say it's `sizeof(char *)`, and 1031 | * `indirection` level of 1, which means that we have to return a Buffer that 1032 | * is pointer sized, and points to a some utf8 string data, so we have to create 1033 | * a 2nd "in-between" buffer. 1034 | */ 1035 | 1036 | types.CString = { 1037 | size: exports.sizeof.pointer, 1038 | alignment: exports.alignof.pointer, 1039 | indirection: 1, 1040 | get: function get (buf, offset) { 1041 | const _buf = exports.readPointer(buf, offset) 1042 | if (exports.isNull(_buf)) { 1043 | return null; 1044 | } 1045 | return exports.readCString(_buf, 0); 1046 | }, 1047 | set: function set (buf, offset, val) { 1048 | let _buf 1049 | if (Buffer.isBuffer(val)) { 1050 | _buf = val; 1051 | } else { 1052 | // assume string 1053 | _buf = exports.allocCString(val); 1054 | } 1055 | return exports.writePointer(buf, offset, _buf); 1056 | } 1057 | } 1058 | 1059 | // alias Utf8String 1060 | var utfstringwarned = false; 1061 | Object.defineProperty(types, 'Utf8String', { 1062 | enumerable: false, 1063 | configurable: true, 1064 | get: function() { 1065 | if (!utfstringwarned) { 1066 | utfstringwarned = true; 1067 | console.error('"Utf8String" type is deprecated, use "CString" instead'); 1068 | } 1069 | return types.CString; 1070 | } 1071 | }); 1072 | 1073 | /** 1074 | * The `bool` type. 1075 | * 1076 | * Wrapper type around `types.uint8` that accepts/returns `true` or 1077 | * `false` Boolean JavaScript values. 1078 | * 1079 | * @name bool 1080 | * 1081 | */ 1082 | 1083 | /** 1084 | * The `byte` type. 1085 | * 1086 | * @name byte 1087 | */ 1088 | 1089 | /** 1090 | * The `char` type. 1091 | * 1092 | * @name char 1093 | */ 1094 | 1095 | /** 1096 | * The `uchar` type. 1097 | * 1098 | * @name uchar 1099 | */ 1100 | 1101 | /** 1102 | * The `short` type. 1103 | * 1104 | * @name short 1105 | */ 1106 | 1107 | /** 1108 | * The `ushort` type. 1109 | * 1110 | * @name ushort 1111 | */ 1112 | 1113 | /** 1114 | * The `int` type. 1115 | * 1116 | * @name int 1117 | */ 1118 | 1119 | /** 1120 | * The `uint` type. 1121 | * 1122 | * @name uint 1123 | */ 1124 | 1125 | /** 1126 | * The `long` type. 1127 | * 1128 | * @name long 1129 | */ 1130 | 1131 | /** 1132 | * The `ulong` type. 1133 | * 1134 | * @name ulong 1135 | */ 1136 | 1137 | /** 1138 | * The `longlong` type. 1139 | * 1140 | * @name longlong 1141 | */ 1142 | 1143 | /** 1144 | * The `ulonglong` type. 1145 | * 1146 | * @name ulonglong 1147 | */ 1148 | 1149 | /** 1150 | * The `size_t` type. 1151 | * 1152 | * @name size_t 1153 | */ 1154 | 1155 | // "typedef"s for the variable-sized types 1156 | [ 'bool', 'byte', 'char', 'uchar', 'short', 'ushort', 'int', 'uint', 'long', 1157 | 'ulong', 'longlong', 'ulonglong', 'size_t' ].forEach(name => { 1158 | const unsigned = name === 'bool' 1159 | || name === 'byte' 1160 | || name === 'size_t' 1161 | || name[0] === 'u'; 1162 | const size = exports.sizeof[name]; 1163 | assert(size >= 1 && size <= 8); 1164 | let typeName = 'int' + (size * 8); 1165 | if (unsigned) { 1166 | typeName = 'u' + typeName; 1167 | } 1168 | const type = exports.types[typeName]; 1169 | assert(type); 1170 | exports.types[name] = Object.create(type); 1171 | }); 1172 | 1173 | // set the "alignment" property on the built-in types 1174 | Object.keys(exports.alignof).forEach((name) => { 1175 | if (name === 'pointer') 1176 | return; 1177 | exports.types[name].alignment = exports.alignof[name]; 1178 | assert(exports.types[name].alignment > 0); 1179 | }); 1180 | 1181 | // make the `bool` type work with JS true/false values 1182 | exports.types.bool.get = (function (_get) { 1183 | return function get (buf, offset) { 1184 | return _get(buf, offset) ? true : false; 1185 | } 1186 | })(exports.types.bool.get); 1187 | exports.types.bool.set = (function (_set) { 1188 | return function set (buf, offset, val) { 1189 | if (typeof val !== 'number') { 1190 | val = val ? 1 : 0; 1191 | } 1192 | return _set(buf, offset, val); 1193 | } 1194 | })(exports.types.bool.set); 1195 | 1196 | /*! 1197 | * Set the `name` property of the types. Used for debugging... 1198 | */ 1199 | 1200 | Object.keys(exports.types).forEach((name) => { 1201 | exports.types[name].name = name; 1202 | }); 1203 | 1204 | /*! 1205 | * This `char *` type is used by "allocCString()" above. 1206 | */ 1207 | 1208 | const charPtrType = exports.refType(exports.types.char); 1209 | 1210 | /*! 1211 | * Set the `type` property of the `NULL` pointer Buffer object. 1212 | */ 1213 | 1214 | exports.NULL.type = exports.types.void; 1215 | 1216 | /** 1217 | * `NULL_POINTER` is a pointer-sized `Buffer` instance pointing to `NULL`. 1218 | * Conceptually, it's equivalent to the following C code: 1219 | * 1220 | * ``` c 1221 | * char *null_pointer; 1222 | * null_pointer = NULL; 1223 | * ``` 1224 | * 1225 | * @type Buffer 1226 | */ 1227 | 1228 | exports.NULL_POINTER = exports.ref(exports.NULL); 1229 | 1230 | /** 1231 | * All these '...' comment blocks below are for the documentation generator. 1232 | * 1233 | * @section buffer 1234 | */ 1235 | 1236 | Buffer.prototype.address = function address () { 1237 | return exports.address(this, 0); 1238 | }; 1239 | 1240 | /** 1241 | * ... 1242 | */ 1243 | 1244 | Buffer.prototype.hexAddress = function hexAddress () { 1245 | return exports.hexAddress(this, 0); 1246 | }; 1247 | 1248 | /** 1249 | * ... 1250 | */ 1251 | 1252 | Buffer.prototype.isNull = function isNull () { 1253 | return exports.isNull(this, 0); 1254 | }; 1255 | 1256 | /** 1257 | * ... 1258 | */ 1259 | 1260 | Buffer.prototype.ref = function ref () { 1261 | return exports.ref(this); 1262 | }; 1263 | 1264 | /** 1265 | * ... 1266 | */ 1267 | 1268 | Buffer.prototype.deref = function deref () { 1269 | return exports.deref(this); 1270 | }; 1271 | 1272 | /** 1273 | * ... 1274 | */ 1275 | 1276 | Buffer.prototype.readObject = function readObject (offset) { 1277 | return exports.readObject(this, offset); 1278 | }; 1279 | 1280 | /** 1281 | * ... 1282 | */ 1283 | 1284 | Buffer.prototype.writeObject = function writeObject (obj, offset) { 1285 | return exports.writeObject(this, offset, obj); 1286 | }; 1287 | 1288 | /** 1289 | * ... 1290 | */ 1291 | 1292 | Buffer.prototype.readPointer = function readPointer (offset, size) { 1293 | return exports.readPointer(this, offset, size); 1294 | }; 1295 | 1296 | /** 1297 | * ... 1298 | */ 1299 | 1300 | Buffer.prototype.writePointer = function writePointer (ptr, offset) { 1301 | return exports.writePointer(this, offset, ptr); 1302 | }; 1303 | 1304 | /** 1305 | * ... 1306 | */ 1307 | 1308 | Buffer.prototype.readCString = function readCString (offset) { 1309 | return exports.readCString(this, offset); 1310 | }; 1311 | 1312 | /** 1313 | * ... 1314 | */ 1315 | 1316 | Buffer.prototype.writeCString = function writeCString (string, offset, encoding) { 1317 | return exports.writeCString(this, offset, string, encoding); 1318 | }; 1319 | 1320 | /** 1321 | * ... 1322 | */ 1323 | 1324 | Buffer.prototype.readInt64BE = function readInt64BE (offset) { 1325 | return exports.readInt64BE(this, offset); 1326 | }; 1327 | 1328 | /** 1329 | * ... 1330 | */ 1331 | 1332 | Buffer.prototype.writeInt64BE = function writeInt64BE (val, offset) { 1333 | return exports.writeInt64BE(this, offset, val); 1334 | }; 1335 | 1336 | /** 1337 | * ... 1338 | */ 1339 | 1340 | Buffer.prototype.readUInt64BE = function readUInt64BE (offset) { 1341 | return exports.readUInt64BE(this, offset); 1342 | }; 1343 | 1344 | /** 1345 | * ... 1346 | */ 1347 | 1348 | Buffer.prototype.writeUInt64BE = function writeUInt64BE (val, offset) { 1349 | return exports.writeUInt64BE(this, offset, val); 1350 | }; 1351 | 1352 | /** 1353 | * ... 1354 | */ 1355 | 1356 | Buffer.prototype.readInt64LE = function readInt64LE (offset) { 1357 | return exports.readInt64LE(this, offset); 1358 | }; 1359 | 1360 | /** 1361 | * ... 1362 | */ 1363 | 1364 | Buffer.prototype.writeInt64LE = function writeInt64LE (val, offset) { 1365 | return exports.writeInt64LE(this, offset, val); 1366 | }; 1367 | 1368 | /** 1369 | * ... 1370 | */ 1371 | 1372 | Buffer.prototype.readUInt64LE = function readUInt64LE (offset) { 1373 | return exports.readUInt64LE(this, offset); 1374 | }; 1375 | 1376 | /** 1377 | * ... 1378 | */ 1379 | 1380 | Buffer.prototype.writeUInt64LE = function writeUInt64LE (val, offset) { 1381 | return exports.writeUInt64LE(this, offset, val); 1382 | }; 1383 | 1384 | /** 1385 | * ... 1386 | */ 1387 | 1388 | Buffer.prototype.reinterpret = function reinterpret (size, offset) { 1389 | return exports.reinterpret(this, size, offset); 1390 | }; 1391 | 1392 | /** 1393 | * ... 1394 | */ 1395 | 1396 | Buffer.prototype.reinterpretUntilZeros = function reinterpretUntilZeros (size, offset) { 1397 | return exports.reinterpretUntilZeros(this, size, offset); 1398 | }; 1399 | 1400 | /** 1401 | * `ref` overwrites the default `Buffer#inspect()` function to include the 1402 | * hex-encoded memory address of the Buffer instance when invoked. 1403 | * 1404 | * This is simply a nice-to-have. 1405 | * 1406 | * **Before**: 1407 | * 1408 | * ``` js 1409 | * console.log(new Buffer('ref')); 1410 | * 1411 | * ``` 1412 | * 1413 | * **After**: 1414 | * 1415 | * ``` js 1416 | * console.log(new Buffer('ref')); 1417 | * 1418 | * ``` 1419 | */ 1420 | 1421 | var inspectSym = inspect.custom || 'inspect'; 1422 | /** 1423 | * in node 6.91, inspect.custom does not give a correct value; so in this case, don't torch the whole process. 1424 | * fixed in >6.9.2 1425 | */ 1426 | if (Buffer.prototype[inspectSym]) { 1427 | Buffer.prototype[inspectSym] = overwriteInspect(Buffer.prototype[inspectSym]); 1428 | } 1429 | 1430 | 1431 | // does SlowBuffer inherit from Buffer? (node >= v0.7.9) 1432 | if (!(exports.NULL instanceof Buffer)) { 1433 | debug('extending SlowBuffer\'s prototype since it doesn\'t inherit from Buffer.prototype'); 1434 | 1435 | /*! 1436 | * SlowBuffer convenience methods. 1437 | */ 1438 | 1439 | var SlowBuffer = require('buffer').SlowBuffer; 1440 | 1441 | SlowBuffer.prototype.address = Buffer.prototype.address; 1442 | SlowBuffer.prototype.hexAddress = Buffer.prototype.hexAddress; 1443 | SlowBuffer.prototype.isNull = Buffer.prototype.isNull; 1444 | SlowBuffer.prototype.ref = Buffer.prototype.ref; 1445 | SlowBuffer.prototype.deref = Buffer.prototype.deref; 1446 | SlowBuffer.prototype.readObject = Buffer.prototype.readObject; 1447 | SlowBuffer.prototype.writeObject = Buffer.prototype.writeObject; 1448 | SlowBuffer.prototype.readPointer = Buffer.prototype.readPointer; 1449 | SlowBuffer.prototype.writePointer = Buffer.prototype.writePointer; 1450 | SlowBuffer.prototype.readCString = Buffer.prototype.readCString; 1451 | SlowBuffer.prototype.writeCString = Buffer.prototype.writeCString; 1452 | SlowBuffer.prototype.reinterpret = Buffer.prototype.reinterpret; 1453 | SlowBuffer.prototype.reinterpretUntilZeros = Buffer.prototype.reinterpretUntilZeros; 1454 | SlowBuffer.prototype.readInt64BE = Buffer.prototype.readInt64BE; 1455 | SlowBuffer.prototype.writeInt64BE = Buffer.prototype.writeInt64BE; 1456 | SlowBuffer.prototype.readUInt64BE = Buffer.prototype.readUInt64BE; 1457 | SlowBuffer.prototype.writeUInt64BE = Buffer.prototype.writeUInt64BE; 1458 | SlowBuffer.prototype.readInt64LE = Buffer.prototype.readInt64LE; 1459 | SlowBuffer.prototype.writeInt64LE = Buffer.prototype.writeInt64LE; 1460 | SlowBuffer.prototype.readUInt64LE = Buffer.prototype.readUInt64LE; 1461 | SlowBuffer.prototype.writeUInt64LE = Buffer.prototype.writeUInt64LE; 1462 | /** 1463 | * in node 6.9.1, inspect.custom does not give a correct value; so in this case, don't torch the whole process. 1464 | * fixed in >6.9.2 1465 | */ 1466 | if (SlowBuffer.prototype[inspectSym]){ 1467 | SlowBuffer.prototype[inspectSym] = overwriteInspect(SlowBuffer.prototype[inspectSym]); 1468 | } 1469 | } 1470 | 1471 | function overwriteInspect (inspect) { 1472 | if (inspect.name === 'refinspect') { 1473 | return inspect; 1474 | } else { 1475 | return function refinspect () { 1476 | var v = inspect.apply(this, arguments); 1477 | return v.replace('Buffer', 'Buffer@0x' + this.hexAddress()); 1478 | } 1479 | } 1480 | } 1481 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ref-napi", 3 | "description": "Turn Buffer instances into \"pointers\"", 4 | "engines": { 5 | "node": ">= 10.0" 6 | }, 7 | "keywords": [ 8 | "native", 9 | "buffer", 10 | "extensions", 11 | "c++", 12 | "pointer", 13 | "reference", 14 | "dereference", 15 | "type", 16 | "int", 17 | "long", 18 | "float", 19 | "double", 20 | "byte", 21 | "64", 22 | "napi" 23 | ], 24 | "version": "3.0.3", 25 | "license": "MIT", 26 | "author": "Anna Henningsen ", 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/node-ffi-napi/ref-napi.git" 30 | }, 31 | "main": "./lib/ref.js", 32 | "scripts": { 33 | "docs": "node docs/compile", 34 | "test": "nyc mocha --expose-gc", 35 | "install": "node-gyp-build", 36 | "prebuild": "prebuildify --napi --tag-armv", 37 | "prepack": "prebuildify-ci download && ([ $(ls prebuilds | wc -l) = '5' ] || (echo 'Some prebuilds are missing'; exit 1))" 38 | }, 39 | "dependencies": { 40 | "debug": "^4.1.1", 41 | "get-symbol-from-current-process-h": "^1.0.2", 42 | "node-addon-api": "^3.0.0", 43 | "node-gyp-build": "^4.2.1" 44 | }, 45 | "devDependencies": { 46 | "dox": "0.9.0", 47 | "highlight.js": "^10.0.0", 48 | "jade": "^1.11.0", 49 | "marked": "^1.0.0", 50 | "mocha": "^7.1.1", 51 | "nyc": "^15.0.0", 52 | "prebuildify": "^3.0.4", 53 | "prebuildify-ci": "^1.0.5", 54 | "weak-napi": "^2.0.0" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/binding.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "ref-napi.h" 7 | 8 | #ifdef _WIN32 9 | #define __alignof__ __alignof 10 | #define snprintf(buf, bufSize, format, arg) _snprintf_s(buf, bufSize, _TRUNCATE, format, arg) 11 | #define strtoll _strtoi64 12 | #define strtoull _strtoui64 13 | #define PRId64 "lld" 14 | #define PRIu64 "llu" 15 | #else 16 | #ifndef __STDC_FORMAT_MACROS 17 | #define __STDC_FORMAT_MACROS 18 | #endif 19 | #include 20 | #endif 21 | 22 | 23 | using namespace Napi; 24 | 25 | namespace { 26 | 27 | #if !defined(NAPI_VERSION) || NAPI_VERSION < 6 28 | napi_status napix_set_instance_data( 29 | napi_env env, void* data, napi_finalize finalize_cb, void* finalize_hint) { 30 | typedef napi_status (*napi_set_instance_data_fn)( 31 | napi_env env, void* data, napi_finalize finalize_cb, void* finalize_hint); 32 | static const napi_set_instance_data_fn napi_set_instance_data__ = 33 | (napi_set_instance_data_fn) 34 | get_symbol_from_current_process("napi_set_instance_data"); 35 | 36 | if (napi_set_instance_data__ == nullptr) 37 | return napi_generic_failure; 38 | return napi_set_instance_data__(env, data, finalize_cb, finalize_hint); 39 | } 40 | 41 | napi_status napix_get_instance_data( 42 | napi_env env, void** data) { 43 | typedef napi_status (*napi_get_instance_data_fn)( 44 | napi_env env, void** data); 45 | static const napi_get_instance_data_fn napi_get_instance_data__ = 46 | (napi_get_instance_data_fn) 47 | get_symbol_from_current_process("napi_get_instance_data"); 48 | 49 | *data = nullptr; 50 | if (napi_get_instance_data__ == nullptr) 51 | return napi_generic_failure; 52 | return napi_get_instance_data__(env, data); 53 | } 54 | #else // NAPI_VERSION >= 6 55 | napi_status napix_set_instance_data( 56 | napi_env env, void* data, napi_finalize finalize_cb, void* finalize_hint) { 57 | return napi_set_instance_data(env, data, finalize_cb, finalize_hint); 58 | } 59 | 60 | napi_status napix_get_instance_data( 61 | napi_env env, void** data) { 62 | return napi_get_instance_data(env, data); 63 | } 64 | #endif 65 | 66 | // used by the Int64 functions to determine whether to return a Number 67 | // or String based on whether or not a Number will lose precision. 68 | // http://stackoverflow.com/q/307179/376773 69 | #define JS_MAX_INT +9007199254740992LL 70 | #define JS_MIN_INT -9007199254740992LL 71 | 72 | // mirrors deps/v8/src/objects.h. 73 | // we could use `node::Buffer::kMaxLength`, but it's not defined on node v0.6.x 74 | static const size_t kMaxLength = 0x3fffffff; 75 | 76 | enum ArrayBufferMode { 77 | AB_CREATED_BY_REF, 78 | AB_PASSED_TO_REF 79 | }; 80 | 81 | // Since Node.js v14.0.0, we have to keep a global list of all ArrayBuffer 82 | // instances that we work with, in order not to create any duplicates. 83 | // Luckily, N-API instance data is available on v14.x and above. 84 | class InstanceData final : public RefNapi::Instance { 85 | public: 86 | InstanceData(Env env) : env(env) {} 87 | 88 | struct ArrayBufferEntry { 89 | Reference ab; 90 | size_t finalizer_count; 91 | }; 92 | 93 | Env env; 94 | std::unordered_map pointer_to_orig_buffer; 95 | FunctionReference buffer_from; 96 | 97 | void RegisterArrayBuffer(napi_value val) override { 98 | ArrayBuffer buf(env, val); 99 | RegisterArrayBuffer(buf, AB_PASSED_TO_REF); 100 | } 101 | 102 | inline void RegisterArrayBuffer(ArrayBuffer buf, ArrayBufferMode mode) { 103 | char* ptr = static_cast(buf.Data()); 104 | if (ptr == nullptr) return; 105 | 106 | auto it = pointer_to_orig_buffer.find(ptr); 107 | if (it != pointer_to_orig_buffer.end()) { 108 | if (!it->second.ab.Value().IsEmpty()) { 109 | // Already have a valid entry, nothing to do. 110 | return; 111 | } 112 | it->second.ab.Reset(buf, 0); 113 | it->second.finalizer_count++; 114 | } else { 115 | pointer_to_orig_buffer.emplace(ptr, ArrayBufferEntry { 116 | Reference::New(buf, 0), 117 | 1 118 | }); 119 | } 120 | 121 | // If AB_CREATED_BY_REF, then another finalizer has been added before this 122 | // as a "real" backing store finalizer. 123 | if (mode != AB_CREATED_BY_REF) { 124 | buf.AddFinalizer([this](Env env, char* ptr) { 125 | UnregisterArrayBuffer(ptr); 126 | }, ptr); 127 | } 128 | } 129 | 130 | inline void UnregisterArrayBuffer(char* ptr) { 131 | auto it = pointer_to_orig_buffer.find(ptr); 132 | if (--it->second.finalizer_count == 0) 133 | pointer_to_orig_buffer.erase(it); 134 | } 135 | 136 | inline ArrayBuffer LookupOrCreateArrayBuffer(char* ptr, size_t length) { 137 | assert(ptr != nullptr); 138 | ArrayBuffer ab; 139 | auto it = pointer_to_orig_buffer.find(ptr); 140 | if (it != pointer_to_orig_buffer.end()) 141 | ab = it->second.ab.Value(); 142 | 143 | if (ab.IsEmpty()) { 144 | length = std::max(length, kMaxLength); 145 | ab = Buffer::New(env, ptr, length, [this](Env env, char* ptr) { 146 | UnregisterArrayBuffer(ptr); 147 | }).ArrayBuffer(); 148 | RegisterArrayBuffer(ab, AB_CREATED_BY_REF); 149 | } 150 | return ab; 151 | } 152 | 153 | napi_value WrapPointer(char* ptr, size_t length) override; 154 | char* GetBufferData(napi_value val) override; 155 | 156 | static InstanceData* Get(Env env) { 157 | void* d = nullptr; 158 | if (napix_get_instance_data(env, &d) == napi_ok) 159 | return static_cast(d); 160 | return nullptr; 161 | } 162 | }; 163 | 164 | /** 165 | * Converts an arbitrary pointer to a node Buffer with specified length 166 | */ 167 | 168 | Value WrapPointer(Env env, char* ptr, size_t length) { 169 | if (ptr == nullptr) 170 | length = 0; 171 | 172 | InstanceData* data; 173 | if (ptr != nullptr && (data = InstanceData::Get(env)) != nullptr) { 174 | ArrayBuffer ab = data->LookupOrCreateArrayBuffer(ptr, length); 175 | assert(!ab.IsEmpty()); 176 | return data->buffer_from.Call({ 177 | ab, Number::New(env, 0), Number::New(env, length) 178 | }); 179 | } 180 | 181 | return Buffer::New(env, ptr, length, [](Env,char*){}); 182 | } 183 | 184 | char* GetBufferData(Value val) { 185 | Buffer buf = val.As>(); 186 | InstanceData* data = InstanceData::Get(val.Env()); 187 | if (data != nullptr) 188 | data->RegisterArrayBuffer(buf.ArrayBuffer()); 189 | return buf.Data(); 190 | } 191 | 192 | napi_value InstanceData::WrapPointer(char* ptr, size_t length) { 193 | return ::WrapPointer(env, ptr, length); 194 | } 195 | 196 | char* InstanceData::GetBufferData(napi_value val) { 197 | return ::GetBufferData(Value(env, val)); 198 | } 199 | 200 | char* AddressForArgs(const CallbackInfo& args, size_t offset_index = 1) { 201 | Value buf = args[0]; 202 | if (!buf.IsBuffer()) { 203 | throw TypeError::New(args.Env(), "Buffer instance expected"); 204 | } 205 | 206 | int64_t offset = args[offset_index].ToNumber(); 207 | return GetBufferData(buf) + offset; 208 | } 209 | 210 | /** 211 | * Returns the pointer address as a Number of the given Buffer instance. 212 | * It's recommended to use `hexAddress()` in most cases instead of this function. 213 | * 214 | * WARNING: a JavaScript Number cannot precisely store a full 64-bit memory 215 | * address, so there's a possibility of an inaccurate value being returned 216 | * on 64-bit systems. 217 | * 218 | * args[0] - Buffer - the Buffer instance get the memory address of 219 | * args[1] - Number - optional (0) - the offset of the Buffer start at 220 | */ 221 | 222 | Value Address (const CallbackInfo& args) { 223 | char* ptr = AddressForArgs(args); 224 | uintptr_t intptr = reinterpret_cast(ptr); 225 | 226 | return Number::New(args.Env(), static_cast(intptr)); 227 | } 228 | 229 | /** 230 | * Returns the pointer address as a hexadecimal String. This function 231 | * is safe to use for displaying memory addresses, as compared to the 232 | * `address()` function which could overflow since it returns a Number. 233 | * 234 | * args[0] - Buffer - the Buffer instance get the memory address of 235 | * args[1] - Number - optional (0) - the offset of the Buffer start at 236 | */ 237 | 238 | Value HexAddress(const CallbackInfo& args) { 239 | char* ptr = AddressForArgs(args); 240 | char strbuf[30]; /* should be plenty... */ 241 | snprintf(strbuf, 30, "%p", ptr); 242 | 243 | if (strbuf[0] == '0' && strbuf[1] == 'x') { 244 | /* strip the leading "0x" from the address */ 245 | ptr = strbuf + 2; 246 | } else { 247 | ptr = strbuf; 248 | } 249 | 250 | return String::New(args.Env(), ptr); 251 | } 252 | 253 | /** 254 | * Returns "true" if the given Buffer points to nullptr, "false" otherwise. 255 | * 256 | * args[0] - Buffer - the Buffer instance to check for nullptr 257 | * args[1] - Number - optional (0) - the offset of the Buffer start at 258 | */ 259 | 260 | Value IsNull(const CallbackInfo& args) { 261 | char* ptr = AddressForArgs(args); 262 | return Boolean::New(args.Env(), ptr == nullptr); 263 | } 264 | 265 | /** 266 | * Retreives a JS Object instance that was previously stored in 267 | * the given Buffer instance at the given offset. 268 | * 269 | * args[0] - Buffer - the "buf" Buffer instance to read from 270 | * args[1] - Number - the offset from the "buf" buffer's address to read from 271 | */ 272 | 273 | Value ReadObject(const CallbackInfo& args) { 274 | char* ptr = AddressForArgs(args); 275 | 276 | if (ptr == nullptr) { 277 | throw Error::New(args.Env(), "readObject: Cannot read from nullptr pointer"); 278 | } 279 | 280 | Reference* rptr = reinterpret_cast*>(ptr); 281 | return rptr->Value(); 282 | } 283 | 284 | /** 285 | * Writes a weak reference to given Object to the given Buffer 286 | * instance and offset. 287 | * 288 | * args[0] - Buffer - the "buf" Buffer instance to write to 289 | * args[1] - Number - the offset from the "buf" buffer's address to write to 290 | * args[2] - Object - the "obj" Object which will have a new Persistent reference 291 | * created for the obj, whose memory address will be written. 292 | */ 293 | 294 | void WriteObject(const CallbackInfo& args) { 295 | Env env = args.Env(); 296 | char* ptr = AddressForArgs(args); 297 | 298 | if (ptr == nullptr) { 299 | throw Error::New(env, "readObject: Cannot write to nullptr pointer"); 300 | } 301 | 302 | Reference* rptr = reinterpret_cast*>(ptr); 303 | if (args[2].IsObject()) { 304 | Object val = args[2].As(); 305 | *rptr = std::move(Reference::New(val)); 306 | } else if (args[2].IsNull()) { 307 | rptr->Reset(); 308 | } else { 309 | throw TypeError::New(env, "WriteObject's 3rd argument needs to be an object"); 310 | } 311 | } 312 | 313 | /** 314 | * Reads the memory address of the given "buf" pointer Buffer at the specified 315 | * offset, and returns a new SlowBuffer instance from the memory address stored. 316 | * 317 | * args[0] - Buffer - the "buf" Buffer instance to read from 318 | * args[1] - Number - the offset from the "buf" buffer's address to read from 319 | * args[2] - Number - the length in bytes of the returned SlowBuffer instance 320 | */ 321 | 322 | Value ReadPointer(const CallbackInfo& args) { 323 | Env env = args.Env(); 324 | char* ptr = AddressForArgs(args); 325 | 326 | if (ptr == nullptr) { 327 | throw Error::New(env, "readPointer: Cannot read from nullptr pointer"); 328 | } 329 | 330 | int64_t size = args[2].ToNumber(); 331 | 332 | char* val = *reinterpret_cast(ptr); 333 | return WrapPointer(env, val, size); 334 | } 335 | 336 | /** 337 | * Writes the memory address of the "input" buffer (and optional offset) to the 338 | * specified "buf" buffer and offset. Essentially making "buf" hold a reference 339 | * to the "input" Buffer. 340 | * 341 | * args[0] - Buffer - the "buf" Buffer instance to write to 342 | * args[1] - Number - the offset from the "buf" buffer's address to write to 343 | * args[2] - Buffer - the "input" Buffer whose memory address will be written 344 | */ 345 | 346 | void WritePointer(const CallbackInfo& args) { 347 | Env env = args.Env(); 348 | char* ptr = AddressForArgs(args); 349 | Value input = args[2]; 350 | 351 | if (!input.IsNull() && !input.IsBuffer()) { 352 | throw TypeError::New(env, "writePointer: Buffer instance expected as third argument"); 353 | } 354 | 355 | if (input.IsNull()) { 356 | *reinterpret_cast(ptr) = nullptr; 357 | } else { 358 | if ((args.Length() == 4) && (args[3].As() == true)) { 359 | // create a node-api reference and finalizer to ensure that 360 | // the buffer whoes pointer is written can only be 361 | // collected after the finalizers for the buffer 362 | // to which the pointer was written have already run 363 | Reference* ref = new Reference; 364 | *ref = Persistent(args[2]); 365 | args[0].As().AddFinalizer([](Env env, Reference* ref) { 366 | delete ref; 367 | }, ref); 368 | } 369 | 370 | char* input_ptr = GetBufferData(input); 371 | *reinterpret_cast(ptr) = input_ptr; 372 | } 373 | } 374 | 375 | /** 376 | * Reads a machine-endian int64_t from the given Buffer at the given offset. 377 | * 378 | * args[0] - Buffer - the "buf" Buffer instance to read from 379 | * args[1] - Number - the offset from the "buf" buffer's address to read from 380 | */ 381 | 382 | Value ReadInt64(const CallbackInfo& args) { 383 | Env env = args.Env(); 384 | char* ptr = AddressForArgs(args); 385 | 386 | if (ptr == nullptr) { 387 | throw TypeError::New(env, "readInt64: Cannot read from nullptr pointer"); 388 | } 389 | 390 | int64_t val = *reinterpret_cast(ptr); 391 | 392 | if (val < JS_MIN_INT || val > JS_MAX_INT) { 393 | char strbuf[128]; 394 | snprintf(strbuf, 128, "%" PRId64, val); 395 | return String::New(env, strbuf); 396 | } else { 397 | return Number::New(env, val); 398 | } 399 | } 400 | 401 | /** 402 | * Writes the input Number/String int64 value as a machine-endian int64_t to 403 | * the given Buffer at the given offset. 404 | * 405 | * args[0] - Buffer - the "buf" Buffer instance to write to 406 | * args[1] - Number - the offset from the "buf" buffer's address to write to 407 | * args[2] - String/Number - the "input" String or Number which will be written 408 | */ 409 | 410 | void WriteInt64(const CallbackInfo& args) { 411 | Env env = args.Env(); 412 | char* ptr = AddressForArgs(args); 413 | 414 | Value in = args[2]; 415 | int64_t val; 416 | if (in.IsNumber()) { 417 | val = in.As(); 418 | } else if (in.IsString()) { 419 | char* endptr; 420 | char* str; 421 | int base = 0; 422 | std::string _str = in.As(); 423 | str = &_str[0]; 424 | 425 | errno = 0; /* To distinguish success/failure after call */ 426 | val = strtoll(str, &endptr, base); 427 | 428 | if (endptr == str) { 429 | throw TypeError::New(env, "writeInt64: no digits we found in input String"); 430 | } else if (errno == ERANGE && (val == INT64_MAX || val == INT64_MIN)) { 431 | throw TypeError::New(env, "writeInt64: input String numerical value out of range"); 432 | } else if (errno != 0 && val == 0) { 433 | char errmsg[200]; 434 | snprintf(errmsg, sizeof(errmsg), "writeInt64: %s", strerror(errno)); 435 | throw TypeError::New(env, errmsg); 436 | } 437 | } else { 438 | throw TypeError::New(env, "writeInt64: Number/String 64-bit value required"); 439 | } 440 | 441 | *reinterpret_cast(ptr) = val; 442 | } 443 | 444 | /** 445 | * Reads a machine-endian uint64_t from the given Buffer at the given offset. 446 | * 447 | * args[0] - Buffer - the "buf" Buffer instance to read from 448 | * args[1] - Number - the offset from the "buf" buffer's address to read from 449 | */ 450 | 451 | Value ReadUInt64(const CallbackInfo& args) { 452 | Env env = args.Env(); 453 | char* ptr = AddressForArgs(args); 454 | 455 | if (ptr == nullptr) { 456 | throw TypeError::New(env, "readUInt64: Cannot read from nullptr pointer"); 457 | } 458 | 459 | uint64_t val = *reinterpret_cast(ptr); 460 | 461 | if (val > JS_MAX_INT) { 462 | char strbuf[128]; 463 | snprintf(strbuf, 128, "%" PRIu64, val); 464 | return String::New(env, strbuf); 465 | } else { 466 | return Number::New(env, val); 467 | } 468 | } 469 | 470 | /** 471 | * Writes the input Number/String uint64 value as a machine-endian uint64_t to 472 | * the given Buffer at the given offset. 473 | * 474 | * args[0] - Buffer - the "buf" Buffer instance to write to 475 | * args[1] - Number - the offset from the "buf" buffer's address to write to 476 | * args[2] - String/Number - the "input" String or Number which will be written 477 | */ 478 | 479 | void WriteUInt64(const CallbackInfo& args) { 480 | Env env = args.Env(); 481 | char* ptr = AddressForArgs(args); 482 | 483 | Value in = args[2]; 484 | uint64_t val; 485 | if (in.IsNumber()) { 486 | val = static_cast(in.As()); 487 | } else if (in.IsString()) { 488 | char* endptr; 489 | char* str; 490 | int base = 0; 491 | std::string _str = in.As(); 492 | str = &_str[0]; 493 | 494 | errno = 0; /* To distinguish success/failure after call */ 495 | val = strtoull(str, &endptr, base); 496 | 497 | if (endptr == str) { 498 | throw TypeError::New(env, "writeUInt64: no digits we found in input String"); 499 | } else if (errno == ERANGE && (val == UINT64_MAX)) { 500 | throw TypeError::New(env, "writeUInt64: input String numerical value out of range"); 501 | } else if (errno != 0 && val == 0) { 502 | char errmsg[200]; 503 | snprintf(errmsg, sizeof(errmsg), "writeUInt64: %s", strerror(errno)); 504 | throw TypeError::New(env, errmsg); 505 | } 506 | } else { 507 | throw TypeError::New(env, "writeUInt64: Number/String 64-bit value required"); 508 | } 509 | 510 | *reinterpret_cast(ptr) = val; 511 | } 512 | 513 | /** 514 | * Reads a Utf8 C String from the given pointer at the given offset (or 0). 515 | * I didn't want to add this function but it ends up being necessary for reading 516 | * past a 0 or 1 length Buffer's boundary in node-ffi :\ 517 | * 518 | * args[0] - Buffer - the "buf" Buffer instance to read from 519 | * args[1] - Number - the offset from the "buf" buffer's address to read from 520 | */ 521 | 522 | Value ReadCString(const CallbackInfo& args) { 523 | Env env = args.Env(); 524 | char* ptr = AddressForArgs(args); 525 | 526 | if (ptr == nullptr) { 527 | throw Error::New(env, "readCString: Cannot read from nullptr pointer"); 528 | } 529 | 530 | return String::New(env, ptr); 531 | } 532 | 533 | /** 534 | * Returns a new Buffer instance that has the same memory address 535 | * as the given buffer, but with the specified size. 536 | * 537 | * args[0] - Buffer - the "buf" Buffer instance to read the address from 538 | * args[1] - Number - the size in bytes that the returned Buffer should be 539 | * args[2] - Number - the offset from the "buf" buffer's address to read from 540 | */ 541 | 542 | Value ReinterpretBuffer(const CallbackInfo& args) { 543 | Env env = args.Env(); 544 | char* ptr = AddressForArgs(args, 2); 545 | 546 | if (ptr == nullptr) { 547 | throw Error::New(env, "reinterpret: Cannot reinterpret from nullptr pointer"); 548 | } 549 | 550 | int64_t size = args[1].ToNumber(); 551 | 552 | return WrapPointer(env, ptr, size); 553 | } 554 | 555 | /** 556 | * Returns a new Buffer instance that has the same memory address 557 | * as the given buffer, but with a length up to the first aligned set of values of 558 | * 0 in a row for the given length. 559 | * 560 | * args[0] - Buffer - the "buf" Buffer instance to read the address from 561 | * args[1] - Number - the number of sequential 0-byte values that need to be read 562 | * args[2] - Number - the offset from the "buf" buffer's address to read from 563 | */ 564 | 565 | Value ReinterpretBufferUntilZeros(const CallbackInfo& args) { 566 | Env env = args.Env(); 567 | char* ptr = AddressForArgs(args, 2); 568 | 569 | if (ptr == nullptr) { 570 | throw Error::New(env, "reinterpretUntilZeros: Cannot reinterpret from nullptr pointer"); 571 | } 572 | 573 | uint32_t numZeros = args[1].ToNumber(); 574 | uint32_t i = 0; 575 | size_t size = 0; 576 | bool end = false; 577 | 578 | while (!end && size < kMaxLength) { 579 | end = true; 580 | for (i = 0; i < numZeros; i++) { 581 | if (ptr[size + i] != 0) { 582 | end = false; 583 | break; 584 | } 585 | } 586 | if (!end) { 587 | size += numZeros; 588 | } 589 | } 590 | 591 | return WrapPointer(env, ptr, size); 592 | } 593 | 594 | 595 | } // anonymous namespace 596 | 597 | Object Init(Env env, Object exports) { 598 | InstanceData* data = new InstanceData(env); 599 | { 600 | Value buffer_ctor = env.Global()["Buffer"]; 601 | Value buffer_from = buffer_ctor.As()["from"]; 602 | data->buffer_from.Reset(buffer_from.As(), 1); 603 | assert(!data->buffer_from.IsEmpty()); 604 | napi_status status = napix_set_instance_data( 605 | env, data, [](napi_env env, void* data, void* hint) { 606 | delete static_cast(data); 607 | }, nullptr); 608 | if (status != napi_ok) { 609 | delete data; 610 | data = nullptr; 611 | } else { 612 | // Hack around the fact that we can't reset buffer_from from the 613 | // InstanceData dtor. 614 | buffer_from.As().AddFinalizer([](Env env, InstanceData* data) { 615 | data->buffer_from.Reset(); 616 | }, data); 617 | } 618 | } 619 | exports["instance"] = External::New(env, data); 620 | 621 | // "sizeof" map 622 | Object smap = Object::New(env); 623 | // fixed sizes 624 | #define SET_SIZEOF(name, type) \ 625 | smap[ #name ] = Number::New(env, sizeof(type)); 626 | SET_SIZEOF(int8, int8_t); 627 | SET_SIZEOF(uint8, uint8_t); 628 | SET_SIZEOF(int16, int16_t); 629 | SET_SIZEOF(uint16, uint16_t); 630 | SET_SIZEOF(int32, int32_t); 631 | SET_SIZEOF(uint32, uint32_t); 632 | SET_SIZEOF(int64, int64_t); 633 | SET_SIZEOF(uint64, uint64_t); 634 | SET_SIZEOF(float, float); 635 | SET_SIZEOF(double, double); 636 | // (potentially) variable sizes 637 | SET_SIZEOF(bool, bool); 638 | SET_SIZEOF(byte, unsigned char); 639 | SET_SIZEOF(char, char); 640 | SET_SIZEOF(uchar, unsigned char); 641 | SET_SIZEOF(short, short); 642 | SET_SIZEOF(ushort, unsigned short); 643 | SET_SIZEOF(int, int); 644 | SET_SIZEOF(uint, unsigned int); 645 | SET_SIZEOF(long, long); 646 | SET_SIZEOF(ulong, unsigned long); 647 | SET_SIZEOF(longlong, long long); 648 | SET_SIZEOF(ulonglong, unsigned long long); 649 | SET_SIZEOF(pointer, char *); 650 | SET_SIZEOF(size_t, size_t); 651 | // size of a weak handle to a JS object 652 | SET_SIZEOF(Object, Reference); 653 | 654 | // "alignof" map 655 | Object amap = Object::New(env); 656 | #define SET_ALIGNOF(name, type) \ 657 | struct s_##name { type a; }; \ 658 | amap[ #name ] = Number::New(env, alignof(struct s_##name)); 659 | SET_ALIGNOF(int8, int8_t); 660 | SET_ALIGNOF(uint8, uint8_t); 661 | SET_ALIGNOF(int16, int16_t); 662 | SET_ALIGNOF(uint16, uint16_t); 663 | SET_ALIGNOF(int32, int32_t); 664 | SET_ALIGNOF(uint32, uint32_t); 665 | SET_ALIGNOF(int64, int64_t); 666 | SET_ALIGNOF(uint64, uint64_t); 667 | SET_ALIGNOF(float, float); 668 | SET_ALIGNOF(double, double); 669 | SET_ALIGNOF(bool, bool); 670 | SET_ALIGNOF(char, char); 671 | SET_ALIGNOF(uchar, unsigned char); 672 | SET_ALIGNOF(short, short); 673 | SET_ALIGNOF(ushort, unsigned short); 674 | SET_ALIGNOF(int, int); 675 | SET_ALIGNOF(uint, unsigned int); 676 | SET_ALIGNOF(long, long); 677 | SET_ALIGNOF(ulong, unsigned long); 678 | SET_ALIGNOF(longlong, long long); 679 | SET_ALIGNOF(ulonglong, unsigned long long); 680 | SET_ALIGNOF(pointer, char *); 681 | SET_ALIGNOF(size_t, size_t); 682 | SET_ALIGNOF(Object, Reference); 683 | 684 | // exports 685 | exports["sizeof"] = smap; 686 | exports["alignof"] = amap; 687 | exports["nullptr"] = exports["NULL"] = WrapPointer(env, nullptr, 0); 688 | exports["address"] = Function::New(env, Address); 689 | exports["hexAddress"] = Function::New(env, HexAddress); 690 | exports["isNull"] = Function::New(env, IsNull); 691 | exports["readObject"] = Function::New(env, ReadObject); 692 | exports["_writeObject"] = Function::New(env, WriteObject); 693 | exports["readPointer"] = Function::New(env, ReadPointer); 694 | exports["_writePointer"] = Function::New(env, WritePointer); 695 | exports["readInt64"] = Function::New(env, ReadInt64); 696 | exports["writeInt64"] = Function::New(env, WriteInt64); 697 | exports["readUInt64"] = Function::New(env, ReadUInt64); 698 | exports["writeUInt64"] = Function::New(env, WriteUInt64); 699 | exports["readCString"] = Function::New(env, ReadCString); 700 | exports["_reinterpret"] = Function::New(env, ReinterpretBuffer); 701 | exports["_reinterpretUntilZeros"] = Function::New(env, ReinterpretBufferUntilZeros); 702 | return exports; 703 | } 704 | 705 | NODE_API_MODULE(binding, Init) 706 | -------------------------------------------------------------------------------- /test/address.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const ref = require('../'); 4 | const inspect = require('util').inspect; 5 | 6 | describe('address', function() { 7 | const buf = Buffer.from('hello'); 8 | 9 | it('should return 0 for the NULL pointer', function() { 10 | assert.strictEqual(0, ref.address(ref.NULL)); 11 | }); 12 | 13 | it('should give a positive value for any other Buffer', function() { 14 | const address = ref.address(buf); 15 | assert.strictEqual(typeof address, 'number'); 16 | assert(isFinite(address)); 17 | assert(address > 0); 18 | }); 19 | 20 | it('should accept an offset value for the 2nd argument', function() { 21 | const address = ref.address(buf); 22 | assert.strictEqual(address + 0, ref.address(buf, 0)); 23 | assert.strictEqual(address + 1, ref.address(buf, 1)); 24 | assert.strictEqual(address + 2, ref.address(buf, 2)); 25 | assert.strictEqual(address + 3, ref.address(buf, 3)); 26 | assert.strictEqual(address + 4, ref.address(buf, 4)); 27 | assert.strictEqual(address + 5, ref.address(buf, 5)); 28 | }) 29 | 30 | it('should accept a negative offset value for the 2nd argument', function() { 31 | const address = ref.address(buf) 32 | assert.strictEqual(address - 0, ref.address(buf, -0)); 33 | assert.strictEqual(address - 1, ref.address(buf, -1)); 34 | assert.strictEqual(address - 2, ref.address(buf, -2)); 35 | assert.strictEqual(address - 3, ref.address(buf, -3)); 36 | assert.strictEqual(address - 4, ref.address(buf, -4)); 37 | assert.strictEqual(address - 5, ref.address(buf, -5)); 38 | }) 39 | 40 | it('should have an offset of zero when none is given', function() { 41 | assert.strictEqual(ref.address(buf), ref.address(buf, 0)); 42 | }); 43 | 44 | describe('inspect()', function() { 45 | it('should overwrite the default Buffer#inspect() to print the memory address', function() { 46 | assert(inspect(buf).includes(buf.hexAddress())); 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/alloc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const ref = require('../'); 4 | 5 | describe('alloc()', function() { 6 | it('should return a new Buffer of "bool" size', function() { 7 | const buf = ref.alloc(ref.types.bool); 8 | assert.strictEqual(ref.sizeof.bool, buf.length); 9 | }); 10 | 11 | it('should coerce string type names', function() { 12 | const buf = ref.alloc('bool'); 13 | assert.strictEqual(ref.types.bool, buf.type); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/bool.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const ref = require('../'); 4 | 5 | describe('bool', function() { 6 | const buf = ref.alloc('bool'); 7 | 8 | it('should return JS "false" for a value of 0', function() { 9 | buf[0] = 0; 10 | assert.strictEqual(false, ref.get(buf)); 11 | }); 12 | 13 | it('should return JS "true" for a value of 1', function() { 14 | buf[0] = 1; 15 | assert.strictEqual(true, ref.get(buf)); 16 | }); 17 | 18 | it('should write a JS "false" value as 0', function() { 19 | ref.set(buf, 0, false); 20 | assert.strictEqual(0, buf[0]); 21 | }); 22 | 23 | it('should write a JS "true" value as 1', function() { 24 | ref.set(buf, 0, true); 25 | assert.strictEqual(1, buf[0]); 26 | }); 27 | 28 | it('should allow uint8 number values to be written to it', function() { 29 | const val = 255; 30 | ref.set(buf, 0, val); 31 | assert.strictEqual(true, ref.get(buf)); 32 | assert.strictEqual(val, buf[0]); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/char.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const ref = require('../'); 4 | 5 | describe('char', function() { 6 | it('should accept a JS String, and write the first char\'s code', function() { 7 | const val = 'a'; 8 | 9 | let buf = ref.alloc('char', val); 10 | assert.strictEqual(val.charCodeAt(0), buf.deref()); 11 | 12 | buf = ref.alloc('uchar', val); 13 | assert.strictEqual(val.charCodeAt(0), buf.deref()); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/coerce.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const ref = require('../'); 4 | 5 | describe('coerce', function() { 6 | it('should return `ref.types.void` for "void"', function() { 7 | const type = ref.coerceType('void'); 8 | assert.strictEqual(ref.types.void, type); 9 | }); 10 | 11 | it('should return a ref type when a "*" is present', function() { 12 | const type = ref.coerceType('void *'); 13 | assert.notStrictEqual(type, ref.types.void); 14 | assert.strictEqual(type.indirection, ref.types.void.indirection + 1); 15 | }); 16 | 17 | it('should coerce the "type" property of a Buffer', function() { 18 | const buf = Buffer.alloc(ref.sizeof.int); 19 | buf.type = 'int'; 20 | const type = ref.getType(buf); 21 | assert.strictEqual(ref.types.int, type); 22 | assert.strictEqual('int', buf.type); 23 | }); 24 | 25 | it('should coerce "Object" to `ref.types.Object`', function() { 26 | assert.strictEqual(ref.types.Object, ref.coerceType('Object')); 27 | }); 28 | 29 | it('should coerce the optional type in `ref.get()`', function() { 30 | const b = Buffer.alloc(ref.sizeof.int8); 31 | b[0] = 5; 32 | assert.strictEqual(5, ref.get(b, 0, 'int8')); 33 | }); 34 | 35 | it('should coerce the optional type in `ref.set()`', function() { 36 | const b = Buffer.alloc(ref.sizeof.int8); 37 | ref.set(b, 0, 5, 'int8'); 38 | assert.strictEqual(5, b[0]); 39 | }); 40 | 41 | it('should throw a TypeError if a "type" can not be inferred', function() { 42 | assert.throws(() => { 43 | ref.coerceType({ }); 44 | }, /could not determine a proper \"type\"/); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/int64.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const ref = require('../'); 4 | 5 | describe('int64', function() { 6 | const JS_MAX_INT = +Number.MAX_SAFE_INTEGER; 7 | const JS_MIN_INT = -Number.MIN_SAFE_INTEGER; 8 | 9 | it('should allow simple ints to be written and read', function() { 10 | const buf = Buffer.alloc(ref.sizeof.int64); 11 | const val = 123456789; 12 | ref.writeInt64(buf, 0, val); 13 | const rtn = ref.readInt64(buf, 0); 14 | assert.strictEqual(val, rtn); 15 | }); 16 | 17 | it('should allow INT64_MAX to be written and read', function() { 18 | const buf = Buffer.alloc(ref.sizeof.int64); 19 | const val = '9223372036854775807'; 20 | ref.writeInt64(buf, 0, val); 21 | const rtn = ref.readInt64(buf, 0); 22 | assert.strictEqual(val, rtn); 23 | }); 24 | 25 | it('should allow a hex String to be input (signed)', function() { 26 | const buf = Buffer.alloc(ref.sizeof.int64); 27 | const val = '-0x1234567890'; 28 | ref.writeInt64(buf, 0, val); 29 | const rtn = ref.readInt64(buf, 0); 30 | assert.strictEqual(parseInt(val, 16), rtn); 31 | }); 32 | 33 | it('should allow an octal String to be input (signed)', function() { 34 | const buf = Buffer.alloc(ref.sizeof.int64); 35 | const val = '-0777'; 36 | ref.writeInt64(buf, 0, val); 37 | const rtn = ref.readInt64(buf, 0); 38 | assert.strictEqual(parseInt(val, 8), rtn); 39 | }); 40 | 41 | it('should allow a hex String to be input (unsigned)', function() { 42 | const buf = Buffer.alloc(ref.sizeof.uint64); 43 | const val = '0x1234567890'; 44 | ref.writeUInt64(buf, 0, val); 45 | const rtn = ref.readUInt64(buf, 0); 46 | assert.strictEqual(parseInt(val, 16), rtn); 47 | }); 48 | 49 | it('should allow an octal String to be input (unsigned)', function() { 50 | const buf = Buffer.alloc(ref.sizeof.uint64); 51 | const val = '0777'; 52 | ref.writeUInt64(buf, 0, val); 53 | const rtn = ref.readUInt64(buf, 0); 54 | assert.strictEqual(parseInt(val, 8), rtn); 55 | }); 56 | 57 | it('should return a Number when reading JS_MIN_INT', function() { 58 | const buf = Buffer.alloc(ref.sizeof.int64); 59 | ref.writeInt64(buf, 0, JS_MIN_INT); 60 | const rtn = ref.readInt64(buf, 0); 61 | assert.strictEqual('number', typeof rtn); 62 | assert.strictEqual(JS_MIN_INT, rtn); 63 | }); 64 | 65 | it('should return a Number when reading JS_MAX_INT', function() { 66 | const buf = Buffer.alloc(ref.sizeof.int64); 67 | ref.writeInt64(buf, 0, JS_MAX_INT); 68 | const rtn = ref.readInt64(buf, 0); 69 | assert.strictEqual('number', typeof rtn); 70 | assert.strictEqual(JS_MAX_INT, rtn); 71 | }); 72 | 73 | it('should return a String when reading JS_MAX_INT+1', function() { 74 | const buf = Buffer.alloc(ref.sizeof.int64); 75 | const plus_one = '9007199254740993'; 76 | ref.writeInt64(buf, 0, plus_one); 77 | const rtn = ref.readInt64(buf, 0); 78 | assert.strictEqual('string', typeof rtn); 79 | assert.strictEqual(plus_one, rtn); 80 | }); 81 | 82 | it('should return a String when reading JS_MIN_INT-1', function() { 83 | const buf = Buffer.alloc(ref.sizeof.int64); 84 | const minus_one = '-9007199254740993'; 85 | ref.writeInt64(buf, 0, minus_one); 86 | const rtn = ref.readInt64(buf, 0); 87 | assert.strictEqual('string', typeof rtn); 88 | assert.strictEqual(minus_one, rtn); 89 | }); 90 | 91 | it('should return a Number when reading 0, even when written as a String', function() { 92 | const buf = Buffer.alloc(ref.sizeof.int64); 93 | const zero = '0'; 94 | ref.writeInt64(buf, 0, zero); 95 | const rtn = ref.readInt64(buf, 0); 96 | assert.strictEqual('number', typeof rtn); 97 | assert.strictEqual(0, rtn); 98 | }); 99 | 100 | it('should throw a "no digits" Error when writing an invalid String (signed)', function() { 101 | assert.throws(() => { 102 | const buf = Buffer.alloc(ref.sizeof.int64); 103 | ref.writeInt64(buf, 0, 'foo'); 104 | }, /no digits we found in input String/); 105 | }); 106 | 107 | it('should throw a "no digits" Error when writing an invalid String (unsigned)', function() { 108 | assert.throws(() => { 109 | const buf = Buffer.alloc(ref.sizeof.uint64); 110 | ref.writeUInt64(buf, 0, 'foo'); 111 | }, /no digits we found in input String/); 112 | }); 113 | 114 | it('should throw an "out of range" Error when writing an invalid String (signed)', function() { 115 | let e; 116 | try { 117 | const buf = Buffer.alloc(ref.sizeof.int64) 118 | ref.writeInt64(buf, 0, '10000000000000000000000000') 119 | } catch (_e) { 120 | e = _e; 121 | } 122 | assert(/input String numerical value out of range/.test(e.message)); 123 | }); 124 | 125 | it('should throw an "out of range" Error when writing an invalid String (unsigned)', function() { 126 | let e; 127 | try { 128 | const buf = Buffer.alloc(ref.sizeof.uint64); 129 | ref.writeUInt64(buf, 0, '10000000000000000000000000'); 130 | } catch (_e) { 131 | e = _e; 132 | } 133 | assert(/input String numerical value out of range/.test(e.message)); 134 | }); 135 | 136 | it('should throw an Error when reading an int64_t from the NULL pointer', function() { 137 | assert.throws(() => { 138 | ref.readInt64(ref.NULL); 139 | }); 140 | }); 141 | 142 | it('should throw an Error when reading an uint64_t from the NULL pointer', function() { 143 | assert.throws(() => { 144 | ref.readUInt64(ref.NULL); 145 | }); 146 | }); 147 | 148 | ['LE', 'BE'].forEach((endianness) => { 149 | describe(endianness, function() { 150 | it('should read and write a signed ' + endianness + ' 64-bit integer', function() { 151 | const val = -123456789; 152 | const buf = Buffer.alloc(ref.sizeof.int64); 153 | ref['writeInt64' + endianness](buf, 0, val); 154 | assert.strictEqual(val, ref['readInt64' + endianness](buf, 0)); 155 | }); 156 | 157 | it('should read and write an unsigned ' + endianness + ' 64-bit integer', function() { 158 | const val = 123456789; 159 | const buf = Buffer.alloc(ref.sizeof.uint64); 160 | ref['writeUInt64' + endianness](buf, 0, val); 161 | assert.strictEqual(val, ref['readUInt64' + endianness](buf, 0)); 162 | }); 163 | }); 164 | }); 165 | }); 166 | -------------------------------------------------------------------------------- /test/iojs3issue.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const ref = require('../'); 4 | 5 | // This will check if the new Buffer implementation behaves like the pre io.js 3.0 one did: 6 | describe('iojs3issue', function() { 7 | it('should not crash', function() { 8 | for (let i = 0; i < 10; i++) { 9 | gc(); 10 | const buf = Buffer.alloc(8); 11 | const buf2 = ref.ref(buf); 12 | const buf3 = ref.deref(buf2); 13 | } 14 | }); 15 | 16 | it('should not crash too', function() { 17 | for (let i = 0; i < 10; i++) { 18 | gc(); 19 | const buf = Buffer.alloc(7); 20 | const buf2 = ref.ref(buf); 21 | const buf3 = ref.deref(buf2); 22 | } 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/isNull.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const ref = require('../'); 4 | 5 | describe('isNull', function() { 6 | it('should return "true" for the NULL pointer', function() { 7 | assert.strictEqual(true, ref.isNull(ref.NULL)); 8 | }); 9 | 10 | it('should return "false" for a valid Buffer', function() { 11 | const buf = Buffer.from('hello'); 12 | assert.strictEqual(false, ref.isNull(buf)); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/object.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const ref = require('../'); 4 | let weak; try { weak = require('weak-napi'); } catch (e) {} 5 | 6 | describe('Object', function() { 7 | const obj = { 8 | foo: 'bar', 9 | test: Math.random(), 10 | now: new Date() 11 | }; 12 | 13 | beforeEach(gc); 14 | 15 | it('should write and read back an Object in a Buffer', function() { 16 | const buf = Buffer.alloc(ref.sizeof.Object); 17 | ref.writeObject(buf, 0, obj); 18 | const out = ref.readObject(buf); 19 | assert.strictEqual(obj, out); 20 | assert.deepEqual(obj, out); 21 | }) 22 | 23 | it('should retain references to written Objects', function (done) { 24 | if (weak === undefined) 25 | return this.skip('weak not avaialbe'); 26 | let o_gc = false; 27 | let buf_gc = false; 28 | let o = { foo: 'bar' }; 29 | let buf = Buffer.alloc(ref.sizeof.Object); 30 | 31 | weak(o, () => { o_gc = true }); 32 | weak(buf, () => { buf_gc = true }); 33 | ref.writeObject(buf, 0, o); 34 | assert(!o_gc, '"o" has been garbage collected too soon'); 35 | assert(!buf_gc, '"buf" has been garbage collected too soon'); 36 | 37 | // try to GC `o` 38 | o = null; 39 | gc(); 40 | assert(!o_gc, '"o" has been garbage collected too soon'); 41 | assert(!buf_gc, '"buf" has been garbage collected too soon'); 42 | 43 | // now GC `buf` 44 | buf = null; 45 | gc(); 46 | setImmediate(() => { 47 | setImmediate(() => { 48 | assert(buf_gc, '"buf" has not been garbage collected'); 49 | gc(); 50 | setImmediate(() => { 51 | setImmediate(() => { 52 | assert(o_gc, '"o" has not been garbage collected'); 53 | done(); 54 | }); 55 | }); 56 | }); 57 | }); 58 | }); 59 | 60 | it('should throw an Error when reading an Object from the NULL pointer', function() { 61 | assert.throws(() => { 62 | ref.NULL.readObject(); 63 | }); 64 | }); 65 | 66 | describe('offset', function() { 67 | it('should read two Objects next to each other in memory', function() { 68 | const buf = Buffer.alloc(ref.sizeof.Object * 2); 69 | const a = {}; 70 | const b = {}; 71 | buf.writeObject(a, 0 * ref.sizeof.Object); 72 | buf.writeObject(b, 1 * ref.sizeof.Object); 73 | const _a = buf.readObject(0 * ref.sizeof.Object); 74 | const _b = buf.readObject(1 * ref.sizeof.Object); 75 | assert.strictEqual(a, _a); 76 | assert.strictEqual(b, _b); 77 | }); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /test/pointer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const ref = require('../'); 4 | let weak; try { weak = require('weak-napi'); } catch (e) {} 5 | 6 | describe('pointer', function() { 7 | const test = Buffer.from('hello world'); 8 | 9 | beforeEach(gc); 10 | 11 | it('should write and read back a pointer (Buffer) in a Buffer', function() { 12 | const buf = Buffer.alloc(ref.sizeof.pointer); 13 | ref.writePointer(buf, 0, test) 14 | const out = ref.readPointer(buf, 0, test.length); 15 | assert.strictEqual(out.length, test.length) 16 | for (let i = 0, l = out.length; i < l; i++) { 17 | assert.strictEqual(out[i], test[i]) 18 | } 19 | assert.strictEqual(ref.address(out), ref.address(test)) 20 | }); 21 | 22 | it('should retain references to a written pointer in a Buffer', function (done) { 23 | if (weak === undefined) 24 | return this.skip('weak not avaialbe'); 25 | let child_gc = false; 26 | let parent_gc = false; 27 | let child = Buffer.from('a pointer holding some data...'); 28 | let parent = Buffer.alloc(ref.sizeof.pointer); 29 | 30 | weak(child, () => { child_gc = true }); 31 | weak(parent, () => { parent_gc = true }); 32 | ref.writePointer(parent, 0, child); 33 | assert(!child_gc, '"child" has been garbage collected too soon'); 34 | assert(!parent_gc, '"parent" has been garbage collected too soon'); 35 | 36 | // try to GC `child` 37 | child = null; 38 | gc(); 39 | assert(!child_gc, '"child" has been garbage collected too soon'); 40 | assert(!parent_gc, '"parent" has been garbage collected too soon'); 41 | 42 | // now GC `parent` 43 | parent = null; 44 | gc(); 45 | setImmediate(() => { 46 | setImmediate(() => { 47 | assert(parent_gc, '"parent" has not been garbage collected'); 48 | gc(); 49 | setImmediate(() => { 50 | setImmediate(() => { 51 | assert(child_gc, '"child" has not been garbage collected'); 52 | done(); 53 | }); 54 | }); 55 | }); 56 | }); 57 | }); 58 | 59 | it('should throw an Error when reading from the NULL pointer', function() { 60 | assert.throws(() => { 61 | ref.NULL.readPointer(); 62 | }); 63 | }); 64 | 65 | it('should return a 0-length Buffer when reading a NULL pointer', function() { 66 | const buf = Buffer.alloc(ref.sizeof.pointer); 67 | ref.writePointer(buf, 0, ref.NULL); 68 | const out = ref.readPointer(buf, 0, 100); 69 | assert.strictEqual(out.length, 0); 70 | }) 71 | 72 | describe('offset', function() { 73 | it('should read two pointers next to each other in memory', function() { 74 | const buf = Buffer.alloc(ref.sizeof.pointer * 2); 75 | const a = Buffer.from('hello'); 76 | const b = Buffer.from('world'); 77 | buf.writePointer(a, 0 * ref.sizeof.pointer); 78 | buf.writePointer(b, 1 * ref.sizeof.pointer); 79 | const _a = buf.readPointer(0 * ref.sizeof.pointer); 80 | const _b = buf.readPointer(1 * ref.sizeof.pointer); 81 | assert.strictEqual(a.address(), _a.address()); 82 | assert.strictEqual(b.address(), _b.address()); 83 | }); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /test/ref-deref.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const ref = require('../'); 4 | 5 | describe('ref(), deref()', function() { 6 | beforeEach(gc); 7 | 8 | it('should work 1 layer deep', function() { 9 | const test = Buffer.from('one layer deep'); 10 | const one = ref.ref(test); 11 | const _test = ref.deref(one); 12 | assert.strictEqual(test.length, _test.length); 13 | assert.strictEqual(test.toString(), _test.toString()); 14 | }); 15 | 16 | it('should work 2 layers deep', function() { 17 | const test = Buffer.from('two layers deep'); 18 | const one = ref.ref(test); 19 | const two = ref.ref(one); 20 | const _one = ref.deref(two); 21 | const _test = ref.deref(_one); 22 | assert.strictEqual(ref.address(one), ref.address(_one)); 23 | assert.strictEqual(ref.address(test), ref.address(_test)); 24 | assert.strictEqual(one.length, _one.length); 25 | assert.strictEqual(test.length, _test.length); 26 | assert.strictEqual(test.toString(), _test.toString()); 27 | }); 28 | 29 | it('should throw when derefing a Buffer with no "type"', function() { 30 | const test = Buffer.from('???'); 31 | assert.throws(() => { 32 | ref.deref(test); 33 | }, /unknown "type"/); 34 | }); 35 | 36 | it('should throw when derefing a Buffer with no "type" 2', function() { 37 | const test = Buffer.from('???'); 38 | const r = ref.ref(test); 39 | const _test = ref.deref(r); 40 | assert.strictEqual(ref.address(test), ref.address(_test)); 41 | assert.throws(() => { 42 | ref.deref(_test) 43 | }, /unknown "type"/); 44 | }) 45 | 46 | it('should deref() a "char" type properly', function() { 47 | const test = Buffer.alloc(ref.sizeof.char); 48 | test.type = ref.types.char; 49 | test[0] = 50; 50 | assert.strictEqual(50, ref.deref(test)); 51 | test[0] = 127; 52 | assert.strictEqual(127, ref.deref(test)); 53 | }); 54 | 55 | it('should not throw when calling ref()/deref() on a `void` type', function() { 56 | const test = ref.alloc(ref.types.void); 57 | assert.strictEqual(null, test.deref()); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /test/reinterpret.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const ref = require('../'); 4 | let weak; try { weak = require('weak-napi'); } catch (e) {} 5 | 6 | describe('reinterpret()', function() { 7 | beforeEach(gc); 8 | 9 | it('should return a new Buffer instance at the same address', function() { 10 | const buf = Buffer.from('hello world'); 11 | const small = buf.slice(0, 0); 12 | assert.strictEqual(0, small.length); 13 | assert.strictEqual(buf.address(), small.address()); 14 | const reinterpreted = small.reinterpret(buf.length); 15 | assert.strictEqual(buf.address(), reinterpreted.address()); 16 | assert.strictEqual(buf.length, reinterpreted.length); 17 | assert.strictEqual(buf.toString(), reinterpreted.toString()); 18 | }) 19 | 20 | it('should return a new Buffer instance starting at the offset address', function() { 21 | const buf = Buffer.from('hello world'); 22 | const offset = 3; 23 | const small = buf.slice(offset, buf.length); 24 | assert.strictEqual(buf.length - offset, small.length); 25 | assert.strictEqual(buf.address() + offset, small.address()); 26 | const reinterpreted = buf.reinterpret(small.length, offset); 27 | assert.strictEqual(small.address(), reinterpreted.address()); 28 | assert.strictEqual(small.length, reinterpreted.length); 29 | assert.strictEqual(small.toString(), reinterpreted.toString()); 30 | }) 31 | 32 | it('should retain a reference to the original Buffer when reinterpreted', function() { 33 | if (weak === undefined) 34 | return this.skip('weak not avaialbe'); 35 | let origGCd = false; 36 | let otherGCd = false; 37 | let buf = Buffer.alloc(1); 38 | weak(buf, () => { origGCd = true; }); 39 | let other = buf.reinterpret(0); 40 | weak(other, () => { otherGCd = true; }); 41 | 42 | assert(!origGCd, '"buf" has been garbage collected too soon'); 43 | assert(!otherGCd, '"other" has been garbage collected too soon'); 44 | 45 | // try to GC `buf` 46 | buf = null; 47 | gc(); 48 | assert(!origGCd, '"buf" has been garbage collected too soon'); 49 | assert(!otherGCd, '"other" has been garbage collected too soon'); 50 | 51 | // now GC `other` 52 | other = null; 53 | gc(); 54 | setImmediate(() => { 55 | setImmediate(() => { 56 | assert(otherGCd, '"other" has not been garbage collected'); 57 | assert(origGCd, '"buf" has not been garbage collected'); 58 | }); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /test/reinterpretUntilZeros.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const fs = require('fs'); 3 | const assert = require('assert'); 4 | const ref = require('../'); 5 | 6 | describe('reinterpretUntilZeros()', function() { 7 | beforeEach(gc); 8 | 9 | it('should return a new Buffer instance up until the first 0', function() { 10 | const buf = Buffer.from('hello\0world'); 11 | const buf2 = buf.reinterpretUntilZeros(1); 12 | assert.strictEqual(buf2.length, 'hello'.length); 13 | assert.strictEqual(buf2.toString(), 'hello'); 14 | }) 15 | 16 | it('should return a new Buffer instance up until the first 0 starting from offset', function() { 17 | const buf = Buffer.from('hello\0world'); 18 | const buf2 = buf.reinterpretUntilZeros(1, 3); 19 | assert.strictEqual(buf2.length, 'lo'.length); 20 | assert.strictEqual(buf2.toString(), 'lo'); 21 | }) 22 | 23 | it('should return a new Buffer instance up until the first 2-byte sequence of 0s', function() { 24 | const str = 'hello world'; 25 | const buf = Buffer.alloc(50); 26 | const len = buf.write(str, 'ucs2'); 27 | buf.writeInt16LE(0, len); // NULL terminate the string 28 | 29 | const buf2 = buf.reinterpretUntilZeros(2); 30 | assert.strictEqual(str.length, buf2.length / 2); 31 | assert.strictEqual(buf2.toString('ucs2'), str); 32 | }) 33 | 34 | it('should return a large Buffer instance > 10,000 bytes with UTF16-LE char bytes', function() { 35 | const data = fs.readFileSync(__dirname + '/utf16le.bin'); 36 | const strBuf = ref.reinterpretUntilZeros(data, 2); 37 | assert(strBuf.length > 10000); 38 | const str = strBuf.toString('ucs2'); 39 | // the data in `utf16le.bin` should be a JSON parsable string 40 | assert(JSON.parse(str)); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/string.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const ref = require('../'); 4 | 5 | describe('C string', function() { 6 | describe('readCString()', function() { 7 | it('should return "" for a Buffer containing "\\0"', function() { 8 | const buf = Buffer.from('\0'); 9 | assert.strictEqual('', buf.readCString(0)); 10 | }); 11 | 12 | it('should return "hello" for a Buffer containing "hello\\0world"', function() { 13 | const buf = Buffer.from('hello\0world'); 14 | assert.strictEqual('hello', buf.readCString(0)); 15 | }); 16 | 17 | it('should throw an Error when reading from the NULL pointer', function() { 18 | assert.throws(() => { 19 | ref.NULL.readCString(); 20 | }); 21 | }); 22 | }); 23 | 24 | describe('writeCString()', function() { 25 | it('should write a C string (NULL terminated) to a Buffer', function() { 26 | const buf = Buffer.alloc(20); 27 | const str = 'hello world'; 28 | buf.writeCString(str); 29 | for (let i = 0; i < str.length; i++) { 30 | assert.strictEqual(str.charCodeAt(i), buf[i]); 31 | } 32 | assert.strictEqual(0, buf[str.length]); 33 | }); 34 | 35 | it('should not write the terminating 0 out of bounds', function() { 36 | const wholebuf = Buffer.alloc(20, 127); 37 | const buf = wholebuf.subarray(0, 10); 38 | const str = 'hello world'; 39 | buf.writeCString(str); 40 | for (let i = 0; i < buf.length - 1; i++) { 41 | assert.strictEqual(str.charCodeAt(i), buf[i]); 42 | } 43 | assert.strictEqual(0, buf[buf.length - 1]); 44 | for (let i = buf.length; i < wholebuf.length; i++) { 45 | assert.strictEqual(127, wholebuf[i]); 46 | } 47 | }); 48 | }); 49 | 50 | describe('allocCString()', function() { 51 | it('should return a new Buffer containing the given string', function() { 52 | const buf = ref.allocCString('hello world'); 53 | assert.strictEqual('hello world', buf.readCString()); 54 | }); 55 | 56 | it('should return the NULL pointer for `null` values', function() { 57 | const buf = ref.allocCString(null); 58 | assert(buf.isNull()); 59 | assert.strictEqual(0, buf.address()); 60 | }); 61 | 62 | it('should return the NULL pointer for `undefined` values', function() { 63 | const buf = ref.allocCString(undefined); 64 | assert(buf.isNull()); 65 | assert.strictEqual(0, buf.address()); 66 | }); 67 | 68 | it('should return the NULL pointer for a NULL pointer Buffer', function() { 69 | const buf = ref.allocCString(ref.NULL); 70 | assert(buf.isNull()); 71 | assert.strictEqual(0, buf.address()); 72 | }) 73 | }); 74 | 75 | describe('CString', function() { 76 | it('should return JS `null` when given a pointer pointing to NULL', function() { 77 | const buf = ref.alloc(ref.types.CString); 78 | buf.writePointer(ref.NULL); 79 | assert.strictEqual(null, buf.deref()); 80 | 81 | // another version of the same test 82 | assert.strictEqual(null, ref.get(ref.NULL_POINTER, 0, ref.types.CString)); 83 | }); 84 | 85 | it('should read a utf8 string from a Buffer', function() { 86 | const str = 'hello world'; 87 | const buf = ref.alloc(ref.types.CString); 88 | buf.writePointer(Buffer.from(str + '\0')); 89 | assert.strictEqual(str, buf.deref()); 90 | }); 91 | 92 | // https://github.com/node-ffi/node-ffi/issues/169 93 | it('should set a Buffer as backing store', function() { 94 | const str = 'hey!'; 95 | const store = Buffer.from(str + '\0'); 96 | const buf = ref.alloc(ref.types.CString); 97 | ref.set(buf, 0, store); 98 | 99 | assert.strictEqual(str, ref.get(buf, 0)); 100 | }); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /test/types.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const ref = require('../'); 4 | 5 | describe('types', function() { 6 | describe('refType()', function() { 7 | it('should return a new "type" with its `indirection` level increased by 1', function() { 8 | const int = ref.types.int; 9 | const intPtr = ref.refType(int); 10 | assert.strictEqual(int.size, intPtr.size); 11 | assert.strictEqual(int.indirection + 1, intPtr.indirection); 12 | }); 13 | 14 | it('should coerce string types', function() { 15 | const intPtr = ref.refType('int'); 16 | assert.strictEqual(2, intPtr.indirection); 17 | assert.strictEqual(intPtr.size, ref.types.int.size); 18 | }); 19 | 20 | it('should override and update a read-only name property', function() { 21 | // a type similar to ref-struct's StructType 22 | // used for types refType name property test 23 | function StructType() {} 24 | StructType.size = 0; 25 | StructType.indirection = 0; 26 | 27 | // read-only name property 28 | assert.strictEqual(StructType.name, 'StructType') 29 | try { 30 | StructType.name = 'foo'; 31 | } catch (err) { 32 | // ignore 33 | } 34 | assert.strictEqual(StructType.name, 'StructType'); 35 | 36 | // name property should be writable and updated 37 | const newObj = ref.refType(StructType); 38 | const newProp = Object.getOwnPropertyDescriptor(newObj, 'name'); 39 | assert.strictEqual(newProp.writable, true); 40 | assert.strictEqual(newObj.name, 'StructType*'); 41 | }); 42 | }); 43 | 44 | describe('derefType()', function() { 45 | it('should return a new "type" with its `indirection` level decreased by 1', function() { 46 | const intPtr = Object.create(ref.types.int); 47 | intPtr.indirection++; 48 | const int = ref.derefType(intPtr); 49 | assert.strictEqual(intPtr.size, intPtr.size); 50 | assert.strictEqual(intPtr.indirection - 1, int.indirection); 51 | }); 52 | 53 | it('should throw an Error when given a "type" with its `indirection` level already at 1', function() { 54 | assert.throws(() => { 55 | ref.derefType(ref.types.int); 56 | }); 57 | }); 58 | }); 59 | 60 | describe('size', function() { 61 | Object.keys(ref.types).forEach((name) => { 62 | if (name === 'void') return; 63 | it('sizeof(' + name + ') should be >= 1', function() { 64 | const type = ref.types[name]; 65 | assert.strictEqual('number', typeof type.size); 66 | assert(type.size >= 1); 67 | }); 68 | }); 69 | }); 70 | 71 | describe('alignment', function() { 72 | Object.keys(ref.types).forEach(function (name) { 73 | if (name === 'void') return; 74 | it('alignof(' + name + ') should be >= 1', function() { 75 | const type = ref.types[name]; 76 | assert.strictEqual('number', typeof type.alignment); 77 | assert(type.alignment >= 1); 78 | }); 79 | }); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /test/uint64.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const ref = require('../'); 4 | 5 | describe('uint64', function() { 6 | const JS_MAX_INT = +Number.MAX_SAFE_INTEGER; 7 | const JS_MIN_INT = -Number.MIN_SAFE_INTEGER; 8 | 9 | it('should allow simple ints to be written and read', function() { 10 | const buf = Buffer.alloc(ref.sizeof.uint64); 11 | const val = 123456789; 12 | ref.writeUInt64(buf, 0, val); 13 | const rtn = ref.readUInt64(buf, 0); 14 | assert.strictEqual(val, rtn); 15 | }); 16 | 17 | it('should allow UINT64_MAX to be written and read', function() { 18 | const buf = Buffer.alloc(ref.sizeof.uint64); 19 | const val = '18446744073709551615'; 20 | ref.writeUInt64(buf, 0, val); 21 | const rtn = ref.readUInt64(buf, 0); 22 | assert.strictEqual(val, rtn); 23 | }); 24 | 25 | it('should allow a hex String to be input (signed)', function() { 26 | const buf = Buffer.alloc(ref.sizeof.uint64); 27 | const val = '-0x1234567890'; 28 | const val_as_uint64 = '18446743995522058096'; 29 | ref.writeUInt64(buf, 0, val); 30 | const rtn = ref.readUInt64(buf, 0); 31 | assert.strictEqual(val_as_uint64, rtn); 32 | }); 33 | 34 | it('should allow an octal String to be input (signed)', function() { 35 | const buf = Buffer.alloc(ref.sizeof.uint64); 36 | const val = '-0777'; 37 | const val_as_uint64 = '18446744073709551105'; 38 | ref.writeUInt64(buf, 0, val); 39 | const rtn = ref.readUInt64(buf, 0); 40 | assert.strictEqual(val_as_uint64, rtn); 41 | }); 42 | 43 | it('should allow a hex String to be input (unsigned)', function() { 44 | const buf = Buffer.alloc(ref.sizeof.uint64); 45 | const val = '0x1234567890'; 46 | ref.writeUInt64(buf, 0, val); 47 | const rtn = ref.readUInt64(buf, 0); 48 | assert.strictEqual(parseInt(val, 16), rtn); 49 | }); 50 | 51 | it('should allow an octal String to be input (unsigned)', function() { 52 | const buf = Buffer.alloc(ref.sizeof.uint64); 53 | const val = '0777'; 54 | ref.writeUInt64(buf, 0, val); 55 | const rtn = ref.readUInt64(buf, 0); 56 | assert.strictEqual(parseInt(val, 8), rtn); 57 | }); 58 | 59 | it('should return a Number when reading JS_MIN_INT', function() { 60 | const buf = Buffer.alloc(ref.sizeof.uint64); 61 | ref.writeUInt64(buf, 0, JS_MIN_INT); 62 | const rtn = ref.readUInt64(buf, 0); 63 | assert.strictEqual('number', typeof rtn); 64 | assert.strictEqual(JS_MIN_INT, rtn); 65 | }); 66 | 67 | it('should return a Number when reading JS_MAX_INT', function() { 68 | const buf = Buffer.alloc(ref.sizeof.uint64); 69 | ref.writeUInt64(buf, 0, JS_MAX_INT); 70 | const rtn = ref.readUInt64(buf, 0); 71 | assert.strictEqual('number', typeof rtn); 72 | assert.strictEqual(JS_MAX_INT, rtn); 73 | }); 74 | 75 | it('should return a String when reading JS_MAX_INT+1', function() { 76 | const buf = Buffer.alloc(ref.sizeof.uint64); 77 | const plus_one = '9007199254740993'; 78 | ref.writeUInt64(buf, 0, plus_one); 79 | const rtn = ref.readUInt64(buf, 0); 80 | assert.strictEqual('string', typeof rtn); 81 | assert.strictEqual(plus_one, rtn); 82 | }); 83 | 84 | it('should return a String when reading JS_MIN_INT-1', function() { 85 | const buf = Buffer.alloc(ref.sizeof.uint64); 86 | const minus_one = '-9007199254740993'; 87 | // uint64_t interpretation of JS_MIN_INT 88 | const minus_one_uint64 = '18437736874454810623'; 89 | ref.writeUInt64(buf, 0, minus_one); 90 | const rtn = ref.readUInt64(buf, 0); 91 | assert.strictEqual('string', typeof rtn); 92 | assert.strictEqual(minus_one_uint64, rtn); 93 | }); 94 | 95 | it('should return a Number when reading 0, even when written as a String', function() { 96 | const buf = Buffer.alloc(ref.sizeof.uint64); 97 | const zero = '0'; 98 | ref.writeUInt64(buf, 0, zero); 99 | const rtn = ref.readUInt64(buf, 0); 100 | assert.strictEqual('number', typeof rtn); 101 | assert.strictEqual(0, rtn); 102 | }); 103 | 104 | it('should throw a "no digits" Error when writing an invalid String (unsigned)', function() { 105 | assert.throws(() => { 106 | const buf = Buffer.alloc(ref.sizeof.uint64); 107 | ref.writeUInt64(buf, 0, 'foo'); 108 | }, /no digits we found in input String/); 109 | }); 110 | 111 | it('should throw an "out of range" Error when writing an invalid String (signed)', function() { 112 | let e; 113 | try { 114 | const buf = Buffer.alloc(ref.sizeof.uint64) 115 | ref.writeUInt64(buf, 0, '-10000000000000000000000000') 116 | } catch (_e) { 117 | e = _e; 118 | } 119 | assert(/input String numerical value out of range/.test(e.message)); 120 | }); 121 | 122 | it('should throw an "out of range" Error when writing an invalid String (unsigned)', function() { 123 | let e; 124 | try { 125 | const buf = Buffer.alloc(ref.sizeof.uint64); 126 | ref.writeUInt64(buf, 0, '10000000000000000000000000'); 127 | } catch (_e) { 128 | e = _e; 129 | } 130 | assert(/input String numerical value out of range/.test(e.message)); 131 | }); 132 | 133 | it('should throw an Error when reading an uint64_t from the NULL pointer', function() { 134 | assert.throws(() => { 135 | ref.readUInt64(ref.NULL); 136 | }); 137 | }); 138 | 139 | }); 140 | -------------------------------------------------------------------------------- /test/utf16le.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-ffi-napi/ref-napi/a7f62a4caa3e0c32aeb66c015641b0bc01ba03ab/test/utf16le.bin --------------------------------------------------------------------------------