From 456eddc5201e3d140c642bd2ea430e37db49b144 Mon Sep 17 00:00:00 2001 From: Hugues Delorme Date: Fri, 29 Jul 2016 13:16:25 +0200 Subject: [PATCH] gmio_amf: add file format support(export only) GitHub issue #6 --- CMakeLists.txt | 5 +- appveyor.yml | 1 + src/3rdparty/base64/b64.c | 258 +++++ src/3rdparty/base64/b64.h | 9 + src/3rdparty/miloyip_itoa/branchlut.c | 293 ++++++ src/3rdparty/miloyip_itoa/branchlut.h | 13 + src/CMakeLists.txt | 12 + src/gmio_amf/amf_document.h | 367 +++++++ src/gmio_amf/amf_error.h | 88 ++ src/gmio_amf/amf_global.h | 66 ++ src/gmio_amf/amf_io.c | 944 ++++++++++++++++++ src/gmio_amf/amf_io.h | 85 ++ src/gmio_amf/amf_io_options.h | 136 +++ src/gmio_core/const_string.h | 87 ++ src/gmio_core/global.h | 4 +- src/gmio_core/internal/float_format_utils.c | 67 ++ src/gmio_core/internal/float_format_utils.h | 45 + .../internal/google_doubleconversion.cpp | 78 +- .../internal/google_doubleconversion.h | 8 + src/gmio_core/internal/itoa.h | 76 ++ src/gmio_core/internal/ostringstream.c | 214 ++++ src/gmio_core/internal/ostringstream.h | 180 ++++ src/gmio_core/internal/string.h | 31 - src/gmio_core/text_format.h | 3 +- src/gmio_stl/internal/stla_write.c | 36 +- src/gmio_stl/stla_read.c | 1 + tests/CMakeLists.txt | 15 +- tests/main_test_amf.c | 55 + tests/main_test_core.c | 1 + tests/stream_buffer.h | 2 +- tests/test_amf_io.c | 268 +++++ tests/test_core_internal.c | 84 ++ 32 files changed, 3441 insertions(+), 91 deletions(-) create mode 100644 src/3rdparty/base64/b64.c create mode 100644 src/3rdparty/base64/b64.h create mode 100644 src/3rdparty/miloyip_itoa/branchlut.c create mode 100644 src/3rdparty/miloyip_itoa/branchlut.h create mode 100644 src/gmio_amf/amf_document.h create mode 100644 src/gmio_amf/amf_error.h create mode 100644 src/gmio_amf/amf_global.h create mode 100644 src/gmio_amf/amf_io.c create mode 100644 src/gmio_amf/amf_io.h create mode 100644 src/gmio_amf/amf_io_options.h create mode 100644 src/gmio_core/const_string.h create mode 100644 src/gmio_core/internal/float_format_utils.c create mode 100644 src/gmio_core/internal/float_format_utils.h create mode 100644 src/gmio_core/internal/itoa.h create mode 100644 src/gmio_core/internal/ostringstream.c create mode 100644 src/gmio_core/internal/ostringstream.h create mode 100644 tests/main_test_amf.c create mode 100644 tests/test_amf_io.c diff --git a/CMakeLists.txt b/CMakeLists.txt index a3c3557..f39d062 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -323,8 +323,11 @@ if(MSVC) #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Oi /Qvec-report:2") # Treat some warnings as errors - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /we4133") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /we4013") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /we4024") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /we4057") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /we4090") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /we4133") # Enable /Zc:strictStrings when msvc_ver > 2012 and build_type != Debug if((MSVC_VERSION GREATER 1700) AND NOT (CMAKE_BUILD_TYPE STREQUAL "Debug")) diff --git a/appveyor.yml b/appveyor.yml index 9bd76a0..14df15b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -43,5 +43,6 @@ build_script: - cmake --build . --config %CONFIGURATION% - cmake --build . --target test_core --config %CONFIGURATION% - cmake --build . --target test_stl --config %CONFIGURATION% + - cmake --build . --target test_amf --config %CONFIGURATION% - set PATH=%APPVEYOR_BUILD_FOLDER%\build\src\%CONFIGURATION%;%PATH% - cd tests && ctest . -V -C %CONFIGURATION% diff --git a/src/3rdparty/base64/b64.c b/src/3rdparty/base64/b64.c new file mode 100644 index 0000000..163ab90 --- /dev/null +++ b/src/3rdparty/base64/b64.c @@ -0,0 +1,258 @@ +/*********************************************************************\ + +MODULE NAME: b64.c + +AUTHOR: Bob Trower 2001/08/04 + +PROJECT: Crypt Data Packaging + +COPYRIGHT: Copyright (c) Trantor Standard Systems Inc., 2001 + +NOTES: This source code may be used as you wish, subject to + the MIT license. See the LICENCE section below. + + Canonical source should be at: + http://base64.sourceforge.net + +DESCRIPTION: + This little utility implements the Base64 + Content-Transfer-Encoding standard described in + RFC1113 (http://www.faqs.org/rfcs/rfc1113.html). + + This is the coding scheme used by MIME to allow + binary data to be transferred by SMTP mail. + + Groups of 3 bytes from a binary stream are coded as + groups of 4 bytes in a text stream. + + The input stream is 'padded' with zeros to create + an input that is an even multiple of 3. + + A special character ('=') is used to denote padding so + that the stream can be decoded back to its exact size. + + Encoded output is formatted in lines which should + be a maximum of 72 characters to conform to the + specification. This program defaults to 72 characters, + but will allow more or less through the use of a + switch. The program enforces a minimum line size + of 4 characters. + + Example encoding: + + The stream 'ABCD' is 32 bits long. It is mapped as + follows: + + ABCD + + A (65) B (66) C (67) D (68) (None) (None) + 01000001 01000010 01000011 01000100 + + 16 (Q) 20 (U) 9 (J) 3 (D) 17 (R) 0 (A) NA (=) NA (=) + 010000 010100 001001 000011 010001 000000 000000 000000 + + + QUJDRA== + + Decoding is the process in reverse. A 'decode' lookup + table has been created to avoid string scans. + +DESIGN GOALS: Specifically: + Code is a stand-alone utility to perform base64 + encoding/decoding. It should be genuinely useful + when the need arises and it meets a need that is + likely to occur for some users. + Code acts as sample code to show the author's + design and coding style. + + Generally: + This program is designed to survive: + Everything you need is in a single source file. + It compiles cleanly using a vanilla ANSI C compiler. + It does its job correctly with a minimum of fuss. + The code is not overly clever, not overly simplistic + and not overly verbose. + Access is 'cut and paste' from a web page. + Terms of use are reasonable. + +VALIDATION: Non-trivial code is never without errors. This + file likely has some problems, since it has only + been tested by the author. It is expected with most + source code that there is a period of 'burn-in' when + problems are identified and corrected. That being + said, it is possible to have 'reasonably correct' + code by following a regime of unit test that covers + the most likely cases and regression testing prior + to release. This has been done with this code and + it has a good probability of performing as expected. + + Unit Test Cases: + + case 0:empty file: + CASE0.DAT -> -> + (Zero length target file created + on both encode and decode.) + + case 1:One input character: + CASE1.DAT A -> QQ== -> A + + case 2:Two input characters: + CASE2.DAT AB -> QUI= -> AB + + case 3:Three input characters: + CASE3.DAT ABC -> QUJD -> ABC + + case 4:Four input characters: + case4.dat ABCD -> QUJDRA== -> ABCD + + case 5:All chars from 0 to ff, linesize set to 50: + + AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIj + JCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZH + SElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWpr + bG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P + kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKz + tLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX + 2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7 + /P3+/w== + + case 6:Mime Block from e-mail: + (Data same as test case 5) + + case 7: Large files: + Tested 28 MB file in/out. + + case 8: Random Binary Integrity: + This binary program (b64.exe) was encoded to base64, + back to binary and then executed. + + case 9 Stress: + All files in a working directory encoded/decoded + and compared with file comparison utility to + ensure that multiple runs do not cause problems + such as exhausting file handles, tmp storage, etc. + + ------------- + + Syntax, operation and failure: + All options/switches tested. Performs as + expected. + + case 10: + No Args -- Shows Usage Screen + Return Code 1 (Invalid Syntax) + case 11: + One Arg (invalid) -- Shows Usage Screen + Return Code 1 (Invalid Syntax) + case 12: + One Arg Help (-?) -- Shows detailed Usage Screen. + Return Code 0 (Success -- help request is valid). + case 13: + One Arg Help (-h) -- Shows detailed Usage Screen. + Return Code 0 (Success -- help request is valid). + case 14: + One Arg (valid) -- Uses stdin/stdout (filter) + Return Code 0 (Sucess) + case 15: + Two Args (invalid file) -- shows system error. + Return Code 2 (File Error) + case 16: + Encode non-existent file -- shows system error. + Return Code 2 (File Error) + case 17: + Out of disk space -- shows system error. + Return Code 3 (File I/O Error) + case 18: + Too many args -- shows system error. + Return Code 1 (Invalid Syntax) + + ------------- + + Compile/Regression test: + gcc compiled binary under Cygwin + Microsoft Visual Studio under Windows 2000 + Microsoft Version 6.0 C under Windows 2000 + +DEPENDENCIES: None + +LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc. + + 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. + +VERSION HISTORY: + Bob Trower 2001/08/04 -- Create Version 0.00.00B + Bob Trower 2001/08/17 -- Correct documentation, messages. + -- Correct help for linesize syntax. + -- Force error on too many arguments. + Bob Trower 2001/08/19 -- Add sourceforge.net reference to + help screen prior to release. + Bob Trower 2004/10/22 -- Cosmetics for package/release. + Bob Trower 2008/02/28 -- More Cosmetics for package/release. + Bob Trower 2011/02/14 -- Cast altered to fix warning in VS6. + Bob Trower 2015/10/29 -- Change *bug* from 0 to EOF in putc + invocation. BIG shout out to people + reviewing on sourceforge, + particularly rachidc, jorgeventura + and zeroxia. + -- Change date format to conform with + latest convention. + +\******************************************************************* */ + +#include "b64.h" + +/* +** Translation Table as described in RFC1113 +*/ +static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* +** Translation Table to decode (created by author) +*/ +static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq"; + +/* +** encodeblock +** +** encode 3 8-bit binary bytes as 4 '6-bit' characters +*/ +void b64_encodeblock( const unsigned char *in, unsigned char *out, size_t len ) +{ + out[0] = (unsigned char) cb64[ (int)(in[0] >> 2) ]; + out[1] = (unsigned char) cb64[ (int)(((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4)) ]; + out[2] = (unsigned char) (len > 1 ? cb64[ (int)(((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6)) ] : '='); + out[3] = (unsigned char) (len > 2 ? cb64[ (int)(in[2] & 0x3f) ] : '='); +} + +/* +** decodeblock +** +** decode 4 '6-bit' characters into 3 8-bit binary bytes +*/ +void b64_decodeblock( const unsigned char *in, unsigned char *out ) +{ + out[ 0 ] = (unsigned char ) (in[0] << 2 | in[1] >> 4); + out[ 1 ] = (unsigned char ) (in[1] << 4 | in[2] >> 2); + out[ 2 ] = (unsigned char ) (((in[2] << 6) & 0xc0) | in[3]); +} + diff --git a/src/3rdparty/base64/b64.h b/src/3rdparty/base64/b64.h new file mode 100644 index 0000000..f29fb2d --- /dev/null +++ b/src/3rdparty/base64/b64.h @@ -0,0 +1,9 @@ +#ifndef BASE64_B64_H +#define BASE64_B64_H + +#include + +void b64_encodeblock(const unsigned char* in, unsigned char* out, size_t len); +void b64_decodeblock(const unsigned char* in, unsigned char* out); + +#endif /* BASE64_B64_H */ diff --git a/src/3rdparty/miloyip_itoa/branchlut.c b/src/3rdparty/miloyip_itoa/branchlut.c new file mode 100644 index 0000000..24a8147 --- /dev/null +++ b/src/3rdparty/miloyip_itoa/branchlut.c @@ -0,0 +1,293 @@ +/* + * Original code from Milo Yip + * https://github.com/miloyip/itoa-benchmark/src/branchlut.cpp + * commit #27c6059 + * + * Adapted to be C90 compatible + */ + +#include "branchlut.h" + +static const char gDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' +}; + +/* + * Branching for different cases (forward) + * Use lookup table of two digits + */ + +char* u32toa_branchlut(uint32_t value, char* buffer) { + if (value < 10000) { + const uint32_t d1 = (value / 100) << 1; + const uint32_t d2 = (value % 100) << 1; + + if (value >= 1000) + *buffer++ = gDigitsLut[d1]; + if (value >= 100) + *buffer++ = gDigitsLut[d1 + 1]; + if (value >= 10) + *buffer++ = gDigitsLut[d2]; + *buffer++ = gDigitsLut[d2 + 1]; + } + else if (value < 100000000) { + /* value = bbbbcccc */ + const uint32_t b = value / 10000; + const uint32_t c = value % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = gDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = gDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = gDigitsLut[d2]; + *buffer++ = gDigitsLut[d2 + 1]; + + *buffer++ = gDigitsLut[d3]; + *buffer++ = gDigitsLut[d3 + 1]; + *buffer++ = gDigitsLut[d4]; + *buffer++ = gDigitsLut[d4 + 1]; + } + else { + /* value = aabbbbcccc in decimal */ + + const uint32_t a = value / 100000000; /* 1 to 42 */ + value %= 100000000; + + if (a >= 10) { + const unsigned i = a << 1; + *buffer++ = gDigitsLut[i]; + *buffer++ = gDigitsLut[i + 1]; + } + else + *buffer++ = '0' + (char)a; + + { + const uint32_t b = value / 10000; /* 0 to 9999 */ + const uint32_t c = value % 10000; /* 0 to 9999 */ + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + *buffer++ = gDigitsLut[d1]; + *buffer++ = gDigitsLut[d1 + 1]; + *buffer++ = gDigitsLut[d2]; + *buffer++ = gDigitsLut[d2 + 1]; + *buffer++ = gDigitsLut[d3]; + *buffer++ = gDigitsLut[d3 + 1]; + *buffer++ = gDigitsLut[d4]; + *buffer++ = gDigitsLut[d4 + 1]; + } + } + /**buffer++ = '\0';*/ + return buffer; +} + +char* i32toa_branchlut(int32_t value, char* buffer) { + uint32_t u = (uint32_t)value; + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u32toa_branchlut(u, buffer); +} + +#ifdef GMIO_HAVE_INT64_TYPE +/* C90 does not support ULL literals */ +#define GMIO_UINT64_C(high32, low32) (((uint64_t)(high32) << 32) | (low32)) +static const uint64_t one_e16 = GMIO_UINT64_C(0x002386f2, 0x6fc10000); +static const uint64_t one_e15 = GMIO_UINT64_C(0x00038d7e, 0xa4c68000); +static const uint64_t one_e14 = GMIO_UINT64_C(0x00005af3, 0x107a4000); +static const uint64_t one_e13 = GMIO_UINT64_C(0x00000918, 0x4e72a000); +static const uint64_t one_e12 = GMIO_UINT64_C(0x000000e8, 0xd4a51000); +static const uint64_t one_e11 = GMIO_UINT64_C(0x00000017, 0x4876e800); +static const uint64_t one_e10 = GMIO_UINT64_C(0x00000002, 0x540be400); + +char* u64toa_branchlut(uint64_t value, char* buffer) { + if (value < 100000000) { + uint32_t v = (uint32_t)value; + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buffer++ = gDigitsLut[d1]; + if (v >= 100) + *buffer++ = gDigitsLut[d1 + 1]; + if (v >= 10) + *buffer++ = gDigitsLut[d2]; + *buffer++ = gDigitsLut[d2 + 1]; + } + else { + /* value = bbbbcccc */ + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = gDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = gDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = gDigitsLut[d2]; + *buffer++ = gDigitsLut[d2 + 1]; + + *buffer++ = gDigitsLut[d3]; + *buffer++ = gDigitsLut[d3 + 1]; + *buffer++ = gDigitsLut[d4]; + *buffer++ = gDigitsLut[d4 + 1]; + } + } + else if (value < one_e16) { + const uint32_t v0 = (uint32_t)(value / 100000000); + const uint32_t v1 = (uint32_t)(value % 100000000); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + if (value >= one_e15) + *buffer++ = gDigitsLut[d1]; + if (value >= one_e14) + *buffer++ = gDigitsLut[d1 + 1]; + if (value >= one_e13) + *buffer++ = gDigitsLut[d2]; + if (value >= one_e12) + *buffer++ = gDigitsLut[d2 + 1]; + if (value >= one_e11) + *buffer++ = gDigitsLut[d3]; + if (value >= one_e10) + *buffer++ = gDigitsLut[d3 + 1]; + if (value >= 1000000000) + *buffer++ = gDigitsLut[d4]; + if (value >= 100000000) + *buffer++ = gDigitsLut[d4 + 1]; + + *buffer++ = gDigitsLut[d5]; + *buffer++ = gDigitsLut[d5 + 1]; + *buffer++ = gDigitsLut[d6]; + *buffer++ = gDigitsLut[d6 + 1]; + *buffer++ = gDigitsLut[d7]; + *buffer++ = gDigitsLut[d7 + 1]; + *buffer++ = gDigitsLut[d8]; + *buffer++ = gDigitsLut[d8 + 1]; + } + else { + const uint32_t a = (uint32_t)(value / one_e16); /* 1 to 1844 */ + value %= one_e16; + + if (a < 10) + *buffer++ = '0' + (char)a; + else if (a < 100) { + const uint32_t i = a << 1; + *buffer++ = gDigitsLut[i]; + *buffer++ = gDigitsLut[i + 1]; + } + else if (a < 1000) { + const uint32_t i = (a % 100) << 1; + *buffer++ = '0' + (char)(a / 100); + *buffer++ = gDigitsLut[i]; + *buffer++ = gDigitsLut[i + 1]; + } + else { + const uint32_t i = (a / 100) << 1; + const uint32_t j = (a % 100) << 1; + *buffer++ = gDigitsLut[i]; + *buffer++ = gDigitsLut[i + 1]; + *buffer++ = gDigitsLut[j]; + *buffer++ = gDigitsLut[j + 1]; + } + + { + const uint32_t v0 = (uint32_t)(value / 100000000); + const uint32_t v1 = (uint32_t)(value % 100000000); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + *buffer++ = gDigitsLut[d1]; + *buffer++ = gDigitsLut[d1 + 1]; + *buffer++ = gDigitsLut[d2]; + *buffer++ = gDigitsLut[d2 + 1]; + *buffer++ = gDigitsLut[d3]; + *buffer++ = gDigitsLut[d3 + 1]; + *buffer++ = gDigitsLut[d4]; + *buffer++ = gDigitsLut[d4 + 1]; + *buffer++ = gDigitsLut[d5]; + *buffer++ = gDigitsLut[d5 + 1]; + *buffer++ = gDigitsLut[d6]; + *buffer++ = gDigitsLut[d6 + 1]; + *buffer++ = gDigitsLut[d7]; + *buffer++ = gDigitsLut[d7 + 1]; + *buffer++ = gDigitsLut[d8]; + *buffer++ = gDigitsLut[d8 + 1]; + } + } + + /**buffer++ = '\0';*/ + return buffer; +} + +char* i64toa_branchlut(int64_t value, char* buffer) { + uint64_t u = (uint64_t)value; + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u64toa_branchlut(u, buffer); +} +#endif /* GMIO_HAVE_INT64_TYPE */ diff --git a/src/3rdparty/miloyip_itoa/branchlut.h b/src/3rdparty/miloyip_itoa/branchlut.h new file mode 100644 index 0000000..dabe51d --- /dev/null +++ b/src/3rdparty/miloyip_itoa/branchlut.h @@ -0,0 +1,13 @@ +#ifndef MILOYIP_ITOA_BRANCHLUT_H +#define MILOYIP_ITOA_BRANCHLUT_H + +#include "../../gmio_core/global.h" + +char* u32toa_branchlut(uint32_t value, char* buffer); +char* i32toa_branchlut(int32_t value, char* buffer); +#ifdef GMIO_HAVE_INT64_TYPE +char* u64toa_branchlut(uint64_t value, char* buffer); +char* i64toa_branchlut(int64_t value, char* buffer); +#endif + +#endif /* MILOYIP_ITOA_BRANCHLUT_H */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2f2a1b4..1ce8cf5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,6 +39,12 @@ file(GLOB GMIO_SRC_FILES gmio_core/* gmio_core/internal/*) if(GMIO_STR2FLOAT_LIBCODE EQUAL 2 OR GMIO_FLOAT2STR_LIBCODE EQUAL 2) file(GLOB GMIO_3RDPARTY_FILES 3rdparty/double-conversion/*) endif() +set(GMIO_3RDPARTY_FILES + ${GMIO_3RDPARTY_FILES} + 3rdparty/miloyip_itoa/branchlut.h + 3rdparty/miloyip_itoa/branchlut.c + 3rdparty/base64/b64.h + 3rdparty/base64/b64.c) set(GMIO_SRC_FILES ${GMIO_SRC_FILES} ${GMIO_3RDPARTY_FILES}) configure_file(gmio_core/version.h.cmake gmio_core/version.h @ONLY) @@ -59,6 +65,12 @@ set(GMIO_SRC_FILES ${GMIO_SRC_FILES} ${GMIO_LIBSTL_SRC_FILES}) file(GLOB GMIO_LIBSTL_HEADERS gmio_stl/*.h) install(FILES ${GMIO_LIBSTL_HEADERS} DESTINATION include/gmio_stl) +# Module libAMF +file(GLOB GMIO_LIBAMF_SRC_FILES gmio_amf/* gmio_amf/internal/*) +set(GMIO_SRC_FILES ${GMIO_SRC_FILES} ${GMIO_LIBAMF_SRC_FILES}) + +file(GLOB GMIO_LIBAMF_HEADERS gmio_amf/*.h) +install(FILES ${GMIO_LIBAMF_HEADERS} DESTINATION include/gmio_amf) # Common for support modules install(FILES gmio_support/support_global.h DESTINATION include/gmio_support) diff --git a/src/gmio_amf/amf_document.h b/src/gmio_amf/amf_document.h new file mode 100644 index 0000000..a2158b4 --- /dev/null +++ b/src/gmio_amf/amf_document.h @@ -0,0 +1,367 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +/*! \file amf_document.h + * Structures for AMF document + * + * \addtogroup gmio_amf + * @{ + */ + +#ifndef GMIO_AMF_DOCUMENT_H +#define GMIO_AMF_DOCUMENT_H + +#include "amf_global.h" + +#include "../gmio_core/endian.h" +#include "../gmio_core/memblock.h" +#include "../gmio_core/vecgeom.h" + +#include + +struct gmio_amf_metadata +{ + const char* type; /* UTF8-encoded */ + const char* data; /* UTF8-encoded */ +}; + +struct gmio_amf_color +{ + double r; /* in [0,1] */ + double g; /* in [0,1] */ + double b; /* in [0,1] */ + double a; /* in [0,1] optional */ + const char* r_formula; + const char* g_formula; + const char* b_formula; + const char* a_formula; +}; + +struct gmio_amf_material +{ + uint32_t id; /* XML:integer */ + struct gmio_amf_color color; + uint32_t composite_count; + uint32_t metadata_count; +}; + +struct gmio_amf_composite +{ + uint32_t materialid; /* XML:nonNegativeInteger, required */ + double value; /* governs the percent of material */ + const char* value_formula; +}; + +struct gmio_amf_vertex +{ + struct gmio_vec3d coords; + bool has_normal; + struct gmio_vec3d normal; /* XML:NegOneToOne: -1 <= coord <= 1 */ + bool has_color; + struct gmio_amf_color color; /* XML:Color */ + uint32_t metadata_count; +}; + +struct gmio_amf_edge +{ + uint32_t v1; /* XML:nonNegativeInteger */ + uint32_t v2; /* XML:nonNegativeInteger */ + /* Direction vectors */ + struct gmio_vec3d d1; /* XML:NegOneToOne: -1 <= coord <= 1 */ + struct gmio_vec3d d2; /* XML:NegOneToOne: -1 <= coord <= 1 */ +}; + +struct gmio_amf_texmap +{ + uint32_t rtexid; /* XML:nonNegativeInteger */ + uint32_t gtexid; /* XML:nonNegativeInteger */ + uint32_t btexid; /* XML:nonNegativeInteger */ + uint32_t atexid; /* XML:nonNegativeInteger */ + struct gmio_vec3d utex; /* XML:Coords : any value */ + struct gmio_vec3d vtex; /* XML:Coords : any value */ + bool has_wtex; + struct gmio_vec3d wtex; /* XML:Coords : any value */ +}; + +struct gmio_amf_triangle +{ + uint32_t v1; /* XML:nonNegativeInteger */ + uint32_t v2; /* XML:nonNegativeInteger */ + uint32_t v3; /* XML:nonNegativeInteger */ + bool has_texmap; + struct gmio_amf_texmap texmap; /* XML:TexMap */ + bool has_color; + struct gmio_amf_color color; /* XML:Color */ +}; + +enum gmio_amf_volume_type +{ + GMIO_AMF_VOLUME_TYPE_OBJECT = 0, + GMIO_AMF_VOLUME_TYPE_SUPPORT +}; + +struct gmio_amf_volume +{ + uint32_t materialid; /* XML:nonNegativeInteger */ + enum gmio_amf_volume_type type; + uint32_t triangle_count; /* Should be >= 4 */ + uint32_t metadata_count; + bool has_color; + struct gmio_amf_color color; /* XML:Color */ +}; + +struct gmio_amf_mesh +{ + uint32_t vertex_count; + uint32_t edge_count; + uint32_t volume_count; +}; + +struct gmio_amf_object +{ + uint32_t id; /* XML:integer */ + uint32_t mesh_count; + uint32_t metadata_count; + bool has_color; + struct gmio_amf_color color; /* XML:Color */ +}; + +struct gmio_amf_constellation +{ + uint32_t id; /* XML:integer */ + uint32_t instance_count; /* Should be >= 2 */ + uint32_t metadata_count; +}; + +struct gmio_amf_instance +{ + uint32_t objectid; /* XML:nonNegativeInteger */ + struct gmio_vec3d delta; /* Any value */ + struct gmio_vec3d rot; /* XML:Degrees: -360 <= coord < 360 */ +}; + +enum gmio_amf_texture_type +{ + GMIO_AMF_TEXTURE_TYPE_GRAYSCALE +}; + +struct gmio_amf_texture +{ + uint32_t id; /* XML:integer */ + uint32_t width; /* XML:nonNegativeInteger */ + uint32_t height; /* XML:nonNegativeInteger */ + uint32_t depth; /* XML:nonNegativeInteger */ + bool tiled; + enum gmio_amf_texture_type type; + struct gmio_memblock binary_data; /* Will be converted to base64 */ +}; + +enum gmio_amf_unit +{ + GMIO_AMF_UNIT_UNKNOWN, + GMIO_AMF_UNIT_MILLIMETER, + GMIO_AMF_UNIT_INCH, + GMIO_AMF_UNIT_FEET, + GMIO_AMF_UNIT_METER, + GMIO_AMF_UNIT_MICRON +}; + +enum gmio_amf_document_element +{ + GMIO_AMF_DOCUMENT_ELEMENT_OBJECT, + GMIO_AMF_DOCUMENT_ELEMENT_MATERIAL, + GMIO_AMF_DOCUMENT_ELEMENT_TEXTURE, + GMIO_AMF_DOCUMENT_ELEMENT_CONSTELLATION, + GMIO_AMF_DOCUMENT_ELEMENT_METADATA +}; + +enum gmio_amf_mesh_element +{ + GMIO_AMF_MESH_ELEMENT_VERTEX, + GMIO_AMF_MESH_ELEMENT_EDGE, + GMIO_AMF_MESH_ELEMENT_VOLUME +}; + +struct gmio_amf_object_mesh_element_index +{ + uint32_t object_index; + uint32_t mesh_index; + uint32_t value; +}; + +/* Note: version is 1.2 */ +struct gmio_amf_document +{ + /*! Opaque pointer on the user AMF document, passed as first argument to + * hook functions */ + const void* cookie; + + enum gmio_amf_unit unit; + uint32_t object_count; /* Must be >= 1 */ + uint32_t material_count; + uint32_t texture_count; + uint32_t constellation_count; + uint32_t metadata_count; + + /*! Pointer on a function that retrieves the i-th document sub-element + * + * \p element is the type of the sub-element of interest.\n + * \p element_index is the index of the sub-element within the AMF + * document.\n + * The domain of this index depends on \p element : + * + * + * + * + * + * + * + *
Element type Domain of index
OBJECT [0 .. object_count[
MATERIAL [0 .. material_count[
TEXTURE [0 .. texture_count[
CONSTELLATION [0 .. constellation_count[
METADATA [0 .. metadata_count[
+ * + * + * + * + * + * + * + * + *
Element type gmio type
OBJECT gmio_amf_object
MATERIAL gmio_amf_material
TEXTURE gmio_amf_texture
CONSTELLATION gmio_amf_constellation
METADATA gmio_amf_metadata
+ */ + void (*func_get_document_element)( + const void* cookie, + enum gmio_amf_document_element element, + uint32_t element_index, + void* ptr_element); + + /*! Pointer on a function that retrieves the i-th \c composite within a + * \c material element */ + void (*func_get_material_composite)( + const void* cookie, + uint32_t material_index, + uint32_t composite_index, + struct gmio_amf_composite* ptr_composite); + + /*! Pointer on a function that retrieves the i-th \c instance within a + * \c constellation element */ + void (*func_get_constellation_instance)( + const void* cookie, + uint32_t constellation_index, + uint32_t instance_index, + struct gmio_amf_instance* ptr_instance); + + /*! Pointer on a function that retrieves the i-th \c mesh within an + * \c object element */ + void (*func_get_object_mesh)( + const void* cookie, + uint32_t object_index, + uint32_t mesh_index, + struct gmio_amf_mesh* ptr_mesh); + + /*! Pointer on a function that retrieves the i-th sub-element of a \c mesh + * element + * + * \p element is the type of the sub-element of interest.\n + * \p element_index is the index of the sub-element within the \c mesh + * element.\n + * The domain of this index(ie. \c value field) depends on \p element : + * + * + * + * + * + *
Element type Domain of index
VERTEX [0 .. gmio_amf_mesh::vertex_count[
EDGE [0 .. gmio_amf_mesh::edge_count[
VOLUME [0 .. gmio_amf_mesh::volume_count[
+ * + * The concrete type of \p ptr_struct_element depends on \p element : + * + * + * + * + * + *
Element type gmio type
VERTEX gmio_amf_vertex
EDGE gmio_amf_edge
VOLUME gmio_amf_volume
+ */ + void (*func_get_object_mesh_element)( + const void* cookie, + enum gmio_amf_mesh_element element, + const struct gmio_amf_object_mesh_element_index* element_index, + void* ptr_element); + + /*! Pointer on a function that retrieves the i-th \c triangle within a + * mesh \c volume element */ + void (*func_get_object_mesh_volume_triangle)( + const void* cookie, + const struct gmio_amf_object_mesh_element_index* volume_index, + uint32_t triangle_index, + struct gmio_amf_triangle* ptr_triangle); + + /* Function pointers to retrieve metadata */ + + /*! Pointer on a function that retrieves the i-th metadata assigned to a + * document sub-element(being \c material, \c object or \c constellation ) + * + * \p element is the type of the sub-element of interest, it can + * take one of this value : + * \li GMIO_AMF_DOCUMENT_ELEMENT_MATERIAL + * \li GMIO_AMF_DOCUMENT_ELEMENT_OBJECT + * \li GMIO_AMF_DOCUMENT_ELEMENT_CONSTELLATION + * + * \p element_index is the index of the sub-element within the AMF + * document.\n + * The domain of this index depends on \p element : + * + * + * + * + * + *
Element type Domain of index
MATERIAL [0 .. material_count[
OBJECT [0 .. object_count[
CONSTELLATION [0 .. constellation_count[
+ */ + void (*func_get_document_element_metadata)( + const void* cookie, + enum gmio_amf_document_element element, + uint32_t element_index, + uint32_t metadata_index, + struct gmio_amf_metadata* ptr_metadata); + + /*! Pointer on a function that retrieves the i-th metadata assigned to a + * mesh vertex */ + void (*func_get_object_mesh_vertex_metadata)( + const void* cookie, + const struct gmio_amf_object_mesh_element_index* vertex_index, + uint32_t metadata_index, + struct gmio_amf_metadata* ptr_metadata); + + /*! Pointer on a function that retrieves the i-th metadata assigned to a + * mesh volume */ + void (*func_get_object_mesh_volume_metadata)( + const void* cookie, + const struct gmio_amf_object_mesh_element_index* volume_index, + uint32_t metadata_index, + struct gmio_amf_metadata* ptr_metadata); +}; + +#endif /* GMIO_AMF_DOCUMENT_H */ diff --git a/src/gmio_amf/amf_error.h b/src/gmio_amf/amf_error.h new file mode 100644 index 0000000..b1c8d76 --- /dev/null +++ b/src/gmio_amf/amf_error.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +/*! \file amf_error.h + * List of errors specific to AMF I/O functions + * + * \addtogroup gmio_amf + * @{ + */ + +#ifndef GMIO_AMF_ERROR_H +#define GMIO_AMF_ERROR_H + +/*! \c GMIO_AMF_ERROR_TAG + * Byte-mask to tag(identify) AMF-specific error codes */ +enum { GMIO_AMF_ERROR_TAG = 0x12000000 }; + +/*! This enum defines the specific error codes reported by AMF read/write + * functions */ +enum gmio_amf_error +{ + /*! The input gmio_amf_document is \c NULL */ + GMIO_AMF_ERROR_NULL_DOCUMENT = GMIO_AMF_ERROR_TAG + 0x01, + + /*! Function pointer gmio_amf_document::func_get_document_element + * is \c NULL */ + GMIO_AMF_ERROR_NULL_FUNC_GET_DOCUMENT_ELEMENT, + + /*! Function pointer gmio_amf_document::func_get_constellation_instance + * is \c NULL */ + GMIO_AMF_ERROR_NULL_FUNC_GET_CONSTELLATION_INSTANCE, + + /*! Function pointer gmio_amf_document::func_get_object_mesh is \c NULL */ + GMIO_AMF_ERROR_NULL_FUNC_GET_OBJECT_MESH, + + /*! Function pointer gmio_amf_document::func_get_object_mesh_element + * is \c NULL */ + GMIO_AMF_ERROR_NULL_FUNC_GET_OBJECT_MESH_ELEMENT, + + /*! Function pointer gmio_amf_document::func_get_object_mesh_volume_triangle + * is \c NULL */ + GMIO_AMF_ERROR_NULL_FUNC_GET_OBJECT_MESH_VOLUME_TRIANGLE, + + /*! Function pointer gmio_amf_document::func_get_document_element_metadata + * is \c NULL while some gmio_amf_material::metadata_count > 0 */ + GMIO_AMF_ERROR_NULL_FUNC_GET_DOCUMENT_ELEMENT_METADATA, + + /*! Function pointer gmio_amf_document::func_get_material_composite + * is \c NULL while some gmio_amf_material::composite_count > 0 */ + GMIO_AMF_ERROR_NULL_FUNC_GET_MATERIAL_COMPOSITE, + + /*! Function pointer gmio_amf_document::func_get_object_mesh_vertex_metadata + * is \c NULL while some gmio_amf_vertex::metadata_count > 0 */ + GMIO_AMF_ERROR_NULL_FUNC_GET_OBJECT_MESH_VERTEX_METADATA, + + /*! Function pointer gmio_amf_document::func_get_object_mesh_volume_metadata + * is \c NULL while some gmio_amf_volume::metadata_count > 0 */ + GMIO_AMF_ERROR_NULL_FUNC_GET_OBJECT_MESH_VOLUME_METADATA +}; + +#endif /* GMIO_AMF_ERROR_H */ +/*! @} */ diff --git a/src/gmio_amf/amf_global.h b/src/gmio_amf/amf_global.h new file mode 100644 index 0000000..dc92177 --- /dev/null +++ b/src/gmio_amf/amf_global.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +/*! \file amf_global.h + * Global declarations for the AMF module + * + * \defgroup gmio_amf gmioAMF + * Provides API to handle input/output operations with the AMF file format + * + * In addition, the gmioAMF module has the following advatanges: + * \li The user keeps its own geometry data structures, no conversion needed. + * \li Fixed memory consumption and independant of the geometry size + * + * In this module, the name of all entities(structures, functions, ...) are + * prefixed either with \c gmio_amf + * + * + * + * + * + * + * + * + * + * + *
Functions Structures
Writegmio_amf_write()
+ * gmio_amf_write_file()
gmio_amf_document
+ * gmio_amf_write_options
+ * + * \addtogroup gmio_amf + * @{ + */ + +#ifndef GMIO_AMF_GLOBAL_H +#define GMIO_AMF_GLOBAL_H + +#include "../gmio_core/global.h" + +#endif /* GMIO_AMF_GLOBAL_H */ +/*! @} */ diff --git a/src/gmio_amf/amf_io.c b/src/gmio_amf/amf_io.c new file mode 100644 index 0000000..2d51e2a --- /dev/null +++ b/src/gmio_amf/amf_io.c @@ -0,0 +1,944 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +#include "amf_io.h" +#include "amf_error.h" + +#include "../gmio_core/error.h" +#include "../gmio_core/internal/error_check.h" +#include "../gmio_core/internal/float_format_utils.h" +#include "../gmio_core/internal/helper_memblock.h" +#include "../gmio_core/internal/helper_stream.h" +#include "../gmio_core/internal/helper_task_iface.h" +#include "../gmio_core/internal/ostringstream.h" + +#include +#include + +/* Writing(output) context */ +struct gmio_amf_wcontext +{ + const struct gmio_amf_write_options* options; + struct gmio_ostringstream* sstream; + const struct gmio_amf_document* document; + const struct gmio_task_iface* task_iface; + intmax_t task_progress_current; + intmax_t task_progress_max; + struct gmio_ostringstream_format_float f64_format; + int error; + + /* zlib specific */ + struct gmio_memblock z_memblock; + struct z_stream_s z_stream; + int z_flush; +}; + +GMIO_INLINE bool gmio_amf_wcontext_set_error( + struct gmio_amf_wcontext* context, int error) +{ + context->error = error; + return gmio_no_error(error); +} + +GMIO_INLINE void gmio_amf_wcontext_incr_task_progress( + struct gmio_amf_wcontext* context) +{ + ++(context->task_progress_current); +} + +/* Writes double value or its formula (if any) to stream */ +static void gmio_amf_write_double( + struct gmio_amf_wcontext* context, + double value, + const char* value_formula) +{ + struct gmio_ostringstream* sstream = context->sstream; + if (value_formula == NULL || *value_formula == '\0') + gmio_ostringstream_write_f64(sstream, value, &context->f64_format); + else + gmio_ostringstream_write_str(sstream, value_formula); +} + +/* Writes gmio_amf_color component to stream */ +static void gmio_amf_write_color_component( + struct gmio_amf_wcontext* context, + double value, + const char* value_formula) +{ + struct gmio_ostringstream* sstream = context->sstream; + if (value_formula == NULL || *value_formula == '\0') { + gmio_ostringstream_write_f64(sstream, value, &context->f64_format); + } + else { + gmio_ostringstream_write_xmlcdata_open(sstream); + gmio_ostringstream_write_str(sstream, value_formula); + gmio_ostringstream_write_xmlcdata_close(sstream); + } +} + +/* Writes gmio_amf_color to stream */ +static void gmio_amf_write_color( + struct gmio_amf_wcontext* context, + const struct gmio_amf_color* color) +{ + struct gmio_ostringstream* sstream = context->sstream; + gmio_ostringstream_write_chararray(sstream, ""); + gmio_amf_write_color_component(context, color->r, color->r_formula); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_amf_write_color_component(context, color->g, color->g_formula); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_amf_write_color_component(context, color->b, color->b_formula); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_amf_write_color_component(context, color->a, color->a_formula); + gmio_ostringstream_write_chararray(sstream, "\n"); +} + +/* Writes gmio_amf_metadata to stream */ +static void gmio_amf_write_metadata( + struct gmio_ostringstream* sstream, + const struct gmio_amf_metadata* metadata) +{ + gmio_ostringstream_write_chararray(sstream, "type); + gmio_ostringstream_write_char(sstream, '>'); + gmio_ostringstream_write_str(sstream, metadata->data); + gmio_ostringstream_write_chararray(sstream, "\n"); +} + +/* Writes to stream */ +static void gmio_amf_write_amf_begin( + struct gmio_ostringstream* sstream, + const struct gmio_amf_document* doc) +{ + gmio_ostringstream_write_chararray( + sstream, "\n"); + gmio_ostringstream_write_chararray(sstream, "unit != GMIO_AMF_UNIT_UNKNOWN) { + const char* unit_str = ""; + switch (doc->unit) { + case GMIO_AMF_UNIT_MILLIMETER: + unit_str = "millimeter"; break; + case GMIO_AMF_UNIT_INCH: + unit_str = "inch"; break; + case GMIO_AMF_UNIT_FEET: + unit_str = "feet"; break; + case GMIO_AMF_UNIT_METER: + unit_str = "meter"; break; + case GMIO_AMF_UNIT_MICRON: + unit_str = "micron"; break; + case GMIO_AMF_UNIT_UNKNOWN: /* Silent compiler warning */ + break; + } + gmio_ostringstream_write_xmlattr_str(sstream, "unit", unit_str); + } + gmio_ostringstream_write_xmlattr_str(sstream, "version", "1.2"); + gmio_ostringstream_write_chararray(sstream, ">\n"); +} + +/* Writes document metadata to stream */ +static bool gmio_amf_write_root_metadata( + struct gmio_amf_wcontext* context) +{ + const struct gmio_amf_document* doc = context->document; + struct gmio_amf_metadata metadata = {0}; + uint32_t imeta; + for (imeta = 0; imeta < doc->metadata_count; ++imeta) { + doc->func_get_document_element( + doc->cookie, + GMIO_AMF_DOCUMENT_ELEMENT_METADATA, imeta, &metadata); + gmio_amf_write_metadata(context->sstream, &metadata); + gmio_amf_wcontext_incr_task_progress(context); + } + return gmio_no_error(context->error); +} + +/* Writes document materials to stream */ +static bool gmio_amf_write_root_materials( + struct gmio_amf_wcontext* context) +{ + const struct gmio_amf_document* doc = context->document; + struct gmio_ostringstream* sstream = context->sstream; + struct gmio_amf_material material = {0}; + uint32_t imat; + for (imat = 0; imat < doc->material_count; ++imat) { + doc->func_get_document_element( + doc->cookie, + GMIO_AMF_DOCUMENT_ELEMENT_MATERIAL, imat, &material); + gmio_ostringstream_write_chararray(sstream, "'); + /* Write material elements */ + if (material.metadata_count > 0) { + uint32_t imeta; + struct gmio_amf_metadata metadata = {0}; + /* Check function pointer is defined */ + if (doc->func_get_document_element_metadata == NULL) { + return gmio_amf_wcontext_set_error( + context, + GMIO_AMF_ERROR_NULL_FUNC_GET_DOCUMENT_ELEMENT_METADATA); + } + for (imeta = 0; imeta < material.metadata_count; ++imeta) { + doc->func_get_document_element_metadata( + doc->cookie, + GMIO_AMF_DOCUMENT_ELEMENT_MATERIAL, + imat, + imeta, + &metadata); + gmio_amf_write_metadata(sstream, &metadata); + } + } + /* Write material element */ + gmio_amf_write_color(context, &material.color); + /* Write material elements */ + if (material.composite_count > 0) { + struct gmio_amf_composite composite = {0}; + uint32_t icomp; + /* Check function pointer is defined */ + if (doc->func_get_material_composite == NULL) { + return gmio_amf_wcontext_set_error( + context, + GMIO_AMF_ERROR_NULL_FUNC_GET_MATERIAL_COMPOSITE); + } + for (icomp = 0; icomp < material.composite_count; ++icomp) { + doc->func_get_material_composite( + doc->cookie, imat, icomp, &composite); + gmio_ostringstream_write_chararray(sstream, "'); + gmio_ostringstream_write_xmlcdata_open(sstream); + gmio_amf_write_double( + context, composite.value, composite.value_formula); + gmio_ostringstream_write_xmlcdata_close(sstream); + gmio_ostringstream_write_chararray(sstream, "\n"); + } + } + gmio_ostringstream_write_chararray(sstream, "\n"); + gmio_amf_wcontext_incr_task_progress(context); + } + return gmio_no_error(context->error); +} + +/* Write gmio_amf_texmap to stream */ +static void gmio_amf_write_texmap( + struct gmio_ostringstream* sstream, + const struct gmio_amf_texmap* texmap) +{ + /* Write triangle element */ + gmio_ostringstream_write_chararray(sstream, "rtexid); + gmio_ostringstream_write_xmlattr_u32(sstream, "gtexid", texmap->gtexid); + gmio_ostringstream_write_xmlattr_u32(sstream, "btexid", texmap->btexid); + gmio_ostringstream_write_xmlattr_u32(sstream, "atexid", texmap->atexid); + gmio_ostringstream_write_char(sstream, '>'); + /* Write triangle elements */ + gmio_ostringstream_write_xmlelt_f64(sstream, "utex1", texmap->utex.x); + gmio_ostringstream_write_xmlelt_f64(sstream, "utex2", texmap->utex.y); + gmio_ostringstream_write_xmlelt_f64(sstream, "utex3", texmap->utex.z); + /* Write triangle elements */ + gmio_ostringstream_write_xmlelt_f64(sstream, "vtex1", texmap->vtex.x); + gmio_ostringstream_write_xmlelt_f64(sstream, "vtex2", texmap->vtex.y); + gmio_ostringstream_write_xmlelt_f64(sstream, "vtex3", texmap->vtex.z); + /* Write triangle elements */ + if (texmap->has_wtex) { + gmio_ostringstream_write_xmlelt_f64(sstream, "wtex1", texmap->wtex.x); + gmio_ostringstream_write_xmlelt_f64(sstream, "wtex2", texmap->wtex.y); + gmio_ostringstream_write_xmlelt_f64(sstream, "wtex3", texmap->wtex.z); + } + gmio_ostringstream_write_chararray(sstream, "\n"); +} + +/* Writes gmio_amf_mesh to stream */ +static bool gmio_amf_write_mesh( + struct gmio_amf_wcontext* context, + const struct gmio_amf_mesh* mesh, + const struct gmio_amf_object_mesh_element_index* base_mesh_element_index) +{ + const struct gmio_amf_document* doc = context->document; + struct gmio_ostringstream* sstream = context->sstream; + struct gmio_amf_object_mesh_element_index mesh_elt_index = + *base_mesh_element_index; + const struct gmio_ostringstream_format_float* f64_format = + &context->f64_format; + struct gmio_amf_vertex vertex = {0}; + uint32_t ivert; + /* Write mesh element */ + gmio_ostringstream_write_chararray(sstream, "\n"); + for (ivert = 0; ivert < mesh->vertex_count; ++ivert) { + mesh_elt_index.value = ivert; + doc->func_get_object_mesh_element( + doc->cookie, + GMIO_AMF_MESH_ELEMENT_VERTEX, &mesh_elt_index, &vertex); + /* Write element */ + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_f64(sstream, vertex.coords.x, f64_format); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_f64(sstream, vertex.coords.y, f64_format); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_f64(sstream, vertex.coords.z, f64_format); + gmio_ostringstream_write_chararray(sstream, ""); + /* Write element */ + if (vertex.has_color) + gmio_amf_write_color(context, &vertex.color); + /* Write element */ + if (vertex.has_normal) { + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_f64(sstream, vertex.normal.x, f64_format); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_f64(sstream, vertex.normal.y, f64_format); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_f64(sstream, vertex.normal.z, f64_format); + gmio_ostringstream_write_chararray(sstream, "\n"); + } + /* Write elements */ + if (vertex.metadata_count > 0) { + struct gmio_amf_metadata metadata = {0}; + uint32_t imeta; + /* Check function pointer */ + if (doc->func_get_object_mesh_vertex_metadata == NULL) { + return gmio_amf_wcontext_set_error( + context, + GMIO_AMF_ERROR_NULL_FUNC_GET_OBJECT_MESH_VERTEX_METADATA); + } + for (imeta = 0; imeta < vertex.metadata_count; ++imeta) { + doc->func_get_object_mesh_vertex_metadata( + doc->cookie, &mesh_elt_index, imeta, &metadata); + gmio_amf_write_metadata(sstream, &metadata); + } + } + gmio_ostringstream_write_chararray(sstream, "\n"); + gmio_amf_wcontext_incr_task_progress(context); + if (gmio_error(context->error)) + return false; + } + /* Write mesh vertices elements */ + if (mesh->edge_count > 0) { + struct gmio_amf_edge edge = {0}; + uint32_t iedge; + for (iedge = 0; iedge < mesh->edge_count; ++iedge) { + mesh_elt_index.value = iedge; + doc->func_get_object_mesh_element( + doc->cookie, + GMIO_AMF_MESH_ELEMENT_EDGE, &mesh_elt_index, &edge); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_u32(sstream, edge.v1); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_f64(sstream, edge.d1.x, f64_format); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_f64(sstream, edge.d1.y, f64_format); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_f64(sstream, edge.d1.z, f64_format); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_u32(sstream, edge.v2); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_f64(sstream, edge.d2.x, f64_format); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_f64(sstream, edge.d2.y, f64_format); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_f64(sstream, edge.d2.z, f64_format); + gmio_ostringstream_write_chararray(sstream, "\n"); + gmio_amf_wcontext_incr_task_progress(context); + if (gmio_error(context->error)) + return false; + } + } + gmio_ostringstream_write_chararray(sstream, "\n"); + /* Write mesh elements */ + if (mesh->volume_count > 0) { + struct gmio_amf_volume volume = {0}; + uint32_t ivol; + for (ivol = 0; ivol < mesh->volume_count; ++ivol) { + const char* str_volume_type = ""; + mesh_elt_index.value = ivol; + doc->func_get_object_mesh_element( + doc->cookie, + GMIO_AMF_MESH_ELEMENT_VOLUME, &mesh_elt_index, &volume); + /* Write element begin */ + gmio_ostringstream_write_chararray(sstream, "'); + /* Write volume elements */ + if (volume.metadata_count > 0) { + struct gmio_amf_metadata metadata = {0}; + uint32_t imeta; + /* Check function pointer */ + if (doc->func_get_object_mesh_volume_metadata == NULL) { + return gmio_amf_wcontext_set_error( + context, + GMIO_AMF_ERROR_NULL_FUNC_GET_OBJECT_MESH_VOLUME_METADATA); + } + for (imeta = 0; imeta < volume.metadata_count; ++imeta) { + doc->func_get_object_mesh_volume_metadata( + doc->cookie, &mesh_elt_index, imeta, &metadata); + gmio_amf_write_metadata(sstream, &metadata); + } + } + /* Write volume element */ + if (volume.has_color) + gmio_amf_write_color(context, &volume.color); + /* Write elements */ + if (volume.triangle_count > 0) { + struct gmio_amf_triangle triangle = {0}; + uint32_t itri; + for (itri = 0; itri < volume.triangle_count; ++itri) { + doc->func_get_object_mesh_volume_triangle( + doc->cookie, &mesh_elt_index, itri, &triangle); + gmio_ostringstream_write_chararray(sstream, ""); + /* Write triangle element */ + if (triangle.has_color) + gmio_amf_write_color(context, &triangle.color); + /* Write triangle elements */ + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_u32(sstream, triangle.v1); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_u32(sstream, triangle.v2); + gmio_ostringstream_write_chararray(sstream, ""); + gmio_ostringstream_write_u32(sstream, triangle.v3); + gmio_ostringstream_write_chararray(sstream, ""); + /* Write triangle element */ + if (triangle.has_texmap) + gmio_amf_write_texmap(sstream, &triangle.texmap); + gmio_ostringstream_write_chararray(sstream, "\n"); + gmio_amf_wcontext_incr_task_progress(context); + if (gmio_error(context->error)) + return false; + } + } + gmio_ostringstream_write_chararray(sstream, "\n"); + } + } + gmio_ostringstream_write_chararray(sstream, "\n"); + return gmio_no_error(context->error); +} + +/* Writes document objects to stream */ +static bool gmio_amf_write_root_objects(struct gmio_amf_wcontext* context) +{ + const struct gmio_amf_document* doc = context->document; + struct gmio_ostringstream* sstream = context->sstream; + struct gmio_amf_object object = {0}; + uint32_t iobj; + for (iobj = 0; iobj < doc->object_count; ++iobj) { + doc->func_get_document_element( + doc->cookie, + GMIO_AMF_DOCUMENT_ELEMENT_OBJECT, iobj, &object); + /* Open object element */ + gmio_ostringstream_write_chararray(sstream, "'); + /* Write metadata elements */ + if (object.metadata_count > 0) { + struct gmio_amf_metadata metadata = {0}; + uint32_t imeta; + /* Check function pointer */ + if (doc->func_get_document_element_metadata == NULL) { + return gmio_amf_wcontext_set_error( + context, + GMIO_AMF_ERROR_NULL_FUNC_GET_DOCUMENT_ELEMENT_METADATA); + } + for (imeta = 0; imeta < object.metadata_count; ++imeta) { + doc->func_get_document_element_metadata( + doc->cookie, + GMIO_AMF_DOCUMENT_ELEMENT_OBJECT, + iobj, + imeta, + &metadata); + gmio_amf_write_metadata(sstream, &metadata); + } + } + /* Write color element if any */ + if (object.has_color) + gmio_amf_write_color(context, &object.color); + /* Write mesh elements */ + if (object.mesh_count > 0) { + struct gmio_amf_mesh mesh = {0}; + uint32_t imesh; + for (imesh = 0; imesh < object.mesh_count; ++imesh) { + struct gmio_amf_object_mesh_element_index base_mesh_elt_index; + doc->func_get_object_mesh(doc->cookie, iobj, imesh, &mesh); + base_mesh_elt_index.object_index = iobj; + base_mesh_elt_index.mesh_index = imesh; + base_mesh_elt_index.value = 0; + gmio_amf_write_mesh(context, &mesh, &base_mesh_elt_index); + if (gmio_error(context->error)) + return false; + } + } + /* Close object element */ + gmio_ostringstream_write_chararray(sstream, "\n"); + } + return gmio_no_error(context->error); +} + +/* Writes document objects to stream */ +static bool gmio_amf_write_root_textures(struct gmio_amf_wcontext* context) +{ + const struct gmio_amf_document* doc = context->document; + struct gmio_ostringstream* sstream = context->sstream; + struct gmio_amf_texture texture = {0}; + uint32_t itex; + for (itex = 0; itex < doc->texture_count; ++itex) { + const char* str_texture_type = ""; + doc->func_get_document_element( + doc->cookie, + GMIO_AMF_DOCUMENT_ELEMENT_TEXTURE, itex, &texture); + gmio_ostringstream_write_chararray(sstream, "'); + gmio_ostringstream_write_base64( + sstream, + texture.binary_data.ptr, + texture.binary_data.size); + gmio_ostringstream_write_chararray(sstream, "\n"); + gmio_amf_wcontext_incr_task_progress(context); + if (gmio_error(context->error)) + return false; + } + return gmio_no_error(context->error); +} + +/* Writes document constellations to stream */ +static bool gmio_amf_write_root_constellations( + struct gmio_amf_wcontext* context) +{ + const struct gmio_amf_document* doc = context->document; + struct gmio_ostringstream* sstream = context->sstream; + struct gmio_amf_constellation constellation = {0}; + uint32_t icons; + for (icons = 0; icons < doc->constellation_count; ++icons) { + doc->func_get_document_element( + doc->cookie, + GMIO_AMF_DOCUMENT_ELEMENT_CONSTELLATION, icons, &constellation); + gmio_ostringstream_write_chararray(sstream, "'); + /* Write constellation elements */ + if (constellation.metadata_count > 0) { + struct gmio_amf_metadata metadata = {0}; + uint32_t imeta; + /* Check function pointer */ + if (doc->func_get_document_element_metadata == NULL) { + return gmio_amf_wcontext_set_error( + context, + GMIO_AMF_ERROR_NULL_FUNC_GET_DOCUMENT_ELEMENT_METADATA); + } + for (imeta = 0; imeta < constellation.metadata_count; ++imeta) { + doc->func_get_document_element_metadata( + doc->cookie, + GMIO_AMF_DOCUMENT_ELEMENT_CONSTELLATION, + icons, + imeta, + &metadata); + gmio_amf_write_metadata(sstream, &metadata); + } + } + /* Write constellation elements */ + if (constellation.instance_count > 0) { + struct gmio_amf_instance instance = {0}; + uint32_t iinst; + for (iinst = 0; iinst < constellation.instance_count; ++iinst) { + doc->func_get_constellation_instance( + doc->cookie, icons, iinst, &instance); + gmio_ostringstream_write_chararray(sstream, "'); + gmio_ostringstream_write_xmlelt_f64( + sstream, "deltax", instance.delta.x); + gmio_ostringstream_write_xmlelt_f64( + sstream, "deltay", instance.delta.y); + gmio_ostringstream_write_xmlelt_f64( + sstream, "deltaz", instance.delta.z); + gmio_ostringstream_write_xmlelt_f64( + sstream, "rx", instance.rot.x); + gmio_ostringstream_write_xmlelt_f64( + sstream, "ry", instance.rot.y); + gmio_ostringstream_write_xmlelt_f64( + sstream, "rz", instance.rot.z); + gmio_ostringstream_write_chararray(sstream, "\n"); + gmio_amf_wcontext_incr_task_progress(context); + if (gmio_error(context->error)) + return false; + } + } + gmio_ostringstream_write_chararray(sstream, "\n"); + } + return gmio_no_error(context->error); +} + +/* Returns true if internal document data are roughly accessible */ +static bool gmio_amf_check_error( + int* error, const struct gmio_amf_document* doc) +{ + if (doc == NULL) { + *error = GMIO_AMF_ERROR_NULL_DOCUMENT; + } + else if (doc->func_get_document_element == NULL) { + *error = GMIO_AMF_ERROR_NULL_FUNC_GET_DOCUMENT_ELEMENT; + } + else if (doc->constellation_count > 0 + && doc->func_get_constellation_instance == NULL) + { + *error = GMIO_AMF_ERROR_NULL_FUNC_GET_CONSTELLATION_INSTANCE; + } + else if (doc->func_get_object_mesh == NULL) { + *error = GMIO_AMF_ERROR_NULL_FUNC_GET_OBJECT_MESH; + } + else if (doc->func_get_object_mesh_element == NULL) { + *error = GMIO_AMF_ERROR_NULL_FUNC_GET_OBJECT_MESH_ELEMENT; + } + else if (doc->func_get_object_mesh_volume_triangle == NULL) { + *error = GMIO_AMF_ERROR_NULL_FUNC_GET_OBJECT_MESH_VOLUME_TRIANGLE; + } + return gmio_no_error(*error); +} + +/* Converts zlib error to gmio "zlib-specific" error */ +static int zlib_error_to_gmio_error(int error) +{ + switch (error) { + case Z_OK: return GMIO_ERROR_OK; + case Z_ERRNO: return GMIO_ERROR_ZLIB_ERRNO; + case Z_STREAM_ERROR: return GMIO_ERROR_ZLIB_STREAM; + case Z_DATA_ERROR: return GMIO_ERROR_ZLIB_DATA; + case Z_MEM_ERROR: return GMIO_ERROR_ZLIB_MEM; + case Z_BUF_ERROR: return GMIO_ERROR_ZLIB_BUF; + case Z_VERSION_ERROR: return GMIO_ERROR_ZLIB_VERSION; + } + return GMIO_ERROR_UNKNOWN; +} + +/* Helper for gmio_amf_ostringstream_write() to write zlib compressed data */ +static size_t gmio_amf_ostringstream_write_zlib( + struct gmio_amf_wcontext* context, + struct gmio_stream* stream, + const char* ptr, + size_t len) +{ + struct gmio_memblock* z_mblock = &context->z_memblock; + struct z_stream_s* z_stream = &context->z_stream; + size_t total_written_len = 0; + int z_retcode = Z_OK; + z_stream->next_in = (z_const Bytef*)ptr; + z_stream->avail_in = len; /* TODO: use better cast */ + /* Run zlib deflate() on input until output buffer not full + * Finish compression when zflush == Z_FINISH */ + do { + z_stream->next_out = z_mblock->ptr; + z_stream->avail_out = z_mblock->size; + z_retcode = deflate(z_stream, context->z_flush); + /* Check state not clobbered */ + if (z_retcode == Z_STREAM_ERROR) { + context->error = zlib_error_to_gmio_error(z_retcode); + return total_written_len; + } + /* Write zlib output to stream */ + { + const size_t z_out_len = + z_mblock->size - z_stream->avail_out; + const size_t written_len = + gmio_stream_write_bytes(stream, z_mblock->ptr, z_out_len); + total_written_len += written_len; + if (written_len != z_out_len || gmio_stream_error(stream)) { + context->error = GMIO_ERROR_STREAM; + return total_written_len; + } + } + } while (z_stream->avail_out == 0); + /* Check all input was used */ + if (z_stream->avail_in != 0) { + /* TODO: set more precise error */ + context->error = GMIO_ERROR_UNKNOWN; + return total_written_len; + } + /* Check stream is complete */ + if (context->z_flush == Z_FINISH && z_retcode != Z_STREAM_END) { + /* TODO: set more precise error */ + context->error = GMIO_ERROR_UNKNOWN; + return total_written_len; + } + return total_written_len; + + /* zlib official "howto" from http://zlib.net/zlib_how.html */ +#if 0 + /* compress until end of file */ + do { + strm.avail_in = fread(in, 1, CHUNK, source); + if (ferror(source)) { + deflateEnd(&strm); + return Z_ERRNO; + } + flush = feof(source) ? Z_FINISH : Z_NO_FLUSH; + strm.next_in = in; + /* run deflate() on input until output buffer not full, finish + compression if all of source has been read in */ + do { + strm.avail_out = CHUNK; + strm.next_out = out; + + ret = deflate(&strm, flush); /* no bad return value */ + assert(ret != Z_STREAM_ERROR); /* state not clobbered */ + + have = CHUNK - strm.avail_out; + if (fwrite(out, 1, have, dest) != have || ferror(dest)) { + deflateEnd(&strm); + return Z_ERRNO; + } + } while (strm.avail_out == 0); + assert(strm.avail_in == 0); /* all input will be used */ + /* done when last data in file processed */ + } while (flush != Z_FINISH); + assert(ret == Z_STREAM_END); /* stream will be complete */ + + /* clean up and return */ + deflateEnd(&strm); + return Z_OK; +#endif +} + +/* Function called through gmio_ostringstream::func_stream_write */ +static size_t gmio_amf_ostringstream_write( + void* cookie, struct gmio_stream* stream, const char* ptr, size_t len) +{ + struct gmio_amf_wcontext* context = (struct gmio_amf_wcontext*)cookie; + size_t len_written = 0; + if (gmio_no_error(context->error)) { + if (context->options->compress) { + len_written = + gmio_amf_ostringstream_write_zlib(context, stream, ptr, len); + } + else { + len_written = gmio_stream_write_bytes(stream, ptr, len); + if (len_written != len) + context->error = GMIO_ERROR_STREAM; + } + if (gmio_no_error(context->error)) { + gmio_task_iface_handle_progress( + context->task_iface, + context->task_progress_current, + context->task_progress_max); + if (gmio_task_iface_is_stop_requested(context->task_iface)) + context->error = GMIO_ERROR_TASK_STOPPED; + } + } + return len_written; +} + +/* Returns computation upper limit of the task progress */ +static intmax_t gmio_amf_task_progress_max(const struct gmio_amf_document* doc) +{ + intmax_t progress_max = 0; + progress_max += doc->metadata_count; + progress_max += doc->material_count; + progress_max += doc->texture_count; + /* Add total object(vertex_count + edge_count + triangle_count) */ + { + struct gmio_amf_object object = {0}; + uint32_t iobj; + for (iobj = 0; iobj < doc->object_count; ++iobj) { + struct gmio_amf_mesh mesh = {0}; + uint32_t imesh; + doc->func_get_document_element( + doc->cookie, + GMIO_AMF_DOCUMENT_ELEMENT_OBJECT, iobj, &object); + for (imesh = 0; imesh < object.mesh_count; ++imesh) { + doc->func_get_object_mesh(doc->cookie, iobj, imesh, &mesh); + progress_max += mesh.vertex_count; + progress_max += mesh.edge_count; + { + struct gmio_amf_object_mesh_element_index mesh_elt_index; + uint32_t ivol; + mesh_elt_index.object_index = iobj; + mesh_elt_index.mesh_index = imesh; + mesh_elt_index.value = 0; + for (ivol = 0; ivol < mesh.volume_count; ++ivol) { + struct gmio_amf_volume volume = {0}; + mesh_elt_index.value = ivol; + doc->func_get_object_mesh_element( + doc->cookie, + GMIO_AMF_MESH_ELEMENT_VOLUME, + &mesh_elt_index, + &volume); + progress_max += volume.triangle_count; + } + } + } + } + } + /* Add total constellation(instance_count) */ + { + struct gmio_amf_constellation constellation = {0}; + uint32_t icons; + for (icons = 0; icons < doc->constellation_count; ++icons) { + doc->func_get_document_element( + doc->cookie, + GMIO_AMF_DOCUMENT_ELEMENT_CONSTELLATION, + icons, + &constellation); + progress_max += constellation.instance_count; + } + } + return progress_max; +} + +int gmio_amf_write( + struct gmio_stream* stream, + const struct gmio_amf_document* doc, + const struct gmio_amf_write_options* opts) +{ + /* Constants */ + static const struct gmio_amf_write_options default_write_opts = {0}; + struct gmio_memblock_helper mblock_helper = + gmio_memblock_helper(opts != NULL ? &opts->stream_memblock : NULL); + const struct gmio_memblock* memblock = &mblock_helper.memblock; + + /* Variables */ + struct gmio_amf_wcontext context = {0}; + struct gmio_ostringstream sstream = + gmio_ostringstream( + *stream, gmio_string(memblock->ptr, 0, memblock->size)); + + opts = opts != NULL ? opts : &default_write_opts; + + /* Check validity of input parameters */ + context.error = GMIO_ERROR_OK; + /* TODO: check stream function pointers */ + if (!gmio_check_memblock(&context.error, memblock)) + goto label_end; + if (!gmio_amf_check_error(&context.error, doc)) + goto label_end; + + /* Initialize writing context */ + { + const struct gmio_string_16 f64_stdio_format = + gmio_to_stdio_float_format( + opts->float64_format, opts->float64_prec); + context.options = opts; + context.sstream = &sstream; + context.document = doc; + context.task_iface = &opts->task_iface; + context.task_progress_current = 0; + if (context.task_iface->func_handle_progress != NULL) + context.task_progress_max += gmio_amf_task_progress_max(doc); + context.f64_format.printf_format = f64_stdio_format.array; + context.f64_format.text_format = opts->float64_format; + context.f64_format.precision = + opts->float64_prec != 0 ? opts->float64_prec : 16; + + /* Initialize internal zlib stream for compression */ + if (opts->compress) { + const struct gmio_zlib_compress_options* z_opts = + &opts->z_compress_options; + const size_t mblock_halfsize = memblock->size / 2; + int z_init_error = Z_OK; + context.sstream->strbuff.capacity = mblock_halfsize; + context.z_memblock = + gmio_memblock( + (uint8_t*)memblock->ptr + mblock_halfsize, + mblock_halfsize, + NULL); + z_init_error = + deflateInit2( + &context.z_stream, + z_opts->level, + Z_DEFLATED, /* Method */ + 15, /* Window bits(default value) */ + z_opts->memory_usage, + z_opts->strategy); + if (z_init_error != Z_OK) { + context.error = zlib_error_to_gmio_error(z_init_error); + goto label_end; + } + context.z_flush = Z_NO_FLUSH; + } + } + + sstream.cookie = &context; + sstream.func_stream_write = &gmio_amf_ostringstream_write; + + gmio_amf_write_amf_begin(&sstream, doc); + if (!gmio_amf_write_root_metadata(&context)) + goto label_end; + if (!gmio_amf_write_root_materials(&context)) + goto label_end; + if (!gmio_amf_write_root_objects(&context)) + goto label_end; + if (!gmio_amf_write_root_textures(&context)) + goto label_end; + if (!gmio_amf_write_root_constellations(&context)) + goto label_end; + + if (opts->compress) { + gmio_ostringstream_flush(&sstream); + context.z_flush = Z_FINISH; + } + gmio_ostringstream_write_chararray(&sstream, "\n"); + gmio_ostringstream_flush(&sstream); + +label_end: + if (opts->compress) + deflateEnd(&context.z_stream); + gmio_memblock_helper_release(&mblock_helper); + return context.error; +} + +int gmio_amf_write_file( + const char* filepath, + const struct gmio_amf_document* doc, + const struct gmio_amf_write_options* opts) +{ + const bool compress = opts != NULL ? opts->compress : false; + FILE* file = fopen(filepath, compress ? "wb" : "w"); + if (file != NULL) { + struct gmio_stream stream = gmio_stream_stdio(file); + const int error = gmio_amf_write(&stream, doc, opts); + fclose(file); + return error; + } + return GMIO_ERROR_STDIO; +} diff --git a/src/gmio_amf/amf_io.h b/src/gmio_amf/amf_io.h new file mode 100644 index 0000000..fb3db88 --- /dev/null +++ b/src/gmio_amf/amf_io.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +/*! \file amf_io.h + * AMF read/write functions + * + * \addtogroup gmio_amf + * @{ + */ + +#ifndef GMIO_AMF_IO_H +#define GMIO_AMF_IO_H + +#include "amf_global.h" +#include "amf_document.h" +#include "amf_io_options.h" +#include "../gmio_core/stream.h" + +GMIO_C_LINKAGE_BEGIN + +/*! Writes AMF document to stream + * + * \pre stream != NULL + * \pre doc != NULL + * + * \p options may be \c NULL in this case default values are used + * + * \return Error code (see gmio_core/error.h and amf_error.h) + * + * \sa gmio_amf_write_file() + */ +GMIO_API int gmio_amf_write( + struct gmio_stream* stream, + const struct gmio_amf_document* doc, + const struct gmio_amf_write_options* opts); + +/*! Writes AMF document to stream + * + * This is just a facility function over gmio_amf_write(). The internal stream + * object is created to read file at \p filepath + * + * \pre filepath != \c NULL \n + * The file is opened with \c fopen() so \p filepath shall follow the file + * name specifications of the running environment + * \pre doc != NULL + * + * \return Error code (see gmio_core/error.h and amf_error.h) + * + * \sa gmio_amf_write(), gmio_stream_stdio(FILE*) + */ +GMIO_API int gmio_amf_write_file( + const char* filepath, + const struct gmio_amf_document* doc, + const struct gmio_amf_write_options* opts); + +GMIO_C_LINKAGE_END + +#endif /* GMIO_AMF_IO_H */ +/*! @} */ diff --git a/src/gmio_amf/amf_io_options.h b/src/gmio_amf/amf_io_options.h new file mode 100644 index 0000000..722a205 --- /dev/null +++ b/src/gmio_amf/amf_io_options.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +/*! \file amf_io_options.h + * Options for AMF read/write functions + * + * \addtogroup gmio_amf + * @{ + */ + +#ifndef GMIO_AMF_IO_OPTIONS_H +#define GMIO_AMF_IO_OPTIONS_H + +#include "amf_global.h" +#include "../gmio_core/memblock.h" +#include "../gmio_core/task_iface.h" +#include "../gmio_core/text_format.h" + +/*! zlib compression level, specific values */ +enum gmio_zlib_compress_level +{ + GMIO_ZLIB_COMPRESS_LEVEL_NONE = -1, /*! Maps to Z_NO_COMPRESSION */ + GMIO_ZLIB_COMPRESS_LEVEL_DEFAULT = 0, /*! Maps to Z_DEFAULT_COMPRESSION */ + GMIO_ZLIB_COMPRESS_LEVEL_BEST_SPEED = 1, /*! Maps to Z_BEST_SPEED */ + GMIO_ZLIB_COMPRESS_LEVEL_BEST_SIZE = 9 /*! Maps to Z_BEST_COMPRESSION */ +}; + +/*! zlib compression strategy */ +enum gmio_zlib_compress_strategy +{ + GMIO_ZLIB_COMPRESSION_STRATEGY_DEFAULT = 0, + GMIO_ZLIB_COMPRESSION_STRATEGY_FILTERED = 1, + GMIO_ZLIB_COMPRESSION_STRATEGY_HUFFMAN_ONLY = 2, + GMIO_ZLIB_COMPRESSION_STRATEGY_RLE = 3, + GMIO_ZLIB_COMPRESSION_STRATEGY_FIXED = 4 +}; + +/*! zlib compression options + * + * Initialising gmio_zlib_compress_options with \c {0} (or \c {} in C++) is the + * convenient way to set default values. + */ +struct gmio_zlib_compress_options +{ + /*! Compression level + * + * \c 0 : default compression + * \c 1 : best speed + * \c 9 : best compression */ + uint8_t level; + + /*! Compression strategy + * + * \c 0 : default strategy + * \c 1 : filtered + * \c 9 : best compression */ + enum gmio_zlib_compress_strategy strategy; + + /*! Specifies how much memory should be allocated for the internal + * compression state + * + * The value must belongs to \c [1..9] or equals to \c 0 which maps to the + * default usage. + * + * \c 1 uses minimum memory but is slow and reduces compression ratio + * \c 9 uses maximum memory for optimal speed + */ + uint8_t memory_usage; +}; + +/*! Options of function gmio_amf_write() + * + * Initialising gmio_amf_write_options with \c {0} (or \c {} in C++) is the + * convenient way to set default values(passing \c NULL to gmio_amf_write() has + * the same effect). + */ +struct gmio_amf_write_options +{ + /*! Used by the stream to bufferize I/O operations + * + * If null, then a temporary memblock is created with the global default + * constructor function + * + * \sa gmio_memblock_isnull() + * \sa gmio_memblock_default() */ + struct gmio_memblock stream_memblock; + + /*! Optional interface by which the I/O operation can be controlled */ + struct gmio_task_iface task_iface; + + /*! The format used when writting double values as strings + * + * Defaulted to \c GMIO_FLOAT_TEXT_FORMAT_DECIMAL_LOWERCASE when calling + * gmio_amf_write() with \c options==NULL + */ + enum gmio_float_text_format float64_format; + + /*! The maximum number of significant digits when writting double values + * + * Defaulted to \c 16 when calling gmio_amf_write() with \c options==NULL + */ + uint8_t float64_prec; + + /* ZIP compression */ + + bool compress; + struct gmio_zlib_compress_options z_compress_options; +}; + +#endif /* GMIO_AMF_IO_OPTIONS_H */ diff --git a/src/gmio_core/const_string.h b/src/gmio_core/const_string.h new file mode 100644 index 0000000..5096044 --- /dev/null +++ b/src/gmio_core/const_string.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +/*! \file const_string.h + * Declaration of gmio_const_string and utility functions + * + * \addtogroup gmio_core + * @{ + */ + +#ifndef GMIO_CONST_STRING_H +#define GMIO_CONST_STRING_H + +#include "global.h" + +#include + +/*! Stores a read-only string of 8-bit chars + * + * For faster lookups, it knowns the length of its contents. + */ +struct gmio_const_string +{ + const char* ptr; /*!< Contents */ + size_t len; /*!< Size(length) of current contents */ +}; + +/*! Expands to bracket initialization of a gmio_const_string from const char[] + * + * Example: + * \code{.c} + * static const char token[] = "woops"; + * struct gmio_const_string token_s = GMIO_CONST_STRING_FROM_ARRAY(token); + * \endcode + */ +#define GMIO_CONST_STRING_FROM_ARRAY(array) { &(array)[0], sizeof(array) - 1 } + +/*! Returns an initialized gmio_const_string object */ +GMIO_INLINE struct gmio_const_string gmio_const_string(const char* ptr, size_t len); + +/*! Returns \c true if \p str has no characters, otherwise returns \c false */ +GMIO_INLINE bool gmio_const_string_is_empty(const struct gmio_const_string* str); + +/* + * -- Implementation + */ + +struct gmio_const_string gmio_const_string(const char* ptr, size_t len) +{ + struct gmio_const_string cstr; + cstr.ptr = ptr; + cstr.len = len; + return cstr; +} + +bool gmio_const_string_is_empty(const struct gmio_const_string* str) +{ + return str->ptr == NULL || str->len == 0; +} + +#endif /* GMIO_CONST_STRING_H */ diff --git a/src/gmio_core/global.h b/src/gmio_core/global.h index ead7555..a2fe994 100644 --- a/src/gmio_core/global.h +++ b/src/gmio_core/global.h @@ -92,8 +92,8 @@ /* GMIO_HAVE_INT64_TYPE */ #if defined(GMIO_HAVE_INT64_T) \ - || defined(GMIO_HAVE_MSVC_INT64) \ - || defined(GMIO_HAVE_LONG_LONG) + || defined(GMIO_HAVE_MSVC_INT64) \ + || defined(GMIO_HAVE_LONG_LONG) # define GMIO_HAVE_INT64_TYPE #endif diff --git a/src/gmio_core/internal/float_format_utils.c b/src/gmio_core/internal/float_format_utils.c new file mode 100644 index 0000000..86b1088 --- /dev/null +++ b/src/gmio_core/internal/float_format_utils.c @@ -0,0 +1,67 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +#include "float_format_utils.h" + +#include + +char gmio_float_text_format_to_stdio_specifier( + enum gmio_float_text_format format) +{ + switch (format) { + case GMIO_FLOAT_TEXT_FORMAT_DECIMAL_LOWERCASE: return 'f'; + case GMIO_FLOAT_TEXT_FORMAT_DECIMAL_UPPERCASE: return 'F'; + case GMIO_FLOAT_TEXT_FORMAT_SCIENTIFIC_LOWERCASE: return 'e'; + case GMIO_FLOAT_TEXT_FORMAT_SCIENTIFIC_UPPERCASE: return 'E'; + case GMIO_FLOAT_TEXT_FORMAT_SHORTEST_LOWERCASE: return 'g'; + case GMIO_FLOAT_TEXT_FORMAT_SHORTEST_UPPERCASE: return 'G'; + } + /* Default, should not be here */ + return GMIO_FLOAT_TEXT_FORMAT_DECIMAL_LOWERCASE; +} + +char* gmio_write_stdio_float_format(char* buffer, char specifier, uint8_t prec) +{ + int prec_len = 0; + + buffer[0] = '%'; + buffer[1] = '.'; + prec_len = sprintf(buffer + 2, "%u", prec); + buffer[2 + prec_len] = specifier; + return buffer + 3 + prec_len; +} + +struct gmio_string_16 gmio_to_stdio_float_format( + enum gmio_float_text_format format, uint8_t prec) +{ + struct gmio_string_16 buff = {0}; + const char spec = gmio_float_text_format_to_stdio_specifier(format); + gmio_write_stdio_float_format(buff.array, spec, prec); + return buff; +} diff --git a/src/gmio_core/internal/float_format_utils.h b/src/gmio_core/internal/float_format_utils.h new file mode 100644 index 0000000..025cfa6 --- /dev/null +++ b/src/gmio_core/internal/float_format_utils.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +#ifndef GMIO_INTERNAL_FLOAT_FORMAT_UTILS_H +#define GMIO_INTERNAL_FLOAT_FORMAT_UTILS_H + +#include "../text_format.h" + +char gmio_float_text_format_to_stdio_specifier( + enum gmio_float_text_format format); + +char* gmio_write_stdio_float_format( + char* buffer, char specifier, uint8_t prec); + +struct gmio_string_16 { char array[16]; }; +struct gmio_string_16 gmio_to_stdio_float_format( + enum gmio_float_text_format format, uint8_t prec); + +#endif /* GMIO_INTERNAL_FLOAT_FORMAT_UTILS_H */ diff --git a/src/gmio_core/internal/google_doubleconversion.cpp b/src/gmio_core/internal/google_doubleconversion.cpp index 35be396..e9b80a7 100644 --- a/src/gmio_core/internal/google_doubleconversion.cpp +++ b/src/gmio_core/internal/google_doubleconversion.cpp @@ -38,17 +38,20 @@ #include -inline float gmio_snanf() -{ - return std::numeric_limits::signaling_NaN(); -} +namespace { -inline float gmio_inff() -{ - return std::numeric_limits::infinity(); -} +template T gmio_snan() +{ return std::numeric_limits::signaling_NaN(); } -float gmio_str2float_googledoubleconversion(const char* num, size_t numlen) +template T gmio_inf() +{ return std::numeric_limits::infinity(); } + +template struct FloatTraits {}; +template<> struct FloatTraits { static float zero() { return 0.f; } }; +template<> struct FloatTraits { static double zero() { return 0.; } }; + +template +T gmio_generic_str2float_googledoubleconversion(const char* num, size_t numlen) { // Important note: implementation adapted from Qt5 source code // qtbase/src/corelib/tools/qlocale_tools.cpp @@ -74,14 +77,14 @@ float gmio_str2float_googledoubleconversion(const char* num, size_t numlen) bool ok = false; int processed = 0; - float f = 0.f; + T f = FloatTraits::zero(); static const int conv_flags = double_conversion::StringToDoubleConverter::NO_FLAGS; if (*num == '\0') { ok = false; processed = 0; - return 0.f; + return FloatTraits::zero(); } ok = true; @@ -90,38 +93,38 @@ float gmio_str2float_googledoubleconversion(const char* num, size_t numlen) // or sscanf, we don't allow "-nan" or "+nan" if (gmio_ascii_stricmp(num, "nan") == 0) { processed = 3; - return gmio_snanf(); + return gmio_snan(); } else if ((num[0] == '-' || num[0] == '+') && gmio_ascii_stricmp(num + 1, "nan") == 0) { processed = 0; ok = false; - return 0.f; + return FloatTraits::zero(); } // Infinity values are implementation defined in the sscanf case. In the // libdouble-conversion case we need infinity as overflow marker if (gmio_ascii_stricmp(num, "+inf") == 0) { processed = 4; - return gmio_inff(); + return gmio_inf(); } else if (gmio_ascii_stricmp(num, "inf") == 0) { processed = 3; - return gmio_inff(); + return gmio_inf(); } else if (gmio_ascii_stricmp(num, "-inf") == 0) { processed = 4; - return -gmio_inff(); + return -gmio_inf(); } static const double_conversion::StringToDoubleConverter conv( - conv_flags, 0.0, gmio_snanf(), 0, 0); + conv_flags, 0., gmio_snan(), NULL, NULL); f = conv.StringToFloat(num, static_cast(numlen), &processed); if (!gmio_isfinite(f)) { ok = false; if (gmio_isnan(f)) { // Garbage found. We don't accept it and return 0 processed = 0; - return 0.f; + return FloatTraits::zero(); } else { // Overflow. That's not OK, but we still return infinity return f; @@ -145,8 +148,9 @@ float gmio_str2float_googledoubleconversion(const char* num, size_t numlen) return f; } -int gmio_float2str_googledoubleconversion( - float value, +template +int gmio_generic_float2str_googledoubleconversion( + T value, char *buff, size_t bufflen, gmio_float_text_format textformat, @@ -181,4 +185,38 @@ int gmio_float2str_googledoubleconversion( return result_builder.position(); } +} // Anonymous namespace + +float gmio_str2float_googledoubleconversion(const char* num, size_t numlen) +{ + return ::gmio_generic_str2float_googledoubleconversion(num, numlen); +} + +double gmio_str2double_googledoubleconversion(const char* num, size_t numlen) +{ + return ::gmio_generic_str2float_googledoubleconversion(num, numlen); +} + +int gmio_float2str_googledoubleconversion( + float value, + char *buff, + size_t bufflen, + gmio_float_text_format textformat, + uint8_t prec) +{ + return gmio_generic_float2str_googledoubleconversion( + value, buff, bufflen, textformat, prec); +} + +int gmio_double2str_googledoubleconversion( + double value, + char* buff, + size_t bufflen, + enum gmio_float_text_format textformat, + uint8_t prec) +{ + return gmio_generic_float2str_googledoubleconversion( + value, buff, bufflen, textformat, prec); +} + #endif /* GMIO_STR2FLOAT_LIB == LIB_GOOGLE_DOUBLE_CONVERSION */ diff --git a/src/gmio_core/internal/google_doubleconversion.h b/src/gmio_core/internal/google_doubleconversion.h index e7e3533..dc3310b 100644 --- a/src/gmio_core/internal/google_doubleconversion.h +++ b/src/gmio_core/internal/google_doubleconversion.h @@ -42,12 +42,20 @@ GMIO_C_LINKAGE_BEGIN float gmio_str2float_googledoubleconversion(const char* num, size_t numlen); +double gmio_str2double_googledoubleconversion(const char* num, size_t numlen); + int gmio_float2str_googledoubleconversion( float value, char* buff, size_t bufflen, enum gmio_float_text_format textformat, uint8_t prec); +int gmio_double2str_googledoubleconversion( + double value, + char* buff, + size_t bufflen, + enum gmio_float_text_format textformat, + uint8_t prec); GMIO_C_LINKAGE_END #endif /* GMIO_INTERNAL_GOOGLE_DOUBLECONVERSION_H */ diff --git a/src/gmio_core/internal/itoa.h b/src/gmio_core/internal/itoa.h new file mode 100644 index 0000000..f017543 --- /dev/null +++ b/src/gmio_core/internal/itoa.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +#ifndef GMIO_INTERNAL_ITOA_H +#define GMIO_INTERNAL_ITOA_H + +#include "../global.h" + +GMIO_INLINE char* gmio_u32toa(uint32_t value, char* str); +GMIO_INLINE char* gmio_i32toa(int32_t value, char* str); +#ifdef GMIO_HAVE_INT64_TYPE +GMIO_INLINE char* gmio_u64toa(uint64_t value, char* str); +GMIO_INLINE char* gmio_i64toa(int64_t value, char* str); +#endif + +/* + * Implementation + */ + +#include "../../3rdparty/miloyip_itoa/branchlut.h" + +char* gmio_u32toa(uint32_t value, char* str) +{ + if (value < 10) { + *str++ = '0' + (char)value; + return str; + } + else { + return u32toa_branchlut(value, str); + } +} + +char* gmio_i32toa(int32_t value, char* str) +{ + return i32toa_branchlut(value, str); +} + +#ifdef GMIO_HAVE_INT64_TYPE +char* gmio_u64toa(uint64_t value, char* str) +{ + return u64toa_branchlut(value, str); +} + +char* gmio_i64toa(int64_t value, char* str) +{ + return i64toa_branchlut(value, str); +} +#endif /* GMIO_HAVE_INT64_TYPE */ + +#endif /* GMIO_INTERNAL_ITOA_H */ diff --git a/src/gmio_core/internal/ostringstream.c b/src/gmio_core/internal/ostringstream.c new file mode 100644 index 0000000..80f9898 --- /dev/null +++ b/src/gmio_core/internal/ostringstream.c @@ -0,0 +1,214 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +#include "ostringstream.h" + +#include "helper_stream.h" +#include "itoa.h" +#include "min_max.h" +#include "../../3rdparty/base64/b64.h" + +#if GMIO_FLOAT2STR_LIB == GMIO_FLOAT2STR_LIB_STD +# include "c99_stdio_compat.h" +#elif GMIO_FLOAT2STR_LIB == GMIO_FLOAT2STR_LIB_DOUBLE_CONVERSION +# include "google_doubleconversion.h" +#endif + +#include + +GMIO_INLINE char* gmio_ostringstream_strbuff_pos( + const struct gmio_ostringstream* sstream) +{ + return sstream->strbuff.ptr + sstream->strbuff.len; +} + +GMIO_INLINE size_t gmio_string_remaining_capacity(const struct gmio_string* str) +{ + return str->capacity - str->len; +} + +struct gmio_ostringstream gmio_ostringstream( + const struct gmio_stream stream, const struct gmio_string strbuff) +{ + struct gmio_ostringstream sstream = {0}; + sstream.stream = stream; + sstream.strbuff = strbuff; + sstream.func_stream_write = gmio_ostringstream_default_func_write; + return sstream; +} + + +size_t gmio_ostringstream_default_func_write( + void* cookie, struct gmio_stream* stream, const char* ptr, size_t len) +{ + GMIO_UNUSED(cookie); + return gmio_stream_write_bytes(stream, ptr, len); +} + +void gmio_ostringstream_write_str( + struct gmio_ostringstream *sstream, const char *str) +{ + gmio_ostringstream_write_nstr(sstream, str, strlen(str)); +} + +void gmio_ostringstream_write_nstr( + struct gmio_ostringstream *sstream, const char *str, size_t n) +{ + struct gmio_string* strbuff = &sstream->strbuff; + if (n > strbuff->capacity) { + /* No need to bufferize copy */ + gmio_ostringstream_flush(sstream); + sstream->func_stream_write(sstream->cookie, &sstream->stream, str, n); + } + else { + /* str fits in sstream->strbuff */ + if (n >= gmio_string_remaining_capacity(strbuff)) + gmio_ostringstream_flush(sstream); + strncpy(gmio_ostringstream_strbuff_pos(sstream), str, n); + strbuff->len += n; + } +} + +void gmio_ostringstream_write_xmlattr_str( + struct gmio_ostringstream *sstream, const char *attr, const char *val) +{ + gmio_ostringstream_write_char(sstream, ' '); + gmio_ostringstream_write_str(sstream, attr); + gmio_ostringstream_write_chararray(sstream, "=\""); + gmio_ostringstream_write_str(sstream, val); + gmio_ostringstream_write_char(sstream, '\"'); +} + +void gmio_ostringstream_write_xmlattr_u32( + struct gmio_ostringstream *sstream, const char *attr, uint32_t val) +{ + gmio_ostringstream_write_char(sstream, ' '); + gmio_ostringstream_write_str(sstream, attr); + gmio_ostringstream_write_chararray(sstream, "=\""); + gmio_ostringstream_write_u32(sstream, val); + gmio_ostringstream_write_char(sstream, '\"'); +} + +void gmio_ostringstream_write_u32( + struct gmio_ostringstream *sstream, uint32_t value) +{ + struct gmio_string* buff = &sstream->strbuff; + char* buffpos; + const char* newbuffpos; + if (gmio_string_remaining_capacity(buff) < 10) + gmio_ostringstream_flush(sstream); + buffpos = buff->ptr + buff->len; + newbuffpos = gmio_u32toa(value, buffpos); + buff->len += newbuffpos - buffpos; +} + +void gmio_ostringstream_write_i32( + struct gmio_ostringstream *sstream, int32_t value) +{ + struct gmio_string* buff = &sstream->strbuff; + char* buffpos; + const char* newbuffpos; + if (gmio_string_remaining_capacity(buff) < 11) /* 10 digits + '-' sign */ + gmio_ostringstream_flush(sstream); + buffpos = buff->ptr + buff->len; + newbuffpos = gmio_i32toa(value, buffpos); + buff->len += newbuffpos - buffpos; +} + +void gmio_ostringstream_write_xmlelt_f64( + struct gmio_ostringstream *sstream, const char *elt, double val) +{ + const size_t elt_strlen = strlen(elt); + gmio_ostringstream_write_char(sstream, '<'); + gmio_ostringstream_write_nstr(sstream, elt, elt_strlen); + gmio_ostringstream_write_char(sstream, '>'); + gmio_ostringstream_write_f64(sstream, val, NULL); + gmio_ostringstream_write_nstr(sstream, "'); +} + +void gmio_ostringstream_write_f64( + struct gmio_ostringstream *sstream, + double value, + const struct gmio_ostringstream_format_float *format) +{ + static const struct gmio_ostringstream_format_float default_format = { + "%.16g", GMIO_FLOAT_TEXT_FORMAT_SHORTEST_UPPERCASE, 16 }; + const struct gmio_ostringstream_format_float* nformat = + format != NULL ? format : &default_format; + struct gmio_string* buff = &sstream->strbuff; + int written_count = 0; + if (buff->capacity - buff->len < 32) + gmio_ostringstream_flush(sstream); +#if GMIO_FLOAT2STR_LIB == GMIO_FLOAT2STR_LIB_STD + written_count = gmio_snprintf( + buff->ptr + buff->len, + buff->capacity - buff->len, + nformat->printf_format, + value); +#elif GMIO_FLOAT2STR_LIB == GMIO_FLOAT2STR_LIB_DOUBLE_CONVERSION + written_count = gmio_double2str_googledoubleconversion( + value, + buff->ptr + buff->len, + buff->capacity - buff->len, + nformat->text_format, + nformat->precision); +#endif + buff->len += written_count; +} + +void gmio_ostringstream_write_base64( + struct gmio_ostringstream *sstream, + const unsigned char *input, + size_t len) +{ + struct gmio_string* buff = &sstream->strbuff; + size_t pos = 0; + while (pos < len) { + const size_t remaining_len = len - pos; + unsigned char* output; + if (gmio_string_remaining_capacity(buff) < 4) + gmio_ostringstream_flush(sstream); + output = (unsigned char*)(buff->ptr + buff->len); + if (remaining_len >= 3) { + b64_encodeblock(input + pos, output, 3); + } + else { + unsigned char temp_in[3] = {0}; + if (remaining_len > 0) + temp_in[0] = *(input + pos); + if (remaining_len > 1) + temp_in[1] = *(input + pos + 1); + b64_encodeblock(temp_in, output, remaining_len); + } + pos += 3; + buff->len += 4; + } +} diff --git a/src/gmio_core/internal/ostringstream.h b/src/gmio_core/internal/ostringstream.h new file mode 100644 index 0000000..144be21 --- /dev/null +++ b/src/gmio_core/internal/ostringstream.h @@ -0,0 +1,180 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +#ifndef GMIO_INTERNAL_OSTRINGSTREAM_H +#define GMIO_INTERNAL_OSTRINGSTREAM_H + +#include "../global.h" +#include "../stream.h" +#include "../text_format.h" +#include "string.h" + +/*! Output stream that operates on a string + * + * To be used with API below. + */ +struct gmio_ostringstream +{ + /*! Stream to iterate over */ + struct gmio_stream stream; + + /*! Holds contents chunk to write in stream(when full) */ + struct gmio_string strbuff; + + /*! Data to be passed to callback func_stream_write */ + void* cookie; + + /*! Pointer on a function called each time next contents chunk has to be + * written */ + size_t (*func_stream_write)( + void* cookie, struct gmio_stream* stream, const char* ptr, size_t len); +}; + +/*! Returns an initialized gmio_ostringstream object */ +struct gmio_ostringstream gmio_ostringstream( + const struct gmio_stream stream, + const struct gmio_string strbuff); + +/*! Default function for gmio_ostringstream::func_stream_write */ +size_t gmio_ostringstream_default_func_write( + void* cookie, struct gmio_stream* stream, const char* ptr, size_t len); + +/*! Writes null-terminated C string \p str to ostringstream \p sstream */ +void gmio_ostringstream_write_str( + struct gmio_ostringstream* sstream, const char* str); + +/*! Writes the first \p n characters from C string \p str to ostringstream + * \p sstream */ +void gmio_ostringstream_write_nstr( + struct gmio_ostringstream* sstream, const char* str, size_t n); + +/*! Writes the character \p array to ostringstream \p sstream + * + * It is a facility around gmio_ostringstream_write_nstr() for string literals + * and char[] + * \code + * const char str[] = "stuff"; + * gmio_ostringstream_write_chararray(sstream, "something"); + * gmio_ostringstream_write_chararray(sstream, str); + * \endcode + */ +#define gmio_ostringstream_write_chararray(sstream, array) \ + gmio_ostringstream_write_nstr((sstream), (array), sizeof(array) - 1) + +/*! Writes character \p c to ostringstream \p sstream */ +GMIO_INLINE void gmio_ostringstream_write_char( + struct gmio_ostringstream* sstream, char c); + +/*! Writes uint32 \p value to ostringstream \p sstream */ +void gmio_ostringstream_write_u32( + struct gmio_ostringstream* sstream, uint32_t value); + +/*! Writes int32 \p value to ostringstream \p sstream */ +void gmio_ostringstream_write_i32( + struct gmio_ostringstream* sstream, int32_t value); + +/*! Format specification for float numbers(single or double precision) */ +struct gmio_ostringstream_format_float +{ + const char* printf_format; /* printf-like format(eg. "%.7f") */ + enum gmio_float_text_format text_format; + uint8_t precision; +}; + +/*! Writes double \p value to ostringstream \p sstream */ +void gmio_ostringstream_write_f64( + struct gmio_ostringstream* sstream, + double value, + const struct gmio_ostringstream_format_float* format); + +/*! Writes \p input encoded to base64 */ +void gmio_ostringstream_write_base64( + struct gmio_ostringstream* sstream, + const unsigned char* input, + size_t len); + +/*! Write pending bytes within sstream->strbuff */ +GMIO_INLINE void gmio_ostringstream_flush(struct gmio_ostringstream* sstream); + +/* XML */ + +/*! Write XML attribute of name \p attr and value \p val */ +void gmio_ostringstream_write_xmlattr_str( + struct gmio_ostringstream* sstream, const char* attr, const char* val); +void gmio_ostringstream_write_xmlattr_u32( + struct gmio_ostringstream* sstream, const char* attr, uint32_t val); + +/*! Write XML element containing \p val : val */ +void gmio_ostringstream_write_xmlelt_f64( + struct gmio_ostringstream* sstream, const char* elt, double val); + + +GMIO_INLINE void gmio_ostringstream_write_xmlcdata_open( + struct gmio_ostringstream* sstream); +GMIO_INLINE void gmio_ostringstream_write_xmlcdata_close( + struct gmio_ostringstream* sstream); + + +/* + * -- Implementation + */ + +void gmio_ostringstream_write_char( + struct gmio_ostringstream* sstream, char c) +{ + struct gmio_string* strbuff = &sstream->strbuff; + if (strbuff->len >= strbuff->capacity) + gmio_ostringstream_flush(sstream); + strbuff->ptr[strbuff->len] = c; + ++strbuff->len; +} + +void gmio_ostringstream_flush(struct gmio_ostringstream* sstream) +{ + struct gmio_string* strbuff = &sstream->strbuff; + if (strbuff->len > 0) { + /* Don't check bytecount returned is != strbuff->len because some + * write() functions may compress data */ + sstream->func_stream_write( + sstream->cookie, &sstream->stream, strbuff->ptr, strbuff->len); + strbuff->len = 0; + } +} + +void gmio_ostringstream_write_xmlcdata_open(struct gmio_ostringstream *sstream) +{ + gmio_ostringstream_write_chararray(sstream, ""); +} + +#endif /* GMIO_INTERNAL_OSTRINGSTREAM_H */ diff --git a/src/gmio_core/internal/string.h b/src/gmio_core/internal/string.h index e4ed3f7..f496bc7 100644 --- a/src/gmio_core/internal/string.h +++ b/src/gmio_core/internal/string.h @@ -34,16 +34,6 @@ #include -/*! Stores a read-only string of 8-bit chars - * - * For faster lookups, it knowns the length of its contents. - */ -struct gmio_const_string -{ - const char* ptr; /*!< Contents */ - size_t len; /*!< Size(length) of current contents */ -}; - /*! Stores a mutable string of 8-bit chars * * For faster lookups, it knowns the length of its contents. Length must not @@ -56,19 +46,6 @@ struct gmio_string size_t capacity; /*!< Maximum contents size */ }; -/*! Expands to bracket initialization of a gmio_const_string from const char[] - * - * Example: - * \code{.c} - * static const char token[] = "woops"; - * struct gmio_const_string token_s = GMIO_CONST_STRING_FROM_ARRAY(token); - * \endcode - */ -#define GMIO_CONST_STRING_FROM_ARRAY(array) { &(array)[0], sizeof(array) - 1 } - -/*! Returns an initialized struct gmio_const_string object */ -GMIO_INLINE struct gmio_const_string gmio_const_string(const char* ptr, size_t len); - /*! Returns an initialized struct gmio_string object * * gmio_string::capacity is set to max(len,capacity) @@ -99,14 +76,6 @@ GMIO_INLINE char* gmio_cstr_copy( #include #include "min_max.h" -struct gmio_const_string gmio_const_string(const char* ptr, size_t len) -{ - struct gmio_const_string cstr; - cstr.ptr = ptr; - cstr.len = len; - return cstr; -} - struct gmio_string gmio_string(char* ptr, size_t len, size_t capacity) { struct gmio_string str; diff --git a/src/gmio_core/text_format.h b/src/gmio_core/text_format.h index e9d295c..98c99d7 100644 --- a/src/gmio_core/text_format.h +++ b/src/gmio_core/text_format.h @@ -39,7 +39,8 @@ #include "global.h" -/*! This enum defines the various formats to textually represent a float */ +/*! This enum defines the various formats to textually represent a float(single + * or double precision) */ enum gmio_float_text_format { /*! Decimal floating point, lowercase (ex: 392.65) */ diff --git a/src/gmio_stl/internal/stla_write.c b/src/gmio_stl/internal/stla_write.c index d809807..61860ac 100644 --- a/src/gmio_stl/internal/stla_write.c +++ b/src/gmio_stl/internal/stla_write.c @@ -37,6 +37,7 @@ #include "../../gmio_core/task_iface.h" #include "../../gmio_core/text_format.h" #include "../../gmio_core/internal/error_check.h" +#include "../../gmio_core/internal/float_format_utils.h" #include "../../gmio_core/internal/helper_memblock.h" #include "../../gmio_core/internal/helper_stream.h" #include "../../gmio_core/internal/helper_task_iface.h" @@ -117,33 +118,6 @@ GMIO_INLINE char* gmio_write_rawstr_eol(char* buffer, const char* str) return gmio_write_eol(buffer); } -GMIO_INLINE char gmio_float_text_format_to_specifier( - enum gmio_float_text_format format) -{ - switch (format) { - case GMIO_FLOAT_TEXT_FORMAT_DECIMAL_LOWERCASE: return 'f'; - case GMIO_FLOAT_TEXT_FORMAT_DECIMAL_UPPERCASE: return 'F'; - case GMIO_FLOAT_TEXT_FORMAT_SCIENTIFIC_LOWERCASE: return 'e'; - case GMIO_FLOAT_TEXT_FORMAT_SCIENTIFIC_UPPERCASE: return 'E'; - case GMIO_FLOAT_TEXT_FORMAT_SHORTEST_LOWERCASE: return 'g'; - case GMIO_FLOAT_TEXT_FORMAT_SHORTEST_UPPERCASE: return 'G'; - } - /* Default, should not be here */ - return GMIO_FLOAT_TEXT_FORMAT_DECIMAL_LOWERCASE; -} - -GMIO_INLINE char* gmio_write_stdio_format( - char* buffer, char format_specifier, uint8_t prec) -{ - int prec_len = 0; - - buffer[0] = '%'; - buffer[1] = '.'; - prec_len = sprintf(buffer + 2, "%u", prec); - buffer[2 + prec_len] = format_specifier; - return buffer + 3 + prec_len; -} - struct gmio_vec3f_text_format { enum gmio_float_text_format coord_format; @@ -223,13 +197,13 @@ int gmio_stla_write( { const uint8_t f32_prec = vec_txtformat.coord_prec; enum gmio_float_text_format f32_format = opts->stla_float32_format; - const char f32_spec = gmio_float_text_format_to_specifier(f32_format); + const char f32_spec = gmio_float_text_format_to_stdio_specifier(f32_format); char* buffpos = vec_txtformat.str_printf_format; - buffpos = gmio_write_stdio_format(buffpos, f32_spec, f32_prec); + buffpos = gmio_write_stdio_float_format(buffpos, f32_spec, f32_prec); buffpos = gmio_write_char(buffpos, ' '); - buffpos = gmio_write_stdio_format(buffpos, f32_spec, f32_prec); + buffpos = gmio_write_stdio_float_format(buffpos, f32_spec, f32_prec); buffpos = gmio_write_char(buffpos, ' '); - buffpos = gmio_write_stdio_format(buffpos, f32_spec, f32_prec); + buffpos = gmio_write_stdio_float_format(buffpos, f32_spec, f32_prec); *buffpos = 0; } diff --git a/src/gmio_stl/stla_read.c b/src/gmio_stl/stla_read.c index 92d2385..34384a9 100644 --- a/src/gmio_stl/stla_read.c +++ b/src/gmio_stl/stla_read.c @@ -35,6 +35,7 @@ #include "internal/stl_error_check.h" #include "internal/stla_parsing.h" +#include "../gmio_core/const_string.h" #include "../gmio_core/error.h" #include "../gmio_core/internal/c99_stdio_compat.h" #include "../gmio_core/internal/error_check.h" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 04dc796..bc59774 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -32,6 +32,7 @@ set(CMAKE_CTEST_COMMAND ctest -V --timeout 120) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}) include_directories(${CMAKE_BINARY_DIR}/src/gmio_core) # For generated cmake headers +include_directories(${ZLIB_INCLUDE_DIRS}) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/temp) @@ -41,7 +42,6 @@ set(GMIO_TEST_CORE_SRC core_utils.c stream_buffer.c ../benchmarks/commons/benchmark_tools.c) - add_executable(test_core EXCLUDE_FROM_ALL ${GMIO_TEST_CORE_SRC}) target_link_libraries(test_core gmio_static) @@ -53,10 +53,17 @@ set(GMIO_TEST_STL_SRC stl_testcases.c core_utils.c stl_utils.c) - add_executable(test_stl EXCLUDE_FROM_ALL ${GMIO_TEST_STL_SRC}) target_link_libraries(test_stl gmio_static) +# test_amf +set(GMIO_TEST_AMF_SRC + main_test_amf.c + stream_buffer.c) +add_executable(test_amf EXCLUDE_FROM_ALL ${GMIO_TEST_AMF_SRC}) +target_link_libraries(test_amf gmio_static) +target_link_libraries(test_amf ${ZLIB_LIBRARIES}) + # fake_support if(GMIO_BUILD_TESTS_FAKE_SUPPORT) add_subdirectory(fake_support) @@ -72,5 +79,5 @@ endif() # Declare tests add_test(NAME test_core COMMAND test_core) add_test(NAME test_stl COMMAND test_stl) - -add_dependencies(check test_core test_stl) +add_test(NAME test_amf COMMAND test_amf) +add_dependencies(check test_core test_stl test_amf) diff --git a/tests/main_test_amf.c b/tests/main_test_amf.c new file mode 100644 index 0000000..71eae47 --- /dev/null +++ b/tests/main_test_amf.c @@ -0,0 +1,55 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +#include "utest_lib.h" + +#include "../src/gmio_core/global.h" +#include "../src/gmio_core/memblock.h" + +#include "test_amf_io.c" + +#include + +/* Static memblock */ +struct gmio_memblock gmio_memblock_for_tests() +{ + return gmio_memblock_malloc(512 * 1024); /* 512KB */ +} + +const char* all_tests() +{ + UTEST_SUITE_START(); + + gmio_memblock_set_default_constructor(gmio_memblock_for_tests); + + UTEST_RUN(test_amf_write); + + return NULL; +} +UTEST_MAIN(all_tests) diff --git a/tests/main_test_core.c b/tests/main_test_core.c index c6339dc..aa6bb30 100644 --- a/tests/main_test_core.c +++ b/tests/main_test_core.c @@ -51,6 +51,7 @@ const char* all_tests() UTEST_RUN(test_internal__fast_atof); UTEST_RUN(test_internal__locale_utils); UTEST_RUN(test_internal__error_check); + UTEST_RUN(test_internal__ostringstream); UTEST_RUN(test_internal__safe_cast); UTEST_RUN(test_internal__stringstream); UTEST_RUN(test_internal__string_ascii_utils); diff --git a/tests/stream_buffer.h b/tests/stream_buffer.h index f3d9050..1d95794 100644 --- a/tests/stream_buffer.h +++ b/tests/stream_buffer.h @@ -52,6 +52,6 @@ struct gmio_ro_buffer gmio_ro_buffer(const void* ptr, size_t len, size_t pos); struct gmio_rw_buffer gmio_rw_buffer(void* ptr, size_t len, size_t pos); struct gmio_stream gmio_istream_buffer(struct gmio_ro_buffer* buff); -struct gmio_stream gmio_iostream_buffer(struct gmio_rw_buffer* buff); +struct gmio_stream gmio_stream_buffer(struct gmio_rw_buffer* buff); #endif /* GMIO_STREAM_BUFFER_H */ diff --git a/tests/test_amf_io.c b/tests/test_amf_io.c new file mode 100644 index 0000000..188daa3 --- /dev/null +++ b/tests/test_amf_io.c @@ -0,0 +1,268 @@ +/**************************************************************************** +** Copyright (c) 2016, Fougue Ltd. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2. Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +#include "utest_assert.h" + +#include "core_utils.h" +#include "stream_buffer.h" + +#include "../src/gmio_core/error.h" +#include "../src/gmio_core/internal/helper_stream.h" +#include "../src/gmio_amf/amf_error.h" +#include "../src/gmio_amf/amf_io.h" + +#include +#include +#include + +struct __tamf__material +{ + double color[3]; + const char* name; +}; + +struct __tamf__triangle +{ + uint32_t vertex[3]; +}; + +struct __tamf__mesh +{ + const struct gmio_vec3d* vec_vertex; + uint32_t vertex_count; + const struct __tamf__triangle* vec_triangle; + uint32_t triangle_count; +}; + +struct __tamf__document +{ + const struct __tamf__material* vec_material; + struct __tamf__mesh mesh; +}; + +static void __tamf__get_document_element( + const void* cookie, + enum gmio_amf_document_element element, + uint32_t element_index, + void* ptr_element) +{ + const struct __tamf__document* doc = + (const struct __tamf__document*)cookie; + switch (element) { + case GMIO_AMF_DOCUMENT_ELEMENT_OBJECT: { + struct gmio_amf_object* ptr_object = + (struct gmio_amf_object*)ptr_element; + ptr_object->id = element_index; + ptr_object->mesh_count = 1; + break; + } + case GMIO_AMF_DOCUMENT_ELEMENT_MATERIAL: { + struct gmio_amf_material* ptr_material = + (struct gmio_amf_material*)ptr_element; + const struct __tamf__material* imat = &doc->vec_material[element_index]; + ptr_material->metadata_count = 1; + ptr_material->id = element_index; + ptr_material->color.r = imat->color[0]; + ptr_material->color.g = imat->color[1]; + ptr_material->color.b = imat->color[2]; + break; + } + case GMIO_AMF_DOCUMENT_ELEMENT_TEXTURE: + break; + case GMIO_AMF_DOCUMENT_ELEMENT_CONSTELLATION: + break; + case GMIO_AMF_DOCUMENT_ELEMENT_METADATA: + break; + } +} + +static void __tamf__get_object_mesh( + const void* cookie, + uint32_t object_index, + uint32_t mesh_index, + struct gmio_amf_mesh* ptr_mesh) +{ + const struct __tamf__document* doc = + (const struct __tamf__document*)cookie; + GMIO_UNUSED(object_index); + GMIO_UNUSED(mesh_index); + ptr_mesh->vertex_count = doc->mesh.vertex_count; + ptr_mesh->edge_count = 0; + ptr_mesh->volume_count = 1; +} + +static void __tamf__get_object_mesh_element( + const void* cookie, + enum gmio_amf_mesh_element element, + const struct gmio_amf_object_mesh_element_index* element_index, + void* ptr_element) +{ + const struct __tamf__document* doc = (const struct __tamf__document*)cookie; + switch (element) { + case GMIO_AMF_MESH_ELEMENT_VERTEX: { + struct gmio_amf_vertex* ptr_vertex = (struct gmio_amf_vertex*)ptr_element; + ptr_vertex->coords = doc->mesh.vec_vertex[element_index->value]; + break; + } + case GMIO_AMF_MESH_ELEMENT_EDGE: + break; + case GMIO_AMF_MESH_ELEMENT_VOLUME: { + struct gmio_amf_volume* ptr_volume = (struct gmio_amf_volume*)ptr_element; + ptr_volume->materialid = 1; + ptr_volume->triangle_count = doc->mesh.triangle_count; + break; + } + } +} + +static void __tamf__get_object_mesh_volume_triangle( + const void* cookie, + const struct gmio_amf_object_mesh_element_index* volume_index, + uint32_t triangle_index, + struct gmio_amf_triangle* ptr_triangle) +{ + const struct __tamf__document* doc = (const struct __tamf__document*)cookie; + const struct __tamf__triangle* tri = &doc->mesh.vec_triangle[triangle_index]; + GMIO_UNUSED(volume_index); + ptr_triangle->v1 = tri->vertex[0]; + ptr_triangle->v2 = tri->vertex[1]; + ptr_triangle->v3 = tri->vertex[2]; +} + +static void __tamf__get_document_element_metadata( + const void* cookie, + enum gmio_amf_document_element element, + uint32_t element_index, + uint32_t metadata_index, + struct gmio_amf_metadata* ptr_metadata) +{ + const struct __tamf__document* doc = (const struct __tamf__document*)cookie; + GMIO_UNUSED(metadata_index); + if (element == GMIO_AMF_DOCUMENT_ELEMENT_MATERIAL) { + ptr_metadata->type = "name"; + ptr_metadata->data = doc->vec_material[element_index].name; + } +} + +static const char* test_amf_write() +{ + { + struct gmio_stream stream = {0}; + struct gmio_amf_document doc = {0}; + const int error = gmio_amf_write(&stream, &doc, NULL); + UTEST_ASSERT(gmio_error(error)); + } + + { + static const size_t wbuffsize = 8192; + struct gmio_rw_buffer wbuff = {0}; + struct gmio_stream stream = gmio_stream_buffer(&wbuff); + struct gmio_amf_document doc = {0}; + struct gmio_amf_write_options options = {0}; + + const struct __tamf__material testmaterials[] = { + { { 1., 0., 0. }, "red" }, + { { 0., 1., 0. }, "green" }, + { { 0., 0., 1. }, "blue" }, + { { 1., 1., 1. }, "white" } + }; + const struct gmio_vec3d testvertices[] = { + { 0., 0., 0.}, + { 1., 0., 0.}, + { 1., -1., 0.}, + { 0., -1., 0.}, + { 1., 0., 1.}, + { 1., -1., 1.}, + { 0., 0., 1.}, + { 0., -1., 1.} + }; + const struct __tamf__triangle testtriangles[] = { + { 0, 1, 2}, + { 0, 2, 3}, + { 1, 5, 2}, + { 1, 4, 5}, + { 6, 5, 7}, + { 6, 4, 5}, + { 0, 6, 7}, + { 0, 7, 3}, + { 0, 6, 4}, + { 0, 4, 1}, + { 3, 7, 5}, + { 3, 5, 2} + }; + struct __tamf__document testdoc = {0}; + + wbuff.ptr = calloc(wbuffsize, 1); + wbuff.len = wbuffsize; + + testdoc.vec_material = testmaterials; + testdoc.mesh.vec_vertex = testvertices; + testdoc.mesh.vertex_count = GMIO_ARRAY_SIZE(testvertices); + testdoc.mesh.vec_triangle = testtriangles; + testdoc.mesh.triangle_count = GMIO_ARRAY_SIZE(testtriangles); + + doc.cookie = &testdoc; + doc.unit = GMIO_AMF_UNIT_MILLIMETER; + doc.func_get_document_element = &__tamf__get_document_element; + doc.func_get_object_mesh = &__tamf__get_object_mesh; + doc.func_get_object_mesh_element = &__tamf__get_object_mesh_element; + doc.func_get_object_mesh_volume_triangle = + &__tamf__get_object_mesh_volume_triangle; + doc.func_get_document_element_metadata = + &__tamf__get_document_element_metadata; + doc.object_count = 1; + doc.material_count = GMIO_ARRAY_SIZE(testmaterials); + + options.float64_prec = 9; + + /* Write as raw contents(uncompressed) */ + { + const int error = gmio_amf_write(&stream, &doc, &options); + if (gmio_error(error)) + printf("\n0x%x\n", error); + UTEST_COMPARE_INT(error, GMIO_ERROR_OK); + printf("%s\n", wbuff.ptr); + } + +#if 0 + /* Write compressed */ + { + int error = GMIO_ERROR_OK; + wbuff.pos = 0; + options.compress = true; + error = gmio_amf_write(&stream, &doc, &options); + UTEST_COMPARE_INT(error, GMIO_ERROR_OK); + } +#endif + + free(wbuff.ptr); + } + + return NULL; +} diff --git a/tests/test_core_internal.c b/tests/test_core_internal.c index 6298436..e53a3d8 100644 --- a/tests/test_core_internal.c +++ b/tests/test_core_internal.c @@ -39,6 +39,7 @@ #include "../src/gmio_core/internal/fast_atof.h" #include "../src/gmio_core/internal/locale_utils.h" #include "../src/gmio_core/internal/numeric_utils.h" +#include "../src/gmio_core/internal/ostringstream.h" #include "../src/gmio_core/internal/safe_cast.h" #include "../src/gmio_core/internal/stringstream.h" #include "../src/gmio_core/internal/stringstream_fast_atof.h" @@ -282,6 +283,89 @@ static const char* test_internal__stringstream() return NULL; } +static const char* test_internal__ostringstream() +{ + static const size_t size = 8192; + char* input = malloc(size); + char* output = malloc(size); + char strbuff[256] = {0}; + struct gmio_rw_buffer rwbuff = gmio_rw_buffer(output, size, 0); + struct gmio_ostringstream sstream = + gmio_ostringstream( + gmio_stream_buffer(&rwbuff), + gmio_string(strbuff, 0, sizeof(strbuff) - 1)); + + { /* Create "input" string */ + size_t i = 0; + for (i = 0; i < size; ++i) { + const char c = 32 + (i % 94); /* Printable ASCII chars */ + input[i] = c; + } + /* Test gmio_ostringstream_write_char() */ + for (i = 0; i < size; ++i) + gmio_ostringstream_write_char(&sstream, input[i]); + gmio_ostringstream_flush(&sstream); + UTEST_ASSERT(strncmp(input, output, size) == 0); + + /* Test gmio_ostringstream_write_[ui]32() */ + { + static const char result[] = + "20 12345 0 -1 -12345678 4294967295 2147483647"; + static const unsigned result_len = sizeof(result) - 1; + rwbuff.pos = 0; + gmio_ostringstream_write_u32(&sstream, 20); + gmio_ostringstream_write_char(&sstream, ' '); + gmio_ostringstream_write_u32(&sstream, 12345); + gmio_ostringstream_write_char(&sstream, ' '); + gmio_ostringstream_write_u32(&sstream, 0); + gmio_ostringstream_write_char(&sstream, ' '); + gmio_ostringstream_write_i32(&sstream, -1); + gmio_ostringstream_write_char(&sstream, ' '); + gmio_ostringstream_write_i32(&sstream, -12345678); + gmio_ostringstream_write_char(&sstream, ' '); + gmio_ostringstream_write_u32(&sstream, (uint32_t)-1); + gmio_ostringstream_write_char(&sstream, ' '); + gmio_ostringstream_write_i32(&sstream, ((uint32_t)1 << 31) - 1); + gmio_ostringstream_flush(&sstream); + UTEST_ASSERT(strncmp(result, sstream.strbuff.ptr, result_len) == 0); + UTEST_ASSERT(strncmp(result, output, result_len) == 0); + } + + /* Test gmio_ostringstream_write_base64() */ + { + static const char str[] = "Fougue+gmio"; + static const char str_b64[] = "Rm91Z3VlK2dtaW8="; + static const unsigned str_len = sizeof(str) - 1; + static const unsigned str_b64_len = sizeof(str_b64) - 1; + rwbuff.pos = 0; + gmio_ostringstream_write_base64( + &sstream, (unsigned const char*)str, str_len); + gmio_ostringstream_flush(&sstream); + UTEST_ASSERT(strncmp(str_b64, sstream.strbuff.ptr, str_b64_len) == 0); + UTEST_ASSERT(strncmp(str_b64, output, str_b64_len) == 0); + } + + /* Test gmio_ostringstream_write_xml...() */ + { + static const char result[] = + " foo=\"crac\" bar=\"456789\""; + static const unsigned result_len = sizeof(result) - 1; + rwbuff.pos = 0; + gmio_ostringstream_write_xmlattr_str(&sstream, "foo", "crac"); + gmio_ostringstream_write_xmlattr_u32(&sstream, "bar", 456789); + gmio_ostringstream_write_xmlcdata_open(&sstream); + gmio_ostringstream_write_xmlcdata_close(&sstream); + gmio_ostringstream_flush(&sstream); + UTEST_ASSERT(strncmp(result, sstream.strbuff.ptr, result_len) == 0); + UTEST_ASSERT(strncmp(result, output, result_len) == 0); + } + } + + free(input); + free(output); + return NULL; +} + static const char* test_internal__string_ascii_utils() { char c; /* for loop counter */