/* 
 * Copyright 2008-2018 Aerospike, Inc.
 *
 * Portions may be licensed to Aerospike, Inc. under one or more contributor
 * license agreements.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
#include <aerospike/mod_lua_bytes.h>
#include <aerospike/as_val.h>
#include <aerospike/mod_lua_val.h>
#include <aerospike/mod_lua_iterator.h>
#include <aerospike/mod_lua_reg.h>
#include <citrusleaf/alloc.h>
#include <citrusleaf/cf_byte_order.h>
#include <stddef.h>
#include <string.h>

#include "internal.h"

/*******************************************************************************
 * MACROS
 ******************************************************************************/

#define OBJECT_NAME "bytes"
#define CLASS_NAME "Bytes"

/*******************************************************************************
 * BOX FUNCTIONS
 ******************************************************************************/

as_bytes *mod_lua_tobytes(lua_State *l, int index)
{
    mod_lua_box *box = mod_lua_tobox(l, index, CLASS_NAME);
    return (as_bytes *)mod_lua_box_value(box);
}

as_bytes *mod_lua_pushbytes(lua_State *l, as_bytes *b)
{
    mod_lua_box *box = mod_lua_pushbox(l, MOD_LUA_SCOPE_LUA, b, CLASS_NAME);
    return (as_bytes *)mod_lua_box_value(box);
}

static as_bytes *mod_lua_checkbytes(lua_State *l, int index)
{
    mod_lua_box *box = mod_lua_checkbox(l, index, CLASS_NAME);
    return (as_bytes *)mod_lua_box_value(box);
}

static int mod_lua_bytes_gc(lua_State *l)
{
    mod_lua_freebox(l, 1, CLASS_NAME);
    return 0;
}

/*******************************************************************************
 * FUNCTIONS
 ******************************************************************************/

static int mod_lua_bytes_size(lua_State *l)
{
    // we expect 1 arg
    if (lua_gettop(l) != 1) {
        lua_pushinteger(l, 0);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);

    // check preconditions:
    //	- b != NULL
    if (!b) {
        lua_pushinteger(l, 0);
        return 1;
    }

    lua_pushinteger(l, as_bytes_size(b));
    return 1;
}

/*
static int mod_lua_bytes_capacity(lua_State * l)
{
	// we expect 1 arg
	if ( lua_gettop(l) != 1 ) {
		lua_pushinteger(l, 0);
		return 1;
	}

	as_bytes * b = mod_lua_checkbytes(l, 1);
	
	// check preconditions:
	//	- b != NULL
	if ( !b ) {
		lua_pushinteger(l, 0);
		return 1;
	}

	lua_pushinteger(l, as_bytes_capacity(b));
	return 1;
}
*/

static int mod_lua_bytes_ensure(lua_State *l)
{
    // we expect 2 args
    if (lua_gettop(l) != 2) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer c = luaL_optinteger(l, 2, 0);
    int r = luaL_optint(l, 3, 0);

    // check preconditions:
    //	- b != NULL
    //	- 0 <= c <= INT32_MAX
    if (!b || c < 0 || c > UINT32_MAX || r < 0 || r > 1) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = as_bytes_ensure(b, (uint32_t)c, r == 1);
    lua_pushboolean(l, res);
    return 1;
}

/*
static int mod_lua_bytes_truncate(lua_State *l)
{
	// we expect 2 args
	if ( lua_gettop(l) != 2) {
		lua_pushboolean(l, false);
		return 1;
	}

	as_bytes * 	b = mod_lua_checkbytes(l, 1);
	lua_Integer n = luaL_optinteger(l, 2, 0);

	// check preconditions:
	//	- b != NULL
	//	- 0 <= v <= INT32_MAX
	if ( !b || 
		 n < 0 || n > UINT32_MAX ) {
		lua_pushboolean(l, false);
		return 1;
	}

	bool res = as_bytes_truncate(b, (uint32_t) n);
	lua_pushboolean(l, res);
	return 1;
}
*/

static int mod_lua_bytes_new(lua_State *l)
{
    int argc = lua_gettop(l);

    as_bytes *bytes = NULL;

    if (argc == 1) {
        // no arguments
        bytes = as_bytes_new(0);
    }
    else if (argc == 2) {
        // single integer argument for capacity
        lua_Integer n = luaL_optinteger(l, 2, 0);
        bytes = as_bytes_new((uint32_t)n);
    }

    if (!bytes) {
        return 0;
    }

    mod_lua_pushbytes(l, bytes);
    return 1;
}

