diff --git a/src/tl/tl/tl.pro b/src/tl/tl/tl.pro index 2fc72a1fe..b74ae8a4d 100644 --- a/src/tl/tl/tl.pro +++ b/src/tl/tl/tl.pro @@ -10,6 +10,7 @@ FORMS = SOURCES = \ tlAssert.cc \ + tlBase64.cc \ tlClassRegistry.cc \ tlCopyOnWrite.cc \ tlDataMapping.cc \ @@ -56,6 +57,7 @@ SOURCES = \ HEADERS = \ tlAlgorithm.h \ tlAssert.h \ + tlBase64.h \ tlClassRegistry.h \ tlCopyOnWrite.h \ tlDataMapping.h \ diff --git a/src/tl/tl/tlBase64.cc b/src/tl/tl/tlBase64.cc new file mode 100644 index 000000000..65a79f01a --- /dev/null +++ b/src/tl/tl/tlBase64.cc @@ -0,0 +1,139 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2022 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "tlBase64.h" +#include "tlException.h" +#include "tlInternational.h" + +namespace tl +{ + +namespace { + +class EncoderTable +{ +public: + EncoderTable () + { + char charset[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + for (unsigned int i = 0; i < 256; ++i) { + m_char2bin[i] = 0xff; + } + for (unsigned int i = 0; i < 64; ++i) { + m_bin2char[i] = charset[i]; + m_char2bin[(unsigned int) charset[i]] = i; + } + } + + inline char c (unsigned char b) const { return m_bin2char[b]; } + inline unsigned char b (char c) const { return m_char2bin[(unsigned char) c]; } + +private: + char m_bin2char[64]; + unsigned char m_char2bin[256]; +}; + +} + +static EncoderTable s_enc; + +std::vector from_base64 (const char *s) +{ + size_t sz = 0; + for (const char *t = s; *t; ++t) { + ++sz; + } + + unsigned int sh = 0; + + std::vector data; + data.reserve ((sz * 6 + 7) / 8); + + for (const char *t = s; *t; ++t) { + + if ((unsigned char) *t <= ' ') { + + // ignore white space characters + + } else if (*t == '=') { + + // padding/termination + if (data.empty () || data.back () != 0) { + throw tl::Exception (tl::to_string (tr ("Error decoding base64 data: padding character does not match zero byte"))); + } + data.pop_back (); + break; + + } else { + + unsigned char b = s_enc.b (*t); + if (b >= 64) { + throw tl::Exception (tl::to_string (tr ("Error decoding base64 data: invalid character '%c'")), *t); + } + + sh += 2; + + if (sh == 8) { + data.back () |= b; + sh = 0; + } else if (sh == 2) { + data.push_back (b << 2); + } else { + data.back () |= (b >> (8 - sh)); + data.push_back (b << sh); + } + + } + + } + + return data; +} + +std::string to_base64 (const unsigned char *data, size_t size) +{ + std::string s; + s.reserve (((size + 2) / 3) * 4); + + size_t bits = size * 8; + + for (size_t b = 0; b < bits; b += 6) { + size_t bit = b % 8; + size_t byte = b / 8; + if (bit <= 2) { + s += s_enc.c ((data [byte] >> (2 - bit)) & 0x3f); + } else if (b + 8 < bits) { + s += s_enc.c (((data [byte] << (bit - 2)) | (data [byte + 1] >> (10 - bit))) & 0x3f); + } else { + s += s_enc.c ((data [byte] << (bit - 2)) & 0x3f); + s += '='; + if (bit == 6) { + s += '='; + } + } + } + + return s; +} + +} diff --git a/src/tl/tl/tlBase64.h b/src/tl/tl/tlBase64.h new file mode 100644 index 000000000..227624185 --- /dev/null +++ b/src/tl/tl/tlBase64.h @@ -0,0 +1,47 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2022 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_tlBase64 +#define HDR_tlBase64 + +#include "tlCommon.h" + +#include +#include + +namespace tl +{ + +/** + * Converts a base64-encoded string into binary data + */ +TL_PUBLIC std::vector from_base64 (const char *s); + +/** + * Converts binary data into a base64-encoded string + */ +TL_PUBLIC std::string to_base64 (const unsigned char *data, size_t size); + +} + +#endif diff --git a/src/tl/unit_tests/tlBase64Tests.cc b/src/tl/unit_tests/tlBase64Tests.cc new file mode 100644 index 000000000..9f0db8eeb --- /dev/null +++ b/src/tl/unit_tests/tlBase64Tests.cc @@ -0,0 +1,98 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2022 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "tlBase64.h" +#include "tlUnitTest.h" + +TEST(1) +{ + std::vector r = tl::from_base64 (""); + EXPECT_EQ (r.empty (), true); + + r = tl::from_base64 ("YQ=="); + EXPECT_EQ (std::string ((const char *) r.begin ().operator-> (), r.size ()), "a"); + + r = tl::from_base64 ("YQ=="); + EXPECT_EQ (std::string ((const char *) r.begin ().operator-> (), r.size ()), "a"); + + r = tl::from_base64 ("YWI="); + EXPECT_EQ (std::string ((const char *) r.begin ().operator-> (), r.size ()), "ab"); + + r = tl::from_base64 ("YWJj"); + EXPECT_EQ (std::string ((const char *) r.begin ().operator-> (), r.size ()), "abc"); + + r = tl::from_base64 ("YWJjZA=="); + EXPECT_EQ (std::string ((const char *) r.begin ().operator-> (), r.size ()), "abcd"); + + r = tl::from_base64 ("YWJjZA="); + EXPECT_EQ (std::string ((const char *) r.begin ().operator-> (), r.size ()), "abcd"); + + r = tl::from_base64 ("SGVsbG8sIHdvcmxkIQo="); + EXPECT_EQ (std::string ((const char *) r.begin ().operator-> (), r.size ()), "Hello, world!\n"); + + r = tl::from_base64 ("SGVsbG\n8sIHd \tvcmxkIQo="); + EXPECT_EQ (std::string ((const char *) r.begin ().operator-> (), r.size ()), "Hello, world!\n"); + + try { + r = tl::from_base64 ("YWJjZ=="); + EXPECT_EQ (true, false); + } catch (tl::Exception &ex) { + EXPECT_EQ (ex.msg (), "Error decoding base64 data: padding character does not match zero byte"); + } + + try { + r = tl::from_base64 ("YW#jZA=="); + EXPECT_EQ (true, false); + } catch (tl::Exception &ex) { + EXPECT_EQ (ex.msg (), "Error decoding base64 data: invalid character '#'"); + } +} + +TEST(2) +{ + std::string s, r; + + s = ""; + r = tl::to_base64 ((const unsigned char *) s.c_str (), s.size ()); + EXPECT_EQ (r, ""); + + s = "a"; + r = tl::to_base64 ((const unsigned char *) s.c_str (), s.size ()); + EXPECT_EQ (r, "YQ=="); + + s = "ab"; + r = tl::to_base64 ((const unsigned char *) s.c_str (), s.size ()); + EXPECT_EQ (r, "YWI="); + + s = "abc"; + r = tl::to_base64 ((const unsigned char *) s.c_str (), s.size ()); + EXPECT_EQ (r, "YWJj"); + + s = "abcd"; + r = tl::to_base64 ((const unsigned char *) s.c_str (), s.size ()); + EXPECT_EQ (r, "YWJjZA=="); + + s = "Hello, world!\n"; + r = tl::to_base64 ((const unsigned char *) s.c_str (), s.size ()); + EXPECT_EQ (r, "SGVsbG8sIHdvcmxkIQo="); +} diff --git a/src/tl/unit_tests/unit_tests.pro b/src/tl/unit_tests/unit_tests.pro index 05fe5f202..6da635bd3 100644 --- a/src/tl/unit_tests/unit_tests.pro +++ b/src/tl/unit_tests/unit_tests.pro @@ -8,6 +8,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ tlAlgorithmTests.cc \ + tlBase64Tests.cc \ tlClassRegistryTests.cc \ tlCommandLineParserTests.cc \ tlCopyOnWriteTests.cc \