diff options
Diffstat (limited to 'storage/FileSystemModule.cpp')
-rw-r--r-- | storage/FileSystemModule.cpp | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/storage/FileSystemModule.cpp b/storage/FileSystemModule.cpp new file mode 100644 index 000000000..ed2f8cdef --- /dev/null +++ b/storage/FileSystemModule.cpp @@ -0,0 +1,304 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FileSystemModule.h" + +#include "sqlite3.h" +#include "nsString.h" +#include "nsISimpleEnumerator.h" +#include "nsIFile.h" + +namespace { + +struct VirtualTableCursorBase +{ + VirtualTableCursorBase() + { + memset(&mBase, 0, sizeof(mBase)); + } + + sqlite3_vtab_cursor mBase; +}; + +struct VirtualTableCursor : public VirtualTableCursorBase +{ +public: + VirtualTableCursor() + : mRowId(-1) + { + mCurrentFileName.SetIsVoid(true); + } + + const nsString& DirectoryPath() const + { + return mDirectoryPath; + } + + const nsString& CurrentFileName() const + { + return mCurrentFileName; + } + + int64_t RowId() const + { + return mRowId; + } + + nsresult Init(const nsAString& aPath); + nsresult NextFile(); + +private: + nsCOMPtr<nsISimpleEnumerator> mEntries; + + nsString mDirectoryPath; + nsString mCurrentFileName; + + int64_t mRowId; +}; + +nsresult +VirtualTableCursor::Init(const nsAString& aPath) +{ + nsCOMPtr<nsIFile> directory = + do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); + NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE); + + nsresult rv = directory->InitWithPath(aPath); + NS_ENSURE_SUCCESS(rv, rv); + + rv = directory->GetPath(mDirectoryPath); + NS_ENSURE_SUCCESS(rv, rv); + + rv = directory->GetDirectoryEntries(getter_AddRefs(mEntries)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = NextFile(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +VirtualTableCursor::NextFile() +{ + bool hasMore; + nsresult rv = mEntries->HasMoreElements(&hasMore); + NS_ENSURE_SUCCESS(rv, rv); + + if (!hasMore) { + mCurrentFileName.SetIsVoid(true); + return NS_OK; + } + + nsCOMPtr<nsISupports> entry; + rv = mEntries->GetNext(getter_AddRefs(entry)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIFile> file = do_QueryInterface(entry); + NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); + + rv = file->GetLeafName(mCurrentFileName); + NS_ENSURE_SUCCESS(rv, rv); + + mRowId++; + + return NS_OK; +} + +int Connect(sqlite3* aDB, void* aAux, int aArgc, const char* const* aArgv, + sqlite3_vtab** aVtab, char** aErr) +{ + static const char virtualTableSchema[] = + "CREATE TABLE fs (" + "name TEXT, " + "path TEXT" + ")"; + + int rc = sqlite3_declare_vtab(aDB, virtualTableSchema); + if (rc != SQLITE_OK) { + return rc; + } + + sqlite3_vtab* vt = new sqlite3_vtab(); + memset(vt, 0, sizeof(*vt)); + + *aVtab = vt; + + return SQLITE_OK; +} + +int Disconnect(sqlite3_vtab* aVtab ) +{ + delete aVtab; + + return SQLITE_OK; +} + +int BestIndex(sqlite3_vtab* aVtab, sqlite3_index_info* aInfo) +{ + // Here we specify what index constraints we want to handle. That is, there + // might be some columns with particular constraints in which we can help + // SQLite narrow down the result set. + // + // For example, take the "path = x" where x is a directory. In this case, + // we can narrow our search to just this directory instead of the entire file + // system. This can be a significant optimization. So, we want to handle that + // constraint. To do so, we would look for two specific input conditions: + // + // 1. aInfo->aConstraint[i].iColumn == 1 + // 2. aInfo->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_EQ + // + // The first states that the path column is being used in one of the input + // constraints and the second states that the constraint involves the equal + // operator. + // + // An even more specific search would be for name='xxx', in which case we + // can limit the search to a single file, if it exists. + // + // What we have to do here is look for all of our index searches and select + // the narrowest. We can only pick one, so obviously we want the one that + // is the most specific, which leads to the smallest result set. + + for(int i = 0; i < aInfo->nConstraint; i++) { + if (aInfo->aConstraint[i].iColumn == 1 && aInfo->aConstraint[i].usable) { + if (aInfo->aConstraint[i].op & SQLITE_INDEX_CONSTRAINT_EQ) { + aInfo->aConstraintUsage[i].argvIndex = 1; + } + break; + } + + // TODO: handle single files (constrained also by the name column) + } + + return SQLITE_OK; +} + +int Open(sqlite3_vtab* aVtab, sqlite3_vtab_cursor** aCursor) +{ + VirtualTableCursor* cursor = new VirtualTableCursor(); + + *aCursor = reinterpret_cast<sqlite3_vtab_cursor*>(cursor); + + return SQLITE_OK; +} + +int Close(sqlite3_vtab_cursor* aCursor) +{ + VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); + + delete cursor; + + return SQLITE_OK; +} + +int Filter(sqlite3_vtab_cursor* aCursor, int aIdxNum, const char* aIdxStr, + int aArgc, sqlite3_value** aArgv) +{ + VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); + + if(aArgc <= 0) { + return SQLITE_OK; + } + + nsDependentString path( + reinterpret_cast<const char16_t*>(::sqlite3_value_text16(aArgv[0]))); + + nsresult rv = cursor->Init(path); + NS_ENSURE_SUCCESS(rv, SQLITE_ERROR); + + return SQLITE_OK; +} + +int Next(sqlite3_vtab_cursor* aCursor) +{ + VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); + + nsresult rv = cursor->NextFile(); + NS_ENSURE_SUCCESS(rv, SQLITE_ERROR); + + return SQLITE_OK; +} + +int Eof(sqlite3_vtab_cursor* aCursor) +{ + VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); + return cursor->CurrentFileName().IsVoid() ? 1 : 0; +} + +int Column(sqlite3_vtab_cursor* aCursor, sqlite3_context* aContext, + int aColumnIndex) +{ + VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); + + switch (aColumnIndex) { + // name + case 0: { + const nsString& name = cursor->CurrentFileName(); + sqlite3_result_text16(aContext, name.get(), + name.Length() * sizeof(char16_t), + SQLITE_TRANSIENT); + break; + } + + // path + case 1: { + const nsString& path = cursor->DirectoryPath(); + sqlite3_result_text16(aContext, path.get(), + path.Length() * sizeof(char16_t), + SQLITE_TRANSIENT); + break; + } + default: + NS_NOTREACHED("Unsupported column!"); + } + + return SQLITE_OK; +} + +int RowId(sqlite3_vtab_cursor* aCursor, sqlite3_int64* aRowid) +{ + VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); + + *aRowid = cursor->RowId(); + + return SQLITE_OK; +} + +} // namespace + +namespace mozilla { +namespace storage { + +int RegisterFileSystemModule(sqlite3* aDB, const char* aName) +{ + static sqlite3_module module = { + 1, + Connect, + Connect, + BestIndex, + Disconnect, + Disconnect, + Open, + Close, + Filter, + Next, + Eof, + Column, + RowId, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }; + + return sqlite3_create_module(aDB, aName, &module, nullptr); +} + +} // namespace storage +} // namespace mozilla |