static int mod_lua_bytes_tostring(lua_State *l)
{
    // we expect 1 arg
    if (lua_gettop(l) != 1) {
        lua_pushinteger(l, 0);
        return 1;
    }

    mod_lua_box *box = mod_lua_checkbox(l, 1, CLASS_NAME);
    as_val *val = mod_lua_box_value(box);
    char *str = NULL;

    if (val) {
        str = as_val_tostring(val);
    }

    if (str) {
        lua_pushstring(l, str);
        cf_free(str);
    }
    else {
        lua_pushstring(l, "Bytes()");
    }

    return 1;
}

/**
 *	Get the type of bytes:
 *
 *	----------{.c}
 *	uint bytes.type(bytes b)
 *	----------
 *
 */
static int mod_lua_bytes_get_type(lua_State *l)
{
    // we expect atleast 1 arg
    if (lua_gettop(l) < 1) {
        return 0;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);

    // check preconditions:
    //	- b != NULL
    if (!b) {
        return 0;
    }

    lua_pushinteger(l, as_bytes_get_type(b));
    return 1;
}

/**
 *	Set the type of bytes:
 *
 *	----------{.c}
 *	bool bytes.type(bytes b, uint 5)
 *	----------
 */
static int mod_lua_bytes_set_type(lua_State *l)
{
    // we expect 2 args
    if (lua_gettop(l) != 2) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer t = luaL_optinteger(l, 2, 0);

    // check preconditions:
    //	- b != NULL
    if (!b || !t) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes_set_type(b, (as_bytes_type)t);
    lua_pushboolean(l, true);
    return 1;
}

/******************************************************************************
 *	APPEND FUNCTIONS
 *****************************************************************************/

/**
 *	Append a byte value.
 *
 *	----------{.c}
 *	bool bytes.append_byte(bytes b, uint8 v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param v	The uint8_t value append to b.
 *	
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_append_byte(lua_State *l)
{
    // we expect 2 args
    if (lua_gettop(l) != 2) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer v = luaL_optinteger(l, 2, 0);

    // check preconditions:
    //	- b != NULL
    //	- UINT8_MIN <= v <= UINT8_MAX
    if (!b || v < 0 || v > UINT8_MAX) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = b->size;
    uint32_t size = 1;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        res = as_bytes_append_byte(b, (uint8_t)v);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Append a big endian int16 value.
 *
 *	----------{.c}
 *	bool bytes.append_int16_be(bytes b, int16 v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param v	The int16_t value to append to b.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_append_int16_be(lua_State *l)
{
    // we expect 2 args
    if (lua_gettop(l) != 2) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer v = luaL_optinteger(l, 2, 0);

    // check preconditions:
    //	- b != NULL
    //	- INT32_MIN <= v <= INT32_MAX
    if (!b || v < INT16_MIN || v > INT16_MAX) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = b->size;
    uint32_t size = 2;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        int16_t val = cf_swap_to_be16((int16_t)v);
        res = as_bytes_append_int16(b, val);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Append a little endian int16 value.
 *
 *	----------{.c}
 *	bool bytes.append_int16_le(bytes b, int16 v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param v	The int16_t value to append to b.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_append_int16_le(lua_State *l)
{
    // we expect 2 args
    if (lua_gettop(l) != 2) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer v = luaL_optinteger(l, 2, 0);

    // check preconditions:
    //	- b != NULL
    //	- INT32_MIN <= v <= INT32_MAX
    if (!b || v < INT16_MIN || v > INT16_MAX) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = b->size;
    uint32_t size = 2;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        int16_t val = cf_swap_to_le16((int16_t)v);
        res = as_bytes_append_int16(b, val);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Append a big endian int32 value.
 *
 *	----------{.c}
 *	bool bytes.append_int32_be(b, v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param v	The int32_t value to append to b.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_append_int32_be(lua_State *l)
{
    // we expect 2 args
    if (lua_gettop(l) != 2) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer v = luaL_optinteger(l, 2, 0);

    // check preconditions:
    //	- b != NULL
    //	- INT32_MIN <= v <= INT32_MAX
    if (!b || v < INT32_MIN || v > INT32_MAX) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = b->size;
    uint32_t size = 4;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        int32_t val = cf_swap_to_be32((int32_t)v);
        res = as_bytes_append_int32(b, val);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Append a little endian int32 value.
 *
 *	----------{.c}
 *	bool bytes.append_int32_le(b, v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param v	The int32_t value to append to b.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_append_int32_le(lua_State *l)
{
    // we expect 2 args
    if (lua_gettop(l) != 2) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer v = luaL_optinteger(l, 2, 0);

    // check preconditions:
    //	- b != NULL
    //	- INT32_MIN <= v <= INT32_MAX
    if (!b || v < INT32_MIN || v > INT32_MAX) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = b->size;
    uint32_t size = 4;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        int32_t val = cf_swap_to_le32((int32_t)v);
        res = as_bytes_append_int32(b, val);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Append a big endian int64 value.
 *
 *	----------{.c}
 *	bool bytes.append_int64_be(b, v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param v	The int64_t value to append to b.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_append_int64_be(lua_State *l)
{
    // we expect 2 args
    if (lua_gettop(l) != 2) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer v = luaL_optinteger(l, 2, 0);

    // check preconditions:
    // 	- b != NULL
    if (!b) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = b->size;
    uint32_t size = 8;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        int64_t val = cf_swap_to_be64((int64_t)v);
        res = as_bytes_append_int64(b, val);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Append a little endian int64 value.
 *
 *	----------{.c}
 *	bool bytes.append_int64_le(b, v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param v	The int64_t value to append to b.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_append_int64_le(lua_State *l)
{
    // we expect 2 args
    if (lua_gettop(l) != 2) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer v = luaL_optinteger(l, 2, 0);

    // check preconditions:
    // 	- b != NULL
    if (!b) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = b->size;
    uint32_t size = 8;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        int64_t val = cf_swap_to_le64((int64_t)v);
        res = as_bytes_append_int64(b, val);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Append an integer in variable 7-bit format at specified index.
 *	The high bit indicates if more bytes are used.
 *
 *	----------{.c}
 *	bool bytes.append_var_int(b, v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param v	The integer value to append to b.
 *
 *	@return number of bytes used to store integer
 */
