/** * This is free and unencumbered software released into the public domain. * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * For more information, please refer to */ #include #include // This example shows how to wrap a type (here: std::string) in another // result type in order to modify the standard behavior for that type. // Use this when you're in a position to wrap the result variable. // // This is the more straightforward of two ways for modifying cppcodec's // behavior for a given type, the other one being able to modify the // default behavior but also being more complex/intricate. // // The overall approach is straightforward: Define a result type with // push_back(char) and size() methods, implement template specializations // for init() and finish() for the result type, and call encode()/decode() // with an object of this type as result parameter. class string_append_wrapper { public: string_append_wrapper(std::string& backing) : m_backing(backing) , m_offset(0) , m_orig_size(0) { } void init(size_t capacity) { m_orig_size = m_backing.size(); m_offset = m_orig_size; m_backing.resize(m_orig_size + capacity); } void finish() { m_backing.resize(m_offset); } // Methods required for satisfying default result type requirements: CPPCODEC_ALWAYS_INLINE void push_back(char c) { m_backing[m_offset++] = c; } CPPCODEC_ALWAYS_INLINE size_t size() const { return m_offset - m_orig_size; } // Note that the above implementation of push_back() is not the fastest, // because operator[] for std::string (for C++11 and above) still includes // a check for whether the size of the string fits into its allocation-less // character array union. // // With C++17 and above, it's legitimate to get the character array as a // mutable (non-const) char pointer, so this check can be skipped. // This is implemented via template specialization in cppcodec's // default behavior for std::string, but omitted here for simplicity. // If you need that last bit of extra performance, see // direct_data_access_result_state in cppcodec/data/access.hpp // for an example of optimal C++17 string access. private: std::string& m_backing; size_t m_offset; size_t m_orig_size; }; // init() and finish() must be declared in the cppcodec::data namespace. namespace cppcodec { namespace data { template <> inline void init( string_append_wrapper& result, empty_result_state&, size_t capacity) { // init() is called to prepare the output buffer. cppcodec will call it // with the maximum output size, null termination not included. // // Any thrown exception will not be caught by cppcodec itself, // the caller of the encode/decode function is responsible for handling it. // // empty_result_state can be ignored in this case because the wrapper type // can carry all required state internally. // // In order to maximize performance, init() should generally try to // allocate or guarantee the entire output buffer at once, so that // subsequent calls to push_back() don't result in extra checks (slower) // or even re-allocations. result.init(capacity); } // Between init() and finish(), cppcodec will call result.push_back(char) // repeatedly, once for each output character with no rewinding. // While init() can ask for greater capacity than the final output length, // cppcodec guarantees that push_back() will never be called too often. // // (If you know exactly how long your output is, you could theoretically // overcommit on capacity while allocating only the exact expected length // of the output buffer. This is of course dangerous, because you can // hardly ever know for sure and everyone's often wrong, so don't try it // unless you have a business-critical reason to reduce/avoid the allocation.) template <> inline void finish( string_append_wrapper& result, empty_result_state&) { // finish() is called after encoding/decoding is done. // Its main purpose is to reduce the size of the result type // from capacity to the actual (often slightly smaller) output length. // // After finish(), cppcodec will assert that result.size() does indeed // equal the number of times that push_back() has been called. result.finish(); } } // namespace data } // namespace cppcodec int main() { using base64 = cppcodec::base64_rfc4648; std::string result = "Result: "; string_append_wrapper appender(result); base64::encode(appender, std::string("any carnal pleasure")); std::cout << result << std::endl; // "Result: YW55IGNhcm5hbCBwbGVhc3VyZQ==" return 0; }