/*
 * 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;