static int mod_lua_bytes_append_var_int(lua_State *l)
{
    // we expect 2 args
    if (lua_gettop(l) != 2) {
        lua_pushinteger(l, 0);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer v = luaL_optinteger(l, 2, 0);

    // check preconditions:
    //	- b != NULL
    //	- INT32_MIN <= v <= INT32_MAX
    if (!b || v < INT32_MIN || v > INT32_MAX) {
        lua_pushinteger(l, 0);
        return 1;
    }

    uint32_t size = 0;
    uint32_t pos = b->size;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + 5, true) == true) {
        size = as_bytes_set_var_int(b, pos, (uint32_t)v);
    }

    lua_pushinteger(l, size);
    return 1;
}

/**
 *	Append a NULL-terminated string value.
 *
 *	----------{.c}
 *	bool bytes.append_string(bytes b, string v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param v	The NULL-terminated string value to append to b.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_append_string(lua_State *l)
{
    // we expect 2 args
    if (lua_gettop(l) != 2) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    size_t n = 0;
    const char *v = luaL_optlstring(l, 2, NULL, &n);

    // check preconditions:
    // 	- b != NULL
    //	- v != NULL
    //	- n != 0
    if (!b || !v || !n) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = b->size;
    uint32_t size = (uint32_t)n;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        res = as_bytes_append(b, (uint8_t *)v, size);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Append a NULL-terminated string value.
 *
 *	----------{.c}
 *	bool bytes.append_string(bytes b, bytes v, uint32 n)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param v	The bytes value to append to b.
 *	@param n	The number of bytes to append to b.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_append_bytes(lua_State *l)
{
    // we expect 3 args
    if (lua_gettop(l) != 3) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    as_bytes *v = mod_lua_checkbytes(l, 2);
    lua_Integer n = luaL_optinteger(l, 3, 0);

    // check preconditions:
    // 	- b != NULL
    //	- v != NULL
    //	- UINT32_MIN <= n <= UINT32_MAX
    if (!b || !v || n < 0 || n > UINT32_MAX) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = b->size;
    uint32_t size = n > v->size ? v->size : (uint32_t)n;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        res = as_bytes_append(b, (uint8_t *)v->value, size);
    }

    lua_pushboolean(l, res);
    return 1;
}

/******************************************************************************
 *	SET FUNCTIONS
 *****************************************************************************/

