diff --git a/src/util/inc/exception.h b/src/util/inc/exception.h new file mode 100644 index 0000000000000000000000000000000000000000..5c9506f802836a7d4065f60afc532753895a0ba3 --- /dev/null +++ b/src/util/inc/exception.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef TDENGINE_EXCEPTION_H +#define TDENGINE_EXCEPTION_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SExceptionNode { + struct SExceptionNode* prev; + jmp_buf jb; + int code; +} SExceptionNode; + +void expPushNode( SExceptionNode* node ); +int expPopNode(); +void expThrow( int code ); + +#define TRY do { \ + SExceptionNode expNode = { 0 }; \ + expPushNode( &expNode ); \ + if( setjmp(expNode.jb) == 0 ) { + +#define CATCH( code ) expPopNode(); \ + } else { \ + int code = expPopNode(); + +#define END_CATCH } } while( 0 ); + +#define THROW( x ) expThrow( (x) ) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/util/inc/tbuffer.h b/src/util/inc/tbuffer.h index 9bc0fd9f43eb9122aa50a438a3eb1053312c7d6e..2d8ea732cce1901970101cf408466d76f34c0e77 100644 --- a/src/util/inc/tbuffer.h +++ b/src/util/inc/tbuffer.h @@ -16,122 +16,170 @@ #ifndef TDENGINE_TBUFFER_H #define TDENGINE_TBUFFER_H -#include "setjmp.h" -#include "os.h" +#include +#include #ifdef __cplusplus extern "C" { #endif /* -SBuffer can be used to read or write a buffer, but cannot be used for both -read & write at a same time. Below is an example: +// SBuffer can be used to read or write a buffer, but cannot be used for both +// read & write at a same time. Below is an example: +#include +#include +#include "exception.h" +#include "tbuffer.h" + +int foo() { + SBuffer wbuf, rbuf; + tbufSetup(&wbuf, NULL, false); + tbufSetup(&rbuf, NULL, false); + + TRY { + //--------------------- write ------------------------ + tbufBeginWrite(&wbuf); + // reserve 1024 bytes for the buffer to improve performance + tbufEnsureCapacity(&wbuf, 1024); + // write 5 integers to the buffer + for (int i = 0; i < 5; i++) { + tbufWriteInt32(&wbuf, i); + } + // write a string to the buffer + tbufWriteString(&wbuf, "this is a string.\n"); + // acquire the result and close the write buffer + size_t size = tbufTell(&wbuf); + char* data = tbufGetData(&wbuf, true); + + //------------------------ read ----------------------- + tbufBeginRead(&rbuf, data, size); + // read & print out 5 integers + for (int i = 0; i < 5; i++) { + printf("%d\n", tbufReadInt32(&rbuf)); + } + // read & print out a string + puts(tbufReadString(&rbuf, NULL)); + // try read another integer, this result in an error as there no this integer + tbufReadInt32(&rbuf); + printf("you should not see this message.\n"); + } CATCH( code ) { + printf("exception code is: %d, you will see this message after print out 5 integers and a string.\n", code); + THROW( code ); + } END_CATCH -int main(int argc, char** argv) { - //--------------------- write ------------------------ - SBuffer wbuf; - int32_t code = tbufBeginWrite(&wbuf); - if (code != 0) { - // handle errors - return 0; - } - - // reserve 1024 bytes for the buffer to improve performance - tbufEnsureCapacity(&wbuf, 1024); - - // write 5 integers to the buffer - for (int i = 0; i < 5; i++) { - tbufWriteInt32(&wbuf, i); - } - - // write a string to the buffer - tbufWriteString(&wbuf, "this is a string.\n"); - - // acquire the result and close the write buffer - size_t size = tbufTell(&wbuf); - char* data = tbufGetData(&wbuf, true); tbufClose(&wbuf, true); + tbufClose(&rbuf, false); + return 0; +} +int main(int argc, char** argv) { + TRY { + printf("in main: you will see this line\n"); + foo(); + printf("in main: you will not see this line\n"); + } CATCH( code ) { + printf("foo raise an exception with code %d\n", code); + } END_CATCH - //------------------------ read ----------------------- - SBuffer rbuf; - code = tbufBeginRead(&rbuf, data, size); - if (code != 0) { - printf("you will see this message after print out 5 integers and a string.\n"); - tbufClose(&rbuf, false); return 0; - } - - // read & print out 5 integers - for (int i = 0; i < 5; i++) { - printf("%d\n", tbufReadInt32(&rbuf)); - } - - // read & print out a string - printf(tbufReadString(&rbuf, NULL)); - - // try read another integer, this result in an error as there no this integer - tbufReadInt32(&rbuf); - - printf("you should not see this message.\n"); - tbufClose(&rbuf, false); - - return 0; } */ + typedef struct { - jmp_buf jb; + void* (*allocator)(void*, size_t); + bool endian; char* data; size_t pos; size_t size; } SBuffer; // common functions can be used in both read & write -#define tbufThrowError(buf, code) longjmp((buf)->jb, (code)) + +// tbufSetup setup the buffer, should be called before tbufBeginRead / tbufBeginWrite +// *allocator*, function to allocate memory, will use 'realloc' if NULL +// *endian*, if true, read/write functions of primitive types will do 'ntoh' or 'hton' automatically +void tbufSetup(SBuffer* buf, void* (*allocator)(void*, size_t), bool endian); size_t tbufTell(SBuffer* buf); size_t tbufSeekTo(SBuffer* buf, size_t pos); -size_t tbufSkip(SBuffer* buf, size_t size); void tbufClose(SBuffer* buf, bool keepData); // basic read functions -#define tbufBeginRead(buf, _data, len) ((buf)->data = (char*)(_data), ((buf)->pos = 0), ((buf)->size = ((_data) == NULL) ? 0 : (len)), setjmp((buf)->jb)) +void tbufBeginRead(SBuffer* buf, void* data, size_t len); +size_t tbufSkip(SBuffer* buf, size_t size); char* tbufRead(SBuffer* buf, size_t size); void tbufReadToBuffer(SBuffer* buf, void* dst, size_t size); const char* tbufReadString(SBuffer* buf, size_t* len); size_t tbufReadToString(SBuffer* buf, char* dst, size_t size); +const char* tbufReadBinary(SBuffer* buf, size_t *len); +size_t tbufReadToBinary(SBuffer* buf, void* dst, size_t size); // basic write functions -#define tbufBeginWrite(buf) ((buf)->data = NULL, ((buf)->pos = 0), ((buf)->size = 0), setjmp((buf)->jb)) -void tbufEnsureCapacity(SBuffer* buf, size_t size); -char* tbufGetData(SBuffer* buf, bool takeOver); -void tbufWrite(SBuffer* buf, const void* data, size_t size); -void tbufWriteAt(SBuffer* buf, size_t pos, const void* data, size_t size); -void tbufWriteStringLen(SBuffer* buf, const char* str, size_t len); -void tbufWriteString(SBuffer* buf, const char* str); - -// read & write function for primitive types -#ifndef TBUFFER_DEFINE_FUNCTION -#define TBUFFER_DEFINE_FUNCTION(type, name) \ - type tbufRead##name(SBuffer* buf); \ - void tbufWrite##name(SBuffer* buf, type data); \ - void tbufWrite##name##At(SBuffer* buf, size_t pos, type data); -#endif - -TBUFFER_DEFINE_FUNCTION(bool, Bool) -TBUFFER_DEFINE_FUNCTION(char, Char) -TBUFFER_DEFINE_FUNCTION(int8_t, Int8) -TBUFFER_DEFINE_FUNCTION(uint8_t, Uint8) -TBUFFER_DEFINE_FUNCTION(int16_t, Int16) -TBUFFER_DEFINE_FUNCTION(uint16_t, Uint16) -TBUFFER_DEFINE_FUNCTION(int32_t, Int32) -TBUFFER_DEFINE_FUNCTION(uint32_t, Uint32) -TBUFFER_DEFINE_FUNCTION(int64_t, Int64) -TBUFFER_DEFINE_FUNCTION(uint64_t, Uint64) -TBUFFER_DEFINE_FUNCTION(float, Float) -TBUFFER_DEFINE_FUNCTION(double, Double) +void tbufBeginWrite(SBuffer* buf); +void tbufEnsureCapacity(SBuffer* buf, size_t size); +size_t tbufReserve(SBuffer* buf, size_t size); +char* tbufGetData(SBuffer* buf, bool takeOver); +void tbufWrite(SBuffer* buf, const void* data, size_t size); +void tbufWriteAt(SBuffer* buf, size_t pos, const void* data, size_t size); +void tbufWriteStringLen(SBuffer* buf, const char* str, size_t len); +void tbufWriteString(SBuffer* buf, const char* str); +// the prototype of WriteBinary and Write is identical +// the difference is: WriteBinary writes the length of the data to the buffer +// first, then the actual data, which means the reader don't need to know data +// size before read. Write only write the data itself, which means the reader +// need to know data size before read. +void tbufWriteBinary(SBuffer* buf, const void* data, size_t len); + +// read / write functions for primitive types +bool tbufReadBool(SBuffer* buf); +void tbufWriteBool(SBuffer* buf, bool data); +void tbufWriteBoolAt(SBuffer* buf, size_t pos, bool data); + +char tbufReadChar(SBuffer* buf); +void tbufWriteChar(SBuffer* buf, char data); +void tbufWriteCharAt(SBuffer* buf, size_t pos, char data); + +int8_t tbufReadInt8(SBuffer* buf); +void tbufWriteInt8(SBuffer* buf, int8_t data); +void tbufWriteInt8At(SBuffer* buf, size_t pos, int8_t data); + +uint8_t tbufReadUint8(SBuffer* buf); +void tbufWriteUint8(SBuffer* buf, uint8_t data); +void tbufWriteUint8At(SBuffer* buf, size_t pos, uint8_t data); + +int16_t tbufReadInt16(SBuffer* buf); +void tbufWriteInt16(SBuffer* buf, int16_t data); +void tbufWriteInt16At(SBuffer* buf, size_t pos, int16_t data); + +uint16_t tbufReadUint16(SBuffer* buf); +void tbufWriteUint16(SBuffer* buf, uint16_t data); +void tbufWriteUint16At(SBuffer* buf, size_t pos, uint16_t data); + +int32_t tbufReadInt32(SBuffer* buf); +void tbufWriteInt32(SBuffer* buf, int32_t data); +void tbufWriteInt32At(SBuffer* buf, size_t pos, int32_t data); + +uint32_t tbufReadUint32(SBuffer* buf); +void tbufWriteUint32(SBuffer* buf, uint32_t data); +void tbufWriteUint32At(SBuffer* buf, size_t pos, uint32_t data); + +int64_t tbufReadInt64(SBuffer* buf); +void tbufWriteInt64(SBuffer* buf, int64_t data); +void tbufWriteInt64At(SBuffer* buf, size_t pos, int64_t data); + +uint64_t tbufReadUint64(SBuffer* buf); +void tbufWriteUint64(SBuffer* buf, uint64_t data); +void tbufWriteUint64At(SBuffer* buf, size_t pos, uint64_t data); + +float tbufReadFloat(SBuffer* buf); +void tbufWriteFloat(SBuffer* buf, float data); +void tbufWriteFloatAt(SBuffer* buf, size_t pos, float data); + +double tbufReadDouble(SBuffer* buf); +void tbufWriteDouble(SBuffer* buf, double data); +void tbufWriteDoubleAt(SBuffer* buf, size_t pos, double data); #ifdef __cplusplus } #endif -#endif \ No newline at end of file +#endif diff --git a/src/util/src/exception.c b/src/util/src/exception.c new file mode 100644 index 0000000000000000000000000000000000000000..6363aaebf61f06be2a1e50cb657f9c30b8e20f56 --- /dev/null +++ b/src/util/src/exception.c @@ -0,0 +1,20 @@ +#include "exception.h" + + +static _Thread_local SExceptionNode* expList; + +void expPushNode( SExceptionNode* node ) { + node->prev = expList; + expList = node; +} + +int expPopNode() { + SExceptionNode* node = expList; + expList = node->prev; + return node->code; +} + +void expThrow( int code ) { + expList->code = code; + longjmp( expList->jb, 1 ); +} diff --git a/src/util/src/tbuffer.c b/src/util/src/tbuffer.c index a83d7dddb0d8e987bfbb670f8f3e4b413539dec2..c254436a4e71f39e9cda9c435f8ef820d4fc0ef3 100644 --- a/src/util/src/tbuffer.c +++ b/src/util/src/tbuffer.c @@ -16,47 +16,44 @@ #include #include #include - -#define TBUFFER_DEFINE_FUNCTION(type, name) \ - type tbufRead##name(SBuffer* buf) { \ - type ret; \ - tbufReadToBuffer(buf, &ret, sizeof(type)); \ - return ret; \ - }\ - void tbufWrite##name(SBuffer* buf, type data) {\ - tbufWrite(buf, &data, sizeof(data));\ - }\ - void tbufWrite##name##At(SBuffer* buf, size_t pos, type data) {\ - tbufWriteAt(buf, pos, &data, sizeof(data));\ - } - +#include #include "tbuffer.h" - +#include "exception.h" +#include //////////////////////////////////////////////////////////////////////////////// // common functions +void tbufSetup( + SBuffer* buf, + void* (*allocator)(void*, size_t), + bool endian +) { + if (allocator != NULL) { + buf->allocator = allocator; + } else { + buf->allocator = realloc; + } + + buf->endian = endian; +} + size_t tbufTell(SBuffer* buf) { return buf->pos; } size_t tbufSeekTo(SBuffer* buf, size_t pos) { if (pos > buf->size) { - // TODO: update error code, other tbufThrowError need to be changed too - tbufThrowError(buf, 1); + THROW( TSDB_CODE_MEMORY_CORRUPTED ); } size_t old = buf->pos; buf->pos = pos; return old; } -size_t tbufSkip(SBuffer* buf, size_t size) { - return tbufSeekTo(buf, buf->pos + size); -} - void tbufClose(SBuffer* buf, bool keepData) { if (!keepData) { - free(buf->data); + (*buf->allocator)(buf->data, 0); } buf->data = NULL; buf->pos = 0; @@ -66,6 +63,16 @@ void tbufClose(SBuffer* buf, bool keepData) { //////////////////////////////////////////////////////////////////////////////// // read functions +void tbufBeginRead(SBuffer* buf, void* data, size_t len) { + buf->data = data; + buf->pos = 0; + buf->size = (data == NULL) ? 0 : len; +} + +size_t tbufSkip(SBuffer* buf, size_t size) { + return tbufSeekTo(buf, buf->pos + size); +} + char* tbufRead(SBuffer* buf, size_t size) { char* ret = buf->data + buf->pos; tbufSkip(buf, size); @@ -78,8 +85,16 @@ void tbufReadToBuffer(SBuffer* buf, void* dst, size_t size) { memcpy(dst, tbufRead(buf, size), size); } -const char* tbufReadString(SBuffer* buf, size_t* len) { +static size_t tbufReadLength(SBuffer* buf) { + // maximum length is 65535, if larger length is required + // this function and the corresponding write function need to be + // revised. uint16_t l = tbufReadUint16(buf); + return l; +} + +const char* tbufReadString(SBuffer* buf, size_t* len) { + size_t l = tbufReadLength(buf); char* ret = buf->data + buf->pos; tbufSkip(buf, l + 1); ret[l] = 0; // ensure the string end with '\0' @@ -101,23 +116,55 @@ size_t tbufReadToString(SBuffer* buf, char* dst, size_t size) { return len; } +const char* tbufReadBinary(SBuffer* buf, size_t *len) { + size_t l = tbufReadLength(buf); + char* ret = buf->data + buf->pos; + tbufSkip(buf, l); + if (len != NULL) { + *len = l; + } + return ret; +} + +size_t tbufReadToBinary(SBuffer* buf, void* dst, size_t size) { + assert(dst != NULL); + size_t len; + const char* data = tbufReadBinary(buf, &len); + if (len >= size) { + len = size; + } + memcpy(dst, data, len); + return len; +} //////////////////////////////////////////////////////////////////////////////// // write functions +void tbufBeginWrite(SBuffer* buf) { + buf->data = NULL; + buf->pos = 0; + buf->size = 0; +} + void tbufEnsureCapacity(SBuffer* buf, size_t size) { size += buf->pos; if (size > buf->size) { size_t nsize = size + buf->size; - char* data = realloc(buf->data, nsize); + char* data = (*buf->allocator)(buf->data, nsize); if (data == NULL) { - tbufThrowError(buf, 2); + // TODO: handle client out of memory + THROW( TSDB_CODE_SERV_OUT_OF_MEMORY ); } buf->data = data; buf->size = nsize; } } +size_t tbufReserve(SBuffer* buf, size_t size) { + tbufEnsureCapacity(buf, size); + return tbufSeekTo(buf, buf->pos + size); +} + char* tbufGetData(SBuffer* buf, bool takeOver) { char* ret = buf->data; if (takeOver) { @@ -129,13 +176,6 @@ char* tbufGetData(SBuffer* buf, bool takeOver) { return ret; } -void tbufEndWrite(SBuffer* buf) { - free(buf->data); - buf->data = NULL; - buf->pos = 0; - buf->size = 0; -} - void tbufWrite(SBuffer* buf, const void* data, size_t size) { assert(data != NULL); tbufEnsureCapacity(buf, size); @@ -151,15 +191,248 @@ void tbufWriteAt(SBuffer* buf, size_t pos, const void* data, size_t size) { memcpy(buf->data + pos, data, size); } -void tbufWriteStringLen(SBuffer* buf, const char* str, size_t len) { - // maximum string length is 65535, if longer string is required +static void tbufWriteLength(SBuffer* buf, size_t len) { + // maximum length is 65535, if larger length is required // this function and the corresponding read function need to be // revised. assert(len <= 0xffff); tbufWriteUint16(buf, (uint16_t)len); - tbufWrite(buf, str, len + 1); +} + +void tbufWriteStringLen(SBuffer* buf, const char* str, size_t len) { + tbufWriteLength(buf, len); + tbufWrite(buf, str, len); + tbufWriteChar(buf, '\0'); } void tbufWriteString(SBuffer* buf, const char* str) { tbufWriteStringLen(buf, str, strlen(str)); } + +void tbufWriteBinary(SBuffer* buf, const void* data, size_t len) { + tbufWriteLength(buf, len); + tbufWrite(buf, data, len); +} + +//////////////////////////////////////////////////////////////////////////////// +// read / write functions for primitive types + +bool tbufReadBool(SBuffer* buf) { + bool ret; + tbufReadToBuffer(buf, &ret, sizeof(ret)); + return ret; +} + +void tbufWriteBool(SBuffer* buf, bool data) { + tbufWrite(buf, &data, sizeof(data)); +} + +void tbufWriteBoolAt(SBuffer* buf, size_t pos, bool data) { + tbufWriteAt(buf, pos, &data, sizeof(data)); +} + +char tbufReadChar(SBuffer* buf) { + char ret; + tbufReadToBuffer(buf, &ret, sizeof(ret)); + return ret; +} + +void tbufWriteChar(SBuffer* buf, char data) { + tbufWrite(buf, &data, sizeof(data)); +} + +void tbufWriteCharAt(SBuffer* buf, size_t pos, char data) { + tbufWriteAt(buf, pos, &data, sizeof(data)); +} + +int8_t tbufReadInt8(SBuffer* buf) { + int8_t ret; + tbufReadToBuffer(buf, &ret, sizeof(ret)); + return ret; +} + +void tbufWriteInt8(SBuffer* buf, int8_t data) { + tbufWrite(buf, &data, sizeof(data)); +} + +void tbufWriteInt8At(SBuffer* buf, size_t pos, int8_t data) { + tbufWriteAt(buf, pos, &data, sizeof(data)); +} + +uint8_t tbufReadUint8(SBuffer* buf) { + uint8_t ret; + tbufReadToBuffer(buf, &ret, sizeof(ret)); + return ret; +} + +void tbufWriteUint8(SBuffer* buf, uint8_t data) { + tbufWrite(buf, &data, sizeof(data)); +} + +void tbufWriteUint8At(SBuffer* buf, size_t pos, uint8_t data) { + tbufWriteAt(buf, pos, &data, sizeof(data)); +} + +int16_t tbufReadInt16(SBuffer* buf) { + int16_t ret; + tbufReadToBuffer(buf, &ret, sizeof(ret)); + if (buf->endian) { + return (int16_t)ntohs(ret); + } + return ret; +} + +void tbufWriteInt16(SBuffer* buf, int16_t data) { + if (buf->endian) { + data = (int16_t)htons(data); + } + tbufWrite(buf, &data, sizeof(data)); +} + +void tbufWriteInt16At(SBuffer* buf, size_t pos, int16_t data) { + if (buf->endian) { + data = (int16_t)htons(data); + } + tbufWriteAt(buf, pos, &data, sizeof(data)); +} + +uint16_t tbufReadUint16(SBuffer* buf) { + uint16_t ret; + tbufReadToBuffer(buf, &ret, sizeof(ret)); + if (buf->endian) { + return ntohs(ret); + } + return ret; +} + +void tbufWriteUint16(SBuffer* buf, uint16_t data) { + if (buf->endian) { + data = htons(data); + } + tbufWrite(buf, &data, sizeof(data)); +} + +void tbufWriteUint16At(SBuffer* buf, size_t pos, uint16_t data) { + if (buf->endian) { + data = htons(data); + } + tbufWriteAt(buf, pos, &data, sizeof(data)); +} + +int32_t tbufReadInt32(SBuffer* buf) { + int32_t ret; + tbufReadToBuffer(buf, &ret, sizeof(ret)); + if (buf->endian) { + return (int32_t)ntohl(ret); + } + return ret; +} + +void tbufWriteInt32(SBuffer* buf, int32_t data) { + if (buf->endian) { + data = (int32_t)htonl(data); + } + tbufWrite(buf, &data, sizeof(data)); +} + +void tbufWriteInt32At(SBuffer* buf, size_t pos, int32_t data) { + if (buf->endian) { + data = (int32_t)htonl(data); + } + tbufWriteAt(buf, pos, &data, sizeof(data)); +} + +uint32_t tbufReadUint32(SBuffer* buf) { + uint32_t ret; + tbufReadToBuffer(buf, &ret, sizeof(ret)); + if (buf->endian) { + return ntohl(ret); + } + return ret; +} + +void tbufWriteUint32(SBuffer* buf, uint32_t data) { + if (buf->endian) { + data = htonl(data); + } + tbufWrite(buf, &data, sizeof(data)); +} + +void tbufWriteUint32At(SBuffer* buf, size_t pos, uint32_t data) { + if (buf->endian) { + data = htonl(data); + } + tbufWriteAt(buf, pos, &data, sizeof(data)); +} + +int64_t tbufReadInt64(SBuffer* buf) { + int64_t ret; + tbufReadToBuffer(buf, &ret, sizeof(ret)); + if (buf->endian) { + return (int64_t)htobe64(ret); // TODO: ntohll + } + return ret; +} + +void tbufWriteInt64(SBuffer* buf, int64_t data) { + if (buf->endian) { + data = (int64_t)htobe64(data); + } + tbufWrite(buf, &data, sizeof(data)); +} + +void tbufWriteInt64At(SBuffer* buf, size_t pos, int64_t data) { + if (buf->endian) { + data = (int64_t)htobe64(data); + } + tbufWriteAt(buf, pos, &data, sizeof(data)); +} + +uint64_t tbufReadUint64(SBuffer* buf) { + uint64_t ret; + tbufReadToBuffer(buf, &ret, sizeof(ret)); + if (buf->endian) { + return htobe64(ret); // TODO: ntohll + } + return ret; +} + +void tbufWriteUint64(SBuffer* buf, uint64_t data) { + if (buf->endian) { + data = htobe64(data); + } + tbufWrite(buf, &data, sizeof(data)); +} + +void tbufWriteUint64At(SBuffer* buf, size_t pos, uint64_t data) { + if (buf->endian) { + data = htobe64(data); + } + tbufWriteAt(buf, pos, &data, sizeof(data)); +} + +float tbufReadFloat(SBuffer* buf) { + uint32_t ret = tbufReadUint32(buf); + return *(float*)(&ret); +} + +void tbufWriteFloat(SBuffer* buf, float data) { + tbufWriteUint32(buf, *(uint32_t*)(&data)); +} + +void tbufWriteFloatAt(SBuffer* buf, size_t pos, float data) { + tbufWriteUint32At(buf, pos, *(uint32_t*)(&data)); +} + +double tbufReadDouble(SBuffer* buf) { + uint64_t ret = tbufReadUint64(buf); + return *(double*)(&ret); +} + +void tbufWriteDouble(SBuffer* buf, double data) { + tbufWriteUint64(buf, *(uint64_t*)(&data)); +} + +void tbufWriteDoubleAt(SBuffer* buf, size_t pos, double data) { + tbufWriteUint64At(buf, pos, *(uint64_t*)(&data)); +}