Compare commits
9 Commits
7b31bd5efe
...
6e66202d5e
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e66202d5e | |||
| a92271a205 | |||
| 0dbfb4deae | |||
| 6e229b6b36 | |||
| 2200de11c8 | |||
| b37feb58dd | |||
| 94a4802824 | |||
| 707dbdb391 | |||
| bdd343bb57 |
+45
-15
@@ -31,11 +31,24 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
|||||||
"MinSizeRel" "RelWithDebInfo")
|
"MinSizeRel" "RelWithDebInfo")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_compile_options(-fdata-sections -ffunction-sections -Wswitch-enum
|
add_compile_options(
|
||||||
-Werror=switch-enum -fPIC)
|
-Werror=switch-enum
|
||||||
|
-Wswitch-enum
|
||||||
|
-Wunused-variable
|
||||||
|
-fPIC
|
||||||
|
-fdata-sections
|
||||||
|
-ffunction-sections
|
||||||
|
-fno-jump-tables # https://github.com/llvm/llvm-project/issues/54247
|
||||||
|
)
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
add_link_options("-Wno-unused-command-line-argument")
|
add_link_options("-Wno-unused-command-line-argument")
|
||||||
|
find_program(LLVM_OBJCOPY llvm-objcopy)
|
||||||
|
if(LLVM_OBJCOPY)
|
||||||
|
set(CMAKE_OBJCOPY
|
||||||
|
${LLVM_OBJCOPY}
|
||||||
|
CACHE FILEPATH "path to objcopy binary" FORCE)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
@@ -56,6 +69,21 @@ if(HAS_FULL_RELRO)
|
|||||||
endif()
|
endif()
|
||||||
cmake_pop_check_state()
|
cmake_pop_check_state()
|
||||||
|
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
|
||||||
|
add_compile_options(-mbranch-protection=standard)
|
||||||
|
else()
|
||||||
|
add_compile_options(-fcf-protection)
|
||||||
|
set(rewrite_endbr_flags "-fuse-ld=mold;LINKER:-z,rewrite-endbr")
|
||||||
|
cmake_push_check_state()
|
||||||
|
list(APPEND CMAKE_REQUIRED_LINK_OPTIONS ${rewrite_endbr_flags})
|
||||||
|
check_cxx_source_compiles("int main(){}" HAS_REWRITE_ENDBR FAIL_REGEX
|
||||||
|
"warning:")
|
||||||
|
if(HAS_REWRITE_ENDBR)
|
||||||
|
add_link_options(${rewrite_endbr_flags})
|
||||||
|
endif()
|
||||||
|
cmake_pop_check_state()
|
||||||
|
endif()
|
||||||
|
|
||||||
set(version_script_flags
|
set(version_script_flags
|
||||||
LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/linker.map)
|
LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/linker.map)
|
||||||
cmake_push_check_state()
|
cmake_push_check_state()
|
||||||
@@ -81,19 +109,11 @@ else()
|
|||||||
add_link_options(-Wl,--gc-sections)
|
add_link_options(-Wl,--gc-sections)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT USE_SIMD_FALLBACK)
|
if(USE_SIMD_FALLBACK)
|
||||||
cmake_push_check_state()
|
add_compile_definitions(USE_SIMD_FALLBACK)
|
||||||
list(APPEND CMAKE_REQUIRED_FLAGS -mavx)
|
else()
|
||||||
check_include_file_cxx("immintrin.h" HAS_AVX)
|
if(CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64)
|
||||||
if(HAS_AVX)
|
|
||||||
add_compile_options(-mavx)
|
add_compile_options(-mavx)
|
||||||
add_compile_definitions(HAS_AVX)
|
|
||||||
endif()
|
|
||||||
cmake_pop_check_state()
|
|
||||||
|
|
||||||
check_include_file_cxx("arm_neon.h" HAS_ARM_NEON)
|
|
||||||
if(HAS_ARM_NEON)
|
|
||||||
add_compile_definitions(HAS_ARM_NEON)
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -323,7 +343,8 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
|||||||
# c++98
|
# c++98
|
||||||
add_executable(conflict_set_cxx_api_test conflict_set_cxx_api_test.cpp)
|
add_executable(conflict_set_cxx_api_test conflict_set_cxx_api_test.cpp)
|
||||||
target_compile_options(conflict_set_cxx_api_test PRIVATE ${TEST_FLAGS})
|
target_compile_options(conflict_set_cxx_api_test PRIVATE ${TEST_FLAGS})
|
||||||
target_link_libraries(conflict_set_cxx_api_test PRIVATE ${PROJECT_NAME})
|
target_link_libraries(conflict_set_cxx_api_test
|
||||||
|
PRIVATE ${PROJECT_NAME}-static)
|
||||||
set_target_properties(conflict_set_cxx_api_test PROPERTIES CXX_STANDARD 98)
|
set_target_properties(conflict_set_cxx_api_test PROPERTIES CXX_STANDARD 98)
|
||||||
set_target_properties(conflict_set_cxx_api_test
|
set_target_properties(conflict_set_cxx_api_test
|
||||||
PROPERTIES CXX_STANDARD_REQUIRED ON)
|
PROPERTIES CXX_STANDARD_REQUIRED ON)
|
||||||
@@ -356,6 +377,15 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
|||||||
${symbol_imports})
|
${symbol_imports})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(NOT CMAKE_CROSSCOMPILING)
|
||||||
|
find_program(HARDENING_CHECK hardening-check)
|
||||||
|
if(HARDENING_CHECK)
|
||||||
|
add_test(NAME hardening_check
|
||||||
|
COMMAND ${HARDENING_CHECK} $<TARGET_FILE:${PROJECT_NAME}>
|
||||||
|
--nofortify --nostackprotector)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# bench
|
# bench
|
||||||
add_executable(conflict_set_bench Bench.cpp)
|
add_executable(conflict_set_bench Bench.cpp)
|
||||||
target_link_libraries(conflict_set_bench PRIVATE ${PROJECT_NAME} nanobench)
|
target_link_libraries(conflict_set_bench PRIVATE ${PROJECT_NAME} nanobench)
|
||||||
|
|||||||
+58
-54
@@ -14,6 +14,16 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if !defined(USE_SIMD_FALLBACK) && defined(__has_include)
|
||||||
|
#if __has_include("immintrin.h")
|
||||||
|
#define HAS_AVX 1
|
||||||
|
#include <immintrin.h>
|
||||||
|
#elif __has_include("arm_neon.h")
|
||||||
|
#define HAS_ARM_NEON 1
|
||||||
|
#include <arm_neon.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "ConflictSet.h"
|
#include "ConflictSet.h"
|
||||||
#include "Internal.h"
|
#include "Internal.h"
|
||||||
#include "LongestCommonPrefix.h"
|
#include "LongestCommonPrefix.h"
|
||||||
@@ -34,12 +44,6 @@ limitations under the License.
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#ifdef HAS_AVX
|
|
||||||
#include <immintrin.h>
|
|
||||||
#elif defined(HAS_ARM_NEON)
|
|
||||||
#include <arm_neon.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef __SANITIZE_THREAD__
|
#ifndef __SANITIZE_THREAD__
|
||||||
#if defined(__has_feature)
|
#if defined(__has_feature)
|
||||||
#if __has_feature(thread_sanitizer)
|
#if __has_feature(thread_sanitizer)
|
||||||
@@ -3183,6 +3187,12 @@ Node *firstGeqPhysical(Node *n, const TrivialSpan key) {
|
|||||||
#define PRESERVE_NONE
|
#define PRESERVE_NONE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if __has_attribute(musttail) && __has_attribute(preserve_none)
|
||||||
|
constexpr bool kEnableInterleaved = true;
|
||||||
|
#else
|
||||||
|
constexpr bool kEnableInterleaved = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace check {
|
namespace check {
|
||||||
|
|
||||||
typedef PRESERVE_NONE void (*Continuation)(struct Job *, struct Context *);
|
typedef PRESERVE_NONE void (*Continuation)(struct Job *, struct Context *);
|
||||||
@@ -4969,51 +4979,50 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
check::Context context;
|
check::Context context;
|
||||||
context.readContext.impl = this;
|
context.readContext.impl = this;
|
||||||
|
|
||||||
#if __has_attribute(musttail)
|
if constexpr (kEnableInterleaved) {
|
||||||
if (count == 1) {
|
if (count == 1) {
|
||||||
useSequential(reads, result, count, context);
|
useSequential(reads, result, count, context);
|
||||||
} else {
|
} else {
|
||||||
constexpr int kConcurrent = 16;
|
constexpr int kConcurrent = 16;
|
||||||
check::Job inProgress[kConcurrent];
|
check::Job inProgress[kConcurrent];
|
||||||
context.count = count;
|
context.count = count;
|
||||||
context.oldestVersionFullPrecision = oldestVersionFullPrecision;
|
context.oldestVersionFullPrecision = oldestVersionFullPrecision;
|
||||||
context.root = root;
|
context.root = root;
|
||||||
context.queries = reads;
|
context.queries = reads;
|
||||||
context.results = result;
|
context.results = result;
|
||||||
int64_t started = std::min(kConcurrent, count);
|
int64_t started = std::min(kConcurrent, count);
|
||||||
context.started = started;
|
context.started = started;
|
||||||
for (int i = 0; i < started; i++) {
|
for (int i = 0; i < started; i++) {
|
||||||
inProgress[i].init(reads + i, result + i, root,
|
inProgress[i].init(reads + i, result + i, root,
|
||||||
oldestVersionFullPrecision);
|
oldestVersionFullPrecision);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < started - 1; i++) {
|
for (int i = 0; i < started - 1; i++) {
|
||||||
inProgress[i].next = inProgress + i + 1;
|
inProgress[i].next = inProgress + i + 1;
|
||||||
}
|
}
|
||||||
for (int i = 1; i < started; i++) {
|
for (int i = 1; i < started; i++) {
|
||||||
inProgress[i].prev = inProgress + i - 1;
|
inProgress[i].prev = inProgress + i - 1;
|
||||||
}
|
}
|
||||||
inProgress[0].prev = inProgress + started - 1;
|
inProgress[0].prev = inProgress + started - 1;
|
||||||
inProgress[started - 1].next = inProgress;
|
inProgress[started - 1].next = inProgress;
|
||||||
|
|
||||||
// Kick off the sequence of tail calls that finally returns once all jobs
|
// Kick off the sequence of tail calls that finally returns once all
|
||||||
// are done
|
// jobs are done
|
||||||
inProgress->continuation(inProgress, &context);
|
inProgress->continuation(inProgress, &context);
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
Arena arena;
|
Arena arena;
|
||||||
auto *results2 = new (arena) Result[count];
|
auto *results2 = new (arena) Result[count];
|
||||||
check::Context context2;
|
check::Context context2;
|
||||||
context2.readContext.impl = this;
|
context2.readContext.impl = this;
|
||||||
useSequential(reads, results2, count, context2);
|
useSequential(reads, results2, count, context2);
|
||||||
assert(memcmp(result, results2, count) == 0);
|
assert(memcmp(result, results2, count) == 0);
|
||||||
assert(context.readContext == context2.readContext);
|
assert(context.readContext == context2.readContext);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
useSequential(reads, result, count, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
|
||||||
useSequential(reads, result, count, context);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
assert(reads[i].readVersion >= 0);
|
assert(reads[i].readVersion >= 0);
|
||||||
assert(reads[i].readVersion <= newestVersionFullPrecision);
|
assert(reads[i].readVersion <= newestVersionFullPrecision);
|
||||||
@@ -5186,11 +5195,6 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
assert(allPointWrites || sorted);
|
assert(allPointWrites || sorted);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if __has_attribute(musttail)
|
|
||||||
constexpr bool kEnableInterleaved = true;
|
|
||||||
#else
|
|
||||||
constexpr bool kEnableInterleaved = false;
|
|
||||||
#endif
|
|
||||||
if (kEnableInterleaved && count > 1) {
|
if (kEnableInterleaved && count > 1) {
|
||||||
interleavedWrites(writes, count, InternalVersionT(writeVersion));
|
interleavedWrites(writes, count, InternalVersionT(writeVersion));
|
||||||
} else {
|
} else {
|
||||||
@@ -5858,13 +5862,13 @@ void checkVersionsGeqOldestExtant(Node *n,
|
|||||||
case Type_Node0: {
|
case Type_Node0: {
|
||||||
} break;
|
} break;
|
||||||
case Type_Node3: {
|
case Type_Node3: {
|
||||||
auto *self = static_cast<Node3 *>(n);
|
[[maybe_unused]] auto *self = static_cast<Node3 *>(n);
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case Type_Node16: {
|
case Type_Node16: {
|
||||||
auto *self = static_cast<Node16 *>(n);
|
[[maybe_unused]] auto *self = static_cast<Node16 *>(n);
|
||||||
for (int i = 0; i < 16; ++i) {
|
for (int i = 0; i < 16; ++i) {
|
||||||
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
||||||
}
|
}
|
||||||
@@ -5874,7 +5878,7 @@ void checkVersionsGeqOldestExtant(Node *n,
|
|||||||
for (int i = 0; i < 48; ++i) {
|
for (int i = 0; i < 48; ++i) {
|
||||||
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
||||||
}
|
}
|
||||||
for (auto m : self->maxOfMax) {
|
for ([[maybe_unused]] auto m : self->maxOfMax) {
|
||||||
assert(m >= oldestExtantVersion);
|
assert(m >= oldestExtantVersion);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -5883,7 +5887,7 @@ void checkVersionsGeqOldestExtant(Node *n,
|
|||||||
for (int i = 0; i < 256; ++i) {
|
for (int i = 0; i < 256; ++i) {
|
||||||
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
||||||
}
|
}
|
||||||
for (auto m : self->maxOfMax) {
|
for ([[maybe_unused]] auto m : self->maxOfMax) {
|
||||||
assert(m >= oldestExtantVersion);
|
assert(m >= oldestExtantVersion);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|||||||
@@ -13,12 +13,14 @@ RUN TZ=America/Los_Angeles DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
|||||||
ccache \
|
ccache \
|
||||||
cmake \
|
cmake \
|
||||||
curl \
|
curl \
|
||||||
|
devscripts \
|
||||||
g++-aarch64-linux-gnu \
|
g++-aarch64-linux-gnu \
|
||||||
gcovr \
|
gcovr \
|
||||||
git \
|
git \
|
||||||
gnupg \
|
gnupg \
|
||||||
libc6-dbg \
|
libc6-dbg \
|
||||||
lsb-release \
|
lsb-release \
|
||||||
|
mold \
|
||||||
ninja-build \
|
ninja-build \
|
||||||
pre-commit \
|
pre-commit \
|
||||||
python3-requests \
|
python3-requests \
|
||||||
|
|||||||
+28
-12
@@ -64,31 +64,47 @@ template <class... Ts> std::string tupleKey(const Ts &...ts) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int kWindowSize = 300000;
|
constexpr int kTotalKeyRange = 1'000'000'000;
|
||||||
|
constexpr int kWindowSize = 1'000'000;
|
||||||
|
constexpr int kNumKeys = 10;
|
||||||
|
|
||||||
void workload(weaselab::ConflictSet *cs) {
|
void workload(weaselab::ConflictSet *cs) {
|
||||||
int64_t version = kWindowSize;
|
int64_t version = kWindowSize;
|
||||||
constexpr int kNumWrites = 16;
|
|
||||||
for (;; transactions.fetch_add(1, std::memory_order_relaxed)) {
|
for (;; transactions.fetch_add(1, std::memory_order_relaxed)) {
|
||||||
std::vector<int64_t> keyIndices;
|
std::vector<int64_t> keyIndices;
|
||||||
for (int i = 0; i < kNumWrites; ++i) {
|
for (int i = 0; i < kNumKeys; ++i) {
|
||||||
keyIndices.push_back(rand() % 100'000'000);
|
keyIndices.push_back(rand() % kTotalKeyRange);
|
||||||
}
|
}
|
||||||
std::sort(keyIndices.begin(), keyIndices.end());
|
std::sort(keyIndices.begin(), keyIndices.end());
|
||||||
std::vector<std::string> keys;
|
std::vector<std::string> keys;
|
||||||
std::vector<weaselab::ConflictSet::WriteRange> writes;
|
constexpr std::string_view fullString =
|
||||||
constexpr std::string_view suffix = "this is a suffix";
|
"this is a string, where a prefix of it is used as an element of the "
|
||||||
for (int i = 0; i < kNumWrites; ++i) {
|
"tuple forming the key";
|
||||||
keys.push_back(tupleKey(0x100, i, keyIndices[i],
|
for (int i = 0; i < kNumKeys; ++i) {
|
||||||
suffix.substr(0, rand() % suffix.size()),
|
keys.push_back(
|
||||||
rand()));
|
tupleKey(0x100, keyIndices[i] / fullString.size(),
|
||||||
|
fullString.substr(0, keyIndices[i] % fullString.size())));
|
||||||
// printf("%s\n", printable(keys.back()).c_str());
|
// printf("%s\n", printable(keys.back()).c_str());
|
||||||
}
|
}
|
||||||
for (int i = 0; i < kNumWrites; ++i) {
|
|
||||||
|
std::vector<weaselab::ConflictSet::ReadRange> reads;
|
||||||
|
std::vector<weaselab::ConflictSet::WriteRange> writes;
|
||||||
|
std::vector<weaselab::ConflictSet::Result> results;
|
||||||
|
for (int i = 0; i < kNumKeys; ++i) {
|
||||||
writes.push_back({{(const uint8_t *)keys[i].data(), int(keys[i].size())},
|
writes.push_back({{(const uint8_t *)keys[i].data(), int(keys[i].size())},
|
||||||
{nullptr, 0}});
|
{nullptr, 0}});
|
||||||
|
reads.push_back({{(const uint8_t *)keys[i].data(), int(keys[i].size())},
|
||||||
|
{nullptr, 0},
|
||||||
|
version - kWindowSize});
|
||||||
}
|
}
|
||||||
cs->addWrites(writes.data(), writes.size(), version);
|
results.resize(reads.size());
|
||||||
|
|
||||||
|
cs->check(reads.data(), results.data(), reads.size());
|
||||||
|
bool ok = true;
|
||||||
|
for (auto result : results) {
|
||||||
|
ok &= result == weaselab::ConflictSet::Commit;
|
||||||
|
}
|
||||||
|
cs->addWrites(writes.data(), ok ? writes.size() : 0, version);
|
||||||
cs->setOldestVersion(version - kWindowSize);
|
cs->setOldestVersion(version - kWindowSize);
|
||||||
++version;
|
++version;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -767,7 +767,9 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
false, true);
|
false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
sortPoints(points);
|
if (!std::is_sorted(points.begin(), points.end())) {
|
||||||
|
sortPoints(points);
|
||||||
|
}
|
||||||
|
|
||||||
int activeWriteCount = 0;
|
int activeWriteCount = 0;
|
||||||
std::vector<std::pair<StringRef, StringRef>> combinedWriteConflictRanges;
|
std::vector<std::pair<StringRef, StringRef>> combinedWriteConflictRanges;
|
||||||
@@ -794,7 +796,6 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
int temp[stripeSize];
|
int temp[stripeSize];
|
||||||
int stripes = (stringCount + stripeSize - 1) / stripeSize;
|
int stripes = (stringCount + stripeSize - 1) / stripeSize;
|
||||||
StringRef values[stripeSize];
|
StringRef values[stripeSize];
|
||||||
int64_t writeVersions[stripeSize / 2];
|
|
||||||
int ss = stringCount - (stripes - 1) * stripeSize;
|
int ss = stringCount - (stripes - 1) * stripeSize;
|
||||||
int64_t entryDelta = 0;
|
int64_t entryDelta = 0;
|
||||||
for (int s = stripes - 1; s >= 0; s--) {
|
for (int s = stripes - 1; s >= 0; s--) {
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user