/**
 *	Set a byte value at specified index.
 *
 *	----------{.c}
 *	bool bytes.set_byte(bytes b, uint32 i, uint8 v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param i	The index in b to set the value of.
 *	@param v	The uint8_t value to set at i.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_set_byte(lua_State *l)
{
    // we expect 3 args
    if (lua_gettop(l) != 3) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);
    lua_Integer v = luaL_optinteger(l, 3, 0);

    // check preconditions:
    // 	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    //	- 0 <= v <= UINT8_MAX
    if (!b || i < 1 || i > UINT32_MAX || v < 0 || v > UINT8_MAX) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = (uint32_t)(i - 1);
    uint32_t size = 1;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        res = as_bytes_set_byte(b, pos, (uint8_t)v);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Set a big endian int16 value at specified index.
 *
 *	----------{.c}
 *	bool bytes.set_int16_be(bytes b, uint32 i, int16 v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param i	The index in b to set the value of.
 *	@param v	The int16_t value to set at i.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_set_int16_be(lua_State *l)
{
    // we expect 3 args
    if (lua_gettop(l) != 3) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);
    lua_Integer v = luaL_optinteger(l, 3, 0);

    // check preconditions:
    // 	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    //	- INT16_MIN <= v <= INT16_MAX
    if (!b || i < 1 || i > UINT32_MAX || v < INT16_MIN || v > INT16_MAX) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = (uint32_t)(i - 1);
    uint32_t size = 2;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        int16_t val = cf_swap_to_be16((int16_t)v);
        res = as_bytes_set_int16(b, pos, val);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Set a little endian int16 value at specified index.
 *
 *	----------{.c}
 *	bool bytes.set_int16_le(bytes b, uint32 i, int16 v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param i	The index in b to set the value of.
 *	@param v	The int16_t value to set at i.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_set_int16_le(lua_State *l)
{
    // we expect 3 args
    if (lua_gettop(l) != 3) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);
    lua_Integer v = luaL_optinteger(l, 3, 0);

    // check preconditions:
    // 	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    //	- INT16_MIN <= v <= INT16_MAX
    if (!b || i < 1 || i > UINT32_MAX || v < INT16_MIN || v > INT16_MAX) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = (uint32_t)(i - 1);
    uint32_t size = 2;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        int16_t val = cf_swap_to_le16((int16_t)v);
        res = as_bytes_set_int16(b, pos, val);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Set a big endian int32 value at specified index.
 *
 *	----------{.c}
 *	bool bytes.set_int32_be(bytes b, uint32 i, int32 v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param i	The index in b to set the value of.
 *	@param v	The int32_t value to set at i.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_set_int32_be(lua_State *l)
{
    // we expect 3 args
    if (lua_gettop(l) != 3) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);
    lua_Integer v = luaL_optinteger(l, 3, 0);

    // check preconditions:
    // 	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    //	- INT32_MIN <= v <= INT32_MAX
    if (!b || i < 1 || i > UINT32_MAX || v < INT32_MIN || v > INT32_MAX) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = (uint32_t)(i - 1);
    uint32_t size = 4;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        int32_t val = cf_swap_to_be32((int32_t)v);
        res = as_bytes_set_int32(b, pos, val);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Set a little endian int32 value at specified index.
 *
 *	----------{.c}
 *	bool bytes.set_int32_le(bytes b, uint32 i, int32 v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param i	The index in b to set the value of.
 *	@param v	The int32_t value to set at i.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_set_int32_le(lua_State *l)
{
    // we expect 3 args
    if (lua_gettop(l) != 3) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);
    lua_Integer v = luaL_optinteger(l, 3, 0);

    // check preconditions:
    // 	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    //	- INT32_MIN <= v <= INT32_MAX
    if (!b || i < 1 || i > UINT32_MAX || v < INT32_MIN || v > INT32_MAX) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = (uint32_t)(i - 1);
    uint32_t size = 4;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        int32_t val = cf_swap_to_le32((int32_t)v);
        res = as_bytes_set_int32(b, pos, val);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Set a big endian int64_t value at specified index.
 *
 *	----------{.c}
 *	bool bytes.set_int64_be(bytes b, uint32 i, int64 v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param i	The index in b to set the value of.
 *	@param v	The int64_t value to set at i.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_set_int64_be(lua_State *l)
{
    // we expect 3 args
    if (lua_gettop(l) != 3) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);
    lua_Integer v = luaL_optinteger(l, 3, 0);

    // check preconditions:
    // 	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    if (!b || i < 1 || i > UINT32_MAX) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = (uint32_t)(i - 1);
    uint32_t size = 8;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        int64_t val = cf_swap_to_be64((int64_t)v);
        res = as_bytes_set_int64(b, pos, val);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Set a little endian int64_t value at specified index.
 *
 *	----------{.c}
 *	bool bytes.set_int64_le(bytes b, uint32 i, int64 v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param i	The index in b to set the value of.
 *	@param v	The int64_t value to set at i.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_set_int64_le(lua_State *l)
{
    // we expect 3 args
    if (lua_gettop(l) != 3) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);
    lua_Integer v = luaL_optinteger(l, 3, 0);

    // check preconditions:
    // 	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    if (!b || i < 1 || i > UINT32_MAX) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = (uint32_t)(i - 1);
    uint32_t size = 8;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        int64_t val = cf_swap_to_le64((int64_t)v);
        res = as_bytes_set_int64(b, pos, val);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Encode an integer in variable 7-bit format at specified index.
 *	The high bit indicates if more bytes are used.
 *
 *	----------{.c}
 *	bool bytes.set_var_int(bytes b, uint32 i, int32 v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param i	The index in b to set the value of.
 *	@param v	The integer value to set at i.
 *
 *	@return number of bytes used to store integer
 */
