/* * Copyright 2015 Cloudius Systems */ #pragma once #include "compound.hh" // // This header provides adaptors between the representation used by our compound_type<> // and representation used by Origin. // // For single-component keys the legacy representation is equivalent // to the only component's serialized form. For composite keys it the following // (See org.apache.cassandra.db.marshal.CompositeType): // // ::= ( )+ // ::= // ::= // ::= // // is component's value in serialized form. is always 0 for partition key. // // Given a representation serialized using @CompoundType, provides a view on the // representation of the same components as they would be serialized by Origin. // // The view is exposed in a form of a byte range. For example of use see to_legacy() function. template class legacy_compound_view { static_assert(!CompoundType::is_prefixable, "Legacy view not defined for prefixes"); CompoundType& _type; bytes_view _packed; public: legacy_compound_view(CompoundType& c, bytes_view packed) : _type(c) , _packed(packed) { } class iterator : public std::iterator { bool _singular; // Offset within virtual output space of a component. // // Offset: -2 -1 0 ... LEN-1 LEN // Field: [ length MSB ] [ length LSB ] [ VALUE ] [ EOC ] // int32_t _offset; typename CompoundType::iterator _i; public: struct end_tag {}; iterator(const legacy_compound_view& v) : _singular(v._type.is_singular()) , _offset(_singular ? 0 : -2) , _i(v._type.begin(v._packed)) { } iterator(const legacy_compound_view& v, end_tag) : _offset(-2) , _i(v._type.end(v._packed)) { } value_type operator*() const { int32_t component_size = _i->size(); if (_offset == -2) { return (component_size >> 8) & 0xff; } else if (_offset == -1) { return component_size & 0xff; } else if (_offset < component_size) { return (*_i)[_offset]; } else { // _offset == component_size return 0; // EOC field } } iterator& operator++() { auto component_size = (int32_t) _i->size(); if (_offset < component_size // When _singular, we skip the EOC byte. && (!_singular || _offset != (component_size - 1))) { ++_offset; } else { ++_i; _offset = -2; } return *this; } bool operator==(const iterator& other) const { return _offset == other._offset && other._i == _i; } bool operator!=(const iterator& other) const { return !(*this == other); } }; // A trichotomic comparator defined on @CompoundType representations which // orders them according to lexicographical ordering of their corresponding // legacy representations. // // tri_comparator(t)(k1, k2) // // ...is equivalent to: // // compare_unsigned(to_legacy(t, k1), to_legacy(t, k2)) // // ...but more efficient. // struct tri_comparator { const CompoundType& _type; tri_comparator(const CompoundType& type) : _type(type) { } // @k1 and @k2 must be serialized using @type, which was passed to the constructor. int operator()(bytes_view k1, bytes_view k2) const { if (_type.is_singular()) { return compare_unsigned(*_type.begin(k1), *_type.begin(k2)); } return lexicographical_tri_compare( _type.begin(k1), _type.end(k1), _type.begin(k2), _type.end(k2), [] (const bytes_view& c1, const bytes_view& c2) -> int { if (c1.size() != c2.size()) { return c1.size() < c2.size() ? -1 : 1; } return memcmp(c1.begin(), c2.begin(), c1.size()); }); } }; // Equivalent to std::distance(begin(), end()), but computes faster size_t size() const { if (_type.is_singular()) { return _type.begin(_packed)->size(); } size_t s = 0; for (auto&& component : _type.components(_packed)) { s += 2 /* length field */ + component.size() + 1 /* EOC */; } return s; } iterator begin() const { return iterator(*this); } iterator end() const { return iterator(*this, typename iterator::end_tag()); } }; // Converts compound_type<> representation to legacy representation // @packed is assumed to be serialized using supplied @type. template static inline bytes to_legacy(CompoundType& type, bytes_view packed) { legacy_compound_view lv(type, packed); bytes legacy_form(bytes::initialized_later(), lv.size()); std::copy(lv.begin(), lv.end(), legacy_form.begin()); return legacy_form; }