/* Copyright (C) 2016 The giomm Development Team * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include #include #include #include // A simple custom stream that base64 encodes data. // Do not copy it to your code, because it's very slow. class Base64OutputStream : public Gio::FilterOutputStream { public: unsigned get_column_width() const { return column_width; } void set_column_width(unsigned cw) { column_width = cw; } static Glib::RefPtr create(const Glib::RefPtr& base_stream) { return Glib::make_refptr_for_instance(new Base64OutputStream(base_stream)); } protected: explicit Base64OutputStream(const Glib::RefPtr& base_stream) : Gio::FilterOutputStream(base_stream), column(0), bit_count(0), bit_buffer(0), column_width(72) {} gssize write_vfunc(const void* buffer, gsize count, const Glib::RefPtr& cancellable) override { char const *byte = (char const *) buffer; for (unsigned i = 0; i < count; ++i, ++byte) { // kindergarten implementation, because the object is not performance :) bit_buffer <<= 8; bit_buffer |= (*byte & 0xff); bit_count += 8; if (bit_count == 24) { clear_pending(); // TODO why is this necessary to avoid an outstanding op. exception? flush(cancellable); set_pending(); bit_count = 0; } if (cancellable && cancellable->is_cancelled()) throw Gio::Error(Gio::Error::CANCELLED, "Operation cancelled"); } return count; } bool flush_vfunc(const Glib::RefPtr& cancellable) override { if (bit_count != 24) return true; char to_write[5]; gsize len = 4; for (unsigned i=0; i<4; ++i) { unsigned index = (bit_buffer & (0x3f<<(i*6))) >> (i*6); to_write[3-i] = base64_encode_str[index]; } column += 4; // Yes, I know this is completely wrong. if (column >= column_width) { column = 0; to_write[4] = '\n'; ++len; } get_base_stream()->write(&to_write, len, cancellable); bit_count = 0; bit_buffer = 0; return true; } bool close_vfunc(const Glib::RefPtr& cancellable) override { char to_write[5] = "===="; //get any last bytes (1 or 2) out of the buffer switch (bit_count) { case 16: bit_buffer <<= 2; //pad to make 18 bits to_write[0] = base64_encode_str[(bit_buffer & (0x3f << 12)) >> 12]; to_write[1] = base64_encode_str[(bit_buffer & (0x3f << 6)) >> 6]; to_write[2] = base64_encode_str[bit_buffer & 0x3f]; break; case 8: bit_buffer <<= 4; //pad to make 12 bits to_write[0] = base64_encode_str[(bit_buffer & (0x3f << 6)) >> 6]; to_write[1] = base64_encode_str[bit_buffer & 0x3f]; break; } if (bit_count > 0) { get_base_stream()->write(&to_write, 5, cancellable); } else { // null terminate output get_base_stream()->write("", 1, cancellable); } if (get_close_base_stream()) get_base_stream()->close(cancellable); return true; } private: static char const *const base64_encode_str; unsigned column; unsigned bit_count; unsigned bit_buffer; unsigned column_width; }; char const *const Base64OutputStream::base64_encode_str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int main(int, char**) { Glib::init(); Gio::init(); try { char result[256]; Glib::RefPtr memory_chunk = Gio::MemoryOutputStream::create(result, 256, nullptr, nullptr); Glib::RefPtr base64 = Base64OutputStream::create(memory_chunk); std::string data = "Custom GIO streams are cool!"; base64->set_close_base_stream(true); base64->write(data); base64->close(); const std::string base64_should_be("Q3VzdG9tIEdJTyBzdHJlYW1zIGFyZSBjb29sIQ=="); std::cout << "Original data: " << data << std::endl; std::cout << "base64-encoded data: " << result << std::endl; std::cout << "base64 should be: " << base64_should_be << std::endl; if (base64_should_be != result) { std::cout << "Not correct!" << std::endl; return EXIT_FAILURE; } } catch (Gio::Error e) { std::cout << "Gio error: " << e.what() << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; }