├── .gitignore ├── AUTHORS ├── CMakeLists.txt ├── LICENSE ├── README.md ├── bench ├── CMakeLists.txt ├── db_bench_cascadb.cpp ├── histogram.cpp ├── histogram.h ├── random.h ├── testutil.cpp └── testutil.h ├── cmake ├── FindLibaio.cmake └── FindSnappy.cmake ├── include └── cascadb │ ├── comparator.h │ ├── db.h │ ├── directory.h │ ├── file.h │ ├── options.h │ └── slice.h ├── src ├── CMakeLists.txt ├── cache │ ├── cache.cpp │ └── cache.h ├── db │ ├── db_impl.cpp │ └── db_impl.h ├── serialize │ ├── block.cpp │ ├── block.h │ ├── layout.cpp │ ├── layout.h │ └── super_block.h ├── store │ ├── file.cpp │ ├── fs_directory.cpp │ ├── fs_directory.h │ ├── ram_directory.cpp │ └── ram_directory.h ├── sys │ ├── linux │ │ ├── linux_fs_directory.cpp │ │ └── linux_fs_directory.h │ ├── posix │ │ ├── posix_fs_directory.cpp │ │ ├── posix_fs_directory.h │ │ ├── posix_sys.cpp │ │ └── posix_sys.h │ └── sys.h ├── tree │ ├── fast_vector.h │ ├── keycomp.h │ ├── msg.cpp │ ├── msg.h │ ├── node.cpp │ ├── node.h │ ├── record.cpp │ ├── record.h │ ├── tree.cpp │ └── tree.h └── util │ ├── any.h │ ├── bits.h │ ├── bloom.cpp │ ├── bloom.h │ ├── callback.h │ ├── compressor.cpp │ ├── compressor.h │ ├── crc16.cpp │ ├── crc16.h │ ├── logger.cpp │ └── logger.h ├── test ├── CMakeLists.txt ├── directory_test.cpp ├── directory_test.h ├── helper.h ├── t_block.cpp ├── t_bloom.cpp ├── t_cache.cpp ├── t_callback.cpp ├── t_comparator.cpp ├── t_compressor.cpp ├── t_crc16.cpp ├── t_db.cpp ├── t_fast_vector.cpp ├── t_keycomp.cpp ├── t_layout.cpp ├── t_linux_fs_directory.cpp ├── t_msg.cpp ├── t_node.cpp ├── t_posix_fs_directory.cpp ├── t_posix_sys.cpp ├── t_ram_directory.cpp └── t_slice.cpp └── thirdparty ├── CMakeLists.txt └── gtest ├── CMakeLists.txt ├── include └── gtest │ ├── gtest-death-test.h │ ├── gtest-message.h │ ├── gtest-param-test.h │ ├── gtest-param-test.h.pump │ ├── gtest-printers.h │ ├── gtest-spi.h │ ├── gtest-test-part.h │ ├── gtest-typed-test.h │ ├── gtest.h │ ├── gtest_pred_impl.h │ ├── gtest_prod.h │ └── internal │ ├── gtest-death-test-internal.h │ ├── gtest-filepath.h │ ├── gtest-internal.h │ ├── gtest-linked_ptr.h │ ├── gtest-param-util-generated.h │ ├── gtest-param-util-generated.h.pump │ ├── gtest-param-util.h │ ├── gtest-port.h │ ├── gtest-string.h │ ├── gtest-tuple.h │ ├── gtest-tuple.h.pump │ ├── gtest-type-util.h │ └── gtest-type-util.h.pump └── src ├── gtest-all.cc ├── gtest-death-test.cc ├── gtest-filepath.cc ├── gtest-internal-inl.h ├── gtest-port.cc ├── gtest-printers.cc ├── gtest-test-part.cc ├── gtest-typed-test.cc ├── gtest.cc └── gtest_main.cc /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | 10 | # Compiled Static libraries 11 | *.lai 12 | *.la 13 | *.a 14 | 15 | #CMake libraries 16 | bin/* 17 | build/* 18 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # Names should be added to this file like so: 2 | # Name or Organization 3 | 4 | # Initial version author: 5 | Wei Cao 6 | 7 | # Contributors: 8 | Yanfei Zhang 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # author: Wei Cao 2 | 3 | CMAKE_MINIMUM_REQUIRED(VERSION 2.6) 4 | 5 | PROJECT(cascadb) 6 | 7 | set ( LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) 8 | set ( EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 9 | 10 | # set rpath 11 | 12 | # use, i.e. don't skip the full RPATH for the build tree 13 | SET(CMAKE_SKIP_BUILD_RPATH FALSE) 14 | 15 | # when building, don't use the install RPATH already 16 | # (but later on when installing) 17 | SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) 18 | 19 | # add the automatically determined parts of the RPATH 20 | # which point to directories outside the build tree to the install RPATH 21 | SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 22 | 23 | # the RPATH to be used when installing, but only if it's not a system directory 24 | LIST(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir) 25 | IF("${isSystemDir}" STREQUAL "-1") 26 | SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") 27 | ENDIF("${isSystemDir}" STREQUAL "-1") 28 | 29 | add_definitions("-g -O2 -Wall") 30 | 31 | include_directories( 32 | ${PROJECT_SOURCE_DIR}/include 33 | ${PROJECT_SOURCE_DIR}/src 34 | ) 35 | 36 | link_directories( 37 | ${PROJECT_SOURCE_DIR}/lib 38 | ) 39 | 40 | # check dependencies 41 | 42 | # Check Snappy 43 | include(${CMAKE_SOURCE_DIR}/cmake/FindSnappy.cmake) 44 | if (SNAPPY_FOUND) 45 | message(STATUS "Find snappy include:${SNAPPY_INCLUDE_DIR} libs:${SNAPPY_LIBRARIES}") 46 | add_definitions("-DHAS_SNAPPY") 47 | include_directories(${SNAPPY_INCLUDE_DIR}) 48 | link_libraries(${SNAPPY_LIBRARIES}) 49 | else (SNAPPY_FOUND) 50 | message(WARNING "Cannot find snappy, compression is disabled in cascadb") 51 | endif (SNAPPY_FOUND) 52 | 53 | # Check Libaio 54 | include(${CMAKE_SOURCE_DIR}/cmake/FindLibaio.cmake) 55 | if (LIBAIO_FOUND) 56 | message(STATUS "Find libaio include:${LIBAIO_INCLUDE_DIR} libs:${LIBAIO_LIBRARIES}") 57 | add_definitions("-DHAS_LIBAIO") 58 | include_directories(${LIBAIO_INCLUDE_DIR}) 59 | link_libraries(${LIBAIO_LIBRARIES}) 60 | else (LIBAIO_FOUND) 61 | message(WARNING "Cannot find libaio, posix aio is used instead") 62 | endif (LIBAIO_FOUND) 63 | 64 | # environment 65 | 66 | if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") 67 | add_definitions("-DOS_LINUX") 68 | endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") 69 | 70 | set(CASCADB_LIBS 71 | pthread m rt dl z 72 | ) 73 | 74 | # Source files 75 | 76 | MESSAGE(STATUS "Installation path is: ${CMAKE_INSTALL_PREFIX} (overwrite with -DCMAKE_INSTALL_PREFIX=/your/path)") 77 | 78 | install(DIRECTORY include/cascadb DESTINATION include) 79 | 80 | add_subdirectory(src) 81 | add_subdirectory(bench) 82 | add_subdirectory(thirdparty) 83 | 84 | enable_testing() 85 | add_subdirectory(test) 86 | 87 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of CascaDB nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CascaDB 2 | ======= 3 | 4 | Yet another write-optimized storage engine, using buffered B-tree algorithm inspired by TokuDB. 5 | 6 | ## Features 7 | * Provides a key-value access API similar to LevelDB. 8 | * Support Snappy compression. 9 | * Support Direct IO and Linux AIO. 10 | 11 | ## Dependencies 12 | CascaDB can have better performance if libaio and google snappy library 're installed. Otherwise 13 | Posix AIO (simulate AIO with multiple threads which is not true asynchronous) is used instead 14 | and data blocks're not compressed. 15 | 16 | * libaio 17 | 18 | On Ubuntu Linux 19 | 20 | sudo apt-get install libaio-dev 21 | On RHEL 22 | 23 | sudo yum install libaio-devel 24 | 25 | * [snappy](http://code.google.com/p/snappy/) 26 | 27 | ## Compile 28 | CascaDB utilizes CMake to build, so first of all you should install CMake. 29 | 30 | It's recommended to make out of source build, that is, object files are generated into a separated directory with the source directory. 31 | 32 | mkdir build 33 | cd build 34 | cmake .. 35 | make && make install 36 | 37 | ## Performance 38 | 39 | Running bin/db_bench to get detailed performance report of how CascaDB running on your machine. 40 | 41 | e.g. `bin/db_bench --num=1000000`, This will create and test on a database of a million records, each record has a 16 byte key, and a 100 byte value. 42 | 43 | ### Write Performance 44 | fillseq : 1.020 micros/op; 45 | fillrandom : 2.559 micros/op; 46 | 47 | ### Read Performance 48 | Read benchmark results after sequential insertions 49 | 50 | readrandom : 1.125 micros/op; 51 | readseq : 1.282 micros/op; 52 | 53 | Read benchmark results after random insertions 54 | 55 | readrandom : 3.227 micros/op; 56 | readseq : 1.295 micros/op; 57 | 58 | ## Have a try! 59 | 60 | 1. Include the header file. 61 | 62 | #include 63 | 64 | 2. Open the database. 65 | 66 | Options opts; 67 | opts.dir = create_fs_directory("/tmp/db_bench"); 68 | opts.comparator = new LexicalComparator(); 69 | opts.compress = kSnappyCompress; 70 | 71 | const char *dbname = "example"; 72 | DB *db = DB::open(dbname, opts); 73 | if (!db) { 74 | fprintf(stderr, "open error %s\n", dbname); 75 | exit(1); 76 | } 77 | 78 | 3. Insert record. 79 | 80 | if (!db->put("This is the key", "This is the value") { 81 | fprintf(stderr, "put error\n"); 82 | } 83 | 84 | 4. Read record. 85 | 86 | Slice value; 87 | if (!db->get("This is the key", value) { 88 | fprintf(stderr, "get error\n"); 89 | } 90 | fprintf(stdout, "the value is %s", value.to_string().c_str()); 91 | 92 | 5. Delete record. 93 | 94 | if (!db->del("This is the key")) { 95 | fprintf(stderr, "del error\n"); 96 | } 97 | 98 | 6. Destory database. 99 | 100 | delete db; 101 | delete opts.comparator; 102 | delete opts.dir; 103 | 104 | ## TODO 105 | * Add forward and backward iteration over data. 106 | * Support more compression methods. 107 | * Implement write ahead log to ensure write operation is atomic. -------------------------------------------------------------------------------- /bench/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable( 2 | db_bench 3 | db_bench_cascadb.cpp 4 | testutil.cpp 5 | histogram.cpp) 6 | target_link_libraries( 7 | db_bench 8 | cascadbShared 9 | ${CASCADB_LIBS} 10 | ) 11 | 12 | -------------------------------------------------------------------------------- /bench/histogram.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | // This file is copied from LevelDB and modifed a little 6 | // to add LevelDB style benchmark 7 | 8 | #include 9 | #include 10 | #include "histogram.h" 11 | 12 | namespace cascadb { 13 | 14 | const double Histogram::kBucketLimit[kNumBuckets] = { 15 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45, 16 | 50, 60, 70, 80, 90, 100, 120, 140, 160, 180, 200, 250, 300, 350, 400, 450, 17 | 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000, 2500, 3000, 18 | 3500, 4000, 4500, 5000, 6000, 7000, 8000, 9000, 10000, 12000, 14000, 19 | 16000, 18000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 60000, 20 | 70000, 80000, 90000, 100000, 120000, 140000, 160000, 180000, 200000, 21 | 250000, 300000, 350000, 400000, 450000, 500000, 600000, 700000, 800000, 22 | 900000, 1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2500000, 23 | 3000000, 3500000, 4000000, 4500000, 5000000, 6000000, 7000000, 8000000, 24 | 9000000, 10000000, 12000000, 14000000, 16000000, 18000000, 20000000, 25 | 25000000, 30000000, 35000000, 40000000, 45000000, 50000000, 60000000, 26 | 70000000, 80000000, 90000000, 100000000, 120000000, 140000000, 160000000, 27 | 180000000, 200000000, 250000000, 300000000, 350000000, 400000000, 28 | 450000000, 500000000, 600000000, 700000000, 800000000, 900000000, 29 | 1000000000, 1200000000, 1400000000, 1600000000, 1800000000, 2000000000, 30 | 2500000000.0, 3000000000.0, 3500000000.0, 4000000000.0, 4500000000.0, 31 | 5000000000.0, 6000000000.0, 7000000000.0, 8000000000.0, 9000000000.0, 32 | 1e200, 33 | }; 34 | 35 | void Histogram::Clear() { 36 | min_ = kBucketLimit[kNumBuckets-1]; 37 | max_ = 0; 38 | num_ = 0; 39 | sum_ = 0; 40 | sum_squares_ = 0; 41 | for (int i = 0; i < kNumBuckets; i++) { 42 | buckets_[i] = 0; 43 | } 44 | } 45 | 46 | void Histogram::Add(double value) { 47 | // Linear search is fast enough for our usage in db_bench 48 | int b = 0; 49 | while (b < kNumBuckets - 1 && kBucketLimit[b] <= value) { 50 | b++; 51 | } 52 | buckets_[b] += 1.0; 53 | if (min_ > value) min_ = value; 54 | if (max_ < value) max_ = value; 55 | num_++; 56 | sum_ += value; 57 | sum_squares_ += (value * value); 58 | } 59 | 60 | void Histogram::Merge(const Histogram& other) { 61 | if (other.min_ < min_) min_ = other.min_; 62 | if (other.max_ > max_) max_ = other.max_; 63 | num_ += other.num_; 64 | sum_ += other.sum_; 65 | sum_squares_ += other.sum_squares_; 66 | for (int b = 0; b < kNumBuckets; b++) { 67 | buckets_[b] += other.buckets_[b]; 68 | } 69 | } 70 | 71 | double Histogram::Median() const { 72 | return Percentile(50.0); 73 | } 74 | 75 | double Histogram::Percentile(double p) const { 76 | double threshold = num_ * (p / 100.0); 77 | double sum = 0; 78 | for (int b = 0; b < kNumBuckets; b++) { 79 | sum += buckets_[b]; 80 | if (sum >= threshold) { 81 | // Scale linearly within this bucket 82 | double left_point = (b == 0) ? 0 : kBucketLimit[b-1]; 83 | double right_point = kBucketLimit[b]; 84 | double left_sum = sum - buckets_[b]; 85 | double right_sum = sum; 86 | double pos = (threshold - left_sum) / (right_sum - left_sum); 87 | double r = left_point + (right_point - left_point) * pos; 88 | if (r < min_) r = min_; 89 | if (r > max_) r = max_; 90 | return r; 91 | } 92 | } 93 | return max_; 94 | } 95 | 96 | double Histogram::Average() const { 97 | if (num_ == 0.0) return 0; 98 | return sum_ / num_; 99 | } 100 | 101 | double Histogram::StandardDeviation() const { 102 | if (num_ == 0.0) return 0; 103 | double variance = (sum_squares_ * num_ - sum_ * sum_) / (num_ * num_); 104 | return sqrt(variance); 105 | } 106 | 107 | std::string Histogram::ToString() const { 108 | std::string r; 109 | char buf[200]; 110 | snprintf(buf, sizeof(buf), 111 | "Count: %.0f Average: %.4f StdDev: %.2f\n", 112 | num_, Average(), StandardDeviation()); 113 | r.append(buf); 114 | snprintf(buf, sizeof(buf), 115 | "Min: %.4f Median: %.4f Max: %.4f\n", 116 | (num_ == 0.0 ? 0.0 : min_), Median(), max_); 117 | r.append(buf); 118 | r.append("------------------------------------------------------\n"); 119 | const double mult = 100.0 / num_; 120 | double sum = 0; 121 | for (int b = 0; b < kNumBuckets; b++) { 122 | if (buckets_[b] <= 0.0) continue; 123 | sum += buckets_[b]; 124 | snprintf(buf, sizeof(buf), 125 | "[ %7.0f, %7.0f ) %7.0f %7.3f%% %7.3f%% ", 126 | ((b == 0) ? 0.0 : kBucketLimit[b-1]), // left 127 | kBucketLimit[b], // right 128 | buckets_[b], // count 129 | mult * buckets_[b], // percentage 130 | mult * sum); // cumulative percentage 131 | r.append(buf); 132 | 133 | // Add hash marks based on percentage; 20 marks for 100%. 134 | int marks = static_cast(20*(buckets_[b] / num_) + 0.5); 135 | r.append(marks, '#'); 136 | r.push_back('\n'); 137 | } 138 | return r; 139 | } 140 | 141 | } // namespace leveldb 142 | -------------------------------------------------------------------------------- /bench/histogram.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | // This file is copied from LevelDB and modifed a little 6 | // to add LevelDB style benchmark 7 | 8 | #ifndef CASCADB_BENCH_HISTOGRAM_H_ 9 | #define CASCADB_BENCH_HISTOGRAM_H_ 10 | 11 | #include 12 | 13 | namespace cascadb { 14 | 15 | class Histogram { 16 | public: 17 | Histogram() { } 18 | ~Histogram() { } 19 | 20 | void Clear(); 21 | void Add(double value); 22 | void Merge(const Histogram& other); 23 | 24 | std::string ToString() const; 25 | 26 | private: 27 | double min_; 28 | double max_; 29 | double num_; 30 | double sum_; 31 | double sum_squares_; 32 | 33 | enum { kNumBuckets = 154 }; 34 | static const double kBucketLimit[kNumBuckets]; 35 | double buckets_[kNumBuckets]; 36 | 37 | double Median() const; 38 | double Percentile(double p) const; 39 | double Average() const; 40 | double StandardDeviation() const; 41 | }; 42 | 43 | } // namespace leveldb 44 | #endif 45 | -------------------------------------------------------------------------------- /bench/random.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | // This file is copied from LevelDB and modifed a little 6 | // to add LevelDB style benchmark 7 | 8 | #ifndef CASCADB_BENCH_RANDOM_H_ 9 | #define CASCADB_BENCH_RANDOM_H_ 10 | 11 | #include 12 | 13 | namespace cascadb { 14 | 15 | // A very simple random number generator. Not especially good at 16 | // generating truly random bits, but good enough for our needs in this 17 | // package. 18 | class Random { 19 | private: 20 | uint32_t seed_; 21 | public: 22 | explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { } 23 | uint32_t Next() { 24 | static const uint32_t M = 2147483647L; // 2^31-1 25 | static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 26 | // We are computing 27 | // seed_ = (seed_ * A) % M, where M = 2^31-1 28 | // 29 | // seed_ must not be zero or M, or else all subsequent computed values 30 | // will be zero or M respectively. For all other values, seed_ will end 31 | // up cycling through every number in [1,M-1] 32 | uint64_t product = seed_ * A; 33 | 34 | // Compute (product % M) using the fact that ((x << 31) % M) == x. 35 | seed_ = static_cast((product >> 31) + (product & M)); 36 | // The first reduction may overflow by 1 bit, so we may need to 37 | // repeat. mod == M is not possible; using > allows the faster 38 | // sign-bit-based test. 39 | if (seed_ > M) { 40 | seed_ -= M; 41 | } 42 | return seed_; 43 | } 44 | // Returns a uniformly distributed value in the range [0..n-1] 45 | // REQUIRES: n > 0 46 | uint32_t Uniform(int n) { return Next() % n; } 47 | 48 | // Randomly returns true ~"1/n" of the time, and false otherwise. 49 | // REQUIRES: n > 0 50 | bool OneIn(int n) { return (Next() % n) == 0; } 51 | 52 | // Skewed: pick "base" uniformly from range [0,max_log] and then 53 | // return "base" random bits. The effect is to pick a number in the 54 | // range [0,2^max_log-1] with exponential bias towards smaller numbers. 55 | uint32_t Skewed(int max_log) { 56 | return Uniform(1 << Uniform(max_log + 1)); 57 | } 58 | }; 59 | 60 | } // namespace leveldb 61 | 62 | #endif // STORAGE_LEVELDB_UTIL_RANDOM_H_ -------------------------------------------------------------------------------- /bench/testutil.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | // This file is copied from LevelDB and modifed a little 6 | // to add LevelDB style benchmark 7 | 8 | #include "testutil.h" 9 | 10 | #include 11 | #include 12 | 13 | namespace cascadb { 14 | 15 | Slice RandomSlice(Random* rnd, size_t len, std::string* dst) { 16 | dst->resize(len); 17 | for (size_t i = 0; i < len; i++) { 18 | (*dst)[i] = static_cast(' ' + rnd->Uniform(95)); // ' ' .. '~' 19 | } 20 | return Slice(*dst); 21 | } 22 | 23 | std::string RandomKey(Random* rnd, size_t len) { 24 | // Make sure to generate a wide variety of characters so we 25 | // test the boundary conditions for short-key optimizations. 26 | static const char kTestChars[] = { 27 | '\0', '\1', 'a', 'b', 'c', 'd', 'e', '\xfd', '\xfe', '\xff' 28 | }; 29 | std::string result; 30 | for (size_t i = 0; i < len; i++) { 31 | result += kTestChars[rnd->Uniform(sizeof(kTestChars))]; 32 | } 33 | return result; 34 | } 35 | 36 | extern Slice CompressibleSlice(Random* rnd, double compressed_fraction, 37 | size_t len, std::string* dst) { 38 | size_t raw = static_cast(len * compressed_fraction); 39 | if (raw < 1) raw = 1; 40 | std::string raw_data; 41 | RandomSlice(rnd, raw, &raw_data); 42 | 43 | // Duplicate the random data until we have filled "len" bytes 44 | dst->clear(); 45 | while (dst->size() < len) { 46 | dst->append(raw_data); 47 | } 48 | dst->resize(len); 49 | return Slice(*dst); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /bench/testutil.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | // This file is copied from LevelDB and modifed a little 6 | // to add LevelDB style benchmark 7 | 8 | #ifndef CASCADB_BENCH_TESTUTIL_H_ 9 | #define CASCADB_BENCH_TESTUTIL_H_ 10 | 11 | #include 12 | #include "cascadb/slice.h" 13 | #include "random.h" 14 | 15 | namespace cascadb { 16 | 17 | // Store in *dst a random string of length "len" and return a Slice that 18 | // references the generated data. 19 | extern Slice RandomSlice(Random* rnd, size_t len, std::string* dst); 20 | 21 | // Return a random key with the specified length that may contain interesting 22 | // characters (e.g. \x00, \xff, etc.). 23 | extern std::string RandomKey(Random* rnd, size_t len); 24 | 25 | // Store in *dst a string of length "len" that will compress to 26 | // "N*compressed_fraction" bytes and return a Slice that references 27 | // the generated data. 28 | extern Slice CompressibleSlice(Random* rnd, double compressed_fraction, 29 | size_t len, std::string* dst); 30 | 31 | } 32 | 33 | #endif -------------------------------------------------------------------------------- /cmake/FindLibaio.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find Libaio 2 | # Once done, this will define 3 | # 4 | # LIBAIO_FOUND - system has Libaio installed 5 | # LIBAIO_INCLUDE_DIR - the Libaio include directories 6 | # LIBAIO_LIBRARIES - link these to use Libaio 7 | # 8 | # The user may wish to set, in the CMake GUI or otherwise, this variable: 9 | # LIBAIO_DIR - path to start searching for the module 10 | 11 | find_path(LIBAIO_INCLUDE_DIR 12 | libaio.h 13 | HINTS 14 | PATH_SUFFIXES 15 | include 16 | ) 17 | 18 | #IF(WIN32) 19 | # SET(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) 20 | #ELSE(WIN32) 21 | # SET(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) 22 | #ENDIF(WIN32) 23 | 24 | 25 | find_library(LIBAIO_LIBRARY 26 | aio 27 | HINTS 28 | PATH_SUFFIXES 29 | lib 30 | ) 31 | 32 | mark_as_advanced(LIBAIO_INCLUDE_DIR LIBAIO_LIBRARY) 33 | 34 | include(FindPackageHandleStandardArgs) 35 | find_package_handle_standard_args(Libaio 36 | DEFAULT_MSG 37 | LIBAIO_INCLUDE_DIR 38 | LIBAIO_LIBRARY) 39 | 40 | if(LIBAIO_FOUND) 41 | set(LIBAIO_LIBRARIES "${LIBAIO_LIBRARY}") # Add any dependencies here 42 | endif() 43 | 44 | -------------------------------------------------------------------------------- /cmake/FindSnappy.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find Snappy 2 | # Once done, this will define 3 | # 4 | # SNAPPY_FOUND - system has Snappy installed 5 | # SNAPPY_INCLUDE_DIR - the Snappy include directories 6 | # SNAPPY_LIBRARIES - link these to use Snappy 7 | # 8 | # The user may wish to set, in the CMake GUI or otherwise, this variable: 9 | # SNAPPY_DIR - path to start searching for the module 10 | 11 | find_path(SNAPPY_INCLUDE_DIR 12 | snappy.h 13 | HINTS 14 | PATH_SUFFIXES 15 | include 16 | ) 17 | 18 | #IF(WIN32) 19 | # SET(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) 20 | #ELSE(WIN32) 21 | # SET(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) 22 | #ENDIF(WIN32) 23 | 24 | 25 | find_library(SNAPPY_LIBRARY 26 | snappy 27 | HINTS 28 | PATH_SUFFIXES 29 | lib 30 | ) 31 | 32 | mark_as_advanced(SNAPPY_INCLUDE_DIR SNAPPY_LIBRARY) 33 | 34 | include(FindPackageHandleStandardArgs) 35 | find_package_handle_standard_args(Snappy 36 | DEFAULT_MSG 37 | SNAPPY_INCLUDE_DIR 38 | SNAPPY_LIBRARY) 39 | 40 | if(SNAPPY_FOUND) 41 | set(SNAPPY_LIBRARIES "${SNAPPY_LIBRARY}") # Add any dependencies here 42 | endif() 43 | 44 | -------------------------------------------------------------------------------- /include/cascadb/comparator.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_COMPARATOR_H_ 6 | #define CASCADB_COMPARATOR_H_ 7 | 8 | #include 9 | #include "slice.h" 10 | 11 | namespace cascadb { 12 | 13 | class Comparator { 14 | public: 15 | virtual int compare(const Slice & s1, const Slice & s2) const = 0; 16 | virtual ~Comparator(){} 17 | }; 18 | 19 | class LexicalComparator : public Comparator { 20 | public: 21 | int compare(const Slice & s1, const Slice & s2) const 22 | { 23 | return s1.compare(s2); 24 | } 25 | }; 26 | 27 | template 28 | class NumericComparator : public Comparator { 29 | public: 30 | int compare(const Slice & s1, const Slice & s2) const 31 | { 32 | assert(s1.size() == sizeof(NumericType) && 33 | s2.size() == sizeof(NumericType)); 34 | NumericType *n1 = (NumericType*)s1.data(); 35 | NumericType *n2 = (NumericType*)s2.data(); 36 | return *n1 - *n2; 37 | } 38 | }; 39 | 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /include/cascadb/db.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_DB_H_ 6 | #define CASCADB_DB_H_ 7 | 8 | #include 9 | 10 | #include "slice.h" 11 | #include "comparator.h" 12 | #include "options.h" 13 | #include "directory.h" 14 | 15 | namespace cascadb { 16 | 17 | class DB { 18 | public: 19 | virtual ~DB() {} 20 | 21 | static DB* open(const std::string& name, const Options& options); 22 | 23 | virtual bool put(Slice key, Slice value) = 0; 24 | 25 | virtual bool del(Slice key) = 0; 26 | 27 | virtual bool get(Slice key, Slice& value) = 0; 28 | 29 | inline bool get(Slice key, std::string& value) 30 | { 31 | Slice v; 32 | if (!get(key, v)) { 33 | return false; 34 | } 35 | value.assign(v.data(), v.size()); 36 | v.destroy(); 37 | return true; 38 | } 39 | 40 | virtual void flush() = 0; 41 | 42 | virtual void debug_print(std::ostream& out) = 0; 43 | }; 44 | 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /include/cascadb/directory.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_DIRECTORY_H_ 6 | #define CASCADB_DIRECTORY_H_ 7 | 8 | #include 9 | 10 | #include "file.h" 11 | 12 | namespace cascadb { 13 | 14 | class Directory { 15 | public: 16 | virtual ~Directory() {} 17 | 18 | virtual bool file_exists(const std::string& filename) = 0; 19 | 20 | virtual SequenceFileReader* open_sequence_file_reader(const std::string& filename) = 0; 21 | 22 | virtual SequenceFileWriter* open_sequence_file_writer(const std::string& filename) = 0; 23 | 24 | virtual AIOFile* open_aio_file(const std::string& filename) = 0; 25 | 26 | virtual size_t file_length(const std::string& filename) = 0; 27 | 28 | virtual void rename_file(const std::string& from, const std::string& to) = 0; 29 | 30 | virtual void delete_file(const std::string& filename) = 0; 31 | 32 | virtual std::string to_string() = 0; 33 | }; 34 | 35 | Directory* create_ram_directory(); 36 | 37 | Directory* create_fs_directory(const std::string& path); 38 | 39 | } 40 | 41 | #endif -------------------------------------------------------------------------------- /include/cascadb/file.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_FILE_H_ 6 | #define CASCADB_FILE_H_ 7 | 8 | #include "slice.h" 9 | 10 | namespace cascadb { 11 | 12 | class SequenceFileReader { 13 | public: 14 | SequenceFileReader() {} 15 | virtual ~SequenceFileReader() {} 16 | 17 | // read a number of bytes from the end of file into buffer, 18 | // The function will block until data is ready, 19 | // Return number of bytes read. 20 | virtual size_t read(Slice buf) = 0; 21 | 22 | // skip a number of bytes 23 | virtual bool skip(size_t n) = 0; 24 | 25 | virtual void close() = 0; 26 | 27 | private: 28 | SequenceFileReader(const SequenceFileReader&); 29 | void operator=(const SequenceFileReader&); 30 | }; 31 | 32 | class SequenceFileWriter { 33 | public: 34 | SequenceFileWriter() {} 35 | virtual ~SequenceFileWriter() {} 36 | 37 | // append buffer to the end of file, 38 | // The function will block until data is ready, 39 | // Return true if written successfully 40 | virtual bool append(Slice buf) = 0; 41 | virtual bool flush() = 0; 42 | virtual void close() = 0; 43 | 44 | private: 45 | SequenceFileWriter(const SequenceFileWriter&); 46 | void operator=(const SequenceFileWriter&); 47 | }; 48 | 49 | struct AIOStatus { 50 | AIOStatus(): succ(false), read(0) {} 51 | 52 | bool succ; // whether AIO read/write completes successfully 53 | size_t read; // number of bytes read in AIO read 54 | }; 55 | 56 | typedef void (*aio_callback_t)(void* context, AIOStatus status); 57 | 58 | class AIOFile { 59 | public: 60 | AIOFile() {} 61 | virtual ~AIOFile() {} 62 | 63 | // blocking wrapper to async read 64 | virtual AIOStatus read(uint64_t offset, Slice buf); 65 | 66 | // blocking wrapper to async write 67 | virtual AIOStatus write(uint64_t offset, Slice buf); 68 | 69 | // prepare to read a number of bytes at specified offset into buffer, 70 | // Callback will be invoked after the write operation completes, 71 | // The context parameter will be passed back into callback 72 | virtual void async_read(uint64_t offset, Slice buf, void* context, aio_callback_t cb) = 0; 73 | 74 | // prepare to write bytes in buffer to file at specified offset, 75 | // Callback will be invoked after the write operation completes, 76 | // The context parameter will be passed back into callback 77 | virtual void async_write(uint64_t offset, Slice buf, void* context, aio_callback_t cb) = 0; 78 | 79 | virtual void truncate(uint64_t offset) {} 80 | 81 | virtual void close() = 0; 82 | 83 | private: 84 | AIOFile(const AIOFile&); 85 | void operator=(const AIOFile&); 86 | }; 87 | 88 | } 89 | 90 | #endif -------------------------------------------------------------------------------- /include/cascadb/options.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_OPTIONS_H_ 6 | #define CASCADB_OPTIONS_H_ 7 | 8 | #include 9 | #include 10 | 11 | namespace cascadb { 12 | 13 | class Directory; 14 | class Comparator; 15 | 16 | enum Compress { 17 | kNoCompress, // No compression 18 | kSnappyCompress // Google's Snappy, used in leveldb 19 | }; 20 | 21 | class Options { 22 | public: 23 | // Set defaults 24 | Options() { 25 | dir = NULL; 26 | comparator = NULL; 27 | 28 | inner_node_page_size = 4<<20; // 4M, bigger inner node improve write performance 29 | // but degrade read performance 30 | inner_node_children_number = 16; // bigger fanout will decrease the number of inner nodes, 31 | // but degrade write performance 32 | leaf_node_page_size = 4<<20; // 4M, smaller leaf improve read performance, 33 | // but increases the number of inner nodes 34 | leaf_node_bucket_size = 128<<10; // leaf nodes 're divdided into serveral buckets, 35 | // bucket is the unit of disk read when do point queries, 36 | // smaller value favors point query but may decrease 37 | // compression ratio. 38 | inner_node_msg_count = -1; // unlimited by default, leaved for writing unit test, 39 | // you should NOT use it 40 | leaf_node_record_count = -1; // unlimited by default, leaved for writing unit test, 41 | // you should NOT use it 42 | cache_limit = 512 << 20; // 512M, it's best to be set twice of the total size of inner nodes 43 | cache_dirty_high_watermark = 30; // 30% 44 | cache_dirty_expire = 60000; // 1 minute 45 | cache_writeback_ratio = 1; // 1% 46 | cache_writeback_interval = 100; // 100ms 47 | cache_evict_ratio = 1; // 1% 48 | cache_evict_high_watermark = 95; //95% 49 | 50 | compress = kNoCompress; 51 | check_crc = false; 52 | } 53 | 54 | /****************************** 55 | Components 56 | ******************************/ 57 | 58 | // Directory where data files and WAL store 59 | Directory *dir; 60 | 61 | // Key comparator 62 | Comparator *comparator; 63 | 64 | /****************************** 65 | Buffered BTree Parameters 66 | ******************************/ 67 | 68 | // Page size of inner node 69 | size_t inner_node_page_size; 70 | 71 | // Maximum children number of iner node 72 | size_t inner_node_children_number; 73 | 74 | // Page size of leaf node 75 | size_t leaf_node_page_size; 76 | 77 | // Bucket size inside leaf node 78 | size_t leaf_node_bucket_size; 79 | 80 | // Maximum count of buffered messages in InnerNode. 81 | // For writing testcase 82 | size_t inner_node_msg_count; 83 | 84 | // Maxium count of records in LeafNode 85 | // For writing testcase 86 | size_t leaf_node_record_count; 87 | 88 | /****************************** 89 | Cache Parameters 90 | ******************************/ 91 | 92 | // Maximum data size in cached nodes, in bytes 93 | size_t cache_limit; 94 | 95 | // When dirty nodes larger than this level, start writeback, 96 | // in percentage * 100 97 | unsigned int cache_dirty_high_watermark; 98 | 99 | // When dirty node is elder than this, start writeback, 100 | // in milliseconds 101 | unsigned int cache_dirty_expire; 102 | 103 | // How many dirty nodes're written back in a turn, 104 | // in percentage * 100 105 | unsigned int cache_writeback_ratio; 106 | 107 | // How often the flusher thread is waked up to check, 108 | // in milliseconds 109 | unsigned int cache_writeback_interval; 110 | 111 | // How many last used clean nodes're replaced out in a turn, 112 | // in percentage * 100 113 | unsigned int cache_evict_ratio; 114 | 115 | // When cache size grows larger than this level, start recycle some unused pages, 116 | // in percentage * 100 117 | unsigned int cache_evict_high_watermark; 118 | 119 | /******************************** 120 | Layout Parameters 121 | ********************************/ 122 | 123 | Compress compress; 124 | 125 | bool check_crc; 126 | }; 127 | 128 | } 129 | 130 | #endif 131 | -------------------------------------------------------------------------------- /include/cascadb/slice.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_SLICE_H_ 6 | #define CASCADB_SLICE_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace cascadb { 16 | 17 | class Slice 18 | { 19 | public: 20 | Slice() : data_(""), size_(0) {} 21 | 22 | Slice(const char *s) 23 | : data_(s) 24 | { 25 | assert(data_ != NULL); 26 | size_ = strlen(s); 27 | } 28 | 29 | Slice(const char *s, size_t n) 30 | : data_(s), size_(n) 31 | { 32 | } 33 | 34 | Slice(const std::string& s) 35 | : data_(s.c_str()), size_(s.size()) 36 | { 37 | } 38 | 39 | const char* data() const { return data_; } 40 | 41 | size_t size() const { return size_; } 42 | 43 | bool empty() const 44 | { 45 | return size_ == 0; 46 | } 47 | 48 | void resize(size_t s) 49 | { 50 | size_ = s; 51 | } 52 | 53 | void clear() 54 | { 55 | data_ = ""; size_ = 0; 56 | } 57 | 58 | char operator[](size_t n) const { 59 | assert(n < size()); 60 | return data_[n]; 61 | } 62 | 63 | int compare(const Slice& x) const { 64 | int common_prefix_len = (size_ < x.size_) ? size_ : x.size_; 65 | int r = memcmp(data_, x.data_, common_prefix_len); 66 | if (r == 0) { 67 | if (size_ < x.size_) r = -1; 68 | else if (size_ > x.size_) r = 1; 69 | } 70 | return r; 71 | } 72 | 73 | std::string to_string() const { 74 | return std::string(data_, size_); 75 | } 76 | 77 | static Slice alloc(size_t size) { 78 | assert(size); 79 | char *buf = new char[size]; 80 | assert(buf); 81 | return Slice(buf, size); 82 | } 83 | 84 | Slice clone() const { 85 | assert(size_); 86 | char *buf = new char[size_]; 87 | assert(buf); 88 | memcpy(buf, data_, size_); 89 | return Slice(buf, size_); 90 | } 91 | 92 | void destroy() { 93 | assert(size_); 94 | delete[] data_; 95 | clear(); 96 | } 97 | 98 | private: 99 | const char *data_; // start address of buffer 100 | uint32_t size_; // buffer length 101 | }; 102 | 103 | inline bool operator==(const Slice& x, const Slice& y) { 104 | return x.compare(y) == 0; 105 | } 106 | 107 | inline bool operator!=(const Slice& x, const Slice& y) { 108 | return !(x == y); 109 | } 110 | 111 | inline bool operator<(const Slice& x, const Slice& y) { 112 | return x.compare(y) < 0; 113 | } 114 | 115 | } 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file( GLOB_RECURSE SOURCE RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" 2 | "*.cpp" 3 | ) 4 | 5 | add_library(cascadbShared SHARED ${SOURCE}) 6 | target_link_libraries(cascadbShared ${CASCADB_LIBS}) 7 | set_target_properties(cascadbShared PROPERTIES OUTPUT_NAME cascadb) 8 | 9 | add_library(cascadbStatic STATIC ${SOURCE}) 10 | 11 | if(WIN32) 12 | install(TARGETS cascadbShared cascadbStatic RUNTIME DESTINATION lib ARCHIVE DESTINATION lib) 13 | else(WIN32) 14 | install(TARGETS cascadbShared cascadbStatic LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) 15 | endif(WIN32) 16 | -------------------------------------------------------------------------------- /src/cache/cache.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_CACHE_H_ 6 | #define CASCADB_CACHE_H_ 7 | 8 | #include "cascadb/options.h" 9 | #include "tree/node.h" 10 | #include "serialize/block.h" 11 | #include "serialize/layout.h" 12 | #include "sys/sys.h" 13 | 14 | #include 15 | #include 16 | 17 | namespace cascadb { 18 | 19 | class NodeFactory { 20 | public: 21 | virtual Node* new_node(bid_t nid) = 0; 22 | virtual ~NodeFactory(){} 23 | }; 24 | 25 | // Node cache of fixed size 26 | // When the percentage of dirty nodes reaches the high watermark, or get expired, 27 | // they're flushed out in the order of timestamp node is modified for the first time. 28 | // A reference count is maintained for each node, When cache is getting almost full, 29 | // clean nodes would be evicted if their reference count drops to 0. 30 | // Nodes're evicted in LRU order. 31 | // Cache can be shared among multiple tables. 32 | 33 | class Cache { 34 | public: 35 | Cache(const Options& options); 36 | 37 | ~Cache(); 38 | 39 | bool init(); 40 | 41 | // Add a table to cache 42 | bool add_table(const std::string& tbn, NodeFactory *factory, Layout *layout); 43 | 44 | // Flush all dirty nodes in a table 45 | void flush_table(const std::string& tbn); 46 | 47 | // Delete a table from cache, all loaded nodes in the table're 48 | // destroied, dirty nodes're flushed by default 49 | void del_table(const std::string& tbn, bool flush = true); 50 | 51 | // Put newly created node into cache 52 | void put(const std::string& tbn, bid_t nid, Node* node); 53 | 54 | // Acquire node, if node doesn't exist in cache, load it from layout 55 | Node* get(const std::string& tbn, bid_t nid, bool skeleton_only); 56 | 57 | // Write back dirty nodes if any condition satisfied, 58 | // Sweep out dead nodes 59 | void write_back(); 60 | 61 | void debug_print(std::ostream& out); 62 | 63 | protected: 64 | struct TableSettings { 65 | NodeFactory *factory; 66 | Layout *layout; 67 | Time last_checkpoint_time; 68 | }; 69 | 70 | bool get_table_settings(const std::string& tbn, TableSettings& tbs); 71 | 72 | void update_last_checkpoint_time(const std::string& tbn, Time t); 73 | 74 | bool must_evict(); 75 | 76 | // Test whether the cache grows larger than high watermark 77 | bool need_evict(); 78 | 79 | // Evict least recently used clean nodes to make room 80 | void evict(); 81 | 82 | void flush_nodes(std::vector& nodes); 83 | 84 | struct WriteCompleteContext { 85 | Node *node; 86 | Layout *layout; 87 | Block *block; 88 | }; 89 | 90 | void write_complete(WriteCompleteContext* context, bool succ); 91 | 92 | void delete_nodes(std::vector& nodes); 93 | 94 | private: 95 | Options options_; 96 | 97 | RWLock tables_lock_; 98 | std::map tables_; 99 | 100 | // TODO make me atomic 101 | Mutex size_mtx_; 102 | // total memory size occupied by nodes, 103 | // updated everytime the flusher thread runs 104 | size_t size_; 105 | 106 | class CacheKey { 107 | public: 108 | CacheKey(const std::string& t, bid_t n): tbn(t), nid(n) {} 109 | 110 | bool operator<(const CacheKey& o) const { 111 | int c = tbn.compare(o.tbn); 112 | if (c < 0) { return true; } 113 | else if (c > 0) { return false; } 114 | else { return nid < o.nid; } 115 | } 116 | 117 | std::string tbn; 118 | bid_t nid; 119 | }; 120 | 121 | RWLock nodes_lock_; 122 | 123 | std::map nodes_; 124 | 125 | // ensure there is only one thread is doing evict/flush 126 | Mutex global_mtx_; 127 | 128 | bool alive_; 129 | // scan nodes not being used, 130 | // async flush dirty page out 131 | Thread* flusher_; 132 | }; 133 | 134 | } 135 | 136 | #endif 137 | -------------------------------------------------------------------------------- /src/db/db_impl.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #include "util/logger.h" 6 | #include "store/ram_directory.h" 7 | #include "sys/linux/linux_fs_directory.h" 8 | #include "db_impl.h" 9 | 10 | using namespace std; 11 | using namespace cascadb; 12 | 13 | #define DAT_FILE_SUFFIX "cdb" 14 | 15 | DBImpl::~DBImpl() 16 | { 17 | delete tree_; 18 | delete cache_; 19 | delete layout_; 20 | delete file_; 21 | } 22 | 23 | bool DBImpl::init() 24 | { 25 | Directory *dir = options_.dir; 26 | if (!dir) { 27 | LOG_ERROR("dir must be set in options"); 28 | return false; 29 | } 30 | 31 | string filename = name_ + "." + DAT_FILE_SUFFIX; 32 | size_t length = 0; 33 | bool create = true; 34 | if (dir->file_exists(filename)) { 35 | length = dir->file_length(filename); 36 | if (length > 0) { 37 | create = false; 38 | } 39 | } 40 | LOG_INFO("init db , data file length " << length << ", create " << create); 41 | 42 | file_ = dir->open_aio_file(filename); 43 | layout_ = new Layout(file_, length, options_); 44 | if (!layout_->init(create)) { 45 | LOG_ERROR("init layout error"); 46 | return false; 47 | } 48 | 49 | cache_ = new Cache(options_); 50 | if (!cache_->init()) { 51 | LOG_ERROR("init cache error"); 52 | return false; 53 | } 54 | 55 | tree_ = new Tree(name_, options_, cache_, layout_); 56 | if (!tree_->init()) { 57 | LOG_ERROR("tree init error"); 58 | return false; 59 | } 60 | 61 | return true; 62 | } 63 | 64 | bool DBImpl::put(Slice key, Slice value) 65 | { 66 | return tree_->put(key, value); 67 | } 68 | 69 | bool DBImpl::del(Slice key) 70 | { 71 | return tree_->del(key); 72 | } 73 | 74 | bool DBImpl::get(Slice key, Slice& value) 75 | { 76 | return tree_->get(key, value); 77 | } 78 | 79 | void DBImpl::flush() 80 | { 81 | cache_->flush_table(name_); 82 | } 83 | 84 | void DBImpl::debug_print(std::ostream& out) 85 | { 86 | cache_->debug_print(out); 87 | } 88 | 89 | DB* cascadb::DB::open(const std::string& name, const Options& options) 90 | { 91 | DBImpl* db = new DBImpl(name, options); 92 | if (!db->init()) { 93 | delete db; 94 | return NULL; 95 | } 96 | return db; 97 | } -------------------------------------------------------------------------------- /src/db/db_impl.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_DB_IMPL_H_ 6 | #define CASCADB_DB_IMPL_H_ 7 | 8 | #include "cascadb/db.h" 9 | #include "serialize/layout.h" 10 | #include "cache/cache.h" 11 | #include "tree/tree.h" 12 | 13 | namespace cascadb { 14 | 15 | class Tree; 16 | 17 | class DBImpl : public DB { 18 | public: 19 | DBImpl(const std::string& name, const Options& options) 20 | : name_(name), options_(options), 21 | file_(NULL), layout_(NULL), 22 | cache_(NULL), tree_(NULL) 23 | { 24 | } 25 | 26 | ~DBImpl(); 27 | 28 | bool init(); 29 | 30 | bool put(Slice key, Slice value); 31 | 32 | bool del(Slice key); 33 | 34 | bool get(Slice key, Slice& value); 35 | 36 | void flush(); 37 | 38 | void debug_print(std::ostream& out); 39 | 40 | private: 41 | std::string name_; 42 | Options options_; 43 | 44 | AIOFile *file_; 45 | Layout *layout_; 46 | Cache *cache_; 47 | Tree* tree_; 48 | }; 49 | 50 | } 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/serialize/block.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #include "block.h" 6 | 7 | using namespace cascadb; 8 | 9 | bool BlockReader::readSlice(Slice& s) 10 | { 11 | uint32_t sz; 12 | if (readUInt32(&sz)) { 13 | assert(offset_ <= block_->size_); 14 | if (offset_ + sz <= block_->size_) { 15 | s = Slice(block_->start() + offset_, sz).clone(); 16 | offset_ += sz; 17 | return true; 18 | } 19 | } 20 | return false; 21 | } 22 | 23 | bool BlockWriter::writeSlice(const Slice& s) 24 | { 25 | size_t sz = s.size(); 26 | if( writeUInt32(sz)) { 27 | assert(offset_ <= block_->capacity()); 28 | if (offset_ + sz <= block_->capacity()) { 29 | memcpy((char *)block_->start() + offset_, s.data(), sz); 30 | offset_ += sz; 31 | if (offset_ > block_->size_) { 32 | block_->size_ = offset_; 33 | } 34 | return true; 35 | } 36 | } 37 | return false; 38 | } 39 | -------------------------------------------------------------------------------- /src/serialize/block.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_SERIALIZE_BLOCK_H_ 6 | #define CASCADB_SERIALIZE_BLOCK_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "cascadb/slice.h" 14 | #include "util/bits.h" 15 | 16 | namespace cascadb { 17 | 18 | typedef uint64_t bid_t; 19 | 20 | // Chunk of memory buffered/managed by Layout 21 | class Block { 22 | public: 23 | Block(Slice buf, size_t start, size_t size) 24 | : buf_(buf), start_(start), size_(size) 25 | { 26 | assert(start_ < buf_.size()); 27 | assert(start_ + size_ <= buf_.size()); 28 | } 29 | 30 | Slice& buffer() {return buf_;} 31 | 32 | size_t size() {return size_;} 33 | 34 | void set_size(size_t size) 35 | { 36 | assert(start_ + size_ <= buf_.size()); 37 | size_ = size; 38 | } 39 | 40 | inline const char* start() 41 | { 42 | return buf_.data() + start_; 43 | } 44 | 45 | inline size_t capacity() 46 | { 47 | return buf_.size() - start_; 48 | } 49 | 50 | inline size_t remain() 51 | { 52 | return capacity() - size_; 53 | } 54 | 55 | void clear() {size_ = 0;} 56 | 57 | private: 58 | friend class BlockReader; 59 | friend class BlockWriter; 60 | 61 | Block(Block& o); // usage denied 62 | Block& operator=(Block& o); // usage denied 63 | 64 | Slice buf_; // buffer 65 | size_t start_; // start offset in buffer 66 | size_t size_; // size of buffer 67 | }; 68 | 69 | // Read operations to Block 70 | class BlockReader 71 | { 72 | public: 73 | BlockReader(Block* block) 74 | : block_(block), offset_(0) 75 | { 76 | } 77 | 78 | char *addr() 79 | { 80 | return (char *)block_->start() + pos(); 81 | } 82 | 83 | void seek(size_t offset) 84 | { 85 | offset_ = offset; 86 | } 87 | 88 | size_t pos() 89 | { 90 | return offset_; 91 | } 92 | 93 | bool skip(size_t length) 94 | { 95 | if (offset_ + length <= block_->size_) { 96 | offset_ += length; 97 | return true; 98 | } else { 99 | return false; 100 | } 101 | } 102 | 103 | size_t remain() 104 | { 105 | assert(offset_ <= block_->size_); 106 | return block_->size_ - offset_; 107 | } 108 | 109 | bool readBool(bool* v) { return readUInt((uint8_t*)v); } 110 | bool readUInt8(uint8_t* v) { return readUInt(v); } 111 | bool readUInt16(uint16_t* v) { return readUInt(v); } 112 | bool readUInt32(uint32_t* v) { return readUInt(v); } 113 | bool readUInt64(uint64_t* v) { return readUInt(v); } 114 | bool readSlice(Slice & s); 115 | 116 | protected: 117 | template 118 | bool readUInt(T* v) { 119 | assert(offset_ <= block_->size_); 120 | if (offset_ + sizeof(T) <= block_->size_) { 121 | *v = *(T*)(block_->start() + offset_); 122 | offset_ += sizeof(T); 123 | return true; 124 | } else { 125 | return false; 126 | } 127 | } 128 | 129 | private: 130 | Block* block_; 131 | size_t offset_; 132 | }; 133 | 134 | // Write operations to Block 135 | class BlockWriter 136 | { 137 | public: 138 | BlockWriter(Block* block) 139 | : block_(block), offset_(0) 140 | { 141 | } 142 | 143 | char *addr() 144 | { 145 | return (char *)block_->start() + pos(); 146 | } 147 | 148 | void seek(size_t offset) 149 | { 150 | offset_ = offset; 151 | } 152 | 153 | size_t pos() 154 | { 155 | return offset_; 156 | } 157 | 158 | bool skip(size_t length) 159 | { 160 | if (offset_ + length <= block_->capacity()) { 161 | offset_ += length; 162 | if(block_->size_ < offset_) { 163 | block_->size_ = offset_; 164 | } 165 | return true; 166 | } else { 167 | return false; 168 | } 169 | } 170 | 171 | size_t remain() 172 | { 173 | return block_->capacity() - offset_; 174 | } 175 | 176 | bool writeBool(bool v) {return writeInt(v);} 177 | bool writeUInt8(uint8_t v) { return writeInt(v); } 178 | bool writeUInt16(uint16_t v) { return writeInt(v); } 179 | bool writeUInt32(uint32_t v) { return writeInt(v); } 180 | bool writeUInt64(uint64_t v) { return writeInt(v); } 181 | bool writeSlice(const Slice &s); 182 | 183 | protected: 184 | template 185 | bool writeInt(T v) { 186 | assert(offset_ <= block_->capacity()); 187 | if (offset_ + sizeof(T) <= block_->capacity()) { 188 | *(T*)(block_->start() + offset_) = v; 189 | offset_ += sizeof(T); 190 | if(block_->size_ < offset_) { 191 | block_->size_ = offset_; 192 | } 193 | return true; 194 | } else { 195 | return false; 196 | } 197 | } 198 | 199 | 200 | private: 201 | Block* block_; 202 | size_t offset_; 203 | }; 204 | 205 | } 206 | 207 | #endif 208 | -------------------------------------------------------------------------------- /src/serialize/layout.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_SERIALIZE_LAYOUT_H_ 6 | #define CASCADB_SERIALIZE_LAYOUT_H_ 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "cascadb/file.h" 16 | #include "cascadb/options.h" 17 | #include "sys/sys.h" 18 | #include "util/callback.h" 19 | #include "block.h" 20 | #include "super_block.h" 21 | 22 | namespace cascadb { 23 | 24 | #define BLOCK_META_SIZE (64 + 32 + 32 + 16 + 16) / 8 25 | 26 | // Metadata for block, stored inside index 27 | struct BlockMeta { 28 | uint64_t offset; // start offset in file 29 | uint32_t skeleton_size; // size of node skeleton 30 | uint32_t total_size; // total size in bytes 31 | uint16_t crc; // crc of block data 32 | uint16_t skeleton_crc; // crc of skeleton data 33 | }; 34 | 35 | // Storage layout, read blocks from file and write blocks into file 36 | 37 | // TODO: 38 | // 1. crc and more compression algorithm 39 | // 2. fragmentation collection 40 | // 3. recover from disaster 41 | 42 | class Layout { 43 | public: 44 | Layout(AIOFile* aio_file, 45 | size_t length, 46 | const Options& options); 47 | 48 | ~Layout(); 49 | 50 | // Read and initialize SuperBlock if create is set to false 51 | // Otherwise, initialize and write SuperBlock out 52 | bool init(bool create = false); 53 | 54 | // Blocking read 55 | // If skeleton_only is set, read node's skeleton only, 56 | // otherwise the whole node is read 57 | Block* read(bid_t bid, bool skeleton_only); 58 | 59 | // Blocking read 60 | // Read from a relative offset from the beginning of block 61 | // and get n bytes, the area should not out of bounds 62 | Block* read(bid_t bid, uint32_t offset, uint32_t size); 63 | 64 | // Initialize a read operation 65 | void async_read(bid_t bid, Block** block, Callback *cb); 66 | 67 | // Initiate a write operation 68 | void async_write(bid_t bid, Block* block, uint32_t skeleton_size, Callback *cb); 69 | 70 | // Delete block from index 71 | void delete_block(bid_t bid); 72 | 73 | // Flush blocks and index out 74 | bool flush(); 75 | 76 | // Flush meta data 77 | bool flush_meta(); 78 | 79 | // Truncate unused space at file end, 80 | // invoked inside init/flush by default 81 | void truncate(); 82 | 83 | // Construct a Block object 84 | Block* create(size_t limit); 85 | 86 | // Destrcut a Block object 87 | void destroy(Block* block); 88 | 89 | protected: 90 | // read and deserialize superblock 91 | bool load_superblock(); 92 | 93 | // read and deserialize the second superblock 94 | // superblock is double written to ensure correctness 95 | bool load_2nd_superblock(); 96 | 97 | // Flush super block (double write) 98 | bool flush_superblock(); 99 | 100 | // Read and deserialize the index block 101 | bool load_index(); 102 | 103 | // Serialize index into the index block and write it out 104 | bool flush_index(); 105 | 106 | // Add fly holes to hole list 107 | void flush_fly_holes(size_t fly_hole_size); 108 | 109 | // Deserialize superblock from buffer 110 | bool read_superblock(BlockReader& reader); 111 | 112 | // Serialize superblock into buffer 113 | bool write_superblock(BlockWriter& writer); 114 | 115 | // Deserialize index from buffer 116 | bool read_index(BlockReader& reader); 117 | 118 | // Calculate the size of buffer after index is serialized 119 | size_t get_index_size(); 120 | 121 | // Serialize index into buffer 122 | bool write_index(BlockWriter& writer); 123 | 124 | // Deserialize block metadata from buffer 125 | bool read_block_meta(BlockMeta* meta, BlockReader& reader); 126 | 127 | // Serialize block metadata into buffer 128 | bool write_block_meta(BlockMeta* meta, BlockWriter& writer); 129 | 130 | // Context of async read operation 131 | struct AsyncReadReq { 132 | bid_t bid; 133 | Callback *cb; 134 | Block **block; 135 | BlockMeta meta; 136 | Slice buffer; 137 | }; 138 | 139 | // called when AIOFile returns the result of async read 140 | void handle_async_read(AsyncReadReq *r, AIOStatus status); 141 | 142 | // Context of async write operation 143 | struct AsyncWriteReq { 144 | bid_t bid; 145 | Callback *cb; 146 | BlockMeta meta; 147 | Slice buffer; 148 | }; 149 | 150 | // called when AIOFile returns the result of asyn write 151 | void handle_async_write(AsyncWriteReq *req, AIOStatus status); 152 | 153 | bool get_block_meta(bid_t bid, BlockMeta& meta); 154 | 155 | void set_block_meta(bid_t bid, const BlockMeta& meta); 156 | 157 | void del_block_meta(bid_t bid); 158 | 159 | bool read_block(const BlockMeta& meta, Block **block); 160 | 161 | bool read_block(const BlockMeta& meta, uint32_t offset, uint32_t size, Block **block); 162 | 163 | // Read from offset and fill buffer 164 | bool read_data(uint64_t offset, Slice& buffer); 165 | 166 | // Write buffer into file offset 167 | bool write_data(uint64_t offset, Slice buffer); 168 | 169 | // get the position to write for given size 170 | uint64_t get_offset(size_t size); 171 | 172 | void print_index_info(); 173 | 174 | void init_block_offset_index(); 175 | 176 | void init_holes(); 177 | 178 | void add_hole(uint64_t offset, size_t size); 179 | 180 | void add_fly_hole(uint64_t offset, size_t size); 181 | 182 | bool get_hole(size_t size, uint64_t& offset); 183 | 184 | Slice alloc_aligned_buffer(size_t size); 185 | 186 | void free_buffer(Slice buffer); 187 | 188 | private: 189 | AIOFile *aio_file_; 190 | uint64_t length_; // file length 191 | Options options_; 192 | 193 | Mutex mtx_; 194 | 195 | // the offset to file end 196 | uint64_t offset_; 197 | 198 | SuperBlock *superblock_; 199 | 200 | Mutex block_index_mtx_; 201 | 202 | // Main index of BlockMeta for each blocks, indexed by offset 203 | typedef std::map BlockIndexType; 204 | BlockIndexType block_index_; 205 | 206 | // Auxiliary index of BlockMeta, sorted by offset in file. 207 | // including BlockMeta of block index 208 | typedef std::map BlockOffsetIndexType; 209 | BlockOffsetIndexType block_offset_index_; 210 | 211 | Mutex hole_list_mtx_; 212 | Mutex fly_hole_list_mtx_; 213 | 214 | // A hole is generated when modify a block, the space taken up 215 | // before modification by the block becomes a hole 216 | struct Hole { 217 | uint64_t offset; 218 | uint64_t size; 219 | }; 220 | 221 | // A chain of holes in file 222 | typedef std::deque HoleListType; 223 | HoleListType hole_list_; 224 | HoleListType fly_hole_list_; 225 | 226 | // the rest are statistics information 227 | size_t fly_writes_; // todo atomic 228 | size_t fly_reads_; // todo atomic 229 | }; 230 | 231 | } 232 | 233 | #endif 234 | -------------------------------------------------------------------------------- /src/serialize/super_block.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_SERIALIZE_SUPER_BLOCK_H_ 6 | #define CASCADB_SERIALIZE_SUPER_BLOCK_H_ 7 | 8 | #include "block.h" 9 | 10 | namespace cascadb { 11 | 12 | #define SUPER_BLOCK_SIZE 4096 13 | #define SUPER_BLOCK_MAGIC_NUM (0x6264616373616) // "cascadb 14 | 15 | class BlockMeta; 16 | 17 | class SuperBlock { 18 | public: 19 | SuperBlock() 20 | { 21 | magic_number0 = SUPER_BLOCK_MAGIC_NUM; // "cascadb 22 | major_version = 0; // "version 0.1" 23 | minor_version = 1; 24 | 25 | index_block_meta = NULL; 26 | magic_number1 = SUPER_BLOCK_MAGIC_NUM; // "cascadb" 27 | } 28 | 29 | uint64_t magic_number0; 30 | uint8_t major_version; 31 | uint8_t minor_version; 32 | 33 | BlockMeta *index_block_meta; 34 | uint64_t magic_number1; 35 | }; 36 | 37 | } 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/store/file.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #include "sys/sys.h" 6 | #include "util/logger.h" 7 | #include "cascadb/file.h" 8 | 9 | using namespace std; 10 | using namespace cascadb; 11 | 12 | class BlockingAIOReqest { 13 | public: 14 | BlockingAIOReqest() : mtx(), condvar(&mtx) {} 15 | 16 | Mutex mtx; 17 | CondVar condvar; 18 | AIOStatus status; 19 | }; 20 | 21 | static void blocking_aio_handler(void *context, AIOStatus status) 22 | { 23 | BlockingAIOReqest* req = (BlockingAIOReqest*) context; 24 | // to prevent race condition when notify() is called before wait() 25 | req->mtx.lock(); 26 | req->status = status; 27 | req->mtx.unlock(); 28 | req->condvar.notify(); 29 | } 30 | 31 | AIOStatus AIOFile::read(uint64_t offset, Slice buf) 32 | { 33 | BlockingAIOReqest* req = new BlockingAIOReqest(); 34 | req->mtx.lock(); 35 | async_read(offset, buf, req, blocking_aio_handler); 36 | req->condvar.wait(); 37 | AIOStatus status = req->status; 38 | req->mtx.unlock(); 39 | delete req; 40 | return status; 41 | } 42 | 43 | AIOStatus AIOFile::write(uint64_t offset, Slice buf) 44 | { 45 | BlockingAIOReqest* req = new BlockingAIOReqest(); 46 | req->mtx.lock(); 47 | async_write(offset, buf, req, blocking_aio_handler); 48 | req->condvar.wait(); 49 | AIOStatus status = req->status; 50 | req->mtx.unlock(); 51 | delete req; 52 | return status; 53 | } -------------------------------------------------------------------------------- /src/store/fs_directory.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #include 6 | 7 | #include "store/fs_directory.h" 8 | 9 | using namespace std; 10 | using namespace cascadb; 11 | 12 | FSDirectory::FSDirectory(const string& dir) : dir_(dir) 13 | { 14 | } 15 | 16 | FSDirectory::~FSDirectory() 17 | { 18 | } 19 | 20 | void FSDirectory::rename_file(const std::string& from, const std::string& to) 21 | { 22 | rename(fullpath(from).c_str(), fullpath(to).c_str()); 23 | } 24 | 25 | void FSDirectory::delete_file(const std::string& filename) 26 | { 27 | remove(fullpath(filename).c_str()); 28 | } 29 | 30 | std::string FSDirectory::to_string() 31 | { 32 | return "FSDirectory:@path=" + dir_; 33 | } 34 | 35 | #ifdef OS_LINUX 36 | #include "sys/linux/linux_fs_directory.h" 37 | #endif 38 | 39 | #include "sys/posix/posix_fs_directory.h" 40 | 41 | Directory* cascadb::create_fs_directory(const std::string& path) 42 | { 43 | #ifdef OS_LINUX 44 | return new LinuxFSDirectory(path); 45 | #else 46 | return new PosixFSDirectory(path); 47 | #endif 48 | } -------------------------------------------------------------------------------- /src/store/fs_directory.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_STORE_FS_DIRECTORY_H_ 6 | #define CASCADB_STORE_FS_DIRECTORY_H_ 7 | 8 | #include "cascadb/directory.h" 9 | 10 | namespace cascadb { 11 | 12 | class FSDirectory : public Directory { 13 | public: 14 | FSDirectory(const std::string & dir); 15 | 16 | virtual ~FSDirectory(); 17 | 18 | virtual bool file_exists(const std::string& filename) = 0; 19 | 20 | virtual SequenceFileReader* open_sequence_file_reader(const std::string& filename) = 0; 21 | 22 | virtual SequenceFileWriter* open_sequence_file_writer(const std::string& filename) = 0; 23 | 24 | virtual AIOFile* open_aio_file(const std::string& filename) = 0; 25 | 26 | virtual size_t file_length(const std::string& filename) = 0; 27 | 28 | virtual void rename_file(const std::string& from, const std::string& to); 29 | 30 | virtual void delete_file(const std::string& filename); 31 | 32 | virtual std::string to_string(); 33 | 34 | protected: 35 | virtual const std::string fullpath(const std::string & filename) = 0; 36 | 37 | std::string dir_; 38 | }; 39 | 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/store/ram_directory.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_STORE_RAM_DIRECTORY_H_ 6 | #define CASCADB_STORE_RAM_DIRECTORY_H_ 7 | 8 | #include 9 | #include 10 | 11 | #include "cascadb/directory.h" 12 | #include "sys/sys.h" 13 | 14 | namespace cascadb { 15 | 16 | #define RAMFILE_BLK_SIZE 4096 17 | 18 | class RAMFile { 19 | public: 20 | RAMFile() : refcnt_(0), total_(0), length_(0) {} 21 | 22 | void inc_refcnt() { 23 | ScopedMutex lock(&mtx_); 24 | refcnt_ ++; 25 | } 26 | 27 | void dec_refcnt() { 28 | ScopedMutex lock(&mtx_); 29 | refcnt_ --; 30 | if (refcnt_ == 0) { 31 | lock.unlock(); 32 | delete this; // fixme: possible to have race condition here 33 | } 34 | } 35 | 36 | int get_refcnt() { 37 | ScopedMutex lock(&mtx_); 38 | return refcnt_; 39 | } 40 | 41 | // Read data maybe not consistent if another thread is writing the same area concurrently 42 | bool read(uint64_t offset, Slice data, size_t& res); 43 | 44 | bool write(uint64_t offset, Slice data); 45 | 46 | void truncate(uint64_t offset); 47 | 48 | uint64_t length() { 49 | ScopedMutex lock(&mtx_); 50 | return length_; 51 | } 52 | 53 | private: 54 | ~RAMFile() { 55 | assert(refcnt_ == 0); 56 | for (std::vector::iterator it = blks_.begin(); 57 | it != blks_.end(); it ++ ) { 58 | delete [] (*it); 59 | } 60 | } 61 | 62 | Mutex mtx_; 63 | size_t refcnt_; 64 | 65 | std::vector blks_; // memory blocks allocated 66 | uint64_t total_; // total memory in allocated in blks_ 67 | uint64_t length_; // length of RAMFile 68 | }; 69 | 70 | class RAMDirectory : public Directory { 71 | public: 72 | RAMDirectory() 73 | { 74 | } 75 | 76 | ~RAMDirectory(); 77 | 78 | bool file_exists(const std::string& filename); 79 | 80 | SequenceFileReader* open_sequence_file_reader(const std::string& filename); 81 | 82 | SequenceFileWriter* open_sequence_file_writer(const std::string& filename); 83 | 84 | AIOFile* open_aio_file(const std::string& filename); 85 | 86 | size_t file_length(const std::string& filename); 87 | 88 | void rename_file(const std::string& from, const std::string& to); 89 | 90 | void delete_file(const std::string& filename); 91 | 92 | std::string to_string(); 93 | 94 | protected: 95 | // create if RAMFile not exist and the create flag is set 96 | RAMFile* open_ramfile(const std::string& filename, bool create = false); 97 | 98 | private: 99 | Mutex mtx_; 100 | std::map files_; 101 | }; 102 | 103 | } 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /src/sys/linux/linux_fs_directory.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | // std 6 | #include 7 | #include 8 | 9 | // posix 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | // linux 17 | #ifdef HAS_LIBAIO 18 | #include 19 | #endif 20 | 21 | #include "sys/sys.h" 22 | #include "util/logger.h" 23 | #include "linux_fs_directory.h" 24 | 25 | using namespace std; 26 | using namespace cascadb; 27 | 28 | #ifdef HAS_LIBAIO 29 | 30 | #define MAX_AIO_EVENTS 128 31 | 32 | static void* handle_io_complete(void *ptr); 33 | 34 | enum AIOOP { 35 | AIORead, 36 | AIOWrite 37 | }; 38 | 39 | class AIOTask { 40 | public: 41 | AIOOP op; 42 | size_t size; 43 | void *context; 44 | aio_callback_t cb; 45 | }; 46 | 47 | // A wrapper of Linxu libaio APIs 48 | class LinuxAIOFile : public AIOFile { 49 | public: 50 | LinuxAIOFile(const std::string& path) 51 | : path_(path), closed_(false), fd_(-1), ctx_(0), thr_(NULL) 52 | { 53 | } 54 | 55 | ~LinuxAIOFile() 56 | { 57 | close(); 58 | } 59 | 60 | bool open() 61 | { 62 | fd_ = ::open(path_.c_str(), O_RDWR | O_DIRECT | O_CREAT, 0644); 63 | if (fd_ == -1) { 64 | LOG_ERROR("open file " << path_ << " error: " << strerror(errno)); 65 | return false; 66 | } 67 | 68 | int ret; 69 | if ((ret = io_setup(MAX_AIO_EVENTS, &ctx_)) < 0) { 70 | LOG_ERROR("linux aio io_setup error " << strerror(-1*ret)); 71 | ::close(fd_); 72 | return false; 73 | } 74 | 75 | thr_ = new Thread(::handle_io_complete); 76 | thr_->start(this); 77 | 78 | closed_ = false; 79 | return true; 80 | } 81 | 82 | void handle_io_complete() 83 | { 84 | while (!closed_) { 85 | struct io_event events[MAX_AIO_EVENTS]; 86 | memset(&events, 0, sizeof(struct io_event) * MAX_AIO_EVENTS); 87 | 88 | struct timespec timeout; 89 | timeout.tv_sec = 0; 90 | timeout.tv_nsec = 100000000; // 100ms 91 | 92 | int nevents; 93 | if ((nevents = ::io_getevents(ctx_, 1, MAX_AIO_EVENTS, events, &timeout)) < 0) { 94 | LOG_ERROR("linux aio io_getevents error " << strerror(-1*nevents)); 95 | if (-1*nevents == EINTR) { 96 | continue; 97 | } else { 98 | break; 99 | } 100 | } 101 | 102 | for (int i = 0; i < nevents; i++) { 103 | AIOTask* task = (AIOTask*)events[i].data; 104 | assert(task); 105 | int res = events[i].res; 106 | switch(task->op) { 107 | case AIORead: 108 | { 109 | AIOStatus status; 110 | if (res < 0) { 111 | LOG_ERROR("linux aio read error: " << strerror(-1*res)); 112 | status.succ = false; 113 | } else { 114 | status.succ = true; 115 | status.read = res; 116 | } 117 | task->cb(task->context, status); 118 | break; 119 | } 120 | case AIOWrite: 121 | { 122 | AIOStatus status; 123 | if (res < 0) { 124 | LOG_ERROR("linux aio write error: " << strerror(-1*res)); 125 | status.succ = false; 126 | } else if ((size_t)res < task->size) { 127 | LOG_ERROR("linux aio write incomplete, should be " << task->size 128 | << " bytes, actually " << res << " bytes"); 129 | status.succ = false; 130 | } else { 131 | assert((size_t)res == task->size); 132 | status.succ = true; 133 | } 134 | task->cb(task->context, status); 135 | break; 136 | } 137 | } 138 | 139 | delete task; 140 | } 141 | } 142 | } 143 | 144 | void async_read(uint64_t offset, Slice buf, void* context, aio_callback_t cb) 145 | { 146 | struct iocb iocb; 147 | struct iocb *iocbp = &iocb; 148 | 149 | LOG_TRACE("read " << buf.size() << " bytes from " << path_ << ":" << offset); 150 | 151 | // prepare 152 | AIOTask* task = new AIOTask(); 153 | assert(task); 154 | task->op = AIORead; 155 | task->size = buf.size(); 156 | task->context = context; 157 | task->cb = cb; 158 | io_prep_pread(iocbp, fd_, (void *)buf.data(), buf.size(), offset); 159 | iocbp->data = task; 160 | 161 | // submit 162 | if (!submit(1, &iocbp)) { 163 | AIOStatus status; 164 | status.succ = false; 165 | cb(context, status); 166 | delete task; 167 | } 168 | } 169 | 170 | void async_write(uint64_t offset, Slice buf, void* context, aio_callback_t cb) 171 | { 172 | struct iocb iocb; 173 | struct iocb *iocbp = &iocb; 174 | 175 | LOG_TRACE("write " << buf.size() << " bytes out to " << path_ << ":" << offset); 176 | 177 | // prepare 178 | AIOTask* task = new AIOTask(); 179 | assert(task); 180 | task->op = AIOWrite; 181 | task->size = buf.size(); 182 | task->context = context; 183 | task->cb = cb; 184 | io_prep_pwrite(iocbp, fd_, (void *)buf.data(), buf.size(), offset); 185 | iocbp->data = task; 186 | 187 | // submit 188 | if (!submit(1, &iocbp)) { 189 | AIOStatus status; 190 | status.succ = false; 191 | cb(context, status); 192 | delete task; 193 | } 194 | } 195 | 196 | void truncate(uint64_t offset) 197 | { 198 | if (::ftruncate(fd_, offset) < 0) { 199 | LOG_ERROR("ftruncate file error " << strerror(errno)); 200 | } 201 | } 202 | 203 | void close() 204 | { 205 | if (!closed_) { 206 | closed_ = true; 207 | 208 | thr_->join(); 209 | delete thr_; 210 | thr_ = NULL; 211 | 212 | int ret; 213 | if ((ret = io_destroy(ctx_)) < 0) { 214 | LOG_ERROR("linux aio io_destroy error " << strerror(-1*ret)); 215 | } 216 | ctx_ = 0; 217 | ::close(fd_); 218 | fd_ = -1; 219 | } 220 | } 221 | protected: 222 | bool submit(int n, struct iocb **iocbpp) { 223 | // submit 224 | while (true) { 225 | int ret = io_submit(ctx_, n, iocbpp); 226 | if (ret < 0) { 227 | int errcode = -1 * ret; 228 | if (errcode == EAGAIN) { 229 | LOG_INFO("linux aio io_submit busy, wait for a while"); 230 | cascadb::usleep(1000); 231 | continue; 232 | } 233 | LOG_ERROR("linux aio io_submit error: " << strerror(errcode)); 234 | return false; 235 | } 236 | assert(ret == n); 237 | return true; 238 | } 239 | } 240 | 241 | private: 242 | std::string path_; 243 | 244 | bool closed_; 245 | 246 | int fd_; 247 | 248 | io_context_t ctx_; // AIO context 249 | 250 | Thread *thr_; // thread to handle io completion events 251 | }; 252 | 253 | static void* handle_io_complete(void *ptr) 254 | { 255 | LinuxAIOFile *aio_file = (LinuxAIOFile*) ptr; 256 | aio_file->handle_io_complete(); 257 | return NULL; 258 | } 259 | 260 | #endif // LIBAIO 261 | 262 | AIOFile* LinuxFSDirectory::open_aio_file(const std::string& filename) 263 | { 264 | #ifdef HAS_LIBAIO 265 | LinuxAIOFile* file = new LinuxAIOFile(fullpath(filename)); 266 | if (file && file->open()) { 267 | return file; 268 | } 269 | delete file; 270 | return NULL; 271 | #else 272 | return PosixFSDirectory::open_aio_file(filename); 273 | #endif 274 | } 275 | 276 | -------------------------------------------------------------------------------- /src/sys/linux/linux_fs_directory.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_SYS_LINUX_LINUX_FS_DIRECTORY_H_ 6 | #define CASCADB_SYS_LINUX_LINUX_FS_DIRECTORY_H_ 7 | 8 | #include "sys/posix/posix_fs_directory.h" 9 | 10 | namespace cascadb { 11 | 12 | class LinuxFSDirectory : public PosixFSDirectory { 13 | public: 14 | LinuxFSDirectory(const std::string& path) : PosixFSDirectory(path) {} 15 | 16 | virtual AIOFile* open_aio_file(const std::string& filename); 17 | 18 | }; 19 | 20 | } 21 | 22 | #endif -------------------------------------------------------------------------------- /src/sys/posix/posix_fs_directory.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_SYS_POSIX_POSIX_FS_DIRECTORY_H_ 6 | #define CASCADB_SYS_POSIX_POSIX_FS_DIRECTORY_H_ 7 | 8 | #include "store/fs_directory.h" 9 | 10 | namespace cascadb { 11 | 12 | class PosixFSDirectory : public FSDirectory { 13 | public: 14 | PosixFSDirectory(const std::string& path) : FSDirectory(path) {} 15 | 16 | virtual ~PosixFSDirectory(); 17 | 18 | virtual bool file_exists(const std::string& filename); 19 | 20 | virtual SequenceFileReader* open_sequence_file_reader(const std::string& filename); 21 | 22 | virtual SequenceFileWriter* open_sequence_file_writer(const std::string& filename); 23 | 24 | // I'm implementing posix AIO here, however, it canbe overriden to support 25 | // kernel-level AIO in Linux, Solaris etc. 26 | virtual AIOFile* open_aio_file(const std::string& filename); 27 | 28 | virtual size_t file_length(const std::string& filename); 29 | 30 | protected: 31 | virtual const std::string fullpath(const std::string& filename); 32 | }; 33 | 34 | } 35 | 36 | #endif -------------------------------------------------------------------------------- /src/sys/posix/posix_sys.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "util/logger.h" 12 | #include "posix_sys.h" 13 | 14 | using namespace std; 15 | 16 | namespace cascadb { 17 | 18 | static exception pthread_call_exception(const char *label, int result) { 19 | string what = "pthread call "; 20 | what += label; 21 | what += ", reason: "; 22 | what += strerror(result); 23 | return runtime_error(what); 24 | } 25 | 26 | static void pthread_call(const char* label, int result) { 27 | if (result != 0) { 28 | throw pthread_call_exception(label, result); 29 | } 30 | } 31 | 32 | Thread::Thread(func f) : f_(f), alive_(false) 33 | { 34 | } 35 | 36 | Thread::~Thread() 37 | { 38 | if(alive_) join(); 39 | } 40 | 41 | void Thread::start(void* arg) 42 | { 43 | pthread_call("start thread", pthread_create(&thr_, NULL, f_, arg)); 44 | alive_ = true; 45 | } 46 | 47 | void Thread::join() 48 | { 49 | if(alive_) { 50 | pthread_call("join thread", pthread_join(thr_, NULL)); 51 | alive_ = false; 52 | } 53 | } 54 | 55 | Mutex::Mutex() { 56 | locked_ = false; 57 | pthread_call("init mutex", pthread_mutex_init(&mu_, NULL)); 58 | } 59 | 60 | Mutex::~Mutex() { 61 | pthread_call("destroy mutex", pthread_mutex_destroy(&mu_)); 62 | } 63 | 64 | void Mutex::lock() { 65 | int res = pthread_mutex_lock(&mu_); 66 | if (res == 0) { 67 | locked_ = true; 68 | } else { 69 | throw pthread_call_exception("lock", res); 70 | } 71 | } 72 | 73 | bool Mutex::lock_try() { 74 | int res = pthread_mutex_trylock(&mu_); 75 | if (res == 0) { 76 | locked_ = true; 77 | return true; 78 | } else if (res == EBUSY || res == EAGAIN) { 79 | return false; 80 | } else { 81 | throw pthread_call_exception("trylock", res); 82 | } 83 | } 84 | 85 | bool Mutex::lock_try(unsigned int millisec) { 86 | struct timespec timeout; 87 | Time t = now(); 88 | 89 | timeout.tv_nsec = t.tv_usec*1000 + millisec*1000000; 90 | timeout.tv_sec = t.tv_sec + timeout.tv_nsec/1000000000; 91 | timeout.tv_nsec = timeout.tv_nsec%1000000000; 92 | 93 | int res = pthread_mutex_timedlock(&mu_, &timeout); 94 | if (res == 0) { 95 | locked_ = true; 96 | return true; 97 | } else if (res == ETIMEDOUT) { 98 | return false; 99 | } else { 100 | throw pthread_call_exception("timedlock", res); 101 | } 102 | } 103 | 104 | void Mutex::unlock() { 105 | int res = pthread_mutex_unlock(&mu_); 106 | if (res == 0) { 107 | locked_ = false; 108 | } else { 109 | throw pthread_call_exception("unlock", res); 110 | } 111 | } 112 | 113 | RWLock::RWLock() 114 | { 115 | pthread_call("init rwlock", pthread_rwlock_init(&rwlock_, NULL)); 116 | } 117 | 118 | RWLock::~RWLock() 119 | { 120 | pthread_call("destroy rwlock", pthread_rwlock_destroy(&rwlock_)); 121 | } 122 | 123 | void RWLock::read_lock() 124 | { 125 | pthread_call("rdlock", pthread_rwlock_rdlock(&rwlock_)); 126 | } 127 | 128 | bool RWLock::try_read_lock() 129 | { 130 | int res = pthread_rwlock_tryrdlock(&rwlock_); 131 | if (res == 0) { 132 | return true; 133 | } else if (res == EBUSY) { 134 | return false; 135 | } else { 136 | throw pthread_call_exception("tryrdlock", res); 137 | } 138 | } 139 | 140 | bool RWLock::try_read_lock(unsigned int millisec) 141 | { 142 | struct timespec timeout; 143 | Time t = now(); 144 | 145 | timeout.tv_nsec = t.tv_usec*1000 + millisec*1000000; 146 | timeout.tv_sec = t.tv_sec + timeout.tv_nsec/1000000000; 147 | timeout.tv_nsec = timeout.tv_nsec%1000000000; 148 | 149 | int res = pthread_rwlock_timedrdlock(&rwlock_, &timeout); 150 | if (res == 0) { 151 | return true; 152 | } else if (res == ETIMEDOUT) { 153 | return false; 154 | } else { 155 | throw pthread_call_exception("timedrdlock", res); 156 | } 157 | } 158 | 159 | void RWLock::write_lock() 160 | { 161 | pthread_call("wrlock", pthread_rwlock_wrlock(&rwlock_)); 162 | } 163 | 164 | bool RWLock::try_write_lock() 165 | { 166 | int res = pthread_rwlock_trywrlock(&rwlock_); 167 | if (res == 0) { 168 | return true; 169 | } else if (res == EBUSY) { 170 | return false; 171 | } else { 172 | throw pthread_call_exception("trywrlock", res); 173 | } 174 | } 175 | 176 | bool RWLock::try_write_lock(unsigned int millisec) 177 | { 178 | struct timespec timeout; 179 | Time t = now(); 180 | 181 | timeout.tv_nsec = t.tv_usec*1000 + millisec*1000000; 182 | timeout.tv_sec = t.tv_sec + timeout.tv_nsec/1000000000; 183 | timeout.tv_nsec = timeout.tv_nsec%1000000000; 184 | 185 | int res = pthread_rwlock_timedwrlock(&rwlock_, &timeout); 186 | if (res == 0) { 187 | return true; 188 | } else if (res == ETIMEDOUT) { 189 | return false; 190 | } else { 191 | throw pthread_call_exception("timedwrlock", res); 192 | } 193 | } 194 | 195 | void RWLock::unlock() 196 | { 197 | pthread_call("rwlock unlock", pthread_rwlock_unlock(&rwlock_)); 198 | } 199 | 200 | CondVar::CondVar(Mutex* mu) : mu_(mu) { 201 | pthread_call("init cv", pthread_cond_init(&cv_, NULL)); 202 | } 203 | 204 | CondVar::~CondVar() { 205 | pthread_call("destroy cv", pthread_cond_destroy(&cv_)); 206 | } 207 | 208 | void CondVar::wait() { 209 | pthread_call("wait", pthread_cond_wait(&cv_, &mu_->mu_)); 210 | } 211 | 212 | bool CondVar::wait(unsigned int millisec) { 213 | struct timespec timeout; 214 | Time t = now(); 215 | 216 | timeout.tv_nsec = t.tv_usec*1000 + millisec*1000000; 217 | timeout.tv_sec = t.tv_sec + timeout.tv_nsec/1000000000; 218 | timeout.tv_nsec = timeout.tv_nsec%1000000000; 219 | 220 | int res = pthread_cond_timedwait(&cv_, &mu_->mu_, &timeout); 221 | if (res == 0) { 222 | return true; 223 | } else if (res == ETIMEDOUT) { 224 | return false; 225 | } else { 226 | throw pthread_call_exception("cv timedwait", res); 227 | } 228 | } 229 | 230 | void CondVar::notify() { 231 | pthread_call("notify", pthread_cond_signal(&cv_)); 232 | } 233 | 234 | void CondVar::notify_all() { 235 | pthread_call("notify all", pthread_cond_broadcast(&cv_)); 236 | } 237 | 238 | bool operator<(const Time& t1, const Time& t2) 239 | { 240 | int c = t1.tv_sec - t2.tv_sec; 241 | if (c < 0) { 242 | return true; 243 | } else if (c > 0) { 244 | return false; 245 | } else { 246 | return t1.tv_usec < t2.tv_usec; 247 | } 248 | 249 | } 250 | 251 | Time now() 252 | { 253 | Time t; 254 | ::gettimeofday(&t, NULL); 255 | return t; 256 | } 257 | 258 | extern std::ostream& operator<<(std::ostream& os, const Time& t) 259 | { 260 | string time = ctime(&t.tv_sec); 261 | time.erase(time.find('\n', 0), 1); 262 | os << time << ", " << setw(6) << t.tv_usec; 263 | return os; 264 | } 265 | 266 | extern uint64_t now_micros() 267 | { 268 | Time t = now(); 269 | return t.tv_sec * 1000000 + t.tv_usec; 270 | } 271 | 272 | void sleep(Second sec) 273 | { 274 | ::sleep(sec); 275 | } 276 | 277 | void usleep(USecond usec) 278 | { 279 | ::usleep(usec); 280 | } 281 | 282 | USecond interval_us(Time t1, Time t2) 283 | { 284 | return (t2.tv_sec - t1.tv_sec) * 1000000 + 285 | (t2.tv_usec - t1.tv_usec); 286 | } 287 | 288 | } 289 | -------------------------------------------------------------------------------- /src/sys/posix/posix_sys.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_SYS_POSIX_SYS_H_ 6 | #define CASCADB_SYS_POSIX_SYS_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace cascadb { 18 | 19 | class Thread { 20 | public: 21 | typedef void* (*func)(void* arg); 22 | Thread(func f); 23 | ~Thread(); 24 | void start(void* arg); 25 | void join(); 26 | private: 27 | Thread(const Thread&); 28 | Thread& operator =(const Thread&); 29 | func f_; 30 | pthread_t thr_; 31 | bool alive_; 32 | }; 33 | 34 | class Mutex { 35 | public: 36 | Mutex(); 37 | ~Mutex(); 38 | void lock(); 39 | bool lock_try(); 40 | bool lock_try(unsigned int millisec); 41 | void unlock(); 42 | bool locked() { return locked_; } 43 | private: 44 | friend class CondVar; 45 | Mutex(const Mutex&); 46 | Mutex& operator =(const Mutex&); 47 | bool locked_; 48 | pthread_mutex_t mu_; 49 | }; 50 | 51 | class RWLock { 52 | public: 53 | RWLock(); 54 | ~RWLock(); 55 | void read_lock(); 56 | bool try_read_lock(); 57 | bool try_read_lock(unsigned int millisec); 58 | 59 | void write_lock(); 60 | bool try_write_lock(); 61 | bool try_write_lock(unsigned int millisec); 62 | 63 | void unlock(); 64 | private: 65 | RWLock(const RWLock&); 66 | RWLock& operator =(const RWLock&); 67 | pthread_rwlock_t rwlock_; 68 | }; 69 | 70 | class CondVar { 71 | public: 72 | CondVar(Mutex* mu); 73 | ~CondVar(); 74 | void wait(); 75 | bool wait(unsigned int millisec); 76 | void notify(); 77 | void notify_all(); 78 | private: 79 | CondVar(const CondVar&); 80 | CondVar& operator =(const CondVar&); 81 | pthread_cond_t cv_; 82 | Mutex* mu_; 83 | }; 84 | 85 | 86 | typedef struct timeval Time; 87 | typedef time_t Second; 88 | typedef useconds_t USecond; 89 | 90 | extern bool operator<(const Time& t1, const Time& t2); 91 | 92 | extern Time now(); 93 | extern std::ostream& operator<<(std::ostream& os, const Time& t); 94 | extern uint64_t now_micros(); 95 | extern void sleep(Second sec); 96 | extern void usleep(USecond usec); 97 | // t2 - t1 98 | extern USecond interval_us(Time t1, Time t2); 99 | 100 | } 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /src/sys/sys.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_SYS_SYS_H_ 6 | #define CASCADB_SYS_SYS_H_ 7 | 8 | #include "sys/posix/posix_sys.h" 9 | 10 | namespace cascadb { 11 | 12 | class ScopedMutex { 13 | public: 14 | ScopedMutex(Mutex *mu) : mu_(mu) { 15 | assert(mu_); 16 | mu_->lock(); 17 | locked_ = true; 18 | } 19 | ~ScopedMutex() { 20 | if (locked_) { 21 | mu_->unlock(); 22 | } 23 | } 24 | void lock() { 25 | mu_->lock(); 26 | locked_ = true; 27 | } 28 | void unlock() { 29 | mu_->unlock(); 30 | locked_ = false; 31 | } 32 | private: 33 | ScopedMutex(const ScopedMutex&); 34 | ScopedMutex& operator =(const ScopedMutex&); 35 | bool locked_; 36 | Mutex *mu_; 37 | }; 38 | 39 | } 40 | #endif 41 | -------------------------------------------------------------------------------- /src/tree/keycomp.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_KEY_COMP_H_ 6 | #define CASCADB_KEY_COMP_H_ 7 | 8 | #include 9 | 10 | #include "cascadb/slice.h" 11 | #include "cascadb/comparator.h" 12 | 13 | namespace cascadb { 14 | 15 | // Compare keys between Msg, Record, Pivot and Slice 16 | class KeyComp 17 | { 18 | public: 19 | KeyComp(Comparator* comp) : comp_(comp) {} 20 | 21 | template 22 | bool operator() (const T1& left, const T2& right) 23 | { 24 | return comp_->compare(left.key, right.key) < 0; 25 | } 26 | 27 | template 28 | bool operator() (const T& left, Slice right) 29 | { 30 | return comp_->compare(left.key, right) < 0; 31 | } 32 | 33 | template 34 | bool operator() (Slice left, const T& right) 35 | { 36 | return comp_->compare(left, right.key) < 0; 37 | } 38 | 39 | private: 40 | Comparator* comp_; 41 | }; 42 | 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/tree/msg.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #include 6 | #include 7 | 8 | #include "msg.h" 9 | #include "keycomp.h" 10 | #include "util/bloom.h" 11 | 12 | using namespace std; 13 | using namespace cascadb; 14 | 15 | size_t Msg::size() const 16 | { 17 | size_t sz = 1 + 4 + key.size(); 18 | if (type == Put) { 19 | sz += (4 + value.size()); 20 | } 21 | return sz; 22 | } 23 | 24 | bool Msg::read_from(BlockReader& reader) 25 | { 26 | if (!reader.readUInt8((uint8_t*)&type)) return false; 27 | if (!reader.readSlice(key)) return false; 28 | if (type == Put) { 29 | if (!reader.readSlice(value)) return false; 30 | } 31 | return true; 32 | } 33 | 34 | bool Msg::write_to(BlockWriter& writer) 35 | { 36 | if (!writer.writeUInt8((uint8_t)type)) return false; 37 | if (!writer.writeSlice(key)) return false; 38 | if (type == Put) { 39 | if (!writer.writeSlice(value)) return false; 40 | } 41 | return true; 42 | } 43 | 44 | void Msg::destroy() 45 | { 46 | switch(type) { 47 | case Put: { 48 | key.destroy(); 49 | value.destroy(); 50 | } 51 | break; 52 | case Del: { 53 | key.destroy(); 54 | } 55 | break; 56 | default: 57 | ; 58 | } 59 | } 60 | 61 | MsgBuf::~MsgBuf() 62 | { 63 | for(ContainerType::iterator it = container_.begin(); 64 | it != container_.end(); it++ ) { 65 | it->destroy(); 66 | } 67 | container_.clear(); 68 | } 69 | 70 | void MsgBuf::write(const Msg& msg) 71 | { 72 | MsgBuf::Iterator it = container_.lower_bound(msg, KeyComp(comp_)); 73 | if (it == end() || it->key != msg.key) { 74 | container_.insert(it, msg); 75 | size_ += msg.size(); 76 | } else { 77 | size_ -= it->size(); 78 | it->destroy(); 79 | *it = msg; 80 | size_ += msg.size(); 81 | } 82 | } 83 | 84 | void MsgBuf::append(MsgBuf::Iterator first, MsgBuf::Iterator last) 85 | { 86 | MsgBuf::Iterator it = container_.begin(); 87 | MsgBuf::Iterator jt = first; 88 | KeyComp comp(comp_); 89 | 90 | while(jt != last) { 91 | it = container_.lower_bound(it, jt->key, comp); 92 | if (it == container_.end() || it->key != jt->key) { 93 | // important, it maybe invalid after insertion 94 | it = container_.insert(it, *jt); 95 | size_ += jt->size(); 96 | } else { 97 | size_ -= it->size(); 98 | it->destroy(); 99 | *it = *jt; 100 | size_ += it->size(); 101 | } 102 | jt ++; 103 | } 104 | } 105 | 106 | MsgBuf::Iterator MsgBuf::find(Slice key) 107 | { 108 | return container_.lower_bound(key, KeyComp(comp_)); 109 | } 110 | 111 | void MsgBuf::clear() 112 | { 113 | container_.clear(); 114 | size_ = 0; 115 | } 116 | 117 | bool MsgBuf::read_from(BlockReader& reader) 118 | { 119 | uint32_t cnt = 0; 120 | if (!reader.readUInt32(&cnt)) return false; 121 | // container_.resize(cnt); 122 | // for (size_t i = 0; i < cnt; i++ ) { 123 | // if (!container_[i].read_from(reader)) return false; 124 | // size_ += container_[i].size(); 125 | // } 126 | for (size_t i = 0; i < cnt; i++ ) { 127 | Msg msg; 128 | if (!msg.read_from(reader)) return false; 129 | size_ += msg.size(); 130 | container_.push_back(msg); 131 | } 132 | return true; 133 | } 134 | 135 | bool MsgBuf::write_to(BlockWriter& writer) 136 | { 137 | if (!writer.writeUInt32(container_.size())) return false; 138 | for (ContainerType::iterator it = container_.begin(); 139 | it != container_.end(); it ++ ) { 140 | if (!it->write_to(writer)) return false; 141 | } 142 | return true; 143 | } 144 | 145 | void MsgBuf::get_filter(std::string* filter) 146 | { 147 | std::vector key_slices; 148 | key_slices.reserve(container_.size()); 149 | 150 | for(ContainerType::iterator it = container_.begin(); 151 | it != container_.end(); it++ ) { 152 | key_slices.push_back(it->key); 153 | } 154 | 155 | bloom_create(&key_slices[0], key_slices.size(), filter); 156 | } 157 | -------------------------------------------------------------------------------- /src/tree/msg.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_MSG_H_ 6 | #define CASCADB_MSG_H_ 7 | 8 | #include 9 | 10 | #include "cascadb/slice.h" 11 | #include "cascadb/comparator.h" 12 | #include "serialize/block.h" 13 | #include "sys/sys.h" 14 | #include "fast_vector.h" 15 | 16 | // Implement message and message buffer 17 | 18 | namespace cascadb { 19 | 20 | // Messages're delayed write operations 21 | enum MsgType { 22 | _Msg, // uninitialized state 23 | Put, 24 | Del, 25 | }; 26 | 27 | class Msg { 28 | public: 29 | Msg() : type(_Msg) {} 30 | 31 | Msg(MsgType t, Slice k, Slice v = Slice()) 32 | : type(t), key(k), value(v) 33 | { 34 | } 35 | 36 | void set_put(Slice k, Slice v) 37 | { 38 | type = Put; 39 | key = k; 40 | value = v; 41 | } 42 | 43 | void set_del(Slice k) 44 | { 45 | type = Del; 46 | key = k; 47 | } 48 | 49 | size_t size() const; 50 | 51 | bool read_from(BlockReader& reader); 52 | 53 | bool write_to(BlockWriter& writer); 54 | 55 | void destroy(); 56 | 57 | MsgType type; 58 | Slice key; 59 | Slice value; 60 | }; 61 | 62 | // Store all messages buffered for a child node 63 | class MsgBuf { 64 | public: 65 | MsgBuf(Comparator *comp) 66 | : comp_(comp), size_(0) 67 | { 68 | } 69 | 70 | ~MsgBuf(); 71 | 72 | // Write a single Msg into MsgBuf 73 | void write(const Msg& msg); 74 | 75 | // Clear all Msg objects buffered but not destroy them 76 | void clear(); 77 | 78 | // You should lock MsgBuf before use iterator related operations 79 | void read_lock() 80 | { 81 | lock_.read_lock(); 82 | } 83 | 84 | void write_lock() 85 | { 86 | lock_.write_lock(); 87 | } 88 | 89 | // unlock MsgBuf after iterator related operations 90 | void unlock() 91 | { 92 | lock_.unlock(); 93 | } 94 | 95 | typedef FastVector ContainerType; 96 | 97 | typedef ContainerType::iterator Iterator; 98 | 99 | Iterator begin() { return container_.begin(); } 100 | 101 | Iterator end() { return container_.end(); } 102 | 103 | // Append range of Msg objects from another MsgBuf 104 | void append(Iterator first, Iterator last); 105 | 106 | // Find the whole buffer for the input key, 107 | // Return position of the first element no less than the input key, 108 | // aka the first element equal or bigger than the input key 109 | Iterator find(Slice key); 110 | 111 | // Return the number of messages buffered 112 | size_t count() const 113 | { 114 | return container_.size(); 115 | } 116 | 117 | const Msg& get(size_t idx) const 118 | { 119 | assert(idx >= 0 && idx < container_.size()); 120 | return container_[idx]; 121 | } 122 | 123 | // Return space taken to store messages buffered 124 | size_t size() const 125 | { 126 | return 4 + size_; 127 | } 128 | 129 | bool read_from(BlockReader& reader); 130 | 131 | bool write_to(BlockWriter& writer); 132 | 133 | // Get the bloom bitsets 134 | void get_filter(std::string* filter); 135 | 136 | private: 137 | Comparator *comp_; 138 | mutable RWLock lock_; 139 | ContainerType container_; 140 | size_t size_; 141 | }; 142 | 143 | } 144 | 145 | #endif 146 | -------------------------------------------------------------------------------- /src/tree/record.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #include 6 | 7 | #include "util/logger.h" 8 | #include "record.h" 9 | 10 | using namespace std; 11 | using namespace cascadb; 12 | 13 | size_t Record::size() 14 | { 15 | return 4 + key.size() + 4 + value.size(); 16 | } 17 | 18 | bool Record::read_from(BlockReader& reader) 19 | { 20 | if (!reader.readSlice(key)) return false; 21 | if (!reader.readSlice(value)) return false; 22 | return true; 23 | } 24 | 25 | bool Record::write_to(BlockWriter& writer) 26 | { 27 | if (!writer.writeSlice(key)) return false; 28 | if (!writer.writeSlice(value)) return false; 29 | return true; 30 | } 31 | 32 | RecordBuckets::~RecordBuckets() 33 | { 34 | for (size_t i = 0; i < buckets_.size(); i++) { 35 | delete buckets_[i].bucket; 36 | } 37 | } 38 | 39 | void RecordBuckets::push_back(Record record) 40 | { 41 | RecordBucket* bucket; 42 | if (buckets_.size() == 0 || last_bucket_length_ + 43 | record.size() > max_bucket_length_) { 44 | bucket = new RecordBucket(); 45 | RecordBucketInfo info; 46 | info.bucket = bucket; 47 | info.length = 4; 48 | buckets_.push_back(info); 49 | last_bucket_length_ = info.length; 50 | length_ += info.length; 51 | } else { 52 | bucket = buckets_.back().bucket; 53 | } 54 | 55 | bucket->push_back(record); 56 | buckets_.back().length += record.size(); 57 | last_bucket_length_ += record.size(); 58 | 59 | length_ += record.size(); 60 | size_ ++; 61 | } 62 | 63 | void RecordBuckets::swap(RecordBuckets &other) 64 | { 65 | buckets_.swap(other.buckets_); 66 | std::swap(last_bucket_length_, other.last_bucket_length_); 67 | std::swap(length_, other.length_); 68 | std::swap(size_, other.size_); 69 | } 70 | 71 | Slice RecordBuckets::split(RecordBuckets &other) 72 | { 73 | assert(other.buckets_number() == 0); 74 | assert(buckets_.size()); 75 | 76 | if (buckets_.size() == 1) { 77 | // divide records in the first bucket 78 | RecordBucket *src = buckets_[0].bucket; 79 | RecordBucket *dst = new RecordBucket(); 80 | 81 | size_t n = src->size() / 2; 82 | dst->resize(src->size() - n); 83 | std::copy(src->begin() + n, src->end(), dst->begin()); 84 | src->resize(n); 85 | 86 | buckets_[0].length = 4; 87 | for (size_t i = 0; i < src->size(); i++) { 88 | buckets_[0].length += (*src)[i].size(); 89 | } 90 | length_ = buckets_[0].length; 91 | size_ = src->size(); 92 | 93 | other.set_buckets_number(1); 94 | other.set_bucket(0, dst); 95 | } else { 96 | // divide buckets 97 | size_t n = buckets_.size() / 2; 98 | other.set_buckets_number(buckets_.size() - n); 99 | for (size_t i = n; i < buckets_.size(); i++ ) { 100 | other.set_bucket(i-n, buckets_[i].bucket); 101 | } 102 | buckets_.resize(n); 103 | 104 | length_ -= other.length(); 105 | size_ -= other.size(); 106 | } 107 | 108 | assert(other.buckets_number()); 109 | RecordBucket *bucket = other.bucket(0); 110 | assert(bucket->size()); 111 | return bucket->front().key; 112 | } -------------------------------------------------------------------------------- /src/tree/record.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_TREE_RECORD_H_ 6 | #define CASCADB_TREE_RECORD_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "cascadb/slice.h" 13 | #include "serialize/block.h" 14 | 15 | namespace cascadb { 16 | 17 | // Data is stored as Record inside leaf node 18 | class Record { 19 | public: 20 | Record() {} 21 | Record(Slice k, Slice v) : key(k), value(v) {} 22 | 23 | size_t size(); 24 | bool read_from(BlockReader& reader); 25 | bool write_to(BlockWriter& writer); 26 | 27 | Slice key; 28 | Slice value; 29 | }; 30 | 31 | typedef std::vector RecordBucket; 32 | 33 | // Records're arranged into multiple buckets inside a single leaf node, 34 | // in CascaDB nodes're configured big enough to accelerate write speed, 35 | // and allow each bucket be read, decompressed, and deserialized individually, 36 | // so that point query can be faster and much more efficient. 37 | // Length of each bucket can be configured in Options::leaf_node_bucket_size. 38 | class RecordBuckets { 39 | public: 40 | class Iterator { 41 | public: 42 | Iterator(RecordBuckets* container) 43 | : container_(container), 44 | bucket_idx_(0), 45 | record_idx_(0) 46 | { 47 | } 48 | 49 | bool valid() 50 | { 51 | if (bucket_idx_ == container_->buckets_number()) { 52 | return false; 53 | } 54 | return true; 55 | } 56 | 57 | void next() 58 | { 59 | RecordBucket* bucket = container_->bucket(bucket_idx_); 60 | assert(bucket); 61 | 62 | record_idx_ ++; 63 | if (record_idx_ == bucket->size()) { 64 | record_idx_ = 0; 65 | bucket_idx_ ++; 66 | } 67 | } 68 | 69 | Record& record() { 70 | RecordBucket* bucket = container_->bucket(bucket_idx_); 71 | assert(bucket && record_idx_ < bucket->size()); 72 | return (*bucket)[record_idx_]; 73 | } 74 | 75 | private: 76 | RecordBuckets *container_; 77 | size_t bucket_idx_; 78 | size_t record_idx_; 79 | }; 80 | 81 | RecordBuckets(size_t max_bucket_length) 82 | : max_bucket_length_(max_bucket_length), 83 | last_bucket_length_(0), 84 | length_(0), 85 | size_(0) 86 | { 87 | } 88 | 89 | ~RecordBuckets(); 90 | 91 | size_t buckets_number() 92 | { 93 | return buckets_.size(); 94 | } 95 | 96 | void set_buckets_number(size_t buckets_number) 97 | { 98 | assert(buckets_.size() == 0); 99 | buckets_.resize(buckets_number); 100 | } 101 | 102 | RecordBucket* bucket(size_t index) 103 | { 104 | assert(index < buckets_.size()); 105 | return buckets_[index].bucket; 106 | } 107 | 108 | size_t bucket_length(size_t index) 109 | { 110 | assert(index < buckets_.size()); 111 | return buckets_[index].length; 112 | } 113 | 114 | void set_bucket(size_t index, RecordBucket* bucket) 115 | { 116 | assert(index < buckets_.size()); 117 | 118 | assert(buckets_[index].bucket == NULL); 119 | buckets_[index].bucket = bucket; 120 | 121 | buckets_[index].length = 4; 122 | for (size_t i = 0; i < bucket->size(); i++) { 123 | buckets_[index].length += (*bucket)[i].size(); 124 | } 125 | 126 | length_ += buckets_[index].length; 127 | size_ += buckets_[index].bucket->size(); 128 | } 129 | 130 | Iterator get_iterator() { return Iterator(this); } 131 | 132 | void push_back(Record record); 133 | 134 | inline size_t length() { return length_; } 135 | 136 | inline size_t size() { return size_; } 137 | 138 | // for writing test purpose only 139 | inline Record& operator[](size_t idx) { 140 | assert(idx < size_); 141 | for (size_t i = 0; i < buckets_.size(); i++ ) { 142 | RecordBucket *bucket = buckets_[i].bucket; 143 | if (idx < bucket->size()) { 144 | return (*bucket)[idx]; 145 | } 146 | idx -= buckets_[i].bucket->size(); 147 | } 148 | throw std::runtime_error("access record buckets, bad idx"); 149 | } 150 | 151 | void swap(RecordBuckets &other); 152 | 153 | Slice split(RecordBuckets &other); 154 | 155 | private: 156 | size_t max_bucket_length_; 157 | 158 | struct RecordBucketInfo { 159 | RecordBucket *bucket; 160 | size_t length; 161 | }; 162 | 163 | std::vector buckets_; 164 | 165 | size_t last_bucket_length_; 166 | 167 | size_t length_; 168 | 169 | size_t size_; 170 | }; 171 | 172 | } 173 | 174 | #endif -------------------------------------------------------------------------------- /src/tree/tree.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #include 6 | 7 | #include "util/logger.h" 8 | #include "tree.h" 9 | 10 | using namespace std; 11 | using namespace cascadb; 12 | 13 | Tree::~Tree() 14 | { 15 | if (root_) { 16 | root_->dec_ref(); 17 | } 18 | 19 | if (schema_) { 20 | schema_->dec_ref(); 21 | } 22 | 23 | cache_->del_table(table_name_); 24 | 25 | delete node_factory_; 26 | 27 | delete compressor_; 28 | } 29 | 30 | bool Tree::init() 31 | { 32 | if(options_.comparator == NULL) { 33 | LOG_ERROR("no comparator set in options"); 34 | return false; 35 | } 36 | 37 | switch(options_.compress) { 38 | case kNoCompress: 39 | break; 40 | case kSnappyCompress: 41 | compressor_ = new SnappyCompressor(); 42 | break; 43 | } 44 | 45 | node_factory_ = new TreeNodeFactory(this); 46 | if (!cache_->add_table(table_name_, node_factory_, layout_)) { 47 | LOG_ERROR("init table in cache error"); 48 | return false; 49 | } 50 | 51 | schema_ = (SchemaNode*) cache_->get(table_name_, NID_SCHEMA, false); 52 | if (schema_ == NULL) { 53 | LOG_INFO("schema node doesn't exist, init empty db"); 54 | schema_ = new SchemaNode(table_name_); 55 | schema_->root_node_id = NID_NIL; 56 | schema_->next_inner_node_id = NID_START; 57 | schema_->next_leaf_node_id = NID_LEAF_START; 58 | schema_->tree_depth = 2; 59 | schema_->set_dirty(true); 60 | cache_->put(table_name_, NID_SCHEMA, schema_); 61 | } 62 | 63 | if (schema_->root_node_id == NID_NIL) { 64 | LOG_INFO("root node doesn't exist, init empty"); 65 | root_ = new_inner_node(); 66 | root_->init_empty_root(); 67 | 68 | schema_->write_lock(); 69 | schema_->root_node_id = root_->nid(); 70 | schema_->set_dirty(true); 71 | schema_->unlock(); 72 | } else { 73 | LOG_INFO("load root node nid " << hex << schema_->root_node_id << dec); 74 | root_ = (InnerNode*)load_node(schema_->root_node_id, false); 75 | } 76 | 77 | assert(root_); 78 | return true; 79 | } 80 | 81 | bool Tree::put(Slice key, Slice value) 82 | { 83 | assert(root_); 84 | InnerNode *root = root_; 85 | root->inc_ref(); 86 | bool ret = root->put(key, value); 87 | root->dec_ref(); 88 | return ret; 89 | } 90 | 91 | bool Tree::del(Slice key) 92 | { 93 | assert(root_); 94 | InnerNode *root = root_; 95 | root->inc_ref(); 96 | bool ret = root->del(key); 97 | root->dec_ref(); 98 | return ret; 99 | } 100 | 101 | bool Tree::get(Slice key, Slice& value) 102 | { 103 | assert(root_); 104 | InnerNode *root = root_; 105 | root->inc_ref(); 106 | bool ret = root->find(key, value, NULL); 107 | root->dec_ref(); 108 | return ret; 109 | } 110 | 111 | InnerNode* Tree::new_inner_node() 112 | { 113 | schema_->write_lock(); 114 | bid_t nid = schema_->next_inner_node_id ++; 115 | schema_->set_dirty(true); 116 | schema_->unlock(); 117 | 118 | InnerNode* node = (InnerNode *)node_factory_->new_node(nid); 119 | assert(node); 120 | 121 | cache_->put(table_name_, nid, node); 122 | return node; 123 | } 124 | 125 | LeafNode* Tree::new_leaf_node() 126 | { 127 | schema_->write_lock(); 128 | bid_t nid = schema_->next_leaf_node_id ++; 129 | schema_->set_dirty(true); 130 | schema_->unlock(); 131 | 132 | LeafNode* node = (LeafNode *)node_factory_->new_node(nid); 133 | assert(node); 134 | 135 | cache_->put(table_name_, nid, node); 136 | return node; 137 | } 138 | 139 | DataNode* Tree::load_node(bid_t nid, bool skeleton_only) 140 | { 141 | assert(nid != NID_NIL && nid != NID_SCHEMA); 142 | return (DataNode*) cache_->get(table_name_, nid, skeleton_only); 143 | } 144 | 145 | void Tree::pileup(InnerNode *root) 146 | { 147 | assert(root_ != root); 148 | root_->dec_ref(); 149 | root_ = root; 150 | 151 | schema_->write_lock(); 152 | schema_->root_node_id = root_->nid(); 153 | schema_->tree_depth ++; 154 | schema_->set_dirty(true); 155 | schema_->unlock(); 156 | } 157 | 158 | void Tree::collapse() 159 | { 160 | root_->dec_ref(); 161 | 162 | root_ = new_inner_node(); 163 | root_->init_empty_root(); 164 | assert(root_); 165 | 166 | schema_->write_lock(); 167 | schema_->root_node_id = root_->nid(); 168 | schema_->tree_depth = 2; 169 | schema_->set_dirty(true); 170 | schema_->unlock(); 171 | } 172 | 173 | void Tree::lock_path(Slice key, std::vector& path) 174 | { 175 | assert(root_); 176 | InnerNode *root = root_; 177 | root->inc_ref(); 178 | root->write_lock(); 179 | path.push_back(root); 180 | root->lock_path(key, path); 181 | } 182 | 183 | Tree::TreeNodeFactory::TreeNodeFactory(Tree *tree) 184 | : tree_(tree) 185 | { 186 | } 187 | 188 | Node* Tree::TreeNodeFactory::new_node(bid_t nid) 189 | { 190 | if (nid == NID_SCHEMA) { 191 | return new SchemaNode(tree_->table_name_); 192 | } else { 193 | DataNode *node; 194 | if (nid >= NID_LEAF_START) { 195 | node = new LeafNode(tree_->table_name_, nid, tree_); 196 | } else { 197 | node = new InnerNode(tree_->table_name_, nid, tree_); 198 | } 199 | return node; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/tree/tree.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_TREE_H_ 6 | #define CASCADB_TREE_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "cascadb/slice.h" 13 | #include "cascadb/comparator.h" 14 | #include "cascadb/options.h" 15 | #include "sys/sys.h" 16 | #include "cache/cache.h" 17 | #include "util/compressor.h" 18 | #include "node.h" 19 | 20 | namespace cascadb { 21 | 22 | // Buffered B-Tree's roughly a B+-Tree except there is a buffer 23 | // at each inner node. 24 | // A write operation reaches the buffer at root node first and 25 | // returns immediately, and sometimes later when the buffer at root 26 | // becomes full, it will be then flushed to the buffers among children. 27 | // This process is repeated on an on, and write operations buffered 28 | // inside a inner node always cascade to children when buffer become full, 29 | // so finally the write arrives to leaf. 30 | // Writes at Buffered B-Tree is optimized because flushing a buffer 31 | // from parent to children is a batch write, that is to say, multiple 32 | // buffered writes can be completed in a single disk write, 33 | // it canbe proved writes in Buffered B-Tree is 10x-100x faster than 34 | // trival B-Tree. 35 | // Structure Modification Operations(SMO) like node split and merge 36 | // are similar to tranditional B+-Tree implementation. 37 | 38 | class Tree { 39 | public: 40 | Tree(const std::string& table_name, 41 | const Options& options, 42 | Cache *cache, 43 | Layout *layout) 44 | : table_name_(table_name), 45 | options_(options), 46 | cache_(cache), 47 | layout_(layout), 48 | node_factory_(NULL), 49 | compressor_(NULL), 50 | schema_(NULL), 51 | root_(NULL) 52 | { 53 | } 54 | 55 | ~Tree(); 56 | 57 | bool init(); 58 | 59 | bool put(Slice key, Slice value); 60 | 61 | bool del(Slice key); 62 | 63 | bool get(Slice key, Slice& value); 64 | 65 | private: 66 | friend class InnerNode; 67 | friend class LeafNode; 68 | 69 | InnerNode* new_inner_node(); 70 | 71 | LeafNode* new_leaf_node(); 72 | 73 | DataNode* load_node(bid_t nid, bool skeleton_only); 74 | 75 | InnerNode* root() { return root_; } 76 | 77 | void pileup(InnerNode *root); 78 | 79 | void collapse(); 80 | 81 | void lock_path(Slice key, std::vector& path); 82 | 83 | class TreeNodeFactory : public NodeFactory { 84 | public: 85 | TreeNodeFactory(Tree *tree); 86 | Node* new_node(bid_t nid); 87 | private: 88 | Tree *tree_; 89 | }; 90 | 91 | std::string table_name_; 92 | 93 | Options options_; 94 | 95 | Cache *cache_; 96 | 97 | Layout *layout_; 98 | 99 | TreeNodeFactory *node_factory_; 100 | 101 | Compressor *compressor_; 102 | 103 | SchemaNode *schema_; 104 | 105 | InnerNode *root_; 106 | }; 107 | 108 | } 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /src/util/any.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_UTIL_ANY_H_ 6 | #define CASCADB_UTIL_ANY_H_ 7 | 8 | namespace cascadb { 9 | 10 | class Any { 11 | public: 12 | virtual ~Any() {} 13 | }; 14 | 15 | } 16 | 17 | #endif -------------------------------------------------------------------------------- /src/util/bits.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_UTIL_BITS_H_ 6 | #define CASCADB_UTIL_BITS_H_ 7 | 8 | #define ROUND_UP(x, n) ((((x)+ (n) - 1) / (n)) * (n)) 9 | #define ROUND_DOWN(x, n) (((x) / (n)) * (n)) 10 | 11 | #define PAGE_SIZE 4096 12 | #define PAGE_ROUND_UP(x) (((x) + PAGE_SIZE-1) & (~(PAGE_SIZE-1))) 13 | #define PAGE_ROUND_DOWN(x) ((x) & (~(PAGE_SIZE-1))) 14 | #define PAGE_ROUNDED(x) ((x) == PAGE_ROUND_DOWN(x) ) 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/util/bloom.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | // leveldb, please bear with me ^_^ 5 | 6 | #include "bloom.h" 7 | 8 | #define BIT_PER_KEY (12) 9 | #define K (BIT_PER_KEY * 0.69) //0.69 =~ ln(2) 10 | 11 | using namespace cascadb; 12 | 13 | static inline uint32_t _hash(const char *key, size_t n, uint32_t seed) 14 | { 15 | size_t i = 0; 16 | uint32_t h = seed; 17 | 18 | while (i < n) 19 | h = ((h<< 5) + h) + (uint32_t)key[i++]; 20 | return h; 21 | } 22 | 23 | size_t cascadb::bloom_size(int n) 24 | { 25 | size_t bits; 26 | size_t bytes; 27 | 28 | bits = BIT_PER_KEY * n; 29 | 30 | if (bits < 64) bits = 64; 31 | bytes = (bits + 7) / 8; 32 | 33 | // probes in filter 34 | bytes += 1; 35 | 36 | return bytes; 37 | } 38 | 39 | void cascadb::bloom_create(const Slice* keys, int n, std::string* bitsets) 40 | { 41 | size_t bits; 42 | size_t bytes; 43 | size_t init_size; 44 | char* array; 45 | 46 | bits = BIT_PER_KEY * n; 47 | 48 | if (bits < 64) bits = 64; 49 | bytes = (bits + 7) / 8; 50 | bits = bytes * 8; 51 | 52 | init_size = bitsets->size(); 53 | bitsets->resize(init_size + bytes, 0); 54 | 55 | // remember # of probes in filter 56 | bitsets->push_back(static_cast(K)); 57 | array = &(*bitsets)[init_size]; 58 | 59 | for (size_t i = 0; i < (size_t)n; i++) { 60 | uint32_t h; 61 | uint32_t delta; 62 | uint32_t bitpos; 63 | 64 | h = _hash(keys[i].data(), keys[i].size(), 0xbc9f1d34); 65 | 66 | // rotate right 17 bits 67 | delta = (h >> 17) | (h << 15); 68 | for (size_t j = 0; j < K; j++) { 69 | bitpos = h % bits; 70 | array[bitpos / 8] |= (1 << (bitpos % 8)); 71 | h += delta; 72 | } 73 | } 74 | } 75 | 76 | bool cascadb::bloom_matches(const Slice& key, const Slice& filter) 77 | { 78 | size_t k; 79 | size_t len; 80 | size_t bits; 81 | 82 | uint32_t h; 83 | uint32_t delta; 84 | uint32_t bitpos; 85 | 86 | const char* array; 87 | 88 | len = filter.size(); 89 | if (len < 2) return false; 90 | 91 | array = filter.data(); 92 | bits = (len - 1) * 8; 93 | 94 | h = _hash(key.data(), key.size(), 0xbc9f1d34); 95 | // rotate right 17 bits 96 | delta = (h >> 17) | (h << 15); 97 | 98 | k = array[len - 1]; 99 | for (size_t j = 0; j < k; j++) { 100 | bitpos = h % bits; 101 | if ((array[bitpos / 8] & (1 << (bitpos % 8))) == 0) return false; 102 | h += delta; 103 | } 104 | return true; 105 | } 106 | -------------------------------------------------------------------------------- /src/util/bloom.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_UTIL_BLOOM_H_ 6 | #define CASCADB_UTIL_BLOOM_H_ 7 | 8 | 9 | #include "cascadb/slice.h" 10 | 11 | namespace cascadb { 12 | size_t bloom_size(int n); 13 | void bloom_create(const Slice* keys, int n, std::string* bitsets); 14 | bool bloom_matches(const Slice& k, const Slice& filter); 15 | } 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/util/callback.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_UTIL_CALLBACK_H_ 6 | #define CASCADB_UTIL_CALLBACK_H_ 7 | 8 | #include 9 | #include 10 | #include "any.h" 11 | 12 | namespace cascadb { 13 | 14 | template 15 | class CallbackBase : public Any { 16 | public: 17 | virtual ~CallbackBase() {} 18 | virtual void exec(A) = 0; 19 | }; 20 | 21 | template 22 | class Bind : public CallbackBase { 23 | public: 24 | typedef void (T::*F)(C, A); 25 | 26 | Bind(T *target, F function, C context) 27 | : target_(target), function_(function), context_(context) 28 | { 29 | } 30 | 31 | void exec(A arg) { 32 | assert(target_); 33 | (target_->*function_)(context_, arg); 34 | } 35 | 36 | private: 37 | T *target_; 38 | F function_; 39 | C context_; 40 | }; 41 | 42 | class Callback { 43 | public: 44 | template 45 | Callback(T *target, void (T::*function)(C, A), C context) { 46 | base_ = new Bind(target, function, context); 47 | } 48 | 49 | ~Callback() { 50 | delete base_; 51 | } 52 | 53 | template 54 | void exec(A arg) { 55 | CallbackBase *cbp = (CallbackBase *) base_; 56 | cbp->exec(arg); 57 | } 58 | 59 | private: 60 | Any *base_; 61 | }; 62 | 63 | } 64 | 65 | #endif -------------------------------------------------------------------------------- /src/util/compressor.cpp: -------------------------------------------------------------------------------- 1 | #include "logger.h" 2 | #include "compressor.h" 3 | 4 | using namespace std; 5 | using namespace cascadb; 6 | 7 | #ifdef HAS_SNAPPY 8 | #include 9 | #endif 10 | 11 | size_t SnappyCompressor::max_compressed_length(size_t size) 12 | { 13 | #ifdef HAS_SNAPPY 14 | return snappy::MaxCompressedLength(size); 15 | #else 16 | return 0; 17 | #endif 18 | } 19 | 20 | bool SnappyCompressor::compress(const char *buf, size_t size, char *obuf, size_t *sp) 21 | { 22 | #ifdef HAS_SNAPPY 23 | snappy::RawCompress(buf, size, obuf, sp); 24 | return true; 25 | #else 26 | return false; 27 | #endif 28 | } 29 | 30 | bool SnappyCompressor::uncompress(const char *buf, size_t size, char *obuf) 31 | { 32 | #ifdef HAS_SNAPPY 33 | if (!snappy::RawUncompress(buf, size, obuf)) { 34 | LOG_ERROR("snappy uncompress error"); 35 | return false; 36 | } 37 | return true; 38 | #else 39 | return false; 40 | #endif 41 | } 42 | -------------------------------------------------------------------------------- /src/util/compressor.h: -------------------------------------------------------------------------------- 1 | #ifndef CASCADB_UTIL_COMPRESSOR_H_ 2 | #define CASCADB_UTIL_COMPRESSOR_H_ 3 | 4 | namespace cascadb { 5 | 6 | // Interface of data compression and decompression. 7 | class Compressor { 8 | public: 9 | virtual ~Compressor() {} 10 | 11 | virtual size_t max_compressed_length(size_t size) = 0; 12 | 13 | // obuf should be larger than max_compressed_length(size) 14 | virtual bool compress(const char *buf, size_t size, char *obuf, size_t *sp) = 0; 15 | 16 | // obuf should be larger than uncompressed length 17 | virtual bool uncompress(const char *buf, size_t size, char *obuf) = 0; 18 | }; 19 | 20 | class SnappyCompressor : public Compressor { 21 | public: 22 | size_t max_compressed_length(size_t size); 23 | 24 | bool compress(const char *buf, size_t size, char *obuf, size_t *sp); 25 | 26 | // obuf should be larger than uncompressed length 27 | bool uncompress(const char *buf, size_t size, char *obuf); 28 | }; 29 | 30 | } 31 | 32 | #endif -------------------------------------------------------------------------------- /src/util/crc16.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2001-2010 Georges Menie (www.menie.org) 2 | // Copyright (c) 2013 The CascaDB Authors(Adapted to CascaDB coding style). All rights reserved. 3 | // Use of this source code is governed by a BSD-style license that can be 4 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 5 | 6 | #include "crc16.h" 7 | 8 | using namespace cascadb; 9 | 10 | // CRC16 implementation acording to CCITT standards 11 | static const uint16_t crc16table_[256]= { 12 | 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, 13 | 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, 14 | 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, 15 | 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, 16 | 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, 17 | 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, 18 | 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, 19 | 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, 20 | 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, 21 | 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, 22 | 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, 23 | 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, 24 | 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, 25 | 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, 26 | 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, 27 | 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, 28 | 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, 29 | 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, 30 | 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, 31 | 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, 32 | 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, 33 | 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, 34 | 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, 35 | 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, 36 | 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, 37 | 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, 38 | 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, 39 | 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, 40 | 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, 41 | 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, 42 | 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, 43 | 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 44 | }; 45 | 46 | uint16_t cascadb::crc16(const char *buf, uint32_t n) 47 | { 48 | register uint32_t counter; 49 | register uint16_t crc = 0; 50 | for( counter = 0; counter < n; counter++) 51 | crc = (crc<<8) ^ crc16table_[((crc>>8) ^ *(char *)buf++)&0x00FF]; 52 | return crc; 53 | } 54 | -------------------------------------------------------------------------------- /src/util/crc16.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_UTIL_CRC16_H_ 6 | #define CASCADB_UTIL_CRC16_H_ 7 | 8 | #include 9 | #include 10 | 11 | namespace cascadb { 12 | uint16_t crc16(const char *buf, uint32_t n); 13 | } 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/util/logger.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #include "logger.h" 6 | 7 | using namespace std; 8 | using namespace cascadb; 9 | 10 | LoggerLevel cascadb::g_logger_level = kInfo; 11 | 12 | // set default logger 13 | static ConsoleLogger console_logger; 14 | Logger *cascadb::g_logger = &console_logger; 15 | 16 | void cascadb::init_logger(LoggerLevel level) 17 | { 18 | g_logger_level = level; 19 | } 20 | 21 | void cascadb::init_logger(const std::string &path, LoggerLevel level) 22 | { 23 | g_logger_level = level; 24 | 25 | g_logger = new FileLogger(path); 26 | assert(g_logger); 27 | } -------------------------------------------------------------------------------- /src/util/logger.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The CascaDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef CASCADB_UTIL_LOGGER_H_ 6 | #define CASCADB_UTIL_LOGGER_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "sys/sys.h" 17 | 18 | namespace cascadb { 19 | 20 | enum LoggerLevel { 21 | kTrace, 22 | kInfo, 23 | kWarn, 24 | kError, 25 | kFatal 26 | }; 27 | 28 | static const char* logger_level_names[] = { 29 | "TRACE", 30 | "INFO", 31 | "WARN", 32 | "ERROR", 33 | "FATAL" 34 | }; 35 | 36 | class Logger { 37 | public: 38 | virtual ~Logger() {} 39 | 40 | virtual void write(const std::string &line) { 41 | fprintf(file_, "%s\n", line.c_str()); 42 | fflush(file_); 43 | } 44 | protected: 45 | FILE *file_; 46 | }; 47 | 48 | class ConsoleLogger : public Logger { 49 | public: 50 | ConsoleLogger() 51 | { 52 | file_ = stderr; 53 | } 54 | }; 55 | 56 | class FileLogger : public Logger { 57 | public: 58 | FileLogger(const std::string& path) 59 | { 60 | file_ = fopen(path.c_str(), "a+"); 61 | if (!file_) { 62 | fprintf(stderr, "cannot open logger %s\n", path.c_str()); 63 | exit(1); 64 | } 65 | } 66 | 67 | ~FileLogger() { 68 | fclose(file_); 69 | } 70 | }; 71 | 72 | class LoggerFormat { 73 | public: 74 | LoggerFormat(Logger* logger) 75 | : logger_(logger) 76 | { 77 | } 78 | 79 | ~LoggerFormat() { 80 | logger_->write(os_.str()); 81 | } 82 | 83 | std::ostream& get(LoggerLevel level) { 84 | os_ << "| " << now() << " | " << logger_level_names[level]; 85 | return os_; 86 | } 87 | 88 | private: 89 | Logger *logger_; 90 | std::ostringstream os_; 91 | }; 92 | 93 | extern LoggerLevel g_logger_level; 94 | extern Logger *g_logger; 95 | 96 | extern void init_logger(LoggerLevel level); 97 | 98 | extern void init_logger(const std::string &path, LoggerLevel level); 99 | 100 | #define SHORT_FILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) 101 | 102 | #define LOG(level, msg) \ 103 | if (level >= g_logger_level) {\ 104 | LoggerFormat(g_logger).get(level) << " | " << SHORT_FILE << ":" << __LINE__ << " | " << msg;\ 105 | } 106 | 107 | #define LOG_TRACE(msg) LOG(kTrace, msg) 108 | #define LOG_INFO(msg) LOG(kInfo, msg) 109 | #define LOG_WARN(msg) LOG(kWarn, msg) 110 | #define LOG_ERROR(msg) LOG(kError, msg) 111 | #define LOG_FATAL(msg) LOG(kFatal, msg) 112 | 113 | } 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file( GLOB_RECURSE SOURCE RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" 2 | "*.cpp" 3 | ) 4 | include_directories( 5 | ${PROJECT_SOURCE_DIR}/thirdparty/gtest/include 6 | ${PROJECT_SOURCE_DIR}/test 7 | ) 8 | set(GTEST_LIBS 9 | gtest_main 10 | gtest) 11 | add_executable( 12 | t_cascadb 13 | ${SOURCE}) 14 | target_link_libraries( 15 | t_cascadb 16 | cascadbStatic 17 | ${CASCADB_LIBS} 18 | ${GTEST_LIBS}) 19 | -------------------------------------------------------------------------------- /test/directory_test.cpp: -------------------------------------------------------------------------------- 1 | #include "directory_test.h" 2 | 3 | Mutex AIOFileTest::mtx; 4 | map AIOFileTest::result; 5 | -------------------------------------------------------------------------------- /test/directory_test.h: -------------------------------------------------------------------------------- 1 | #ifndef CASCADB_TEST_DIRECTORY_TEST_H_ 2 | #define CASCADB_TEST_DIRECTORY_TEST_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "cascadb/directory.h" 9 | #include "sys/sys.h" 10 | 11 | using namespace cascadb; 12 | using namespace std; 13 | 14 | class SequenceFileTest : public testing::Test { 15 | public: 16 | virtual ~SequenceFileTest() { 17 | delete dir; 18 | } 19 | 20 | virtual void SetUp() { 21 | dir->delete_file("sequence_file_test"); 22 | } 23 | 24 | virtual void TearDown() { 25 | dir->delete_file("sequence_file_test"); 26 | } 27 | 28 | void TestReadAndWrite() { 29 | char buf[4096]; 30 | 31 | SequenceFileWriter* writer = dir->open_sequence_file_writer("sequence_file_test"); 32 | for(int i = 0; i < 1000; i ++) { 33 | memset(buf, i&0xFF, 4096); 34 | EXPECT_TRUE(writer->append(Slice(buf, 4096))); 35 | } 36 | delete writer; 37 | 38 | SequenceFileReader* reader = dir->open_sequence_file_reader("sequence_file_test"); 39 | for (int i = 0; i < 1000; i++ ) { 40 | EXPECT_EQ(4096U, reader->read(Slice(buf, 4096))); 41 | char shouldbe[4096]; 42 | memset(shouldbe, i&0xFF, 4096); 43 | EXPECT_TRUE(memcmp(buf, shouldbe, 4096) == 0); 44 | } 45 | delete reader; 46 | } 47 | 48 | Directory *dir; 49 | }; 50 | 51 | 52 | class AIOFileTest : public testing::Test { 53 | public: 54 | virtual ~AIOFileTest() { 55 | delete dir; 56 | } 57 | 58 | virtual void SetUp() 59 | { 60 | dir->delete_file("aio_file_test"); 61 | file = dir->open_aio_file("aio_file_test"); 62 | } 63 | 64 | virtual void TearDown() 65 | { 66 | delete file; 67 | dir->delete_file("aio_file_test"); 68 | } 69 | 70 | void TestBlockingReadAndWrite() 71 | { 72 | Slice buf = allocbuf(4096); 73 | 74 | for(int i = 0; i < 1000; i ++) { 75 | memset((void*)buf.data(), i&0xFF, 4096); 76 | AIOStatus status = file->write(i*4096, buf); 77 | EXPECT_TRUE(status.succ); 78 | } 79 | 80 | for (int i = 0; i < 1000; i++ ) { 81 | AIOStatus status = file->read(i*4096, buf); 82 | EXPECT_TRUE(status.succ); 83 | 84 | char shouldbe[4096]; 85 | memset(shouldbe, i&0xFF, 4096); 86 | EXPECT_TRUE(memcmp(buf.data(), shouldbe, 4096) == 0); 87 | } 88 | 89 | buf.destroy(); 90 | } 91 | 92 | void TestReadAndWrite() 93 | { 94 | int id[1000]; 95 | Slice buf[1000]; 96 | 97 | for (int i = 0; i < 1000; i++ ) { 98 | id[i] = i; 99 | buf[i] = allocbuf(4096); 100 | memset((void*)buf[i].data(), i&0xFF, 4096); 101 | file->async_write(i * 4096, buf[i], id + i, io_complete); 102 | } 103 | 104 | while(result.size() != 1000) cascadb::usleep(10000); // 10ms 105 | for (int i = 0; i < 1000; i++ ) { 106 | EXPECT_EQ(true, result[i].succ); 107 | buf[i].destroy(); 108 | } 109 | result.clear(); 110 | 111 | for (int i = 0; i < 1000; i++ ) { 112 | id[i] = i; 113 | buf[i] = allocbuf(4096); 114 | file->async_read(i * 4096, buf[i], id + i, io_complete); 115 | } 116 | 117 | while(result.size() != 1000) cascadb::usleep(10000); // 10ms 118 | for (int i = 0; i < 1000; i++ ) { 119 | EXPECT_EQ(true, result[i].succ); 120 | EXPECT_EQ(4096U, result[i].read); 121 | char shouldbe[4096]; 122 | memset(shouldbe, i&0xFF, 4096); 123 | EXPECT_TRUE(memcmp(buf[i].data(), shouldbe, 4096) == 0); 124 | buf[i].destroy(); 125 | } 126 | } 127 | 128 | void TestReadPartial() 129 | { 130 | int id = 0; 131 | Slice buf = allocbuf(4096); 132 | memset((void*)buf.data(), 0, 4096); 133 | file->async_write(0, buf, &id, io_complete); 134 | 135 | cascadb::usleep(100000); // wait 100 ms 136 | EXPECT_EQ(true, result[0].succ); 137 | buf.destroy(); 138 | result.clear(); 139 | 140 | buf = allocbuf(8192); 141 | file->async_read(0, buf, &id, io_complete); 142 | 143 | cascadb::usleep(100000); // wait 100 ms 144 | EXPECT_EQ(true, result[0].succ); 145 | EXPECT_EQ(4096U, result[0].read); 146 | buf.destroy(); 147 | } 148 | 149 | Slice allocbuf(size_t size) 150 | { 151 | void *ptr = memalign(4096, size); 152 | return Slice((char*)ptr, size); 153 | } 154 | 155 | static void io_complete(void* context, AIOStatus status) 156 | { 157 | ScopedMutex lock(&mtx); 158 | result[*((int*)context)] = status; 159 | } 160 | 161 | Directory *dir; 162 | AIOFile* file; 163 | static Mutex mtx; 164 | static map result; 165 | }; 166 | 167 | 168 | 169 | #endif -------------------------------------------------------------------------------- /test/helper.h: -------------------------------------------------------------------------------- 1 | #ifndef CASCADB_TEST_HELPER_H_ 2 | #define CASCADB_TEST_HELPER_H_ 3 | 4 | #define PUT(mb, k, v) \ 5 | {\ 6 | (mb).write(Msg(Put, Slice(k).clone(), Slice(v).clone()));\ 7 | } 8 | 9 | #define DEL(mb, k) \ 10 | {\ 11 | (mb).write(Msg(Del, Slice(k).clone()));\ 12 | } 13 | 14 | #define CHK_MSG(m, t, k, v) \ 15 | EXPECT_EQ(t, (m).type);\ 16 | EXPECT_EQ(k, (m).key);\ 17 | EXPECT_EQ(v, (m).value); 18 | 19 | 20 | #define CHK_REC(r, k, v)\ 21 | EXPECT_EQ(k, (r).key);\ 22 | EXPECT_EQ(v, (r).value); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /test/t_block.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "serialize/block.h" 3 | 4 | using namespace cascadb; 5 | using namespace std; 6 | 7 | TEST(Block, searialize) 8 | { 9 | char buffer[4096]; 10 | Block blk(Slice(buffer, 4096), 0, 0); 11 | 12 | BlockWriter bw(&blk); 13 | ASSERT_TRUE(bw.writeUInt8(1)); 14 | ASSERT_TRUE(bw.writeUInt16(12345)); 15 | ASSERT_TRUE(bw.writeUInt32(123456789)); 16 | ASSERT_TRUE(bw.writeUInt64(123456789000000)); 17 | Slice s1("abcdefg"); 18 | Slice s2("hijklmn"); 19 | ASSERT_TRUE(bw.writeSlice(s1)); 20 | EXPECT_EQ("abcdefg", s1); 21 | ASSERT_TRUE(bw.writeSlice(s2)); 22 | EXPECT_EQ("hijklmn",s2); 23 | 24 | BlockReader br(&blk); 25 | uint8_t a; 26 | uint16_t b; 27 | uint32_t c; 28 | uint64_t d; 29 | Slice e; 30 | Slice f; 31 | ASSERT_TRUE(br.readUInt8(&a)); 32 | ASSERT_TRUE(br.readUInt16(&b)); 33 | ASSERT_TRUE(br.readUInt32(&c)); 34 | ASSERT_TRUE(br.readUInt64(&d)); 35 | ASSERT_TRUE(br.readSlice(e)); 36 | ASSERT_TRUE(br.readSlice(f)); 37 | EXPECT_EQ(a, 1U); 38 | EXPECT_EQ(b, 12345U); 39 | EXPECT_EQ(c, 123456789U); 40 | EXPECT_EQ(d, 123456789000000U); 41 | EXPECT_EQ(e, "abcdefg"); 42 | EXPECT_EQ(f, "hijklmn"); 43 | } 44 | 45 | TEST(Block, writer_overflow) 46 | { 47 | char buffer[4096]; 48 | Block blk(Slice(buffer, 4096), 0, 0); 49 | BlockWriter bw(&blk); 50 | 51 | bw.seek(4095); 52 | EXPECT_TRUE(bw.writeUInt8(1)); 53 | bw.seek(4096); 54 | EXPECT_FALSE(bw.writeUInt8(1)); 55 | 56 | char data[4092]; 57 | bw.seek(0); 58 | Slice s1(data, 4092); 59 | EXPECT_TRUE(bw.writeSlice(s1)); 60 | bw.seek(1); 61 | Slice s2(data, 4092); 62 | EXPECT_FALSE(bw.writeSlice(s2)); 63 | } 64 | 65 | TEST(Block, reader_overflow) 66 | { 67 | char buffer[4096]; 68 | Block blk(Slice(buffer, 4096), 0, 0); 69 | BlockReader br(&blk); 70 | BlockWriter bw(&blk); 71 | uint8_t n; 72 | 73 | EXPECT_TRUE(bw.writeUInt8(1)); 74 | EXPECT_TRUE(br.readUInt8(&n)); 75 | EXPECT_FALSE(br.readUInt8(&n)); 76 | EXPECT_TRUE(bw.writeUInt8(2)); 77 | EXPECT_TRUE(br.readUInt8(&n)); 78 | } 79 | -------------------------------------------------------------------------------- /test/t_bloom.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cascadb/slice.h" 3 | #include "util/bloom.h" 4 | 5 | using namespace std; 6 | using namespace cascadb; 7 | 8 | std::string filter_; 9 | std::vector keys_; 10 | 11 | static Slice build_key(int i, char *buffer) 12 | { 13 | memcpy(buffer, &i, sizeof(i)); 14 | return Slice(buffer, sizeof(uint32_t)); 15 | } 16 | 17 | void add(const Slice& s) 18 | { 19 | keys_.push_back(s.to_string()); 20 | } 21 | 22 | void reset() 23 | { 24 | keys_.clear(); 25 | filter_.clear(); 26 | } 27 | 28 | /* 29 | * build all keys bloom filter bitsets which in keys_ list 30 | */ 31 | void build() 32 | { 33 | std::vector key_slices; 34 | for (size_t i = 0; i < keys_.size(); i++) { 35 | key_slices.push_back(Slice(keys_[i])); 36 | } 37 | filter_.clear(); 38 | cascadb::bloom_create(&key_slices[0], key_slices.size(), &filter_); 39 | keys_.clear(); 40 | } 41 | 42 | /* 43 | * do match from the bloom filter bitsets 44 | */ 45 | bool matches(const Slice& s) 46 | { 47 | if (!keys_.empty()) build(); 48 | return cascadb::bloom_matches(s, filter_); 49 | } 50 | 51 | size_t filter_size(int n) 52 | { 53 | return cascadb::bloom_size(n); 54 | } 55 | 56 | double false_positive_rate() 57 | { 58 | char buffer[sizeof(int)]; 59 | int result = 0; 60 | for (int i = 0; i < 10000; i++) { 61 | if (matches(build_key(i + 1000000000, buffer))) { 62 | result++; 63 | } 64 | } 65 | return result / 10000.0; 66 | } 67 | 68 | static int nextlen(int length) 69 | { 70 | if (length < 10) { 71 | length += 1; 72 | } else if (length < 100) { 73 | length += 10; 74 | } else if (length < 1000) { 75 | length += 100; 76 | } else { 77 | length += 1000; 78 | } 79 | return length; 80 | } 81 | 82 | TEST(Bloom, emptyfilter) { 83 | ASSERT_TRUE(! matches("hello")); 84 | ASSERT_TRUE(! matches("world")); 85 | } 86 | 87 | TEST(Bloom, small) 88 | { 89 | add("hello"); 90 | add("world"); 91 | ASSERT_TRUE(matches("hello")); 92 | ASSERT_TRUE(matches("world")); 93 | ASSERT_TRUE(!matches("x")); 94 | ASSERT_TRUE(!matches("foo")); 95 | } 96 | 97 | TEST(Bloom, varying_lengths_and_check_postiverate) 98 | { 99 | double rate; 100 | char buffer[sizeof(int)]; 101 | int mediocre_filters = 0; 102 | int good_filters = 0; 103 | 104 | for (int length = 1; length <= 10000; length = nextlen(length)) { 105 | reset(); 106 | for (int i = 0; i < length; i++) { 107 | add(build_key(i, buffer)); 108 | } 109 | build(); 110 | 111 | ASSERT_EQ(filter_.size(), filter_size(length)); 112 | 113 | // all added keys must match 114 | for (int i = 0; i < length; i++) { 115 | ASSERT_TRUE(matches(build_key(i, buffer))) 116 | << "Length " << length << "; key " << i; 117 | } 118 | 119 | // check false positive rate 120 | rate = false_positive_rate(); 121 | fprintf(stderr, "False positives: %5.2f%% @ length = %6d ; bytes = %6d\n", 122 | rate*100.0, length, static_cast(filter_.size())); 123 | 124 | // must not be over 3.5%(leveldb is 2%, but its a hard hash 125 | ASSERT_LE(rate, 0.035); 126 | 127 | // allowed, but not too often 128 | if (rate > 0.0125) 129 | mediocre_filters++; 130 | else 131 | good_filters++; 132 | } 133 | fprintf(stderr, "Filters: %d good, %d mediocre\n", good_filters, mediocre_filters); 134 | 135 | ASSERT_LE(mediocre_filters, good_filters / 6); 136 | } 137 | -------------------------------------------------------------------------------- /test/t_cache.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "cascadb/options.h" 4 | #include "sys/sys.h" 5 | #include "store/ram_directory.h" 6 | #include "serialize/layout.h" 7 | #include "cache/cache.h" 8 | 9 | using namespace std; 10 | using namespace cascadb; 11 | 12 | class FakeNode : public Node { 13 | public: 14 | FakeNode(const std::string& table_name, bid_t nid) : Node(table_name, nid), data(0) {} 15 | 16 | bool cascade(MsgBuf *mb, InnerNode* parent) { return false; } 17 | 18 | bool find(Slice key, Slice& value, InnerNode* parent) { return false; } 19 | 20 | void lock_path(Slice key, std::vector& path) {} 21 | 22 | size_t size() 23 | { 24 | return 4096; 25 | } 26 | 27 | size_t estimated_buffer_size() 28 | { 29 | return size(); 30 | } 31 | 32 | bool read_from(BlockReader& reader, bool skeleton_only) { 33 | EXPECT_TRUE(reader.readUInt64(&data)); 34 | Slice s; 35 | EXPECT_TRUE(reader.readSlice(s)); 36 | return true; 37 | } 38 | 39 | bool write_to(BlockWriter& writer, size_t& skeleton_size) { 40 | EXPECT_TRUE(writer.writeUInt64(nid_)); 41 | char buf[4084] = {0}; 42 | Slice s(buf, sizeof(buf)); 43 | EXPECT_TRUE(writer.writeSlice(s)); 44 | skeleton_size = size(); 45 | return true; 46 | } 47 | 48 | uint64_t data; 49 | }; 50 | 51 | class FakeNodeFactory : public NodeFactory { 52 | public: 53 | FakeNodeFactory(const std::string& table_name) 54 | : table_name_(table_name) 55 | { 56 | } 57 | 58 | Node* new_node(bid_t nid) { 59 | return new FakeNode(table_name_, nid); 60 | } 61 | 62 | std::string table_name_; 63 | }; 64 | 65 | TEST(Cache, read_and_write) { 66 | Options opts; 67 | opts.cache_limit = 4096 * 1000; 68 | 69 | Directory *dir = new RAMDirectory(); 70 | AIOFile *file = dir->open_aio_file("cache_test"); 71 | Layout *layout = new Layout(file, 0, opts); 72 | layout->init(true); 73 | 74 | Cache *cache = new Cache(opts); 75 | cache->init(); 76 | 77 | NodeFactory *factory = new FakeNodeFactory("t1"); 78 | cache->add_table("t1", factory, layout); 79 | 80 | for (int i = 0; i < 1000; i++) { 81 | Node *node = new FakeNode("t1", i); 82 | node->set_dirty(true); 83 | cache->put("t1", i, node); 84 | node->dec_ref(); 85 | } 86 | 87 | // give it time to flush 88 | cascadb::sleep(5); 89 | // flush rest and clear nodes 90 | cache->del_table("t1"); 91 | 92 | cache->add_table("t1", factory, layout); 93 | for (int i = 0; i < 1000; i++) { 94 | Node *node = cache->get("t1", i, false); 95 | EXPECT_TRUE(node != NULL); 96 | EXPECT_EQ((uint64_t)i, ((FakeNode*)node)->data); 97 | node->dec_ref(); 98 | } 99 | cache->del_table("t1"); 100 | 101 | delete cache; 102 | delete factory; 103 | delete layout; 104 | delete file; 105 | delete dir; 106 | } -------------------------------------------------------------------------------- /test/t_callback.cpp: -------------------------------------------------------------------------------- 1 | #include "util/callback.h" 2 | #include 3 | 4 | using namespace std; 5 | using namespace cascadb; 6 | 7 | class ClassA { 8 | public: 9 | ClassA() : value(0) {} 10 | 11 | void add(int a, int b) { 12 | value += a*b; 13 | } 14 | 15 | int value; 16 | }; 17 | 18 | TEST(Callback, all) { 19 | ClassA a; 20 | 21 | Callback *cb = new Callback(&a, &ClassA::add, 2); 22 | cb->exec(3); 23 | delete cb; 24 | 25 | EXPECT_EQ(6, a.value); 26 | } 27 | -------------------------------------------------------------------------------- /test/t_comparator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cascadb/comparator.h" 3 | 4 | using namespace cascadb; 5 | using namespace std; 6 | 7 | TEST(Comparator, lexical) { 8 | LexicalComparator comp; 9 | EXPECT_TRUE(comp.compare(Slice("a"), Slice("ab")) < 0); 10 | EXPECT_TRUE(comp.compare(Slice("ab"), Slice("ab")) == 0); 11 | EXPECT_TRUE(comp.compare(Slice("ab"), Slice("a")) > 0); 12 | } 13 | 14 | TEST(Comparator, numeric) { 15 | NumericComparator comp; 16 | int a = 100, b = 200; 17 | EXPECT_TRUE(comp.compare(Slice((char *)&a, sizeof(a)), Slice((char *)&b, sizeof(b))) < 0); 18 | b = 100; 19 | EXPECT_TRUE(comp.compare(Slice((char *)&a, sizeof(a)), Slice((char *)&b, sizeof(b))) == 0); 20 | a = 200; 21 | EXPECT_TRUE(comp.compare(Slice((char *)&a, sizeof(a)), Slice((char *)&b, sizeof(b))) > 0); 22 | } 23 | -------------------------------------------------------------------------------- /test/t_compressor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "util/compressor.h" 3 | 4 | using namespace cascadb; 5 | using namespace std; 6 | 7 | const char *text = 8 | "Yet another write-optimized storage engine, " 9 | "using buffered B-tree algorithm inspired by TokuDB."; 10 | 11 | #ifdef HAS_SNAPPY 12 | 13 | TEST(SnappyCompressor, compress) { 14 | Compressor *compressor = new SnappyCompressor(); 15 | 16 | size_t len1; 17 | char* str1 = new char[compressor->max_compressed_length(strlen(text))]; 18 | EXPECT_TRUE(compressor->compress(text, strlen(text), str1, &len1)); 19 | EXPECT_NE(len1, strlen(text)); 20 | 21 | char *str2 = new char[strlen(text)]; 22 | EXPECT_TRUE(compressor->uncompress(str1, len1, str2)); 23 | 24 | EXPECT_TRUE(strncmp(text, str2, strlen(text)) == 0); 25 | } 26 | 27 | #endif -------------------------------------------------------------------------------- /test/t_crc16.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cascadb/slice.h" 3 | #include "util/crc16.h" 4 | 5 | using namespace cascadb; 6 | using namespace std; 7 | 8 | TEST(crc16, calc) 9 | { 10 | Slice s("Hello World"); 11 | 12 | EXPECT_EQ(cascadb::crc16(s.data(), 11), cascadb::crc16(s.data(), 11)); 13 | } 14 | -------------------------------------------------------------------------------- /test/t_db.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "cascadb/db.h" 6 | 7 | using namespace std; 8 | using namespace cascadb; 9 | 10 | TEST(DB, put) { 11 | Options opts; 12 | opts.dir = create_ram_directory(); 13 | opts.comparator = new LexicalComparator(); 14 | 15 | DB *db = DB::open("test_db", opts); 16 | ASSERT_TRUE(db != NULL); 17 | 18 | ASSERT_TRUE(db->put("key1", "value1")); 19 | ASSERT_TRUE(db->put("key2", "value2")); 20 | ASSERT_TRUE(db->put("key3", "value3")); 21 | 22 | string value; 23 | ASSERT_TRUE(db->get("key1", value)); 24 | ASSERT_EQ("value1", value); 25 | 26 | ASSERT_TRUE(db->get("key2", value)); 27 | ASSERT_EQ("value2", value); 28 | 29 | ASSERT_TRUE(db->get("key3", value)); 30 | ASSERT_EQ("value3", value); 31 | 32 | delete db; 33 | delete opts.dir; 34 | delete opts.comparator; 35 | } 36 | 37 | TEST(DB, del) { 38 | Options opts; 39 | opts.dir = create_ram_directory(); 40 | opts.comparator = new LexicalComparator(); 41 | 42 | DB *db = DB::open("test_db", opts); 43 | ASSERT_TRUE(db != NULL); 44 | 45 | ASSERT_TRUE(db->put("key1", "value1")); 46 | ASSERT_TRUE(db->put("key2", "value2")); 47 | ASSERT_TRUE(db->put("key3", "value3")); 48 | 49 | ASSERT_TRUE(db->del("key2")); 50 | 51 | string value; 52 | ASSERT_TRUE(db->get("key1", value)); 53 | ASSERT_EQ("value1", value); 54 | 55 | ASSERT_FALSE(db->get("key2", value)); 56 | 57 | ASSERT_TRUE(db->get("key3", value)); 58 | ASSERT_EQ("value3", value); 59 | 60 | delete db; 61 | delete opts.dir; 62 | delete opts.comparator; 63 | } 64 | 65 | TEST(DB, batch_write) { 66 | Options opts; 67 | opts.dir = create_ram_directory(); 68 | opts.comparator = new NumericComparator(); 69 | opts.inner_node_page_size = 4 * 1024; 70 | opts.inner_node_children_number = 64; 71 | opts.leaf_node_page_size = 4 * 1024; 72 | opts.leaf_node_bucket_size = 512; 73 | opts.cache_limit = 32 * 1024; 74 | opts.compress = kNoCompress; 75 | 76 | DB *db = DB::open("test_db", opts); 77 | ASSERT_TRUE(db != NULL); 78 | 79 | for (uint64_t i = 0; i < 100000; i++ ) { 80 | char buf[16] = {0}; 81 | sprintf(buf, "%ld", i); 82 | Slice key = Slice((char*)&i, sizeof(uint64_t)); 83 | Slice value = Slice(buf, strlen(buf)); 84 | ASSERT_TRUE(db->put(key, value)) << "put key " << i << " error"; 85 | if (i % 10000 == 0) { 86 | cout << "write " << i << " records" << endl; 87 | } 88 | } 89 | 90 | db->flush(); 91 | 92 | for (uint64_t i = 0; i < 100000; i++ ) { 93 | Slice key = Slice((char*)&i, sizeof(uint64_t)); 94 | Slice value; 95 | ASSERT_TRUE(db->get(key, value)) << "get key " << i << " error"; 96 | 97 | char buf[16] = {0}; 98 | sprintf(buf, "%ld", i); 99 | ASSERT_EQ(value.size(), strlen(buf)) << "get key " << i << " value size unequal" ; 100 | ASSERT_TRUE(strncmp(buf, value.data(), value.size()) == 0) << "get key " << i << " value data unequal"; 101 | value.destroy(); 102 | 103 | if (i % 10000 == 0) { 104 | cout << "read " << i << " records" << endl; 105 | } 106 | } 107 | 108 | delete db; 109 | delete opts.dir; 110 | delete opts.comparator; 111 | } 112 | 113 | TEST(DB, batch_delete) { 114 | Options opts; 115 | opts.dir = create_ram_directory(); 116 | opts.comparator = new NumericComparator(); 117 | opts.inner_node_page_size = 4 * 1024; 118 | opts.inner_node_children_number = 64; 119 | opts.leaf_node_page_size = 4 * 1024; 120 | opts.leaf_node_bucket_size = 512; 121 | opts.cache_limit = 32 * 1024; 122 | opts.compress = kNoCompress; 123 | 124 | DB *db = DB::open("test_db", opts); 125 | ASSERT_TRUE(db != NULL); 126 | 127 | for (uint64_t i = 0; i < 100000; i++ ) { 128 | char buf[16] = {0}; 129 | sprintf(buf, "%ld", i); 130 | Slice key = Slice((char*)&i, sizeof(uint64_t)); 131 | Slice value = Slice(buf, strlen(buf)); 132 | ASSERT_TRUE(db->put(key, value)) << "put key " << i << " error"; 133 | if (i % 10000 == 0) { 134 | cout << "write " << i << " records" << endl; 135 | } 136 | } 137 | 138 | db->flush(); 139 | 140 | for (uint64_t i = 0; i < 100000; i++ ) { 141 | Slice key = Slice((char*)&i, sizeof(uint64_t)); 142 | ASSERT_TRUE(db->del(key)) << "del key " << i << " error"; 143 | if (i % 10000 == 0) { 144 | cout << "del " << i << " records" << endl; 145 | } 146 | } 147 | 148 | db->flush(); 149 | 150 | for (uint64_t i = 0; i < 100000; i++ ) { 151 | Slice key = Slice((char*)&i, sizeof(uint64_t)); 152 | Slice value; 153 | ASSERT_FALSE(db->get(key, value)) << "get key " << i << " error"; 154 | 155 | if (i % 10000 == 0) { 156 | cout << "read " << i << " records" << endl; 157 | } 158 | } 159 | 160 | delete db; 161 | delete opts.dir; 162 | delete opts.comparator; 163 | } -------------------------------------------------------------------------------- /test/t_fast_vector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tree/fast_vector.h" 3 | 4 | using namespace cascadb; 5 | using namespace std; 6 | 7 | TEST(FastVector, sequential_insert) { 8 | FastVector vec; 9 | for (size_t i = 0; i < 1000; i++) { 10 | FastVector::iterator it = vec.insert(vec.end(), i); 11 | ASSERT_EQ(i, (size_t)*it); 12 | } 13 | ASSERT_EQ(1000U, vec.size()); 14 | 15 | FastVector::iterator it = vec.begin(); 16 | for (size_t i = 0; i < 1000; i++) { 17 | ASSERT_EQ(i, (size_t)*it); 18 | it ++; 19 | } 20 | ASSERT_EQ(it, vec.end()); 21 | } 22 | 23 | 24 | TEST(FastVector, lower_bound) { 25 | FastVector vec; 26 | for (size_t i = 0; i < 1000; i++) { 27 | FastVector::iterator it = vec.insert(vec.end(), i); 28 | ASSERT_EQ(i, (size_t)*it); 29 | } 30 | ASSERT_EQ(1000U, vec.size()); 31 | 32 | std::less compare; 33 | for (size_t i = 0; i < 1000; i++) { 34 | FastVector::iterator it = vec.lower_bound(i, compare); 35 | ASSERT_EQ(i, (size_t)*it); 36 | } 37 | } 38 | 39 | TEST(FastVector, random_insert) { 40 | FastVector vec; 41 | for (size_t i = 0; i < 1000; i++) { 42 | if (i % 100 == 0) continue; 43 | 44 | FastVector::iterator it = vec.insert(vec.end(), i); 45 | ASSERT_EQ(i, (size_t)*it); 46 | } 47 | ASSERT_EQ(990U, vec.size()); 48 | 49 | std::less compare; 50 | for (size_t i = 0; i < 10; i++) { 51 | size_t k = i*100; 52 | FastVector::iterator it = vec.lower_bound(k, compare); 53 | ASSERT_EQ(k + 1, (size_t)*it); 54 | it = vec.insert(it, i*100); 55 | ASSERT_EQ(k, (size_t)*it); 56 | } 57 | ASSERT_EQ(1000U, vec.size()); 58 | 59 | FastVector::iterator it = vec.begin(); 60 | for (size_t i = 0; i < 1000; i++) { 61 | ASSERT_EQ(i, (size_t)*it); 62 | it ++; 63 | } 64 | ASSERT_EQ(it, vec.end()); 65 | } 66 | 67 | TEST(FastVector, index) { 68 | FastVector vec; 69 | for (size_t i = 0; i < 1000; i++) { 70 | FastVector::iterator it = vec.insert(vec.end(), i); 71 | ASSERT_EQ(i, (size_t)*it); 72 | } 73 | ASSERT_EQ(1000U, vec.size()); 74 | 75 | for (size_t i = 0; i < 1000; i++) { 76 | ASSERT_EQ(i, (size_t)vec[i]); 77 | } 78 | } -------------------------------------------------------------------------------- /test/t_keycomp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tree/keycomp.h" 3 | #include "tree/msg.h" 4 | #include "tree/node.h" 5 | 6 | using namespace cascadb; 7 | using namespace std; 8 | 9 | TEST(Query, compare) 10 | { 11 | LexicalComparator c; 12 | KeyComp comp(&c); 13 | 14 | // msg to string 15 | EXPECT_TRUE(comp(Msg(Put, "a", "1"), Slice("b") )); 16 | EXPECT_FALSE(comp(Slice("b"), Msg(Put, "a", "1"))); 17 | 18 | // record to string 19 | EXPECT_TRUE(comp(Record("a", "1"), Slice("b") )); 20 | EXPECT_FALSE(comp(Slice("b"), Record("a", "1"))); 21 | 22 | // record to msg 23 | EXPECT_TRUE(comp(Record("a", "1"), Msg(Put, "b", "1"))); 24 | EXPECT_FALSE(comp(Msg(Put, "b", "1"), Record("a", "1"))); 25 | } 26 | -------------------------------------------------------------------------------- /test/t_layout.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "sys/sys.h" 6 | #include "store/ram_directory.h" 7 | #include "serialize/layout.h" 8 | 9 | using namespace std; 10 | using namespace cascadb; 11 | 12 | class LayoutTest : public testing::Test { 13 | public: 14 | LayoutTest() { 15 | dir = new RAMDirectory(); 16 | file = NULL; 17 | layout = NULL; 18 | min_page_size = 1; 19 | max_page_size = 64 * 1024; 20 | 21 | srand(0); 22 | } 23 | 24 | ~LayoutTest() { 25 | delete dir; 26 | } 27 | 28 | virtual void SetUp() 29 | { 30 | dir->delete_file("layout_test"); 31 | file = dir->open_aio_file("layout_test"); 32 | } 33 | 34 | virtual void TearDown() 35 | { 36 | delete file; 37 | dir->delete_file("layout_test"); 38 | } 39 | 40 | uint64_t GetLength() { 41 | return dir->file_length("layout_test"); 42 | } 43 | 44 | void OpenLayout(const Options& opts, bool create) { 45 | size_t length; 46 | if (create) { 47 | length = 0; 48 | } else { 49 | length = dir->file_length("layout_test"); 50 | } 51 | layout = new Layout(file, length, opts); 52 | ASSERT_TRUE(layout->init(create)); 53 | } 54 | 55 | void CloseLayout() { 56 | ASSERT_TRUE(layout->flush()); 57 | delete layout; 58 | } 59 | 60 | void Write() { 61 | results.clear(); 62 | for (int i = 0; i < 1000; i++ ) { 63 | size_t size = min_page_size + rand() % (max_page_size - min_page_size); 64 | write_bufs[i] = layout->create(size); 65 | BlockWriter writer(write_bufs[i]); 66 | for (size_t j = 0; j < size; j++ ) { 67 | writer.writeUInt8(i&0xff); 68 | } 69 | Callback *cb = new Callback(this, &LayoutTest::callback, (bid_t)i); 70 | layout->async_write(i, write_bufs[i], size, cb); 71 | } 72 | 73 | while(results.size() != 1000) cascadb::usleep(10000); // 10ms 74 | for (int i = 0; i < 1000; i++ ) { 75 | ASSERT_TRUE(results[i]); 76 | } 77 | } 78 | 79 | void ClearWriteBufs() { 80 | for (map::iterator it = write_bufs.begin(); 81 | it != write_bufs.end(); it++ ) { 82 | layout->destroy(it->second); 83 | } 84 | write_bufs.clear(); 85 | } 86 | 87 | void AsyncRead() { 88 | results.clear(); 89 | Block *read_bufs[1000]; 90 | for (int i = 0; i < 1000; i++ ) { 91 | Callback *cb = new Callback(this, &LayoutTest::callback, (bid_t)i); 92 | layout->async_read(i, &(read_bufs[i]), cb); 93 | } 94 | 95 | while(results.size() != 1000) cascadb::usleep(10000); // 10ms 96 | for (int i = 0; i < 1000; i++ ) { 97 | ASSERT_TRUE(results[i]); 98 | if (results[i]) { 99 | ASSERT_EQ(write_bufs[i]->size(), read_bufs[i]->size()); 100 | ASSERT_EQ(0, memcmp(write_bufs[i]->start(), read_bufs[i]->start(), write_bufs[i]->size())); 101 | } 102 | layout->destroy(read_bufs[i]); 103 | } 104 | } 105 | 106 | void BlockingRead() { 107 | for (int i = 0; i < 1000; i++ ) { 108 | Block *read_buf = layout->read(i, false); 109 | ASSERT_TRUE(read_buf != NULL); 110 | ASSERT_EQ(write_bufs[i]->size(), read_buf->size()); 111 | ASSERT_EQ(0, memcmp(write_bufs[i]->start(), read_buf->start(), write_bufs[i]->size())); 112 | layout->destroy(read_buf); 113 | } 114 | } 115 | 116 | void callback(bid_t bid, bool is_succ) 117 | { 118 | ScopedMutex lock(&mtx); 119 | results[bid] = is_succ; 120 | } 121 | 122 | Directory *dir; 123 | AIOFile *file; 124 | Layout *layout; 125 | 126 | size_t min_page_size; 127 | size_t max_page_size; 128 | map write_bufs; 129 | 130 | Mutex mtx; 131 | map results; 132 | }; 133 | 134 | 135 | TEST_F(LayoutTest, async_read) 136 | { 137 | Options opts; 138 | 139 | OpenLayout(opts, true); 140 | Write(); 141 | CloseLayout(); 142 | 143 | OpenLayout(opts, false); 144 | AsyncRead(); 145 | CloseLayout(); 146 | 147 | ClearWriteBufs(); 148 | } 149 | 150 | TEST_F(LayoutTest, blocking_read) 151 | { 152 | Options opts; 153 | 154 | OpenLayout(opts, true); 155 | Write(); 156 | CloseLayout(); 157 | 158 | OpenLayout(opts, false); 159 | BlockingRead(); 160 | CloseLayout(); 161 | 162 | ClearWriteBufs(); 163 | } 164 | 165 | TEST_F(LayoutTest, async_read_compress) 166 | { 167 | Options opts; 168 | opts.compress = kSnappyCompress; 169 | 170 | OpenLayout(opts, true); 171 | Write(); 172 | CloseLayout(); 173 | 174 | OpenLayout(opts, false); 175 | AsyncRead(); 176 | CloseLayout(); 177 | 178 | ClearWriteBufs(); 179 | } 180 | 181 | TEST_F(LayoutTest, blocking_read_compress) 182 | { 183 | Options opts; 184 | opts.compress = kSnappyCompress; 185 | 186 | OpenLayout(opts, true); 187 | Write(); 188 | CloseLayout(); 189 | 190 | OpenLayout(opts, false); 191 | BlockingRead(); 192 | CloseLayout(); 193 | 194 | ClearWriteBufs(); 195 | } 196 | 197 | TEST_F(LayoutTest, update) 198 | { 199 | Options opts; 200 | 201 | OpenLayout(opts, true); 202 | Write(); 203 | CloseLayout(); 204 | 205 | OpenLayout(opts, false); 206 | AsyncRead(); 207 | CloseLayout(); 208 | 209 | ClearWriteBufs(); 210 | 211 | OpenLayout(opts, false); 212 | Write(); // update all records 213 | CloseLayout(); 214 | 215 | OpenLayout(opts, false); 216 | AsyncRead(); 217 | CloseLayout(); 218 | 219 | ClearWriteBufs(); 220 | 221 | uint64_t len1 = GetLength(); 222 | 223 | OpenLayout(opts, false); 224 | Write(); // update all records 225 | CloseLayout(); 226 | 227 | uint64_t len2 = GetLength(); 228 | 229 | EXPECT_TRUE(len2 > len1 * 0.9 && len2 < len1 * 1.1); // fragment collection should works 230 | } 231 | -------------------------------------------------------------------------------- /test/t_linux_fs_directory.cpp: -------------------------------------------------------------------------------- 1 | #include "sys/linux/linux_fs_directory.h" 2 | 3 | #include "directory_test.h" 4 | 5 | class LinuxSequenceFileTest : public SequenceFileTest { 6 | public: 7 | LinuxSequenceFileTest() 8 | { 9 | dir = new LinuxFSDirectory("/tmp"); 10 | } 11 | }; 12 | 13 | TEST_F(LinuxSequenceFileTest, read_and_write) { 14 | TestReadAndWrite(); 15 | } 16 | 17 | class LinuxAIOFileTest : public AIOFileTest { 18 | public: 19 | LinuxAIOFileTest() 20 | { 21 | dir = new LinuxFSDirectory("/tmp"); 22 | } 23 | }; 24 | 25 | TEST_F(LinuxAIOFileTest, blocking_read_and_write) 26 | { 27 | TestBlockingReadAndWrite(); 28 | } 29 | 30 | TEST_F(LinuxAIOFileTest, read_and_write) 31 | { 32 | TestReadAndWrite(); 33 | } 34 | 35 | TEST_F(LinuxAIOFileTest, read_partial) 36 | { 37 | TestReadPartial(); 38 | } 39 | -------------------------------------------------------------------------------- /test/t_msg.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tree/msg.h" 3 | #include "helper.h" 4 | 5 | using namespace cascadb; 6 | using namespace std; 7 | 8 | TEST(Msg, searialize) 9 | { 10 | char buffer[4096]; 11 | Block blk(Slice(buffer, 4096), 0, 0); 12 | BlockReader reader(&blk); 13 | BlockWriter writer(&blk); 14 | 15 | Msg m1; 16 | m1.set_put(Slice("put"), Slice("value")); 17 | ASSERT_TRUE(m1.write_to(writer)); 18 | 19 | Msg m2; 20 | m2.set_del(Slice("del")); 21 | ASSERT_TRUE(m2.write_to(writer)); 22 | 23 | EXPECT_TRUE(blk.size() == m1.size() + m2.size()); 24 | 25 | Msg m3; 26 | ASSERT_TRUE(m3.read_from(reader)); 27 | EXPECT_EQ(Put, m3.type); 28 | EXPECT_EQ("put", m3.key); 29 | EXPECT_EQ("value", m3.value); 30 | 31 | Msg m4; 32 | ASSERT_TRUE(m4.read_from(reader)); 33 | EXPECT_EQ(Del, m4.type); 34 | EXPECT_EQ("del", m4.key); 35 | EXPECT_EQ(Slice(), m4.value); 36 | 37 | EXPECT_TRUE(blk.size() == m3.size() + m4.size()); 38 | EXPECT_EQ(0U ,reader.remain()); 39 | } 40 | 41 | TEST(MsgBuf, write) 42 | { 43 | LexicalComparator comp; 44 | MsgBuf mb(&comp); 45 | 46 | PUT(mb, "abc", "1"); 47 | PUT(mb, "aaa", "1"); 48 | PUT(mb, "b", "1"); 49 | PUT(mb, "aaa", "2"); 50 | PUT(mb, "c", "1"); 51 | PUT(mb, "b", "1"); 52 | PUT(mb, "b", "2"); 53 | PUT(mb, "b", "3"); 54 | DEL(mb, "c"); 55 | 56 | EXPECT_EQ(4U, mb.count()); 57 | 58 | CHK_MSG(mb.get(0), Put, "aaa", "2"); 59 | CHK_MSG(mb.get(1), Put, "abc", "1"); 60 | CHK_MSG(mb.get(2), Put, "b", "3"); 61 | CHK_MSG(mb.get(3), Del, "c", Slice()); 62 | } 63 | 64 | TEST(MsgBuf, append) 65 | { 66 | LexicalComparator comp; 67 | MsgBuf mb1(&comp); 68 | MsgBuf mb2(&comp); 69 | 70 | PUT(mb1, "abc", "1"); 71 | PUT(mb1, "aaa", "1"); 72 | PUT(mb1, "b", "1"); 73 | PUT(mb1, "aaa", "2"); 74 | PUT(mb1, "c", "1"); 75 | 76 | PUT(mb2, "b", "1"); 77 | PUT(mb2, "b", "2"); 78 | PUT(mb2, "b", "3"); 79 | DEL(mb2, "c"); 80 | 81 | mb1.append(mb2.begin(), mb2.end()); 82 | mb2.clear(); 83 | 84 | EXPECT_EQ(4U, mb1.count()); 85 | 86 | CHK_MSG(mb1.get(0), Put, "aaa", "2"); 87 | CHK_MSG(mb1.get(1), Put, "abc", "1"); 88 | CHK_MSG(mb1.get(2), Put, "b", "3"); 89 | CHK_MSG(mb1.get(3), Del, "c", Slice()); 90 | } 91 | 92 | TEST(MsgBuf, find) 93 | { 94 | LexicalComparator comp; 95 | MsgBuf mb(&comp); 96 | 97 | PUT(mb, "abc", "1"); 98 | PUT(mb, "aaa", "1"); 99 | PUT(mb, "b", "1"); 100 | PUT(mb, "aaa", "2"); 101 | PUT(mb, "c", "1"); 102 | PUT(mb, "b", "1"); 103 | PUT(mb, "b", "2"); 104 | PUT(mb, "b", "3"); 105 | DEL(mb, "c"); 106 | 107 | CHK_MSG(*mb.find("a"), Put, "aaa", "2"); 108 | CHK_MSG(*mb.find("aaa"), Put, "aaa", "2"); 109 | CHK_MSG(*mb.find("abc"), Put, "abc", "1"); 110 | CHK_MSG(*mb.find("b"), Put, "b", "3"); 111 | CHK_MSG(*mb.find("c"), Del, "c", Slice()); 112 | EXPECT_TRUE(mb.end() == (mb.find("d"))); 113 | } 114 | 115 | TEST(MsgBuf, searialize) 116 | { 117 | char buffer[4096]; 118 | Block blk(Slice(buffer, 4096), 0, 0); 119 | BlockReader reader(&blk); 120 | BlockWriter writer(&blk); 121 | 122 | LexicalComparator comp; 123 | MsgBuf mb1(&comp); 124 | 125 | PUT(mb1, "a", "1"); 126 | DEL(mb1, "b"); 127 | 128 | mb1.write_to(writer); 129 | 130 | EXPECT_EQ(mb1.size(), blk.size()); 131 | 132 | MsgBuf mb2(&comp); 133 | mb2.read_from(reader); 134 | 135 | EXPECT_EQ(2U, mb2.count()); 136 | CHK_MSG(mb2.get(0), Put, "a", "1"); 137 | CHK_MSG(mb2.get(1), Del, "b", Slice()); 138 | 139 | EXPECT_EQ(mb2.size(), blk.size()); 140 | } 141 | -------------------------------------------------------------------------------- /test/t_posix_fs_directory.cpp: -------------------------------------------------------------------------------- 1 | #include "sys/posix/posix_fs_directory.h" 2 | 3 | #include "directory_test.h" 4 | 5 | class PosixSequenceFileTest : public SequenceFileTest { 6 | public: 7 | PosixSequenceFileTest() 8 | { 9 | dir = new PosixFSDirectory("/tmp"); 10 | } 11 | }; 12 | 13 | TEST_F(PosixSequenceFileTest, read_and_write) { 14 | TestReadAndWrite(); 15 | } 16 | 17 | class PosixAIOFileTest : public AIOFileTest { 18 | public: 19 | PosixAIOFileTest() 20 | { 21 | dir = new PosixFSDirectory("/tmp"); 22 | } 23 | }; 24 | 25 | TEST_F(PosixAIOFileTest, blocking_read_and_write) 26 | { 27 | TestBlockingReadAndWrite(); 28 | } 29 | 30 | TEST_F(PosixAIOFileTest, read_and_write) 31 | { 32 | TestReadAndWrite(); 33 | } 34 | 35 | TEST_F(PosixAIOFileTest, read_partial) 36 | { 37 | TestReadPartial(); 38 | } 39 | -------------------------------------------------------------------------------- /test/t_posix_sys.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "sys/sys.h" 3 | 4 | using namespace cascadb; 5 | using namespace std; 6 | 7 | int count1; 8 | void *body1(void* arg) 9 | { 10 | count1 ++; 11 | return NULL; 12 | } 13 | 14 | TEST(Thread, run) { 15 | Thread thr(body1); 16 | count1 = 0; 17 | thr.start(NULL); 18 | thr.join(); 19 | EXPECT_EQ(count1, 1); 20 | } 21 | 22 | Mutex mu2; 23 | void *body2(void* arg) 24 | { 25 | mu2.lock(); 26 | cascadb::usleep(100000); // 100 ms 27 | mu2.unlock(); 28 | return NULL; 29 | } 30 | 31 | TEST(Mutex, lock) { 32 | Thread thr1(body2); 33 | Thread thr2(body2); 34 | 35 | Time t1 = now(); 36 | 37 | thr1.start(NULL); 38 | thr2.start(NULL); 39 | thr1.join(); 40 | thr2.join(); 41 | 42 | Time t2 = now(); 43 | 44 | EXPECT_NEAR(200000, interval_us(t1, t2), 5000); 45 | } 46 | 47 | TEST(Mutex, trylock) { 48 | Thread thr(body2); 49 | thr.start(NULL); 50 | cascadb::usleep(1000); 51 | 52 | EXPECT_FALSE(mu2.lock_try()); 53 | cascadb::usleep(100000); 54 | EXPECT_TRUE(mu2.lock_try()); 55 | mu2.unlock(); 56 | 57 | thr.join(); 58 | } 59 | 60 | TEST(Mutex, timedlock) { 61 | Thread thr(body2); 62 | thr.start(NULL); 63 | cascadb::usleep(1000); 64 | 65 | EXPECT_FALSE(mu2.lock_try()); 66 | EXPECT_FALSE(mu2.lock_try(50)); 67 | EXPECT_TRUE(mu2.lock_try(50)); 68 | mu2.unlock(); 69 | 70 | thr.join(); 71 | } 72 | 73 | int count; 74 | Mutex mu3; 75 | CondVar cond3(&mu3); 76 | void *body3(void* arg) 77 | { 78 | cascadb::usleep(100000); // 100 ms 79 | mu3.lock(); 80 | count ++; 81 | mu3.unlock(); 82 | cond3.notify(); 83 | return NULL; 84 | } 85 | 86 | TEST(CondVar, wait) { 87 | count = 0; 88 | 89 | Thread thr(body3); 90 | thr.start(NULL); 91 | 92 | mu3.lock(); 93 | cond3.wait(); 94 | EXPECT_EQ(count, 1); 95 | mu3.unlock(); 96 | 97 | thr.join(); 98 | } 99 | 100 | TEST(CondVar, timedwait) { 101 | count = 0; 102 | 103 | Thread thr(body3); 104 | thr.start(NULL); 105 | 106 | mu3.lock(); 107 | cond3.wait(50); 108 | EXPECT_EQ(count, 0); 109 | cond3.wait(51); 110 | EXPECT_EQ(count, 1); 111 | mu3.unlock(); 112 | 113 | thr.join(); 114 | } 115 | 116 | RWLock rwl; 117 | 118 | void *body4(void* arg) 119 | { 120 | rwl.read_lock(); 121 | cascadb::usleep(100000); // 100 ms 122 | rwl.unlock(); 123 | 124 | return NULL; 125 | } 126 | 127 | TEST(RWLock, rdlock) { 128 | Thread thr1(body4); 129 | thr1.start(NULL); 130 | 131 | Thread thr2(body4); 132 | thr2.start(NULL); 133 | 134 | cascadb::usleep(10000); 135 | EXPECT_TRUE(rwl.try_read_lock()); 136 | rwl.unlock(); 137 | 138 | EXPECT_FALSE(rwl.try_write_lock()); 139 | cascadb::usleep(100000); // 100 ms 140 | 141 | EXPECT_TRUE(rwl.try_write_lock()); 142 | rwl.unlock(); 143 | 144 | thr1.join(); 145 | thr2.join(); 146 | } 147 | -------------------------------------------------------------------------------- /test/t_ram_directory.cpp: -------------------------------------------------------------------------------- 1 | #include "store/ram_directory.h" 2 | 3 | #include "directory_test.h" 4 | 5 | class RAMSequenceFileTest : public SequenceFileTest { 6 | public: 7 | RAMSequenceFileTest() 8 | { 9 | dir = new RAMDirectory(); 10 | } 11 | }; 12 | 13 | TEST_F(RAMSequenceFileTest, read_and_write) { 14 | TestReadAndWrite(); 15 | } 16 | 17 | class RAMAIOFileTest : public AIOFileTest { 18 | public: 19 | RAMAIOFileTest() 20 | { 21 | dir = new RAMDirectory(); 22 | } 23 | }; 24 | 25 | TEST_F(RAMAIOFileTest, blocking_read_and_write) 26 | { 27 | TestBlockingReadAndWrite(); 28 | } 29 | 30 | TEST_F(RAMAIOFileTest, read_and_write) 31 | { 32 | TestReadAndWrite(); 33 | } 34 | 35 | TEST_F(RAMAIOFileTest, read_partial) 36 | { 37 | TestReadPartial(); 38 | } 39 | -------------------------------------------------------------------------------- /test/t_slice.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cascadb/slice.h" 3 | 4 | using namespace cascadb; 5 | using namespace std; 6 | 7 | TEST(Slice, constructor) { 8 | EXPECT_EQ(Slice().size(), 0U); 9 | EXPECT_LT(Slice(), Slice("a")); 10 | EXPECT_LT(Slice("a"), Slice("ab")); 11 | EXPECT_EQ(Slice("a"), Slice("ab", 1)); 12 | EXPECT_EQ(Slice(string("ab")), Slice("ab")); 13 | } 14 | 15 | TEST(Slice, empty) { 16 | EXPECT_TRUE(Slice().empty()); 17 | EXPECT_TRUE(Slice("").empty()); 18 | EXPECT_FALSE(Slice("a").empty()); 19 | } 20 | 21 | TEST(Slice, clear) { 22 | Slice ab("ab"); 23 | ab.clear(); 24 | EXPECT_EQ(ab, Slice()); 25 | } 26 | -------------------------------------------------------------------------------- /thirdparty/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(gtest) 2 | -------------------------------------------------------------------------------- /thirdparty/gtest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories( 2 | ${CMAKE_CURRENT_SOURCE_DIR}/include 3 | ${CMAKE_CURRENT_SOURCE_DIR}) 4 | add_library( 5 | gtest 6 | "src/gtest-all.cc" 7 | ) 8 | add_library( 9 | gtest_main 10 | "src/gtest_main.cc" 11 | ) 12 | -------------------------------------------------------------------------------- /thirdparty/gtest/include/gtest/gtest-message.h: -------------------------------------------------------------------------------- 1 | // Copyright 2005, Google Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of Google Inc. nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | // Author: wan@google.com (Zhanyong Wan) 31 | // 32 | // The Google C++ Testing Framework (Google Test) 33 | // 34 | // This header file defines the Message class. 35 | // 36 | // IMPORTANT NOTE: Due to limitation of the C++ language, we have to 37 | // leave some internal implementation details in this header file. 38 | // They are clearly marked by comments like this: 39 | // 40 | // // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. 41 | // 42 | // Such code is NOT meant to be used by a user directly, and is subject 43 | // to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user 44 | // program! 45 | 46 | #ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ 47 | #define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ 48 | 49 | #include 50 | 51 | #include "gtest/internal/gtest-string.h" 52 | #include "gtest/internal/gtest-internal.h" 53 | 54 | namespace testing { 55 | 56 | // The Message class works like an ostream repeater. 57 | // 58 | // Typical usage: 59 | // 60 | // 1. You stream a bunch of values to a Message object. 61 | // It will remember the text in a stringstream. 62 | // 2. Then you stream the Message object to an ostream. 63 | // This causes the text in the Message to be streamed 64 | // to the ostream. 65 | // 66 | // For example; 67 | // 68 | // testing::Message foo; 69 | // foo << 1 << " != " << 2; 70 | // std::cout << foo; 71 | // 72 | // will print "1 != 2". 73 | // 74 | // Message is not intended to be inherited from. In particular, its 75 | // destructor is not virtual. 76 | // 77 | // Note that stringstream behaves differently in gcc and in MSVC. You 78 | // can stream a NULL char pointer to it in the former, but not in the 79 | // latter (it causes an access violation if you do). The Message 80 | // class hides this difference by treating a NULL char pointer as 81 | // "(null)". 82 | class GTEST_API_ Message { 83 | private: 84 | // The type of basic IO manipulators (endl, ends, and flush) for 85 | // narrow streams. 86 | typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); 87 | 88 | public: 89 | // Constructs an empty Message. 90 | // We allocate the stringstream separately because otherwise each use of 91 | // ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's 92 | // stack frame leading to huge stack frames in some cases; gcc does not reuse 93 | // the stack space. 94 | Message() : ss_(new ::std::stringstream) { 95 | // By default, we want there to be enough precision when printing 96 | // a double to a Message. 97 | *ss_ << std::setprecision(std::numeric_limits::digits10 + 2); 98 | } 99 | 100 | // Copy constructor. 101 | Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT 102 | *ss_ << msg.GetString(); 103 | } 104 | 105 | // Constructs a Message from a C-string. 106 | explicit Message(const char* str) : ss_(new ::std::stringstream) { 107 | *ss_ << str; 108 | } 109 | 110 | #if GTEST_OS_SYMBIAN 111 | // Streams a value (either a pointer or not) to this object. 112 | template 113 | inline Message& operator <<(const T& value) { 114 | StreamHelper(typename internal::is_pointer::type(), value); 115 | return *this; 116 | } 117 | #else 118 | // Streams a non-pointer value to this object. 119 | template 120 | inline Message& operator <<(const T& val) { 121 | ::GTestStreamToHelper(ss_.get(), val); 122 | return *this; 123 | } 124 | 125 | // Streams a pointer value to this object. 126 | // 127 | // This function is an overload of the previous one. When you 128 | // stream a pointer to a Message, this definition will be used as it 129 | // is more specialized. (The C++ Standard, section 130 | // [temp.func.order].) If you stream a non-pointer, then the 131 | // previous definition will be used. 132 | // 133 | // The reason for this overload is that streaming a NULL pointer to 134 | // ostream is undefined behavior. Depending on the compiler, you 135 | // may get "0", "(nil)", "(null)", or an access violation. To 136 | // ensure consistent result across compilers, we always treat NULL 137 | // as "(null)". 138 | template 139 | inline Message& operator <<(T* const& pointer) { // NOLINT 140 | if (pointer == NULL) { 141 | *ss_ << "(null)"; 142 | } else { 143 | ::GTestStreamToHelper(ss_.get(), pointer); 144 | } 145 | return *this; 146 | } 147 | #endif // GTEST_OS_SYMBIAN 148 | 149 | // Since the basic IO manipulators are overloaded for both narrow 150 | // and wide streams, we have to provide this specialized definition 151 | // of operator <<, even though its body is the same as the 152 | // templatized version above. Without this definition, streaming 153 | // endl or other basic IO manipulators to Message will confuse the 154 | // compiler. 155 | Message& operator <<(BasicNarrowIoManip val) { 156 | *ss_ << val; 157 | return *this; 158 | } 159 | 160 | // Instead of 1/0, we want to see true/false for bool values. 161 | Message& operator <<(bool b) { 162 | return *this << (b ? "true" : "false"); 163 | } 164 | 165 | // These two overloads allow streaming a wide C string to a Message 166 | // using the UTF-8 encoding. 167 | Message& operator <<(const wchar_t* wide_c_str) { 168 | return *this << internal::String::ShowWideCString(wide_c_str); 169 | } 170 | Message& operator <<(wchar_t* wide_c_str) { 171 | return *this << internal::String::ShowWideCString(wide_c_str); 172 | } 173 | 174 | #if GTEST_HAS_STD_WSTRING 175 | // Converts the given wide string to a narrow string using the UTF-8 176 | // encoding, and streams the result to this Message object. 177 | Message& operator <<(const ::std::wstring& wstr); 178 | #endif // GTEST_HAS_STD_WSTRING 179 | 180 | #if GTEST_HAS_GLOBAL_WSTRING 181 | // Converts the given wide string to a narrow string using the UTF-8 182 | // encoding, and streams the result to this Message object. 183 | Message& operator <<(const ::wstring& wstr); 184 | #endif // GTEST_HAS_GLOBAL_WSTRING 185 | 186 | // Gets the text streamed to this object so far as a String. 187 | // Each '\0' character in the buffer is replaced with "\\0". 188 | // 189 | // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. 190 | internal::String GetString() const { 191 | return internal::StringStreamToString(ss_.get()); 192 | } 193 | 194 | private: 195 | 196 | #if GTEST_OS_SYMBIAN 197 | // These are needed as the Nokia Symbian Compiler cannot decide between 198 | // const T& and const T* in a function template. The Nokia compiler _can_ 199 | // decide between class template specializations for T and T*, so a 200 | // tr1::type_traits-like is_pointer works, and we can overload on that. 201 | template 202 | inline void StreamHelper(internal::true_type /*dummy*/, T* pointer) { 203 | if (pointer == NULL) { 204 | *ss_ << "(null)"; 205 | } else { 206 | ::GTestStreamToHelper(ss_.get(), pointer); 207 | } 208 | } 209 | template 210 | inline void StreamHelper(internal::false_type /*dummy*/, const T& value) { 211 | ::GTestStreamToHelper(ss_.get(), value); 212 | } 213 | #endif // GTEST_OS_SYMBIAN 214 | 215 | // We'll hold the text streamed to this object here. 216 | const internal::scoped_ptr< ::std::stringstream> ss_; 217 | 218 | // We declare (but don't implement) this to prevent the compiler 219 | // from implementing the assignment operator. 220 | void operator=(const Message&); 221 | }; 222 | 223 | // Streams a Message to an ostream. 224 | inline std::ostream& operator <<(std::ostream& os, const Message& sb) { 225 | return os << sb.GetString(); 226 | } 227 | 228 | } // namespace testing 229 | 230 | #endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ 231 | -------------------------------------------------------------------------------- /thirdparty/gtest/include/gtest/gtest-test-part.h: -------------------------------------------------------------------------------- 1 | // Copyright 2008, Google Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of Google Inc. nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | // Author: mheule@google.com (Markus Heule) 31 | // 32 | 33 | #ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ 34 | #define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ 35 | 36 | #include 37 | #include 38 | #include "gtest/internal/gtest-internal.h" 39 | #include "gtest/internal/gtest-string.h" 40 | 41 | namespace testing { 42 | 43 | // A copyable object representing the result of a test part (i.e. an 44 | // assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). 45 | // 46 | // Don't inherit from TestPartResult as its destructor is not virtual. 47 | class GTEST_API_ TestPartResult { 48 | public: 49 | // The possible outcomes of a test part (i.e. an assertion or an 50 | // explicit SUCCEED(), FAIL(), or ADD_FAILURE()). 51 | enum Type { 52 | kSuccess, // Succeeded. 53 | kNonFatalFailure, // Failed but the test can continue. 54 | kFatalFailure // Failed and the test should be terminated. 55 | }; 56 | 57 | // C'tor. TestPartResult does NOT have a default constructor. 58 | // Always use this constructor (with parameters) to create a 59 | // TestPartResult object. 60 | TestPartResult(Type a_type, 61 | const char* a_file_name, 62 | int a_line_number, 63 | const char* a_message) 64 | : type_(a_type), 65 | file_name_(a_file_name), 66 | line_number_(a_line_number), 67 | summary_(ExtractSummary(a_message)), 68 | message_(a_message) { 69 | } 70 | 71 | // Gets the outcome of the test part. 72 | Type type() const { return type_; } 73 | 74 | // Gets the name of the source file where the test part took place, or 75 | // NULL if it's unknown. 76 | const char* file_name() const { return file_name_.c_str(); } 77 | 78 | // Gets the line in the source file where the test part took place, 79 | // or -1 if it's unknown. 80 | int line_number() const { return line_number_; } 81 | 82 | // Gets the summary of the failure message. 83 | const char* summary() const { return summary_.c_str(); } 84 | 85 | // Gets the message associated with the test part. 86 | const char* message() const { return message_.c_str(); } 87 | 88 | // Returns true iff the test part passed. 89 | bool passed() const { return type_ == kSuccess; } 90 | 91 | // Returns true iff the test part failed. 92 | bool failed() const { return type_ != kSuccess; } 93 | 94 | // Returns true iff the test part non-fatally failed. 95 | bool nonfatally_failed() const { return type_ == kNonFatalFailure; } 96 | 97 | // Returns true iff the test part fatally failed. 98 | bool fatally_failed() const { return type_ == kFatalFailure; } 99 | private: 100 | Type type_; 101 | 102 | // Gets the summary of the failure message by omitting the stack 103 | // trace in it. 104 | static internal::String ExtractSummary(const char* message); 105 | 106 | // The name of the source file where the test part took place, or 107 | // NULL if the source file is unknown. 108 | internal::String file_name_; 109 | // The line in the source file where the test part took place, or -1 110 | // if the line number is unknown. 111 | int line_number_; 112 | internal::String summary_; // The test failure summary. 113 | internal::String message_; // The test failure message. 114 | }; 115 | 116 | // Prints a TestPartResult object. 117 | std::ostream& operator<<(std::ostream& os, const TestPartResult& result); 118 | 119 | // An array of TestPartResult objects. 120 | // 121 | // Don't inherit from TestPartResultArray as its destructor is not 122 | // virtual. 123 | class GTEST_API_ TestPartResultArray { 124 | public: 125 | TestPartResultArray() {} 126 | 127 | // Appends the given TestPartResult to the array. 128 | void Append(const TestPartResult& result); 129 | 130 | // Returns the TestPartResult at the given index (0-based). 131 | const TestPartResult& GetTestPartResult(int index) const; 132 | 133 | // Returns the number of TestPartResult objects in the array. 134 | int size() const; 135 | 136 | private: 137 | std::vector array_; 138 | 139 | GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray); 140 | }; 141 | 142 | // This interface knows how to report a test part result. 143 | class TestPartResultReporterInterface { 144 | public: 145 | virtual ~TestPartResultReporterInterface() {} 146 | 147 | virtual void ReportTestPartResult(const TestPartResult& result) = 0; 148 | }; 149 | 150 | namespace internal { 151 | 152 | // This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a 153 | // statement generates new fatal failures. To do so it registers itself as the 154 | // current test part result reporter. Besides checking if fatal failures were 155 | // reported, it only delegates the reporting to the former result reporter. 156 | // The original result reporter is restored in the destructor. 157 | // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. 158 | class GTEST_API_ HasNewFatalFailureHelper 159 | : public TestPartResultReporterInterface { 160 | public: 161 | HasNewFatalFailureHelper(); 162 | virtual ~HasNewFatalFailureHelper(); 163 | virtual void ReportTestPartResult(const TestPartResult& result); 164 | bool has_new_fatal_failure() const { return has_new_fatal_failure_; } 165 | private: 166 | bool has_new_fatal_failure_; 167 | TestPartResultReporterInterface* original_reporter_; 168 | 169 | GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper); 170 | }; 171 | 172 | } // namespace internal 173 | 174 | } // namespace testing 175 | 176 | #endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ 177 | -------------------------------------------------------------------------------- /thirdparty/gtest/include/gtest/gtest_prod.h: -------------------------------------------------------------------------------- 1 | // Copyright 2006, Google Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of Google Inc. nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | // Author: wan@google.com (Zhanyong Wan) 31 | // 32 | // Google C++ Testing Framework definitions useful in production code. 33 | 34 | #ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_ 35 | #define GTEST_INCLUDE_GTEST_GTEST_PROD_H_ 36 | 37 | // When you need to test the private or protected members of a class, 38 | // use the FRIEND_TEST macro to declare your tests as friends of the 39 | // class. For example: 40 | // 41 | // class MyClass { 42 | // private: 43 | // void MyMethod(); 44 | // FRIEND_TEST(MyClassTest, MyMethod); 45 | // }; 46 | // 47 | // class MyClassTest : public testing::Test { 48 | // // ... 49 | // }; 50 | // 51 | // TEST_F(MyClassTest, MyMethod) { 52 | // // Can call MyClass::MyMethod() here. 53 | // } 54 | 55 | #define FRIEND_TEST(test_case_name, test_name)\ 56 | friend class test_case_name##_##test_name##_Test 57 | 58 | #endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_ 59 | -------------------------------------------------------------------------------- /thirdparty/gtest/include/gtest/internal/gtest-linked_ptr.h: -------------------------------------------------------------------------------- 1 | // Copyright 2003 Google Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of Google Inc. nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | // Authors: Dan Egnor (egnor@google.com) 31 | // 32 | // A "smart" pointer type with reference tracking. Every pointer to a 33 | // particular object is kept on a circular linked list. When the last pointer 34 | // to an object is destroyed or reassigned, the object is deleted. 35 | // 36 | // Used properly, this deletes the object when the last reference goes away. 37 | // There are several caveats: 38 | // - Like all reference counting schemes, cycles lead to leaks. 39 | // - Each smart pointer is actually two pointers (8 bytes instead of 4). 40 | // - Every time a pointer is assigned, the entire list of pointers to that 41 | // object is traversed. This class is therefore NOT SUITABLE when there 42 | // will often be more than two or three pointers to a particular object. 43 | // - References are only tracked as long as linked_ptr<> objects are copied. 44 | // If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS 45 | // will happen (double deletion). 46 | // 47 | // A good use of this class is storing object references in STL containers. 48 | // You can safely put linked_ptr<> in a vector<>. 49 | // Other uses may not be as good. 50 | // 51 | // Note: If you use an incomplete type with linked_ptr<>, the class 52 | // *containing* linked_ptr<> must have a constructor and destructor (even 53 | // if they do nothing!). 54 | // 55 | // Bill Gibbons suggested we use something like this. 56 | // 57 | // Thread Safety: 58 | // Unlike other linked_ptr implementations, in this implementation 59 | // a linked_ptr object is thread-safe in the sense that: 60 | // - it's safe to copy linked_ptr objects concurrently, 61 | // - it's safe to copy *from* a linked_ptr and read its underlying 62 | // raw pointer (e.g. via get()) concurrently, and 63 | // - it's safe to write to two linked_ptrs that point to the same 64 | // shared object concurrently. 65 | // TODO(wan@google.com): rename this to safe_linked_ptr to avoid 66 | // confusion with normal linked_ptr. 67 | 68 | #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ 69 | #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ 70 | 71 | #include 72 | #include 73 | 74 | #include "gtest/internal/gtest-port.h" 75 | 76 | namespace testing { 77 | namespace internal { 78 | 79 | // Protects copying of all linked_ptr objects. 80 | GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_linked_ptr_mutex); 81 | 82 | // This is used internally by all instances of linked_ptr<>. It needs to be 83 | // a non-template class because different types of linked_ptr<> can refer to 84 | // the same object (linked_ptr(obj) vs linked_ptr(obj)). 85 | // So, it needs to be possible for different types of linked_ptr to participate 86 | // in the same circular linked list, so we need a single class type here. 87 | // 88 | // DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr. 89 | class linked_ptr_internal { 90 | public: 91 | // Create a new circle that includes only this instance. 92 | void join_new() { 93 | next_ = this; 94 | } 95 | 96 | // Many linked_ptr operations may change p.link_ for some linked_ptr 97 | // variable p in the same circle as this object. Therefore we need 98 | // to prevent two such operations from occurring concurrently. 99 | // 100 | // Note that different types of linked_ptr objects can coexist in a 101 | // circle (e.g. linked_ptr, linked_ptr, and 102 | // linked_ptr). Therefore we must use a single mutex to 103 | // protect all linked_ptr objects. This can create serious 104 | // contention in production code, but is acceptable in a testing 105 | // framework. 106 | 107 | // Join an existing circle. 108 | // L < g_linked_ptr_mutex 109 | void join(linked_ptr_internal const* ptr) { 110 | MutexLock lock(&g_linked_ptr_mutex); 111 | 112 | linked_ptr_internal const* p = ptr; 113 | while (p->next_ != ptr) p = p->next_; 114 | p->next_ = this; 115 | next_ = ptr; 116 | } 117 | 118 | // Leave whatever circle we're part of. Returns true if we were the 119 | // last member of the circle. Once this is done, you can join() another. 120 | // L < g_linked_ptr_mutex 121 | bool depart() { 122 | MutexLock lock(&g_linked_ptr_mutex); 123 | 124 | if (next_ == this) return true; 125 | linked_ptr_internal const* p = next_; 126 | while (p->next_ != this) p = p->next_; 127 | p->next_ = next_; 128 | return false; 129 | } 130 | 131 | private: 132 | mutable linked_ptr_internal const* next_; 133 | }; 134 | 135 | template 136 | class linked_ptr { 137 | public: 138 | typedef T element_type; 139 | 140 | // Take over ownership of a raw pointer. This should happen as soon as 141 | // possible after the object is created. 142 | explicit linked_ptr(T* ptr = NULL) { capture(ptr); } 143 | ~linked_ptr() { depart(); } 144 | 145 | // Copy an existing linked_ptr<>, adding ourselves to the list of references. 146 | template linked_ptr(linked_ptr const& ptr) { copy(&ptr); } 147 | linked_ptr(linked_ptr const& ptr) { // NOLINT 148 | assert(&ptr != this); 149 | copy(&ptr); 150 | } 151 | 152 | // Assignment releases the old value and acquires the new. 153 | template linked_ptr& operator=(linked_ptr const& ptr) { 154 | depart(); 155 | copy(&ptr); 156 | return *this; 157 | } 158 | 159 | linked_ptr& operator=(linked_ptr const& ptr) { 160 | if (&ptr != this) { 161 | depart(); 162 | copy(&ptr); 163 | } 164 | return *this; 165 | } 166 | 167 | // Smart pointer members. 168 | void reset(T* ptr = NULL) { 169 | depart(); 170 | capture(ptr); 171 | } 172 | T* get() const { return value_; } 173 | T* operator->() const { return value_; } 174 | T& operator*() const { return *value_; } 175 | 176 | bool operator==(T* p) const { return value_ == p; } 177 | bool operator!=(T* p) const { return value_ != p; } 178 | template 179 | bool operator==(linked_ptr const& ptr) const { 180 | return value_ == ptr.get(); 181 | } 182 | template 183 | bool operator!=(linked_ptr const& ptr) const { 184 | return value_ != ptr.get(); 185 | } 186 | 187 | private: 188 | template 189 | friend class linked_ptr; 190 | 191 | T* value_; 192 | linked_ptr_internal link_; 193 | 194 | void depart() { 195 | if (link_.depart()) delete value_; 196 | } 197 | 198 | void capture(T* ptr) { 199 | value_ = ptr; 200 | link_.join_new(); 201 | } 202 | 203 | template void copy(linked_ptr const* ptr) { 204 | value_ = ptr->get(); 205 | if (value_) 206 | link_.join(&ptr->link_); 207 | else 208 | link_.join_new(); 209 | } 210 | }; 211 | 212 | template inline 213 | bool operator==(T* ptr, const linked_ptr& x) { 214 | return ptr == x.get(); 215 | } 216 | 217 | template inline 218 | bool operator!=(T* ptr, const linked_ptr& x) { 219 | return ptr != x.get(); 220 | } 221 | 222 | // A function to convert T* into linked_ptr 223 | // Doing e.g. make_linked_ptr(new FooBarBaz(arg)) is a shorter notation 224 | // for linked_ptr >(new FooBarBaz(arg)) 225 | template 226 | linked_ptr make_linked_ptr(T* ptr) { 227 | return linked_ptr(ptr); 228 | } 229 | 230 | } // namespace internal 231 | } // namespace testing 232 | 233 | #endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ 234 | -------------------------------------------------------------------------------- /thirdparty/gtest/src/gtest-all.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2008, Google Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of Google Inc. nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | // Author: mheule@google.com (Markus Heule) 31 | // 32 | // Google C++ Testing Framework (Google Test) 33 | // 34 | // Sometimes it's desirable to build Google Test by compiling a single file. 35 | // This file serves this purpose. 36 | 37 | // This line ensures that gtest.h can be compiled on its own, even 38 | // when it's fused. 39 | #include "gtest/gtest.h" 40 | 41 | // The following lines pull in the real gtest *.cc files. 42 | #include "src/gtest.cc" 43 | #include "src/gtest-death-test.cc" 44 | #include "src/gtest-filepath.cc" 45 | #include "src/gtest-port.cc" 46 | #include "src/gtest-printers.cc" 47 | #include "src/gtest-test-part.cc" 48 | #include "src/gtest-typed-test.cc" 49 | -------------------------------------------------------------------------------- /thirdparty/gtest/src/gtest-test-part.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2008, Google Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of Google Inc. nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | // Author: mheule@google.com (Markus Heule) 31 | // 32 | // The Google C++ Testing Framework (Google Test) 33 | 34 | #include "gtest/gtest-test-part.h" 35 | 36 | // Indicates that this translation unit is part of Google Test's 37 | // implementation. It must come before gtest-internal-inl.h is 38 | // included, or there will be a compiler error. This trick is to 39 | // prevent a user from accidentally including gtest-internal-inl.h in 40 | // his code. 41 | #define GTEST_IMPLEMENTATION_ 1 42 | #include "src/gtest-internal-inl.h" 43 | #undef GTEST_IMPLEMENTATION_ 44 | 45 | namespace testing { 46 | 47 | using internal::GetUnitTestImpl; 48 | 49 | // Gets the summary of the failure message by omitting the stack trace 50 | // in it. 51 | internal::String TestPartResult::ExtractSummary(const char* message) { 52 | const char* const stack_trace = strstr(message, internal::kStackTraceMarker); 53 | return stack_trace == NULL ? internal::String(message) : 54 | internal::String(message, stack_trace - message); 55 | } 56 | 57 | // Prints a TestPartResult object. 58 | std::ostream& operator<<(std::ostream& os, const TestPartResult& result) { 59 | return os 60 | << result.file_name() << ":" << result.line_number() << ": " 61 | << (result.type() == TestPartResult::kSuccess ? "Success" : 62 | result.type() == TestPartResult::kFatalFailure ? "Fatal failure" : 63 | "Non-fatal failure") << ":\n" 64 | << result.message() << std::endl; 65 | } 66 | 67 | // Appends a TestPartResult to the array. 68 | void TestPartResultArray::Append(const TestPartResult& result) { 69 | array_.push_back(result); 70 | } 71 | 72 | // Returns the TestPartResult at the given index (0-based). 73 | const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const { 74 | if (index < 0 || index >= size()) { 75 | printf("\nInvalid index (%d) into TestPartResultArray.\n", index); 76 | internal::posix::Abort(); 77 | } 78 | 79 | return array_[index]; 80 | } 81 | 82 | // Returns the number of TestPartResult objects in the array. 83 | int TestPartResultArray::size() const { 84 | return static_cast(array_.size()); 85 | } 86 | 87 | namespace internal { 88 | 89 | HasNewFatalFailureHelper::HasNewFatalFailureHelper() 90 | : has_new_fatal_failure_(false), 91 | original_reporter_(GetUnitTestImpl()-> 92 | GetTestPartResultReporterForCurrentThread()) { 93 | GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this); 94 | } 95 | 96 | HasNewFatalFailureHelper::~HasNewFatalFailureHelper() { 97 | GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread( 98 | original_reporter_); 99 | } 100 | 101 | void HasNewFatalFailureHelper::ReportTestPartResult( 102 | const TestPartResult& result) { 103 | if (result.fatally_failed()) 104 | has_new_fatal_failure_ = true; 105 | original_reporter_->ReportTestPartResult(result); 106 | } 107 | 108 | } // namespace internal 109 | 110 | } // namespace testing 111 | -------------------------------------------------------------------------------- /thirdparty/gtest/src/gtest-typed-test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2008 Google Inc. 2 | // All Rights Reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of Google Inc. nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | // Author: wan@google.com (Zhanyong Wan) 31 | 32 | #include "gtest/gtest-typed-test.h" 33 | #include "gtest/gtest.h" 34 | 35 | namespace testing { 36 | namespace internal { 37 | 38 | #if GTEST_HAS_TYPED_TEST_P 39 | 40 | // Skips to the first non-space char in str. Returns an empty string if str 41 | // contains only whitespace characters. 42 | static const char* SkipSpaces(const char* str) { 43 | while (IsSpace(*str)) 44 | str++; 45 | return str; 46 | } 47 | 48 | // Verifies that registered_tests match the test names in 49 | // defined_test_names_; returns registered_tests if successful, or 50 | // aborts the program otherwise. 51 | const char* TypedTestCasePState::VerifyRegisteredTestNames( 52 | const char* file, int line, const char* registered_tests) { 53 | typedef ::std::set::const_iterator DefinedTestIter; 54 | registered_ = true; 55 | 56 | // Skip initial whitespace in registered_tests since some 57 | // preprocessors prefix stringizied literals with whitespace. 58 | registered_tests = SkipSpaces(registered_tests); 59 | 60 | Message errors; 61 | ::std::set tests; 62 | for (const char* names = registered_tests; names != NULL; 63 | names = SkipComma(names)) { 64 | const String name = GetPrefixUntilComma(names); 65 | if (tests.count(name) != 0) { 66 | errors << "Test " << name << " is listed more than once.\n"; 67 | continue; 68 | } 69 | 70 | bool found = false; 71 | for (DefinedTestIter it = defined_test_names_.begin(); 72 | it != defined_test_names_.end(); 73 | ++it) { 74 | if (name == *it) { 75 | found = true; 76 | break; 77 | } 78 | } 79 | 80 | if (found) { 81 | tests.insert(name); 82 | } else { 83 | errors << "No test named " << name 84 | << " can be found in this test case.\n"; 85 | } 86 | } 87 | 88 | for (DefinedTestIter it = defined_test_names_.begin(); 89 | it != defined_test_names_.end(); 90 | ++it) { 91 | if (tests.count(*it) == 0) { 92 | errors << "You forgot to list test " << *it << ".\n"; 93 | } 94 | } 95 | 96 | const String& errors_str = errors.GetString(); 97 | if (errors_str != "") { 98 | fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), 99 | errors_str.c_str()); 100 | fflush(stderr); 101 | posix::Abort(); 102 | } 103 | 104 | return registered_tests; 105 | } 106 | 107 | #endif // GTEST_HAS_TYPED_TEST_P 108 | 109 | } // namespace internal 110 | } // namespace testing 111 | -------------------------------------------------------------------------------- /thirdparty/gtest/src/gtest_main.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2006, Google Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of Google Inc. nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | #include 31 | 32 | #include "gtest/gtest.h" 33 | 34 | GTEST_API_ int main(int argc, char **argv) { 35 | std::cout << "Running main() from gtest_main.cc\n"; 36 | 37 | testing::InitGoogleTest(&argc, argv); 38 | return RUN_ALL_TESTS(); 39 | } 40 | --------------------------------------------------------------------------------