static int mod_lua_bytes_set_var_int(lua_State *l)
{
    // we expect 3 args
    if (lua_gettop(l) != 3) {
        lua_pushinteger(l, 0);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);
    lua_Integer v = luaL_optinteger(l, 3, 0);

    // check preconditions:
    // 	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    //	- INT32_MIN <= v <= INT32_MAX
    if (!b || i < 1 || i > UINT32_MAX || v < INT32_MIN || v > INT32_MAX) {
        lua_pushinteger(l, 0);
        return 1;
    }

    uint32_t size = 0;
    uint32_t pos = (uint32_t)(i - 1);

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + 5, true) == true) {
        size = as_bytes_set_var_int(b, pos, (uint32_t)v);
    }

    lua_pushinteger(l, size);
    return 1;
}

/**
 *	Set an NULL-terminated string value at specified index.
 *
 *	----------{.c}
 *	bool bytes.set_string(bytes b, uint32 i, string v)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param i	The index in b to set the value of.
 *	@param v	The NULL-terminated string value to set at i.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_set_string(lua_State *l)
{
    // we expect 3 args
    if (lua_gettop(l) != 3) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);
    size_t n = 0;
    const char *v = luaL_optlstring(l, 3, NULL, &n);

    // check preconditions:
    //	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    //	- v != NULL
    if (!b || i < 1 || i > UINT32_MAX || !v) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = (uint32_t)(i - 1);
    uint32_t size = (uint32_t)n;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        res = as_bytes_set(b, pos, (uint8_t *)v, size);
    }

    lua_pushboolean(l, res);
    return 1;
}

/**
 *	Copy `n` data from bytes `v` to bytes `b` value at specified index.
 *	
 *	----------{.c}
 *	bool bytes.set_bytes(bytes b, uint32 i, bytes v, uint32 n)
 *	----------
 *
 *	@param b 	The bytes to set a value in.
 *	@param i	The index in b to set the value of.
 *	@param v	The NULL-terminated string value to set at i.
 *	@param n	The number of bytes to copy from v in to b.
 *
 *	@return On success, true. Otherwise, false on error.
 */
static int mod_lua_bytes_set_bytes(lua_State *l)
{
    // we expect 3 args
    if (lua_gettop(l) != 4) {
        lua_pushboolean(l, false);
        return 1;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);
    as_bytes *v = mod_lua_checkbytes(l, 3);
    lua_Integer n = luaL_optinteger(l, 4, 0);

    // check preconditions:
    //	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    //	- v != NULL
    //	- 0 <= n <= UINT32_MAX
    if (!b || i < 1 || i > UINT32_MAX || !v || n < 0 || n > UINT32_MAX) {
        lua_pushboolean(l, false);
        return 1;
    }

    bool res = false;
    uint32_t pos = (uint32_t)(i - 1);
    uint32_t size = n > v->size ? v->size : (uint32_t)n;

    // ensure we have capacity, if not, then resize
    if (as_bytes_ensure(b, pos + size, true) == true) {
        // write the bytes
        res = as_bytes_set(b, pos, (uint8_t *)v->value, (uint32_t)n);
    }

    lua_pushboolean(l, res);
    return 1;
}

/******************************************************************************
 *	GET FUNCTIONS
 *****************************************************************************/

/**
 *	Get an uint8_t value from the specified index.
 *	
 *	----------{.c}
 *	uint8 bytes.get_byte(bytes b, uint32 i)
 *	----------
 *	
 *	@param b 	The bytes to get a value from.
 *	@param i	The index in b to get the value of.
 *	
 *	@return On success, the value. Otherwise nil on failure.
 */
static int mod_lua_bytes_get_byte(lua_State *l)
{
    // we expect exactly 2 args
    if (lua_gettop(l) != 2) {
        return 0;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);

    // check preconditions:
    //	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    if (!b || i < 1 || i > UINT32_MAX) {
        return 0;
    }

    uint32_t pos = (uint32_t)(i - 1);
    uint8_t val = 0;

    // get returns 0 on failure
    if (as_bytes_get_byte(b, pos, &val) == 0) {
        return 0;
    }

    lua_pushinteger(l, val);
    return 1;
}

