1094 lines
28 KiB
C
1094 lines
28 KiB
C
#include "stdio.h"// DEBUG
|
|
|
|
#include "stdint.h"
|
|
#include "stddef.h"
|
|
#include "stdlib.h"
|
|
#include "string.h"
|
|
#include "lauxlib.h"
|
|
#include "setjmp.h"
|
|
#include "math.h"
|
|
|
|
#ifndef BYTEARRAY_RESERVE_SIZE
|
|
#define BYTEARRAY_RESERVE_SIZE 128
|
|
#endif
|
|
|
|
#define BYTEARRAY_USE_CSTRING
|
|
|
|
#define ENDIAN_LITTLE 0
|
|
#define ENDIAN_BIG 1
|
|
#define READ_WRITE 0
|
|
#define READ_ONLY 1
|
|
|
|
typedef struct {
|
|
uint8_t endian: 1;
|
|
uint8_t readonly: 1;
|
|
} BufFlag;
|
|
|
|
enum {
|
|
ERR_OK,
|
|
ERR_NOMEM,
|
|
ERR_OVERFLOW,
|
|
ERR_READONLY,
|
|
ERR_OUTOFRANGE
|
|
};
|
|
|
|
typedef uint32_t buflen_t;
|
|
|
|
typedef struct {
|
|
uint8_t *buffer;
|
|
BufFlag flag;
|
|
buflen_t position;
|
|
buflen_t length;
|
|
buflen_t szbuffer;
|
|
} Buf;
|
|
|
|
static jmp_buf except;
|
|
|
|
static int8_t nativeEndian = -1;
|
|
static int getNativeEndian()
|
|
{
|
|
if( nativeEndian < 0 )
|
|
{
|
|
int x = 1;
|
|
if( *(char*)&x == 1 )
|
|
nativeEndian = ENDIAN_LITTLE;
|
|
else nativeEndian = ENDIAN_BIG;
|
|
}
|
|
return nativeEndian;
|
|
}
|
|
|
|
static inline BufFlag flag( int endian, int ro )
|
|
{
|
|
BufFlag r = {endian, ro};
|
|
return r;
|
|
}
|
|
|
|
static inline buflen_t grow( buflen_t len, buflen_t need )
|
|
{
|
|
int times = (len + need) / BYTEARRAY_RESERVE_SIZE;
|
|
return (times+1) * BYTEARRAY_RESERVE_SIZE;
|
|
}
|
|
|
|
#define SWAP(a, b, t) { \
|
|
t tmp = (a); \
|
|
(a) = (b); \
|
|
(b) = tmp; \
|
|
}
|
|
|
|
static inline void adjustEndian( uint8_t *first, buflen_t sz, int e )
|
|
{
|
|
if( sz > 1 && e != nativeEndian ){
|
|
for(uint8_t *last = &first[sz-1]; first < last; ++first, --last ){
|
|
SWAP( *first, *last, uint8_t );
|
|
}
|
|
}
|
|
}
|
|
|
|
// constructor
|
|
static Buf* createBuf( buflen_t sz, int endian )
|
|
{
|
|
Buf* retval = realloc( NULL, sizeof(Buf) );
|
|
if( retval == NULL ) return retval;
|
|
|
|
retval->flag = flag( endian, READ_WRITE );
|
|
retval->position = 0;
|
|
retval->buffer = NULL;
|
|
retval->length = 0;
|
|
retval->szbuffer = 0;
|
|
if( sz > 0 ){
|
|
retval->buffer = realloc( NULL, sz );
|
|
if( retval->buffer == NULL ){
|
|
free( retval );
|
|
return NULL;
|
|
}
|
|
else {
|
|
memset( retval->buffer, 0, sz );
|
|
retval->szbuffer = sz;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
static Buf* fromArray( void *arr, buflen_t len, int endian )
|
|
{
|
|
Buf* retval = realloc( NULL, sizeof(Buf) );
|
|
if( retval == NULL ) return retval;
|
|
|
|
retval->flag = flag( endian, READ_ONLY );
|
|
retval->position = 0;
|
|
retval->buffer = arr;
|
|
retval->length = len;
|
|
retval->szbuffer = len;
|
|
|
|
return retval;
|
|
}
|
|
|
|
static void release( Buf *p )
|
|
{
|
|
if( !p->flag.readonly )
|
|
free( p->buffer );
|
|
free( p );
|
|
}
|
|
|
|
static inline uint8_t* getBuffer( Buf *p )
|
|
{
|
|
return p->buffer;
|
|
}
|
|
|
|
static inline buflen_t getLength( Buf *p )
|
|
{
|
|
return p->length;
|
|
}
|
|
|
|
static inline buflen_t getCapacity( Buf *p )
|
|
{
|
|
return p->szbuffer;
|
|
}
|
|
|
|
static void resizeBuffer( Buf *p, buflen_t size )
|
|
{
|
|
buflen_t l = getCapacity(p);
|
|
if( l == size ) return;
|
|
|
|
if( p->flag.readonly ) longjmp( except, ERR_READONLY );
|
|
|
|
uint8_t *new_buffer = realloc( p->buffer, size );
|
|
if( new_buffer == NULL ) longjmp( except, ERR_NOMEM );
|
|
|
|
if( l < size ) memset( new_buffer+l, 0, size-l );
|
|
p->buffer = new_buffer;
|
|
p->szbuffer = size;
|
|
}
|
|
|
|
static inline buflen_t getPosition( Buf *p )
|
|
{
|
|
return p->position;
|
|
}
|
|
|
|
static inline void setPosition( Buf *p, buflen_t pos )
|
|
{
|
|
if( pos < getLength(p) )
|
|
p->position = pos;
|
|
else p->position = getLength(p);
|
|
}
|
|
|
|
static void setLength( Buf *p, buflen_t len )
|
|
{
|
|
buflen_t l = getLength(p);
|
|
if( len == l ) return;
|
|
|
|
if( p->flag.readonly ) longjmp( except, ERR_READONLY );
|
|
|
|
buflen_t size = getCapacity(p);
|
|
if( size < len )
|
|
resizeBuffer(p, len);
|
|
|
|
if( l < len )
|
|
memset( getBuffer(p)+l, 0, len-l );
|
|
|
|
p->length = len;
|
|
|
|
if( len < getPosition(p) )
|
|
setPosition(p, len);
|
|
}
|
|
|
|
static inline int getEndian( Buf *p )
|
|
{
|
|
return p->flag.endian;
|
|
}
|
|
|
|
static inline void setEndian( Buf *p, int e )
|
|
{
|
|
p->flag.endian = e;
|
|
}
|
|
|
|
static inline buflen_t getBytesAvailable( Buf *p )
|
|
{
|
|
return getLength(p) - getPosition(p);
|
|
}
|
|
|
|
static inline int at( Buf *p, buflen_t pos )
|
|
{
|
|
if( pos < getLength(p) )
|
|
return p->buffer[pos];
|
|
else return EOF;
|
|
}
|
|
|
|
static inline void assign( Buf *p, buflen_t pos, uint8_t val )
|
|
{
|
|
if( p->flag.readonly ) longjmp( except, ERR_READONLY );
|
|
|
|
if( getCapacity(p) < pos )
|
|
resizeBuffer(p, pos+1);
|
|
|
|
getBuffer(p)[ pos++ ] = val;
|
|
p->length = pos > p->length ? pos : p->length;
|
|
}
|
|
|
|
static inline void clear( Buf *p )
|
|
{
|
|
if( !p->flag.readonly ) {
|
|
memset( p->buffer, 0, p->szbuffer );
|
|
p->position = 0;
|
|
p->length = 0;
|
|
}
|
|
else longjmp( except, ERR_READONLY );
|
|
}
|
|
|
|
static Buf* cut( Buf *p, size_t pos, size_t len )
|
|
{
|
|
size_t size = getLength(p);
|
|
if( pos + len > size ) len = size - pos;
|
|
|
|
Buf *retval = createBuf( len, getEndian(p) );
|
|
if( retval == NULL ) longjmp( except, ERR_NOMEM );
|
|
|
|
if( pos < size && len > 0 ){
|
|
memcpy( retval->buffer, p->buffer+pos, len );
|
|
}
|
|
|
|
retval->length = len;
|
|
return retval;
|
|
}
|
|
|
|
// ------------ read data ---------------
|
|
#define UPDATE_LENGTH(p) p->length = p->length < p->position ? p->position : p->length;
|
|
|
|
#define RANGE_CHECK( p, sz ) { \
|
|
if(getBytesAvailable(p) < sz) longjmp( except, ERR_OUTOFRANGE ); \
|
|
}
|
|
|
|
static int readBoolean( Buf *p )
|
|
{
|
|
RANGE_CHECK( p, sizeof(uint8_t) );
|
|
|
|
return at(p, p->position++) != 0;
|
|
}
|
|
|
|
static void readBytes( Buf *p, void *bytes, uint32_t offset, size_t length )
|
|
{
|
|
RANGE_CHECK( p, length );
|
|
|
|
uint8_t *p_data_src = bytes;
|
|
p_data_src += offset;
|
|
|
|
memcpy( p_data_src, p->buffer + p->position, length );
|
|
p->position += length;
|
|
}
|
|
|
|
#ifdef _MEMORY_ALIGN_SAFE
|
|
#define READ_BUILDIN_TEMPLATE( type, name ) \
|
|
static type name( Buf *p ) \
|
|
{ \
|
|
size_t sz = sizeof(type); \
|
|
RANGE_CHECK(p, sz); \
|
|
\
|
|
type retval; \
|
|
memcpy( &retval, p->buffer + p->position, sz ); \
|
|
adjustEndian( (uint8_t*)&retval, sz, getEndian(p) ); \
|
|
\
|
|
p->position += sz; \
|
|
return retval; \
|
|
}
|
|
#else
|
|
#define READ_BUILDIN_TEMPLATE( type, name ) \
|
|
static type name( Buf *p ) \
|
|
{ \
|
|
size_t sz = sizeof(type); \
|
|
RANGE_CHECK(p, sz); \
|
|
\
|
|
type retval = *(type*)(p->buffer + p->position); \
|
|
adjustEndian( (uint8_t*)&retval, sz, getEndian(p) ); \
|
|
\
|
|
p->position += sz; \
|
|
return retval; \
|
|
}
|
|
#endif//_MEMORY_ALIGN_SAFE
|
|
|
|
READ_BUILDIN_TEMPLATE( uint8_t, readUnsignedByte )
|
|
READ_BUILDIN_TEMPLATE( int8_t, readByte )
|
|
READ_BUILDIN_TEMPLATE( uint16_t, readUnsignedShort )
|
|
READ_BUILDIN_TEMPLATE( int16_t, readShort )
|
|
READ_BUILDIN_TEMPLATE( uint32_t, readUnsignedInt )
|
|
READ_BUILDIN_TEMPLATE( int32_t, readInt )
|
|
READ_BUILDIN_TEMPLATE( double, readDouble )
|
|
READ_BUILDIN_TEMPLATE( float, readFloat )
|
|
|
|
// ------------ write data ---------------
|
|
|
|
#define RANGE_RESERVE( p, sz ) { \
|
|
if( p->flag.readonly ) longjmp( except, ERR_READONLY ); \
|
|
\
|
|
if( getCapacity(p) - getPosition(p) < sz ){ \
|
|
buflen_t nsz = grow(getCapacity(p), sz); \
|
|
if( nsz < getCapacity(p) ) longjmp(except, ERR_OVERFLOW); \
|
|
\
|
|
resizeBuffer(p, nsz); \
|
|
} \
|
|
}
|
|
|
|
static void writeBoolean( Buf *p, int value )
|
|
{
|
|
RANGE_RESERVE(p, 1);
|
|
|
|
uint8_t boolean = value != 0;
|
|
getBuffer(p)[p->position++] = boolean;
|
|
UPDATE_LENGTH(p);
|
|
}
|
|
|
|
static void writeBytes( Buf *p, const void *bytes, uint32_t offset, size_t length )
|
|
{
|
|
RANGE_RESERVE(p, length);
|
|
|
|
const uint8_t *src = (uint8_t*)bytes + offset;
|
|
memcpy( p->buffer + p->position, src, length );
|
|
p->position += length;
|
|
UPDATE_LENGTH(p);
|
|
}
|
|
|
|
#ifdef _MEMORY_ALIGN_SAFE
|
|
#define WRITE_BUILDIN_TEMPLATE( type, name ) \
|
|
static void name( Buf *p, type value ) \
|
|
{ \
|
|
size_t sz = sizeof(type); \
|
|
RANGE_RESERVE(p, sz); \
|
|
\
|
|
uint8_t *pvalue = p->buffer + p->position; \
|
|
memcpy( pvalue, &value, sz ); \
|
|
adjustEndian(pvalue, sz, getEndian(p)); \
|
|
\
|
|
p->position += sz; \
|
|
UPDATE_LENGTH(p); \
|
|
}
|
|
#else
|
|
#define WRITE_BUILDIN_TEMPLATE( type, name ) \
|
|
static void name( Buf *p, type value ) \
|
|
{ \
|
|
size_t sz = sizeof(type); \
|
|
RANGE_RESERVE(p, sz); \
|
|
\
|
|
type *pvalue = (type*)(p->buffer + p->position); \
|
|
*pvalue = value; \
|
|
adjustEndian((uint8_t*)pvalue, sz, getEndian(p)); \
|
|
\
|
|
p->position += sz; \
|
|
UPDATE_LENGTH(p); \
|
|
}
|
|
#endif//_MEMORY_ALIGN_SAFE
|
|
|
|
WRITE_BUILDIN_TEMPLATE( uint8_t, writeUnsignedByte )
|
|
WRITE_BUILDIN_TEMPLATE( int8_t, writeByte )
|
|
WRITE_BUILDIN_TEMPLATE( uint16_t, writeUnsignedShort )
|
|
WRITE_BUILDIN_TEMPLATE( int16_t, writeShort )
|
|
WRITE_BUILDIN_TEMPLATE( uint32_t, writeUnsignedInt )
|
|
WRITE_BUILDIN_TEMPLATE( int32_t, writeInt )
|
|
WRITE_BUILDIN_TEMPLATE( double, writeDouble )
|
|
WRITE_BUILDIN_TEMPLATE( float, writeFloat )
|
|
|
|
// ------------------- for lua -------------------
|
|
|
|
// -------------- literal constant in lvm ----------------
|
|
|
|
#ifdef _MEMORY_ECONOMY
|
|
// declare lua_error message content
|
|
#define MSG_NOMEM "NoMem"
|
|
#define MSG_OUTOFRANGE "EndOfBuf"
|
|
#define MSG_OVERFLOW "LenOvfl"
|
|
#define MSG_INVALIDTYPE "ErrorType"
|
|
#define MSG_READONLY "RoBuf"
|
|
|
|
// declare name for module
|
|
#define MODULE_NAME "buf"
|
|
|
|
#define CONSTANT_ENDIAN_L "LE"
|
|
#define CONSTANT_ENDIAN_B "BE"
|
|
|
|
// declare constructor
|
|
#define CONSTRUCTOR_CREATE "create" // local b = buf.create(size, endian)
|
|
#define CONSTRUCTOR_INITER "init" // local b = buf.init( 49, 50, 51 )
|
|
#define CONSTRUCTOR_FROMARR "load" // local b = buf.load( "hello,world" ) -- read only
|
|
|
|
// declare member
|
|
#define MEMBER_LENGTH "len" // local l = b.length OR b.length = 1024
|
|
#define MEMBER_POSITION "pos" // local p = b.pos OR b.pos = 1
|
|
#define MEMBER_ENDIAN "endian" // local e = b.endian OR b.endian = 0
|
|
#define MEMBER_AVAILABLE "free" // local a = b.free -- read only
|
|
|
|
// declare method
|
|
#define METHOD_READBOOL "rdb" // local t = b:rdb()
|
|
#define METHOD_WRITEBOOL "wrb" // b:wrb( false )
|
|
#define METHOD_READU8 "u8r" // local u = b:u8r()
|
|
#define METHOD_WRITEU8 "u8w" // b:u8w( 0x21 )
|
|
#define METHOD_READS8 "s8r" // local i = b:s8r()
|
|
#define METHOD_WRITES8 "s8w" // b:s8w( 49 )
|
|
#define METHOD_READU16 "u16r"
|
|
#define METHOD_WRITEU16 "u16w"
|
|
#define METHOD_READS16 "s16r"
|
|
#define METHOD_WRITES16 "s16w"
|
|
#define METHOD_READU32 "u32r"
|
|
#define METHOD_WRITEU32 "u32w"
|
|
#define METHOD_READS32 "s32r"
|
|
#define METHOD_WRITES32 "s32w"
|
|
#define METHOD_READFLOAT "f32r"
|
|
#define METHOD_WRITEFLOAT "f32w"
|
|
#define METHOD_READDOUBLE "f64r"
|
|
#define METHOD_WRITEDOUBLE "f64w"
|
|
#define METHOD_READCSTR "trr"
|
|
#define METHOD_WRITECSTR "trw"
|
|
#define METHOD_READSTR "strr"
|
|
#define METHOD_WRITESTR "strw"
|
|
|
|
#define METHOD_READBYTES "read" // local s = buf.create(); b:read(s, 0, b.length)
|
|
#define METHOD_WRITEBYTES "write" // local s = buf.load("hello"); b:write(s, 0, s.length)
|
|
|
|
#define METHOD_CUT "cut" // local t = buf.load("hello,world"):cut( 6, 11 )
|
|
#define METHOD_CLEAR "clear" // b:clear()
|
|
#define METHOD_TOSTRING "str" // b:str()
|
|
#else
|
|
// declare lua_error message content
|
|
#define MSG_NOMEM "memory not enough"
|
|
#define MSG_OUTOFRANGE "out of buffer range"
|
|
#define MSG_OVERFLOW "buffer size overflow"
|
|
#define MSG_INVALIDTYPE "invalid type"
|
|
#define MSG_READONLY "buffer is readonly"
|
|
|
|
// declare name for module
|
|
#define MODULE_NAME "ByteArray"
|
|
|
|
#define CONSTANT_ENDIAN_L "LITTLE_ENDIAN"
|
|
#define CONSTANT_ENDIAN_B "BIG_ENDIAN"
|
|
|
|
// declare constructor
|
|
#define CONSTRUCTOR_CREATE "create"
|
|
#define CONSTRUCTOR_INITER "init"
|
|
#define CONSTRUCTOR_FROMARR "load"
|
|
|
|
// declare member
|
|
#define MEMBER_LENGTH "length"
|
|
#define MEMBER_POSITION "position"
|
|
#define MEMBER_ENDIAN "endian"
|
|
#define MEMBER_AVAILABLE "bytesAvailable"
|
|
|
|
// declare method
|
|
#define METHOD_READBOOL "readBoolean"
|
|
#define METHOD_WRITEBOOL "writeBoolean"
|
|
#define METHOD_READU8 "readUnsignedByte"
|
|
#define METHOD_WRITEU8 "writeUnsignedByte"
|
|
#define METHOD_READS8 "readByte"
|
|
#define METHOD_WRITES8 "writeByte"
|
|
#define METHOD_READU16 "readUnsignedShort"
|
|
#define METHOD_WRITEU16 "writeUnsignedShort"
|
|
#define METHOD_READS16 "readShort"
|
|
#define METHOD_WRITES16 "writeShort"
|
|
#define METHOD_READU32 "readUnsignedInt"
|
|
#define METHOD_WRITEU32 "writeUnsignedInt"
|
|
#define METHOD_READS32 "readInt"
|
|
#define METHOD_WRITES32 "writeInt"
|
|
#define METHOD_READFLOAT "readFloat"
|
|
#define METHOD_WRITEFLOAT "writeFloat"
|
|
#define METHOD_READDOUBLE "readDouble"
|
|
#define METHOD_WRITEDOUBLE "writeDouble"
|
|
#define METHOD_READCSTR "readCString"
|
|
#define METHOD_WRITECSTR "writeCString"
|
|
#define METHOD_READSTR "readString"
|
|
#define METHOD_WRITESTR "writeString"
|
|
|
|
#define METHOD_READBYTES "readBytes"
|
|
#define METHOD_WRITEBYTES "writeBytes"
|
|
|
|
#define METHOD_CUT "slice"
|
|
#define METHOD_CLEAR "clear"
|
|
#define METHOD_TOSTRING "toString"
|
|
#endif
|
|
|
|
#define new_buffer( p, sz, e ) { \
|
|
p = createBuf( sz, e ); \
|
|
if( !p ){ \
|
|
error_handle(L, ERR_NOMEM); \
|
|
lua_error(L); \
|
|
return 0; \
|
|
} \
|
|
}
|
|
|
|
#define check_userdata_self( L ) \
|
|
if( !lua_isuserdata(L, 1) ){ \
|
|
luaL_argerror(L, 1, MSG_INVALIDTYPE); \
|
|
return 0; \
|
|
}
|
|
|
|
#define set_bytearr_metatable( L ) { \
|
|
lua_getfield( L, LUA_REGISTRYINDEX, MODULE_NAME "#mt" ); \
|
|
lua_setmetatable( L, -2 ); \
|
|
}
|
|
|
|
#define lua_pushbuffer( L, p ){ \
|
|
void *m = lua_newuserdata(L, sizeof(p)); \
|
|
memcpy(m, &p, sizeof(p)); \
|
|
set_bytearr_metatable( L ); \
|
|
}
|
|
|
|
static inline Buf* lua_tobuffer(lua_State *L, int index)
|
|
{
|
|
Buf **ud = lua_touserdata(L, index);
|
|
return *ud;
|
|
}
|
|
|
|
void error_handle( lua_State *L, int errno )
|
|
{
|
|
if( errno == ERR_NOMEM ){
|
|
lua_pushstring( L, MSG_NOMEM );
|
|
}
|
|
else if( errno == ERR_OVERFLOW ){
|
|
lua_pushstring( L, MSG_OVERFLOW );
|
|
}
|
|
else if( errno == ERR_READONLY ){
|
|
lua_pushstring( L, MSG_READONLY );
|
|
}
|
|
else if( errno == ERR_OUTOFRANGE ){
|
|
lua_pushstring( L, MSG_OUTOFRANGE );
|
|
}
|
|
}
|
|
|
|
// buf.create( [size, endian] )
|
|
static int lbytearr_create( lua_State *L )
|
|
{
|
|
int size = BYTEARRAY_RESERVE_SIZE;
|
|
if( lua_isnumber( L, 1 ) ) {
|
|
size = luaL_checkint( L, 1 );
|
|
}
|
|
|
|
int endian = getNativeEndian();
|
|
if( lua_isnumber( L, 2 ) ) {
|
|
endian = luaL_checkint( L, 2 );
|
|
}
|
|
|
|
Buf* retval;
|
|
new_buffer( retval, size, endian );
|
|
|
|
lua_pushbuffer( L, retval );
|
|
return 1;
|
|
}
|
|
|
|
// buf.init( 1,2,3 ) or buf.init( {1,2,3} )
|
|
static int lbytearr_init( lua_State *L )
|
|
{
|
|
int n = lua_gettop( L );
|
|
if( n < 1 ) {
|
|
luaL_argerror(L, 1, MSG_INVALIDTYPE);
|
|
return 0;
|
|
}
|
|
|
|
Buf *retval = NULL;
|
|
int endian = getNativeEndian();
|
|
|
|
if(lua_isnumber(L, 1)){
|
|
new_buffer( retval, n, endian );
|
|
|
|
for( int i=1; i <= n; ++i ){
|
|
if( !lua_isnumber(L, i) ){
|
|
release( retval );
|
|
luaL_argerror(L, i, MSG_INVALIDTYPE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
getBuffer(retval)[i-1] = (uint8_t)lua_tointeger(L, i);
|
|
}
|
|
retval->length = n;
|
|
}
|
|
else {
|
|
buflen_t max = ~0;
|
|
size_t total = 0;
|
|
for( int i=1; i <= n; ++i ){
|
|
luaL_checktype(L, i, LUA_TTABLE);
|
|
total += lua_objlen(L, i);
|
|
}
|
|
|
|
if( total > (size_t) max ){
|
|
error_handle(L, ERR_OVERFLOW);
|
|
lua_error(L);
|
|
return 0;
|
|
}
|
|
|
|
new_buffer( retval, total, endian );
|
|
for( int i=1; i <= n; ++i ){
|
|
size_t len = lua_objlen( L, i );
|
|
for( int j=1; j <= len; ++j ){
|
|
lua_pushinteger(L, j);
|
|
lua_gettable(L, i);
|
|
if( !lua_isnumber(L, -1) ){
|
|
release(retval);
|
|
luaL_argerror( L, i, MSG_INVALIDTYPE );
|
|
return 0;
|
|
}
|
|
|
|
writeUnsignedByte(retval, lua_tointeger(L, -1));
|
|
lua_pop(L, 1);
|
|
}// end of j
|
|
}// end of i
|
|
|
|
// rewind the cursor
|
|
setPosition(retval, 0);
|
|
}
|
|
|
|
lua_pushbuffer(L, retval);
|
|
return 1;
|
|
}
|
|
|
|
// local buf = ByteArray.load("hello,world")
|
|
static int lbytearr_load( lua_State *L )
|
|
{
|
|
luaL_checkstring(L, 1);
|
|
|
|
size_t sz;
|
|
const char *p = lua_tolstring(L, 1, &sz);
|
|
int endian = getNativeEndian();
|
|
if( lua_isnumber(L, 2) ){
|
|
endian = lua_tonumber( L, 2 );
|
|
}
|
|
|
|
Buf *retval = fromArray( (void*)p, sz, endian );
|
|
if( !retval ){
|
|
error_handle(L, ERR_NOMEM);
|
|
lua_error(L);
|
|
return 0;
|
|
}
|
|
|
|
lua_pushbuffer( L, retval );
|
|
return 1;
|
|
}
|
|
|
|
// local str = buf:toString()
|
|
static int lbytearr_tostring( lua_State *L )
|
|
{
|
|
check_userdata_self(L);
|
|
|
|
Buf *p = lua_tobuffer(L, 1);
|
|
char *b = (char*)getBuffer(p);
|
|
size_t len = getLength(p);
|
|
lua_pushlstring(L, b, len);
|
|
return 1;
|
|
}
|
|
|
|
#define handle_scope_except() \
|
|
int err = setjmp( except ); \
|
|
if( err ){ \
|
|
error_handle( L, err ); \
|
|
lua_error(L); \
|
|
return 0; \
|
|
}
|
|
|
|
#define bytes_param_check(p, s, offset, length) { \
|
|
luaL_checktype(L, 1, LUA_TUSERDATA); \
|
|
luaL_checktype(L, 2, LUA_TUSERDATA); \
|
|
p = lua_tobuffer(L, 1); \
|
|
s = lua_tobuffer(L, 2); \
|
|
buflen_t max = ~0; \
|
|
offset = 0; \
|
|
length = 0; \
|
|
int n = lua_gettop(L); \
|
|
if( n >= 3 ){ \
|
|
offset = luaL_checkint(L, 3); \
|
|
} \
|
|
if( n >= 4 ) { \
|
|
length = luaL_checkint(L, 4); \
|
|
} \
|
|
if( offset > (size_t)max || length > (size_t)max ){ \
|
|
error_handle(L, ERR_OVERFLOW); \
|
|
lua_error(L); \
|
|
return 0; \
|
|
} \
|
|
}
|
|
|
|
// local buffer = ByteArray.load("hello,world")
|
|
// local s = ByteArray.create()
|
|
// buffer:readBytes( s, 0, buffer.bytesAvailable )
|
|
static int lbytearr_readbytes( lua_State *L )
|
|
{
|
|
Buf *p, *bytes;
|
|
size_t offset, length;
|
|
|
|
bytes_param_check(p, bytes, offset, length);
|
|
|
|
handle_scope_except();
|
|
|
|
if( length == 0 ) length = getBytesAvailable(p);
|
|
if( getLength(bytes) < offset + length ) {
|
|
setLength(bytes, offset+length);
|
|
}
|
|
|
|
readBytes(p, getBuffer(bytes), offset, length);
|
|
return 0;
|
|
}
|
|
|
|
// local buffer = ByteArray.load("hello,world")
|
|
// local s = ByteArray.create()
|
|
// s:writeBytes( buffer, 6, 5 )
|
|
static int lbytearr_writebytes( lua_State *L )
|
|
{
|
|
Buf *p, *bytes;
|
|
size_t offset, length;
|
|
|
|
bytes_param_check(p, bytes, offset, length);
|
|
|
|
handle_scope_except();
|
|
|
|
if( length == 0 ) length = getLength(bytes) - offset;
|
|
if( getLength(bytes) < offset + length ){
|
|
length = getLength(bytes) - offset;
|
|
}
|
|
|
|
writeBytes( p, getBuffer(bytes), offset, length );
|
|
return 0;
|
|
}
|
|
|
|
#define LUA_BIND_BUILDIN_WRITER( NAME, FUNC, CHECKF, TYPE ) \
|
|
static int lbytearr_##NAME( lua_State *L ) \
|
|
{ \
|
|
check_userdata_self(L); \
|
|
\
|
|
Buf *p = lua_tobuffer(L, 1); \
|
|
TYPE b = CHECKF(L, 2); \
|
|
\
|
|
handle_scope_except(); \
|
|
\
|
|
FUNC(p, b); \
|
|
lua_pushvalue(L, 1); \
|
|
return 1; \
|
|
}
|
|
|
|
LUA_BIND_BUILDIN_WRITER( writebool, writeBoolean, lua_toboolean, int );
|
|
LUA_BIND_BUILDIN_WRITER( writes8, writeByte, luaL_checknumber, int );
|
|
LUA_BIND_BUILDIN_WRITER( writeu8, writeUnsignedByte, luaL_checknumber, int );
|
|
LUA_BIND_BUILDIN_WRITER( writes16, writeShort, luaL_checknumber, int );
|
|
LUA_BIND_BUILDIN_WRITER( writeu16, writeUnsignedShort, luaL_checknumber, int );
|
|
LUA_BIND_BUILDIN_WRITER( writes32, writeInt, luaL_checknumber, int );
|
|
LUA_BIND_BUILDIN_WRITER( writeu32, writeUnsignedInt, luaL_checknumber, uint32_t );
|
|
LUA_BIND_BUILDIN_WRITER( writef32, writeFloat, luaL_checknumber, float );
|
|
LUA_BIND_BUILDIN_WRITER( writef64, writeDouble, luaL_checknumber, double );
|
|
|
|
#define LUA_BIND_BUILDIN_READER( NAME, FUNC, TYPE, PUSHF ) \
|
|
static int lbytearr_##NAME( lua_State *L ) \
|
|
{ \
|
|
check_userdata_self(L); \
|
|
\
|
|
Buf *p = lua_tobuffer(L, 1); \
|
|
\
|
|
handle_scope_except(); \
|
|
\
|
|
TYPE retval = FUNC(p); \
|
|
lua_##PUSHF(L, retval); \
|
|
return 1; \
|
|
}
|
|
|
|
LUA_BIND_BUILDIN_READER( readbool, readBoolean, int, pushboolean );
|
|
LUA_BIND_BUILDIN_READER( reads8, readByte, int, pushinteger );
|
|
LUA_BIND_BUILDIN_READER( readu8, readUnsignedByte, int, pushinteger );
|
|
LUA_BIND_BUILDIN_READER( reads16, readShort, int, pushinteger );
|
|
LUA_BIND_BUILDIN_READER( readu16, readUnsignedShort, int, pushinteger );
|
|
LUA_BIND_BUILDIN_READER( reads32, readInt, int, pushinteger );
|
|
LUA_BIND_BUILDIN_READER( readu32, readUnsignedInt, uint32_t, pushnumber );
|
|
LUA_BIND_BUILDIN_READER( readf32, readFloat, float, pushnumber );
|
|
LUA_BIND_BUILDIN_READER( readf64, readDouble, double, pushnumber );
|
|
|
|
// local s = buf:readString( 3 ) -- read 3 byte as lua string
|
|
static int lbytearr_readlstr( lua_State *L )
|
|
{
|
|
check_userdata_self(L);
|
|
|
|
Buf *p = lua_tobuffer(L, 1);
|
|
size_t l = lua_tointeger( L, 2 );
|
|
char *str = (char*)&getBuffer(p)[getPosition(p)];
|
|
|
|
if( getBytesAvailable(p) < l ){
|
|
error_handle(L, ERR_OUTOFRANGE);
|
|
lua_error(L);
|
|
return 0;
|
|
}
|
|
|
|
lua_pushlstring(L, str, l);
|
|
p->position += l;
|
|
return 1;
|
|
}
|
|
|
|
// buf:writeString( "hello" )
|
|
static int lbytearr_writelstr( lua_State *L )
|
|
{
|
|
check_userdata_self(L);
|
|
|
|
luaL_checktype(L, 2, LUA_TSTRING);
|
|
|
|
Buf *p = lua_tobuffer(L, 1);
|
|
const char *pstr = lua_tostring(L, 2);
|
|
size_t l = lua_objlen(L, 2);
|
|
|
|
handle_scope_except();
|
|
writeBytes(p, pstr, 0, l);
|
|
|
|
lua_pushvalue(L, 1);
|
|
return 1;
|
|
}
|
|
|
|
#ifdef BYTEARRAY_USE_CSTRING
|
|
// local t = b:readCString()
|
|
static int lbytearr_readcstr( lua_State *L )
|
|
{
|
|
check_userdata_self(L);
|
|
|
|
Buf *p = lua_tobuffer(L, 1);
|
|
char *str = (char*)&getBuffer(p)[getPosition(p)];
|
|
|
|
size_t l = 1 + strlen( str );
|
|
if( getBytesAvailable(p) < l ){
|
|
error_handle(L, ERR_OUTOFRANGE);
|
|
lua_error(L);
|
|
return 0;
|
|
}
|
|
|
|
lua_pushstring(L, str);
|
|
p->position += l;
|
|
return 1;
|
|
}
|
|
static int lbytearr_writecstr( lua_State *L )
|
|
{
|
|
check_userdata_self(L);
|
|
|
|
luaL_checktype(L, 2, LUA_TSTRING);
|
|
|
|
Buf *p = lua_tobuffer(L, 1);
|
|
const char *pstr = lua_tostring(L, 2);
|
|
uint8_t *str = &getBuffer(p)[getPosition(p)];
|
|
|
|
size_t l = 1 + strlen(pstr);
|
|
|
|
handle_scope_except();
|
|
|
|
RANGE_RESERVE( p, l );
|
|
|
|
memcpy( str, pstr, l-1 );
|
|
str[l-1] = '\0';
|
|
p->position += l;
|
|
p->length += l;
|
|
|
|
lua_pushvalue(L, 1);
|
|
return 1;
|
|
}
|
|
#endif//BYTEARRAY_USE_CSTRING
|
|
|
|
static int lbytearr_clear( lua_State *L )
|
|
{
|
|
check_userdata_self(L);
|
|
|
|
handle_scope_except();
|
|
|
|
Buf *p = lua_tobuffer(L, 1);
|
|
clear( p );
|
|
return 1;
|
|
}
|
|
|
|
static int lbytearr_slice( lua_State *L )
|
|
{
|
|
check_userdata_self(L);
|
|
|
|
Buf *p = lua_tobuffer(L, 1);
|
|
|
|
int start = 0;
|
|
int end = getLength(p);
|
|
size_t n = lua_gettop( L );
|
|
if( n > 1 ){
|
|
start = luaL_checkint(L, 2);
|
|
}
|
|
if( n > 2 ){
|
|
end = luaL_checkint(L, 3);
|
|
}
|
|
if( start < 0 ){
|
|
start += getLength(p);
|
|
}
|
|
if( end <= 0 ){
|
|
end += getLength(p);
|
|
}
|
|
|
|
handle_scope_except();
|
|
|
|
Buf *r;
|
|
if( end < 0 || start > end || (int)getLength(p) <= start ){ // empty array
|
|
r = createBuf( BYTEARRAY_RESERVE_SIZE, getNativeEndian() );
|
|
}
|
|
else{
|
|
if( start < 0 ){ // set start to 0 )
|
|
start = 0;
|
|
}
|
|
if( getLength(p) <= end ){
|
|
end = getLength(p);
|
|
}
|
|
r = cut(p, start, end-start);
|
|
}
|
|
lua_pushbuffer(L, r);
|
|
return 1;
|
|
}
|
|
|
|
static luaL_Reg bytearr_map[] = {
|
|
{ CONSTRUCTOR_CREATE, lbytearr_create },
|
|
{ CONSTRUCTOR_INITER, lbytearr_init },
|
|
{ CONSTRUCTOR_FROMARR, lbytearr_load },
|
|
|
|
{ METHOD_TOSTRING, lbytearr_tostring },
|
|
{ METHOD_CLEAR, lbytearr_clear },
|
|
{ METHOD_CUT, lbytearr_slice },
|
|
|
|
{ METHOD_WRITEBOOL, lbytearr_writebool },
|
|
{ METHOD_WRITEU8, lbytearr_writeu8 },
|
|
{ METHOD_WRITES8, lbytearr_writes8 },
|
|
{ METHOD_WRITEU16, lbytearr_writeu16 },
|
|
{ METHOD_WRITES16, lbytearr_writes16 },
|
|
{ METHOD_WRITEU32, lbytearr_writeu32 },
|
|
{ METHOD_WRITES32, lbytearr_writes32 },
|
|
{ METHOD_WRITEFLOAT, lbytearr_writef32 },
|
|
{ METHOD_WRITEDOUBLE, lbytearr_writef64 },
|
|
{ METHOD_READBOOL, lbytearr_readbool },
|
|
{ METHOD_READU8, lbytearr_readu8 },
|
|
{ METHOD_READS8, lbytearr_reads8 },
|
|
{ METHOD_READU16, lbytearr_readu16 },
|
|
{ METHOD_READS16, lbytearr_reads16 },
|
|
{ METHOD_READU32, lbytearr_readu32 },
|
|
{ METHOD_READS32, lbytearr_reads32 },
|
|
{ METHOD_READFLOAT, lbytearr_readf32 },
|
|
{ METHOD_READDOUBLE, lbytearr_readf64 },
|
|
{ METHOD_READBYTES, lbytearr_readbytes },
|
|
{ METHOD_WRITEBYTES, lbytearr_writebytes },
|
|
{ METHOD_READSTR, lbytearr_readlstr },
|
|
{ METHOD_WRITESTR, lbytearr_writelstr },
|
|
#ifdef BYTEARRAY_USE_CSTRING
|
|
{ METHOD_READCSTR, lbytearr_readcstr },
|
|
{ METHOD_WRITECSTR, lbytearr_writecstr },
|
|
#endif
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static int lbytearr_getlen( lua_State *L )
|
|
{
|
|
check_userdata_self(L);
|
|
|
|
Buf *p = lua_tobuffer(L, 1);
|
|
lua_pushinteger( L, getLength(p) );
|
|
return 1;
|
|
}
|
|
|
|
static int lbytearr_getter( lua_State *L )
|
|
{
|
|
check_userdata_self(L);
|
|
|
|
Buf *p = lua_tobuffer(L, 1);
|
|
|
|
if( lua_type(L, 2) == LUA_TNUMBER ){
|
|
lua_Number arg2 = lua_tonumber(L, 2);
|
|
buflen_t idx = (buflen_t)arg2;
|
|
if( fabs( (float)arg2 - (float)idx ) < 0.00001f )
|
|
lua_pushinteger( L, at(p, lua_tointeger(L, 2)-1) );
|
|
else lua_pushinteger(L, -1);
|
|
}
|
|
else if( lua_isstring(L, 2) ){
|
|
const char *key = lua_tostring(L, 2);
|
|
// if exist in MODULE
|
|
lua_getglobal(L, MODULE_NAME);
|
|
lua_getfield(L, -1, key);
|
|
|
|
if( lua_isnil(L, -1) ){
|
|
//if the member name
|
|
if( 0 == strcmp(key, MEMBER_LENGTH) ){
|
|
lua_pushinteger(L, getLength(p));
|
|
}
|
|
else if( 0 == strcmp(key, MEMBER_POSITION) ){
|
|
lua_pushinteger(L, getPosition(p));
|
|
}
|
|
else if( 0 == strcmp(key, MEMBER_AVAILABLE) ){
|
|
lua_pushinteger(L, getBytesAvailable(p));
|
|
}
|
|
else if( 0 == strcmp(key, MEMBER_ENDIAN) ){
|
|
lua_pushinteger(L, getEndian(p));
|
|
}
|
|
}
|
|
}
|
|
else lua_pushnil(L);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int lbytearr_setter( lua_State *L )
|
|
{
|
|
check_userdata_self(L);
|
|
|
|
Buf *p = lua_tobuffer(L, 1);
|
|
|
|
handle_scope_except();
|
|
|
|
int val;
|
|
val = luaL_checkint(L, 3);
|
|
if( lua_isnumber(L, 2) ){
|
|
int index = lua_tointeger(L, 2) - 1;
|
|
luaL_argcheck(L, 0 <= index, 2, MSG_OUTOFRANGE);
|
|
assign( p, index, (uint8_t)(val & 0xff) );
|
|
}
|
|
else if( lua_isstring(L, 2) ){
|
|
const char *key = lua_tostring(L, 2);
|
|
|
|
//if the member name
|
|
if( 0 == strcmp(key, MEMBER_LENGTH) ){
|
|
if( val < 0 ) longjmp( except, ERR_OUTOFRANGE );
|
|
setLength(p, val);
|
|
}
|
|
else if( 0 == strcmp(key, MEMBER_POSITION) ){
|
|
if( val < 0 ) longjmp( except, ERR_OUTOFRANGE );
|
|
setPosition(p, val);
|
|
}
|
|
else if( 0 == strcmp(key, MEMBER_ENDIAN) ){
|
|
setEndian(p, val);
|
|
}
|
|
else longjmp( except, ERR_OUTOFRANGE );
|
|
}
|
|
else longjmp( except, ERR_OUTOFRANGE );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int lbytearr_gc( lua_State *L )
|
|
{
|
|
check_userdata_self(L);
|
|
|
|
Buf *p = lua_tobuffer(L, 1);
|
|
release( p );
|
|
return 0;
|
|
}
|
|
|
|
int luaopen_bytearr( lua_State *L )
|
|
{
|
|
luaL_register(L, MODULE_NAME, bytearr_map );
|
|
|
|
lua_pushinteger(L, ENDIAN_LITTLE);
|
|
lua_setfield(L, -2, CONSTANT_ENDIAN_L );
|
|
|
|
lua_pushinteger(L, ENDIAN_BIG);
|
|
lua_setfield(L, -2, CONSTANT_ENDIAN_B );
|
|
|
|
// metatable
|
|
lua_newtable(L);
|
|
|
|
lua_pushcfunction(L, lbytearr_getlen);
|
|
lua_setfield(L, -2, "__len");
|
|
|
|
lua_pushcfunction(L, lbytearr_getter);
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
lua_pushcfunction(L, lbytearr_setter);
|
|
lua_setfield(L, -2, "__newindex");
|
|
|
|
lua_pushcfunction(L, lbytearr_gc);
|
|
lua_setfield(L, -2, "__gc");
|
|
|
|
//lua_pushcfunction(L, lbytearr_add);
|
|
//lua_pushstring(L, "__add");
|
|
lua_setfield(L, LUA_REGISTRYINDEX, MODULE_NAME "#mt");
|
|
|
|
return 0;
|
|
}
|