├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── README.md ├── appveyor.yml ├── lib └── struct.js ├── package.json └── test ├── binding.gyp ├── struct.js └── struct_tests.cc /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /test/build 3 | /?.js 4 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "asi": true, 3 | "laxcomma": true, 4 | "node": true, 5 | "strict": false 6 | } 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /test -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | env: 4 | - CXX=g++-4.8 5 | addons: 6 | apt: 7 | sources: 8 | - ubuntu-toolchain-r-test 9 | packages: 10 | - g++-4.8 11 | 12 | language: node_js 13 | 14 | node_js: 15 | - "4" 16 | - "5" 17 | - "6" 18 | - "7" 19 | - "8" 20 | 21 | install: 22 | - PATH="`npm bin`:`npm bin -g`:$PATH" 23 | # Node 0.8 comes with a too obsolete npm 24 | - if [[ "`node --version`" =~ ^v0\.8\. ]]; then npm install -g npm@1.4.28 ; fi 25 | # Install dependencies and build 26 | - npm install 27 | 28 | script: 29 | # Output useful info for debugging 30 | - node --version 31 | - npm --version 32 | # Run tests 33 | - npm test 34 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 1.1.0 / 2016-08-02 3 | ================== 4 | 5 | * [[`eb4550fdaa`](https://github.com/TooTallNate/ref-struct/commit/eb4550fdaa)] - Implement `pack` option (Lee, SungUk) 6 | * [[`17c1c7a8ef`](https://github.com/TooTallNate/ref-struct/commit/17c1c7a8ef)] - fix StructType constructor name in README (typo) (David Corticchiato) 7 | * [[`54b72bde07`](https://github.com/TooTallNate/ref-struct/commit/54b72bde07)] - **appveyor**: fix node v0.8, test io.js v2.5 and v3 (Nathan Rajlich) 8 | 9 | 1.0.2 / 2015-08-27 10 | ================== 11 | 12 | * appveyor: drop v0.11, iojs v1.5.1, test x64 13 | * travis: drop v0.11, test iojs v2.5 and v3 14 | * package: add license attribute (#17, @pdehaan) 15 | * package: update "ref-array" to v1.1.2 16 | * package: update "nan" v2 for native tests 17 | * README: use SVG for appveyor badge 18 | 19 | 1.0.1 / 2015-03-24 20 | ================== 21 | 22 | * removed travis testing for node_js version "0.6" and added 0.12 and iojs 23 | * added appveyor test versions as per node-ffi library for iojs & 0.12 24 | * package: allow any "ref" v "1" 25 | * npmignore: add `test` dir 26 | 27 | 1.0.0 / 2014-11-03 28 | ================== 29 | 30 | * bumping to v1.0.0 for better-defined semver semantics 31 | 32 | 0.0.7 / 2014-11-03 33 | ================== 34 | 35 | * gitignore: ignore single letter ?.js test files 36 | * lib: only slice buffer in set() in the non-instance case 37 | * package: allow any "debug" v2 38 | 39 | 0.0.6 / 2014-06-19 40 | ================== 41 | 42 | * package: update "ref" to v0.3.2 43 | * package: update "debug" to v1.0.1 44 | * test: remove v8 namespace import 45 | * test: use "bindings" module to load the native tests 46 | * README: add appveyor build badge 47 | * History: match `git changelog` syntax 48 | * package: loosely pin the deps 49 | * test: nan-ify tests 50 | * README: fix Travis badge 51 | * README: use svg for Travis badge 52 | * travis: test node v0.8, v0.10, and v0.11 53 | * add appveyor.yml file for Windows testing 54 | * package: remove "engines" field 55 | * package: beautify 56 | * add a couple comments 57 | 58 | 0.0.5 / 2013-01-24 59 | ================== 60 | 61 | * rename the backing buffer property to `ref.buffer` 62 | * add .jshintrc file 63 | * some minor optimizations 64 | 65 | 0.0.4 / 2012-09-26 66 | ================== 67 | 68 | * struct: correct the field alignment logic (TJ Fontaine) 69 | * test: add failing test from #1 70 | * test: more stucts with arrays tests 71 | * add support for "ref-array" types 72 | * add `toObject()`, `toJSON()`, and `inspect()` functions to struct instances 73 | * change `_pointer` to `buffer` 74 | * don't allow types with size == 0 like 'void' 75 | * test: add test case using "void *" as the type 76 | * test: fix deprecation warning 77 | * package: use the -C switch on node-gyp for the `npm test` command 78 | * travis: test node v0.7 and node v0.8 79 | * adjust the custom `toString()` output 80 | 81 | 0.0.3 / 2012-06-01 82 | ================== 83 | 84 | * set the "name" property of StructType instances 85 | * add a `toString()` override 86 | * fix a bug in the alignment calculation logic 87 | 88 | 0.0.2 / 2012-05-16 89 | ================== 90 | 91 | * Windows support (only the test suite needed tweaks) 92 | * make ref().deref() work 93 | * make string type identifiers work (type coersion) 94 | * don't make the constructors' prototype inherit from `Function.protoype` 95 | 96 | 0.0.1 / 2012-05-09 97 | ================== 98 | 99 | * Initial release 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ref-struct 2 | ========== 3 | ### Create ABI-compliant "[struct][]" instances on top of Buffers 4 | [![Build Status](https://secure.travis-ci.org/TooTallNate/ref-struct.svg)](https://travis-ci.org/TooTallNate/ref-struct) 5 | [![Build Status](https://ci.appveyor.com/api/projects/status/6v4h5v5kh9kmtke8?svg=true)](https://ci.appveyor.com/project/TooTallNate/ref-struct) 6 | 7 | 8 | This module offers a "struct" implementation on top of Node.js Buffers 9 | using the ref "type" interface. 10 | 11 | Installation 12 | ------------ 13 | 14 | Install with `npm`: 15 | 16 | ``` bash 17 | $ npm install ref-struct 18 | ``` 19 | 20 | 21 | Examples 22 | -------- 23 | 24 | Say you wanted to emulate the `timeval` struct from the stdlib: 25 | 26 | ``` c 27 | struct timeval { 28 | time_t tv_sec; /* seconds since Jan. 1, 1970 */ 29 | suseconds_t tv_usec; /* and microseconds */ 30 | }; 31 | ``` 32 | 33 | ``` js 34 | var ref = require('ref') 35 | var StructType = require('ref-struct') 36 | 37 | // define the time types 38 | var time_t = ref.types.long 39 | var suseconds_t = ref.types.long 40 | 41 | // define the "timeval" struct type 42 | var timeval = StructType({ 43 | tv_sec: time_t, 44 | tv_usec: suseconds_t 45 | }) 46 | 47 | // now we can create instances of it 48 | var tv = new timeval 49 | ``` 50 | 51 | #### With `node-ffi` 52 | 53 | This gets very powerful when combined with `node-ffi` to invoke C functions: 54 | 55 | ``` js 56 | var ffi = require('ffi') 57 | 58 | var tv = new timeval 59 | gettimeofday(tv.ref(), null) 60 | ``` 61 | 62 | #### Progressive API 63 | 64 | You can build up a Struct "type" incrementally (useful when interacting with a 65 | parser) using the `defineProperty()` function. But as soon as you _create_ an 66 | instance of the struct type, then the struct type is finalized, and no more 67 | properties may be added to it. 68 | 69 | ``` js 70 | var ref = require('ref') 71 | var StructType = require('ref-struct') 72 | 73 | var MyStruct = StructType() 74 | MyStruct.defineProperty('width', ref.types.int) 75 | MyStruct.defineProperty('height', ref.types.int) 76 | 77 | var i = new MyStruct({ width: 5, height: 10 }) 78 | 79 | MyStruct.defineProperty('weight', ref.types.int) 80 | // AssertionError: an instance of this Struct type has already been created, cannot add new "fields" anymore 81 | // at Function.defineProperty (/Users/nrajlich/ref-struct/lib/struct.js:180:3) 82 | ``` 83 | 84 | 85 | License 86 | ------- 87 | 88 | (The MIT License) 89 | 90 | Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net> 91 | 92 | Permission is hereby granted, free of charge, to any person obtaining 93 | a copy of this software and associated documentation files (the 94 | 'Software'), to deal in the Software without restriction, including 95 | without limitation the rights to use, copy, modify, merge, publish, 96 | distribute, sublicense, and/or sell copies of the Software, and to 97 | permit persons to whom the Software is furnished to do so, subject to 98 | the following conditions: 99 | 100 | The above copyright notice and this permission notice shall be 101 | included in all copies or substantial portions of the Software. 102 | 103 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 104 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 105 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 106 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 107 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 108 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 109 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 110 | 111 | [struct]: http://wikipedia.org/wiki/Struct_(C_programming_language) 112 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # http://www.appveyor.com/docs/appveyor-yml 2 | 3 | # Test against these versions of Node.js. 4 | environment: 5 | # Visual Studio Version 6 | MSVS_VERSION: 2013 7 | # Test against these versions of Node.js and io.js 8 | matrix: 9 | - nodejs_version: "4" 10 | - nodejs_version: "6" 11 | - nodejs_version: "7" 12 | - nodejs_version: "8" 13 | 14 | platform: 15 | - x86 16 | - x64 17 | 18 | # Install scripts. (runs after repo cloning) 19 | install: 20 | # Get the latest stable version of Node 0.STABLE.latest 21 | - ps: if($env:nodejs_version -eq "0.8") {Install-Product node $env:nodejs_version} 22 | - ps: if($env:nodejs_version -ne "0.8") {Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version)} 23 | # Node 0.8 comes with a too obsolete npm 24 | - IF %nodejs_version% == 0.8 (npm install -g npm@1.4.28) 25 | # Install latest NPM only for node.js versions until built in node-gyp adds io.js support 26 | # Update is required for node.js 0.8 because built in npm(node-gyp) does not know VS2013 27 | - IF %nodejs_version% LSS 1 (npm install -g npm@2) 28 | - IF %nodejs_version% LSS 1 set PATH=%APPDATA%\npm;%PATH% 29 | # Typical npm stuff. 30 | - npm install --msvs_version=%MSVS_VERSION% 31 | 32 | # Post-install test scripts. 33 | test_script: 34 | # Output useful info for debugging. 35 | - node --version 36 | - npm --version 37 | # run tests 38 | - npm test 39 | 40 | # Don't actually build. 41 | build: off 42 | 43 | # Set build version format here instead of in the admin panel. 44 | version: "{build}" 45 | -------------------------------------------------------------------------------- /lib/struct.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * An interface for modeling and instantiating C-style data structures. This is 4 | * not a constructor per-say, but a constructor generator. It takes an array of 5 | * tuples, the left side being the type, and the right side being a field name. 6 | * The order should be the same order it would appear in the C-style struct 7 | * definition. It returns a function that can be used to construct an object that 8 | * reads and writes to the data structure using properties specified by the 9 | * initial field list. 10 | * 11 | * The only verboten field names are "ref", which is used used on struct 12 | * instances as a function to retrieve the backing Buffer instance of the 13 | * struct, and "ref.buffer" which contains the backing Buffer instance. 14 | * 15 | * 16 | * Example: 17 | * 18 | * ``` javascript 19 | * var ref = require('ref') 20 | * var Struct = require('ref-struct') 21 | * 22 | * // create the `char *` type 23 | * var charPtr = ref.refType(ref.types.char) 24 | * var int = ref.types.int 25 | * 26 | * // create the struct "type" / constructor 27 | * var PasswordEntry = Struct({ 28 | * 'username': 'string' 29 | * , 'password': 'string' 30 | * , 'salt': int 31 | * }) 32 | * 33 | * // create an instance of the struct, backed a Buffer instance 34 | * var pwd = new PasswordEntry() 35 | * pwd.username = 'ricky' 36 | * pwd.password = 'rbransonlovesnode.js' 37 | * pwd.salt = (Math.random() * 1000000) | 0 38 | * 39 | * pwd.username // → 'ricky' 40 | * pwd.password // → 'rbransonlovesnode.js' 41 | * pwd.salt // → 820088 42 | * ``` 43 | */ 44 | 45 | /** 46 | * Module dependencies. 47 | */ 48 | 49 | var ref = require('ref') 50 | var util = require('util') 51 | var assert = require('assert') 52 | var debug = require('debug')('ref:struct') 53 | 54 | /** 55 | * Module exports. 56 | */ 57 | 58 | module.exports = Struct 59 | 60 | /** 61 | * The Struct "type" meta-constructor. 62 | */ 63 | 64 | function Struct () { 65 | debug('defining new struct "type"') 66 | 67 | /** 68 | * This is the "constructor" of the Struct type that gets returned. 69 | * 70 | * Invoke it with `new` to create a new Buffer instance backing the struct. 71 | * Pass it an existing Buffer instance to use that as the backing buffer. 72 | * Pass in an Object containing the struct fields to auto-populate the 73 | * struct with the data. 74 | */ 75 | 76 | function StructType (arg, data) { 77 | if (!(this instanceof StructType)) { 78 | return new StructType(arg, data) 79 | } 80 | debug('creating new struct instance') 81 | var store 82 | if (Buffer.isBuffer(arg)) { 83 | debug('using passed-in Buffer instance to back the struct', arg) 84 | assert(arg.length >= StructType.size, 'Buffer instance must be at least ' + 85 | StructType.size + ' bytes to back this struct type') 86 | store = arg 87 | arg = data 88 | } else { 89 | debug('creating new Buffer instance to back the struct (size: %d)', StructType.size) 90 | store = new Buffer(StructType.size) 91 | } 92 | 93 | // set the backing Buffer store 94 | store.type = StructType 95 | this['ref.buffer'] = store 96 | 97 | if (arg) { 98 | for (var key in arg) { 99 | // hopefully hit the struct setters 100 | this[key] = arg[key] 101 | } 102 | } 103 | StructType._instanceCreated = true 104 | } 105 | 106 | // make instances inherit from the `proto` 107 | StructType.prototype = Object.create(proto, { 108 | constructor: { 109 | value: StructType 110 | , enumerable: false 111 | , writable: true 112 | , configurable: true 113 | } 114 | }) 115 | 116 | StructType.defineProperty = defineProperty 117 | StructType.toString = toString 118 | StructType.fields = {} 119 | 120 | var opt = (arguments.length > 0 && arguments[1]) ? arguments[1] : {}; 121 | // Setup the ref "type" interface. The constructor doubles as the "type" object 122 | StructType.size = 0 123 | StructType.alignment = 0 124 | StructType.indirection = 1 125 | StructType.isPacked = opt.packed ? Boolean(opt.packed) : false 126 | StructType.get = get 127 | StructType.set = set 128 | 129 | // Read the fields list and apply all the fields to the struct 130 | // TODO: Better arg handling... (maybe look at ES6 binary data API?) 131 | var arg = arguments[0] 132 | if (Array.isArray(arg)) { 133 | // legacy API 134 | arg.forEach(function (a) { 135 | var type = a[0] 136 | var name = a[1] 137 | StructType.defineProperty(name, type) 138 | }) 139 | } else if (typeof arg === 'object') { 140 | Object.keys(arg).forEach(function (name) { 141 | var type = arg[name] 142 | StructType.defineProperty(name, type) 143 | }) 144 | } 145 | 146 | return StructType 147 | } 148 | 149 | /** 150 | * The "get" function of the Struct "type" interface 151 | */ 152 | 153 | function get (buffer, offset) { 154 | debug('Struct "type" getter for buffer at offset', buffer, offset) 155 | if (offset > 0) { 156 | buffer = buffer.slice(offset) 157 | } 158 | return new this(buffer) 159 | } 160 | 161 | /** 162 | * The "set" function of the Struct "type" interface 163 | */ 164 | 165 | function set (buffer, offset, value) { 166 | debug('Struct "type" setter for buffer at offset', buffer, offset, value) 167 | var isStruct = value instanceof this 168 | if (isStruct) { 169 | // optimization: copy the buffer contents directly rather 170 | // than going through the ref-struct constructor 171 | value['ref.buffer'].copy(buffer, offset, 0, this.size) 172 | } else { 173 | if (offset > 0) { 174 | buffer = buffer.slice(offset) 175 | } 176 | new this(buffer, value) 177 | } 178 | } 179 | 180 | /** 181 | * Custom `toString()` override for struct type instances. 182 | */ 183 | 184 | function toString () { 185 | return '[StructType]' 186 | } 187 | 188 | /** 189 | * Adds a new field to the struct instance with the given name and type. 190 | * Note that this function will throw an Error if any instances of the struct 191 | * type have already been created, therefore this function must be called at the 192 | * beginning, before any instances are created. 193 | */ 194 | 195 | function defineProperty (name, type) { 196 | debug('defining new struct type field', name) 197 | 198 | // allow string types for convenience 199 | type = ref.coerceType(type) 200 | 201 | assert(!this._instanceCreated, 'an instance of this Struct type has already ' + 202 | 'been created, cannot add new "fields" anymore') 203 | assert.equal('string', typeof name, 'expected a "string" field name') 204 | assert(type && /object|function/i.test(typeof type) && 'size' in type && 205 | 'indirection' in type 206 | , 'expected a "type" object describing the field type: "' + type + '"') 207 | assert(type.indirection > 1 || type.size > 0, 208 | '"type" object must have a size greater than 0') 209 | assert(!(name in this.prototype), 'the field "' + name + 210 | '" already exists in this Struct type') 211 | 212 | var field = { 213 | type: type 214 | } 215 | this.fields[name] = field 216 | 217 | // define the getter/setter property 218 | var desc = { enumerable: true , configurable: true } 219 | desc.get = function () { 220 | debug('getting "%s" struct field (offset: %d)', name, field.offset) 221 | return ref.get(this['ref.buffer'], field.offset, type) 222 | } 223 | desc.set = function (value) { 224 | debug('setting "%s" struct field (offset: %d)', name, field.offset, value) 225 | return ref.set(this['ref.buffer'], field.offset, value, type) 226 | } 227 | 228 | // calculate the new size and field offsets 229 | recalc(this) 230 | 231 | Object.defineProperty(this.prototype, name, desc) 232 | } 233 | 234 | function recalc (struct) { 235 | 236 | // reset size and alignment 237 | struct.size = 0 238 | struct.alignment = 0 239 | 240 | var fieldNames = Object.keys(struct.fields) 241 | 242 | // first loop through is to determine the `alignment` of this struct 243 | fieldNames.forEach(function (name) { 244 | var field = struct.fields[name] 245 | var type = field.type 246 | var alignment = type.alignment || ref.alignof.pointer 247 | if (type.indirection > 1) { 248 | alignment = ref.alignof.pointer 249 | } 250 | if (struct.isPacked) { 251 | struct.alignment = Math.min(struct.alignment || alignment, alignment) 252 | } else { 253 | struct.alignment = Math.max(struct.alignment, alignment) 254 | } 255 | }) 256 | 257 | // second loop through sets the `offset` property on each "field" 258 | // object, and sets the `struct.size` as we go along 259 | fieldNames.forEach(function (name) { 260 | var field = struct.fields[name] 261 | var type = field.type 262 | 263 | if (null != type.fixedLength) { 264 | // "ref-array" types set the "fixedLength" prop. don't treat arrays like one 265 | // contiguous entity. instead, treat them like individual elements in the 266 | // struct. doing this makes the padding end up being calculated correctly. 267 | field.offset = addType(type.type) 268 | for (var i = 1; i < type.fixedLength; i++) { 269 | addType(type.type) 270 | } 271 | } else { 272 | field.offset = addType(type) 273 | } 274 | }) 275 | 276 | function addType (type) { 277 | var offset = struct.size 278 | var align = type.indirection === 1 ? type.alignment : ref.alignof.pointer 279 | var padding = struct.isPacked ? 0 : (align - (offset % align)) % align 280 | var size = type.indirection === 1 ? type.size : ref.sizeof.pointer 281 | 282 | offset += padding 283 | 284 | if (!struct.isPacked) { 285 | assert.equal(offset % align, 0, "offset should align") 286 | } 287 | 288 | // adjust the "size" of the struct type 289 | struct.size = offset + size 290 | 291 | // return the calulated offset 292 | return offset 293 | } 294 | 295 | // any final padding? 296 | var left = struct.size % struct.alignment 297 | if (left > 0) { 298 | debug('additional padding to the end of struct:', struct.alignment - left) 299 | struct.size += struct.alignment - left 300 | } 301 | } 302 | 303 | /** 304 | * this is the custom prototype of Struct type instances. 305 | */ 306 | 307 | var proto = {} 308 | 309 | /** 310 | * set a placeholder variable on the prototype so that defineProperty() will 311 | * throw an error if you try to define a struct field with the name "buffer". 312 | */ 313 | 314 | proto['ref.buffer'] = ref.NULL 315 | 316 | /** 317 | * Flattens the Struct instance into a regular JavaScript Object. This function 318 | * "gets" all the defined properties. 319 | * 320 | * @api public 321 | */ 322 | 323 | proto.toObject = function toObject () { 324 | var obj = {} 325 | Object.keys(this.constructor.fields).forEach(function (k) { 326 | obj[k] = this[k] 327 | }, this) 328 | return obj 329 | } 330 | 331 | /** 332 | * Basic `JSON.stringify(struct)` support. 333 | */ 334 | 335 | proto.toJSON = function toJSON () { 336 | return this.toObject() 337 | } 338 | 339 | /** 340 | * `.inspect()` override. For the REPL. 341 | * 342 | * @api public 343 | */ 344 | 345 | proto.inspect = function inspect () { 346 | var obj = this.toObject() 347 | // add instance's "own properties" 348 | Object.keys(this).forEach(function (k) { 349 | obj[k] = this[k] 350 | }, this) 351 | return util.inspect(obj) 352 | } 353 | 354 | /** 355 | * returns a Buffer pointing to this struct data structure. 356 | */ 357 | 358 | proto.ref = function ref () { 359 | return this['ref.buffer'] 360 | } 361 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ref-struct", 3 | "description": "Create ABI-compliant \"struct\" instances on top of Buffers", 4 | "keywords": [ 5 | "struct", 6 | "ref", 7 | "abi", 8 | "c", 9 | "c++", 10 | "ffi" 11 | ], 12 | "version": "1.1.0", 13 | "author": "Nathan Rajlich (http://tootallnate.net)", 14 | "repository": { 15 | "type": "git", 16 | "url": "git://github.com/TooTallNate/ref-struct.git" 17 | }, 18 | "main": "./lib/struct.js", 19 | "license": "MIT", 20 | "scripts": { 21 | "test": "node-gyp rebuild --directory test && mocha -gc --reporter spec" 22 | }, 23 | "dependencies": { 24 | "debug": "2", 25 | "ref": "1" 26 | }, 27 | "devDependencies": { 28 | "bindings": "~1.2.0", 29 | "nan": "2", 30 | "mocha": "*", 31 | "ref-array": "~1.1.2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'struct_tests', 5 | 'sources': [ 'struct_tests.cc' ], 6 | 'include_dirs': [ 7 | ' 2 | #include "nan.h" 3 | 4 | #ifdef _WIN32 5 | #define __alignof__ __alignof 6 | #endif 7 | 8 | using namespace node; 9 | 10 | namespace { 11 | 12 | typedef struct _test1 { 13 | int a; 14 | int b; 15 | double c; 16 | } test1; 17 | 18 | typedef struct _test2 { 19 | int a; 20 | double b; 21 | int c; 22 | } test2; 23 | 24 | typedef struct _test3 { 25 | double a; 26 | int b; 27 | int c; 28 | } test3; 29 | 30 | typedef struct _test4 { 31 | double a; 32 | double b; 33 | int c; 34 | } test4; 35 | 36 | typedef struct _test5 { 37 | int a; 38 | double b; 39 | double c; 40 | } test5; 41 | 42 | typedef struct _test6 { 43 | char a; 44 | short b; 45 | int c; 46 | } test6; 47 | 48 | typedef struct _test7 { 49 | int a; 50 | short b; 51 | char c; 52 | } test7; 53 | 54 | typedef struct _test8 { 55 | int a; 56 | short b; 57 | char c; 58 | char d; 59 | } test8; 60 | 61 | typedef struct _test9 { 62 | int a; 63 | short b; 64 | char c; 65 | char d; 66 | char e; 67 | } test9; 68 | 69 | typedef struct _test10 { 70 | test1 a; 71 | char b; 72 | } test10; 73 | 74 | // this one simulates the `ffi_type` struct 75 | typedef struct _test11 { 76 | size_t a; 77 | unsigned short b; 78 | unsigned short c; 79 | struct _test11 **d; 80 | } test11; 81 | 82 | typedef struct _test12 { 83 | char *a; 84 | int b; 85 | } test12; 86 | 87 | typedef struct _test13 { 88 | char a; 89 | char b[2]; 90 | } test13; 91 | 92 | typedef struct _test14 { 93 | char a; 94 | char b[2]; 95 | short c; 96 | char d; 97 | } test14; 98 | 99 | typedef struct _test15 { 100 | test1 a; 101 | test1 b; 102 | } test15; 103 | 104 | typedef struct _test16 { 105 | double a[10]; 106 | char b[3]; 107 | int c[6]; 108 | } test16; 109 | 110 | typedef struct _test17 { 111 | char a[3]; 112 | } test17; 113 | 114 | typedef struct _test18 { 115 | test17 a[100]; 116 | } test18; 117 | 118 | /* test19 example is from libdespotify 119 | * See: https://github.com/TooTallNate/ref-struct/issues/1 120 | */ 121 | 122 | #define STRING_LENGTH 256 123 | typedef struct _test19 { 124 | bool has_meta_data; 125 | bool playable; 126 | bool geo_restricted; 127 | unsigned char track_id[33]; 128 | unsigned char file_id[41]; 129 | unsigned int file_bitrate; 130 | unsigned char album_id[33]; 131 | unsigned char cover_id[41]; 132 | unsigned char *key; 133 | 134 | char *allowed; 135 | char *forbidden; 136 | 137 | char title[STRING_LENGTH]; 138 | struct artist* artist; 139 | char album[STRING_LENGTH]; 140 | int length; 141 | int tracknumber; 142 | int year; 143 | float popularity; 144 | struct _test19 *next; /* in case of multiple tracks 145 | in an album or playlist struct */ 146 | } test19; 147 | 148 | #pragma pack(1) 149 | typedef struct _test20 { 150 | char a; 151 | void *p; 152 | } test20; 153 | 154 | #pragma pack() 155 | typedef struct _test21 { 156 | char a; 157 | void *p; 158 | } test21; 159 | 160 | void Initialize(v8::Handle target) { 161 | Nan::HandleScope scope; 162 | 163 | target->Set(Nan::New("test1 sizeof").ToLocalChecked(), Nan::New(sizeof(test1))); 164 | target->Set(Nan::New("test1 alignof").ToLocalChecked(), Nan::New(__alignof__(test1))); 165 | target->Set(Nan::New("test1 offsetof a").ToLocalChecked(), Nan::New(offsetof(test1, a))); 166 | target->Set(Nan::New("test1 offsetof b").ToLocalChecked(), Nan::New(offsetof(test1, b))); 167 | target->Set(Nan::New("test1 offsetof c").ToLocalChecked(), Nan::New(offsetof(test1, c))); 168 | 169 | target->Set(Nan::New("test2 sizeof").ToLocalChecked(), Nan::New(sizeof(test2))); 170 | target->Set(Nan::New("test2 alignof").ToLocalChecked(), Nan::New(__alignof__(test2))); 171 | target->Set(Nan::New("test2 offsetof a").ToLocalChecked(), Nan::New(offsetof(test2, a))); 172 | target->Set(Nan::New("test2 offsetof b").ToLocalChecked(), Nan::New(offsetof(test2, b))); 173 | target->Set(Nan::New("test2 offsetof c").ToLocalChecked(), Nan::New(offsetof(test2, c))); 174 | 175 | target->Set(Nan::New("test3 sizeof").ToLocalChecked(), Nan::New(sizeof(test3))); 176 | target->Set(Nan::New("test3 alignof").ToLocalChecked(), Nan::New(__alignof__(test3))); 177 | target->Set(Nan::New("test3 offsetof a").ToLocalChecked(), Nan::New(offsetof(test3, a))); 178 | target->Set(Nan::New("test3 offsetof b").ToLocalChecked(), Nan::New(offsetof(test3, b))); 179 | target->Set(Nan::New("test3 offsetof c").ToLocalChecked(), Nan::New(offsetof(test3, c))); 180 | 181 | target->Set(Nan::New("test4 sizeof").ToLocalChecked(), Nan::New(sizeof(test4))); 182 | target->Set(Nan::New("test4 alignof").ToLocalChecked(), Nan::New(__alignof__(test4))); 183 | target->Set(Nan::New("test4 offsetof a").ToLocalChecked(), Nan::New(offsetof(test4, a))); 184 | target->Set(Nan::New("test4 offsetof b").ToLocalChecked(), Nan::New(offsetof(test4, b))); 185 | target->Set(Nan::New("test4 offsetof c").ToLocalChecked(), Nan::New(offsetof(test4, c))); 186 | 187 | target->Set(Nan::New("test5 sizeof").ToLocalChecked(), Nan::New(sizeof(test5))); 188 | target->Set(Nan::New("test5 alignof").ToLocalChecked(), Nan::New(__alignof__(test5))); 189 | target->Set(Nan::New("test5 offsetof a").ToLocalChecked(), Nan::New(offsetof(test5, a))); 190 | target->Set(Nan::New("test5 offsetof b").ToLocalChecked(), Nan::New(offsetof(test5, b))); 191 | target->Set(Nan::New("test5 offsetof c").ToLocalChecked(), Nan::New(offsetof(test5, c))); 192 | 193 | target->Set(Nan::New("test6 sizeof").ToLocalChecked(), Nan::New(sizeof(test6))); 194 | target->Set(Nan::New("test6 alignof").ToLocalChecked(), Nan::New(__alignof__(test6))); 195 | target->Set(Nan::New("test6 offsetof a").ToLocalChecked(), Nan::New(offsetof(test6, a))); 196 | target->Set(Nan::New("test6 offsetof b").ToLocalChecked(), Nan::New(offsetof(test6, b))); 197 | target->Set(Nan::New("test6 offsetof c").ToLocalChecked(), Nan::New(offsetof(test6, c))); 198 | 199 | target->Set(Nan::New("test7 sizeof").ToLocalChecked(), Nan::New(sizeof(test7))); 200 | target->Set(Nan::New("test7 alignof").ToLocalChecked(), Nan::New(__alignof__(test7))); 201 | target->Set(Nan::New("test7 offsetof a").ToLocalChecked(), Nan::New(offsetof(test7, a))); 202 | target->Set(Nan::New("test7 offsetof b").ToLocalChecked(), Nan::New(offsetof(test7, b))); 203 | target->Set(Nan::New("test7 offsetof c").ToLocalChecked(), Nan::New(offsetof(test7, c))); 204 | 205 | target->Set(Nan::New("test8 sizeof").ToLocalChecked(), Nan::New(sizeof(test8))); 206 | target->Set(Nan::New("test8 alignof").ToLocalChecked(), Nan::New(__alignof__(test8))); 207 | target->Set(Nan::New("test8 offsetof a").ToLocalChecked(), Nan::New(offsetof(test8, a))); 208 | target->Set(Nan::New("test8 offsetof b").ToLocalChecked(), Nan::New(offsetof(test8, b))); 209 | target->Set(Nan::New("test8 offsetof c").ToLocalChecked(), Nan::New(offsetof(test8, c))); 210 | target->Set(Nan::New("test8 offsetof d").ToLocalChecked(), Nan::New(offsetof(test8, d))); 211 | 212 | target->Set(Nan::New("test9 sizeof").ToLocalChecked(), Nan::New(sizeof(test9))); 213 | target->Set(Nan::New("test9 alignof").ToLocalChecked(), Nan::New(__alignof__(test9))); 214 | target->Set(Nan::New("test9 offsetof a").ToLocalChecked(), Nan::New(offsetof(test9, a))); 215 | target->Set(Nan::New("test9 offsetof b").ToLocalChecked(), Nan::New(offsetof(test9, b))); 216 | target->Set(Nan::New("test9 offsetof c").ToLocalChecked(), Nan::New(offsetof(test9, c))); 217 | target->Set(Nan::New("test9 offsetof d").ToLocalChecked(), Nan::New(offsetof(test9, d))); 218 | target->Set(Nan::New("test9 offsetof e").ToLocalChecked(), Nan::New(offsetof(test9, e))); 219 | 220 | target->Set(Nan::New("test10 sizeof").ToLocalChecked(), Nan::New(sizeof(test10))); 221 | target->Set(Nan::New("test10 alignof").ToLocalChecked(), Nan::New(__alignof__(test10))); 222 | target->Set(Nan::New("test10 offsetof a").ToLocalChecked(), Nan::New(offsetof(test10, a))); 223 | target->Set(Nan::New("test10 offsetof b").ToLocalChecked(), Nan::New(offsetof(test10, b))); 224 | 225 | target->Set(Nan::New("test11 sizeof").ToLocalChecked(), Nan::New(sizeof(test11))); 226 | target->Set(Nan::New("test11 alignof").ToLocalChecked(), Nan::New(__alignof__(test11))); 227 | target->Set(Nan::New("test11 offsetof a").ToLocalChecked(), Nan::New(offsetof(test11, a))); 228 | target->Set(Nan::New("test11 offsetof b").ToLocalChecked(), Nan::New(offsetof(test11, b))); 229 | target->Set(Nan::New("test11 offsetof c").ToLocalChecked(), Nan::New(offsetof(test11, c))); 230 | target->Set(Nan::New("test11 offsetof d").ToLocalChecked(), Nan::New(offsetof(test11, d))); 231 | 232 | target->Set(Nan::New("test12 sizeof").ToLocalChecked(), Nan::New(sizeof(test12))); 233 | target->Set(Nan::New("test12 alignof").ToLocalChecked(), Nan::New(__alignof__(test12))); 234 | target->Set(Nan::New("test12 offsetof a").ToLocalChecked(), Nan::New(offsetof(test12, a))); 235 | target->Set(Nan::New("test12 offsetof b").ToLocalChecked(), Nan::New(offsetof(test12, b))); 236 | 237 | target->Set(Nan::New("test13 sizeof").ToLocalChecked(), Nan::New(sizeof(test13))); 238 | target->Set(Nan::New("test13 alignof").ToLocalChecked(), Nan::New(__alignof__(test13))); 239 | target->Set(Nan::New("test13 offsetof a").ToLocalChecked(), Nan::New(offsetof(test13, a))); 240 | target->Set(Nan::New("test13 offsetof b").ToLocalChecked(), Nan::New(offsetof(test13, b))); 241 | 242 | target->Set(Nan::New("test14 sizeof").ToLocalChecked(), Nan::New(sizeof(test14))); 243 | target->Set(Nan::New("test14 alignof").ToLocalChecked(), Nan::New(__alignof__(test14))); 244 | target->Set(Nan::New("test14 offsetof a").ToLocalChecked(), Nan::New(offsetof(test14, a))); 245 | target->Set(Nan::New("test14 offsetof b").ToLocalChecked(), Nan::New(offsetof(test14, b))); 246 | target->Set(Nan::New("test14 offsetof c").ToLocalChecked(), Nan::New(offsetof(test14, c))); 247 | target->Set(Nan::New("test14 offsetof d").ToLocalChecked(), Nan::New(offsetof(test14, d))); 248 | 249 | target->Set(Nan::New("test15 sizeof").ToLocalChecked(), Nan::New(sizeof(test15))); 250 | target->Set(Nan::New("test15 alignof").ToLocalChecked(), Nan::New(__alignof__(test15))); 251 | target->Set(Nan::New("test15 offsetof a").ToLocalChecked(), Nan::New(offsetof(test15, a))); 252 | target->Set(Nan::New("test15 offsetof b").ToLocalChecked(), Nan::New(offsetof(test15, b))); 253 | 254 | target->Set(Nan::New("test16 sizeof").ToLocalChecked(), Nan::New(sizeof(test16))); 255 | target->Set(Nan::New("test16 alignof").ToLocalChecked(), Nan::New(__alignof__(test16))); 256 | target->Set(Nan::New("test16 offsetof a").ToLocalChecked(), Nan::New(offsetof(test16, a))); 257 | target->Set(Nan::New("test16 offsetof b").ToLocalChecked(), Nan::New(offsetof(test16, b))); 258 | target->Set(Nan::New("test16 offsetof c").ToLocalChecked(), Nan::New(offsetof(test16, c))); 259 | 260 | target->Set(Nan::New("test17 sizeof").ToLocalChecked(), Nan::New(sizeof(test17))); 261 | target->Set(Nan::New("test17 alignof").ToLocalChecked(), Nan::New(__alignof__(test17))); 262 | target->Set(Nan::New("test17 offsetof a").ToLocalChecked(), Nan::New(offsetof(test17, a))); 263 | 264 | target->Set(Nan::New("test18 sizeof").ToLocalChecked(), Nan::New(sizeof(test18))); 265 | target->Set(Nan::New("test18 alignof").ToLocalChecked(), Nan::New(__alignof__(test18))); 266 | target->Set(Nan::New("test18 offsetof a").ToLocalChecked(), Nan::New(offsetof(test18, a))); 267 | 268 | target->Set(Nan::New("test19 sizeof").ToLocalChecked(), Nan::New(sizeof(test19))); 269 | target->Set(Nan::New("test19 alignof").ToLocalChecked(), Nan::New(__alignof__(test19))); 270 | target->Set(Nan::New("test19 offsetof has_meta_data").ToLocalChecked(), Nan::New(offsetof(test19, has_meta_data))); 271 | target->Set(Nan::New("test19 offsetof playable").ToLocalChecked(), Nan::New(offsetof(test19, playable))); 272 | target->Set(Nan::New("test19 offsetof geo_restricted").ToLocalChecked(), Nan::New(offsetof(test19, geo_restricted))); 273 | target->Set(Nan::New("test19 offsetof track_id").ToLocalChecked(), Nan::New(offsetof(test19, track_id))); 274 | target->Set(Nan::New("test19 offsetof file_id").ToLocalChecked(), Nan::New(offsetof(test19, file_id))); 275 | target->Set(Nan::New("test19 offsetof file_bitrate").ToLocalChecked(), Nan::New(offsetof(test19, file_bitrate))); 276 | target->Set(Nan::New("test19 offsetof album_id").ToLocalChecked(), Nan::New(offsetof(test19, album_id))); 277 | target->Set(Nan::New("test19 offsetof cover_id").ToLocalChecked(), Nan::New(offsetof(test19, cover_id))); 278 | target->Set(Nan::New("test19 offsetof key").ToLocalChecked(), Nan::New(offsetof(test19, key))); 279 | target->Set(Nan::New("test19 offsetof allowed").ToLocalChecked(), Nan::New(offsetof(test19, allowed))); 280 | target->Set(Nan::New("test19 offsetof forbidden").ToLocalChecked(), Nan::New(offsetof(test19, forbidden))); 281 | target->Set(Nan::New("test19 offsetof title").ToLocalChecked(), Nan::New(offsetof(test19, title))); 282 | target->Set(Nan::New("test19 offsetof artist").ToLocalChecked(), Nan::New(offsetof(test19, artist))); 283 | target->Set(Nan::New("test19 offsetof album").ToLocalChecked(), Nan::New(offsetof(test19, album))); 284 | target->Set(Nan::New("test19 offsetof length").ToLocalChecked(), Nan::New(offsetof(test19, length))); 285 | target->Set(Nan::New("test19 offsetof tracknumber").ToLocalChecked(), Nan::New(offsetof(test19, tracknumber))); 286 | target->Set(Nan::New("test19 offsetof year").ToLocalChecked(), Nan::New(offsetof(test19, year))); 287 | target->Set(Nan::New("test19 offsetof popularity").ToLocalChecked(), Nan::New(offsetof(test19, popularity))); 288 | target->Set(Nan::New("test19 offsetof next").ToLocalChecked(), Nan::New(offsetof(test19, next))); 289 | 290 | target->Set(Nan::New("test20 sizeof").ToLocalChecked(), Nan::New(sizeof(test20))); 291 | target->Set(Nan::New("test20 alignof").ToLocalChecked(), Nan::New(__alignof__(test20))); 292 | 293 | target->Set(Nan::New("test21 sizeof").ToLocalChecked(), Nan::New(sizeof(test21))); 294 | target->Set(Nan::New("test21 alignof").ToLocalChecked(), Nan::New(__alignof__(test21))); 295 | } 296 | 297 | } // anonymous namespace 298 | 299 | NODE_MODULE(struct_tests, Initialize); 300 | --------------------------------------------------------------------------------