/**
 *	Get a big endian int16 value from the specified index.
 *	
 *	----------{.c}
 *	int16 bytes.get_int16_be(bytes b, uint32 i)
 *	----------
 *	
 *	@param b 	The bytes to get a value from.
 *	@param i	The index in b to get the value of.
 *	
 *	@return On success, the value. Otherwise nil on failure.
 */
static int mod_lua_bytes_get_int16_be(lua_State *l)
{
    // we expect exactly 2 args
    if (lua_gettop(l) != 2) {
        return 0;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);

    // check preconditions:
    //	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    if (!b || i < 1 || i > UINT32_MAX) {
        return 0;
    }

    uint32_t pos = (uint32_t)(i - 1);
    int16_t val = 0;

    // get returns 0 on failure
    if (as_bytes_get_int16(b, pos, &val) == 0) {
        return 0;
    }

    int16_t res = cf_swap_from_be16(val);
    lua_pushinteger(l, res);
    return 1;
}

/**
 *	Get a little endian int16 value from the specified index.
 *
 *	----------{.c}
 *	int16 bytes.get_int16_le(bytes b, uint32 i)
 *	----------
 *
 *	@param b 	The bytes to get a value from.
 *	@param i	The index in b to get the value of.
 *
 *	@return On success, the value. Otherwise nil on failure.
 */
static int mod_lua_bytes_get_int16_le(lua_State *l)
{
    // we expect exactly 2 args
    if (lua_gettop(l) != 2) {
        return 0;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);

    // check preconditions:
    //	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    if (!b || i < 1 || i > UINT32_MAX) {
        return 0;
    }

    uint32_t pos = (uint32_t)(i - 1);
    int16_t val = 0;

    // get returns 0 on failure
    if (as_bytes_get_int16(b, pos, &val) == 0) {
        return 0;
    }

    int16_t res = cf_swap_from_le16(val);
    lua_pushinteger(l, res);
    return 1;
}

/**
 *	Get a big endian int32 value from the specified index.
 *	
 *	----------{.c}
 *	int32 bytes.get_int32_be(bytes b, uint32 i)
 *	----------
 *	
 *	@param b 	The bytes to get a value from.
 *	@param i	The index in b to get the value of.
 *	
 *	@return On success, the value. Otherwise nil on failure.
 */
static int mod_lua_bytes_get_int32_be(lua_State *l)
{
    // we expect exactly 2 args
    if (lua_gettop(l) != 2) {
        return 0;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);

    // check preconditions:
    //	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    if (!b || i < 1 || i > UINT32_MAX) {
        return 0;
    }

    uint32_t pos = (uint32_t)(i - 1);
    int32_t val = 0;

    // get returns 0 on failure
    if (as_bytes_get_int32(b, pos, &val) == 0) {
        return 0;
    }

    int32_t res = cf_swap_from_be32(val);
    lua_pushinteger(l, res);
    return 1;
}

/**
 *	Get a little endian int32 value from the specified index.
 *
 *	----------{.c}
 *	int32 bytes.get_int32_le(bytes b, uint32 i)
 *	----------
 *
 *	@param b 	The bytes to get a value from.
 *	@param i	The index in b to get the value of.
 *
 *	@return On success, the value. Otherwise nil on failure.
 */
static int mod_lua_bytes_get_int32_le(lua_State *l)
{
    // we expect exactly 2 args
    if (lua_gettop(l) != 2) {
        return 0;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);

    // check preconditions:
    //	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    if (!b || i < 1 || i > UINT32_MAX) {
        return 0;
    }

    uint32_t pos = (uint32_t)(i - 1);
    int32_t val = 0;

    // get returns 0 on failure
    if (as_bytes_get_int32(b, pos, &val) == 0) {
        return 0;
    }

    int32_t res = cf_swap_from_le32(val);
    lua_pushinteger(l, res);
    return 1;
}

/**
 *	Get a big endian int64 value from the specified index.
 *	
 *	----------{.c}
 *	int64 bytes.get_int64_be(bytes b, uint32 i)
 *	----------
 *	
 *	@param b 	The bytes to get a value from.
 *	@param i	The index in b to get the value of.
 *	
 *	@return On success, the value. Otherwise nil on failure.
 */
