diff options
Diffstat (limited to 'storage/FileSystemModule.cpp')
-rw-r--r-- | storage/FileSystemModule.cpp | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/storage/FileSystemModule.cpp b/storage/FileSystemModule.cpp new file mode 100644 index 0000000000..df756838f8 --- /dev/null +++ b/storage/FileSystemModule.cpp @@ -0,0 +1,254 @@ +/* -*- 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 "nsComponentManagerUtils.h" +#include "nsString.h" +#include "nsIDirectoryEnumerator.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<nsIDirectoryEnumerator> 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; + } + + const char16_t* value = + static_cast<const char16_t*>(::sqlite3_value_text16(aArgv[0])); + + nsDependentString path(value, + ::sqlite3_value_bytes16(aArgv[0]) / sizeof(char16_t)); + + 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: + MOZ_ASSERT_UNREACHABLE("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 |