text: /*
   * rm - Feb 2011
   * ctype.js
   *
   * This module provides a simple abstraction towards reading and writing
   * different types of binary data. It is designed to use ctio.js and provide a
   * richer and more expressive API on top of it.
   *
   * By default we support the following as built in basic types:
   *	int8_t
   *	int16_t
   *	int32_t
   *	uint8_t
   *	uint16_t
   *	uint32_t
   *	uint64_t
   *	float
   *	double
   *	char
   *	char[]
   *
   * Each type is returned as a Number, with the exception of char and char[]
   * which are returned as Node Buffers. A char is considered a uint8_t.
   *
   * Requests to read and write data are specified as an array of JSON objects.
   * This is also the same way that one declares structs. Even if just a single
   * value is requested, it must be done as a struct. The array order determines
   * the order that we try and read values. Each entry has the following format
   * with values marked with a * being optional.
   *
   * { key: { type: /type/, value*: /value/, offset*: /offset/ }
   *
   * If offset is defined, we lseek(offset, SEEK_SET) before reading the next
   * value. Value is defined when we're writing out data, otherwise it's ignored.
   *
   */
  
  var mod_ctf = require('./ctf.js');
  var mod_ctio = require('./ctio.js');
  var mod_assert = require('assert');
  
  /*
   * This is the set of basic types that we support.
   *
   *	read		The function to call to read in a value from a buffer
   *
   *	write		The function to call to write a value to a buffer
   *
   */
  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 },
      'char[]': { read: ctReadCharArray, write: ctWriteCharArray }
  };
  
  /*
   * The following are wrappers around the CType IO low level API. They encode
   * knowledge about the size and return something in the expected format.
   */
  function ctReadUint8(endian, buffer, offset)
  {
  	var val = mod_ctio.ruint8(buffer, endian, offset);
  	return ({ value: val, size: 1 });
  }
  
  function ctReadUint16(endian, buffer, offset)
  {
  	var val = mod_ctio.ruint16(buffer, endian, offset);
  	return ({ value: val, size: 2 });
  }
  
  function ctReadUint32(endian, buffer, offset)
  {
  	var val = mod_ctio.ruint32(buffer, endian, 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);
  	return ({ value: val, size: 1 });
  }
  
  function ctReadSint16(endian, buffer, offset)
  {
  	var val = mod_ctio.rsint16(buffer, endian, offset);
  	return ({ value: val, size: 2 });
  }
  
  function ctReadSint32(endian, buffer, offset)
  {
  	var val = mod_ctio.rsint32(buffer, endian, offset);
  	return ({ value: val, size: 4 });
  }
  
  function ctReadSint64(endian, buffer, offset)
  {
  	var val = mod_ctio.rsint64(buffer, endian, offset);
  	return ({ value: val, size: 8 });
  }
  
  function ctReadFloat(endian, buffer, offset)
  {
  	var val = mod_ctio.rfloat(buffer, endian, offset);
  	return ({ value: val, size: 4 });
  }
  
  function ctReadDouble(endian, buffer, offset)
  {
  	var val = mod_ctio.rdouble(buffer, endian, offset);
  	return ({ value: val, size: 8 });
  }
  
  /*
   * Reads a single character into a node buffer
   */
  function ctReadChar(endian, buffer, offset)
  {
  	var res = new Buffer(1);
  	res[0] = mod_ctio.ruint8(buffer, endian, offset);
  	return ({ value: res, size: 1 });
  }
  
  function ctReadCharArray(length, endian, buffer, offset)
  {
  	var ii;
  	var res = new Buffer(length);
  
  	for (ii = 0; ii < length; ii++)
  		res[ii] = mod_ctio.ruint8(buffer, endian, offset + ii);
  
  	return ({ value: res, size: length });
  }
  
  function ctWriteUint8(value, endian, buffer, offset)
  {
  	mod_ctio.wuint8(value, endian, buffer, offset);
  	return (1);
  }
  
  function ctWriteUint16(value, endian, buffer, offset)
  {
  	mod_ctio.wuint16(value, endian, buffer, offset);
  	return (2);
  }
  
  function ctWriteUint32(value, endian, buffer, offset)
  {
  	mod_ctio.wuint32(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);
  	return (1);
  }
  
  function ctWriteSint16(value, endian, buffer, offset)
  {
  	mod_ctio.wsint16(value, endian, buffer, offset);
  	return (2);
  }
  
  function ctWriteSint32(value, endian, buffer, offset)
  {
  	mod_ctio.wsint32(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);
  	return (4);
  }
  
  function ctWriteDouble(value, endian, buffer, offset)
  {
  	mod_ctio.wdouble(value, endian, buffer, offset);
  	return (8);
  }
  
  /*
   * Writes a single character into a node buffer
   */
  function ctWriteChar(value, endian, buffer, offset)
  {
  	if (!(value instanceof Buffer))
  		throw (new Error('Input must be a buffer'));
  
  	mod_ctio.ruint8(value[0], endian, buffer, offset);
  	return (1);
  }
  
  /*
   * We're going to write 0s into the buffer if the string is shorter than the
   * length of the array.
   */
  function ctWriteCharArray(value, length, endian, buffer, offset)
  {
  	var ii;
  
  	if (!(value instanceof Buffer))
  		throw (new Error('Input must be a buffer'));
  
  	if (value.length > length)
  		throw (new Error('value length greater than array length'));
  
  	for (ii = 0; ii < value.length && ii < length; ii++)
  		mod_ctio.wuint8(value[ii], endian, buffer, offset + ii);
  
  	for (; ii < length; ii++)
  		mod_ctio.wuint8(0, endian, offset + ii);
  
  
  	return (length);
  }
  
  /*
   * Each parser has their own set of types. We want to make sure that they each
   * get their own copy as they may need to modify it.
   */
  function ctGetBasicTypes()
  {
  	var ret = {};
  	var key;
  	for (key in deftypes)
  		ret[key] = deftypes[key];
  
  	return (ret);
  }
  
  /*
   * Given a string in the form of type[length] we want to split this into an
   * object that extracts that information. We want to note that we could possibly
   * have nested arrays so this should only check the furthest one. It may also be
   * the case that we have no [] pieces, in which case we just return the current
   * type.
   */
  function ctParseType(str)
  {
  	var begInd, endInd;
  	var type, len;
  	if (typeof (str) != 'string')
  		throw (new Error('type must be a Javascript string'));
  
  	endInd = str.lastIndexOf(']');
  	if (endInd == -1) {
  		if (str.lastIndexOf('[') != -1)
  			throw (new Error('found invalid type with \'[\' but ' +
  			    'no corresponding \']\''));
  
  		return ({ type: str });
  	}
  
  	begInd = str.lastIndexOf('[');
  	if (begInd == -1)
  		throw (new Error('found invalid type with \']\' but ' +
  		    'no corresponding \'[\''));
  
  	if (begInd >= endInd)
  		throw (new Error('malformed type, \']\' appears before \'[\''));
  
  	type = str.substring(0, begInd);
  	len = str.substring(begInd + 1, endInd);
  
  	return ({ type: type, len: len });
  }
  
  /*
   * Given a request validate that all of the fields for it are valid and make
   * sense. This includes verifying the following notions:
   *  - Each type requested is present in types
   *  - Only allow a name for a field to be specified once
   *  - If an array is specified, validate that the requested field exists and
   *    comes before it.
   *  - If fields is defined, check that each entry has the occurrence of field
   */
  function ctCheckReq(def, types, fields)
  {
  	var ii, jj;
  	var req, keys, key;
  	var found = {};
  
  	if (!(def instanceof Array))
  		throw (new Error('definition is not an array'));
  
  	if (def.length === 0)
  		throw (new Error('definition must have at least one element'));
  
  	for (ii = 0; ii < def.length; ii++) {
  		req = def[ii];
  		if (!(req instanceof Object))
  			throw (new Error('definition must be an array of' +
  			    'objects'));
  
  		keys = Object.keys(req);
  		if (keys.length != 1)
  			throw (new Error('definition entry must only have ' +
  			    'one key'));
  
  		if (keys[0] in found)
  			throw (new Error('Specified name already ' +
  			    'specified: ' + keys[0]));
  
  		if (!('type' in req[keys[0]]))
  			throw (new Error('missing required type definition'));
  
  		key = ctParseType(req[keys[0]]['type']);
  
  		/*
  		 * We may have nested arrays, we need to check the validity of
  		 * the types until the len field is undefined in key. However,
  		 * each time len is defined we need to verify it is either an
  		 * integer or corresponds to an already seen key.
  		 */
  		while (key['len'] !== undefined) {
  			if (isNaN(parseInt(key['len'], 10))) {
  				if (!(key['len'] in found))
  					throw (new Error('Given an array ' +
  					    'length without a matching type'));
  
  			}
  
  			key = ctParseType(key['type']);
  		}
  
  		/* Now we can validate if the type is valid */
  		if (!(key['type'] in types))
  			throw (new Error('type not found or typdefed: ' +
  			    key['type']));
  
  		/* Check for any required fields */
  		if (fields !== undefined) {
  			for (jj = 0; jj < fields.length; jj++) {
  				if (!(fields[jj] in req[keys[0]]))
  					throw (new Error('Missing required ' +
  					    'field: ' + fields[jj]));
  			}
  		}
  
  		found[keys[0]] = true;
  	}
  }
  
  
  /*
   * Create a new instance of the parser. Each parser has its own store of
   * typedefs and endianness. Conf is an object with the following required
   * values:
   *
   *	endian		Either 'big' or 'little' do determine the endianness we
   *			want to read from or write to.
   *
   * And the following optional values:
   *
   * 	char-type	Valid options here are uint8 and int8. If uint8 is
   * 			specified this changes the default behavior of a single
   * 			char from being a buffer of a single character to being
   * 			a uint8_t. If int8, it becomes an int8_t instead.
   */
  function CTypeParser(conf)
  {
  	if (!conf) throw (new Error('missing required argument'));
  
  	if (!('endian' in conf))
  		throw (new Error('missing required endian value'));
  
  	if (conf['endian'] != 'big' && conf['endian'] != 'little')
  		throw (new Error('Invalid endian type'));
  
  	if ('char-type' in conf && (conf['char-type'] != 'uint8' &&
  	    conf['char-type'] != 'int8'))
  		throw (new Error('invalid option for char-type: ' +
  		    conf['char-type']));
  
  	this.endian = conf['endian'];
  	this.types = ctGetBasicTypes();
  
  	/*
  	 * 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')
  		this.types['char'] = this.types['uint8_t'];
  
  	if ('char-type' in conf && conf['char-type'] == 'int8')
  		this.types['char'] = this.types['int8_t'];
  }
  
  /*
   * 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.
   *
   */
  CTypeParser.prototype.setEndian = function (endian)
  {
  	if (endian != 'big' && endian != 'little')
  		throw (new Error('invalid endian type, must be big or ' +
  		    'little'));
  
  	this.endian = endian;
  };
  
  /*
   * Returns the current value of the endian value for the parser.
   */
  CTypeParser.prototype.getEndian = function ()
  {
  	return (this.endian);
  };
  
  /*
   * 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.
   */
  CTypeParser.prototype.typedef = function (name, value)
  {
  	var type;
  
  	if (name === undefined)
  		throw (new (Error('missing required typedef argument: name')));
  
  	if (value === undefined)
  		throw (new (Error('missing required typedef argument: value')));
  
  	if (typeof (name) != 'string')
  		throw (new (Error('the name of a type must be a string')));
  
  	type = ctParseType(name);
  
  	if (type['len'] !== undefined)
  		throw (new Error('Cannot have an array in the typedef name'));
  
  	if (name in this.types)
  		throw (new Error('typedef name already present: ' + name));
  
  	if (typeof (value) != 'string' && !(value instanceof Array))
  		throw (new Error('typedef value must either be a string or ' +
  		    'struct'));
  
  	if (typeof (value) == 'string') {
  		type = ctParseType(value);
  		if (type['len'] !== undefined) {
  			if (isNaN(parseInt(type['len'], 10)))
  				throw (new (Error('typedef value must use ' +
  				    'fixed size array when outside of a ' +
  				    'struct')));
  		}
  
  		this.types[name] = value;
  	} else {
  		/* We have a struct, validate it */
  		ctCheckReq(value, this.types);
  		this.types[name] = value;
  	}
  };
  
  /*
   * Include all of the typedefs, but none of the built in types. This should be
   * treated as read-only.
   */
  CTypeParser.prototype.lstypes = function ()
  {
  	var key;
  	var ret = {};
  
  	for (key in this.types) {
  		if (key in deftypes)
  			continue;
  		ret[key] = this.types[key];
  	}
  
  	return (ret);
  };
  
  /*
   * Given a type string that may have array types that aren't numbers, try and
   * fill them in from the values object. The object should be of the format where
   * indexing into it should return a number for that type.
   *
   *	str		The type string
   *
   *	values		An object that can be used to fulfill type information
   */
  function ctResolveArray(str, values)
  {
  	var ret = '';
  	var type = ctParseType(str);
  
  	while (type['len'] !== undefined) {
  		if (isNaN(parseInt(type['len'], 10))) {
  			if (typeof (values[type['len']]) != 'number')
  				throw (new Error('cannot sawp in non-number ' +
  				    'for array value'));
  			ret = '[' + values[type['len']] + ']' + ret;
  		} else {
  			ret = '[' + type['len'] + ']' + ret;
  		}
  		type = ctParseType(type['type']);
  	}
  
  	ret = type['type'] + ret;
  
  	return (ret);
  }
  
  /*
   * [private] Either the typedef resolves to another type string or to a struct.
   * If it resolves to a struct, we just pass it off to read struct. If not, we
   * can just pass it off to read entry.
   */
  CTypeParser.prototype.resolveTypedef = function (type, dispatch, buffer,
      offset, value)
  {
  	var pt;
  
  	mod_assert.ok(type in this.types);
  	if (typeof (this.types[type]) == 'string') {
  		pt = ctParseType(this.types[type]);
  		if (dispatch == 'read')
  			return (this.readEntry(pt, buffer, offset));
  		else if (dispatch == 'write')
  			return (this.writeEntry(value, pt, buffer, offset));
  		else
  			throw (new Error('invalid dispatch type to ' +
  			    'resolveTypedef'));
  	} else {
  		if (dispatch == 'read')
  			return (this.readStruct(this.types[type], buffer,
  			    offset));
  		else if (dispatch == 'write')
  			return (this.writeStruct(value, this.types[type],
  			    buffer, offset));
  		else
  			throw (new Error('invalid dispatch type to ' +
  			    'resolveTypedef'));
  	}
  
  };
  
  /*
   * [private] Try and read in the specific entry.
   */
  CTypeParser.prototype.readEntry = function (type, buffer, offset)
  {
  	var parse, len;
  
  	/*
  	 * Because we want to special case char[]s this is unfortunately
  	 * a bit uglier than it really should be. We want to special
  	 * case char[]s so that we return a node buffer, thus they are a
  	 * first class type where as all other arrays just call into a
  	 * generic array routine which calls their data-specific routine
  	 * the specified number of times.
  	 *
  	 * The valid dispatch options we have are:
  	 *  - Array and char => char[] handler
  	 *  - Generic array handler
  	 *  - Generic typedef handler
  	 *  - Basic type handler
  	 */
  	if (type['len'] !== undefined) {
  		len = parseInt(type['len'], 10);
  		if (isNaN(len))
  			throw (new Error('somehow got a non-numeric length'));
  
  		if (type['type'] == 'char')
  			parse = this.types['char[]']['read'](len,
  			    this.endian, buffer, offset);
  		else
  			parse = this.readArray(type['type'],
  			    len, buffer, offset);
  	} else {
  		if (type['type'] in deftypes)
  			parse = this.types[type['type']]['read'](this.endian,
  			    buffer, offset);
  		else
  			parse = this.resolveTypedef(type['type'], 'read',
  			    buffer, offset);
  	}
  
  	return (parse);
  };
  
  /*
   * [private] Read an array of data
   */
  CTypeParser.prototype.readArray = function (type, length, buffer, offset)
  {
  	var ii, ent, pt;
  	var baseOffset = offset;
  	var ret = new Array(length);
  	pt = ctParseType(type);
  
  	for (ii = 0; ii < length; ii++) {
  		ent = this.readEntry(pt, buffer, offset);
  		offset += ent['size'];
  		ret[ii] = ent['value'];
  	}
  
  	return ({ value: ret, size: offset - baseOffset });
  };
  
  /*
   * [private] Read a single struct in.
   */
  CTypeParser.prototype.readStruct = function (def, buffer, offset)
  {
  	var parse, ii, type, entry, key;
  	var baseOffset = offset;
  	var ret = {};
  
  	/* Walk it and handle doing what's necessary */
  	for (ii = 0; ii < def.length; ii++) {
  		key = Object.keys(def[ii])[0];
  		entry = def[ii][key];
  
  		/* Resolve all array values */
  		type = ctParseType(ctResolveArray(entry['type'], ret));
  
  		if ('offset' in entry)
  			offset = baseOffset + entry['offset'];
  
  		parse = this.readEntry(type, buffer, offset);
  
  		offset += parse['size'];
  		ret[key] = parse['value'];
  	}
  
  	return ({ value: ret, size: (offset-baseOffset)});
  };
  
  /*
   * 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.
   */
  CTypeParser.prototype.readData = function (def, buffer, offset)
  {
  	/* Sanity check for arguments */
  	if (def === undefined)
  		throw (new Error('missing definition for what we should be' +
  		    'parsing'));
  
  	if (buffer === undefined)
  		throw (new Error('missing buffer for what we should be ' +
  		    'parsing'));
  
  	if (offset === undefined)
  		throw (new Error('missing offset for what we should be ' +
  		    'parsing'));
  
  	/* Sanity check the object definition */
  	ctCheckReq(def, this.types);
  
  	return (this.readStruct(def, buffer, offset)['value']);
  };
  
  /*
   * [private] Write out an array of data
   */
  CTypeParser.prototype.writeArray = function (value, type, length, buffer,
      offset)
  {
  	var ii, pt;
  	var baseOffset = offset;
  	if (!(value instanceof Array))
  		throw (new Error('asked to write an array, but value is not ' +
  		    'an array'));
  
  	if (value.length != length)
  		throw (new Error('asked to write array of length ' + length +
  		    ' but that does not match value length: ' + value.length));
  
  	pt = ctParseType(type);
  	for (ii = 0; ii < length; ii++)
  		offset += this.writeEntry(value[ii], pt, buffer, offset);
  
  	return (offset - baseOffset);
  };
  
  /*
   * [private] Write the specific entry
   */
  CTypeParser.prototype.writeEntry = function (value, type, buffer, offset)
  {
  	var len, ret;
  
  	if (type['len'] !== undefined) {
  		len = parseInt(type['len'], 10);
  		if (isNaN(len))
  			throw (new Error('somehow got a non-numeric length'));
  
  		if (type['type'] == 'char')
  			ret = this.types['char[]']['write'](value, len,
  			    this.endian, buffer, offset);
  		else
  			ret = this.writeArray(value, type['type'],
  			    len, buffer, offset);
  	} else {
  		if (type['type'] in deftypes)
  			ret = this.types[type['type']]['write'](value,
  			    this.endian, buffer, offset);
  		else
  			ret = this.resolveTypedef(type['type'], 'write',
  			    buffer, offset, value);
  	}
  
  	return (ret);
  };
  
  /*
   * [private] Write a single struct out.
   */
  CTypeParser.prototype.writeStruct = function (value, def, buffer, offset)
  {
  	var ii, entry, type, key;
  	var baseOffset = offset;
  	var vals = {};
  
  	for (ii = 0; ii < def.length; ii++) {
  		key = Object.keys(def[ii])[0];
  		entry = def[ii][key];
  
  		type = ctParseType(ctResolveArray(entry['type'], vals));
  
  		if ('offset' in entry)
  			offset = baseOffset + entry['offset'];
  
  		offset += this.writeEntry(value[ii], type, buffer, offset);
  		/* Now that we've written it out, we can use it for arrays */
  		vals[key] = value[ii];
  	}
  
  	return (offset);
  };
  
  /*
   * 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. 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
   *
   *	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, values)
  {
  	var hv;
  
  	if (def === undefined)
  		throw (new Error('missing definition for what we should be' +
  		    'parsing'));
  
  	if (buffer === undefined)
  		throw (new Error('missing buffer for what we should be ' +
  		    'parsing'));
  
  	if (offset === undefined)
  		throw (new Error('missing offset for what we should be ' +
  		    'parsing'));
  
  	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(hv ? values : getValues(def), def, buffer, offset);
  };
  
  /*
   * Functions to go to and from 64 bit numbers in a way that is compatible with
   * Javascript limitations. There are two sets. One where the user is okay with
   * an approximation and one where they are definitely not okay with an
   * approximation.
   */
  
  /*
   * 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
   */
  function toAbs64(val)
  {
  	if (val === undefined)
  		throw (new Error('missing required arg: value'));
  
  	if (!Array.isArray(val))
  		throw (new Error('value must be an array'));
  
  	if (val.length != 2)
  		throw (new Error('value must be an array of length 2'));
  
  	/* We have 20 bits worth of precision in this range */
  	if (val[0] >= 0x100000)
  		throw (new Error('value would become approximated'));
  
  	return (val[0] * Math.pow(2, 32) + val[1]);
  }
  
  /*
   * 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
   */
  function toApprox64(val)
  {
  	if (val === undefined)
  		throw (new Error('missing required arg: value'));
  
  	if (!Array.isArray(val))
  		throw (new Error('value must be an array'));
  
  	if (val.length != 2)
  		throw (new Error('value must be an array of length 2'));
  
  	return (Math.pow(2, 32) * val[0] + val[1]);
  }
  
  function parseCTF(json, conf)
  {
  	var ctype = new CTypeParser(conf);
  	mod_ctf.ctfParseJson(json, ctype);
  
  	return (ctype);
  }
  
  /*
   * Export the few things we actually want to. Currently this is just the CType
   * Parser and ctio.
   */
  exports.Parser = CTypeParser;
  exports.toAbs64 = toAbs64;
  exports.toApprox64 = toApprox64;
  
  exports.parseCTF = parseCTF;
  
  exports.ruint8 = mod_ctio.ruint8;
  exports.ruint16 = mod_ctio.ruint16;
  exports.ruint32 = mod_ctio.ruint32;
  exports.ruint64 = mod_ctio.ruint64;
  exports.wuint8 = mod_ctio.wuint8;
  exports.wuint16 = mod_ctio.wuint16;
  exports.wuint32 = mod_ctio.wuint32;
  exports.wuint64 = mod_ctio.wuint64;
  
  exports.rsint8 = mod_ctio.rsint8;
  exports.rsint16 = mod_ctio.rsint16;
  exports.rsint32 = mod_ctio.rsint32;
  exports.rsint64 = mod_ctio.rsint64;
  exports.wsint8 = mod_ctio.wsint8;
  exports.wsint16 = mod_ctio.wsint16;
  exports.wsint32 = mod_ctio.wsint32;
  exports.wsint64 = mod_ctio.wsint64;
  
  exports.rfloat = mod_ctio.rfloat;
  exports.rdouble = mod_ctio.rdouble;
  exports.wfloat = mod_ctio.wfloat;
  exports.wdouble = mod_ctio.wdouble;
  