static int mod_lua_bytes_get_int64_be(lua_State *l)
{
    // we expect exactly 2 args
    if (lua_gettop(l) != 2) {
        return 0;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);

    // check preconditions:
    //	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    if (!b || i < 1 || i > UINT32_MAX) {
        return 0;
    }

    uint32_t pos = (uint32_t)(i - 1);
    int64_t val = 0;

    // get returns 0 on failure
    if (as_bytes_get_int64(b, pos, &val) == 0) {
        return 0;
    }

    int64_t res = cf_swap_from_be64(val);
    lua_pushinteger(l, res);
    return 1;
}

/**
 *	Get a little endian int64 value from the specified index.
 *
 *	----------{.c}
 *	int64 bytes.get_int64_le(bytes b, uint32 i)
 *	----------
 *
 *	@param b 	The bytes to get a value from.
 *	@param i	The index in b to get the value of.
 *
 *	@return On success, the value. Otherwise nil on failure.
 */
static int mod_lua_bytes_get_int64_le(lua_State *l)
{
    // we expect exactly 2 args
    if (lua_gettop(l) != 2) {
        return 0;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);

    // check preconditions:
    //	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    if (!b || i < 1 || i > UINT32_MAX) {
        return 0;
    }

    uint32_t pos = (uint32_t)(i - 1);
    int64_t val = 0;

    // get returns 0 on failure
    if (as_bytes_get_int64(b, pos, &val) == 0) {
        return 0;
    }

    int64_t res = cf_swap_from_le64(val);
    lua_pushinteger(l, res);
    return 1;
}

/**
 *	Decode an integer in variable 7-bit format.
 *	The high bit indicates if more bytes are used.
 *
 *	----------{.c}
 *	int bytes.get_var_int(bytes b, uint32 i)
 *	----------
 *
 *	@param b 	The bytes to get a value from.
 *	@param i	The index in b to get the value of.
 *
 *	@return On success, the value. Otherwise nil on failure.
 */
static int mod_lua_bytes_get_var_int(lua_State *l)
{
    // we expect exactly 2 args
    if (lua_gettop(l) != 2) {
        return 0;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);

    // check preconditions:
    //	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    if (!b || i < 1 || i > UINT32_MAX) {
        return 0;
    }

    uint32_t pos = (uint32_t)(i - 1);
    uint32_t val = 0;
    uint32_t size = as_bytes_get_var_int(b, pos, &val);

    lua_pushinteger(l, val);
    lua_pushinteger(l, size);
    return 2;
}

/**
 *	Get an bytes value from the specified index.
 *	
 *	----------{.c}
 *	bytes bytes.get_bytes(bytes b, uint32 i, uint32 n)
 *	----------
 *	
 *	@param b 	The bytes to get a value from.
 *	@param i	The index in b to get the value from.
 *	@param n	The the length of the bytes to copy.
 *	
 *	@return On success, the value. Otherwise nil on failure.
 */

static int mod_lua_bytes_get_string(lua_State *l)
{
    // we expect exactly 3 args
    if (lua_gettop(l) != 3) {
        return 0;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);
    lua_Integer n = luaL_optinteger(l, 3, 0);

    // check preconditions:
    //	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    //	- 0 <= n <= UINT32_MAX
    if (!b || i < 1 || i > UINT32_MAX || n < 0 || n > UINT32_MAX) {
        return 0;
    }

    uint32_t pos = (uint32_t)(i - 1);
    uint32_t len = (uint32_t)n;
    char *val = (char *)cf_calloc(len + 1, sizeof(char));

    if (!val) {
        return 0;
    }

    // copy into the the string
    memcpy(val, b->value + pos, len);
    val[len] = '\0';

    lua_pushlstring(l, val, len);
    cf_free(val);
    return 1;
}

/**
 *	Get an bytes value from the specified index.
 *	
 *	----------{.c}
 *	bytes bytes.get_bytes(bytes b, uint32 i, uint32 n)
 *	----------
 *	
 *	@param b 	The bytes to get a value from.
 *	@param i	The index in b to get the value from.
 *	@param n	The the length of the bytes to copy.
 *	
 *	@return On success, the value. Otherwise nil on failure.
 */
static int mod_lua_bytes_get_bytes(lua_State *l)
{
    // we expect exactly 3 args
    if (lua_gettop(l) != 3) {
        return 0;
    }

    as_bytes *b = mod_lua_checkbytes(l, 1);
    lua_Integer i = luaL_optinteger(l, 2, 0);
    lua_Integer n = luaL_optinteger(l, 3, 0);

    // check preconditions:
    //	- b != NULL
    //	- 1 <= i <= UINT32_MAX
    //	- 0 <= n <= UINT32_MAX
    if (!b || i < 1 || i > UINT32_MAX || n < 0 || n > UINT32_MAX) {
        return 0;
    }

    uint32_t pos = (uint32_t)(i - 1);
    uint32_t len = (uint32_t)n;
    uint8_t *raw = (uint8_t *)cf_calloc(len, sizeof(uint8_t));

    if (!raw) {
        return 0;
    }

    // copy into the the buffer
    memcpy(raw, b->value + pos, len);

    // create a new bytes
    as_bytes *val = as_bytes_new_wrap(raw, len, true);

    if (!val) {
        return 0;
    }

    mod_lua_pushbytes(l, val);
    return 1;
}

