indexed_db_cursor.cc revision 868fa2fe829687343ffae624259930155e16dbd8
1868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// found in the LICENSE file. 4868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 5868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "content/browser/indexed_db/indexed_db_cursor.h" 6868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 7868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/logging.h" 8868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "content/browser/indexed_db/indexed_db_backing_store.h" 9868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "content/browser/indexed_db/indexed_db_callbacks_wrapper.h" 10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "content/browser/indexed_db/indexed_db_database.h" 11868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "content/browser/indexed_db/indexed_db_database_error.h" 12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "content/browser/indexed_db/indexed_db_tracing.h" 13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "content/browser/indexed_db/indexed_db_transaction.h" 14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "content/common/indexed_db/indexed_db_key_range.h" 15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 16868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)namespace content { 17868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 18868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)class IndexedDBCursor::CursorIterationOperation 19868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) : public IndexedDBTransaction::Operation { 20868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) public: 21868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) CursorIterationOperation(scoped_refptr<IndexedDBCursor> cursor, 22868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_ptr<IndexedDBKey> key, 23868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_refptr<IndexedDBCallbacksWrapper> callbacks) 24868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) : cursor_(cursor), key_(key.Pass()), callbacks_(callbacks) {} 25868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; 26868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 27868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) private: 28868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_refptr<IndexedDBCursor> cursor_; 29868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_ptr<IndexedDBKey> key_; 30868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_refptr<IndexedDBCallbacksWrapper> callbacks_; 31868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}; 32868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 33868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)class IndexedDBCursor::CursorAdvanceOperation 34868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) : public IndexedDBTransaction::Operation { 35868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) public: 36868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) CursorAdvanceOperation(scoped_refptr<IndexedDBCursor> cursor, 37868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) uint32 count, 38868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_refptr<IndexedDBCallbacksWrapper> callbacks) 39868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) : cursor_(cursor), count_(count), callbacks_(callbacks) {} 40868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; 41868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 42868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) private: 43868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_refptr<IndexedDBCursor> cursor_; 44868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) uint32 count_; 45868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_refptr<IndexedDBCallbacksWrapper> callbacks_; 46868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}; 47868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 48868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)class IndexedDBCursor::CursorPrefetchIterationOperation 49868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) : public IndexedDBTransaction::Operation { 50868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) public: 51868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) CursorPrefetchIterationOperation( 52868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_refptr<IndexedDBCursor> cursor, 53868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) int number_to_fetch, 54868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_refptr<IndexedDBCallbacksWrapper> callbacks) 55868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) : cursor_(cursor), 56868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) number_to_fetch_(number_to_fetch), 57868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) callbacks_(callbacks) {} 58868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; 59868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 60868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) private: 61868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_refptr<IndexedDBCursor> cursor_; 62868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) int number_to_fetch_; 63868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_refptr<IndexedDBCallbacksWrapper> callbacks_; 64868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}; 65868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 66868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)IndexedDBCursor::IndexedDBCursor( 67868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_ptr<IndexedDBBackingStore::Cursor> cursor, 68868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) indexed_db::CursorType cursor_type, 69868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) IndexedDBDatabase::TaskType task_type, 70868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) IndexedDBTransaction* transaction) 71868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) : task_type_(task_type), 72868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) cursor_type_(cursor_type), 73868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) transaction_(transaction), 74868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) cursor_(cursor.Pass()), 75868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) closed_(false) { 76868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) transaction_->RegisterOpenCursor(this); 77868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 78868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 79868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)IndexedDBCursor::~IndexedDBCursor() { 80868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) transaction_->UnregisterOpenCursor(this); 81868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 82868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 83868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void IndexedDBCursor::ContinueFunction( 84868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_ptr<IndexedDBKey> key, 85868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_refptr<IndexedDBCallbacksWrapper> callbacks) { 86868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) IDB_TRACE("IndexedDBCursor::continue"); 87868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 88868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) transaction_->ScheduleTask( 89868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) task_type_, new CursorIterationOperation(this, key.Pass(), callbacks)); 90868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 91868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 92868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void IndexedDBCursor::Advance( 93868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) uint32 count, 94868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_refptr<IndexedDBCallbacksWrapper> callbacks) { 95868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) IDB_TRACE("IndexedDBCursor::advance"); 96868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 97868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) transaction_->ScheduleTask( 98868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) new CursorAdvanceOperation(this, count, callbacks)); 99868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 100868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 101868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void IndexedDBCursor::CursorAdvanceOperation::Perform( 102868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) IndexedDBTransaction* /*transaction*/) { 103868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) IDB_TRACE("CursorAdvanceOperation"); 104868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (!cursor_->cursor_ || !cursor_->cursor_->Advance(count_)) { 105868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) cursor_->cursor_.reset(); 106868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) callbacks_->OnSuccess(static_cast<std::vector<char>*>(NULL)); 107868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) return; 108868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 109868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 110868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) callbacks_->OnSuccess( 111868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) cursor_->key(), cursor_->primary_key(), cursor_->Value()); 112868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 113868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 114868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void IndexedDBCursor::CursorIterationOperation::Perform( 115868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) IndexedDBTransaction* /*transaction*/) { 116868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) IDB_TRACE("CursorIterationOperation"); 117868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (!cursor_->cursor_ || !cursor_->cursor_->ContinueFunction(key_.get())) { 118868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) cursor_->cursor_.reset(); 119868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) callbacks_->OnSuccess(static_cast<std::vector<char>*>(NULL)); 120868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) return; 121868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 122868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 123868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) callbacks_->OnSuccess( 124868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) cursor_->key(), cursor_->primary_key(), cursor_->Value()); 125868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 126868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 127868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void IndexedDBCursor::PrefetchContinue( 128868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) int number_to_fetch, 129868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scoped_refptr<IndexedDBCallbacksWrapper> callbacks) { 130868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) IDB_TRACE("IndexedDBCursor::prefetch_continue"); 131868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 132868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) transaction_->ScheduleTask( 133868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) task_type_, 134868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) new CursorPrefetchIterationOperation(this, number_to_fetch, callbacks)); 135868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 136868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 137868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void IndexedDBCursor::CursorPrefetchIterationOperation::Perform( 138868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) IndexedDBTransaction* /*transaction*/) { 139868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) IDB_TRACE("CursorPrefetchIterationOperation"); 140868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 141868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) std::vector<IndexedDBKey> found_keys; 142868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) std::vector<IndexedDBKey> found_primary_keys; 143868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) std::vector<std::vector<char> > found_values; 144868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 145868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (cursor_->cursor_) 146868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) cursor_->saved_cursor_.reset(cursor_->cursor_->Clone()); 147868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) const size_t max_size_estimate = 10 * 1024 * 1024; 148868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) size_t size_estimate = 0; 149868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 150868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) for (int i = 0; i < number_to_fetch_; ++i) { 151868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (!cursor_->cursor_ || !cursor_->cursor_->ContinueFunction(0)) { 152868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) cursor_->cursor_.reset(); 153868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) break; 154868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 155868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 156868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) found_keys.push_back(cursor_->cursor_->key()); 157868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) found_primary_keys.push_back(cursor_->cursor_->primary_key()); 158868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 159868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) switch (cursor_->cursor_type_) { 160868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) case indexed_db::CURSOR_KEY_ONLY: 161868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) found_values.push_back(std::vector<char>()); 162868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) break; 163868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) case indexed_db::CURSOR_KEY_AND_VALUE: { 164868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) std::vector<char> value; 165868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) value.swap(*cursor_->cursor_->Value()); 166868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) size_estimate += value.size(); 167868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) found_values.push_back(value); 168868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) break; 169868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 170868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) default: 171868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) NOTREACHED(); 172868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 173868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) size_estimate += cursor_->cursor_->key().size_estimate(); 174868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) size_estimate += cursor_->cursor_->primary_key().size_estimate(); 175868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 176868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (size_estimate > max_size_estimate) 177868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) break; 178868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 179868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 180868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (!found_keys.size()) { 181868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) callbacks_->OnSuccess(static_cast<std::vector<char>*>(NULL)); 182868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) return; 183868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 184868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 185868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) callbacks_->OnSuccessWithPrefetch( 186868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) found_keys, found_primary_keys, found_values); 187868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 188868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 189868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void IndexedDBCursor::PrefetchReset(int used_prefetches, int) { 190868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) IDB_TRACE("IndexedDBCursor::prefetch_reset"); 191868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) cursor_.swap(saved_cursor_); 192868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) saved_cursor_.reset(); 193868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 194868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (closed_) 195868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) return; 196868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (cursor_) { 197868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) for (int i = 0; i < used_prefetches; ++i) { 198868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) bool ok = cursor_->ContinueFunction(); 199868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) DCHECK(ok); 200868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 201868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 202868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 203868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 204868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void IndexedDBCursor::Close() { 205868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) IDB_TRACE("IndexedDBCursor::close"); 206868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) closed_ = true; 207868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) cursor_.reset(); 208868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) saved_cursor_.reset(); 209868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 210868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 211868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} // namespace content 212