diff --git a/external/glim/ChangeLog.darcs.txt b/external/glim/ChangeLog.darcs.txt new file mode 100644 index 00000000..61340d55 --- /dev/null +++ b/external/glim/ChangeLog.darcs.txt @@ -0,0 +1,62 @@ +Wed Dec 6 23:50:10 Russian Standard Time 2006 ArtemGr + * Fixes discovered under FreeBSD. + +Wed Dec 6 04:43:04 Russian Standard Time 2006 ArtemGr + * Ported to Gentoo gcc 4.1.1 + +Fri Nov 24 14:17:08 Russian Standard Time 2006 ArtemGr + * Documentation improvements for the release. + +Fri Nov 24 13:13:50 Russian Standard Time 2006 ArtemGr + * A few fixes. + +Fri Nov 24 13:13:20 Russian Standard Time 2006 ArtemGr + * Do not remove the "html" directory when cleaning. + +Wed Nov 22 15:30:56 Russian Standard Time 2006 ArtemGr + * Added a compatibility #pair method to bufstr_t. + +Tue Nov 21 21:54:40 Russian Standard Time 2006 ArtemGr + * Implemented the sugared way to querying. + +Mon Oct 9 23:19:11 Russian Daylight Time 2006 ArtemGr + * Fixed incorrect statement = NULL in #step. Implemented parameter bindings. + +Mon Oct 9 20:09:17 Russian Daylight Time 2006 ArtemGr + * SqliteQuery and SqliteParQuery classes (no binding support as of yet). + +Fri Oct 6 12:11:43 Russian Daylight Time 2006 ArtemGr + * typo + +Thu Oct 5 23:26:21 Russian Daylight Time 2006 ArtemGr + * Basic mutex operations in SqliteSession. + +Sun Oct 1 23:19:36 Russian Daylight Time 2006 ArtemGr + * Compatibility with std::string. + +Fri Sep 29 11:42:09 Russian Daylight Time 2006 ArtemGr + * Invented SqliteSession. + +Fri Sep 29 01:23:31 Russian Daylight Time 2006 ArtemGr + * SQLite wrapper: initial documentation; opening, closing. + +Thu Sep 28 23:15:37 Russian Daylight Time 2006 ArtemGr + * Apache version 2 license. + +Thu Sep 28 23:12:41 Russian Daylight Time 2006 ArtemGr + * Multiple source files for tests. + +Thu Sep 28 01:05:21 Russian Daylight Time 2006 ArtemGr + * Append from another bufstr_t and from a pair. + +Thu Sep 28 01:04:46 Russian Daylight Time 2006 ArtemGr + * Macro to construct pair from C character array. + +Tue Sep 26 23:23:40 Russian Daylight Time 2006 ArtemGr + * char const* instead of const char* + +Tue Sep 26 20:56:07 Russian Daylight Time 2006 ArtemGr + * Everything seems to work. The library is now headers-only. + +Mon Sep 25 22:52:34 Russian Daylight Time 2006 ArtemGr + * Initial revision, containing bufstr_t; compiles, but does not work. diff --git a/external/glim/LICENSE b/external/glim/LICENSE new file mode 100755 index 00000000..255eeeb1 --- /dev/null +++ b/external/glim/LICENSE @@ -0,0 +1,13 @@ +Copyright 2006-2012 Kozarezov Artem Aleksandrovich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/external/glim/NsecTimer.hpp b/external/glim/NsecTimer.hpp new file mode 100644 index 00000000..e6612641 --- /dev/null +++ b/external/glim/NsecTimer.hpp @@ -0,0 +1,51 @@ +#ifndef _NSEC_TIMER_H +#define _NSEC_TIMER_H + +#include // clock_gettime, CLOCK_MONOTONIC +#include +#include +#include + +namespace glim { + +//! Safe nanoseconds timer. +struct NsecTimer { + timespec start; + NsecTimer () {restart();} + //! Nanoseconds since the creation or restart of the timer. + int64_t operator()() const { + timespec nsecStop; clock_gettime (CLOCK_MONOTONIC, &nsecStop); + return (int64_t) (nsecStop.tv_sec - start.tv_sec) * 1000000000LL + (int64_t) (nsecStop.tv_nsec - start.tv_nsec); + } + /** Seconds since the creation or restart of the timer. */ + double sec() const { + timespec nsecStop; clock_gettime (CLOCK_MONOTONIC, &nsecStop); + double seconds = nsecStop.tv_sec - start.tv_sec; + seconds += (double)(nsecStop.tv_nsec - start.tv_nsec) / 1000000000.0; + return seconds; + } + //! Seconds since the creation or restart of the timer. + std::string seconds (int precision = 9) const { + // The trick is to avoid the scientific notation by printing integers. + double sec = this->sec(); + std::ostringstream buf; + int isec = (int) sec; + buf << isec; + + sec -= isec; + for (int pc = precision; pc; --pc) sec *= 10.0; + int ifrac = (int) sec; + if (ifrac > 0) { + buf << '.'; + buf.fill ('0'); buf.width (precision); + buf << ifrac; + } + return buf.str(); + } + void restart() {clock_gettime (CLOCK_MONOTONIC, &start);} + int64_t getAndRestart() {int64_t tmp = operator()(); restart(); return tmp;} +}; + +} + +#endif // _NSEC_TIMER_H diff --git a/external/glim/SerializablePool.hpp b/external/glim/SerializablePool.hpp new file mode 100644 index 00000000..016e0664 --- /dev/null +++ b/external/glim/SerializablePool.hpp @@ -0,0 +1,236 @@ +#ifndef _GLIM_SERIALIZABLEPOOL_HPP_INCLUDED +#define _GLIM_SERIALIZABLEPOOL_HPP_INCLUDED + +#include "gstring.hpp" +#ifndef _SERIALIZABLEPOOL_NOLDB +# include "ldb.hpp" // Reuse `ldbSerialize` and `ldbDeserialize` in the `with` method. +#endif +namespace glim { + +namespace SerializablePoolHelpers { + struct Impl { + /** + * Pool format: \code + * uint32_t valuesStart; // Network byte order. + * uint32_t value0Offset, value1Offset, ... valueNOffset; // Network byte order. + * char value0[]; char zero; char value1[]; char zero; ... char valueN[]; char zero; + * \endcode + */ + gstring _pool; + std::vector _changes; + std::vector _changed; + bool _readOnly = false; + Impl() = default; + Impl (const gstring& poolBytes): _pool (poolBytes), _readOnly (false) {} + }; + + /// Can be used to avoid a deep copy of the pool and change vectors (pImpl idiom). + /// Example: \code glim::SerializablePool pool; \endcode + struct SharedPtr { + std::shared_ptr _impl; + SharedPtr() = default; + SharedPtr (const gstring& poolBytes): _impl (std::make_shared (poolBytes)) {} + Impl* get() const {return _impl.get();} + Impl* operator->() const {return _impl.get();} + Impl& operator*() const {return *_impl;} + Impl* instance() {if (!_impl) _impl = std::make_shared(); return _impl.get();} + }; + + /// Can be used instead of SharedPtr to avoid the shared_ptr indirection when the SerializablePoolTpl isn't going to be copied. + /// Example: \code glim::InlineSerializablePool temporaryPool (bytes); \endcode + struct InlinePtr { + Impl _impl; + InlinePtr() = default; + InlinePtr (const gstring& poolBytes): _impl (poolBytes) {} + Impl* get() const {return const_cast (&_impl);} + Impl* operator->() const {return const_cast (&_impl);} + Impl& operator*() const {return const_cast (_impl);} + Impl* instance() {return &_impl;} + }; +} + +/** Serialization with lazy parsing: fields are accessed without "unpacking" the byte array. + * Changes are stored separately, allowing the user to know exactly what fields has been changed and compare the old values to the new ones. */ +template +class SerializablePoolTpl { +protected: + using Impl = SerializablePoolHelpers::Impl; + PI _impl; + /** @param ref Return a zero-copy view. The view should not be used outside of the pool buffer's lifetime. */ + static gstring original (const gstring& pool, uint32_t num, bool ref = false) { + uint32_t poolLength = pool.length(); if (poolLength < 4) return gstring(); + uint32_t valuesStart = ntohl (*(uint32_t*) pool.data()); + assert (valuesStart <= poolLength); + uint32_t valueOffsetOffset = 4 + num * 4; + if ((int) valuesStart - (int) valueOffsetOffset < 4) return gstring(); // num > size + uint32_t valueOffset = ntohl (*(uint32_t*) (pool.data() + valueOffsetOffset)); + valueOffsetOffset += 4; + uint32_t nextValueOffset = ((int) valuesStart - (int) valueOffsetOffset < 4) + ? poolLength + : ntohl (*(uint32_t*) (pool.data() + valueOffsetOffset)); + return gstring (0, (void*) (pool.data() + valueOffset), false, nextValueOffset - 1 - valueOffset, ref); + } + /** How many elements are in the pool. */ + static uint32_t poolSize (const gstring& pool) { + if (pool.length() < 4) return 0; + uint32_t valuesStart = ntohl (*(uint32_t*) pool.data()); + return (valuesStart - 4) / 4; + } + void toBytes (gstring& newPool, uint32_t size, const gstring* oldPool) const { + newPool.clear(); + const Impl* impl = _impl.get(); + const std::vector& changed = impl->_changed; + const std::vector& changes = impl->_changes; + if (changed.empty()) return; + uint32_t valuesStart = 4 + size * 4; + uint32_t networkOrder = 0; + newPool.append ((char*) &(networkOrder = htonl (valuesStart)), 4); + for (uint32_t num = 0; num < size; ++num) newPool.append ((char*) &(networkOrder = 0), 4); + for (uint32_t num = 0; num < size; ++num) { + uint32_t start = newPool.length(); + if (num < changed.size() && changed[num]) newPool << changes[num]; + else newPool << original (oldPool ? *oldPool : impl->_pool, num); + newPool << '\0'; + uint32_t valuesOffsetOffset = 4 + num * 4; assert (valuesOffsetOffset < valuesStart); + *(uint32_t*)(newPool.data() + valuesOffsetOffset) = htonl (start); + } + } +public: + /** Field, old value, new value. Might be used to maintain indexes. */ + typedef std::function ChangeVisitor; + + SerializablePoolTpl() = default; + /** Copy the given pool bytes from the outside source (e.g. from the database). */ + SerializablePoolTpl (const gstring& poolBytes): _impl (poolBytes) {} + /** Returns a view into the original serialized field (ignores the current changes).\n + * Returns an empty string if the field is not in the pool (num > size). + * @param ref Return a zero-copy view. The view becomes invalid after the value has been changed or when the pool's `Impl` is destroyed. */ + const gstring original (uint32_t num, bool ref = false) const {return original (_impl->_pool, num, ref);} + /** Returns the original serialized field (ignores the current changes).\n + * Returns an empty string if the field is not in the pool (num > size). */ + const char* cstringOriginal (uint32_t num) const { + gstring gs (original (_impl->_pool, num)); + return gs.empty() ? "" : gs.data(); // All fields in the _pool are 0-terminated. + } + /** Returns the field. + * @param ref Return a zero-copy view. The view becomes invalid after the value has been changed or when the pool's `Impl` is destroyed. */ + const gstring current (uint32_t num, bool ref = false) const { + const Impl* impl = _impl.get(); if (!impl) return gstring(); + if (num < impl->_changed.size() && impl->_changed[num]) { + const gstring& value = impl->_changes[num]; return ref ? value.ref() : value;} + return original (impl->_pool, num); + } + /** Set the new value of the field. */ + void set (uint32_t num, const gstring& value) { + Impl* impl = _impl.instance(); + if (__builtin_expect (impl->_readOnly, 0)) throw std::runtime_error ("Attempt to modify a read-only SerializablePool"); + if (num >= impl->_changed.size()) {impl->_changed.resize (num + 1); impl->_changes.resize (num + 1);} + impl->_changed[num] = true; + impl->_changes[num] = value; + } + void reserve (uint32_t fields) { + Impl* impl = _impl.instance(); + if (__builtin_expect (impl->_readOnly, 0)) throw std::runtime_error ("Attempt to modify a read-only SerializablePool"); + impl->_changed.reserve (fields); + impl->_changes.reserve (fields); + } + /** Peek into the pool.\n + * Returned reference should not be used after the SerializablePool goes out of scope (and destroyed). */ + const gstring& originalPool() {return _impl->_pool;} + /** Serialize the pool. + * @param changeVisitor is called for every field that was really changed (e.g. the bytes differ). */ + void toBytes (gstring& newPool, ChangeVisitor changeVisitor = ChangeVisitor()) const { + if (changeVisitor) { + const Impl* impl = _impl.get(); + const std::vector& changed = impl->_changed; + const std::vector& changes = impl->_changes; + for (uint32_t num = 0, size = changed.size(); num < size; ++num) if (changed[num]) { + const gstring& from = original (impl->_pool, num); const gstring& to = changes[num]; + if (from != to) changeVisitor (num, from, to); + } + } + toBytes (newPool, (uint32_t) _impl->_changed.size(), nullptr); + } + /** + * Performs "delta" serialization of the pool: creates a new pool where values which has not changed are copied from the `oldPool`.\n + * \code Use case: 1) pools X and Y are loaded from a database by users A and B; + * 2) user A changes field 0 in pool X; 3) user B changes field 1 in pool Y; + * 4) user A loads `oldPool` from the database, does `toBytesDelta` from pool X and saves to the database; + * 5) user B loads `oldPool` from the database, does `toBytesDelta` from pool Y and saves to the database; + * result: database contains both changes (field 0 from user A and field 1 from user B). \endcode + * @param changeVisitor is called for every field that was changed between the oldPool and the current one. + * Returns `false` and leaves `newPool` *empty* if there are no changes found against the `oldPool`. + */ + bool toBytesDelta (gstring& newPool, const gstring& oldPool, ChangeVisitor changeVisitor = ChangeVisitor()) const { + newPool.clear(); + const Impl* impl = _impl.get(); + const std::vector& changed = impl->_changed; + const std::vector& changes = impl->_changes; + bool verifiedChanges = false; + for (uint32_t num = 0, size = changed.size(); num < size; ++num) if (changed[num]) { + const gstring& from = original (oldPool, num); const gstring& to = changes[num]; + if (from != to) { + verifiedChanges = true; + if (changeVisitor) changeVisitor (num, from, to); else break; + } + } + if (!verifiedChanges) return false; + + toBytes (newPool, std::max ((uint32_t) changed.size(), poolSize (oldPool)), &oldPool); + return true; + } + /** True if the field has been `set` in this pool instance.\n + * NB: Does *not* check if the `set` value is equal to the `original` value or not. */ + bool changed (uint32_t num) const {const auto& changed = _impl->_changed; return num < changed.size() ? changed[num] : false;} + /** True if a field has been `set` in this pool instance. */ + bool changed() const {return !_impl->_changed.empty();} + + bool operator == (const SerializablePoolTpl& rhs) const {return _impl.get() == rhs._impl.get();} + /** Useful for storing SerializablePool in a map. */ + intptr_t implId() const {return (intptr_t) _impl.get();} + + /** If set to `true` then modifying the pool will throw an exception.\n + * Useful for freezing the pool before sharing it with other threads. */ + void readOnly (bool ro) {if (_impl) _impl->_readOnly = true;} + bool readOnly() const {return (_impl ? _impl->_readOnly : false);} + + /** Number of elements in the pool. Equals to max(num)-1. */ + uint32_t size() const { + Impl* impl = _impl.get(); if (__builtin_expect (!impl, 0)) return 0; + return std::max (poolSize (impl->_pool), (uint32_t) impl->_changed.size()); + } + +#ifndef _SERIALIZABLEPOOL_NOLDB + /** Serialize the `value` with `ldbSerialize` and `set` it to `num`. + * @param stackSize is the amount of space to preallocate on stack for the temporary buffer. */ + template void serialize (uint32_t num, const T& value, uint32_t stackSize = 256) { + GSTRING_ON_STACK (bytes, stackSize); + ldbSerialize (bytes, value); + set (num, bytes); + } + /** If the field is not empty then `ldbDeserialize` it into `value`. */ + template void deserialize (uint32_t num, T& value) const { + const gstring& bytes = current (num); + if (bytes.length()) ldbDeserialize (current (num), value); + } + /** Deserialize the `num` field with `ldbDeserialize`, run `visitor` on it, then optionally serialize the field back using `ldbSerialize`. + * Example: \code + * typedef std::map MyMap; + * pool.with (_myMap, [](MyMap& myMap) {myMap["foo"] = "bar"; return true;}); + * \endcode + * @param visitor must return `true` to serialize the field back to the pool. + */ + template void with (uint32_t num, std::function visitor) { + const gstring& fromBytes = current (num, true); + T value; if (fromBytes.length()) ldbDeserialize (fromBytes, value); + if (visitor (value)) serialize (num, value, 16 + fromBytes.length() * 2); + } +#endif +}; + +using SerializablePool = SerializablePoolTpl; +using InlineSerializablePool = SerializablePoolTpl; + +} + +#endif // _GLIM_SERIALIZABLEPOOL_HPP_INCLUDED diff --git a/external/glim/TscTimer.hpp b/external/glim/TscTimer.hpp new file mode 100644 index 00000000..ce22c7cb --- /dev/null +++ b/external/glim/TscTimer.hpp @@ -0,0 +1,32 @@ +#ifndef _TSC_TIMER_H +#define _TSC_TIMER_H + +namespace glim { + +extern "C" { // http://en.wikipedia.org/wiki/Rdtsc +#if (defined(__GNUC__) || defined(__ICC)) && defined(__i386__) + static __inline__ unsigned long long rdTsc(void) { + unsigned long long ret; + __asm__ __volatile__("rdtsc": "=A" (ret)); + return ret; + } +#elif (defined(__GNUC__) || defined(__ICC) || defined(__SUNPRO_C)) && defined(__x86_64__) + static __inline__ unsigned long long rdTsc(void) { + unsigned a, d; + asm volatile("rdtsc" : "=a" (a), "=d" (d)); + return ((unsigned long long)a) | (((unsigned long long)d) << 32); + } +#endif +} + +//! CPU cycles timer. Fast, not safe. +//! cf. http://en.wikipedia.org/wiki/Rdtsc +struct TscTimer { + int64_t start; + TscTimer (): start (rdTsc()) {} + int64_t operator()() const {return rdTsc() - start;} +}; + +} + +#endif // _TSC_TIMER_H diff --git a/external/glim/cbcoro.hpp b/external/glim/cbcoro.hpp new file mode 100644 index 00000000..f788a5bf --- /dev/null +++ b/external/glim/cbcoro.hpp @@ -0,0 +1,203 @@ +/** \file + * ucontext-based coroutine library designed to emulate a normal control flow around callbacks. */ + +// http://en.wikipedia.org/wiki/Setcontext; man 3 makecontext; man 2 getcontext +// http://www.boost.org/doc/libs/1_53_0/libs/context/doc/html/index.html +// g++ -std=c++11 -O1 -Wall -g test_cbcoro.cc -pthread && ./a.out + +// NB: There is now a coroutine support in Boost ASIO which can be used to make asynchronous APIs look synchronous in a similar way: +// https://svn.boost.org/trac/boost/changeset/84311 + +#include +#include // mmap +#include // strerror +#include +#include +#include +#include +#include +#include + +namespace glim { + +/// Simplifies turning callback control flows into normal imperative control flows. +class CBCoro { + public: + /// "Holds" the CBCoro and will delete it when it is no longer used. + struct CBCoroPtr { + CBCoro* _coro; + CBCoroPtr (CBCoro* coro): _coro (coro) { + _coro->_users++; + } + ~CBCoroPtr() { + if (--_coro->_users <= 0 && _coro->_delete) delete _coro; + } + CBCoro* operator ->() const {return _coro;} + }; + + static constexpr size_t defaultStackSize() {return 512 * 1024;} + static constexpr uint8_t defaultCacheSize() {return 2;} + protected: + typedef boost::container::flat_map > cache_t; + /// The cached stacks; stackSize -> free list. + static cache_t& cache() {static cache_t CACHE; return CACHE;} + static std::mutex& cacheMutex() {static std::mutex CACHE_MUTEX; return CACHE_MUTEX;} + + ucontext_t _context; + ucontext_t* _returnTo; + std::recursive_mutex _mutex; ///< This one is locked most of the time. + std::atomic_int_fast32_t _users; ///< Counter used by `CBCoroPtr`. + bool _delete; ///< Whether the `CBCoroPtr` should `delete` this instance when it is no longer used (default is `true`). + bool _invokeFromYield; ///< True if `invokeFromCallback()` was called directly from `yieldForCallback()`. + bool _yieldFromInvoke; ///< True if `yieldForCallback()` now runs from `invokeFromCallback()`. + uint8_t const _cacheStack; ///< Tells `freeStack()` to cache the stack if the number of cached `#_stackSize` stacks is less than it. + void* _stack; + size_t const _stackSize; ///< Keeps the size of the stack. + + /// Peek a stack from the cache or allocate one with `mmap` (and register with Valgrind). + virtual void allocateStack() { + if (_cacheStack) { + std::lock_guard lock (cacheMutex()); + auto& freeList = cache()[_stackSize]; + if (!freeList.empty()) {_stack = freeList.front(); freeList.pop_front(); return;} + } + _stack = mmap (nullptr, _stackSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK | MAP_NORESERVE, -1, 0); + if (_stack == MAP_FAILED) GTHROW (std::string ("mmap allocation failed: ") + ::strerror (errno)); + #pragma GCC diagnostic ignored "-Wunused-value" + VALGRIND_STACK_REGISTER (_stack, (char*) _stack + _stackSize); + } + /// Release a stack into the cache or free it with `munmap` (and deregister with Valgrind). + virtual void freeStack() { + if (_cacheStack) { + std::lock_guard lock (cacheMutex()); + auto& freeList = cache()[_stackSize]; + if (freeList.size() < _cacheStack) {freeList.push_front (_stack); _stack = nullptr; return;} + } + VALGRIND_STACK_DEREGISTER (_stack); + if (munmap (_stack, _stackSize)) GTHROW (std::string ("!munmap: ") + ::strerror (errno));; + _stack = nullptr; + } + + /// Prepare the coroutine (initialize context, allocate stack and register it with Valgrind). + CBCoro (uint8_t cacheStack = defaultCacheSize(), size_t stackSize = defaultStackSize()): + _returnTo (nullptr), _users (0), _delete (true), _invokeFromYield (false), _yieldFromInvoke (false), + _cacheStack (cacheStack), _stack (nullptr), _stackSize (stackSize) { + if (getcontext (&_context)) GTHROW ("!getcontext"); + allocateStack(); + _context.uc_stack.ss_sp = _stack; + _context.uc_stack.ss_size = stackSize; + } + virtual ~CBCoro() { + freeStack(); + } + public: + /// Starts the coroutine on the `_stack` (makecontext, swapcontext), calling the `CBCoro::run`. + CBCoroPtr start() { + CBCoroPtr ptr (this); + ucontext_t back; _context.uc_link = &back; + makecontext (&_context, (void(*)()) cbcRun, 1, (intptr_t) this); + // Since we have to "return" from inside the `yieldForCallback`, + // we're not actually using the `_context.uc_link` and `return`, we use `setcontext (_returnTo)` instead. + _returnTo = &back; + _mutex.lock(); + swapcontext (&back, &_context); // Now our stack lives and the caller stack is no longer in control. + _mutex.unlock(); + return ptr; + } + protected: + /// Logs exception thrown from `CBCoro::run`. + virtual void log (const std::exception& ex) { + std::cerr << "glim::CBCoro, exception: " << ex.what() << std::endl; + } + static void cbcRun (CBCoro* cbCoro) { + try { + cbCoro->run(); + } catch (const std::exception& ex) { + cbCoro->log (ex); + } + cbCoro->cbcReturn(); // Return the control to the rightful owner, e.g. to a last callback who ran `invokeFromCallback`, or otherwise to `cbcStart`. + } + /// Relinquish the control to the original owner of the thread, restoring its stack. + void cbcReturn() { + ucontext_t* returnTo = _returnTo; + if (returnTo != nullptr) {_returnTo = nullptr; setcontext (returnTo);} + } + /// This method is performed on the CBCoro stack, allowing it to be suspended and then reanimated from callbacks. + virtual void run() = 0; + public: + /** Use this method to wrap a return-via-callback code. + * For example, the callback code \code + * startSomeWork ([=]() { + * continueWhenWorkIsFinished(); + * }); + * \endcode should be turned into \code + * yieldForCallback ([&]() { + * startSomeWork ([&]() { + * invokeFromCallback(); + * }); + * }); + * continueWhenWorkIsFinished(); + * \endcode + * + * Captures the stack, runs the `fun` and relinquish the control to `_returnTo`.\n + * This method will never "return" by itself, in order for it to "return" the + * `fun` MUST call `invokeFromCallback`, maybe later and from a different stack. */ + template CBCoroPtr yieldForCallback (F fun) { + CBCoroPtr ptr (this); + _yieldFromInvoke = false; + if (getcontext (&_context)) GTHROW ("!getcontext"); // Capture. + if (_yieldFromInvoke) { + // We're now in the future, revived by the `invokeFromCallback`. + // All we do now is "return" to the caller whose stack we captured earlier. + } else { + // We're still in the present, still have some work to do. + fun(); // The `fun` is supposed to do something resulting in the `invokeFromCallback` being called later. + if (_invokeFromYield) { + // The `fun` used the `invokeFromCallback` directly, not resorting to callbacks, meaning we don't have to do our magick. + _invokeFromYield = false; + } else { + // So, the `fun` took measures to revive us later, it's time for us to go into torpor and return the control to whoever we've borrowed it from. + cbcReturn(); + } + } + return ptr; + } + + /// To be called from a callback in order to lend the control to CBCoro, continuing it from where it called `yieldForCallback`. + CBCoroPtr invokeFromCallback() { + CBCoroPtr ptr (this); + _mutex.lock(); // Wait for an other-thready `yieldForCallback` to finish. + if (_returnTo != nullptr) { + // We have not yet "returned" from the `yieldForCallback`, + // meaning that the `invokeFromCallback` was executed immediately from inside the `yieldForCallback`. + // In that case we must DO NOTHING, we must simply continue running on the current stack. + _invokeFromYield = true; // Tells `yieldForCallback` to do nothing. + } else { + // Revive the CBCoro, letting it continue from where it was suspended in `yieldForCallback`. + ucontext_t cbContext; _returnTo = &cbContext; _yieldFromInvoke = true; + if (swapcontext (&cbContext, &_context)) GTHROW ("!swapcontext"); + // NB: When the CBCoro is suspended or exits, the control returns back there and then back to the callback from which we borrowed it. + if (_returnTo == &cbContext) _returnTo = nullptr; + } + _mutex.unlock(); // Other-thready `yieldForCallback` has finished and `cbcReturn`ed here. + return ptr; + } +}; + +/** CBCoro running a given functor. + * The functor's first argument must be a CBCoro pointer, like this: \code (new CBCoroForFunctor ([](CBCoro* cbcoro) {}))->start(); \endcode */ +template struct CBCoroForFunctor: public CBCoro { + FUN _fun; + template CBCoroForFunctor (CFUN&& fun, uint8_t cacheStack, size_t stackSize): CBCoro (cacheStack, stackSize), _fun (std::forward (fun)) {} + virtual void run() {_fun (this);} + virtual ~CBCoroForFunctor() {} +}; + +/** Syntactic sugar: Runs a given functor in a CBCoro instance. + * Example: \code glim::cbCoro ([](glim::CBCoro* cbcoro) {}); \endcode + * Returns a `CBCoroPtr` to the CBCoro instance holding the `fun` which might be held somewhere in order to delay the deletion of `fun`. */ +template inline CBCoro::CBCoroPtr cbCoro (FUN&& fun, uint8_t cacheStack = CBCoro::defaultCacheSize(), size_t stackSize = CBCoro::defaultStackSize()) { + return (new CBCoroForFunctor (std::forward (fun), cacheStack, stackSize))->start(); +} + +} diff --git a/external/glim/channel.hpp b/external/glim/channel.hpp new file mode 100644 index 00000000..e5ca23bd --- /dev/null +++ b/external/glim/channel.hpp @@ -0,0 +1,40 @@ +#ifndef _GLIM_CHANNEL_INCLUDED +#define _GLIM_CHANNEL_INCLUDED + +#include +#include +#include + +namespace glim { + +/// Unbuffered channel. +/// Optimized for a single value (busy-waits on a second one). +template +struct Channel { + V _v; + std::mutex _mutex; // Locked when there is no value. + std::atomic_int_fast8_t _state; enum State {EMPTY = 0, WRITING = 1, FULL = 2}; + Channel(): _state (EMPTY) {_mutex.lock();} + // Waits until the Channel is empty then stores the value. + template void send (VA&& v) { + for (;;) { + int_fast8_t expectEmpty = EMPTY; if (_state.compare_exchange_weak (expectEmpty, WRITING)) break; + std::this_thread::sleep_for (std::chrono::milliseconds (20)); + } + try {_v = std::forward (v);} catch (...) {_state = EMPTY; throw;} + _state = FULL; + _mutex.unlock(); // Allows the reader to proceed. + } + // Waits untill there is a value to receive. + V receive() { + _mutex.lock(); // Wait. + V tmp = std::move (_v); + assert (_state == FULL); + _state = EMPTY; + return tmp; + } +}; + +} // namespace glim + +#endif diff --git a/external/glim/curl.hpp b/external/glim/curl.hpp new file mode 100644 index 00000000..ff2ba9ac --- /dev/null +++ b/external/glim/curl.hpp @@ -0,0 +1,304 @@ +/** \file + * Very simple header-only wrapper around libcurl.\n + * See also: https://github.com/venam/Browser\n + * See also: https://github.com/mologie/curl-asio\n + * See also: http://thread.gmane.org/gmane.comp.web.curl.library/1322 (this one uses a temporary file). */ + +#ifndef _GLIM_CURL_INCLUDED +#define _GLIM_CURL_INCLUDED + +#include "gstring.hpp" +#include "exception.hpp" +#include +#include +#include +#include +#include + +namespace glim { + +inline size_t curlWriteToString (void *buffer, size_t size, size_t nmemb, void *userp) { + ((std::string*) userp)->append ((const char*) buffer, size * nmemb); + return size * nmemb;}; + +inline size_t curlReadFromString (void *ptr, size_t size, size_t nmemb, void *userdata); +inline size_t curlReadFromGString (void *ptr, size_t size, size_t nmemb, void *userdata); +inline size_t curlWriteHeader (void *ptr, size_t size, size_t nmemb, void *curlPtr); +inline int curlDebugCB (CURL* curl, curl_infotype type, char* bytes, size_t size, void* curlPtr); + +/** + Simple HTTP requests using cURL. + Example: \code + std::string w3 = glim::Curl() .http ("http://www.w3.org/") .go().str(); + \endcode + */ +class Curl { + protected: + Curl (const Curl&): _curl (NULL), _headers (NULL), _sent (0), _needs_cleanup (true) {} // No copying. + public: + struct PerformError: public glim::Exception { + PerformError (const char* message, const char* file, int32_t line): + glim::Exception (message, file, line) {} + }; + struct GetinfoError: public glim::Exception { + CURLcode _code; std::string _error; + GetinfoError (CURLcode code, const std::string& error, const char* file, int32_t line): + glim::Exception (error, file, line), + _code (code), _error (error) {} + }; + public: + CURL* _curl; + struct curl_slist *_headers; + std::function _headerListener; + std::function _debugListener; + std::string _sendStr; ///< We're using `std::string` instead of `gstring` in order to support payloads larger than 16 MiB. + glim::gstring _sendGStr; ///< `gstring::view` and `gstring::ref` allow us to zero-copy. + uint32_t _sent; + std::string _got; + bool _needs_cleanup:1; ///< ~Curl will do `curl_easy_cleanup` if `true`. + char _errorBuf[CURL_ERROR_SIZE]; + + Curl (Curl&&) = default; + + /// @param cleanup can be turned off if the cURL is freed elsewhere. + Curl (bool cleanup = true): _curl (curl_easy_init()), _headers (NULL), _sent (0), _needs_cleanup (cleanup) { + curl_easy_setopt (_curl, CURLOPT_NOSIGNAL, 1L); // required per http://curl.haxx.se/libcurl/c/libcurl-tutorial.html#Multi-threading + *_errorBuf = 0;} + /// Wraps an existing handle (will invoke `curl_easy_cleanup` nevertheless). + /// @param cleanup can be turned off if the cURL is freed elsewhere. + Curl (CURL* curl, bool cleanup = true): _curl (curl), _headers (NULL), _sent (0), _needs_cleanup (cleanup) { + curl_easy_setopt (_curl, CURLOPT_NOSIGNAL, 1L); // required per http://curl.haxx.se/libcurl/c/libcurl-tutorial.html#Multi-threading + *_errorBuf = 0;} + ~Curl(){ + if (_headers) {curl_slist_free_all (_headers); _headers = NULL;} + if (_curl) {if (_needs_cleanup) curl_easy_cleanup (_curl); _curl = NULL;} + } + + /** Stores the content to be sent into an `std::string` inside `Curl`. + * NB: In order to have an effect this method should be used *before* the `http()` and `smtp()` methods. */ + template Curl& send (STR&& text) { + _sendStr = std::forward (text); + _sendGStr.clear(); + _sent = 0; + return *this;} + + /// Adds "Content-Type" header into `_headers`. + Curl& contentType (const char* ct) { + char ctb[64]; gstring cth (sizeof (ctb), ctb, false, 0); + cth << "Content-Type: " << ct << "\r\n"; + _headers = curl_slist_append (_headers, cth.c_str()); + return *this; + } + + /// @param fullHeader is a full HTTP header and a newline, e.g. "User-Agent: Me\r\n". + Curl& header (const char* fullHeader) { + _headers = curl_slist_append (_headers, fullHeader); + return *this; + } + + /** + Sets the majority of options for the http request. + NB: If `send` was used with a non-empty string then `http` will use `CURLOPT_UPLOAD`, setting http method to `PUT` (use the `method()` to override). + \n + Example: \code + glim::Curl curl; + curl.http (url.c_str()) .go(); + std::cout << curl.status() << std::endl << curl.str() << std::endl; + \endcode + */ + Curl& http (const char* url, int timeoutSec = 20) { + curl_easy_setopt (_curl, CURLOPT_NOSIGNAL, 1L); // required per http://curl.haxx.se/libcurl/c/libcurl-tutorial.html#Multi-threading + curl_easy_setopt (_curl, CURLOPT_URL, url); + curl_easy_setopt (_curl, CURLOPT_WRITEFUNCTION, curlWriteToString); + curl_easy_setopt (_curl, CURLOPT_WRITEDATA, &_got); + curl_easy_setopt (_curl, CURLOPT_TIMEOUT, timeoutSec); + curl_easy_setopt (_curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP); + curl_easy_setopt (_curl, CURLOPT_ERRORBUFFER, _errorBuf); + if (_sendStr.size() || _sendGStr.size()) { + curl_easy_setopt (_curl, CURLOPT_UPLOAD, 1L); // http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTUPLOAD + if (_sendStr.size()) { + curl_easy_setopt (_curl, CURLOPT_INFILESIZE, (long) _sendStr.size()); + curl_easy_setopt (_curl, CURLOPT_READFUNCTION, curlReadFromString); + } else { + curl_easy_setopt (_curl, CURLOPT_INFILESIZE, (long) _sendGStr.size()); + curl_easy_setopt (_curl, CURLOPT_READFUNCTION, curlReadFromGString);} + curl_easy_setopt (_curl, CURLOPT_READDATA, this);} + if (_headers) + curl_easy_setopt (_curl, CURLOPT_HTTPHEADER, _headers); // http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTHTTPHEADER + return *this; + } + + /** + Set options for smtp request. + Example: \code + long rc = glim::Curl().send ("Subject: subject\r\n\r\n" "text\r\n") .smtp ("from", "to") .go().status(); + if (rc != 250) std::cerr << "Error sending email: " << rc << std::endl; + \endcode */ + Curl& smtp (const char* from, const char* to) { + curl_easy_setopt (_curl, CURLOPT_NOSIGNAL, 1L); // required per http://curl.haxx.se/libcurl/c/libcurl-tutorial.html#Multi-threading + curl_easy_setopt (_curl, CURLOPT_URL, "smtp://127.0.0.1"); + if (from) curl_easy_setopt (_curl, CURLOPT_MAIL_FROM, from); + bcc (to); + if (_headers) curl_easy_setopt (_curl, CURLOPT_MAIL_RCPT, _headers); + curl_easy_setopt (_curl, CURLOPT_WRITEFUNCTION, curlWriteToString); + curl_easy_setopt (_curl, CURLOPT_WRITEDATA, &_got); + if (_sendStr.size()) { + curl_easy_setopt (_curl, CURLOPT_INFILESIZE, (long) _sendStr.size()); + curl_easy_setopt (_curl, CURLOPT_READFUNCTION, curlReadFromString); + curl_easy_setopt (_curl, CURLOPT_READDATA, this); + } else if (_sendGStr.size()) { + curl_easy_setopt (_curl, CURLOPT_INFILESIZE, (long) _sendGStr.size()); + curl_easy_setopt (_curl, CURLOPT_READFUNCTION, curlReadFromGString); + curl_easy_setopt (_curl, CURLOPT_READDATA, this); + } + curl_easy_setopt (_curl, CURLOPT_UPLOAD, 1L); // cURL now needs this to actually send the email, cf. "http://curl.haxx.se/mail/lib-2013-12/0152.html". + return *this; + } + + /** Add SMTP recipient to the `_headers` (which are then set into `CURLOPT_MAIL_RCPT` by the `Curl::smtp`). + * NB: Should be used *before* the `Curl::smtp`! */ + Curl& bcc (const char* to) { + if (to) _headers = curl_slist_append (_headers, to); + return *this; + } + + /** + Uses `CURLOPT_CUSTOMREQUEST` to set the http method. + Can be used both before and after the `http` method.\n + Example sending a POST request to ElasticSearch: \code + glim::Curl curl; + curl.send (C2GSTRING (R"({"query":{"match_all":{}},"facets":{"tags":{"terms":{"field":"tags","size":1000}}}})")); + curl.method ("POST") .http ("http://127.0.0.1:9200/froples/frople/_search", 120); + if (curl.verbose().go().status() != 200) GTHROW ("Error fetching tags: " + std::to_string (curl.status()) + ", " + curl.str()); + cout << curl.gstr() << endl; + \endcode */ + Curl& method (const char* method) { + curl_easy_setopt (_curl, CURLOPT_CUSTOMREQUEST, method); + return *this; + } + + /** Setup a handler to process the headers cURL gets from the response. + * "The header callback will be called once for each header and only complete header lines are passed on to the callback".\n + * See http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTHEADERFUNCTION */ + Curl& headerListener (std::function listener) { + curl_easy_setopt (_curl, CURLOPT_HEADERFUNCTION, curlWriteHeader); + curl_easy_setopt (_curl, CURLOPT_WRITEHEADER, this); + _headerListener = listener; + return *this; + } + + /** Setup a handler to get the debug messages generated by cURL. + * See http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTDEBUGFUNCTION */ + Curl& debugListener (std::function listener) { + curl_easy_setopt (_curl, CURLOPT_DEBUGFUNCTION, curlDebugCB); + curl_easy_setopt (_curl, CURLOPT_DEBUGDATA, this); + _debugListener = listener; + return verbose (true); + } + + /** + Setup a handler to get some of the debug messages generated by cURL. + Listener gets a formatted text: outbound data is prepended with "> " and inbound with "< ".\n + Usage example: \code + auto curlDebug = std::make_shared(); + curl->debugListenerF ([curlDebug](const char* bytes, size_t size) {curlDebug->append (bytes, size);}); + ... + if (curl->status() != 200) std::cerr << "cURL status != 200; debug follows: " << *curlDebug << std::endl; + \endcode + See http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTDEBUGFUNCTION + @param listener The receiver of the debug information. + @param data Whether to pass the data (`CURLINFO_DATA_IN`, `CURLINFO_DATA_OUT`) to the `listener`. + */ + Curl& debugListenerF (std::function listener, bool data = false) { + return debugListener ([listener/* = std::move (listener)*/,data] (curl_infotype type, char* bytes, size_t size) { + GSTRING_ON_STACK (buf, 256); + auto prepend = [&](const char* prefix) { + buf << prefix; for (char *p = bytes, *end = bytes + size; p < end; ++p) {buf << *p; if (*p == '\n' && p + 2 < end) buf << prefix;}}; + if (type == CURLINFO_HEADER_IN || (type == CURLINFO_DATA_IN && data)) prepend ("< "); + else if (type == CURLINFO_HEADER_OUT || (type == CURLINFO_DATA_OUT && data)) prepend ("> "); + listener (buf.c_str(), buf.size()); + }); + } + + /// Whether to print debug information to `CURLOPT_STDERR`. + /// Note that when `debugListener` is used, verbose output will go to the listener and not to `CURLOPT_STDERR`. + Curl& verbose (bool on = true) { + curl_easy_setopt (_curl, CURLOPT_VERBOSE, on ? 1L : 0L); + return *this; + } + + /// Reset the buffers and perform the cURL request. + Curl& go() { + _got.clear(); + *_errorBuf = 0; + if (curl_easy_perform (_curl)) throw PerformError (_errorBuf, __FILE__, __LINE__); + return *this; + } + + /// The contents of the response. + const std::string& str() const {return _got;} + /// CString of `str`. + const char* c_str() const {return _got.c_str();} + /// Returns a gstring "view" into `str`. + gstring gstr() const {return gstring (0, (void*) _got.data(), false, _got.size());} + + /// The status of the response (For HTTP it's 200 ok, 404 not found, 500 error, etc). + long status() const { + long status; CURLcode err = curl_easy_getinfo (_curl, CURLINFO_RESPONSE_CODE, &status); + if (err) { + GSTRING_ON_STACK (message, 128) << "CURL error " << (int) err << ": " << curl_easy_strerror (err); + throw GetinfoError (err, message.str(), __FILE__, __LINE__); + } + return status;} +}; + +/** Moves the content to be sent into a `glim::gstring` inside `Curl`. + * NB: In order to have an effect this method should be used *before* the `http()` and `smtp()` methods. */ +template<> inline Curl& Curl::send (gstring&& text) { + _sendStr.clear(); + _sendGStr = std::move (text); + _sent = 0; + return *this;} + +inline size_t curlReadFromString (void *ptr, size_t size, size_t nmemb, void *userdata) { + Curl* curl = (Curl*) userdata; + size_t len = std::min (curl->_sendStr.size() - curl->_sent, size * nmemb); + if (len) memcpy (ptr, curl->_sendStr.data() + curl->_sent, len); + curl->_sent += len; + return len;} + +inline size_t curlReadFromGString (void *ptr, size_t size, size_t nmemb, void *userdata) { + Curl* curl = (Curl*) userdata; + size_t len = std::min (curl->_sendGStr.size() - curl->_sent, size * nmemb); + if (len) memcpy (ptr, curl->_sendGStr.data() + curl->_sent, len); + curl->_sent += len; + return len;} + +// http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTHEADERFUNCTION +inline size_t curlWriteHeader (void *ptr, size_t size, size_t nmemb, void *curlPtr) { + Curl* curl = (Curl*) curlPtr; + std::function& listener = curl->_headerListener; + int len = size * nmemb; + if (listener) listener ((const char*) ptr, len); + return (size_t) len; +} + +// http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTDEBUGFUNCTION +inline int curlDebugCB (CURL*, curl_infotype type, char* bytes, size_t size, void* curlPtr) { + Curl* curl = (Curl*) curlPtr; + auto& listener = curl->_debugListener; + if (listener) listener (type, bytes, size); + return 0; +} + +/// Example: std::string w3 = glim::curl2str ("http://www.w3.org/"); +inline std::string curl2str (const char* url, int timeoutSec = 20) { + try { + return glim::Curl().http (url, timeoutSec) .go().str(); + } catch (const std::exception&) {} + return std::string(); +} + +} + +#endif diff --git a/external/glim/doxyconf b/external/glim/doxyconf new file mode 100644 index 00000000..592d0460 --- /dev/null +++ b/external/glim/doxyconf @@ -0,0 +1,237 @@ +# Doxyfile 1.8.4; http://www.stack.nl/~dimitri/doxygen/manual/config.html +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "libglim" +PROJECT_NUMBER = 0.7 +OUTPUT_DIRECTORY = doc +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 2 +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +# http://daringfireball.net/projects/markdown/ +MARKDOWN_SUPPORT = YES +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = YES +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = YES +TYPEDEF_HIDES_STRUCT = NO +LOOKUP_CACHE_SIZE = 0 +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_CLASSES = YES +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = YES +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" + +INPUT = ./ +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.hpp +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = test_*.cc +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +FILTER_SOURCE_FILES = NO +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = NO +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +CLANG_ASSISTED_PARSING = NO +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = YES +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +GENERATE_AUTOGEN_DEF = NO +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES +PERL_PATH = /usr/bin/perl +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/external/glim/exception.hpp b/external/glim/exception.hpp new file mode 100644 index 00000000..5fbfc5aa --- /dev/null +++ b/external/glim/exception.hpp @@ -0,0 +1,259 @@ +#ifndef _GLIM_EXCEPTION_HPP_INCLUDED +#define _GLIM_EXCEPTION_HPP_INCLUDED + +/// \file +/// Exceptions with configurable behaviour. +/// Requires `thread_local` support introduced in [gcc-4.8](http://gcc.gnu.org/gcc-4.8/changes.html) +/// (`__thread` is not reliable with GCC 4.7.2 across shared libraries). + +#include +#include +#include +#include +#include // free +#include // write + +/// Throws `::glim::Exception` passing the current file and line into constructor. +#define GTHROW(message) throw ::glim::Exception (message, __FILE__, __LINE__) +/// Throws a `::glim::Exception` derived exception `name` passing the current file and line into constructor. +#define GNTHROW(name, message) throw name (message, __FILE__, __LINE__) +/// Helps defining new `::glim::Exception`-based exceptions. +/// Named exceptions might be useful in a debugger. +#define G_DEFINE_EXCEPTION(name) \ + struct name: public ::glim::Exception { \ + name (const ::std::string& message, const char* file, int line): ::glim::Exception (message, file, line) {} \ + } + +// Workaround to compile under GCC 4.7. +#if defined (__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 7 && !defined (thread_local) +# define thread_local __thread +#endif + +namespace glim { + +// Ideas: +// RAII control via thread-local integer (with bits): option to capture stack trace (printed on "what()") +// see http://stacktrace.svn.sourceforge.net/viewvc/stacktrace/stacktrace/call_stack_gcc.cpp?revision=40&view=markup +// A handler to log exception with VALGRIND (with optional trace) +// A handler to log thread id and *pause* the thread in exception constructor (user can attach GDB and investigate) +// (or we might call an empty function: "I once used something similar, +// but with an empty function debug_breakpoint. When debugging, I simply entered "bre debug_breakpoint" +// at the gdb prompt - no asembler needed (compile debug_breakpoint in a separate compilation unit to avoid having the call optimized away)." +// - http://stackoverflow.com/a/4720403/257568) +// A handler to call a debugger? (see: http://stackoverflow.com/a/4732119/257568) + +// todo: Try a helper which uses cairo's backtrace-symbols.c +// http://code.ohloh.net/file?fid=zUOUdEl-Id-ijyPOmCkVnBJt2d8&cid=zGpizbyIjEw&s=addr2line&browser=Default#L7 + +// todo: Try a helper which uses cairo's lookup-symbol.c +// http://code.ohloh.net/file?fid=Je2jZqsOxge_SvWVrvywn2I0TIs&cid=zGpizbyIjEw&s=addr2line&browser=Default#L0 + +// todo: A helper converting backtrace to addr2line invocation, e.g. +// bin/test_exception() [0x4020cc];bin/test_exception(__cxa_throw+0x47) [0x402277];bin/test_exception() [0x401c06];/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xfd) [0x57f0ead];bin/test_exception() [0x401fd1]; +// should be converted to +// addr2line -pifCa -e bin/test_exception 0x4020cc 0x402277 0x401c06 0x57f0ead 0x401fd1 +// +// The helper should read the shared library addresses from /proc/.../map and generate separate addr2line invocations +// for groups of addresses inside the same shared library. +// => dladdr instead of /proc/../map; http://stackoverflow.com/a/2606152/257568 +// +// Shared libraries (http://stackoverflow.com/a/7557756/257568). +// Example, backtrace: /usr/local/lib/libfrople.so(_ZN5mongo14BSONObjBuilder8appendAsERKNS_11BSONElementERKNS_10StringDataE+0x1ca) [0x2aef5b45eb8a] +// cat /proc/23630/maps | grep libfrople +// -> 2aef5b363000-2aef5b53e000 +// 0x2aef5b45eb8a - 2aef5b363000 = FBB8A +// addr2line -pifCa -e /usr/local/lib/libfrople.so 0xFBB8A +// +// cat /proc/`pidof FropleAndImg2`/maps | grep libfrople +// addr2line -pifCa -e /usr/local/lib/libfrople.so `perl -e 'printf ("%x", 0x2aef5b45eb8a - 0x2aef5b363000)'` + +inline void captureBacktrace (void* stdStringPtr); + +typedef void (*exception_handler_fn)(void*); + +/// Exception with file and line information and optional stack trace capture. +/// Requires `thread_local` support ([gcc-4.8](http://gcc.gnu.org/gcc-4.8/changes.html)). +class Exception: public std::runtime_error { + protected: + const char* _file; int32_t _line; + std::string _what; + uint32_t _options; + + /// Append [{file}:{line}] into `buf`. + void appendLine (std::string& buf) const { + if (_file || _line > 0) { + std::ostringstream oss; + oss << '['; + if (_file) oss << _file; + if (_line >= 0) oss << ':' << _line; + oss << "] "; + buf.append (oss.str()); + } + } + + /// Append a stack trace to `_what`. + void capture() { + if (_options & RENDEZVOUS) rendezvous(); + if (_options & CAPTURE_TRACE) { + appendLine (_what); + _what += "[at "; + captureBacktrace (&_what); + _what.append ("] "); + _what += std::runtime_error::what(); + } + } + + public: + /** The reference to the thread-local options. */ + inline static uint32_t& options() { + static thread_local uint32_t EXCEPTION_OPTIONS = 0; + return EXCEPTION_OPTIONS; + } + enum Options: uint32_t { + PLAIN_WHAT = 1, ///< Pass `what` as is, do not add any information to it. + HANDLE_ALL = 1 << 1, ///< Run the custom handler from `__cxa_throw`. + CAPTURE_TRACE = 1 << 2, ///< Append a stack trace into the `Exception::_what` (with the help of the `captureBacktrace`). + RENDEZVOUS = 1 << 3 ///< Call the rendezvous function in `throw` and in `what`, so that the GDB can catch it (break glim::Exception::rendezvous). + }; + + /** The pointer to the thread-local exception handler. */ + inline static exception_handler_fn* handler() { + static thread_local exception_handler_fn EXCEPTION_HANDLER = nullptr; + return &EXCEPTION_HANDLER; + } + /** The pointer to the thread-local argument for the exception handler. */ + inline static void** handlerArg() { + static thread_local void* EXCEPTION_HANDLER_ARG = nullptr; + return &EXCEPTION_HANDLER_ARG; + } + + /// Invoked when the `RENDEZVOUS` option is set in order to help the debugger catch the exception (break glim::Exception::rendezvous). + static void rendezvous() __attribute__((noinline)) { + asm (""); // Prevents the function from being optimized away. + } + + Exception (const std::string& message): + std::runtime_error (message), _file (0), _line (-1), _options (options()) { + capture();} + Exception (const std::string& message, const char* file, int32_t line): + std::runtime_error (message), _file (file), _line (line), _options (options()) { + capture();} + ~Exception() throw() {} + virtual const char* what() const throw() { + if (_options & RENDEZVOUS) rendezvous(); + if (_options & PLAIN_WHAT) return std::runtime_error::what(); + std::string& buf = const_cast (_what); + if (buf.empty()) { + appendLine (buf); + buf.append (std::runtime_error::what()); + } + return buf.c_str(); + } +}; + +/// RAII control of thrown `Exception`s. +/// Example: \code +/// glim::ExceptionControl trace (glim::Exception::Options::CAPTURE_TRACE); +/// \endcode +/// Modifies the `Exception` options via a thread-local variable and restores them back upon destruction.\n +/// Currently uses http://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Thread_002dLocal.html +/// (might use C++11 `thread_local` in the future). +class ExceptionControl { + protected: + uint32_t _savedOptions; + public: + ExceptionControl (uint32_t newOptions) { + uint32_t& options = Exception::options(); + _savedOptions = options; + options = newOptions; + } + ~ExceptionControl() { + Exception::options() = _savedOptions; + } +}; + +class ExceptionHandler { +protected: + uint32_t _savedOptions; + exception_handler_fn _savedHandler; + void* _savedHandlerArg; +public: + ExceptionHandler (uint32_t newOptions, exception_handler_fn handler, void* handlerArg) { + uint32_t& options = Exception::options(); _savedOptions = options; options = newOptions; + exception_handler_fn* handler_ = Exception::handler(); _savedHandler = *handler_; *handler_ = handler; + void** handlerArg_ = Exception::handlerArg(); _savedHandlerArg = *handlerArg_; *handlerArg_ = handlerArg; + } + ~ExceptionHandler() { + Exception::options() = _savedOptions; + *Exception::handler() = _savedHandler; + *Exception::handlerArg() = _savedHandlerArg; + } +}; + +} // namespace glim + +#if defined(__GNUC__) && (defined (__linux__) || defined (_SYSTYPE_BSD)) +# include // backtrace; http://www.gnu.org/software/libc/manual/html_node/Backtraces.html +# define _GLIM_USE_EXECINFO +#endif + +namespace glim { + +/** If `stdStringPtr` is not null then backtrace is saved there (must point to an std::string instance), + * otherwise printed to write(2). */ +void captureBacktrace (void* stdStringPtr) { +#ifdef _GLIM_USE_EXECINFO + const int arraySize = 10; void *array[arraySize]; + int got = ::backtrace (array, arraySize); + if (stdStringPtr) { + std::string* out = (std::string*) stdStringPtr; + char **strings = ::backtrace_symbols (array, got); + for (int tn = 0; tn < got; ++tn) {out->append (strings[tn]); out->append (1, ';');} + ::free (strings); + } else ::backtrace_symbols_fd (array, got, 2); +#else +# warning captureBacktrace: I do not know how to capture backtrace there. Patches welcome. +#endif +} + +} // namespace glim + +#endif // _GLIM_EXCEPTION_HPP_INCLUDED + +/** + * Special handler for ALL exceptions. Usage: + * 1) In the `main` module inject this code with: + * #define _GLIM_ALL_EXCEPTIONS_CODE + * #include + * 2) Link with "-ldl" (for `dlsym`). + * 3) Use the ExceptionHandler to enable special behaviour in the current thread: + * glim::ExceptionHandler traceExceptions (glim::Exception::Options::HANDLE_ALL, glim::captureBacktrace, nullptr); + * + * About handing all exceptions see: + * http://stackoverflow.com/a/11674810/257568 + * http://blog.sjinks.pro/c-cpp/969-track-uncaught-exceptions/ + */ +#ifdef _GLIM_ALL_EXCEPTIONS_CODE + +#include // dlsym + +typedef void(*cxa_throw_type)(void*, void*, void(*)(void*)); // Tested with GCC 4.7. +static cxa_throw_type NATIVE_CXA_THROW = 0; + +extern "C" void __cxa_throw (void* thrown_exception, void* tinfo, void (*dest)(void*)) { + if (!NATIVE_CXA_THROW) NATIVE_CXA_THROW = reinterpret_cast (::dlsym (RTLD_NEXT, "__cxa_throw")); + if (!NATIVE_CXA_THROW) ::std::terminate(); + + using namespace glim; + uint32_t options = Exception::options(); + if (options & Exception::RENDEZVOUS) Exception::rendezvous(); + if (options & Exception::HANDLE_ALL) { + exception_handler_fn handler = *Exception::handler(); + if (handler) handler (*Exception::handlerArg()); + } + + NATIVE_CXA_THROW (thrown_exception, tinfo, dest); +} + +#undef _GLIM_ALL_EXCEPTIONS_CODE +#endif // _GLIM_ALL_EXCEPTIONS_CODE diff --git a/external/glim/gstring.hpp b/external/glim/gstring.hpp new file mode 100644 index 00000000..eb0ea2cb --- /dev/null +++ b/external/glim/gstring.hpp @@ -0,0 +1,578 @@ +#ifndef _GSTRING_INCLUDED +#define _GSTRING_INCLUDED + +/** + * A C++ char string.\n + * Can reuse (stack-allocated) buffers.\n + * Can create zero-copy views. + * @code +Copyright 2012 Kozarezov Artem Aleksandrovich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * @endcode + * @file + */ + +#include +#include // malloc, realloc, free +#include +#include // memcpy, memmem +#include // snprintf +#include +#include +#include + +#include "exception.hpp" + +/// Make a read-only gstring from a C string: `const gstring foo = C2GSTRING("foo")`. +#define C2GSTRING(CSTR) ::glim::gstring (::glim::gstring::ReferenceConstructor(), CSTR, sizeof (CSTR) - 1, true) +/// Usage: GSTRING_ON_STACK (buf, 64) << "foo" << "bar"; +#define GSTRING_ON_STACK(NAME, SIZE) char NAME##Buf[SIZE]; ::glim::gstring NAME (SIZE, NAME##Buf, false, 0); NAME.self() + +namespace glim { + +/** + * Based on: C++ version 0.4 char* style "itoa": Written by Lukás Chmela, http://www.strudel.org.uk/itoa/ (GPLv3). + * Returns a pointer to the end of the string. + * NB about `inline`: http://stackoverflow.com/a/1759575/257568 + * @param base Maximum is 36 (see http://en.wikipedia.org/wiki/Base_36). + */ +inline char* itoa (char* ptr, int64_t value, const int base = 10) { + // check that the base is valid + if (base < 2 || base > 36) {*ptr = '\0'; return ptr;} + + char *ptr1 = ptr; + int64_t tmp_value; + + do { + tmp_value = value; + value /= base; + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; + } while (value); + + // Apply negative sign + if (tmp_value < 0) *ptr++ = '-'; + char* end = ptr; + *ptr-- = '\0'; + char tmp_char; + while (ptr1 < ptr) { + tmp_char = *ptr; + *ptr--= *ptr1; + *ptr1++ = tmp_char; + } + return end; +} + +class gstring_stream; + +class gstring { + enum Flags { + FREE_FLAG = 0x80000000, // 1st bit; `_buf` needs `free`ing + FREE_OFFSET = 31, + REF_FLAG = 0x40000000, // 2nd bit; `_buf` has an extended life-time (such as C string literals) and can be shared (passed by reference) + REF_OFFSET = 30, + CAPACITY_MASK = 0x3F000000, // 3..8 bits; `_buf` size is 2^this + CAPACITY_OFFSET = 24, + LENGTH_MASK = 0x00FFFFFF, // 9th bit; allocated capacity + }; + uint32_t _meta; +public: + void* _buf; +public: + constexpr gstring() noexcept: _meta (0), _buf (nullptr) {} + /** + * Reuse `buf` of size `bufSize`. + * To fully use `buf` the `bufSize` should be the power of two. + * @param bufSize The size of the memory allocated to `buf`. + * @param buf The memory region to be reused. + * @param free Whether the `buf` should be `free`d on resize or gstring destruction. + * @param length String length inside the `buf`. + * @param ref If true then the `buf` isn't copied by gstring's copy constructors. + * This is useful for wrapping C string literals. + */ + explicit gstring (uint32_t bufSize, void* buf, bool free, uint32_t length, bool ref = false) noexcept { + uint32_t power = 0; while (((uint32_t) 1 << (power + 1)) <= bufSize) ++power; + _meta = ((uint32_t) free << FREE_OFFSET) | + ((uint32_t) ref << REF_OFFSET) | + (power << CAPACITY_OFFSET) | + (length & LENGTH_MASK); + _buf = buf; + } + + struct ReferenceConstructor {}; + /// Make a view to the given cstring. + /// @param buf The memory region to be reused. + /// @param length String length inside the `buf`. + /// @param ref If true then the `buf` isn't copied by gstring's copy constructors. + /// This is useful for wrapping C string literals. + explicit constexpr gstring (ReferenceConstructor, const char* buf, uint32_t length, bool ref = false) noexcept: + _meta (((uint32_t) ref << REF_OFFSET) | (length & LENGTH_MASK)), _buf ((void*) buf) {} + + /// Copy the characters into `gstring`. + gstring (const char* chars): _meta (0), _buf (nullptr) { + if (chars && *chars) { + size_t length = ::strlen (chars); + _buf = ::malloc (length); + ::memcpy (_buf, chars, length); + _meta = (uint32_t) FREE_FLAG | + (length & LENGTH_MASK); + } + } + + /// Copy the characters into `gstring`. + gstring (const char* chars, size_t length) { + if (length != 0) { + _buf = ::malloc (length); + ::memcpy (_buf, chars, length); + } else _buf = nullptr; + _meta = (uint32_t) FREE_FLAG | + (length & LENGTH_MASK); + } + + /// Copy into `gstring`. + gstring (const std::string& str): _meta (0), _buf (nullptr) { + if (!str.empty()) { + _buf = ::malloc (str.length()); + ::memcpy (_buf, str.data(), str.length()); + _meta = (uint32_t) FREE_FLAG | + (str.length() & LENGTH_MASK); + } + } + + /// If `gstr` is `copiedByReference` then make a shallow copy of it, + /// otherwise copy `gstr` contents into a `malloc`ed buffer. + gstring (const gstring& gstr) { + uint32_t glen = gstr.length(); + if (glen != 0) { + if (gstr.copiedByReference()) { + _meta = gstr._meta; _buf = gstr._buf; + } else { + _buf = ::malloc (glen); + if (!_buf) GTHROW ("!malloc"); + ::memcpy (_buf, gstr._buf, glen); + _meta = (uint32_t) FREE_FLAG | + (glen & LENGTH_MASK); + } + } else { + _meta = 0; _buf = nullptr; + } + } + gstring (gstring&& gstr) noexcept: _meta (gstr._meta), _buf (gstr._buf) { + gstr._meta = 0; gstr._buf = nullptr; + } + gstring& operator = (const gstring& gstr) { + // cf. http://stackoverflow.com/questions/9322174/move-assignment-operator-and-if-this-rhs + if (this != &gstr) { + uint32_t glen = gstr.length(); + uint32_t power = 0; + uint32_t capacity = this->capacity(); + if (glen <= capacity && capacity > 1) { // `capacity <= 1` means there is no _buf. + // We reuse existing buffer. Keep capacity info. + power = (_meta & CAPACITY_MASK) >> CAPACITY_OFFSET; + } else { + if (_buf != nullptr && needsFreeing()) ::free (_buf); + if (gstr.copiedByReference()) { + _meta = gstr._meta; _buf = gstr._buf; + return *this; + } + _buf = ::malloc (glen); + if (_buf == nullptr) GTHROW ("malloc failed"); + } + ::memcpy (_buf, gstr._buf, glen); + _meta = (uint32_t) FREE_FLAG | + (power << CAPACITY_OFFSET) | + (glen & LENGTH_MASK); + } + return *this; + } + gstring& operator = (gstring&& gstr) noexcept { + assert (this != &gstr); + if (_buf != nullptr && needsFreeing()) free (_buf); + _meta = gstr._meta; _buf = gstr._buf; + gstr._meta = 0; gstr._buf = nullptr; + return *this; + } + + /// Return a copy of the string. + gstring clone() const {return gstring (data(), length());} + /// If the gstring's buffer is not owned then copy the bytes into the owned one. + /// Useful for turning a stack-allocated gstring into a heap-allocated gstring. + gstring& owned() {if (!needsFreeing()) *this = gstring (data(), length()); return *this;} + /** Returns a reference to the gstring: when the reference is copied the internal buffer is not copied but referenced (shallow copy).\n + * This method should only be used if it is know that the life-time of the reference and its copies is less than the life-time of the buffer. */ + gstring ref() const noexcept {return gstring (0, _buf, false, length(), true);} + + bool needsFreeing() const noexcept {return _meta & FREE_FLAG;} + bool copiedByReference() const noexcept {return _meta & REF_FLAG;} + /// Current buffer capacity (memory allocated to the string). Returns 1 if no memory allocated. + uint32_t capacity() const noexcept {return 1 << ((_meta & CAPACITY_MASK) >> CAPACITY_OFFSET);} + uint32_t length() const noexcept {return _meta & LENGTH_MASK;} + size_t size() const noexcept {return _meta & LENGTH_MASK;} + bool empty() const noexcept {return (_meta & LENGTH_MASK) == 0;} + std::string str() const {size_t len = size(); return len ? std::string ((const char*) _buf, len) : std::string();} + /// NB: might move the string to a new buffer. + const char* c_str() const { + uint32_t len = length(); if (len == 0) return ""; + uint32_t cap = capacity(); + // c_str should work even for const gstring's, otherwise it's too much of a pain. + if (cap < len + 1) const_cast (this) ->reserve (len + 1); + char* buf = (char*) _buf; buf[len] = 0; return buf; + } + bool equals (const char* cstr) const noexcept { + const char* cstr_; uint32_t clen_; + if (cstr != nullptr) {cstr_ = cstr; clen_ = strlen (cstr);} else {cstr_ = ""; clen_ = 0;} + const uint32_t len = length(); + if (len != clen_) return false; + const char* gstr_ = _buf != nullptr ? (const char*) _buf : ""; + return memcmp (gstr_, cstr_, len) == 0; + } + bool equals (const gstring& gs) const noexcept { + uint32_t llen = length(), olen = gs.length(); + if (llen != olen) return false; + return memcmp ((const char*) _buf, (const char*) gs._buf, llen) == 0; + } + + char& operator[] (unsigned index) noexcept {return ((char*)_buf)[index];} + const char& operator[] (unsigned index) const noexcept {return ((const char*)_buf)[index];} + + /// Access `_buf` as `char*`. `_buf` might be nullptr. + char* data() noexcept {return (char*)_buf;} + const char* data() const noexcept {return (const char*)_buf;} + + char* endp() noexcept {return (char*)_buf + length();} + const char* endp() const noexcept {return (const char*)_buf + length();} + + gstring view (uint32_t pos, int32_t count = -1) noexcept { + return gstring (0, data() + pos, false, count >= 0 ? count : length() - pos, copiedByReference());} + const gstring view (uint32_t pos, int32_t count = -1) const noexcept { + return gstring (0, (void*)(data() + pos), false, count >= 0 ? count : length() - pos, copiedByReference());} + + // http://en.cppreference.com/w/cpp/concept/Iterator + template struct iterator_t: public std::iterator { + CT* _ptr; + iterator_t () noexcept: _ptr (nullptr) {} + iterator_t (CT* ptr) noexcept: _ptr (ptr) {} + iterator_t (const iterator_t& it) noexcept: _ptr (it._ptr) {} + + CT& operator*() const noexcept {return *_ptr;} + CT* operator->() const noexcept {return _ptr;} + CT& operator[](int32_t ofs) const noexcept {return _ptr[ofs];} + + iterator_t& operator++() noexcept {++_ptr; return *this;} + iterator_t operator++(int) noexcept {return iterator_t (_ptr++);}; + iterator_t& operator--() noexcept {--_ptr; return *this;} + iterator_t operator--(int) noexcept {return iterator_t (_ptr--);}; + bool operator == (const iterator_t& i2) const noexcept {return _ptr == i2._ptr;} + bool operator != (const iterator_t& i2) const noexcept {return _ptr != i2._ptr;} + bool operator < (const iterator_t& i2) const noexcept {return _ptr < i2._ptr;} + bool operator > (const iterator_t& i2) const noexcept {return _ptr > i2._ptr;} + bool operator <= (const iterator_t& i2) const noexcept {return _ptr <= i2._ptr;} + bool operator >= (const iterator_t& i2) const noexcept {return _ptr >= i2._ptr;} + iterator_t operator + (int32_t ofs) const noexcept {return iterator (_ptr + ofs);} + iterator_t& operator += (int32_t ofs) noexcept {_ptr += ofs; return *this;} + iterator_t operator - (int32_t ofs) const noexcept {return iterator (_ptr - ofs);} + iterator_t& operator -= (int32_t ofs) noexcept {_ptr -= ofs; return *this;} + }; + // http://en.cppreference.com/w/cpp/concept/Container + typedef char value_type; + typedef char& reference; + typedef const char& const_reference; + typedef uint32_t size_type; + typedef int32_t difference_type; + typedef iterator_t iterator; + typedef iterator_t const_iterator; + iterator begin() noexcept {return iterator ((char*) _buf);} + const_iterator begin() const noexcept {return const_iterator ((char*) _buf);} + iterator end() noexcept {return iterator ((char*) _buf + size());} + const_iterator end() const noexcept {return const_iterator ((char*) _buf + size());} + const_iterator cbegin() const noexcept {return const_iterator ((char*) _buf);} + const_iterator cend() const noexcept {return const_iterator ((char*) _buf + size());} + + /** Returns -1 if not found. */ + int32_t find (const char* str, int32_t pos, int32_t count) const noexcept { + const int32_t hlen = (int32_t) length() - pos; + if (hlen <= 0) return -1; + char* haystack = (char*) _buf + pos; + void* mret = memmem (haystack, hlen, str, count); + if (mret == 0) return -1; + return (char*) mret - (char*) _buf; + } + int32_t find (const char* str, int32_t pos = 0) const noexcept {return find (str, pos, strlen (str));} + + /** Index of `ch` inside the string or -1 if not found. */ + int32_t indexOf (char ch) const noexcept { + void* ret = memchr (_buf, ch, size()); + return ret == nullptr ? -1 : (char*) ret - (char*) _buf; + } + + // Helps to workaround the "statement has no effect" warning in `GSTRING_ON_STACK`. + gstring& self() noexcept {return *this;} + + /** Grow buffer to be at least `to` characters long. */ + void reserve (uint32_t to) { + uint32_t power = (_meta & CAPACITY_MASK) >> CAPACITY_OFFSET; + if (((uint32_t) 1 << power) < to) { + ++power; + while (((uint32_t) 1 << power) < to) ++power; + if (power > 24) {GSTRING_ON_STACK (error, 64) << "gstring too large: " << (int) to; GTHROW (error.str());} + } else if (power) { + // No need to grow. + return; + } + _meta = (_meta & ~CAPACITY_MASK) | (power << CAPACITY_OFFSET); + if (needsFreeing() && _buf != nullptr) { + _buf = ::realloc (_buf, capacity()); + if (_buf == nullptr) GTHROW ("realloc failed"); + } else { + const char* oldBuf = (const char*) _buf; + _buf = ::malloc (capacity()); + if (_buf == nullptr) GTHROW ("malloc failed"); + if (oldBuf != nullptr) ::memcpy (_buf, oldBuf, length()); + _meta |= FREE_FLAG; + } + } + + /** Length setter. Useful when you manually write into the buffer or to cut the string. */ + void length (uint32_t len) noexcept { + _meta = (_meta & ~LENGTH_MASK) | (len & LENGTH_MASK); + } + +protected: + friend class gstring_stream; +public: + /** Appends an integer to the string. + * @param base Radix, from 1 to 36 (default 10). + * @param bytes How many bytes to reserve (24 by default). */ + void append64 (int64_t iv, int base = 10, uint_fast8_t bytes = 24) { + uint32_t pos = length(); + if (capacity() < pos + bytes) reserve (pos + bytes); + length (itoa ((char*) _buf + pos, iv, base) - (char*) _buf); + } + void append (char ch) { + uint32_t pos = length(); + const uint32_t cap = capacity(); + if (pos >= cap || cap <= 1) reserve (pos + 1); + ((char*)_buf)[pos] = ch; + length (++pos); + } + void append (const char* cstr, uint32_t clen) { + uint32_t len = length(); + uint32_t need = len + clen; + const uint32_t cap = capacity(); + if (need > cap || cap <= 1) reserve (need); + ::memcpy ((char*) _buf + len, cstr, clen); + length (need); + } + /** This one is for http://code.google.com/p/re2/; `clear` then `append`. */ + bool ParseFrom (const char* cstr, int clen) { + if (clen < 0 || clen > (int) LENGTH_MASK) return false; + length (0); append (cstr, (uint32_t) clen); return true;} + gstring& operator << (const gstring& gs) {append (gs.data(), gs.length()); return *this;} + gstring& operator << (const std::string& str) {append (str.data(), str.length()); return *this;} + gstring& operator << (const char* cstr) {if (cstr) append (cstr, ::strlen (cstr)); return *this;} + gstring& operator << (char ch) {append (ch); return *this;} + gstring& operator << (int iv) {append64 (iv, 10, sizeof (int) * 3); return *this;} + gstring& operator << (long iv) {append64 (iv, 10, sizeof (long) * 3); return *this;} + gstring& operator << (long long iv) {append64 (iv, 10, sizeof (long long) * 3); return *this;} + gstring& operator << (double dv) { + uint32_t len = length(); + reserve (len + 32); + int rc = snprintf (endp(), 31, "%f", dv); + if (rc > 0) {length (len + std::min (rc, 31));} + return *this; + } + + bool operator < (const gstring &gs) const noexcept { + uint32_t len1 = length(); uint32_t len2 = gs.length(); + if (len1 == len2) return ::strncmp (data(), gs.data(), len1) < 0; + int cmp = ::strncmp (data(), gs.data(), std::min (len1, len2)); + if (cmp) return cmp < 0; + return len1 < len2; + } + + /// Asks `strftime` to generate a time string. Capacity is increased if necessary (up to a limit of +1024 bytes). + gstring& appendTime (const char* format, struct tm* tmv) { + int32_t pos = length(), cap = capacity(), left = cap - pos; + if (left < 8) {reserve (pos + 8); return appendTime (format, tmv);} + size_t got = strftime ((char*) _buf + pos, left, format, tmv); + if (got == 0) { + if (left > 1024) return *this; // Guard against perpetual growth. + reserve (pos + left * 2); return appendTime (format, tmv); + } + length (pos + got); + return *this; + } + + /// Append the characters to this `gstring` wrapping them in the netstring format. + gstring& appendNetstring (const char* cstr, uint32_t clen) { + *this << (int) clen; append (':'); append (cstr, clen); append (','); return *this;} + /// Append the `gstr` wrapping it in the netstring format. + gstring& appendNetstring (const gstring& gstr) {return appendNetstring (gstr.data(), gstr.length());} + + std::ostream& writeAsNetstring (std::ostream& stream) const; + + /// Parse netstring at `pos` and return a `gstring` *pointing* at the parsed netstring.\n + /// No heap space allocated.\n + /// Throws std::runtime_error if netstring parsing fails.\n + /// If parsing was successfull, then `after` is set to point after the parsed netstring. + gstring netstringAt (uint32_t pos, uint32_t* after = nullptr) const { + const uint32_t len = length(); char* buf = (char*) _buf; + if (buf == nullptr) GTHROW ("gstring: netstringAt: nullptr"); + uint32_t next = pos; + while (next < len && buf[next] >= '0' && buf[next] <= '9') ++next; + if (next >= len || buf[next] != ':' || next - pos > 10) GTHROW ("gstring: netstringAt: no header"); + char* endptr = 0; + long nlen = ::strtol (buf + pos, &endptr, 10); + if (endptr != buf + next) GTHROW ("gstring: netstringAt: unexpected header end"); + pos = next + 1; next = pos + nlen; + if (next >= len || buf[next] != ',') GTHROW ("gstring: netstringAt: no body"); + if (after) *after = next + 1; + return gstring (0, buf + pos, false, next - pos); + } + + /// Wrapper around strtol, not entirely safe (make sure the string is terminated with a non-digit, by calling c_str, for example). + long intAt (uint32_t pos, uint32_t* after = nullptr, int base = 10) const { + // BTW: http://www.kumobius.com/2013/08/c-string-to-int/ + const uint32_t len = length(); char* buf = (char*) _buf; + if (pos >= len || buf == nullptr) GTHROW ("gstring: intAt: pos >= len"); + char* endptr = 0; + long lv = ::strtol (buf + pos, &endptr, base); + uint32_t next = endptr - buf; + if (next > len) GTHROW ("gstring: intAt: endptr > len"); + if (after) *after = next; + return lv; + } + + /// Wrapper around strtol. Copies the string into a temporary buffer in order to pass it to strtol. Empty string returns 0. + long toInt (int base = 10) const noexcept { + const uint32_t len = length(); if (len == 0) return 0; + char buf[len + 1]; memcpy (buf, _buf, len); buf[len] = 0; + return ::strtol (buf, nullptr, base); + } + + /// Get a single netstring from the `stream` and append it to the end of `gstring`. + /// Throws an exception if the input is not a well-formed netstring. + gstring& readNetstring (std::istream& stream) { + int32_t nlen; stream >> nlen; + if (!stream.good() || nlen < 0) GTHROW ("!netstring"); + int ch = stream.get(); + if (!stream.good() || ch != ':') GTHROW ("!netstring"); + uint32_t glen = length(); + const uint32_t cap = capacity(); + if (cap < glen + nlen || cap <= 1) reserve (glen + nlen); + stream.read ((char*) _buf + glen, nlen); + if (!stream.good()) GTHROW ("!netstring"); + ch = stream.get(); + if (ch != ',') GTHROW ("!netstring"); + length (glen + nlen); + return *this; + } + + /// Set length to 0. `_buf` not changed. + gstring& clear() noexcept {length (0); return *this;} + + /// Removes `count` characters starting at `pos`. + gstring& erase (uint32_t pos, uint32_t count = 1) noexcept { + const char* buf = (const char*) _buf; + const char* pt1 = buf + pos; + const char* pt2 = pt1 + count; + uint32_t len = length(); + const char* end = buf + len; + if (pt2 <= end) { + length (len - count); + ::memmove ((void*) pt1, (void*) pt2, end - pt2); + } + return *this; + } + /// Remove characters [from,till) and return `from`.\n + /// Compatible with "boost/algorithm/string/trim.hpp". + iterator_t erase (iterator_t from, iterator_t till) noexcept { + intptr_t ipos = from._ptr - (char*) _buf; + intptr_t count = till._ptr - from._ptr; + if (ipos >= 0 && count > 0) erase (ipos, count); + return from; + } + + ~gstring() noexcept { + if (_buf != nullptr && needsFreeing()) {::free (_buf); _buf = nullptr;} + } +}; + +inline bool operator == (const gstring& gs1, const gstring& gs2) noexcept {return gs1.equals (gs2);} +inline bool operator == (const char* cstr, const gstring& gstr) noexcept {return gstr.equals (cstr);} +inline bool operator == (const gstring& gstr, const char* cstr) noexcept {return gstr.equals (cstr);} +inline bool operator != (const gstring& gs1, const gstring& gs2) noexcept {return !gs1.equals (gs2);} +inline bool operator != (const char* cstr, const gstring& gstr) noexcept {return !gstr.equals (cstr);} +inline bool operator != (const gstring& gstr, const char* cstr) noexcept {return !gstr.equals (cstr);} + +inline bool operator == (const gstring& gstr, const std::string& str) noexcept { + return gstr.equals (gstring (gstring::ReferenceConstructor(), str.data(), str.size()));} +inline bool operator != (const gstring& gstr, const std::string& str) noexcept {return !(gstr == str);} +inline bool operator == (const std::string& str, const gstring& gstr) noexcept {return gstr == str;} +inline bool operator != (const std::string& str, const gstring& gstr) noexcept {return !(gstr == str);} +inline std::string operator += (std::string& str, const gstring& gstr) {return str.append (gstr.data(), gstr.size());} +inline std::string operator + (const std::string& str, const gstring& gstr) {return std::string (str) .append (gstr.data(), gstr.size());} + +inline std::ostream& operator << (std::ostream& os, const gstring& gstr) { + if (gstr._buf != nullptr) os.write ((const char*) gstr._buf, gstr.length()); + return os; +} + +/// Encode this `gstring` into `stream` as a netstring. +inline std::ostream& gstring::writeAsNetstring (std::ostream& stream) const { + stream << length() << ':' << *this << ','; + return stream; +} + +// http://www.mr-edd.co.uk/blog/beginners_guide_streambuf +// http://www.dreamincode.net/code/snippet2499.htm +// http://spec.winprog.org/streams/ +class gstring_stream: public std::basic_streambuf > { + gstring& _gstr; +public: + gstring_stream (gstring& gstr) noexcept: _gstr (gstr) { + char* buf = (char*) gstr._buf; + if (buf != nullptr) setg (buf, buf, buf + gstr.length()); + } +protected: + virtual int_type overflow (int_type ch) { + if (__builtin_expect (ch != traits_type::eof(), 1)) _gstr.append ((char) ch); + return 0; + } + // no copying + gstring_stream (const gstring_stream &); + gstring_stream& operator = (const gstring_stream &); +}; + +} // namespace glim + +// hash specialization +// cf. http://stackoverflow.com/questions/8157937/how-to-specialize-stdhashkeyoperator-for-user-defined-type-in-unordered +namespace std { + template <> struct hash { + size_t operator()(const glim::gstring& gs) const noexcept { + // cf. http://stackoverflow.com/questions/7666509/hash-function-for-string + // Would be nice to use https://131002.net/siphash/ here. + uint32_t hash = 5381; + uint32_t len = gs.length(); + if (len) { + const char* str = (const char*) gs._buf; + const char* end = str + len; + while (str < end) hash = ((hash << 5) + hash) + *str++; /* hash * 33 + c */ + } + return hash; + } + }; +} + +#endif // _GSTRING_INCLUDED diff --git a/external/glim/hget.hpp b/external/glim/hget.hpp new file mode 100644 index 00000000..cebbe416 --- /dev/null +++ b/external/glim/hget.hpp @@ -0,0 +1,255 @@ +// Simple header-only wrapper around libevent's evhttp client. +// See also: https://github.com/cpp-netlib/cpp-netlib/issues/160 + +#ifndef _GLIM_HGET_INCLUDED +#define _GLIM_HGET_INCLUDED + +#include +#include +#include // http://stackoverflow.com/a/5237994; http://archives.seul.org/libevent/users/Sep-2010/msg00050.html + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "exception.hpp" +#include "gstring.hpp" + +namespace glim { + +/// HTTP results +struct hgot { + int32_t status = 0; + /// Uses errno codes. + int32_t error = 0; + struct evbuffer* body = 0; + struct evhttp_request* req = 0; + size_t bodyLength() const {return body ? evbuffer_get_length (body) : 0;} + /// Warning: the string is NOT zero-terminated. + const char* bodyData() {return body ? (const char*) evbuffer_pullup (body, -1) : "";} + /// Returns a zero-terminated string. Warning: modifies the `body` every time in order to add the terminator. + const char* cbody() {if (!body) return ""; evbuffer_add (body, "", 1); return (const char*) evbuffer_pullup (body, -1);} + /// A gstring *view* into the `body`. + glim::gstring gbody() { + if (!body) return glim::gstring(); + return glim::gstring (glim::gstring::ReferenceConstructor(), (const char*) evbuffer_pullup (body, -1), evbuffer_get_length (body));} +}; + +/// Used internally to pass both connection and handler into callback. +struct hgetContext { + struct evhttp_connection* conn; + std::function handler; + hgetContext (struct evhttp_connection* conn, std::function handler): conn (conn), handler (handler) {} +}; + +/// Invoked when evhttp finishes a request. +inline void hgetCB (struct evhttp_request* req, void* ctx_){ + hgetContext* ctx = (hgetContext*) ctx_; + + hgot gt; + if (req == NULL) gt.error = ETIMEDOUT; + else if (req->response_code == 0) gt.error = ECONNREFUSED; + else { + gt.status = req->response_code; + gt.body = req->input_buffer; + gt.req = req; + } + + try { + ctx->handler (gt); + } catch (const std::runtime_error& ex) { // Shouldn't normally happen: + std::cerr << "glim::hget, handler exception: " << ex.what() << std::endl; + } + + evhttp_connection_free ((struct evhttp_connection*) ctx->conn); + //freed by libevent//if (req != NULL) evhttp_request_free (req); + delete ctx; +} + +/** + C++ wrapper around libevent's http client. + Example: \code + hget (evbase, dnsbase) .setRequestBuilder ([](struct evhttp_request* req){ + evbuffer_add (req->output_buffer, "foo", 3); + evhttp_add_header (req->output_headers, "Content-Length", "3"); + }) .go ("http://127.0.0.1:8080/test", [](hgot& got){ + if (got.error) log_warn ("127.0.0.1:8080 " << strerror (got.error)); + else if (got.status != 200) log_warn ("127.0.0.1:8080 != 200"); + else log_info ("got " << evbuffer_get_length (got.body) << " bytes from /test: " << evbuffer_pullup (got.body, -1)); + }); \endcode + */ +class hget { + public: + std::shared_ptr _evbase; + std::shared_ptr _dnsbase; + std::function _requestBuilder; + enum evhttp_cmd_type _method; + public: + typedef std::shared_ptr uri_t; + /// The third parameter is the request number, starting from 1. + typedef std::function until_handler_t; + public: + hget (std::shared_ptr evbase, std::shared_ptr dnsbase): + _evbase (evbase), _dnsbase (dnsbase), _method (EVHTTP_REQ_GET) {} + + /// Modifies the request before its execution. + hget& setRequestBuilder (std::function rb) { + _requestBuilder = rb; + return *this; + } + + /** Uses a simple request builder to send the `str`. + * `str` is a `char` string class with methods `data` and `size`. */ + template hget& payload (STR str, const char* contentType = nullptr, enum evhttp_cmd_type method = EVHTTP_REQ_POST) { + _method = method; + return setRequestBuilder ([str,contentType](struct evhttp_request* req) { + if (contentType) evhttp_add_header (req->output_headers, "Content-Type", contentType); + char buf[64]; + *glim::itoa (buf, (int) str.size()) = 0; + evhttp_add_header (req->output_headers, "Content-Length", buf); + evbuffer_add (req->output_buffer, (const void*) str.data(), (size_t) str.size()); + }); + } + + struct evhttp_request* go (uri_t uri, int32_t timeoutSec, std::function handler) { + int port = evhttp_uri_get_port (uri.get()); + if (port == -1) port = 80; + struct evhttp_connection* conn = evhttp_connection_base_new (_evbase.get(), _dnsbase.get(), + evhttp_uri_get_host (uri.get()), port); + evhttp_connection_set_timeout (conn, timeoutSec); + struct evhttp_request *req = evhttp_request_new (hgetCB, new hgetContext(conn, handler)); + int ret = evhttp_add_header (req->output_headers, "Host", evhttp_uri_get_host (uri.get())); + if (ret) throw std::runtime_error ("hget: evhttp_add_header(Host) != 0"); + if (_requestBuilder) _requestBuilder (req); + const char* get = evhttp_uri_get_path (uri.get()); + const char* qs = evhttp_uri_get_query (uri.get()); + if (qs == NULL) { + ret = evhttp_make_request (conn, req, _method, get); + } else { + size_t getLen = strlen (get); + size_t qsLen = strlen (qs); + char buf[getLen + 1 + qsLen + 1]; + char* caret = stpcpy (buf, get); + *caret++ = '?'; + caret = stpcpy (caret, qs); + assert (caret - buf < sizeof (buf)); + ret = evhttp_make_request (conn, req, _method, buf); + } + if (ret) throw std::runtime_error ("hget: evhttp_make_request != 0"); + return req; + } + struct evhttp_request* go (const char* url, int32_t timeoutSec, std::function handler) { + return go (std::shared_ptr (evhttp_uri_parse (url), evhttp_uri_free), timeoutSec, handler); + } + + void goUntil (std::vector urls, until_handler_t handler, int32_t timeoutSec = 20); + /** + Parse urls and call `goUntil`. + Example (trying ten times to reach the servers): \code + std::string path ("/path"); + hget.goUntilS (boost::assign::list_of ("http://server1" + path) ("http://server2" + path), + [](hgot& got, hget::uri_t uri, int32_t num)->float { + std::cout << "server: " << evhttp_uri_get_host (uri.get()) << "; request number: " << num << std::endl; + if (got.status != 200 && num < 10) return 1.f; // Retry in a second. + return -1.f; // No need to retry the request. + }); + \endcode + @param urls is a for-compatible container of strings (where string has methods `data` and `size`). + */ + template void goUntilS (URLS&& urls, until_handler_t handler, int32_t timeoutSec = 20) { + std::vector parsedUrls; + for (auto&& url: urls) { + // Copying to stack might be cheaper than malloc in c_str. + int len = url.size(); char buf[len + 1]; memcpy (buf, url.data(), len); buf[len] = 0; + struct evhttp_uri* uri = evhttp_uri_parse (buf); + if (!uri) GTHROW (std::string ("!evhttp_uri_parse: ") + buf); + parsedUrls.push_back (uri_t (uri, evhttp_uri_free)); + } + goUntil (parsedUrls, handler, timeoutSec); + } + /** + Parse urls and call `goUntil`. + Example (trying ten times to reach the servers): \code + hget.goUntilC (boost::assign::list_of ("http://server1/") ("http://server2/"), + [](hgot& got, hget::uri_t uri, int32_t num)->float { + std::cout << "server: " << evhttp_uri_get_host (uri.get()) << "; request number: " << num << std::endl; + if (got.status != 200 && num < 10) return 1.f; // Retry in a second. + return -1.f; // No need to retry the request. + }); + \endcode + Or with `std::array` instead of `boost::assign::list_of`: \code + std::array urls {{"http://server1/", "http://server2/"}}; + hget.goUntilC (urls, [](hgot& got, hget::uri_t uri, int32_t num)->float { + return got.status != 200 && num < 10 ? 0.f : -1.f;}); + \endcode + @param urls is a for-compatible container of C strings (const char*). + */ + template void goUntilC (URLS&& urls, until_handler_t handler, int32_t timeoutSec = 20) { + std::vector parsedUrls; + for (auto url: urls) { + struct evhttp_uri* uri = evhttp_uri_parse (url); + if (!uri) GTHROW (std::string ("Can't parse url: ") + url); + parsedUrls.push_back (uri_t (uri, evhttp_uri_free)); + } + goUntil (parsedUrls, handler, timeoutSec); + } +}; + +inline void hgetUntilRetryCB (evutil_socket_t, short, void* utilHandlerPtr); // event_callback_fn + +/** `hget::goUntil` implementation. + * This function object is passed to `hget::go` as a handler and calls `hget::go` again if necessary. */ +struct HgetUntilHandler { + hget _hget; + hget::until_handler_t _handler; + std::vector _urls; + int32_t _timeoutSec; + int32_t _requestNum; + uint8_t _nextUrl; ///< A round-robin pointer to the next url in `_urls`. + HgetUntilHandler (hget& hg, hget::until_handler_t handler, std::vector urls, int32_t timeoutSec): + _hget (hg), _handler (handler), _urls (urls), _timeoutSec (timeoutSec), _requestNum (0), _nextUrl (0) {} + void operator() (hgot& got) { + uint8_t urlNum = _nextUrl ? _nextUrl - 1 : _urls.size() - 1; + float retryAfterSec = _handler (got, _urls[urlNum], _requestNum); + if (retryAfterSec == 0.f) retry(); + else if (retryAfterSec > 0.f) { + struct timeval wait; + wait.tv_sec = (int) retryAfterSec; + retryAfterSec -= wait.tv_sec; + wait.tv_usec = (int) (retryAfterSec * 1000000.f); + int rc = event_base_once (_hget._evbase.get(), -1, EV_TIMEOUT, hgetUntilRetryCB, new HgetUntilHandler (*this), &wait); + if (rc) throw std::runtime_error ("HgetUntilHandler: event_base_once != 0"); + } + } + void start() {retry();} + void retry() { + uint8_t nextUrl = _nextUrl++; + if (_nextUrl >= _urls.size()) _nextUrl = 0; + ++_requestNum; + _hget.go (_urls[nextUrl], _timeoutSec, *this); + } +}; + +/// Used in `hget::goUntil` to wait in `evtimer_new` before repeating the request. +inline void hgetUntilRetryCB (evutil_socket_t, short, void* utilHandlerPtr) { // event_callback_fn + std::unique_ptr untilHandler ((HgetUntilHandler*) utilHandlerPtr); + untilHandler->retry(); +} + +/** + * Allows to retry the request using multiple URLs in a round-robin fashion. + * The `handler` returns the number of seconds to wait before retrying the request or -1 if no retry is necessary. + */ +inline void hget::goUntil (std::vector urls, until_handler_t handler, int32_t timeoutSec) { + HgetUntilHandler (*this, handler, urls, timeoutSec) .start(); +} + +} + +#endif // _GLIM_HGET_INCLUDED diff --git a/external/glim/ldb.hpp b/external/glim/ldb.hpp new file mode 100644 index 00000000..efc82d7d --- /dev/null +++ b/external/glim/ldb.hpp @@ -0,0 +1,384 @@ +#ifndef _GLIM_LDB_HPP_INCLUDED +#define _GLIM_LDB_HPP_INCLUDED + +/** + * Leveldb (http://code.google.com/p/leveldb/) wrapper. + * @code +Copyright 2012 Kozarezov Artem Aleksandrovich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * @endcode + * @file + */ + +#include +//#include // having SIGFPE as in http://stackoverflow.com/q/13580823/257568 +#include +#include // CHAR_MAX + +#include +#include +#include +#include +#include +#include +#include +#include +#include // http://www.boost.org/doc/libs/1_52_0/libs/range/doc/html/range/reference/utilities/iterator_range.html + +#include // htonl, ntohl +#include // mkdir +#include // mkdir +#include // strerror +#include +#include + +#include "gstring.hpp" +#include "exception.hpp" + +namespace glim { + +G_DEFINE_EXCEPTION (LdbEx); + +template inline void ldbSerialize (gstring& bytes, const T& data) { + gstring_stream stream (bytes); + boost::archive::binary_oarchive oa (stream, boost::archive::no_header); + oa << data; +} +template inline void ldbDeserialize (const gstring& bytes, V& data) { + gstring_stream stream (const_cast (bytes)); + boost::archive::binary_iarchive ia (stream, boost::archive::no_header); + ia >> data; +} + +/** uint32_t keys are stored big-endian (network byte order) in order to be compatible with lexicographic ordering. */ +template <> inline void ldbSerialize (gstring& bytes, const uint32_t& ui) { + uint32_t nui = htonl (ui); bytes.append ((const char*) &nui, sizeof (uint32_t));} +/** Deserialize uint32_t from big-endian (network byte order). */ +template <> inline void ldbDeserialize (const gstring& bytes, uint32_t& ui) { + if (bytes.size() != sizeof (uint32_t)) GNTHROW (LdbEx, "Not uint32_t, wrong number of bytes"); + uint32_t nui = * (uint32_t*) bytes.data(); ui = ntohl (nui);} + +/** If the data is `gstring` then use the data's buffer directly, no copy. */ +template <> inline void ldbSerialize (gstring& bytes, const gstring& data) { + bytes = gstring (0, (void*) data.data(), false, data.length());} +/** Deserializing into `gstring` copies the bytes into it, reusing its buffer. */ +template <> inline void ldbDeserialize (const gstring& bytes, gstring& data) { + data.clear() << bytes;} + +/** If the data is `std::string` then use the data's buffer directly, no copy. */ +template <> inline void ldbSerialize (gstring& bytes, const std::string& data) { + bytes = gstring (0, (void*) data.data(), false, data.length());} +/** Deserializing into `std::string` copies the bytes into it, reusing its buffer. */ +template <> inline void ldbDeserialize (const gstring& bytes, std::string& data) { + data.clear(); data.append (bytes.data(), bytes.size());} + +/** + * Header-only Leveldb wrapper.\n + * Uses Boost Serialization to pack keys and values (glim::gstring can be used for raw bytes).\n + * Allows semi-automatic indexing with triggers. + */ +struct Ldb { + std::shared_ptr _db; + std::shared_ptr _filter; + + struct IteratorEntry { ///< Something to be `dereference`d from the Iterator. Also a pImpl allowing to keep the `_valid` and the `_lit` in sync. + leveldb::Iterator* _lit; + bool _valid:1; + + IteratorEntry (const IteratorEntry&) = delete; // Owns `leveldb::Iterator`, should not be copied. + IteratorEntry (IteratorEntry&&) = default; + + IteratorEntry (leveldb::Iterator* lit, bool valid = false): _lit (lit), _valid (valid) {} + ~IteratorEntry() {delete _lit;} + + /** Zero-copy view of the current key bytes. Should *not* be used after the Iterator is changed or destroyed. + * @param ref If true then the *copies* of the returned gstring will keep pointing to LevelDB memory which is valid only until the iterator changes. */ + const gstring keyView (bool ref = false) const { + if (!_valid) return gstring(); + const leveldb::Slice& key = _lit->key(); + return gstring (0, (void*) key.data(), false, key.size(), ref);} // Zero copy. + /** Zero-copy view of the current value bytes. Should *not* be used after the Iterator is changed or destroyed. + * @param ref If true then the *copies* of the returned gstring will keep pointing to LevelDB memory which is valid only until the iterator changes. */ + const gstring valueView (bool ref = false) const { + if (!_valid) return gstring(); + const leveldb::Slice& val = _lit->value(); + return gstring (0, (void*) val.data(), false, val.size(), ref);} // Zero copy. + /** Deserialize into `key`. */ + template void getKey (T& key) const {ldbDeserialize (keyView (true), key);} + /** Deserialize the key into a temporary and return it. */ + template T getKey() const {T key; getKey (key); return key;} + /** Deserialize into `value`. */ + template void getValue (T& value) const {ldbDeserialize (valueView (true), value);} + /** Deserialize the value into a temporary and return it. */ + template T getValue() const {T value; getValue (value); return value;} + }; + struct NoSeekFlag {}; ///< Tells the `Iterator` constructor not to seek to the beginning of the database. + /** Wraps Leveldb iterator. + * Note: "In fact the iterator is a light-weight snapshot. It will see exactly the version of the DB that existed when the iterator was created + * (i.e., any insert/delete done after the iterator is created, regardless of which thread does them) will be invisible to the iterator" + * (https://groups.google.com/d/msg/leveldb/nX8S5KKiSn4/PI92Yf1Hf6UJ). */ + struct Iterator: public boost::iterator_facade { + std::shared_ptr _entry; ///< The Iterator might be copied around, therefore we keep the real iterator and the state in the shared_ptr. + + Iterator (const Iterator&) = default; + Iterator (Iterator&&) = default; + Iterator& operator= (const Iterator&) = default; + Iterator& operator= (Iterator&&) = default; + /** Iterate from the beginning or the end of the database. + * @param position can be MDB_FIRST or MDB_LAST */ + Iterator (Ldb* ldb, leveldb::ReadOptions options = leveldb::ReadOptions()): + _entry (std::make_shared (ldb->_db->NewIterator (options))) { + IteratorEntry* entry = _entry.get(); + entry->_lit->SeekToFirst(); + entry->_valid = entry->_lit->Valid(); + } + + Iterator (Ldb* ldb, NoSeekFlag, leveldb::ReadOptions options = leveldb::ReadOptions()): + _entry (std::make_shared (ldb->_db->NewIterator (options))) {} + /** True if the iterator isn't pointing anywhere. */ + bool end() const {return !_entry->_valid;} + + bool equal (const Iterator& other) const { + bool weAreValid = _entry->_valid, theyAreValid = other._entry->_valid; + if (!weAreValid) return !theyAreValid; + if (!theyAreValid) return false; + auto&& ourKey = _entry->_lit->key(), theirKey = other._entry->_lit->key(); + if (ourKey.size() != theirKey.size()) return false; + return memcmp (ourKey.data(), theirKey.data(), ourKey.size()) == 0; + } + IteratorEntry& dereference() const { + // NB: Boost iterator_facade expects the `dereference` to be a `const` method. + // I guess Iterator is not modified, so the `dereference` is `const`, even though the Entry can be modified. + return *_entry; + } + virtual void increment() { + IteratorEntry* entry = _entry.get(); + if (entry->_valid) entry->_lit->Next(); + else entry->_lit->SeekToFirst(); + entry->_valid = entry->_lit->Valid(); + } + virtual void decrement() { + IteratorEntry* entry = _entry.get(); + if (entry->_valid) entry->_lit->Prev(); + else entry->_lit->SeekToLast(); + entry->_valid = entry->_lit->Valid(); + } + + Iterator& seek (const gstring& key) { + IteratorEntry* entry = _entry.get(); + entry->_lit->Seek (leveldb::Slice (key.data(), key.size())); + entry->_valid = entry->_lit->Valid(); + return *this; + } + }; + Iterator begin() {return Iterator (this);} + Iterator end() {return Iterator (this, NoSeekFlag());} + + /** Range from `from` (inclusive) to `till` (exclusive). */ + template + boost::iterator_range range (const K& from, const K& till, leveldb::ReadOptions options = leveldb::ReadOptions()) { + char kbuf[64]; // Allow up to 64 bytes to be serialized without heap allocations. + gstring kbytes (sizeof (kbuf), kbuf, false, 0); + ldbSerialize (kbytes, from); + Iterator fit (this, NoSeekFlag(), options); fit.seek (kbytes); + + ldbSerialize (kbytes.clear(), till); + Iterator tit (this, NoSeekFlag(), options); tit.seek (kbytes); + + return boost::iterator_range (fit, tit); + } + + struct StartsWithIterator: public Iterator { + gstring _starts; + StartsWithIterator (const StartsWithIterator&) = default; + StartsWithIterator (StartsWithIterator&&) = default; + StartsWithIterator& operator= (const StartsWithIterator&) = default; + StartsWithIterator& operator= (StartsWithIterator&&) = default; + + StartsWithIterator (Ldb* ldb, const char* data, uint32_t length, leveldb::ReadOptions options = leveldb::ReadOptions()): + Iterator (ldb, NoSeekFlag(), options), _starts (data, length) { + IteratorEntry* entry = _entry.get(); + entry->_lit->Seek (leveldb::Slice (data, length)); + entry->_valid = checkValidity(); + } + /** End iterator, pointing nowhere. */ + StartsWithIterator (Ldb* ldb, const char* data, uint32_t length, NoSeekFlag, leveldb::ReadOptions options = leveldb::ReadOptions()): + Iterator (ldb, NoSeekFlag(), options), _starts (data, length) {} + + bool checkValidity() const { + IteratorEntry* entry = _entry.get(); + return entry->_lit->Valid() && entry->_lit->key().starts_with (leveldb::Slice (_starts.data(), _starts.length())); + } + virtual void increment() override { + IteratorEntry* entry = _entry.get(); + if (entry->_valid) entry->_lit->Next(); + else entry->_lit->Seek (leveldb::Slice (_starts.data(), _starts.length())); + entry->_valid = checkValidity(); + } + virtual void decrement() override { + IteratorEntry* entry = _entry.get(); + if (entry->_valid) entry->_lit->Prev(); else seekLast(); + entry->_valid = checkValidity(); + } + void seekLast() { + leveldb::Iterator* lit = _entry->_lit; + // Go somewhere *below* the `_starts` prefix. + char after[_starts.length()]; if (sizeof (after)) { + memcpy (after, _starts.data(), sizeof (after)); + uint32_t pos = sizeof (after); while (--pos >= 0) if (after[pos] < CHAR_MAX) {++after[pos]; break;} + if (pos >= 0) {lit->Seek (leveldb::Slice (after, sizeof (after))); if (!lit->Valid()) lit->SeekToLast();} else lit->SeekToLast(); + } else lit->SeekToLast(); + // Seek back until we are in the `_starts` prefix. + for (leveldb::Slice prefix (_starts.data(), _starts.length()); lit->Valid(); lit->Prev()) { + leveldb::Slice key (lit->key()); + if (key.starts_with (prefix)) break; // We're "back" in the `_starts` prefix. + if (key.compare (prefix) < 0) break; // Gone too far (no prefix entries in the db). + } + } + }; + + /** Range over entries starting with `key`. */ + template + boost::iterator_range startsWith (const K& key) { + char kbuf[64]; // Allow up to 64 bytes to be serialized without heap allocations. + gstring kbytes (sizeof (kbuf), kbuf, false, 0); + ldbSerialize (kbytes, key); + return boost::iterator_range ( + StartsWithIterator (this, kbytes.data(), kbytes.length()), + StartsWithIterator (this, kbytes.data(), kbytes.length(), NoSeekFlag())); + } + + struct Trigger { + virtual gstring triggerName() const {return C2GSTRING ("defaultTriggerName");}; + virtual void put (Ldb& ldb, void* key, gstring& kbytes, void* value, gstring& vbytes, leveldb::WriteBatch& batch) = 0; + virtual void del (Ldb& ldb, void* key, gstring& kbytes, leveldb::WriteBatch& batch) = 0; + }; + std::map> _triggers; + + /** Register the trigger (by its `triggerName`). */ + void putTrigger (std::shared_ptr trigger) { + _triggers[trigger->triggerName()] = trigger; + } + + public: + + Ldb() {} + + /** Opens Leveldb database. */ + Ldb (const char* path, leveldb::Options* options = nullptr, mode_t mode = 0770) { + int rc = ::mkdir (path, mode); + if (rc && errno != EEXIST) GNTHROW (LdbEx, std::string ("Can't create ") + path + ": " + ::strerror (errno)); + leveldb::DB* db; + leveldb::Status status; + if (options) { + status = leveldb::DB::Open (*options, path, &db); + } else { + leveldb::Options localOptions; + localOptions.create_if_missing = true; + _filter.reset (leveldb::NewBloomFilterPolicy (8)); + localOptions.filter_policy = _filter.get(); + status = leveldb::DB::Open (localOptions, path, &db); + } + if (!status.ok()) GNTHROW (LdbEx, std::string ("Ldb: Can't open ") + path + ": " + status.ToString()); + _db.reset (db); + } + + /** Wraps an existing Leveldb handler. */ + Ldb (std::shared_ptr db): _db (db) {} + + template void put (const K& key, const V& value, leveldb::WriteBatch& batch) { + char kbuf[64]; // Allow up to 64 bytes to be serialized without heap allocations. + gstring kbytes (sizeof (kbuf), kbuf, false, 0); + ldbSerialize (kbytes, key); + + char vbuf[64]; // Allow up to 64 bytes to be serialized without heap allocations. + gstring vbytes (sizeof (vbuf), vbuf, false, 0); + ldbSerialize (vbytes, value); + + for (auto& trigger: _triggers) trigger.second->put (*this, (void*) &key, kbytes, (void*) &value, vbytes, batch); + + batch.Put (leveldb::Slice (kbytes.data(), kbytes.size()), leveldb::Slice (vbytes.data(), vbytes.size())); + } + template void put (const K& key, const V& value) { + leveldb::WriteBatch batch; + put (key, value, batch); + leveldb::Status status (_db->Write (leveldb::WriteOptions(), &batch)); + if (!status.ok()) GNTHROW (LdbEx, "Ldb: add: " + status.ToString()); + } + + /** Returns `true` if the key exists. Throws on error. */ + template bool have (const K& key, leveldb::ReadOptions options = leveldb::ReadOptions()) { + char kbuf[64]; // Allow up to 64 bytes to be serialized without heap allocations. + gstring kbytes (sizeof (kbuf), kbuf, false, 0); + ldbSerialize (kbytes, key); + leveldb::Slice keySlice (kbytes.data(), kbytes.size()); + + // NB: "BloomFilter only helps for Get() calls" - https://groups.google.com/d/msg/leveldb/oEiDztqHiHc/LMY3tHxzRGAJ + // "Apart from the lack of Bloom filter functionality, creating an iterator is really quite slow" - qpu2jSA8mCEJ + std::string str; + leveldb::Status status (_db->Get (options, keySlice, &str)); + if (status.ok()) return true; + else if (status.IsNotFound()) return false; + else GTHROW ("Ldb.have: " + status.ToString()); + } + + /** Returns `true` and modifies `value` if `key` is found. */ + template bool get (const K& key, V& value, leveldb::ReadOptions options = leveldb::ReadOptions()) { + char kbuf[64]; // Allow up to 64 bytes to be serialized without heap allocations. + gstring kbytes (sizeof (kbuf), kbuf, false, 0); + ldbSerialize (kbytes, key); + leveldb::Slice keySlice (kbytes.data(), kbytes.size()); + + // NB: "BloomFilter only helps for Get() calls" - https://groups.google.com/d/msg/leveldb/oEiDztqHiHc/LMY3tHxzRGAJ + // "Apart from the lack of Bloom filter functionality, creating an iterator is really quite slow" - qpu2jSA8mCEJ + std::string str; + leveldb::Status status (_db->Get (options, keySlice, &str)); + if (status.ok()) { + ldbDeserialize (gstring (0, (void*) str.data(), false, str.size()), value); + return true; + } else if (status.IsNotFound()) return false; + else GTHROW ("Ldb.get: " + status.ToString()); + } + + template void del (const K& key, leveldb::WriteBatch& batch) { + char kbuf[64]; // Allow up to 64 bytes to be serialized without heap allocations. + gstring kbytes (sizeof (kbuf), kbuf, false, 0); + ldbSerialize (kbytes, key); + if (kbytes.empty()) GNTHROW (LdbEx, "del: key is empty"); + + for (auto& trigger: _triggers) trigger.second->del (*this, (void*) &key, kbytes, batch); + + batch.Delete (leveldb::Slice (kbytes.data(), kbytes.size())); + } + template void del (const K& key) { + leveldb::WriteBatch batch; + del (key, batch); + leveldb::Status status (_db->Write (leveldb::WriteOptions(), &batch)); + if (!status.ok()) GNTHROW (LdbEx, "Ldb: del: " + status.ToString()); + } + + /** Writes the batch. Throws LdbEx if not successfull. */ + void write (leveldb::WriteBatch& batch, leveldb::WriteOptions options = leveldb::WriteOptions()) { + leveldb::Status status (_db->Write (options, &batch)); + if (!status.ok()) GNTHROW (LdbEx, status.ToString()); + } + + virtual ~Ldb() { + _triggers.clear(); // Destroy triggers before closing the database. + } +}; + +} // namespace glim + +#endif // _GLIM_LDB_HPP_INCLUDED diff --git a/external/glim/makefile b/external/glim/makefile new file mode 100644 index 00000000..88e240ac --- /dev/null +++ b/external/glim/makefile @@ -0,0 +1,93 @@ + +PREFIX = /usr/local +INSTALL2 = ${PREFIX}/include/glim +CXXFLAGS = -std=c++1y -Wall -O2 -ggdb -DBOOST_ALL_DYN_LINK + +all: test + +help: + @echo "make test\nmake install\nmake uninstall\nmake clean" + +doc: doxyconf *.hpp + mkdir -p doc + doxygen doxyconf + +test: test_sqlite test_gstring test_runner test_exception test_ldb + +test_sqlite: bin/test_sqlite + cp bin/test_sqlite /tmp/libglim_test_sqlite && chmod +x /tmp/libglim_test_sqlite && /tmp/libglim_test_sqlite && rm -f /tmp/libglim_test_sqlite + +bin/test_sqlite: test_sqlite.cc + mkdir -p bin + g++ $(CXXFLAGS) test_sqlite.cc -o bin/test_sqlite -lsqlite3 + +test_memcache: bin/test_memcache + cp bin/test_memcache /tmp/libglim_test_memcache && chmod +x /tmp/libglim_test_memcache && /tmp/libglim_test_memcache && rm -f /tmp/libglim_test_memcache + +bin/test_memcache: test_memcache.cc memcache.hpp + mkdir -p bin + g++ $(CXXFLAGS) test_memcache.cc -o bin/test_memcache -lmemcache + +bin/test_gstring: test_gstring.cc gstring.hpp + mkdir -p bin + g++ $(CXXFLAGS) test_gstring.cc -o bin/test_gstring + +test_gstring: bin/test_gstring + cp bin/test_gstring /tmp/libglim_test_gstring + chmod +x /tmp/libglim_test_gstring + /tmp/libglim_test_gstring + rm -f /tmp/libglim_test_gstring + +bin/test_runner: test_runner.cc runner.hpp curl.hpp + mkdir -p bin + g++ $(CXXFLAGS) test_runner.cc -o bin/test_runner -pthread -lboost_log -levent -levent_pthreads -lcurl + +test_runner: bin/test_runner + valgrind -q bin/test_runner + +bin/test_exception: test_exception.cc exception.hpp + mkdir -p bin + g++ $(CXXFLAGS) test_exception.cc -o bin/test_exception -ldl -rdynamic + +test_exception: bin/test_exception + valgrind -q bin/test_exception + +test_ldb: test_ldb.cc ldb.hpp + mkdir -p bin + g++ $(CXXFLAGS) test_ldb.cc -o bin/test_ldb \ + -lleveldb -lboost_serialization -lboost_filesystem -lboost_system + valgrind -q bin/test_ldb + +bin/test_cbcoro: test_cbcoro.cc + mkdir -p bin + g++ $(CXXFLAGS) test_cbcoro.cc -o bin/test_cbcoro -pthread + +test_cbcoro: bin/test_cbcoro + bin/test_cbcoro + +install: + mkdir -p ${INSTALL2}/ + cp sqlite.hpp ${INSTALL2}/ + cp NsecTimer.hpp ${INSTALL2}/ + cp TscTimer.hpp ${INSTALL2}/ + cp memcache.hpp ${INSTALL2}/ + cp gstring.hpp ${INSTALL2}/ + cp runner.hpp ${INSTALL2}/ + cp hget.hpp ${INSTALL2}/ + cp curl.hpp ${INSTALL2}/ + cp mdb.hpp ${INSTALL2}/ + cp ldb.hpp ${INSTALL2}/ + cp exception.hpp ${INSTALL2}/ + cp SerializablePool.hpp ${INSTALL2}/ + cp cbcoro.hpp ${INSTALL2}/ + cp raii.hpp ${INSTALL2}/ + cp channel.hpp ${INSTALL2}/ + +uninstall: + rm -rf ${INSTALL2} + +clean: + rm -rf bin/* + rm -rf doc + rm -f /tmp/libglim_test_* + rm -f *.exe.stackdump diff --git a/external/glim/mdb.hpp b/external/glim/mdb.hpp new file mode 100644 index 00000000..eca66f37 --- /dev/null +++ b/external/glim/mdb.hpp @@ -0,0 +1,499 @@ +#ifndef _GLIM_MDB_HPP_INCLUDED +#define _GLIM_MDB_HPP_INCLUDED + +/** + * A C++ wrapper around MDB (http://www.symas.com/mdb/). + * @code +Copyright 2012 Kozarezov Artem Aleksandrovich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * @endcode + * @file + */ + +#include +#include +#include +#include +#include +#include +#include + +#include // htonl, ntohl + +#include "gstring.hpp" + +namespace glim { + +struct MdbEx: public std::runtime_error {MdbEx (std::string message): std::runtime_error (message) {}}; + +template inline void mdbSerialize (gstring& bytes, const T& data) { + gstring_stream stream (bytes); + boost::archive::binary_oarchive oa (stream, boost::archive::no_header); + oa << data; +} +template inline void mdbDeserialize (const gstring& bytes, V& data) { + gstring_stream stream (const_cast (bytes)); + boost::archive::binary_iarchive ia (stream, boost::archive::no_header); + ia >> data; +} + +/** uint32_t keys are stored big-endian (network byte order) in order to be compatible with lexicographic ordering. */ +template <> inline void mdbSerialize (gstring& bytes, const uint32_t& ui) { + uint32_t nui = htonl (ui); bytes.append ((const char*) &nui, sizeof (uint32_t));} +/** Deserialize uint32_t from big-endian (network byte order). */ +template <> inline void mdbDeserialize (const gstring& bytes, uint32_t& ui) { + if (bytes.size() != sizeof (uint32_t)) throw MdbEx ("Not uint32_t, wrong number of bytes"); + uint32_t nui = * (uint32_t*) bytes.data(); ui = ntohl (nui);} + +/** If the data is `gstring` then use the data's buffer directly, no copy. */ +template <> inline void mdbSerialize (gstring& bytes, const gstring& data) { + bytes = gstring (0, (void*) data.data(), false, data.length());} +/** Deserializing into `gstring` copies the bytes into it, reusing its buffer. */ +template <> inline void mdbDeserialize (const gstring& bytes, gstring& data) { + data.clear() << bytes;} + +/** + * Header-only C++ wrapper around OpenLDAP-MDB.\n + * Uses Boost Serialization to pack keys and values (glim::gstring can be used for raw bytes).\n + * Allows semi-automatic indexing with triggers.\n + * Known issues: http://www.openldap.org/its/index.cgi?findid=7448 + */ +struct Mdb { + std::shared_ptr _env; + MDB_dbi _dbi = 0; + + typedef std::unique_ptr Transaction; + + /** Holds the current key and value of the Iterator. */ + struct IteratorEntry { + MDB_val _key = {0, 0}, _val = {0, 0}; + /** Zero-copy view of the current key bytes. Should *not* be used after the Iterator is changed or destroyed. */ + const gstring keyView() const {return gstring (0, _key.mv_data, false, _key.mv_size, true);} // Zero copy. + /** Zero-copy view of the current value bytes. Should *not* be used after the Iterator is changed or destroyed. */ + const gstring valueView() const {return gstring (0, _val.mv_data, false, _val.mv_size, true);} // Zero copy. + /** Deserialize into `key`. */ + template void getKey (T& key) const {mdbDeserialize (keyView(), key);} + /** Deserialize the key into a temporary and return it. */ + template T getKey() const {T key; getKey (key); return key;} + /** Deserialize into `value`. */ + template void getValue (T& value) const {mdbDeserialize (valueView(), value);} + /** Deserialize the value into a temporary and return it. */ + template T getValue() const {T value; getValue (value); return value;} + }; + /** Holds the Iterator's unique transaction and cursor, allowing the Iterator to be copied. */ + struct IteratorImpl: boost::noncopyable { + Mdb* _mdb; + Transaction _txn; + MDB_cursor* _cur; + IteratorImpl (Mdb* mdb, Transaction&& txn, MDB_cursor* cur): _mdb (mdb), _txn (std::move (txn)), _cur (cur) {} + ~IteratorImpl() { + if (_cur) {::mdb_cursor_close (_cur); _cur = nullptr;} + if (_mdb && _txn) {_mdb->commitTransaction (_txn); _mdb = nullptr;} + } + }; + /** Wraps MDB cursor and cursor's transaction. */ + struct Iterator: public boost::iterator_facade { + std::shared_ptr _impl; // Iterator might be copied around, thus we keep the unique things in IteratorImpl. + IteratorEntry _entry; + bool _stayInKey = false; + + Iterator (const Iterator&) = default; + Iterator (Iterator&&) = default; + /** Iterate from the beginning or the end of the database. + * @param position can be MDB_FIRST or MDB_LAST */ + Iterator (Mdb* mdb, int position = 0): _stayInKey (false) { + Transaction txn (mdb->beginTransaction()); + MDB_cursor* cur = nullptr; int rc = ::mdb_cursor_open (txn.get(), mdb->_dbi, &cur); + if (rc) throw MdbEx ("mdb_cursor_open"); + _impl = std::make_shared (mdb, std::move (txn), cur); + if (position == ::MDB_FIRST || position == ::MDB_LAST) { + rc = ::mdb_cursor_get (cur, &_entry._key, &_entry._val, (MDB_cursor_op) position); + if (rc) throw MdbEx ("mdb_cursor_get"); + } + } + /** Iterate over `key` values. + * @param stayInKey if `false` then iterator can go farther than the `key`. */ + Iterator (Mdb* mdb, const gstring& key, bool stayInKey = true): _stayInKey (stayInKey) { + Transaction txn (mdb->beginTransaction()); + MDB_cursor* cur = nullptr; int rc = ::mdb_cursor_open (txn.get(), mdb->_dbi, &cur); + if (rc) throw MdbEx ("mdb_cursor_open"); + _impl = std::make_shared (mdb, std::move (txn), cur); + _entry._key = {key.size(), (void*) key.data()}; + rc = ::mdb_cursor_get (cur, &_entry._key, &_entry._val, ::MDB_SET_KEY); + if (rc == MDB_NOTFOUND) {_entry._key = {0, 0}; _entry._val = {0, 0};} + else if (rc) throw MdbEx ("mdb_cursor_get"); + } + + struct EndIteratorFlag {}; + /** The "end" iterator does not open an MDB transaction (this is essential for having a pair of iterators without a deadlock). */ + Iterator (EndIteratorFlag): _stayInKey (false) {} + /** True if the iterator isn't pointing anywhere. */ + bool end() const {return _entry._key.mv_size == 0;} + + bool equal (const Iterator& other) const { + IteratorImpl* impl = _impl.get(); + if (mdb_cmp (impl->_txn.get(), impl->_mdb->_dbi, &_entry._key, &other._entry._key)) return false; + if (mdb_dcmp (impl->_txn.get(), impl->_mdb->_dbi, &_entry._val, &other._entry._val)) return false; + return true; + } + IteratorEntry& dereference() const { + // NB: Boost iterator_facade expects the `dereference` to be a `const` method. + // I guess Iterator is not modified, so the `dereference` is `const`, even though the Entry can be modified. + return const_cast (_entry);} + void increment() { + int rc = ::mdb_cursor_get (_impl->_cur, &_entry._key, &_entry._val, _stayInKey ? ::MDB_NEXT_DUP : ::MDB_NEXT); + if (rc) {_entry._key = {0,0}; _entry._val = {0,0};} + } + void decrement() { + int rc = ::mdb_cursor_get (_impl->_cur, &_entry._key, &_entry._val, _stayInKey ? ::MDB_PREV_DUP : ::MDB_PREV); + if (rc) {_entry._key = {0,0}; _entry._val = {0,0};} + } + }; + Iterator begin() {return Iterator (this, ::MDB_FIRST);} + const Iterator end() {return Iterator (Iterator::EndIteratorFlag());} + /** Position the cursor at the first `key` record.\n + * The iterator increment will use `MDB_NEXT_DUP`, staying withing the `key`.\n + * See also the `all` method. */ + template Iterator values (const K& key) { + char kbuf[64]; // Allow up to 64 bytes to be serialized without heap allocations. + gstring kbytes (sizeof (kbuf), kbuf, false, 0); + mdbSerialize (kbytes, key); + return Iterator (this, kbytes); + } + /** Range over the `key` values.\n + * See also the `all` method. */ + template boost::iterator_range valuesRange (const K& key) {return boost::iterator_range (values (key), end());} + + struct Trigger { + virtual gstring getTriggerName() const {return C2GSTRING ("defaultTriggerName");}; + virtual void add (Mdb& mdb, void* key, gstring& kbytes, void* value, gstring& vbytes, Transaction& txn) = 0; + virtual void erase (Mdb& mdb, void* key, gstring& kbytes, Transaction& txn) = 0; + virtual void eraseKV (Mdb& mdb, void* key, gstring& kbytes, void* value, gstring& vbytes, Transaction& txn) = 0; + }; + std::map> _triggers; + + void setTrigger (std::shared_ptr trigger) { + _triggers[trigger->getTriggerName()] = trigger; + } + + /** `flags` can be `MDB_RDONLY` */ + Transaction beginTransaction (unsigned flags = 0) { + MDB_txn* txn = 0; int rc = ::mdb_txn_begin (_env.get(), nullptr, flags, &txn); + if (rc) throw MdbEx (std::string ("mdb_txn_begin: ") + ::strerror (rc)); + return Transaction (txn, ::mdb_txn_abort); + } + void commitTransaction (Transaction& txn) { + int rc = ::mdb_txn_commit (txn.get()); + txn.release(); // Must prevent `mdb_txn_abort` from happening (even if rc != 0). + if (rc) throw MdbEx (std::string ("mdb_txn_commit: ") + ::strerror (rc)); + } + + virtual unsigned envFlags (uint8_t sync) { + unsigned flags = MDB_NOSUBDIR; + if (sync < 1) flags |= MDB_NOSYNC; else if (sync < 2) flags |= MDB_NOMETASYNC; + return flags; + } + /** Used before `mdb_env_open`. By default sets the number of database to 32. */ + virtual void envConf (MDB_env* env) { + int rc = ::mdb_env_set_maxdbs (env, 32); + if (rc) throw MdbEx (std::string ("envConf: ") + ::strerror (rc)); + } + virtual void dbFlags (unsigned& flags) {} + + protected: + void open (const char* dbName, bool dup) { + auto txn = beginTransaction(); + unsigned flags = MDB_CREATE; + if (dup) flags |= MDB_DUPSORT; + dbFlags (flags); + int rc = ::mdb_open (txn.get(), dbName, flags, &_dbi); + if (rc) throw MdbEx (std::string ("mdb_open (") + dbName + "): " + ::strerror (rc)); + commitTransaction (txn); + } + public: + + /** Opens MDB environment and MDB database. */ + Mdb (const char* path, size_t maxSizeMb = 1024, const char* dbName = "main", uint8_t sync = 0, bool dup = true, mode_t mode = 0660) { + MDB_env* env = 0; int rc = ::mdb_env_create (&env); + if (rc) throw MdbEx (std::string ("mdb_env_create: ") + ::strerror (rc)); + _env.reset (env, ::mdb_env_close); + rc = ::mdb_env_set_mapsize (env, maxSizeMb * 1024 * 1024); + if (rc) throw MdbEx (std::string ("mdb_env_set_mapsize: ") + ::strerror (rc)); + envConf (env); + rc = ::mdb_env_open (env, path, envFlags (sync), mode); + _dbi = 0; open (dbName, dup); + } + + /** Opens MDB database in the provided environment. */ + Mdb (std::shared_ptr env, const char* dbName, bool dup = true): _env (env), _dbi (0) { + open (dbName, dup); + } + + template void add (const K& key, const V& value, Transaction& txn) { + char kbuf[64]; // Allow up to 64 bytes to be serialized without heap allocations. + gstring kbytes (sizeof (kbuf), kbuf, false, 0); + mdbSerialize (kbytes, key); + MDB_val mkey = {kbytes.size(), (void*) kbytes.data()}; + + char vbuf[64]; // Allow up to 64 bytes to be serialized without heap allocations. + gstring vbytes (sizeof (vbuf), vbuf, false, 0); + mdbSerialize (vbytes, value); + MDB_val mvalue = {vbytes.size(), (void*) vbytes.data()}; + + for (auto& trigger: _triggers) trigger.second->add (*this, (void*) &key, kbytes, (void*) &value, vbytes, txn); + + int rc = ::mdb_put (txn.get(), _dbi, &mkey, &mvalue, 0); + if (rc) throw MdbEx (std::string ("mdb_put: ") + ::strerror (rc)); + } + template void add (const K& key, const V& value) { + Transaction txn (beginTransaction()); + add (key, value, txn); + commitTransaction (txn); + } + + template bool first (const K& key, V& value, Transaction& txn) { + char kbuf[64]; // Allow up to 64 bytes to be serialized without heap allocations. + gstring kbytes (sizeof (kbuf), kbuf, false, 0); + mdbSerialize (kbytes, key); + MDB_val mkey = {kbytes.size(), (void*) kbytes.data()}; + MDB_val mvalue; + int rc = ::mdb_get (txn.get(), _dbi, &mkey, &mvalue); + if (rc == MDB_NOTFOUND) return false; + if (rc) throw MdbEx (std::string ("mdb_get: ") + ::strerror (rc)); + gstring vstr (0, mvalue.mv_data, false, mvalue.mv_size); + mdbDeserialize (vstr, value); + return true; + } + template bool first (const K& key, V& value) { + Transaction txn (beginTransaction (MDB_RDONLY)); + bool rb = first (key, value, txn); + commitTransaction (txn); + return rb; + } + + /** Iterate over `key` values until `visitor` returns `false`. Return the number of values visited. */ + template int32_t all (const K& key, std::function visitor, Transaction& txn) { + char kbuf[64]; // Allow up to 64 bytes to be serialized without heap allocations. + gstring kbytes (sizeof (kbuf), kbuf, false, 0); + mdbSerialize (kbytes, key); + MDB_val mkey = {kbytes.size(), (void*) kbytes.data()}; + + MDB_cursor* cur = 0; int rc = ::mdb_cursor_open (txn.get(), _dbi, &cur); + if (rc) throw MdbEx (std::string ("mdb_cursor_open: ") + ::strerror (rc)); + std::unique_ptr curHolder (cur, ::mdb_cursor_close); + MDB_val mval = {0, 0}; + rc = ::mdb_cursor_get (cur, &mkey, &mval, ::MDB_SET_KEY); if (rc == MDB_NOTFOUND) return 0; + if (rc) throw MdbEx (std::string ("mdb_cursor_get: ") + ::strerror (rc)); + + V value; + gstring vstr (0, mval.mv_data, false, mval.mv_size); + mdbDeserialize (vstr, value); + bool goOn = visitor (value); + int32_t count = 1; + + while (goOn) { + rc = ::mdb_cursor_get (cur, &mkey, &mval, ::MDB_NEXT_DUP); if (rc == MDB_NOTFOUND) return count; + if (rc) throw MdbEx (std::string ("mdb_cursor_get: ") + ::strerror (rc)); + gstring vstr (0, mval.mv_data, false, mval.mv_size); + mdbDeserialize (vstr, value); + goOn = visitor (value); + ++count; + } + return count; + } + /** Iterate over `key` values until `visitor` returns `false`. Return the number of values visited. */ + template int32_t all (const K& key, std::function visitor) { + Transaction txn (beginTransaction (MDB_RDONLY)); + int32_t count = all (key, visitor, txn); + commitTransaction (txn); + return count; + } + + template bool eraseKV (const K& key, const V& value, Transaction& txn) { + char kbuf[64]; // Allow up to 64 bytes to be serialized without heap allocations. + gstring kbytes (sizeof (kbuf), kbuf, false, 0); + mdbSerialize (kbytes, key); + if (kbytes.empty()) throw MdbEx ("eraseKV: key is empty"); + MDB_val mkey = {kbytes.size(), (void*) kbytes.data()}; + + char vbuf[64]; // Allow up to 64 bytes to be serialized without heap allocations. + gstring vbytes (sizeof (vbuf), vbuf, false, 0); + mdbSerialize (vbytes, value); + MDB_val mvalue = {vbytes.size(), (void*) vbytes.data()}; + + for (auto& trigger: _triggers) trigger.second->eraseKV (*this, (void*) &key, kbytes, (void*) &value, vbytes, txn); + + int rc = ::mdb_del (txn.get(), _dbi, &mkey, &mvalue); + if (rc == MDB_NOTFOUND) return false; + if (rc) throw MdbEx (std::string ("mdb_del: ") + ::strerror (rc)); + return true; + } + template bool eraseKV (const K& key, const V& value) { + Transaction txn (beginTransaction()); + bool rb = eraseKV (key, value, txn); + commitTransaction (txn); + return rb; + } + + /** Erase all values of the `key`. */ + template bool erase (const K& key, Transaction& txn) { + char kbuf[64]; // Allow up to 64 bytes to be serialized without heap allocations. + gstring kbytes (sizeof (kbuf), kbuf, false, 0); + mdbSerialize (kbytes, key); + if (kbytes.empty()) throw MdbEx ("erase: key is empty"); + MDB_val mkey = {kbytes.size(), (void*) kbytes.data()}; + + for (auto& trigger: _triggers) trigger.second->erase (*this, (void*) &key, kbytes, txn); + + int rc = ::mdb_del (txn.get(), _dbi, &mkey, nullptr); + if (rc == MDB_NOTFOUND) return false; + if (rc) throw MdbEx (std::string ("mdb_del: ") + ::strerror (rc)); + return true; + } + /** Erase all values of the `key`. */ + template bool erase (const K& key) { + Transaction txn (beginTransaction()); + bool rb = erase (key, txn); + commitTransaction (txn); + return rb; + } + + static void test (Mdb& mdb) { + mdb.add (std::string ("foo"), std::string ("bar")); + // NB: "MDB_DUPSORT doesn't allow duplicate duplicates" (Howard Chu) + mdb.add (std::string ("foo"), std::string ("bar")); + mdb.add ((uint32_t) 123, 1); + mdb.add ((uint32_t) 123, 2); + mdb.add (C2GSTRING ("foo"), 3); + mdb.add (C2GSTRING ("foo"), 4); + mdb.add (C2GSTRING ("gsk"), C2GSTRING ("gsv")); + string ts; int ti; gstring tgs; + auto fail = [](string msg) {throw std::runtime_error ("assertion failed: " + msg);}; + if (!mdb.first (std::string ("foo"), ts) || ts != "bar") fail ("!foo=bar"); + if (!mdb.first ((uint32_t) 123, ti) || ti < 1 || ti > 2) fail ("!123"); + if (!mdb.first (C2GSTRING ("foo"), ti) || ti < 3 || ti > 4) fail ("!foo=3,4"); + if (!mdb.first (C2GSTRING ("gsk"), tgs) || tgs != "gsv") fail ("!gsk=gsv"); + + // Test range-based for. + int count = 0; bool haveGskGsv = false; + for (auto&& entry: mdb) { + if (!entry._key.mv_size || !entry._val.mv_size) fail ("!entry"); + if (entry.keyView() == "gsk") { + if (entry.getKey() != "gsk") fail ("getKey(gsk)!=gsk"); + if (entry.getValue() != "gsv") fail ("getValue(gsk)!=gsv"); + haveGskGsv = true; + } + ++count;} + if (count != 6) fail ("count!=6"); // foo=bar, 123=1, 123=2, foo=3, foo=4, gsk=gsv + if (!haveGskGsv) fail ("!haveGskGsv"); + + // Test `values`. + count = 0; int sum = 0; + for (auto&& entry: mdb.valuesRange ((uint32_t) 123)) { + if (entry.getKey() != (uint32_t) 123) fail("values(123).key!=123"); + ++count; sum += entry.getValue(); + } + if (count != 2) fail ("count(123)!=2"); + if (sum != 3) fail ("sum(123)!=3"); + + if (!mdb.eraseKV ((uint32_t) 123, 1)) fail ("!eraseKV(123,1)"); + if (!mdb.first ((uint32_t) 123, ti) || ti != 2) fail ("!123=2"); + if (!mdb.eraseKV ((uint32_t) 123, 2)) fail ("!eraseKV(123,2)"); + if (mdb.first ((uint32_t) 123, ti)) fail ("123"); + + if (!mdb.erase (C2GSTRING ("foo"))) fail ("!erase(g(foo))"); + if (mdb.first (C2GSTRING ("foo"), ti)) fail ("foo"); + if (!mdb.erase (std::string ("foo"))) fail ("!erase(str(foo))"); + + { // We've erased "123" and "foo", the only key left is "gsk" (gsk=gsv), let's test the iterator boundaries on this small dataset. + auto&& it = mdb.begin(); + if (it->getKey() != "gsk") fail ("first key !gsk " + it->keyView().str()); + if (!(++it).end()) fail ("++it != end"); + if ((--it)->getKey() != "gsk") fail ("can't go back to gsk"); + if (!(--it).end()) fail ("--it != end"); + if ((++it)->getKey() != "gsk") fail ("can't go forward to gsk"); + } + + struct SimpleIndexTrigger: public Trigger { + const char* _name; Mdb _indexDb; + SimpleIndexTrigger (Mdb& mdb, const char* name = "index"): _name (name), _indexDb (mdb._env, name) {} + gstring getTriggerName() {return gstring (0, (void*) _name, false, strlen (_name), true);} + void add (Mdb& mdb, void* key, gstring& kbytes, void* value, gstring& vbytes, Transaction& txn) { + MDB_val mkey = {vbytes.size(), (void*) vbytes.data()}; + MDB_val mvalue = {kbytes.size(), (void*) kbytes.data()}; + int rc = ::mdb_put (txn.get(), _indexDb._dbi, &mkey, &mvalue, 0); + if (rc) throw MdbEx (std::string ("index, mdb_put: ") + ::strerror (rc)); + } + void erase (Mdb& mdb, void* ekey, gstring& kbytes, Transaction& txn) { + // Get all the values and remove them from the index. + MDB_cursor* cur = 0; int rc = ::mdb_cursor_open (txn.get(), mdb._dbi, &cur); + if (rc) throw MdbEx (std::string ("index, erase, mdb_cursor_open: ") + ::strerror (rc)); + std::unique_ptr curHolder (cur, ::mdb_cursor_close); + MDB_val mkey = {kbytes.size(), (void*) kbytes.data()}, val = {0, 0}; + rc = ::mdb_cursor_get (cur, &mkey, &val, ::MDB_SET_KEY); if (rc == MDB_NOTFOUND) return; + if (rc) throw MdbEx (std::string ("index, erase, mdb_cursor_get: ") + ::strerror (rc)); + rc = ::mdb_del (txn.get(), _indexDb._dbi, &val, &mkey); + if (rc && rc != MDB_NOTFOUND) throw MdbEx (std::string ("index, erase, mdb_del: ") + ::strerror (rc)); + for (;;) { + rc = ::mdb_cursor_get (cur, &mkey, &val, ::MDB_NEXT_DUP); if (rc == MDB_NOTFOUND) return; + if (rc) throw MdbEx (std::string ("index, erase, mdb_cursor_get: ") + ::strerror (rc)); + rc = ::mdb_del (txn.get(), _indexDb._dbi, &val, &mkey); + if (rc && rc != MDB_NOTFOUND) throw MdbEx (std::string ("index, erase, mdb_del: ") + ::strerror (rc)); + } + } + void eraseKV (Mdb& mdb, void* key, gstring& kbytes, void* value, gstring& vbytes, Transaction& txn) { + MDB_val mkey = {vbytes.size(), (void*) vbytes.data()}; + MDB_val mvalue = {kbytes.size(), (void*) kbytes.data()}; + int rc = ::mdb_del (txn.get(), _indexDb._dbi, &mkey, &mvalue); + if (rc && rc != MDB_NOTFOUND) throw MdbEx (std::string ("index, mdb_del: ") + ::strerror (rc)); + } + }; + auto indexTrigger = std::make_shared (mdb); mdb.setTrigger (indexTrigger); auto& indexDb = indexTrigger->_indexDb; + mdb.erase (C2GSTRING ("gsk")); // NB: "gsk" wasn't indexed here. `IndexTrigger.erase` should handle this gracefully. + + // Add indexed. + mdb.add (C2GSTRING ("ik"), C2GSTRING ("iv1")); + mdb.add (C2GSTRING ("ik"), string ("iv2")); + mdb.add (C2GSTRING ("ik"), 3); + // Check the index. + gstring ik; + if (!indexDb.first (C2GSTRING ("iv1"), ik) || ik != "ik") fail ("!iv1=ik"); + if (!indexDb.first (string ("iv2"), ik) || ik != "ik") fail ("!iv2=ik"); + if (!indexDb.first (3, ik) || ik != "ik") fail ("!iv3=ik"); + + // Remove indexed. + mdb.eraseKV (C2GSTRING ("ik"), string ("iv2")); + // Check the index. + if (!indexDb.first (C2GSTRING ("iv1"), ik) || ik != "ik") fail ("!iv1=ik"); + if (indexDb.first (string ("iv2"), ik)) fail ("iv2=ik"); + if (!indexDb.first (3, ik) || ik != "ik") fail ("!iv3=ik"); + + // Remove indexed. + mdb.erase (C2GSTRING ("ik")); + // Check the index. + if (indexDb.first (C2GSTRING ("iv1"), ik)) fail ("iv1"); + if (indexDb.first (3, ik)) fail ("iv3"); + // Check the data. + if (mdb.first (C2GSTRING ("ik"), ik)) fail ("ik"); + } + + virtual ~Mdb() { + _triggers.clear(); // Destroy triggers before closing the database. + if (_dbi) {::mdb_close (_env.get(), _dbi); _dbi = 0;} + } +}; + +} // namespace glim + +#endif // _GLIM_MDB_HPP_INCLUDED diff --git a/external/glim/ql2.pb.cc b/external/glim/ql2.pb.cc new file mode 100644 index 00000000..42e6e466 --- /dev/null +++ b/external/glim/ql2.pb.cc @@ -0,0 +1,3741 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! + +#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION +#include "ql2.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace { + +const ::google::protobuf::Descriptor* VersionDummy_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + VersionDummy_reflection_ = NULL; +const ::google::protobuf::EnumDescriptor* VersionDummy_Version_descriptor_ = NULL; +const ::google::protobuf::Descriptor* Query_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + Query_reflection_ = NULL; +const ::google::protobuf::Descriptor* Query_AssocPair_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + Query_AssocPair_reflection_ = NULL; +const ::google::protobuf::EnumDescriptor* Query_QueryType_descriptor_ = NULL; +const ::google::protobuf::Descriptor* Frame_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + Frame_reflection_ = NULL; +const ::google::protobuf::EnumDescriptor* Frame_FrameType_descriptor_ = NULL; +const ::google::protobuf::Descriptor* Backtrace_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + Backtrace_reflection_ = NULL; +const ::google::protobuf::Descriptor* Response_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + Response_reflection_ = NULL; +const ::google::protobuf::EnumDescriptor* Response_ResponseType_descriptor_ = NULL; +const ::google::protobuf::Descriptor* Datum_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + Datum_reflection_ = NULL; +const ::google::protobuf::Descriptor* Datum_AssocPair_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + Datum_AssocPair_reflection_ = NULL; +const ::google::protobuf::EnumDescriptor* Datum_DatumType_descriptor_ = NULL; +const ::google::protobuf::Descriptor* Term_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + Term_reflection_ = NULL; +const ::google::protobuf::Descriptor* Term_AssocPair_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + Term_AssocPair_reflection_ = NULL; +const ::google::protobuf::EnumDescriptor* Term_TermType_descriptor_ = NULL; + +} // namespace + + +void protobuf_AssignDesc_ql2_2eproto() { + protobuf_AddDesc_ql2_2eproto(); + const ::google::protobuf::FileDescriptor* file = + ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName( + "ql2.proto"); + GOOGLE_CHECK(file != NULL); + VersionDummy_descriptor_ = file->message_type(0); + static const int VersionDummy_offsets_[1] = { + }; + VersionDummy_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + VersionDummy_descriptor_, + VersionDummy::default_instance_, + VersionDummy_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(VersionDummy, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(VersionDummy, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(VersionDummy)); + VersionDummy_Version_descriptor_ = VersionDummy_descriptor_->enum_type(0); + Query_descriptor_ = file->message_type(1); + static const int Query_offsets_[5] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Query, type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Query, query_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Query, token_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Query, obsolete_noreply_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Query, global_optargs_), + }; + Query_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + Query_descriptor_, + Query::default_instance_, + Query_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Query, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Query, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(Query)); + Query_AssocPair_descriptor_ = Query_descriptor_->nested_type(0); + static const int Query_AssocPair_offsets_[2] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Query_AssocPair, key_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Query_AssocPair, val_), + }; + Query_AssocPair_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + Query_AssocPair_descriptor_, + Query_AssocPair::default_instance_, + Query_AssocPair_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Query_AssocPair, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Query_AssocPair, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(Query_AssocPair)); + Query_QueryType_descriptor_ = Query_descriptor_->enum_type(0); + Frame_descriptor_ = file->message_type(2); + static const int Frame_offsets_[3] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Frame, type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Frame, pos_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Frame, opt_), + }; + Frame_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + Frame_descriptor_, + Frame::default_instance_, + Frame_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Frame, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Frame, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(Frame)); + Frame_FrameType_descriptor_ = Frame_descriptor_->enum_type(0); + Backtrace_descriptor_ = file->message_type(3); + static const int Backtrace_offsets_[1] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Backtrace, frames_), + }; + Backtrace_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + Backtrace_descriptor_, + Backtrace::default_instance_, + Backtrace_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Backtrace, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Backtrace, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(Backtrace)); + Response_descriptor_ = file->message_type(4); + static const int Response_offsets_[4] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Response, type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Response, token_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Response, response_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Response, backtrace_), + }; + Response_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + Response_descriptor_, + Response::default_instance_, + Response_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Response, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Response, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(Response)); + Response_ResponseType_descriptor_ = Response_descriptor_->enum_type(0); + Datum_descriptor_ = file->message_type(5); + static const int Datum_offsets_[6] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Datum, type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Datum, r_bool_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Datum, r_num_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Datum, r_str_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Datum, r_array_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Datum, r_object_), + }; + Datum_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + Datum_descriptor_, + Datum::default_instance_, + Datum_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Datum, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Datum, _unknown_fields_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Datum, _extensions_), + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(Datum)); + Datum_AssocPair_descriptor_ = Datum_descriptor_->nested_type(0); + static const int Datum_AssocPair_offsets_[2] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Datum_AssocPair, key_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Datum_AssocPair, val_), + }; + Datum_AssocPair_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + Datum_AssocPair_descriptor_, + Datum_AssocPair::default_instance_, + Datum_AssocPair_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Datum_AssocPair, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Datum_AssocPair, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(Datum_AssocPair)); + Datum_DatumType_descriptor_ = Datum_descriptor_->enum_type(0); + Term_descriptor_ = file->message_type(6); + static const int Term_offsets_[4] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Term, type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Term, datum_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Term, args_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Term, optargs_), + }; + Term_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + Term_descriptor_, + Term::default_instance_, + Term_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Term, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Term, _unknown_fields_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Term, _extensions_), + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(Term)); + Term_AssocPair_descriptor_ = Term_descriptor_->nested_type(0); + static const int Term_AssocPair_offsets_[2] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Term_AssocPair, key_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Term_AssocPair, val_), + }; + Term_AssocPair_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + Term_AssocPair_descriptor_, + Term_AssocPair::default_instance_, + Term_AssocPair_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Term_AssocPair, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Term_AssocPair, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(Term_AssocPair)); + Term_TermType_descriptor_ = Term_descriptor_->enum_type(0); +} + +namespace { + +GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_); +inline void protobuf_AssignDescriptorsOnce() { + ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_, + &protobuf_AssignDesc_ql2_2eproto); +} + +void protobuf_RegisterTypes(const ::std::string&) { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + VersionDummy_descriptor_, &VersionDummy::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + Query_descriptor_, &Query::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + Query_AssocPair_descriptor_, &Query_AssocPair::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + Frame_descriptor_, &Frame::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + Backtrace_descriptor_, &Backtrace::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + Response_descriptor_, &Response::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + Datum_descriptor_, &Datum::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + Datum_AssocPair_descriptor_, &Datum_AssocPair::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + Term_descriptor_, &Term::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + Term_AssocPair_descriptor_, &Term_AssocPair::default_instance()); +} + +} // namespace + +void protobuf_ShutdownFile_ql2_2eproto() { + delete VersionDummy::default_instance_; + delete VersionDummy_reflection_; + delete Query::default_instance_; + delete Query_reflection_; + delete Query_AssocPair::default_instance_; + delete Query_AssocPair_reflection_; + delete Frame::default_instance_; + delete Frame_reflection_; + delete Backtrace::default_instance_; + delete Backtrace_reflection_; + delete Response::default_instance_; + delete Response_reflection_; + delete Datum::default_instance_; + delete Datum_reflection_; + delete Datum_AssocPair::default_instance_; + delete Datum_AssocPair_reflection_; + delete Term::default_instance_; + delete Term_reflection_; + delete Term_AssocPair::default_instance_; + delete Term_AssocPair_reflection_; +} + +void protobuf_AddDesc_ql2_2eproto() { + static bool already_here = false; + if (already_here) return; + already_here = true; + GOOGLE_PROTOBUF_VERIFY_VERSION; + + ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( + "\n\tql2.proto\"5\n\014VersionDummy\"%\n\007Version\022\014" + "\n\004V0_1\020\266\364\206\373\003\022\014\n\004V0_2\020\341\203\302\221\007\"\365\001\n\005Query\022\036\n\004" + "type\030\001 \001(\0162\020.Query.QueryType\022\024\n\005query\030\002 " + "\001(\0132\005.Term\022\r\n\005token\030\003 \001(\003\022\037\n\020OBSOLETE_no" + "reply\030\004 \001(\010:\005false\022(\n\016global_optargs\030\006 \003" + "(\0132\020.Query.AssocPair\032,\n\tAssocPair\022\013\n\003key" + "\030\001 \001(\t\022\022\n\003val\030\002 \001(\0132\005.Term\".\n\tQueryType\022" + "\t\n\005START\020\001\022\014\n\010CONTINUE\020\002\022\010\n\004STOP\020\003\"`\n\005Fr" + "ame\022\036\n\004type\030\001 \001(\0162\020.Frame.FrameType\022\013\n\003p" + "os\030\002 \001(\003\022\013\n\003opt\030\003 \001(\t\"\035\n\tFrameType\022\007\n\003PO" + "S\020\001\022\007\n\003OPT\020\002\"#\n\tBacktrace\022\026\n\006frames\030\001 \003(" + "\0132\006.Frame\"\376\001\n\010Response\022$\n\004type\030\001 \001(\0162\026.R" + "esponse.ResponseType\022\r\n\005token\030\002 \001(\003\022\030\n\010r" + "esponse\030\003 \003(\0132\006.Datum\022\035\n\tbacktrace\030\004 \001(\013" + "2\n.Backtrace\"\203\001\n\014ResponseType\022\020\n\014SUCCESS" + "_ATOM\020\001\022\024\n\020SUCCESS_SEQUENCE\020\002\022\023\n\017SUCCESS" + "_PARTIAL\020\003\022\020\n\014CLIENT_ERROR\020\020\022\021\n\rCOMPILE_" + "ERROR\020\021\022\021\n\rRUNTIME_ERROR\020\022\"\240\002\n\005Datum\022\036\n\004" + "type\030\001 \001(\0162\020.Datum.DatumType\022\016\n\006r_bool\030\002" + " \001(\010\022\r\n\005r_num\030\003 \001(\001\022\r\n\005r_str\030\004 \001(\t\022\027\n\007r_" + "array\030\005 \003(\0132\006.Datum\022\"\n\010r_object\030\006 \003(\0132\020." + "Datum.AssocPair\032-\n\tAssocPair\022\013\n\003key\030\001 \001(" + "\t\022\023\n\003val\030\002 \001(\0132\006.Datum\"T\n\tDatumType\022\n\n\006R" + "_NULL\020\001\022\n\n\006R_BOOL\020\002\022\t\n\005R_NUM\020\003\022\t\n\005R_STR\020" + "\004\022\013\n\007R_ARRAY\020\005\022\014\n\010R_OBJECT\020\006*\007\010\220N\020\241\234\001\"\247\n" + "\n\004Term\022\034\n\004type\030\001 \001(\0162\016.Term.TermType\022\025\n\005" + "datum\030\002 \001(\0132\006.Datum\022\023\n\004args\030\003 \003(\0132\005.Term" + "\022 \n\007optargs\030\004 \003(\0132\017.Term.AssocPair\032,\n\tAs" + "socPair\022\013\n\003key\030\001 \001(\t\022\022\n\003val\030\002 \001(\0132\005.Term" + "\"\373\010\n\010TermType\022\t\n\005DATUM\020\001\022\016\n\nMAKE_ARRAY\020\002" + "\022\014\n\010MAKE_OBJ\020\003\022\007\n\003VAR\020\n\022\016\n\nJAVASCRIPT\020\013\022" + "\t\n\005ERROR\020\014\022\020\n\014IMPLICIT_VAR\020\r\022\006\n\002DB\020\016\022\t\n\005" + "TABLE\020\017\022\007\n\003GET\020\020\022\013\n\007GET_ALL\020N\022\006\n\002EQ\020\021\022\006\n" + "\002NE\020\022\022\006\n\002LT\020\023\022\006\n\002LE\020\024\022\006\n\002GT\020\025\022\006\n\002GE\020\026\022\007\n" + "\003NOT\020\027\022\007\n\003ADD\020\030\022\007\n\003SUB\020\031\022\007\n\003MUL\020\032\022\007\n\003DIV" + "\020\033\022\007\n\003MOD\020\034\022\n\n\006APPEND\020\035\022\013\n\007PREPEND\020P\022\016\n\n" + "DIFFERENCE\020_\022\016\n\nSET_INSERT\020X\022\024\n\020SET_INTE" + "RSECTION\020Y\022\r\n\tSET_UNION\020Z\022\022\n\016SET_DIFFERE" + "NCE\020[\022\t\n\005SLICE\020\036\022\010\n\004SKIP\020F\022\t\n\005LIMIT\020G\022\016\n" + "\nINDEXES_OF\020W\022\014\n\010CONTAINS\020]\022\013\n\007GETATTR\020\037" + "\022\010\n\004KEYS\020^\022\016\n\nHAS_FIELDS\020 \022\017\n\013WITH_FIELD" + "S\020`\022\t\n\005PLUCK\020!\022\013\n\007WITHOUT\020\"\022\t\n\005MERGE\020#\022\013" + "\n\007BETWEEN\020$\022\n\n\006REDUCE\020%\022\007\n\003MAP\020&\022\n\n\006FILT" + "ER\020\'\022\r\n\tCONCATMAP\020(\022\013\n\007ORDERBY\020)\022\014\n\010DIST" + "INCT\020*\022\t\n\005COUNT\020+\022\014\n\010IS_EMPTY\020V\022\t\n\005UNION" + "\020,\022\007\n\003NTH\020-\022\026\n\022GROUPED_MAP_REDUCE\020.\022\013\n\007G" + "ROUPBY\020/\022\016\n\nINNER_JOIN\0200\022\016\n\nOUTER_JOIN\0201" + "\022\013\n\007EQ_JOIN\0202\022\007\n\003ZIP\020H\022\r\n\tINSERT_AT\020R\022\r\n" + "\tDELETE_AT\020S\022\r\n\tCHANGE_AT\020T\022\r\n\tSPLICE_AT" + "\020U\022\r\n\tCOERCE_TO\0203\022\n\n\006TYPEOF\0204\022\n\n\006UPDATE\020" + "5\022\n\n\006DELETE\0206\022\013\n\007REPLACE\0207\022\n\n\006INSERT\0208\022\r" + "\n\tDB_CREATE\0209\022\013\n\007DB_DROP\020:\022\013\n\007DB_LIST\020;\022" + "\020\n\014TABLE_CREATE\020<\022\016\n\nTABLE_DROP\020=\022\016\n\nTAB" + "LE_LIST\020>\022\020\n\014INDEX_CREATE\020K\022\016\n\nINDEX_DRO" + "P\020L\022\016\n\nINDEX_LIST\020M\022\013\n\007FUNCALL\020@\022\n\n\006BRAN" + "CH\020A\022\007\n\003ANY\020B\022\007\n\003ALL\020C\022\013\n\007FOREACH\020D\022\010\n\004F" + "UNC\020E\022\007\n\003ASC\020I\022\010\n\004DESC\020J\022\010\n\004INFO\020O\022\t\n\005MA" + "TCH\020a\022\n\n\006SAMPLE\020Q\022\013\n\007DEFAULT\020\\*\007\010\220N\020\241\234\001", 2319); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( + "ql2.proto", &protobuf_RegisterTypes); + VersionDummy::default_instance_ = new VersionDummy(); + Query::default_instance_ = new Query(); + Query_AssocPair::default_instance_ = new Query_AssocPair(); + Frame::default_instance_ = new Frame(); + Backtrace::default_instance_ = new Backtrace(); + Response::default_instance_ = new Response(); + Datum::default_instance_ = new Datum(); + Datum_AssocPair::default_instance_ = new Datum_AssocPair(); + Term::default_instance_ = new Term(); + Term_AssocPair::default_instance_ = new Term_AssocPair(); + VersionDummy::default_instance_->InitAsDefaultInstance(); + Query::default_instance_->InitAsDefaultInstance(); + Query_AssocPair::default_instance_->InitAsDefaultInstance(); + Frame::default_instance_->InitAsDefaultInstance(); + Backtrace::default_instance_->InitAsDefaultInstance(); + Response::default_instance_->InitAsDefaultInstance(); + Datum::default_instance_->InitAsDefaultInstance(); + Datum_AssocPair::default_instance_->InitAsDefaultInstance(); + Term::default_instance_->InitAsDefaultInstance(); + Term_AssocPair::default_instance_->InitAsDefaultInstance(); + ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_ql2_2eproto); +} + +// Force AddDescriptors() to be called at static initialization time. +struct StaticDescriptorInitializer_ql2_2eproto { + StaticDescriptorInitializer_ql2_2eproto() { + protobuf_AddDesc_ql2_2eproto(); + } +} static_descriptor_initializer_ql2_2eproto_; + + +// =================================================================== + +const ::google::protobuf::EnumDescriptor* VersionDummy_Version_descriptor() { + protobuf_AssignDescriptorsOnce(); + return VersionDummy_Version_descriptor_; +} +bool VersionDummy_Version_IsValid(int value) { + switch(value) { + case 1063369270: + case 1915781601: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const VersionDummy_Version VersionDummy::V0_1; +const VersionDummy_Version VersionDummy::V0_2; +const VersionDummy_Version VersionDummy::Version_MIN; +const VersionDummy_Version VersionDummy::Version_MAX; +const int VersionDummy::Version_ARRAYSIZE; +#endif // _MSC_VER +#ifndef _MSC_VER +#endif // !_MSC_VER + +VersionDummy::VersionDummy() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void VersionDummy::InitAsDefaultInstance() { +} + +VersionDummy::VersionDummy(const VersionDummy& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void VersionDummy::SharedCtor() { + _cached_size_ = 0; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +VersionDummy::~VersionDummy() { + SharedDtor(); +} + +void VersionDummy::SharedDtor() { + if (this != default_instance_) { + } +} + +void VersionDummy::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* VersionDummy::descriptor() { + protobuf_AssignDescriptorsOnce(); + return VersionDummy_descriptor_; +} + +const VersionDummy& VersionDummy::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_ql2_2eproto(); return *default_instance_; +} + +VersionDummy* VersionDummy::default_instance_ = NULL; + +VersionDummy* VersionDummy::New() const { + return new VersionDummy; +} + +void VersionDummy::Clear() { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool VersionDummy::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + } + return true; +#undef DO_ +} + +void VersionDummy::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* VersionDummy::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int VersionDummy::ByteSize() const { + int total_size = 0; + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void VersionDummy::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const VersionDummy* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void VersionDummy::MergeFrom(const VersionDummy& from) { + GOOGLE_CHECK_NE(&from, this); + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void VersionDummy::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void VersionDummy::CopyFrom(const VersionDummy& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool VersionDummy::IsInitialized() const { + + return true; +} + +void VersionDummy::Swap(VersionDummy* other) { + if (other != this) { + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata VersionDummy::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = VersionDummy_descriptor_; + metadata.reflection = VersionDummy_reflection_; + return metadata; +} + + +// =================================================================== + +const ::google::protobuf::EnumDescriptor* Query_QueryType_descriptor() { + protobuf_AssignDescriptorsOnce(); + return Query_QueryType_descriptor_; +} +bool Query_QueryType_IsValid(int value) { + switch(value) { + case 1: + case 2: + case 3: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const Query_QueryType Query::START; +const Query_QueryType Query::CONTINUE; +const Query_QueryType Query::STOP; +const Query_QueryType Query::QueryType_MIN; +const Query_QueryType Query::QueryType_MAX; +const int Query::QueryType_ARRAYSIZE; +#endif // _MSC_VER +#ifndef _MSC_VER +const int Query_AssocPair::kKeyFieldNumber; +const int Query_AssocPair::kValFieldNumber; +#endif // !_MSC_VER + +Query_AssocPair::Query_AssocPair() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void Query_AssocPair::InitAsDefaultInstance() { + val_ = const_cast< ::Term*>(&::Term::default_instance()); +} + +Query_AssocPair::Query_AssocPair(const Query_AssocPair& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void Query_AssocPair::SharedCtor() { + _cached_size_ = 0; + key_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + val_ = NULL; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +Query_AssocPair::~Query_AssocPair() { + SharedDtor(); +} + +void Query_AssocPair::SharedDtor() { + if (key_ != &::google::protobuf::internal::kEmptyString) { + delete key_; + } + if (this != default_instance_) { + delete val_; + } +} + +void Query_AssocPair::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* Query_AssocPair::descriptor() { + protobuf_AssignDescriptorsOnce(); + return Query_AssocPair_descriptor_; +} + +const Query_AssocPair& Query_AssocPair::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_ql2_2eproto(); return *default_instance_; +} + +Query_AssocPair* Query_AssocPair::default_instance_ = NULL; + +Query_AssocPair* Query_AssocPair::New() const { + return new Query_AssocPair; +} + +void Query_AssocPair::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (has_key()) { + if (key_ != &::google::protobuf::internal::kEmptyString) { + key_->clear(); + } + } + if (has_val()) { + if (val_ != NULL) val_->::Term::Clear(); + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool Query_AssocPair::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional string key = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_key())); + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->key().data(), this->key().length(), + ::google::protobuf::internal::WireFormat::PARSE); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(18)) goto parse_val; + break; + } + + // optional .Term val = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_val: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, mutable_val())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void Query_AssocPair::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // optional string key = 1; + if (has_key()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->key().data(), this->key().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormatLite::WriteString( + 1, this->key(), output); + } + + // optional .Term val = 2; + if (has_val()) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 2, this->val(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* Query_AssocPair::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // optional string key = 1; + if (has_key()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->key().data(), this->key().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 1, this->key(), target); + } + + // optional .Term val = 2; + if (has_val()) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 2, this->val(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int Query_AssocPair::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string key = 1; + if (has_key()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->key()); + } + + // optional .Term val = 2; + if (has_val()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->val()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void Query_AssocPair::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const Query_AssocPair* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void Query_AssocPair::MergeFrom(const Query_AssocPair& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_key()) { + set_key(from.key()); + } + if (from.has_val()) { + mutable_val()->::Term::MergeFrom(from.val()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void Query_AssocPair::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void Query_AssocPair::CopyFrom(const Query_AssocPair& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Query_AssocPair::IsInitialized() const { + + if (has_val()) { + if (!this->val().IsInitialized()) return false; + } + return true; +} + +void Query_AssocPair::Swap(Query_AssocPair* other) { + if (other != this) { + std::swap(key_, other->key_); + std::swap(val_, other->val_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata Query_AssocPair::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = Query_AssocPair_descriptor_; + metadata.reflection = Query_AssocPair_reflection_; + return metadata; +} + + +// ------------------------------------------------------------------- + +#ifndef _MSC_VER +const int Query::kTypeFieldNumber; +const int Query::kQueryFieldNumber; +const int Query::kTokenFieldNumber; +const int Query::kOBSOLETENoreplyFieldNumber; +const int Query::kGlobalOptargsFieldNumber; +#endif // !_MSC_VER + +Query::Query() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void Query::InitAsDefaultInstance() { + query_ = const_cast< ::Term*>(&::Term::default_instance()); +} + +Query::Query(const Query& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void Query::SharedCtor() { + _cached_size_ = 0; + type_ = 1; + query_ = NULL; + token_ = GOOGLE_LONGLONG(0); + obsolete_noreply_ = false; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +Query::~Query() { + SharedDtor(); +} + +void Query::SharedDtor() { + if (this != default_instance_) { + delete query_; + } +} + +void Query::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* Query::descriptor() { + protobuf_AssignDescriptorsOnce(); + return Query_descriptor_; +} + +const Query& Query::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_ql2_2eproto(); return *default_instance_; +} + +Query* Query::default_instance_ = NULL; + +Query* Query::New() const { + return new Query; +} + +void Query::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + type_ = 1; + if (has_query()) { + if (query_ != NULL) query_->::Term::Clear(); + } + token_ = GOOGLE_LONGLONG(0); + obsolete_noreply_ = false; + } + global_optargs_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool Query::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional .Query.QueryType type = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + int value; + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>( + input, &value))); + if (::Query_QueryType_IsValid(value)) { + set_type(static_cast< ::Query_QueryType >(value)); + } else { + mutable_unknown_fields()->AddVarint(1, value); + } + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(18)) goto parse_query; + break; + } + + // optional .Term query = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_query: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, mutable_query())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(24)) goto parse_token; + break; + } + + // optional int64 token = 3; + case 3: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_token: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_INT64>( + input, &token_))); + set_has_token(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(32)) goto parse_OBSOLETE_noreply; + break; + } + + // optional bool OBSOLETE_noreply = 4 [default = false]; + case 4: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_OBSOLETE_noreply: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( + input, &obsolete_noreply_))); + set_has_obsolete_noreply(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(50)) goto parse_global_optargs; + break; + } + + // repeated .Query.AssocPair global_optargs = 6; + case 6: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_global_optargs: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, add_global_optargs())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(50)) goto parse_global_optargs; + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void Query::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // optional .Query.QueryType type = 1; + if (has_type()) { + ::google::protobuf::internal::WireFormatLite::WriteEnum( + 1, this->type(), output); + } + + // optional .Term query = 2; + if (has_query()) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 2, this->query(), output); + } + + // optional int64 token = 3; + if (has_token()) { + ::google::protobuf::internal::WireFormatLite::WriteInt64(3, this->token(), output); + } + + // optional bool OBSOLETE_noreply = 4 [default = false]; + if (has_obsolete_noreply()) { + ::google::protobuf::internal::WireFormatLite::WriteBool(4, this->obsolete_noreply(), output); + } + + // repeated .Query.AssocPair global_optargs = 6; + for (int i = 0; i < this->global_optargs_size(); i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 6, this->global_optargs(i), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* Query::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // optional .Query.QueryType type = 1; + if (has_type()) { + target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray( + 1, this->type(), target); + } + + // optional .Term query = 2; + if (has_query()) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 2, this->query(), target); + } + + // optional int64 token = 3; + if (has_token()) { + target = ::google::protobuf::internal::WireFormatLite::WriteInt64ToArray(3, this->token(), target); + } + + // optional bool OBSOLETE_noreply = 4 [default = false]; + if (has_obsolete_noreply()) { + target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(4, this->obsolete_noreply(), target); + } + + // repeated .Query.AssocPair global_optargs = 6; + for (int i = 0; i < this->global_optargs_size(); i++) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 6, this->global_optargs(i), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int Query::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional .Query.QueryType type = 1; + if (has_type()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::EnumSize(this->type()); + } + + // optional .Term query = 2; + if (has_query()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->query()); + } + + // optional int64 token = 3; + if (has_token()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int64Size( + this->token()); + } + + // optional bool OBSOLETE_noreply = 4 [default = false]; + if (has_obsolete_noreply()) { + total_size += 1 + 1; + } + + } + // repeated .Query.AssocPair global_optargs = 6; + total_size += 1 * this->global_optargs_size(); + for (int i = 0; i < this->global_optargs_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->global_optargs(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void Query::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const Query* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void Query::MergeFrom(const Query& from) { + GOOGLE_CHECK_NE(&from, this); + global_optargs_.MergeFrom(from.global_optargs_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_type()) { + set_type(from.type()); + } + if (from.has_query()) { + mutable_query()->::Term::MergeFrom(from.query()); + } + if (from.has_token()) { + set_token(from.token()); + } + if (from.has_obsolete_noreply()) { + set_obsolete_noreply(from.obsolete_noreply()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void Query::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void Query::CopyFrom(const Query& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Query::IsInitialized() const { + + if (has_query()) { + if (!this->query().IsInitialized()) return false; + } + for (int i = 0; i < global_optargs_size(); i++) { + if (!this->global_optargs(i).IsInitialized()) return false; + } + return true; +} + +void Query::Swap(Query* other) { + if (other != this) { + std::swap(type_, other->type_); + std::swap(query_, other->query_); + std::swap(token_, other->token_); + std::swap(obsolete_noreply_, other->obsolete_noreply_); + global_optargs_.Swap(&other->global_optargs_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata Query::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = Query_descriptor_; + metadata.reflection = Query_reflection_; + return metadata; +} + + +// =================================================================== + +const ::google::protobuf::EnumDescriptor* Frame_FrameType_descriptor() { + protobuf_AssignDescriptorsOnce(); + return Frame_FrameType_descriptor_; +} +bool Frame_FrameType_IsValid(int value) { + switch(value) { + case 1: + case 2: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const Frame_FrameType Frame::POS; +const Frame_FrameType Frame::OPT; +const Frame_FrameType Frame::FrameType_MIN; +const Frame_FrameType Frame::FrameType_MAX; +const int Frame::FrameType_ARRAYSIZE; +#endif // _MSC_VER +#ifndef _MSC_VER +const int Frame::kTypeFieldNumber; +const int Frame::kPosFieldNumber; +const int Frame::kOptFieldNumber; +#endif // !_MSC_VER + +Frame::Frame() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void Frame::InitAsDefaultInstance() { +} + +Frame::Frame(const Frame& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void Frame::SharedCtor() { + _cached_size_ = 0; + type_ = 1; + pos_ = GOOGLE_LONGLONG(0); + opt_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +Frame::~Frame() { + SharedDtor(); +} + +void Frame::SharedDtor() { + if (opt_ != &::google::protobuf::internal::kEmptyString) { + delete opt_; + } + if (this != default_instance_) { + } +} + +void Frame::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* Frame::descriptor() { + protobuf_AssignDescriptorsOnce(); + return Frame_descriptor_; +} + +const Frame& Frame::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_ql2_2eproto(); return *default_instance_; +} + +Frame* Frame::default_instance_ = NULL; + +Frame* Frame::New() const { + return new Frame; +} + +void Frame::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + type_ = 1; + pos_ = GOOGLE_LONGLONG(0); + if (has_opt()) { + if (opt_ != &::google::protobuf::internal::kEmptyString) { + opt_->clear(); + } + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool Frame::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional .Frame.FrameType type = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + int value; + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>( + input, &value))); + if (::Frame_FrameType_IsValid(value)) { + set_type(static_cast< ::Frame_FrameType >(value)); + } else { + mutable_unknown_fields()->AddVarint(1, value); + } + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(16)) goto parse_pos; + break; + } + + // optional int64 pos = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_pos: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_INT64>( + input, &pos_))); + set_has_pos(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(26)) goto parse_opt; + break; + } + + // optional string opt = 3; + case 3: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_opt: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_opt())); + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->opt().data(), this->opt().length(), + ::google::protobuf::internal::WireFormat::PARSE); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void Frame::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // optional .Frame.FrameType type = 1; + if (has_type()) { + ::google::protobuf::internal::WireFormatLite::WriteEnum( + 1, this->type(), output); + } + + // optional int64 pos = 2; + if (has_pos()) { + ::google::protobuf::internal::WireFormatLite::WriteInt64(2, this->pos(), output); + } + + // optional string opt = 3; + if (has_opt()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->opt().data(), this->opt().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormatLite::WriteString( + 3, this->opt(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* Frame::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // optional .Frame.FrameType type = 1; + if (has_type()) { + target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray( + 1, this->type(), target); + } + + // optional int64 pos = 2; + if (has_pos()) { + target = ::google::protobuf::internal::WireFormatLite::WriteInt64ToArray(2, this->pos(), target); + } + + // optional string opt = 3; + if (has_opt()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->opt().data(), this->opt().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 3, this->opt(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int Frame::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional .Frame.FrameType type = 1; + if (has_type()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::EnumSize(this->type()); + } + + // optional int64 pos = 2; + if (has_pos()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int64Size( + this->pos()); + } + + // optional string opt = 3; + if (has_opt()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->opt()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void Frame::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const Frame* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void Frame::MergeFrom(const Frame& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_type()) { + set_type(from.type()); + } + if (from.has_pos()) { + set_pos(from.pos()); + } + if (from.has_opt()) { + set_opt(from.opt()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void Frame::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void Frame::CopyFrom(const Frame& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Frame::IsInitialized() const { + + return true; +} + +void Frame::Swap(Frame* other) { + if (other != this) { + std::swap(type_, other->type_); + std::swap(pos_, other->pos_); + std::swap(opt_, other->opt_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata Frame::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = Frame_descriptor_; + metadata.reflection = Frame_reflection_; + return metadata; +} + + +// =================================================================== + +#ifndef _MSC_VER +const int Backtrace::kFramesFieldNumber; +#endif // !_MSC_VER + +Backtrace::Backtrace() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void Backtrace::InitAsDefaultInstance() { +} + +Backtrace::Backtrace(const Backtrace& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void Backtrace::SharedCtor() { + _cached_size_ = 0; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +Backtrace::~Backtrace() { + SharedDtor(); +} + +void Backtrace::SharedDtor() { + if (this != default_instance_) { + } +} + +void Backtrace::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* Backtrace::descriptor() { + protobuf_AssignDescriptorsOnce(); + return Backtrace_descriptor_; +} + +const Backtrace& Backtrace::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_ql2_2eproto(); return *default_instance_; +} + +Backtrace* Backtrace::default_instance_ = NULL; + +Backtrace* Backtrace::New() const { + return new Backtrace; +} + +void Backtrace::Clear() { + frames_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool Backtrace::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // repeated .Frame frames = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_frames: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, add_frames())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(10)) goto parse_frames; + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void Backtrace::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // repeated .Frame frames = 1; + for (int i = 0; i < this->frames_size(); i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 1, this->frames(i), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* Backtrace::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // repeated .Frame frames = 1; + for (int i = 0; i < this->frames_size(); i++) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 1, this->frames(i), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int Backtrace::ByteSize() const { + int total_size = 0; + + // repeated .Frame frames = 1; + total_size += 1 * this->frames_size(); + for (int i = 0; i < this->frames_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->frames(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void Backtrace::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const Backtrace* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void Backtrace::MergeFrom(const Backtrace& from) { + GOOGLE_CHECK_NE(&from, this); + frames_.MergeFrom(from.frames_); + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void Backtrace::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void Backtrace::CopyFrom(const Backtrace& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Backtrace::IsInitialized() const { + + return true; +} + +void Backtrace::Swap(Backtrace* other) { + if (other != this) { + frames_.Swap(&other->frames_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata Backtrace::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = Backtrace_descriptor_; + metadata.reflection = Backtrace_reflection_; + return metadata; +} + + +// =================================================================== + +const ::google::protobuf::EnumDescriptor* Response_ResponseType_descriptor() { + protobuf_AssignDescriptorsOnce(); + return Response_ResponseType_descriptor_; +} +bool Response_ResponseType_IsValid(int value) { + switch(value) { + case 1: + case 2: + case 3: + case 16: + case 17: + case 18: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const Response_ResponseType Response::SUCCESS_ATOM; +const Response_ResponseType Response::SUCCESS_SEQUENCE; +const Response_ResponseType Response::SUCCESS_PARTIAL; +const Response_ResponseType Response::CLIENT_ERROR; +const Response_ResponseType Response::COMPILE_ERROR; +const Response_ResponseType Response::RUNTIME_ERROR; +const Response_ResponseType Response::ResponseType_MIN; +const Response_ResponseType Response::ResponseType_MAX; +const int Response::ResponseType_ARRAYSIZE; +#endif // _MSC_VER +#ifndef _MSC_VER +const int Response::kTypeFieldNumber; +const int Response::kTokenFieldNumber; +const int Response::kResponseFieldNumber; +const int Response::kBacktraceFieldNumber; +#endif // !_MSC_VER + +Response::Response() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void Response::InitAsDefaultInstance() { + backtrace_ = const_cast< ::Backtrace*>(&::Backtrace::default_instance()); +} + +Response::Response(const Response& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void Response::SharedCtor() { + _cached_size_ = 0; + type_ = 1; + token_ = GOOGLE_LONGLONG(0); + backtrace_ = NULL; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +Response::~Response() { + SharedDtor(); +} + +void Response::SharedDtor() { + if (this != default_instance_) { + delete backtrace_; + } +} + +void Response::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* Response::descriptor() { + protobuf_AssignDescriptorsOnce(); + return Response_descriptor_; +} + +const Response& Response::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_ql2_2eproto(); return *default_instance_; +} + +Response* Response::default_instance_ = NULL; + +Response* Response::New() const { + return new Response; +} + +void Response::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + type_ = 1; + token_ = GOOGLE_LONGLONG(0); + if (has_backtrace()) { + if (backtrace_ != NULL) backtrace_->::Backtrace::Clear(); + } + } + response_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool Response::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional .Response.ResponseType type = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + int value; + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>( + input, &value))); + if (::Response_ResponseType_IsValid(value)) { + set_type(static_cast< ::Response_ResponseType >(value)); + } else { + mutable_unknown_fields()->AddVarint(1, value); + } + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(16)) goto parse_token; + break; + } + + // optional int64 token = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_token: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_INT64>( + input, &token_))); + set_has_token(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(26)) goto parse_response; + break; + } + + // repeated .Datum response = 3; + case 3: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_response: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, add_response())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(26)) goto parse_response; + if (input->ExpectTag(34)) goto parse_backtrace; + break; + } + + // optional .Backtrace backtrace = 4; + case 4: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_backtrace: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, mutable_backtrace())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void Response::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // optional .Response.ResponseType type = 1; + if (has_type()) { + ::google::protobuf::internal::WireFormatLite::WriteEnum( + 1, this->type(), output); + } + + // optional int64 token = 2; + if (has_token()) { + ::google::protobuf::internal::WireFormatLite::WriteInt64(2, this->token(), output); + } + + // repeated .Datum response = 3; + for (int i = 0; i < this->response_size(); i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 3, this->response(i), output); + } + + // optional .Backtrace backtrace = 4; + if (has_backtrace()) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 4, this->backtrace(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* Response::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // optional .Response.ResponseType type = 1; + if (has_type()) { + target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray( + 1, this->type(), target); + } + + // optional int64 token = 2; + if (has_token()) { + target = ::google::protobuf::internal::WireFormatLite::WriteInt64ToArray(2, this->token(), target); + } + + // repeated .Datum response = 3; + for (int i = 0; i < this->response_size(); i++) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 3, this->response(i), target); + } + + // optional .Backtrace backtrace = 4; + if (has_backtrace()) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 4, this->backtrace(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int Response::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional .Response.ResponseType type = 1; + if (has_type()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::EnumSize(this->type()); + } + + // optional int64 token = 2; + if (has_token()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int64Size( + this->token()); + } + + // optional .Backtrace backtrace = 4; + if (has_backtrace()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->backtrace()); + } + + } + // repeated .Datum response = 3; + total_size += 1 * this->response_size(); + for (int i = 0; i < this->response_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->response(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void Response::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const Response* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void Response::MergeFrom(const Response& from) { + GOOGLE_CHECK_NE(&from, this); + response_.MergeFrom(from.response_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_type()) { + set_type(from.type()); + } + if (from.has_token()) { + set_token(from.token()); + } + if (from.has_backtrace()) { + mutable_backtrace()->::Backtrace::MergeFrom(from.backtrace()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void Response::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void Response::CopyFrom(const Response& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Response::IsInitialized() const { + + for (int i = 0; i < response_size(); i++) { + if (!this->response(i).IsInitialized()) return false; + } + return true; +} + +void Response::Swap(Response* other) { + if (other != this) { + std::swap(type_, other->type_); + std::swap(token_, other->token_); + response_.Swap(&other->response_); + std::swap(backtrace_, other->backtrace_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata Response::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = Response_descriptor_; + metadata.reflection = Response_reflection_; + return metadata; +} + + +// =================================================================== + +const ::google::protobuf::EnumDescriptor* Datum_DatumType_descriptor() { + protobuf_AssignDescriptorsOnce(); + return Datum_DatumType_descriptor_; +} +bool Datum_DatumType_IsValid(int value) { + switch(value) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const Datum_DatumType Datum::R_NULL; +const Datum_DatumType Datum::R_BOOL; +const Datum_DatumType Datum::R_NUM; +const Datum_DatumType Datum::R_STR; +const Datum_DatumType Datum::R_ARRAY; +const Datum_DatumType Datum::R_OBJECT; +const Datum_DatumType Datum::DatumType_MIN; +const Datum_DatumType Datum::DatumType_MAX; +const int Datum::DatumType_ARRAYSIZE; +#endif // _MSC_VER +#ifndef _MSC_VER +const int Datum_AssocPair::kKeyFieldNumber; +const int Datum_AssocPair::kValFieldNumber; +#endif // !_MSC_VER + +Datum_AssocPair::Datum_AssocPair() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void Datum_AssocPair::InitAsDefaultInstance() { + val_ = const_cast< ::Datum*>(&::Datum::default_instance()); +} + +Datum_AssocPair::Datum_AssocPair(const Datum_AssocPair& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void Datum_AssocPair::SharedCtor() { + _cached_size_ = 0; + key_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + val_ = NULL; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +Datum_AssocPair::~Datum_AssocPair() { + SharedDtor(); +} + +void Datum_AssocPair::SharedDtor() { + if (key_ != &::google::protobuf::internal::kEmptyString) { + delete key_; + } + if (this != default_instance_) { + delete val_; + } +} + +void Datum_AssocPair::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* Datum_AssocPair::descriptor() { + protobuf_AssignDescriptorsOnce(); + return Datum_AssocPair_descriptor_; +} + +const Datum_AssocPair& Datum_AssocPair::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_ql2_2eproto(); return *default_instance_; +} + +Datum_AssocPair* Datum_AssocPair::default_instance_ = NULL; + +Datum_AssocPair* Datum_AssocPair::New() const { + return new Datum_AssocPair; +} + +void Datum_AssocPair::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (has_key()) { + if (key_ != &::google::protobuf::internal::kEmptyString) { + key_->clear(); + } + } + if (has_val()) { + if (val_ != NULL) val_->::Datum::Clear(); + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool Datum_AssocPair::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional string key = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_key())); + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->key().data(), this->key().length(), + ::google::protobuf::internal::WireFormat::PARSE); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(18)) goto parse_val; + break; + } + + // optional .Datum val = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_val: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, mutable_val())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void Datum_AssocPair::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // optional string key = 1; + if (has_key()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->key().data(), this->key().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormatLite::WriteString( + 1, this->key(), output); + } + + // optional .Datum val = 2; + if (has_val()) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 2, this->val(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* Datum_AssocPair::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // optional string key = 1; + if (has_key()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->key().data(), this->key().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 1, this->key(), target); + } + + // optional .Datum val = 2; + if (has_val()) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 2, this->val(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int Datum_AssocPair::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string key = 1; + if (has_key()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->key()); + } + + // optional .Datum val = 2; + if (has_val()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->val()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void Datum_AssocPair::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const Datum_AssocPair* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void Datum_AssocPair::MergeFrom(const Datum_AssocPair& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_key()) { + set_key(from.key()); + } + if (from.has_val()) { + mutable_val()->::Datum::MergeFrom(from.val()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void Datum_AssocPair::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void Datum_AssocPair::CopyFrom(const Datum_AssocPair& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Datum_AssocPair::IsInitialized() const { + + if (has_val()) { + if (!this->val().IsInitialized()) return false; + } + return true; +} + +void Datum_AssocPair::Swap(Datum_AssocPair* other) { + if (other != this) { + std::swap(key_, other->key_); + std::swap(val_, other->val_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata Datum_AssocPair::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = Datum_AssocPair_descriptor_; + metadata.reflection = Datum_AssocPair_reflection_; + return metadata; +} + + +// ------------------------------------------------------------------- + +#ifndef _MSC_VER +const int Datum::kTypeFieldNumber; +const int Datum::kRBoolFieldNumber; +const int Datum::kRNumFieldNumber; +const int Datum::kRStrFieldNumber; +const int Datum::kRArrayFieldNumber; +const int Datum::kRObjectFieldNumber; +#endif // !_MSC_VER + +Datum::Datum() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void Datum::InitAsDefaultInstance() { +} + +Datum::Datum(const Datum& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void Datum::SharedCtor() { + _cached_size_ = 0; + type_ = 1; + r_bool_ = false; + r_num_ = 0; + r_str_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +Datum::~Datum() { + SharedDtor(); +} + +void Datum::SharedDtor() { + if (r_str_ != &::google::protobuf::internal::kEmptyString) { + delete r_str_; + } + if (this != default_instance_) { + } +} + +void Datum::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* Datum::descriptor() { + protobuf_AssignDescriptorsOnce(); + return Datum_descriptor_; +} + +const Datum& Datum::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_ql2_2eproto(); return *default_instance_; +} + +Datum* Datum::default_instance_ = NULL; + +Datum* Datum::New() const { + return new Datum; +} + +void Datum::Clear() { + _extensions_.Clear(); + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + type_ = 1; + r_bool_ = false; + r_num_ = 0; + if (has_r_str()) { + if (r_str_ != &::google::protobuf::internal::kEmptyString) { + r_str_->clear(); + } + } + } + r_array_.Clear(); + r_object_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool Datum::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional .Datum.DatumType type = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + int value; + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>( + input, &value))); + if (::Datum_DatumType_IsValid(value)) { + set_type(static_cast< ::Datum_DatumType >(value)); + } else { + mutable_unknown_fields()->AddVarint(1, value); + } + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(16)) goto parse_r_bool; + break; + } + + // optional bool r_bool = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_r_bool: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( + input, &r_bool_))); + set_has_r_bool(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(25)) goto parse_r_num; + break; + } + + // optional double r_num = 3; + case 3: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_FIXED64) { + parse_r_num: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + double, ::google::protobuf::internal::WireFormatLite::TYPE_DOUBLE>( + input, &r_num_))); + set_has_r_num(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(34)) goto parse_r_str; + break; + } + + // optional string r_str = 4; + case 4: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_r_str: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_r_str())); + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->r_str().data(), this->r_str().length(), + ::google::protobuf::internal::WireFormat::PARSE); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(42)) goto parse_r_array; + break; + } + + // repeated .Datum r_array = 5; + case 5: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_r_array: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, add_r_array())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(42)) goto parse_r_array; + if (input->ExpectTag(50)) goto parse_r_object; + break; + } + + // repeated .Datum.AssocPair r_object = 6; + case 6: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_r_object: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, add_r_object())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(50)) goto parse_r_object; + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + if ((80000u <= tag && tag < 160008u)) { + DO_(_extensions_.ParseField(tag, input, default_instance_, + mutable_unknown_fields())); + continue; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void Datum::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // optional .Datum.DatumType type = 1; + if (has_type()) { + ::google::protobuf::internal::WireFormatLite::WriteEnum( + 1, this->type(), output); + } + + // optional bool r_bool = 2; + if (has_r_bool()) { + ::google::protobuf::internal::WireFormatLite::WriteBool(2, this->r_bool(), output); + } + + // optional double r_num = 3; + if (has_r_num()) { + ::google::protobuf::internal::WireFormatLite::WriteDouble(3, this->r_num(), output); + } + + // optional string r_str = 4; + if (has_r_str()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->r_str().data(), this->r_str().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormatLite::WriteString( + 4, this->r_str(), output); + } + + // repeated .Datum r_array = 5; + for (int i = 0; i < this->r_array_size(); i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 5, this->r_array(i), output); + } + + // repeated .Datum.AssocPair r_object = 6; + for (int i = 0; i < this->r_object_size(); i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 6, this->r_object(i), output); + } + + // Extension range [10000, 20001) + _extensions_.SerializeWithCachedSizes( + 10000, 20001, output); + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* Datum::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // optional .Datum.DatumType type = 1; + if (has_type()) { + target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray( + 1, this->type(), target); + } + + // optional bool r_bool = 2; + if (has_r_bool()) { + target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(2, this->r_bool(), target); + } + + // optional double r_num = 3; + if (has_r_num()) { + target = ::google::protobuf::internal::WireFormatLite::WriteDoubleToArray(3, this->r_num(), target); + } + + // optional string r_str = 4; + if (has_r_str()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->r_str().data(), this->r_str().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 4, this->r_str(), target); + } + + // repeated .Datum r_array = 5; + for (int i = 0; i < this->r_array_size(); i++) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 5, this->r_array(i), target); + } + + // repeated .Datum.AssocPair r_object = 6; + for (int i = 0; i < this->r_object_size(); i++) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 6, this->r_object(i), target); + } + + // Extension range [10000, 20001) + target = _extensions_.SerializeWithCachedSizesToArray( + 10000, 20001, target); + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int Datum::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional .Datum.DatumType type = 1; + if (has_type()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::EnumSize(this->type()); + } + + // optional bool r_bool = 2; + if (has_r_bool()) { + total_size += 1 + 1; + } + + // optional double r_num = 3; + if (has_r_num()) { + total_size += 1 + 8; + } + + // optional string r_str = 4; + if (has_r_str()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->r_str()); + } + + } + // repeated .Datum r_array = 5; + total_size += 1 * this->r_array_size(); + for (int i = 0; i < this->r_array_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->r_array(i)); + } + + // repeated .Datum.AssocPair r_object = 6; + total_size += 1 * this->r_object_size(); + for (int i = 0; i < this->r_object_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->r_object(i)); + } + + total_size += _extensions_.ByteSize(); + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void Datum::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const Datum* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void Datum::MergeFrom(const Datum& from) { + GOOGLE_CHECK_NE(&from, this); + r_array_.MergeFrom(from.r_array_); + r_object_.MergeFrom(from.r_object_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_type()) { + set_type(from.type()); + } + if (from.has_r_bool()) { + set_r_bool(from.r_bool()); + } + if (from.has_r_num()) { + set_r_num(from.r_num()); + } + if (from.has_r_str()) { + set_r_str(from.r_str()); + } + } + _extensions_.MergeFrom(from._extensions_); + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void Datum::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void Datum::CopyFrom(const Datum& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Datum::IsInitialized() const { + + for (int i = 0; i < r_array_size(); i++) { + if (!this->r_array(i).IsInitialized()) return false; + } + for (int i = 0; i < r_object_size(); i++) { + if (!this->r_object(i).IsInitialized()) return false; + } + + if (!_extensions_.IsInitialized()) return false; return true; +} + +void Datum::Swap(Datum* other) { + if (other != this) { + std::swap(type_, other->type_); + std::swap(r_bool_, other->r_bool_); + std::swap(r_num_, other->r_num_); + std::swap(r_str_, other->r_str_); + r_array_.Swap(&other->r_array_); + r_object_.Swap(&other->r_object_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + _extensions_.Swap(&other->_extensions_); + } +} + +::google::protobuf::Metadata Datum::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = Datum_descriptor_; + metadata.reflection = Datum_reflection_; + return metadata; +} + + +// =================================================================== + +const ::google::protobuf::EnumDescriptor* Term_TermType_descriptor() { + protobuf_AssignDescriptorsOnce(); + return Term_TermType_descriptor_; +} +bool Term_TermType_IsValid(int value) { + switch(value) { + case 1: + case 2: + case 3: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + case 38: + case 39: + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + case 48: + case 49: + case 50: + case 51: + case 52: + case 53: + case 54: + case 55: + case 56: + case 57: + case 58: + case 59: + case 60: + case 61: + case 62: + case 64: + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 84: + case 85: + case 86: + case 87: + case 88: + case 89: + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const Term_TermType Term::DATUM; +const Term_TermType Term::MAKE_ARRAY; +const Term_TermType Term::MAKE_OBJ; +const Term_TermType Term::VAR; +const Term_TermType Term::JAVASCRIPT; +const Term_TermType Term::ERROR; +const Term_TermType Term::IMPLICIT_VAR; +const Term_TermType Term::DB; +const Term_TermType Term::TABLE; +const Term_TermType Term::GET; +const Term_TermType Term::GET_ALL; +const Term_TermType Term::EQ; +const Term_TermType Term::NE; +const Term_TermType Term::LT; +const Term_TermType Term::LE; +const Term_TermType Term::GT; +const Term_TermType Term::GE; +const Term_TermType Term::NOT; +const Term_TermType Term::ADD; +const Term_TermType Term::SUB; +const Term_TermType Term::MUL; +const Term_TermType Term::DIV; +const Term_TermType Term::MOD; +const Term_TermType Term::APPEND; +const Term_TermType Term::PREPEND; +const Term_TermType Term::DIFFERENCE; +const Term_TermType Term::SET_INSERT; +const Term_TermType Term::SET_INTERSECTION; +const Term_TermType Term::SET_UNION; +const Term_TermType Term::SET_DIFFERENCE; +const Term_TermType Term::SLICE; +const Term_TermType Term::SKIP; +const Term_TermType Term::LIMIT; +const Term_TermType Term::INDEXES_OF; +const Term_TermType Term::CONTAINS; +const Term_TermType Term::GETATTR; +const Term_TermType Term::KEYS; +const Term_TermType Term::HAS_FIELDS; +const Term_TermType Term::WITH_FIELDS; +const Term_TermType Term::PLUCK; +const Term_TermType Term::WITHOUT; +const Term_TermType Term::MERGE; +const Term_TermType Term::BETWEEN; +const Term_TermType Term::REDUCE; +const Term_TermType Term::MAP; +const Term_TermType Term::FILTER; +const Term_TermType Term::CONCATMAP; +const Term_TermType Term::ORDERBY; +const Term_TermType Term::DISTINCT; +const Term_TermType Term::COUNT; +const Term_TermType Term::IS_EMPTY; +const Term_TermType Term::UNION; +const Term_TermType Term::NTH; +const Term_TermType Term::GROUPED_MAP_REDUCE; +const Term_TermType Term::GROUPBY; +const Term_TermType Term::INNER_JOIN; +const Term_TermType Term::OUTER_JOIN; +const Term_TermType Term::EQ_JOIN; +const Term_TermType Term::ZIP; +const Term_TermType Term::INSERT_AT; +const Term_TermType Term::DELETE_AT; +const Term_TermType Term::CHANGE_AT; +const Term_TermType Term::SPLICE_AT; +const Term_TermType Term::COERCE_TO; +const Term_TermType Term::TYPEOF; +const Term_TermType Term::UPDATE; +const Term_TermType Term::DELETE; +const Term_TermType Term::REPLACE; +const Term_TermType Term::INSERT; +const Term_TermType Term::DB_CREATE; +const Term_TermType Term::DB_DROP; +const Term_TermType Term::DB_LIST; +const Term_TermType Term::TABLE_CREATE; +const Term_TermType Term::TABLE_DROP; +const Term_TermType Term::TABLE_LIST; +const Term_TermType Term::INDEX_CREATE; +const Term_TermType Term::INDEX_DROP; +const Term_TermType Term::INDEX_LIST; +const Term_TermType Term::FUNCALL; +const Term_TermType Term::BRANCH; +const Term_TermType Term::ANY; +const Term_TermType Term::ALL; +const Term_TermType Term::FOREACH; +const Term_TermType Term::FUNC; +const Term_TermType Term::ASC; +const Term_TermType Term::DESC; +const Term_TermType Term::INFO; +const Term_TermType Term::MATCH; +const Term_TermType Term::SAMPLE; +const Term_TermType Term::DEFAULT; +const Term_TermType Term::TermType_MIN; +const Term_TermType Term::TermType_MAX; +const int Term::TermType_ARRAYSIZE; +#endif // _MSC_VER +#ifndef _MSC_VER +const int Term_AssocPair::kKeyFieldNumber; +const int Term_AssocPair::kValFieldNumber; +#endif // !_MSC_VER + +Term_AssocPair::Term_AssocPair() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void Term_AssocPair::InitAsDefaultInstance() { + val_ = const_cast< ::Term*>(&::Term::default_instance()); +} + +Term_AssocPair::Term_AssocPair(const Term_AssocPair& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void Term_AssocPair::SharedCtor() { + _cached_size_ = 0; + key_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + val_ = NULL; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +Term_AssocPair::~Term_AssocPair() { + SharedDtor(); +} + +void Term_AssocPair::SharedDtor() { + if (key_ != &::google::protobuf::internal::kEmptyString) { + delete key_; + } + if (this != default_instance_) { + delete val_; + } +} + +void Term_AssocPair::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* Term_AssocPair::descriptor() { + protobuf_AssignDescriptorsOnce(); + return Term_AssocPair_descriptor_; +} + +const Term_AssocPair& Term_AssocPair::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_ql2_2eproto(); return *default_instance_; +} + +Term_AssocPair* Term_AssocPair::default_instance_ = NULL; + +Term_AssocPair* Term_AssocPair::New() const { + return new Term_AssocPair; +} + +void Term_AssocPair::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (has_key()) { + if (key_ != &::google::protobuf::internal::kEmptyString) { + key_->clear(); + } + } + if (has_val()) { + if (val_ != NULL) val_->::Term::Clear(); + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool Term_AssocPair::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional string key = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_key())); + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->key().data(), this->key().length(), + ::google::protobuf::internal::WireFormat::PARSE); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(18)) goto parse_val; + break; + } + + // optional .Term val = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_val: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, mutable_val())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void Term_AssocPair::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // optional string key = 1; + if (has_key()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->key().data(), this->key().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormatLite::WriteString( + 1, this->key(), output); + } + + // optional .Term val = 2; + if (has_val()) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 2, this->val(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* Term_AssocPair::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // optional string key = 1; + if (has_key()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->key().data(), this->key().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 1, this->key(), target); + } + + // optional .Term val = 2; + if (has_val()) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 2, this->val(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int Term_AssocPair::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string key = 1; + if (has_key()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->key()); + } + + // optional .Term val = 2; + if (has_val()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->val()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void Term_AssocPair::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const Term_AssocPair* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void Term_AssocPair::MergeFrom(const Term_AssocPair& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_key()) { + set_key(from.key()); + } + if (from.has_val()) { + mutable_val()->::Term::MergeFrom(from.val()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void Term_AssocPair::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void Term_AssocPair::CopyFrom(const Term_AssocPair& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Term_AssocPair::IsInitialized() const { + + if (has_val()) { + if (!this->val().IsInitialized()) return false; + } + return true; +} + +void Term_AssocPair::Swap(Term_AssocPair* other) { + if (other != this) { + std::swap(key_, other->key_); + std::swap(val_, other->val_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata Term_AssocPair::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = Term_AssocPair_descriptor_; + metadata.reflection = Term_AssocPair_reflection_; + return metadata; +} + + +// ------------------------------------------------------------------- + +#ifndef _MSC_VER +const int Term::kTypeFieldNumber; +const int Term::kDatumFieldNumber; +const int Term::kArgsFieldNumber; +const int Term::kOptargsFieldNumber; +#endif // !_MSC_VER + +Term::Term() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void Term::InitAsDefaultInstance() { + datum_ = const_cast< ::Datum*>(&::Datum::default_instance()); +} + +Term::Term(const Term& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void Term::SharedCtor() { + _cached_size_ = 0; + type_ = 1; + datum_ = NULL; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +Term::~Term() { + SharedDtor(); +} + +void Term::SharedDtor() { + if (this != default_instance_) { + delete datum_; + } +} + +void Term::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* Term::descriptor() { + protobuf_AssignDescriptorsOnce(); + return Term_descriptor_; +} + +const Term& Term::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_ql2_2eproto(); return *default_instance_; +} + +Term* Term::default_instance_ = NULL; + +Term* Term::New() const { + return new Term; +} + +void Term::Clear() { + _extensions_.Clear(); + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + type_ = 1; + if (has_datum()) { + if (datum_ != NULL) datum_->::Datum::Clear(); + } + } + args_.Clear(); + optargs_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool Term::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional .Term.TermType type = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + int value; + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>( + input, &value))); + if (::Term_TermType_IsValid(value)) { + set_type(static_cast< ::Term_TermType >(value)); + } else { + mutable_unknown_fields()->AddVarint(1, value); + } + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(18)) goto parse_datum; + break; + } + + // optional .Datum datum = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_datum: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, mutable_datum())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(26)) goto parse_args; + break; + } + + // repeated .Term args = 3; + case 3: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_args: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, add_args())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(26)) goto parse_args; + if (input->ExpectTag(34)) goto parse_optargs; + break; + } + + // repeated .Term.AssocPair optargs = 4; + case 4: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_optargs: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, add_optargs())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(34)) goto parse_optargs; + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + if ((80000u <= tag && tag < 160008u)) { + DO_(_extensions_.ParseField(tag, input, default_instance_, + mutable_unknown_fields())); + continue; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void Term::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // optional .Term.TermType type = 1; + if (has_type()) { + ::google::protobuf::internal::WireFormatLite::WriteEnum( + 1, this->type(), output); + } + + // optional .Datum datum = 2; + if (has_datum()) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 2, this->datum(), output); + } + + // repeated .Term args = 3; + for (int i = 0; i < this->args_size(); i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 3, this->args(i), output); + } + + // repeated .Term.AssocPair optargs = 4; + for (int i = 0; i < this->optargs_size(); i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 4, this->optargs(i), output); + } + + // Extension range [10000, 20001) + _extensions_.SerializeWithCachedSizes( + 10000, 20001, output); + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* Term::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // optional .Term.TermType type = 1; + if (has_type()) { + target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray( + 1, this->type(), target); + } + + // optional .Datum datum = 2; + if (has_datum()) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 2, this->datum(), target); + } + + // repeated .Term args = 3; + for (int i = 0; i < this->args_size(); i++) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 3, this->args(i), target); + } + + // repeated .Term.AssocPair optargs = 4; + for (int i = 0; i < this->optargs_size(); i++) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 4, this->optargs(i), target); + } + + // Extension range [10000, 20001) + target = _extensions_.SerializeWithCachedSizesToArray( + 10000, 20001, target); + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int Term::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional .Term.TermType type = 1; + if (has_type()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::EnumSize(this->type()); + } + + // optional .Datum datum = 2; + if (has_datum()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->datum()); + } + + } + // repeated .Term args = 3; + total_size += 1 * this->args_size(); + for (int i = 0; i < this->args_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->args(i)); + } + + // repeated .Term.AssocPair optargs = 4; + total_size += 1 * this->optargs_size(); + for (int i = 0; i < this->optargs_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->optargs(i)); + } + + total_size += _extensions_.ByteSize(); + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void Term::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const Term* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void Term::MergeFrom(const Term& from) { + GOOGLE_CHECK_NE(&from, this); + args_.MergeFrom(from.args_); + optargs_.MergeFrom(from.optargs_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_type()) { + set_type(from.type()); + } + if (from.has_datum()) { + mutable_datum()->::Datum::MergeFrom(from.datum()); + } + } + _extensions_.MergeFrom(from._extensions_); + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void Term::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void Term::CopyFrom(const Term& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Term::IsInitialized() const { + + if (has_datum()) { + if (!this->datum().IsInitialized()) return false; + } + for (int i = 0; i < args_size(); i++) { + if (!this->args(i).IsInitialized()) return false; + } + for (int i = 0; i < optargs_size(); i++) { + if (!this->optargs(i).IsInitialized()) return false; + } + + if (!_extensions_.IsInitialized()) return false; return true; +} + +void Term::Swap(Term* other) { + if (other != this) { + std::swap(type_, other->type_); + std::swap(datum_, other->datum_); + args_.Swap(&other->args_); + optargs_.Swap(&other->optargs_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + _extensions_.Swap(&other->_extensions_); + } +} + +::google::protobuf::Metadata Term::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = Term_descriptor_; + metadata.reflection = Term_reflection_; + return metadata; +} + + +// @@protoc_insertion_point(namespace_scope) + +// @@protoc_insertion_point(global_scope) diff --git a/external/glim/ql2.pb.h b/external/glim/ql2.pb.h new file mode 100644 index 00000000..c8ac57a1 --- /dev/null +++ b/external/glim/ql2.pb.h @@ -0,0 +1,2532 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: ql2.proto + +#ifndef PROTOBUF_ql2_2eproto__INCLUDED +#define PROTOBUF_ql2_2eproto__INCLUDED + +#include + +#include + +#if GOOGLE_PROTOBUF_VERSION < 2004000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 2004001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +// Internal implementation detail -- do not call these. +void protobuf_AddDesc_ql2_2eproto(); +void protobuf_AssignDesc_ql2_2eproto(); +void protobuf_ShutdownFile_ql2_2eproto(); + +class VersionDummy; +class Query; +class Query_AssocPair; +class Frame; +class Backtrace; +class Response; +class Datum; +class Datum_AssocPair; +class Term; +class Term_AssocPair; + +enum VersionDummy_Version { + VersionDummy_Version_V0_1 = 1063369270, + VersionDummy_Version_V0_2 = 1915781601 +}; +bool VersionDummy_Version_IsValid(int value); +const VersionDummy_Version VersionDummy_Version_Version_MIN = VersionDummy_Version_V0_1; +const VersionDummy_Version VersionDummy_Version_Version_MAX = VersionDummy_Version_V0_2; +const int VersionDummy_Version_Version_ARRAYSIZE = VersionDummy_Version_Version_MAX + 1; + +const ::google::protobuf::EnumDescriptor* VersionDummy_Version_descriptor(); +inline const ::std::string& VersionDummy_Version_Name(VersionDummy_Version value) { + return ::google::protobuf::internal::NameOfEnum( + VersionDummy_Version_descriptor(), value); +} +inline bool VersionDummy_Version_Parse( + const ::std::string& name, VersionDummy_Version* value) { + return ::google::protobuf::internal::ParseNamedEnum( + VersionDummy_Version_descriptor(), name, value); +} +enum Query_QueryType { + Query_QueryType_START = 1, + Query_QueryType_CONTINUE = 2, + Query_QueryType_STOP = 3 +}; +bool Query_QueryType_IsValid(int value); +const Query_QueryType Query_QueryType_QueryType_MIN = Query_QueryType_START; +const Query_QueryType Query_QueryType_QueryType_MAX = Query_QueryType_STOP; +const int Query_QueryType_QueryType_ARRAYSIZE = Query_QueryType_QueryType_MAX + 1; + +const ::google::protobuf::EnumDescriptor* Query_QueryType_descriptor(); +inline const ::std::string& Query_QueryType_Name(Query_QueryType value) { + return ::google::protobuf::internal::NameOfEnum( + Query_QueryType_descriptor(), value); +} +inline bool Query_QueryType_Parse( + const ::std::string& name, Query_QueryType* value) { + return ::google::protobuf::internal::ParseNamedEnum( + Query_QueryType_descriptor(), name, value); +} +enum Frame_FrameType { + Frame_FrameType_POS = 1, + Frame_FrameType_OPT = 2 +}; +bool Frame_FrameType_IsValid(int value); +const Frame_FrameType Frame_FrameType_FrameType_MIN = Frame_FrameType_POS; +const Frame_FrameType Frame_FrameType_FrameType_MAX = Frame_FrameType_OPT; +const int Frame_FrameType_FrameType_ARRAYSIZE = Frame_FrameType_FrameType_MAX + 1; + +const ::google::protobuf::EnumDescriptor* Frame_FrameType_descriptor(); +inline const ::std::string& Frame_FrameType_Name(Frame_FrameType value) { + return ::google::protobuf::internal::NameOfEnum( + Frame_FrameType_descriptor(), value); +} +inline bool Frame_FrameType_Parse( + const ::std::string& name, Frame_FrameType* value) { + return ::google::protobuf::internal::ParseNamedEnum( + Frame_FrameType_descriptor(), name, value); +} +enum Response_ResponseType { + Response_ResponseType_SUCCESS_ATOM = 1, + Response_ResponseType_SUCCESS_SEQUENCE = 2, + Response_ResponseType_SUCCESS_PARTIAL = 3, + Response_ResponseType_CLIENT_ERROR = 16, + Response_ResponseType_COMPILE_ERROR = 17, + Response_ResponseType_RUNTIME_ERROR = 18 +}; +bool Response_ResponseType_IsValid(int value); +const Response_ResponseType Response_ResponseType_ResponseType_MIN = Response_ResponseType_SUCCESS_ATOM; +const Response_ResponseType Response_ResponseType_ResponseType_MAX = Response_ResponseType_RUNTIME_ERROR; +const int Response_ResponseType_ResponseType_ARRAYSIZE = Response_ResponseType_ResponseType_MAX + 1; + +const ::google::protobuf::EnumDescriptor* Response_ResponseType_descriptor(); +inline const ::std::string& Response_ResponseType_Name(Response_ResponseType value) { + return ::google::protobuf::internal::NameOfEnum( + Response_ResponseType_descriptor(), value); +} +inline bool Response_ResponseType_Parse( + const ::std::string& name, Response_ResponseType* value) { + return ::google::protobuf::internal::ParseNamedEnum( + Response_ResponseType_descriptor(), name, value); +} +enum Datum_DatumType { + Datum_DatumType_R_NULL = 1, + Datum_DatumType_R_BOOL = 2, + Datum_DatumType_R_NUM = 3, + Datum_DatumType_R_STR = 4, + Datum_DatumType_R_ARRAY = 5, + Datum_DatumType_R_OBJECT = 6 +}; +bool Datum_DatumType_IsValid(int value); +const Datum_DatumType Datum_DatumType_DatumType_MIN = Datum_DatumType_R_NULL; +const Datum_DatumType Datum_DatumType_DatumType_MAX = Datum_DatumType_R_OBJECT; +const int Datum_DatumType_DatumType_ARRAYSIZE = Datum_DatumType_DatumType_MAX + 1; + +const ::google::protobuf::EnumDescriptor* Datum_DatumType_descriptor(); +inline const ::std::string& Datum_DatumType_Name(Datum_DatumType value) { + return ::google::protobuf::internal::NameOfEnum( + Datum_DatumType_descriptor(), value); +} +inline bool Datum_DatumType_Parse( + const ::std::string& name, Datum_DatumType* value) { + return ::google::protobuf::internal::ParseNamedEnum( + Datum_DatumType_descriptor(), name, value); +} +enum Term_TermType { + Term_TermType_DATUM = 1, + Term_TermType_MAKE_ARRAY = 2, + Term_TermType_MAKE_OBJ = 3, + Term_TermType_VAR = 10, + Term_TermType_JAVASCRIPT = 11, + Term_TermType_ERROR = 12, + Term_TermType_IMPLICIT_VAR = 13, + Term_TermType_DB = 14, + Term_TermType_TABLE = 15, + Term_TermType_GET = 16, + Term_TermType_GET_ALL = 78, + Term_TermType_EQ = 17, + Term_TermType_NE = 18, + Term_TermType_LT = 19, + Term_TermType_LE = 20, + Term_TermType_GT = 21, + Term_TermType_GE = 22, + Term_TermType_NOT = 23, + Term_TermType_ADD = 24, + Term_TermType_SUB = 25, + Term_TermType_MUL = 26, + Term_TermType_DIV = 27, + Term_TermType_MOD = 28, + Term_TermType_APPEND = 29, + Term_TermType_PREPEND = 80, + Term_TermType_DIFFERENCE = 95, + Term_TermType_SET_INSERT = 88, + Term_TermType_SET_INTERSECTION = 89, + Term_TermType_SET_UNION = 90, + Term_TermType_SET_DIFFERENCE = 91, + Term_TermType_SLICE = 30, + Term_TermType_SKIP = 70, + Term_TermType_LIMIT = 71, + Term_TermType_INDEXES_OF = 87, + Term_TermType_CONTAINS = 93, + Term_TermType_GETATTR = 31, + Term_TermType_KEYS = 94, + Term_TermType_HAS_FIELDS = 32, + Term_TermType_WITH_FIELDS = 96, + Term_TermType_PLUCK = 33, + Term_TermType_WITHOUT = 34, + Term_TermType_MERGE = 35, + Term_TermType_BETWEEN = 36, + Term_TermType_REDUCE = 37, + Term_TermType_MAP = 38, + Term_TermType_FILTER = 39, + Term_TermType_CONCATMAP = 40, + Term_TermType_ORDERBY = 41, + Term_TermType_DISTINCT = 42, + Term_TermType_COUNT = 43, + Term_TermType_IS_EMPTY = 86, + Term_TermType_UNION = 44, + Term_TermType_NTH = 45, + Term_TermType_GROUPED_MAP_REDUCE = 46, + Term_TermType_GROUPBY = 47, + Term_TermType_INNER_JOIN = 48, + Term_TermType_OUTER_JOIN = 49, + Term_TermType_EQ_JOIN = 50, + Term_TermType_ZIP = 72, + Term_TermType_INSERT_AT = 82, + Term_TermType_DELETE_AT = 83, + Term_TermType_CHANGE_AT = 84, + Term_TermType_SPLICE_AT = 85, + Term_TermType_COERCE_TO = 51, + Term_TermType_TYPEOF = 52, + Term_TermType_UPDATE = 53, + Term_TermType_DELETE = 54, + Term_TermType_REPLACE = 55, + Term_TermType_INSERT = 56, + Term_TermType_DB_CREATE = 57, + Term_TermType_DB_DROP = 58, + Term_TermType_DB_LIST = 59, + Term_TermType_TABLE_CREATE = 60, + Term_TermType_TABLE_DROP = 61, + Term_TermType_TABLE_LIST = 62, + Term_TermType_INDEX_CREATE = 75, + Term_TermType_INDEX_DROP = 76, + Term_TermType_INDEX_LIST = 77, + Term_TermType_FUNCALL = 64, + Term_TermType_BRANCH = 65, + Term_TermType_ANY = 66, + Term_TermType_ALL = 67, + Term_TermType_FOREACH = 68, + Term_TermType_FUNC = 69, + Term_TermType_ASC = 73, + Term_TermType_DESC = 74, + Term_TermType_INFO = 79, + Term_TermType_MATCH = 97, + Term_TermType_SAMPLE = 81, + Term_TermType_DEFAULT = 92 +}; +bool Term_TermType_IsValid(int value); +const Term_TermType Term_TermType_TermType_MIN = Term_TermType_DATUM; +const Term_TermType Term_TermType_TermType_MAX = Term_TermType_MATCH; +const int Term_TermType_TermType_ARRAYSIZE = Term_TermType_TermType_MAX + 1; + +const ::google::protobuf::EnumDescriptor* Term_TermType_descriptor(); +inline const ::std::string& Term_TermType_Name(Term_TermType value) { + return ::google::protobuf::internal::NameOfEnum( + Term_TermType_descriptor(), value); +} +inline bool Term_TermType_Parse( + const ::std::string& name, Term_TermType* value) { + return ::google::protobuf::internal::ParseNamedEnum( + Term_TermType_descriptor(), name, value); +} +// =================================================================== + +class VersionDummy : public ::google::protobuf::Message { + public: + VersionDummy(); + virtual ~VersionDummy(); + + VersionDummy(const VersionDummy& from); + + inline VersionDummy& operator=(const VersionDummy& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const VersionDummy& default_instance(); + + void Swap(VersionDummy* other); + + // implements Message ---------------------------------------------- + + VersionDummy* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const VersionDummy& from); + void MergeFrom(const VersionDummy& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + typedef VersionDummy_Version Version; + static const Version V0_1 = VersionDummy_Version_V0_1; + static const Version V0_2 = VersionDummy_Version_V0_2; + static inline bool Version_IsValid(int value) { + return VersionDummy_Version_IsValid(value); + } + static const Version Version_MIN = + VersionDummy_Version_Version_MIN; + static const Version Version_MAX = + VersionDummy_Version_Version_MAX; + static const int Version_ARRAYSIZE = + VersionDummy_Version_Version_ARRAYSIZE; + static inline const ::google::protobuf::EnumDescriptor* + Version_descriptor() { + return VersionDummy_Version_descriptor(); + } + static inline const ::std::string& Version_Name(Version value) { + return VersionDummy_Version_Name(value); + } + static inline bool Version_Parse(const ::std::string& name, + Version* value) { + return VersionDummy_Version_Parse(name, value); + } + + // accessors ------------------------------------------------------- + + // @@protoc_insertion_point(class_scope:VersionDummy) + private: + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[1]; + + friend void protobuf_AddDesc_ql2_2eproto(); + friend void protobuf_AssignDesc_ql2_2eproto(); + friend void protobuf_ShutdownFile_ql2_2eproto(); + + void InitAsDefaultInstance(); + static VersionDummy* default_instance_; +}; +// ------------------------------------------------------------------- + +class Query_AssocPair : public ::google::protobuf::Message { + public: + Query_AssocPair(); + virtual ~Query_AssocPair(); + + Query_AssocPair(const Query_AssocPair& from); + + inline Query_AssocPair& operator=(const Query_AssocPair& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const Query_AssocPair& default_instance(); + + void Swap(Query_AssocPair* other); + + // implements Message ---------------------------------------------- + + Query_AssocPair* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const Query_AssocPair& from); + void MergeFrom(const Query_AssocPair& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string key = 1; + inline bool has_key() const; + inline void clear_key(); + static const int kKeyFieldNumber = 1; + inline const ::std::string& key() const; + inline void set_key(const ::std::string& value); + inline void set_key(const char* value); + inline void set_key(const char* value, size_t size); + inline ::std::string* mutable_key(); + inline ::std::string* release_key(); + + // optional .Term val = 2; + inline bool has_val() const; + inline void clear_val(); + static const int kValFieldNumber = 2; + inline const ::Term& val() const; + inline ::Term* mutable_val(); + inline ::Term* release_val(); + + // @@protoc_insertion_point(class_scope:Query.AssocPair) + private: + inline void set_has_key(); + inline void clear_has_key(); + inline void set_has_val(); + inline void clear_has_val(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::std::string* key_; + ::Term* val_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32]; + + friend void protobuf_AddDesc_ql2_2eproto(); + friend void protobuf_AssignDesc_ql2_2eproto(); + friend void protobuf_ShutdownFile_ql2_2eproto(); + + void InitAsDefaultInstance(); + static Query_AssocPair* default_instance_; +}; +// ------------------------------------------------------------------- + +class Query : public ::google::protobuf::Message { + public: + Query(); + virtual ~Query(); + + Query(const Query& from); + + inline Query& operator=(const Query& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const Query& default_instance(); + + void Swap(Query* other); + + // implements Message ---------------------------------------------- + + Query* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const Query& from); + void MergeFrom(const Query& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + typedef Query_AssocPair AssocPair; + + typedef Query_QueryType QueryType; + static const QueryType START = Query_QueryType_START; + static const QueryType CONTINUE = Query_QueryType_CONTINUE; + static const QueryType STOP = Query_QueryType_STOP; + static inline bool QueryType_IsValid(int value) { + return Query_QueryType_IsValid(value); + } + static const QueryType QueryType_MIN = + Query_QueryType_QueryType_MIN; + static const QueryType QueryType_MAX = + Query_QueryType_QueryType_MAX; + static const int QueryType_ARRAYSIZE = + Query_QueryType_QueryType_ARRAYSIZE; + static inline const ::google::protobuf::EnumDescriptor* + QueryType_descriptor() { + return Query_QueryType_descriptor(); + } + static inline const ::std::string& QueryType_Name(QueryType value) { + return Query_QueryType_Name(value); + } + static inline bool QueryType_Parse(const ::std::string& name, + QueryType* value) { + return Query_QueryType_Parse(name, value); + } + + // accessors ------------------------------------------------------- + + // optional .Query.QueryType type = 1; + inline bool has_type() const; + inline void clear_type(); + static const int kTypeFieldNumber = 1; + inline ::Query_QueryType type() const; + inline void set_type(::Query_QueryType value); + + // optional .Term query = 2; + inline bool has_query() const; + inline void clear_query(); + static const int kQueryFieldNumber = 2; + inline const ::Term& query() const; + inline ::Term* mutable_query(); + inline ::Term* release_query(); + + // optional int64 token = 3; + inline bool has_token() const; + inline void clear_token(); + static const int kTokenFieldNumber = 3; + inline ::google::protobuf::int64 token() const; + inline void set_token(::google::protobuf::int64 value); + + // optional bool OBSOLETE_noreply = 4 [default = false]; + inline bool has_obsolete_noreply() const; + inline void clear_obsolete_noreply(); + static const int kOBSOLETENoreplyFieldNumber = 4; + inline bool obsolete_noreply() const; + inline void set_obsolete_noreply(bool value); + + // repeated .Query.AssocPair global_optargs = 6; + inline int global_optargs_size() const; + inline void clear_global_optargs(); + static const int kGlobalOptargsFieldNumber = 6; + inline const ::Query_AssocPair& global_optargs(int index) const; + inline ::Query_AssocPair* mutable_global_optargs(int index); + inline ::Query_AssocPair* add_global_optargs(); + inline const ::google::protobuf::RepeatedPtrField< ::Query_AssocPair >& + global_optargs() const; + inline ::google::protobuf::RepeatedPtrField< ::Query_AssocPair >* + mutable_global_optargs(); + + // @@protoc_insertion_point(class_scope:Query) + private: + inline void set_has_type(); + inline void clear_has_type(); + inline void set_has_query(); + inline void clear_has_query(); + inline void set_has_token(); + inline void clear_has_token(); + inline void set_has_obsolete_noreply(); + inline void clear_has_obsolete_noreply(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::Term* query_; + int type_; + bool obsolete_noreply_; + ::google::protobuf::int64 token_; + ::google::protobuf::RepeatedPtrField< ::Query_AssocPair > global_optargs_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(5 + 31) / 32]; + + friend void protobuf_AddDesc_ql2_2eproto(); + friend void protobuf_AssignDesc_ql2_2eproto(); + friend void protobuf_ShutdownFile_ql2_2eproto(); + + void InitAsDefaultInstance(); + static Query* default_instance_; +}; +// ------------------------------------------------------------------- + +class Frame : public ::google::protobuf::Message { + public: + Frame(); + virtual ~Frame(); + + Frame(const Frame& from); + + inline Frame& operator=(const Frame& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const Frame& default_instance(); + + void Swap(Frame* other); + + // implements Message ---------------------------------------------- + + Frame* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const Frame& from); + void MergeFrom(const Frame& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + typedef Frame_FrameType FrameType; + static const FrameType POS = Frame_FrameType_POS; + static const FrameType OPT = Frame_FrameType_OPT; + static inline bool FrameType_IsValid(int value) { + return Frame_FrameType_IsValid(value); + } + static const FrameType FrameType_MIN = + Frame_FrameType_FrameType_MIN; + static const FrameType FrameType_MAX = + Frame_FrameType_FrameType_MAX; + static const int FrameType_ARRAYSIZE = + Frame_FrameType_FrameType_ARRAYSIZE; + static inline const ::google::protobuf::EnumDescriptor* + FrameType_descriptor() { + return Frame_FrameType_descriptor(); + } + static inline const ::std::string& FrameType_Name(FrameType value) { + return Frame_FrameType_Name(value); + } + static inline bool FrameType_Parse(const ::std::string& name, + FrameType* value) { + return Frame_FrameType_Parse(name, value); + } + + // accessors ------------------------------------------------------- + + // optional .Frame.FrameType type = 1; + inline bool has_type() const; + inline void clear_type(); + static const int kTypeFieldNumber = 1; + inline ::Frame_FrameType type() const; + inline void set_type(::Frame_FrameType value); + + // optional int64 pos = 2; + inline bool has_pos() const; + inline void clear_pos(); + static const int kPosFieldNumber = 2; + inline ::google::protobuf::int64 pos() const; + inline void set_pos(::google::protobuf::int64 value); + + // optional string opt = 3; + inline bool has_opt() const; + inline void clear_opt(); + static const int kOptFieldNumber = 3; + inline const ::std::string& opt() const; + inline void set_opt(const ::std::string& value); + inline void set_opt(const char* value); + inline void set_opt(const char* value, size_t size); + inline ::std::string* mutable_opt(); + inline ::std::string* release_opt(); + + // @@protoc_insertion_point(class_scope:Frame) + private: + inline void set_has_type(); + inline void clear_has_type(); + inline void set_has_pos(); + inline void clear_has_pos(); + inline void set_has_opt(); + inline void clear_has_opt(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::google::protobuf::int64 pos_; + ::std::string* opt_; + int type_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; + + friend void protobuf_AddDesc_ql2_2eproto(); + friend void protobuf_AssignDesc_ql2_2eproto(); + friend void protobuf_ShutdownFile_ql2_2eproto(); + + void InitAsDefaultInstance(); + static Frame* default_instance_; +}; +// ------------------------------------------------------------------- + +class Backtrace : public ::google::protobuf::Message { + public: + Backtrace(); + virtual ~Backtrace(); + + Backtrace(const Backtrace& from); + + inline Backtrace& operator=(const Backtrace& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const Backtrace& default_instance(); + + void Swap(Backtrace* other); + + // implements Message ---------------------------------------------- + + Backtrace* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const Backtrace& from); + void MergeFrom(const Backtrace& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // repeated .Frame frames = 1; + inline int frames_size() const; + inline void clear_frames(); + static const int kFramesFieldNumber = 1; + inline const ::Frame& frames(int index) const; + inline ::Frame* mutable_frames(int index); + inline ::Frame* add_frames(); + inline const ::google::protobuf::RepeatedPtrField< ::Frame >& + frames() const; + inline ::google::protobuf::RepeatedPtrField< ::Frame >* + mutable_frames(); + + // @@protoc_insertion_point(class_scope:Backtrace) + private: + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::google::protobuf::RepeatedPtrField< ::Frame > frames_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32]; + + friend void protobuf_AddDesc_ql2_2eproto(); + friend void protobuf_AssignDesc_ql2_2eproto(); + friend void protobuf_ShutdownFile_ql2_2eproto(); + + void InitAsDefaultInstance(); + static Backtrace* default_instance_; +}; +// ------------------------------------------------------------------- + +class Response : public ::google::protobuf::Message { + public: + Response(); + virtual ~Response(); + + Response(const Response& from); + + inline Response& operator=(const Response& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const Response& default_instance(); + + void Swap(Response* other); + + // implements Message ---------------------------------------------- + + Response* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const Response& from); + void MergeFrom(const Response& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + typedef Response_ResponseType ResponseType; + static const ResponseType SUCCESS_ATOM = Response_ResponseType_SUCCESS_ATOM; + static const ResponseType SUCCESS_SEQUENCE = Response_ResponseType_SUCCESS_SEQUENCE; + static const ResponseType SUCCESS_PARTIAL = Response_ResponseType_SUCCESS_PARTIAL; + static const ResponseType CLIENT_ERROR = Response_ResponseType_CLIENT_ERROR; + static const ResponseType COMPILE_ERROR = Response_ResponseType_COMPILE_ERROR; + static const ResponseType RUNTIME_ERROR = Response_ResponseType_RUNTIME_ERROR; + static inline bool ResponseType_IsValid(int value) { + return Response_ResponseType_IsValid(value); + } + static const ResponseType ResponseType_MIN = + Response_ResponseType_ResponseType_MIN; + static const ResponseType ResponseType_MAX = + Response_ResponseType_ResponseType_MAX; + static const int ResponseType_ARRAYSIZE = + Response_ResponseType_ResponseType_ARRAYSIZE; + static inline const ::google::protobuf::EnumDescriptor* + ResponseType_descriptor() { + return Response_ResponseType_descriptor(); + } + static inline const ::std::string& ResponseType_Name(ResponseType value) { + return Response_ResponseType_Name(value); + } + static inline bool ResponseType_Parse(const ::std::string& name, + ResponseType* value) { + return Response_ResponseType_Parse(name, value); + } + + // accessors ------------------------------------------------------- + + // optional .Response.ResponseType type = 1; + inline bool has_type() const; + inline void clear_type(); + static const int kTypeFieldNumber = 1; + inline ::Response_ResponseType type() const; + inline void set_type(::Response_ResponseType value); + + // optional int64 token = 2; + inline bool has_token() const; + inline void clear_token(); + static const int kTokenFieldNumber = 2; + inline ::google::protobuf::int64 token() const; + inline void set_token(::google::protobuf::int64 value); + + // repeated .Datum response = 3; + inline int response_size() const; + inline void clear_response(); + static const int kResponseFieldNumber = 3; + inline const ::Datum& response(int index) const; + inline ::Datum* mutable_response(int index); + inline ::Datum* add_response(); + inline const ::google::protobuf::RepeatedPtrField< ::Datum >& + response() const; + inline ::google::protobuf::RepeatedPtrField< ::Datum >* + mutable_response(); + + // optional .Backtrace backtrace = 4; + inline bool has_backtrace() const; + inline void clear_backtrace(); + static const int kBacktraceFieldNumber = 4; + inline const ::Backtrace& backtrace() const; + inline ::Backtrace* mutable_backtrace(); + inline ::Backtrace* release_backtrace(); + + // @@protoc_insertion_point(class_scope:Response) + private: + inline void set_has_type(); + inline void clear_has_type(); + inline void set_has_token(); + inline void clear_has_token(); + inline void set_has_backtrace(); + inline void clear_has_backtrace(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::google::protobuf::int64 token_; + ::google::protobuf::RepeatedPtrField< ::Datum > response_; + ::Backtrace* backtrace_; + int type_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32]; + + friend void protobuf_AddDesc_ql2_2eproto(); + friend void protobuf_AssignDesc_ql2_2eproto(); + friend void protobuf_ShutdownFile_ql2_2eproto(); + + void InitAsDefaultInstance(); + static Response* default_instance_; +}; +// ------------------------------------------------------------------- + +class Datum_AssocPair : public ::google::protobuf::Message { + public: + Datum_AssocPair(); + virtual ~Datum_AssocPair(); + + Datum_AssocPair(const Datum_AssocPair& from); + + inline Datum_AssocPair& operator=(const Datum_AssocPair& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const Datum_AssocPair& default_instance(); + + void Swap(Datum_AssocPair* other); + + // implements Message ---------------------------------------------- + + Datum_AssocPair* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const Datum_AssocPair& from); + void MergeFrom(const Datum_AssocPair& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string key = 1; + inline bool has_key() const; + inline void clear_key(); + static const int kKeyFieldNumber = 1; + inline const ::std::string& key() const; + inline void set_key(const ::std::string& value); + inline void set_key(const char* value); + inline void set_key(const char* value, size_t size); + inline ::std::string* mutable_key(); + inline ::std::string* release_key(); + + // optional .Datum val = 2; + inline bool has_val() const; + inline void clear_val(); + static const int kValFieldNumber = 2; + inline const ::Datum& val() const; + inline ::Datum* mutable_val(); + inline ::Datum* release_val(); + + // @@protoc_insertion_point(class_scope:Datum.AssocPair) + private: + inline void set_has_key(); + inline void clear_has_key(); + inline void set_has_val(); + inline void clear_has_val(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::std::string* key_; + ::Datum* val_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32]; + + friend void protobuf_AddDesc_ql2_2eproto(); + friend void protobuf_AssignDesc_ql2_2eproto(); + friend void protobuf_ShutdownFile_ql2_2eproto(); + + void InitAsDefaultInstance(); + static Datum_AssocPair* default_instance_; +}; +// ------------------------------------------------------------------- + +class Datum : public ::google::protobuf::Message { + public: + Datum(); + virtual ~Datum(); + + Datum(const Datum& from); + + inline Datum& operator=(const Datum& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const Datum& default_instance(); + + void Swap(Datum* other); + + // implements Message ---------------------------------------------- + + Datum* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const Datum& from); + void MergeFrom(const Datum& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + typedef Datum_AssocPair AssocPair; + + typedef Datum_DatumType DatumType; + static const DatumType R_NULL = Datum_DatumType_R_NULL; + static const DatumType R_BOOL = Datum_DatumType_R_BOOL; + static const DatumType R_NUM = Datum_DatumType_R_NUM; + static const DatumType R_STR = Datum_DatumType_R_STR; + static const DatumType R_ARRAY = Datum_DatumType_R_ARRAY; + static const DatumType R_OBJECT = Datum_DatumType_R_OBJECT; + static inline bool DatumType_IsValid(int value) { + return Datum_DatumType_IsValid(value); + } + static const DatumType DatumType_MIN = + Datum_DatumType_DatumType_MIN; + static const DatumType DatumType_MAX = + Datum_DatumType_DatumType_MAX; + static const int DatumType_ARRAYSIZE = + Datum_DatumType_DatumType_ARRAYSIZE; + static inline const ::google::protobuf::EnumDescriptor* + DatumType_descriptor() { + return Datum_DatumType_descriptor(); + } + static inline const ::std::string& DatumType_Name(DatumType value) { + return Datum_DatumType_Name(value); + } + static inline bool DatumType_Parse(const ::std::string& name, + DatumType* value) { + return Datum_DatumType_Parse(name, value); + } + + // accessors ------------------------------------------------------- + + // optional .Datum.DatumType type = 1; + inline bool has_type() const; + inline void clear_type(); + static const int kTypeFieldNumber = 1; + inline ::Datum_DatumType type() const; + inline void set_type(::Datum_DatumType value); + + // optional bool r_bool = 2; + inline bool has_r_bool() const; + inline void clear_r_bool(); + static const int kRBoolFieldNumber = 2; + inline bool r_bool() const; + inline void set_r_bool(bool value); + + // optional double r_num = 3; + inline bool has_r_num() const; + inline void clear_r_num(); + static const int kRNumFieldNumber = 3; + inline double r_num() const; + inline void set_r_num(double value); + + // optional string r_str = 4; + inline bool has_r_str() const; + inline void clear_r_str(); + static const int kRStrFieldNumber = 4; + inline const ::std::string& r_str() const; + inline void set_r_str(const ::std::string& value); + inline void set_r_str(const char* value); + inline void set_r_str(const char* value, size_t size); + inline ::std::string* mutable_r_str(); + inline ::std::string* release_r_str(); + + // repeated .Datum r_array = 5; + inline int r_array_size() const; + inline void clear_r_array(); + static const int kRArrayFieldNumber = 5; + inline const ::Datum& r_array(int index) const; + inline ::Datum* mutable_r_array(int index); + inline ::Datum* add_r_array(); + inline const ::google::protobuf::RepeatedPtrField< ::Datum >& + r_array() const; + inline ::google::protobuf::RepeatedPtrField< ::Datum >* + mutable_r_array(); + + // repeated .Datum.AssocPair r_object = 6; + inline int r_object_size() const; + inline void clear_r_object(); + static const int kRObjectFieldNumber = 6; + inline const ::Datum_AssocPair& r_object(int index) const; + inline ::Datum_AssocPair* mutable_r_object(int index); + inline ::Datum_AssocPair* add_r_object(); + inline const ::google::protobuf::RepeatedPtrField< ::Datum_AssocPair >& + r_object() const; + inline ::google::protobuf::RepeatedPtrField< ::Datum_AssocPair >* + mutable_r_object(); + + GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(Datum) + // @@protoc_insertion_point(class_scope:Datum) + private: + inline void set_has_type(); + inline void clear_has_type(); + inline void set_has_r_bool(); + inline void clear_has_r_bool(); + inline void set_has_r_num(); + inline void clear_has_r_num(); + inline void set_has_r_str(); + inline void clear_has_r_str(); + + ::google::protobuf::internal::ExtensionSet _extensions_; + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + int type_; + bool r_bool_; + double r_num_; + ::std::string* r_str_; + ::google::protobuf::RepeatedPtrField< ::Datum > r_array_; + ::google::protobuf::RepeatedPtrField< ::Datum_AssocPair > r_object_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(6 + 31) / 32]; + + friend void protobuf_AddDesc_ql2_2eproto(); + friend void protobuf_AssignDesc_ql2_2eproto(); + friend void protobuf_ShutdownFile_ql2_2eproto(); + + void InitAsDefaultInstance(); + static Datum* default_instance_; +}; +// ------------------------------------------------------------------- + +class Term_AssocPair : public ::google::protobuf::Message { + public: + Term_AssocPair(); + virtual ~Term_AssocPair(); + + Term_AssocPair(const Term_AssocPair& from); + + inline Term_AssocPair& operator=(const Term_AssocPair& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const Term_AssocPair& default_instance(); + + void Swap(Term_AssocPair* other); + + // implements Message ---------------------------------------------- + + Term_AssocPair* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const Term_AssocPair& from); + void MergeFrom(const Term_AssocPair& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string key = 1; + inline bool has_key() const; + inline void clear_key(); + static const int kKeyFieldNumber = 1; + inline const ::std::string& key() const; + inline void set_key(const ::std::string& value); + inline void set_key(const char* value); + inline void set_key(const char* value, size_t size); + inline ::std::string* mutable_key(); + inline ::std::string* release_key(); + + // optional .Term val = 2; + inline bool has_val() const; + inline void clear_val(); + static const int kValFieldNumber = 2; + inline const ::Term& val() const; + inline ::Term* mutable_val(); + inline ::Term* release_val(); + + // @@protoc_insertion_point(class_scope:Term.AssocPair) + private: + inline void set_has_key(); + inline void clear_has_key(); + inline void set_has_val(); + inline void clear_has_val(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::std::string* key_; + ::Term* val_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32]; + + friend void protobuf_AddDesc_ql2_2eproto(); + friend void protobuf_AssignDesc_ql2_2eproto(); + friend void protobuf_ShutdownFile_ql2_2eproto(); + + void InitAsDefaultInstance(); + static Term_AssocPair* default_instance_; +}; +// ------------------------------------------------------------------- + +class Term : public ::google::protobuf::Message { + public: + Term(); + virtual ~Term(); + + Term(const Term& from); + + inline Term& operator=(const Term& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const Term& default_instance(); + + void Swap(Term* other); + + // implements Message ---------------------------------------------- + + Term* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const Term& from); + void MergeFrom(const Term& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + typedef Term_AssocPair AssocPair; + + typedef Term_TermType TermType; + static const TermType DATUM = Term_TermType_DATUM; + static const TermType MAKE_ARRAY = Term_TermType_MAKE_ARRAY; + static const TermType MAKE_OBJ = Term_TermType_MAKE_OBJ; + static const TermType VAR = Term_TermType_VAR; + static const TermType JAVASCRIPT = Term_TermType_JAVASCRIPT; + static const TermType ERROR = Term_TermType_ERROR; + static const TermType IMPLICIT_VAR = Term_TermType_IMPLICIT_VAR; + static const TermType DB = Term_TermType_DB; + static const TermType TABLE = Term_TermType_TABLE; + static const TermType GET = Term_TermType_GET; + static const TermType GET_ALL = Term_TermType_GET_ALL; + static const TermType EQ = Term_TermType_EQ; + static const TermType NE = Term_TermType_NE; + static const TermType LT = Term_TermType_LT; + static const TermType LE = Term_TermType_LE; + static const TermType GT = Term_TermType_GT; + static const TermType GE = Term_TermType_GE; + static const TermType NOT = Term_TermType_NOT; + static const TermType ADD = Term_TermType_ADD; + static const TermType SUB = Term_TermType_SUB; + static const TermType MUL = Term_TermType_MUL; + static const TermType DIV = Term_TermType_DIV; + static const TermType MOD = Term_TermType_MOD; + static const TermType APPEND = Term_TermType_APPEND; + static const TermType PREPEND = Term_TermType_PREPEND; + static const TermType DIFFERENCE = Term_TermType_DIFFERENCE; + static const TermType SET_INSERT = Term_TermType_SET_INSERT; + static const TermType SET_INTERSECTION = Term_TermType_SET_INTERSECTION; + static const TermType SET_UNION = Term_TermType_SET_UNION; + static const TermType SET_DIFFERENCE = Term_TermType_SET_DIFFERENCE; + static const TermType SLICE = Term_TermType_SLICE; + static const TermType SKIP = Term_TermType_SKIP; + static const TermType LIMIT = Term_TermType_LIMIT; + static const TermType INDEXES_OF = Term_TermType_INDEXES_OF; + static const TermType CONTAINS = Term_TermType_CONTAINS; + static const TermType GETATTR = Term_TermType_GETATTR; + static const TermType KEYS = Term_TermType_KEYS; + static const TermType HAS_FIELDS = Term_TermType_HAS_FIELDS; + static const TermType WITH_FIELDS = Term_TermType_WITH_FIELDS; + static const TermType PLUCK = Term_TermType_PLUCK; + static const TermType WITHOUT = Term_TermType_WITHOUT; + static const TermType MERGE = Term_TermType_MERGE; + static const TermType BETWEEN = Term_TermType_BETWEEN; + static const TermType REDUCE = Term_TermType_REDUCE; + static const TermType MAP = Term_TermType_MAP; + static const TermType FILTER = Term_TermType_FILTER; + static const TermType CONCATMAP = Term_TermType_CONCATMAP; + static const TermType ORDERBY = Term_TermType_ORDERBY; + static const TermType DISTINCT = Term_TermType_DISTINCT; + static const TermType COUNT = Term_TermType_COUNT; + static const TermType IS_EMPTY = Term_TermType_IS_EMPTY; + static const TermType UNION = Term_TermType_UNION; + static const TermType NTH = Term_TermType_NTH; + static const TermType GROUPED_MAP_REDUCE = Term_TermType_GROUPED_MAP_REDUCE; + static const TermType GROUPBY = Term_TermType_GROUPBY; + static const TermType INNER_JOIN = Term_TermType_INNER_JOIN; + static const TermType OUTER_JOIN = Term_TermType_OUTER_JOIN; + static const TermType EQ_JOIN = Term_TermType_EQ_JOIN; + static const TermType ZIP = Term_TermType_ZIP; + static const TermType INSERT_AT = Term_TermType_INSERT_AT; + static const TermType DELETE_AT = Term_TermType_DELETE_AT; + static const TermType CHANGE_AT = Term_TermType_CHANGE_AT; + static const TermType SPLICE_AT = Term_TermType_SPLICE_AT; + static const TermType COERCE_TO = Term_TermType_COERCE_TO; + static const TermType TYPEOF = Term_TermType_TYPEOF; + static const TermType UPDATE = Term_TermType_UPDATE; + static const TermType DELETE = Term_TermType_DELETE; + static const TermType REPLACE = Term_TermType_REPLACE; + static const TermType INSERT = Term_TermType_INSERT; + static const TermType DB_CREATE = Term_TermType_DB_CREATE; + static const TermType DB_DROP = Term_TermType_DB_DROP; + static const TermType DB_LIST = Term_TermType_DB_LIST; + static const TermType TABLE_CREATE = Term_TermType_TABLE_CREATE; + static const TermType TABLE_DROP = Term_TermType_TABLE_DROP; + static const TermType TABLE_LIST = Term_TermType_TABLE_LIST; + static const TermType INDEX_CREATE = Term_TermType_INDEX_CREATE; + static const TermType INDEX_DROP = Term_TermType_INDEX_DROP; + static const TermType INDEX_LIST = Term_TermType_INDEX_LIST; + static const TermType FUNCALL = Term_TermType_FUNCALL; + static const TermType BRANCH = Term_TermType_BRANCH; + static const TermType ANY = Term_TermType_ANY; + static const TermType ALL = Term_TermType_ALL; + static const TermType FOREACH = Term_TermType_FOREACH; + static const TermType FUNC = Term_TermType_FUNC; + static const TermType ASC = Term_TermType_ASC; + static const TermType DESC = Term_TermType_DESC; + static const TermType INFO = Term_TermType_INFO; + static const TermType MATCH = Term_TermType_MATCH; + static const TermType SAMPLE = Term_TermType_SAMPLE; + static const TermType DEFAULT = Term_TermType_DEFAULT; + static inline bool TermType_IsValid(int value) { + return Term_TermType_IsValid(value); + } + static const TermType TermType_MIN = + Term_TermType_TermType_MIN; + static const TermType TermType_MAX = + Term_TermType_TermType_MAX; + static const int TermType_ARRAYSIZE = + Term_TermType_TermType_ARRAYSIZE; + static inline const ::google::protobuf::EnumDescriptor* + TermType_descriptor() { + return Term_TermType_descriptor(); + } + static inline const ::std::string& TermType_Name(TermType value) { + return Term_TermType_Name(value); + } + static inline bool TermType_Parse(const ::std::string& name, + TermType* value) { + return Term_TermType_Parse(name, value); + } + + // accessors ------------------------------------------------------- + + // optional .Term.TermType type = 1; + inline bool has_type() const; + inline void clear_type(); + static const int kTypeFieldNumber = 1; + inline ::Term_TermType type() const; + inline void set_type(::Term_TermType value); + + // optional .Datum datum = 2; + inline bool has_datum() const; + inline void clear_datum(); + static const int kDatumFieldNumber = 2; + inline const ::Datum& datum() const; + inline ::Datum* mutable_datum(); + inline ::Datum* release_datum(); + + // repeated .Term args = 3; + inline int args_size() const; + inline void clear_args(); + static const int kArgsFieldNumber = 3; + inline const ::Term& args(int index) const; + inline ::Term* mutable_args(int index); + inline ::Term* add_args(); + inline const ::google::protobuf::RepeatedPtrField< ::Term >& + args() const; + inline ::google::protobuf::RepeatedPtrField< ::Term >* + mutable_args(); + + // repeated .Term.AssocPair optargs = 4; + inline int optargs_size() const; + inline void clear_optargs(); + static const int kOptargsFieldNumber = 4; + inline const ::Term_AssocPair& optargs(int index) const; + inline ::Term_AssocPair* mutable_optargs(int index); + inline ::Term_AssocPair* add_optargs(); + inline const ::google::protobuf::RepeatedPtrField< ::Term_AssocPair >& + optargs() const; + inline ::google::protobuf::RepeatedPtrField< ::Term_AssocPair >* + mutable_optargs(); + + GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(Term) + // @@protoc_insertion_point(class_scope:Term) + private: + inline void set_has_type(); + inline void clear_has_type(); + inline void set_has_datum(); + inline void clear_has_datum(); + + ::google::protobuf::internal::ExtensionSet _extensions_; + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::Datum* datum_; + ::google::protobuf::RepeatedPtrField< ::Term > args_; + ::google::protobuf::RepeatedPtrField< ::Term_AssocPair > optargs_; + int type_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32]; + + friend void protobuf_AddDesc_ql2_2eproto(); + friend void protobuf_AssignDesc_ql2_2eproto(); + friend void protobuf_ShutdownFile_ql2_2eproto(); + + void InitAsDefaultInstance(); + static Term* default_instance_; +}; +// =================================================================== + + +// =================================================================== + +// VersionDummy + +// ------------------------------------------------------------------- + +// Query_AssocPair + +// optional string key = 1; +inline bool Query_AssocPair::has_key() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void Query_AssocPair::set_has_key() { + _has_bits_[0] |= 0x00000001u; +} +inline void Query_AssocPair::clear_has_key() { + _has_bits_[0] &= ~0x00000001u; +} +inline void Query_AssocPair::clear_key() { + if (key_ != &::google::protobuf::internal::kEmptyString) { + key_->clear(); + } + clear_has_key(); +} +inline const ::std::string& Query_AssocPair::key() const { + return *key_; +} +inline void Query_AssocPair::set_key(const ::std::string& value) { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + key_->assign(value); +} +inline void Query_AssocPair::set_key(const char* value) { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + key_->assign(value); +} +inline void Query_AssocPair::set_key(const char* value, size_t size) { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + key_->assign(reinterpret_cast(value), size); +} +inline ::std::string* Query_AssocPair::mutable_key() { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + return key_; +} +inline ::std::string* Query_AssocPair::release_key() { + clear_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = key_; + key_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} + +// optional .Term val = 2; +inline bool Query_AssocPair::has_val() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void Query_AssocPair::set_has_val() { + _has_bits_[0] |= 0x00000002u; +} +inline void Query_AssocPair::clear_has_val() { + _has_bits_[0] &= ~0x00000002u; +} +inline void Query_AssocPair::clear_val() { + if (val_ != NULL) val_->::Term::Clear(); + clear_has_val(); +} +inline const ::Term& Query_AssocPair::val() const { + return val_ != NULL ? *val_ : *default_instance_->val_; +} +inline ::Term* Query_AssocPair::mutable_val() { + set_has_val(); + if (val_ == NULL) val_ = new ::Term; + return val_; +} +inline ::Term* Query_AssocPair::release_val() { + clear_has_val(); + ::Term* temp = val_; + val_ = NULL; + return temp; +} + +// ------------------------------------------------------------------- + +// Query + +// optional .Query.QueryType type = 1; +inline bool Query::has_type() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void Query::set_has_type() { + _has_bits_[0] |= 0x00000001u; +} +inline void Query::clear_has_type() { + _has_bits_[0] &= ~0x00000001u; +} +inline void Query::clear_type() { + type_ = 1; + clear_has_type(); +} +inline ::Query_QueryType Query::type() const { + return static_cast< ::Query_QueryType >(type_); +} +inline void Query::set_type(::Query_QueryType value) { + GOOGLE_DCHECK(::Query_QueryType_IsValid(value)); + set_has_type(); + type_ = value; +} + +// optional .Term query = 2; +inline bool Query::has_query() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void Query::set_has_query() { + _has_bits_[0] |= 0x00000002u; +} +inline void Query::clear_has_query() { + _has_bits_[0] &= ~0x00000002u; +} +inline void Query::clear_query() { + if (query_ != NULL) query_->::Term::Clear(); + clear_has_query(); +} +inline const ::Term& Query::query() const { + return query_ != NULL ? *query_ : *default_instance_->query_; +} +inline ::Term* Query::mutable_query() { + set_has_query(); + if (query_ == NULL) query_ = new ::Term; + return query_; +} +inline ::Term* Query::release_query() { + clear_has_query(); + ::Term* temp = query_; + query_ = NULL; + return temp; +} + +// optional int64 token = 3; +inline bool Query::has_token() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void Query::set_has_token() { + _has_bits_[0] |= 0x00000004u; +} +inline void Query::clear_has_token() { + _has_bits_[0] &= ~0x00000004u; +} +inline void Query::clear_token() { + token_ = GOOGLE_LONGLONG(0); + clear_has_token(); +} +inline ::google::protobuf::int64 Query::token() const { + return token_; +} +inline void Query::set_token(::google::protobuf::int64 value) { + set_has_token(); + token_ = value; +} + +// optional bool OBSOLETE_noreply = 4 [default = false]; +inline bool Query::has_obsolete_noreply() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void Query::set_has_obsolete_noreply() { + _has_bits_[0] |= 0x00000008u; +} +inline void Query::clear_has_obsolete_noreply() { + _has_bits_[0] &= ~0x00000008u; +} +inline void Query::clear_obsolete_noreply() { + obsolete_noreply_ = false; + clear_has_obsolete_noreply(); +} +inline bool Query::obsolete_noreply() const { + return obsolete_noreply_; +} +inline void Query::set_obsolete_noreply(bool value) { + set_has_obsolete_noreply(); + obsolete_noreply_ = value; +} + +// repeated .Query.AssocPair global_optargs = 6; +inline int Query::global_optargs_size() const { + return global_optargs_.size(); +} +inline void Query::clear_global_optargs() { + global_optargs_.Clear(); +} +inline const ::Query_AssocPair& Query::global_optargs(int index) const { + return global_optargs_.Get(index); +} +inline ::Query_AssocPair* Query::mutable_global_optargs(int index) { + return global_optargs_.Mutable(index); +} +inline ::Query_AssocPair* Query::add_global_optargs() { + return global_optargs_.Add(); +} +inline const ::google::protobuf::RepeatedPtrField< ::Query_AssocPair >& +Query::global_optargs() const { + return global_optargs_; +} +inline ::google::protobuf::RepeatedPtrField< ::Query_AssocPair >* +Query::mutable_global_optargs() { + return &global_optargs_; +} + +// ------------------------------------------------------------------- + +// Frame + +// optional .Frame.FrameType type = 1; +inline bool Frame::has_type() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void Frame::set_has_type() { + _has_bits_[0] |= 0x00000001u; +} +inline void Frame::clear_has_type() { + _has_bits_[0] &= ~0x00000001u; +} +inline void Frame::clear_type() { + type_ = 1; + clear_has_type(); +} +inline ::Frame_FrameType Frame::type() const { + return static_cast< ::Frame_FrameType >(type_); +} +inline void Frame::set_type(::Frame_FrameType value) { + GOOGLE_DCHECK(::Frame_FrameType_IsValid(value)); + set_has_type(); + type_ = value; +} + +// optional int64 pos = 2; +inline bool Frame::has_pos() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void Frame::set_has_pos() { + _has_bits_[0] |= 0x00000002u; +} +inline void Frame::clear_has_pos() { + _has_bits_[0] &= ~0x00000002u; +} +inline void Frame::clear_pos() { + pos_ = GOOGLE_LONGLONG(0); + clear_has_pos(); +} +inline ::google::protobuf::int64 Frame::pos() const { + return pos_; +} +inline void Frame::set_pos(::google::protobuf::int64 value) { + set_has_pos(); + pos_ = value; +} + +// optional string opt = 3; +inline bool Frame::has_opt() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void Frame::set_has_opt() { + _has_bits_[0] |= 0x00000004u; +} +inline void Frame::clear_has_opt() { + _has_bits_[0] &= ~0x00000004u; +} +inline void Frame::clear_opt() { + if (opt_ != &::google::protobuf::internal::kEmptyString) { + opt_->clear(); + } + clear_has_opt(); +} +inline const ::std::string& Frame::opt() const { + return *opt_; +} +inline void Frame::set_opt(const ::std::string& value) { + set_has_opt(); + if (opt_ == &::google::protobuf::internal::kEmptyString) { + opt_ = new ::std::string; + } + opt_->assign(value); +} +inline void Frame::set_opt(const char* value) { + set_has_opt(); + if (opt_ == &::google::protobuf::internal::kEmptyString) { + opt_ = new ::std::string; + } + opt_->assign(value); +} +inline void Frame::set_opt(const char* value, size_t size) { + set_has_opt(); + if (opt_ == &::google::protobuf::internal::kEmptyString) { + opt_ = new ::std::string; + } + opt_->assign(reinterpret_cast(value), size); +} +inline ::std::string* Frame::mutable_opt() { + set_has_opt(); + if (opt_ == &::google::protobuf::internal::kEmptyString) { + opt_ = new ::std::string; + } + return opt_; +} +inline ::std::string* Frame::release_opt() { + clear_has_opt(); + if (opt_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = opt_; + opt_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} + +// ------------------------------------------------------------------- + +// Backtrace + +// repeated .Frame frames = 1; +inline int Backtrace::frames_size() const { + return frames_.size(); +} +inline void Backtrace::clear_frames() { + frames_.Clear(); +} +inline const ::Frame& Backtrace::frames(int index) const { + return frames_.Get(index); +} +inline ::Frame* Backtrace::mutable_frames(int index) { + return frames_.Mutable(index); +} +inline ::Frame* Backtrace::add_frames() { + return frames_.Add(); +} +inline const ::google::protobuf::RepeatedPtrField< ::Frame >& +Backtrace::frames() const { + return frames_; +} +inline ::google::protobuf::RepeatedPtrField< ::Frame >* +Backtrace::mutable_frames() { + return &frames_; +} + +// ------------------------------------------------------------------- + +// Response + +// optional .Response.ResponseType type = 1; +inline bool Response::has_type() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void Response::set_has_type() { + _has_bits_[0] |= 0x00000001u; +} +inline void Response::clear_has_type() { + _has_bits_[0] &= ~0x00000001u; +} +inline void Response::clear_type() { + type_ = 1; + clear_has_type(); +} +inline ::Response_ResponseType Response::type() const { + return static_cast< ::Response_ResponseType >(type_); +} +inline void Response::set_type(::Response_ResponseType value) { + GOOGLE_DCHECK(::Response_ResponseType_IsValid(value)); + set_has_type(); + type_ = value; +} + +// optional int64 token = 2; +inline bool Response::has_token() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void Response::set_has_token() { + _has_bits_[0] |= 0x00000002u; +} +inline void Response::clear_has_token() { + _has_bits_[0] &= ~0x00000002u; +} +inline void Response::clear_token() { + token_ = GOOGLE_LONGLONG(0); + clear_has_token(); +} +inline ::google::protobuf::int64 Response::token() const { + return token_; +} +inline void Response::set_token(::google::protobuf::int64 value) { + set_has_token(); + token_ = value; +} + +// repeated .Datum response = 3; +inline int Response::response_size() const { + return response_.size(); +} +inline void Response::clear_response() { + response_.Clear(); +} +inline const ::Datum& Response::response(int index) const { + return response_.Get(index); +} +inline ::Datum* Response::mutable_response(int index) { + return response_.Mutable(index); +} +inline ::Datum* Response::add_response() { + return response_.Add(); +} +inline const ::google::protobuf::RepeatedPtrField< ::Datum >& +Response::response() const { + return response_; +} +inline ::google::protobuf::RepeatedPtrField< ::Datum >* +Response::mutable_response() { + return &response_; +} + +// optional .Backtrace backtrace = 4; +inline bool Response::has_backtrace() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void Response::set_has_backtrace() { + _has_bits_[0] |= 0x00000008u; +} +inline void Response::clear_has_backtrace() { + _has_bits_[0] &= ~0x00000008u; +} +inline void Response::clear_backtrace() { + if (backtrace_ != NULL) backtrace_->::Backtrace::Clear(); + clear_has_backtrace(); +} +inline const ::Backtrace& Response::backtrace() const { + return backtrace_ != NULL ? *backtrace_ : *default_instance_->backtrace_; +} +inline ::Backtrace* Response::mutable_backtrace() { + set_has_backtrace(); + if (backtrace_ == NULL) backtrace_ = new ::Backtrace; + return backtrace_; +} +inline ::Backtrace* Response::release_backtrace() { + clear_has_backtrace(); + ::Backtrace* temp = backtrace_; + backtrace_ = NULL; + return temp; +} + +// ------------------------------------------------------------------- + +// Datum_AssocPair + +// optional string key = 1; +inline bool Datum_AssocPair::has_key() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void Datum_AssocPair::set_has_key() { + _has_bits_[0] |= 0x00000001u; +} +inline void Datum_AssocPair::clear_has_key() { + _has_bits_[0] &= ~0x00000001u; +} +inline void Datum_AssocPair::clear_key() { + if (key_ != &::google::protobuf::internal::kEmptyString) { + key_->clear(); + } + clear_has_key(); +} +inline const ::std::string& Datum_AssocPair::key() const { + return *key_; +} +inline void Datum_AssocPair::set_key(const ::std::string& value) { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + key_->assign(value); +} +inline void Datum_AssocPair::set_key(const char* value) { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + key_->assign(value); +} +inline void Datum_AssocPair::set_key(const char* value, size_t size) { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + key_->assign(reinterpret_cast(value), size); +} +inline ::std::string* Datum_AssocPair::mutable_key() { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + return key_; +} +inline ::std::string* Datum_AssocPair::release_key() { + clear_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = key_; + key_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} + +// optional .Datum val = 2; +inline bool Datum_AssocPair::has_val() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void Datum_AssocPair::set_has_val() { + _has_bits_[0] |= 0x00000002u; +} +inline void Datum_AssocPair::clear_has_val() { + _has_bits_[0] &= ~0x00000002u; +} +inline void Datum_AssocPair::clear_val() { + if (val_ != NULL) val_->::Datum::Clear(); + clear_has_val(); +} +inline const ::Datum& Datum_AssocPair::val() const { + return val_ != NULL ? *val_ : *default_instance_->val_; +} +inline ::Datum* Datum_AssocPair::mutable_val() { + set_has_val(); + if (val_ == NULL) val_ = new ::Datum; + return val_; +} +inline ::Datum* Datum_AssocPair::release_val() { + clear_has_val(); + ::Datum* temp = val_; + val_ = NULL; + return temp; +} + +// ------------------------------------------------------------------- + +// Datum + +// optional .Datum.DatumType type = 1; +inline bool Datum::has_type() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void Datum::set_has_type() { + _has_bits_[0] |= 0x00000001u; +} +inline void Datum::clear_has_type() { + _has_bits_[0] &= ~0x00000001u; +} +inline void Datum::clear_type() { + type_ = 1; + clear_has_type(); +} +inline ::Datum_DatumType Datum::type() const { + return static_cast< ::Datum_DatumType >(type_); +} +inline void Datum::set_type(::Datum_DatumType value) { + GOOGLE_DCHECK(::Datum_DatumType_IsValid(value)); + set_has_type(); + type_ = value; +} + +// optional bool r_bool = 2; +inline bool Datum::has_r_bool() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void Datum::set_has_r_bool() { + _has_bits_[0] |= 0x00000002u; +} +inline void Datum::clear_has_r_bool() { + _has_bits_[0] &= ~0x00000002u; +} +inline void Datum::clear_r_bool() { + r_bool_ = false; + clear_has_r_bool(); +} +inline bool Datum::r_bool() const { + return r_bool_; +} +inline void Datum::set_r_bool(bool value) { + set_has_r_bool(); + r_bool_ = value; +} + +// optional double r_num = 3; +inline bool Datum::has_r_num() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void Datum::set_has_r_num() { + _has_bits_[0] |= 0x00000004u; +} +inline void Datum::clear_has_r_num() { + _has_bits_[0] &= ~0x00000004u; +} +inline void Datum::clear_r_num() { + r_num_ = 0; + clear_has_r_num(); +} +inline double Datum::r_num() const { + return r_num_; +} +inline void Datum::set_r_num(double value) { + set_has_r_num(); + r_num_ = value; +} + +// optional string r_str = 4; +inline bool Datum::has_r_str() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void Datum::set_has_r_str() { + _has_bits_[0] |= 0x00000008u; +} +inline void Datum::clear_has_r_str() { + _has_bits_[0] &= ~0x00000008u; +} +inline void Datum::clear_r_str() { + if (r_str_ != &::google::protobuf::internal::kEmptyString) { + r_str_->clear(); + } + clear_has_r_str(); +} +inline const ::std::string& Datum::r_str() const { + return *r_str_; +} +inline void Datum::set_r_str(const ::std::string& value) { + set_has_r_str(); + if (r_str_ == &::google::protobuf::internal::kEmptyString) { + r_str_ = new ::std::string; + } + r_str_->assign(value); +} +inline void Datum::set_r_str(const char* value) { + set_has_r_str(); + if (r_str_ == &::google::protobuf::internal::kEmptyString) { + r_str_ = new ::std::string; + } + r_str_->assign(value); +} +inline void Datum::set_r_str(const char* value, size_t size) { + set_has_r_str(); + if (r_str_ == &::google::protobuf::internal::kEmptyString) { + r_str_ = new ::std::string; + } + r_str_->assign(reinterpret_cast(value), size); +} +inline ::std::string* Datum::mutable_r_str() { + set_has_r_str(); + if (r_str_ == &::google::protobuf::internal::kEmptyString) { + r_str_ = new ::std::string; + } + return r_str_; +} +inline ::std::string* Datum::release_r_str() { + clear_has_r_str(); + if (r_str_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = r_str_; + r_str_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} + +// repeated .Datum r_array = 5; +inline int Datum::r_array_size() const { + return r_array_.size(); +} +inline void Datum::clear_r_array() { + r_array_.Clear(); +} +inline const ::Datum& Datum::r_array(int index) const { + return r_array_.Get(index); +} +inline ::Datum* Datum::mutable_r_array(int index) { + return r_array_.Mutable(index); +} +inline ::Datum* Datum::add_r_array() { + return r_array_.Add(); +} +inline const ::google::protobuf::RepeatedPtrField< ::Datum >& +Datum::r_array() const { + return r_array_; +} +inline ::google::protobuf::RepeatedPtrField< ::Datum >* +Datum::mutable_r_array() { + return &r_array_; +} + +// repeated .Datum.AssocPair r_object = 6; +inline int Datum::r_object_size() const { + return r_object_.size(); +} +inline void Datum::clear_r_object() { + r_object_.Clear(); +} +inline const ::Datum_AssocPair& Datum::r_object(int index) const { + return r_object_.Get(index); +} +inline ::Datum_AssocPair* Datum::mutable_r_object(int index) { + return r_object_.Mutable(index); +} +inline ::Datum_AssocPair* Datum::add_r_object() { + return r_object_.Add(); +} +inline const ::google::protobuf::RepeatedPtrField< ::Datum_AssocPair >& +Datum::r_object() const { + return r_object_; +} +inline ::google::protobuf::RepeatedPtrField< ::Datum_AssocPair >* +Datum::mutable_r_object() { + return &r_object_; +} + +// ------------------------------------------------------------------- + +// Term_AssocPair + +// optional string key = 1; +inline bool Term_AssocPair::has_key() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void Term_AssocPair::set_has_key() { + _has_bits_[0] |= 0x00000001u; +} +inline void Term_AssocPair::clear_has_key() { + _has_bits_[0] &= ~0x00000001u; +} +inline void Term_AssocPair::clear_key() { + if (key_ != &::google::protobuf::internal::kEmptyString) { + key_->clear(); + } + clear_has_key(); +} +inline const ::std::string& Term_AssocPair::key() const { + return *key_; +} +inline void Term_AssocPair::set_key(const ::std::string& value) { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + key_->assign(value); +} +inline void Term_AssocPair::set_key(const char* value) { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + key_->assign(value); +} +inline void Term_AssocPair::set_key(const char* value, size_t size) { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + key_->assign(reinterpret_cast(value), size); +} +inline ::std::string* Term_AssocPair::mutable_key() { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + return key_; +} +inline ::std::string* Term_AssocPair::release_key() { + clear_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = key_; + key_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} + +// optional .Term val = 2; +inline bool Term_AssocPair::has_val() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void Term_AssocPair::set_has_val() { + _has_bits_[0] |= 0x00000002u; +} +inline void Term_AssocPair::clear_has_val() { + _has_bits_[0] &= ~0x00000002u; +} +inline void Term_AssocPair::clear_val() { + if (val_ != NULL) val_->::Term::Clear(); + clear_has_val(); +} +inline const ::Term& Term_AssocPair::val() const { + return val_ != NULL ? *val_ : *default_instance_->val_; +} +inline ::Term* Term_AssocPair::mutable_val() { + set_has_val(); + if (val_ == NULL) val_ = new ::Term; + return val_; +} +inline ::Term* Term_AssocPair::release_val() { + clear_has_val(); + ::Term* temp = val_; + val_ = NULL; + return temp; +} + +// ------------------------------------------------------------------- + +// Term + +// optional .Term.TermType type = 1; +inline bool Term::has_type() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void Term::set_has_type() { + _has_bits_[0] |= 0x00000001u; +} +inline void Term::clear_has_type() { + _has_bits_[0] &= ~0x00000001u; +} +inline void Term::clear_type() { + type_ = 1; + clear_has_type(); +} +inline ::Term_TermType Term::type() const { + return static_cast< ::Term_TermType >(type_); +} +inline void Term::set_type(::Term_TermType value) { + GOOGLE_DCHECK(::Term_TermType_IsValid(value)); + set_has_type(); + type_ = value; +} + +// optional .Datum datum = 2; +inline bool Term::has_datum() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void Term::set_has_datum() { + _has_bits_[0] |= 0x00000002u; +} +inline void Term::clear_has_datum() { + _has_bits_[0] &= ~0x00000002u; +} +inline void Term::clear_datum() { + if (datum_ != NULL) datum_->::Datum::Clear(); + clear_has_datum(); +} +inline const ::Datum& Term::datum() const { + return datum_ != NULL ? *datum_ : *default_instance_->datum_; +} +inline ::Datum* Term::mutable_datum() { + set_has_datum(); + if (datum_ == NULL) datum_ = new ::Datum; + return datum_; +} +inline ::Datum* Term::release_datum() { + clear_has_datum(); + ::Datum* temp = datum_; + datum_ = NULL; + return temp; +} + +// repeated .Term args = 3; +inline int Term::args_size() const { + return args_.size(); +} +inline void Term::clear_args() { + args_.Clear(); +} +inline const ::Term& Term::args(int index) const { + return args_.Get(index); +} +inline ::Term* Term::mutable_args(int index) { + return args_.Mutable(index); +} +inline ::Term* Term::add_args() { + return args_.Add(); +} +inline const ::google::protobuf::RepeatedPtrField< ::Term >& +Term::args() const { + return args_; +} +inline ::google::protobuf::RepeatedPtrField< ::Term >* +Term::mutable_args() { + return &args_; +} + +// repeated .Term.AssocPair optargs = 4; +inline int Term::optargs_size() const { + return optargs_.size(); +} +inline void Term::clear_optargs() { + optargs_.Clear(); +} +inline const ::Term_AssocPair& Term::optargs(int index) const { + return optargs_.Get(index); +} +inline ::Term_AssocPair* Term::mutable_optargs(int index) { + return optargs_.Mutable(index); +} +inline ::Term_AssocPair* Term::add_optargs() { + return optargs_.Add(); +} +inline const ::google::protobuf::RepeatedPtrField< ::Term_AssocPair >& +Term::optargs() const { + return optargs_; +} +inline ::google::protobuf::RepeatedPtrField< ::Term_AssocPair >* +Term::mutable_optargs() { + return &optargs_; +} + + +// @@protoc_insertion_point(namespace_scope) + +#ifndef SWIG +namespace google { +namespace protobuf { + +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::VersionDummy_Version>() { + return ::VersionDummy_Version_descriptor(); +} +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::Query_QueryType>() { + return ::Query_QueryType_descriptor(); +} +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::Frame_FrameType>() { + return ::Frame_FrameType_descriptor(); +} +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::Response_ResponseType>() { + return ::Response_ResponseType_descriptor(); +} +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::Datum_DatumType>() { + return ::Datum_DatumType_descriptor(); +} +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::Term_TermType>() { + return ::Term_TermType_descriptor(); +} + +} // namespace google +} // namespace protobuf +#endif // SWIG + +// @@protoc_insertion_point(global_scope) + +#endif // PROTOBUF_ql2_2eproto__INCLUDED diff --git a/external/glim/raii.hpp b/external/glim/raii.hpp new file mode 100644 index 00000000..a09d3577 --- /dev/null +++ b/external/glim/raii.hpp @@ -0,0 +1,34 @@ +#include +namespace glim { + +// http://stackoverflow.com/questions/2121607/any-raii-template-in-boost-or-c0x/ + +/// RAII helper. Keeps the functor and runs it in the destructor. +/// Example: \code auto unmap = raiiFun ([&]() {munmap (fd, size);}); \endcode +template struct RAIIFun { + Fun _fun; + RAIIFun (RAIIFun&&) = default; + RAIIFun (const RAIIFun&) = default; + template RAIIFun (FunArg&& fun): _fun (std::forward (fun)) {} + ~RAIIFun() {_fun();} +}; + +/// The idea to name it `finally` comes from http://www.codeproject.com/Tips/476970/finally-clause-in-Cplusplus. +/// Example: \code finally unmap ([&]() {munmap (fd, size);}); \endcode +typedef RAIIFun> finally; + +/// Runs the given functor when going out of scope. +/// Example: \code +/// auto closeFd = raiiFun ([&]() {close (fd);}); +/// auto unmap = raiiFun ([&]() {munmap (fd, size);}); +/// \endcode +template RAIIFun raiiFun (const Fun& fun) {return RAIIFun (fun);} + +/// Runs the given functor when going out of scope. +/// Example: \code +/// auto closeFd = raiiFun ([&]() {close (fd);}); +/// auto unmap = raiiFun ([&]() {munmap (fd, size);}); +/// \endcode +template RAIIFun raiiFun (Fun&& fun) {return RAIIFun (std::move (fun));} + +} diff --git a/external/glim/runner.hpp b/external/glim/runner.hpp new file mode 100644 index 00000000..57c7e524 --- /dev/null +++ b/external/glim/runner.hpp @@ -0,0 +1,402 @@ +#ifndef _GLIM_RUNNER_INCLUDED +#define _GLIM_RUNNER_INCLUDED + +#include // min +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // cf. hiperfifo.cpp at http://article.gmane.org/gmane.comp.web.curl.library/37752 + +#include +#include // http://www.boost.org/doc/libs/1_53_0/doc/html/boost/lockfree/queue.html +#include + +#include +#include // rand +#include + +#include "gstring.hpp" +#include "exception.hpp" + +namespace glim { + +/// Listens to messages returned by `curl_multi_info_read`. +/// NB: When CURL is queued with `addToCURLM` the CURL's `CURLOPT_PRIVATE` must point to the instance of `CurlmInformationListener`. +struct CurlmInformationListener { + enum FreeOptions {REMOVE_CURL_FROM_CURLM = 1, CURL_CLEANUP = 2, DELETE_LISTENER = 4, REMOVE_CLEAN_DELETE = 1|2|4}; + virtual FreeOptions information (CURLMsg*, CURLM*) = 0; + virtual ~CurlmInformationListener() {} +}; + +/// Listener deferring to a lambda. +struct FunCurlmLisneter: public glim::CurlmInformationListener { + std::function _fun; + FreeOptions _freeOptions; + FunCurlmLisneter (std::function && fun, FreeOptions freeOptions): _fun (std::move (fun)), _freeOptions (freeOptions) {} + virtual FreeOptions information (CURLMsg* msg, CURLM* curlm) override { + if (__builtin_expect ((bool) _fun, 1)) + try {_fun (msg, curlm);} catch (const std::exception& ex) {BOOST_LOG_TRIVIAL (error) << "FunCurlmLisneter] " << ex.what();} + return _freeOptions; + } +}; + +/// Running cURL jobs in a single thread. +/// NB: The RunnerV2 *must* be allocated with `boost::intrusive_ptr` (typically you'd use `RunnerV2::instance()`). +class RunnerV2 { + std::atomic_int_fast32_t _references {0}; // For intrusive_ptr. + CURLM* _multi = nullptr; ///< Initialized in `run`. Should not be used outside of it. + int _eventFd = 0; ///< Used to give the `curl_multi_wait` some work when there's no cURL descriptors and to wake it from `withCURLM`. + boost::lockfree::queue> _queue; ///< `CURL` handles waiting to be added to `CURL_MULTI`. + std::thread _thread; + std::atomic_bool _running {false}; /// True if the `_thread` is running. + + using FreeOptions = CurlmInformationListener::FreeOptions; + + friend inline void intrusive_ptr_add_ref (RunnerV2*); + friend inline void intrusive_ptr_release (RunnerV2*); + + void run() noexcept { + try { + if (__builtin_expect (_references <= 0, 0)) GTHROW ("RunnerV2] Must be allocated with boost::intrusive_ptr!"); + _running = true; // NB: _running only becomes true if we're in the intrusive_ptr. ^^ + pthread_setname_np (pthread_self(), "Runner"); + _multi = curl_multi_init(); if (__builtin_expect (_multi == nullptr, 0)) GTHROW ("!curl_multi_init"); + _eventFd = eventfd (0, EFD_CLOEXEC | EFD_NONBLOCK); // Used to pause `curl_multi_wait` when there's no other jobs. + if (__builtin_expect (_eventFd == -1, 0)) GTHROW (std::string ("eventfd: ") + ::strerror (errno)); + while (__builtin_expect (_references > 0, 0)) { + // Reset the CURL_EVENT_FD value to 0, so that the `curl_multi_wait` can sleep. + if (__builtin_expect (_eventFd > 0, 1)) {eventfd_t count = 0; eventfd_read (_eventFd, &count);} + + // Add the queued CURL handles to our CURLM. + CURL* easy = nullptr; while (_queue.pop (easy)) curl_multi_add_handle (_multi, easy); + + // Run the cURL. + int runningHandles = 0; + CURLMcode rc = curl_multi_perform (_multi, &runningHandles); // http://curl.haxx.se/libcurl/c/curl_multi_perform.html + if (__builtin_expect (rc != CURLM_OK, 0)) BOOST_LOG_TRIVIAL (error) << "Runner] curl_multi_perform: " << curl_multi_strerror (rc); + + // Process the finished handles. + for (;;) { + int messagesLeft = 0; CURLMsg* msg = curl_multi_info_read (_multi, &messagesLeft); if (msg) try { + CURL* curl = msg->easy_handle; CurlmInformationListener* listener = 0; + if (__builtin_expect (curl_easy_getinfo (curl, CURLINFO_PRIVATE, &listener) == CURLE_OK, 1)) { + using FOP = CurlmInformationListener::FreeOptions; + FOP fop = listener->information (msg, _multi); + if (fop & FOP::REMOVE_CURL_FROM_CURLM) curl_multi_remove_handle (_multi, curl); + if (fop & FOP::CURL_CLEANUP) curl_easy_cleanup (curl); + if (fop & FOP::DELETE_LISTENER) delete listener; + } else { + curl_multi_remove_handle (_multi, curl); + curl_easy_cleanup (curl); + } + } catch (const std::exception& ex) {BOOST_LOG_TRIVIAL (error) << "Runner] " << ex.what();} + if (messagesLeft == 0) break; + } + + // Wait on the cURL file descriptors. + int descriptors = 0; + curl_waitfd waitfd = {_eventFd, CURL_WAIT_POLLIN, 0}; + eventfd_t eValue = 0; eventfd_read (_eventFd, &eValue); // Reset the curlEventFd value to zero. + rc = curl_multi_wait (_multi, &waitfd, 1, 100, &descriptors); // http://curl.haxx.se/libcurl/c/curl_multi_wait.html + if (__builtin_expect (rc != CURLM_OK, 0)) BOOST_LOG_TRIVIAL (error) << "Runner] curl_multi_wait: " << curl_multi_strerror (rc); + } + } catch (const std::exception& ex) {BOOST_LOG_TRIVIAL (error) << "Runner] " << ex.what();} + // Delayed destruction: when we're in intrusive_ptr (_running == true) but no longer referenced. + if (_running && _references == 0) delete this; // http://www.parashift.com/c++-faq-lite/delete-this.html + else _running = false; + } +public: + RunnerV2() { + // Start a thread using CURLM in a thread-safe way (that is, from this single thread only). + // NB: Handles *can* be passed between threads: http://article.gmane.org/gmane.comp.web.curl.library/33188 + _thread = std::thread (&RunnerV2::run, this); + } + ~RunnerV2() { + _thread.detach(); + } + + /// A singletone instance of the Runner used in order for different programes to reuse the same cURL thread. + static boost::intrusive_ptr& instance() { + static boost::intrusive_ptr INSTANCE (new RunnerV2()); + return INSTANCE; + } + + /// Schedule a CURL handler to be executed in the cURL thread. + /// NB: If the handle have a `CURLOPT_PRIVATE` option then it MUST point to an instance of `CurlmInformationListener`. + void addToCURLM (CURL* easyHandle) { + if (__builtin_expect (!_queue.push (easyHandle), 0)) GTHROW ("Can't push CURL* into the queue."); + if (__builtin_expect (_eventFd > 0, 1)) eventfd_write (_eventFd, 1); // Will wake the `curl_multi_wait` up, in order to run the `curl_multi_add_handle`. + } + + /// Schedule a CURL handler to be executed in the cURL thread. + /// NB: `CURLOPT_PRIVATE` is overwritten with a pointer to `FunCurlmLisneter`. + void addToCURLM (CURL* easyHandle, std::function && listener, + FreeOptions freeOptions = static_cast (FreeOptions::REMOVE_CURL_FROM_CURLM | FreeOptions::DELETE_LISTENER)) { + FunCurlmLisneter* funListener = new FunCurlmLisneter (std::move (listener), freeOptions); // Will be deleted by the Runner. + curl_easy_setopt (easyHandle, CURLOPT_PRIVATE, funListener); // Tells `addToCURLM` to call this listener later. + addToCURLM (easyHandle); + } +}; + +inline void intrusive_ptr_add_ref (RunnerV2* runner) {++ runner->_references;} +inline void intrusive_ptr_release (RunnerV2* runner) {if (-- runner->_references == 0 && !runner->_running) delete runner;} + +/// Run CURLM requests and completion handlers, as well as other periodic jobs. +class Runner { + G_DEFINE_EXCEPTION (RunnerEx); + /// Free CURL during stack unwinding. + struct FreeCurl { + Runner* runner; CURL* curl; + FreeCurl (Runner* runner, CURL* curl): runner (runner), curl (curl) {} + ~FreeCurl() { + runner->_handlers.erase (curl); + curl_multi_remove_handle (runner->_curlm, curl); + curl_easy_cleanup (curl); + } + }; + public: + struct JobInfo; + /// The job must return `true` if Runner is to continue invoking it. + typedef std::function job_t; + struct JobInfo { + job_t job; + float pauseSec = 1.0f; + struct timespec ran = {0, 0}; + }; +protected: + typedef std::function handler_t; + typedef std::function errlog_t; + std::shared_ptr _evbase; + errlog_t _errlog; + std::recursive_mutex _mutex; + typedef std::unique_ptr event_t; + std::unordered_map> _handlers; + /// Functions to run periodically. + typedef std::unordered_map jobs_map_t; + jobs_map_t _jobs; + CURLM* _curlm = nullptr; + struct event* _timer = nullptr; + + /// Schedule a function to be run on the event loop. Useful to run all cURL methods on the single event loop thread. + template + void doInEv (F fun, struct timeval after = {0, 0}) { + struct Dugout {F fun; struct event* timer; Dugout (F&& fun): fun (std::move (fun)), timer (nullptr) {}} *dugout = new Dugout (std::move (fun)); + event_callback_fn cb = [](evutil_socket_t, short, void* dugout_)->void { + Dugout* dugout = static_cast (dugout_); + event_free (dugout->timer); dugout->timer = nullptr; + F fun = std::move (dugout->fun); delete dugout; + fun(); + }; + dugout->timer = evtimer_new (_evbase.get(), cb, dugout); + evtimer_add (dugout->timer, &after); + } + + bool shouldRun (jobs_map_t::value_type& entry, const struct timespec& ct) { + JobInfo& jobInfo = entry.second; + if (jobInfo.pauseSec <= 0.f) return true; // Run always. + if (jobInfo.ran.tv_sec == 0) {jobInfo.ran = ct; return true;} + float delta = (float)(ct.tv_sec - jobInfo.ran.tv_sec); + delta += (float)(ct.tv_nsec - jobInfo.ran.tv_nsec) / 1000000000.0f; + if (delta >= jobInfo.pauseSec) {jobInfo.ran = ct; return true;} + return false; + } + + /// Used for debugging. + static uint64_t ms() { + return std::chrono::duration_cast (std::chrono::system_clock::now().time_since_epoch()) .count(); + } + /// Tells CURL to check its sockets. + void callCurlWithTimeout() { + //std::cout << __LINE__ << ',' << ms() << ": callCurlWithTimeout" << std::endl; + int running_handles = 0; + CURLMcode rc = curl_multi_socket_action (_curlm, CURL_SOCKET_TIMEOUT, 0, &running_handles); + if (rc != CURLM_OK) {GSTRING_ON_STACK (err, 256) << "glim::Runner: curl_multi_socket_action: " << curl_multi_strerror (rc); _errlog (err.c_str());} + } + + /// Should only be run when the _mutex is locked. + void checkForFinishedCurlJobs() { + //std::cout << __LINE__ << ',' << ms() << ": checkForFinishedCurlJobs" << std::endl; + nextMessage: + int msgs_in_queue = 0; + CURLMsg* msg = curl_multi_info_read (_curlm, &msgs_in_queue); + if (msg) try { + auto curl = msg->easy_handle; + FreeCurl freeCurl (this, curl); + auto it = _handlers.find (curl); + if (it != _handlers.end()) it->second.first (msg); + if (msgs_in_queue > 0) goto nextMessage; + } catch (const std::exception& ex) { + char eBuf[512]; gstring err (sizeof(eBuf), eBuf, false, 0); + err << "glim::Runner: handler: " << ex.what(); + _errlog (err.c_str()); + } + } + /// Will reset the timer unless there is a shorter timer already set. + void restartTimer (uint32_t nextInMicro = 100000) { // 100ms = 100000µs + struct timeval tv; + if (event_pending (_timer, EV_TIMEOUT, &tv) && !tv.tv_sec && tv.tv_usec < nextInMicro) return; // Already have a shorter timeout. + tv = {0, nextInMicro}; + evtimer_add (_timer, &tv); + } + static void evTimerCB (evutil_socket_t, short, void* runner_) { + //std::cout << __LINE__ << ',' << ms() << ": evTimerCB" << std::endl; + Runner* runner = (Runner*) runner_; + runner->callCurlWithTimeout(); + runner->run(); + } + /// event_callback_fn: There is an activity on a socket we are monitoring for CURL. + static void evSocketCB (evutil_socket_t sock, short events, void* runner_) { + //std::cout << __LINE__ << ',' << ms() << ": evSocketCB; sock: " << sock << "; events: " << events << std::endl; + Runner* runner = (Runner*) runner_; + int ev_bitmask = (events & EV_READ ? CURL_CSELECT_IN : 0) | (events & EV_WRITE ? CURL_CSELECT_OUT : 0); + int running_handles = 0; + CURLMcode rc = curl_multi_socket_action (runner->_curlm, sock, ev_bitmask, &running_handles); + if (rc != CURLM_OK) {GSTRING_ON_STACK (err, 256) << "glim::Runner: curl_multi_socket_action: " << curl_multi_strerror (rc); runner->_errlog (err.c_str());} + } + static void deleteEvent (struct event* ev) { + //std::cout << __LINE__ << ',' << ms() << ": deleteEvent: " << ev << std::endl; + event_del (ev); event_free (ev); + }; + /// curl_socket_callback: CURL asks us to monitor the socket. + static int curlSocketCB (CURL* easy, curl_socket_t sock, int what, void* runner_, void* socketp) { + //std::cout << __LINE__ << ',' << ms() << ": curlSocketCB; sock: " << sock << "; what: " << what; + //std::cout << " (" << (what == 0 ? "none" : what == 1 ? "in" : what == 2 ? "out" : what == 3 ? "inout" : what == 4 ? "remove" : "?") << ")" << std::endl; + Runner* runner = (Runner*) runner_; + std::lock_guard lock (runner->_mutex); + if (what & CURL_POLL_REMOVE) { + auto it = runner->_handlers.find (easy); if (it != runner->_handlers.end()) it->second.second.reset(); + // We can't run `checkForFinishedCurlJobs` from there or bad things would happen + // (`curl_multi_remove_handle` will be called while we are still in the `curl_multi_socket_action`), + // but we can schedule the check via the libevent timer. + runner->restartTimer (0); + } else { + auto it = runner->_handlers.find (easy); if (it != runner->_handlers.end() && !it->second.second) { + event_callback_fn cb = evSocketCB; + struct event* ev = event_new (runner->_evbase.get(), sock, EV_READ | EV_WRITE | EV_ET | EV_PERSIST, cb, runner); + event_add (ev, nullptr); + //std::cout << __LINE__ << ',' << ms() << ": new event: " << ev << std::endl; + it->second.second = event_t (ev, deleteEvent); + } + } + return 0; + } + /// curl_multi_timer_callback: Schedule a CURL timer event or if `timeout_ms` is 0 then run immediately. + static int curlTimerCB (CURLM* multi, long timeout_ms, void* runner_) { + //std::cout << __LINE__ << ',' << ms() << ": curlTimerCB; timeout_ms: " << timeout_ms << std::endl; + if (timeout_ms == -1) return 0; // CURL tells us it doesn't need no timer. + Runner* runner = (Runner*) runner_; + if (timeout_ms == 0) { // CURL tells us it wants to run NOW. + runner->callCurlWithTimeout(); + return 0; + } + // CURL asks us to run it `timeout_ms` from now. + runner->restartTimer (std::min ((uint32_t) timeout_ms, (uint32_t) 100) * 1000); // We wait no more than 100ms. + return 0; + } +public: + Runner (std::shared_ptr evbase, errlog_t errlog): _evbase (evbase), _errlog (errlog) { + doInEv ([this]() { + std::lock_guard lock (_mutex); + _curlm = curl_multi_init(); if (!_curlm) GNTHROW (RunnerEx, "!curl_multi_init"); + auto check = [this](CURLMcode rc) {if (rc != CURLM_OK) {curl_multi_cleanup (_curlm); GNTHROW (RunnerEx, "curl_multi_setopt: " + std::to_string (rc));}}; + check (curl_multi_setopt (_curlm, CURLMOPT_SOCKETDATA, this)); + curl_socket_callback socketCB = curlSocketCB; check (curl_multi_setopt (_curlm, CURLMOPT_SOCKETFUNCTION, socketCB)); + check (curl_multi_setopt (_curlm, CURLMOPT_TIMERDATA, this)); + curl_multi_timer_callback curlTimerCB_ = curlTimerCB; check (curl_multi_setopt (_curlm, CURLMOPT_TIMERFUNCTION, curlTimerCB_)); + event_callback_fn evTimerCB_ = evTimerCB; _timer = evtimer_new (_evbase.get(), evTimerCB_, this); + restartTimer(); + }); + } + ~Runner() { + //std::cout << __LINE__ << ',' << ms() << ": ~Runner" << std::endl; + std::lock_guard lock (_mutex); + if (_timer) {evtimer_del (_timer); event_free (_timer); _timer = nullptr;} + doInEv ([curlm = _curlm, handlers = std::move (_handlers)]() { + for (auto it = handlers.begin(), end = handlers.end(); it != end; ++it) { + curl_multi_remove_handle (curlm, it->first); + curl_easy_cleanup (it->first); + } + if (curlm) {curl_multi_cleanup (curlm);} + }); + _curlm = nullptr; + } + + /** Turns HTTP Pipelining on (or off). + * See http://curl.haxx.se/libcurl/c/curl_multi_setopt.html#CURLMOPTPIPELINING */ + Runner& pipeline (long enabled = 1) { + CURLMcode rc = curl_multi_setopt (_curlm, CURLMOPT_PIPELINING, enabled); + if (rc != CURLM_OK) GNTHROW (RunnerEx, "curl_multi_setopt: " + std::to_string (rc)); + return *this; + } + + /// Wait for the operation to complete, then call the `handler`, then free the `curl`. + void multi (CURL* curl, handler_t handler) { + { std::lock_guard lock (_mutex); + _handlers.insert (std::make_pair (curl, std::make_pair (std::move (handler), event_t (nullptr, nullptr)))); } + doInEv ([this,curl]() { + curl_multi_add_handle (_curlm, curl); + }); + } + /// Register a new job to be run on the thread loop. + JobInfo& job (const gstring& name) { + std::lock_guard lock (_mutex); + return _jobs[name]; + } + /// Register a new job to be run on the thread loop. + void schedule (const gstring& name, float pauseSec, job_t job) { + struct timespec ct; if (pauseSec > 0.f) clock_gettime (CLOCK_MONOTONIC, &ct); + std::lock_guard lock (_mutex); + JobInfo& jobInfo = _jobs[name]; + jobInfo.job = job; + jobInfo.pauseSec = pauseSec; + if (pauseSec > 0.f) jobInfo.ran = ct; // If we need a pause then we also need to know when the job was scheduled. + } + /// Register a new job to be run on the thread loop. + void schedule (float pauseSec, job_t job) { + // Find a unique job name. + anotherName: + GSTRING_ON_STACK (name, 64) << "job" << rand(); + if (_jobs.find (name) != _jobs.end()) goto anotherName; + schedule (name, pauseSec, std::move (job)); + } + void removeJob (const gstring& name) { + std::lock_guard lock (_mutex); + _jobs.erase (name); + } + /// Invoked automatically from a libevent timer; can also be invoked manually. + void run() { + _mutex.lock(); + checkForFinishedCurlJobs(); + // Run non-CURL jobs. Copy jobs into a local array in order not to run them with the `_mutex` locked. + struct timespec ct; clock_gettime (CLOCK_MONOTONIC, &ct); + JobInfo jobs[_jobs.size()]; gstring jobNames[_jobs.size()]; int jn = -1; { + for (auto it = _jobs.begin(), end = _jobs.end(); it != end; ++it) if (shouldRun (*it, ct)) { + ++jn; jobNames[jn] = it->first; jobs[jn] = it->second; + } } + _mutex.unlock(); + + for (; jn >= 0; --jn) try { + if (!jobs[jn].job (jobs[jn])) removeJob (jobNames[jn]); + } catch (const std::exception& ex) { + char eBuf[512]; gstring err (sizeof(eBuf), eBuf, false, 0); + err << "glim::Runner: error in job " << jobNames[jn] << ": " << ex.what(); + _errlog (err.c_str()); + } + restartTimer(); + } + + /// Expose CURLM. Useful for curl_multi_setopt (http://curl.haxx.se/libcurl/c/curl_multi_setopt.html). + CURLM* curlm() const {return _curlm;} +}; + +} // namespace glim + +#endif // _GLIM_RUNNER_INCLUDED diff --git a/external/glim/sqlite.hpp b/external/glim/sqlite.hpp new file mode 100644 index 00000000..c7e92ac4 --- /dev/null +++ b/external/glim/sqlite.hpp @@ -0,0 +1,538 @@ +#ifndef GLIM_SQLITE_HPP_ +#define GLIM_SQLITE_HPP_ + +/** + * A threaded interface to SQLite. + * This file is a header-only library, + * whose sole dependencies should be standard STL and posix threading libraries. + * You can extract this file out of the "glim" library to include it separately in your project. + * @code +Copyright 2006-2012 Kozarezov Artem Aleksandrovich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + * @endcode + * @file + */ + +#include +#include +#include +#include +#include // strerror +#include // stat +#include // stat +#include // stat +#include // stat +#include // snprintf +#include + +namespace glim { + +class SqliteSession; +class SqliteQuery; + +struct SqliteEx: public std::runtime_error { + SqliteEx (const std::string& what): std::runtime_error (what) {} +}; + +/** + * The database. + * According to sqlite3_open documentation, + * only the thread that opened the database can safely access it. This restriction was + * relaxed, as described in the FAQ + * (the "Is SQLite threadsafe?" question), so we can use the library from multiple + * threads, but only if no more than one thread at a time accesses the database. + * This restriction is, in fact, beneficial if the database is used from a single application: + * by restricting access to a sigle thread at a time, we effectively avoid all deadlock issues.\n + * This library goals are:\n + * \li to ensure that SQLite is used in a thread-safe way, + * \li to provide additional threaded quirks, such as delayed updates (not implemented). + * + * The library is targeted at SQLite setup which is \b not \c -DTHREADSAFE, + * since this is the default setup on UNIX architectures.\n + * \n + * This file is a header-only library, + * whose sole dependencies should be standard STL and posix threading libraries. + * You can extract this file out of the "glim" library to include it separately in your project.\n + * \n + * This library is targeted at UTF-8 API. There is no plans to support the UTF-16 API.\n + * \n + * See also:\n + * \li http://www.sqlite.org/cvstrac/fileview?f=sqlite/src/server.c\n + * for another way of handling multithreading with SQLite. + */ +class Sqlite { + /// No copying allowed. + Sqlite& operator = (const Sqlite& other) {return *this;} + /// No copying allowed. + Sqlite (const Sqlite& other) = delete; + friend class SqliteSession; + protected: + /// Filename the database was opened with; we need it to reopen the database on fork()s. + /// std::string is used to avoid memory allocation issues. + std::string filename; + ::sqlite3* handler; + ::pthread_mutex_t mutex; + public: + /// Flags for the Sqlite constructor. + enum Flags { + /** + * The file will be checked for existence. + * SqliteEx is thrown if the file is not accessible; + * format of the error description is "$filename: $strerror".\n + * Usage example: \code Sqlite db ("filename", Sqlite::existing); \endcode + */ + existing = 1 + }; + /** + * Opens the database. + * @param filename Database filename (UTF-8). + * @param flags Optional. Currently there is the #existing flag. + * @throws SqliteEx Thrown if we can't open the database. + */ + Sqlite (std::string filename, int flags = 0) { + if (flags & existing) { + // Check if the file exists already. + struct stat st; if (stat (filename.c_str(), &st)) + throw SqliteEx (filename + ": " + ::strerror(errno)); + } + ::pthread_mutex_init (&mutex, NULL); + this->filename = filename; + if (::sqlite3_open(filename.c_str(), &handler) != SQLITE_OK) + throw SqliteEx (std::string("sqlite3_open(") + filename + "): " + ::sqlite3_errmsg(handler)); + } + /** + * Closes the database. + * @throws SqliteEx Thrown if we can't close the database. + */ + ~Sqlite () { + ::pthread_mutex_destroy (&mutex); + if (::sqlite3_close(handler) != SQLITE_OK) + throw SqliteEx (std::string ("sqlite3_close(): ") + ::sqlite3_errmsg(handler)); + } + + Sqlite& exec (const char* query); + /** + * Invokes `exec` on `query.c_str()`. + * Example:\code + * glim::Sqlite sqlite (":memory:"); + * for (std::string pv: {"page_size = 4096", "secure_delete = 1"}) sqlite->exec2 ("PRAGMA " + pv); \endcode + */ + template Sqlite& exec2 (StringLike query) {return exec (query.c_str());} +}; + +/** + * A single thread session with Sqlite. + * Only a sigle thread at a time can have an SqliteSession, + * all other threads will wait, in the SqliteSession constructor, + * till the active session is either closed or destructed. + */ +class SqliteSession { + /// No copying allowed. + SqliteSession& operator = (const SqliteSession& other) {return *this;} + /// No copying allowed. + SqliteSession(SqliteSession& other): db (NULL) {} + protected: + Sqlite* db; + public: + /** + * Locks the database. + * @throws SqliteEx if a mutex error occurs. + */ + SqliteSession (Sqlite* sqlite): db (sqlite) { + int err = ::pthread_mutex_lock (&(db->mutex)); + if (err != 0) throw SqliteEx (std::string ("error locking the mutex: ") + ::strerror(err)); + } + /** + * A shorter way to construct query from the session. + * Usage example: \code ses.query(S("create table test (i integer)")).step() \endcode + * @see SqliteQuery#qstep + */ + template + SqliteQuery query (T t); + /// Automatically unlocks the database. + /// @see close + ~SqliteSession () {close();} + /** + * Unlock the database. + * It is safe to call this method multiple times.\n + * You must not use the session after it was closed.\n + * All resources allocated within this session must be released before the session is closed. + * @throws SqliteEx if a mutex error occurs. + */ + void close () { + if (db == NULL) return; + int err = ::pthread_mutex_unlock (&(db->mutex)); + db = NULL; + if (err != 0) throw SqliteEx (std::string ("error unlocking the mutex: ") + ::strerror(err)); + } + /// True if the \c close method has been already called on this SqliteSession. + bool isClosed () const { + return db == NULL; + } + /** + * This class can be used in place of the SQLite handler. + * Make sure you've released any resources thus manually acquired before this SqliteSession is closed. + * Usage example: + * @code + * glim::Sqlite db (":memory:"); + * glim::SqliteSession ses (&db); + * sqlite3_exec (ses, "PRAGMA page_size = 4096;", NULL, NULL, NULL); + * @endcode + */ + operator ::sqlite3* () const {return db->handler;} +}; + +/** + * Execute the given query, throwing SqliteEx on failure.\n + * Example:\code + * glim::Sqlite sqlite (":memory:"); + * sqlite.exec ("PRAGMA page_size = 4096") .exec ("PRAGMA secure_delete = 1"); \endcode + */ +inline Sqlite& Sqlite::exec (const char* query) { + SqliteSession ses (this); // Maintains the locks. + char* errmsg = NULL; ::sqlite3_exec (handler, query, NULL, NULL, &errmsg); + if (errmsg) throw SqliteEx (std::string ("Sqlite::exec, error in query (") + query + "): " + errmsg); + return *this; +} + +/** + * Wraps the sqlite3_stmt; will prepare it, bind values, query and finalize. + */ +class SqliteQuery { + protected: + ::sqlite3_stmt* statement; + SqliteSession* session; + int bindCounter; + /// -1 if statement isn't DONE. + int mChanges; + void prepare (SqliteSession* session, char const* query, int queryLength) { + ::sqlite3* handler = *session; + if (::sqlite3_prepare_v2 (handler, query, queryLength, &statement, NULL) != SQLITE_OK) + throw SqliteEx (std::string(query, queryLength) + ": " + ::sqlite3_errmsg(handler)); + } + /** Shan't copy. */ + SqliteQuery (const SqliteQuery& other) = delete; + public: + SqliteQuery (SqliteQuery&& rvalue) { + statement = rvalue.statement; + session = rvalue.session; + bindCounter = rvalue.bindCounter; + mChanges = rvalue.mChanges; + rvalue.statement = nullptr; + } + /** + * Prepares the query. + * @throws SqliteEx if sqlite3_prepare fails; format of the error message is "$query: $errmsg". + */ + SqliteQuery (SqliteSession* session, char const* query, int queryLength) + : statement (NULL), session (session), bindCounter (0), mChanges (-1) { + prepare (session, query, queryLength); + } + /** + * Prepares the query. + * @throws SqliteEx if sqlite3_prepare fails; format of the error message is "$query: $errmsg". + */ + SqliteQuery (SqliteSession* session, std::pair query) + : statement (NULL), session (session), bindCounter (0), mChanges (-1) { + prepare (session, query.first, query.second); + } + /** + * Prepares the query. + * @throws SqliteEx if sqlite3_prepare fails; format of the error message is "$query: $errmsg". + */ + SqliteQuery (SqliteSession* session, std::string query) + : statement (NULL), session (session), bindCounter (0), mChanges (-1) { + prepare (session, query.c_str(), query.length()); + } + /** + * Release resources. + * @see http://sqlite.org/capi3ref.html#sqlite3_finalize + */ + ~SqliteQuery () { + if (statement) ::sqlite3_finalize (statement); + } + + /// Call this (followed by the #step) if you need the query to be re-executed. + /// @see http://sqlite.org/capi3ref.html#sqlite3_reset + SqliteQuery& reset () { + bindCounter = 0; + mChanges = -1; + ::sqlite3_reset (statement); + return *this; + } + + /// Synonym for #step. + bool next () {return step();} + /** + * Invokes sqlite3_step. + * @return \c true if there was a row fetched successfully, \c false if there is no more rows. + * @see http://sqlite.org/capi3ref.html#sqlite3_step + */ + bool step () { + if (mChanges >= 0) {mChanges = 0; return false;} + int ret = ::sqlite3_step (statement); + if (ret == SQLITE_ROW) return true; + if (ret == SQLITE_DONE) { + mChanges = ::sqlite3_changes (*session); + return false; + } + throw SqliteEx (std::string(::sqlite3_errmsg(*session))); + } + /** + * Perform #step and throw an exception if #step has returned \c false. + * Usage example: + * \code (ses.query(S("select count(*) from test where idx = ?")) << 12345).qstep().intAt(1) \endcode + */ + SqliteQuery& qstep () { + if (!step()) + throw SqliteEx (std::string("qstep: no rows returned / affected")); + return *this; + } + /** + * Invokes a DML query and returns the number of rows affected. + * Example: \code + * int affected = (ses.query(S("update test set count = count + ? where id = ?")) << 1 << 9).ustep(); + * \endcode + * @see http://sqlite.org/capi3ref.html#sqlite3_step + */ + int ustep () { + int ret = ::sqlite3_step (statement); + if (ret == SQLITE_DONE) { + mChanges = ::sqlite3_changes (*session); + return mChanges; + } + if (ret == SQLITE_ROW) return 0; + throw SqliteEx (std::string(::sqlite3_errmsg(*session))); + } + + /** + * The number of rows changed by the query. + * Providing the query was a DML (Data Modification Language), + * returns the number of rows updated.\n + * If the query wasn't a DML, returned value is undefined.\n + * -1 is returned if the query wasn't executed, or after #reset.\n + * Example: \code + * SqliteQuery query (&ses, S("update test set count = count + ? where id = ?")); + * query.bind (1, 1); + * query.bind (2, 9); + * query.step (); + * int affected = query.changes (); + * \endcode + * @see #ustep + */ + int changes () {return mChanges;} + + /** + * The integer value of the given column. + * @param column 1-based. + * @see http://sqlite.org/capi3ref.html#sqlite3_column_text + */ + int intAt (int column) { + return ::sqlite3_column_int (statement, --column); + } + + /** + * The integer value of the given column. + * @param column 1-based. + * @see http://sqlite.org/capi3ref.html#sqlite3_column_text + */ + sqlite3_int64 int64at (int column) { + return ::sqlite3_column_int64 (statement, --column); + } + + /** + * The floating point number from the given column. + * @param column 1-based. + * @see http://sqlite.org/capi3ref.html#sqlite3_column_text + */ + double doubleAt (int column) { + return ::sqlite3_column_double (statement, --column); + } + + /** + * Return the column as UTF-8 characters, which can be used until the next #step. + * @param column 1-based. + * @see http://sqlite.org/capi3ref.html#sqlite3_column_text + */ + std::pair charsAt (int column) { + return std::pair ((char const*) ::sqlite3_column_text (statement, column-1), + ::sqlite3_column_bytes (statement, column-1)); + } + + /** + * Return the column as C++ string (UTF-8). + * @param column 1-based. + */ + std::string stringAt (int column) { + return std::string ((char const*) ::sqlite3_column_text (statement, column-1), + ::sqlite3_column_bytes (statement, column-1)); + } + + /** + * The type of the column. + * SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB or SQLITE_NULL. + * @param column 1-based. + * @see http://sqlite.org/capi3ref.html#sqlite3_column_text + */ + int typeAt (int column) { + return ::sqlite3_column_type (statement, --column); + } + + /** + * Binds a value using one of the bind methods. + */ + template + SqliteQuery& operator << (T value) { + return bind (++bindCounter, value); + } + /** + * Binds a value using the named parameter and one of the bind methods. + * @throws SqliteEx if the name could not be found. + * @see http://sqlite.org/capi3ref.html#sqlite3_bind_parameter_index + */ + template + SqliteQuery& bind (char const* name, T value) { + int index = ::sqlite3_bind_parameter_index (statement, name); + if (index == 0) + throw SqliteEx (std::string ("No such parameter in the query: ") + name); + return bind (index, value); + } + + /** + * Bind a string to the query. + * @param transient must be true, if lifetime of the string might be shorter than that of the query. + */ + SqliteQuery& bind (int index, const char* text, int length, bool transient = false) { + if (::sqlite3_bind_text (statement, index, text, length, + transient ? SQLITE_TRANSIENT : SQLITE_STATIC) != SQLITE_OK) + throw SqliteEx (std::string (::sqlite3_errmsg (*session))); + return *this; + } + /** + * Bind a string to the query. + * @param transient must be true, if lifetime of the string might be shorter than that of the query. + */ + SqliteQuery& bind (int index, std::pair text, bool transient = false) { + if (::sqlite3_bind_text (statement, index, text.first, text.second, + transient ? SQLITE_TRANSIENT : SQLITE_STATIC) != SQLITE_OK) + throw SqliteEx (std::string (::sqlite3_errmsg (*session))); + return *this; + } + /** + * Bind a string to the query. + * @param transient must be true, if lifetime of the string might be shorter than that of the query. + */ + SqliteQuery& bind (int index, const std::string& text, bool transient = true) { + if (::sqlite3_bind_text (statement, index, text.data(), text.length(), + transient ? SQLITE_TRANSIENT : SQLITE_STATIC) != SQLITE_OK) + throw SqliteEx (std::string (::sqlite3_errmsg (*session))); + return *this; + } + /** + * Bind an integer to the query. + */ + SqliteQuery& bind (int index, int value) { + if (::sqlite3_bind_int (statement, index, value) != SQLITE_OK) + throw SqliteEx (std::string (::sqlite3_errmsg (*session))); + return *this; + } + /** + * Bind an 64-bit integer to the query. + */ + SqliteQuery& bind (int index, sqlite3_int64 value) { + if (::sqlite3_bind_int64 (statement, index, value) != SQLITE_OK) + throw SqliteEx (std::string (::sqlite3_errmsg (*session))); + return *this; + } +}; + +/** + * Version of SqliteQuery suitable for using SQLite in parallel with other processes. + * Will automatically handle the SQLITE_SCHEMA error + * and will automatically repeat attempts after SQLITE_BUSY, + * but it requires that the query string supplied + * is constant and available during the SqliteParQuery lifetime. + * Error messages, contained in exceptions, may differ from SqliteQuery by containing the query + * (for example, the #step method will throw "$query: $errmsg" instead of just "$errmsg"). + */ +class SqliteParQuery: public SqliteQuery { + protected: + char const* query; + int queryLength; + int repeat; + int wait; + public: + /** + * Prepares the query. + * @param repeat the number of times we try to repeat the query when SQLITE_BUSY is returned. + * @param wait how long, in milliseconds (1/1000 of a second) we are to wait before repeating. + * @throws SqliteEx if sqlite3_prepare fails; format of the error message is "$query: $errmsg". + */ + SqliteParQuery (SqliteSession* session, char const* query, int queryLength, int repeat = 90, int wait = 20) + : SqliteQuery (session, query, queryLength) { + this->query = query; + this->queryLength = queryLength; + this->repeat = repeat; + this->wait = wait; + } + /** + * Prepares the query. + * @param query the SQL query together with its length. + * @param repeat the number of times we try to repeat the query when SQLITE_BUSY is returned. + * @param wait how long, in milliseconds (1/1000 of a second) we are to wait before repeating. + * @throws SqliteEx if sqlite3_prepare fails; format of the error message is "$query: $errmsg". + */ + SqliteParQuery (SqliteSession* session, std::pair query, int repeat = 90, int wait = 20) + : SqliteQuery (session, query) { + this->query = query.first; + this->queryLength = query.second; + this->repeat = repeat; + this->wait = wait; + } + + bool next () {return step();} + bool step () { + if (mChanges >= 0) {mChanges = 0; return false;} + repeat: + int ret = ::sqlite3_step (statement); + if (ret == SQLITE_ROW) return true; + if (ret == SQLITE_DONE) { + mChanges = ::sqlite3_changes (*session); + return false; + } + if (ret == SQLITE_SCHEMA) { + ::sqlite3_stmt* old = statement; + prepare (session, query, queryLength); + ::sqlite3_transfer_bindings(old, statement); + ::sqlite3_finalize (old); + goto repeat; + } + if (ret == SQLITE_BUSY) for (int repeat = this->repeat; ret == SQLITE_BUSY && repeat >= 0; --repeat) { + //struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = wait * 1000000; // nan is 10^-9 of sec. + //while (::nanosleep (&ts, &ts) == EINTR); + ::sqlite3_sleep (wait); + ret = ::sqlite3_step (statement); + } + throw SqliteEx (std::string(query, queryLength) + ::sqlite3_errmsg(*session)); + } +}; + +template +SqliteQuery SqliteSession::query (T t) { + return SqliteQuery (this, t); +} + +}; // namespace glim + +#endif // GLIM_SQLITE_HPP_ diff --git a/external/glim/test_cbcoro.cc b/external/glim/test_cbcoro.cc new file mode 100644 index 00000000..f47cdac3 --- /dev/null +++ b/external/glim/test_cbcoro.cc @@ -0,0 +1,89 @@ +// http://en.wikipedia.org/wiki/Setcontext; man 3 makecontext; man 2 getcontext +// http://www.boost.org/doc/libs/1_53_0/libs/context/doc/html/index.html +// g++ -std=c++11 -O1 -Wall -g test_cbcoro.cc -pthread && ./a.out + +#include +#include + +#include "cbcoro.hpp" +#include +#include +#include // sleep +#include // strerror +#include +#include +using std::function; +#include +#include +using std::shared_ptr; using std::make_shared; +#include +using std::string; using std::to_string; +#include +using std::cout; using std::endl; + +/** A typical remote service with callback. */ +void esDelete (int frople, std::function cb) { + std::thread th ([cb,frople]() { + cout << "esDelete: sleeping for a second" << endl; + std::this_thread::sleep_for (std::chrono::seconds (1)); + cb (frople); + }); th.detach(); +} + +struct RemoveFroples: public glim::CBCoro { + const char* _argument; + RemoveFroples (const char* argument): _argument (argument) { + cout << "RF: constructor" << endl; + } + virtual ~RemoveFroples() {puts ("~RemoveFroples");} + virtual void run() override { + for (int i = 1; i <= 4; ++i) { + cout << "RF: Removing frople " << i << "..." << endl; + int returnedFrople = 0; + yieldForCallback ([this,i,&returnedFrople]() { + if (i != 2) { + // Sometimes we use a callback. + esDelete (i, [this,&returnedFrople](int frople) { + cout << "RF,CB: frople " << frople << "." << endl; + returnedFrople = frople; + invokeFromCallback(); + }); + } else { + // Sometimes we don't use a callback. + returnedFrople = 0; + invokeFromCallback(); + } + }); + cout << "RF: Returned from callback; _returnTo is: " << (intptr_t) _returnTo << "; frople " << returnedFrople << endl; + } + cout << "RF: finish! _returnTo is: " << (intptr_t) _returnTo << endl; + }; +}; + +int main() { + glim::cbCoro ([](glim::CBCoro* cbcoro) { + cout << "main: run1, thread " << std::this_thread::get_id() << endl; // Runs on the `main` thread. + cbcoro->yieldForCallback ([&]() { + std::thread callbackThread ([&]() { + std::this_thread::sleep_for (std::chrono::seconds (4)); + cbcoro->invokeFromCallback(); + }); callbackThread.detach(); + }); + cout << "main: run2, thread " << std::this_thread::get_id() << endl; // Runs on the `callbackThread`. + }); + + (new RemoveFroples ("argument"))->start(); + cout << "main: returned from RemoveFroples" << endl; + + glim::NsecTimer timer; const int ops = RUNNING_ON_VALGRIND ? 999 : 9999; + for (int i = 0; i < ops; ++i) glim::cbCoro ([](glim::CBCoro* cbcoro) {}); + double speedEmpty = ops / timer.sec(); + timer.restart(); + for (int i = 0; i < ops; ++i) glim::cbCoro ([](glim::CBCoro* cbcoro) {cbcoro->yieldForCallback ([&]() {cbcoro->invokeFromCallback();});}); + double speedImmediate = ops / timer.sec(); + + sleep (5); + cout << "speed: empty: " << speedEmpty << " o/s" << endl; + cout << "speed: immediate: " << speedImmediate << " o/s" << endl; + return 0; +} diff --git a/external/glim/test_exception.cc b/external/glim/test_exception.cc new file mode 100644 index 00000000..74759f4d --- /dev/null +++ b/external/glim/test_exception.cc @@ -0,0 +1,73 @@ +#define _GLIM_ALL_EXCEPTIONS_CODE +#include "exception.hpp" +#include +#include +#include + +// NB: Controlling exceptions across shared object (.so) boundaries is tested separately in frople/src/test.cpp/testExceptionControl. + +static void testThrowLine() { + int line = 0; std::string message; try { + line = __LINE__; GTHROW ("message"); + } catch (const std::exception& ex) { + message = ex.what(); + } + //std::cout << message << ' ' << std::flush; + assert (message.size()); + assert (std::string (message) .find (":" + std::to_string (line)) != std::string::npos); + + line = 0; message.clear(); std::string name; try { + line = __LINE__; G_DEFINE_EXCEPTION (FooEx); GNTHROW (FooEx, "foo"); + } catch (const std::exception& ex) { + message = ex.what(); name = typeid (ex) .name(); + } + //std::cout << "testThrowLine: " << message << ' ' << name << ' ' << std::flush; + assert (message.size()); + assert (std::string (message) .find (":" + std::to_string (line)) != std::string::npos); + assert (name.find ("FooEx") != std::string::npos); + + message.clear(); try { + glim::ExceptionControl plainWhat (glim::Exception::PLAIN_WHAT); + GTHROW ("bar"); + } catch (const std::exception& ex) { + message = ex.what(); + } + assert (message == "bar"); + assert (glim::Exception::options() == 0); +} + +static void testBacktrace() { + assert (glim::Exception::options() == 0); + glim::ExceptionControl captureTrace (glim::Exception::CAPTURE_TRACE); + assert (glim::Exception::options() != 0); + std::string message; + try { + GTHROW ("message"); + } catch (const std::exception& ex) { + message = ex.what(); + } + //std::cout << "testBacktrace: " << message << std::endl; + if (message.find ("[at bin/test_exception") == std::string::npos && message.find ("[test_exception") == std::string::npos) + GTHROW ("No expected string in " + message); +} + +static void testAllExceptionsHack() { + assert (glim::Exception::options() == 0); + std::string traceBuf; + glim::ExceptionHandler traceExceptions (glim::Exception::HANDLE_ALL | glim::Exception::RENDEZVOUS, glim::captureBacktrace, &traceBuf); + assert (glim::Exception::options() != 0); + try { + throw "catch me"; // Catched by `_GLIM_ALL_EXCEPTIONS_CODE` and handled with `glim::ExceptionControl::backtrace`. + } catch (const char* skip) {} + //std::cout << "testAllExceptionsHack: " << std::endl << traceBuf << std::endl; + assert (traceBuf.size()); +} + +int main () { + std::cout << "Testing exception.hpp ... " << std::flush; + testThrowLine(); + testBacktrace(); + testAllExceptionsHack(); + std::cout << "pass." << std::endl; + return 0; +} diff --git a/external/glim/test_gstring.cc b/external/glim/test_gstring.cc new file mode 100644 index 00000000..974fa293 --- /dev/null +++ b/external/glim/test_gstring.cc @@ -0,0 +1,131 @@ +#include "gstring.hpp" +using glim::gstring; +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static void testIterators(); +static void testBoost(); +static void testStrftime(); + +int main () { + std::cout << "Testing gstring.hpp ... " << std::flush; + + gstring gs; + if (gs.needsFreeing()) throw std::runtime_error ("Default gstring needsFreeing"); + if (gs.capacity() != 1) throw std::runtime_error ("Default gstring capacity is not 1"); + char buf16[16]; + gstring gs16 (sizeof (buf16), buf16, false, 0); + if (gs16.capacity() != 16) throw std::runtime_error ("gs16 capacity != 16"); + if (gs16.size() != 0) throw std::runtime_error ("gs16 size != 0"); + gstring gsFree (17, NULL, true, 0); + if (!gsFree.needsFreeing()) throw std::runtime_error ("!needsFreeing"); + if (gsFree.capacity() != 16) throw std::runtime_error ("gsFree capacity != 16"); + if (gsFree.size() != 0) throw std::runtime_error ("gsFree size != 0"); + gstring gsRO (0, NULL, false, 0); + if (gsRO.needsFreeing()) throw std::runtime_error ("needsFreeing"); + if (gsRO.capacity() != 1) throw std::runtime_error ("gsRO capacity != 1"); + if (gsRO.size() != 0) throw std::runtime_error ("gsRO size != 0"); + char buf32[32]; + gstring gs32 (sizeof (buf32), buf32, false, 0); + if (gs32.capacity() != 32) throw std::runtime_error ("capacity != 32"); + if (gs32.size() != 0) throw std::runtime_error ("gs32 size != 0"); + const gstring foo = C2GSTRING ("foo"); + if (foo.needsFreeing()) throw std::runtime_error ("foo needsFreeing"); + if (foo != "foo") throw std::runtime_error ("foo != foo"); + if (foo.size() != 3) throw std::runtime_error ("foo not 3"); + std::ostringstream oss; oss << gs16 << gsFree << gsRO << gs32 << foo; + if (oss.str() != "foo") throw std::runtime_error ("oss foo != foo"); + glim::gstring_stream gss (gs16); std::ostream gsos (&gss); + gsos << "bar" << std::flush; + if (gs16 != "bar") throw std::runtime_error ("gs16 != bar"); + gsos << "beer" << std::flush; + if (gs16 != "barbeer") throw std::runtime_error ("gs16 != barbeer"); + gsos << "123456789" << std::flush; + if (gs16 != "barbeer123456789") throw std::runtime_error ("gs16 != barbeer123456789"); + if (gs16.capacity() != 16) throw std::runtime_error ("gs16 != 16"); + gsos << '0' << std::flush; + if (gs16 != "barbeer1234567890") throw std::runtime_error ("gs16 != barbeer1234567890"); + if (gs16.capacity() != 32) throw std::runtime_error ("gs16 != 32"); + + gstring gsb; std::string str ("abc"); + gsb << 'a' << 1 << 2LL << str; + std::string ns ("1:3,"); std::istringstream nsi (ns); + gsb.readNetstring (nsi); + if (gsb != "a12abc3") throw std::runtime_error ("gsb != a12abc3"); + if (strcmp (gsb.c_str(), "a12abc3") != 0) throw std::runtime_error ("strcmp ! 0"); + + gsb.clear().appendNetstring ("foo") .appendNetstring ("bar"); + if (gsb != "3:foo,3:bar,") throw std::runtime_error ("gsb != 3:foo,3:bar,"); + uint32_t pos = 0; + if (gsb.netstringAt (pos, &pos) != "foo" || gsb.netstringAt (pos, &pos) != "bar" || pos != gsb.length()) + throw std::runtime_error ("gsb !netstringAt"); + + gs32.clear() << 12345 << ','; + if (gs32.intAt (0, &pos) != 12345 || pos != 5) throw std::runtime_error ("gsb !12345"); + if (gs32.intAt (1, &pos) != 2345 || pos != 5) throw std::runtime_error ("gsb !2345"); + if (gs32.intAt (5, &pos) != 0 || pos != 5) throw std::runtime_error ("gsb !0"); + + if ((gs32.clear() << 123).erase (0) != "23") throw std::runtime_error ("!23"); + if ((gs32.clear() << 123).erase (1) != "13") throw std::runtime_error ("!13"); + if ((gs32.clear() << 123).erase (2) != "12") throw std::runtime_error ("!12"); + + std::unordered_map map; + map[glim::gstring ("foo")] = 1; + glim::gstring bar ("bar"); + map[bar] = 1; + map[glim::gstring ("sum")] = map[glim::gstring ("foo")] + map[glim::gstring ("bar")]; + if (map[glim::gstring ("sum")] != 2) throw std::runtime_error ("sum != 2"); + map.clear(); + + gstring gs1 ("foo"); gstring gs2 ("bar"); + gs1 = gstring (gs2 << "_"); // Copying in order to malloc length() bytes. + if (gs1 != "bar_") throw std::runtime_error ("!bar_"); + if (gs1.capacity() != 1) throw std::runtime_error ("bar_ != 4"); + + testIterators(); + testBoost(); + testStrftime(); + + std::cout << "pass." << std::endl; + return 0; +} + +static void testIterators() { + gstring foo (C2GSTRING ("foo")); + gstring buf; for (auto it = foo.begin(), end = foo.end(); it != end; ++it) buf << *it; + assert (buf == "foo"); + assert (boost::starts_with (foo, "f") && boost::ends_with (foo, "oo")); +} + +static void testBoost() { + gstring str (" foo\t\r\n"); + boost::trim (str); + assert (str == "foo"); + + gstring up ("FOO"); boost::to_lower (up); + assert (up == "foo"); +} + +static void testStrftime() { + time_t tim = time(0); struct tm ltime; memset (<ime, 0, sizeof ltime); if (!localtime_r (&tim, <ime)) GTHROW ("!localtime_r"); + GSTRING_ON_STACK (t1, 8); assert (t1.capacity() == 8); + t1.appendTime ("", <ime); assert (t1 == ""); + t1.appendTime ("foo %a, %d %b %Y %T %z bar", <ime); + assert (t1.capacity() > 8); // Capacity increased to account for the large string. + assert (boost::starts_with (t1, "foo ")); + assert (boost::ends_with (t1, " bar")); + + GSTRING_ON_STACK (t2, 8); assert (t2.capacity() == 8); + t2.appendTime ("%H", <ime); + assert (t2.capacity() == 8); // 8 is big enought, isn't it? + assert (t2.intAt (0) == ltime.tm_hour); // NB: intAt is safe here because strftime adds an uncounted null-terminator. +} diff --git a/external/glim/test_ldb.cc b/external/glim/test_ldb.cc new file mode 100644 index 00000000..74587ae1 --- /dev/null +++ b/external/glim/test_ldb.cc @@ -0,0 +1,165 @@ +#include "ldb.hpp" +using glim::Ldb; +using glim::gstring; +#include +using std::cout; using std::flush; using std::endl; +#include +#include + +void test1 (Ldb& ldb) { + ldb.put (std::string ("foo_"), std::string ("bar")); + ldb.put ((uint32_t) 123, 1); + ldb.put ((uint32_t) 123, 2); + ldb.put (C2GSTRING ("foo"), 3); + ldb.put (C2GSTRING ("foo"), 4); + ldb.put (C2GSTRING ("gsk"), C2GSTRING ("gsv")); + std::string ts; int ti; gstring tgs; + auto fail = [](std::string msg) {throw std::runtime_error ("assertion failed: " + msg);}; + if (!ldb.get (std::string ("foo_"), ts) || ts != "bar") fail ("!foo_=bar"); + if (!ldb.get ((uint32_t) 123, ti) || ti != 2) fail ("!123=2"); + if (!ldb.get (C2GSTRING ("foo"), ti) || ti != 4) fail ("!foo=4"); + if (!ldb.get (C2GSTRING ("gsk"), tgs) || tgs != "gsv") fail ("!gsk=gsv"); + + // Test range-based for. + int count = 0; bool haveGskGsv = false; + for (auto&& entry: ldb) { + if (!entry._lit->Valid()) fail ("!entry"); + if (entry.keyView() == "gsk") { + if (entry.getKey() != "gsk") fail ("getKey(gsk)!=gsk"); + if (entry.getValue() != "gsv") fail ("getValue(gsk)!=gsv"); + haveGskGsv = true; + } + ++count;} + if (count != 4) fail ("count!=4"); // foo_=bar, 123=2, foo=4, gsk=gsv + if (!haveGskGsv) fail ("!haveGskGsv"); + + ldb.del ((uint32_t) 123); if (ldb.get ((uint32_t) 123, ti)) fail ("123"); + ldb.del (C2GSTRING ("foo")); if (ldb.get (C2GSTRING ("foo"), ti)) fail ("foo"); + ldb.del (std::string ("foo_")); + + { // We've erased "123" and "foo", the only key left is "gsk" (gsk=gsv), let's test the iterator boundaries on this small dataset. + auto&& it = ldb.begin(); + if (it->getKey() != "gsk") fail ("first key !gsk " + it->keyView().str()); + if (!(++it).end()) fail ("++it != end"); + if ((--it)->getKey() != "gsk") fail ("can't go back to gsk"); + if (!(--it).end()) fail ("--it != end"); + if ((++it)->getKey() != "gsk") fail ("can't go forward to gsk"); + } + +// todo: index trigger example +// struct SimpleIndexTrigger: public Trigger { // Uses key space partitioning (cf. http://stackoverflow.com/a/12503799/257568) +// const char* _name; Ldb _indexDb; +// SimpleIndexTrigger (Ldb& ldb, const char* name = "index"): _name (name), _indexDb (ldb._env, name) {} +// gstring triggerName() {return gstring (0, (void*) _name, false, strlen (_name), true);} +// void add (Ldb& ldb, void* key, gstring& kbytes, void* value, gstring& vbytes, Transaction& txn) { +// MDB_val mkey = {vbytes.size(), (void*) vbytes.data()}; +// MDB_val mvalue = {kbytes.size(), (void*) kbytes.data()}; +// int rc = ::ldb_put (txn.get(), _indexDb._dbi, &mkey, &mvalue, 0); +// if (rc) GNTHROW (LdbEx, std::string ("index, ldb_put: ") + ::strerror (rc)); +// } +// void erase (Ldb& ldb, void* ekey, gstring& kbytes, Transaction& txn) { +// // Get all the values and remove them from the index. +// MDB_cursor* cur = 0; int rc = ::ldb_cursor_open (txn.get(), ldb._dbi, &cur); +// if (rc) GNTHROW (LdbEx, std::string ("index, erase, ldb_cursor_open: ") + ::strerror (rc)); +// std::unique_ptr curHolder (cur, ::ldb_cursor_close); +// MDB_val mkey = {kbytes.size(), (void*) kbytes.data()}, val = {0, 0}; +// rc = ::ldb_cursor_get (cur, &mkey, &val, ::MDB_SET_KEY); if (rc == MDB_NOTFOUND) return; +// if (rc) GNTHROW (LdbEx, std::string ("index, erase, ldb_cursor_get: ") + ::strerror (rc)); +// rc = ::ldb_del (txn.get(), _indexDb._dbi, &val, &mkey); +// if (rc && rc != MDB_NOTFOUND) GNTHROW (LdbEx, std::string ("index, erase, ldb_del: ") + ::strerror (rc)); +// for (;;) { +// rc = ::ldb_cursor_get (cur, &mkey, &val, ::MDB_NEXT_DUP); if (rc == MDB_NOTFOUND) return; +// if (rc) GNTHROW (LdbEx, std::string ("index, erase, ldb_cursor_get: ") + ::strerror (rc)); +// rc = ::ldb_del (txn.get(), _indexDb._dbi, &val, &mkey); +// if (rc && rc != MDB_NOTFOUND) GNTHROW (LdbEx, std::string ("index, erase, ldb_del: ") + ::strerror (rc)); +// } +// } +// void eraseKV (Ldb& ldb, void* key, gstring& kbytes, void* value, gstring& vbytes, Transaction& txn) { +// MDB_val mkey = {vbytes.size(), (void*) vbytes.data()}; +// MDB_val mvalue = {kbytes.size(), (void*) kbytes.data()}; +// int rc = ::ldb_del (txn.get(), _indexDb._dbi, &mkey, &mvalue); +// if (rc && rc != MDB_NOTFOUND) GNTHROW (LdbEx, std::string ("index, ldb_del: ") + ::strerror (rc)); +// } +// }; +// auto indexTrigger = std::make_shared (ldb); ldb.setTrigger (indexTrigger); auto& indexDb = indexTrigger->_indexDb; +// ldb.erase (C2GSTRING ("gsk")); // NB: "gsk" wasn't indexed here. `IndexTrigger.erase` should handle this gracefully. + + // Add indexed. +// ldb.put (C2GSTRING ("ik"), C2GSTRING ("iv1")); +// ldb.put (C2GSTRING ("ik"), string ("iv2")); +// ldb.put (C2GSTRING ("ik"), 3); + // Check the index. +// gstring ik; +// if (!indexDb.first (C2GSTRING ("iv1"), ik) || ik != "ik") fail ("!iv1=ik"); +// if (!indexDb.first (string ("iv2"), ik) || ik != "ik") fail ("!iv2=ik"); +// if (!indexDb.first (3, ik) || ik != "ik") fail ("!iv3=ik"); + + // Remove indexed. +// ldb.eraseKV (C2GSTRING ("ik"), string ("iv2")); + // Check the index. +// if (!indexDb.first (C2GSTRING ("iv1"), ik) || ik != "ik") fail ("!iv1=ik"); +// if (indexDb.first (string ("iv2"), ik)) fail ("iv2=ik"); +// if (!indexDb.first (3, ik) || ik != "ik") fail ("!iv3=ik"); + + // Remove indexed. +// ldb.erase (C2GSTRING ("ik")); + // Check the index. +// if (indexDb.first (C2GSTRING ("iv1"), ik)) fail ("iv1"); +// if (indexDb.first (3, ik)) fail ("iv3"); + // Check the data. +// if (ldb.first (C2GSTRING ("ik"), ik)) fail ("ik"); +} + +void testStartsWith (Ldb& ldb) { + // Using `gstring`s because the Boost Serialization encoding for CStrings is not prefix-friendly. + ldb.put (C2GSTRING ("01"), ""); ldb.put (C2GSTRING ("02"), ""); + ldb.put (C2GSTRING ("11"), ""); + ldb.put (C2GSTRING ("21"), ""); ldb.put (C2GSTRING ("222"), ""); ldb.put (C2GSTRING ("2"), ""); + + auto range = ldb.startsWith (C2GSTRING ("0")); auto it = range.begin(); + assert (it->keyView() == "01"); assert (it != range.end()); + assert ((++it)->keyView() == "02"); assert (it != range.end()); + assert ((++it)->keyView().empty()); assert (it == range.end()); + assert ((--it)->keyView() == "02"); assert (it != range.end()); + assert ((--it)->keyView() == "01"); assert (it != range.end()); + assert ((--it)->keyView().empty()); assert (it == range.end()); + // `it` and `range.begin` point to the same `leveldb::Iterator`. + assert (range.begin()._entry->_lit == it._entry->_lit); + assert (!range.begin()._entry->_valid); assert (range.begin()->keyView().empty()); + + range = ldb.startsWith (C2GSTRING ("0")); it = range.end(); + assert (it.end() && it->keyView().empty()); assert (it != range.begin()); + assert ((--it)->keyView() == "02"); assert (it != range.begin()); + assert ((--it)->keyView() == "01"); assert (it == range.begin()); + assert ((--it)->keyView().empty()); assert (it != range.begin()); + + int8_t count = 0; for (auto& en: ldb.startsWith (C2GSTRING ("1"))) {en.keyView(); ++count;} assert (count == 1); + count = 0; for (auto& en: ldb.startsWith (C2GSTRING ("2"))) {en.keyView(); ++count;} assert (count == 3); + count = 0; for (auto& en: ldb.startsWith (C2GSTRING ("-"))) {en.keyView(); ++count;} assert (count == 0); + count = 0; for (auto& en: ldb.startsWith (C2GSTRING (""))) {en.keyView(); ++count;} assert (count == 6); + + assert (ldb.startsWith (C2GSTRING ("-")) .empty()); + + count = 0; for (auto& en: boost::make_iterator_range (ldb.end().seek ("1"), ldb.end().seek ("2"))) {en.keyView(); ++count;} assert (count == 1); + count = 0; for (auto& en: boost::make_iterator_range (ldb.end().seek ("2"), ldb.end().seek ("3"))) {en.keyView(); ++count;} assert (count == 3); + count = 0; for (auto& en: ldb.range (C2GSTRING ("1"), C2GSTRING ("2"))) {en.keyView(); ++count;} assert (count == 1); + + { auto range = ldb.range (C2GSTRING ("0"), C2GSTRING ("1")); // 01 and 02, but not 11. + count = 0; for (auto& en: range) {en.keyView(); ++count;} assert (count == 2); } +} + +int main() { + cout << "Testing ldb.hpp ... " << flush; + boost::filesystem::remove_all ("/dev/shm/ldbTest"); + + Ldb ldb ("/dev/shm/ldbTest"); + test1 (ldb); + + for (auto& en: ldb) ldb.del (en.keyView()); + testStartsWith (ldb); + + ldb._db.reset(); // Close. + boost::filesystem::remove_all ("/dev/shm/ldbTest"); + cout << "pass." << endl; + return 0; +} diff --git a/external/glim/test_runner.cc b/external/glim/test_runner.cc new file mode 100644 index 00000000..8d9e0838 --- /dev/null +++ b/external/glim/test_runner.cc @@ -0,0 +1,74 @@ +#include "runner.hpp" +#include "curl.hpp" +#include +#include +#include +#include + +static void testRunner() { + using glim::RunnerV2; + auto runner = RunnerV2::instance(); + + struct Dugout: public glim::CurlmInformationListener { + glim::Curl _curl; + std::promise _promise; + void schedule (RunnerV2* runner) { + _curl.http ("http://glim.ru/", 2); + curl_easy_setopt (_curl._curl, CURLOPT_PRIVATE, this); // Tells `addToCURLM` to call this listener later. + runner->addToCURLM (_curl._curl); + } + virtual FreeOptions information (CURLMsg* msg, CURLM* curlm) override { + _promise.set_value (_curl.str()); + return static_cast (REMOVE_CURL_FROM_CURLM | DELETE_LISTENER); + } + virtual ~Dugout() {} + }; + { Dugout* dugout = new Dugout(); + auto future = dugout->_promise.get_future(); + dugout->schedule (runner.get()); + if (future.get().find ("(); curl->http ("http://glim.ru/", 2); + auto promise = std::make_shared>(); auto future = promise->get_future(); + runner->addToCURLM (curl->_curl, [curl,promise](CURLMsg*, CURLM*) {promise->set_value (curl->str());}); + if (future.get().find (" evbase (event_base_new(), event_base_free); + glim::Runner runner (evbase, [](const char* error) {std::cerr << error << std::endl;}); + + auto scheduledJobFired = std::make_shared (false); + runner.schedule (0.f, [=](glim::Runner::JobInfo&)->bool {*scheduledJobFired = true; return false;}); + + auto curl = std::make_shared (false); curl->http ("http://glim.ru/env.cgi?pause=50", 5); + auto curlDebug = std::make_shared(); curl->debugListenerF ([curlDebug](const char* bytes, size_t size) {curlDebug->append (bytes, size);}); + volatile bool ran = false; + runner.multi (curl->_curl, [curl,&ran,evbase,curlDebug](CURLMsg* msg) { + std::cout << " status: " << curl->status(); + if (curl->status() == 200) std::cout << " ip: " << curl->gstr().view (0, std::max (curl->gstr().find ("\n"), 0)); + if (curlDebug->find ("GET /env.cgi") == std::string::npos) std::cerr << " No headers in debug? " << *curlDebug << std::endl; + ran = true; + event_base_loopbreak (evbase.get()); + }); + //struct timeval tv {1, 0}; event_base_loopexit (evbase.get(), &tv); // Exit the loop in a sec. + event_base_dispatch (evbase.get()); + if (!ran) GTHROW ("!ran"); + if (!*scheduledJobFired) GTHROW ("!scheduledJobFired"); + + std::cout << " pass." << std::endl; + //waiting: "was introduced in Libevent 2.1.1-alpha"//libevent_global_shutdown(); +} + +int main () { + std::cout << "Testing runner.hpp ..." << std::flush; try { + + testRunner(); + oldTest(); + + } catch (const std::exception& ex) { + std::cerr << " exception: " << ex.what() << std::endl; + return 1; + } + return 0; +} diff --git a/external/glim/test_sqlite.cc b/external/glim/test_sqlite.cc new file mode 100644 index 00000000..72624487 --- /dev/null +++ b/external/glim/test_sqlite.cc @@ -0,0 +1,18 @@ + +#include "sqlite.hpp" +#define S(cstr) (std::pair (cstr, sizeof (cstr) - 1)) +#include +#include +using namespace glim; + +int main () { + std::cout << "Testing sqlite.hpp ... " << std::flush; + Sqlite sqlite (":memory:"); + SqliteSession sqs (&sqlite); + assert (sqs.query ("CREATE TABLE test (t TEXT, i INTEGER)") .ustep() == 0); + assert (sqs.query ("INSERT INTO test VALUES (?, ?)") .bind (1, S("foo")) .bind (2, 27) .ustep() == 1); + assert (sqs.query ("SELECT t FROM test") .qstep() .stringAt (1) == "foo"); + assert (sqs.query ("SELECT i FROM test") .qstep() .intAt (1) == 27); + std::cout << "pass." << std::endl; + return 0; +}