/******************************************************************************
 * OBJECT TABLE
 *****************************************************************************/

static const luaL_reg bytes_object_table[] = {

    {"size", mod_lua_bytes_size},
    {"set_size", mod_lua_bytes_ensure},

    {"get_type", mod_lua_bytes_get_type},
    {"set_type", mod_lua_bytes_set_type},

    {"get_string", mod_lua_bytes_get_string},
    {"get_bytes", mod_lua_bytes_get_bytes},
    {"get_byte", mod_lua_bytes_get_byte},
    {"get_int16", mod_lua_bytes_get_int16_be},
    {"get_int16_be", mod_lua_bytes_get_int16_be},
    {"get_int16_le", mod_lua_bytes_get_int16_le},
    {"get_int32", mod_lua_bytes_get_int32_be},
    {"get_int32_be", mod_lua_bytes_get_int32_be},
    {"get_int32_le", mod_lua_bytes_get_int32_le},
    {"get_int64", mod_lua_bytes_get_int64_be},
    {"get_int64_be", mod_lua_bytes_get_int64_be},
    {"get_int64_le", mod_lua_bytes_get_int64_le},
    {"get_var_int", mod_lua_bytes_get_var_int},

    {"set_string", mod_lua_bytes_set_string},
    {"set_bytes", mod_lua_bytes_set_bytes},
    {"set_byte", mod_lua_bytes_set_byte},
    {"set_int16", mod_lua_bytes_set_int16_be},
    {"set_int16_be", mod_lua_bytes_set_int16_be},
    {"set_int16_le", mod_lua_bytes_set_int16_le},
    {"set_int32", mod_lua_bytes_set_int32_be},
    {"set_int32_be", mod_lua_bytes_set_int32_be},
    {"set_int32_le", mod_lua_bytes_set_int32_le},
    {"set_int64", mod_lua_bytes_set_int64_be},
    {"set_int64_be", mod_lua_bytes_set_int64_be},
    {"set_int64_le", mod_lua_bytes_set_int64_le},
    {"set_var_int", mod_lua_bytes_set_var_int},

    {"append_string", mod_lua_bytes_append_string},
    {"append_bytes", mod_lua_bytes_append_bytes},
    {"append_byte", mod_lua_bytes_append_byte},
    {"append_int16", mod_lua_bytes_append_int16_be},
    {"append_int16_be", mod_lua_bytes_append_int16_be},
    {"append_int16_le", mod_lua_bytes_append_int16_le},
    {"append_int32", mod_lua_bytes_append_int32_be},
    {"append_int32_be", mod_lua_bytes_append_int32_be},
    {"append_int32_le", mod_lua_bytes_append_int32_le},
    {"append_int64", mod_lua_bytes_append_int64_be},
    {"append_int64_be", mod_lua_bytes_append_int64_be},
    {"append_int64_le", mod_lua_bytes_append_int64_le},
    {"append_var_int", mod_lua_bytes_append_var_int},

    {0, 0}};

static const luaL_reg bytes_object_metatable[] = {{"__call", mod_lua_bytes_new},
                                                  {0, 0}};

/******************************************************************************
 * CLASS TABLE
 *****************************************************************************/

/*
static const luaL_reg bytes_class_table[] = {
	{"putX",            mod_lua_bytes_tostring},
	{0, 0}
};
*/

static const luaL_reg bytes_class_metatable[] = {
    {"__index", mod_lua_bytes_get_byte}, {"__newindex", mod_lua_bytes_set_byte},
    {"__len", mod_lua_bytes_size},       {"__tostring", mod_lua_bytes_tostring},
    {"__gc", mod_lua_bytes_gc},          {0, 0}};

/******************************************************************************
 * REGISTER
 *****************************************************************************/

int mod_lua_bytes_register(lua_State *l)
{
    mod_lua_reg_object(l, OBJECT_NAME, bytes_object_table,
                       bytes_object_metatable);
    mod_lua_reg_class(l, CLASS_NAME, NULL, bytes_class_metatable);
    return 1;
}
