├── .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 | [![Build Status](https://travis-ci.org/artemkin/z85.svg?branch=master)](https://travis-ci.org/artemkin/z85) 5 | [![Coverage Status](https://coveralls.io/repos/artemkin/z85/badge.png?branch=master)](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 | --------------------------------------------------------------------------------