diff --git a/.gitignore b/.gitignore index 6229cfe..d57298b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,4 @@ cwpackModuleTest json2cwpack2json # Data files -*.msgpack *.msgpack.json diff --git a/README.md b/README.md old mode 100755 new mode 100644 index e1885ce..b1faa02 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # CWPack -CWPack is a lightweight and yet complete implementation of the -[MessagePack](http://msgpack.org) serialization format +CWPack is a lightweight and yet complete implementation of the +[MessagePack](http://msgpack.org) serialization format [version 5](https://github.com/msgpack/msgpack/blob/master/spec.md). +It also supports the Timestamp extension type. ## Excellent Performance @@ -11,15 +12,11 @@ Together with [MPack](https://github.com/ludocode/mpack), CWPack is the fastest ## Design -CWPack does no memory allocations and no file handling. All that is done -outside of CWPack. +CWPack does no memory allocations and no file handling in its basic setup. All that is done outside of CWPack. Example extensions are included. -CWPack is working against memory buffers. User defined handlers are called when buffers are -filled up (packing) or needs refill (unpack). +CWPack is working against memory buffers. User defined handlers are called when buffers are filled up (packing) or needs refill (unpack). -Containers (arrays, maps) are read/written in parts, first the item containing the size and -then the contained items one by one. Exception to this is the `cw_skip_items` function which -skip whole containers. +Containers (arrays, maps) are read/written in parts, first the item containing the size and then the contained items one by one. Exception to this is the `cw_skip_items` function which skip whole containers. ## Example @@ -38,29 +35,22 @@ void example (void) cw_pack_str (&pc, "schema", 6); cw_pack_unsigned (&pc, 0); + if (pc.return_code != CWP_RC_OK) ERROR; int length = pc.current - pc.start; - if (length > 18) ERROR; + if (length != 18) ERROR; cw_unpack_context uc; cw_unpack_context_init (&uc, pc.start, length, 0); - cw_unpack_next(&uc); - if (uc.item.type != CWP_ITEM_MAP || uc.item.as.map.size != 2) ERROR; - - cw_unpack_next(&uc); - if (uc.item.type != CWP_ITEM_STR || uc.item.as.str.length != 7) ERROR; + if (cw_unpack_next_map_size(&uc) != 2) ERROR; + if (cw_unpack_next_str_lengh(&uc) != 7) ERROR; if (strncmp("compact", uc.item.as.str.start, 7)) ERROR; - - cw_unpack_next(&uc); - if (uc.item.type != CWP_ITEM_BOOLEAN || uc.item.as.boolean != true) ERROR; - - cw_unpack_next(&uc); - if (uc.item.type != CWP_ITEM_STR || uc.item.as.str.length != 6) ERROR; + if (cw_unpack_next_bool(&uc) != true) ERROR; + if (cw_unpack_next_str_lengh(&uc) != 6) ERROR; if (strncmp("schema", uc.item.as.str.start, 6)) ERROR; + if (cw_unpack_next_signed32(&uc) != 0) ERROR; - cw_unpack_next(&uc); - if (uc.item.type != CWP_ITEM_POSITIVE_INTEGER || uc.item.as.u64 != 0) ERROR; - + if (uc.return_code != CWP_RC_OK) ERROR; cw_unpack_next(&uc); if (uc.return_code != CWP_RC_END_OF_INPUT) ERROR; } @@ -70,22 +60,32 @@ In the examples folder there are more examples. ## Backward compatibility -CWPack may be run in compatibility mode. It affects only packing; EXT is considered illegal, BIN are transformed to STR and generation of STR8 is supressed. +CWPack may be run in compatibility mode. It affects only packing; EXT & TIMESTAMP is considered illegal, BIN are transformed to STR and generation of STR8 is supressed. ## Error handling -When an error is detected in a context, the context is stopped and all future calls to that context are immediatly returned without any actions. +When an error is detected in a context, the context is stopped and all future calls to that context are immediatly returned without any actions. Thus it is possible to make some calls and delay error checking until all calls are done. CWPack does not check for illegal values (e.g. in STR for illegal unicode characters). ## Build -CWPack consists of a single src file and two header files. It is written -in strict ansi C and the files are together ~ 1.2K lines. No separate build is neccesary, just include the -files in your own build. +CWPack consists of a single src file and three header files. It is written in strict ansi C and the files are together ~ 1.4K lines. No separate build is neccesary, just include the files in your own build. CWPack has no dependencies to other libraries. ## Test Included in the test folder are a module test and a performance test and shell scripts to run them. + +# Objective-C + +CWPack also contains an Objective-C interface. The MessagePack home page example would look as: + +```C +CWPackContext *pc = [CWPackContext newWithContext:my_cw_pack_context]; +[pc packObject:@{@"compact":@YES, @"schema":@0}]; + +CWUnpackContext *uc = [CWUnpackContext newWithContext:my_cw_unpack_context]; +NSDictionary *dict = [uc unpackNextObject]; +``` diff --git a/example/README.md b/example/README.md index 88d29a2..5556a27 100755 --- a/example/README.md +++ b/example/README.md @@ -1,10 +1,10 @@ -#CWPack - Example +# CWPack / Example The example contains a program that takes a json file and converts it to a messagePack file, then converts the latter back to json. In the script runExample.sh the 2 json files are also diffed. -The files `item.*` contains a memory tree representation of json data and the conversion routines: +The files `item.*` contains a memory tree representation of json data and the conversion routines: - Item Tree To Json File - Item Tree To MessagePack File diff --git a/example/item.c b/example/item.c index 732084a..d49ccba 100644 --- a/example/item.c +++ b/example/item.c @@ -1,18 +1,18 @@ /* CWPack/example - item.c */ /* The MIT License (MIT) - + Copyright (c) 2017 Claes Wihlborg - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, @@ -44,7 +44,7 @@ void freeItem3 (item_root* root) { freeItem3(ic->items[i]); } - + default: free(root); } @@ -62,7 +62,7 @@ static void item32jsonFile (FILE* file, item_root* item) char c; unsigned u, ti; static unsigned tabs = 0; - + #define NEW_LINE {fprintf (file, "\n"); for (ti=0; tiitem_type) @@ -83,7 +83,7 @@ static void item32jsonFile (FILE* file, item_root* item) NEW_LINE fprintf (file, "}"); break; - + case ITEM_ARRAY: jc = (item_container*)item; fprintf (file, "["); @@ -98,23 +98,23 @@ static void item32jsonFile (FILE* file, item_root* item) NEW_LINE fprintf(file, "]"); break; - + case ITEM_NIL: fprintf (file, "null"); break; - + case ITEM_TRUE: fprintf (file, "true"); break; - + case ITEM_FALSE: fprintf (file, "false"); break; - + case ITEM_INTEGER: fprintf (file, "%lld", ((item_integer*)item)->value); break; - + case ITEM_REAL: sprintf (tmp, "%-25.15g", ((item_real*)item)->value); for (i=0;i<30;i++) @@ -129,7 +129,7 @@ static void item32jsonFile (FILE* file, item_root* item) } fprintf (file, "%s", tmp); break; - + case ITEM_STRING: fprintf (file, "\""); cp = ((item_string*)item)->string; @@ -161,7 +161,7 @@ static void item32jsonFile (FILE* file, item_root* item) case 0x0a: fprintf (file, "\\n"); break; /* LF */ case 0x0c: fprintf (file, "\\f"); break; /* FF */ case 0x0d: fprintf (file, "\\r"); break; /* CR */ - + default: fprintf (file, "%c", c); break; @@ -169,7 +169,7 @@ static void item32jsonFile (FILE* file, item_root* item) } fprintf (file, "\""); break; - + default: break; } } @@ -299,7 +299,7 @@ static item_root* jsonString2item3 (const char** ptr) { scanSpace; item_root* result = NULL; - + char c = *(*ptr)++; switch (c) { case '{': @@ -309,7 +309,7 @@ static item_root* jsonString2item3 (const char** ptr) else result = (item_root*)pullMapPair (ptr, 0); break; - + case '[': scanSpace; if (**ptr == ']') @@ -317,12 +317,12 @@ static item_root* jsonString2item3 (const char** ptr) else result = (item_root*)pullArray (ptr, 0); break; - + case '"': result = (item_root*)pullString (ptr, 0);break; case 'n': result = allocate_item(item_root,ITEM_NIL,0); *ptr+=3;break; case 't': result = allocate_item(item_root,ITEM_TRUE,0); *ptr+=3;break; case 'f': result = allocate_item(item_root,ITEM_FALSE,0); *ptr+=4;break; - + case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { @@ -355,11 +355,11 @@ static item_root* jsonString2item3 (const char** ptr) } } break; - + default: break; } - + return result; } @@ -369,7 +369,7 @@ item_root* jsonFile2item3 (FILE* file) fseek (file, 0, SEEK_END); long length = ftell(file); char* buffer = malloc (length+1); - + fseek (file, 0l, SEEK_SET); fread (buffer, 1, length, file); buffer[length] = 0; @@ -387,7 +387,7 @@ static void item32packContext(cw_pack_context* pc, item_root* item) int i; item_container* ic; char* cp; - + switch (item->item_type) { case ITEM_MAP: @@ -396,39 +396,39 @@ static void item32packContext(cw_pack_context* pc, item_root* item) for( i=0; i< ic->count; i++) item32packContext (pc, ic->items[i]); break; - + case ITEM_ARRAY: ic = (item_container*)item; cw_pack_array_size(pc, ic->count); for( i=0; i< ic->count; i++) item32packContext (pc, ic->items[i]); break; - + case ITEM_NIL: cw_pack_nil (pc); break; - + case ITEM_TRUE: cw_pack_boolean(pc, true); break; - + case ITEM_FALSE: cw_pack_boolean(pc, false); break; - + case ITEM_INTEGER: cw_pack_signed(pc, ((item_integer*)item)->value); break; - + case ITEM_REAL: cw_pack_double(pc, ((item_real*)item)->value); break; - + case ITEM_STRING: cp = ((item_string*)item)->string; cw_pack_str(pc, cp, (unsigned)strlen(cp)); break; - + default: break; } } @@ -457,7 +457,7 @@ static item_root* packContext2item3 (cw_unpack_context* uc) case CWP_ITEM_NIL: result = allocate_item(item_root,ITEM_NIL,0); break; - + case CWP_ITEM_BOOLEAN: if (uc->item.as.boolean) { @@ -474,23 +474,23 @@ static item_root* packContext2item3 (cw_unpack_context* uc) result = (item_root*)allocate_item(item_integer,ITEM_INTEGER,0); ((item_integer*)result)->value = uc->item.as.i64; break; - + case CWP_ITEM_FLOAT: result = (item_root*)allocate_item(item_real,ITEM_REAL,0); ((item_real*)result)->value = uc->item.as.real; break; - + case CWP_ITEM_DOUBLE: result = (item_root*)allocate_item(item_real,ITEM_REAL,0); ((item_real*)result)->value = uc->item.as.long_real; break; - + case CWP_ITEM_STR: result = (item_root*)allocate_item(item_string,ITEM_STRING,uc->item.as.str.length + 1); strncpy(((item_string*)result)->string, (const char*)uc->item.as.str.start, uc->item.as.str.length); ((item_string*)result)->string[uc->item.as.str.length] = 0; break; - + case CWP_ITEM_MAP: dim = 2 * uc->item.as.map.size; ic = allocate_container(ITEM_MAP, dim); @@ -500,7 +500,7 @@ static item_root* packContext2item3 (cw_unpack_context* uc) } result = (item_root*)ic; break; - + case CWP_ITEM_ARRAY: dim = uc->item.as.array.size; ic = allocate_container(ITEM_ARRAY, dim); @@ -510,7 +510,7 @@ static item_root* packContext2item3 (cw_unpack_context* uc) } result = (item_root*)ic; break; - + default: result = NULL; break; diff --git a/example/item.h b/example/item.h index 03268d7..ecaf7d4 100644 --- a/example/item.h +++ b/example/item.h @@ -1,18 +1,18 @@ /* CWPack/example - item.h */ /* The MIT License (MIT) - + Copyright (c) 2017 Claes Wihlborg - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, @@ -26,9 +26,9 @@ - + /************** ITEMS **********************/ - + typedef enum { ITEM_MAP, ITEM_ARRAY, @@ -39,47 +39,47 @@ ITEM_REAL, ITEM_STRING } item_types; - + typedef struct { item_types item_type; } item_root; - + typedef struct { item_types item_type; int count; /* in maps every association counts for 2 */ item_root* items[]; } item_container; - + typedef struct { item_types item_type; long long value; } item_integer; - + typedef struct { item_types item_type; double value; } item_real; - + typedef struct { item_types item_type; char string[]; } item_string; - - + + void freeItem3 (item_root* root); - - - + + + /************** ITEMS TO/FROM FILE **********************/ - + void item32JsonFile (FILE* file, item_root* item); - + item_root* jsonFile2item3 (FILE* file); - + void item32cwpackFile (FILE* file, item_root* item); - + item_root* cwpackFile2item3 (FILE* file); - - - + + + #endif /* item_h */ diff --git a/example/json2cwpack2json.c b/example/json2cwpack2json.c index c455dcb..d51aaac 100644 --- a/example/json2cwpack2json.c +++ b/example/json2cwpack2json.c @@ -1,18 +1,18 @@ /* CWPack/example - json2cwpack2json.c */ /* The MIT License (MIT) - + Copyright (c) 2017 Claes Wihlborg - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, @@ -37,22 +37,22 @@ int main(int argc, const char * argv[]) } char filename[200]; strcpy(filename, argv[1]); - + FILE* jsonFileIn; FILE* jsonFileOut; FILE* cwpackFileIn; FILE* cwpackFileOut; - + jsonFileIn = fopen (filename, "r"); item_root* root = jsonFile2item3 (jsonFileIn); fclose(jsonFileIn); - + strcat (filename, ".msgpack"); cwpackFileOut = fopen (filename, "w"); item32cwpackFile (cwpackFileOut, root); fclose(cwpackFileOut); freeItem3(root); - + cwpackFileIn = fopen (filename, "r"); root = cwpackFile2item3 (cwpackFileIn); fclose(cwpackFileIn); @@ -62,6 +62,6 @@ int main(int argc, const char * argv[]) item32JsonFile (jsonFileOut, root); fclose(jsonFileOut); freeItem3(root); - + return 0; } diff --git a/goodies/README.md b/goodies/README.md new file mode 100755 index 0000000..b6cc9e9 --- /dev/null +++ b/goodies/README.md @@ -0,0 +1,15 @@ +# CWPack / Goodies + + +Goodies contains the following: + +**basic_contexts** has contexts for dynamic memory contexts and a set of file contexts. + +**dump** presents a msgpack file in human readable form. + +**numeric_extensions** use when your Ext data is integer or real. + +**objC** Objective-C wrapper. + +**utils** convenience calls and expect api for CWPack. + diff --git a/goodies/basic-contexts/README.md b/goodies/basic-contexts/README.md index d32f6bd..7ae436d 100755 --- a/goodies/basic-contexts/README.md +++ b/goodies/basic-contexts/README.md @@ -1,7 +1,7 @@ -#CWPack - Goodies - Basic Contexts +# CWPack / Goodies / Basic Contexts -Basic contexts contains 5 contexts: +Basic contexts contains 5 contexts that meet most demands: - **Dynamic Memory Pack Context** is used when you want to pack to a malloc´d memory buffer. At buffer overflow the context handler tries to reallocate the buffer to a larger size. @@ -9,10 +9,8 @@ Basic contexts contains 5 contexts: - **Stream Unpack Context** is used when you unpack from a C stream. As with Stream Pack Context, the handler asserts that an item will always fit in the buffer. -- **File Pack Context** is used when you pack to a file descriptor. At buffer overflow the context handler writes the buffer out and then reuses it. However, if the barrier is active, the subsequent content is kept in buffer. If an item is larger than the buffer, the handler tries to reallocate the buffer so the item would fit. +- **File Pack Context** is used when you pack to a file descriptor. At buffer overflow the context handler writes the buffer out and then reuses it. However, if the barrier is active, the subsequent content is kept in the buffer. If an item is larger than the buffer, the handler tries to reallocate the buffer so the item would fit. - **File Unpack Context** is used when you unpack from a file descriptor. If the barrier is active, the subsequent content is always kept in buffer. The handler asserts that an item will always fit in the buffer. -With the stream/file contexts, it is assumed that the stream/file has been opened before the -context is initialized. Before a packed stream/file is closed, the corresponding terminate context -should be called so the last buffer is saved. +With the stream/file contexts, it is assumed that the stream/file has been opened before the context is initialized. Before a packed stream/file is closed, the corresponding terminate context should be called so the last buffer is saved. diff --git a/goodies/basic-contexts/basic_contexts.c b/goodies/basic-contexts/basic_contexts.c index de1135c..f37b6d7 100644 --- a/goodies/basic-contexts/basic_contexts.c +++ b/goodies/basic-contexts/basic_contexts.c @@ -1,18 +1,18 @@ -/* CWPack/example - basic_contexts.c */ +/* CWPack/goodies - basic_contexts.c */ /* The MIT License (MIT) - + Copyright (c) 2017 Claes Wihlborg - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, @@ -31,7 +31,7 @@ -/***************************************** MEMORY PACK CONTEXT ********************************/ +/***************************************** DYNAMIC MEMORY PACK CONTEXT ********************************/ static int handle_memory_pack_overflow(struct cw_pack_context* pc, unsigned long more) @@ -44,7 +44,7 @@ static int handle_memory_pack_overflow(struct cw_pack_context* pc, unsigned long void *new_buffer = realloc (pc->start, buffer_length); if (!new_buffer) return CWP_RC_BUFFER_OVERFLOW; - + pc->start = (uint8_t*)new_buffer; pc->current = pc->start + contains; pc->end = pc->start + buffer_length; @@ -61,7 +61,7 @@ void init_dynamic_memory_pack_context (dynamic_memory_pack_context* dmpc, unsign dmpc->pc.return_code = CWP_RC_MALLOC_ERROR; return; } - + cw_pack_context_init((cw_pack_context*)dmpc, buffer, buffer_length, &handle_memory_pack_overflow); } @@ -78,15 +78,16 @@ void free_dynamic_memory_pack_context(dynamic_memory_pack_context* dmpc) -static int flush_stream_pack_context(stream_pack_context* spc) +static int flush_stream_pack_context(struct cw_pack_context* pc) { - unsigned long contains = (unsigned long)(spc->pc.current - spc->pc.start); + stream_pack_context* spc = (stream_pack_context*)pc; + unsigned long contains = (unsigned long)(pc->current - pc->start); if (contains) { - unsigned long rc = fwrite(spc->pc.start, contains, 1, spc->file); + unsigned long rc = fwrite(pc->start, contains, 1, spc->file); if (rc != 1) { - spc->pc.err_no = ferror(spc->file); + pc->err_no = ferror(spc->file); return CWP_RC_ERROR_IN_HANDLER; } } @@ -96,7 +97,7 @@ static int flush_stream_pack_context(stream_pack_context* spc) static int handle_stream_pack_overflow(struct cw_pack_context* pc, unsigned long more) { - int rc = flush_stream_pack_context((stream_pack_context*)pc); + int rc = flush_stream_pack_context(pc); if (rc != CWP_RC_OK) return rc; @@ -105,11 +106,11 @@ static int handle_stream_pack_overflow(struct cw_pack_context* pc, unsigned long { while (buffer_length < more) buffer_length = 2 * buffer_length; - + void *new_buffer = malloc (buffer_length); if (!new_buffer) return CWP_RC_BUFFER_OVERFLOW; - + free(pc->start); pc->start = (uint8_t*)new_buffer; pc->end = pc->start + buffer_length; @@ -129,18 +130,19 @@ void init_stream_pack_context (stream_pack_context* spc, unsigned long initial_b return; } spc->file = file; - + cw_pack_context_init((cw_pack_context*)spc, buffer, buffer_length, &handle_stream_pack_overflow); + cw_pack_set_flush_handler((cw_pack_context*)spc, &flush_stream_pack_context); } void terminate_stream_pack_context(stream_pack_context* spc) { - if (spc->pc.return_code == CWP_RC_OK) - spc->pc.return_code = flush_stream_pack_context(spc); + cw_pack_context* pc = (cw_pack_context*)spc; + cw_pack_flush(pc); - if (spc->pc.return_code != CWP_RC_MALLOC_ERROR) - free(spc->pc.start); + if (pc->return_code != CWP_RC_MALLOC_ERROR) + free(pc->start); } @@ -156,16 +158,16 @@ static int handle_stream_unpack_underflow(struct cw_unpack_context* uc, unsigned { memmove (uc->start, uc->current, remains); } - + if (suc->buffer_length < more) { while (suc->buffer_length < more) suc->buffer_length = 2 * suc->buffer_length; - + void *new_buffer = realloc (uc->start, suc->buffer_length); if (!new_buffer) return CWP_RC_BUFFER_UNDERFLOW; - + uc->start = (uint8_t*)new_buffer; } uc->current = uc->start; @@ -178,7 +180,7 @@ static int handle_stream_unpack_underflow(struct cw_unpack_context* uc, unsigned suc->uc.err_no = ferror(suc->file); return CWP_RC_ERROR_IN_HANDLER; } - + uc->end += l; return CWP_RC_OK; @@ -196,7 +198,7 @@ void init_stream_unpack_context (stream_unpack_context* suc, unsigned long initi } suc->file = file; suc->buffer_length = buffer_length; - + cw_unpack_context_init((cw_unpack_context*)suc, buffer, 0, &handle_stream_unpack_underflow); } @@ -212,49 +214,50 @@ void terminate_stream_unpack_context(stream_unpack_context* suc) /***************************************** FILE PACK CONTEXT **********************************/ -static int flush_file_pack_context(file_pack_context* fpc) +static int flush_file_pack_context(struct cw_pack_context* pc) { - uint8_t *bStart = fpc->barrier ? fpc->barrier : fpc->pc.current; - unsigned long contains = (unsigned long)(bStart - fpc->pc.start); + file_pack_context* fpc = (file_pack_context*)pc; + uint8_t *bStart = fpc->barrier ? fpc->barrier : pc->current; + unsigned long contains = (unsigned long)(bStart - pc->start); if (contains) { - long rc = write (fpc->fileDescriptor, fpc->pc.start, contains); - if (rc != contains) + long rc = write (fpc->fileDescriptor, pc->start, contains); + if (rc != (long)contains) { - fpc->pc.err_no = errno; + pc->err_no = errno; return CWP_RC_ERROR_IN_HANDLER; } } if (fpc->barrier) { - long kept = fpc->pc.current - bStart; + long kept = pc->current - bStart; if (kept) { - memcpy(fpc->pc.start, bStart, kept); + memcpy(pc->start, bStart, kept); } - fpc->barrier = fpc->pc.start; - fpc->pc.current = fpc->pc.start + kept; + fpc->barrier = pc->start; + pc->current = pc->start + kept; } else fpc->pc.current = fpc->pc.start; - + return CWP_RC_OK; } static int handle_file_pack_overflow(struct cw_pack_context* pc, unsigned long more) { file_pack_context* fpc = (file_pack_context*)pc; - int rc = flush_file_pack_context(fpc); + int rc = flush_file_pack_context(pc); if (rc != CWP_RC_OK) return rc; - - uint8_t *bStart = fpc->barrier ? fpc->barrier : fpc->pc.current; - long kept = pc->current - bStart; + + uint8_t *bStart = fpc->barrier ? fpc->barrier : pc->current; + unsigned long kept = (unsigned long)(pc->current - bStart); unsigned long buffer_length = (unsigned long)(pc->end - pc->start); if (buffer_length < more + kept) { while (buffer_length < more + kept) buffer_length = 2 * buffer_length; - + void *new_buffer = malloc (buffer_length); if (!new_buffer) return CWP_RC_BUFFER_OVERFLOW; @@ -268,12 +271,12 @@ static int handle_file_pack_overflow(struct cw_pack_context* pc, unsigned long m { memcpy(pc->start, bStart, kept); } - + if (fpc->barrier) { fpc->barrier = pc->start; } - + pc->current = pc->start + kept; return CWP_RC_OK; } @@ -281,18 +284,19 @@ static int handle_file_pack_overflow(struct cw_pack_context* pc, unsigned long m void init_file_pack_context (file_pack_context* fpc, unsigned long initial_buffer_length, int fileDescriptor) { - unsigned long buffer_length = (initial_buffer_length > 0 ? initial_buffer_length : 4096); + unsigned long buffer_length = (initial_buffer_length > 32 ? initial_buffer_length : 4096); void *buffer = malloc (buffer_length); if (!buffer) { fpc->pc.return_code = CWP_RC_MALLOC_ERROR; return; } - fpc->fileDescriptor = fileDescriptor; + fpc->fileDescriptor = fileDescriptor; fpc->barrier = NULL; - + cw_pack_context_init((cw_pack_context*)fpc, buffer, buffer_length, &handle_file_pack_overflow); + cw_pack_set_flush_handler((cw_pack_context*)fpc, &flush_file_pack_context); } @@ -311,11 +315,11 @@ void file_pack_context_release_barrier (file_pack_context* fpc) void terminate_file_pack_context(file_pack_context* fpc) { fpc->barrier = NULL; - if (fpc->pc.return_code == CWP_RC_OK) - fpc->pc.return_code = flush_file_pack_context(fpc); - - if (fpc->pc.return_code != CWP_RC_MALLOC_ERROR) - free(fpc->pc.start); + cw_pack_context* pc = (cw_pack_context*)fpc; + cw_pack_flush(pc); + + if (pc->return_code != CWP_RC_MALLOC_ERROR) + free(pc->start); } @@ -327,30 +331,32 @@ static int handle_file_unpack_underflow(struct cw_unpack_context* uc, unsigned l { file_unpack_context* auc = (file_unpack_context*)uc; uint8_t *bStart = auc->barrier ? auc->barrier : uc->current; - unsigned long kept = uc->current - bStart; + unsigned long kept = (unsigned long)(uc->current - bStart); unsigned long remains = (unsigned long)(uc->end - bStart); if (remains) { memcpy (uc->start, bStart, remains); } - + if (auc->buffer_length < more + kept) { while (auc->buffer_length < more + kept) auc->buffer_length = 2 * auc->buffer_length; - + void *new_buffer = realloc (uc->start, auc->buffer_length); if (!new_buffer) return CWP_RC_BUFFER_UNDERFLOW; - + uc->start = (uint8_t*)new_buffer; } uc->current = uc->start + kept; uc->end = uc->start + remains; - - while (uc->end - uc->current < more) + if (auc->barrier) + auc->barrier = uc->start; + + while ((unsigned long)(uc->end - uc->current) < more) { - long l = read(auc->fileDescriptor, uc->end, auc->buffer_length - (uc->end - uc->start)); + long l = read(auc->fileDescriptor, uc->end, auc->buffer_length - (unsigned long)(uc->end - uc->start)); if (l == 0) { return CWP_RC_END_OF_INPUT; @@ -362,7 +368,7 @@ static int handle_file_unpack_underflow(struct cw_unpack_context* uc, unsigned l } uc->end += l; } - + return CWP_RC_OK; } @@ -379,7 +385,7 @@ void init_file_unpack_context (file_unpack_context* fuc, unsigned long initial_b fuc->fileDescriptor = fileDescriptor; fuc->barrier = NULL; fuc->buffer_length = buffer_length; - + cw_unpack_context_init((cw_unpack_context*)fuc, buffer, 0, &handle_file_unpack_underflow); } @@ -390,6 +396,11 @@ void file_unpack_context_set_barrier (file_unpack_context* fuc) } +void file_unpack_context_rescan_from_barrier (file_unpack_context* fuc) +{ + fuc->uc.current = fuc->barrier; +} + void file_unpack_context_release_barrier (file_unpack_context* fuc) { fuc->barrier = NULL; @@ -400,6 +411,7 @@ void terminate_file_unpack_context(file_unpack_context* fuc) { if (fuc->uc.return_code != CWP_RC_MALLOC_ERROR) free(fuc->uc.start); + fuc->uc.start = 0; } diff --git a/goodies/basic-contexts/basic_contexts.h b/goodies/basic-contexts/basic_contexts.h index 99b7718..ead648b 100644 --- a/goodies/basic-contexts/basic_contexts.h +++ b/goodies/basic-contexts/basic_contexts.h @@ -1,18 +1,18 @@ -/* CWPack/example - basic_contexts.h */ +/* CWPack/goodies - basic_contexts.h */ /* The MIT License (MIT) - + Copyright (c) 2017 Claes Wihlborg - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, @@ -105,6 +105,7 @@ typedef struct void init_file_unpack_context (file_unpack_context* suc, unsigned long initial_buffer_length, int fileDescriptor); void file_unpack_context_set_barrier (file_unpack_context* suc); +void file_unpack_context_rescan_from_barrier (file_unpack_context* suc); void file_unpack_context_release_barrier (file_unpack_context* suc); void terminate_file_unpack_context(file_unpack_context* suc); diff --git a/goodies/dump/README.md b/goodies/dump/README.md new file mode 100755 index 0000000..69990f1 --- /dev/null +++ b/goodies/dump/README.md @@ -0,0 +1,53 @@ +# CWPack / Goodies / Dump + + +Dump is a small program taking a msgpack file as input and produces a human readable file as output. The output is not json as msgpack is more feature-rich. + +Syntax: +cwpack_dump [-t 9] [-v][-r] [-h] < msgpackFile > humanReadableFile +-t 9 Tab size +-v Version +-r Recognize records +-h Help + +Each topmost msgpack item in the file starts on a new line. Each line starts with a file offset (hex) of the first item on the line. +If Tab size isn't given, structures are written on a single line. + + +`cwpack_dump < testdump.msgpack` prints: + +``` + 0 [10000000 3.14 "åäöÅÄÖ"] + 1c {"binary": <62696e617279> "extension": (-5,<68656c6c6f>) "time": '2020-05-20 18:40:00'} + 49 +``` +and `cwpack_dump -t 4 < testdump.msgpack` prints: + +``` + 0 [ + 1 10000000 + 6 3.14 + f "åäöÅÄÖ" + 1c ] + 1c { + 1d "binary": <62696e617279> + 2c "extension": (-5,<68656c6c6f>) + 3e "time": '2020-05-20 18:40:00' + 49 } + 49 +``` +The -r option makes dump recognize Objective-C objects. `cwpack_dump < testdump2.msgpack` prints: + +``` + 0 [(127,) [[(127,)]]] + 9 [(127,<01>) "MyClass" 10 [(127,<02>) "MyClass" 20 [(127,<01>)]]] + 27 +``` +and `cwpack_dump -r < testdump2.msgpack` prints + +``` + 0 -1->[->-1] + 9 1->MyClass(10 2->MyClass(20 ->1)) + 27 +``` + diff --git a/goodies/dump/cwpack_dump.c b/goodies/dump/cwpack_dump.c new file mode 100755 index 0000000..d57ad8d --- /dev/null +++ b/goodies/dump/cwpack_dump.c @@ -0,0 +1,302 @@ + +/* CWPack/goodies/dump - cwpack_dump.c */ + +/* + The MIT License (MIT) + + Copyright (c) 2017 Claes Wihlborg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include +#include +#include +#include "basic_contexts.h" +#include "numeric_extensions.h" + +char tabString[21] = " "; +bool recognizeObjects = false; + +#define NEW_LINE(tablevel) {printf ("\n%6x ",(unsigned)(context->current - context->start)); for (ti=0; ti"); +} +static void dump_item( cw_unpack_context* context, int tabLevel); + +static void dump_next_item( cw_unpack_context* context, int tabLevel) +{ + cw_unpack_next (context); + if (context->return_code) return; + dump_item (context, tabLevel); +} + +static void dump_item( cw_unpack_context* context, int tabLevel) +{ + long long dim =99; + int i,ti; + double d; + struct tm tm; + char s[128]; + + switch (context->item.type) + { + case CWP_ITEM_NIL: + printf("nil"); + break; + + case CWP_ITEM_BOOLEAN: + if (context->item.as.boolean) + printf("YES"); + else + printf("NO"); + break; + + case CWP_ITEM_POSITIVE_INTEGER: + printf("%llu", context->item.as.u64); + break; + + case CWP_ITEM_NEGATIVE_INTEGER: + printf("%lld", context->item.as.i64); + break; + + case CWP_ITEM_FLOAT: + context->item.as.long_real = (double)context->item.as.real; + + case CWP_ITEM_DOUBLE: + printf ("%g", context->item.as.long_real); + break; + + case CWP_ITEM_STR: { + printf("\""); + + for (i=0; i < (int)context->item.as.str.length; i++) + { + unsigned char c = ((unsigned char*)(context->item.as.str.start))[i]; + switch (c) + { + case '"': printf("\\\""); break; + case '\\': printf("\\\\"); break; + case '\b': printf("\\b"); break; + case '\f': printf("\\f"); break; + case '\n': printf("\\n"); break; + case '\r': printf("\\r"); break; + case '\t': printf("\\t"); break; + + default: + if (c < ' ') + printf("\\u%04x",c); + else + printf("%c",c); + break; + } + } + + printf("\""); + break;} + + case CWP_ITEM_BIN: + dump_as_hex (context->item.as.bin.start, context->item.as.bin.length); + break; + + case CWP_ITEM_ARRAY: + { + dim = context->item.as.array.size; + if (!dim) + { + printf("[]"); + break; + } + + cw_unpack_next (context); + if (context->return_code) break; + + if (recognizeObjects && (context->item.type == 127)) + { + long label = get_ext_integer(context); + bool userObject = label >= 0; + if (dim == 1) /* reference */ + { + printf("->%ld",label); + break; + } + if (label) + printf("%ld->",label); + if (!userObject) + { + if (dim != 2) + { + context->return_code = CWP_RC_MALFORMED_INPUT; + break; + } + dump_next_item(context,tabLevel); + break; + } + cw_unpack_next (context); + if (context->return_code) break; + if (context->item.type != CWP_ITEM_STR) + { + context->return_code = CWP_RC_MALFORMED_INPUT; + break; + } + printf("%.*s(",context->item.as.str.length, context->item.as.str.start); + tabLevel++; + for (i = 0; i < dim-2; i++) + { + CHECK_NEW_LINE; + dump_next_item(context,tabLevel); + } + tabLevel--; + if(*tabString) NEW_LINE(tabLevel); + printf(")"); + } + else + { + printf("["); + tabLevel++; + i = 0; + CHECK_NEW_LINE; + dump_item(context,tabLevel); + for (i = 1; i < dim; i++) + { + CHECK_NEW_LINE; + dump_next_item(context,tabLevel); + } + tabLevel--; + if(*tabString) NEW_LINE(tabLevel); + printf("]"); + } + break; + } + + case CWP_ITEM_MAP: + printf("{"); + dim = context->item.as.map.size; + tabLevel++; + for (i = 0; i < dim; i++) + { + CHECK_NEW_LINE; + dump_next_item(context,tabLevel); + printf(":"); + dump_next_item(context,tabLevel); + } + tabLevel--; + if(*tabString) NEW_LINE(tabLevel); + printf("}"); + break; + + case CWP_ITEM_TIMESTAMP: + printf("'"); + time_t tv_sec = context->item.as.time.tv_sec; + gmtime_r(&tv_sec,&tm); + strftime(s,128,"%F %T", &tm); + printf("%s",s); + if (context->item.as.time.tv_nsec) + { + d = (double)(context->item.as.time.tv_nsec) / 1000000000; + sprintf(s,"%f",d); + printf("%s",s+1); + } + printf("'"); + break; + + default: + if (CWP_ITEM_MIN_RESERVED_EXT <= context->item.type && context->item.type <= CWP_ITEM_MAX_USER_EXT) + { + printf("(%d,",context->item.type); + dump_as_hex (context->item.as.ext.start, context->item.as.ext.length); + printf(")"); + } + else + printf("????? type = %d", context->item.type ); + break; + } +} + + +/******************************* M A I N ******************************/ + + +int main(int argc, const char * argv[]) +{ + int i; + int t = 0; + for (i = 1; i < argc; i++) + { + if (!strcmp(argv[i],"-t") && (i++ < argc)) + { + t = *argv[i] - '0'; + if (strlen(argv[i]) != 1 || t < 1 || t > 9) { + printf("Tab size must be between 1 and 9\n"); + exit(0); + } + } + else if (!strcmp(argv[i],"-v") || !strcmp(argv[i],"--version")) + { + printf("cwpack_dump version = 1.0\n"); + exit(0); + } + else if (!strcmp(argv[i],"-r")) + { + recognizeObjects = true; + } + else + { + printf("cwpack_dump [-t 9] [-r] [-v] [-h]\n"); + printf("-h Help\n"); + printf("-r Recognize records\n"); + printf("-t 9 Tab size\n"); + printf("-v Version\n"); + printf("\nIf Tab size isn't given, structures are written on a single line\n"); + printf("\nInput is taken from stdin and output is written to stdout\n"); + exit(0); + } + } + tabString[t] = 0; + + file_unpack_context fuc; + cw_unpack_context *context = (cw_unpack_context*)&fuc; + + init_file_unpack_context (&fuc, 4096, STDIN_FILENO); + file_unpack_context_set_barrier (&fuc); /* keep whole file in memory buffer to simplify offset calculation */ + + while (!context->return_code) + { + int ti; + NEW_LINE(0); + dump_next_item(context,0); + } + printf("\n"); + if (context->return_code != CWP_RC_END_OF_INPUT) + printf("\nERROR RC = %d\n",context->return_code); + + terminate_file_unpack_context (&fuc); +} + diff --git a/goodies/dump/runCWpack_dump.sh b/goodies/dump/runCWpack_dump.sh new file mode 100755 index 0000000..d9fc8fc --- /dev/null +++ b/goodies/dump/runCWpack_dump.sh @@ -0,0 +1,3 @@ +clang -ansi -I ../../src/ -I ../basic-contexts/ -I ../numeric-extensions/ -o cwpack_dump *.c ../../src/cwpack.c ../basic-contexts/*.c ../numeric-extensions/numeric_extensions.c +./cwpack_dump < testdump.msgpack +./cwpack_dump -t 4 < testdump.msgpack diff --git a/goodies/dump/testdump.msgpack b/goodies/dump/testdump.msgpack new file mode 100644 index 0000000..0ddfac0 Binary files /dev/null and b/goodies/dump/testdump.msgpack differ diff --git a/goodies/dump/testdump2.msgpack b/goodies/dump/testdump2.msgpack new file mode 100644 index 0000000..6e565bc --- /dev/null +++ b/goodies/dump/testdump2.msgpack @@ -0,0 +1,2 @@ +’Ôÿ‘‘Ôÿ”Ô§MyClass +”Ô§MyClass‘Ô \ No newline at end of file diff --git a/goodies/numeric-extensions/README.md b/goodies/numeric-extensions/README.md index 4894f9e..3772d48 100755 --- a/goodies/numeric-extensions/README.md +++ b/goodies/numeric-extensions/README.md @@ -1,11 +1,11 @@ -#CWPack - Goodies - Numeric Extensions +# CWPack / Goodies / Numeric Extensions -Numeric Extensions solves the byte order problem when the value of an extension item is a numeric entity. It uses the same byte order as `cw_pack_signed` and `cw_pack_real`. +Numeric Extensions solves the byte order problem when the value of an extension item is a numeric entity. It uses the same byte order as `cw_pack_signed` and `cw_pack_double`. ## Pack -```C +``` void cw_pack_ext_integer (cw_pack_context* pack_context, int8_t type, int64_t i); void cw_pack_ext_float (cw_pack_context* pack_context, int8_t type, float f); void cw_pack_ext_double (cw_pack_context* pack_context, int8_t type, double d); @@ -13,9 +13,9 @@ void cw_pack_ext_double (cw_pack_context* pack_context, int8_t type, double d); ## Unpack -```C -int get_ext_integer (cw_unpack_context* unpack_context, int64_t* value); -int get_ext_float (cw_unpack_context* unpack_context, float* value); -int get_ext_double (cw_unpack_context* unpack_context, double* value); +``` +int64_t get_ext_integer (cw_unpack_context* unpack_context); +float get_ext_float (cw_unpack_context* unpack_context); +double get_ext_double (cw_unpack_context* unpack_context); ``` The `get_ext_...` functions assume that the user first has done a successful `cw_unpack_next` call. They return error if the unpacked item isn't an ext item or if the item has wrong length. diff --git a/goodies/numeric-extensions/numeric_extensions.c b/goodies/numeric-extensions/numeric_extensions.c index 8741bd8..17af768 100644 --- a/goodies/numeric-extensions/numeric_extensions.c +++ b/goodies/numeric-extensions/numeric_extensions.c @@ -1,14 +1,27 @@ -// -// numeric_extensions.c -// CWPack -// -// Created by Claes Wihlborg on 2017-01-16. -// Copyright © 2017 Claes Wihlborg. All rights reserved. -// +/* CWPack/goodies - numeric_extensions.c */ +/* + The MIT License (MIT) + Copyright (c) 2017 Claes Wihlborg -#include "cwpack.h" -#include "cwpack_defines.h" + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include "cwpack_internals.h" #include "numeric_extensions.h" @@ -28,32 +41,32 @@ void cw_pack_ext_integer (cw_pack_context* pack_context, int8_t type, int64_t i) { if (pack_context->return_code) return; - + uint8_t *p; if (i >= 0) { if (i < 128) cw_storeN(3,0xd4,8); - + if (i < 32768) cw_storeN(4,0xd5,16); - + if (i < 0x80000000LL) cw_storeN(6,0xd6,32); - + cw_storeN(10,0xd7,64); } - + if (i >= -128) cw_storeN(3,0xd4,8); - + if (i >= -32768) cw_storeN(4,0xd5,16); - + if (i >= (int64_t)0xffffffff80000000LL) cw_storeN(6,0xd6,32); - + cw_storeN(10,0xd7,64); } @@ -62,13 +75,13 @@ void cw_pack_ext_float (cw_pack_context* pack_context, int8_t type, float f) { if (pack_context->return_code) return; - + uint8_t *p; - + cw_pack_reserve_space(6); *p++ = (uint8_t)0xd6; *p++ = (uint8_t)type; - + uint32_t tmp = *((uint32_t*)&f); cw_store32(tmp); } @@ -78,92 +91,109 @@ void cw_pack_ext_double (cw_pack_context* pack_context, int8_t type, double d) { if (pack_context->return_code) return; - + uint8_t *p; - + cw_pack_reserve_space(10); *p++ = (uint8_t)0xd7; *p++ = (uint8_t)type; - + uint64_t tmp = *((uint64_t*)&d); cw_store64(tmp); } -int get_ext_integer (cw_unpack_context* unpack_context, int64_t* value) +int64_t get_ext_integer (cw_unpack_context* unpack_context) { + if (unpack_context->return_code != CWP_RC_OK) + { + return 0; + } + uint16_t tmpu16; uint32_t tmpu32; uint64_t tmpu64; - + if (unpack_context->item.type > CWP_ITEM_MAX_USER_EXT) { - return NUMEXT_ERROR_NOT_EXT; + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return 0; } switch (unpack_context->item.as.ext.length) { + case 0: + return 0; + case 1: - *value = (int64_t)*unpack_context->item.as.ext.start; - break; - + return *(int8_t*)unpack_context->item.as.ext.start; + case 2: cw_load16(unpack_context->item.as.ext.start); - *value = (int16_t)tmpu16; - break; - + return (int16_t)tmpu16; + case 4: cw_load32(unpack_context->item.as.ext.start); - *value = (int32_t)tmpu32; - break; - + return (int32_t)tmpu32; + case 8: - cw_load64(unpack_context->item.as.ext.start); - *value = (int64_t)tmpu64; - break; - + cw_load64(unpack_context->item.as.ext.start,tmpu64); + return (int64_t)tmpu64; + default: - return NUMEXT_ERROR_WRONG_LENGTH; + unpack_context->return_code = CWP_RC_VALUE_ERROR; } return 0; } -int get_ext_float (cw_unpack_context* unpack_context, float* value) +float get_ext_float (cw_unpack_context* unpack_context) { + if (unpack_context->return_code != CWP_RC_OK) + { + return 0; + } + uint32_t tmpu32; if (unpack_context->item.type > CWP_ITEM_MAX_USER_EXT) { - return NUMEXT_ERROR_NOT_EXT; + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return 0.0; } - + if (unpack_context->item.as.ext.length != 4) { - return NUMEXT_ERROR_WRONG_LENGTH; + unpack_context->return_code = CWP_RC_VALUE_ERROR; + return 0.0; } - + cw_load32(unpack_context->item.as.ext.start); - *value = *(float*)&tmpu32; - return 0; + return *(float*)&tmpu32; } -int get_ext_double (cw_unpack_context* unpack_context, double* value) +double get_ext_double (cw_unpack_context* unpack_context) { + if (unpack_context->return_code != CWP_RC_OK) + { + return 0; + } + uint64_t tmpu64; if (unpack_context->item.type > CWP_ITEM_MAX_USER_EXT) { - return NUMEXT_ERROR_NOT_EXT; + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return 0.0; } - + if (unpack_context->item.as.ext.length != 8) { - return NUMEXT_ERROR_WRONG_LENGTH; + unpack_context->return_code = CWP_RC_VALUE_ERROR; + return 0.0; } - - cw_load64(unpack_context->item.as.ext.start); - *value = *(double*)&tmpu64; - return 0; + + cw_load64(unpack_context->item.as.ext.start,tmpu64); + return *(double*)&tmpu64; } diff --git a/goodies/numeric-extensions/numeric_extensions.h b/goodies/numeric-extensions/numeric_extensions.h index 9d7dbdf..6fbd1d5 100644 --- a/goodies/numeric-extensions/numeric_extensions.h +++ b/goodies/numeric-extensions/numeric_extensions.h @@ -1,10 +1,24 @@ -// -// numeric_extensions.h -// CWPack -// -// Created by Claes Wihlborg on 2017-01-16. -// Copyright © 2017 Claes Wihlborg. All rights reserved. -// +/* CWPack/goodies - numeric_extensions.h */ +/* + The MIT License (MIT) + + Copyright (c) 2017 Claes Wihlborg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ #ifndef numeric_extensions_h #define numeric_extensions_h @@ -13,21 +27,21 @@ -#define NUMEXT_ERROR_NOT_EXT 1; -#define NUMEXT_ERROR_WRONG_LENGTH 2; +#define NUMEXT_ERROR_NOT_EXT CWP_RC_TYPE_ERROR; +#define NUMEXT_ERROR_WRONG_LENGTH CWP_RC_VALUE_ERROR; + + + - - - void cw_pack_ext_integer (cw_pack_context* pack_context, int8_t type, int64_t i); void cw_pack_ext_float (cw_pack_context* pack_context, int8_t type, float f); void cw_pack_ext_double (cw_pack_context* pack_context, int8_t type, double d); - - int get_ext_integer (cw_unpack_context* unpack_context, int64_t* value); - int get_ext_float (cw_unpack_context* unpack_context, float* value); - int get_ext_double (cw_unpack_context* unpack_context, double* value); - + int64_t get_ext_integer (cw_unpack_context* unpack_context); + float get_ext_float (cw_unpack_context* unpack_context); + double get_ext_double (cw_unpack_context* unpack_context); + + #endif /* numeric_extensions_h */ diff --git a/goodies/numeric-extensions/numeric_extensions_test.c b/goodies/numeric-extensions/numeric_extensions_test.c index 27ffa44..0eb28c0 100644 --- a/goodies/numeric-extensions/numeric_extensions_test.c +++ b/goodies/numeric-extensions/numeric_extensions_test.c @@ -1,17 +1,32 @@ -// -// numeric_extensions_test.c -// CWPack -// -// Created by Claes Wihlborg on 2017-01-17. -// Copyright © 2017 Claes Wihlborg. All rights reserved. -// +/* CWPack/goodies - numeric_extensions_test.c */ +/* + The MIT License (MIT) + + Copyright (c) 2017 Claes Wihlborg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + #include #include #include #include "cwpack.h" -#include "cwpack_defines.h" +#include "cwpack_internals.h" #include "numeric_extensions.h" @@ -44,7 +59,7 @@ static char char2hex (char c) return c - '0'; if (c <= 'F') return c - 'A' + 10; - + return c - 'a' + 10; } @@ -55,7 +70,7 @@ static void check_pack_result(const char* expected_header, unsigned long data_le unsigned long header_length = strlen(expected_header) / 2; if (pack_ctx.current - outbuffer == (long)(header_length + data_length)) { - + if (header_length*2 == strlen(expected_header)) { unsigned long i; @@ -87,14 +102,14 @@ static void check_pack_result(const char* expected_header, unsigned long data_le hex <<= 4; hex += (uc - 'A' + 10); } - - + + if (*p++ != hex) { ERROR("Different header value"); } } - + if (data_length > 0) { ucp = TEST_area; @@ -118,19 +133,18 @@ int main(int argc, const char * argv[]) { printf("CWPack numeric extensions test started.\n"); error_count = 0; - int i; - + bool endian_switch_found = false; #ifdef COMPILE_FOR_BIG_ENDIAN printf("Compiled for big endian.\n\n"); endian_switch_found = true; #endif - + #ifdef COMPILE_FOR_LITTLE_ENDIAN printf("Compiled for little endian.\n\n"); endian_switch_found = true; #endif - + if (!endian_switch_found) { printf("Compiled for all endians.\n"); @@ -140,51 +154,51 @@ int main(int argc, const char * argv[]) case 0x31323334UL: printf("Running on big endian hardware.\n\n"); break; - + case 0x34333231UL: printf("Running on little endian hardware.\n\n"); break; - + default: printf("Running on neither little nor big endian hardware.\n\n"); break; } } - - + + //******************* TEST numeric extensions **************************** - + cw_pack_context_init (&pack_ctx, outbuffer, 70000, 0); if (pack_ctx.return_code == CWP_RC_WRONG_BYTE_ORDER) { ERROR("***** Compiled for wrong byte order, test terminated *****\n\n"); exit(1); } - - - - + + + + #define TESTP_EXT(call,type,value,header) \ pack_ctx.current = outbuffer; \ cw_pack_ext_##call (&pack_ctx, type, value); \ if(pack_ctx.return_code) \ ERROR("In pack"); \ check_pack_result(header, 0) - + // TESTP ext TESTP_EXT(integer,15,1,"d40f01"); TESTP_EXT(integer,15,128,"d50f0080"); TESTP_EXT(integer,15,-32769,"d60fffff7fff"); - + float f1 = (float)3.14; TESTP_EXT(float,15,f1,"d60f4048f5c3"); TESTP_EXT(double,15,f1,"d70f40091eb860000000"); - + int64_t integer_var; float float_var; double double_var; char inputbuf[30]; - + #define TESTUP_EXT(buffer,etype,call,value) \ { \ unsigned long ui; \ @@ -197,35 +211,36 @@ int main(int argc, const char * argv[]) ERROR1("In unpack_next, rc = ",unpack_ctx.return_code); \ if (unpack_ctx.item.type != etype) \ ERROR("In unpack, type error"); \ - if ((i=get_ext_##call (&unpack_ctx, &call##_var))) \ - ERROR1("In get_ext, rc = ",i); \ + call##_var = get_ext_##call (&unpack_ctx); \ + if (unpack_ctx.return_code) \ + ERROR1("In get_ext, rc = ",unpack_ctx.return_code); \ if (call##_var != value) \ ERROR("In unpack, value error"); \ } - + TESTUP_EXT("d40f01",15,integer,1); TESTUP_EXT("d50f0080",15,integer,128); TESTUP_EXT("d60fffff7fff",15,integer,-32769); TESTUP_EXT("d60f4048f5c3",15,float,f1); TESTUP_EXT("d70f40091eb860000000",15,double,(double)f1); //************************************************************* - + printf("CWPack numeric extensions test completed, "); switch (error_count) { case 0: printf("no errors detected\n"); break; - + case 1: printf("1 error detected\n"); break; - + default: printf("%d errors detected\n", error_count); break; } - + return error_count; } diff --git a/goodies/objC/CWPackContext.h b/goodies/objC/CWPackContext.h new file mode 100644 index 0000000..69513f1 --- /dev/null +++ b/goodies/objC/CWPackContext.h @@ -0,0 +1,97 @@ + +/* CWPack/goodies/ObjC - CWPackContext.h */ + +/* + The MIT License (MIT) + + Copyright (c) 2021 Claes Wihlborg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#import +#include "cwpack.h" + +NS_ASSUME_NONNULL_BEGIN + + + + + +@interface CWPackContext : NSObject + +@property (readonly) cw_pack_context *context; +@property (readwrite) BOOL useLabels; // default NO + ++ (instancetype) newWithContext:(cw_pack_context*)context; +- (void) packObject:(nullable NSObject*)object; + +@end + + + + + +@interface CWUnpackContext : NSObject + +@property (readonly) cw_unpack_context *context; + ++ (instancetype) newWithContext:(cw_unpack_context*)context; +- (id) unpackNextObject; + +@end + + + + +@protocol CWPackable + +@required +@property (readonly) int persistentAttributeCount; +- (void) cwPackSub:(CWPackContext*)ctx; +- (void) cwUnpackSub:(CWUnpackContext*)ctx remainingAttributes:(int)remainingAttributes; + +@optional +- (instancetype) cwUnpackSubInit:(CWUnpackContext*)ctx remainingAttributes:(int)remainingAttributes; + +@end + + + + +@interface CWPackExternalItem : NSObject + +@property (readonly) int type; +@property (readwrite, strong) NSData* data; + ++ (instancetype) itemWithType:(int)type data:(NSData*)data; + +@end + + + + +@interface CWPackGenericClass : NSObject + +@property (readwrite,strong) NSString *packerClassName; +@property (readwrite,strong) NSMutableArray *attributes; + ++ (instancetype) newWithClassName:(NSString*)className; +@end + + +NS_ASSUME_NONNULL_END diff --git a/goodies/objC/CWPackContext.m b/goodies/objC/CWPackContext.m new file mode 100644 index 0000000..a5501a9 --- /dev/null +++ b/goodies/objC/CWPackContext.m @@ -0,0 +1,694 @@ + +/* CWPack/goodies/ObjC - CWPackContext.m */ + +/* + The MIT License (MIT) + + Copyright (c) 2021 Claes Wihlborg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include "cwpack_internals.h" +#include "numeric_extensions.h" +#include "cwpack_utils.h" + +#import "CWPackContext.h" + + +#define CWP_ITEM_OBJECT_MARKER CWP_ITEM_USER_EXT_127 + + + + +@protocol CWPackPackerClasses +@required +- (void) cwPackPackerTo:(CWPackContext*)ctx; +@end + + +@interface CWUnpackContext () +- (id) cwUnpackNextObjectSub:(long)label; +@end + + +@interface NSObject (CWPackable) +- (void) cwPackTo:(CWPackContext*)ctx; +@end + + + + +#pragma mark - +#pragma mark ************************************** CWPackContext + + +@implementation CWPackContext +{ + CFMutableDictionaryRef _labelMap; + long userLabelGenerator; + long packerLabelGenerator; +} + +- (instancetype)initWithContext:(cw_pack_context*)context +{ + self = [super init]; + if (self) { + _context = context; + _labelMap = CFDictionaryCreateMutable(nil, 0, nil, nil); + } + return self; +} + + ++ (instancetype) newWithContext:(cw_pack_context*)context { + return [self.alloc initWithContext:context]; +} + + +- (long) labelForObject:(id)object { + return (long)CFDictionaryGetValue (_labelMap, (const void *)object); +} + + +- (long) assignLabelForObject:(id)object isMsgpackObject:(BOOL)isMsgpackObject +{ + long label = isMsgpackObject ? --packerLabelGenerator : ++userLabelGenerator; + CFDictionarySetValue (_labelMap, (const void *)object,(const void *)label); + return label; +} + + +- (void) packUserObject:(id)object +{ + [object cwPackTo:self]; +} + + +- (void) packObject:(nullable NSObject*)object +{ + if (!object) + { + cw_pack_nil(_context); + return; + } + + BOOL isMsgpackObject = [object conformsToProtocol:@protocol(CWPackPackerClasses)]; + + if (_useLabels) + { + long label = [self labelForObject:object]; + if (label) // object already packed, just pack a reference. + { + cw_pack_array_size(_context,1); + cw_pack_ext_integer(_context, CWP_ITEM_OBJECT_MARKER, label); + return; + } + + cw_pack_array_size(_context, 2 + object.persistentAttributeCount); + label = [self assignLabelForObject:object isMsgpackObject:isMsgpackObject]; + cw_pack_ext_integer (_context, CWP_ITEM_OBJECT_MARKER, label); + if (isMsgpackObject) { + [(NSObject*)object cwPackPackerTo:self]; + } + else { + [self packUserObject:object]; + } + } + else if (isMsgpackObject) + { + [(NSObject*)object cwPackPackerTo:self]; + } + else + { + cw_pack_array_size(_context, 2 + object.persistentAttributeCount); + cw_pack_ext_integer (_context, CWP_ITEM_OBJECT_MARKER, 0); + [self packUserObject:object]; + } +} + +@end + + + +#pragma mark - +#pragma mark ************************************** CWUnpackContext + + + +@implementation CWUnpackContext +{ + NSMutableArray *_userReferences; + NSMutableArray *_packerReferences; +} + +- (instancetype)initWithContext:(cw_unpack_context*)context +{ + self = [super init]; + if (self) { + _context = context; + _userReferences = [NSMutableArray arrayWithObject:NSNull.null]; //no reference has value 0 + _packerReferences = [NSMutableArray arrayWithObject:NSNull.null]; //no reference has value 0 + } + return self; +} + + ++ (instancetype) newWithContext:(cw_unpack_context*)context +{ + return [self.alloc initWithContext:context]; +} + + +- (id) cwUnpackObject:(long)label +{ + id object; + + switch (_context->item.type) + { + case CWP_ITEM_NIL: + object = [NSNull null]; + break; + + case CWP_ITEM_BOOLEAN: + object = [NSNumber numberWithBool:_context->item.as.boolean]; + break; + + case CWP_ITEM_POSITIVE_INTEGER: + object = [NSNumber numberWithUnsignedLongLong:_context->item.as.u64]; + break; + + case CWP_ITEM_NEGATIVE_INTEGER: + object = [NSNumber numberWithLongLong:_context->item.as.i64]; + break; + + case CWP_ITEM_FLOAT: + object = [[NSNumber alloc] initWithFloat:_context->item.as.real]; + break; + + case CWP_ITEM_DOUBLE: + object = [NSNumber numberWithDouble:_context->item.as.long_real]; + break; + + case CWP_ITEM_STR: + { + object = [NSString.alloc initWithBytes:_context->item.as.str.start length:_context->item.as.str.length encoding:NSUTF8StringEncoding]; + if (!object) { + _context->return_code = CWP_RC_VALUE_ERROR; + return nil; + } + break; + } + + case CWP_ITEM_BIN: + { + object = [NSData dataWithBytes:_context->item.as.bin.start length:_context->item.as.bin.length]; + break; + } + + case CWP_ITEM_TIMESTAMP: + object = [NSDate dateWithTimeIntervalSince1970:_context->item.as.time.tv_sec + _context->item.as.time.tv_nsec / 1000000000.0]; + break; + + case CWP_ITEM_MAP: + { + int i, dim = _context->item.as.map.size; + NSMutableDictionary *dict = NSMutableDictionary.new; + if (label) [_packerReferences addObject:dict]; + for(i = 0; i < dim; i++) + { + id key = [self cwUnpackNextObjectSub:0]; + id val = [self cwUnpackNextObjectSub:0]; + if (_context->return_code == CWP_RC_OK) + dict[key] = val; + } + return dict; + } + + case CWP_ITEM_ARRAY: + { + int dim = _context->item.as.array.size; + if (dim == 0) + { + object = NSMutableArray.new; + break; + } + else + { + cw_unpack_next(_context); + if (_context->item.type == CWP_ITEM_OBJECT_MARKER) //object + { + long idx = get_ext_integer(_context); + if (dim == 1) // object reference + { + if ((idx<0)? (-idx >= _packerReferences.count) : (idx >= _userReferences.count)) + { + _context->return_code = CWP_RC_VALUE_ERROR; + return nil; + } + return idx>0 ? _userReferences[idx] : _packerReferences[-idx]; + } + if (idx < 0) // we had a labeled MessagePack object + { + if (dim != 2) + { + _context->return_code = CWP_RC_VALUE_ERROR; + return nil; + } + return [self cwUnpackNextObjectSub:idx]; + } + + NSString *objClassName = [self cwUnpackNextObjectSub:0]; + Class objClass = NSClassFromString(objClassName); + + if (!objClass) + object = [CWPackGenericClass newWithClassName:objClassName]; + else + object = [objClass alloc]; + object = [object cwUnpackSubInit:self remainingAttributes:dim - 2]; + if (idx == _userReferences.count) + { + [_userReferences addObject:object]; + } + else + { + if (idx != 0) + { + _context->return_code = CWP_RC_VALUE_ERROR; + return nil; + } + } + [object cwUnpackSub:self remainingAttributes:dim - 2]; + return object; + } + else // standard MsgPack array + { + int i; + NSMutableArray *arr = NSMutableArray.new; + if (label) [_packerReferences addObject:arr]; + id item = [self cwUnpackObject:0]; + if (_context->return_code == CWP_RC_OK) + [arr addObject:item]; + for(i = 1; i < dim; i++) + { + id item = [self cwUnpackNextObjectSub:0]; + if (_context->return_code == CWP_RC_OK) + [arr addObject:item]; + } + return arr; + } + } + } + + default: + object = [CWPackExternalItem itemWithType:_context->item.type + data:[NSData dataWithBytes:_context->item.as.ext.start length:_context->item.as.ext.length]]; + } + if (label) + { + if (label != -_packerReferences.count) + _context->return_code = CWP_RC_MALFORMED_INPUT; + else + [_packerReferences addObject:object]; + } + return object; +} + + +- (id) cwUnpackNextObjectSub:(long)label +{ + cw_unpack_next(_context); + if (_context->return_code) + return nil; + return [self cwUnpackObject:label]; +} + + +- (id) unpackNextObject +{ + id result = [self cwUnpackNextObjectSub:0]; + if (result == [NSNull null]) + return nil; + return result; +} + +@end + + + +#pragma mark ************************************** NSObject + + + +@implementation NSObject (CWPackable) + +- (int) persistentAttributeCount {return 0;} + +- (NSString*) packerClassName {return self.className;} + +- (void) cwPackTo:(CWPackContext*)ctx +{ + cw_pack_cstr(ctx.context, self.packerClassName.UTF8String); + [self cwPackSub:ctx]; +} + +- (void) cwPackSub:(CWPackContext*)ctx; +{ + [NSException raise:@"Object Not Packable" format:@"Class \"%s\" does not conform to protocol \n", object_getClassName(self)]; +} + +- (void) cwUnpackSub:(CWUnpackContext*)ctx remainingAttributes:(int)remainingAttributes +{ + [NSException raise:@"Object Not Unpackable" format:@"Class \"%s\" does not conform to protocol \n", object_getClassName(self)]; +} + +- (instancetype) cwUnpackSubInit:(CWUnpackContext*)ctx remainingAttributes:(int)remainingAttributes +{ + return self.init; +} + +@end + + +#pragma mark ************************************** NSNull + +@interface NSNull (CWPackable) +@end + + +@implementation NSNull (CWPackable) + +- (void) cwPackPackerTo:(CWPackContext*)ctx +{ + cw_pack_nil (ctx.context); +} +@end + + + +#pragma mark ************************************** NSString, NSMutableString + + +@interface NSString (CWPackable) +@end + + +@implementation NSString (CWPackable) + +- (void) cwPackPackerTo:(CWPackContext*)ctx +{ + cw_pack_cstr (ctx.context, self.UTF8String); +} +@end + + +#pragma mark ************************************** NSData + +@interface NSData (CWPackable) +@end + +@implementation NSData (CWPackable) + +- (void) cwPackPackerTo:(CWPackContext*)ctx +{ + cw_pack_bin(ctx.context, self.bytes, (uint32_t)self.length); +} +@end + + + +#pragma mark ************************************** NSDate + +@interface NSDate (CWPackable) +@end + +@implementation NSDate (CWPackable) + +- (void) cwPackPackerTo:(CWPackContext*)ctx +{ + NSTimeInterval ti = self.timeIntervalSince1970; + cw_pack_time_interval (ctx.context, ti); +} +@end + + +#pragma mark ************************************** NSNumber + +@interface NSNumber (CWPackable) +@end + +@implementation NSNumber (CWPackable) + +- (void) cwPackPackerTo:(CWPackContext*)ctx +{ + CFNumberType numberType = CFNumberGetType((CFNumberRef)self); + switch (numberType) + { + case kCFNumberSInt8Type: + case kCFNumberSInt16Type: + case kCFNumberSInt32Type: + case kCFNumberSInt64Type: + case kCFNumberShortType: + case kCFNumberIntType: + case kCFNumberLongType: + case kCFNumberCFIndexType: + case kCFNumberNSIntegerType: + case kCFNumberLongLongType: + cw_pack_signed(ctx.context, self.longLongValue); + return; + + case kCFNumberFloat32Type: + case kCFNumberFloatType: + case kCFNumberCGFloatType: + cw_pack_float(ctx.context, self.floatValue); + return; + + case kCFNumberFloat64Type: + case kCFNumberDoubleType: + cw_pack_double(ctx.context, self.doubleValue); + return; + + case kCFNumberCharType: + { + int theValue = self.intValue; + if (theValue == 0) + { + cw_pack_boolean(ctx.context, NO); + } + if (theValue == 1) + { + cw_pack_boolean(ctx.context, YES); + } + else + { + cw_pack_signed(ctx.context, theValue); + } + return; + } + default: + ctx.context->return_code = CWP_RC_ILLEGAL_CALL; // No pack defined for this object type + [NSException raise:@"[NSNumber packIn:]" format:@"Cannot recognise type (%ld) of: %@", (long)numberType, self]; + } +} +@end + + +#pragma mark ************************************** NSArray + +@interface NSArray (CWPackable) +@end + +@implementation NSArray (CWPackable) + +- (void) cwPackPackerTo:(CWPackContext*)ctx +{ + uint32_t i, cnt = (uint32_t)self.count; + cw_pack_array_size(ctx.context, cnt); + for (i = 0; i < cnt; i++) + [ctx packObject:self[i]]; +} +@end + + +#pragma mark ************************************** NSDictionary + + +@interface NSDictionary (CWPackable) +@end + + +@implementation NSDictionary (CWPackable) + +- (void) cwPackPackerTo:(CWPackContext*)ctx +{ + cw_pack_map_size(ctx.context, (uint32_t)self.count); + for (id obj in self) + { + [ctx packObject:obj]; + [ctx packObject:self[obj]]; + } +} +@end + + +#pragma mark ************************************** CWPackExternalItem + + +@interface CWPackExternalItem (CWPackable) +@end + + +@implementation CWPackExternalItem + +- (instancetype) initWithType:(int)type data:(NSData*)data +{ + self = [super init]; + if (self) { + _type = type; + _data = data; + } + return self; +} + + ++ (instancetype) itemWithType:(int)type data:(NSData*)data; +{ + return [[self alloc] initWithType:type data:data]; +} + + +- (void) cwPackPackerTo:(CWPackContext*)ctx +{ + cw_pack_ext(ctx.context, _type, _data.bytes, (int)_data.length); +} + +@end + + +#pragma mark - +#pragma mark ************************************** CWPackGenericClass + + +@implementation CWPackGenericClass + +- (instancetype)initWithClassName:(NSString*)className +{ + self = [self init]; + if (self) { + _packerClassName = className; + } + return self; +} + ++ (instancetype) newWithClassName:(NSString*)className {return [self.alloc initWithClassName:className];} + +- (int) persistentAttributeCount {return (int)_attributes.count;} + +- (void) cwPackSub:(CWPackContext*)ctx +{ + for (id o in _attributes) {[ctx packObject:o];} +} + +- (void) cwUnpackSub:(CWUnpackContext*)ctx remainingAttributes:(int)remainingAttributes +{ + _attributes = NSMutableArray.new; + int i; + for (i = 0; i < remainingAttributes; i++) { + id item = [ctx cwUnpackNextObjectSub:0]; + if (ctx.context->return_code == CWP_RC_OK) + [_attributes addObject:item]; + } +} + +@end + + +#pragma mark ************************************** NSSet + +@interface NSSet (CWPackable) +@end + +@implementation NSSet (CWPackable) +- (int) persistentAttributeCount {return (int)self.count + super.persistentAttributeCount;} + +- (NSString*) packerClassName {return @"NSSet";} + +- (void) cwPackSub:(CWPackContext*)ctx +{ + for (id object in self) + [ctx packObject:object]; +} + +- (instancetype) cwUnpackSubInit:(CWUnpackContext*)ctx remainingAttributes:(int)remainingAttributes +{ + return NSMutableSet.new; +} + +@end + + +@interface NSMutableSet (CWPackable) +@end + +@implementation NSMutableSet (CWPackable) +- (instancetype) cwUnpackSub:(CWUnpackContext*)ctx remainingAttributes:(int)remainingAttributes +{ + int i; + for (i=0; i + +@interface NSCountedSet (CWPackable) +@end + +@implementation NSCountedSet (CWPackable) +- (int) persistentAttributeCount {return 2*(int)self.count + super.persistentAttributeCount;} + +- (void) cwPackSub:(CWPackContext*)ctx +{ + for (id object in self) + { + [ctx packObject:object]; + cw_pack_signed(ctx.context, [self countForObject:object]); + } +} + +- (instancetype) cwUnpackSubInit:(CWUnpackContext*)ctx remainingAttributes:(int)remainingAttributes +{ + return [self initWithCapacity:remainingAttributes/2]; +} + +- (instancetype) cwUnpackSub:(CWUnpackContext*)ctx remainingAttributes:(int)remainingAttributes +{ + long count = remainingAttributes/2; + int i; + for (i=0; i +@property int level; +@property MyClass *link; +- (instancetype) initWithLevel:(int)level link:(MyClass*)link; +@end +``` +Note first, one attribute is a link to another instance, so here we should set useLabels to YES. Our pack method could be like this: + +```C +- (void) cwPackSub:(CWPackContext*)ctx { + [super cwPackSub:ctx]; + cw_pack_signed(ctx.context, level); + [ctx packObject:link]; +} +``` +If the immediate ancestor to MyClass is NSObject, then super must not be called. + +Unpacking can be a little more tangled due to the possibility of circular references, that is a referenced object has a reference back to the root object. Unpacking happens in two steps: first the object is allocated and in the next step the attributes are set. + +Step 1: Allocation & Initiating + +##### - (instancetype) cwUnpackSubInit:(CWUnpackContext*)ctx remainingAttributes:(int)remainingAttributes + + +When `cwUnpackSubInit` is called, self is allocated but not inited. The sole purpose of the method is to return a suitable inited object. It is not neccesary to be the same object as self. If it is sufficient to sent init to the preallocated object (self), this method could be skipped, it's optional. + +Step 2: Fetching attributes. + +##### - (void) cwUnpackSub:(CWUnpackContext*)ctx remainingAttributes:(int)remainingAttributes + +In the pack example an unpack method could be: + +```C +- (void) cwUnpackSub:(CWUnpackContext*)ctx remainingAttributes:(int)remainingAttributes { + [super cwUnpackSub:ctx remainingAttributes:remainingAttributes - 2] + level = cw_unpack_next_signed32 (ctx.context); + link = [ctx unpackNextObject]; +} +``` +As usual, don't call super on NSObject. + +### Packable system objects +(Un)packing of the following system classes is predefined in ObjC: +NSArray, NSDictionary, NSData, NSDate, NSNull, NSNumber, NSSet, NSCountableSet and NSString. In most cases they are mapped at their corresponding MessagePack item. !WARNING! MessagePack has an arbitrary type as key in map. Objective-C demands a string key in the corresponding NSDictionary. + +Of those who have both mutable and immutable versions, NSData and NSString are created immutable, and NSArray, NSDictionary and NSSet are created mutable at unpack. + +### Special +There are two special cases. + +First, when the MessagePack item is of an unknown EXT type, it will be unpacked as an instance of the `CWPackExternalItem` class. + +Second, when the object to be unpacked is of unknown user class, it is unpacked as an instance of the class `CWPackGenericClass`. diff --git a/goodies/objC/Technique.md b/goodies/objC/Technique.md new file mode 100755 index 0000000..34851ee --- /dev/null +++ b/goodies/objC/Technique.md @@ -0,0 +1,60 @@ +# CWPack / Goodies / ObjC / Technique + +We have choosen to represent objects as arrays. To be able to differentiate objects from normal arrays we have put an object marker as the first slot of the array. The object marker is an EXT item with type 127. The second array slot is kept by the class name. The rest of the array slots are kept by the objects attributes. + +### User objects + +We have an example: + +```C +@interface MyClass: NSObject +@property int level; +@property MyClass *link; +- (instancetype) initWithLevel:(int)level link:(MyClass*)link; +@end +``` +When packed into a MessagePack stream it may look like: + +```C +[(127,<00>) "MyClass" 37 NULL] +``` +This was the simple example where the link was nil. But look at this code fragment: + +```C +MyClass *a = [MyClass.alloc initWithLevel:10 link:nil]; +MyClass *b = [MyClass.alloc initWithLevel:20 link:a]; +a.link = b; +[myContext packObject:a]; +``` +To not fall into eternal recursion we need to break the circular reference in some way. We do it by giving the objects labels (set useLabel = YES on pack context). The labels are just consecutive numbers starting at 1. We store the label in the objects markers payload. Let´s start packing: + +```C +[(127,<01>) // object a got the label 1, we note that in a table +[(127,<01>) "MyClass" 10 [(127,<02>) "MyClass" 20 +``` +Now it is time to insert object *a* again. But this time we have the object in our table and instead of packing the object we just pack a reference to it. A reference is an array with a single slot that is the object marker. The final result becomes: + +```C +[(127,<01>) "MyClass" 10 [(127,<02>) "MyClass" 20 [(127,<01>)]]] +``` +The cwpack dump utility "knows" object markers and prints the above message as: + +1->MyClass(10 2->MyClass(20 ->1)) + +### MessagePack objects + +It is not just user objects that can contain circular references. Take this example: + +```C +NSMutableArray *a = NSMutableArray.new; +[a addObject:a]; +[myContext packObject:a]; +``` +Obviously we need a way to label MessagePack objects also. We do as for user objects, but instead for a class-name in the second slot we put the MessagePack object. In this case, there are just 2 slots. + +There is one problem here. If the MessagePack object is a string, it looks exactly like an user object with no attributes. We solve this ambiguity by letting MessagePack objects have negative labels starting with -1. So the packing above becomes: + +```C +[(127,) [[(127,)]]] +``` +Or prettyprinted: -1->[-> -1] diff --git a/goodies/objC/obsolete/README.md b/goodies/objC/obsolete/README.md new file mode 100755 index 0000000..a2aa1cf --- /dev/null +++ b/goodies/objC/obsolete/README.md @@ -0,0 +1,40 @@ +# CWPack / Goodies / ObjC + + +ObjC contains a wrapper for objective-C in the form of a category to NSObject. +The category contains two methods: + +```C +/* *********************** P A C K *****************************/ +- (void) packIn:(cw_pack_context*) buff; + +/* *********************** U N P A C K *************************/ ++ (instancetype) unpackFrom:(cw_unpack_context*) buff; +``` + +The methods are defined for the standard classes NSString, NSData, NSNumber, NSDate, NSNull, NSDictionary, NSArray and NSSet. + +For other classes you need to define the methods yourself. E.g. + +```C +@interface myClass: NSObject { + int theInt; + NSMutableSet theSet; +} +@end + +@implementation myClass +- (void) packIn:(cw_pack_context*) buff +{ + cw_pack_signed( buff, theInt ); + [theSet packIn:buff]; +} ++ (instancetype) unpackFrom:(cw_unpack_context*) buff +{ + int anInt = cw_unpack_next_signed32( buff ); + NSMutableSet aSet = [NSMutableSet unpackFrom:buff]; + return [[self alloc] initWithInt:anInt set:aSet]; +} +@end +``` + diff --git a/goodies/objC/obsolete/cwpack_objc.h b/goodies/objC/obsolete/cwpack_objc.h new file mode 100755 index 0000000..0f9dbc0 --- /dev/null +++ b/goodies/objC/obsolete/cwpack_objc.h @@ -0,0 +1,38 @@ +/* CWPack/goodies - cwpack_objc.h */ +/* + The MIT License (MIT) + + Copyright (c) 2017 Claes Wihlborg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#import +#import "cwpack.h" + + + + +@interface NSObject (cwPack) + +/* *********************** P A C K *****************************/ +- (void) packIn:(cw_pack_context*) buff; + +/* *********************** U N P A C K *************************/ +- (id) initFromContext:(cw_unpack_context*) buff; ++ (instancetype) unpackFrom:(cw_unpack_context*) buff; +@end diff --git a/goodies/objC/obsolete/cwpack_objc.m b/goodies/objC/obsolete/cwpack_objc.m new file mode 100755 index 0000000..d1be3a6 --- /dev/null +++ b/goodies/objC/obsolete/cwpack_objc.m @@ -0,0 +1,299 @@ +/* CWPack/goodies - cwpack_objc.m */ +/* + The MIT License (MIT) + + Copyright (c) 2017 Claes Wihlborg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#import "cwpack_objc.h" +#import "cwpack_utils.h" + +#define CWP_ITEM_CLASS_NAME 127 + + +id cwObjectFromBuffer (cw_unpack_context* inbuf); + + +@implementation NSObject (cwPack) + +- (void) packIn:(cw_pack_context*) buff +{ + const char *className= object_getClassName(self); + cw_pack_ext (buff, CWP_ITEM_CLASS_NAME, className, (uint32_t)strlen(className)); +} + + ++ (instancetype) setWithArray:(NSArray*)array {return nil;} + + ++ (instancetype) unpackFrom:(cw_unpack_context*) buff +{ + id result = cwObjectFromBuffer(buff); + + if (![[result class] isSubclassOfClass:self]) + { + if ([[result class] isSubclassOfClass:[NSArray class]] && [self isSubclassOfClass:[NSSet class]]) + return [self setWithArray:result]; + + if ([[result class] isSubclassOfClass:[NSNull class]]) + return nil; + + else + { + buff->return_code = CWP_RC_TYPE_ERROR; // Unexpected object type + [NSException raise:@"[NSObject unpackFrom:]" format:@"Class %@ detected when expected class: %@", [result class], self]; + } + } + return result; +} + + +- (id) initFromContext:(cw_unpack_context*) buff +{ + self = [self init]; // satisfy compiler + buff->return_code = CWP_RC_ILLEGAL_CALL; // No unpack defined for this object type + [NSException raise:@"Not defined" format:@"[%@ initFromContext:]", [self class]]; + return nil; +} + + +@end + + +@implementation NSNull (cwPack) + +- (void) packIn:(cw_pack_context*) buff +{ + cw_pack_nil (buff); +} +@end + + +@implementation NSString (cwPack) + +- (void) packIn:(cw_pack_context*) buff +{ + cw_pack_cstr (buff, self.UTF8String); +} +@end + + +@implementation NSData (cwPack) + +- (void) packIn:(cw_pack_context*) buff +{ + cw_pack_bin(buff, self.bytes, (uint32_t)self.length); +} +@end + + +@implementation NSNumber (cwPack) + +- (void) packIn:(cw_pack_context*) buff +{ + CFNumberType numberType = CFNumberGetType((CFNumberRef)self); + switch (numberType) + { + case kCFNumberSInt8Type: + case kCFNumberSInt16Type: + case kCFNumberShortType: + case kCFNumberSInt32Type: + case kCFNumberIntType: + case kCFNumberLongType: + case kCFNumberCFIndexType: + case kCFNumberNSIntegerType: + case kCFNumberSInt64Type: + case kCFNumberLongLongType: + cw_pack_signed(buff, self.longLongValue); + return; + + case kCFNumberFloat32Type: + case kCFNumberFloatType: + case kCFNumberCGFloatType: + cw_pack_float(buff, self.floatValue); + return; + + case kCFNumberFloat64Type: + case kCFNumberDoubleType: + cw_pack_double(buff, self.doubleValue); + return; + + case kCFNumberCharType: + { + int theValue = self.intValue; + if (theValue == 0) + { + cw_pack_boolean(buff, NO); + } + if (theValue == 1) + { + cw_pack_boolean(buff, YES); + } + else + { + cw_pack_signed(buff, theValue); + } + return; + } + default: + buff->return_code = CWP_RC_ILLEGAL_CALL; // No pack defined for this object type + [NSException raise:@"[NSNumber packIn:]" format:@"Cannot recognise type of: %@", self]; + } +} +@end + + +@implementation NSArray (cwPack) + +- (void) packIn:(cw_pack_context*) buff +{ + uint32_t i, cnt = (uint32_t)self.count; + cw_pack_array_size(buff, cnt); + for (i = 0; i < cnt; i++) + [self[i] packIn:buff]; +} +@end + + +@implementation NSSet (cwPack) + +- (void) packIn:(cw_pack_context*) buff +{ + uint32_t cnt = (uint32_t)self.count; + cw_pack_array_size(buff, cnt); // Hide the set in an array + for (id object in self) + [object packIn:buff]; +} +@end + + +@implementation NSDictionary (cwPack) + +- (void) packIn:(cw_pack_context*) buff +{ + cw_pack_map_size(buff, (uint32_t)self.count); + for (id obj in self) + { + [obj packIn:buff]; + [self[obj] packIn:buff]; + } +} +@end + + +@implementation NSDate (cwPack) + +- (void) packIn:(cw_pack_context*) buff +{ + NSTimeInterval ti = self.timeIntervalSince1970; + cw_pack_time_interval (buff, ti); +} + + +@end + + + + +/******************************* U N P A C K ******************************/ + + +id cwObjectFromBuffer (cw_unpack_context* inbuf) +{ + cw_unpack_next(inbuf); + if (inbuf->return_code) + return nil; + + switch (inbuf->item.type) + { + case CWP_ITEM_NIL: + return [NSNull null]; + + case CWP_ITEM_BOOLEAN: + return [NSNumber numberWithBool:inbuf->item.as.boolean]; + + case CWP_ITEM_POSITIVE_INTEGER: + return [NSNumber numberWithUnsignedLongLong:inbuf->item.as.u64]; + + case CWP_ITEM_NEGATIVE_INTEGER: + return [NSNumber numberWithLongLong:inbuf->item.as.i64]; + + case CWP_ITEM_FLOAT: + return [[NSNumber alloc] initWithFloat:inbuf->item.as.real]; + + case CWP_ITEM_DOUBLE: + return [NSNumber numberWithDouble:inbuf->item.as.long_real]; + + case CWP_ITEM_STR: + return [[NSString alloc] initWithBytes:inbuf->item.as.str.start length:inbuf->item.as.str.length encoding:NSUTF8StringEncoding]; + + case CWP_ITEM_BIN: + return [NSData dataWithBytes:inbuf->item.as.bin.start length:inbuf->item.as.bin.length]; + + case CWP_ITEM_ARRAY: + { + int i, dim = inbuf->item.as.array.size; + NSMutableArray *arr = [NSMutableArray arrayWithCapacity:dim]; + for(i = 0; i < dim; i++) + { + id item = cwObjectFromBuffer (inbuf); + if (!inbuf->return_code) + [arr addObject:item]; + } + return arr; + } + + case CWP_ITEM_MAP: + { + int i, dim = inbuf->item.as.map.size; + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:dim]; + for(i = 0; i < dim; i++) + { + id key = cwObjectFromBuffer (inbuf); + id val = cwObjectFromBuffer (inbuf); + if (!inbuf->return_code) + [dict setValue:val forKey:key]; + } + return dict; + } + + case CWP_ITEM_TIMESTAMP: + { + return [NSDate dateWithTimeIntervalSince1970:inbuf->item.as.time.tv_sec + inbuf->item.as.time.tv_nsec / 1000000000.0]; + } + + case CWP_ITEM_CLASS_NAME: + { + NSString *cName = [[NSString alloc] initWithBytes:inbuf->item.as.ext.start length:inbuf->item.as.ext.length encoding:NSUTF8StringEncoding]; + Class objectClass = NSClassFromString(cName); + if (objectClass == NULL) + { + [NSException raise:@"cwObjectFromBuffer" format:@"Class not defined for class: %@", cName]; + } + else + return [[objectClass alloc] initFromContext:inbuf]; + } + + default: + return nil; + } +} + + + diff --git a/goodies/utils/README.md b/goodies/utils/README.md new file mode 100755 index 0000000..8ed6894 --- /dev/null +++ b/goodies/utils/README.md @@ -0,0 +1,52 @@ +# CWPack / Goodies / Utils + + +Utils contains some convenience routines: + +### C string packing +```C +void cw_pack_cstr (cw_pack_context* pack_context, const char* v); +``` + +### Optimized packing of real numbers +Pack float as signed if precision isn't destroyed + +```C +void cw_pack_float_opt (cw_pack_context* pack_context, float f); +``` +Pack double as signed or float if precision isn't destroyed + +```C +void cw_pack_double_opt (cw_pack_context* pack_context, double d); +``` + +### Packing seconds and fractions thereof that have elapsed since epoch +```C +void cw_pack_time_interval (cw_pack_context* pack_context, double ti); +``` +### Easy retreival (expect api) + +```C +bool cw_unpack_next_boolean (cw_unpack_context* unpack_context); + +int64_t cw_unpack_next_signed64 (cw_unpack_context* unpack_context); +int32_t cw_unpack_next_signed32 (cw_unpack_context* unpack_context); +int16_t cw_unpack_next_signed16 (cw_unpack_context* unpack_context); +int8_t cw_unpack_next_signed8 (cw_unpack_context* unpack_context); + +uint64_t cw_unpack_next_unsigned64 (cw_unpack_context* unpack_context); +uint32_t cw_unpack_next_unsigned32 (cw_unpack_context* unpack_context); +uint16_t cw_unpack_next_unsigned16 (cw_unpack_context* unpack_context); +uint8_t cw_unpack_next_unsigned8 (cw_unpack_context* unpack_context); + +float cw_unpack_next_float (cw_unpack_context* unpack_context); +double cw_unpack_next_double (cw_unpack_context* unpack_context); +double cw_unpack_next_time_interval (cw_unpack_context* unpack_context); + +unsigned int cw_unpack_next_str_lengh (cw_unpack_context* unpack_context); + +unsigned int cw_unpack_next_array_size(cw_unpack_context* unpack_context); +unsigned int cw_unpack_next_map_size(cw_unpack_context* unpack_context); +``` +The functions signals `CWP_RC_TYPE_ERROR` if next item isn't compatible with the expected type. For int and uint types the functions signals `CWP_RC_VALUE_ERROR` if value is compatible but out of range. + diff --git a/goodies/utils/cwpack_utils.c b/goodies/utils/cwpack_utils.c new file mode 100755 index 0000000..1748ce9 --- /dev/null +++ b/goodies/utils/cwpack_utils.c @@ -0,0 +1,366 @@ +/* CWPack/goodies - cwpack_utils.c */ +/* + The MIT License (MIT) + + Copyright (c) 2017 Claes Wihlborg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include +#include "cwpack_utils.h" + + + + +/******************************* P A C K **********************************/ + + +void cw_pack_double_opt (cw_pack_context* pack_context, double d) +{ + int i = (int)d; + if (i == d) + cw_pack_signed(pack_context, i); + else + { + float f = (float)d; + if (f == d) + cw_pack_float (pack_context, f); + else + cw_pack_double (pack_context, d); + } +} + + +void cw_pack_float_opt (cw_pack_context* pack_context, float f) +{ + int i = (int)f; + if (i == f) + cw_pack_signed(pack_context, i); + else + cw_pack_float (pack_context, f); +} + +void cw_pack_time_interval (cw_pack_context* pack_context, double ti) +{ + int64_t sec = (int64_t)floor(ti); + uint32_t nsec = (uint32_t)((ti - (double)sec) * 1000000000.0); + cw_pack_time(pack_context, sec, nsec); +} + +/******************************* U N P A C K ******************************/ + +#define NaN 0 + +float cw_unpack_next_float (cw_unpack_context* unpack_context) +{ + cw_unpack_next (unpack_context); + if (unpack_context->return_code) return NaN; + + switch (unpack_context->item.type) { + case CWP_ITEM_POSITIVE_INTEGER: return unpack_context->item.as.u64; + case CWP_ITEM_NEGATIVE_INTEGER: return unpack_context->item.as.i64; + case CWP_ITEM_FLOAT: return unpack_context->item.as.real; + case CWP_ITEM_DOUBLE: return (float)unpack_context->item.as.long_real; + default: unpack_context->return_code = CWP_RC_TYPE_ERROR; + return NaN; + } +} + +double cw_unpack_next_double (cw_unpack_context* unpack_context) +{ + cw_unpack_next (unpack_context); + if (unpack_context->return_code) return NaN; + + switch (unpack_context->item.type) { + case CWP_ITEM_POSITIVE_INTEGER: return unpack_context->item.as.u64; + case CWP_ITEM_NEGATIVE_INTEGER: return unpack_context->item.as.i64; + case CWP_ITEM_FLOAT: return unpack_context->item.as.real; + case CWP_ITEM_DOUBLE: return unpack_context->item.as.long_real; + default: unpack_context->return_code = CWP_RC_TYPE_ERROR; + return NaN; + } +} + +bool cw_unpack_next_boolean (cw_unpack_context* unpack_context) +{ + cw_unpack_next (unpack_context); + if (unpack_context->return_code) + return false; + + if (unpack_context->item.type == CWP_ITEM_BOOLEAN) + return unpack_context->item.as.boolean; + + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return false; +} + + +int64_t cw_unpack_next_signed64 (cw_unpack_context* unpack_context) +{ + cw_unpack_next (unpack_context); + if (unpack_context->return_code) + return 0; + + if (unpack_context->item.type == CWP_ITEM_POSITIVE_INTEGER) + { + if (unpack_context->item.as.u64 <= INT64_MAX) + return unpack_context->item.as.i64; + else + { + unpack_context->return_code = CWP_RC_VALUE_ERROR; + return 0; + } + } + + if (unpack_context->item.type == CWP_ITEM_NEGATIVE_INTEGER) + return unpack_context->item.as.i64; + + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return 0; +} + + +int32_t cw_unpack_next_signed32 (cw_unpack_context* unpack_context) +{ + cw_unpack_next (unpack_context); + if (unpack_context->return_code) + return 0; + + if (unpack_context->item.type == CWP_ITEM_POSITIVE_INTEGER) + { + if (unpack_context->item.as.u64 <= INT32_MAX) + return (int)unpack_context->item.as.i64; + else + { + unpack_context->return_code = CWP_RC_VALUE_ERROR; + return 0; + } + } + if (unpack_context->item.type == CWP_ITEM_NEGATIVE_INTEGER) + { + if (unpack_context->item.as.i64 >= INT32_MIN) + return (int)unpack_context->item.as.i64; + else + { + unpack_context->return_code = CWP_RC_VALUE_ERROR; + return 0; + } + } + + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return 0; +} + + +int16_t cw_unpack_next_signed16 (cw_unpack_context* unpack_context) +{ + cw_unpack_next (unpack_context); + if (unpack_context->return_code) + return 0; + + if (unpack_context->item.type == CWP_ITEM_POSITIVE_INTEGER) + { + if (unpack_context->item.as.u64 <= INT16_MAX) + return (int16_t)unpack_context->item.as.i64; + else + { + unpack_context->return_code = CWP_RC_VALUE_ERROR; + return 0; + } + } + if (unpack_context->item.type == CWP_ITEM_NEGATIVE_INTEGER) + { + if (unpack_context->item.as.i64 >= INT16_MIN) + return (int16_t)unpack_context->item.as.i64; + else + { + unpack_context->return_code = CWP_RC_VALUE_ERROR; + return 0; + } + } + + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return 0; +} + + +int8_t cw_unpack_next_signed8 (cw_unpack_context* unpack_context) +{ + cw_unpack_next (unpack_context); + if (unpack_context->return_code) + return 0; + + if (unpack_context->item.type == CWP_ITEM_POSITIVE_INTEGER) + { + if (unpack_context->item.as.u64 <= INT8_MAX) + return (int8_t)unpack_context->item.as.i64; + else + { + unpack_context->return_code = CWP_RC_VALUE_ERROR; + return 0; + } + } + if (unpack_context->item.type == CWP_ITEM_NEGATIVE_INTEGER) + { + if (unpack_context->item.as.i64 >= INT8_MIN) + return (int8_t)unpack_context->item.as.i64; + else + { + unpack_context->return_code = CWP_RC_VALUE_ERROR; + return 0; + } + } + + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return 0; +} + + + +uint64_t cw_unpack_next_unsigned64 (cw_unpack_context* unpack_context) +{ + cw_unpack_next (unpack_context); + if (unpack_context->return_code) + return 0; + + if (unpack_context->item.type == CWP_ITEM_POSITIVE_INTEGER) + { + return unpack_context->item.as.u64; + } + + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return 0; +} + + +uint32_t cw_unpack_next_unsigned32 (cw_unpack_context* unpack_context) +{ + cw_unpack_next (unpack_context); + if (unpack_context->return_code) + return 0; + + if (unpack_context->item.type == CWP_ITEM_POSITIVE_INTEGER) + { + if (unpack_context->item.as.u64 <= UINT32_MAX) + return (uint32_t)unpack_context->item.as.u64; + else + { + unpack_context->return_code = CWP_RC_VALUE_ERROR; + return 0; + } + } + + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return 0; +} + + +uint16_t cw_unpack_next_unsigned16 (cw_unpack_context* unpack_context) +{ + cw_unpack_next (unpack_context); + if (unpack_context->return_code) + return 0; + + if (unpack_context->item.type == CWP_ITEM_POSITIVE_INTEGER) + { + if (unpack_context->item.as.u64 <= UINT16_MAX) + return (uint16_t)unpack_context->item.as.u64; + else + { + unpack_context->return_code = CWP_RC_VALUE_ERROR; + return 0; + } + } + + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return 0; +} + + +uint8_t cw_unpack_next_unsigned8 (cw_unpack_context* unpack_context) +{ + cw_unpack_next (unpack_context); + if (unpack_context->return_code) + return 0; + + if (unpack_context->item.type == CWP_ITEM_POSITIVE_INTEGER) + { + if (unpack_context->item.as.u64 <= UINT8_MAX) + return (uint8_t)unpack_context->item.as.u64; + else + { + unpack_context->return_code = CWP_RC_VALUE_ERROR; + return 0; + } + } + + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return 0; +} + + +double cw_unpack_next_time_interval (cw_unpack_context* unpack_context) +{ + cw_unpack_next (unpack_context); + if (unpack_context->return_code) return NaN; + + if (unpack_context->item.type == CWP_ITEM_TIMESTAMP) + { + return (double)unpack_context->item.as.time.tv_sec + (double)unpack_context->item.as.time.tv_nsec/1000000000; + } + + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return NaN; +} + +unsigned int cw_unpack_next_str_lengh (cw_unpack_context* unpack_context) +{ + cw_unpack_next (unpack_context); + if (unpack_context->return_code) return 0; + + if (unpack_context->item.type == CWP_ITEM_STR) + return unpack_context->item.as.str.length; + + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return 0; +} + + +unsigned int cw_unpack_next_array_size(cw_unpack_context* unpack_context) +{ + cw_unpack_next (unpack_context); + if (unpack_context->return_code) return 0; + + if (unpack_context->item.type == CWP_ITEM_ARRAY) + return unpack_context->item.as.array.size; + + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return 0; +} + +unsigned int cw_unpack_next_map_size(cw_unpack_context* unpack_context) +{ + cw_unpack_next (unpack_context); + if (unpack_context->return_code) return 0; + + if (unpack_context->item.type == CWP_ITEM_MAP) + return unpack_context->item.as.map.size; + + unpack_context->return_code = CWP_RC_TYPE_ERROR; + return 0; +} diff --git a/goodies/utils/cwpack_utils.h b/goodies/utils/cwpack_utils.h new file mode 100644 index 0000000..61dcbb7 --- /dev/null +++ b/goodies/utils/cwpack_utils.h @@ -0,0 +1,66 @@ +/* CWPack/goodies - cwpack_utils.h */ +/* + The MIT License (MIT) + + Copyright (c) 2017 Claes Wihlborg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef CWPack_utils_H__ +#define CWPack_utils_H__ + + +#include "cwpack.h" + +/******************************* P A C K **********************************/ + +#define cw_pack_cstr(context,string) cw_pack_str (context, string, (uint32)strlen(string)) + +void cw_pack_float_opt (cw_pack_context* pack_context, float f); /* Pack as signed if precision isn't destroyed */ +void cw_pack_double_opt (cw_pack_context* pack_context, double d); /* Pack as signed or float if precision isn't destroyed */ +#define cw_pack_real cw_pack_double_opt /* Backward compatibility */ + +#define cw_pack_timespec (pack_contextptr, timespecptr) cw_pack_time ((pack_contextptr), (int64_t)((timespecptr)->tv_sec), (uint32_t)((timespecptr)->tv_nsec)) + +void cw_pack_time_interval (cw_pack_context* pack_context, double ti); /* ti is seconds relative epoch */ + +/***************************** U N P A C K ********************************/ + +bool cw_unpack_next_boolean (cw_unpack_context* unpack_context); + +int64_t cw_unpack_next_signed64 (cw_unpack_context* unpack_context); +int32_t cw_unpack_next_signed32 (cw_unpack_context* unpack_context); +int16_t cw_unpack_next_signed16 (cw_unpack_context* unpack_context); +int8_t cw_unpack_next_signed8 (cw_unpack_context* unpack_context); + +uint64_t cw_unpack_next_unsigned64 (cw_unpack_context* unpack_context); +uint32_t cw_unpack_next_unsigned32 (cw_unpack_context* unpack_context); +uint16_t cw_unpack_next_unsigned16 (cw_unpack_context* unpack_context); +uint8_t cw_unpack_next_unsigned8 (cw_unpack_context* unpack_context); + +float cw_unpack_next_float (cw_unpack_context* unpack_context); +double cw_unpack_next_double (cw_unpack_context* unpack_context); +double cw_unpack_next_time_interval (cw_unpack_context* unpack_context); +#define cw_unpack_next_real cw_unpack_next_double /* Backward compatibility */ + +unsigned int cw_unpack_next_str_lengh (cw_unpack_context* unpack_context); + +unsigned int cw_unpack_next_array_size(cw_unpack_context* unpack_context); +unsigned int cw_unpack_next_map_size(cw_unpack_context* unpack_context); + +#endif /* CWPack_utils_H__ */ + diff --git a/src/README.md b/src/README.md new file mode 100755 index 0000000..62f03fe --- /dev/null +++ b/src/README.md @@ -0,0 +1,28 @@ +# CWPack / src + +The src folder contains all basic functionallity to use CWPack. + +## Files + +**cwpack.h** contains the interface to CWPack. You should include this in your code. + +**cwpack_config.h** contains info about your processor. Byte order, alignment etc. Update this file to suit you. + +**cwpack_internals.h** contains internal macros for cwpack.c. If you are an experienced developer you can use them to access the inner mechanics of CWPack. + +**cwpack.c** contains the code. + +## Contexts +Central to CWPack is the concept of contexts. There are two: `cw_pack_context` and `cw_unpack_context`. They contains all the necessary bookkeeping and a reference to the appropriate context is given in all routine calls. + +CWPack is working against memory buffers. Handlers, stored in the context, are called when a buffer is filled up (packing) or needs refill (unpack). The contexts in this folder handles static memory buffers, but more complex contexts that handles dynamic memory, files and sockets can be found in [goodies/basic-contexts](https://github.com/clwi/CWPack/tree/master/goodies/basic-contexts). + +## How to use +First you choose a context that suits your needs and initiates it. Then you can do the packing/unpacking. + +CWpack is using a streaming model, containers (arrays, maps) are read/written in parts, first the item containing the size and then the contained items one by one. Exception to this is the `cw_skip_items` function which skips whole containers. + +You find some convenience routines for packing and an expect api for unpacking in [goodies/utils](https://github.com/clwi/CWPack/tree/master/goodies/utils). +You find an Objective-C wrapper in [goodies/objC](https://github.com/clwi/CWPack/tree/master/goodies/objC). + + diff --git a/src/cwpack.c b/src/cwpack.c index 02a73fd..94b0516 100644 --- a/src/cwpack.c +++ b/src/cwpack.c @@ -21,9 +21,10 @@ */ #include +#include #include "cwpack.h" -#include "cwpack_defines.h" +#include "cwpack_internals.h" @@ -78,6 +79,7 @@ int CWP_CALL cw_pack_context_init (cw_pack_context* pack_context, void* data, un pack_context->be_compatible = false; pack_context->err_no = 0; pack_context->handle_pack_overflow = hpo; + pack_context->handle_flush = NULL; pack_context->client_data = client_data; pack_context->return_code = test_byte_order(); return pack_context->return_code; @@ -88,6 +90,11 @@ void CWP_CALL cw_pack_set_compatibility (cw_pack_context* pack_context, bool be_ pack_context->be_compatible = be_compatible; } +void CWP_CALL cw_pack_set_flush_handler (cw_pack_context* pack_context, pack_flush_handler handle_flush) +{ + pack_context->handle_flush = handle_flush; +} + /* Packing routines -------------------------------------------------------------------------------- */ @@ -170,17 +177,6 @@ void CWP_CALL cw_pack_double(cw_pack_context* pack_context, double d) } -void CWP_CALL cw_pack_real (cw_pack_context* pack_context, double d) -{ - float f = (float)d; - double df = f; - if (df == d) - cw_pack_float (pack_context, f); - else - cw_pack_double (pack_context, d); -} - - void CWP_CALL cw_pack_nil(cw_pack_context* pack_context) { if (pack_context->return_code) @@ -385,6 +381,51 @@ void CWP_CALL cw_pack_ext (cw_pack_context* pack_context, int8_t type, const voi } +void CWP_CALL cw_pack_time (cw_pack_context* pack_context, int64_t sec, uint32_t nsec) +{ + if (pack_context->return_code) + return; + + if (pack_context->be_compatible) + PACK_ERROR(CWP_RC_ILLEGAL_CALL); + + if (nsec >= 1000000000) + PACK_ERROR(CWP_RC_VALUE_ERROR); + + uint8_t *p; + + if ((uint64_t)sec & 0xfffffffc00000000L) { + // timestamp 96 + //serialize(0xc7, 12, -1, nsec, sec) + cw_pack_reserve_space(15); + *p++ = (uint8_t)0xc7; + *p++ = (uint8_t)12; + *p++ = (uint8_t)0xff; + cw_store32(nsec); p += 4; + cw_store64(sec); + } + else { + uint64_t data64 = (((uint64_t)nsec << 34) | (uint64_t)sec); + if (data64 & 0xffffffff00000000L) { + // timestamp 64 + //serialize(0xd7, -1, data64) + cw_pack_reserve_space(10); + *p++ = (uint8_t)0xd7; + *p++ = (uint8_t)0xff; + cw_store64(data64); + } + else { + // timestamp 32 + uint32_t data32 = (uint32_t)data64; + //serialize(0xd6, -1, data32) + cw_pack_reserve_space(6); + *p++ = (uint8_t)0xd6; + *p++ = (uint8_t)0xff; + cw_store32(data32); + } + } +} + void CWP_CALL cw_pack_insert (cw_pack_context* pack_context, const void* v, uint32_t l) { uint8_t *p; @@ -392,6 +433,17 @@ void CWP_CALL cw_pack_insert (cw_pack_context* pack_context, const void* v, uint memcpy(p,v,l); } + +void CWP_CALL cw_pack_flush (cw_pack_context* pack_context) +{ + if (pack_context->return_code == CWP_RC_OK) + pack_context->return_code = + pack_context->handle_flush ? + pack_context->handle_flush(pack_context) : + CWP_RC_ILLEGAL_CALL; +} + + /******************************* U N P A C K **********************************/ @@ -469,15 +521,29 @@ void CWP_CALL cw_unpack_next (cw_unpack_context* unpack_context) cw_unpack_assert_blob(bin); case 0xc7: getDDItem1(CWP_ITEM_EXT, ext.length, uint8_t); // ext 8 cw_unpack_assert_space(1); - unpack_context->item.type = *(int8_t*)p; + unpack_context->item.type = (cwpack_item_types)*(int8_t*)p; + if (unpack_context->item.type == CWP_ITEM_TIMESTAMP) + { + if (unpack_context->item.as.ext.length == 12) + { + cw_unpack_assert_space(4); + cw_load32(p); + unpack_context->item.as.time.tv_nsec = tmpu32; + cw_unpack_assert_space(8); + cw_load64(p,tmpu64); + unpack_context->item.as.time.tv_sec = (int64_t)tmpu64; + return; + } + UNPACK_ERROR(CWP_RC_WRONG_TIMESTAMP_LENGTH) + } cw_unpack_assert_blob(ext); case 0xc8: getDDItem2(CWP_ITEM_EXT, ext.length, uint16_t); // ext 16 cw_unpack_assert_space(1); - unpack_context->item.type = *(int8_t*)p; + unpack_context->item.type = (cwpack_item_types)*(int8_t*)p; cw_unpack_assert_blob(ext); case 0xc9: getDDItem4(CWP_ITEM_EXT, ext.length, uint32_t); // ext 32 cw_unpack_assert_space(1); - unpack_context->item.type = *(int8_t*)p; + unpack_context->item.type = (cwpack_item_types)*(int8_t*)p; cw_unpack_assert_blob(ext); case 0xca: unpack_context->item.type = CWP_ITEM_FLOAT; // float cw_unpack_assert_space(4); @@ -500,7 +566,7 @@ void CWP_CALL cw_unpack_next (cw_unpack_context* unpack_context) if (unpack_context->item.as.i64 >= 0) unpack_context->item.type = CWP_ITEM_POSITIVE_INTEGER; return; - case 0xd3: getDDItem8(CWP_ITEM_NEGATIVE_INTEGER); // signed int 64 + case 0xd3: getDDItem8(CWP_ITEM_NEGATIVE_INTEGER); // signed int 64 if (unpack_context->item.as.i64 >= 0) unpack_context->item.type = CWP_ITEM_POSITIVE_INTEGER; return; @@ -527,8 +593,6 @@ void CWP_CALL cw_unpack_next (cw_unpack_context* unpack_context) default: UNPACK_ERROR(CWP_RC_MALFORMED_INPUT) } - - return; } #define cw_skip_bytes(n) \ @@ -672,7 +736,6 @@ void CWP_CALL cw_skip_items (cw_unpack_context* unpack_context, long item_count) UNPACK_ERROR(CWP_RC_MALFORMED_INPUT) } } - return; } /* end cwpack.c */ diff --git a/src/cwpack.h b/src/cwpack.h index d2dae99..85a832d 100644 --- a/src/cwpack.h +++ b/src/cwpack.h @@ -29,6 +29,7 @@ extern "C" { #include #include +#include #ifndef CWP_CALL @@ -38,16 +39,19 @@ extern "C" { /******************************* Return Codes *****************************/ -#define CWP_RC_OK 0 -#define CWP_RC_END_OF_INPUT -1 -#define CWP_RC_BUFFER_OVERFLOW -2 -#define CWP_RC_BUFFER_UNDERFLOW -3 -#define CWP_RC_MALFORMED_INPUT -4 -#define CWP_RC_WRONG_BYTE_ORDER -5 -#define CWP_RC_ERROR_IN_HANDLER -6 -#define CWP_RC_ILLEGAL_CALL -7 -#define CWP_RC_MALLOC_ERROR -8 -#define CWP_RC_STOPPED -9 +#define CWP_RC_OK 0 +#define CWP_RC_END_OF_INPUT -1 +#define CWP_RC_BUFFER_OVERFLOW -2 +#define CWP_RC_BUFFER_UNDERFLOW -3 +#define CWP_RC_MALFORMED_INPUT -4 +#define CWP_RC_WRONG_BYTE_ORDER -5 +#define CWP_RC_ERROR_IN_HANDLER -6 +#define CWP_RC_ILLEGAL_CALL -7 +#define CWP_RC_MALLOC_ERROR -8 +#define CWP_RC_STOPPED -9 +#define CWP_RC_TYPE_ERROR -10 +#define CWP_RC_VALUE_ERROR -11 +#define CWP_RC_WRONG_TIMESTAMP_LENGTH -12 @@ -56,21 +60,25 @@ extern "C" { struct cw_pack_context; typedef int (CWP_CALL *pack_overflow_handler)(struct cw_pack_context*, unsigned long); +typedef int (CWP_CALL *pack_flush_handler)(struct cw_pack_context*); typedef struct cw_pack_context { - uint8_t* start; uint8_t* current; + uint8_t* start; uint8_t* end; bool be_compatible; int return_code; int err_no; /* handlers can save error here */ pack_overflow_handler handle_pack_overflow; + pack_flush_handler handle_flush; void* client_data; } cw_pack_context; int CWP_CALL cw_pack_context_init (cw_pack_context* pack_context, void* data, unsigned long length, pack_overflow_handler hpo, void* client_data); void CWP_CALL cw_pack_set_compatibility (cw_pack_context* pack_context, bool be_compatible); +void CWP_CALL cw_pack_set_flush_handler (cw_pack_context* pack_context, pack_flush_handler handle_flush); +void CWP_CALL cw_pack_flush (cw_pack_context* pack_context); void CWP_CALL cw_pack_nil (cw_pack_context* pack_context); void CWP_CALL cw_pack_true (cw_pack_context* pack_context); @@ -79,10 +87,12 @@ void CWP_CALL cw_pack_boolean (cw_pack_context* pack_context, bool b); void CWP_CALL cw_pack_signed (cw_pack_context* pack_context, int64_t i); void CWP_CALL cw_pack_unsigned (cw_pack_context* pack_context, uint64_t i); +/* void CWP_CALL cw_pack_real (cw_pack_context* pack_context, double d); moved to cwpack_utils */ void CWP_CALL cw_pack_float (cw_pack_context* pack_context, float f); void CWP_CALL cw_pack_double (cw_pack_context* pack_context, double d); void CWP_CALL cw_pack_real (cw_pack_context* pack_context, double d); /* Pack as float if precision isn't destroyed */ +void CWP_CALL cw_pack_time (cw_pack_context* pack_context, int64_t sec, uint32_t nsec); void CWP_CALL cw_pack_array_size (cw_pack_context* pack_context, uint32_t n); void CWP_CALL cw_pack_map_size (cw_pack_context* pack_context, uint32_t n); @@ -98,9 +108,139 @@ void CWP_CALL cw_pack_insert (cw_pack_context* pack_context, const void* v, uint typedef enum { CWP_ITEM_MIN_RESERVED_EXT = -128, + CWP_ITEM_TIMESTAMP = -1, CWP_ITEM_MAX_RESERVED_EXT = -1, CWP_ITEM_MIN_USER_EXT = 0, + CWP_ITEM_USER_EXT_0 = 0, + CWP_ITEM_USER_EXT_1 = 1, + CWP_ITEM_USER_EXT_2 = 2, + CWP_ITEM_USER_EXT_3 = 3, + CWP_ITEM_USER_EXT_4 = 4, + CWP_ITEM_USER_EXT_5 = 5, + CWP_ITEM_USER_EXT_6 = 6, + CWP_ITEM_USER_EXT_7 = 7, + CWP_ITEM_USER_EXT_8 = 8, + CWP_ITEM_USER_EXT_9 = 9, + CWP_ITEM_USER_EXT_10 = 10, + CWP_ITEM_USER_EXT_11 = 11, + CWP_ITEM_USER_EXT_12 = 12, + CWP_ITEM_USER_EXT_13 = 13, + CWP_ITEM_USER_EXT_14 = 14, + CWP_ITEM_USER_EXT_15 = 15, + CWP_ITEM_USER_EXT_16 = 16, + CWP_ITEM_USER_EXT_17 = 17, + CWP_ITEM_USER_EXT_18 = 18, + CWP_ITEM_USER_EXT_19 = 19, + CWP_ITEM_USER_EXT_20 = 20, + CWP_ITEM_USER_EXT_21 = 21, + CWP_ITEM_USER_EXT_22 = 22, + CWP_ITEM_USER_EXT_23 = 23, + CWP_ITEM_USER_EXT_24 = 24, + CWP_ITEM_USER_EXT_25 = 25, + CWP_ITEM_USER_EXT_26 = 26, + CWP_ITEM_USER_EXT_27 = 27, + CWP_ITEM_USER_EXT_28 = 28, + CWP_ITEM_USER_EXT_29 = 29, + CWP_ITEM_USER_EXT_30 = 30, + CWP_ITEM_USER_EXT_31 = 31, + CWP_ITEM_USER_EXT_32 = 32, + CWP_ITEM_USER_EXT_33 = 33, + CWP_ITEM_USER_EXT_34 = 34, + CWP_ITEM_USER_EXT_35 = 35, + CWP_ITEM_USER_EXT_36 = 36, + CWP_ITEM_USER_EXT_37 = 37, + CWP_ITEM_USER_EXT_38 = 38, + CWP_ITEM_USER_EXT_39 = 39, + CWP_ITEM_USER_EXT_40 = 40, + CWP_ITEM_USER_EXT_41 = 41, + CWP_ITEM_USER_EXT_42 = 42, + CWP_ITEM_USER_EXT_43 = 43, + CWP_ITEM_USER_EXT_44 = 44, + CWP_ITEM_USER_EXT_45 = 45, + CWP_ITEM_USER_EXT_46 = 46, + CWP_ITEM_USER_EXT_47 = 47, + CWP_ITEM_USER_EXT_48 = 48, + CWP_ITEM_USER_EXT_49 = 49, + CWP_ITEM_USER_EXT_50 = 50, + CWP_ITEM_USER_EXT_51 = 51, + CWP_ITEM_USER_EXT_52 = 52, + CWP_ITEM_USER_EXT_53 = 53, + CWP_ITEM_USER_EXT_54 = 54, + CWP_ITEM_USER_EXT_55 = 55, + CWP_ITEM_USER_EXT_56 = 56, + CWP_ITEM_USER_EXT_57 = 57, + CWP_ITEM_USER_EXT_58 = 58, + CWP_ITEM_USER_EXT_59 = 59, + CWP_ITEM_USER_EXT_60 = 60, + CWP_ITEM_USER_EXT_61 = 61, + CWP_ITEM_USER_EXT_62 = 62, + CWP_ITEM_USER_EXT_63 = 63, + CWP_ITEM_USER_EXT_64 = 64, + CWP_ITEM_USER_EXT_65 = 65, + CWP_ITEM_USER_EXT_66 = 66, + CWP_ITEM_USER_EXT_67 = 67, + CWP_ITEM_USER_EXT_68 = 68, + CWP_ITEM_USER_EXT_69 = 69, + CWP_ITEM_USER_EXT_70 = 70, + CWP_ITEM_USER_EXT_71 = 71, + CWP_ITEM_USER_EXT_72 = 72, + CWP_ITEM_USER_EXT_73 = 73, + CWP_ITEM_USER_EXT_74 = 74, + CWP_ITEM_USER_EXT_75 = 75, + CWP_ITEM_USER_EXT_76 = 76, + CWP_ITEM_USER_EXT_77 = 77, + CWP_ITEM_USER_EXT_78 = 78, + CWP_ITEM_USER_EXT_79 = 79, + CWP_ITEM_USER_EXT_80 = 80, + CWP_ITEM_USER_EXT_81 = 81, + CWP_ITEM_USER_EXT_82 = 82, + CWP_ITEM_USER_EXT_83 = 83, + CWP_ITEM_USER_EXT_84 = 84, + CWP_ITEM_USER_EXT_85 = 85, + CWP_ITEM_USER_EXT_86 = 86, + CWP_ITEM_USER_EXT_87 = 87, + CWP_ITEM_USER_EXT_88 = 88, + CWP_ITEM_USER_EXT_89 = 89, + CWP_ITEM_USER_EXT_90 = 90, + CWP_ITEM_USER_EXT_91 = 91, + CWP_ITEM_USER_EXT_92 = 92, + CWP_ITEM_USER_EXT_93 = 93, + CWP_ITEM_USER_EXT_94 = 94, + CWP_ITEM_USER_EXT_95 = 95, + CWP_ITEM_USER_EXT_96 = 96, + CWP_ITEM_USER_EXT_97 = 97, + CWP_ITEM_USER_EXT_98 = 98, + CWP_ITEM_USER_EXT_99 = 99, + CWP_ITEM_USER_EXT_100 = 100, + CWP_ITEM_USER_EXT_101 = 101, + CWP_ITEM_USER_EXT_102 = 102, + CWP_ITEM_USER_EXT_103 = 103, + CWP_ITEM_USER_EXT_104 = 104, + CWP_ITEM_USER_EXT_105 = 105, + CWP_ITEM_USER_EXT_106 = 106, + CWP_ITEM_USER_EXT_107 = 107, + CWP_ITEM_USER_EXT_108 = 108, + CWP_ITEM_USER_EXT_109 = 109, + CWP_ITEM_USER_EXT_110 = 110, + CWP_ITEM_USER_EXT_111 = 111, + CWP_ITEM_USER_EXT_112 = 112, + CWP_ITEM_USER_EXT_113 = 113, + CWP_ITEM_USER_EXT_114 = 114, + CWP_ITEM_USER_EXT_115 = 115, + CWP_ITEM_USER_EXT_116 = 116, + CWP_ITEM_USER_EXT_117 = 117, + CWP_ITEM_USER_EXT_118 = 118, + CWP_ITEM_USER_EXT_119 = 119, + CWP_ITEM_USER_EXT_120 = 120, + CWP_ITEM_USER_EXT_121 = 121, + CWP_ITEM_USER_EXT_122 = 122, + CWP_ITEM_USER_EXT_123 = 123, + CWP_ITEM_USER_EXT_124 = 124, + CWP_ITEM_USER_EXT_125 = 125, + CWP_ITEM_USER_EXT_126 = 126, + CWP_ITEM_USER_EXT_127 = 127, CWP_ITEM_MAX_USER_EXT = 127, + CWP_ITEM_NIL = 300, CWP_ITEM_BOOLEAN = 301, CWP_ITEM_POSITIVE_INTEGER = 302, @@ -112,7 +252,7 @@ typedef enum CWP_ITEM_ARRAY = 308, CWP_ITEM_MAP = 309, CWP_ITEM_EXT = 310, - CWP_NOT_AN_ITEM = 999, + CWP_NOT_AN_ITEM = 999 } cwpack_item_types; @@ -127,6 +267,12 @@ typedef struct { } cwpack_container; +typedef struct { + int64_t tv_sec; + uint32_t tv_nsec; +} cwpack_timespec; + + typedef struct { cwpack_item_types type; union @@ -141,6 +287,7 @@ typedef struct { cwpack_blob str; cwpack_blob bin; cwpack_blob ext; + cwpack_timespec time; } as; } cwpack_item; diff --git a/src/cwpack_config.h b/src/cwpack_config.h new file mode 100644 index 0000000..10ec9fe --- /dev/null +++ b/src/cwpack_config.h @@ -0,0 +1,100 @@ +/* CWPack - cwpack_config.h */ +/* + The MIT License (MIT) + + Copyright (c) 2017 Claes Wihlborg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#ifndef cwpack_config_h +#define cwpack_config_h + + + +/************************* A L I G N M E N T ******************************/ + +/* + * Some processors demand that integer access is to an even memory address. + * In that case define FORCE_ALIGNMENT + */ + +/* #define FORCE_ALIGNMENT */ + +/* + * Some processors demand that 64 bit integer access is aligned. + * In that case define FORCE_ALIGNMENT_64BIT + */ + +/* #define FORCE_ALIGNMENT_64BIT */ + + + +/************************* C S Y S T E M L I B R A R Y ****************/ + +/* + * The packer uses "memcpy" to move blobs. If you dont want to load C system library + * for just that, define FORCE_NO_LIBRARY and CWPack will use an internal "memcpy" + */ + +/* #define FORCE_NO_LIBRARY */ + + + +/************************* B Y T E O R D E R ****************************/ + +/* + * The pack/unpack routines are written in three versions: for big endian, for + * little endian and insensitive to byte order. As you can get some speed gain + * if the byte order is known, we try that when we can certainly detect it. + * Define COMPILE_FOR_BIG_ENDIAN or COMPILE_FOR_LITTLE_ENDIAN if you know. + */ + +#ifndef FORCE_ALIGNMENT +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define COMPILE_FOR_BIG_ENDIAN +#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define COMPILE_FOR_LITTLE_ENDIAN +#endif + +#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN) + +#if __BYTE_ORDER == __BIG_ENDIAN +#define COMPILE_FOR_BIG_ENDIAN +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define COMPILE_FOR_LITTLE_ENDIAN +#endif + +#elif defined(__BIG_ENDIAN__) +#define COMPILE_FOR_BIG_ENDIAN + +#elif defined(__LITTLE_ENDIAN__) +#define COMPILE_FOR_LITTLE_ENDIAN + +#elif defined(__i386__) || defined(__x86_64__) +#define COMPILE_FOR_LITTLE_ENDIAN + +#endif +#endif + +//#undef COMPILE_FOR_LITTLE_ENDIAN + + + +#endif /* cwpack_config_h */ diff --git a/src/cwpack_defines.h b/src/cwpack_internals.h similarity index 73% rename from src/cwpack_defines.h rename to src/cwpack_internals.h index ccd4566..513fd03 100644 --- a/src/cwpack_defines.h +++ b/src/cwpack_internals.h @@ -24,69 +24,17 @@ #ifndef cwpack_defines_h #define cwpack_defines_h +#include "cwpack_config.h" -/************************* A L I G N M E N T ******************************/ - -/* - * Sometime the processor demands that integer access is to an even memory address. - * In that case define FORCE_ALIGNMENT - */ - -/* #define FORCE_ALIGNMENT */ - - -/************************* C S Y S T E M L I B R A R Y ****************/ - -/* - * The packer uses "memcpy" to move blobs. If you dont want to load C system library - * for just that, define FORCE_NO_LIBRARY and CWPack will use an internal "memcpy" - */ - -/* #define FORCE_NO_LIBRARY */ - - - -/************************* B Y T E O R D E R ****************************/ - -/* - * The pack/unpack routines are written in three versions: for big endian, for - * little endian and insensitive to byte order. As you can get some speed gain - * if the byte order is known, we try that when we can certainly detect it. - * Define COMPILE_FOR_BIG_ENDIAN or COMPILE_FOR_LITTLE_ENDIAN if you know. - */ - -#if !defined(FORCE_ALIGNMENT) && !defined(COMPILE_FOR_BIG_ENDIAN) && !defined(COMPILE_FOR_LITTLE_ENDIAN) -#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) - -#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -#define COMPILE_FOR_BIG_ENDIAN -#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -#define COMPILE_FOR_LITTLE_ENDIAN +#ifndef MOST_LIKELY +#if defined(__GNUC__) || defined(__clang__) +#define MOST_LIKELY(a,b) __builtin_expect((a),(b)) +#else +#define MOST_LIKELY(a,b) ((a) == (b)) #endif - -#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN) - -#if __BYTE_ORDER == __BIG_ENDIAN -#define COMPILE_FOR_BIG_ENDIAN -#elif __BYTE_ORDER == __LITTLE_ENDIAN -#define COMPILE_FOR_LITTLE_ENDIAN #endif -#elif defined(__BIG_ENDIAN__) -#define COMPILE_FOR_BIG_ENDIAN - -#elif defined(__LITTLE_ENDIAN__) -#define COMPILE_FOR_LITTLE_ENDIAN - -#elif defined(__i386__) || defined(__x86_64__) -#define COMPILE_FOR_LITTLE_ENDIAN - -#endif -#endif - -//#undef COMPILE_FOR_LITTLE_ENDIAN - /******************************* P A C K **********************************/ @@ -102,9 +50,13 @@ #ifdef COMPILE_FOR_BIG_ENDIAN -#define cw_store16(x) *(uint16_t*)p = *(uint16_t*)&x; -#define cw_store32(x) *(uint32_t*)p = *(uint32_t*)&x; -#define cw_store64(x) *(uint64_t*)p = *(uint64_t*)&x; +#define cw_store16(x) *(uint16_t*)p = (uint16_t)x; +#define cw_store32(x) *(uint32_t*)p = (uint32_t)x; +#ifndef FORCE_ALIGNMENT_64BIT +#define cw_store64(x) *(uint64_t*)p = (uint64_t)x; +#else +#define cw_store64(x) memcpy(p,&x,8); +#endif #else /* Byte order little endian or undetermined */ @@ -120,6 +72,7 @@ (((uint32_t)(x) & 0x0000ff00) << 8) | \ (((uint32_t)(x)) << 24))); \ +#ifndef FORCE_ALIGNMENT_64BIT #define cw_store64(x) \ *(uint64_t*)p = \ ((uint64_t)( \ @@ -130,7 +83,18 @@ (((uint64_t)(x) & 0x000000ff00000000ULL) >> 8) | \ (((uint64_t)(x) & 0x00000000ff000000ULL) << 8) | \ (((uint64_t)(x)) >> 56) | \ - (((uint64_t)(x)) << 56))); \ + (((uint64_t)(x)) << 56))); +#else +#define cw_store64(z) \ + *p = (uint8_t)(z >> 56); \ + p[1] = (uint8_t)(z >> 48); \ + p[2] = (uint8_t)(z >> 40); \ + p[3] = (uint8_t)(z >> 32); \ + p[4] = (uint8_t)(z >> 24); \ + p[5] = (uint8_t)(z >> 16); \ + p[6] = (uint8_t)(z >> 8); \ + p[7] = (uint8_t)z; +#endif #else /* Byte order undetermined */ @@ -158,17 +122,23 @@ +#define cw_pack_new_buffer(more) \ +{ \ + if (!pack_context->handle_pack_overflow) \ + PACK_ERROR(CWP_RC_BUFFER_OVERFLOW) \ + int rc = pack_context->handle_pack_overflow (pack_context, (unsigned long)(more)); \ + if (rc) \ + PACK_ERROR(rc) \ +} + + #define cw_pack_reserve_space(more) \ { \ p = pack_context->current; \ uint8_t* nyp = p + more; \ if (nyp > pack_context->end) \ { \ - if (!pack_context->handle_pack_overflow) \ - PACK_ERROR(CWP_RC_BUFFER_OVERFLOW) \ - int rc = pack_context->handle_pack_overflow (pack_context, (unsigned long)(more)); \ - if (rc) \ - PACK_ERROR(rc) \ + cw_pack_new_buffer(more) \ p = pack_context->current; \ nyp = p + more; \ } \ @@ -178,9 +148,9 @@ #define tryMove0(t) \ { \ - uint8_t *p; \ - cw_pack_reserve_space(1) \ - *p = (uint8_t)(t); \ + if (pack_context->current == pack_context->end) \ + cw_pack_new_buffer(1) \ + *pack_context->current++ = (uint8_t)(t); \ return; \ } @@ -240,7 +210,11 @@ #define cw_load16(ptr) tmpu16 = *(uint16_t*)ptr; #define cw_load32(ptr) tmpu32 = *(uint32_t*)ptr; -#define cw_load64(ptr) tmpu64 = *(uint64_t*)ptr; +#ifndef FORCE_ALIGNMENT_64BIT +#define cw_load64(ptr,dest) dest = *(uint64_t*)ptr; +#else +#define cw_load64(ptr,dest) memcpy(&dest,ptr,8); +#endif #else /* Byte order little endian or undetermined */ @@ -255,9 +229,10 @@ tmpu32 = (tmpu32<<24) | ((tmpu32 & 0xff00)<<8) | \ ((tmpu32 & 0xff0000)>>8) | (tmpu32>>24) -#define cw_load64(ptr) \ +#ifndef FORCE_ALIGNMENT_64BIT +#define cw_load64(ptr,dest) \ tmpu64 = *((uint64_t*)ptr); \ - tmpu64 = ( \ + dest = ( \ (((tmpu64 >> 40) | \ (tmpu64 << 24)) & 0x0000ff000000ff00ULL) | \ (((tmpu64 >> 24) | \ @@ -266,6 +241,17 @@ ((tmpu64 & 0x00000000ff000000ULL) << 8) | \ (tmpu64 >> 56) | \ (tmpu64 << 56) ) +#else +#define cw_load64(ptr,dest) \ + tmpu64 = ((uint64_t)*ptr++) << 56; \ + tmpu64 |= ((uint64_t)*ptr++) << 48; \ + tmpu64 |= ((uint64_t)*ptr++) << 40; \ + tmpu64 |= ((uint64_t)*ptr++) << 32; \ + tmpu64 |= ((uint64_t)*ptr++) << 24; \ + tmpu64 |= ((uint64_t)*ptr++) << 16; \ + tmpu64 |= ((uint64_t)*ptr++) << 8; \ + dest = tmpu64 | (uint64_t)*ptr++; +#endif #else /* Byte order undetermined */ @@ -279,7 +265,7 @@ tmpu32 |= (uint32_t)(*ptr++ << 8); \ tmpu32 |= (uint32_t)(*ptr++) -#define cw_load64(ptr) \ +#define cw_load64(ptr,dest) \ tmpu64 = ((uint64_t)*ptr++) << 56; \ tmpu64 |= ((uint64_t)*ptr++) << 48; \ tmpu64 |= ((uint64_t)*ptr++) << 40; \ @@ -287,7 +273,7 @@ tmpu64 |= ((uint64_t)*ptr++) << 24; \ tmpu64 |= ((uint64_t)*ptr++) << 16; \ tmpu64 |= ((uint64_t)*ptr++) << 8; \ - tmpu64 |= (uint64_t)*ptr++ + dest = tmpu64 | (uint64_t)*ptr++; #endif #endif @@ -347,15 +333,35 @@ #define getDDItem8(typ) \ unpack_context->item.type = typ; \ cw_unpack_assert_space(8); \ - cw_load64(p); \ - unpack_context->item.as.u64 = tmpu64; + cw_load64(p,unpack_context->item.as.u64); #define getDDItemFix(len) \ - cw_unpack_assert_space(1); \ - unpack_context->item.type = *(int8_t*)p; \ + cw_unpack_assert_space(len+1); \ + unpack_context->item.type = (cwpack_item_types)*(int8_t*)p++; \ + if (unpack_context->item.type == CWP_ITEM_TIMESTAMP) \ + { \ + if (len == 4) \ + { \ + cw_load32(p); \ + unpack_context->item.as.time.tv_sec = (long)tmpu32; \ + unpack_context->item.as.time.tv_nsec = 0; \ + return; \ + } \ + else if (len == 8) \ + { \ + cw_load64(p,tmpu64); \ + unpack_context->item.as.time.tv_sec = tmpu64 & 0x00000003ffffffffL; \ + unpack_context->item.as.time.tv_nsec = tmpu64 >> 34; \ + return; \ + } \ + else \ + { \ + UNPACK_ERROR(CWP_RC_WRONG_TIMESTAMP_LENGTH) \ + } \ + } \ unpack_context->item.as.ext.length = len; \ - cw_unpack_assert_blob(ext); - + unpack_context->item.as.ext.start = p; \ + return; diff --git a/test/README.md b/test/README.md index a555414..1f795e7 100755 --- a/test/README.md +++ b/test/README.md @@ -1,4 +1,4 @@ -#CWPack - Test +# CWPack / Test The folder has two tests. - A module test to check that the packer/unpacker behaves as expected. @@ -8,8 +8,10 @@ The folder has two tests. The shell script `runModuleTest.sh` runs the module test. The test checks that it is compiled with compatible byte order and then checks the different calls to CWPack. -## The speed test +## The performance test -The speed test is run by the shell script `runPerformanceTest.sh`. The script assumes that the repositories for CWPack, MPack and CMP are side by side in the same folder. +The performance test is run by the shell script `runPerformanceTest.sh`. The script assumes that the repositories for CWPack, MPack and CMP are side by side in the same folder. -The speed test checks the duration of a number of calls by calling them 1.000.000 times. \ No newline at end of file +The performance test is targeted to CMP v19 and MPack v1.0. + +The performance test checks the duration of a number of calls by calling them 1.000.000 times. diff --git a/test/cwpack_module_test.c b/test/cwpack_module_test.c index 95ebf2e..3d5dab5 100644 --- a/test/cwpack_module_test.c +++ b/test/cwpack_module_test.c @@ -1,18 +1,18 @@ -/* CWPack Module test - main.c */ +/* CWPack/test cwpack_module_test.c */ /* The MIT License (MIT) - + Copyright (c) 2017 Claes Wihlborg - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, @@ -28,7 +28,8 @@ #include #include "cwpack.h" -#include "cwpack_defines.h" +#include "cwpack_config.h" +#include "cwpack_utils.h" cw_pack_context pack_ctx; @@ -64,7 +65,7 @@ static char char2hex (char c) return c - '0'; if (c <= 'F') return c - 'A' + 10; - + return c - 'a' + 10; } @@ -75,7 +76,7 @@ static void check_pack_result(const char* expected_header, unsigned long data_le unsigned long header_length = strlen(expected_header) / 2; if (pack_ctx.current - outbuffer == (long)(header_length + data_length)) { - + if (header_length*2 == strlen(expected_header)) { unsigned long i; @@ -107,14 +108,14 @@ static void check_pack_result(const char* expected_header, unsigned long data_le hex <<= 4; hex += (uc - 'A' + 10); } - - + + if (*p++ != hex) { ERROR("Different header value"); } } - + if (data_length > 0) { ucp = TEST_area; @@ -149,68 +150,68 @@ int main(int argc, const char * argv[]) { printf("CWPack module test started.\n"); error_count = 0; - + bool endian_switch_found = false; #ifdef COMPILE_FOR_BIG_ENDIAN printf("Compiled for big endian.\n"); endian_switch_found = true; #endif - + #ifdef COMPILE_FOR_LITTLE_ENDIAN printf("Compiled for little endian.\n"); endian_switch_found = true; #endif - + if (!endian_switch_found) printf("Compiled for all endians.\n"); - + const char *endianness = "1234"; switch (*(uint32_t*)endianness) { case 0x31323334UL: printf("Running on big endian hardware.\n\n"); break; - + case 0x34333231UL: printf("Running on little endian hardware.\n\n"); break; - + default: printf("Running on neither little nor big endian hardware.\n\n"); break; } - - + + //******************* TEST cwpack pack **************************** - - + + #define TESTP(call,data,result) \ pack_ctx.current = outbuffer; \ cw_pack_##call (&pack_ctx, data); \ if(pack_ctx.return_code) \ ERROR("In pack"); \ check_pack_result(result,0) - - - + + + cw_pack_context_init (&pack_ctx, outbuffer, 70000, 0); if (pack_ctx.return_code == CWP_RC_WRONG_BYTE_ORDER) { ERROR("***** Compiled for wrong byte order, test terminated *****\n\n"); exit(1); } - + unsigned int ui; for (ui=0; ui<70000; ui++) { TEST_area[ui] = ui & 0x7fUL; } - - + + // TESTP NIL cw_pack_nil(&pack_ctx); check_pack_result("c0",0); - + // TESTP boolean pack_ctx.current = outbuffer; cw_pack_true(&pack_ctx); @@ -220,7 +221,7 @@ int main(int argc, const char * argv[]) check_pack_result("c2",0); TESTP(boolean,0,"c2"); TESTP(boolean,1,"c3"); - + // TESTP unsigned int TESTP(unsigned,0,"00"); TESTP(unsigned,127,"7f"); @@ -229,10 +230,11 @@ int main(int argc, const char * argv[]) TESTP(unsigned,256,"cd0100"); TESTP(unsigned,65535,"cdffff"); TESTP(unsigned,65536,"ce00010000"); + TESTP(unsigned,500000000,"ce1dcd6500"); TESTP(unsigned,0xffffffffUL,"ceffffffff"); TESTP(unsigned,0x100000000ULL,"cf0000000100000000"); TESTP(unsigned,0xffffffffffffffffULL,"cfffffffffffffffff"); - + // TESTP signed int TESTP(signed,-1,"ff"); TESTP(signed,-32,"e0"); @@ -241,7 +243,7 @@ int main(int argc, const char * argv[]) TESTP(signed,-129,"d1ff7f"); TESTP(signed,-32768,"d18000"); TESTP(signed,-32769,"d2ffff7fff"); - + // TESTP real float f1 = (float)3.14; TESTP(float,0.0,"ca00000000"); @@ -251,31 +253,33 @@ int main(int argc, const char * argv[]) TESTP(double,f1,"cb40091eb860000000"); TESTP(double,3.14,"cb40091eb851eb851f"); TESTP(double,37.25,"cb4042a00000000000"); - TESTP(real,37.25,"ca42150000"); - TESTP(real,3.14,"cb40091eb851eb851f"); - + TESTP(double_opt,37.25,"ca42150000"); + TESTP(double_opt,f1,"ca4048f5c3"); + TESTP(double_opt,3.14,"cb40091eb851eb851f"); + TESTP(double_opt,-32,"e0"); + // TESTP array TESTP(array_size,0,"90"); TESTP(array_size,15,"9f"); TESTP(array_size,16,"dc0010"); TESTP(array_size,65535,"dcffff"); TESTP(array_size,65536,"dd00010000"); - + // TESTP map TESTP(map_size,0,"80"); TESTP(map_size,15,"8f"); TESTP(map_size,16,"de0010"); TESTP(map_size,65535,"deffff"); TESTP(map_size,65536,"df00010000"); - - + + #define TESTP_AREA(call,len,header) \ pack_ctx.current = outbuffer; \ cw_pack_##call (&pack_ctx, TEST_area, len); \ if(pack_ctx.return_code) \ ERROR("In pack"); \ check_pack_result(header, len) - + // TESTP str TESTP_AREA(str,0,"a0"); TESTP_AREA(str,31,"bf"); @@ -284,21 +288,21 @@ int main(int argc, const char * argv[]) TESTP_AREA(str,256,"da0100"); TESTP_AREA(str,65535,"daffff"); TESTP_AREA(str,65536,"db00010000"); - + // TESTP bin TESTP_AREA(bin,0,"c400"); TESTP_AREA(bin,255,"c4ff"); TESTP_AREA(bin,256,"c50100"); TESTP_AREA(bin,65535,"c5ffff"); TESTP_AREA(bin,65536,"c600010000"); - + #define TESTP_EXT(call,type,len,header) \ pack_ctx.current = outbuffer; \ cw_pack_##call (&pack_ctx, type, TEST_area, len); \ if(pack_ctx.return_code) \ ERROR("In pack"); \ check_pack_result(header, len) - + // TESTP ext TESTP_EXT(ext,15,1,"d40f"); TESTP_EXT(ext,16,2,"d510"); @@ -310,14 +314,24 @@ int main(int argc, const char * argv[]) TESTP_EXT(ext,21,256,"c8010015"); TESTP_EXT(ext,21,65535,"c8ffff15"); TESTP_EXT(ext,21,65536,"c90001000015"); - - - + + pack_ctx.current = outbuffer; + cw_pack_time(&pack_ctx, 1, 0); + check_pack_result("d6ff00000001", 0); + pack_ctx.current = outbuffer; + cw_pack_time(&pack_ctx, 1, 2); + check_pack_result("d7ff0000000800000001", 0); + pack_ctx.current = outbuffer; + cw_pack_time(&pack_ctx, -1, 500000000); + check_pack_result("c70cff1dcd6500ffffffffffffffff", 0); + + TESTP(time_interval,-0.5,"c70cff1dcd6500ffffffffffffffff"); + //******************* TEST cwpack unpack ********************** - + char inputbuf[30]; - - + + #define TESTUP(buffer,etype) \ { \ unsigned long len = strlen(buffer)/2; \ @@ -328,21 +342,21 @@ int main(int argc, const char * argv[]) if (unpack_ctx.item.type != CWP_ITEM_##etype) \ ERROR("In unpack, type error"); \ } - + #define TESTUP_VAL(buffer,etype,var,val) \ TESTUP(buffer,etype); \ - if (unpack_ctx.item.as.var != val) \ + if (unpack_ctx.item.as.var != val) \ ERROR("In unpack, value error"); unsigned long blob_length = 0; - + // TESTUP NIL TESTUP("c0",NIL); - + // TESTUP boolean TESTUP_VAL("c2",BOOLEAN,boolean,false); TESTUP_VAL("c3",BOOLEAN,boolean,true); - + // TESTUP unsigned int TESTUP_VAL("00",POSITIVE_INTEGER,u64,0) TESTUP_VAL("7f",POSITIVE_INTEGER,u64,127) @@ -354,7 +368,7 @@ int main(int argc, const char * argv[]) TESTUP_VAL("ceffffffff",POSITIVE_INTEGER,u64,0xffffffffUL) TESTUP_VAL("cf0000000100000000",POSITIVE_INTEGER,u64,0x100000000ULL) TESTUP_VAL("cfffffffffffffffff",POSITIVE_INTEGER,u64,0xffffffffffffffffULL) - + // TESTUP signed int TESTUP_VAL("ff",NEGATIVE_INTEGER,i64,-1) TESTUP_VAL("e0",NEGATIVE_INTEGER,i64,-32) @@ -364,7 +378,7 @@ int main(int argc, const char * argv[]) TESTUP_VAL("d18000",NEGATIVE_INTEGER,i64,-32768) TESTUP_VAL("d2ffff7fff",NEGATIVE_INTEGER,i64,-32769) TESTUP_VAL("d3ffffffff7fffffff",NEGATIVE_INTEGER,i64,-2147483649) - + // TESTUP real // float f1 = 3.14; TESTUP_VAL("ca00000000",FLOAT,real,0.0) @@ -372,22 +386,29 @@ int main(int argc, const char * argv[]) TESTUP_VAL("cb0000000000000000",DOUBLE,long_real,0.0) TESTUP_VAL("cb40091eb860000000",DOUBLE,long_real,f1) TESTUP_VAL("cb40091eb851eb851f",DOUBLE,long_real,3.14) - + // TESTUP array TESTUP_VAL("90",ARRAY,array.size,0) TESTUP_VAL("9f",ARRAY,array.size,15) TESTUP_VAL("dc0010",ARRAY,array.size,16) TESTUP_VAL("dcffff",ARRAY,array.size,65535) TESTUP_VAL("dd00010000",ARRAY,array.size,65536) - + // TESTUP map TESTUP_VAL("80",MAP,map.size,0) TESTUP_VAL("8f",MAP,map.size,15) TESTUP_VAL("de0010",MAP,map.size,16) TESTUP_VAL("deffff",MAP,map.size,65535) TESTUP_VAL("df00010000",MAP,map.size,65536) - - + + // TESTUP timeStamp + TESTUP_VAL("d6ff00000001",TIMESTAMP,time.tv_sec,1); + TESTUP_VAL("d6ff00000001",TIMESTAMP,time.tv_nsec,0); + TESTUP_VAL("d7ff0000000800000001",TIMESTAMP,time.tv_sec,1); + TESTUP_VAL("d7ff0000000800000001",TIMESTAMP,time.tv_nsec,2); + TESTUP_VAL("c70cff1dcd6500ffffffffffffffff",TIMESTAMP,time.tv_sec,-1); + TESTUP_VAL("c70cff1dcd6500ffffffffffffffff",TIMESTAMP,time.tv_nsec,500000000); + #define TESTUP_AREA(buffer,etype,blob,len) \ blob_length = len; \ TESTUP_VAL(buffer,etype,blob.length,len) \ @@ -400,14 +421,14 @@ int main(int argc, const char * argv[]) TESTUP_AREA("da0100",STR,str,256); TESTUP_AREA("daffff",STR,str,65535); TESTUP_AREA("db00010000",STR,str,65536); - + // TESTUP bin TESTUP_AREA("c400",BIN,bin,0); TESTUP_AREA("c4ff",BIN,bin,255); TESTUP_AREA("c50100",BIN,bin,256); TESTUP_AREA("c5ffff",BIN,bin,65535); TESTUP_AREA("c600010000",BIN,bin,65536); - + // TESTUP ext #define CWP_ITEM_15 15 #define CWP_ITEM_16 16 @@ -416,7 +437,7 @@ int main(int argc, const char * argv[]) #define CWP_ITEM_19 19 #define CWP_ITEM_20 20 #define CWP_ITEM_21 21 - + TESTUP_AREA("d40f",15,ext,1); TESTUP_AREA("d510",16,ext,2); TESTUP_AREA("c70311",17,ext,3); @@ -427,11 +448,11 @@ int main(int argc, const char * argv[]) TESTUP_AREA("c8010015",21,ext,256); TESTUP_AREA("c8ffff15",21,ext,65535); TESTUP_AREA("c90001000015",21,ext,65536); - - + + //******************* TEST skip *************************** - + cw_pack_context_init (&pack_ctx, outbuffer, 100, 0); cw_pack_array_size(&pack_ctx,2); cw_pack_str(&pack_ctx,"Test of skip",12); //array component @@ -444,13 +465,13 @@ int main(int argc, const char * argv[]) else { cw_unpack_context_init (&unpack_ctx, pack_ctx.start, (unsigned long)(pack_ctx.current-pack_ctx.start), 0); - + cw_skip_items (&unpack_ctx, 1); /* skip whole array */ check_unpack (0x952, CWP_RC_OK); check_unpack (0, CWP_RC_END_OF_INPUT); } - - + + //************************************************************* printf("CWPack module test completed, "); @@ -459,15 +480,15 @@ int main(int argc, const char * argv[]) case 0: printf("no errors detected\n"); break; - + case 1: printf("1 error detected\n"); break; - + default: printf("%d errors detected\n", error_count); break; } - + return error_count; } diff --git a/test/cwpack_performance_test.c b/test/cwpack_performance_test.c index 0a6ac4e..2417b1b 100644 --- a/test/cwpack_performance_test.c +++ b/test/cwpack_performance_test.c @@ -1,16 +1,31 @@ -// -// main.m -// st -// -// Created by Claes Wihlborg on 2017-01-20. -// Copyright © 2017 Claes Wihlborg. All rights reserved. -// +/* CWPack/test cwpack_performance_test.c */ +/* + The MIT License (MIT) + + Copyright (c) 2017 Claes Wihlborg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + #include #include #include "cwpack.h" -#include "cwpack_defines.h" #include "cmp.h" #include "mpack.h" #include "basic_contexts.h" @@ -25,7 +40,7 @@ static double milliseconds(void) { #define BEFORE_PTEST(code) \ cw_pack_context_init(&pc, buffer, BUF_Length, 0);\ - cmp_init(&cc, buffer, 0, b_writer);\ + cmp_init(&cc, buffer, 0, 0, b_writer);\ mpack_writer_init(&mw, buffer, BUF_Length); \ code; \ itemSize = (int)(pc.current - pc.start); \ @@ -84,64 +99,64 @@ static size_t b_writer(cmp_ctx_t *ctx, const void *data, size_t count) static void pack_test(void) { /*************** Test of pack *****************/ - + cw_pack_context pc; cmp_ctx_t cc; mpack_writer_t mw; - + int ii, itemSize; for (ii=0; iibuf + limit) > (buffer + BUF_Length)) return false; - + memcpy (data,ctx->buf,limit); ctx->buf = (uint8_t*)ctx->buf + limit; return true; @@ -198,43 +213,43 @@ static bool b_reader(struct cmp_ctx_s *ctx, void *data, size_t limit) static void unpack_test(void) { /*************** Test of unpack *****************/ - + cw_pack_context pc; cw_unpack_context uc; mpack_reader_t mr; cmp_ctx_t cc; cmp_object_t cobj; - + BEFORE_UTEST(cw_pack_nil(&pc)); UTEST("CMP", cmp_read_object(&cc, &cobj)); UTEST("MPack", mpack_read_tag(&mr)); UTEST("CWPack", cw_unpack_next(&uc)); AFTER_UTEST; - + BEFORE_UTEST(cw_pack_signed(&pc, -1)); UTEST("CMP", cmp_read_object(&cc, &cobj)); UTEST("MPack", mpack_read_tag(&mr)); UTEST("CWPack", cw_unpack_next(&uc)); AFTER_UTEST; - + BEFORE_UTEST(cw_pack_signed(&pc, 100000)); UTEST("CMP", cmp_read_object(&cc, &cobj)); UTEST("MPack", mpack_read_tag(&mr)); UTEST("CWPack", cw_unpack_next(&uc)); AFTER_UTEST; - + BEFORE_UTEST(cw_pack_float(&pc, (float)3.14)); UTEST("CMP", cmp_read_object(&cc, &cobj)); UTEST("MPack", mpack_read_tag(&mr)); UTEST("CWPack", cw_unpack_next(&uc)); AFTER_UTEST; - + BEFORE_UTEST(cw_pack_double(&pc, 3.14)); UTEST("CMP", cmp_read_object(&cc, &cobj)); UTEST("MPack", mpack_read_tag(&mr)); UTEST("CWPack", cw_unpack_next(&uc)); AFTER_UTEST; - + BEFORE_UTEST(cw_pack_str(&pc, "Claes",5)); UTEST("CMP", cmp_read_object(&cc, &cobj)); UTEST("MPack", mpack_skip_bytes(&mr,mpack_expect_str(&mr));mpack_done_str(&mr)); diff --git a/test/runModuleTest.sh b/test/runModuleTest.sh index b8a54b9..aa48e59 100755 --- a/test/runModuleTest.sh +++ b/test/runModuleTest.sh @@ -1,3 +1,3 @@ -clang -O3 -I ../src/ -o cwpackModuleTest cwpack_module_test.c ../src/cwpack.c +clang -O3 -I ../src/ -I ../goodies/utils/ -o cwpackModuleTest cwpack_module_test.c ../src/cwpack.c ../goodies/utils/cwpack_utils.c ./cwpackModuleTest rm -f *.o cwpackModuleTest