susumu.yata
null+****@clear*****
Mon May 27 15:46:10 JST 2013
susumu.yata 2013-05-27 15:46:10 +0900 (Mon, 27 May 2013) New Revision: 04fd8405a74b3c5ef66564025fd3d6b4e4fae463 https://github.com/groonga/grnxx/commit/04fd8405a74b3c5ef66564025fd3d6b4e4fae463 Message: Add implementations of grnxx::MapCursor. Added files: lib/grnxx/map/cursor_impl.cpp lib/grnxx/map/cursor_impl.hpp lib/grnxx/map_cursor.cpp lib/grnxx/map_cursor.hpp lib/grnxx/map_cursor_query.hpp Copied files: lib/grnxx/map/scanner_impl.hpp (from lib/grnxx/map/scanner.hpp) Removed files: lib/grnxx/map/cursor.cpp Modified files: lib/grnxx/Makefile.am lib/grnxx/map.cpp lib/grnxx/map.hpp lib/grnxx/map/Makefile.am Renamed files: lib/grnxx/map/scanner_impl.cpp (from lib/grnxx/map/scanner.cpp) lib/grnxx/map_scanner.cpp (from lib/grnxx/map/cursor.hpp) lib/grnxx/map_scanner.hpp (from lib/grnxx/map/scanner.hpp) Modified: lib/grnxx/Makefile.am (+5 -0) =================================================================== --- lib/grnxx/Makefile.am 2013-05-24 20:28:33 +0900 (759da75) +++ lib/grnxx/Makefile.am 2013-05-27 15:46:10 +0900 (7625569) @@ -30,6 +30,8 @@ libgrnxx_la_SOURCES = \ grnxx.cpp \ logger.cpp \ map.cpp \ + map_cursor.cpp \ + map_scanner.cpp \ mutex.cpp \ os.cpp \ periodic_clock.cpp \ @@ -63,6 +65,9 @@ libgrnxx_include_HEADERS = \ lock.hpp \ logger.hpp \ map.hpp \ + map_cursor.hpp \ + map_cursor_query.hpp \ + map_scanner.hpp \ mutex.hpp \ os.hpp \ periodic_clock.hpp \ Modified: lib/grnxx/map.cpp (+14 -45) =================================================================== --- lib/grnxx/map.cpp 2013-05-24 20:28:33 +0900 (eaec002) +++ lib/grnxx/map.cpp 2013-05-27 15:46:10 +0900 (e4f112d) @@ -25,9 +25,10 @@ #include "grnxx/storage.hpp" #include "grnxx/string_builder.hpp" #include "grnxx/map/array_map.hpp" +#include "grnxx/map/cursor_impl.hpp" #include "grnxx/map/header.hpp" #include "grnxx/map/helper.hpp" -#include "grnxx/map/scanner.hpp" +#include "grnxx/map/scanner_impl.hpp" namespace grnxx { @@ -53,33 +54,6 @@ StringBuilder &operator<<(StringBuilder &builder, MapType type) { MapOptions::MapOptions() {} -MapCursorOptions::MapCursorOptions() - : flags(MAP_CURSOR_DEFAULT), - offset(0), - limit(std::numeric_limits<uint64_t>::max()) {} - -template <typename T> -MapCursor<T>::MapCursor() : key_id_(MAP_INVALID_KEY_ID), key_() {} - -template <typename T> -MapCursor<T>::~MapCursor() {} - -template <typename T> -bool MapCursor<T>::remove() { - GRNXX_ERROR() << "invalid operation"; - return false; -} - -template <typename T> -MapScanner<T>::MapScanner() - : offset_(0), - size_(0), - key_id_(MAP_INVALID_KEY_ID), - key_() {} - -template <typename T> -MapScanner<T>::~MapScanner() {} - template <typename T> Map<T>::Map() {} @@ -273,14 +247,23 @@ bool Map<T>::truncate() { } template <typename T> -MapCursor<T> *Map<T>::create_cursor(const MapCursorOptions &) { +MapCursor<T> *Map<T>::create_cursor(MapCursorAll<T>, + const MapCursorOptions &) { + // TODO: Give a naive implementation. + GRNXX_ERROR() << "invalid operation"; + return nullptr; +} + +template <typename T> +MapCursor<T> *Map<T>::create_cursor(const MapCursorKeyIDRange<T> &, + const MapCursorOptions &) { // TODO: Give a naive implementation. GRNXX_ERROR() << "invalid operation"; return nullptr; } template <typename T> -MapCursor<T> *Map<T>::create_cursor(const map::CursorQuery<T> &, +MapCursor<T> *Map<T>::create_cursor(const MapCursorKeyRange<T> &, const MapCursorOptions &) { // TODO: Give a naive implementation. GRNXX_ERROR() << "invalid operation"; @@ -296,23 +279,9 @@ MapScanner<T> *Map<T>::create_scanner(KeyArg, const Charset *) { template <> MapScanner<Bytes> *Map<Bytes>::create_scanner(KeyArg query, const Charset *charset) { - return map::Scanner<Bytes>::create(this, query, charset); + return map::ScannerImpl<Bytes>::create(this, query, charset); } -template class MapCursor<int8_t>; -template class MapCursor<uint8_t>; -template class MapCursor<int16_t>; -template class MapCursor<uint16_t>; -template class MapCursor<int32_t>; -template class MapCursor<uint32_t>; -template class MapCursor<int64_t>; -template class MapCursor<uint64_t>; -template class MapCursor<double>; -template class MapCursor<GeoPoint>; -template class MapCursor<Bytes>; - -template class MapScanner<Bytes>; - template class Map<int8_t>; template class Map<uint8_t>; template class Map<int16_t>; Modified: lib/grnxx/map.hpp (+25 -114) =================================================================== --- lib/grnxx/map.hpp 2013-05-24 20:28:33 +0900 (9281bc4) +++ lib/grnxx/map.hpp 2013-05-27 15:46:10 +0900 (e99fada) @@ -18,36 +18,30 @@ #ifndef GRNXX_MAP_HPP #define GRNXX_MAP_HPP +#include "grnxx/features.hpp" + #include "grnxx/flags_impl.hpp" +#include "grnxx/map_cursor.hpp" +#include "grnxx/map_cursor_query.hpp" +#include "grnxx/map_scanner.hpp" #include "grnxx/traits.hpp" #include "grnxx/types.hpp" namespace grnxx { -namespace map { - -template <typename T> class DummyKeyID; -template <typename T> class DummyKey; -template <typename T> class CursorQuery; - -} // namespace map -class StringBuilder; - -class Storage; class Charset; - -template <typename T> class Map; +class Storage; +class StringBuilder; constexpr int64_t MAP_MIN_KEY_ID = 0; constexpr int64_t MAP_MAX_KEY_ID = (1LL << 40) - 2; constexpr int64_t MAP_INVALID_KEY_ID = MAP_MAX_KEY_ID + 1; enum MapType : uint32_t { - MAP_UNKNOWN = 0, - MAP_ARRAY = 1, // Array-based implementation. - MAP_DOUBLE_ARRAY = 2, // TODO: DoubleArray-based implementation. - MAP_PATRICIA = 3, // TODO: Patricia-based implementation. - MAP_HASH_TABLE = 4 // TODO: HashTable-based implementation. + MAP_ARRAY = 0, // Array-based implementation. + MAP_DOUBLE_ARRAY = 1, // TODO: DoubleArray-based implementation. + MAP_PATRICIA = 2, // TODO: Patricia-based implementation. + MAP_HASH_TABLE = 3 // TODO: HashTable-based implementation. }; StringBuilder &operator<<(StringBuilder &builder, MapType type); @@ -57,103 +51,11 @@ struct MapOptions { MapOptions(); }; -// TODO: How to implement NEAR cursor. -struct MapCursorFlagsIdentifier; -using MapCursorFlags = FlagsImpl<MapCursorFlagsIdentifier>; - -// Use the default settings. -constexpr MapCursorFlags MAP_CURSOR_DEFAULT = - MapCursorFlags::define(0x000); -// Sort keys by ID. -constexpr MapCursorFlags MAP_CURSOR_ORDER_BY_ID = - MapCursorFlags::define(0x001); -// Sort keys by key. -constexpr MapCursorFlags MAP_CURSOR_ORDER_BY_KEY = - MapCursorFlags::define(0x002); -// Access keys in reverse order. -constexpr MapCursorFlags MAP_CURSOR_REVERSE_ORDER = - MapCursorFlags::define(0x010); - -struct MapCursorOptions { - MapCursorFlags flags; - uint64_t offset; - uint64_t limit; - - // Initialize the members. - MapCursorOptions(); -}; - -template <typename T> -class MapCursor { - public: - using Key = typename Traits<T>::Type; - using KeyArg = typename Traits<T>::ArgumentType; - - MapCursor(); - virtual ~MapCursor(); - - // Move the cursor to the next key and return true on success. - virtual bool next() = 0; - // Remove the current key and return true on success. - virtual bool remove(); - - // Return the ID of the current key. - int64_t key_id() const { - return key_id_; - } - // Return the current key. - const Key &key() const { - return key_; - } - - protected: - int64_t key_id_; - Key key_; -}; - -template <typename T> -class MapScanner { - public: - using Key = typename Traits<T>::Type; - using KeyArg = typename Traits<T>::ArgumentType; - - MapScanner(); - virtual ~MapScanner(); - - // Find the next key from the rest of the query and return true on success. - virtual bool next() = 0; - - // Return the start position of the found key. - uint64_t offset() const { - return offset_; - } - // Return the size of the found key. - uint64_t size() const { - return size_; - } - // Return the ID of the found key. - int64_t key_id() const { - return key_id_; - } - // Return the found key. - const Key &key() const { - return key_; - } - - protected: - uint64_t offset_; - uint64_t size_; - int64_t key_id_; - Key key_; -}; - template <typename T> class Map { public: using Key = typename Traits<T>::Type; using KeyArg = typename Traits<T>::ArgumentType; - using DummyKeyID = map::DummyKeyID<T>; - using DummyKey = map::DummyKey<T>; using Cursor = MapCursor<T>; using Scanner = MapScanner<T>; @@ -227,20 +129,29 @@ class Map { // TODO: Not yet fixed. // Return a reference to create a cursor query. - const DummyKeyID &key_id() const { - return *static_cast<const DummyKeyID *>(nullptr); + MapCursorAll<T> all() const { + return MapCursorAll<T>(); } // Return a reference to create a cursor query. - const DummyKey &key() const { - return *static_cast<const DummyKey *>(nullptr); + MapCursorKeyID<T> key_id() const { + return MapCursorKeyID<T>(); + } + // Return a reference to create a cursor query. + MapCursorKey<T> key() const { + return MapCursorKey<T>(); } // Create a cursor for accessing all the keys. virtual Cursor *create_cursor( + MapCursorAll<T> query, + const MapCursorOptions &options = MapCursorOptions()); + // Create a cursor for accessing keys that satisfy "query". + virtual Cursor *create_cursor( + const MapCursorKeyIDRange<T> &query, const MapCursorOptions &options = MapCursorOptions()); // Create a cursor for accessing keys that satisfy "query". virtual Cursor *create_cursor( - const map::CursorQuery<T> &query, + const MapCursorKeyRange<T> &query, const MapCursorOptions &options = MapCursorOptions()); // Create a MapScanner object to find keys in "query". Modified: lib/grnxx/map/Makefile.am (+4 -4) =================================================================== --- lib/grnxx/map/Makefile.am 2013-05-24 20:28:33 +0900 (d430696) +++ lib/grnxx/map/Makefile.am 2013-05-27 15:46:10 +0900 (76179d5) @@ -5,14 +5,14 @@ libgrnxx_map_la_LDFLAGS = @AM_LTLDFLAGS@ libgrnxx_map_la_SOURCES = \ array_map.cpp \ bytes_store.cpp \ - cursor.cpp \ - scanner.cpp + cursor_impl.cpp \ + scanner_impl.cpp libgrnxx_map_includedir = ${includedir}/grnxx/map libgrnxx_map_include_HEADERS = \ array_map.hpp \ bytes_store.hpp \ - cursor.hpp \ + cursor_impl.hpp \ header.hpp \ helper.hpp \ - scanner.hpp + scanner_impl.hpp Deleted: lib/grnxx/map/cursor.cpp (+0 -24) 100644 =================================================================== --- lib/grnxx/map/cursor.cpp 2013-05-24 20:28:33 +0900 (6e6c0d9) +++ /dev/null @@ -1,24 +0,0 @@ -/* - Copyright (C) 2013 Brazil, Inc. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#include "grnxx/map/cursor.hpp" - -namespace grnxx { -namespace map { - -} // namespace map -} // namespace grnxx Added: lib/grnxx/map/cursor_impl.cpp (+269 -0) 100644 =================================================================== --- /dev/null +++ lib/grnxx/map/cursor_impl.cpp 2013-05-27 15:46:10 +0900 (ed981c4) @@ -0,0 +1,269 @@ +/* + Copyright (C) 2013 Brazil, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "grnxx/map/cursor_impl.hpp" + +#include <memory> +#include <new> + +#include "grnxx/bytes.hpp" +#include "grnxx/geo_point.hpp" +#include "grnxx/logger.hpp" +#include "grnxx/map.hpp" + +namespace grnxx { +namespace map { + +template <typename T> +KeyIDRangeCursor<T>::KeyIDRangeCursor() + : MapCursor<T>(), map_(), cur_(), end_(), step_(), count_(0), + query_(), options_() {} + +template <typename T> +KeyIDRangeCursor<T>::~KeyIDRangeCursor() {} + +template <typename T> +KeyIDRangeCursor<T> *KeyIDRangeCursor<T>::create( + Map<T> *map, + const MapCursorKeyIDRange<T> &query, + const MapCursorOptions &options) { + std::unique_ptr<KeyIDRangeCursor<T>> cursor( + new (std::nothrow) KeyIDRangeCursor<T>); + if (!cursor) { + GRNXX_ERROR() << "new grnxx::map::KeyIDRangeCursor<T> failed"; + return nullptr; + } + if (!cursor->init(map, query, options)) { + return nullptr; + } + return cursor.release(); +} + +template <typename T> +bool KeyIDRangeCursor<T>::next() { + if (count_ >= options_.limit) { + return false; + } + while (cur_ != end_) { + cur_ += step_; + if (map_->get(cur_, &this->key_)) { + this->key_id_ = cur_; + ++count_; + return true; + } + } + return false; +} + +template <typename T> +bool KeyIDRangeCursor<T>::remove() { + return map_->unset(this->key_id_); +} + +template <typename T> +bool KeyIDRangeCursor<T>::init(Map<T> *map, + const MapCursorKeyIDRange<T> &query, + const MapCursorOptions &options) { + map_ = map; + query_ = query; + options_ = options; + options_.flags = MAP_CURSOR_ORDER_BY_ID; + if (options.flags & MAP_CURSOR_REVERSE_ORDER) { + options_.flags |= MAP_CURSOR_REVERSE_ORDER; + } + + int64_t min; + if (query.flags & MAP_CURSOR_KEY_ID_GREATER) { + min = query_.min + 1; + } else if (query.flags & MAP_CURSOR_KEY_ID_GREATER_EQUAL) { + min = query_.min; + } else { + min = map->min_key_id(); + } + + int64_t max; + if (query.flags & MAP_CURSOR_KEY_ID_LESS) { + max = query_.max + 1; + } else if (query.flags & MAP_CURSOR_KEY_ID_LESS_EQUAL) { + max = query_.max; + } else { + max = map->max_key_id(); + } + + if (min > max) { + // There are no keys in the range [min, max]. + cur_ = end_ = 0; + return true; + } + + if (options_.flags & MAP_CURSOR_REVERSE_ORDER) { + cur_ = max + 1; + end_ = min; + step_ = -1; + } else { + cur_ = min - 1; + end_ = max; + step_ = 1; + } + + // Skip the first "options_.offset" keys in range. + for (uint64_t count = 0; (count < options_.offset) && (cur_ != end_); ) { + cur_ += step_; + if (map_->get(cur_)) { + ++count; + } + } + return true; +} + +template <typename T> +KeyFilterCursor<T>::KeyFilterCursor() + : MapCursor<T>(), map_(), cur_(), end_(), step_(), count_(0), options_() {} + +template <typename T> +KeyFilterCursor<T>::~KeyFilterCursor() {} + +template <typename T> +bool KeyFilterCursor<T>::next() { + if (count_ >= options_.limit) { + return false; + } + while (cur_ != end_) { + cur_ += step_; + if (map_->get(cur_, &this->key_)) { + if (filter(this->key_)) { + this->key_id_ = cur_; + ++count_; + return true; + } + } + } + return false; +} + +template <typename T> +bool KeyFilterCursor<T>::remove() { + return map_->unset(this->key_id_); +} + +template <typename T> +bool KeyFilterCursor<T>::init(Map<T> *map, const MapCursorOptions &options) { + map_ = map; + options_ = options; + options_.flags = MAP_CURSOR_ORDER_BY_ID; + if (options.flags & MAP_CURSOR_REVERSE_ORDER) { + options_.flags |= MAP_CURSOR_REVERSE_ORDER; + } + + if (options_.flags & MAP_CURSOR_REVERSE_ORDER) { + cur_ = map_->max_key_id() + 1; + end_ = 0; + step_ = -1; + } else { + cur_ = -1; + end_ = map_->max_key_id(); + step_ = 1; + } + + // Skip the first "options_.offset" keys in range. + for (uint64_t count = 0; (count < options_.offset) && (cur_ != end_); ) { + cur_ += step_; + if (map_->get(cur_, &this->key_)) { + if (filter(this->key_)) { + ++count; + } + } + } + return true; +} + +template <typename T> +KeyRangeCursor<T>::KeyRangeCursor() : KeyFilterCursor<T>(), query_() {} + +template <typename T> +KeyRangeCursor<T>::~KeyRangeCursor() {} + +template <typename T> +KeyRangeCursor<T> *KeyRangeCursor<T>::create( + Map<T> *map, + const MapCursorKeyRange<T> &query, + const MapCursorOptions &options) { + std::unique_ptr<KeyRangeCursor<T>> cursor( + new (std::nothrow) KeyRangeCursor<T>); + if (!cursor) { + GRNXX_ERROR() << "new grnxx::map::KeyRangeCursor<T> failed"; + return nullptr; + } + query_ = query; + if (!cursor->init(map, options)) { + return nullptr; + } + return cursor.release(); +} + +template <typename T> +bool KeyRangeCursor<T>::filter(KeyArg key) const { + if (query_.flags & MAP_CURSOR_KEY_LESS) { + if (key >= query_.min) { + return false; + } + } else if (query_.flags & MAP_CURSOR_KEY_LESS_EQUAL) { + if (key > query_.min) { + return false; + } + } + if (query_.flags & MAP_CURSOR_KEY_GREATER) { + if (key <= query_.min) { + return false; + } + } else if (query_.flags & MAP_CURSOR_KEY_GREATER_EQUAL) { + if (key < query_.min) { + return false; + } + } + return true; +} + +template class KeyIDRangeCursor<int8_t>; +template class KeyIDRangeCursor<int16_t>; +template class KeyIDRangeCursor<int32_t>; +template class KeyIDRangeCursor<int64_t>; +template class KeyIDRangeCursor<uint8_t>; +template class KeyIDRangeCursor<uint16_t>; +template class KeyIDRangeCursor<uint32_t>; +template class KeyIDRangeCursor<uint64_t>; +template class KeyIDRangeCursor<double>; +template class KeyIDRangeCursor<GeoPoint>; +// TODO: To be enabled. +//template class KeyIDRangeCursor<Bytes>; + +template class KeyRangeCursor<int8_t>; +template class KeyRangeCursor<int16_t>; +template class KeyRangeCursor<int32_t>; +template class KeyRangeCursor<int64_t>; +template class KeyRangeCursor<uint8_t>; +template class KeyRangeCursor<uint16_t>; +template class KeyRangeCursor<uint32_t>; +template class KeyRangeCursor<uint64_t>; +template class KeyRangeCursor<double>; +// GeoPoint does not support comparison operators (<, <=, >, >=). +//template class KeyRangeCursor<GeoPoint>; +// TODO: To be enabled. +//template class KeyRangeCursor<Bytes>; + +} // namespace map +} // namespace grnxx Added: lib/grnxx/map/cursor_impl.hpp (+108 -0) 100644 =================================================================== --- /dev/null +++ lib/grnxx/map/cursor_impl.hpp 2013-05-27 15:46:10 +0900 (c72155d) @@ -0,0 +1,108 @@ +/* + Copyright (C) 2013 Brazil, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef GRNXX_MAP_CURSOR_IMPL_HPP +#define GRNXX_MAP_CURSOR_IMPL_HPP + +#include "grnxx/features.hpp" + +#include "grnxx/map_cursor.hpp" +#include "grnxx/map_cursor_query.hpp" +#include "grnxx/traits.hpp" +#include "grnxx/types.hpp" + +namespace grnxx { +namespace map { + +template <typename T> +class KeyIDRangeCursor : public MapCursor<T> { + public: + using Key = typename Traits<T>::Type; + using KeyArg = typename Traits<T>::ArgumentType; + + KeyIDRangeCursor(); + ~KeyIDRangeCursor(); + + static KeyIDRangeCursor *create(Map<T> *map, + const MapCursorKeyIDRange<T> &query, + const MapCursorOptions &options); + + bool next(); + bool remove(); + + private: + Map<T> *map_; + int64_t cur_; + int64_t end_; + int64_t step_; + uint64_t count_; + MapCursorKeyIDRange<T> query_; + MapCursorOptions options_; + + bool init(Map<T> *map, + const MapCursorKeyIDRange<T> &query, + const MapCursorOptions &options); +}; + +template <typename T> +class KeyFilterCursor : public MapCursor<T> { + public: + using Key = typename Traits<T>::Type; + using KeyArg = typename Traits<T>::ArgumentType; + + KeyFilterCursor(); + virtual ~KeyFilterCursor(); + + bool next(); + bool remove(); + + protected: + Map<T> *map_; + int64_t cur_; + int64_t end_; + int64_t step_; + uint64_t count_; + MapCursorOptions options_; + + bool init(Map<T> *map, const MapCursorOptions &options); + + // Return true if "key" satisfies the query. + virtual bool filter(KeyArg key) const = 0; +}; + +template <typename T> +class KeyRangeCursor : public KeyFilterCursor<T> { + public: + using Key = typename Traits<T>::Type; + using KeyArg = typename Traits<T>::ArgumentType; + + KeyRangeCursor(); + ~KeyRangeCursor(); + + KeyRangeCursor *create(Map<T> *map, const MapCursorKeyRange<T> &query, + const MapCursorOptions &options); + + private: + MapCursorKeyRange<T> query_; + + bool filter(KeyArg key) const; +}; + +} // namespace map +} // namespace grnxx + +#endif // GRNXX_MAP_CURSOR_IMPL_HPP Renamed: lib/grnxx/map/scanner_impl.cpp (+10 -9) 78% =================================================================== --- lib/grnxx/map/scanner.cpp 2013-05-24 20:28:33 +0900 (2a6db90) +++ lib/grnxx/map/scanner_impl.cpp 2013-05-27 15:46:10 +0900 (c12d1f3) @@ -15,7 +15,7 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "grnxx/map/scanner.hpp" +#include "grnxx/map/scanner_impl.hpp" #include <memory> #include <new> @@ -23,6 +23,7 @@ #include "grnxx/bytes.hpp" #include "grnxx/charset.hpp" #include "grnxx/logger.hpp" +#include "grnxx/map.hpp" // TODO: To be removed in future. #include "grnxx/slice.hpp" @@ -31,17 +32,17 @@ namespace grnxx { namespace map { template <typename T> -Scanner<T>::Scanner() : map_(), query_(), charset_() {} +ScannerImpl<T>::ScannerImpl() : map_(), query_(), charset_() {} template <typename T> -Scanner<T>::~Scanner() {} +ScannerImpl<T>::~ScannerImpl() {} template <typename T> -Scanner<T> *Scanner<T>::create(Map<T> *map, KeyArg query, - const Charset *charset) { - std::unique_ptr<Scanner> scanner(new (std::nothrow) Scanner); +ScannerImpl<T> *ScannerImpl<T>::create(Map<T> *map, KeyArg query, + const Charset *charset) { + std::unique_ptr<ScannerImpl> scanner(new (std::nothrow) ScannerImpl); if (!scanner) { - GRNXX_ERROR() << "new grnxx::map::Scanner failed"; + GRNXX_ERROR() << "new grnxx::map::ScannerImpl failed"; return nullptr; } scanner->map_ = map; @@ -51,7 +52,7 @@ Scanner<T> *Scanner<T>::create(Map<T> *map, KeyArg query, } template <typename T> -bool Scanner<T>::next() { +bool ScannerImpl<T>::next() { this->offset_ += this->size_; while (this->offset_ < query_.size()) { const T rest = query_.except_prefix(this->offset_); @@ -71,7 +72,7 @@ bool Scanner<T>::next() { return false; } -template class Scanner<Bytes>; +template class ScannerImpl<Bytes>; } // namespace map } // namespace grnxx Copied: lib/grnxx/map/scanner_impl.hpp (+10 -8) 75% =================================================================== --- lib/grnxx/map/scanner.hpp 2013-05-24 20:28:33 +0900 (a62b08b) +++ lib/grnxx/map/scanner_impl.hpp 2013-05-27 15:46:10 +0900 (3d23ce2) @@ -15,29 +15,31 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef GRNXX_MAP_SCANNER_HPP -#define GRNXX_MAP_SCANNER_HPP +#ifndef GRNXX_MAP_SCANNER_IMPL_HPP +#define GRNXX_MAP_SCANNER_IMPL_HPP #include "grnxx/features.hpp" -#include "grnxx/map.hpp" +#include "grnxx/map_scanner.hpp" namespace grnxx { class Charset; +template <typename T> class Map; namespace map { template <typename T> -class Scanner : public MapScanner<T> { +class ScannerImpl : public MapScanner<T> { public: using Key = typename MapScanner<T>::Key; using KeyArg = typename MapScanner<T>::KeyArg; - Scanner(); - ~Scanner(); + ScannerImpl(); + ~ScannerImpl(); - static Scanner *create(Map<T> *map, KeyArg query, const Charset *charset); + static ScannerImpl *create(Map<T> *map, KeyArg query, + const Charset *charset); bool next(); @@ -50,4 +52,4 @@ class Scanner : public MapScanner<T> { } // namespace map } // namespace grnxx -#endif // GRNXX_MAP_SCANNER_HPP +#endif // GRNXX_MAP_SCANNER_IMPL_HPP Added: lib/grnxx/map_cursor.cpp (+78 -0) 100644 =================================================================== --- /dev/null +++ lib/grnxx/map_cursor.cpp 2013-05-27 15:46:10 +0900 (4e06734) @@ -0,0 +1,78 @@ +/* + Copyright (C) 2013 Brazil, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "grnxx/map_cursor.hpp" + +#include "grnxx/bytes.hpp" +#include "grnxx/geo_point.hpp" +#include "grnxx/logger.hpp" +#include "grnxx/map.hpp" +#include "grnxx/string_builder.hpp" + +namespace grnxx { + +#define GRNXX_FLAGS_WRITE(flag) do { \ + if (flags & flag) { \ + if (!is_first) { \ + builder << " | "; \ + } \ + builder << #flag; \ + is_first = false; \ + } \ +} while (false) + +StringBuilder &operator<<(StringBuilder &builder, MapCursorFlags flags) { + bool is_first = true; + GRNXX_FLAGS_WRITE(MAP_CURSOR_ORDER_BY_ID); + GRNXX_FLAGS_WRITE(MAP_CURSOR_ORDER_BY_KEY); + GRNXX_FLAGS_WRITE(MAP_CURSOR_REVERSE_ORDER); + if (is_first) { + builder << "MAP_CURSOR_DEFAULT"; + } + return builder; +} + +MapCursorOptions::MapCursorOptions() + : flags(MAP_CURSOR_DEFAULT), + offset(0), + limit(std::numeric_limits<uint64_t>::max()) {} + +template <typename T> +MapCursor<T>::MapCursor() : key_id_(MAP_INVALID_KEY_ID), key_() {} + +template <typename T> +MapCursor<T>::~MapCursor() {} + +template <typename T> +bool MapCursor<T>::remove() { + GRNXX_ERROR() << "invalid operation"; + return false; +} + +template class MapCursor<int8_t>; +template class MapCursor<uint8_t>; +template class MapCursor<int16_t>; +template class MapCursor<uint16_t>; +template class MapCursor<int32_t>; +template class MapCursor<uint32_t>; +template class MapCursor<int64_t>; +template class MapCursor<uint64_t>; +template class MapCursor<double>; +template class MapCursor<GeoPoint>; +template class MapCursor<Bytes>; + +} // namespace grnxx Added: lib/grnxx/map_cursor.hpp (+91 -0) 100644 =================================================================== --- /dev/null +++ lib/grnxx/map_cursor.hpp 2013-05-27 15:46:10 +0900 (9297eba) @@ -0,0 +1,91 @@ +/* + Copyright (C) 2013 Brazil, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef GRNXX_MAP_CURSOR_HPP +#define GRNXX_MAP_CURSOR_HPP + +#include "grnxx/features.hpp" + +#include "grnxx/flags_impl.hpp" +#include "grnxx/traits.hpp" +#include "grnxx/types.hpp" + +namespace grnxx { + +class StringBuilder; + +template <typename T> class Map; + +// TODO: How to implement NEAR cursor. +struct MapCursorFlagsIdentifier; +using MapCursorFlags = FlagsImpl<MapCursorFlagsIdentifier>; + +// Use the default settings. +constexpr MapCursorFlags MAP_CURSOR_DEFAULT = + MapCursorFlags::define(0x00); +// Sort keys by ID. +constexpr MapCursorFlags MAP_CURSOR_ORDER_BY_ID = + MapCursorFlags::define(0x01); +// Sort keys by key. +constexpr MapCursorFlags MAP_CURSOR_ORDER_BY_KEY = + MapCursorFlags::define(0x02); +// Access keys in reverse order. +constexpr MapCursorFlags MAP_CURSOR_REVERSE_ORDER = + MapCursorFlags::define(0x10); + +StringBuilder &operator<<(StringBuilder &builder, MapCursorFlags flags); + +struct MapCursorOptions { + MapCursorFlags flags; + uint64_t offset; + uint64_t limit; + + // Initialize the members. + MapCursorOptions(); +}; + +template <typename T> +class MapCursor { + public: + using Key = typename Traits<T>::Type; + using KeyArg = typename Traits<T>::ArgumentType; + + MapCursor(); + virtual ~MapCursor(); + + // Move the cursor to the next key and return true on success. + virtual bool next() = 0; + // Remove the current key and return true on success. + virtual bool remove(); + + // Return the ID of the current key. + int64_t key_id() const { + return key_id_; + } + // Return the current key. + const Key &key() const { + return key_; + } + + protected: + int64_t key_id_; + Key key_; +}; + +} // namespace grnxx + +#endif // GRNXX_MAP_CURSOR_HPP Added: lib/grnxx/map_cursor_query.hpp (+326 -0) 100644 =================================================================== --- /dev/null +++ lib/grnxx/map_cursor_query.hpp 2013-05-27 15:46:10 +0900 (7e86ef8) @@ -0,0 +1,326 @@ +/* + Copyright (C) 2013 Brazil, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef GRNXX_MAP_CURSOR_QUERY_HPP +#define GRNXX_MAP_CURSOR_QUERY_HPP + +#include "grnxx/features.hpp" + +#include "grnxx/flags_impl.hpp" +#include "grnxx/types.hpp" + +namespace grnxx { + +// MapCursorAll. + +template <typename T> struct MapCursorAll {}; + +// MapCursorKeyID + +template <typename T> struct MapCursorKeyID {}; + +struct MapCursorKeyIDFlagsIdentifier; +using MapCursorKeyIDFlags = FlagsImpl<MapCursorKeyIDFlagsIdentifier>; + +constexpr MapCursorKeyIDFlags MAP_CURSOR_KEY_ID_LESS = + MapCursorKeyIDFlags::define(0x01); +constexpr MapCursorKeyIDFlags MAP_CURSOR_KEY_ID_LESS_EQUAL = + MapCursorKeyIDFlags::define(0x02); +constexpr MapCursorKeyIDFlags MAP_CURSOR_KEY_ID_GREATER = + MapCursorKeyIDFlags::define(0x04); +constexpr MapCursorKeyIDFlags MAP_CURSOR_KEY_ID_GREATER_EQUAL = + MapCursorKeyIDFlags::define(0x08); + +template <typename T> +struct MapCursorKeyIDRange { + MapCursorKeyIDFlags flags; + int64_t min; + int64_t max; +}; + +template <typename T> +struct MapCursorKeyIDLess { + int64_t max; + constexpr MapCursorKeyIDFlags flags() { + return MAP_CURSOR_KEY_ID_LESS; + } + operator MapCursorKeyIDRange<T>() const { + return MapCursorKeyIDRange<T>{ flags(), 0, max }; + } +}; + +template <typename T> +struct MapCursorKeyIDLessEqual { + int64_t max; + constexpr MapCursorKeyIDFlags flags() { + return MAP_CURSOR_KEY_ID_LESS_EQUAL; + } + operator MapCursorKeyIDRange<T>() const { + return MapCursorKeyIDRange<T>{ flags(), 0, max }; + }; +}; + +template <typename T> +struct MapCursorKeyIDGreater { + int64_t min; + constexpr MapCursorKeyIDFlags flags() { + return MAP_CURSOR_KEY_ID_GREATER; + } + operator MapCursorKeyIDRange<T>() const { + return MapCursorKeyIDRange<T>{ flags(), min, 0 }; + } +}; + +template <typename T> +struct MapCursorKeyIDGreaterEqual { + int64_t min; + constexpr MapCursorKeyIDFlags flags() { + return MAP_CURSOR_KEY_ID_GREATER_EQUAL; + } + operator MapCursorKeyIDRange<T>() const { + return MapCursorKeyIDRange<T>{ flags(), min }; + } +}; + +template <typename T> +MapCursorKeyIDLess<T> operator<(MapCursorKeyID<T>, int64_t max) { + return MapCursorKeyIDLess<T>{ max }; +} +template <typename T> +MapCursorKeyIDLessEqual<T> operator<=(MapCursorKeyID<T>, int64_t max) { + return MapCursorKeyIDLessEqual<T>{ max }; +} +template <typename T> +MapCursorKeyIDGreater<T> operator>(MapCursorKeyID<T>, int64_t min) { + return MapCursorKeyIDGreater<T>{ min }; +} +template <typename T> +MapCursorKeyIDGreaterEqual<T> operator>=(MapCursorKeyID<T>, int64_t min) { + return MapCursorKeyIDGreaterEqual<T>{ min }; +} + +template <typename T> +MapCursorKeyIDGreater<T> operator<(int64_t min, MapCursorKeyID<T>) { + return MapCursorKeyIDGreater<T>{ min }; +} +template <typename T> +MapCursorKeyIDGreaterEqual<T> operator<=(int64_t min, MapCursorKeyID<T>) { + return MapCursorKeyIDGreaterEqual<T>{ min }; +} +template <typename T> +MapCursorKeyIDLess<T> operator>(int64_t max, MapCursorKeyID<T>) { + return MapCursorKeyIDLess<T>{ max }; +} +template <typename T> +MapCursorKeyIDLessEqual<T> operator>=(int64_t max, MapCursorKeyID<T>) { + return MapCursorKeyIDLessEqual<T>{ max }; +} + +template <typename T> +MapCursorKeyIDRange<T> operator&&(MapCursorKeyIDLess<T> less, + MapCursorKeyIDGreater<T> greater) { + return MapCursorKeyIDRange<T>{ less.flags() | greater.flags(), + greater.min, less.max }; +} +template <typename T> +MapCursorKeyIDRange<T> operator&&(MapCursorKeyIDLess<T> less, + MapCursorKeyIDGreaterEqual<T> greater) { + return MapCursorKeyIDRange<T>{ less.flags() | greater.flags(), + greater.min, less.max }; +} +template <typename T> +MapCursorKeyIDRange<T> operator&&(MapCursorKeyIDLessEqual<T> less, + MapCursorKeyIDGreater<T> greater) { + return MapCursorKeyIDRange<T>{ less.flags() | greater.flags(), + greater.min, less.max }; +} +template <typename T> +MapCursorKeyIDRange<T> operator&&(MapCursorKeyIDLessEqual<T> less, + MapCursorKeyIDGreaterEqual<T> greater) { + return MapCursorKeyIDRange<T>{ less.flags() | greater.flags(), + greater.min, less.max }; +} +template <typename T> +MapCursorKeyIDRange<T> operator&&(MapCursorKeyIDGreater<T> greater, + MapCursorKeyIDLess<T> less) { + return less && greater; +} +template <typename T> +MapCursorKeyIDRange<T> operator&&(MapCursorKeyIDGreater<T> greater, + MapCursorKeyIDLessEqual<T> less) { + return less && greater; +} +template <typename T> +MapCursorKeyIDRange<T> operator&&(MapCursorKeyIDGreaterEqual<T> greater, + MapCursorKeyIDLess<T> less) { + return less && greater; +} +template <typename T> +MapCursorKeyIDRange<T> operator&&(MapCursorKeyIDGreaterEqual<T> greater, + MapCursorKeyIDLessEqual<T> less) { + return less && greater; +} + +// MapCursorKey + +template <typename T> struct MapCursorKey {}; + +struct MapCursorKeyFlagsIdentifier; +using MapCursorKeyFlags = FlagsImpl<MapCursorKeyFlagsIdentifier>; + +constexpr MapCursorKeyFlags MAP_CURSOR_KEY_LESS = + MapCursorKeyFlags::define(0x01); +constexpr MapCursorKeyFlags MAP_CURSOR_KEY_LESS_EQUAL = + MapCursorKeyFlags::define(0x02); +constexpr MapCursorKeyFlags MAP_CURSOR_KEY_GREATER = + MapCursorKeyFlags::define(0x04); +constexpr MapCursorKeyFlags MAP_CURSOR_KEY_GREATER_EQUAL = + MapCursorKeyFlags::define(0x08); + +template <typename T> +struct MapCursorKeyRange { + MapCursorKeyFlags flags; + T min; + T max; +}; + +template <typename T> +struct MapCursorKeyLess { + T max; + constexpr MapCursorKeyFlags flags() { + return MAP_CURSOR_KEY_LESS; + } + operator MapCursorKeyRange<T>() const { + return MapCursorKeyRange<T>{ flags(), T(), max }; + } +}; + +template <typename T> +struct MapCursorKeyLessEqual { + T max; + constexpr MapCursorKeyFlags flags() { + return MAP_CURSOR_KEY_LESS_EQUAL; + } + operator MapCursorKeyRange<T>() const { + return MapCursorKeyRange<T>{ flags(), T(), max }; + } +}; + +template <typename T> +struct MapCursorKeyGreater { + T min; + constexpr MapCursorKeyFlags flags() { + return MAP_CURSOR_KEY_GREATER; + } + operator MapCursorKeyRange<T>() const { + return MapCursorKeyRange<T>{ flags(), min, T() }; + } +}; + +template <typename T> +struct MapCursorKeyGreaterEqual { + T min; + constexpr MapCursorKeyFlags flags() { + return MAP_CURSOR_KEY_GREATER_EQUAL; + } + operator MapCursorKeyRange<T>() const { + return MapCursorKeyRange<T>{ flags(), min, T() }; + } +}; + +template <typename T> +MapCursorKeyLess<T> operator<(MapCursorKey<T>, T max) { + return MapCursorKeyLess<T>{ max }; +} +template <typename T> +MapCursorKeyLessEqual<T> operator<=(MapCursorKey<T>, T max) { + return MapCursorKeyLessEqual<T>{ max }; +} +template <typename T> +MapCursorKeyGreater<T> operator>(MapCursorKey<T>, T min) { + return MapCursorKeyGreater<T>{ min }; +} +template <typename T> +MapCursorKeyGreaterEqual<T> operator>=(MapCursorKey<T>, T min) { + return MapCursorKeyGreaterEqual<T>{ min }; +} + +template <typename T> +MapCursorKeyGreater<T> operator<(T min, MapCursorKey<T>) { + return MapCursorKeyGreater<T>{ min }; +} +template <typename T> +MapCursorKeyGreaterEqual<T> operator<=(T min, MapCursorKey<T>) { + return MapCursorKeyGreaterEqual<T>{ min }; +} +template <typename T> +MapCursorKeyLess<T> operator>(T max, MapCursorKey<T>) { + return MapCursorKeyLess<T>{ max }; +} +template <typename T> +MapCursorKeyLessEqual<T> operator>=(T max, MapCursorKey<T>) { + return MapCursorKeyLessEqual<T>{ max }; +} + +template <typename T> +MapCursorKeyRange<T> operator&&(MapCursorKeyLess<T> less, + MapCursorKeyGreater<T> greater) { + return MapCursorKeyRange<T>{ less.flags() | greater.flags(), + greater.min, less.max }; +} +template <typename T> +MapCursorKeyRange<T> operator&&(MapCursorKeyLess<T> less, + MapCursorKeyGreaterEqual<T> greater) { + return MapCursorKeyRange<T>{ less.flags() | greater.flags(), + greater.min, less.max }; +} +template <typename T> +MapCursorKeyRange<T> operator&&(MapCursorKeyLessEqual<T> less, + MapCursorKeyGreater<T> greater) { + return MapCursorKeyRange<T>{ less.flags() | greater.flags(), + greater.min, less.max }; +} +template <typename T> +MapCursorKeyRange<T> operator&&(MapCursorKeyLessEqual<T> less, + MapCursorKeyGreaterEqual<T> greater) { + return MapCursorKeyRange<T>{ less.flags() | greater.flags(), + greater.min, less.max }; +} +template <typename T> +MapCursorKeyRange<T> operator&&(MapCursorKeyGreater<T> greater, + MapCursorKeyLess<T> less) { + return less && greater; +} +template <typename T> +MapCursorKeyRange<T> operator&&(MapCursorKeyGreater<T> greater, + MapCursorKeyLessEqual<T> less) { + return less && greater; +} +template <typename T> +MapCursorKeyRange<T> operator&&(MapCursorKeyGreaterEqual<T> greater, + MapCursorKeyLess<T> less) { + return less && greater; +} +template <typename T> +MapCursorKeyRange<T> operator&&(MapCursorKeyGreaterEqual<T> greater, + MapCursorKeyLessEqual<T> less) { + return less && greater; +} + +} // namespace grnxx + +#endif // GRNXX_MAP_CURSOR_QUERY_HPP Renamed: lib/grnxx/map_scanner.cpp (+15 -7) 72% =================================================================== --- lib/grnxx/map/cursor.hpp 2013-05-24 20:28:33 +0900 (5ac9df5) +++ lib/grnxx/map_scanner.cpp 2013-05-27 15:46:10 +0900 (8814983) @@ -15,15 +15,23 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef GRNXX_MAP_CURSOR_HPP -#define GRNXX_MAP_CURSOR_HPP +#include "grnxx/map_scanner.hpp" -#include "grnxx/features.hpp" +#include "grnxx/bytes.hpp" +#include "grnxx/map.hpp" namespace grnxx { -namespace map { -} // namespace map -} // namespace grnxx +template <typename T> +MapScanner<T>::MapScanner() + : offset_(0), + size_(0), + key_id_(MAP_INVALID_KEY_ID), + key_() {} + +template <typename T> +MapScanner<T>::~MapScanner() {} -#endif // GRNXX_MAP_CURSOR_HPP +template class MapScanner<Bytes>; + +} // namespace grnxx Renamed: lib/grnxx/map_scanner.hpp (+31 -14) 56% =================================================================== --- lib/grnxx/map/scanner.hpp 2013-05-24 20:28:33 +0900 (a62b08b) +++ lib/grnxx/map_scanner.hpp 2013-05-27 15:46:10 +0900 (b28af1c) @@ -20,34 +20,51 @@ #include "grnxx/features.hpp" -#include "grnxx/map.hpp" +#include "grnxx/flags_impl.hpp" +#include "grnxx/traits.hpp" +#include "grnxx/types.hpp" namespace grnxx { class Charset; - -namespace map { +class Storage; template <typename T> -class Scanner : public MapScanner<T> { +class MapScanner { public: - using Key = typename MapScanner<T>::Key; - using KeyArg = typename MapScanner<T>::KeyArg; + using Key = typename Traits<T>::Type; + using KeyArg = typename Traits<T>::ArgumentType; - Scanner(); - ~Scanner(); + MapScanner(); + virtual ~MapScanner(); - static Scanner *create(Map<T> *map, KeyArg query, const Charset *charset); + // Find the next key from the rest of the query and return true on success. + virtual bool next() = 0; - bool next(); + // Return the start position of the found key. + uint64_t offset() const { + return offset_; + } + // Return the size of the found key. + uint64_t size() const { + return size_; + } + // Return the ID of the found key. + int64_t key_id() const { + return key_id_; + } + // Return the found key. + const Key &key() const { + return key_; + } protected: - Map<T> *map_; - Key query_; - const Charset *charset_; + uint64_t offset_; + uint64_t size_; + int64_t key_id_; + Key key_; }; -} // namespace map } // namespace grnxx #endif // GRNXX_MAP_SCANNER_HPP -------------- next part -------------- HTML����������������������������...Download