├── .gitignore
├── .travis.yml
├── CMakeLists.txt
├── LICENSE
├── README.md
├── TODO.md
├── cmake.sh
├── src
├── CMakeLists.txt
├── z85.c
├── z85.h
├── z85.hpp
└── z85_impl.cpp
└── test
├── CMakeLists.txt
├── lest.hpp
└── test.cpp
/.gitignore:
--------------------------------------------------------------------------------
1 | # Object files
2 | *.o
3 |
4 | # Libraries
5 | *.lib
6 | *.a
7 |
8 | # Shared objects (inc. Windows DLLs)
9 | *.dll
10 | *.so
11 | *.so.*
12 | *.dylib
13 |
14 | # Executables
15 | *.exe
16 | *.out
17 | *.app
18 |
19 | _build
20 |
21 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: cpp
2 |
3 | compiler:
4 | - gcc
5 | - clang
6 |
7 | before_install:
8 | # g++4.8.1
9 | - if [ "$CXX" == "g++" ]; then sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; fi
10 |
11 | # clang 3.4
12 | - if [ "$CXX" == "clang++" ]; then sudo add-apt-repository -y ppa:h-rayflood/llvm; fi
13 |
14 | - sudo apt-get update -qq
15 |
16 | install:
17 | # g++4.8.1
18 | - if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.8; fi
19 | - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8" COVERAGE=1; fi
20 |
21 | # clang 3.4
22 | - if [ "$CXX" == "clang++" ]; then sudo apt-get install --allow-unauthenticated -qq clang-3.4; fi
23 | - if [ "$CXX" == "clang++" ]; then cwd=$(pwd); fi
24 |
25 | # install libc++
26 | - if [ "$CXX" == "clang++" ]; then export CXXFLAGS="-std=c++0x -stdlib=libc++"; fi
27 | - if [ "$CXX" == "clang++" ]; then svn co --quiet http://llvm.org/svn/llvm-project/libcxx/trunk libcxx; fi
28 |
29 | - if [ "$CXX" == "clang++" ]; then cd libcxx/lib && bash buildit; fi
30 | - if [ "$CXX" == "clang++" ]; then sudo cp ./libc++.so.1.0 /usr/lib/; fi
31 | - if [ "$CXX" == "clang++" ]; then sudo mkdir /usr/include/c++/v1; fi
32 | - if [ "$CXX" == "clang++" ]; then cd .. && sudo cp -r include/* /usr/include/c++/v1/; fi
33 | - if [ "$CXX" == "clang++" ]; then cd /usr/lib && sudo ln -sf libc++.so.1.0 libc++.so; fi
34 | - if [ "$CXX" == "clang++" ]; then sudo ln -sf libc++.so.1.0 libc++.so.1 && cd $cwd; fi
35 |
36 | - if [ "$CXX" == "clang++" ]; then export CXX="clang++-3.4" CC="clang-3.4"; fi
37 |
38 | # install cpp-coveralls
39 | - if [ -n "$COVERAGE" ]; then sudo pip install cpp-coveralls --use-mirrors; fi
40 |
41 | script:
42 | - ./cmake.sh && cd _build && make && make test
43 |
44 | after_success:
45 | - cd ..
46 | - if [ -n "$COVERAGE" ]; then coveralls --gcov gcov-4.8 -e test -e _build/CMakeFiles -E ".*\.(h|hpp)$"; fi
47 | # - find . -iname "*.gcno"
48 | # - find . -iname "*.gcda"
49 | # - find . -iname "*.gcov"
50 |
51 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required (VERSION 2.8)
2 | project (Z85)
3 |
4 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
5 |
6 | # Disable assert() to run all unit tests
7 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG")
8 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNDEBUG")
9 |
10 | # Compiler-specific C++11 activation
11 | if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
12 | execute_process(
13 | COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
14 | if (NOT (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7))
15 | message(FATAL_ERROR "${PROJECT_NAME} requires g++ 4.7 or greater.")
16 | endif ()
17 |
18 | # Add gcov support
19 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
20 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
21 | set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")
22 |
23 | elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
24 |
25 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage")
26 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage")
27 |
28 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
29 | endif ()
30 |
31 | add_subdirectory (src)
32 | add_subdirectory (test)
33 | enable_testing ()
34 | add_test (NAME Z85Test COMMAND Test)
35 |
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013, Stanislav Artemkin
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification,
5 | are permitted provided that the following conditions are met:
6 |
7 | Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | Redistributions in binary form must reproduce the above copyright notice, this
11 | list of conditions and the following disclaimer in the documentation and/or
12 | other materials provided with the distribution.
13 |
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Z85
2 | ===
3 |
4 | [](https://travis-ci.org/artemkin/z85)
5 | [](https://coveralls.io/r/artemkin/z85?branch=master)
6 |
7 | Z85 is a binary-to-text encoding library.
8 | It implements [ZeroMQ Base-85 Encoding Algorithm](http://rfc.zeromq.org/spec:32/Z85) and provides custom padding. Z85 is written in C and C++.
9 |
10 | Install
11 | -------
12 |
13 | Just grab z85.h
and z85.c
to your project, compile and link.
14 |
15 | Don't forget to take z85.hpp
and z85_impl.cpp
if you need C++ interface.
16 |
17 | Usage
18 | -----
19 |
20 | ### Hello World
21 | ```c
22 | #include "stdio.h"
23 | #include "string.h"
24 | #include "z85/z85.h"
25 |
26 | int main()
27 | {
28 | char helloData[8] = "\x86\x4F\xD2\x6F\xB5\x59\xF7\x5B";
29 |
30 | // Encode binary to printable string
31 | char encBuf[10+1] = {}; // +1 for null terminating char
32 | size_t bytesEncoded = Z85_encode(helloData, encBuf, 8);
33 |
34 | // Decode printable string to binary
35 | char decBuf[8] = {};
36 | size_t bytesDecoded = Z85_decode(encBuf, decBuf, bytesEncoded);
37 |
38 | printf("%s", encBuf);
39 |
40 | if (bytesEncoded == 10 &&
41 | bytesDecoded == 8 &&
42 | !memcmp(helloData, decBuf, 8))
43 | {
44 | printf("!\n");
45 | }
46 |
47 | return 0;
48 | }
49 | ```
50 |
51 | Output
52 | ```
53 | HelloWorld!
54 | ```
55 | 8 bytes of helloData
are encoded into "HelloWorld" (10 ASCII symbols). The overhead of encoding is 25%.
56 |
57 | Z85_encode
and Z85_decode
are implemented according to
58 | [Z85 specification](http://rfc.zeromq.org/spec:32/Z85).
59 | So, Z85_encode
expects as input a binary string that length is divisible by 4 with no remainder, and
60 | Z85_decode
expects as input a printable string that length is divisible by 5 with no remainder.
61 | It may be inconvenient, so the library provides functions that pad input strings to meet the above-mentioned requirements:
62 | Z85_encode_with_padding
and Z85_decode_with_padding
.
63 |
64 | ### Hello World 2
65 | ```c
66 | #include "stdio.h"
67 | #include "string.h"
68 | #include "stdlib.h"
69 | #include "z85/z85.h"
70 |
71 | char* encode(const char* src, size_t len)
72 | {
73 | // allocate output buffer (+1 for null terminating char)
74 | char* dest = (char*)malloc(Z85_encode_with_padding_bound(len) + 1);
75 |
76 | if (len == 0)
77 | {
78 | dest[0] = '\0'; // write null terminating char
79 | return dest;
80 | }
81 |
82 | // encode the input buffer, padding it if necessary
83 | len = Z85_encode_with_padding(src, dest, len);
84 |
85 | if (len == 0) // something went wrong
86 | {
87 | free(dest);
88 | return NULL;
89 | }
90 |
91 | dest[len] = '\0'; // write null terminating char
92 |
93 | return dest;
94 | }
95 |
96 | int main()
97 | {
98 | char* str = encode("\x86\x4F\xD2\x6F\xB5\x59\xF7\x5B", 8);
99 |
100 | if (str)
101 | {
102 | printf("%s\n", str);
103 | free(str);
104 | }
105 |
106 | return 0;
107 | }
108 | ```
109 |
110 | Output
111 | ```
112 | 4HelloWorld
113 | ```
114 |
115 | Z85_encode_with_padding_bound
is used to evaluate the size of output buffer.
116 | This function returns exact size of the output buffer, so you do not need to shrink it after encoding.
117 |
118 | The first symbol in encoded string ('4' in our example) stores a number of significant bytes contained in the remainder of input data.
119 | Original Z85 algorithm can't encode byte sequence that length is not divisible by 4 with no remainder. In Z85_encode_with_padding
120 | we pad the input remainder with '\0' bytes, encode the whole input with original algorithm and save a number of significat bytes
121 | in the reminder. '4' means no padding was even applied. '1', '2', '3' and '4' are possible values.
122 |
123 | See [z85.h](https://github.com/artemkin/z85/blob/master/src/z85.h)
for more details. It is well commented, so you can figure out
124 | how to decode padded string by yourself.
125 |
126 | Good luck!
127 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 |
2 | - [ ] generate single-include for both C and C++
3 |
--------------------------------------------------------------------------------
/cmake.sh:
--------------------------------------------------------------------------------
1 |
2 | mkdir _build
3 | cd _build
4 | cmake .. -DCMAKE_INSTALL_PREFIX=../_install
5 |
6 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library (Z85 z85.c z85.h)
2 | add_library (Z85cpp z85_impl.cpp z85.hpp)
3 | target_link_libraries (Z85cpp Z85)
4 |
5 | install (TARGETS Z85 DESTINATION lib)
6 | install (TARGETS Z85cpp DESTINATION lib)
7 |
8 |
--------------------------------------------------------------------------------
/src/z85.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Stanislav Artemkin .
3 | *
4 | * Redistribution and use in source and binary forms, with or without
5 | * modification, are permitted provided that the following conditions are
6 | * met:
7 | *
8 | * * Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | * * Redistributions in binary form must reproduce the above
11 | * copyright notice, this list of conditions and the following disclaimer
12 | * in the documentation and/or other materials provided with the
13 | * distribution.
14 | *
15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 | *
27 | * Implementation of 32/Z85 specification (http://rfc.zeromq.org/spec:32/Z85)
28 | * Source repository: http://github.com/artemkin/z85
29 | */
30 |
31 | #include
32 | #include
33 |
34 | #include "z85.h"
35 |
36 | typedef unsigned int uint32_t;
37 | typedef unsigned char byte;
38 |
39 | // make sure uint32_t is 32-bit
40 | typedef char Z85_uint32_t_static_assert[(sizeof(uint32_t) * CHAR_BIT == 32) * 2 - 1];
41 |
42 | #define DIV85_MAGIC 3233857729ULL
43 | // make sure magic constant is 64-bit
44 | typedef char Z85_div85_magic_static_assert[(sizeof(DIV85_MAGIC) * CHAR_BIT == 64) * 2 - 1];
45 |
46 | #define DIV85(number) ((uint32_t)((DIV85_MAGIC * number) >> 32) >> 6)
47 |
48 | static const char* base85 =
49 | {
50 | "0123456789"
51 | "abcdefghij"
52 | "klmnopqrst"
53 | "uvwxyzABCD"
54 | "EFGHIJKLMN"
55 | "OPQRSTUVWX"
56 | "YZ.-:+=^!/"
57 | "*?&<>()[]{"
58 | "}@%$#"
59 | };
60 |
61 | static byte base256[] =
62 | {
63 | 0x00, 0x44, 0x00, 0x54, 0x53, 0x52, 0x48, 0x00,
64 | 0x4B, 0x4C, 0x46, 0x41, 0x00, 0x3F, 0x3E, 0x45,
65 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
66 | 0x08, 0x09, 0x40, 0x00, 0x49, 0x42, 0x4A, 0x47,
67 | 0x51, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
68 | 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
69 | 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
70 | 0x3B, 0x3C, 0x3D, 0x4D, 0x00, 0x4E, 0x43, 0x00,
71 | 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
72 | 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
73 | 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
74 | 0x21, 0x22, 0x23, 0x4F, 0x00, 0x50, 0x00, 0x00
75 | };
76 |
77 | char* Z85_encode_unsafe(const char* source, const char* sourceEnd, char* dest)
78 | {
79 | byte* src = (byte*)source;
80 | byte* end = (byte*)sourceEnd;
81 | byte* dst = (byte*)dest;
82 | uint32_t value;
83 | uint32_t value2;
84 |
85 | for (; src != end; src += 4, dst += 5)
86 | {
87 | // unpack big-endian frame
88 | value = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
89 |
90 | value2 = DIV85(value); dst[4] = base85[value - value2 * 85]; value = value2;
91 | value2 = DIV85(value); dst[3] = base85[value - value2 * 85]; value = value2;
92 | value2 = DIV85(value); dst[2] = base85[value - value2 * 85]; value = value2;
93 | value2 = DIV85(value); dst[1] = base85[value - value2 * 85];
94 | dst[0] = base85[value2];
95 | }
96 |
97 | return (char*)dst;
98 | }
99 |
100 | char* Z85_decode_unsafe(const char* source, const char* sourceEnd, char* dest)
101 | {
102 | byte* src = (byte*)source;
103 | byte* end = (byte*)sourceEnd;
104 | byte* dst = (byte*)dest;
105 | uint32_t value;
106 |
107 | for (; src != end; src += 5, dst += 4)
108 | {
109 | value = base256[(src[0] - 32) & 127];
110 | value = value * 85 + base256[(src[1] - 32) & 127];
111 | value = value * 85 + base256[(src[2] - 32) & 127];
112 | value = value * 85 + base256[(src[3] - 32) & 127];
113 | value = value * 85 + base256[(src[4] - 32) & 127];
114 |
115 | // pack big-endian frame
116 | dst[0] = value >> 24;
117 | dst[1] = (byte)(value >> 16);
118 | dst[2] = (byte)(value >> 8);
119 | dst[3] = (byte)(value);
120 | }
121 |
122 | return (char*)dst;
123 | }
124 |
125 | size_t Z85_encode_bound(size_t size)
126 | {
127 | return size * 5 / 4;
128 | }
129 |
130 | size_t Z85_decode_bound(size_t size)
131 | {
132 | return size * 4 / 5;
133 | }
134 |
135 | size_t Z85_encode(const char* source, char* dest, size_t inputSize)
136 | {
137 | if (!source || !dest || inputSize % 4)
138 | {
139 | assert(!"wrong source, destination or input size");
140 | return 0;
141 | }
142 |
143 | return Z85_encode_unsafe(source, source + inputSize, dest) - dest;
144 | }
145 |
146 | size_t Z85_decode(const char* source, char* dest, size_t inputSize)
147 | {
148 | if (!source || !dest || inputSize % 5)
149 | {
150 | assert(!"wrong source, destination or input size");
151 | return 0;
152 | }
153 |
154 | return Z85_decode_unsafe(source, source + inputSize, dest) - dest;
155 | }
156 |
157 | size_t Z85_encode_with_padding_bound(size_t size)
158 | {
159 | if (size == 0) return 0;
160 | size = Z85_encode_bound(size);
161 | return size + (5 - size % 5) % 5 + 1;
162 | }
163 |
164 | size_t Z85_decode_with_padding_bound(const char* source, size_t size)
165 | {
166 | if (size == 0 || !source || (byte)(source[0] - '0' - 1) > 3) return 0;
167 | return Z85_decode_bound(size - 1) - 4 + (source[0] - '0');
168 | }
169 |
170 | size_t Z85_encode_with_padding(const char* source, char* dest, size_t inputSize)
171 | {
172 | size_t tailBytes = inputSize % 4;
173 | char tailBuf[4] = { 0 };
174 | char* dst = dest;
175 | const char* end = source + inputSize - tailBytes;
176 |
177 | assert(source && dest);
178 |
179 | // zero length string is not padded
180 | if (!source || !dest || inputSize == 0)
181 | {
182 | return 0;
183 | }
184 |
185 | (dst++)[0] = (tailBytes == 0 ? '4' : '0' + (char)tailBytes); // write tail bytes count
186 | dst = Z85_encode_unsafe(source, end, dst); // write body
187 |
188 | // write tail
189 | switch (tailBytes)
190 | {
191 | case 3:
192 | tailBuf[2] = end[2];
193 | case 2:
194 | tailBuf[1] = end[1];
195 | case 1:
196 | tailBuf[0] = end[0];
197 | dst = Z85_encode_unsafe(tailBuf, tailBuf + 4, dst);
198 | }
199 |
200 | return dst - dest;
201 | }
202 |
203 | size_t Z85_decode_with_padding(const char* source, char* dest, size_t inputSize)
204 | {
205 | char* dst = dest;
206 | size_t tailBytes;
207 | char tailBuf[4] = { 0 };
208 | const char* end = source + inputSize;
209 |
210 | assert(source && dest && (inputSize == 0 || (inputSize - 1) % 5 == 0));
211 |
212 | // zero length string is not padded
213 | if (!source || !dest || inputSize == 0 || (inputSize - 1) % 5)
214 | {
215 | return 0;
216 | }
217 |
218 | tailBytes = (source++)[0] - '0'; // possible values: 1, 2, 3 or 4
219 | if (tailBytes - 1 > 3)
220 | {
221 | assert(!"wrong tail bytes count");
222 | return 0;
223 | }
224 |
225 | end -= 5;
226 | if (source != end)
227 | {
228 | // decode body
229 | dst = Z85_decode_unsafe(source, end, dst);
230 | }
231 |
232 | // decode last 5 bytes chunk
233 | Z85_decode_unsafe(end, end + 5, tailBuf);
234 |
235 | switch (tailBytes)
236 | {
237 | case 4:
238 | dst[3] = tailBuf[3];
239 | case 3:
240 | dst[2] = tailBuf[2];
241 | case 2:
242 | dst[1] = tailBuf[1];
243 | case 1:
244 | dst[0] = tailBuf[0];
245 | }
246 |
247 | return dst - dest + tailBytes;
248 | }
249 |
--------------------------------------------------------------------------------
/src/z85.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Stanislav Artemkin .
3 | *
4 | * Redistribution and use in source and binary forms, with or without
5 | * modification, are permitted provided that the following conditions are
6 | * met:
7 | *
8 | * * Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | * * Redistributions in binary form must reproduce the above
11 | * copyright notice, this list of conditions and the following disclaimer
12 | * in the documentation and/or other materials provided with the
13 | * distribution.
14 | *
15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 | *
27 | * Implementation of 32/Z85 specification (http://rfc.zeromq.org/spec:32/Z85)
28 | * Source repository: http://github.com/artemkin/z85
29 | */
30 |
31 | #pragma once
32 |
33 | #include
34 |
35 | #if defined (__cplusplus)
36 | extern "C" {
37 | #endif
38 |
39 | /*******************************************************************************
40 | * ZeroMQ Base-85 encoding/decoding functions with custom padding *
41 | *******************************************************************************/
42 |
43 | /**
44 | * @brief Encodes 'inputSize' bytes from 'source' into 'dest'.
45 | * If 'inputSize' is not divisible by 4 with no remainder, 'source' is padded.
46 | * Destination buffer must be already allocated. Use Z85_encode_with_padding_bound() to
47 | * evaluate size of the destination buffer.
48 | *
49 | * @param source in, input buffer (binary string to be encoded)
50 | * @param dest out, destination buffer
51 | * @param inputSize in, number of bytes to be encoded
52 | * @return number of printable symbols written into 'dest' or 0 if something goes wrong
53 | */
54 | size_t Z85_encode_with_padding(const char* source, char* dest, size_t inputSize);
55 |
56 | /**
57 | * @brief Decodes 'inputSize' printable symbols from 'source' into 'dest',
58 | * encoded with Z85_encode_with_padding().
59 | * Destination buffer must be already allocated. Use Z85_decode_with_padding_bound() to
60 | * evaluate size of the destination buffer.
61 | *
62 | * @param source in, input buffer (printable string to be decoded)
63 | * @param dest out, destination buffer
64 | * @param inputSize in, number of symbols to be decoded
65 | * @return number of bytes written into 'dest' or 0 if something goes wrong
66 | */
67 | size_t Z85_decode_with_padding(const char* source, char* dest, size_t inputSize);
68 |
69 | /**
70 | * @brief Evaluates a size of output buffer needed to encode 'size' bytes
71 | * into string of printable symbols using Z85_encode_with_padding().
72 | *
73 | * @param size in, number of bytes to be encoded
74 | * @return minimal size of output buffer in bytes
75 | */
76 | size_t Z85_encode_with_padding_bound(size_t size);
77 |
78 | /**
79 | * @brief Evaluates a size of output buffer needed to decode 'size' symbols
80 | * into binary string using Z85_decode_with_padding().
81 | *
82 | * @param source in, input buffer (first symbol is read from 'source' to evaluate padding)
83 | * @param size in, number of symbols to be decoded
84 | * @return minimal size of output buffer in bytes
85 | */
86 | size_t Z85_decode_with_padding_bound(const char* source, size_t size);
87 |
88 |
89 |
90 | /*******************************************************************************
91 | * ZeroMQ Base-85 encoding/decoding functions (specification compliant) *
92 | *******************************************************************************/
93 |
94 | /**
95 | * @brief Encodes 'inputSize' bytes from 'source' into 'dest'.
96 | * If 'inputSize' is not divisible by 4 with no remainder, 0 is retured.
97 | * Destination buffer must be already allocated. Use Z85_encode_bound() to
98 | * evaluate size of the destination buffer.
99 | *
100 | * @param source in, input buffer (binary string to be encoded)
101 | * @param dest out, destination buffer
102 | * @param inputSize in, number of bytes to be encoded
103 | * @return number of printable symbols written into 'dest' or 0 if something goes wrong
104 | */
105 | size_t Z85_encode(const char* source, char* dest, size_t inputSize);
106 |
107 | /**
108 | * @brief Decodes 'inputSize' printable symbols from 'source' into 'dest'.
109 | * If 'inputSize' is not divisible by 5 with no remainder, 0 is returned.
110 | * Destination buffer must be already allocated. Use Z85_decode_bound() to
111 | * evaluate size of the destination buffer.
112 | *
113 | * @param source in, input buffer (printable string to be decoded)
114 | * @param dest out, destination buffer
115 | * @param inputSize in, number of symbols to be decoded
116 | * @return number of bytes written into 'dest' or 0 if something goes wrong
117 | */
118 | size_t Z85_decode(const char* source, char* dest, size_t inputSize);
119 |
120 | /**
121 | * @brief Evaluates a size of output buffer needed to encode 'size' bytes
122 | * into string of printable symbols using Z85_encode().
123 | *
124 | * @param size in, number of bytes to be encoded
125 | * @return minimal size of output buffer in bytes
126 | */
127 | size_t Z85_encode_bound(size_t size);
128 |
129 | /**
130 | * @brief Evaluates a size of output buffer needed to decode 'size' symbols
131 | * into binary string using Z85_decode().
132 | *
133 | * @param size in, number of symbols to be decoded
134 | * @return minimal size of output buffer in bytes
135 | */
136 | size_t Z85_decode_bound(size_t size);
137 |
138 |
139 |
140 | /*******************************************************************************
141 | * ZeroMQ Base-85 unsafe encoding/decoding functions (specification compliant) *
142 | *******************************************************************************/
143 |
144 | /**
145 | * @brief Encodes bytes from [source;sourceEnd) range into 'dest'.
146 | * It can be used for implementation of your own padding scheme.
147 | * Preconditions:
148 | * - (sourceEnd - source) % 4 == 0
149 | * - destination buffer must be already allocated
150 | *
151 | * @param source in, begin of input buffer
152 | * @param sourceEnd in, end of input buffer (not included)
153 | * @param dest out, output buffer
154 | * @return a pointer immediately after last symbol written into the 'dest'
155 | */
156 | char* Z85_encode_unsafe(const char* source, const char* sourceEnd, char* dest);
157 |
158 | /**
159 | * @brief Decodes symbols from [source;sourceEnd) range into 'dest'.
160 | * It can be used for implementation of your own padding scheme.
161 | * Preconditions:
162 | * - (sourceEnd - source) % 5 == 0
163 | * - destination buffer must be already allocated
164 | *
165 | * @param source in, begin of input buffer
166 | * @param sourceEnd in, end of input buffer (not included)
167 | * @param dest out, output buffer
168 | * @return a pointer immediately after last byte written into the 'dest'
169 | */
170 | char* Z85_decode_unsafe(const char* source, const char* sourceEnd, char* dest);
171 |
172 | #if defined (__cplusplus)
173 | }
174 | #endif
175 |
176 |
--------------------------------------------------------------------------------
/src/z85.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Stanislav Artemkin .
3 | *
4 | * Redistribution and use in source and binary forms, with or without
5 | * modification, are permitted provided that the following conditions are
6 | * met:
7 | *
8 | * * Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | * * Redistributions in binary form must reproduce the above
11 | * copyright notice, this list of conditions and the following disclaimer
12 | * in the documentation and/or other materials provided with the
13 | * distribution.
14 | *
15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 | *
27 | * Implementation of 32/Z85 specification (http://rfc.zeromq.org/spec:32/Z85)
28 | * Source repository: http://github.com/artemkin/z85
29 | */
30 |
31 | #pragma once
32 |
33 | #include
34 | #include
35 |
36 | // Used to forbid implicit std::string construction from const char*
37 | #if __cplusplus > 199711L // if C++11
38 | #define Z85_DELETE_FUNCTION_DEFINITION = delete
39 | #else
40 | #define Z85_DELETE_FUNCTION_DEFINITION
41 | #endif
42 |
43 | namespace z85
44 | {
45 |
46 | /*******************************************************************************
47 | * ZeroMQ Base-85 encoding/decoding functions with custom padding *
48 | *******************************************************************************/
49 |
50 | /**
51 | * @brief Encodes 'inputSize' bytes from 'source'.
52 | * If 'inputSize' is not divisible by 4 with no remainder, 'source' is padded.
53 | *
54 | * @param source in, input buffer (binary string to be encoded)
55 | * @param inputSize in, number of bytes to be encoded
56 | * @return printable string
57 | */
58 | std::string encode_with_padding(const char* source, size_t inputSize);
59 | std::string encode_with_padding(const std::string& source);
60 |
61 | std::string encode_with_padding(const char*) Z85_DELETE_FUNCTION_DEFINITION;
62 |
63 | /**
64 | * @brief Decodes 'inputSize' printable symbols from 'source',
65 | * encoded with encode_with_padding().
66 | *
67 | * @param source in, input buffer (printable string to be decoded)
68 | * @param inputSize in, number of symbols to be decoded
69 | * @return decoded string
70 | */
71 | std::string decode_with_padding(const char* source, size_t inputSize);
72 | std::string decode_with_padding(const std::string& source);
73 |
74 | std::string decode_with_padding(const char*) Z85_DELETE_FUNCTION_DEFINITION;
75 |
76 |
77 | /*******************************************************************************
78 | * ZeroMQ Base-85 encoding/decoding functions (specification compliant) *
79 | *******************************************************************************/
80 |
81 | /**
82 | * @brief Encodes 'inputSize' bytes from 'source'.
83 | * If 'inputSize' is not divisible by 4 with no remainder, empty string is retured.
84 | *
85 | * @param source in, input buffer (binary string to be encoded)
86 | * @param inputSize in, number of bytes to be encoded
87 | * @return printable string
88 | */
89 | std::string encode(const char* source, size_t inputSize);
90 | std::string encode(const std::string& source);
91 |
92 | std::string encode(const char*) Z85_DELETE_FUNCTION_DEFINITION;
93 |
94 | /**
95 | * @brief Decodes 'inputSize' printable symbols from 'source'.
96 | * If 'inputSize' is not divisible by 5 with no remainder, empty string is returned.
97 | *
98 | * @param source in, input buffer (printable string to be decoded)
99 | * @param inputSize in, number of symbols to be decoded
100 | * @return decoded string
101 | */
102 | std::string decode(const char* source, size_t inputSize);
103 | std::string decode(const std::string& source);
104 |
105 | std::string decode(const char*) Z85_DELETE_FUNCTION_DEFINITION;
106 |
107 | } // namespace z85
108 |
109 | #undef Z85_DELETE_FUNCTION_DEFINITION
110 |
111 |
--------------------------------------------------------------------------------
/src/z85_impl.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Stanislav Artemkin .
3 | *
4 | * Redistribution and use in source and binary forms, with or without
5 | * modification, are permitted provided that the following conditions are
6 | * met:
7 | *
8 | * * Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | * * Redistributions in binary form must reproduce the above
11 | * copyright notice, this list of conditions and the following disclaimer
12 | * in the documentation and/or other materials provided with the
13 | * distribution.
14 | *
15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 | *
27 | * Implementation of 32/Z85 specification (http://rfc.zeromq.org/spec:32/Z85)
28 | * Source repository: http://github.com/artemkin/z85
29 | */
30 |
31 | #include "z85.hpp"
32 |
33 | #include
34 |
35 | #include "z85.h"
36 |
37 |
38 | namespace z85
39 | {
40 |
41 | std::string encode_with_padding(const char* source, size_t inputSize)
42 | {
43 | if (!source || inputSize == 0)
44 | {
45 | return std::string();
46 | }
47 |
48 | std::string buf;
49 | buf.resize(Z85_encode_with_padding_bound(inputSize));
50 |
51 | const size_t encodedBytes = Z85_encode_with_padding(source, &buf[0], inputSize);
52 | assert(encodedBytes == buf.size()); (void)encodedBytes;
53 |
54 | return buf;
55 | }
56 |
57 | std::string encode_with_padding(const std::string& source)
58 | {
59 | return encode_with_padding(source.c_str(), source.size());
60 | }
61 |
62 | std::string decode_with_padding(const char* source, size_t inputSize)
63 | {
64 | if (!source || inputSize == 0)
65 | {
66 | return std::string();
67 | }
68 |
69 | const size_t bufSize = Z85_decode_with_padding_bound(source, inputSize);
70 | if (bufSize == 0)
71 | {
72 | assert(!"wrong padding");
73 | return std::string();
74 | }
75 |
76 | std::string buf;
77 | buf.resize(bufSize);
78 |
79 | const size_t decodedBytes = Z85_decode_with_padding(source, &buf[0], inputSize);
80 | assert(decodedBytes == buf.size()); (void)decodedBytes;
81 |
82 | return buf;
83 | }
84 |
85 | std::string decode_with_padding(const std::string& source)
86 | {
87 | return decode_with_padding(source.c_str(), source.size());
88 | }
89 |
90 | std::string encode(const char* source, size_t inputSize)
91 | {
92 | if (!source || inputSize == 0)
93 | {
94 | return std::string();
95 | }
96 |
97 | std::string buf;
98 | buf.resize(Z85_encode_bound(inputSize));
99 |
100 | const size_t encodedBytes = Z85_encode(source, &buf[0], inputSize);
101 | if (encodedBytes == 0)
102 | {
103 | assert(!"wrong input size");
104 | return std::string();
105 | }
106 |
107 | return buf;
108 | }
109 |
110 | std::string encode(const std::string& source)
111 | {
112 | return encode(source.c_str(), source.size());
113 | }
114 |
115 | std::string decode(const char* source, size_t inputSize)
116 | {
117 | if (!source || inputSize == 0)
118 | {
119 | return std::string();
120 | }
121 |
122 | std::string buf;
123 | buf.resize(Z85_decode_bound(inputSize));
124 |
125 | const size_t decodedBytes = Z85_decode(source, &buf[0], inputSize);
126 | if (decodedBytes == 0)
127 | {
128 | assert(!"wrong input size");
129 | return std::string();
130 | }
131 |
132 | return buf;
133 | }
134 |
135 | std::string decode(const std::string& source)
136 | {
137 | return decode(source.c_str(), source.size());
138 | }
139 |
140 | } // namespace z85
141 |
142 |
--------------------------------------------------------------------------------
/test/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | include_directories (${Z85_SOURCE_DIR}/src)
2 | add_executable (Test test.cpp)
3 | target_link_libraries (Test Z85cpp)
4 |
5 |
--------------------------------------------------------------------------------
/test/lest.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013 by Martin Moene
2 | //
3 | // lest is based on ideas by Kevlin Henney, see video at
4 | // http://skillsmatter.com/podcast/agile-testing/kevlin-henney-rethinking-unit-testing-in-c-plus-plus
5 | //
6 | // Distributed under the Boost Software License, Version 1.0. (See accompanying
7 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 |
9 | #ifndef LEST_LEST_H_INCLUDED
10 | #define LEST_LEST_H_INCLUDED
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | #ifndef lest_NO_SHORT_ASSERTION_NAMES
19 | # define EXPECT lest_EXPECT
20 | # define EXPECT_THROWS lest_EXPECT_THROWS
21 | # define EXPECT_THROWS_AS lest_EXPECT_THROWS_AS
22 | #endif
23 |
24 | #define lest_EXPECT( expr ) \
25 | try \
26 | { \
27 | if ( ! (expr) ) \
28 | throw lest::failure{ lest_LOCATION, #expr }; \
29 | } \
30 | catch( lest::failure const & ) \
31 | { \
32 | throw ; \
33 | } \
34 | catch( std::exception const & e ) \
35 | { \
36 | throw lest::unexpected{ lest_LOCATION, #expr, lest::with_message( e.what() ) }; \
37 | } \
38 | catch(...) \
39 | { \
40 | throw lest::unexpected{ lest_LOCATION, #expr, "of unknown type" }; \
41 | }
42 |
43 | #define lest_EXPECT_THROWS( expr ) \
44 | for (;;) \
45 | { \
46 | try { lest::serum( expr ); } catch (...) { break; } \
47 | throw lest::expected{ lest_LOCATION, #expr }; \
48 | }
49 |
50 | #define lest_EXPECT_THROWS_AS( expr, excpt ) \
51 | for (;;) \
52 | { \
53 | try { lest::serum( expr ); } catch ( excpt & ) { break; } catch (...) {} \
54 | throw lest::expected{ lest_LOCATION, #expr, lest::of_type( #excpt ) }; \
55 | }
56 |
57 | #define lest_LOCATION lest::location{__FILE__, __LINE__}
58 |
59 | namespace lest {
60 |
61 | struct test
62 | {
63 | const std::string name;
64 | const std::function behaviour;
65 | };
66 |
67 | struct location
68 | {
69 | const std::string file;
70 | const int line;
71 |
72 | location( std::string file, int line )
73 | : file{ file }, line{ line } {}
74 | };
75 |
76 | struct comment
77 | {
78 | const std::string text;
79 |
80 | comment( std::string text ) : text{ text } {}
81 | explicit operator bool() { return ! text.empty(); }
82 | };
83 |
84 | struct message : std::runtime_error
85 | {
86 | const std::string kind;
87 | const location where;
88 | const comment note;
89 |
90 | ~message() throw() {} // GCC 4.6
91 |
92 | message( std::string kind, location where, std::string expr, std::string note = "" )
93 | : std::runtime_error{ expr }, kind{ kind }, where{ where }, note{ note } {}
94 | };
95 |
96 | struct failure : message
97 | {
98 | failure( location where, std::string expr )
99 | : message{ "failed", where, expr } {}
100 | };
101 |
102 | struct expected : message
103 | {
104 | expected( location where, std::string expr, std::string excpt = "" )
105 | : message{ "failed: didn't get exception", where, expr, excpt } {}
106 | };
107 |
108 | struct unexpected : message
109 | {
110 | unexpected( location where, std::string expr, std::string note )
111 | : message{ "failed: got unexpected exception", where, expr, note } {}
112 | };
113 |
114 | inline bool serum( bool verum ) { return verum; }
115 |
116 | inline std::string with_message( std::string text )
117 | {
118 | return "with message \"" + text + "\"";
119 | }
120 |
121 | inline std::string of_type( std::string text )
122 | {
123 | return "of type " + text;
124 | }
125 |
126 | inline std::string pluralise( int n, std::string text )
127 | {
128 | return n == 1 ? text : text + "s";
129 | }
130 |
131 | inline std::ostream & operator<<( std::ostream & os, comment note )
132 | {
133 | return os << (note ? " " + note.text : "" );
134 | }
135 |
136 | inline std::ostream & operator<<( std::ostream & os, location where )
137 | {
138 | #ifdef __GNUG__
139 | return os << where.file << ":" << where.line;
140 | #else
141 | return os << where.file << "(" << where.line << ")";
142 | #endif
143 | }
144 |
145 | inline void report( std::ostream & os, message const & e, std::string test )
146 | {
147 | os << e.where << ": " << e.kind << e.note << ": " << test << ": " << e.what() << std::endl;
148 | }
149 |
150 | template
151 | int run( test const (&specification)[N], std::ostream & os = std::cout )
152 | {
153 | int failures = 0;
154 |
155 | for ( auto & testing : specification )
156 | {
157 | try
158 | {
159 | testing.behaviour();
160 | }
161 | catch( message const & e )
162 | {
163 | ++failures;
164 | report( os, e, testing.name );
165 | }
166 | }
167 |
168 | if ( failures > 0 )
169 | {
170 | os << failures << " out of " << N << " " << pluralise(N, "test") << " failed." << std::endl;
171 | }
172 |
173 | return failures;
174 | }
175 |
176 | } // namespace lest
177 |
178 | #endif // LEST_LEST_H_INCLUDED
179 |
--------------------------------------------------------------------------------
/test/test.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 | #include
5 |
6 | #include "lest.hpp"
7 | #include "z85.h"
8 | #include "z85.hpp"
9 |
10 | using namespace std;
11 |
12 |
13 | const string c_badfood = "baadfoodbaadfoodbaadfoodbaadfood";
14 |
15 | // Checks for buffer overruns
16 | class strict_buf
17 | {
18 | string m_buf;
19 |
20 | public:
21 | explicit strict_buf(size_t i_size)
22 | : m_buf(c_badfood + string(i_size + 1, '\0') + c_badfood)
23 | {
24 | }
25 |
26 | char* p()
27 | {
28 | return &m_buf[0] + c_badfood.size();
29 | }
30 |
31 | std::string data() const
32 | {
33 | size_t count = c_badfood.size();
34 |
35 | return m_buf.substr(count, m_buf.size() - 2 * count - 1);
36 | }
37 |
38 | void check_buffer()
39 | {
40 | size_t count = c_badfood.size();
41 |
42 | const bool strict_buf_bad_food =
43 | m_buf.substr(0, count) != c_badfood ||
44 | m_buf[m_buf.size()-1-count] != '\0' ||
45 | m_buf.substr(m_buf.size()-count) != c_badfood;
46 |
47 | EXPECT(strict_buf_bad_food == false);
48 | }
49 | };
50 |
51 | template
52 | void with_strict_buf(size_t bufSize, Fn f)
53 | {
54 | strict_buf buf(bufSize);
55 | f(buf);
56 | buf.check_buffer();
57 | }
58 |
59 | template
60 | void for_random_data(size_t divisibleBy, Fn f)
61 | {
62 | srand(0); //magic seed)
63 |
64 | std::string data;
65 |
66 | for (size_t i = 0; i < 10000; ++i)
67 | {
68 | const char ch = rand() % 256;
69 | data += ch;
70 |
71 | if (data.size() % divisibleBy == 0)
72 | {
73 | f(data);
74 | }
75 | }
76 | }
77 |
78 | const lest::test specification[] =
79 | {
80 | "Hello world!", []
81 | {
82 | EXPECT(z85::encode(string("\x86\x4F\xD2\x6F\xB5\x59\xF7\x5B")) == "HelloWorld");
83 | },
84 |
85 | "Test strict buffer", []
86 | {
87 | {
88 | strict_buf buf_holder(0);
89 | char* buf = buf_holder.p();
90 |
91 | EXPECT(buf[-1] != '\0');
92 | EXPECT(buf[0] == '\0');
93 | EXPECT(buf[1] != '\0');
94 | }
95 | {
96 | strict_buf buf_holder(1);
97 | char* buf = buf_holder.p();
98 |
99 | EXPECT(buf[-1] != '\0');
100 | EXPECT(buf[0] == '\0');
101 | EXPECT(buf[1] == '\0');
102 | EXPECT(buf[2] != '\0');
103 | }
104 | },
105 |
106 | "Test unsafe", []
107 | {
108 | auto test = [](const string& bin, const string& txt)
109 | {
110 | with_strict_buf(txt.size(), [&](strict_buf& txt_buf) {
111 | with_strict_buf(bin.size(), [&](strict_buf& bin_buf) {
112 |
113 | char* txt_end = Z85_encode_unsafe(bin.c_str(), bin.c_str() + bin.size(), txt_buf.p());
114 | char* bin_end = Z85_decode_unsafe(txt_buf.p(), txt_end, bin_buf.p());
115 |
116 | EXPECT((txt_end - txt_buf.p()) == (int)txt.size());
117 | EXPECT((bin_end - bin_buf.p()) == (int)bin.size());
118 | EXPECT(bin_buf.data() == bin);
119 | EXPECT(txt_buf.data() == txt);
120 |
121 | });});
122 | };
123 |
124 | test("", "");
125 | test("\x86\x4F\xD2\x6F\xB5\x59\xF7\x5B", "HelloWorld");
126 | test("\x8E\x0B\xDD\x69\x76\x28\xB9\x1D\x8F\x24\x55\x87\xEE\x95\xC5\xB0"
127 | "\x4D\x48\x96\x3F\x79\x25\x98\x77\xB4\x9C\xD9\x06\x3A\xEA\xD3\xB7",
128 | "JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6");
129 | },
130 |
131 | "Test no padding", []
132 | {
133 | auto test = [](const string& bin, const string& txt)
134 | {
135 | with_strict_buf(txt.size(), [&](strict_buf& txt_buf) {
136 | with_strict_buf(bin.size(), [&](strict_buf& bin_buf) {
137 |
138 | size_t txt_written = Z85_encode(bin.c_str(), txt_buf.p(), bin.size());
139 | size_t bin_written = Z85_decode(txt_buf.p(), bin_buf.p(), txt_written);
140 |
141 | EXPECT(txt_written == txt.size());
142 | EXPECT(bin_written == bin.size());
143 | EXPECT(bin_buf.data() == bin);
144 | EXPECT(txt_buf.data() == txt);
145 |
146 | });});
147 | };
148 |
149 | test("", "");
150 | test("\x86\x4F\xD2\x6F", "Hello");
151 | test("\x86\x4F\xD2\x6F\xB5\x59\xF7\x5B", "HelloWorld");
152 | test("\x8E\x0B\xDD\x69\x76\x28\xB9\x1D\x8F\x24\x55\x87\xEE\x95\xC5\xB0"
153 | "\x4D\x48\x96\x3F\x79\x25\x98\x77\xB4\x9C\xD9\x06\x3A\xEA\xD3\xB7",
154 | "JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6");
155 | },
156 |
157 | "Test with padding", []
158 | {
159 | auto test = [](const string& bin, const string& txt)
160 | {
161 | with_strict_buf(txt.size(), [&](strict_buf& txt_buf) {
162 | with_strict_buf(bin.size(), [&](strict_buf& bin_buf) {
163 |
164 | size_t txt_written = Z85_encode_with_padding(bin.c_str(), txt_buf.p(), bin.size());
165 | size_t bin_written = Z85_decode_with_padding(txt_buf.p(), bin_buf.p(), txt_written);
166 |
167 | EXPECT(txt_written == txt.size());
168 | EXPECT(bin_written == bin.size());
169 | EXPECT(bin_buf.data() == bin);
170 | EXPECT(txt_buf.data() == txt);
171 |
172 | });});
173 | };
174 |
175 | test("", "");
176 | test("\x86\x4F\xD2\x6F\xB5\x59\xF7\x5B", "4HelloWorld");
177 | test("\x8E\x0B\xDD\x69\x76\x28\xB9\x1D\x8F\x24\x55\x87\xEE\x95\xC5\xB0"
178 | "\x4D\x48\x96\x3F\x79\x25\x98\x77\xB4\x9C\xD9\x06\x3A\xEA\xD3\xB7",
179 | "4JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6");
180 | },
181 |
182 | "Test no padding roundtrip", []
183 | {
184 | for_random_data(4, [](const string& bin)
185 | {
186 | const string txt = z85::encode(bin);
187 | const string new_bin = z85::decode(txt);
188 |
189 | EXPECT(new_bin == bin);
190 | });
191 | },
192 |
193 | "Test with padding roundtrip", []
194 | {
195 | for_random_data(1, [](const string& bin)
196 | {
197 | const string txt = z85::encode_with_padding(bin);
198 | const string new_bin = z85::decode_with_padding(txt);
199 |
200 | EXPECT(new_bin == bin);
201 | });
202 | },
203 |
204 | "Test Z85_encode_bound()", []
205 | {
206 | char buf[1300];
207 | std::string input;
208 | for (size_t i = 0; i < 1000; i += 4)
209 | {
210 | const size_t estimate = Z85_encode_bound(input.size());
211 | const size_t encoded = Z85_encode(input.c_str(), buf, input.size());
212 | EXPECT(estimate == encoded);
213 | input += i % 256;
214 | input += (i + 1) % 256;
215 | input += (i + 2) % 256;
216 | input += (i + 3) % 256;
217 | }
218 | },
219 |
220 | "Test Z85_decode_bound()", []
221 | {
222 | char buf[1300];
223 | std::string input;
224 | for (size_t i = 0; i < 1000; i += 5)
225 | {
226 | const size_t estimate = Z85_decode_bound(input.size());
227 | const size_t decoded = Z85_decode(input.c_str(), buf, input.size());
228 | EXPECT(estimate == decoded);
229 | input += i % 256;
230 | input += (i + 1) % 256;
231 | input += (i + 2) % 256;
232 | input += (i + 3) % 256;
233 | input += (i + 4) % 256;
234 | }
235 | },
236 |
237 | "Test Z85_encode_with_padding_bound()", []
238 | {
239 | char buf[1300];
240 | std::string input;
241 | for (size_t i = 0; i < 1000; ++i)
242 | {
243 | const size_t estimate = Z85_encode_with_padding_bound(input.size());
244 | const size_t encoded = Z85_encode_with_padding(input.c_str(), buf, input.size());
245 | EXPECT(estimate == encoded);
246 | input += i % 256;
247 | }
248 | },
249 |
250 | "Test Z85_decode_with_padding_bound()", []
251 | {
252 | char buf[1300];
253 | char buf2[1300];
254 | std::string input;
255 | for (size_t i = 0; i < 1000; ++i)
256 | {
257 | const size_t encoded = Z85_encode_with_padding(input.c_str(), buf, input.size());
258 | const size_t estimate = Z85_decode_with_padding_bound(buf, encoded);
259 | const size_t decoded = Z85_decode_with_padding(buf, buf2, encoded);
260 | EXPECT(estimate == decoded);
261 | input += i % 256;
262 | }
263 | },
264 |
265 | "Test Z85_encode()/Z85_decode() wrong input", []
266 | {
267 | char buf[100];
268 | EXPECT(Z85_encode(NULL, NULL, 5) == 0);
269 | EXPECT(Z85_encode("some binary data", NULL, 5) == 0);
270 | EXPECT(Z85_encode("some binary data", buf, 5) == 0);
271 | EXPECT(Z85_encode("some binary data", buf, 16) == 20);
272 |
273 | EXPECT(Z85_decode(NULL, NULL, 4) == 0);
274 | EXPECT(Z85_decode("some text.", NULL, 4) == 0);
275 | EXPECT(Z85_decode("some text.", buf, 4) == 0);
276 | EXPECT(Z85_decode("some text.", buf, 10) == 8);
277 | },
278 |
279 | "Test Z85_decode_with_padding() wrong tail bytes count", []
280 | {
281 | char buf[100];
282 | EXPECT(Z85_decode_with_padding("0HelloWorld", buf, 11) == 0);
283 | EXPECT(Z85_decode_with_padding("5HelloWorld", buf, 11) == 0);
284 | EXPECT(Z85_decode_with_padding("AHelloWorld", buf, 11) == 0);
285 | EXPECT(Z85_decode_with_padding("4HelloWorld", buf, 11) == 8);
286 | },
287 |
288 | "Test wrong input for z85:: functions", []
289 | {
290 | EXPECT(z85::encode_with_padding(NULL, 0) == "");
291 | EXPECT(z85::encode_with_padding("bin", 0) == "");
292 | EXPECT(z85::encode_with_padding(NULL, 4) == "");
293 |
294 | EXPECT(z85::decode_with_padding(NULL, 0) == "");
295 | EXPECT(z85::decode_with_padding("txt", 0) == "");
296 | EXPECT(z85::decode_with_padding(NULL, 5) == "");
297 |
298 | EXPECT(z85::encode(NULL, 0) == "");
299 | EXPECT(z85::encode("bin", 0) == "");
300 | EXPECT(z85::encode(NULL, 4) == "");
301 |
302 | EXPECT(z85::decode(NULL, 0) == "");
303 | EXPECT(z85::decode("txt", 0) == "");
304 | EXPECT(z85::decode(NULL, 5) == "");
305 | }
306 | };
307 |
308 | int main()
309 | {
310 | return lest::run(specification);
311 | }
312 |
313 |
--------------------------------------------------------------------------------