From a362a6239c4c86879faf6973ce642b75c536f930 Mon Sep 17 00:00:00 2001 From: Robert Mustacchi Date: Tue, 13 Dec 2011 13:22:12 -0800 Subject: [PATCH] CTYPE-6 want additional entry point for write CTYPE-20 Add 64-bit int support into core parser CTYPE-31 Fix bounds errors node/2129 CTYPE-33 Update copyright holders CTYPE-34 ctf.js confuses sign bit. CTYPE-35 Make the README more useful for getting started CTYPE-36 want manual page on ctio functions --- LICENSE | 1 + README | 332 ++++--------------- README.old | 298 +++++++++++++++++ ctf.js | 25 +- ctio.js | 80 ++++- ctype.js | 87 ++++- man/man3ctype/ctio.3ctype | 241 ++++++++++++++ tst/ctf/tst.int.js | 2 +- tst/ctf/tst.struct.js | 2 +- tst/ctf/tst.typedef.js | 2 +- tst/ctio/int/tst.64.js | 638 ++++++++++++++++++++++++++++++++++++ tst/ctio/int/tst.wbounds.js | 53 +++ tst/ctio/uint/tst.64.js | 451 +++++++++++++++++++++++++ tst/ctype/tst.oldwrite.js | 28 ++ tst/ctype/tst.structw.js | 28 ++ 15 files changed, 1956 insertions(+), 312 deletions(-) create mode 100644 README.old create mode 100644 man/man3ctype/ctio.3ctype create mode 100644 tst/ctio/int/tst.64.js create mode 100644 tst/ctio/int/tst.wbounds.js create mode 100644 tst/ctio/uint/tst.64.js create mode 100644 tst/ctype/tst.oldwrite.js create mode 100644 tst/ctype/tst.structw.js diff --git a/LICENSE b/LICENSE index a976bc3..22ced3e 100644 --- a/LICENSE +++ b/LICENSE @@ -4,6 +4,7 @@ Each file specified below has its license information embedded in it: tools/jsstyle Copyright 2011, Robert Mustacchi. All rights reserved. +Copyright 2011, Joyent, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the diff --git a/README b/README index 9326b72..4efd7ee 100644 --- a/README +++ b/README @@ -1,24 +1,54 @@ -This library provides a way to read and write binary data. +Node-CType is a way to read and write binary data in structured and easy to use +format. Its name comes from the C header file. -Node CType is a way to read and write binary data in structured and easy to use -formats. It's name comes from the header file, though it does not share as much -with it as it perhaps should. +To get started, simply clone the repository or use npm to install it. Once it is +there, simply require it. -There are two levels of the API. One is the raw API which everything is built on -top of, while the other provides a much nicer abstraction and is built entirely -by using the lower level API. The hope is that the low level API is both clear -and useful. The low level API gets it's names from stdint.h (a rather -appropriate source). The lower level API is presented at the end of this -document. +git clone git://github.com/rmustacc/node-ctype +npm install ctype +var mod_ctype = require('ctype') -Standard CType API -The CType interface is presented as a parser object that controls the -endianness combined with a series of methods to change that value, parse and -write out buffers, and a way to provide typedefs. Standard Types +There are two APIs that you can use, depending on what abstraction you'd like. +The low level API let's you read and write individual integers and floats from +buffers. The higher level API let's you read and write structures of these. To +illustrate this, let's looks look at how we would read and write a binary +encoded x,y point. -The CType parser supports the following basic types which return Numbers except -as indicated: +In C we would define this structure as follows: + +typedef struct point { + uint16_t p_x; + uint16_t p_y; +} point_t; + +To read a binary encoded point from a Buffer, we first need to create a CType +parser (where we specify the endian and other options) and add the typedef. + +var parser = new mod_ctype.Parser({ endian: 'big' }); +parser.typedef('point_t', [ + { x: { type: 'uint16_t' } }, + { y: { type: 'uint16_t' } } +]); + +From here, given a buffer buf and an offset into it, we can read a point. + +var out = parser.readData([ { point: { type: 'point_t' } } ], buffer, 0); +console.log(out); +{ point: { x: 23, y: 42 } } + +Another way to get the same information would be to use the low level methods. +Note that these require you to manually deal with the offset. Here's how we'd +get the same values of x and y from the buffer. + +var x = mod_ctype.ruint16(buf, 'big', 0); +var y = mod_ctype.ruint16(buf, 'big', 2); +console.log(x + ', ' + y); +23, 42 + +The true power of this API comes from the ability to define and nest typedefs, +just as you would in C. By default, the following types are defined by default. +Note that they return a Number, unless indicated otherwise. * int8_t * int16_t @@ -30,269 +60,23 @@ as indicated: * uint64_t (returns an array where val[0] << 32 + val[1] would be the value) * float * double - * char (returns a buffer with just that single character) + * char (either returns a buffer with that character or a uint8_t) * char[] (returns an object with the buffer and the number of characters read which is either the total amount requested or until the first 0) -Specifying Structs - -The CType parser also supports the notion of structs. A struct is an array of -JSON objects that defines an order of keys which have types and values. One -would build a struct to represent a point (x,y) as follows: - -[ - { x: { type: 'int16_t' }}, - { y: { type: 'int16_t' }} -] - -When this is passed into the read routine, it would read the first two bytes -(as defined by int16_t) to determine the Number to use for X, and then it would -read the next two bytes to determine the value of Y. When read this could -return something like: - -{ - x: 42, - y: -23 -} - -When someone wants to write values, we use the same format as above, but with -additional value field: - -[ - { x: { type: 'int16_t', value: 42 }}, - { y: { type: 'int16_t', value: -23 }} -] - -Now, the structure above may be optionally annotated with offsets. This tells -us to rather than read continuously we should read the given value at the -specified offset. If an offset is provided, it is is effectively the equivalent -of lseek(offset, SEEK_SET). Thus, subsequent values will be read from that -offset and incremented by the appropriate value. As an example: - -[ - { x: { type: 'int16_t' }}, - { y: { type: 'int16_t', offset: 20 }}, - { z: { type: 'int16_t' }} -] - -We would read x from the first starting offset given to us, for the sake of -example, let's assume that's 0. After reading x, the next offset to read from -would be 2; however, y specifies an offset, thus we jump directly to that -offset and read y from byte 20. We would then read z from byte 22. - -The same offsets may be used when writing values. - -Typedef - -The basic set of types while covers the basics, is somewhat limiting. To make -this richer, there is functionality to typedef something like in C. One can use -typedef to add a new name to an existing type or to define a name to refer to a -struct. Thus the following are all examples of a typedef: - -typedef('size_t', 'uint32_t'); -typedef('ssize_t', 'int32_t'); -typedef('point_t', [ - { x: { type: 'int16_t' }}, - { y: { type: 'int16_t' }} -]); - -Once something has been typedef'd it can be used in any of the definitions -previously shown. - -One cannot remove a typedef once created, this is analogous to C. - -The set of defined types can be printed with lstypes. The format of this output -is subject to change, but likely will look something like: - -> lstypes(); -{ - size_t: 'uint32_t', - ssize_t: 'int32_t', - point_t: [ - { x: { type: 'int16_t' }}, - { y: { type: 'int16_t' }} - ] -} - -Specifying arrays - -Arrays can be specified by appending []s to a type. Arrays must have the size -specified. The size must be specified and it can be done in one of two ways: - - * An explicit non-zero integer size - * A name of a previously declared variable in the struct whose value is a - number. - -Note, that when using the name of a variable, it should be the string name for -the key. This is only valid inside structs and the value must be declared -before the value with the array. The following are examples: - -[ - { ip_addr4: { type: 'uint8_t[4]' }}, - { len: { type: 'uint32_t' }}, - { data: { type: 'uint8_t[len]' }} -] - -Arrays are permitted in typedefs; however, they must have a declared integer -size. The following are examples of valid and invalid arrays: - -typedef('path', 'char[1024]'); /* Good */ -typedef('path', 'char[len]'); /* Bad! */ - -64 bit values: - -Unfortunately Javascript represents values with a double, so you lose precision -and the ability to represent Integers roughly beyond 2^53. To alleviate this, I -propose the following for returning 64 bit integers when read: - -value[2]: Each entry is a 32 bit number which can be reconstructed to the -original by the following formula: - -value[0] << 32 + value[1] (Note this will not work in Javascript) - -CTF JSON data: - -node-ctype can also handle JSON data that mathces the format described in the -documentation of the tool ctf2json. Given the JSON data which specifies type -information, it will transform that into a parser that understands all of the -types defined inside of it. This is useful for more complicated structures that -have a lot of typedefs. - -Interface overview - -The following is the header-file like interface to the parser object: - -/* - * Create a new instance of the parser. Each parser has its own store of - * typedefs and endianness. Conf is an object with the following values: - * - * endian Either 'big' or 'little' do determine the endianness we - * want to read from or write to. - * - */ -function CTypeParser(conf); - -/* - * Parses the CTF JSON data and creates a parser that understands all of those - * types. - * - * data Parsed JSON data that maches that CTF JSON - * specification. - * - * conf The configuration object to create a new CTypeParser - * from. - */ -CTypeParser parseCTF(data, conf); - -/* - * This is what we were born to do. We read the data from a buffer and return it - * in an object whose keys match the values from the object. - * - * def The array definition of the data to read in - * - * buffer The buffer to read data from - * - * offset The offset to start writing to - * - * Returns an object where each key corresponds to an entry in def and the value - * is the read value. - */ -Object CTypeParser.readData(, buffer, offset); - -/* - * This is the second half of what we were born to do, write out the data - * itself. - * - * def The array definition of the data to write out with - * values - * - * buffer The buffer to write to - * - * offset The offset in the buffer to write to - */ -void CTypeParser.writeData(, buffer, offset); - -/* - * A user has requested to add a type, let us honor their request. Yet, if their - * request doth spurn us, send them unto the Hells which Dante describes. - * - * name The string for the type definition we're adding - * - * value Either a string that is a type/array name or an object - * that describes a struct. - */ -void CTypeParser.prototype.typedef(name, value); - -Object CTypeParser.prototype.lstypes(); - -/* - * Get the endian value for the current parser - */ -String CTypeParser.prototype.getEndian(); - -/* - * Sets the current endian value for the Parser. If the value is not valid, - * throws an Error. - * - * endian Either 'big' or 'little' do determine the endianness we - * want to read from or write to. - * - */ -void CTypeParser.protoype.setEndian(String); - -/* - * Attempts to convert an array of two integers returned from rsint64 / ruint64 - * into an absolute 64 bit number. If however the value would exceed 2^52 this - * will instead throw an error. The mantissa in a double is a 52 bit number and - * rather than potentially give you a value that is an approximation this will - * error. If you would rather an approximation, please see toApprox64. - * - * val An array of two 32-bit integers - */ -Number function toAbs64(val) - -/* - * Will return the 64 bit value as returned in an array from rsint64 / ruint64 - * to a value as close as it can. Note that Javascript stores all numbers as a - * double and the mantissa only has 52 bits. Thus this version may approximate - * the value. - * - * val An array of two 32-bit integers - */ -Number function toApprox64(val) - -Low Level API - -The following function are provided at the low level: - -Read unsigned integers from a buffer: -Number ruint8(buffer, endian, offset); -Number ruint16(buffer, endian, offset); -Number ruint32(buffer, endian, offset); -Number[] ruint64(buffer, endian, offset); -Read signed integers from a buffer: -Number rsint8(buffer, endian, offset); -Number rsint16(buffer, endian, offset); -Number rsint32(buffer, endian, offset); -Number[] rsint64(buffer, endian, offset); +ctf2json integration: -Read floating point numbers from a buffer: -Number rfloat(buffer, endian, offset); /* IEEE-754 Single precision */ -Number rdouble(buffer, endian, offset); /* IEEE-754 Double precision */ +Node-CType supports consuming the output of ctf2json. Once you read in a JSON file, +all you have to do to add all the definitions it contains is: -Write unsigned integers to a buffer: -void wuint8(Number, endian, buffer, offset); -void wuint16(Number, endian, buffer, offset); -void wuint32(Number, endian, buffer, offset); -void wuint64(Number[], endian, buffer, offset); +var data, parser; +data = JSON.parse(parsedJSONData); +parser = mod_ctype.parseCTF(data, { endian: 'big' }); -Write signed integers from a buffer: -void wsint8(Number, endian, buffer, offset); -void wsint16(Number, endian, buffer, offset); -void wsint32(Number, endian, buffer, offset); -void wsint64(Number[], endian, buffer offset); +For more documentation, see the file README.old. Full documentation is in the +process of being rewritten as a series of manual pages which will be available +in the repository and online for viewing. -Write floating point numbers from a buffer: -void wfloat(Number, buffer, endian, offset); /* IEEE-754 Single precision */ -void wdouble(Number, buffer, endian, offset); /* IEEE-754 Double precision */ +To read the ctio manual page simple run, from the root of the workspace: +man -Mman -s 3ctype ctio diff --git a/README.old b/README.old new file mode 100644 index 0000000..9326b72 --- /dev/null +++ b/README.old @@ -0,0 +1,298 @@ +This library provides a way to read and write binary data. + +Node CType is a way to read and write binary data in structured and easy to use +formats. It's name comes from the header file, though it does not share as much +with it as it perhaps should. + +There are two levels of the API. One is the raw API which everything is built on +top of, while the other provides a much nicer abstraction and is built entirely +by using the lower level API. The hope is that the low level API is both clear +and useful. The low level API gets it's names from stdint.h (a rather +appropriate source). The lower level API is presented at the end of this +document. + +Standard CType API + +The CType interface is presented as a parser object that controls the +endianness combined with a series of methods to change that value, parse and +write out buffers, and a way to provide typedefs. Standard Types + +The CType parser supports the following basic types which return Numbers except +as indicated: + + * int8_t + * int16_t + * int32_t + * int64_t (returns an array where val[0] << 32 + val[1] would be the value) + * uint8_t + * uint16_t + * uint32_t + * uint64_t (returns an array where val[0] << 32 + val[1] would be the value) + * float + * double + * char (returns a buffer with just that single character) + * char[] (returns an object with the buffer and the number of characters read which is either the total amount requested or until the first 0) + +Specifying Structs + +The CType parser also supports the notion of structs. A struct is an array of +JSON objects that defines an order of keys which have types and values. One +would build a struct to represent a point (x,y) as follows: + +[ + { x: { type: 'int16_t' }}, + { y: { type: 'int16_t' }} +] + +When this is passed into the read routine, it would read the first two bytes +(as defined by int16_t) to determine the Number to use for X, and then it would +read the next two bytes to determine the value of Y. When read this could +return something like: + +{ + x: 42, + y: -23 +} + +When someone wants to write values, we use the same format as above, but with +additional value field: + +[ + { x: { type: 'int16_t', value: 42 }}, + { y: { type: 'int16_t', value: -23 }} +] + +Now, the structure above may be optionally annotated with offsets. This tells +us to rather than read continuously we should read the given value at the +specified offset. If an offset is provided, it is is effectively the equivalent +of lseek(offset, SEEK_SET). Thus, subsequent values will be read from that +offset and incremented by the appropriate value. As an example: + +[ + { x: { type: 'int16_t' }}, + { y: { type: 'int16_t', offset: 20 }}, + { z: { type: 'int16_t' }} +] + +We would read x from the first starting offset given to us, for the sake of +example, let's assume that's 0. After reading x, the next offset to read from +would be 2; however, y specifies an offset, thus we jump directly to that +offset and read y from byte 20. We would then read z from byte 22. + +The same offsets may be used when writing values. + +Typedef + +The basic set of types while covers the basics, is somewhat limiting. To make +this richer, there is functionality to typedef something like in C. One can use +typedef to add a new name to an existing type or to define a name to refer to a +struct. Thus the following are all examples of a typedef: + +typedef('size_t', 'uint32_t'); +typedef('ssize_t', 'int32_t'); +typedef('point_t', [ + { x: { type: 'int16_t' }}, + { y: { type: 'int16_t' }} +]); + +Once something has been typedef'd it can be used in any of the definitions +previously shown. + +One cannot remove a typedef once created, this is analogous to C. + +The set of defined types can be printed with lstypes. The format of this output +is subject to change, but likely will look something like: + +> lstypes(); +{ + size_t: 'uint32_t', + ssize_t: 'int32_t', + point_t: [ + { x: { type: 'int16_t' }}, + { y: { type: 'int16_t' }} + ] +} + +Specifying arrays + +Arrays can be specified by appending []s to a type. Arrays must have the size +specified. The size must be specified and it can be done in one of two ways: + + * An explicit non-zero integer size + * A name of a previously declared variable in the struct whose value is a + number. + +Note, that when using the name of a variable, it should be the string name for +the key. This is only valid inside structs and the value must be declared +before the value with the array. The following are examples: + +[ + { ip_addr4: { type: 'uint8_t[4]' }}, + { len: { type: 'uint32_t' }}, + { data: { type: 'uint8_t[len]' }} +] + +Arrays are permitted in typedefs; however, they must have a declared integer +size. The following are examples of valid and invalid arrays: + +typedef('path', 'char[1024]'); /* Good */ +typedef('path', 'char[len]'); /* Bad! */ + +64 bit values: + +Unfortunately Javascript represents values with a double, so you lose precision +and the ability to represent Integers roughly beyond 2^53. To alleviate this, I +propose the following for returning 64 bit integers when read: + +value[2]: Each entry is a 32 bit number which can be reconstructed to the +original by the following formula: + +value[0] << 32 + value[1] (Note this will not work in Javascript) + +CTF JSON data: + +node-ctype can also handle JSON data that mathces the format described in the +documentation of the tool ctf2json. Given the JSON data which specifies type +information, it will transform that into a parser that understands all of the +types defined inside of it. This is useful for more complicated structures that +have a lot of typedefs. + +Interface overview + +The following is the header-file like interface to the parser object: + +/* + * Create a new instance of the parser. Each parser has its own store of + * typedefs and endianness. Conf is an object with the following values: + * + * endian Either 'big' or 'little' do determine the endianness we + * want to read from or write to. + * + */ +function CTypeParser(conf); + +/* + * Parses the CTF JSON data and creates a parser that understands all of those + * types. + * + * data Parsed JSON data that maches that CTF JSON + * specification. + * + * conf The configuration object to create a new CTypeParser + * from. + */ +CTypeParser parseCTF(data, conf); + +/* + * This is what we were born to do. We read the data from a buffer and return it + * in an object whose keys match the values from the object. + * + * def The array definition of the data to read in + * + * buffer The buffer to read data from + * + * offset The offset to start writing to + * + * Returns an object where each key corresponds to an entry in def and the value + * is the read value. + */ +Object CTypeParser.readData(, buffer, offset); + +/* + * This is the second half of what we were born to do, write out the data + * itself. + * + * def The array definition of the data to write out with + * values + * + * buffer The buffer to write to + * + * offset The offset in the buffer to write to + */ +void CTypeParser.writeData(, buffer, offset); + +/* + * A user has requested to add a type, let us honor their request. Yet, if their + * request doth spurn us, send them unto the Hells which Dante describes. + * + * name The string for the type definition we're adding + * + * value Either a string that is a type/array name or an object + * that describes a struct. + */ +void CTypeParser.prototype.typedef(name, value); + +Object CTypeParser.prototype.lstypes(); + +/* + * Get the endian value for the current parser + */ +String CTypeParser.prototype.getEndian(); + +/* + * Sets the current endian value for the Parser. If the value is not valid, + * throws an Error. + * + * endian Either 'big' or 'little' do determine the endianness we + * want to read from or write to. + * + */ +void CTypeParser.protoype.setEndian(String); + +/* + * Attempts to convert an array of two integers returned from rsint64 / ruint64 + * into an absolute 64 bit number. If however the value would exceed 2^52 this + * will instead throw an error. The mantissa in a double is a 52 bit number and + * rather than potentially give you a value that is an approximation this will + * error. If you would rather an approximation, please see toApprox64. + * + * val An array of two 32-bit integers + */ +Number function toAbs64(val) + +/* + * Will return the 64 bit value as returned in an array from rsint64 / ruint64 + * to a value as close as it can. Note that Javascript stores all numbers as a + * double and the mantissa only has 52 bits. Thus this version may approximate + * the value. + * + * val An array of two 32-bit integers + */ +Number function toApprox64(val) + +Low Level API + +The following function are provided at the low level: + +Read unsigned integers from a buffer: +Number ruint8(buffer, endian, offset); +Number ruint16(buffer, endian, offset); +Number ruint32(buffer, endian, offset); +Number[] ruint64(buffer, endian, offset); + +Read signed integers from a buffer: +Number rsint8(buffer, endian, offset); +Number rsint16(buffer, endian, offset); +Number rsint32(buffer, endian, offset); +Number[] rsint64(buffer, endian, offset); + +Read floating point numbers from a buffer: +Number rfloat(buffer, endian, offset); /* IEEE-754 Single precision */ +Number rdouble(buffer, endian, offset); /* IEEE-754 Double precision */ + +Write unsigned integers to a buffer: +void wuint8(Number, endian, buffer, offset); +void wuint16(Number, endian, buffer, offset); +void wuint32(Number, endian, buffer, offset); +void wuint64(Number[], endian, buffer, offset); + +Write signed integers from a buffer: +void wsint8(Number, endian, buffer, offset); +void wsint16(Number, endian, buffer, offset); +void wsint32(Number, endian, buffer, offset); +void wsint64(Number[], endian, buffer offset); + +Write floating point numbers from a buffer: +void wfloat(Number, buffer, endian, offset); /* IEEE-754 Single precision */ +void wdouble(Number, buffer, endian, offset); /* IEEE-754 Double precision */ + diff --git a/ctf.js b/ctf.js index 8c0843f..66d5f73 100644 --- a/ctf.js +++ b/ctf.js @@ -35,24 +35,21 @@ function ctfParseInteger(entry, ctype) type = null; if (sign && len == 1) - type = 'uint8_t'; - else if (len == 1) type = 'int8_t'; + else if (len == 1) + type = 'uint8_t'; else if (sign && len == 2) - type = 'uint16_t'; - else if (len == 2) type = 'int16_t'; + else if (len == 2) + type = 'uint16_t'; else if (sign && len == 4) - type = 'uint32_t'; - else if (len == 4) type = 'int32_t'; -/* - * TODO: Add this back in once the parser supports 64-bit values - * else if (sign && len == 8) - * type = 'uint64_t'; - * else if (len == 8) - * type = 'int64_t'; - */ + else if (len == 4) + type = 'uint32_t'; + else if (sign && len == 8) + type = 'int64_t'; + else if (len == 8) + type = 'uint64_t'; if (type === null) throw (new Error('Malformed CTF JSON: integer has ' + @@ -67,7 +64,7 @@ function ctfParseInteger(entry, ctype) return; if (name == 'char') { - ASSERT(type == 'uint8_t'); + ASSERT(type == 'int8_t'); return; } diff --git a/ctio.js b/ctio.js index dac1c20..62c5d7b 100644 --- a/ctio.js +++ b/ctio.js @@ -40,6 +40,7 @@ * Big Endian: MSB -> First byte * Little Endian: MSB->Last byte */ +var mod_assert = require('assert'); /* * An 8 bit unsigned integer involves doing no significant work. @@ -104,9 +105,12 @@ function ruint16(buffer, endian, offset) * > 200 << 24 * -939524096 * - * Not the value you'd expect. To work around this, we instead do a - * multiplication by (1 << 24) which does the same thing, but in a way that - * ensures we don't lose that bit. + * Not the value you'd expect. To work around this, we end up having to do some + * abuse of the JavaScript standard. in this case, we know that a >>> shift is + * defined to cast our value to an *unsigned* 32-bit number. Because of that, we + * use that instead to save us some additional math, though it does feel a + * little weird and it isn't obvious as to why you woul dwant to do this at + * first. */ function rgint32(buffer, endian, offset) { @@ -360,6 +364,20 @@ function rsint64(buffer, endian, offset) val[0] = (0xffffffff - val[0]) * -1; val[1] = (0xffffffff - val[1] + 1) * -1; + /* + * If we had the key 0x8000000000000000, that would leave the lower 32 + * bits as 0xffffffff, however, since we're goint to add one, that would + * actually leave the lower 32-bits as 0x100000000, which would break + * our ability to write back a value that we received. To work around + * this, if we actually get that value, we're going to bump the upper + * portion by 1 and set this to zero. + */ + mod_assert.ok(val[1] <= 0x100000000); + if (val[1] == -0x100000000) { + val[1] = 0; + val[0]--; + } + return (val); } @@ -919,7 +937,7 @@ function wsint8(value, endian, buffer, offset) if (offset >= buffer.length) throw (new Error('Trying to read beyond buffer length')); - val = prepsint(value, 0x7f, -0xf0); + val = prepsint(value, 0x7f, -0x80); if (val >= 0) wuint8(val, endian, buffer, offset); else @@ -948,7 +966,7 @@ function wsint16(value, endian, buffer, offset) if (offset + 1 >= buffer.length) throw (new Error('Trying to read beyond buffer length')); - val = prepsint(value, 0x7fff, -0xf000); + val = prepsint(value, 0x7fff, -0x8000); if (val >= 0) wgint16(val, endian, buffer, offset); else @@ -979,7 +997,7 @@ function wsint32(value, endian, buffer, offset) if (offset + 3 >= buffer.length) throw (new Error('Trying to read beyond buffer length')); - val = prepsint(value, 0x7fffffff, -0xf0000000); + val = prepsint(value, 0x7fffffff, -0x80000000); if (val >= 0) wgint32(val, endian, buffer, offset); else @@ -989,10 +1007,12 @@ function wsint32(value, endian, buffer, offset) /* * The signed 64 bit integer should by in the same format as when received. * Mainly it should ensure that the value is an array of two integers where - * value[0] << 32 + value[1] is the desired number. + * value[0] << 32 + value[1] is the desired number. Furthermore, the two values + * need to be equal. */ function wsint64(value, endian, buffer, offset) { + var vzpos, vopos; var vals = new Array(2); if (value === undefined) @@ -1016,8 +1036,50 @@ function wsint64(value, endian, buffer, offset) if (offset + 7 >= buffer.length) throw (new Error('Trying to read beyond buffer length')); - prepsint(value[0], 0x7fffffff, -0xf0000000); - prepsint(value[1], 0xffffffff, -0xffffffff); + /* + * We need to make sure that we have the same sign on both values. The + * hokiest way to to do this is to multiply the number by +inf. If we do + * this, we'll get either +/-inf depending on the sign of the value. + * Once we have this, we can compare it to +inf to see if the number is + * positive or not. + */ + vzpos = (value[0] * Number.POSITIVE_INFINITY) == + Number.POSITIVE_INFINITY; + vopos = (value[1] * Number.POSITIVE_INFINITY) == + Number.POSITIVE_INFINITY; + + /* + * If either of these is zero, then we don't actually need this check. + */ + if (value[0] != 0 && value[1] != 0 && vzpos != vopos) + throw (new Error('Both entries in the array must have ' + + 'the same sign')); + + /* + * Doing verification for a signed 64-bit integer is actually a big + * trickier than it appears. We can't quite use our standard techniques + * because we need to compare both sets of values. The first value is + * pretty straightforward. If the first value is beond the extremes than + * we error out. However, the valid range of the second value varies + * based on the first one. If the first value is negative, and *not* the + * largest negative value, than it can be any integer within the range [ + * 0, 0xffffffff ]. If it is the largest negative number, it must be + * zero. + * + * If the first number is positive, than it doesn't matter what the + * value is. We just simply have to make sure we have a valid positive + * integer. + */ + if (vzpos) { + prepuint(value[0], 0x7fffffff); + prepuint(value[1], 0xffffffff); + } else { + prepsint(value[0], 0, -0x80000000); + prepsint(value[1], 0, -0xffffffff); + if (value[0] == -0x80000000 && value[1] != 0) + throw (new Error('value smaller than minimum ' + + 'allowed value')); + } /* Fix negative numbers */ if (value[0] < 0 || value[1] < 0) { diff --git a/ctype.js b/ctype.js index 5a4e7e1..75ccba4 100644 --- a/ctype.js +++ b/ctype.js @@ -13,6 +13,7 @@ * uint8_t * uint16_t * uint32_t + * uint64_t * float * double * char @@ -36,7 +37,7 @@ var mod_ctf = require('./ctf.js'); var mod_ctio = require('./ctio.js'); -var ASSERT = require('assert'); +var mod_assert = require('assert'); /* * This is the set of basic types that we support. @@ -50,9 +51,11 @@ var deftypes = { 'uint8_t': { read: ctReadUint8, write: ctWriteUint8 }, 'uint16_t': { read: ctReadUint16, write: ctWriteUint16 }, 'uint32_t': { read: ctReadUint32, write: ctWriteUint32 }, + 'uint64_t': { read: ctReadUint64, write: ctWriteUint64 }, 'int8_t': { read: ctReadSint8, write: ctWriteSint8 }, 'int16_t': { read: ctReadSint16, write: ctWriteSint16 }, 'int32_t': { read: ctReadSint32, write: ctWriteSint32 }, + 'int64_t': { read: ctReadSint64, write: ctWriteSint64 }, 'float': { read: ctReadFloat, write: ctWriteFloat }, 'double': { read: ctReadDouble, write: ctWriteDouble }, 'char': { read: ctReadChar, write: ctWriteChar }, @@ -81,6 +84,12 @@ function ctReadUint32(endian, buffer, offset) return ({ value: val, size: 4 }); } +function ctReadUint64(endian, buffer, offset) +{ + var val = mod_ctio.ruint64(buffer, endian, offset); + return ({ value: val, size: 8 }); +} + function ctReadSint8(endian, buffer, offset) { var val = mod_ctio.rsint8(buffer, endian, offset); @@ -99,6 +108,12 @@ function ctReadSint32(endian, buffer, offset) return ({ value: val, size: 4 }); } +function ctReadSint64(endian, buffer, offset) +{ + var val = mod_ctio.rsint64(buffer, endian, offset); + return ({ value: val, size: 4 }); +} + function ctReadFloat(endian, buffer, offset) { var val = mod_ctio.rfloat(buffer, endian, offset); @@ -150,6 +165,12 @@ function ctWriteUint32(value, endian, buffer, offset) return (4); } +function ctWriteUint64(value, endian, buffer, offset) +{ + mod_ctio.wuint64(value, endian, buffer, offset); + return (8); +} + function ctWriteSint8(value, endian, buffer, offset) { mod_ctio.wsint8(value, endian, buffer, offset); @@ -168,6 +189,12 @@ function ctWriteSint32(value, endian, buffer, offset) return (4); } +function ctWriteSint64(value, endian, buffer, offset) +{ + mod_ctio.wsint64(value, endian, buffer, offset); + return (8); +} + function ctWriteFloat(value, endian, buffer, offset) { mod_ctio.wfloat(value, endian, buffer, offset); @@ -378,7 +405,7 @@ function CTypeParser(conf) this.types = ctGetBasicTypes(); /* - * This may be a more graceful way to do this, but this will have to + * There may be a more graceful way to do this, but this will have to * serve. */ if ('char-type' in conf && conf['char-type'] == 'uint8') @@ -523,7 +550,7 @@ CTypeParser.prototype.resolveTypedef = function (type, dispatch, buffer, { var pt; - ASSERT.ok(type in this.types); + mod_assert.ok(type in this.types); if (typeof (this.types[type]) == 'string') { pt = ctParseType(this.types[type]); if (dispatch == 'read') @@ -538,7 +565,7 @@ CTypeParser.prototype.resolveTypedef = function (type, dispatch, buffer, return (this.readStruct(this.types[type], buffer, offset)); else if (dispatch == 'write') - return (this.readStruct(value, this.types[type], + return (this.writeStruct(value, this.types[type], buffer, offset)); else throw (new Error('invalid dispatch type to ' + @@ -729,7 +756,7 @@ CTypeParser.prototype.writeEntry = function (value, type, buffer, offset) /* * [private] Write a single struct out. */ -CTypeParser.prototype.writeStruct = function (def, buffer, offset) +CTypeParser.prototype.writeStruct = function (value, def, buffer, offset) { var ii, entry, type, key; var baseOffset = offset; @@ -744,16 +771,41 @@ CTypeParser.prototype.writeStruct = function (def, buffer, offset) if ('offset' in entry) offset = baseOffset + entry['offset']; - offset += this.writeEntry(entry['value'], type, buffer, offset); - + offset += this.writeEntry(value[ii], type, buffer, offset); /* Now that we've written it out, we can use it for arrays */ - vals[key] = entry['value']; + vals[key] = value[ii]; } }; +/* + * Unfortunately, we're stuck with the sins of an initial poor design. Because + * of that, we are going to have to support the old way of writing data via + * writeData. There we insert the values that you want to write into the + * definition. A little baroque. Internally, we use the new model. So we need to + * just get those values out of there. But to maintain the principle of least + * surprise, we're not going to modify the input data. + */ +function getValues(def) +{ + var ii, out, key; + out = []; + for (ii = 0; ii < def.length; ii++) { + key = Object.keys(def[ii])[0]; + mod_assert.ok('value' in def[ii][key]); + out.push(def[ii][key]['value']); + } + + return (out); +} + /* * This is the second half of what we were born to do, write out the data - * itself. + * itself. Historically this function required you to put your values in the + * definition section. This was not the smartest thing to do and a bit of an + * oversight to be honest. As such, this function now takes a values argument. + * If values is non-null and non-undefined, it will be used to determine the + * values. This means that the old method is still supported, but is no longer + * acceptable. * * def The array definition of the data to write out with * values @@ -761,9 +813,13 @@ CTypeParser.prototype.writeStruct = function (def, buffer, offset) * buffer The buffer to write to * * offset The offset in the buffer to write to + * + * values An array of values to write. */ -CTypeParser.prototype.writeData = function (def, buffer, offset) +CTypeParser.prototype.writeData = function (def, buffer, offset, values) { + var hv; + if (def === undefined) throw (new Error('missing definition for what we should be' + 'parsing')); @@ -776,9 +832,16 @@ CTypeParser.prototype.writeData = function (def, buffer, offset) throw (new Error('missing offset for what we should be ' + 'parsing')); - ctCheckReq(def, this.types, [ 'value' ]); + hv = (values != null && values != undefined); + if (hv) { + if (!Array.isArray(values)) + throw (new Error('missing values for writing')); + ctCheckReq(def, this.types); + } else { + ctCheckReq(def, this.types, [ 'value' ]); + } - this.writeStruct(def, buffer, offset); + this.writeStruct(hv ? values : getValues(def), def, buffer, offset); }; /* diff --git a/man/man3ctype/ctio.3ctype b/man/man3ctype/ctio.3ctype new file mode 100644 index 0000000..3f94986 --- /dev/null +++ b/man/man3ctype/ctio.3ctype @@ -0,0 +1,241 @@ +'\" te +.\" Copyright (c) 2011, Robert Mustacchi. All Rights Reserved. +.\" Copyright (c) 2011, Joyent, Inc. All Rights Reserved. +.\" +.\" Permission is hereby granted, free of charge, to any person obtaining a copy +.\" of this software and associated documentation files (the "Software"), to +.\" deal in the Software without restriction, including without limitation the +.\" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +.\" sell copies of the Software, and to permit persons to whom the Software is +.\" furnished to do so, subject to the following conditions: +.\" +.\" The above copyright notice and this permission notice shall be included in +.\" all copies or substantial portions of the Software. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +.\" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +.\" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +.\" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +.\" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +.\" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +.\" IN THE SOFTWARE. +.TH CTIO 3CTYPE "December 12, 2011" +.SH NAME +ctio, ruint8, ruint16, ruint32, ruint64, wuint8, wuint16, wuint32, wuint64, +rsint8, rsint16, rsint32, rsint64, wsint8, wsint16, wsint32, wsint64, rfloat, +rdouble, wfloat, wdouble \- integer and float operations +.SH SYNOPSIS +.LP +.nf +var mod_ctype = require('ctype'); + +\fBNumber\fR \fBmod_ctype.ruint8\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBNumber\fR \fBmod_ctype.ruint16\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBNumber\fR \fBmod_ctype.ruint32\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBNumber[2]\fR \fBmod_ctype.ruint64\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBNumber\fR \fBmod_ctype.rsint8\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBNumber\fR \fBmod_ctype.rsint16\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBNumber\fR \fBmod_ctype.rsint32\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBNumber[2]\fR \fBmod_ctype.rsint64\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBNumber\fR \fBmod_ctype.rfloat\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBNumber\fR \fBmod_ctype.rdouble\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBmod_ctype.wuint8\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBmod_ctype.wuint16\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBmod_ctype.wuint32\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBmod_ctype.wuint64\fR(\fBNumber[2]\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBmod_ctype.wsint8\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBmod_ctype.wsint16\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBmod_ctype.wsint32\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBmod_ctype.wsint64\fR(\fBNumber[2]\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBmod_ctype.wfloat\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBmod_ctype.wdouble\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR); +.fi + +.SH DESCRIPTION +.sp +.LP +The argument \fIbuf\fR refers to a valid buffer (from calling new Buffer()). The +argument \fIendian\fR is either the string 'big' or 'little' and controls +whether the data in the buffer is interpreted as big or little endian. The argument +\fIoffset\fR indicates the starting index into the buffer to read or write. All +functions ensure that starting at \fIoffset\fR does not overflow the end of the +buffer. The argument \fIvalue\fR is a Number that is the valid type for the +specific function. All functions that take \fIvalue\fR as an argument, verify +that the passed value is valid. + +.SS "\fBruint8()\fR, \fBruint16()\fR, \fBruint32()\fR" +.sp +.LP +The \fBruint8()\fR, \fBruint16()\fR, and \fBruint32()\fR functions read an 8, +16, and 32-bit unsigned value from \fIbuf\fR and return it. The value read is +influenced by the values of \fIoffset\fR and \fRendian\fI. + + +.SS "\fBrsint8()\fR, \fBrsint16()\fR, \fBrsint32()\fR" +.sp +.LP +The \fBruint8()\fR, \fBruint16()\fR, and \fBruint32()\fR functions work just as +\fBruint8()\fR, \fBruint16()\fR, and \fBruint32()\fR, except they return signed +integers. + +.SS "\fBruint64()\fR, \fBrsint64()\fR" +.sp +.LP +The \fBruint64()\fR and \fBrsint64()\fR functions read unsigned and signed 64 +bit integers respectively from \fBbuf\fR. Due to the limitations of ECMAScript's +\fBNumber\fR type, they cannot be stored as one value without a loss of +precision. Instead of returning the values as a single \fBNumber\fR, the +functions return an array of two numbers. The first entry always contains the +upper 32-bits and the second value contains the lower 32-bits. The lossy +transformation into a number would be \fIres[0]*Math.pow(2,32)+res[1]\fR. +Note that, unless an entry is zero, both array entries are guaranteed to have +the same sign. + +.SS "\fBwuint8()\fR, \fBwuint16()\fR, \fBwuint32()\fR" +.sp +.LP +The functions \fBwuint8()\fR, \fBwuint16()\fR, and \fBwuint32()\fR modify the +contents of \fBbuf\fR by writing an 8, 16, and 32-bit unsigned integer +respectively to \fBbuf\fR. It is illegal to pass a number that is not an integer +within the domain of the integer size, for example, for \fBwuint8()\fR the valid +range is \fB[0, 255]\fR. The value will be written in either big or little +endian format based upon the value of \fBendian\fR. + + +.SS "\fBwsint8()\fR, \fBwsint16()\fR, \fBwsint32()\fR" +.sp +.LP +The functions \fBwsint8()\fR, \fBwsint16()\fR, and \fBwsint32()\fR function +identically to the functions \fBwuint8()\fR, \fBwuint16()\fR, and +\fBwuint32()\fR except that they the valid domain for \fBvalue\fR is that of a +signed number instead of an unsigned number. For example the \fBwsint8()\fR has +a domain of \fB[-128, 127]\fR. + +.SS "\fBwuint64()\fR, \fBwsint64()\fR" +.sp +.LP +The functions \fBwuint64()\fR and \fBswint64()\fR write out 64-bit unsigned and +signed integers to \fBbuf\fR. The \fBvalue\fR argument must be in the same +format as described in \fBruint64()\fR and \fBrsint64()\fR. + +.SS "\fBrfloat()\fR, \fBrdouble()\fR" +.sp +.LP +The functions "\fBrfloat()\fR and \fBrdouble()\fR" work like the other read +functions, except that they read a single precision and double precision +IEEE-754 floating point value instead. + +.SS "\fBwfloat()\fR, \fBwdouble()\fR" +.sp +.LP +The functions "\fBrfloat()\fR and \fBrdouble()\fR" work like the other write +functions, except that the domain for a float is that of a single precision 4 +byte value. The domain for a double is any \fBNumber\fR in ECMAScript, which is +defined to be represented by a double. + +.SH ATTRIBUTES +.sp +.LP +See \fBattributes\fR(5) for descriptions of the following attributes: +.sp + +.sp +.TS +box; +c | c +l | l . +ATTRIBUTE TYPE ATTRIBUTE VALUE +_ +Interface Stability Committed +_ +MT-Level See below. +_ +Standard Not standardized. +.TE + +.sp +.LP + +All functions are MT-safe in so far as there aren't shared memory MT concerns in +most node programs. If one where to concoct such an environment, these functions +wouldn't be MT-safe. + +.SH SEE ALSO +.sp +.LP diff --git a/tst/ctf/tst.int.js b/tst/ctf/tst.int.js index 80b57a7..0ec0f76 100644 --- a/tst/ctf/tst.int.js +++ b/tst/ctf/tst.int.js @@ -8,7 +8,7 @@ function test() data = JSON.parse(mod_fs.readFileSync('./int.json').toString()); parser = mod_ctype.parseCTF(data, { endian: 'big' }); - mod_assert.deepEqual(parser.lstypes(), { 'int': 'uint32_t' }); + mod_assert.deepEqual(parser.lstypes(), { 'int': 'int32_t' }); } test(); diff --git a/tst/ctf/tst.struct.js b/tst/ctf/tst.struct.js index 9dcc552..c62f41f 100644 --- a/tst/ctf/tst.struct.js +++ b/tst/ctf/tst.struct.js @@ -8,7 +8,7 @@ function test() data = JSON.parse(mod_fs.readFileSync('./struct.json').toString()); parser = mod_ctype.parseCTF(data, { endian: 'big' }); - mod_assert.deepEqual(parser.lstypes(), { 'long': 'uint32_t', + mod_assert.deepEqual(parser.lstypes(), { 'long': 'int32_t', 'time_t': 'long', 'timestruc_t': 'struct timespec', 'struct timespec': [ { 'tv_sec': { 'type': 'time_t' } }, diff --git a/tst/ctf/tst.typedef.js b/tst/ctf/tst.typedef.js index 2595822..9006cd1 100644 --- a/tst/ctf/tst.typedef.js +++ b/tst/ctf/tst.typedef.js @@ -9,7 +9,7 @@ function test() data = JSON.parse(mod_fs.readFileSync('./typedef.json').toString()); parser = mod_ctype.parseCTF(data, { endian: 'big' }); mod_assert.deepEqual(parser.lstypes(), { 'bar_t': 'int', - 'int': 'uint32_t' }); + 'int': 'int32_t' }); } test(); diff --git a/tst/ctio/int/tst.64.js b/tst/ctio/int/tst.64.js new file mode 100644 index 0000000..1ffdac0 --- /dev/null +++ b/tst/ctio/int/tst.64.js @@ -0,0 +1,638 @@ +/* + * Test our ability to read and write signed 64-bit integers. + */ + +var mod_ctype = require('../../../ctio.js'); +var ASSERT = require('assert'); + +function testRead() +{ + var res, data; + data = new Buffer(10); + + data[0] = 0x32; + data[1] = 0x65; + data[2] = 0x42; + data[3] = 0x56; + data[4] = 0x23; + data[5] = 0xff; + data[6] = 0xff; + data[7] = 0xff; + data[8] = 0x89; + data[9] = 0x11; + res = mod_ctype.rsint64(data, 'big', 0); + ASSERT.equal(0x32654256, res[0]); + ASSERT.equal(0x23ffffff, res[1]); + res = mod_ctype.rsint64(data, 'big', 1); + ASSERT.equal(0x65425623, res[0]); + ASSERT.equal(0xffffff89, res[1]); + res = mod_ctype.rsint64(data, 'big', 2); + ASSERT.equal(0x425623ff, res[0]); + ASSERT.equal(0xffff8911, res[1]); + res = mod_ctype.rsint64(data, 'little', 0); + ASSERT.equal(-0x000000dc, res[0]); + ASSERT.equal(-0xa9bd9ace, res[1]); + res = mod_ctype.rsint64(data, 'little', 1); + ASSERT.equal(-0x76000000, res[0]); + ASSERT.equal(-0xdca9bd9b, res[1]); + res = mod_ctype.rsint64(data, 'little', 2); + ASSERT.equal(0x1189ffff, res[0]); + ASSERT.equal(0xff235642, res[1]); + + data.fill(0x00); + res = mod_ctype.rsint64(data, 'big', 0); + ASSERT.equal(0x00000000, res[0]); + ASSERT.equal(0x00000000, res[1]); + res = mod_ctype.rsint64(data, 'big', 1); + ASSERT.equal(0x00000000, res[0]); + ASSERT.equal(0x00000000, res[1]); + res = mod_ctype.rsint64(data, 'big', 2); + ASSERT.equal(0x00000000, res[0]); + ASSERT.equal(0x00000000, res[1]); + res = mod_ctype.rsint64(data, 'little', 0); + ASSERT.equal(0x00000000, res[0]); + ASSERT.equal(0x00000000, res[1]); + res = mod_ctype.rsint64(data, 'little', 1); + ASSERT.equal(0x00000000, res[0]); + ASSERT.equal(0x00000000, res[1]); + res = mod_ctype.rsint64(data, 'little', 2); + ASSERT.equal(0x00000000, res[0]); + ASSERT.equal(0x00000000, res[1]); + + data.fill(0xff); + res = mod_ctype.rsint64(data, 'big', 0); + ASSERT.equal(0x00000000, res[0]); + ASSERT.equal(-1, res[1]); + res = mod_ctype.rsint64(data, 'big', 1); + ASSERT.equal(0x00000000, res[0]); + ASSERT.equal(-1, res[1]); + res = mod_ctype.rsint64(data, 'big', 2); + ASSERT.equal(0x00000000, res[0]); + ASSERT.equal(-1, res[1]); + res = mod_ctype.rsint64(data, 'little', 0); + ASSERT.equal(0x00000000, res[0]); + ASSERT.equal(-1, res[1]); + res = mod_ctype.rsint64(data, 'little', 1); + ASSERT.equal(0x00000000, res[0]); + ASSERT.equal(-1, res[1]); + res = mod_ctype.rsint64(data, 'little', 2); + ASSERT.equal(0x00000000, res[0]); + ASSERT.equal(-1, res[1]); + + data[0] = 0x80; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + data[4] = 0x00; + data[5] = 0x00; + data[6] = 0x00; + data[7] = 0x00; + res = mod_ctype.rsint64(data, 'big', 0); + ASSERT.equal(-0x80000000, res[0]); + ASSERT.equal(0, res[1]); + + + data[7] = 0x80; + data[6] = 0x00; + data[5] = 0x00; + data[4] = 0x00; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x00; + res = mod_ctype.rsint64(data, 'little', 0); + ASSERT.equal(-0x80000000, res[0]); + ASSERT.equal(0, res[1]); + + data[0] = 0x80; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + data[4] = 0x00; + data[5] = 0x00; + data[6] = 0x00; + data[7] = 0x01; + res = mod_ctype.rsint64(data, 'big', 0); + ASSERT.equal(-0x7fffffff, res[0]); + ASSERT.equal(-0xffffffff, res[1]); + + +} + +function testWriteZero() +{ + var data, buf; + buf = new Buffer(10); + + buf.fill(0x66); + data = [0, 0]; + mod_ctype.wsint64(data, 'big', buf, 0); + ASSERT.equal(0, buf[0]); + ASSERT.equal(0, buf[1]); + ASSERT.equal(0, buf[2]); + ASSERT.equal(0, buf[3]); + ASSERT.equal(0, buf[4]); + ASSERT.equal(0, buf[5]); + ASSERT.equal(0, buf[6]); + ASSERT.equal(0, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + data = [0, 0]; + mod_ctype.wsint64(data, 'big', buf, 1); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0, buf[1]); + ASSERT.equal(0, buf[2]); + ASSERT.equal(0, buf[3]); + ASSERT.equal(0, buf[4]); + ASSERT.equal(0, buf[5]); + ASSERT.equal(0, buf[6]); + ASSERT.equal(0, buf[7]); + ASSERT.equal(0, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + data = [0, 0]; + mod_ctype.wsint64(data, 'big', buf, 2); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x66, buf[1]); + ASSERT.equal(0, buf[2]); + ASSERT.equal(0, buf[3]); + ASSERT.equal(0, buf[4]); + ASSERT.equal(0, buf[5]); + ASSERT.equal(0, buf[6]); + ASSERT.equal(0, buf[7]); + ASSERT.equal(0, buf[8]); + ASSERT.equal(0, buf[9]); + + + buf.fill(0x66); + data = [0, 0]; + mod_ctype.wsint64(data, 'little', buf, 0); + ASSERT.equal(0, buf[0]); + ASSERT.equal(0, buf[1]); + ASSERT.equal(0, buf[2]); + ASSERT.equal(0, buf[3]); + ASSERT.equal(0, buf[4]); + ASSERT.equal(0, buf[5]); + ASSERT.equal(0, buf[6]); + ASSERT.equal(0, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + data = [0, 0]; + mod_ctype.wsint64(data, 'little', buf, 1); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0, buf[1]); + ASSERT.equal(0, buf[2]); + ASSERT.equal(0, buf[3]); + ASSERT.equal(0, buf[4]); + ASSERT.equal(0, buf[5]); + ASSERT.equal(0, buf[6]); + ASSERT.equal(0, buf[7]); + ASSERT.equal(0, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + data = [0, 0]; + mod_ctype.wsint64(data, 'little', buf, 2); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x66, buf[1]); + ASSERT.equal(0, buf[2]); + ASSERT.equal(0, buf[3]); + ASSERT.equal(0, buf[4]); + ASSERT.equal(0, buf[5]); + ASSERT.equal(0, buf[6]); + ASSERT.equal(0, buf[7]); + ASSERT.equal(0, buf[8]); + ASSERT.equal(0, buf[9]); +} + +/* + * Also include tests that are going to force us to go into a negative value and + * insure that it's written correctly. + */ +function testWrite() +{ + var data, buf; + + buf = new Buffer(10); + data = [ 0x234456, 0x87 ]; + buf.fill(0x66); + mod_ctype.wsint64(data, 'big', buf, 0); + ASSERT.equal(0x00, buf[0]); + ASSERT.equal(0x23, buf[1]); + ASSERT.equal(0x44, buf[2]); + ASSERT.equal(0x56, buf[3]); + ASSERT.equal(0x00, buf[4]); + ASSERT.equal(0x00, buf[5]); + ASSERT.equal(0x00, buf[6]); + ASSERT.equal(0x87, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wsint64(data, 'big', buf, 1); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x00, buf[1]); + ASSERT.equal(0x23, buf[2]); + ASSERT.equal(0x44, buf[3]); + ASSERT.equal(0x56, buf[4]); + ASSERT.equal(0x00, buf[5]); + ASSERT.equal(0x00, buf[6]); + ASSERT.equal(0x00, buf[7]); + ASSERT.equal(0x87, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wsint64(data, 'big', buf, 2); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x66, buf[1]); + ASSERT.equal(0x00, buf[2]); + ASSERT.equal(0x23, buf[3]); + ASSERT.equal(0x44, buf[4]); + ASSERT.equal(0x56, buf[5]); + ASSERT.equal(0x00, buf[6]); + ASSERT.equal(0x00, buf[7]); + ASSERT.equal(0x00, buf[8]); + ASSERT.equal(0x87, buf[9]); + + buf.fill(0x66); + mod_ctype.wsint64(data, 'little', buf, 0); + ASSERT.equal(0x87, buf[0]); + ASSERT.equal(0x00, buf[1]); + ASSERT.equal(0x00, buf[2]); + ASSERT.equal(0x00, buf[3]); + ASSERT.equal(0x56, buf[4]); + ASSERT.equal(0x44, buf[5]); + ASSERT.equal(0x23, buf[6]); + ASSERT.equal(0x00, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wsint64(data, 'little', buf, 1); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x87, buf[1]); + ASSERT.equal(0x00, buf[2]); + ASSERT.equal(0x00, buf[3]); + ASSERT.equal(0x00, buf[4]); + ASSERT.equal(0x56, buf[5]); + ASSERT.equal(0x44, buf[6]); + ASSERT.equal(0x23, buf[7]); + ASSERT.equal(0x00, buf[8]); + ASSERT.equal(0x66, buf[9]); + + + buf.fill(0x66); + mod_ctype.wsint64(data, 'little', buf, 2); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x66, buf[1]); + ASSERT.equal(0x87, buf[2]); + ASSERT.equal(0x00, buf[3]); + ASSERT.equal(0x00, buf[4]); + ASSERT.equal(0x00, buf[5]); + ASSERT.equal(0x56, buf[6]); + ASSERT.equal(0x44, buf[7]); + ASSERT.equal(0x23, buf[8]); + ASSERT.equal(0x00, buf[9]); + + data = [0x3421, 0x34abcdba]; + buf.fill(0x66); + mod_ctype.wsint64(data, 'big', buf, 0); + ASSERT.equal(0x00, buf[0]); + ASSERT.equal(0x00, buf[1]); + ASSERT.equal(0x34, buf[2]); + ASSERT.equal(0x21, buf[3]); + ASSERT.equal(0x34, buf[4]); + ASSERT.equal(0xab, buf[5]); + ASSERT.equal(0xcd, buf[6]); + ASSERT.equal(0xba, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wsint64(data, 'big', buf, 1); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x00, buf[1]); + ASSERT.equal(0x00, buf[2]); + ASSERT.equal(0x34, buf[3]); + ASSERT.equal(0x21, buf[4]); + ASSERT.equal(0x34, buf[5]); + ASSERT.equal(0xab, buf[6]); + ASSERT.equal(0xcd, buf[7]); + ASSERT.equal(0xba, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wsint64(data, 'big', buf, 2); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x66, buf[1]); + ASSERT.equal(0x00, buf[2]); + ASSERT.equal(0x00, buf[3]); + ASSERT.equal(0x34, buf[4]); + ASSERT.equal(0x21, buf[5]); + ASSERT.equal(0x34, buf[6]); + ASSERT.equal(0xab, buf[7]); + ASSERT.equal(0xcd, buf[8]); + ASSERT.equal(0xba, buf[9]); + + buf.fill(0x66); + mod_ctype.wsint64(data, 'little', buf, 0); + ASSERT.equal(0xba, buf[0]); + ASSERT.equal(0xcd, buf[1]); + ASSERT.equal(0xab, buf[2]); + ASSERT.equal(0x34, buf[3]); + ASSERT.equal(0x21, buf[4]); + ASSERT.equal(0x34, buf[5]); + ASSERT.equal(0x00, buf[6]); + ASSERT.equal(0x00, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wsint64(data, 'little', buf, 1); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0xba, buf[1]); + ASSERT.equal(0xcd, buf[2]); + ASSERT.equal(0xab, buf[3]); + ASSERT.equal(0x34, buf[4]); + ASSERT.equal(0x21, buf[5]); + ASSERT.equal(0x34, buf[6]); + ASSERT.equal(0x00, buf[7]); + ASSERT.equal(0x00, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wsint64(data, 'little', buf, 2); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x66, buf[1]); + ASSERT.equal(0xba, buf[2]); + ASSERT.equal(0xcd, buf[3]); + ASSERT.equal(0xab, buf[4]); + ASSERT.equal(0x34, buf[5]); + ASSERT.equal(0x21, buf[6]); + ASSERT.equal(0x34, buf[7]); + ASSERT.equal(0x00, buf[8]); + ASSERT.equal(0x00, buf[9]); + + + data = [ -0x80000000, 0 ]; + buf.fill(0x66); + mod_ctype.wsint64(data, 'big', buf, 0); + ASSERT.equal(0x80, buf[0]); + ASSERT.equal(0x00, buf[1]); + ASSERT.equal(0x00, buf[2]); + ASSERT.equal(0x00, buf[3]); + ASSERT.equal(0x00, buf[4]); + ASSERT.equal(0x00, buf[5]); + ASSERT.equal(0x00, buf[6]); + ASSERT.equal(0x00, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wsint64(data, 'little', buf, 0); + ASSERT.equal(0x00, buf[0]); + ASSERT.equal(0x00, buf[1]); + ASSERT.equal(0x00, buf[2]); + ASSERT.equal(0x00, buf[3]); + ASSERT.equal(0x00, buf[4]); + ASSERT.equal(0x00, buf[5]); + ASSERT.equal(0x00, buf[6]); + ASSERT.equal(0x80, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + data = [ -0x7fffffff, -0xffffffff ]; + buf.fill(0x66); + mod_ctype.wsint64(data, 'big', buf, 0); + ASSERT.equal(0x80, buf[0]); + ASSERT.equal(0x00, buf[1]); + ASSERT.equal(0x00, buf[2]); + ASSERT.equal(0x00, buf[3]); + ASSERT.equal(0x00, buf[4]); + ASSERT.equal(0x00, buf[5]); + ASSERT.equal(0x00, buf[6]); + ASSERT.equal(0x01, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wsint64(data, 'little', buf, 0); + ASSERT.equal(0x01, buf[0]); + ASSERT.equal(0x00, buf[1]); + ASSERT.equal(0x00, buf[2]); + ASSERT.equal(0x00, buf[3]); + ASSERT.equal(0x00, buf[4]); + ASSERT.equal(0x00, buf[5]); + ASSERT.equal(0x00, buf[6]); + ASSERT.equal(0x80, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + data = [ 0x0, -0x1]; + buf.fill(0x66); + mod_ctype.wsint64(data, 'big', buf, 0); + ASSERT.equal(0xff, buf[0]); + ASSERT.equal(0xff, buf[1]); + ASSERT.equal(0xff, buf[2]); + ASSERT.equal(0xff, buf[3]); + ASSERT.equal(0xff, buf[4]); + ASSERT.equal(0xff, buf[5]); + ASSERT.equal(0xff, buf[6]); + ASSERT.equal(0xff, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wsint64(data, 'little', buf, 0); + ASSERT.equal(0xff, buf[0]); + ASSERT.equal(0xff, buf[1]); + ASSERT.equal(0xff, buf[2]); + ASSERT.equal(0xff, buf[3]); + ASSERT.equal(0xff, buf[4]); + ASSERT.equal(0xff, buf[5]); + ASSERT.equal(0xff, buf[6]); + ASSERT.equal(0xff, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); +} + +/* + * Make sure we catch invalid writes. + */ +function testWriteInvalid() +{ + var data, buf; + + /* Buffer too small */ + buf = new Buffer(4); + data = [ 0, 0]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 0); + }, Error, 'buffer too small'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 0); + }, Error, 'buffer too small'); + + /* Beyond the end of the buffer */ + buf = new Buffer(12); + data = [ 0, 0]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 11); + }, Error, 'write beyond end of buffer'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 11); + }, Error, 'write beyond end of buffer'); + + /* Write fractional values */ + buf = new Buffer(12); + data = [ 3.33, 0 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write fractions'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write fractions'); + + data = [ 0, 3.3 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write fractions'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write fractions'); + + data = [ -3.33, 0 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write fractions'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write fractions'); + + data = [ 0, -3.3 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write fractions'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write fractions'); + + data = [ 3.33, 2.42 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write fractions'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write fractions'); + + data = [ 3.33, -2.42 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write fractions'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write fractions'); + + data = [ -3.33, -2.42 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write fractions'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write fractions'); + + data = [ -3.33, 2.42 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write fractions'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write fractions'); + + /* Signs don't match */ + buf = new Buffer(12); + data = [ 0x800000, -0x32 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write too large'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write too large'); + + data = [ -0x800000, 0x32 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write too large'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write too large'); + + /* Write values that are too large */ + buf = new Buffer(12); + data = [ 0x80000000, 0 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write too large'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write too large'); + + data = [ 0x7fffffff, 0x100000000 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write too large'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write too large'); + + data = [ 0x00, 0x800000000 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write too large'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write too large'); + + data = [ 0xffffffffff, 0xffffff238 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write too large'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write too large'); + + data = [ 0x23, 0xffffff238 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write too large'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write too large'); + + data = [ -0x80000000, -0xfff238 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write too large'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write too large'); + + data = [ -0x80000004, -0xfff238 ]; + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'big', buf, 1); + }, Error, 'write too large'); + ASSERT.throws(function () { + mod_ctype.wsint64(data, 'little', buf, 1); + }, Error, 'write too large'); +} + + +testRead(); +testWrite(); +testWriteZero(); +testWriteInvalid(); diff --git a/tst/ctio/int/tst.wbounds.js b/tst/ctio/int/tst.wbounds.js new file mode 100644 index 0000000..5488177 --- /dev/null +++ b/tst/ctio/int/tst.wbounds.js @@ -0,0 +1,53 @@ +/* + * Test to make sure that we properly are erroring whenever we try to write + * beyond the size of the integer. + */ + +var mod_ctio = require('../../../ctio.js'); +var mod_assert = require('assert'); +var tb = new Buffer(16); /* Largest buffer we'll need */ + +var cases = [ + { func: + function () { + mod_ctio.wsint8(0x80, 'big', tb, 0); + }, test: '+int8_t' }, + { func: + function () { + mod_ctio.wsint8(-0x81, 'big', tb, 0); + }, test: '-int8_t' }, + + { func: + function () { + mod_ctio.wsint16(0x8000, 'big', tb, 0); + }, test: '+int16_t' }, + { func: + function () { + mod_ctio.wsint16(-0x8001, 'big', tb, 0); + }, test: '-int16_t' }, + { func: + function () { + mod_ctio.wsint32(0x80000000, 'big', tb, 0); + }, test: '+int32_t' }, + { func: + function () { + mod_ctio.wsint32(-0x80000001, 'big', tb, 0); + }, test: '-int32_t' }, + { func: + function () { + mod_ctio.wsint64([ 0x80000000, 0 ], 'big', tb, 0); + }, test: '+int64_t' }, + { func: + function () { + mod_ctio.wsint64([ -0x80000000, -1 ], 'big', tb, 0); + }, test: '-int64_t' } +]; + +function test() +{ + var ii; + for (ii = 0; ii < cases.length; ii++) + mod_assert.throws(cases[ii]['func'], Error, cases[ii]['test']); +} + +test(); diff --git a/tst/ctio/uint/tst.64.js b/tst/ctio/uint/tst.64.js new file mode 100644 index 0000000..cf66ac7 --- /dev/null +++ b/tst/ctio/uint/tst.64.js @@ -0,0 +1,451 @@ +/* + * Test our ability to read and write unsigned 64-bit integers. + */ + +var mod_ctype = require('../../../ctio.js'); +var ASSERT = require('assert'); + +function testRead() +{ + var res, data; + data = new Buffer(10); + + data[0] = 0x32; + data[1] = 0x65; + data[2] = 0x42; + data[3] = 0x56; + data[4] = 0x23; + data[5] = 0xff; + data[6] = 0xff; + data[7] = 0xff; + data[8] = 0x89; + data[9] = 0x11; + res = mod_ctype.ruint64(data, 'big', 0); + ASSERT.equal(0x32654256, res[0]); + ASSERT.equal(0x23ffffff, res[1]); + res = mod_ctype.ruint64(data, 'big', 1); + ASSERT.equal(0x65425623, res[0]); + ASSERT.equal(0xffffff89, res[1]); + res = mod_ctype.ruint64(data, 'big', 2); + ASSERT.equal(0x425623ff, res[0]); + ASSERT.equal(0xffff8911, res[1]); + res = mod_ctype.ruint64(data, 'little', 0); + ASSERT.equal(0xffffff23, res[0]); + ASSERT.equal(0x56426532, res[1]); + res = mod_ctype.ruint64(data, 'little', 1); + ASSERT.equal(0x89ffffff, res[0]); + ASSERT.equal(0x23564265, res[1]); + res = mod_ctype.ruint64(data, 'little', 2); + ASSERT.equal(0x1189ffff, res[0]); + ASSERT.equal(0xff235642, res[1]); + +} + +function testReadOver() +{ + var res, data; + data = new Buffer(10); + + data[0] = 0x80; + data[1] = 0xff; + data[2] = 0x80; + data[3] = 0xff; + data[4] = 0x80; + data[5] = 0xff; + data[6] = 0x80; + data[7] = 0xff; + data[8] = 0x80; + data[9] = 0xff; + res = mod_ctype.ruint64(data, 'big', 0); + ASSERT.equal(0x80ff80ff, res[0]); + ASSERT.equal(0x80ff80ff, res[1]); + res = mod_ctype.ruint64(data, 'big', 1); + ASSERT.equal(0xff80ff80, res[0]); + ASSERT.equal(0xff80ff80, res[1]); + res = mod_ctype.ruint64(data, 'big', 2); + ASSERT.equal(0x80ff80ff, res[0]); + ASSERT.equal(0x80ff80ff, res[1]); + res = mod_ctype.ruint64(data, 'little', 0); + ASSERT.equal(0xff80ff80, res[0]); + ASSERT.equal(0xff80ff80, res[1]); + res = mod_ctype.ruint64(data, 'little', 1); + ASSERT.equal(0x80ff80ff, res[0]); + ASSERT.equal(0x80ff80ff, res[1]); + res = mod_ctype.ruint64(data, 'little', 2); + ASSERT.equal(0xff80ff80, res[0]); + ASSERT.equal(0xff80ff80, res[1]); +} + +function testWriteZero() +{ + var data, buf; + buf = new Buffer(10); + + buf.fill(0x66); + data = [0, 0]; + mod_ctype.wuint64(data, 'big', buf, 0); + ASSERT.equal(0, buf[0]); + ASSERT.equal(0, buf[1]); + ASSERT.equal(0, buf[2]); + ASSERT.equal(0, buf[3]); + ASSERT.equal(0, buf[4]); + ASSERT.equal(0, buf[5]); + ASSERT.equal(0, buf[6]); + ASSERT.equal(0, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + data = [0, 0]; + mod_ctype.wuint64(data, 'big', buf, 1); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0, buf[1]); + ASSERT.equal(0, buf[2]); + ASSERT.equal(0, buf[3]); + ASSERT.equal(0, buf[4]); + ASSERT.equal(0, buf[5]); + ASSERT.equal(0, buf[6]); + ASSERT.equal(0, buf[7]); + ASSERT.equal(0, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + data = [0, 0]; + mod_ctype.wuint64(data, 'big', buf, 2); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x66, buf[1]); + ASSERT.equal(0, buf[2]); + ASSERT.equal(0, buf[3]); + ASSERT.equal(0, buf[4]); + ASSERT.equal(0, buf[5]); + ASSERT.equal(0, buf[6]); + ASSERT.equal(0, buf[7]); + ASSERT.equal(0, buf[8]); + ASSERT.equal(0, buf[9]); + + + buf.fill(0x66); + data = [0, 0]; + mod_ctype.wuint64(data, 'little', buf, 0); + ASSERT.equal(0, buf[0]); + ASSERT.equal(0, buf[1]); + ASSERT.equal(0, buf[2]); + ASSERT.equal(0, buf[3]); + ASSERT.equal(0, buf[4]); + ASSERT.equal(0, buf[5]); + ASSERT.equal(0, buf[6]); + ASSERT.equal(0, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + data = [0, 0]; + mod_ctype.wuint64(data, 'little', buf, 1); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0, buf[1]); + ASSERT.equal(0, buf[2]); + ASSERT.equal(0, buf[3]); + ASSERT.equal(0, buf[4]); + ASSERT.equal(0, buf[5]); + ASSERT.equal(0, buf[6]); + ASSERT.equal(0, buf[7]); + ASSERT.equal(0, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + data = [0, 0]; + mod_ctype.wuint64(data, 'little', buf, 2); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x66, buf[1]); + ASSERT.equal(0, buf[2]); + ASSERT.equal(0, buf[3]); + ASSERT.equal(0, buf[4]); + ASSERT.equal(0, buf[5]); + ASSERT.equal(0, buf[6]); + ASSERT.equal(0, buf[7]); + ASSERT.equal(0, buf[8]); + ASSERT.equal(0, buf[9]); +} + +/* + * Also include tests that are going to force us to go into a negative value and + * insure that it's written correctly. + */ +function testWrite() +{ + var data, buf; + + buf = new Buffer(10); + data = [ 0x234456, 0x87 ]; + buf.fill(0x66); + mod_ctype.wuint64(data, 'big', buf, 0); + ASSERT.equal(0x00, buf[0]); + ASSERT.equal(0x23, buf[1]); + ASSERT.equal(0x44, buf[2]); + ASSERT.equal(0x56, buf[3]); + ASSERT.equal(0x00, buf[4]); + ASSERT.equal(0x00, buf[5]); + ASSERT.equal(0x00, buf[6]); + ASSERT.equal(0x87, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wuint64(data, 'big', buf, 1); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x00, buf[1]); + ASSERT.equal(0x23, buf[2]); + ASSERT.equal(0x44, buf[3]); + ASSERT.equal(0x56, buf[4]); + ASSERT.equal(0x00, buf[5]); + ASSERT.equal(0x00, buf[6]); + ASSERT.equal(0x00, buf[7]); + ASSERT.equal(0x87, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wuint64(data, 'big', buf, 2); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x66, buf[1]); + ASSERT.equal(0x00, buf[2]); + ASSERT.equal(0x23, buf[3]); + ASSERT.equal(0x44, buf[4]); + ASSERT.equal(0x56, buf[5]); + ASSERT.equal(0x00, buf[6]); + ASSERT.equal(0x00, buf[7]); + ASSERT.equal(0x00, buf[8]); + ASSERT.equal(0x87, buf[9]); + + buf.fill(0x66); + mod_ctype.wuint64(data, 'little', buf, 0); + ASSERT.equal(0x87, buf[0]); + ASSERT.equal(0x00, buf[1]); + ASSERT.equal(0x00, buf[2]); + ASSERT.equal(0x00, buf[3]); + ASSERT.equal(0x56, buf[4]); + ASSERT.equal(0x44, buf[5]); + ASSERT.equal(0x23, buf[6]); + ASSERT.equal(0x00, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wuint64(data, 'little', buf, 1); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x87, buf[1]); + ASSERT.equal(0x00, buf[2]); + ASSERT.equal(0x00, buf[3]); + ASSERT.equal(0x00, buf[4]); + ASSERT.equal(0x56, buf[5]); + ASSERT.equal(0x44, buf[6]); + ASSERT.equal(0x23, buf[7]); + ASSERT.equal(0x00, buf[8]); + ASSERT.equal(0x66, buf[9]); + + + buf.fill(0x66); + mod_ctype.wuint64(data, 'little', buf, 2); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x66, buf[1]); + ASSERT.equal(0x87, buf[2]); + ASSERT.equal(0x00, buf[3]); + ASSERT.equal(0x00, buf[4]); + ASSERT.equal(0x00, buf[5]); + ASSERT.equal(0x56, buf[6]); + ASSERT.equal(0x44, buf[7]); + ASSERT.equal(0x23, buf[8]); + ASSERT.equal(0x00, buf[9]); + + data = [0xffff3421, 0x34abcdba]; + buf.fill(0x66); + mod_ctype.wuint64(data, 'big', buf, 0); + ASSERT.equal(0xff, buf[0]); + ASSERT.equal(0xff, buf[1]); + ASSERT.equal(0x34, buf[2]); + ASSERT.equal(0x21, buf[3]); + ASSERT.equal(0x34, buf[4]); + ASSERT.equal(0xab, buf[5]); + ASSERT.equal(0xcd, buf[6]); + ASSERT.equal(0xba, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wuint64(data, 'big', buf, 1); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0xff, buf[1]); + ASSERT.equal(0xff, buf[2]); + ASSERT.equal(0x34, buf[3]); + ASSERT.equal(0x21, buf[4]); + ASSERT.equal(0x34, buf[5]); + ASSERT.equal(0xab, buf[6]); + ASSERT.equal(0xcd, buf[7]); + ASSERT.equal(0xba, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wuint64(data, 'big', buf, 2); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x66, buf[1]); + ASSERT.equal(0xff, buf[2]); + ASSERT.equal(0xff, buf[3]); + ASSERT.equal(0x34, buf[4]); + ASSERT.equal(0x21, buf[5]); + ASSERT.equal(0x34, buf[6]); + ASSERT.equal(0xab, buf[7]); + ASSERT.equal(0xcd, buf[8]); + ASSERT.equal(0xba, buf[9]); + + buf.fill(0x66); + mod_ctype.wuint64(data, 'little', buf, 0); + ASSERT.equal(0xba, buf[0]); + ASSERT.equal(0xcd, buf[1]); + ASSERT.equal(0xab, buf[2]); + ASSERT.equal(0x34, buf[3]); + ASSERT.equal(0x21, buf[4]); + ASSERT.equal(0x34, buf[5]); + ASSERT.equal(0xff, buf[6]); + ASSERT.equal(0xff, buf[7]); + ASSERT.equal(0x66, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wuint64(data, 'little', buf, 1); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0xba, buf[1]); + ASSERT.equal(0xcd, buf[2]); + ASSERT.equal(0xab, buf[3]); + ASSERT.equal(0x34, buf[4]); + ASSERT.equal(0x21, buf[5]); + ASSERT.equal(0x34, buf[6]); + ASSERT.equal(0xff, buf[7]); + ASSERT.equal(0xff, buf[8]); + ASSERT.equal(0x66, buf[9]); + + buf.fill(0x66); + mod_ctype.wuint64(data, 'little', buf, 2); + ASSERT.equal(0x66, buf[0]); + ASSERT.equal(0x66, buf[1]); + ASSERT.equal(0xba, buf[2]); + ASSERT.equal(0xcd, buf[3]); + ASSERT.equal(0xab, buf[4]); + ASSERT.equal(0x34, buf[5]); + ASSERT.equal(0x21, buf[6]); + ASSERT.equal(0x34, buf[7]); + ASSERT.equal(0xff, buf[8]); + ASSERT.equal(0xff, buf[9]); +} + +/* + * Make sure we catch invalid writes. + */ +function testWriteInvalid() +{ + var data, buf; + + /* Buffer too small */ + buf = new Buffer(4); + data = [ 0, 0]; + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'big', buf, 0); + }, Error, 'buffer too small'); + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'little', buf, 0); + }, Error, 'buffer too small'); + + /* Beyond the end of the buffer */ + buf = new Buffer(12); + data = [ 0, 0]; + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'little', buf, 11); + }, Error, 'write beyond end of buffer'); + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'big', buf, 11); + }, Error, 'write beyond end of buffer'); + + /* Write negative values */ + buf = new Buffer(12); + data = [ -3, 0 ]; + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'big', buf, 1); + }, Error, 'write negative number'); + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'little', buf, 1); + }, Error, 'write negative number'); + + data = [ 0, -3 ]; + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'big', buf, 1); + }, Error, 'write negative number'); + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'little', buf, 1); + }, Error, 'write negative number'); + + data = [ -3, -3 ]; + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'big', buf, 1); + }, Error, 'write negative number'); + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'little', buf, 1); + }, Error, 'write negative number'); + + + /* Write fractional values */ + buf = new Buffer(12); + data = [ 3.33, 0 ]; + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'big', buf, 1); + }, Error, 'write fractions'); + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'little', buf, 1); + }, Error, 'write fractions'); + + data = [ 0, 3.3 ]; + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'big', buf, 1); + }, Error, 'write fractions'); + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'little', buf, 1); + }, Error, 'write fractions'); + + data = [ 3.33, 2.42 ]; + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'big', buf, 1); + }, Error, 'write fractions'); + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'little', buf, 1); + }, Error, 'write fractions'); + + /* Write values that are too large */ + buf = new Buffer(12); + data = [ 0xffffffffff, 23 ]; + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'big', buf, 1); + }, Error, 'write too large'); + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'little', buf, 1); + }, Error, 'write too large'); + + data = [ 0xffffffffff, 0xffffff238 ]; + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'big', buf, 1); + }, Error, 'write too large'); + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'little', buf, 1); + }, Error, 'write too large'); + + data = [ 0x23, 0xffffff238 ]; + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'big', buf, 1); + }, Error, 'write too large'); + ASSERT.throws(function () { + mod_ctype.wuint64(data, 'little', buf, 1); + }, Error, 'write too large'); +} + + +testRead(); +testReadOver(); +testWriteZero(); +testWrite(); +testWriteInvalid(); diff --git a/tst/ctype/tst.oldwrite.js b/tst/ctype/tst.oldwrite.js new file mode 100644 index 0000000..9491cf6 --- /dev/null +++ b/tst/ctype/tst.oldwrite.js @@ -0,0 +1,28 @@ +/* + * A long overdue test to go through and verify that we can read and write + * structures as well as nested structures. + */ + +var mod_ctype = require('../../ctype.js'); +var mod_assert = require('assert'); + +function test() +{ + var parser, buf, data; + parser = new mod_ctype.Parser({ + endian: 'little' + }); + parser.typedef('point_t', [ + { x: { type: 'uint8_t' } }, + { y: { type: 'uint8_t' } } + ]); + buf = new Buffer(2); + data = [ + { point: { type: 'point_t', value: [ 23, 42 ] } } + ]; + parser.writeData(data, buf, 0); + mod_assert.ok(buf[0] == 23); + mod_assert.ok(buf[1] == 42); +} + +test(); diff --git a/tst/ctype/tst.structw.js b/tst/ctype/tst.structw.js new file mode 100644 index 0000000..09c1a5b --- /dev/null +++ b/tst/ctype/tst.structw.js @@ -0,0 +1,28 @@ +/* + * A long overdue test to go through and verify that we can read and write + * structures as well as nested structures. + */ + +var mod_ctype = require('../../ctype.js'); +var mod_assert = require('assert'); + +function test() +{ + var parser, buf, data; + parser = new mod_ctype.Parser({ + endian: 'little' + }); + parser.typedef('point_t', [ + { x: { type: 'uint8_t' } }, + { y: { type: 'uint8_t' } } + ]); + buf = new Buffer(2); + data = [ + { point: { type: 'point_t' } } + ]; + parser.writeData(data, buf, 0, [ [ 23, 42 ] ]); + mod_assert.ok(buf[0] == 23); + mod_assert.ok(buf[1] == 42); +} + +test();