// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- #include "include/interval_set.h" #include "include/buffer.h" #include "include/encoding.h" #include #include #include #include #ifndef OBJECT_H #define OBJECT_H class ContDesc { public: int objnum; int cursnap; unsigned seqnum; std::string prefix; std::string oid; ContDesc() : objnum(0), cursnap(0), seqnum(0), prefix("") {} ContDesc(int objnum, int cursnap, unsigned seqnum, const std::string &prefix) : objnum(objnum), cursnap(cursnap), seqnum(seqnum), prefix(prefix) {} bool operator==(const ContDesc &rhs) { return (rhs.objnum == objnum && rhs.cursnap == cursnap && rhs.seqnum == seqnum && rhs.prefix == prefix && rhs.oid == oid); } bool operator<(const ContDesc &rhs) const { return seqnum < rhs.seqnum; } bool operator!=(const ContDesc &rhs) { return !((*this) == rhs); } void encode(bufferlist &bl) const; void decode(bufferlist::iterator &bp); }; WRITE_CLASS_ENCODER(ContDesc) std::ostream &operator<<(std::ostream &out, const ContDesc &rhs); class ContentsGenerator { public: class iterator_impl { public: virtual char operator*() = 0; virtual iterator_impl &operator++() = 0; virtual void seek(uint64_t pos) = 0; virtual bool end() = 0; virtual ContDesc get_cont() const = 0; virtual uint64_t get_pos() const = 0; virtual bufferlist gen_bl_advance(uint64_t s) { bufferptr ret = buffer::create(s); for (uint64_t i = 0; i < s; ++i, ++(*this)) { ret[i] = **this; } bufferlist _ret; _ret.push_back(ret); return _ret; } virtual bool check_bl_advance(bufferlist &bl, uint64_t *off = nullptr) { uint64_t _off = 0; for (bufferlist::iterator i = bl.begin(); !i.end(); ++i, ++_off, ++(*this)) { if (*i != **this) { if (off) *off = _off; return false; } } return true; } virtual ~iterator_impl() {}; }; class iterator { public: ContentsGenerator *parent; iterator_impl *impl; char operator *() { return **impl; } iterator &operator++() { ++(*impl); return *this; }; void seek(uint64_t pos) { impl->seek(pos); } bool end() { return impl->end(); } ~iterator() { parent->put_iterator_impl(impl); } iterator(const iterator &rhs) : parent(rhs.parent) { impl = parent->dup_iterator_impl(rhs.impl); } iterator &operator=(const iterator &rhs) { iterator new_iter(rhs); swap(new_iter); return *this; } void swap(iterator &other) { ContentsGenerator *otherparent = other.parent; other.parent = parent; parent = otherparent; iterator_impl *otherimpl = other.impl; other.impl = impl; impl = otherimpl; } bufferlist gen_bl_advance(uint64_t s) { return impl->gen_bl_advance(s); } bool check_bl_advance(bufferlist &bl, uint64_t *off = nullptr) { return impl->check_bl_advance(bl, off); } iterator(ContentsGenerator *parent, iterator_impl *impl) : parent(parent), impl(impl) {} }; virtual uint64_t get_length(const ContDesc &in) = 0; virtual void get_ranges_map( const ContDesc &cont, std::map &out) = 0; void get_ranges(const ContDesc &cont, interval_set &out) { std::map ranges; get_ranges_map(cont, ranges); for (std::map::iterator i = ranges.begin(); i != ranges.end(); ++i) { out.insert(i->first, i->second); } } virtual iterator_impl *get_iterator_impl(const ContDesc &in) = 0; virtual iterator_impl *dup_iterator_impl(const iterator_impl *in) = 0; virtual void put_iterator_impl(iterator_impl *in) = 0; virtual ~ContentsGenerator() {}; iterator get_iterator(const ContDesc &in) { return iterator(this, get_iterator_impl(in)); } }; class RandGenerator : public ContentsGenerator { public: typedef std::minstd_rand0 RandWrap; class iterator_impl : public ContentsGenerator::iterator_impl { public: uint64_t pos; ContDesc cont; RandWrap rand; RandGenerator *cont_gen; char current; iterator_impl(const ContDesc &cont, RandGenerator *cont_gen) : pos(0), cont(cont), rand(cont.seqnum), cont_gen(cont_gen) { current = rand(); } ContDesc get_cont() const override { return cont; } uint64_t get_pos() const override { return pos; } iterator_impl &operator++() override { pos++; current = rand(); return *this; } char operator*() override { return current; } void seek(uint64_t _pos) override { if (_pos < pos) { iterator_impl begin = iterator_impl(cont, cont_gen); begin.seek(_pos); *this = begin; } while (pos < _pos) { ++(*this); } } bool end() override { return pos >= cont_gen->get_length(cont); } }; ContentsGenerator::iterator_impl *get_iterator_impl(const ContDesc &in) override { RandGenerator::iterator_impl *i = new iterator_impl(in, this); return i; } void put_iterator_impl(ContentsGenerator::iterator_impl *in) override { delete in; } ContentsGenerator::iterator_impl *dup_iterator_impl( const ContentsGenerator::iterator_impl *in) override { ContentsGenerator::iterator_impl *retval = get_iterator_impl(in->get_cont()); retval->seek(in->get_pos()); return retval; } }; class VarLenGenerator : public RandGenerator { uint64_t max_length; uint64_t min_stride_size; uint64_t max_stride_size; public: VarLenGenerator( uint64_t length, uint64_t min_stride_size, uint64_t max_stride_size) : max_length(length), min_stride_size(min_stride_size), max_stride_size(max_stride_size) {} void get_ranges_map( const ContDesc &cont, std::map &out) override; uint64_t get_length(const ContDesc &in) override { RandWrap rand(in.seqnum); if (max_length == 0) return 0; return (rand() % (max_length/2)) + ((max_length - 1)/2) + 1; } }; class AttrGenerator : public RandGenerator { uint64_t max_len; uint64_t big_max_len; public: AttrGenerator(uint64_t max_len, uint64_t big_max_len) : max_len(max_len), big_max_len(big_max_len) {} void get_ranges_map( const ContDesc &cont, std::map &out) override { out.insert(std::pair(0, get_length(cont))); } uint64_t get_length(const ContDesc &in) override { RandWrap rand(in.seqnum); // make some attrs big if (in.seqnum & 3) return (rand() % max_len); else return (rand() % big_max_len); } bufferlist gen_bl(const ContDesc &in) { bufferlist bl; for (iterator i = get_iterator(in); !i.end(); ++i) { bl.append(*i); } assert(bl.length() < big_max_len); return bl; } }; class AppendGenerator : public RandGenerator { uint64_t off; uint64_t alignment; uint64_t min_append_size; uint64_t max_append_size; uint64_t max_append_total; uint64_t round_up(uint64_t in, uint64_t by) { if (by) in += (by - (in % by)); return in; } public: AppendGenerator( uint64_t off, uint64_t alignment, uint64_t min_append_size, uint64_t _max_append_size, uint64_t max_append_multiple) : off(off), alignment(alignment), min_append_size(round_up(min_append_size, alignment)), max_append_size(round_up(_max_append_size, alignment)) { if (_max_append_size == min_append_size) max_append_size += alignment; max_append_total = max_append_multiple * max_append_size; } uint64_t get_append_size(const ContDesc &in) { RandWrap rand(in.seqnum); return round_up(rand() % max_append_total, alignment); } uint64_t get_length(const ContDesc &in) override { return off + get_append_size(in); } void get_ranges_map( const ContDesc &cont, std::map &out) override; }; class ObjectDesc { public: ObjectDesc() : exists(false), dirty(false), version(0) {} ObjectDesc(const ContDesc &init, ContentsGenerator *cont_gen) : exists(false), dirty(false), version(0) { layers.push_front(std::pair, ContDesc>(ceph::shared_ptr(cont_gen), init)); } class iterator { public: uint64_t pos; uint64_t size; uint64_t cur_valid_till; class ContState { interval_set ranges; const uint64_t size; public: ContDesc cont; ceph::shared_ptr gen; ContentsGenerator::iterator iter; ContState( ContDesc _cont, ceph::shared_ptr _gen, ContentsGenerator::iterator _iter) : size(_gen->get_length(_cont)), cont(_cont), gen(_gen), iter(_iter) { gen->get_ranges(cont, ranges); } const interval_set &get_ranges() { return ranges; } uint64_t get_size() { return gen->get_length(cont); } bool covers(uint64_t pos) { return ranges.contains(pos) || (!ranges.starts_after(pos) && pos >= size); } uint64_t next(uint64_t pos) { assert(!covers(pos)); return ranges.starts_after(pos) ? ranges.start_after(pos) : size; } uint64_t valid_till(uint64_t pos) { assert(covers(pos)); return ranges.contains(pos) ? ranges.end_after(pos) : std::numeric_limits::max(); } }; std::list layers; struct StackState { const uint64_t next; const uint64_t size; }; std::list::iterator, StackState> > stack; std::list::iterator current; explicit iterator(ObjectDesc &obj) : pos(0), size(obj.layers.begin()->first->get_length(obj.layers.begin()->second)), cur_valid_till(0) { for (auto &&i : obj.layers) { layers.push_back({i.second, i.first, i.first->get_iterator(i.second)}); } current = layers.begin(); adjust_stack(); } void adjust_stack(); iterator &operator++() { assert(cur_valid_till >= pos); ++pos; if (pos >= cur_valid_till) { adjust_stack(); } return *this; } char operator*() { if (current == layers.end()) { return '\0'; } else { return pos >= size ? '\0' : *(current->iter); } } bool end() { return pos >= size; } void seek(uint64_t _pos) { if (_pos < pos) { ceph_abort(); } while (pos < _pos) { assert(cur_valid_till >= pos); uint64_t next = std::min(_pos - pos, cur_valid_till - pos); pos += next; if (pos >= cur_valid_till) { assert(pos == cur_valid_till); adjust_stack(); } } assert(pos == _pos); } bufferlist gen_bl_advance(uint64_t s) { bufferlist ret; while (s > 0) { assert(cur_valid_till >= pos); uint64_t next = std::min(s, cur_valid_till - pos); if (current != layers.end() && pos < size) { ret.append(current->iter.gen_bl_advance(next)); } else { ret.append_zero(next); } pos += next; assert(next <= s); s -= next; if (pos >= cur_valid_till) { assert(cur_valid_till == pos); adjust_stack(); } } return ret; } bool check_bl_advance(bufferlist &bl, uint64_t *error_at = nullptr) { uint64_t off = 0; while (off < bl.length()) { assert(cur_valid_till >= pos); uint64_t next = std::min(bl.length() - off, cur_valid_till - pos); bufferlist to_check; to_check.substr_of(bl, off, next); if (current != layers.end() && pos < size) { if (!current->iter.check_bl_advance(to_check, error_at)) { if (error_at) *error_at += off; return false; } } else { uint64_t at = pos; for (auto i = to_check.begin(); !i.end(); ++i, ++at) { if (*i) { if (error_at) *error_at = at; return false; } } } pos += next; off += next; assert(off <= bl.length()); if (pos >= cur_valid_till) { assert(cur_valid_till == pos); adjust_stack(); } } assert(off == bl.length()); return true; } }; iterator begin() { return iterator(*this); } bool deleted() { return !exists; } bool has_contents() { return layers.size(); } // takes ownership of gen void update(ContentsGenerator *gen, const ContDesc &next); bool check(bufferlist &to_check); bool check_sparse(const std::map& extends, bufferlist &to_check); const ContDesc &most_recent(); ContentsGenerator *most_recent_gen() { return layers.begin()->first.get(); } std::map attrs; // Both omap and xattrs bufferlist header; bool exists; bool dirty; uint64_t version; std::string redirect_target; private: std::list, ContDesc> > layers; }; #endif