From 6cc42f96603b86edeb597dac607eb20117e4385d Mon Sep 17 00:00:00 2001 From: Roman Babenko Date: Sun, 24 Jul 2022 16:49:45 +0300 Subject: [PATCH] upd --- .circleci/config.yml | 21 - .../{codeql-analysis.yml => codeql.yml} | 2 +- .github/workflows/main.yml | 39 +- CMakeLists.txt | 7 +- README.md | 3 + SECURITY.md | 18 + base91.cpp | 96 ++-- base91.h | 479 +++++++++--------- jni/Android.mk | 8 - jni/Application.mk | 5 - jni/build.sh | 1 - test.sh | 2 + test_base91.cpp | 123 +++++ 13 files changed, 468 insertions(+), 336 deletions(-) delete mode 100644 .circleci/config.yml rename .github/workflows/{codeql-analysis.yml => codeql.yml} (99%) create mode 100644 SECURITY.md delete mode 100644 jni/Android.mk delete mode 100644 jni/Application.mk delete mode 100644 jni/build.sh create mode 100644 test_base91.cpp diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 4c7bcb7..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,21 +0,0 @@ -version: 2.1 -jobs: - build: - docker: - - image: ubuntu:18.04 - steps: - - run: apt-get update && apt-get install cmake build-essential -y - - checkout - - run: cmake . - - run: make - - run: md5sum base91 > base91.md5 - - run: ./base91 -e base91 base91.b91 - - run: mv base91 base91.bak - - run: ./base91.bak -d base91.b91 base91 - - run: md5sum -c base91.md5 -workflows: - # Name the workflow "welcome" - build_and_check: - # Run the welcome/run job in its own container - jobs: - - build diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql.yml similarity index 99% rename from .github/workflows/codeql-analysis.yml rename to .github/workflows/codeql.yml index 1811fc3..40349f3 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql.yml @@ -18,7 +18,7 @@ on: # The branches below must be a subset of the branches above branches: [ master ] schedule: - - cron: '31 5 * * 5' + - cron: '20 21 * * 5' jobs: analyze: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6a3dde2..fc50d4b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,15 +1,42 @@ name: CI -on: [push] +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + release: + types: [created] jobs: + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - name: Run a one-line script - run: echo Hello, world! - - name: Run a multi-line script - run: cmake . && make + + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJSON(github) }} + run: echo "$GITHUB_CONTEXT" + + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: CMake + run: cmake . + + - name: Make + run: make all + + - name: Test + run: make test + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + diff --git a/CMakeLists.txt b/CMakeLists.txt index abae1dd..847866a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,14 @@ cmake_minimum_required(VERSION 3.10) project(base91) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) add_executable(base91 base91.cpp) +add_executable(test_base91 test_base91.cpp test_base91.cpp) +add_test(NAME test_base91 COMMAND test_base91) +enable_testing() + +install(TARGETS base91) diff --git a/README.md b/README.md index c53e4fa..568f0ba 100644 --- a/README.md +++ b/README.md @@ -31,3 +31,6 @@ It may hurt C or C++ code when the string is placed into code. But sequence %%% should not appear. So, encoded string might be placed with raw string literal: char string[]=R"%%%( a string )%%%"; + +Workaround: use space | line feed | tab in encoded text to break wrong sequence due the algorithm skips non alphabet symbols. +e.g. Ma^7*/0629 -> Ma^7* /0629 diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..642eb76 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,18 @@ +# Security Policy + +## Supported Versions + +Use this section to tell people about which versions of your project are +currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 0.0.0 | :x: | + +## Reporting a Vulnerability + +Use this section to tell people how to report a vulnerability. + +Tell them where to go, how often they can expect to get an update on a +reported vulnerability, what to expect if the vulnerability is accepted or +declined, etc. diff --git a/base91.cpp b/base91.cpp index 9f93ff2..0acdb30 100644 --- a/base91.cpp +++ b/base91.cpp @@ -6,72 +6,68 @@ //------------------------------------------------------------------------------ -template -void -readFile(const std::string & fileName, Container &container) +template +void readFile(const std::string &fileName, Container &container) { - std::fstream fs(fileName, std::ios::in | std::ios::binary); - if (fs.is_open() and fs.good()) - { - fs.seekg(0, std::ios::end); - auto newSize = fs.tellp(); - fs.seekg(0, std::ios::beg); - container.clear(); - container.resize(newSize); - fs.read(&container[0], newSize); - fs.close(); - } - return; + std::fstream fs(fileName, std::ios::in | std::ios::binary); + if (fs.is_open() and fs.good()) + { + fs.seekg(0, std::ios::end); + auto newSize = fs.tellp(); + fs.seekg(0, std::ios::beg); + container.clear(); + container.resize(newSize); + fs.read(&container[0], newSize); + fs.close(); + } + return; } //------------------------------------------------------------------------------ -template -void -writeFile(const std::string & fileName, const Container &container) +template +void writeFile(const std::string &fileName, const Container &container) { - std::fstream fs(fileName, std::ios::trunc | std::ios::out | std::ios::binary); + std::fstream fs(fileName, std::ios::trunc | std::ios::out | std::ios::binary); - if (fs.good()) - { - fs.write(container.data(), container.size()); - fs.close(); - } - return; + if (fs.good()) + { + fs.write(container.data(), container.size()); + fs.close(); + } + return; } //------------------------------------------------------------------------------ -int -main(const int argc, const char *argv[]) +int main(const int argc, const char *argv[]) { - if (argc!=4 or not(0==strncmp("-e", argv[1], 3) or 0==strncmp("-d", argv[1], 3))) - { - std::cerr << "base91 -e|-d "; - return -1; - } + if (argc != 4 or not(0 == strncmp("-e", argv[1], 3) or 0 == strncmp("-d", argv[1], 3))) + { + std::cerr << "base91 -e|-d "; + return -1; + } - if (0==strncmp("-e", argv[1], 3)) - { - std::vector in; - std::string out; + if (0 == strncmp("-e", argv[1], 3)) + { + std::vector in; + std::string out; - readFile(argv[2], in); - base91::encode(in, out); - writeFile(argv[3], out); - } - else - { - std::string in; - std::vector out; + readFile(argv[2], in); + base91::encode(in, out); + writeFile(argv[3], out); + } + else + { + std::string in; + std::vector out; - readFile(argv[2], in); - base91::decode(in, out); - writeFile(argv[3], out); - } + readFile(argv[2], in); + base91::decode(in, out); + writeFile(argv[3], out); + } - return 0; + return 0; } //------------------------------------------------------------------------------ - diff --git a/base91.h b/base91.h index 6c668cf..eb57fae 100644 --- a/base91.h +++ b/base91.h @@ -27,7 +27,7 @@ SOFTWARE. #include #include -#if __CHAR_BIT__!=8 +#if __CHAR_BIT__ != 8 #error DESIGNED ONLY FOR 8 BIT BYTE (CHAR) #endif @@ -49,257 +49,250 @@ SOFTWARE. class base91 { private: - /* - BASE91 JSON OPTIMIZED ALPHABET: + /* + BASE91 JSON OPTIMIZED ALPHABET: !~}|{zyxwvutsrqponmlkjihgfedcba`_^]#[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-+*)($&% - */ - /** Base of the numeric system is 91dec equals ASCII symbol [ */ - static const char base = '['; + */ + /** Base of the numeric system is 91dec equals ASCII symbol [ */ + static const char base = '['; - /** Bits in one byte. Should be 8 */ - static const unsigned char_bit = __CHAR_BIT__; + /** Bits in one byte. Should be 8 */ + static const unsigned char_bit = __CHAR_BIT__; - /** Pair of base91 symbols might code 13 bits */ - static const unsigned b91word_bit = 13; + /** Pair of base91 symbols might code 13 bits */ + static const unsigned b91word_bit = 13; - /** 8192 possibly values for 13 bits */ - static const unsigned b91word_size = 0x2000; + /** 8192 possibly values for 13 bits */ + static const unsigned b91word_size = 0x2000; - /** Mask for 13 bits */ - static const unsigned b91word_mask = 0x1FFF; + /** Mask for 13 bits */ + static const unsigned b91word_mask = 0x1FFF; protected: - - /** - * Convert number 0 - 90 to symbol - * @param a[in] - number 0 - 90 - * @return symbol from alphabet or -1 if input is out of alphabet - */ - static inline char - encodeSymbol(const char a) - { - switch (a) - { - case '\0': return '!'; - break; - case '#': return '#'; - break; - case 'X': return '$'; - break; - default: - if ('\0' <= a and base > a) - { - return 0x7F ^ a; - } - break; - } - return -1; - } - - /** - * Convert base91 symbol to decimal values - * @param a[in] - any symbol - * @return 0-90 if symbol is in alphabet -1 - in other case - */ - static inline char - decodeSymbol(const char a) - { - switch (a) - { - case '!': - { - return '\0'; - } - break; - case '#': - { - return '#'; - } - break; - case '$': - { - return 'X'; - } - break; - default: - { - - if ('!' <= a and '~' >= a and '\''!=a and '\"'!=a and '\\'!=a) - { - return 0x7F ^ a; - } - } - break; - } - return -1; - } + /** + * Convert number 0 - 90 to symbol + * @param a[in] - number 0 - 90 + * @return symbol from alphabet or -1 if input is out of alphabet + */ + static inline char encodeSymbol(const char a) + { + switch (a) + { + case '\0': + return '!'; + break; + case '#': + return '#'; + break; + case 'X': + return '$'; + break; + default: + if ('\0' <= a and base > a) + { + return 0x7F ^ a; + } + break; + } + return -1; + } + + /** + * Convert base91 symbol to decimal values + * @param a[in] - any symbol + * @return 0-90 if symbol is in alphabet -1 - in other case + */ + static inline char decodeSymbol(const char a) + { + switch (a) + { + case '!': { + return '\0'; + } + break; + case '#': { + return '#'; + } + break; + case '$': { + return 'X'; + } + break; + default: { + + if ('!' <= a and '~' >= a and '\'' != a and '\"' != a and '\\' != a) + { + return 0x7F ^ a; + } + } + break; + } + return -1; + } public: - /** - * Encode 8bit based container(vector) to string - * @param in - 8bit based std::vector - * @param out - std::string - * @param dummy - do not use - */ - template - static void - encode(const Container &in, std::string &out, - typename std::enable_if::type * - dummy = nullptr) - { - out.clear(); - // reserved a bit more than necessary to avoid extra calculation - out.reserve(char_bit + ((2 * char_bit)*in.size()) / (b91word_bit)); - - auto HI = new char[b91word_size]; - auto LO = new char[b91word_size]; - { - // initialize encoder - char hi = 0; - char lo = 0; - unsigned n = 0; - while (n < b91word_size) - { - LO[n] = encodeSymbol(lo); - HI[n] = encodeSymbol(hi); - if (base==(++lo)) - { - lo = 0; - hi++; - } - n++; - } - } - - unsigned collector = 0; - unsigned bit_collected = 0; - - for (auto &n : in) - { - collector |= static_cast(n) << bit_collected; - bit_collected += char_bit; - while (b91word_bit <= bit_collected) - { - const unsigned cod = b91word_mask & collector; - out.push_back(LO[cod]); - out.push_back(HI[cod]); - collector >>= b91word_bit; - bit_collected -= b91word_bit; - } - } - - if (0!=bit_collected) - { - const unsigned cod = b91word_mask & collector; - out.push_back(LO[cod]); - if (7 <= bit_collected) - { - out.push_back(HI[cod]); - } - } - - delete[] LO; - delete[] HI; - - return; - } - - /** - * Decode string to binary std::vector - * @param in - std::string - * @param out - std::vector of 8bit elements - * @param dummy - do not use - */ - template - static void - decode(const std::string &in, Container &out, - typename std::enable_if::type * - dummy = nullptr) - { - out.clear(); - // reserved a bit more than necessary to avoid extra calculation - out.reserve(char_bit + (b91word_bit*in.size()) / (2 * char_bit)); - - auto ZYX = new char[1 << char_bit]; - for (unsigned n = 0; n < (1 << char_bit); ++n) - { - // fill reverse alphabet - ZYX[n] = decodeSymbol(n); - } - auto HILO = new short[base][base]; - { - // fill reverse codes - unsigned hi = 0; - unsigned lo = 0; - - for (unsigned n = 0; n < b91word_size; ++n) - { - HILO[hi][lo] = n; - if (base==(++lo)) - { - lo = 0; - hi++; - } - } - // subzero values to optional purpose - for (int n = 2; n < static_cast(base); ++n) - { - HILO[base - 1][n] = 0 - n; - } - } - - unsigned collector = 0; - int bit_collected = 0; - char lower = -1; - - for (auto &n : in) - { - const char digit = ZYX[n]; - if (-1==digit) - { - continue; - } - if (-1==lower) - { - lower = digit; - continue; - } - const short cod = HILO[digit][lower]; - if ((b91word_mask & cod)!=cod) - { - lower = -1; - continue; - // there is possibility to use 89 subzero values as codes - } - collector |= cod << bit_collected; - bit_collected += b91word_bit; - lower = -1; - - while (char_bit <= bit_collected) - { - out.push_back(static_cast(0xFF & collector)); - collector >>= char_bit; - bit_collected -= char_bit; - } - } - - if (-1!=lower) - { - collector |= HILO[0][lower] << bit_collected; - bit_collected += (char_bit - 1); - } - - if (char_bit <= bit_collected) - { - out.push_back(static_cast(0xFF & collector)); - } - - delete[] HILO; - delete[] ZYX; - - } - + /** + * Encode 8bit based container(vector) to string + * @param in - 8bit based std::vector + * @param out - std::string + * @param dummy - do not use + */ + template + static void encode(const Container &in, std::string &out, + typename std::enable_if::type *dummy = nullptr) + { + out.clear(); + // reserved a bit more than necessary to avoid extra calculation + out.reserve(char_bit + ((2 * char_bit) * in.size()) / (b91word_bit)); + + auto HI = new char[b91word_size]; + auto LO = new char[b91word_size]; + { + // initialize encoder + char hi = 0; + char lo = 0; + unsigned n = 0; + while (n < b91word_size) + { + LO[n] = encodeSymbol(lo); + HI[n] = encodeSymbol(hi); + if (base == (++lo)) + { + lo = 0; + hi++; + } + n++; + } + } + + unsigned collector = 0; + unsigned bit_collected = 0; + + for (auto &n : in) + { + collector |= static_cast(n) << bit_collected; + bit_collected += char_bit; + while (b91word_bit <= bit_collected) + { + const unsigned cod = b91word_mask & collector; + out.push_back(LO[cod]); + out.push_back(HI[cod]); + collector >>= b91word_bit; + bit_collected -= b91word_bit; + } + } + + if (0 != bit_collected) + { + const unsigned cod = b91word_mask & collector; + out.push_back(LO[cod]); + if (7 <= bit_collected) + { + out.push_back(HI[cod]); + } + } + + delete[] LO; + delete[] HI; + + return; + } + + /** + * Decode string to binary std::vector + * @param in - std::string + * @param out - std::vector of 8bit elements + * @param dummy - do not use + */ + template + static void decode(const std::string &in, Container &out, + typename std::enable_if::type *dummy = nullptr) + { + out.clear(); + // reserved a bit more than necessary to avoid extra calculation + out.reserve(char_bit + (b91word_bit * in.size()) / (2 * char_bit)); + + auto ZYX = new char[1 << char_bit]; + for (unsigned n = 0; n < (1 << char_bit); ++n) + { + // fill reverse alphabet + ZYX[n] = decodeSymbol(n); + } + auto HILO = new short[base][base]; + { + // fill reverse codes + unsigned hi = 0; + unsigned lo = 0; + std::cerr << "[["; + for (unsigned n = 0; n < b91word_size; ++n) + { + std::cerr << n << ","; + HILO[hi][lo] = n; + if (base == (++lo)) + { + lo = 0; + hi++; + std::cerr << "]," << std::endl << "["; + } + } + // subzero values to optional purpose + for (int n = 2; n < static_cast(base); ++n) + { + HILO[base - 1][n] = 0 - n; + std::cerr << 0 - n << ","; + } + std::cerr << "]]" << std::endl; + } + + unsigned collector = 0; + int bit_collected = 0; + char lower = -1; + + for (auto &n : in) + { + const char digit = ZYX[n]; + if (-1 == digit) + { + continue; + } + if (-1 == lower) + { + lower = digit; + continue; + } + const short cod = HILO[digit][lower]; + if ((b91word_mask & cod) != cod) + { + lower = -1; + continue; + // there is possibility to use 89 subzero values as codes + } + collector |= cod << bit_collected; + bit_collected += b91word_bit; + lower = -1; + + while (char_bit <= bit_collected) + { + out.push_back(static_cast(0xFF & collector)); + collector >>= char_bit; + bit_collected -= char_bit; + } + } + + if (-1 != lower) + { + collector |= HILO[0][lower] << bit_collected; + bit_collected += (char_bit - 1); + } + + if (char_bit <= bit_collected) + { + out.push_back(static_cast(0xFF & collector)); + } + + delete[] HILO; + delete[] ZYX; + } }; //------------------------------------------------------------------------------ - diff --git a/jni/Android.mk b/jni/Android.mk deleted file mode 100644 index daed4ff..0000000 --- a/jni/Android.mk +++ /dev/null @@ -1,8 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := base91 -LOCAL_SRC_FILES := base91.cpp - -include $(BUILD_SHARED_LIBRARY) diff --git a/jni/Application.mk b/jni/Application.mk deleted file mode 100644 index 7920228..0000000 --- a/jni/Application.mk +++ /dev/null @@ -1,5 +0,0 @@ -APP_STL:= c++_shared -APP_ABI := armeabi-v7a arm64-v8a -APP_OPTIM := release -APP_CPPFLAGS += -fexceptions -frtti -NDK_TOOLCHAIN_VERSION := clang \ No newline at end of file diff --git a/jni/build.sh b/jni/build.sh deleted file mode 100644 index 3b8d03e..0000000 --- a/jni/build.sh +++ /dev/null @@ -1 +0,0 @@ -/home/user/android/android-ndk-r19b/ndk-build NDK_PROJECT_PATH=/home/user/android/proj APP_PLATFORM=android-23 NDK_APPLICATION_MK=/home/user/android/proj/jni/Application.mk \ No newline at end of file diff --git a/test.sh b/test.sh index a1d1556..d135b32 100644 --- a/test.sh +++ b/test.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -e + for n in `seq 0 16384`; do original="/dev/shm/$n.original" diff --git a/test_base91.cpp b/test_base91.cpp new file mode 100644 index 0000000..acc3198 --- /dev/null +++ b/test_base91.cpp @@ -0,0 +1,123 @@ +#include +#include +#include +#include + +#include "base91.h" + +//------------------------------------------------------------------------------ + +template +bool test_refurbish(const size_t size) +{ + T data_original(size); + std::generate(data_original.begin(), data_original.end(), std::rand); + std::cerr << __LINE__ << ": Run test with size = " << data_original.size() << std::endl; + + std::string text; + base91::encode(data_original, text); + std::cerr << __LINE__ << ": Encoded data size = " << text.size() << std::endl; + + size_t n = 80 < text.size() ? 80 : text.size(); + std::cerr << __LINE__ << ": First " << n << " symbols are:" << std::endl; + + auto lim_text = text; + lim_text.resize(n); + std::cerr << lim_text << std::endl; + + T data_refurbed; + base91::decode(text, data_refurbed); + + if (data_original.size() != data_refurbed.size()) + { + std::cerr << __LINE__ << ": data_original size (" << data_original.size() + << ") does not equal data_refurbed size (" << data_refurbed.size() << ")" << std::endl; + return false; + } + + n = 0; + auto it_original = data_original.begin(); + auto it_refurbed = data_refurbed.begin(); + while (it_original != data_original.end() and it_refurbed != data_refurbed.end()) + { + if (*it_original != *it_refurbed) + { + std::cerr << __LINE__ << ": error on " << n << " element" << std::endl; + return false; + } + ++n; + ++it_original; + ++it_refurbed; + } + std::cerr << __LINE__ << ": Test passed with size = " << data_original.size() << std::endl; + return true; +} + +//------------------------------------------------------------------------------ + +bool test_static_with_space() +{ + std::string pangram = "The quick brown fox\tjumps\nover\rthe lazy dog"; + std::vector data; + base91::decode(pangram, data); + + if (26 != data.size()) + { + std::cerr << __LINE__ << ": data size = " << data.size() << std::endl; + return false; + } + + std::vector data_=// + {0xbc,0x43,0x41,0x8d,0xa9,0xde,0x32,0x61,0x92,0x8b,0xdf,0x81,0x33 + ,0x53,0x64,0x68,0xe4,0x12,0x1c,0xb4,0x6d,0xca,0xc3,0x67,0xc2,0x44}; + + size_t n = 0; + auto it = data.begin(); + auto it_ = data_.begin(); + while (it != data.end() and it_ != data_.end()) + { + if (*it != *it_) + { + std::cerr << __LINE__ << ": error on " << n << " element" << std::endl; + return false; + } + ++n; + ++it; + ++it_; + } + + if (data.size() != n) + { + std::cerr << __LINE__ << ": Pass only " << n << "bytes. Expected " << data.size() << std::endl; + return false; + } + + std::string pangram_; + base91::encode(data, pangram_); + + if ("Thequickbrownfoxjumpsoverthelazydog" != pangram_) + { + std::cerr << __LINE__ << ":" << std::endl << pangram << std::endl << pangram_ << std::endl; + return false; + } + std::cerr << __LINE__ << ": Static test passed" << std::endl; + return true; +} + +//------------------------------------------------------------------------------ + +int main(const int argc, const char *argv[]) +{ + std::srand(std::time(nullptr)); + if ( // + test_refurbish>(std::rand() % (1 << 20)) // + && // + test_refurbish>(std::rand() % (1 << 20)) // + && // + test_static_with_space() // + ) + return 0; + return 1; +} + +//------------------------------------------------------------------------------